├── vue-admin ├── .husky │ ├── pre-commit │ └── commit-msg ├── src │ ├── types │ │ ├── shims-vue.d.ts │ │ └── env.d.ts │ ├── assets │ │ ├── logo.png │ │ ├── images │ │ │ ├── 401.gif │ │ │ ├── 404.png │ │ │ ├── 404_cloud.png │ │ │ ├── login-bg.jpg │ │ │ └── login-bg-dark.jpg │ │ └── icons │ │ │ ├── size.svg │ │ │ ├── chart.svg │ │ │ ├── fullscreen-exit.svg │ │ │ ├── fullscreen.svg │ │ │ ├── setting.svg │ │ │ ├── close_all.svg │ │ │ ├── close_other.svg │ │ │ ├── guide.svg │ │ │ ├── drag.svg │ │ │ ├── rabbitmq.svg │ │ │ ├── close.svg │ │ │ ├── language.svg │ │ │ ├── refresh.svg │ │ │ ├── shrink.svg │ │ │ ├── ip.svg │ │ │ ├── close_left.svg │ │ │ ├── theme.svg │ │ │ ├── close_right.svg │ │ │ ├── nested.svg │ │ │ ├── monitor.svg │ │ │ ├── document.svg │ │ │ ├── eye.svg │ │ │ ├── user.svg │ │ │ ├── uv.svg │ │ │ ├── dict_item.svg │ │ │ ├── link.svg │ │ │ ├── advert.svg │ │ │ ├── download.svg │ │ │ ├── visit.svg │ │ │ ├── skill.svg │ │ │ ├── perm.svg │ │ │ ├── multi_level.svg │ │ │ ├── eye-open.svg │ │ │ ├── menu.svg │ │ │ ├── system.svg │ │ │ ├── role.svg │ │ │ ├── message.svg │ │ │ ├── client.svg │ │ │ ├── publish.svg │ │ │ ├── cart.svg │ │ │ ├── todolist.svg │ │ │ ├── bug.svg │ │ │ ├── github.svg │ │ │ └── project.svg │ ├── plugins │ │ ├── index.ts │ │ ├── i18n.ts │ │ └── icons.ts │ ├── api │ │ ├── file │ │ │ ├── types.ts │ │ │ └── index.ts │ │ ├── auth │ │ │ └── types.ts │ │ └── system │ │ │ ├── dept │ │ │ └── types.ts │ │ │ └── role │ │ │ └── types.ts │ ├── directive │ │ ├── index.ts │ │ └── permission │ │ │ └── index.ts │ ├── enums │ │ ├── ThemeEnum.ts │ │ ├── LayoutEnum.ts │ │ └── MenuTypeEnum.ts │ ├── views │ │ ├── redirect │ │ │ └── index.vue │ │ └── login │ │ │ └── OAuth2Redirect.vue │ ├── utils │ │ ├── nprogress.ts │ │ ├── i18n.ts │ │ └── pkce.ts │ ├── layout │ │ └── components │ │ │ ├── NavBar │ │ │ ├── index.vue │ │ │ └── components │ │ │ │ └── NavbarLeft.vue │ │ │ ├── Settings │ │ │ └── components │ │ │ │ └── ThemeColorPicker.vue │ │ │ └── Sidebar │ │ │ ├── components │ │ │ └── SidebarMenuItemTitle.vue │ │ │ └── index.vue │ ├── lang │ │ ├── package │ │ │ ├── zh-cn.ts │ │ │ └── en.ts │ │ └── index.ts │ ├── settings.ts │ ├── store │ │ └── index.ts │ ├── styles │ │ ├── variables.module.scss │ │ ├── index.scss │ │ ├── variables.scss │ │ └── reset.scss │ ├── main.ts │ ├── components │ │ ├── Hamburger │ │ │ └── index.vue │ │ ├── AppLink │ │ │ └── index.vue │ │ ├── SizeSelect │ │ │ └── index.vue │ │ ├── SvgIcon │ │ │ └── index.vue │ │ └── LangSelect │ │ │ └── index.vue │ └── App.vue ├── public │ └── favicon.ico ├── .env.production ├── .prettierignore ├── .stylelintignore ├── .eslintignore ├── .gitignore ├── .vscode │ └── extensions.json ├── .editorconfig ├── .env.development ├── tsconfig.json ├── LICENSE ├── uno.config.ts ├── .stylelintrc.cjs └── .prettierrc.cjs ├── docs └── nacos │ └── nacos_config.zip ├── youlai-auth ├── src │ └── main │ │ ├── resources │ │ ├── bootstrap.yml │ │ ├── bootstrap-prod.yml │ │ └── bootstrap-dev.yml │ │ └── java │ │ └── com │ │ └── youlai │ │ └── auth │ │ ├── enums │ │ ├── CaptchaCodeTypeEnum.java │ │ └── CaptchaTypeEnum.java │ │ ├── model │ │ ├── LoginUserInfo.java │ │ └── CaptchaResult.java │ │ ├── oauth2 │ │ ├── extension │ │ │ └── captcha │ │ │ │ └── CaptchaParameterNames.java │ │ ├── jackson │ │ │ └── SysUserMixin.java │ │ └── oidc │ │ │ └── CustomOidcAuthenticationConverter.java │ │ ├── AuthApplication.java │ │ ├── controller │ │ └── AuthController.java │ │ └── config │ │ └── CorsConfig.java └── Dockerfile ├── youlai-gateway ├── src │ └── main │ │ ├── resources │ │ ├── bootstrap.yml │ │ ├── bootstrap-prod.yml │ │ └── bootstrap-dev.yml │ │ └── java │ │ └── com │ │ └── youlai │ │ └── gateway │ │ ├── GatewayApplication.java │ │ └── config │ │ └── SentinelConfig.java └── Dockerfile ├── youlai-system ├── system-boot │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── bootstrap.yml │ │ │ ├── excel-templates │ │ │ │ └── 用户导入模板.xlsx │ │ │ ├── mapper │ │ │ │ ├── SysDeptMapper.xml │ │ │ │ ├── SysDictMapper.xml │ │ │ │ ├── SysRoleMapper.xml │ │ │ │ ├── SysUserRoleMapper.xml │ │ │ │ └── SysDictTypeMapper.xml │ │ │ ├── bootstrap-prod.yml │ │ │ └── bootstrap-dev.yml │ │ │ └── java │ │ │ └── com │ │ │ └── youlai │ │ │ └── system │ │ │ ├── mapper │ │ │ ├── SysDictMapper.java │ │ │ ├── SysDictTypeMapper.java │ │ │ ├── SysRoleMapper.java │ │ │ ├── SysMenuMapper.java │ │ │ ├── SysUserRoleMapper.java │ │ │ ├── SysDeptMapper.java │ │ │ └── SysRoleMenuMapper.java │ │ │ ├── model │ │ │ ├── vo │ │ │ │ ├── FileInfoVO.java │ │ │ │ ├── DictPageVO.java │ │ │ │ ├── DictTypePageVO.java │ │ │ │ ├── UserImportVO.java │ │ │ │ ├── UserInfoVO.java │ │ │ │ ├── RolePageVO.java │ │ │ │ ├── UserProfileVO.java │ │ │ │ ├── DeptVO.java │ │ │ │ ├── UserExportVO.java │ │ │ │ ├── UserPageVO.java │ │ │ │ └── MenuVO.java │ │ │ ├── query │ │ │ │ ├── DictTypePageQuery.java │ │ │ │ ├── RolePageQuery.java │ │ │ │ ├── DictPageQuery.java │ │ │ │ ├── DeptQuery.java │ │ │ │ ├── MenuQuery.java │ │ │ │ ├── PermPageQuery.java │ │ │ │ └── UserPageQuery.java │ │ │ ├── bo │ │ │ │ ├── RolePermsBO.java │ │ │ │ ├── UserFormBO.java │ │ │ │ ├── UserProfileBO.java │ │ │ │ ├── UserBO.java │ │ │ │ └── RouteBO.java │ │ │ ├── entity │ │ │ │ ├── SysRoleMenu.java │ │ │ │ ├── SysUserRole.java │ │ │ │ ├── SysDictType.java │ │ │ │ ├── SysDept.java │ │ │ │ ├── SysRole.java │ │ │ │ ├── SysDict.java │ │ │ │ └── SysUser.java │ │ │ └── form │ │ │ │ ├── DictTypeForm.java │ │ │ │ ├── DeptForm.java │ │ │ │ ├── DictForm.java │ │ │ │ ├── RoleForm.java │ │ │ │ ├── UserRegisterForm.java │ │ │ │ ├── MenuForm.java │ │ │ │ └── UserForm.java │ │ │ ├── listener │ │ │ └── MyAnalysisEventListener.java │ │ │ ├── SystemApplication.java │ │ │ ├── converter │ │ │ ├── DeptConverter.java │ │ │ ├── MenuConverter.java │ │ │ ├── DictTypeConverter.java │ │ │ ├── DictConverter.java │ │ │ └── RoleConverter.java │ │ │ ├── service │ │ │ ├── OssService.java │ │ │ ├── SysUserRoleService.java │ │ │ ├── SysRoleMenuService.java │ │ │ └── SysDeptService.java │ │ │ ├── config │ │ │ └── PasswordEncoderConfig.java │ │ │ ├── enums │ │ │ └── MenuTypeEnum.java │ │ │ ├── handler │ │ │ └── UserBlockHandler.java │ │ │ └── controller │ │ │ └── FileController.java │ └── Dockerfile ├── system-api │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring.factories │ │ │ └── java │ │ │ └── com │ │ │ └── youlai │ │ │ └── system │ │ │ ├── api │ │ │ ├── fallback │ │ │ │ └── UserFeignFallbackClient.java │ │ │ └── UserFeignClient.java │ │ │ └── dto │ │ │ └── UserAuthInfo.java │ └── pom.xml └── pom.xml ├── youlai-common ├── common-apidoc │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ └── java │ │ │ └── com │ │ │ └── youlai │ │ │ └── common │ │ │ └── apidoc │ │ │ └── ApiDocInfoProperties.java │ └── pom.xml ├── common-mybatis │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── com │ │ └── youlai │ │ └── common │ │ └── mybatis │ │ ├── enums │ │ └── DataScopeEnum.java │ │ ├── handler │ │ ├── StringArrayJsonTypeHandler.java │ │ ├── LongArrayJsonTypeHandler.java │ │ ├── IntegerArrayJsonTypeHandler.java │ │ └── MyMetaObjectHandler.java │ │ └── annotation │ │ └── DataPermission.java ├── common-sms │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── com │ │ └── youlai │ │ └── common │ │ └── sms │ │ ├── service │ │ └── SmsService.java │ │ └── property │ │ └── AliyunSmsProperties.java ├── common-redis │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── com │ │ └── youlai │ │ └── common │ │ └── redis │ │ ├── BusinessSnGenerator.java │ │ └── RedisConfig.java ├── common-web │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── youlai │ │ │ └── common │ │ │ └── web │ │ │ ├── constraint │ │ │ ├── CityType.java │ │ │ ├── CityEntity.java │ │ │ └── CheckCityValid.java │ │ │ ├── annotation │ │ │ └── PreventDuplicateResubmit.java │ │ │ ├── exception │ │ │ └── BizException.java │ │ │ ├── config │ │ │ ├── FeignDecoderConfig.java │ │ │ ├── RestTemplateConfig.java │ │ │ └── ValidationConfig.java │ │ │ └── model │ │ │ └── Option.java │ │ └── resources │ │ ├── META-INF │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── ValidationMessages.properties ├── common-core │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── com │ │ │ └── youlai │ │ │ └── common │ │ │ ├── result │ │ │ ├── IResultCode.java │ │ │ └── PageResult.java │ │ │ ├── constant │ │ │ ├── MemberConstants.java │ │ │ ├── GlobalConstants.java │ │ │ ├── SystemConstants.java │ │ │ ├── ProductConstants.java │ │ │ ├── OrderConstants.java │ │ │ ├── JwtClaimConstants.java │ │ │ └── RedisConstants.java │ │ │ ├── base │ │ │ ├── BaseVO.java │ │ │ ├── BasePageQuery.java │ │ │ └── BaseEntity.java │ │ │ ├── enums │ │ │ ├── StatusEnum.java │ │ │ └── GenderEnum.java │ │ │ └── factory │ │ │ └── NamedThreadFactory.java │ └── pom.xml ├── common-security │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── com │ │ └── youlai │ │ └── common │ │ └── security │ │ └── exception │ │ ├── MyAccessDeniedHandler.java │ │ └── MyAuthenticationEntryPoint.java └── pom.xml ├── README.md └── .gitignore /vue-admin/.husky/pre-commit: -------------------------------------------------------------------------------- 1 | 2 | npm run lint:lint-staged 3 | -------------------------------------------------------------------------------- /vue-admin/src/types/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "xlsx/xlsx.mjs"; 2 | -------------------------------------------------------------------------------- /vue-admin/.husky/commit-msg: -------------------------------------------------------------------------------- 1 | 2 | npx --no-install commitlint --edit $1 3 | -------------------------------------------------------------------------------- /docs/nacos/nacos_config.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/docs/nacos/nacos_config.zip -------------------------------------------------------------------------------- /vue-admin/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/vue-admin/public/favicon.ico -------------------------------------------------------------------------------- /vue-admin/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/vue-admin/src/assets/logo.png -------------------------------------------------------------------------------- /vue-admin/.env.production: -------------------------------------------------------------------------------- 1 | ## 生产环境 2 | NODE_ENV='production' 3 | 4 | # 代理前缀 5 | VITE_APP_BASE_API = '/prod-api' 6 | 7 | -------------------------------------------------------------------------------- /vue-admin/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./icons"; 2 | export * from "./i18n"; 3 | export * from "./permission"; 4 | -------------------------------------------------------------------------------- /vue-admin/src/assets/images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/vue-admin/src/assets/images/401.gif -------------------------------------------------------------------------------- /vue-admin/src/assets/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/vue-admin/src/assets/images/404.png -------------------------------------------------------------------------------- /youlai-auth/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: youlai-auth 4 | profiles: 5 | active: dev 6 | -------------------------------------------------------------------------------- /vue-admin/src/api/file/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 文件API类型声明 3 | */ 4 | export interface FileInfo { 5 | name: string; 6 | url: string; 7 | } 8 | -------------------------------------------------------------------------------- /vue-admin/src/assets/images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/vue-admin/src/assets/images/404_cloud.png -------------------------------------------------------------------------------- /vue-admin/src/assets/images/login-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/vue-admin/src/assets/images/login-bg.jpg -------------------------------------------------------------------------------- /youlai-gateway/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: youlai-gateway 4 | profiles: 5 | active: dev 6 | -------------------------------------------------------------------------------- /vue-admin/src/assets/images/login-bg-dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/vue-admin/src/assets/images/login-bg-dark.jpg -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: youlai-system 4 | profiles: 5 | active: dev 6 | -------------------------------------------------------------------------------- /vue-admin/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | public 4 | .husky 5 | .vscode 6 | .idea 7 | *.sh 8 | *.md 9 | 10 | src/assets 11 | stats.html 12 | -------------------------------------------------------------------------------- /vue-admin/.stylelintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | public 4 | .husky 5 | .vscode 6 | .idea 7 | *.sh 8 | *.md 9 | 10 | src/assets 11 | stats.html 12 | -------------------------------------------------------------------------------- /youlai-common/common-apidoc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.youlai.common.apidoc.OpenApiConfig 2 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/size.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /vue-admin/src/plugins/i18n.ts: -------------------------------------------------------------------------------- 1 | // 国际化 2 | import i18n from "@/lang/index"; 3 | import type { App } from "vue"; 4 | 5 | export function setupI18n(app: App) { 6 | app.use(i18n); 7 | } 8 | -------------------------------------------------------------------------------- /youlai-system/system-api/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.youlai.system.api.fallback.UserFeignFallbackClient 3 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/resources/excel-templates/用户导入模板.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youlaitech/youlai-cloud/HEAD/youlai-system/system-boot/src/main/resources/excel-templates/用户导入模板.xlsx -------------------------------------------------------------------------------- /vue-admin/.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | public 4 | .husky 5 | .vscode 6 | .idea 7 | *.sh 8 | *.md 9 | 10 | src/assets 11 | 12 | .eslintrc.cjs 13 | .prettierrc.cjs 14 | .stylelintrc.cjs 15 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/fullscreen-exit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-common/common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.youlai.common.mybatis.config.MybatisPlusConfig 2 | com.youlai.common.mybatis.handler.MyMetaObjectHandler -------------------------------------------------------------------------------- /youlai-common/common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.youlai.common.sms.property.AliyunSmsProperties 2 | com.youlai.common.sms.service.impl.AliyunSmsService 3 | -------------------------------------------------------------------------------- /youlai-common/common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.youlai.common.redis.RedisCacheConfig 2 | com.youlai.common.redis.RedisConfig 3 | com.youlai.common.redis.BusinessSnGenerator -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/java/com/youlai/common/web/constraint/CityType.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.web.constraint; 2 | 3 | /** 4 | * @author Gadfly 5 | * @since 2021-08-06 16:02 6 | */ 7 | public enum CityType { 8 | PROVINCE,CITY,AREA 9 | } 10 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/result/IResultCode.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.result; 2 | 3 | /** 4 | * @author haoxr 5 | **/ 6 | public interface IResultCode { 7 | 8 | String getCode(); 9 | 10 | String getMsg(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /vue-admin/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | .history 7 | 8 | # Editor directories and files 9 | .idea 10 | *.suo 11 | *.ntvs* 12 | *.njsproj 13 | *.sln 14 | *.local 15 | 16 | package-lock.json 17 | pnpm-lock.yaml 18 | stats.html 19 | -------------------------------------------------------------------------------- /vue-admin/src/directive/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from "vue"; 2 | 3 | import { hasPerm } from "./permission"; 4 | 5 | // 全局注册 directive 6 | export function setupDirective(app: App) { 7 | // 使 v-hasPerm 在所有组件中都可用 8 | app.directive("hasPerm", hasPerm); 9 | } 10 | -------------------------------------------------------------------------------- /vue-admin/src/enums/ThemeEnum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 主题枚举 3 | */ 4 | export enum ThemeEnum { 5 | /** 6 | * 明亮主题 7 | */ 8 | LIGHT = "light", 9 | /** 10 | * 暗黑主题 11 | */ 12 | DARK = "dark", 13 | 14 | /** 15 | * 系统自动 16 | */ 17 | AUTO = "auto", 18 | } 19 | -------------------------------------------------------------------------------- /youlai-auth/src/main/java/com/youlai/auth/enums/CaptchaCodeTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.youlai.auth.enums; 2 | 3 | /** 4 | * 验证码字符类型枚举 5 | * 6 | * @author haoxr 7 | * @since 3.1.0 8 | */ 9 | public enum CaptchaCodeTypeEnum { 10 | 11 | MATH, 12 | RANDOM; 13 | } 14 | -------------------------------------------------------------------------------- /vue-admin/src/enums/LayoutEnum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 菜单布局枚举 3 | */ 4 | export enum LayoutEnum { 5 | /** 6 | * 左侧菜单布局 7 | */ 8 | LEFT = "left", 9 | /** 10 | * 顶部菜单布局 11 | */ 12 | TOP = "top", 13 | 14 | /** 15 | * 混合菜单布局 16 | */ 17 | MIX = "mix", 18 | } 19 | -------------------------------------------------------------------------------- /youlai-auth/src/main/java/com/youlai/auth/model/LoginUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.youlai.auth.model; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author haoxr 7 | * @since 2024/1/30 8 | */ 9 | @Data 10 | public class LoginUserInfo { 11 | 12 | private Long id; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/setting.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/close_all.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/constant/MemberConstants.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.constant; 2 | 3 | /** 4 | * @author Gadfly 5 | * @since 2021-08-10 12:12 6 | */ 7 | public interface MemberConstants { 8 | String USER_PRODUCT_HISTORY = "user:product:history:"; 9 | } 10 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/close_other.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/resources/mapper/SysDeptMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/resources/mapper/SysDictMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.youlai.common.web.config.WebMvcConfig 2 | com.youlai.common.web.config.FeignConfig 3 | com.youlai.common.web.exception.GlobalExceptionHandler 4 | com.youlai.common.web.aspect.DuplicateSubmitAspect 5 | -------------------------------------------------------------------------------- /vue-admin/src/plugins/icons.ts: -------------------------------------------------------------------------------- 1 | import type { App } from "vue"; 2 | import * as ElementPlusIconsVue from "@element-plus/icons-vue"; 3 | 4 | // 注册所有图标 5 | export function setupElIcons(app: App) { 6 | for (const [key, component] of Object.entries(ElementPlusIconsVue)) { 7 | app.component(key, component); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vue-admin/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vue.volar", 4 | "vue.vscode-typescript-vue-plugin", 5 | "antfu.unocss", 6 | "lokalise.i18n-ally", 7 | "dbaeumer.vscode-eslint", 8 | "esbenp.prettier-vscode", 9 | "stylelint.vscode-stylelint", 10 | "editorconfig.editorconfig" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/enums/MenuTypeEnum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 菜单类型枚举 3 | */ 4 | export enum MenuTypeEnum { 5 | /** 6 | * 目录 7 | */ 8 | CATALOG = "CATALOG", 9 | /** 10 | * 菜单 11 | */ 12 | MENU = "MENU", 13 | 14 | /** 15 | * 按钮 16 | */ 17 | BUTTON = "BUTTON", 18 | /** 19 | * 外链 20 | */ 21 | EXTLINK = "EXTLINK", 22 | } 23 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/constant/GlobalConstants.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.constant; 2 | 3 | /** 4 | * 全局常量 5 | * 6 | * @author haoxr 7 | * @since 2021/10/30 9:32 8 | */ 9 | public interface GlobalConstants { 10 | 11 | /** 12 | * 全局状态-是 13 | */ 14 | Integer STATUS_YES = 1; 15 | } 16 | -------------------------------------------------------------------------------- /youlai-common/common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.youlai.common.security.service.PermissionService 2 | com.youlai.common.security.config.ResourceServerConfig 3 | com.youlai.common.security.exception.MyAccessDeniedHandler 4 | com.youlai.common.security.exception.MyAuthenticationEntryPoint -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/java/com/youlai/common/web/constraint/CityEntity.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.web.constraint; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author Gadfly 7 | * @since 2021-08-06 16:46 8 | */ 9 | @Data 10 | public class CityEntity { 11 | private String value; 12 | private String name; 13 | private String parent; 14 | } 15 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/rabbitmq.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/utils/nprogress.ts: -------------------------------------------------------------------------------- 1 | import NProgress from "nprogress"; 2 | import "nprogress/nprogress.css"; 3 | 4 | // 进度条 5 | NProgress.configure({ 6 | // 动画方式 7 | easing: "ease", 8 | // 递增进度条的速度 9 | speed: 500, 10 | // 是否显示加载ico 11 | showSpinner: false, 12 | // 自动递增间隔 13 | trickleSpeed: 200, 14 | // 初始化时的最小百分比 15 | minimum: 0.3, 16 | }); 17 | 18 | export default NProgress; 19 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/mapper/SysDictMapper.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.youlai.system.model.entity.SysDict; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface SysDictMapper extends BaseMapper { 9 | 10 | } 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/mapper/SysDictTypeMapper.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.youlai.system.model.entity.SysDictType; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface SysDictTypeMapper extends BaseMapper { 9 | 10 | } 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /vue-admin/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | # 表示所有文件适用 5 | [*] 6 | charset = utf-8 # 设置文件字符集为 utf-8 7 | end_of_line = lf # 控制换行类型(lf | cr | crlf) 8 | indent_style = space # 缩进风格(tab | space) 9 | indent_size = 2 # 缩进大小 10 | insert_final_newline = true # 始终在文件末尾插入一个新行 11 | 12 | # 表示仅 md 文件适用以下规则 13 | [*.md] 14 | max_line_length = off # 关闭最大行长度限制 15 | trim_trailing_whitespace = false # 关闭末尾空格修剪 16 | -------------------------------------------------------------------------------- /vue-admin/src/layout/components/NavBar/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | -------------------------------------------------------------------------------- /vue-admin/src/utils/i18n.ts: -------------------------------------------------------------------------------- 1 | // translate router.meta.title, be used in breadcrumb sidebar tagsview 2 | import i18n from "@/lang/index"; 3 | 4 | export function translateRouteTitle(title: any) { 5 | // 判断是否存在国际化配置,如果没有原生返回 6 | const hasKey = i18n.global.te("route." + title); 7 | if (hasKey) { 8 | const translatedTitle = i18n.global.t("route." + title); 9 | return translatedTitle; 10 | } 11 | return title; 12 | } 13 | -------------------------------------------------------------------------------- /vue-admin/src/lang/package/zh-cn.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | // 路由国际化 3 | route: { 4 | dashboard: "首页", 5 | document: "项目文档", 6 | }, 7 | // 登录页面国际化 8 | login: { 9 | username: "用户名", 10 | password: "密码", 11 | login: "登 录", 12 | captchaCode: "验证码", 13 | }, 14 | // 导航栏国际化 15 | navbar: { 16 | dashboard: "首页", 17 | logout: "注销", 18 | document: "项目文档", 19 | gitee: "码云", 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/vo/FileInfoVO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | @Schema(description = "文件对象") 7 | @Data 8 | public class FileInfoVO { 9 | 10 | @Schema(description = "文件名称") 11 | private String name; 12 | 13 | @Schema(description = "文件URL") 14 | private String url; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /vue-admin/src/settings.ts: -------------------------------------------------------------------------------- 1 | const defaultSettings: AppSettings = { 2 | title: "mall-admin", 3 | version: "v3.2.0", 4 | showSettings: true, 5 | tagsView: true, 6 | fixedHeader: false, 7 | sidebarLogo: true, 8 | layout: "left", 9 | theme: "light", 10 | size: "default", 11 | language: "zh-cn", 12 | themeColor: "#409EFF", 13 | watermarkEnabled: false, 14 | watermarkContent: "mall-admin", 15 | }; 16 | 17 | export default defaultSettings; 18 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/listener/MyAnalysisEventListener.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.listener; 2 | 3 | import com.alibaba.excel.event.AnalysisEventListener; 4 | 5 | /** 6 | * 自定义解析结果监听器 7 | * 8 | * @author: haoxr 9 | * @date: 2023/03/01 10 | */ 11 | public abstract class MyAnalysisEventListener extends AnalysisEventListener { 12 | 13 | private String msg; 14 | public abstract String getMsg(); 15 | } 16 | -------------------------------------------------------------------------------- /vue-admin/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from "vue"; 2 | import { createPinia } from "pinia"; 3 | 4 | const store = createPinia(); 5 | 6 | // 全局注册 store 7 | export function setupStore(app: App) { 8 | app.use(store); 9 | } 10 | 11 | export * from "./modules/app"; 12 | export * from "./modules/permission"; 13 | export * from "./modules/settings"; 14 | export * from "./modules/tagsView"; 15 | export * from "./modules/user"; 16 | export { store }; 17 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/base/BaseVO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.base; 2 | 3 | import lombok.Data; 4 | import lombok.ToString; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author huawei 10 | * @desc VO 基类 11 | * @email huawei_code@163.com 12 | * @since 2021/1/11 13 | */ 14 | @Data 15 | @ToString 16 | public class BaseVO implements Serializable { 17 | 18 | private static final long serialVersionUID = 1L; 19 | } 20 | -------------------------------------------------------------------------------- /vue-admin/src/lang/package/en.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | // 路由国际化 3 | route: { 4 | dashboard: "Dashboard", 5 | document: "Document", 6 | }, 7 | // 登录页面国际化 8 | login: { 9 | username: "Username", 10 | password: "Password", 11 | login: "Login", 12 | captchaCode: "Verify Code", 13 | }, 14 | // 导航栏国际化 15 | navbar: { 16 | dashboard: "Dashboard", 17 | logout: "Logout", 18 | document: "Document", 19 | gitee: "Gitee", 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/query/DictTypePageQuery.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.query; 2 | 3 | 4 | import com.youlai.common.base.BasePageQuery; 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import lombok.Data; 7 | 8 | @Schema(description ="字典类型分页查询对象") 9 | @Data 10 | public class DictTypePageQuery extends BasePageQuery { 11 | 12 | @Schema(description="关键字(类型名称/类型编码)") 13 | private String keywords; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /vue-admin/src/layout/components/NavBar/components/NavbarLeft.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | -------------------------------------------------------------------------------- /youlai-auth/src/main/java/com/youlai/auth/enums/CaptchaTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.youlai.auth.enums; 2 | 3 | /** 4 | * EasyCaptcha 验证码类型枚举 5 | * 6 | * @author haoxr 7 | * @since 2.5.1 8 | */ 9 | public enum CaptchaTypeEnum { 10 | 11 | /** 12 | * 圆圈干扰验证码 13 | */ 14 | CIRCLE, 15 | /** 16 | * GIF验证码 17 | */ 18 | GIF, 19 | /** 20 | * 干扰线验证码 21 | */ 22 | LINE, 23 | /** 24 | * 扭曲干扰验证码 25 | */ 26 | SHEAR 27 | } 28 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/bo/RolePermsBO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.bo; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Set; 6 | 7 | /** 8 | * 角色权限业务对象 9 | * 10 | * @author haoxr 11 | * @since 2023/11/29 12 | */ 13 | @Data 14 | public class RolePermsBO { 15 | 16 | /** 17 | * 角色编码 18 | */ 19 | private String roleCode; 20 | 21 | /** 22 | * 权限标识集合 23 | */ 24 | private Set perms; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /vue-admin/src/styles/variables.module.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable property-no-unknown */ 2 | :export { 3 | sidebar-width: $sidebar-width; 4 | navbar-height: $navbar-height; 5 | menu-background: $menu-background; 6 | menu-text: $menu-text; 7 | menu-active-text: $menu-active-text; 8 | menu-hover: $menu-hover; 9 | sub-menu-background: $sub-menu-background; 10 | sub-menu-active-text: $sub-menu-active-text; 11 | sub-menu-hover: $sub-menu-hover; 12 | } 13 | /* stylelint-enable property-no-unknown */ 14 | -------------------------------------------------------------------------------- /youlai-auth/src/main/java/com/youlai/auth/model/CaptchaResult.java: -------------------------------------------------------------------------------- 1 | package com.youlai.auth.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | /** 7 | * 验证码响应对象 8 | * 9 | * @author haoxr 10 | * @since 3.1.0 11 | */ 12 | 13 | @Builder 14 | @Data 15 | public class CaptchaResult { 16 | 17 | /** 18 | * 验证码唯一标识(用于从Redis获取验证码Code) 19 | */ 20 | private String captchaId; 21 | 22 | /** 23 | * 验证码图片Base64字符串 24 | */ 25 | private String captchaBase64; 26 | } 27 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/query/RolePageQuery.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.query; 2 | 3 | import com.youlai.common.base.BasePageQuery; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | /** 8 | * 角色分页查询实体 9 | * 10 | * @author haoxr 11 | * @since 2022/6/3 12 | * 13 | */ 14 | @Data 15 | public class RolePageQuery extends BasePageQuery { 16 | 17 | @Schema(description="关键字(角色名称/角色编码)") 18 | private String keywords; 19 | } 20 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/language.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/constant/SystemConstants.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.constant; 2 | 3 | /** 4 | * 系统常量 5 | * 6 | * @author haoxr 7 | * @since 2021/10/30 8 | */ 9 | public interface SystemConstants { 10 | 11 | /** 12 | * 根部门ID 13 | */ 14 | Long ROOT_NODE_ID = 0L; 15 | 16 | 17 | /** 18 | * 系统默认密码 19 | */ 20 | String DEFAULT_PASSWORD = "123456"; 21 | 22 | /** 23 | * 超级管理员角色编码 24 | */ 25 | String ROOT_ROLE_CODE = "ROOT"; 26 | } 27 | -------------------------------------------------------------------------------- /youlai-gateway/src/main/java/com/youlai/gateway/GatewayApplication.java: -------------------------------------------------------------------------------- 1 | package com.youlai.gateway; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @SpringBootApplication 8 | @EnableDiscoveryClient 9 | public class GatewayApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(GatewayApplication.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/SystemApplication.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @SpringBootApplication 8 | @EnableDiscoveryClient 9 | public class SystemApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(SystemApplication.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/query/DictPageQuery.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.query; 2 | 3 | 4 | import com.youlai.common.base.BasePageQuery; 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import lombok.Data; 7 | 8 | @Schema(description ="字典数据项分页查询对象") 9 | @Data 10 | public class DictPageQuery extends BasePageQuery { 11 | 12 | @Schema(description="关键字(字典项名称)") 13 | private String keywords; 14 | 15 | @Schema(description="字典类型编码") 16 | private String typeCode; 17 | } 18 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/query/DeptQuery.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.query; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | /** 7 | * 部门查询对象 8 | * 9 | * @author haoxr 10 | * @since 2022/6/11 11 | */ 12 | @Schema(description ="部门分页查询对象") 13 | @Data 14 | public class DeptQuery { 15 | 16 | @Schema(description="关键字(部门名称)") 17 | private String keywords; 18 | 19 | @Schema(description="状态(1->正常;0->禁用)") 20 | private Integer status; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/query/MenuQuery.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.query; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | /** 7 | * 菜单查询对象 8 | * 9 | * @author haoxr 10 | * @since 2022/10/28 11 | */ 12 | @Schema(description ="部门分页查询对象") 13 | @Data 14 | public class MenuQuery { 15 | 16 | @Schema(description="关键字(菜单名称)") 17 | private String keywords; 18 | 19 | @Schema(description="状态(1->显示;0->隐藏)") 20 | private Integer status; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /youlai-auth/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre 2 | MAINTAINER youlai youlaitech@163.com 3 | 4 | RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \&& echo 'Asia/Shanghai' >/etc/timezone 5 | 6 | # /tmp 目录作为容器数据卷目录,SpringBoot内嵌Tomcat容器默认使用/tmp作为工作目录,任何向 /tmp 中写入的信息不会记录进容器存储层,从而保证容器存储层的无状态化 7 | # 在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录 8 | VOLUME /tmp 9 | 10 | # 复制jar到镜像 11 | ADD target/youlai-auth.jar app.jar 12 | 13 | ENTRYPOINT ["java", "-Xmx128m", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"] 14 | 15 | EXPOSE 8000 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /youlai-auth/src/main/resources/bootstrap-prod.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9000 3 | 4 | spring: 5 | mvc: 6 | path-match: 7 | matching-strategy: ant_path_matcher 8 | cloud: 9 | nacos: 10 | # 注册中心 11 | discovery: 12 | server-addr: http://localhost:8848 13 | namespace: youlai-cloud 14 | # 配置中心 15 | config: 16 | server-addr: http://localhost:8848 17 | namespace: youlai-cloud 18 | file-extension: yaml 19 | shared-configs[0]: 20 | data-id: youlai-common.yaml 21 | refresh: true -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/java/com/youlai/common/web/annotation/PreventDuplicateResubmit.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.web.annotation; 2 | 3 | 4 | import java.lang.annotation.*; 5 | 6 | /** 7 | * 防重提交注解 8 | * 9 | * @author haoxr 10 | * @since 2023/5/9 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | @Inherited 16 | public @interface PreventDuplicateResubmit { 17 | 18 | /** 19 | * 防重提交锁过期时间(秒) 20 | *

21 | * 默认5秒内不允许重复提交 22 | */ 23 | int expire() default 5; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /youlai-gateway/src/main/resources/bootstrap-prod.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9999 3 | 4 | spring: 5 | main: 6 | allow-bean-definition-overriding: true 7 | application: 8 | name: youlai-gateway 9 | cloud: 10 | nacos: 11 | discovery: 12 | server-addr: http://localhost:8848 13 | namespace: youlai-cloud 14 | config: 15 | server-addr: http://localhost:8848 16 | file-extension: yaml 17 | namespace: youlai-cloud 18 | shared-configs[0]: 19 | data-id: youlai-common.yaml 20 | refresh: true 21 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/mapper/SysRoleMapper.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.youlai.system.model.entity.SysRole; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | import java.util.Set; 8 | 9 | @Mapper 10 | public interface SysRoleMapper extends BaseMapper { 11 | 12 | 13 | /** 14 | * 获取最大范围的数据权限 15 | * 16 | * @param roles 17 | * @return 18 | */ 19 | Integer getMaxDataRangeDataScope(Set roles); 20 | } 21 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/lang/index.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from "vue-i18n"; 2 | import { useAppStoreHook } from "@/store/modules/app"; 3 | // 本地语言包 4 | import enLocale from "./package/en"; 5 | import zhCnLocale from "./package/zh-cn"; 6 | 7 | const appStore = useAppStoreHook(); 8 | 9 | const messages = { 10 | "zh-cn": { 11 | ...zhCnLocale, 12 | }, 13 | en: { 14 | ...enLocale, 15 | }, 16 | }; 17 | 18 | const i18n = createI18n({ 19 | legacy: false, 20 | locale: appStore.language, 21 | messages: messages, 22 | globalInjection: true, 23 | }); 24 | 25 | export default i18n; 26 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/constant/ProductConstants.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.constant; 2 | 3 | /** 4 | * 商品模块常量 5 | * 6 | * @author haoxr 7 | * @since 2021/02/28 8 | */ 9 | public interface ProductConstants { 10 | 11 | /** 12 | * 订单锁定的商品列表key前缀 13 | */ 14 | String ORDER_LOCKED_SKUS_PREFIX = "order:locked:skus:"; 15 | 16 | /** 17 | * 商品分布式锁key前缀 18 | */ 19 | String SKU_LOCK_PREFIX = "product:sku:lock:"; 20 | 21 | /** 22 | * 临时规格ID前缀 23 | */ 24 | String SPEC_TEMP_ID_PREFIX = "tid_"; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/query/PermPageQuery.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.query; 2 | 3 | import com.youlai.common.base.BasePageQuery; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | /** 8 | * 权限分页查询对象 9 | * 10 | * @author haoxr 11 | * @since 2022/1/14 22:22 12 | */ 13 | @Data 14 | @Schema 15 | public class PermPageQuery extends BasePageQuery { 16 | 17 | @Schema(description="权限名称") 18 | private String name; 19 | 20 | @Schema(description="菜单ID") 21 | private Long menuId; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/vo/DictPageVO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.vo; 2 | 3 | 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | @Schema(description ="字典分页对象") 8 | @Data 9 | public class DictPageVO { 10 | 11 | @Schema(description="字典ID") 12 | private Long id; 13 | 14 | @Schema(description="字典名称") 15 | private String name; 16 | 17 | @Schema(description="字典值") 18 | private String value; 19 | 20 | @Schema(description="状态(1:启用;0:禁用)") 21 | private Integer status; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/base/BasePageQuery.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.base; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 基础分页请求对象 10 | * 11 | * @author huawei 12 | * @since 2021/2/28 13 | */ 14 | @Data 15 | @Schema 16 | public class BasePageQuery implements Serializable { 17 | 18 | @Schema(description = "页码", example = "1") 19 | private int pageNum = 1; 20 | 21 | @Schema(description = "每页记录数", example = "10") 22 | private int pageSize = 10; 23 | } 24 | -------------------------------------------------------------------------------- /youlai-auth/src/main/java/com/youlai/auth/oauth2/extension/captcha/CaptchaParameterNames.java: -------------------------------------------------------------------------------- 1 | package com.youlai.auth.oauth2.extension.captcha; 2 | 3 | /** 4 | * 验证码模式请求参数名 5 | * 6 | * @author Ray Hao 7 | * @since 3.0.0 8 | */ 9 | 10 | public class CaptchaParameterNames { 11 | 12 | /** 13 | * 验证码ID 14 | */ 15 | public static final String CAPTCHA_ID = "captchaId"; 16 | 17 | 18 | /** 19 | * 验证码 Code 20 | */ 21 | public static final String CAPTCHA_CODE = "captchaCode"; 22 | 23 | 24 | 25 | 26 | private CaptchaParameterNames() { 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/constant/OrderConstants.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.constant; 2 | 3 | /** 4 | * 订单常量 5 | * 6 | * @author haoxr 7 | * @since 2021/03/16 8 | */ 9 | public interface OrderConstants { 10 | 11 | /** 12 | * 会员购物车缓存KEY前缀 13 | */ 14 | String MEMBER_CART_PREFIX = "MEMBER:CART:"; 15 | 16 | /** 17 | * 订单防重提交锁KEY前缀 18 | */ 19 | String ORDER_RESUBMIT_LOCK_PREFIX = "ORDER:RESUBMIT_LOCK:"; 20 | 21 | 22 | /** 23 | * 订单锁前缀 24 | * 25 | */ 26 | String ORDER_LOCK_PREFIX = "ORDER:LOCK:"; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /youlai-gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre 2 | MAINTAINER youlai youlaitech@163.com 3 | 4 | 5 | RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \&& echo 'Asia/Shanghai' >/etc/timezone 6 | 7 | # /tmp 目录作为容器数据卷目录,SpringBoot内嵌Tomcat容器默认使用/tmp作为工作目录,任何向 /tmp 中写入的信息不会记录进容器存储层,从而保证容器存储层的无状态化 8 | # 在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录 9 | VOLUME /tmp 10 | 11 | # 复制jar至镜像 12 | ADD target/youlai-gateway.jar app.jar 13 | 14 | ENTRYPOINT ["java", "-Xmx128m", "-Djava.security.egd=file:/dev/./urandom","-Dcsp.sentinel.app.type=1", "-jar", "/app.jar"] 15 | 16 | EXPOSE 9999 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/vo/DictTypePageVO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.vo; 2 | 3 | 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | @Schema(description ="字典类型分页对象") 8 | @Data 9 | public class DictTypePageVO { 10 | 11 | @Schema(description="字典类型ID") 12 | private Long id; 13 | 14 | @Schema(description="类型名称") 15 | private String name; 16 | 17 | @Schema(description="类型编码") 18 | private String code; 19 | 20 | @Schema(description="状态:1:启用;0:禁用") 21 | private Integer status; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/shrink.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-common/common-sms/src/main/java/com/youlai/common/sms/service/SmsService.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.sms.service; 2 | 3 | 4 | /** 5 | * 短信服务接口层 6 | * 7 | * @author haoxr 8 | * @since 3.0.0 9 | */ 10 | public interface SmsService { 11 | 12 | /** 13 | * 发送短信验证码 14 | * 15 | * @param mobile 手机号 13388886666 16 | * @param templateCode 短信模板 SMS_194640010 17 | * @param templateParam 模板参数 "[{"code":"123456"}]" 18 | * 19 | * @return boolean 是否发送成功 20 | */ 21 | boolean sendSms(String mobile, String templateCode, String templateParam); 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/mapper/SysMenuMapper.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.mapper; 2 | 3 | /** 4 | * 菜单持久接口层 5 | * 6 | * @author haoxr 7 | * @since 2022/1/24 8 | */ 9 | 10 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 11 | import com.youlai.system.model.bo.RouteBO; 12 | import com.youlai.system.model.entity.SysMenu; 13 | import org.apache.ibatis.annotations.Mapper; 14 | 15 | import java.util.List; 16 | import java.util.Set; 17 | 18 | @Mapper 19 | public interface SysMenuMapper extends BaseMapper { 20 | 21 | List listRoutes(); 22 | } 23 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/mapper/SysUserRoleMapper.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.youlai.system.model.entity.SysUserRole; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * 用户角色访问层 9 | * 10 | * @author haoxr 11 | * @since 2022/1/15 12 | */ 13 | @Mapper 14 | public interface SysUserRoleMapper extends BaseMapper { 15 | 16 | /** 17 | * 获取角色绑定的用户数 18 | * 19 | * @param roleId 角色ID 20 | */ 21 | int countUsersForRole(Long roleId); 22 | } 23 | -------------------------------------------------------------------------------- /vue-admin/src/types/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module "*.vue" { 4 | import { DefineComponent } from "vue"; 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any>; 7 | export default component; 8 | } 9 | 10 | interface ImportMetaEnv { 11 | /** 应用端口 */ 12 | VITE_APP_PORT: string; 13 | /** API 基础路径 */ 14 | VITE_APP_BASE_API: string; 15 | VITE_APP_API_URL: string; 16 | VITE_MOCK_DEV_SERVER:boolean; 17 | } 18 | 19 | interface ImportMeta { 20 | readonly env: ImportMetaEnv; 21 | } 22 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/converter/DeptConverter.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.converter; 2 | 3 | import com.youlai.system.model.entity.SysDept; 4 | import com.youlai.system.model.form.DeptForm; 5 | import com.youlai.system.model.vo.DeptVO; 6 | import org.mapstruct.Mapper; 7 | 8 | /** 9 | * 部门对象转换器 10 | * 11 | * @author haoxr 12 | * @since 2022/7/29 13 | */ 14 | @Mapper(componentModel = "spring") 15 | public interface DeptConverter { 16 | 17 | DeptForm entity2Form(SysDept entity); 18 | DeptVO entity2Vo(SysDept entity); 19 | 20 | SysDept form2Entity(DeptForm deptForm); 21 | 22 | } -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/resources/bootstrap-prod.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8800 3 | 4 | spring: 5 | main: 6 | allow-circular-references: true 7 | mvc: 8 | path-match: 9 | matching-strategy: ant_path_matcher 10 | cloud: 11 | nacos: 12 | # 注册中心 13 | discovery: 14 | server-addr: http://localhost:8848 15 | namespace: youlai-cloud 16 | # 配置中心 17 | config: 18 | server-addr: http://localhost:8848 19 | namespace: youlai-cloud 20 | file-extension: yaml 21 | shared-configs[0]: 22 | data-id: youlai-common.yaml 23 | refresh: true -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/enums/StatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.enums; 2 | 3 | import com.youlai.common.base.IBaseEnum; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 状态枚举 8 | * 9 | * @author haoxr 10 | * @since 2022/10/14 11 | */ 12 | public enum StatusEnum implements IBaseEnum { 13 | 14 | ENABLE(1, "启用"), 15 | DISABLE (0, "禁用"); 16 | 17 | @Getter 18 | private Integer value; 19 | 20 | @Getter 21 | private String label; 22 | 23 | StatusEnum(Integer value, String label) { 24 | this.value = value; 25 | this.label = label; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/converter/MenuConverter.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.converter; 2 | 3 | import com.youlai.system.model.entity.SysMenu; 4 | import com.youlai.system.model.form.MenuForm; 5 | import com.youlai.system.model.vo.MenuVO; 6 | import org.mapstruct.Mapper; 7 | 8 | /** 9 | * 菜单对象转换器 10 | * 11 | * @author haoxr 12 | * @since 2022/7/29 13 | */ 14 | @Mapper(componentModel = "spring") 15 | public interface MenuConverter { 16 | 17 | MenuVO entity2Vo(SysMenu entity); 18 | 19 | 20 | MenuForm entity2Form(SysMenu entity); 21 | 22 | SysMenu form2Entity(MenuForm menuForm); 23 | 24 | } -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/resources/bootstrap-dev.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8800 3 | 4 | spring: 5 | main: 6 | allow-circular-references: true 7 | mvc: 8 | path-match: 9 | matching-strategy: ant_path_matcher 10 | cloud: 11 | nacos: 12 | # 注册中心 13 | discovery: 14 | server-addr: http://localhost:8848 15 | namespace: youlai-cloud 16 | # 配置中心 17 | config: 18 | server-addr: http://localhost:8848 19 | namespace: youlai-cloud 20 | file-extension: yaml 21 | shared-configs[0]: 22 | data-id: youlai-common.yaml 23 | refresh: true 24 | 25 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/ip.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /vue-admin/.env.development: -------------------------------------------------------------------------------- 1 | ## 开发环境 2 | NODE_ENV='development' 3 | 4 | # 应用端口 5 | VITE_APP_PORT = 9527 6 | 7 | # 代理前缀 8 | VITE_APP_BASE_API = '/dev-api' 9 | 10 | 11 | # 开发接口地址 12 | VITE_APP_API_URL = http://localhost:9999 13 | 14 | # 线上接口地址(暂不可用) 15 | # VITE_APP_API_URL = https://api.youlai.tech 16 | 17 | # 是否启用 Mock 服务 18 | VITE_MOCK_DEV_SERVER = false 19 | 20 | # OAuth2 客户端ID 21 | VITE_OAUTH_CLIENT_ID = messaging-client 22 | 23 | # OAuth2 客户端密钥 24 | VITE_OAUTH_CLIENT_SECRET = 123456 25 | 26 | # OAuth2 重定向地址 27 | VITE_OAUTH_REDIRECT_URI = http://127.0.0.1:9527/OAuth2Redirect 28 | 29 | # OAuth2 认证服务器地址 30 | VITE_OAUTH_ISSUER=http://127.0.0.1:9000 31 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/close_left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @use "./reset"; 2 | 3 | .app-container { 4 | padding: 10px; 5 | } 6 | 7 | .search-container { 8 | padding: 18px 0 0 10px; 9 | margin-bottom: 10px; 10 | background-color: var(--el-bg-color-overlay); 11 | border: 1px solid var(--el-border-color-light); 12 | border-radius: 4px; 13 | box-shadow: var(--el-box-shadow-light); 14 | } 15 | 16 | .table-container > .el-card__header { 17 | padding: calc(var(--el-card-padding) - 8px) var(--el-card-padding); 18 | } 19 | 20 | .link-type, 21 | .link-type:focus { 22 | color: #337ab7; 23 | cursor: pointer; 24 | 25 | &:hover { 26 | color: rgb(32 160 255); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/constant/JwtClaimConstants.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.constant; 2 | 3 | /** 4 | * JWT常量 5 | */ 6 | public interface JwtClaimConstants { 7 | 8 | /** 9 | * 用户ID 10 | */ 11 | String USER_ID = "userId"; 12 | 13 | /** 14 | * 用户名 15 | */ 16 | String USERNAME = "username"; 17 | 18 | /** 19 | * 部门ID 20 | */ 21 | String DEPT_ID = "deptId"; 22 | 23 | /** 24 | * 数据权限 25 | */ 26 | String DATA_SCOPE = "dataScope"; 27 | 28 | /** 29 | * JWT claim中存储授权信息(角色)的字段名称 30 | */ 31 | String AUTHORITIES = "authorities"; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/entity/SysRoleMenu.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | 9 | /** 10 | * 角色和菜单关联表 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class SysRoleMenu { 16 | /** 17 | * 角色ID 18 | */ 19 | private Long roleId; 20 | 21 | /** 22 | * 菜单ID 23 | */ 24 | private Long menuId; 25 | 26 | @TableField(exist = false) 27 | private static final long serialVersionUID = 1L; 28 | } -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/query/UserPageQuery.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.query; 2 | 3 | import com.youlai.common.base.BasePageQuery; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | /** 8 | * 用户分页查询对象 9 | * 10 | * @author haoxr 11 | * @since 2022/1/14 12 | */ 13 | @Schema 14 | @Data 15 | public class UserPageQuery extends BasePageQuery { 16 | 17 | @Schema(description="关键字(用户名/昵称/手机号)") 18 | private String keywords; 19 | 20 | @Schema(description="用户状态") 21 | private Integer status; 22 | 23 | @Schema(description="部门ID") 24 | private Long deptId; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/form/DictTypeForm.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.form; 2 | 3 | 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | @Schema(description = "字典类型") 8 | @Data 9 | public class DictTypeForm { 10 | 11 | @Schema(description="字典类型ID") 12 | private Long id; 13 | 14 | @Schema(description="类型名称") 15 | private String name; 16 | 17 | @Schema(description="类型编码") 18 | private String code; 19 | 20 | @Schema(description="类型状态(1:启用;0:禁用)") 21 | private Integer status; 22 | 23 | @Schema(description = "备注") 24 | private String remark; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /youlai-system/system-boot/Dockerfile: -------------------------------------------------------------------------------- 1 | # 基础镜像 2 | FROM openjdk:8-jre 3 | 4 | # 维护者信息 5 | MAINTAINER youlai 6 | 7 | # 设置容器时区为当前时区 8 | RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \&& echo 'Asia/Shanghai' >/etc/timezone 9 | 10 | # /tmp 目录作为容器数据卷目录,SpringBoot内嵌Tomcat容器默认使用/tmp作为工作目录,任何向 /tmp 中写入的信息不会记录进容器存储层 11 | # 在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录 12 | VOLUME /tmp 13 | 14 | # 复制主机文件至镜像内,复制的目录需放置在 Dockerfile 文件同级目录下 15 | ADD target/system-boot.jar app.jar 16 | 17 | # 容器启动执行命令 18 | ENTRYPOINT ["java", "-Xmx128m", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"] 19 | 20 | # 声明容器提供服务端口 21 | EXPOSE 8800 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/service/OssService.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.service; 2 | 3 | import com.youlai.system.model.vo.FileInfoVO; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | /** 7 | * 对象存储服务接口层 8 | * 9 | * @author haoxr 10 | * @since 3.0.0 11 | */ 12 | public interface OssService { 13 | 14 | /** 15 | * 上传文件 16 | * @param file 表单文件对象 17 | * @return 文件信息 18 | */ 19 | FileInfoVO uploadFile(MultipartFile file); 20 | 21 | /** 22 | * 删除文件 23 | * 24 | * @param filePath 文件完整URL 25 | * @return 删除结果 26 | */ 27 | boolean deleteFile(String filePath); 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/config/PasswordEncoderConfig.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | import org.springframework.security.crypto.password.PasswordEncoder; 7 | 8 | /** 9 | * 密码编码器(修改、重置密码使用) 10 | * 11 | * @author haoxr 12 | * @since 0.0.1 13 | */ 14 | @Configuration 15 | public class PasswordEncoderConfig { 16 | 17 | @Bean 18 | public PasswordEncoder passwordEncoder() { 19 | return new BCryptPasswordEncoder(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/close_right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /youlai-auth/src/main/java/com/youlai/auth/AuthApplication.java: -------------------------------------------------------------------------------- 1 | package com.youlai.auth; 2 | 3 | import com.youlai.system.api.UserFeignClient; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 | import org.springframework.cloud.openfeign.EnableFeignClients; 8 | 9 | @EnableFeignClients(basePackageClasses = {UserFeignClient.class}) 10 | @SpringBootApplication 11 | @EnableDiscoveryClient 12 | public class AuthApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(AuthApplication.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /youlai-system/system-api/src/main/java/com/youlai/system/api/fallback/UserFeignFallbackClient.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.api.fallback; 2 | 3 | import com.youlai.system.api.UserFeignClient; 4 | import com.youlai.system.dto.UserAuthInfo; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * 系统用户服务远程调用异常后的降级处理类 10 | * 11 | * @author haoxr 12 | * @since 2021/4/24 13 | */ 14 | @Component 15 | @Slf4j 16 | public class UserFeignFallbackClient implements UserFeignClient { 17 | 18 | @Override 19 | public UserAuthInfo getUserAuthInfo(String username) { 20 | log.error("feign远程调用系统用户服务异常后的降级方法"); 21 | return new UserAuthInfo(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/entity/SysUserRole.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | 9 | /** 10 | * 用户和角色关联表 11 | * 12 | * @author haoxr 13 | * @since 2022/12/17 14 | */ 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class SysUserRole { 19 | /** 20 | * 用户ID 21 | */ 22 | private Long userId; 23 | 24 | /** 25 | * 角色ID 26 | */ 27 | private Long roleId; 28 | 29 | @TableField(exist = false) 30 | private static final long serialVersionUID = 1L; 31 | } -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/base/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.base; 2 | import com.baomidou.mybatisplus.annotation.FieldFill; 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import lombok.Data; 5 | 6 | import java.io.Serial; 7 | import java.io.Serializable; 8 | import java.time.LocalDateTime; 9 | 10 | /** 11 | * 基础实体类 12 | */ 13 | @Data 14 | public class BaseEntity implements Serializable { 15 | 16 | @Serial 17 | private static final long serialVersionUID = 1L; 18 | 19 | @TableField(fill = FieldFill.INSERT) 20 | private LocalDateTime createTime; 21 | 22 | @TableField(fill = FieldFill.INSERT_UPDATE) 23 | private LocalDateTime updateTime; 24 | } 25 | -------------------------------------------------------------------------------- /youlai-system/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | youlai-cloud 7 | com.youlai 8 | 0.0.1 9 | 10 | 4.0.0 11 | 12 | youlai-system 13 | pom 14 | 15 | 16 | system-api 17 | system-boot 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # youlai-cloud 2 | 3 | #### 介绍 4 | 面向 SpringBoot3,基于 Java17、Spring Cloud&Alibaba 2021、Spring Boot 2.7 、Spring Authorization Server 0.3.1 全新升级OAuth2授权+微服务+UPMS管理系统解决方案。 5 | 6 | 7 | #### Spring Authorization Server 授权码模式测试流程 8 | 1. 创建名为 oauth2 的数据库,执行docs/sql/oauth2.sql的脚本创建表 9 | 2. 启动nacos,创建 cloud-namespace-id 的namespace,导入docs/nacos/DEFAULT_GROUP.zip配置 10 | 3. 依次启动 youlai-auth、youlai-system、youlai-gateway 11 | 4. 浏览器输入网关: http://127.0.0.1:9999/youlai-system/messages 12 | 5. 因为第一次未授权会跳转到认证中心进行认证,输入用户名/密码(admin/123456),认证成功网关会路由到资源服务器获取数据。 13 | 14 | ![](https://oss.youlai.tech/blog/sas-authorization-code.gif) 15 | 16 | 17 | #### 参与贡献 18 | 19 | 1. Fork 本仓库 20 | 2. 新建 Feat_xxx 分支 21 | 3. 提交代码 22 | 4. 新建 Pull Request -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/enums/GenderEnum.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.enums; 2 | 3 | import com.youlai.common.base.IBaseEnum; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 性别枚举 8 | * 9 | * @author haoxr 10 | * @since 2022/4/10 11 | */ 12 | public enum GenderEnum implements IBaseEnum { 13 | 14 | MALE(1, "男"), 15 | FEMALE(2, "女"), 16 | UNKNOWN(0, "未知"); 17 | 18 | @Getter 19 | // @EnumValue // Mybatis-Plus 提供注解表示插入数据库时插入该值 20 | private Integer value; 21 | 22 | @Getter 23 | // @JsonValue // 表示对枚举序列化时返回此字段 24 | private String label; 25 | 26 | GenderEnum(Integer value, String label) { 27 | this.value = value; 28 | this.label = label; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/form/DeptForm.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.form; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotNull; 5 | import lombok.Data; 6 | 7 | @Schema(description = "部门表单对象") 8 | @Data 9 | public class DeptForm { 10 | 11 | @Schema(description="部门ID") 12 | private Long id; 13 | 14 | @Schema(description="部门名称") 15 | private String name; 16 | 17 | @Schema(description="父部门ID") 18 | @NotNull(message = "父部门ID不能为空") 19 | private Long parentId; 20 | 21 | @Schema(description="状态(1:启用;0:禁用)") 22 | private Integer status; 23 | 24 | @Schema(description="排序(数字越小排名越靠前)") 25 | private Integer sort; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/converter/DictTypeConverter.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.converter; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import com.youlai.system.model.entity.SysDictType; 5 | import com.youlai.system.model.form.DictTypeForm; 6 | import com.youlai.system.model.vo.DictTypePageVO; 7 | import org.mapstruct.Mapper; 8 | 9 | /** 10 | * 字典类型对象转换器 11 | * 12 | * @author haoxr 13 | * @since 2022/6/8 14 | */ 15 | @Mapper(componentModel = "spring") 16 | public interface DictTypeConverter { 17 | 18 | Page entity2Page(Page page); 19 | 20 | DictTypeForm entity2Form(SysDictType entity); 21 | 22 | SysDictType form2Entity(DictTypeForm entity); 23 | } 24 | -------------------------------------------------------------------------------- /youlai-common/common-mybatis/src/main/java/com/youlai/common/mybatis/enums/DataScopeEnum.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.mybatis.enums; 2 | 3 | import com.youlai.common.base.IBaseEnum; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 数据权限枚举 8 | * 9 | * @author haoxr 10 | * @since 2022/10/14 11 | */ 12 | public enum DataScopeEnum implements IBaseEnum { 13 | 14 | /** 15 | * value 越小,数据权限范围越大 16 | */ 17 | ALL(0, "所有数据"), 18 | DEPT_AND_SUB(1, "部门及子部门数据"), 19 | DEPT(2, "本部门数据"), 20 | SELF(3, "本人数据"); 21 | 22 | @Getter 23 | private Integer value; 24 | 25 | @Getter 26 | private String label; 27 | 28 | DataScopeEnum(Integer value, String label) { 29 | this.value = value; 30 | this.label = label; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/vo/UserImportVO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.vo; 2 | 3 | import com.alibaba.excel.annotation.ExcelProperty; 4 | import lombok.Data; 5 | 6 | /** 7 | * 用户导入对象 8 | * 9 | * @author haoxr 10 | * @since 2022/4/10 11 | */ 12 | @Data 13 | public class UserImportVO { 14 | 15 | @ExcelProperty(value = "用户名") 16 | private String username; 17 | 18 | @ExcelProperty(value = "昵称") 19 | private String nickname; 20 | 21 | @ExcelProperty(value = "性别") 22 | private String gender; 23 | 24 | @ExcelProperty(value = "手机号码") 25 | private String mobile; 26 | 27 | @ExcelProperty(value = "邮箱") 28 | private String email; 29 | 30 | @ExcelProperty("角色") 31 | private String roleCodes; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /vue-admin/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import router from "@/router"; 4 | import { setupStore } from "@/store"; 5 | import { setupDirective } from "@/directive"; 6 | import { setupElIcons, setupI18n, setupPermission } from "@/plugins"; 7 | 8 | // 本地SVG图标 9 | import "virtual:svg-icons-register"; 10 | 11 | // 样式 12 | import "element-plus/theme-chalk/dark/css-vars.css"; 13 | import "@/styles/index.scss"; 14 | import "uno.css"; 15 | import "animate.css"; 16 | 17 | const app = createApp(App); 18 | // 全局注册 自定义指令(directive) 19 | setupDirective(app); 20 | // 全局注册 状态管理(store) 21 | setupStore(app); 22 | // 全局注册Element-plus图标 23 | setupElIcons(app); 24 | // 国际化 25 | setupI18n(app); 26 | // 注册动态路由 27 | setupPermission(); 28 | app.use(router).mount("#app"); 29 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/mapper/SysDeptMapper.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.Wrapper; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import com.baomidou.mybatisplus.core.toolkit.Constants; 6 | import com.youlai.common.mybatis.annotation.DataPermission; 7 | import com.youlai.system.model.entity.SysDept; 8 | import org.apache.ibatis.annotations.Mapper; 9 | import org.apache.ibatis.annotations.Param; 10 | 11 | import java.util.List; 12 | 13 | 14 | @Mapper 15 | public interface SysDeptMapper extends BaseMapper { 16 | 17 | @DataPermission(deptIdColumnName = "id") 18 | @Override 19 | List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); 20 | } 21 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/vo/UserInfoVO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.util.Set; 7 | 8 | /** 9 | * 用户登录视图对象 10 | * 11 | * @author haoxr 12 | * @since 2022/1/14 13 | */ 14 | @Schema(description ="当前登录用户视图对象") 15 | @Data 16 | public class UserInfoVO { 17 | 18 | @Schema(description="用户ID") 19 | private Long userId; 20 | 21 | @Schema(description="用户昵称") 22 | private String nickname; 23 | 24 | @Schema(description="头像地址") 25 | private String avatar; 26 | 27 | @Schema(description="用户角色编码集合") 28 | private Set roles; 29 | 30 | @Schema(description="用户权限标识集合") 31 | private Set perms; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /youlai-common/common-mybatis/src/main/java/com/youlai/common/mybatis/handler/StringArrayJsonTypeHandler.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.mybatis.handler; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.ibatis.type.JdbcType; 5 | import org.apache.ibatis.type.MappedJdbcTypes; 6 | import org.apache.ibatis.type.MappedTypes; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * String 数组类型转换 json 11 | * 12 | * @author Gadfly 13 | * @since 1.0.0 14 | */ 15 | @Slf4j 16 | @Component 17 | @MappedTypes(value = {String[].class}) 18 | @MappedJdbcTypes(value = {JdbcType.OTHER}, includeNullJdbcType = true) 19 | public class StringArrayJsonTypeHandler extends ArrayObjectJsonTypeHandler { 20 | public StringArrayJsonTypeHandler() { 21 | super(String[].class); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/form/DictForm.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.form; 2 | 3 | 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | @Schema(description = "字典表单对象") 8 | @Data 9 | public class DictForm { 10 | 11 | @Schema(description="字典ID") 12 | private Long id; 13 | 14 | @Schema(description="类型编码") 15 | private String typeCode; 16 | 17 | @Schema(description="字典名称") 18 | private String name; 19 | 20 | @Schema(description="字典值") 21 | private String value; 22 | 23 | @Schema(description="状态(1:启用;0:禁用)") 24 | private Integer status; 25 | 26 | @Schema(description="排序") 27 | private Integer sort; 28 | 29 | @Schema(description = "字典备注") 30 | private String remark; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /youlai-system/system-api/src/main/java/com/youlai/system/api/UserFeignClient.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.api; 2 | 3 | import com.youlai.common.web.config.FeignDecoderConfig; 4 | import com.youlai.system.api.fallback.UserFeignFallbackClient; 5 | import com.youlai.system.dto.UserAuthInfo; 6 | import com.youlai.common.result.Result; 7 | import org.springframework.cloud.openfeign.FeignClient; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | 11 | @FeignClient(value = "youlai-system", fallback = UserFeignFallbackClient.class, configuration = {FeignDecoderConfig.class}) 12 | public interface UserFeignClient { 13 | 14 | @GetMapping("/api/v1/users/{username}/authInfo") 15 | UserAuthInfo getUserAuthInfo(@PathVariable String username); 16 | } 17 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/converter/DictConverter.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.converter; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import com.youlai.system.model.entity.SysDict; 5 | import com.youlai.system.model.form.DictForm; 6 | import com.youlai.system.model.vo.DictPageVO; 7 | import org.mapstruct.InheritInverseConfiguration; 8 | import org.mapstruct.Mapper; 9 | 10 | /** 11 | * 字典数据项对象转换器 12 | * 13 | * @author haoxr 14 | * @since 2022/6/8 15 | */ 16 | @Mapper(componentModel = "spring") 17 | public interface DictConverter { 18 | 19 | Page entity2Page(Page page); 20 | 21 | DictForm entity2Form(SysDict entity); 22 | 23 | @InheritInverseConfiguration(name="entity2Form") 24 | SysDict form2Entity(DictForm entity); 25 | } 26 | -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/resources/ValidationMessages.properties: -------------------------------------------------------------------------------- 1 | # \u901A\u7528\u5F02\u5E38\u4FE1\u606F 2 | id.positive=id\u5FC5\u987B\u4E3A\u6B63\u6574\u6570 3 | page.count.min=\u5206\u9875\u6570\u91CF\u5FC5\u987B\u4E3A\u6B63\u6574\u6570 4 | page.count.max=\u5206\u9875\u6570\u91CF\u5FC5\u987B\u5C0F\u4E8E{value} 5 | page.number.min=\u5206\u9875\u9875\u7801\u5FC5\u987B\u4E3A\u6B63\u6574\u6570 6 | date.past=\u65E5\u671F\u5FC5\u987B\u662F\u8FC7\u53BB\u7684\u65F6\u95F4 7 | date.interval=\u7ED3\u675F\u65E5\u671F\u5FC5\u987B\u65E9\u4E8E\u5F00\u59CB\u65E5\u671F 8 | text.length.min=\u6700\u5C0F\u957F\u5EA6\u5FC5\u987B\u5927\u4E8E{min} 9 | text.length.max=\u6700\u5927\u957F\u5EA6\u5FC5\u987B\u5C0F\u4E8E{max} 10 | phone.valid=\u5FC5\u987B\u662F\u5927\u964611\u4F4D\u624B\u673A\u53F7 11 | zipcode.valid=\u90AE\u7F16\u5FC5\u987B\u4E3A6\u4F4D\u6570\u5B57 -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/mapper/SysRoleMenuMapper.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.youlai.system.model.bo.RolePermsBO; 5 | import com.youlai.system.model.entity.SysRoleMenu; 6 | import org.apache.ibatis.annotations.Mapper; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 角色菜单持久层 12 | * 13 | * @author haoxr 14 | * @since 2022/6/4 15 | */ 16 | @Mapper 17 | public interface SysRoleMenuMapper extends BaseMapper { 18 | 19 | /** 20 | * 获取角色拥有的菜单ID集合 21 | * 22 | * @param roleId 23 | * @return 24 | */ 25 | List listMenuIdsByRoleId(Long roleId); 26 | 27 | /** 28 | * 获取权限和拥有权限的角色列表 29 | */ 30 | List getRolePermsList(String roleCode); 31 | } 32 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/constant/RedisConstants.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.constant; 2 | 3 | public interface RedisConstants { 4 | 5 | /** 6 | * 黑名单TOKEN Key前缀 7 | */ 8 | String TOKEN_BLACKLIST_PREFIX = "token:blacklist:"; 9 | 10 | /** 11 | * 图形验证码key前缀 12 | */ 13 | String CAPTCHA_CODE_PREFIX = "captcha_code:"; 14 | 15 | /** 16 | * 登录短信验证码key前缀 17 | */ 18 | String LOGIN_SMS_CODE_PREFIX = "sms_code:login"; 19 | 20 | /** 21 | * 注册短信验证码key前缀 22 | */ 23 | String REGISTER_SMS_CODE_PREFIX = "sms_code:register"; 24 | 25 | 26 | /** 27 | * 角色和权限缓存前缀 28 | */ 29 | String ROLE_PERMS_PREFIX = "role_perms:"; 30 | 31 | 32 | /** 33 | * JWT 密钥对(包含公钥和私钥) 34 | */ 35 | String JWK_SET_KEY = "jwk_set"; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /vue-admin/src/api/file/index.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | import { AxiosPromise } from "axios"; 3 | import { FileInfo } from "./types"; 4 | 5 | /** 6 | * 上传文件 7 | * 8 | * @param file 9 | */ 10 | export function uploadFileApi(file: File): AxiosPromise { 11 | const formData = new FormData(); 12 | formData.append("file", file); 13 | return request({ 14 | url: "/youlai-system/api/v1/files", 15 | method: "post", 16 | data: formData, 17 | headers: { 18 | "Content-Type": "multipart/form-data", 19 | }, 20 | }); 21 | } 22 | 23 | /** 24 | * 删除文件 25 | * 26 | * @param filePath 文件完整路径 27 | */ 28 | export function deleteFileApi(filePath?: string) { 29 | return request({ 30 | url: "/youlai-system/api/v1/files", 31 | method: "delete", 32 | params: { filePath: filePath }, 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/form/RoleForm.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.form; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import lombok.Data; 6 | 7 | @Schema(description = "角色表单对象") 8 | @Data 9 | public class RoleForm { 10 | 11 | @Schema(description="角色ID") 12 | private Long id; 13 | 14 | @Schema(description="角色名称") 15 | @NotBlank(message = "角色名称不能为空") 16 | private String name; 17 | 18 | @Schema(description="角色编码") 19 | @NotBlank(message = "角色编码不能为空") 20 | private String code; 21 | 22 | @Schema(description="排序") 23 | private Integer sort; 24 | 25 | @Schema(description="角色状态(1-正常;0-停用)") 26 | private Integer status; 27 | 28 | @Schema(description="数据权限") 29 | private Integer dataScope; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/service/SysUserRoleService.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.service; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.youlai.system.model.entity.SysUserRole; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 用户角色关联业务接口 11 | * 12 | * @author haoxr 13 | * @since 0.0.1 14 | */ 15 | public interface SysUserRoleService extends IService { 16 | 17 | /** 18 | * 保存用户角色 19 | * 20 | * @param userId 用户ID 21 | * @param roleIds 角色ID集合 22 | * @return boolean 是否保存成功 23 | */ 24 | boolean saveUserRoles(Long userId, List roleIds); 25 | 26 | 27 | /** 28 | * 判断角色是否存在绑定的用户 29 | * 30 | * @param roleId 角色ID 31 | * @return true:已分配 false:未分配 32 | */ 33 | boolean hasAssignedUsers(Long roleId); 34 | } 35 | -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/java/com/youlai/common/web/exception/BizException.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.web.exception; 2 | 3 | import com.youlai.common.result.IResultCode; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 自定义业务异常 8 | * 9 | * @author haoxr 10 | * @since 2022/7/31 11 | */ 12 | @Getter 13 | public class BizException extends RuntimeException { 14 | 15 | public IResultCode resultCode; 16 | 17 | public BizException(IResultCode errorCode) { 18 | super(errorCode.getMsg()); 19 | this.resultCode = errorCode; 20 | } 21 | 22 | public BizException(String message){ 23 | super(message); 24 | } 25 | 26 | public BizException(String message, Throwable cause){ 27 | super(message, cause); 28 | } 29 | 30 | public BizException(Throwable cause){ 31 | super(cause); 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /vue-admin/src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 28 | 29 | 40 | -------------------------------------------------------------------------------- /youlai-common/common-mybatis/src/main/java/com/youlai/common/mybatis/annotation/DataPermission.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.mybatis.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * MP数据权限注解 7 | *

8 | * https://gitee.com/baomidou/mybatis-plus/issues/I37I90 9 | * 10 | * @author zc 11 | * @since 2021-12-10 15:48 12 | */ 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.TYPE, ElementType.METHOD}) 16 | public @interface DataPermission { 17 | 18 | /** 19 | * 数据权限 {@link com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor} 20 | */ 21 | String deptAlias() default ""; 22 | 23 | String deptIdColumnName() default "dept_id"; 24 | String userAlias() default ""; 25 | 26 | String userIdColumnName() default "create_by"; 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/entity/SysDictType.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.youlai.common.base.BaseEntity; 6 | import lombok.Data; 7 | 8 | /** 9 | * 字典类型实体 10 | * 11 | * @author haoxr 12 | * @since 2022/12/17 13 | */ 14 | @Data 15 | public class SysDictType extends BaseEntity { 16 | /** 17 | * 主键 18 | */ 19 | @TableId(type = IdType.AUTO) 20 | private Long id; 21 | 22 | /** 23 | * 类型名称 24 | */ 25 | private String name; 26 | 27 | /** 28 | * 类型编码 29 | */ 30 | private String code; 31 | 32 | /** 33 | * 状态(0:正常;1:禁用) 34 | */ 35 | private Integer status; 36 | 37 | /** 38 | * 备注 39 | */ 40 | private String remark; 41 | } -------------------------------------------------------------------------------- /vue-admin/src/components/AppLink/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 41 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/enums/MenuTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.enums; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import com.youlai.common.base.IBaseEnum; 5 | import lombok.Getter; 6 | 7 | /** 8 | * 菜单类型枚举 9 | * 10 | * @author haoxr 11 | * @since 2022/4/23 12 | */ 13 | 14 | public enum MenuTypeEnum implements IBaseEnum { 15 | 16 | NULL(0, null), 17 | MENU(1, "菜单"), 18 | CATALOG(2, "目录"), 19 | EXTLINK(3, "外链"), 20 | 21 | BUTTON(4, "按钮"); 22 | 23 | @Getter 24 | @EnumValue // Mybatis-Plus 提供注解表示插入数据库时插入该值 25 | private Integer value; 26 | 27 | @Getter 28 | // @JsonValue // 表示对枚举序列化时返回此字段 29 | private String label; 30 | 31 | MenuTypeEnum(Integer value, String label) { 32 | this.value = value; 33 | this.label = label; 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/monitor.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /youlai-common/common-mybatis/src/main/java/com/youlai/common/mybatis/handler/LongArrayJsonTypeHandler.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.mybatis.handler; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.ibatis.type.JdbcType; 5 | import org.apache.ibatis.type.MappedJdbcTypes; 6 | import org.apache.ibatis.type.MappedTypes; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * Long 数组类型转换 json 11 | * https://www.jianshu.com/p/ab832f3fe81c 12 | * 13 | * @author Gadfly 14 | * @since 2021-06-30 15:26 15 | */ 16 | @Slf4j 17 | @Component 18 | @MappedTypes(value = {Long[].class}) 19 | @MappedJdbcTypes(value = {JdbcType.OTHER}, includeNullJdbcType = true) 20 | public class LongArrayJsonTypeHandler extends ArrayObjectJsonTypeHandler { 21 | public LongArrayJsonTypeHandler() { 22 | super(Long[].class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-common/common-mybatis/src/main/java/com/youlai/common/mybatis/handler/IntegerArrayJsonTypeHandler.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.mybatis.handler; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.ibatis.type.JdbcType; 5 | import org.apache.ibatis.type.MappedJdbcTypes; 6 | import org.apache.ibatis.type.MappedTypes; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * Integer 数组类型转换 json 11 | * https://www.jianshu.com/p/ab832f3fe81c 12 | * 13 | * @author Gadfly 14 | * @since 2021-06-30 15:19 15 | */ 16 | @Slf4j 17 | @Component 18 | @MappedTypes(value = {Integer[].class}) 19 | @MappedJdbcTypes(value = {JdbcType.VARCHAR}, includeNullJdbcType = true) 20 | public class IntegerArrayJsonTypeHandler extends ArrayObjectJsonTypeHandler { 21 | public IntegerArrayJsonTypeHandler() { 22 | super(Integer[].class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/vo/RolePageVO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | @Schema(description ="角色分页对象") 10 | @Data 11 | public class RolePageVO { 12 | 13 | @Schema(description="角色ID") 14 | private Long id; 15 | 16 | @Schema(description="角色名称") 17 | private String name; 18 | 19 | @Schema(description="角色编码") 20 | private String code; 21 | 22 | @Schema(description="角色状态") 23 | private Integer status; 24 | 25 | @Schema(description="排序") 26 | private Integer sort; 27 | 28 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 29 | private LocalDateTime createTime; 30 | 31 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 32 | private LocalDateTime updateTime; 33 | } 34 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/layout/components/Settings/components/ThemeColorPicker.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 36 | 37 | 42 | -------------------------------------------------------------------------------- /youlai-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | youlai-cloud 9 | com.youlai 10 | 0.0.1 11 | 12 | 13 | youlai-common 14 | pom 15 | 16 | 17 | common-apidoc 18 | common-core 19 | common-redis 20 | common-web 21 | common-mybatis 22 | common-security 23 | common-sms 24 | 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example SysUserDetails template template 3 | ### Example SysUserDetails template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen 10 | target 11 | *.log 12 | 13 | # Nacos 14 | !**/nacos/target 15 | **/nacos/bin/work 16 | **/nacos/data 17 | logs 18 | **/nacos/LICENSE 19 | **/nacos/NOTICE 20 | 21 | # Canal 22 | h2.mv.db 23 | **/meta.dat 24 | 25 | # https://raw.githubusercontent.com/redhat-developer/vscode-java/master/.gitignore 26 | # vscode 27 | server 28 | node_modules 29 | *.vsix 30 | .DS_Store 31 | .vscode-test 32 | undefined 33 | dist 34 | jre 35 | .settings 36 | .classpath 37 | .project 38 | test/resources/projects/**/.vscode 39 | test/resources/projects/maven/salut/testGradle 40 | 41 | 42 | 43 | *.local 44 | .history 45 | 46 | # Editor directories and files 47 | *.suo 48 | *.ntvs* 49 | *.njsproj 50 | *.sln 51 | 52 | package-lock.json 53 | pnpm-lock.yaml 54 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/handler/UserBlockHandler.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.handler; 2 | 3 | import com.alibaba.csp.sentinel.slots.block.BlockException; 4 | import com.youlai.common.result.Result; 5 | import com.youlai.system.model.vo.UserInfoVO; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | /** 9 | * 用户接口降级逻辑 10 | * @author haoxr 11 | * @createTime 2021/4/23 23:30 12 | */ 13 | @Slf4j 14 | public class UserBlockHandler { 15 | 16 | /** 17 | * 获取当前登录用户信息的熔断降级处理 18 | * @param blockException 19 | * @return 20 | */ 21 | public static Result handleGetCurrentUserBlock(BlockException blockException) { 22 | return Result.success(new UserInfoVO()); 23 | } 24 | 25 | 26 | public static Result handleGetUserByUsernameBlock(String username,BlockException blockException){ 27 | log.info("降级了:{}",username); 28 | return Result.failed("降级了"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/user.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/java/com/youlai/common/web/config/FeignDecoderConfig.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.web.config; 2 | 3 | import com.youlai.common.web.decoder.FeignDecoder; 4 | import feign.codec.Decoder; 5 | import feign.optionals.OptionalDecoder; 6 | import org.springframework.beans.factory.ObjectProvider; 7 | import org.springframework.boot.autoconfigure.http.HttpMessageConverters; 8 | import org.springframework.cloud.openfeign.support.ResponseEntityDecoder; 9 | import org.springframework.cloud.openfeign.support.SpringDecoder; 10 | import org.springframework.context.annotation.Bean; 11 | 12 | /** 13 | * 14 | * @author haoxr 15 | * @since 2023/8/24 16 | */ 17 | public class FeignDecoderConfig { 18 | 19 | @Bean 20 | public Decoder feignDecoder(ObjectProvider messageConverters) { 21 | return new OptionalDecoder((new ResponseEntityDecoder(new FeignDecoder(new SpringDecoder(messageConverters))))); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /vue-admin/src/api/auth/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 登录请求参数 3 | */ 4 | export interface LoginData { 5 | /** 6 | * 用户名 7 | */ 8 | username: string; 9 | /** 10 | * 密码 11 | */ 12 | password: string; 13 | /** 14 | * 授权类型 15 | */ 16 | grant_type?: string; 17 | /** 18 | * 验证码Code 19 | */ 20 | captchaCode?: string; 21 | /** 22 | * 验证码唯一标识(UUID) 23 | */ 24 | captchaId?: string; 25 | } 26 | 27 | /** 28 | * 登录响应 29 | */ 30 | export interface LoginResult { 31 | /** 32 | * 访问token 33 | */ 34 | access_token?: string; 35 | /** 36 | * 过期时间(单位:毫秒) 37 | */ 38 | expires?: number; 39 | /** 40 | * 刷新token 41 | */ 42 | refresh_token?: string; 43 | /** 44 | * token 类型 45 | */ 46 | token_type?: string; 47 | } 48 | 49 | /** 50 | * 验证码响应 51 | */ 52 | export interface CaptchaResult { 53 | /** 54 | * 验证码缓存key 55 | */ 56 | captchaId: string; 57 | /** 58 | * 验证码图片Base64字符串 59 | */ 60 | captchaBase64: string; 61 | } 62 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/uv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/dict_item.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-auth/src/main/java/com/youlai/auth/oauth2/jackson/SysUserMixin.java: -------------------------------------------------------------------------------- 1 | package com.youlai.auth.oauth2.jackson; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 7 | 8 | /** 9 | * SysUserDetails 反序列化注册 10 | * 11 | * 刷新模式根据 refresh_token 从 oauth2_authorization 表中获取字段 attributes 内容反序列化成 12 | * 13 | * @author haoxr 14 | * @see org.springframework.security.jackson2.UserMixin 15 | * @since 2023/7/4 16 | */ 17 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) 18 | @JsonDeserialize(using = SysUserDeserializer.class) 19 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE) 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class SysUserMixin { 22 | } 23 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/advert.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/java/com/youlai/common/web/constraint/CheckCityValid.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.web.constraint; 2 | 3 | import jakarta.validation.Constraint; 4 | import jakarta.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | import static java.lang.annotation.ElementType.*; 10 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 11 | 12 | /** 13 | * 校验城市地名等是否合法,不接受null 14 | * 15 | * @author Gadfly 16 | * @since 2021-08-06 16:02 17 | */ 18 | @Documented 19 | @Constraint(validatedBy = CityValidator.class) 20 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 21 | @Retention(RUNTIME) 22 | public @interface CheckCityValid { 23 | /* 校验字段类型 */ 24 | CityType value() default CityType.CITY; 25 | 26 | String message() default "{city.valid}"; 27 | 28 | Class[] groups() default {}; 29 | 30 | Class[] payload() default {}; 31 | } 32 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/resources/mapper/SysRoleMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 27 | 28 | -------------------------------------------------------------------------------- /vue-admin/src/components/SizeSelect/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 36 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-common/common-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | youlai-common 7 | com.youlai 8 | 0.0.1 9 | 10 | 4.0.0 11 | 12 | common-core 13 | 14 | 15 | 16 | 17 | io.swagger.core.v3 18 | swagger-annotations 19 | 20 | 21 | 22 | 23 | com.baomidou 24 | mybatis-plus-spring-boot3-starter 25 | true 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/resources/mapper/SysUserRoleMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 16 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /vue-admin/src/layout/components/Sidebar/components/SidebarMenuItemTitle.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 24 | 25 | 43 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/entity/SysDept.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.youlai.common.base.BaseEntity; 6 | import lombok.Data; 7 | 8 | /** 9 | * 部门表 10 | */ 11 | @Data 12 | public class SysDept extends BaseEntity { 13 | /** 14 | * 主键 15 | */ 16 | @TableId(type = IdType.AUTO) 17 | private Long id; 18 | 19 | /** 20 | * 部门名称 21 | */ 22 | private String name; 23 | 24 | /** 25 | * 父节点id 26 | */ 27 | private Long parentId; 28 | 29 | /** 30 | * 父节点id路径 31 | */ 32 | private String treePath; 33 | 34 | /** 35 | * 显示顺序 36 | */ 37 | private Integer sort; 38 | 39 | /** 40 | * 状态(1:正常;0:禁用) 41 | */ 42 | private Integer status; 43 | 44 | /** 45 | * 逻辑删除标识(1:已删除;0:未删除) 46 | */ 47 | private Integer deleted; 48 | 49 | /* private Long createBy; 50 | 51 | private Long updateBy;*/ 52 | 53 | } -------------------------------------------------------------------------------- /vue-admin/src/api/system/dept/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 部门查询参数 3 | */ 4 | export interface DeptQuery { 5 | keywords?: string; 6 | status?: number; 7 | } 8 | 9 | /** 10 | * 部门类型 11 | */ 12 | export interface DeptVO { 13 | /** 14 | * 子部门 15 | */ 16 | children?: DeptVO[]; 17 | /** 18 | * 创建时间 19 | */ 20 | createTime?: Date; 21 | /** 22 | * 部门ID 23 | */ 24 | id?: number; 25 | /** 26 | * 部门名称 27 | */ 28 | name?: string; 29 | /** 30 | * 父部门ID 31 | */ 32 | parentId?: number; 33 | /** 34 | * 排序 35 | */ 36 | sort?: number; 37 | /** 38 | * 状态(1:启用;0:禁用) 39 | */ 40 | status?: number; 41 | /** 42 | * 修改时间 43 | */ 44 | updateTime?: Date; 45 | } 46 | 47 | /** 48 | * 部门表单类型 49 | */ 50 | export interface DeptForm { 51 | /** 52 | * 部门ID(新增不填) 53 | */ 54 | id?: number; 55 | /** 56 | * 部门名称 57 | */ 58 | name?: string; 59 | /** 60 | * 父部门ID 61 | */ 62 | parentId: number; 63 | /** 64 | * 排序 65 | */ 66 | sort?: number; 67 | /** 68 | * 状态(1:启用;0:禁用) 69 | */ 70 | status?: number; 71 | } 72 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/entity/SysRole.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import com.youlai.common.base.BaseEntity; 7 | import lombok.Data; 8 | 9 | /** 10 | * 角色表 11 | */ 12 | @TableName(value ="sys_role") 13 | @Data 14 | public class SysRole extends BaseEntity { 15 | /** 16 | * 17 | */ 18 | @TableId(type = IdType.AUTO) 19 | private Long id; 20 | 21 | /** 22 | * 角色名称 23 | */ 24 | private String name; 25 | 26 | /** 27 | * 角色编码 28 | */ 29 | private String code; 30 | 31 | /** 32 | * 显示顺序 33 | */ 34 | private Integer sort; 35 | 36 | /** 37 | * 角色状态(1-正常;0-停用) 38 | */ 39 | private Integer status; 40 | 41 | /** 42 | * 逻辑删除标识(0-未删除;1-已删除) 43 | */ 44 | private Integer deleted; 45 | 46 | /** 47 | * 数据权限 48 | */ 49 | private Integer dataScope; 50 | } -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/service/SysRoleMenuService.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.service; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.youlai.system.model.entity.SysRoleMenu; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 角色菜单业务接口 11 | * 12 | * @author haoxr 13 | * @since 0.0.1 14 | */ 15 | public interface SysRoleMenuService extends IService { 16 | 17 | /** 18 | * 获取角色拥有的菜单ID集合 19 | * 20 | * @param roleId 21 | * @return 22 | */ 23 | List listMenuIdsByRoleId(Long roleId); 24 | 25 | 26 | /** 27 | * 刷新权限缓存(所有角色) 28 | */ 29 | void refreshRolePermsCache(); 30 | 31 | /** 32 | * 刷新权限缓存(指定角色) 33 | * 34 | * @param roleCode 角色编码 35 | */ 36 | void refreshRolePermsCache(String roleCode); 37 | 38 | /** 39 | * 刷新权限缓存(修改角色编码时调用) 40 | * 41 | * @param oldRoleCode 旧角色编码 42 | * @param newRoleCode 新角色编码 43 | */ 44 | void refreshRolePermsCache(String oldRoleCode, String newRoleCode); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/vo/UserProfileVO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.util.Set; 7 | 8 | /** 9 | * 个人中心用户视图对象 10 | * 11 | * @author haoxr 12 | * @since 2022/1/14 13 | */ 14 | @Schema(description ="个人中心用户视图对象") 15 | @Data 16 | public class UserProfileVO { 17 | 18 | @Schema(description="用户ID") 19 | private Long id; 20 | 21 | @Schema(description="登录账号") 22 | private String username; 23 | 24 | @Schema(description="用户昵称") 25 | private String nickname; 26 | 27 | @Schema(description="手机号码") 28 | private String mobile; 29 | 30 | @Schema(description="头像地址") 31 | private String avatar; 32 | 33 | @Schema(description="用户角色名称集合") 34 | private Set roleNames; 35 | 36 | @Schema(description="部门名称") 37 | private String deptName; 38 | 39 | @Schema(description="邮箱") 40 | private String email; 41 | 42 | @Schema(description="性别") 43 | private String genderLabel; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/form/UserRegisterForm.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.form; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotEmpty; 6 | import jakarta.validation.constraints.Pattern; 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 用户注册表单 13 | * 14 | * @author haoxr 15 | * @since 3.1.0 16 | */ 17 | @Schema(description = "用户注册表单") 18 | @Data 19 | public class UserRegisterForm { 20 | 21 | @Schema(description="登录账号") 22 | @NotBlank(message = "登录账号不能为空") 23 | private String username; 24 | 25 | @Schema(description="手机号码") 26 | @Pattern(regexp = "^$|^1(3\\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$", message = "手机号码格式不正确") 27 | private String mobile; 28 | 29 | @Schema(description="密码") 30 | @NotBlank(message = "密码不能为空") 31 | private String password; 32 | 33 | @Schema(description="验证码") 34 | @NotBlank(message = "验证码不能为空") 35 | private String code; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /vue-admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "noLib": false, 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "lib": ["esnext", "dom"], 13 | "baseUrl": ".", 14 | "allowJs": true, 15 | "paths": { 16 | "@/*": ["src/*"] 17 | }, 18 | "types": ["vite/client", "unplugin-icons/types/vue", "element-plus/global"], 19 | "skipLibCheck": true /* Skip type checking all .d.ts files. */, 20 | "allowSyntheticDefaultImports": true /* 允许默认导入 */, 21 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 22 | 23 | "jsx": "preserve", 24 | "jsxFactory": "h", 25 | "jsxFragmentFactory": "Fragment" 26 | }, 27 | "include": [ 28 | "src/**/*.ts", 29 | "src/**/*.vue", 30 | "src/types/**/*.d.ts", 31 | "mock/**/*.ts", 32 | "vite.config.ts" 33 | ], 34 | "exclude": ["node_modules", "dist", "**/*.js"] 35 | } 36 | -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/java/com/youlai/common/web/model/Option.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.web.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 下拉选项对象 12 | * 13 | * @author haoxr 14 | * @since 2022/1/22 15 | */ 16 | @Schema(description ="下拉选项对象") 17 | @Data 18 | @NoArgsConstructor 19 | public class Option { 20 | 21 | public Option(T value, String label) { 22 | this.value = value; 23 | this.label = label; 24 | } 25 | 26 | public Option(T value, String label, List

14 | * 获取验证码、退出登录等接口 15 | * 注:登录接口不在此控制器,在过滤器OAuth2TokenEndpointFilter拦截端点(/oauth2/token)处理 16 | * 17 | * @author haoxr 18 | * @since 3.1.0 19 | */ 20 | @RestController 21 | @RequestMapping("/api/v1/auth") 22 | @RequiredArgsConstructor 23 | public class AuthController { 24 | 25 | private final AuthService authService; 26 | 27 | @Operation(summary = "获取验证码") 28 | @GetMapping("/captcha") 29 | public Result getCaptcha() { 30 | CaptchaResult captchaResult = authService.getCaptcha(); 31 | return Result.success(captchaResult); 32 | } 33 | 34 | @Operation(summary = "发送手机短信验证码") 35 | @PostMapping("/sms_code") 36 | public Result sendLoginSmsCode( 37 | @Parameter(description = "手机号") @RequestParam String mobile 38 | ) { 39 | boolean result = authService.sendLoginSmsCode(mobile); 40 | return Result.judge(result); 41 | } 42 | 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/controller/FileController.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.controller; 2 | 3 | import com.youlai.common.result.Result; 4 | import com.youlai.system.model.vo.FileInfoVO; 5 | import com.youlai.system.service.OssService; 6 | import io.swagger.v3.oas.annotations.tags.Tag; 7 | import io.swagger.v3.oas.annotations.Operation; 8 | import io.swagger.v3.oas.annotations.Parameter; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.web.bind.annotation.*; 11 | import org.springframework.web.multipart.MultipartFile; 12 | 13 | @Tag(name = "06.文件接口") 14 | @RestController 15 | @RequestMapping("/api/v1/files") 16 | @RequiredArgsConstructor 17 | public class FileController { 18 | 19 | private final OssService ossService; 20 | 21 | @PostMapping 22 | @Operation(summary= "文件上传") 23 | public Result uploadFile( 24 | @Parameter(name = "表单文件对象") @RequestParam(value = "file") MultipartFile file 25 | ) { 26 | FileInfoVO fileInfo = ossService.uploadFile(file); 27 | return Result.success(fileInfo); 28 | } 29 | 30 | @DeleteMapping 31 | @Operation(summary= "文件删除") 32 | public Result deleteFile( 33 | @Parameter(name = "文件路径") @RequestParam String filePath 34 | ) { 35 | boolean result = ossService.deleteFile(filePath); 36 | return Result.judge(result); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /vue-admin/src/styles/reset.scss: -------------------------------------------------------------------------------- 1 | *, 2 | ::before, 3 | ::after { 4 | box-sizing: border-box; 5 | border-color: currentcolor; 6 | border-style: solid; 7 | border-width: 0; 8 | } 9 | 10 | #app { 11 | width: 100%; 12 | height: 100%; 13 | } 14 | 15 | html { 16 | box-sizing: border-box; 17 | width: 100%; 18 | height: 100%; 19 | line-height: 1.5; 20 | tab-size: 4; 21 | text-size-adjust: 100%; 22 | } 23 | 24 | body { 25 | width: 100%; 26 | height: 100%; 27 | margin: 0; 28 | font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", 29 | "Microsoft YaHei", "微软雅黑", Arial, sans-serif; 30 | line-height: inherit; 31 | -moz-osx-font-smoothing: grayscale; 32 | -webkit-font-smoothing: antialiased; 33 | text-rendering: optimizelegibility; 34 | } 35 | 36 | a { 37 | color: inherit; 38 | text-decoration: inherit; 39 | } 40 | 41 | img, 42 | svg { 43 | display: inline-block; 44 | } 45 | 46 | svg { 47 | // 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 48 | vertical-align: -0.15em; 49 | } 50 | 51 | ul, 52 | li { 53 | padding: 0; 54 | margin: 0; 55 | list-style: none; 56 | } 57 | 58 | *, 59 | *::before, 60 | *::after { 61 | box-sizing: inherit; 62 | } 63 | 64 | a, 65 | a:focus, 66 | a:hover { 67 | color: inherit; 68 | text-decoration: none; 69 | cursor: pointer; 70 | } 71 | 72 | a:focus, 73 | a:active, 74 | div:focus { 75 | outline: none; 76 | } 77 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/form/MenuForm.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.form; 2 | 3 | import com.youlai.system.enums.MenuTypeEnum; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | @Schema(description = "菜单表单对象") 8 | @Data 9 | public class MenuForm { 10 | 11 | @Schema(description = "菜单ID") 12 | private Long id; 13 | 14 | @Schema(description = "父菜单ID") 15 | private Long parentId; 16 | 17 | @Schema(description = "菜单名称") 18 | private String name; 19 | 20 | @Schema(description = "菜单类型(1-菜单;2-目录;3-外链;4-按钮权限)") 21 | private MenuTypeEnum type; 22 | 23 | @Schema(description = "路由路径") 24 | private String path; 25 | 26 | @Schema(description = "组件路径(vue页面完整路径,省略.vue后缀)") 27 | private String component; 28 | 29 | @Schema(description = "权限标识") 30 | private String perm; 31 | 32 | @Schema(description = "显示状态(1:显示;0:隐藏)") 33 | private Integer visible; 34 | 35 | @Schema(description = "排序(数字越小排名越靠前)") 36 | private Integer sort; 37 | 38 | @Schema(description = "菜单图标") 39 | private String icon; 40 | 41 | @Schema(description = "跳转路径") 42 | private String redirect; 43 | 44 | @Schema(description = "【菜单】是否开启页面缓存", example = "1") 45 | private Integer keepAlive; 46 | 47 | @Schema(description = "【目录】只有一个子路由是否始终显示", example = "1") 48 | private Integer alwaysShow; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /vue-admin/src/utils/pkce.ts: -------------------------------------------------------------------------------- 1 | import CryptoJS from 'crypto-js' 2 | 3 | /** 4 | * 生成 CodeVerifier 5 | * 6 | * return CodeVerifier 7 | */ 8 | export function generateCodeVerifier() { 9 | return generateRandomString(32) 10 | } 11 | 12 | /** 13 | * 生成随机字符串 14 | * @param length 随机字符串的长度 15 | * @returns 随机字符串 16 | */ 17 | export function generateRandomString(length: number) { 18 | let text = '' 19 | const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' 20 | for (let i = 0; i < length; i++) { 21 | text += possible.charAt(Math.floor(Math.random() * possible.length)) 22 | } 23 | return text 24 | } 25 | 26 | /** 27 | * 生成 Code Challenge 28 | * @param code_verifier 上边生成的 CodeVerifier 29 | * @returns Code Challenge 30 | */ 31 | export function generateCodeChallenge(code_verifier: string) { 32 | return base64URL(CryptoJS.SHA256(code_verifier)) 33 | } 34 | 35 | /** 36 | * 将字符串base64加密后在转为url string 37 | * @param str 字符串 38 | * @returns bese64转码后转为url string 39 | */ 40 | export function base64URL(str: CryptoJS.lib.WordArray) { 41 | return str 42 | .toString(CryptoJS.enc.Base64) 43 | .replace(/=/g, '') 44 | .replace(/\+/g, '-') 45 | .replace(/\//g, '_') 46 | } 47 | 48 | /** 49 | * 将字符串加密为Base64格式的 50 | * @param str 将要转为base64的字符串 51 | * @returns 返回base64格式的字符串 52 | */ 53 | export function base64Str(str: string) { 54 | return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(str)); 55 | } 56 | -------------------------------------------------------------------------------- /youlai-common/common-web/src/main/java/com/youlai/common/web/config/ValidationConfig.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.web.config; 2 | 3 | import org.hibernate.validator.HibernateValidator; 4 | import org.springframework.beans.factory.config.AutowireCapableBeanFactory; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory; 8 | 9 | import jakarta.validation.Validation; 10 | import jakarta.validation.Validator; 11 | import jakarta.validation.ValidatorFactory; 12 | 13 | /** 14 | * 运行时入参校验配置 15 | * 16 | * @author haoxr 17 | * @since 2022/11/10 18 | */ 19 | @Configuration 20 | public class ValidationConfig { 21 | 22 | /** 23 | * 自定义validator实现快速失败 24 | * 25 | * @param autowireCapableBeanFactory 26 | * @return 27 | */ 28 | @Bean 29 | public Validator validator( AutowireCapableBeanFactory autowireCapableBeanFactory) { 30 | ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) 31 | .configure() 32 | .failFast(true) // failFast=true 不校验所有参数,只要出现校验失败情况直接返回,不再进行后续参数校验 33 | .constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory)) 34 | .buildValidatorFactory(); 35 | 36 | return validatorFactory.getValidator(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/role.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /youlai-auth/src/main/java/com/youlai/auth/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package com.youlai.auth.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.cors.CorsConfiguration; 6 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 7 | import org.springframework.web.filter.CorsFilter; 8 | 9 | /** 10 | * 跨域共享配置 11 | * 12 | * 13 | * @author haoxr 14 | * @since 2024/3/14 15 | */ 16 | @Configuration 17 | public class CorsConfig { 18 | 19 | @Bean 20 | public CorsFilter corsFilter() { 21 | CorsConfiguration configuration = new CorsConfiguration(); 22 | 23 | configuration.addAllowedOrigin("http://127.0.0.1:9527"); 24 | 25 | // 设置跨域访问可以携带cookie 26 | configuration.setAllowCredentials(true); 27 | // 允许所有的请求方法 ==> GET POST PUT Delete 28 | configuration.addAllowedMethod("*"); 29 | // 允许携带任何头信息 30 | configuration.addAllowedHeader("*"); 31 | 32 | // 初始化cors配置源对象 33 | UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource(); 34 | 35 | // 给配置源对象设置过滤的参数 36 | // 参数一: 过滤的路径 == > 所有的路径都要求校验是否跨域 37 | // 参数二: 配置类 38 | configurationSource.registerCorsConfiguration("/**", configuration); 39 | 40 | // 返回配置好的过滤器 41 | return new CorsFilter(configurationSource); 42 | 43 | } 44 | 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /youlai-common/common-apidoc/src/main/java/com/youlai/common/apidoc/ApiDocInfoProperties.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.apidoc; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * API 信息属性 8 | * 9 | * @author haoxr 10 | * @since 2023/10/30 11 | */ 12 | @Data 13 | @ConfigurationProperties(prefix = "springdoc.info") 14 | public class ApiDocInfoProperties { 15 | 16 | /** 17 | * API文档标题 18 | */ 19 | private String title; 20 | 21 | /** 22 | * API文档版本 23 | */ 24 | private String version; 25 | 26 | /** 27 | * API文档描述 28 | */ 29 | private String description; 30 | 31 | /** 32 | * 联系人信息 33 | */ 34 | private Contact contact; 35 | 36 | /** 37 | * 许可证信息 38 | */ 39 | private License license; 40 | 41 | @Data 42 | public static class Contact { 43 | /** 44 | * 联系人姓名 45 | */ 46 | private String name; 47 | /** 48 | * 联系人主页 49 | */ 50 | private String url; 51 | /** 52 | * 联系人邮箱 53 | */ 54 | private String email; 55 | 56 | } 57 | 58 | /** 59 | * 许可证信息 60 | */ 61 | @Data 62 | public static class License{ 63 | /** 64 | * 许可证名称 65 | */ 66 | private String name; 67 | /** 68 | * 许可证URL 69 | */ 70 | private String url; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /vue-admin/src/views/login/OAuth2Redirect.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /youlai-common/common-core/src/main/java/com/youlai/common/factory/NamedThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.factory; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | 5 | import java.util.concurrent.ThreadFactory; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | /** 9 | * 自定义 ThreadFactory 10 | *

11 | * 重命名线程池 12 | * 13 | * @author haoxr 14 | * @since 2022/2/13 0:14 15 | */ 16 | public class NamedThreadFactory implements ThreadFactory { 17 | 18 | private final AtomicInteger poolNumber = new AtomicInteger(1); 19 | 20 | private final ThreadGroup threadGroup; 21 | 22 | private final AtomicInteger threadNumber = new AtomicInteger(1); 23 | 24 | public final String namePrefix; 25 | 26 | public NamedThreadFactory(String name) { 27 | 28 | this.threadGroup = Thread.currentThread().getThreadGroup(); 29 | 30 | if (StrUtil.isBlank(name)) { 31 | name = "pool"; 32 | } 33 | namePrefix = name + "-" + poolNumber.getAndIncrement() + "-thread-"; 34 | } 35 | 36 | @Override 37 | public Thread newThread(Runnable r) { 38 | Thread t = new Thread(threadGroup, r, 39 | namePrefix + threadNumber.getAndIncrement(), 40 | 0); 41 | if (t.isDaemon()) { 42 | t.setDaemon(false); 43 | } 44 | 45 | if (t.getPriority() != Thread.NORM_PRIORITY) { 46 | t.setPriority(Thread.NORM_PRIORITY); 47 | } 48 | return t; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /youlai-gateway/src/main/resources/bootstrap-dev.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9999 3 | 4 | spring: 5 | main: 6 | allow-bean-definition-overriding: true 7 | cloud: 8 | nacos: 9 | # 注册中心 10 | discovery: 11 | server-addr: http://localhost:8848 12 | namespace: youlai-cloud 13 | # 配置中心 14 | config: 15 | server-addr: http://localhost:8848 16 | namespace: youlai-cloud 17 | file-extension: yaml 18 | shared-configs[0]: 19 | data-id: youlai-common.yaml 20 | refresh: true 21 | security: 22 | oauth2: 23 | resourceserver: 24 | jwt: 25 | issuer-uri: http://127.0.0.1:9000 26 | client: 27 | registration: 28 | messaging-client-oidc: 29 | # 认证提供者,和provider中的名称对应 30 | provider: custom-issuer 31 | client-id: messaging-client 32 | client-secret: 123456 33 | client-authentication-method: client_secret_basic 34 | authorization-grant-type: authorization_code 35 | # 授权成功后的回调地址,Spring Security Client 使用 Code 换 Token 的地址 36 | redirect-uri: http://127.0.0.1:9999/login/oauth2/code/messaging-client-oidc 37 | scope: openid, profile 38 | client-name: messaging-client-oidc 39 | provider: 40 | custom-issuer: 41 | issuer-uri: http://127.0.0.1:9000 42 | # user-info-uri: http://127.0.0.1:9000/userinfo 43 | # user-name-attribute: name 44 | -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/message.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-common/common-redis/src/main/java/com/youlai/common/redis/BusinessSnGenerator.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.redis; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.time.LocalDateTime; 10 | import java.time.ZoneOffset; 11 | import java.time.format.DateTimeFormatter; 12 | 13 | @Component 14 | @Slf4j 15 | @RequiredArgsConstructor 16 | public class BusinessSnGenerator { 17 | 18 | 19 | private final RedisTemplate redisTemplate; 20 | 21 | /** 22 | * @param digit 业务序号位数 23 | * @return 24 | */ 25 | public String generateSerialNo(String businessType, Integer digit) { 26 | if (StrUtil.isBlank(businessType)) { 27 | businessType = "COMMON"; 28 | } 29 | String date = LocalDateTime.now(ZoneOffset.of("+8")) 30 | .format(DateTimeFormatter.ofPattern("yyyyMMdd")); 31 | String key = "SN:" + businessType + ":" + date; 32 | Long increment = redisTemplate.opsForValue().increment(key); 33 | return date + String.format("%0" + digit + "d", increment); 34 | } 35 | 36 | public String generateSerialNo(Integer digit) { 37 | return this.generateSerialNo(null,6); 38 | } 39 | 40 | public String generateSerialNo(String businessType) { 41 | return this.generateSerialNo(businessType,6); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/service/SysDeptService.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.youlai.system.model.entity.SysDept; 5 | import com.youlai.system.model.form.DeptForm; 6 | import com.youlai.system.model.query.DeptQuery; 7 | import com.youlai.system.model.vo.DeptVO; 8 | import com.youlai.common.web.model.Option; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * 部门业务接口 14 | * 15 | * @author haoxr 16 | * @since 2021/8/22 17 | */ 18 | public interface SysDeptService extends IService { 19 | /** 20 | * 部门列表 21 | * 22 | * @return 23 | */ 24 | List listDepartments(DeptQuery queryParams); 25 | 26 | /** 27 | * 部门树形下拉选项 28 | * 29 | * @return 30 | */ 31 | List

23 | * 修改 Redis 序列化方式,默认 JdkSerializationRedisSerializer 24 | * 25 | * @param redisConnectionFactory {@link RedisConnectionFactory} 26 | * @return {@link RedisTemplate} 27 | */ 28 | @Bean 29 | public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { 30 | 31 | RedisTemplate redisTemplate = new RedisTemplate<>(); 32 | redisTemplate.setConnectionFactory(redisConnectionFactory); 33 | 34 | redisTemplate.setKeySerializer(RedisSerializer.string()); 35 | redisTemplate.setValueSerializer(RedisSerializer.json()); 36 | 37 | redisTemplate.setHashKeySerializer(RedisSerializer.string()); 38 | redisTemplate.setHashValueSerializer(RedisSerializer.json()); 39 | 40 | redisTemplate.afterPropertiesSet(); 41 | return redisTemplate; 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/form/UserForm.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.form; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotEmpty; 6 | import jakarta.validation.constraints.Pattern; 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 用户表单对象 13 | * 14 | * @author haoxr 15 | * @since 2022/4/12 11:04 16 | */ 17 | @Schema(description = "用户表单对象") 18 | @Data 19 | public class UserForm { 20 | 21 | @Schema(description="用户ID") 22 | private Long id; 23 | 24 | @Schema(description="用户名") 25 | @NotBlank(message = "用户名不能为空") 26 | private String username; 27 | 28 | @Schema(description="昵称") 29 | @NotBlank(message = "昵称不能为空") 30 | private String nickname; 31 | 32 | 33 | @Schema(description="手机号码") 34 | @Pattern(regexp = "^$|^1(3\\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$", message = "手机号码格式不正确") 35 | private String mobile; 36 | 37 | @Schema(description="性别") 38 | private Integer gender; 39 | 40 | @Schema(description="用户头像") 41 | private String avatar; 42 | 43 | @Schema(description="邮箱") 44 | private String email; 45 | 46 | @Schema(description="用户状态(1:正常;0:禁用)") 47 | private Integer status; 48 | 49 | @Schema(description="部门ID") 50 | private Long deptId; 51 | 52 | @Schema(description="角色ID集合") 53 | @NotEmpty(message = "用户角色不能为空") 54 | private List roleIds; 55 | 56 | } 57 | -------------------------------------------------------------------------------- /vue-admin/.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // (x)=>{},单个参数箭头函数是否显示小括号。(always:始终显示;avoid:省略括号。默认:always) 3 | arrowParens: "always", 4 | // 开始标签的右尖括号是否跟随在最后一行属性末尾,默认false 5 | bracketSameLine: false, 6 | // 对象字面量的括号之间打印空格 (true - Example: { foo: bar } ; false - Example: {foo:bar}) 7 | bracketSpacing: true, 8 | // 是否格式化一些文件中被嵌入的代码片段的风格(auto|off;默认auto) 9 | embeddedLanguageFormatting: "auto", 10 | // 指定 HTML 文件的空格敏感度 (css|strict|ignore;默认css) 11 | htmlWhitespaceSensitivity: "css", 12 | // 当文件已经被 Prettier 格式化之后,是否会在文件顶部插入一个特殊的 @format 标记,默认false 13 | insertPragma: false, 14 | // 在 JSX 中使用单引号替代双引号,默认false 15 | jsxSingleQuote: false, 16 | // 每行最多字符数量,超出换行(默认80) 17 | printWidth: 80, 18 | // 超出打印宽度 (always | never | preserve ) 19 | proseWrap: "preserve", 20 | // 对象属性是否使用引号(as-needed | consistent | preserve;默认as-needed:对象的属性需要加引号才添加;) 21 | quoteProps: "as-needed", 22 | // 是否只格式化在文件顶部包含特定注释(@prettier| @format)的文件,默认false 23 | requirePragma: false, 24 | // 结尾添加分号 25 | semi: true, 26 | // 使用单引号 (true:单引号;false:双引号) 27 | singleQuote: false, 28 | // 缩进空格数,默认2个空格 29 | tabWidth: 2, 30 | // 元素末尾是否加逗号,默认es5: ES5中的 objects, arrays 等会添加逗号,TypeScript 中的 type 后不加逗号 31 | trailingComma: "es5", 32 | // 指定缩进方式,空格或tab,默认false,即使用空格 33 | useTabs: false, 34 | // vue 文件中是否缩进 -------------------------------------------------------------------------------- /vue-admin/src/assets/icons/project.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /youlai-common/common-security/src/main/java/com/youlai/common/security/exception/MyAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.youlai.common.security.exception; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.youlai.common.result.Result; 5 | import com.youlai.common.result.ResultCode; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | import org.springframework.security.core.AuthenticationException; 9 | import org.springframework.security.web.AuthenticationEntryPoint; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * 自定义 token 无效异常 16 | * 17 | * @author haoxr 18 | * @date 2022/11/13 19 | */ 20 | @Component 21 | public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint { 22 | @Override 23 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) 24 | throws IOException { 25 | response.setContentType("application/json"); 26 | 27 | int status = response.getStatus(); 28 | ObjectMapper mapper = new ObjectMapper(); 29 | if (HttpServletResponse.SC_NOT_FOUND == status) { 30 | response.setStatus(HttpServletResponse.SC_NOT_FOUND); 31 | mapper.writeValue(response.getOutputStream(), Result.failed(ResultCode.RESOURCE_NOT_FOUND)); 32 | } else { 33 | response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 34 | mapper.writeValue(response.getOutputStream(), Result.failed(ResultCode.TOKEN_INVALID)); 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /youlai-system/system-boot/src/main/java/com/youlai/system/model/bo/RouteBO.java: -------------------------------------------------------------------------------- 1 | package com.youlai.system.model.bo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.youlai.system.enums.MenuTypeEnum; 6 | import lombok.Data; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 路由 12 | */ 13 | @Data 14 | public class RouteBO { 15 | /** 16 | * 17 | */ 18 | @TableId(type = IdType.AUTO) 19 | private Long id; 20 | 21 | /** 22 | * 父菜单ID 23 | */ 24 | private Long parentId; 25 | 26 | /** 27 | * 菜单名称 28 | */ 29 | private String name; 30 | 31 | /** 32 | * 菜单类型(1-菜单;2-目录;3-外链;4-按钮权限) 33 | */ 34 | private MenuTypeEnum type; 35 | 36 | /** 37 | * 路由路径(浏览器地址栏路径) 38 | */ 39 | private String path; 40 | 41 | /** 42 | * 组件路径(vue页面完整路径,省略.vue后缀) 43 | */ 44 | private String component; 45 | 46 | /** 47 | * 权限标识 48 | */ 49 | private String perm; 50 | 51 | /** 52 | * 显示状态(1:显示;0:隐藏) 53 | */ 54 | private Integer visible; 55 | 56 | /** 57 | * 排序 58 | */ 59 | private Integer sort; 60 | 61 | /** 62 | * 菜单图标 63 | */ 64 | private String icon; 65 | 66 | /** 67 | * 跳转路径 68 | */ 69 | private String redirect; 70 | 71 | /** 72 | * 拥有路由的权限 73 | */ 74 | private List roles; 75 | 76 | /** 77 | * 【目录】只有一个子路由是否始终显示(1:是 0:否) 78 | */ 79 | private Integer alwaysShow; 80 | 81 | /** 82 | * 【菜单】是否开启页面缓存(1:是 0:否) 83 | */ 84 | private Integer keepAlive; 85 | 86 | } --------------------------------------------------------------------------------