├── demo ├── src │ ├── main │ │ ├── resources │ │ │ ├── sqls │ │ │ │ ├── .gitkeep │ │ │ │ └── acl │ │ │ │ │ └── role.sql │ │ │ └── templates │ │ │ │ └── myservice.java.btl │ │ └── java │ │ │ └── tech │ │ │ └── riemann │ │ │ └── demo │ │ │ ├── dto │ │ │ ├── A.java │ │ │ ├── B.java │ │ │ ├── response │ │ │ │ ├── PermissionInfo.java │ │ │ │ ├── RoleInfo.java │ │ │ │ ├── ActionInfo.java │ │ │ │ ├── WxLogin.java │ │ │ │ ├── ModuleInfo.java │ │ │ │ └── Permission.java │ │ │ └── request │ │ │ │ ├── SocialLoginBindDTO.java │ │ │ │ └── VXETableSaveDTO.java │ │ │ ├── controller │ │ │ ├── BaseController.java │ │ │ ├── wechat │ │ │ │ ├── DemoController.java │ │ │ │ └── WechatController.java │ │ │ ├── auth │ │ │ │ └── AuthController.java │ │ │ └── acl │ │ │ │ └── ActionController.java │ │ │ ├── service │ │ │ ├── acl │ │ │ │ ├── LoginChannelService.java │ │ │ │ ├── ActionService.java │ │ │ │ ├── UserRoleService.java │ │ │ │ ├── UserPermissionService.java │ │ │ │ ├── RolePermissionService.java │ │ │ │ └── ModuleService.java │ │ │ └── dictionary │ │ │ │ ├── GroupService.java │ │ │ │ └── CodebookService.java │ │ │ ├── SpringBootNutzDemoApplication.java │ │ │ ├── config │ │ │ ├── wechat │ │ │ │ └── WechatOauthConfigurationProperties.java │ │ │ ├── apm │ │ │ │ └── ApmConfiguration.java │ │ │ └── AdministratorConfigurationProperties.java │ │ │ ├── entity │ │ │ ├── DemoEntity.java │ │ │ ├── acl │ │ │ │ ├── UserRole.java │ │ │ │ ├── Role.java │ │ │ │ ├── Module.java │ │ │ │ ├── LoginChannel.java │ │ │ │ ├── UserPermission.java │ │ │ │ ├── RolePermission.java │ │ │ │ ├── Action.java │ │ │ │ └── User.java │ │ │ └── dictionary │ │ │ │ └── Group.java │ │ │ └── vo │ │ │ ├── InstalledAction.java │ │ │ └── InstalledModule.java │ ├── frontend │ │ ├── .browserslistrc │ │ ├── babel.config.js │ │ ├── src │ │ │ ├── components │ │ │ │ ├── ModalForm │ │ │ │ │ └── index.ts │ │ │ │ ├── tools │ │ │ │ │ ├── index.ts │ │ │ │ │ └── Breadcrumb.vue │ │ │ │ ├── PageHeader │ │ │ │ │ └── index.ts │ │ │ │ ├── SettingDrawer │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── SettingItem.vue │ │ │ │ │ └── index.less │ │ │ │ ├── BackgroundAnimation │ │ │ │ │ └── index.ts │ │ │ │ ├── index.less │ │ │ │ ├── SelectLang │ │ │ │ │ ├── index.less │ │ │ │ │ └── SelectLang.vue │ │ │ │ ├── GlobalFooter │ │ │ │ │ └── index.vue │ │ │ │ ├── GlobalHeader │ │ │ │ │ └── RightContent.vue │ │ │ │ └── NProgress │ │ │ │ │ └── nprogress.less │ │ │ ├── assets │ │ │ │ ├── logo.png │ │ │ │ ├── img │ │ │ │ │ ├── bj.jpg │ │ │ │ │ ├── bg-1.png │ │ │ │ │ ├── bg-2.jpg │ │ │ │ │ ├── bg-4.jpg │ │ │ │ │ └── bg-5.jpg │ │ │ │ └── logo.svg │ │ │ ├── plugins │ │ │ │ ├── formCreate.ts │ │ │ │ ├── index.ts │ │ │ │ └── vux-table.ts │ │ │ ├── views │ │ │ │ ├── About.vue │ │ │ │ ├── Home.vue │ │ │ │ ├── user │ │ │ │ │ ├── QRLogin.vue │ │ │ │ │ └── WechatLogin.vue │ │ │ │ ├── dictionary │ │ │ │ │ └── Dictionary.vue │ │ │ │ └── acl │ │ │ │ │ └── module │ │ │ │ │ └── ActionFormDesign.ts │ │ │ ├── api │ │ │ │ ├── login │ │ │ │ │ ├── mods │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── auth │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── login.ts │ │ │ │ │ └── api.d.ts │ │ │ │ ├── wechat │ │ │ │ │ ├── mods │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── socialLogin │ │ │ │ │ │ │ ├── bind.ts │ │ │ │ │ │ │ ├── authUrl.ts │ │ │ │ │ │ │ ├── oauth.ts │ │ │ │ │ │ │ ├── qrOption.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── api.d.ts │ │ │ │ ├── dictionary │ │ │ │ │ └── mods │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── codebook │ │ │ │ │ │ ├── remove.ts │ │ │ │ │ │ ├── get.ts │ │ │ │ │ │ ├── byGroup.ts │ │ │ │ │ │ ├── edit.ts │ │ │ │ │ │ ├── add.ts │ │ │ │ │ │ └── vxeSave.ts │ │ │ │ │ │ └── group │ │ │ │ │ │ ├── remove.ts │ │ │ │ │ │ ├── get.ts │ │ │ │ │ │ ├── edit.ts │ │ │ │ │ │ ├── add.ts │ │ │ │ │ │ └── search.ts │ │ │ │ ├── api.d.ts │ │ │ │ ├── acl │ │ │ │ │ └── mods │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── role │ │ │ │ │ │ ├── get.ts │ │ │ │ │ │ ├── remove.ts │ │ │ │ │ │ ├── add.ts │ │ │ │ │ │ ├── edit.ts │ │ │ │ │ │ ├── permissions.ts │ │ │ │ │ │ ├── grant.ts │ │ │ │ │ │ ├── grantInfo.ts │ │ │ │ │ │ └── search.ts │ │ │ │ │ │ ├── user │ │ │ │ │ │ ├── get.ts │ │ │ │ │ │ ├── remove.ts │ │ │ │ │ │ ├── edit.ts │ │ │ │ │ │ ├── add.ts │ │ │ │ │ │ ├── roles.ts │ │ │ │ │ │ ├── permissions.ts │ │ │ │ │ │ ├── grant.ts │ │ │ │ │ │ ├── grantRoles.ts │ │ │ │ │ │ ├── grantInfo.ts │ │ │ │ │ │ └── search.ts │ │ │ │ │ │ ├── module │ │ │ │ │ │ ├── remove.ts │ │ │ │ │ │ ├── get.ts │ │ │ │ │ │ ├── edit.ts │ │ │ │ │ │ ├── actions.ts │ │ │ │ │ │ ├── add.ts │ │ │ │ │ │ └── search.ts │ │ │ │ │ │ └── action │ │ │ │ │ │ ├── get.ts │ │ │ │ │ │ ├── remove.ts │ │ │ │ │ │ ├── edit.ts │ │ │ │ │ │ ├── add.ts │ │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── utils │ │ │ │ ├── wechatLongin.ts │ │ │ │ ├── util.ts │ │ │ │ ├── WxLogin.ts │ │ │ │ ├── device.ts │ │ │ │ ├── auth.ts │ │ │ │ └── mixin.ts │ │ │ ├── core │ │ │ │ ├── validators │ │ │ │ │ └── index.ts │ │ │ │ ├── directives │ │ │ │ │ └── auth.ts │ │ │ │ ├── filters │ │ │ │ │ └── index.ts │ │ │ │ ├── config │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── shims-ant-design-vue.d.ts │ │ │ ├── shims-vue.d.ts │ │ │ ├── shims-axios.d.ts │ │ │ ├── store │ │ │ │ ├── index.ts │ │ │ │ ├── getters.ts │ │ │ │ ├── mutation-types.ts │ │ │ │ └── modules │ │ │ │ │ └── user.ts │ │ │ ├── shims-tsx.d.ts │ │ │ ├── layouts │ │ │ │ ├── RouteView.vue │ │ │ │ ├── BasicLayout.less │ │ │ │ └── UserLayout.vue │ │ │ ├── App.vue │ │ │ ├── main.ts │ │ │ ├── types │ │ │ │ └── index.d.ts │ │ │ ├── locales │ │ │ │ ├── lang │ │ │ │ │ ├── enUS │ │ │ │ │ │ └── page.ts │ │ │ │ │ ├── zhCN │ │ │ │ │ │ └── page.ts │ │ │ │ │ └── zh-CN.ts │ │ │ │ └── index.ts │ │ │ └── global.less │ │ ├── public │ │ │ ├── favicon.ico │ │ │ └── index.html │ │ ├── pont-config.json │ │ ├── .gitignore │ │ ├── README.md │ │ ├── .eslintrc.js │ │ ├── tsconfig.json │ │ └── package.json │ └── test │ │ └── java │ │ └── org │ │ └── nutz │ │ └── demo │ │ └── NutzDemoApplicationTests.java ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties └── .gitignore ├── src └── main │ ├── resources │ ├── META-INF │ │ ├── spring-devtools.properties │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── banner.txt │ └── java │ └── org │ └── nutz │ └── spring │ └── boot │ ├── filepool │ ├── package-info.java │ ├── FilePoolAutoConfigurationProperties.java │ └── FilePoolAutoConfiguration.java │ ├── package-info.java │ ├── dao │ ├── package-info.java │ ├── SpringResourceLoactionConfiguration.java │ ├── SpringDaoRunner.java │ ├── sqltpl │ │ ├── NutSqlTpl.java │ │ ├── VarSetMap.java │ │ └── impl │ │ │ ├── jetbrick │ │ │ └── JetbrickSqlTpl.java │ │ │ ├── beetl │ │ │ ├── ClasspathStringResourceLoader.java │ │ │ └── BeetlSqlTpl.java │ │ │ ├── velocity │ │ │ └── VelocitySqlTpl.java │ │ │ └── freemarker │ │ │ └── FreeMarkerSqlTpl.java │ ├── sqlmanager │ │ └── XmlSqlManager.java │ └── NutzDatabaseInitializer.java │ ├── json │ └── package-info.java │ ├── ngrok │ ├── package-info.java │ └── NgrokAutoConfigurationProperties.java │ ├── service │ ├── package-info.java │ ├── entity │ │ ├── package-info.java │ │ ├── IdEntity.java │ │ └── Entity.java │ ├── interfaces │ │ ├── package-info.java │ │ ├── IdNameEntityService.java │ │ ├── NameEntityService.java │ │ └── IdEntityService.java │ ├── IdBaseService.java │ ├── NameBaseService.java │ ├── IdNameBaseService.java │ └── BaseService.java │ ├── request │ ├── ProgressListener.java │ ├── NutzHttpAutoConfigurationProperties.java │ ├── NutzHttpRequestFactory.java │ └── NutzClientHttpResponse.java │ ├── log │ └── NutzLogConfigurationProperties.java │ └── hanlder │ └── JsonLocalDateLikeHandler.java ├── .gitpod.yml ├── Jenkinsfile ├── .gitpod.Dockerfile └── .gitignore /demo/src/main/resources/sqls/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/src/frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring-devtools.properties: -------------------------------------------------------------------------------- 1 | restart.exclude.nutz=/nutz-.*.jar -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: mvn install -DskipTests=false 3 | image: 4 | file: .gitpod.Dockerfile 5 | -------------------------------------------------------------------------------- /demo/src/frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/ModalForm/index.ts: -------------------------------------------------------------------------------- 1 | import ModalForm from "./ModalForm.vue"; 2 | export default ModalForm; 3 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/tools/index.ts: -------------------------------------------------------------------------------- 1 | import Breadcrumb from "./Breadcrumb.vue"; 2 | export default Breadcrumb; 3 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/filepool/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.nutz.spring.boot.filepool; 5 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/PageHeader/index.ts: -------------------------------------------------------------------------------- 1 | import PageHeader from "./PageHeader.vue"; 2 | export default PageHeader; 3 | -------------------------------------------------------------------------------- /demo/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nutzam/nutz-spring-boot-starter/HEAD/demo/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /demo/src/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nutzam/nutz-spring-boot-starter/HEAD/demo/src/frontend/public/favicon.ico -------------------------------------------------------------------------------- /demo/src/frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nutzam/nutz-spring-boot-starter/HEAD/demo/src/frontend/src/assets/logo.png -------------------------------------------------------------------------------- /demo/src/frontend/src/assets/img/bj.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nutzam/nutz-spring-boot-starter/HEAD/demo/src/frontend/src/assets/img/bj.jpg -------------------------------------------------------------------------------- /demo/src/frontend/src/components/SettingDrawer/index.ts: -------------------------------------------------------------------------------- 1 | import SettingDrawer from "./SettingDrawer.vue"; 2 | 3 | export default SettingDrawer; 4 | -------------------------------------------------------------------------------- /demo/src/frontend/src/assets/img/bg-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nutzam/nutz-spring-boot-starter/HEAD/demo/src/frontend/src/assets/img/bg-1.png -------------------------------------------------------------------------------- /demo/src/frontend/src/assets/img/bg-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nutzam/nutz-spring-boot-starter/HEAD/demo/src/frontend/src/assets/img/bg-2.jpg -------------------------------------------------------------------------------- /demo/src/frontend/src/assets/img/bg-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nutzam/nutz-spring-boot-starter/HEAD/demo/src/frontend/src/assets/img/bg-4.jpg -------------------------------------------------------------------------------- /demo/src/frontend/src/assets/img/bg-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nutzam/nutz-spring-boot-starter/HEAD/demo/src/frontend/src/assets/img/bg-5.jpg -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Kerbores (kerbores@gmail.com) 3 | * 4 | */ 5 | package org.nutz.spring.boot; -------------------------------------------------------------------------------- /demo/src/frontend/src/plugins/formCreate.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import formCreate from "@form-create/ant-design-vue"; 3 | 4 | Vue.use(formCreate); 5 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author kerbores(kerbores@gmail.com) 3 | * 4 | */ 5 | package org.nutz.spring.boot.dao; -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/json/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author kerbores(kerbores@gmail.com) 3 | * 4 | */ 5 | package org.nutz.spring.boot.json; -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/ngrok/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author kerbores(kerbores@gmail.com) 3 | * 4 | */ 5 | package org.nutz.spring.boot.ngrok; -------------------------------------------------------------------------------- /demo/src/frontend/src/components/BackgroundAnimation/index.ts: -------------------------------------------------------------------------------- 1 | import BackgroundAnimation from "./BackgroundAnimation.vue"; 2 | export default BackgroundAnimation; 3 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author kerbores(kerbores@gmail.com) 3 | * 4 | */ 5 | package org.nutz.spring.boot.service; -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/entity/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author kerbores(kerbores@gmail.com) 3 | * 4 | */ 5 | package org.nutz.spring.boot.service.entity; -------------------------------------------------------------------------------- /demo/src/frontend/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import { ApiPlugin } from "@/api/index"; 3 | 4 | import "./formCreate"; 5 | import "./vux-table"; 6 | 7 | Vue.use(ApiPlugin); 8 | -------------------------------------------------------------------------------- /demo/src/frontend/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/index.less: -------------------------------------------------------------------------------- 1 | @import "~ant-design-vue/lib/style/index"; 2 | 3 | // The prefix to use on all css classes from ant-pro. 4 | @ant-pro-prefix : ant-pro; 5 | @ant-global-header-zindex : 105; -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/interfaces/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 此包下的接口专供 mybatis-plus 和 nutz 共存时使用 3 | * 4 | * @author wkipy 5 | * 6 | */ 7 | package org.nutz.spring.boot.service.interfaces; 8 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/login/mods/index.ts: -------------------------------------------------------------------------------- 1 | import auth, { AuthApi } from './auth'; 2 | 3 | export class LoginApi { 4 | constructor(public auth: AuthApi) {} 5 | } 6 | 7 | export default { 8 | auth, 9 | } as LoginApi; 10 | -------------------------------------------------------------------------------- /demo/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar 3 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/wechat/mods/index.ts: -------------------------------------------------------------------------------- 1 | import socialLogin, { SocialLoginApi } from './socialLogin'; 2 | 3 | export class WechatApi { 4 | constructor(public socialLogin: SocialLoginApi) {} 5 | } 6 | 7 | export default { 8 | socialLogin, 9 | } as WechatApi; 10 | -------------------------------------------------------------------------------- /demo/src/frontend/pont-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "originUrl": "", 3 | "outDir": "./src/api", 4 | "templatePath": "./serviceTemplate", 5 | "usingMultipleOrigins": true, 6 | "origins": [ 7 | { 8 | "name": "login", 9 | "originUrl": "http://localhost:8080/v2/api-docs?group=acl" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /demo/src/test/java/org/nutz/demo/NutzDemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.nutz.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class NutzDemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | stages { 4 | stage('SonarQube') { 5 | steps { 6 | sh 'mvn clean package sonar:sonar' 7 | } 8 | } 9 | stage('打包发布') { 10 | steps { 11 | sh 'mvn clean deploy -Dmaven.test.skip=true -Dgpg.passphrase=$gpg_password' 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demo/src/frontend/src/utils/wechatLongin.ts: -------------------------------------------------------------------------------- 1 | import { default as wechatApi } from "@/api/wechat/mods"; 2 | 3 | /** 4 | * 获取网页授权地址 5 | * 6 | * @param state 7 | */ 8 | export function wechatLogin(state: string): void { 9 | wechatApi.socialLogin.authUrl({ state: state }, ({ data }) => { 10 | window.location.href = data; 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /demo/src/frontend/src/core/validators/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export default { 3 | mobile: 4 | /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/, 5 | email: /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/, 6 | emailOrUserName: /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/, 7 | }; 8 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/interfaces/IdNameEntityService.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service.interfaces; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author wkipy 7 | * 8 | */ 9 | public interface IdNameEntityService extends IdEntityService, NameEntityService { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/index.ts: -------------------------------------------------------------------------------- 1 | import codebook, { CodebookApi } from './codebook'; 2 | 3 | import group, { GroupApi } from './group'; 4 | 5 | export class DictionaryApi { 6 | constructor(public codebook: CodebookApi, public group: GroupApi) {} 7 | } 8 | 9 | export default { 10 | codebook, 11 | group, 12 | } as DictionaryApi; 13 | -------------------------------------------------------------------------------- /demo/src/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | 25 | .mocks/ -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/A.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author wkipy 10 | * 11 | */ 12 | @Data 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class A { 17 | 18 | T t; 19 | 20 | S s; 21 | } 22 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.controller; 2 | 3 | import tech.riemann.demo.entity.acl.User; 4 | 5 | /** 6 | * @author kerbores 7 | * 8 | */ 9 | public class BaseController { 10 | 11 | /** 12 | * 当前用户 13 | * 14 | * @return 15 | */ 16 | protected User currentUser() { 17 | return null; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-mysql 2 | 3 | USER gitpod 4 | 5 | # Install custom tools, runtime, etc. using apt-get 6 | # For example, the command below would install "bastet" - a command line tetris clone: 7 | # 8 | # RUN sudo apt-get -q update && # sudo apt-get install -yq bastet && # sudo rm -rf /var/lib/apt/lists/* 9 | # 10 | # More information: https://www.gitpod.io/docs/config-docker/ 11 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/api.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | 3 | declare type ObjectMap< 4 | Key extends string | number | symbol = any, 5 | Value = any 6 | > = { 7 | [key in Key]: Value; 8 | }; 9 | 10 | /// 11 | /// 12 | /// 13 | /// 14 | -------------------------------------------------------------------------------- /demo/src/frontend/src/shims-ant-design-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | declare module "ant-design-vue/lib/locale-provider/zh_CN" { 3 | const zhCN: any; 4 | export default zhCN; 5 | } 6 | 7 | declare module "ant-design-vue/lib/locale-provider/en_US" { 8 | const enUS: any; 9 | export default enUS; 10 | } 11 | 12 | declare module "moment/locale/eu"; 13 | declare module "moment/locale/zh-cn"; 14 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/B.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto; 2 | 3 | import java.util.List; 4 | 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * @author wkipy 12 | * 13 | */ 14 | @Data 15 | @Builder 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class B { 19 | 20 | A> kk; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/service/acl/LoginChannelService.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.service.acl; 2 | 3 | import org.nutz.spring.boot.service.IdNameBaseService; 4 | import org.springframework.stereotype.Service; 5 | 6 | import tech.riemann.demo.entity.acl.LoginChannel; 7 | 8 | /** 9 | * @author wkipy 10 | * 11 | */ 12 | @Service 13 | public class LoginChannelService extends IdNameBaseService { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /demo/src/frontend/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } 5 | 6 | declare module "vue-particles"; 7 | declare module "store"; 8 | declare module "enquire.js"; 9 | declare module "*.png"; 10 | declare module "*.svg"; 11 | declare module "*.svg?inline"; 12 | declare module "@ant-design-vue/pro-layout"; 13 | declare module "webpack-theme-color-replacer"; 14 | declare module "nprogress"; 15 | -------------------------------------------------------------------------------- /demo/src/frontend/src/shims-axios.d.ts: -------------------------------------------------------------------------------- 1 | import { AxiosInstance } from "axios"; 2 | import { Api } from "./api/index"; 3 | 4 | declare global { 5 | interface Window { 6 | axios: AxiosInstance; 7 | api: Api; 8 | } 9 | } 10 | 11 | declare module "vue/types/vue" { 12 | interface Vue { 13 | $axios: AxiosInstance; 14 | $api: Api; 15 | } 16 | interface VueConstructor { 17 | $axios: AxiosInstance; 18 | $api: Api; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demo/src/frontend/README.md: -------------------------------------------------------------------------------- 1 | # antdv-typescript-starter 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | yarn lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /demo/src/frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 4 | env: { 5 | node: true, 6 | }, 7 | 8 | extends: [ 9 | "plugin:vue/strongly-recommended", 10 | "@vue/typescript/recommended", 11 | "@vue/prettier", 12 | "@vue/prettier/@typescript-eslint", 13 | ], 14 | 15 | parserOptions: { 16 | ecmaVersion: 2020, 17 | }, 18 | 19 | rules: { 20 | "no-console": "off", 21 | "no-debugger": "off", 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /demo/src/frontend/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | import getters from "./getters"; 4 | import { AppState } from "./modules/app"; 5 | import { UserState } from "./modules/user"; 6 | 7 | Vue.use(Vuex); 8 | 9 | export interface RootState { 10 | app: AppState; 11 | user: UserState; 12 | } 13 | // Declare empty store first, dynamically register all modules later. 14 | export default new Vuex.Store({ 15 | getters, 16 | }); 17 | -------------------------------------------------------------------------------- /demo/src/frontend/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | 3 | declare global { 4 | interface Window { 5 | umi_plugin_ant_themeVar: any; 6 | acl: any; 7 | } 8 | namespace JSX { 9 | // tslint:disable no-empty-interface 10 | interface Element extends VNode {} 11 | // tslint:disable no-empty-interface 12 | interface ElementClass extends Vue {} 13 | interface IntrinsicElements { 14 | [elem: string]: any; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/service/acl/ActionService.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.service.acl; 2 | 3 | import org.nutz.spring.boot.service.IdNameBaseService; 4 | import org.springframework.stereotype.Service; 5 | 6 | import tech.riemann.demo.entity.acl.Action; 7 | 8 | /** 9 | *

10 | * 功能动作 服务实现类 11 | *

12 | * 13 | * @author Kerbores 14 | * @since 2021-08-12 15 | */ 16 | @Service 17 | public class ActionService extends IdNameBaseService { 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | org.nutz.spring.boot.dao.NutzDaoAutoConfiguration 2 | org.nutz.spring.boot.dao.NutzDatabaseInitializer 3 | org.nutz.spring.boot.json.NutzJsonMessageConverterAutoConfiguration 4 | org.nutz.spring.boot.filepool.FilePoolAutoConfiguration 5 | org.nutz.spring.boot.ngrok.NgrokAutoConfiguration 6 | org.nutz.spring.boot.request.NutzHttpAutoConfiguration 7 | org.nutz.spring.boot.log.NutzLogAutoConfiguration -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/service/acl/UserRoleService.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.service.acl; 2 | 3 | import org.nutz.spring.boot.service.IdBaseService; 4 | import org.springframework.stereotype.Service; 5 | 6 | import tech.riemann.demo.entity.acl.UserRole; 7 | 8 | /** 9 | *

10 | * 用户角色关系 服务实现类 11 | *

12 | * 13 | * @author Kerbores 14 | * @since 2021-08-12 15 | */ 16 | @Service 17 | public class UserRoleService extends IdBaseService { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /demo/src/main/resources/sqls/acl/role.sql: -------------------------------------------------------------------------------- 1 | /* 2 | list.all.roles.by.user.id 3 | */ 4 | select 5 | r.* 6 | from 7 | t_role r 8 | left join t_user_role ur on 9 | r.id = ur.ur_role_id 10 | where 11 | ur.ur_user_id = @userId 12 | /* 13 | get.role.infos.by.user.id 14 | */ 15 | select 16 | r.*, 17 | ur.id is not null as `selected` 18 | from 19 | t_role r 20 | left join ( 21 | select 22 | * 23 | from 24 | t_user_role 25 | where 26 | ur_user_id = @userId ) ur on 27 | r.id = ur.ur_role_id -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/service/dictionary/GroupService.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.service.dictionary; 2 | 3 | import org.nutz.spring.boot.service.IdBaseService; 4 | import org.springframework.stereotype.Service; 5 | 6 | import tech.riemann.demo.entity.dictionary.Group; 7 | 8 | /** 9 | *

10 | * 码本分组 服务实现类 11 | *

12 | * 13 | * @author Kerbores 14 | * @since 2021-08-12 15 | */ 16 | @Service 17 | public class GroupService extends IdBaseService { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | .project 4 | .classpath 5 | .factorypath 6 | .vscode 7 | # Log file 8 | *.log 9 | 10 | # BlueJ files 11 | *.ctxt 12 | 13 | # Mobile Tools for Java (J2ME) 14 | .mtj.tmp/ 15 | .settings/ 16 | target/ 17 | # Package Files # 18 | *.jar 19 | *.war 20 | *.nar 21 | *.ear 22 | *.zip 23 | *.tar.gz 24 | *.rar 25 | 26 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 27 | hs_err_pid* 28 | node_modules/ 29 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/service/acl/UserPermissionService.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.service.acl; 2 | 3 | import org.nutz.spring.boot.service.IdBaseService; 4 | import org.springframework.stereotype.Service; 5 | 6 | import tech.riemann.demo.entity.acl.UserPermission; 7 | 8 | /** 9 | *

10 | * 用户权限关系 服务实现类 11 | *

12 | * 13 | * @author Kerbores 14 | * @since 2021-08-12 15 | */ 16 | @Service 17 | public class UserPermissionService extends IdBaseService { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/service/acl/RolePermissionService.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.service.acl; 2 | 3 | import org.nutz.spring.boot.service.IdBaseService; 4 | import org.springframework.stereotype.Service; 5 | 6 | import tech.riemann.demo.entity.acl.RolePermission; 7 | 8 | /** 9 | *

10 | * 角色权限关系表 服务实现类 11 | *

12 | * 13 | * @author Kerbores 14 | * @since 2021-08-12 15 | */ 16 | @Service 17 | public class RolePermissionService extends IdBaseService { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/SpringResourceLoactionConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao; 2 | 3 | import org.springframework.boot.autoconfigure.AutoConfiguration; 4 | import org.springframework.context.annotation.Bean; 5 | 6 | /** 7 | * @author wkipy 8 | * 9 | */ 10 | @AutoConfiguration 11 | public class SpringResourceLoactionConfiguration { 12 | 13 | @Bean 14 | public SpringResourceLoaction springResourceLoaction() { 15 | return new SpringResourceLoaction(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/filepool/FilePoolAutoConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.filepool; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | import lombok.Data; 6 | 7 | /** 8 | * @author Kerbores(kerbores@gmail.com) 9 | * 10 | */ 11 | @Data 12 | @ConfigurationProperties("nutz.filepool") 13 | public class FilePoolAutoConfigurationProperties { 14 | 15 | boolean enabled = true; 16 | 17 | String path = "~/.temp"; 18 | 19 | int size = 0; 20 | } 21 | -------------------------------------------------------------------------------- /demo/src/frontend/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/index.ts: -------------------------------------------------------------------------------- 1 | import action, { ActionApi } from './action'; 2 | 3 | import module, { ModuleApi } from './module'; 4 | 5 | import role, { RoleApi } from './role'; 6 | 7 | import user, { UserApi } from './user'; 8 | 9 | export class AclApi { 10 | constructor( 11 | public action: ActionApi, 12 | public module: ModuleApi, 13 | public role: RoleApi, 14 | public user: UserApi, 15 | ) {} 16 | } 17 | 18 | export default { 19 | action, 20 | module, 21 | role, 22 | user, 23 | } as AclApi; 24 | -------------------------------------------------------------------------------- /demo/src/frontend/src/core/directives/auth.ts: -------------------------------------------------------------------------------- 1 | import { check } from "@/utils/auth"; 2 | import { VueConstructor } from "vue"; 3 | 4 | interface Options { 5 | name: string; 6 | } 7 | function install( 8 | Vue: VueConstructor, 9 | options: Options = { name: "auth" } 10 | ): void { 11 | Vue.directive(options.name || "auth", { 12 | inserted(el, binding) { 13 | if (!check(binding.value)) { 14 | el.parentNode && el.parentNode.removeChild(el); 15 | } 16 | }, 17 | }); 18 | } 19 | 20 | export default { install }; 21 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/request/ProgressListener.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.request; 2 | 3 | import org.nutz.lang.util.Callback; 4 | 5 | /** 6 | * @author kerbores 7 | * 8 | */ 9 | public abstract class ProgressListener implements Callback { 10 | 11 | /** 12 | * @param obj 13 | * @see org.nutz.lang.util.Callback#invoke(java.lang.Object) 14 | */ 15 | @Override 16 | public void invoke(Integer obj) { 17 | onProcess(obj); 18 | } 19 | 20 | public abstract void onProcess(int process); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /demo/src/frontend/src/views/user/QRLogin.vue: -------------------------------------------------------------------------------- 1 | 4 | 21 | 22 | -------------------------------------------------------------------------------- /demo/src/frontend/src/plugins/vux-table.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import Vue from "vue"; 3 | import "xe-utils"; 4 | import VXETable from "vxe-table"; 5 | import VXETablePluginAntd from "vxe-table-plugin-antd"; 6 | import "vxe-table/lib/index.css"; 7 | import "vxe-table-plugin-antd/dist/style.css"; 8 | import VXETablePluginExportXLSX from "vxe-table-plugin-export-xlsx"; 9 | 10 | import i18n from "@/locales"; 11 | Vue.use(VXETable, { i18n: (key: any, value: any) => i18n.t(key, value) }); 12 | VXETable.use(VXETablePluginAntd); 13 | VXETable.use(VXETablePluginExportXLSX); 14 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/response/PermissionInfo.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto.response; 2 | 3 | import java.util.List; 4 | 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.NoArgsConstructor; 11 | 12 | /** 13 | * @author Kerbores(kerbores@gmail.com) 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | @EqualsAndHashCode 20 | public class PermissionInfo { 21 | 22 | @Schema(description = "模块信息列表", required = true) 23 | List modules; 24 | } 25 | -------------------------------------------------------------------------------- /demo/src/main/resources/templates/myservice.java.btl: -------------------------------------------------------------------------------- 1 | package ${package.ServiceImpl}; 2 | 3 | import org.nutz.spring.boot.service.IdBaseService; 4 | import org.springframework.stereotype.Service; 5 | 6 | import ${package.Entity}.${entity}; 7 | 8 | /** 9 | *

10 | * ${table.comment!} 服务实现类 11 | *

12 | * 13 | * @author ${author} 14 | * @since ${date} 15 | */ 16 | @Service 17 | <% if(kotlin){ %> 18 | open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} { 19 | 20 | } 21 | <% }else{ %> 22 | public class ${table.serviceImplName} extends IdBaseService<${entity}> { 23 | 24 | } 25 | <% } %> 26 | -------------------------------------------------------------------------------- /demo/src/frontend/src/layouts/RouteView.vue: -------------------------------------------------------------------------------- 1 | 7 | 24 | 25 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/SpringBootNutzDemoApplication.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.ApplicationPidFileWriter; 6 | 7 | @SpringBootApplication 8 | public class SpringBootNutzDemoApplication { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication springApplication = new SpringApplication(SpringBootNutzDemoApplication.class); 12 | springApplication.addListeners(new ApplicationPidFileWriter()); 13 | springApplication.run(args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/response/RoleInfo.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto.response; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import tech.riemann.demo.entity.acl.Role; 9 | 10 | /** 11 | * @author Kerbores(kerbores@gmail.com) 12 | */ 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @Schema(name = "RoleInfo", description = "角色信息") 16 | public class RoleInfo extends Role { 17 | private static final long serialVersionUID = 1L; 18 | @Column("selected") 19 | @Schema(description = "是否选中标识") 20 | boolean selected; 21 | } 22 | -------------------------------------------------------------------------------- /demo/src/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/request/SocialLoginBindDTO.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto.request; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import tech.riemann.demo.entity.acl.LoginChannel.Channel; 6 | 7 | /** 8 | * @author wkipy 9 | * 10 | */ 11 | @Data 12 | @Schema(description = "社会化登录用户绑定数据对象") 13 | public class SocialLoginBindDTO { 14 | 15 | @Schema(description = "手机号", required = true) 16 | String mobile; 17 | @Schema(description = "验证码", required = true) 18 | String code; 19 | @Schema(description = "openid", required = true) 20 | String openid; 21 | @Schema(description = "渠道", required = true) 22 | Channel channel; 23 | } 24 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/login/mods/auth/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @description 登录认证模块 4 | */ 5 | import login, { LoginParams } from './login'; 6 | 7 | export class AuthApi { 8 | constructor( 9 | public login: ( 10 | params: LoginParams, 11 | success?: ({ 12 | data, 13 | ext, 14 | state, 15 | errors, 16 | }: { 17 | data: login.AuthUser; 18 | ext: ObjectMap; 19 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 20 | errors?: Array; 21 | }) => any, 22 | fail?: (error: string) => any, 23 | ) => void, 24 | ) {} 25 | } 26 | 27 | export default { 28 | login, 29 | } as AuthApi; 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/SelectLang/index.less: -------------------------------------------------------------------------------- 1 | @import "~ant-design-vue/es/style/themes/default"; 2 | 3 | @header-menu-prefix-cls: ~'@{ant-prefix}-pro-header-menu'; 4 | @header-drop-down-prefix-cls: ~'@{ant-prefix}-pro-drop-down'; 5 | 6 | .@{header-menu-prefix-cls} { 7 | 8 | .anticon { 9 | margin-right: 8px; 10 | } 11 | .ant-dropdown-menu-item { 12 | min-width: 160px; 13 | } 14 | } 15 | 16 | .@{header-drop-down-prefix-cls} { 17 | 18 | line-height: @layout-header-height; 19 | vertical-align: top; 20 | cursor: pointer; 21 | 22 | > i { 23 | font-size: 16px !important; 24 | transform: none !important; 25 | 26 | svg { 27 | position: relative; 28 | top: -1px; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/controller/wechat/DemoController.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.controller.wechat; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | import club.zhcs.Result; 7 | import tech.riemann.demo.dto.A; 8 | import tech.riemann.demo.dto.B; 9 | 10 | /** 11 | * @author wkipy 12 | * 13 | */ 14 | @RestController 15 | public class DemoController { 16 | 17 | @GetMapping("demo1") 18 | public Result> demo1() { 19 | return Result.success(); 20 | } 21 | 22 | @GetMapping("demo2") 23 | public Result> demo2() { 24 | return Result.success(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /demo/src/frontend/src/core/filters/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import moment from "moment"; 3 | import "moment/locale/zh-cn"; 4 | moment.locale("zh-cn"); 5 | 6 | Vue.filter("NumberFormat", function (value: number) { 7 | if (!value) { 8 | return "0"; 9 | } 10 | // 将整数部分逢三一段添加千分位 11 | return value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, "$1,"); 12 | }); 13 | 14 | Vue.filter("dayjs", function (dataStr: string, pattern?: string) { 15 | pattern = pattern ? pattern : "YYYY-MM-DD HH:mm:ss"; 16 | return moment(dataStr).format(pattern); 17 | }); 18 | 19 | Vue.filter("moment", function (dataStr: string, pattern?: string) { 20 | pattern = pattern ? pattern : "YYYY-MM-DD HH:mm:ss"; 21 | return moment(dataStr).format(pattern); 22 | }); 23 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/GlobalFooter/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/entity/IdEntity.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service.entity; 2 | 3 | import org.nutz.dao.entity.annotation.Id; 4 | 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.NoArgsConstructor; 9 | import lombok.experimental.SuperBuilder; 10 | 11 | /** 12 | * @author kerbores(kerbores@gmail.com) 13 | * 14 | */ 15 | @Data 16 | @SuperBuilder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | @EqualsAndHashCode(callSuper = false, of = "id") 20 | public class IdEntity extends Entity { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public static final String ID_FIELD = "id"; 25 | 26 | @Id 27 | private long id; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/role/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取角色详情 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.Role; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/role/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取用户详情 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.User; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/user/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/remove.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 删除用户 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'delete', 25 | url: `/user/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/role/remove.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 删除指定角色 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'delete', 25 | url: `/role/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/module/remove.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id删除系统功能 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'delete', 25 | url: `/module/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/store/getters.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from "@/store/index"; 2 | import config from "@/core/config"; 3 | 4 | const getters = { 5 | sidebar: (state: RootState): boolean => state.app.fixedSidebar, 6 | // device: (state: IRootState) => state.app.device, 7 | fixSiderbar: (state: RootState): boolean => state.app.fixedSidebar, 8 | layoutMode: (state: RootState): string => state.app.layout, 9 | navTheme: (state: RootState): string => state.app.theme, 10 | language: (state: RootState): string => state.app.lang || config.language, 11 | primaryColor: (state: RootState): string => state.app.color, 12 | colorWeak: (state: RootState): boolean => state.app.weak, 13 | token: (state: RootState): string => state.user.token, 14 | }; 15 | 16 | export default getters; 17 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/action/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id获取功能动作详情 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.Action; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/action/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/action/remove.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id获取功能动作详情 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'delete', 25 | url: `/action/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/module/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id查询系统功能详情 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.Module; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/module/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/codebook/remove.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 禁用指定字典项 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'delete', 25 | url: `/code/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/group/remove.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 删除字典分组 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'delete', 25 | url: `/group/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/role/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 创建角色 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | role: acl.Role, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.Role; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'post', 25 | url: `/role`, 26 | data: role, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/role/edit.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id更新角色 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | role: acl.Role, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'put', 25 | url: `/role`, 26 | data: role, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/edit.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id更新用户 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | user: acl.User, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'put', 25 | url: `/user`, 26 | data: user, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/group/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取指定字典分组 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: dictionary.Group; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/group/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 创建用户 4 | */ 5 | import { defaultSuccess, defaultError, http } from "@/plugins/axios"; 6 | 7 | export default async function ( 8 | user: acl.User, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.User; 17 | ext: ObjectMap; 18 | state: "SUCCESS" | "FAIL" | "EXCEPTION"; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError 22 | ): Promise { 23 | return http({ 24 | method: "post", 25 | url: `/user`, 26 | data: user, 27 | }) 28 | .then((data) => success(data as any)) 29 | .catch((error) => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/codebook/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取指定字典项 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: dictionary.Codebook; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/code/${id}`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/action/edit.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id更新功能动作 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | action: acl.Action, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'put', 25 | url: `/action`, 26 | data: action, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/module/edit.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id更新系统功能 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | module: acl.Module, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'put', 25 | url: `/module`, 26 | data: module, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/roles.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取指定用户的角色授权 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: Array; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/user/${id}/grant/role`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/utils/util.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | setDocumentTitle(title: string): void { 3 | document.title = title; 4 | const ua = navigator.userAgent; 5 | const regex = /\bMicroMessenger\/([\d.]+)/; 6 | if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) { 7 | const i = document.createElement("iframe"); 8 | i.src = "/favicon.ico"; 9 | i.style.display = "none"; 10 | i.onload = function () { 11 | setTimeout(function () { 12 | i.remove(); 13 | }, 9); 14 | }; 15 | document.body.appendChild(i); 16 | } 17 | }, 18 | triggerWindowResizeEvent(): void { 19 | const event: Event = document.createEvent("HTMLEvents"); 20 | event.initEvent("resize", true, true); 21 | window.dispatchEvent(event); 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/action/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 新增功能动作 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | action: acl.Action, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.Action; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'post', 25 | url: `/action`, 26 | data: action, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/module/actions.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 查询指定系统功能下的所有功能动作 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: Array; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/module/${id}/actions`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/module/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 创建系统功能 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | module: acl.Module, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.Module; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'post', 25 | url: `/module`, 26 | data: module, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/role/permissions.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取指定角色的授权情况 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.PermissionInfo; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/role/${id}/permissions`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/permissions.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取指定用户的授权情况 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: acl.PermissionInfo; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/user/${id}/permissions`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/group/edit.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id更新字典分组 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | group: dictionary.Group, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'put', 25 | url: `/group`, 26 | data: group, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/config/wechat/WechatOauthConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.config.wechat; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | import lombok.Data; 6 | 7 | /** 8 | * @author wkipy 9 | * 10 | */ 11 | @Data 12 | @ConfigurationProperties("wechat.oauth") 13 | public class WechatOauthConfigurationProperties { 14 | 15 | Client mp = new Client(); 16 | 17 | Client pc = new Client(); 18 | 19 | @Data 20 | public class Client { 21 | String domain; 22 | boolean ssl = false; 23 | String router = "/user/login"; 24 | 25 | public String getUrl() { 26 | return String.format("%s://%s/index.html#%s", ssl ? "https" : "http", domain, router); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/codebook/byGroup.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取指定分组下的全部字典 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: Array; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'get', 25 | url: `/group/${id}/codes`, 26 | }) 27 | .then(data => success(data as any)) 28 | .catch(error => fail(error)); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/codebook/edit.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id更新字典项 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | codebook: dictionary.Codebook, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'put', 25 | url: `/code`, 26 | data: codebook, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/group/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 新增字典分组 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | group: dictionary.Group, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: dictionary.Group; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'post', 25 | url: `/group`, 26 | data: group, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/codebook/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 新增字典项 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | codebook: dictionary.Codebook, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: dictionary.Codebook; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'post', 25 | url: `/code`, 26 | data: codebook, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/config/apm/ApmConfiguration.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.config.apm; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import club.zhcs.apm.URLProvider; 9 | import club.zhcs.apm.UserCollector; 10 | import club.zhcs.auth.AuthService; 11 | 12 | /** 13 | * 14 | * @author kerbores(kerbores@gmail.com) 15 | */ 16 | @Configuration 17 | public class ApmConfiguration { 18 | 19 | @Bean 20 | public UserCollector userCollector(AuthService authService) { 21 | return authService::userName; 22 | } 23 | 24 | @Bean 25 | public URLProvider urlProvider(HttpServletRequest request) { 26 | return request::getRequestURI; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/role/grant.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 为指定角色授权 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | actionInfos: Array, 10 | 11 | success: ({ 12 | data, 13 | ext, 14 | state, 15 | errors, 16 | }: { 17 | data: void; 18 | ext: ObjectMap; 19 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 20 | errors?: Array; 21 | }) => void = defaultSuccess, 22 | fail: (error: string) => void = defaultError, 23 | ): Promise { 24 | return http({ 25 | method: 'post', 26 | url: `/role/${id}/grant`, 27 | data: actionInfos, 28 | }) 29 | .then(data => success(data as any)) 30 | .catch(error => fail(error)); 31 | } 32 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/grant.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 为指定用户授权 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | actionInfos: Array, 10 | 11 | success: ({ 12 | data, 13 | ext, 14 | state, 15 | errors, 16 | }: { 17 | data: void; 18 | ext: ObjectMap; 19 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 20 | errors?: Array; 21 | }) => void = defaultSuccess, 22 | fail: (error: string) => void = defaultError, 23 | ): Promise { 24 | return http({ 25 | method: 'post', 26 | url: `/user/${id}/grant`, 27 | data: actionInfos, 28 | }) 29 | .then(data => success(data as any)) 30 | .catch(error => fail(error)); 31 | } 32 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/grantRoles.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 为指定用户设置角色 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | id: number, 9 | roles: Array, 10 | 11 | success: ({ 12 | data, 13 | ext, 14 | state, 15 | errors, 16 | }: { 17 | data: void; 18 | ext: ObjectMap; 19 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 20 | errors?: Array; 21 | }) => void = defaultSuccess, 22 | fail: (error: string) => void = defaultError, 23 | ): Promise { 24 | return http({ 25 | method: 'post', 26 | url: `/user/${id}/grant/role`, 27 | data: roles, 28 | }) 29 | .then(data => success(data as any)) 30 | .catch(error => fail(error)); 31 | } 32 | -------------------------------------------------------------------------------- /demo/src/frontend/src/views/dictionary/Dictionary.vue: -------------------------------------------------------------------------------- 1 | 13 | 26 | 27 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/codebook/vxeSave.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 保存指定分组下的字典项变更情况 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | vxeData: dictionary.VXETableSaveDTO, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: void; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'post', 25 | url: `/code/vxe/save`, 26 | data: vxeData, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/wechat/mods/socialLogin/bind.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 绑定用户 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export default async function( 8 | socialLoginBindDTO: wechat.TieSocialLoginUserDataObject, 9 | 10 | success: ({ 11 | data, 12 | ext, 13 | state, 14 | errors, 15 | }: { 16 | data: string; 17 | ext: ObjectMap; 18 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 19 | errors?: Array; 20 | }) => void = defaultSuccess, 21 | fail: (error: string) => void = defaultError, 22 | ): Promise { 23 | return http({ 24 | method: 'post', 25 | url: `/social/login/bind`, 26 | data: socialLoginBindDTO, 27 | }) 28 | .then(data => success(data as any)) 29 | .catch(error => fail(error)); 30 | } 31 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/SettingDrawer/SettingItem.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 30 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/entity/Entity.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | import org.nutz.json.Json; 6 | import org.nutz.json.JsonFormat; 7 | 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | import lombok.experimental.SuperBuilder; 11 | 12 | /** 13 | * @author kerbores(kerbores@gmail.com) 14 | * 15 | */ 16 | @Data 17 | @SuperBuilder 18 | @NoArgsConstructor 19 | public class Entity implements Serializable { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | public T exchange(Class clazz) { 24 | return Json.fromJson(clazz, Json.toJson(this, JsonFormat.compact().ignoreJsonShape())); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return Json.toJson(this, JsonFormat.nice()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import { PluginObject } from "vue"; 2 | 3 | import { default as aclApi, AclApi } from "./acl/mods"; 4 | 5 | import { default as dictionaryApi, DictionaryApi } from "./dictionary/mods"; 6 | 7 | import { default as loginApi, LoginApi } from "./login/mods"; 8 | 9 | import { default as wechatApi, WechatApi } from "./wechat/mods"; 10 | 11 | export interface Api { 12 | aclApi: AclApi; 13 | dictionaryApi: DictionaryApi; 14 | loginApi: LoginApi; 15 | wechatApi: WechatApi; 16 | } 17 | 18 | export const ApiPlugin: PluginObject = { 19 | install: (Vue) => { 20 | Object.defineProperties(Vue.prototype, { 21 | $api: { 22 | get() { 23 | return { 24 | aclApi, 25 | dictionaryApi, 26 | loginApi, 27 | wechatApi, 28 | }; 29 | }, 30 | }, 31 | }); 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /demo/src/frontend/src/store/mutation-types.ts: -------------------------------------------------------------------------------- 1 | export const ACCESS_TOKEN = "Access-Token"; 2 | 3 | export const SIDEBAR_TYPE = "sidebar_type"; 4 | export const TOGGLE_MOBILE_TYPE = "is_mobile"; 5 | export const TOGGLE_NAV_THEME = "nav_theme"; 6 | export const TOGGLE_LAYOUT = "layout"; 7 | export const TOGGLE_FIXED_HEADER = "fixed_header"; 8 | export const TOGGLE_FIXED_SIDEBAR = "fixed_sidebar"; 9 | export const TOGGLE_CONTENT_WIDTH = "content_width"; 10 | export const TOGGLE_HIDE_HEADER = "auto_hide_header"; 11 | export const TOGGLE_COLOR = "color"; 12 | export const TOGGLE_WEAK = "weak"; 13 | export const TOGGLE_MULTI_TAB = "multi_tab"; 14 | export const APP_LANGUAGE = "app_language"; 15 | export const LOGIN_USER = "user"; 16 | export const CONTENT_WIDTH_TYPE = { 17 | Fluid: "Fluid", 18 | Fixed: "Fixed", 19 | }; 20 | 21 | export const NAV_THEME = { 22 | LIGHT: "light", 23 | DARK: "dark", 24 | }; 25 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/response/ActionInfo.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto.response; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Builder.Default; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * @author Kerbores(kerbores@gmail.com) 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @EqualsAndHashCode 19 | public class ActionInfo { 20 | 21 | @Schema(description = "操作key", required = true) 22 | String key; 23 | 24 | @Schema(description = "操作名称", required = true) 25 | String name; 26 | 27 | @Schema(description = "操作id", required = true) 28 | long id; 29 | 30 | @Default 31 | @Schema(description = "是否选中标识") 32 | boolean selected = true; 33 | } 34 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/login/mods/auth/login.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 用户登录 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class LoginParams { 8 | constructor(public password: string, public user: string) {} 9 | } 10 | 11 | export default async function( 12 | params: LoginParams, 13 | success: ({ 14 | data, 15 | ext, 16 | state, 17 | errors, 18 | }: { 19 | data: login.AuthUser; 20 | ext: ObjectMap; 21 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 22 | errors?: Array; 23 | }) => void = defaultSuccess, 24 | fail: (error: string) => void = defaultError, 25 | ): Promise { 26 | return http({ 27 | method: 'post', 28 | url: `/auth/login`, 29 | 30 | params, 31 | }) 32 | .then(data => success(data as any)) 33 | .catch(error => fail(error)); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/SpringDaoRunner.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao; 2 | 3 | import java.sql.Connection; 4 | 5 | import javax.sql.DataSource; 6 | 7 | import org.nutz.dao.ConnCallback; 8 | import org.nutz.dao.impl.sql.run.NutDaoRunner; 9 | import org.nutz.lang.Lang; 10 | import org.springframework.jdbc.datasource.DataSourceUtils; 11 | 12 | /** 13 | * @author kerbores 14 | * 15 | */ 16 | public class SpringDaoRunner extends NutDaoRunner { 17 | 18 | @Override 19 | public void _run(DataSource dataSource, ConnCallback callback) { 20 | 21 | Connection con = DataSourceUtils.getConnection(dataSource); 22 | try { 23 | callback.invoke(con); 24 | } 25 | catch (Exception e) { 26 | throw Lang.wrapThrow(e); 27 | } 28 | finally { 29 | DataSourceUtils.releaseConnection(con, dataSource); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/IdBaseService.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service; 2 | 3 | import javax.annotation.Resource; 4 | 5 | import org.nutz.dao.Dao; 6 | import org.nutz.log.Log; 7 | import org.nutz.log.Logs; 8 | import org.nutz.service.IdEntityService; 9 | import org.nutz.spring.boot.service.entity.IdEntity; 10 | 11 | /** 12 | * @author kerbores(kerbores@gmail.com) 13 | * 14 | */ 15 | public class IdBaseService extends IdEntityService implements ExtService { 16 | 17 | protected Log logger = Logs.get(); 18 | 19 | @Resource(type = Dao.class) 20 | public void init(Dao dao) { 21 | super.setDao(dao); 22 | } 23 | 24 | /** 25 | * @return 26 | * @see org.nutz.spring.boot.service.ExtService#getEntityType() 27 | */ 28 | @Override 29 | public Class getEntityType() { 30 | return getEntityClass(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/NameBaseService.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service; 2 | 3 | import javax.annotation.Resource; 4 | 5 | import org.nutz.dao.Dao; 6 | import org.nutz.log.Log; 7 | import org.nutz.log.Logs; 8 | import org.nutz.service.NameEntityService; 9 | import org.nutz.spring.boot.service.entity.Entity; 10 | 11 | /** 12 | * @author kerbores(kerbores@gmail.com) 13 | * 14 | */ 15 | public class NameBaseService extends NameEntityService implements ExtService { 16 | 17 | protected Log logger = Logs.get(); 18 | 19 | @Resource(type = Dao.class) 20 | public void init(Dao dao) { 21 | super.setDao(dao); 22 | } 23 | 24 | /** 25 | * @return 26 | * @see org.nutz.spring.boot.service.ExtService#getEntityType() 27 | */ 28 | @Override 29 | public Class getEntityType() { 30 | return getEntityClass(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/IdNameBaseService.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service; 2 | 3 | import javax.annotation.Resource; 4 | 5 | import org.nutz.dao.Dao; 6 | import org.nutz.log.Log; 7 | import org.nutz.log.Logs; 8 | import org.nutz.service.IdNameEntityService; 9 | import org.nutz.spring.boot.service.entity.Entity; 10 | 11 | /** 12 | * @author kerbores(kerbores@gmail.com) 13 | * 14 | */ 15 | public class IdNameBaseService extends IdNameEntityService implements ExtService { 16 | 17 | protected Log logger = Logs.get(); 18 | 19 | @Resource(type = Dao.class) 20 | public void init(Dao dao) { 21 | super.setDao(dao); 22 | } 23 | 24 | /** 25 | * @return 26 | * @see org.nutz.spring.boot.service.ExtService#getEntityType() 27 | */ 28 | @Override 29 | public Class getEntityType() { 30 | return getEntityClass(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /demo/src/frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules", 39 | "serviceTemplate.ts" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/request/VXETableSaveDTO.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto.request; 2 | 3 | import java.util.List; 4 | 5 | import org.nutz.lang.Lang; 6 | 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Builder.Default; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | /** 15 | * @author Kerbores(kerbores@gmail.com) 16 | */ 17 | @Data 18 | @Builder 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | public class VXETableSaveDTO { 22 | @Default 23 | @Schema(description = "新增记录数据", required = true) 24 | List insertRecords = Lang.list(); 25 | 26 | @Default 27 | @Schema(description = "删除记录数据", required = true) 28 | List removeRecords = Lang.list(); 29 | 30 | @Default 31 | @Schema(description = "更新记录数据", required = true) 32 | List updateRecords = Lang.list(); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/filepool/FilePoolAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.filepool; 2 | 3 | import org.nutz.filepool.FilePool; 4 | import org.nutz.filepool.NutFilePool; 5 | import org.springframework.boot.autoconfigure.AutoConfiguration; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 7 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | /** 11 | * @author Kerbores(kerbores@gmail.com) 12 | * 13 | */ 14 | @AutoConfiguration 15 | @EnableConfigurationProperties(FilePoolAutoConfigurationProperties.class) 16 | public class FilePoolAutoConfiguration { 17 | 18 | @Bean 19 | @ConditionalOnExpression("${nutz.filepool.enabled:true}") 20 | public FilePool filePool(FilePoolAutoConfigurationProperties config) { 21 | return new NutFilePool(config.getPath(), config.getSize()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/role/grantInfo.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id获取指定角色下指定模块下的授权情况 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class GrantInfoParams { 8 | constructor(public mids?: Array) {} 9 | } 10 | 11 | export default async function( 12 | id: number, 13 | 14 | params: GrantInfoParams, 15 | success: ({ 16 | data, 17 | ext, 18 | state, 19 | errors, 20 | }: { 21 | data: Array; 22 | ext: ObjectMap; 23 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 24 | errors?: Array; 25 | }) => void = defaultSuccess, 26 | fail: (error: string) => void = defaultError, 27 | ): Promise { 28 | return http({ 29 | method: 'get', 30 | url: `/role/${id}/grant/info`, 31 | 32 | params, 33 | }) 34 | .then(data => success(data as any)) 35 | .catch(error => fail(error)); 36 | } 37 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/grantInfo.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 根据id获取指定用户在指定模块下的授权情况 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class GrantInfoParams { 8 | constructor(public mids?: Array) {} 9 | } 10 | 11 | export default async function( 12 | id: number, 13 | 14 | params: GrantInfoParams, 15 | success: ({ 16 | data, 17 | ext, 18 | state, 19 | errors, 20 | }: { 21 | data: Array; 22 | ext: ObjectMap; 23 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 24 | errors?: Array; 25 | }) => void = defaultSuccess, 26 | fail: (error: string) => void = defaultError, 27 | ): Promise { 28 | return http({ 29 | method: 'get', 30 | url: `/user/${id}/grant/info`, 31 | 32 | params, 33 | }) 34 | .then(data => success(data as any)) 35 | .catch(error => fail(error)); 36 | } 37 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/service/acl/ModuleService.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.service.acl; 2 | 3 | import java.util.List; 4 | 5 | import org.nutz.dao.Cnd; 6 | import org.nutz.spring.boot.service.IdNameBaseService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | import tech.riemann.demo.entity.acl.Action; 11 | import tech.riemann.demo.entity.acl.Module; 12 | 13 | /** 14 | *

15 | * 功能模块 服务实现类 16 | *

17 | * 18 | * @author Kerbores 19 | * @since 2021-08-12 20 | */ 21 | @Service 22 | public class ModuleService extends IdNameBaseService { 23 | 24 | @Autowired 25 | ActionService actionService; 26 | 27 | /** 28 | * @param id 29 | * @return 30 | */ 31 | public List actions(long id) { 32 | return actionService.query(Cnd.where(Action.Fields.moduleId, "=", id).or(Action.Fields.moduleId, "=", 0)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/role/search.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 分页查询角色 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class SearchParams { 8 | constructor( 9 | public key?: string, 10 | public page?: number, 11 | public size?: number, 12 | ) {} 13 | } 14 | 15 | export default async function( 16 | params: SearchParams, 17 | success: ({ 18 | data, 19 | ext, 20 | state, 21 | errors, 22 | }: { 23 | data: acl.Pagination; 24 | ext: ObjectMap; 25 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 26 | errors?: Array; 27 | }) => void = defaultSuccess, 28 | fail: (error: string) => void = defaultError, 29 | ): Promise { 30 | return http({ 31 | method: 'get', 32 | url: `/roles`, 33 | 34 | params, 35 | }) 36 | .then(data => success(data as any)) 37 | .catch(error => fail(error)); 38 | } 39 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/user/search.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 分页查询用户 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class SearchParams { 8 | constructor( 9 | public key?: string, 10 | public page?: number, 11 | public size?: number, 12 | ) {} 13 | } 14 | 15 | export default async function( 16 | params: SearchParams, 17 | success: ({ 18 | data, 19 | ext, 20 | state, 21 | errors, 22 | }: { 23 | data: acl.Pagination; 24 | ext: ObjectMap; 25 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 26 | errors?: Array; 27 | }) => void = defaultSuccess, 28 | fail: (error: string) => void = defaultError, 29 | ): Promise { 30 | return http({ 31 | method: 'get', 32 | url: `/users`, 33 | 34 | params, 35 | }) 36 | .then(data => success(data as any)) 37 | .catch(error => fail(error)); 38 | } 39 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/module/search.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 分页查询系统功能 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class SearchParams { 8 | constructor( 9 | public key?: string, 10 | public page?: number, 11 | public size?: number, 12 | ) {} 13 | } 14 | 15 | export default async function( 16 | params: SearchParams, 17 | success: ({ 18 | data, 19 | ext, 20 | state, 21 | errors, 22 | }: { 23 | data: acl.Pagination; 24 | ext: ObjectMap; 25 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 26 | errors?: Array; 27 | }) => void = defaultSuccess, 28 | fail: (error: string) => void = defaultError, 29 | ): Promise { 30 | return http({ 31 | method: 'get', 32 | url: `/modules`, 33 | 34 | params, 35 | }) 36 | .then(data => success(data as any)) 37 | .catch(error => fail(error)); 38 | } 39 | -------------------------------------------------------------------------------- /demo/src/frontend/src/utils/WxLogin.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export function WxLogin(a: wechat.WxLogin): void { 3 | let c = "default"; 4 | a.self_redirect === !0 5 | ? (c = "true") 6 | : a.self_redirect === !1 && (c = "false"); 7 | const d = document.createElement("iframe"); 8 | let e = 9 | "https://open.weixin.qq.com/connect/qrconnect?appid=" + 10 | a.appid + 11 | "&scope=" + 12 | a.scope + 13 | "&redirect_uri=" + 14 | a.redirect_uri + 15 | "&state=" + 16 | a.state + 17 | "&login_type=jssdk&self_redirect=" + 18 | c; 19 | e += a.style ? "&style=" + a.style : ""; 20 | e += a.href ? "&href=" + a.href : ""; 21 | (d.src = e), (d.frameBorder = "0"); 22 | (d as any).allowTransparency = "true"; 23 | d.scrolling = "no"; 24 | d.width = "300px"; 25 | d.height = "400px"; 26 | const f = document.getElementById(a.id); 27 | (f as HTMLElement).innerHTML = ""; 28 | f && f.appendChild(d); 29 | } 30 | -------------------------------------------------------------------------------- /demo/src/frontend/src/utils/device.ts: -------------------------------------------------------------------------------- 1 | import enquireJs from "enquire.js"; 2 | 3 | export enum DEVICE_TYPE { 4 | DESKTOP = "desktop", 5 | TABLET = "tablet", 6 | MOBILE = "mobile", 7 | } 8 | 9 | export const deviceEnquire = function (callback: (type: string) => void): void { 10 | const matchDesktop = { 11 | match: () => { 12 | callback && callback(DEVICE_TYPE.DESKTOP); 13 | }, 14 | }; 15 | 16 | const matchLablet = { 17 | match: () => { 18 | callback && callback(DEVICE_TYPE.TABLET); 19 | }, 20 | }; 21 | 22 | const matchMobile = { 23 | match: () => { 24 | callback && callback(DEVICE_TYPE.MOBILE); 25 | }, 26 | }; 27 | 28 | // screen and (max-width: 1087.99px) 29 | enquireJs 30 | .register("screen and (max-width: 576px)", matchMobile) 31 | .register( 32 | "screen and (min-width: 576px) and (max-width: 1199px)", 33 | matchLablet 34 | ) 35 | .register("screen and (min-width: 1200px)", matchDesktop); 36 | }; 37 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/wechat/mods/socialLogin/authUrl.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取网页授权地址(H5端,公众号),H5端通过此接口获取到授权地址,然后直接location跳转 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class AuthUrlParams { 8 | constructor( 9 | public state: string, 10 | public scope?: 'snsapi_base' | 'snsapi_userinfo', 11 | ) {} 12 | } 13 | 14 | export default async function( 15 | params: AuthUrlParams, 16 | success: ({ 17 | data, 18 | ext, 19 | state, 20 | errors, 21 | }: { 22 | data: string; 23 | ext: ObjectMap; 24 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 25 | errors?: Array; 26 | }) => void = defaultSuccess, 27 | fail: (error: string) => void = defaultError, 28 | ): Promise { 29 | return http({ 30 | method: 'get', 31 | url: `/social/login/auth-url`, 32 | 33 | params, 34 | }) 35 | .then(data => success(data as any)) 36 | .catch(error => fail(error)); 37 | } 38 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/dictionary/mods/group/search.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 分页加载字典分组 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class SearchParams { 8 | constructor( 9 | public key?: string, 10 | public page?: number, 11 | public size?: number, 12 | public state?: boolean, 13 | ) {} 14 | } 15 | 16 | export default async function( 17 | params: SearchParams, 18 | success: ({ 19 | data, 20 | ext, 21 | state, 22 | errors, 23 | }: { 24 | data: dictionary.Pagination; 25 | ext: ObjectMap; 26 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 27 | errors?: Array; 28 | }) => void = defaultSuccess, 29 | fail: (error: string) => void = defaultError, 30 | ): Promise { 31 | return http({ 32 | method: 'get', 33 | url: `/groups`, 34 | 35 | params, 36 | }) 37 | .then(data => success(data as any)) 38 | .catch(error => fail(error)); 39 | } 40 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/wechat/mods/socialLogin/oauth.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 用code进行oauth登录,各端根据前置操作获取到code调用此接口,如果已经绑定,返回token,如果没有绑定则返回openid 4 | */ 5 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 6 | 7 | export class OauthParams { 8 | constructor(public code: string) {} 9 | } 10 | 11 | export default async function( 12 | channel: 'MP' | 'MINIAPP' | 'WECHAT_SCAN' | 'WECHAT', 13 | 14 | params: OauthParams, 15 | success: ({ 16 | data, 17 | ext, 18 | state, 19 | errors, 20 | }: { 21 | data: string; 22 | ext: ObjectMap; 23 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 24 | errors?: Array; 25 | }) => void = defaultSuccess, 26 | fail: (error: string) => void = defaultError, 27 | ): Promise { 28 | return http({ 29 | method: 'get', 30 | url: `/social/login/${channel}/oauth`, 31 | 32 | params, 33 | }) 34 | .then(data => success(data as any)) 35 | .catch(error => fail(error)); 36 | } 37 | -------------------------------------------------------------------------------- /demo/src/frontend/src/layouts/BasicLayout.less: -------------------------------------------------------------------------------- 1 | @import '~@/var.less'; 2 | .ant-pro-sider-menu-logo{ 3 | background-color: @layout-header-background; 4 | } 5 | .ant-menu-dark .ant-menu-inline.ant-menu-sub{ 6 | background-color: @layout-header-background; 7 | } 8 | .ant-pro-global-header-index-right { 9 | margin-right: 8px; 10 | 11 | &.ant-pro-global-header-index-dark { 12 | .ant-pro-global-header-index-action { 13 | color: hsla(0, 0%, 100%, .85); 14 | 15 | &:hover { 16 | background: @primary-color; 17 | } 18 | } 19 | } 20 | 21 | .ant-pro-account-avatar { 22 | .antd-pro-global-header-index-avatar { 23 | margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0; 24 | margin-right: 8px; 25 | color: #ff0000; 26 | vertical-align: top; 27 | background: rgba(255, 255, 255, 0.85); 28 | } 29 | } 30 | 31 | .menu { 32 | .anticon { 33 | margin-right: 8px; 34 | } 35 | 36 | .ant-dropdown-menu-item { 37 | min-width: 100px; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/controller/wechat/WechatController.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.controller.wechat; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import io.swagger.v3.oas.annotations.Hidden; 12 | import me.chanjar.weixin.mp.api.WxMpService; 13 | 14 | /** 15 | * @author wkipy 16 | * 17 | */ 18 | @RestController 19 | public class WechatController { 20 | 21 | @Autowired 22 | WxMpService wxMpService; 23 | 24 | @Autowired 25 | HttpServletResponse response; 26 | 27 | @GetMapping("wechat") 28 | @Hidden 29 | public void check(String timestamp, String signature, String nonce, String echostr) throws IOException { 30 | if (wxMpService.checkSignature(timestamp, nonce, signature)) { 31 | response.getWriter().write(echostr); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/response/WxLogin.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto.response; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Builder.Default; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * @author wkipy 12 | * 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class WxLogin { 19 | @Default 20 | @Schema(required = true) 21 | boolean self_redirect = true; 22 | 23 | @Schema(required = true) 24 | String id; 25 | 26 | @Schema(required = true) 27 | String appid; 28 | 29 | @Default 30 | @Schema(required = true) 31 | String scope = "snsapi_login"; 32 | 33 | @Schema(required = true) 34 | String redirect_uri; 35 | 36 | @Schema(required = true) 37 | String state; 38 | 39 | @Default 40 | Style style = Style.black; 41 | 42 | String href; 43 | 44 | public enum Style { 45 | black, white 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/config/AdministratorConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Builder.Default; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * @author kerbores(kerbores@gmail.com) 13 | */ 14 | @Data 15 | @ConfigurationProperties("nutz.demo.acl") 16 | public class AdministratorConfigurationProperties { 17 | 18 | String userName = "kerbores"; 19 | 20 | String cypher = "G00dl^ck"; 21 | 22 | String mobile = "18996359755"; 23 | 24 | Role admin = Role.builder().build(); 25 | 26 | @Data 27 | @Builder 28 | @NoArgsConstructor 29 | @AllArgsConstructor 30 | public static class Role { 31 | @Default 32 | String key = "admin"; 33 | @Default 34 | String name = "超级管理员"; 35 | @Default 36 | String description = "超级管理员,创世角色,一切均在掌控之中"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /demo/src/frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 32 | 46 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/sqltpl/NutSqlTpl.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao.sqltpl; 2 | 3 | import org.nutz.dao.impl.sql.NutSql; 4 | import org.nutz.dao.jdbc.ValueAdaptor; 5 | 6 | /** 7 | * @author kerbores 8 | * 9 | */ 10 | public abstract class NutSqlTpl extends NutSql { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | protected boolean renderComplete; 15 | 16 | protected NutSqlTpl(String source) { 17 | super(source); 18 | } 19 | 20 | @Override 21 | public ValueAdaptor[] getAdaptors() { 22 | checkRender(); 23 | return super.getAdaptors(); 24 | } 25 | 26 | @Override 27 | public String toPreparedStatement() { 28 | checkRender(); 29 | return super.toPreparedStatement(); 30 | } 31 | 32 | @Override 33 | public Object[][] getParamMatrix() { 34 | checkRender(); 35 | return super.getParamMatrix(); 36 | } 37 | 38 | protected void checkRender() { 39 | if (!renderComplete) { 40 | render(); 41 | renderComplete = true; 42 | } 43 | } 44 | 45 | protected abstract void render(); 46 | } 47 | -------------------------------------------------------------------------------- /demo/src/frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import store from "./store"; 5 | import Antd, { Icon } from "ant-design-vue"; 6 | 7 | import vueParticles from "vue-particles"; 8 | import ProLayout, { PageHeaderWrapper } from "@ant-design-vue/pro-layout"; 9 | import i18n from "./locales"; 10 | import "./global.less"; // global style 11 | import "./plugins"; 12 | import theme from "@/core/config/theme"; 13 | import bootstrap from "./core/index"; 14 | 15 | Vue.use(Antd); 16 | Vue.use(vueParticles); 17 | Vue.component("ProLayout", ProLayout); 18 | Vue.component("PageContainer", PageHeaderWrapper); 19 | Vue.component("PageHeaderWrapper", PageHeaderWrapper); 20 | 21 | Vue.config.productionTip = false; 22 | window.umi_plugin_ant_themeVar = theme.theme; 23 | 24 | const Iconfont = Icon.createFromIconfontCN({ 25 | scriptUrl: "//at.alicdn.com/t/font_2754865_21aagocihfp.js", // 在 iconfont.cn 上生成 26 | extraCommonProps: { class: "icon-font" }, 27 | }); 28 | 29 | Vue.component("Iconfont", Iconfont); 30 | new Vue({ 31 | router, 32 | store, 33 | i18n, 34 | mounted: () => { 35 | bootstrap(); 36 | }, 37 | render: (h) => h(App), 38 | }).$mount("#app"); 39 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/wechat/mods/socialLogin/qrOption.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @desc 获取扫码登录二维码选项信息,PC端调用此接口获取显示二维码参数,然后调用 var obj = new WxLogin({ 4 | self_redirect:true, 5 | id:"login_container", 6 | appid: "", 7 | scope: "", 8 | redirect_uri: "", 9 | state: "", 10 | style: "", 11 | href: "" 12 | });显示二维码 13 | */ 14 | import { defaultSuccess, defaultError, http } from '@/plugins/axios'; 15 | 16 | export class QrOptionParams { 17 | constructor( 18 | public href?: string, 19 | public id?: string, 20 | public state?: string, 21 | ) {} 22 | } 23 | 24 | export default async function( 25 | params: QrOptionParams, 26 | success: ({ 27 | data, 28 | ext, 29 | state, 30 | errors, 31 | }: { 32 | data: wechat.WxLogin; 33 | ext: ObjectMap; 34 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 35 | errors?: Array; 36 | }) => void = defaultSuccess, 37 | fail: (error: string) => void = defaultError, 38 | ): Promise { 39 | return http({ 40 | method: 'get', 41 | url: `/social/login/qr-option`, 42 | 43 | params, 44 | }) 45 | .then(data => success(data as any)) 46 | .catch(error => fail(error)); 47 | } 48 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/service/dictionary/CodebookService.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.service.dictionary; 2 | 3 | import java.util.stream.Collectors; 4 | 5 | import org.nutz.spring.boot.service.IdBaseService; 6 | import org.springframework.stereotype.Service; 7 | 8 | import tech.riemann.demo.dto.request.VXETableSaveDTO; 9 | import tech.riemann.demo.entity.dictionary.Codebook; 10 | 11 | /** 12 | *

13 | * 码本数据 服务实现类 14 | *

15 | * 16 | * @author Kerbores 17 | * @since 2021-08-12 18 | */ 19 | @Service 20 | public class CodebookService extends IdBaseService { 21 | 22 | /** 23 | * @param data 24 | * @return 25 | */ 26 | public boolean vxeSave(VXETableSaveDTO data) { 27 | dao().insert(data.getInsertRecords()); 28 | data.getRemoveRecords().stream().forEach(codebook -> delete(codebook.getId())); 29 | data.getUpdateRecords().stream().filter(codebook -> codebook.getId() > 0).forEach(this::update); 30 | dao().insert(data.getUpdateRecords() 31 | .stream() 32 | .filter(codebook -> codebook.getId() == 0) 33 | .collect(Collectors.toList())); 34 | return true; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /demo/src/frontend/src/layouts/UserLayout.vue: -------------------------------------------------------------------------------- 1 | 26 | 36 | 45 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/DemoEntity.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity; 2 | 3 | import java.util.Date; 4 | 5 | import org.nutz.dao.entity.annotation.Column; 6 | import org.nutz.dao.entity.annotation.Comment; 7 | import org.nutz.lang.Times; 8 | import org.nutz.spring.boot.service.entity.IdEntity; 9 | 10 | import lombok.AllArgsConstructor; 11 | import lombok.Builder.Default; 12 | import lombok.Data; 13 | import lombok.EqualsAndHashCode; 14 | import lombok.NoArgsConstructor; 15 | import lombok.experimental.SuperBuilder; 16 | 17 | /** 18 | * @author mdp 代码生成器 19 | */ 20 | @Data 21 | @SuperBuilder 22 | @NoArgsConstructor 23 | @AllArgsConstructor 24 | @EqualsAndHashCode(callSuper = true) 25 | public class DemoEntity extends IdEntity { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | @Column("created_by") 30 | @Comment("创建人") 31 | private String creater; 32 | 33 | @Column("created_time") 34 | @Comment("创建时间") 35 | @Default 36 | private Date createTime = Times.now(); 37 | 38 | @Column("updated_by") 39 | @Comment("更新人") 40 | private String updater; 41 | 42 | @Column("updated_time") 43 | @Comment("更新时间") 44 | @Default 45 | private Date updateTime = Times.now(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /demo/src/frontend/src/core/config/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | navTheme: "dark", // theme for nav menu 3 | primaryColor: "#2f7aa1", // primary color of ant design 4 | primaryColorName: "默认蓝", // primary color of ant design 5 | primaryColorNameEn: "default blue", // primary color of ant design 6 | layout: "topmenu", // nav menu position: sidemenu or topmenu 7 | contentWidth: "Fluid", // layout of content: Fluid or Fixed, only works when layout is topmenu 8 | fixedHeader: false, // sticky header 9 | fixSiderbar: false, // sticky siderbar 10 | colorWeak: false, 11 | autoHideHeader: false, // auto hide header 12 | multiTab: false, 13 | sideCollapsed: false, 14 | language: "zh-CN", 15 | showDrawer: true, 16 | menu: { 17 | locale: true, 18 | }, 19 | 20 | name: "ATS", 21 | title: "ANTDV STARTER", 22 | description: "基于typescript 开发 ant design vue 应用的一键启动器", 23 | copyright: "Copyright © ats.riemann.tech", 24 | storageOptions: { 25 | namespace: "ats__", // key prefix 26 | name: "ls", // name variable Vue.[ls] or this.[$ls], 27 | storage: "local", // storage name session, local, memory 28 | }, 29 | http: { 30 | prefix: process.env.NODE_ENV === "production" ? "/" : "/api", 31 | timeout: 60 * 1000, 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/sqltpl/VarSetMap.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao.sqltpl; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.nutz.dao.sql.Sql; 7 | import org.nutz.dao.sql.VarSet; 8 | 9 | /** 10 | * @author kerbores 11 | * 12 | */ 13 | public class VarSetMap { 14 | 15 | /** 16 | * 17 | */ 18 | private VarSetMap() {} 19 | 20 | protected static Map global; 21 | 22 | public static void setGlobal(Map global) { 23 | VarSetMap.global = global; 24 | } 25 | 26 | public static Map asMap(VarSet vars) { 27 | Map map = new HashMap<>(); 28 | for (String key : vars.keys()) { 29 | map.put(key, vars.get(key)); 30 | } 31 | return map; 32 | } 33 | 34 | public static Map asCtx(Sql sql) { 35 | Map params = VarSetMap.asMap(sql.params()); 36 | Map ctx = new HashMap<>(); 37 | if (global != null) { 38 | ctx.putAll(global); 39 | } 40 | ctx.putAll(params); 41 | ctx.put("params", params); 42 | ctx.put("vars", VarSetMap.asMap(sql.vars())); 43 | return ctx; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/ngrok/NgrokAutoConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.ngrok; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | import lombok.Data; 6 | 7 | /** 8 | * @author kerbores(kerbores@gmail.com) 9 | * 10 | */ 11 | @Data 12 | @ConfigurationProperties(prefix = "nutz.ngrok") 13 | public class NgrokAutoConfigurationProperties { 14 | 15 | Server server = new Server(); 16 | 17 | Client client = new Client(); 18 | 19 | @Data 20 | public class Server { 21 | boolean enabled = false; 22 | String sslJksPassword = ""; 23 | String sslJksPath; 24 | int port = 4443; 25 | String host = "wendal.cn"; 26 | int httpPort = 9080; 27 | boolean redis; 28 | String redisHost = "127.0.0.1"; 29 | int redisPort = 6379; 30 | String redisKey = "ngrok"; 31 | boolean debug; 32 | } 33 | 34 | @Data 35 | public class Client { 36 | /** 37 | * token 38 | */ 39 | String token; 40 | 41 | /** 42 | * 代理端口,默认会使用server.port 43 | */ 44 | int port; 45 | 46 | String server = "wendal.cn"; 47 | 48 | int serverPort = 4443; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/tools/Breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 13 | 41 | 42 | -------------------------------------------------------------------------------- /demo/src/frontend/src/utils/auth.ts: -------------------------------------------------------------------------------- 1 | import { UserModule } from "@/store/modules/user"; 2 | import { RouteConfig } from "vue-router"; 3 | export function getCurrentAuthority(): string[] { 4 | return UserModule.permissions; 5 | } 6 | 7 | export function isLogin(): boolean { 8 | return !!UserModule.token; 9 | } 10 | 11 | export function check(authority: string): boolean { 12 | if (!isLogin()) return false; 13 | if (!authority || authority == null) return true; 14 | return getCurrentAuthority().filter((p) => p === authority).length > 0; 15 | } 16 | 17 | /** 18 | * 菜单过滤 19 | * @param routers 路由树 20 | * @returns 菜单树 21 | */ 22 | export function dynamicFilteredMenus( 23 | routers: RouteConfig[] 24 | ): RouteConfig[] | undefined { 25 | return routers 26 | .filter((item) => check(item.meta?.authority)) //第一层筛选 27 | .map((item) => 28 | Object.assign(item, { 29 | children: item.children 30 | ? dynamicFilteredMenus(item.children) 31 | : undefined, 32 | }) 33 | ) //递归 34 | .filter((item) => { 35 | return !(item.children && item.children.length == 0); 36 | }) //有且为空,那么就代表是被筛成这样的,直接干掉之 37 | .map((item) => { 38 | if (item.children && item.children.length == 1) { 39 | //有且只有一个,将下级拉上来玩儿 40 | return item.children[0]; 41 | } 42 | return item; //没有或者是有多个的 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/GlobalHeader/RightContent.vue: -------------------------------------------------------------------------------- 1 | 7 | 50 | 51 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/vo/InstalledAction.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.vo; 2 | 3 | import tech.riemann.demo.entity.acl.Action; 4 | 5 | /** 6 | * @author Kerbores(kerbores@gmail.com) 7 | */ 8 | public enum InstalledAction { 9 | /** 10 | * 11 | */ 12 | ADD("add", "添加"), 13 | /** 14 | * 15 | */ 16 | DELETE("delete", "删除"), 17 | /** 18 | * 19 | */ 20 | EDIT("edit", "编辑"), 21 | /** 22 | * 23 | */ 24 | EXPORT("export", "导出"), 25 | /** 26 | * 27 | */ 28 | IMPORT("import", "导入"), 29 | /** 30 | * 31 | */ 32 | LIST("list", "列表"); 33 | 34 | String key; 35 | 36 | String name; 37 | 38 | /** 39 | * @param key 40 | * @param name 41 | */ 42 | private InstalledAction(String key, String name) { 43 | this.key = key; 44 | this.name = name; 45 | } 46 | 47 | public String getKey() { 48 | return key; 49 | } 50 | 51 | public String getName() { 52 | return name; 53 | } 54 | 55 | /** 56 | * 57 | */ 58 | public Action toAction() { 59 | return Action.builder() 60 | .key(getKey()) 61 | .name(getName()) 62 | .installed(true) 63 | .moduleId(0L) 64 | .build(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/sqltpl/impl/jetbrick/JetbrickSqlTpl.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao.sqltpl.impl.jetbrick; 2 | 3 | import java.io.StringWriter; 4 | 5 | import org.nutz.dao.sql.Sql; 6 | import org.nutz.spring.boot.dao.sqltpl.NutSqlTpl; 7 | import org.nutz.spring.boot.dao.sqltpl.VarSetMap; 8 | 9 | import jetbrick.template.JetEngine; 10 | import jetbrick.template.JetTemplate; 11 | 12 | /** 13 | * @author kerbores 14 | * 15 | */ 16 | public class JetbrickSqlTpl extends NutSqlTpl { 17 | 18 | public JetbrickSqlTpl(String source) { 19 | super(source); 20 | } 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | protected static JetEngine engine; 25 | 26 | public static Sql c(Sql sql) { 27 | JetTemplate template = engine().createTemplate(sql.getSourceSql()); 28 | StringWriter sw = new StringWriter(); 29 | template.render(VarSetMap.asCtx(sql), sw); 30 | sql.setSourceSql(sw.toString()); 31 | return sql; 32 | } 33 | 34 | public static JetEngine engine() { 35 | if (engine == null) 36 | engine = JetEngine.create(); 37 | return engine; 38 | } 39 | 40 | public static void setEngine(JetEngine engine) { 41 | JetbrickSqlTpl.engine = engine; 42 | } 43 | 44 | @Override 45 | protected void render() { 46 | c(this); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/interfaces/NameEntityService.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service.interfaces; 2 | 3 | import java.io.Serializable; 4 | 5 | import org.nutz.dao.Cnd; 6 | import org.nutz.dao.entity.EntityField; 7 | 8 | /** 9 | * @author wkipy 10 | * 11 | */ 12 | public interface NameEntityService extends EntityService { 13 | /** 14 | * 根据@Name所在的属性的值删除一条记录 15 | * 16 | * @param name 17 | * 属性的值 18 | * @return 删除的记录数,通常是0或者1 19 | */ 20 | public default int delete(String name) { 21 | return dao().delete(getEntityType(), name); 22 | } 23 | 24 | /** 25 | * 根据@Name所在的属性的值获取一个实体对象 26 | * 27 | * @param name 28 | * 属性的值 29 | * @return 实体对象,若没有符合条件的记录,则返回null 30 | */ 31 | public default T fetch(String name) { 32 | return dao().fetch(getEntityType(), name); 33 | } 34 | 35 | /** 36 | * 是否存在@Name所在的属性与指定值相符的记录 37 | * 38 | * @param name 39 | * 属性的值 40 | * @return true,如果存在符合条件的记录 41 | */ 42 | public default boolean exists(String name) { 43 | EntityField ef = getEntity().getNameField(); 44 | if (null == ef) 45 | return false; 46 | return dao().count(getEntityType(), Cnd.where(ef.getName(), "=", name)) > 0; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/BaseService.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service; 2 | 3 | import javax.annotation.Resource; 4 | 5 | import org.nutz.dao.Dao; 6 | import org.nutz.log.Log; 7 | import org.nutz.log.Logs; 8 | import org.nutz.service.EntityService; 9 | import org.nutz.spring.boot.service.entity.Entity; 10 | 11 | /** 12 | * @author kerbores(kerbores@gmail.com) 13 | * 14 | */ 15 | public class BaseService extends EntityService implements ExtService { 16 | 17 | public static final String EQ = "="; 18 | public static final String NEQ = "!="; 19 | public static final String LT = "<"; 20 | public static final String GT = ">"; 21 | public static final String IS = "is"; 22 | public static final String IS_NOT = "is not"; 23 | public static final String NOT = "not"; 24 | public static final String LIKE = "like"; 25 | public static final String IN = "in"; 26 | public static final String LT_AND_EQ = "=<"; 27 | public static final String GT_AND_EQ = ">="; 28 | 29 | protected Log logger = Logs.get(); 30 | 31 | @Resource(type = Dao.class) 32 | public void init(Dao dao) { 33 | super.setDao(dao); 34 | } 35 | 36 | /** 37 | * @return 38 | * @see org.nutz.spring.boot.service.ExtService#getEntityType() 39 | */ 40 | @Override 41 | public Class getEntityType() { 42 | return getEntityClass(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/vo/InstalledModule.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.vo; 2 | 3 | import tech.riemann.demo.entity.acl.Module; 4 | 5 | /** 6 | * @author Kerbores(kerbores@gmail.com) 7 | */ 8 | public enum InstalledModule { 9 | /** 10 | * 11 | */ 12 | USER("user", "用户", "用户管理"), 13 | /** 14 | * 15 | */ 16 | ROLE("role", "角色", "角色管理"), 17 | /** 18 | * 19 | */ 20 | MODULE("module", "功能模块", "模块管理"), 21 | /** 22 | * 23 | */ 24 | ACTION("action", "操作按钮", "操作管理"); 25 | 26 | String key; 27 | 28 | String name; 29 | 30 | String description; 31 | 32 | /** 33 | * @param key 34 | * @param name 35 | */ 36 | private InstalledModule(String key, String name, String description) { 37 | this.key = key; 38 | this.name = name; 39 | this.description = description; 40 | } 41 | 42 | public String getKey() { 43 | return key; 44 | } 45 | 46 | public String getName() { 47 | return name; 48 | } 49 | 50 | public String getDescription() { 51 | return description; 52 | } 53 | 54 | /** 55 | * 56 | */ 57 | public Module toModule() { 58 | return Module.builder() 59 | .description(getDescription()) 60 | .key(getKey()) 61 | .name(getName()) 62 | .build(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/response/ModuleInfo.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto.response; 2 | 3 | import java.util.List; 4 | 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Builder.Default; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.NoArgsConstructor; 12 | import tech.riemann.demo.entity.acl.Module; 13 | 14 | /** 15 | * @author Kerbores(kerbores@gmail.com) 16 | */ 17 | @Data 18 | @Builder 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | @EqualsAndHashCode(of = "moduleId") 22 | public class ModuleInfo { 23 | 24 | @Schema(description = "模块名称", required = true) 25 | String moduleName; 26 | 27 | @Schema(description = "模块key", required = true) 28 | String moduleKey; 29 | 30 | @Schema(description = "模块描述") 31 | String descr; 32 | 33 | @Schema(description = "模块Id") 34 | long moduleId; 35 | 36 | @Schema(description = "模块操作列表", required = true) 37 | List actions; 38 | 39 | @Default 40 | @Schema(description = "是否选中标识") 41 | boolean selected = true; 42 | 43 | public Module toModule() { 44 | return Module.builder() 45 | .key(moduleKey) 46 | .description(descr) 47 | .name(moduleName) 48 | .id(moduleId) 49 | .build(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /demo/src/frontend/src/utils/mixin.ts: -------------------------------------------------------------------------------- 1 | import { Vue, Component } from "vue-property-decorator"; 2 | import { AppModule } from "@/store/modules/app"; 3 | import { UserModule } from "@/store/modules/user"; 4 | import defaultImg from "@/assets/logo.png"; 5 | import config from "@/core/config"; 6 | import { i18nRender } from "../locales/index"; 7 | import { TranslateResult } from "vue-i18n"; 8 | 9 | @Component 10 | class I18nMixin extends Vue { 11 | get currentLang(): string { 12 | return AppModule.lang || config.language; 13 | } 14 | 15 | public setLang(lang: string): void { 16 | this.$store.dispatch("setLang", lang); 17 | } 18 | 19 | public t(key: string): TranslateResult { 20 | return i18nRender(key); 21 | } 22 | } 23 | 24 | @Component 25 | class Mixin extends Vue { 26 | public AppModule = AppModule; 27 | public UserModule = UserModule; 28 | public isTopMenu(): boolean { 29 | return AppModule.layout === "topmenu"; 30 | } 31 | public isSideMenu(): boolean { 32 | return !this.isTopMenu(); 33 | } 34 | 35 | public preventDefault(e: Event): void { 36 | e.preventDefault(); 37 | } 38 | } 39 | 40 | @Component 41 | class DeviceMixin extends Vue { 42 | public AppModule = AppModule; 43 | public UserModule = UserModule; 44 | public toImgUrl(imgKey: string): string { 45 | if (imgKey) return `http://image.kerbores.com/${imgKey}`; 46 | return defaultImg; 47 | } 48 | } 49 | 50 | export { I18nMixin, Mixin, DeviceMixin }; 51 | -------------------------------------------------------------------------------- /demo/src/frontend/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/controller/auth/AuthController.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.controller.auth; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestParam; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import club.zhcs.Result; 10 | import club.zhcs.auth.AuthService; 11 | import club.zhcs.auth.AuthService.LoginDto; 12 | import club.zhcs.auth.AuthUser; 13 | import io.swagger.v3.oas.annotations.Operation; 14 | import io.swagger.v3.oas.annotations.Parameter; 15 | import io.swagger.v3.oas.annotations.tags.Tag; 16 | 17 | /** 18 | * @author kerbores 19 | * 20 | */ 21 | @RestController 22 | @Tag(name = "Login", description = "登录认证模块") 23 | @RequestMapping("auth") 24 | public class AuthController { 25 | 26 | @Autowired 27 | AuthService authService; 28 | 29 | @PostMapping("login") 30 | @Operation(summary = "用户登录") 31 | public Result login( 32 | @Parameter(description = "用户名", required = true) @RequestParam(value = "user", required = true) String user, 33 | @Parameter(description = "密码", required = true) @RequestParam(value = "password", required = true) String password) { 34 | 35 | return Result.success(authService.login(new LoginDto(user, password))); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/SettingDrawer/index.less: -------------------------------------------------------------------------------- 1 | @import '~@/var.less'; 2 | .setting-drawer-handle { 3 | position: absolute; 4 | top: 240px; 5 | right: 300px; 6 | width: 48px; 7 | height: 48px; 8 | background: @primary-color; 9 | color: #fff; 10 | font-size: 20px; 11 | text-align: center; 12 | line-height: 48px; 13 | border-radius: 3px 0 0 3px; 14 | cursor: pointer; 15 | } 16 | .setting-drawer-index-content { 17 | .setting-drawer-index-blockChecbox { 18 | display: flex; 19 | .setting-drawer-index-item { 20 | margin-right: 16px; 21 | position: relative; 22 | border-radius: 4px; 23 | cursor: pointer; 24 | img { 25 | width: 48px; 26 | } 27 | .setting-drawer-index-selectIcon { 28 | position: absolute; 29 | top: 0; 30 | right: 0; 31 | width: 100%; 32 | padding-top: 15px; 33 | padding-left: 24px; 34 | height: 100%; 35 | color: @primary-color; 36 | font-size: 14px; 37 | font-weight: 700; 38 | } 39 | } 40 | } 41 | .setting-drawer-theme-color-colorBlock { 42 | width: 20px; 43 | height: 20px; 44 | border-radius: 2px; 45 | float: left; 46 | cursor: pointer; 47 | margin-right: 8px; 48 | padding-left: 0px; 49 | padding-right: 0px; 50 | text-align: center; 51 | color: #fff; 52 | font-weight: 700; 53 | i { 54 | font-size: 14px; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/acl/UserRole.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.acl; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Table; 6 | 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.NoArgsConstructor; 12 | import lombok.experimental.Accessors; 13 | import lombok.experimental.FieldNameConstants; 14 | import lombok.experimental.SuperBuilder; 15 | import tech.riemann.demo.entity.DemoEntity; 16 | 17 | /** 18 | *

19 | * 用户角色关系 20 | *

21 | * 22 | * @author Kerbores 23 | * @since 2021-08-12 24 | */ 25 | @Data 26 | @EqualsAndHashCode(callSuper = true) 27 | @SuperBuilder 28 | @NoArgsConstructor 29 | @AllArgsConstructor 30 | @FieldNameConstants 31 | @Accessors(chain = true) 32 | @Table("t_user_role") 33 | @Comment("用户角色关系") 34 | @Schema(name = "UserRole", description = "用户角色关系") 35 | public class UserRole extends DemoEntity { 36 | 37 | private static final long serialVersionUID = 1L; 38 | 39 | @Schema(description = "角色 id", required = true) 40 | @Column("ur_role_id") 41 | @Comment("角色 id") 42 | private Long roleId; 43 | 44 | @Schema(description = "用户 id", required = true) 45 | @Column("ur_user_id") 46 | @Comment("用户 id") 47 | private Long userId; 48 | 49 | public static final String UR_ROLE_ID = "ur_role_id"; 50 | 51 | public static final String UR_USER_ID = "ur_user_id"; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/SelectLang/SelectLang.vue: -------------------------------------------------------------------------------- 1 | 21 | 49 | 52 | -------------------------------------------------------------------------------- /demo/src/frontend/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | export interface Columns { 3 | title: string | any; 4 | dataIndex: string; 5 | align: string; 6 | width?: string; 7 | scopedSlots: { customRender: string }; 8 | key?: string; 9 | ellipsis?: boolean; 10 | sorter?: boolean; 11 | filters?: any; 12 | fixed?: string; 13 | } 14 | 15 | export interface UserInfo { 16 | id: number; 17 | password?: string; 18 | name?: string; 19 | mobile?: string; 20 | updateTime?: string; 21 | createTime?: string; 22 | permissionInfo?: Array; 23 | } 24 | export interface RoleInfo { 25 | createTime?: string; 26 | description?: string; 27 | id?: number; 28 | key?: string; 29 | name?: string; 30 | permissionInfo?: Array; 31 | updateTime?: string; 32 | } 33 | 34 | export interface ModuleInfo { 35 | actions?: Array; 36 | descr?: string; 37 | moduleId?: number; 38 | moduleKey: string; 39 | moduleName: string; 40 | selected?: boolean; 41 | } 42 | 43 | export interface AntPagination { 44 | size?: string; 45 | total?: number; 46 | current?: number; 47 | pageSize?: number; 48 | showSizeChanger?: boolean; 49 | showQuickJumper?: boolean; 50 | hideOnSinglePage?: boolean; 51 | simple?: boolean; 52 | } 53 | 54 | export interface FormCol { 55 | xs: Span; 56 | sm: Span; 57 | } 58 | export interface FormValue { 59 | key?: string; 60 | name?: string; 61 | id?: number; 62 | } 63 | export interface FormErrors { 64 | name?: { 65 | errors?: any[]; 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/service/interfaces/IdEntityService.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.service.interfaces; 2 | 3 | import java.io.Serializable; 4 | 5 | import org.nutz.dao.Cnd; 6 | import org.nutz.dao.entity.EntityField; 7 | 8 | /** 9 | * @author wkipy 10 | * 11 | */ 12 | public interface IdEntityService extends EntityService { 13 | 14 | /** 15 | * 根据@Id所在的属性的值获取一个实体对象 16 | * 17 | * @param id 18 | * 属性的值 19 | * @return 实体对象,如不存在则返回null 20 | */ 21 | public default T fetch(long id) { 22 | return dao().fetch(getEntityType(), id); 23 | } 24 | 25 | /** 26 | * 根据@Id所在的属性的值删除一个实体对象 27 | * 28 | * @param id 29 | * 属性的值 30 | * @return 删除的记录数, 通常是0或者1 31 | */ 32 | public default int delete(long id) { 33 | return dao().delete(getEntityType(), id); 34 | } 35 | 36 | /** 37 | * 根据@Id所在的属性在数据库中的最大值 38 | * 39 | * @return 最大值,若数据库中没有数据,会抛出空指针异常 40 | */ 41 | public default int getMaxId() { 42 | return dao().getMaxId(getEntityType()); 43 | } 44 | 45 | /** 46 | * 是否存在@Id所在属性的值为指定值的记录 47 | * 48 | * @param id 49 | * 属性的值 50 | * @return true,如果存在的话 51 | */ 52 | public default boolean exists(long id) { 53 | EntityField ef = getEntity().getIdField(); 54 | if (null == ef) 55 | return false; 56 | return dao().count(getEntityType(), Cnd.where(ef.getName(), "=", id)) > 0; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /demo/src/frontend/src/locales/lang/enUS/page.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | user: { 3 | name: "User Name", 4 | mobile: "Mobile", 5 | createTime: "Create Time", 6 | action: "Actions", 7 | add: "Add User", 8 | delete: "Delete", 9 | grant: "Grant", 10 | role: "Grant Role", 11 | edit: "Edit", 12 | search: { 13 | placeholder: "input keyword please", 14 | }, 15 | confirm: { 16 | title: "Confirm to delete this user?", 17 | textYes: "Yes", 18 | textNo: "No", 19 | }, 20 | message: { 21 | deleteSuccess: "Delete user succeeded!", 22 | }, 23 | }, 24 | role: { 25 | key: "Role key", 26 | name: "Role Name", 27 | createdAt: "Create Time", 28 | action: "Actions", 29 | add: "Add Role", 30 | delete: "Delete", 31 | edit: "Edit", 32 | grant: "Grant", 33 | search: { 34 | placeholder: "input keyword please", 35 | }, 36 | confirm: { 37 | title: "Confirm to delete this role?", 38 | textYes: "Yes", 39 | textNo: "No", 40 | }, 41 | message: { 42 | deleteSuccess: "Delete role succeeded!", 43 | }, 44 | }, 45 | module: { 46 | key: "Module key", 47 | name: "Module Name", 48 | createTime: "Create Time", 49 | action: "Actions", 50 | add: "Add Module", 51 | delete: "Delete", 52 | edit: "Edit", 53 | search: { 54 | placeholder: "input keyword please", 55 | }, 56 | confirm: { 57 | title: "Confirm to delete this module?", 58 | textYes: "Yes", 59 | textNo: "No", 60 | }, 61 | message: { 62 | deleteSuccess: "Delete module succeeded!", 63 | }, 64 | }, 65 | }; 66 | -------------------------------------------------------------------------------- /demo/src/frontend/src/views/user/WechatLogin.vue: -------------------------------------------------------------------------------- 1 | 10 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/request/NutzHttpAutoConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.request; 2 | 3 | import java.util.List; 4 | 5 | import org.nutz.lang.Lang; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | 8 | import lombok.Data; 9 | 10 | /** 11 | * @author kerbores 12 | * 13 | */ 14 | @Data 15 | @ConfigurationProperties(prefix = "nutz.http") 16 | public class NutzHttpAutoConfigurationProperties { 17 | 18 | /** 19 | * 是否启用 20 | */ 21 | boolean enabled = true; 22 | 23 | /** 24 | * sender http 配置 25 | */ 26 | Http http = new Http(); 27 | 28 | /** 29 | * 代理配置 30 | */ 31 | Proxy proxy = new Proxy(); 32 | 33 | @Data 34 | public class Http { 35 | /** 36 | * 连接超时时间 37 | */ 38 | int connectionTimeout = 30 * 1000; 39 | 40 | /** 41 | * 读取超时时间 42 | */ 43 | int timeout = 10 * 60 * 1000; 44 | 45 | /** 46 | * 支持重定向跟随 47 | */ 48 | boolean followRedirects = true; 49 | 50 | /** 51 | * 开启ssl证书验证 52 | */ 53 | boolean jvmHttpsCheck = true; 54 | } 55 | 56 | /** 57 | * 代理配置 58 | * 59 | * @author kerbores 60 | * 61 | */ 62 | @Data 63 | public class Proxy { 64 | /** 65 | * 是否启用代理 66 | */ 67 | boolean enabled = false; 68 | /** 69 | * 代理主机 70 | */ 71 | String host; 72 | /** 73 | * 代理端口 74 | */ 75 | int port; 76 | /** 77 | * 被代理的主机列表 78 | */ 79 | List proxiedHosts = Lang.list(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/log/NutzLogConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.log; 2 | 3 | import org.nutz.log.LogAdapter; 4 | import org.nutz.log.impl.Log4jLogAdapter; 5 | import org.nutz.log.impl.NopLog; 6 | import org.nutz.log.impl.Slf4jLogAdapter; 7 | import org.nutz.log.impl.SystemLogAdapter; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | 10 | import lombok.AllArgsConstructor; 11 | import lombok.Data; 12 | import lombok.Getter; 13 | 14 | /** 15 | * @author kerbores 16 | * 17 | */ 18 | @Data 19 | @ConfigurationProperties("nutz.log") 20 | public class NutzLogConfigurationProperties { 21 | 22 | /** 23 | * 日志类型 24 | */ 25 | Type type = Type.LOG4J2; 26 | 27 | /** 28 | * 自定义日志适配器全限定名 29 | */ 30 | String customerLogAdapter; 31 | 32 | @Getter 33 | @AllArgsConstructor 34 | public enum Type { 35 | /** 36 | * log4j2 37 | */ 38 | LOG4J2("Log4j2", Log4j2Adapter.class), 39 | /** 40 | * log4j 41 | */ 42 | LOG4J("Log4j", Log4jLogAdapter.class), 43 | /** 44 | * nop 45 | */ 46 | NOP("Nop", NopLog.class), 47 | /** 48 | * slf4j 49 | */ 50 | SLF4J("slf4j", Slf4jLogAdapter.class), 51 | /** 52 | * Console 53 | */ 54 | CONSOLE("Console", SystemLogAdapter.class), 55 | /** 56 | * 自定义 57 | */ 58 | CUSTOMER("customer", LogAdapter.class); 59 | 60 | /** 61 | * 名称 62 | */ 63 | String name; 64 | /** 65 | * 适配器类型 66 | */ 67 | Class clazz; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /demo/src/frontend/src/views/acl/module/ActionFormDesign.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | type: "input", 4 | field: "key", 5 | title: "操作Key", 6 | info: "", 7 | props: { 8 | type: "text", 9 | minlength: 3, 10 | maxlength: 10, 11 | placeholder: "请输入操作key", 12 | clearable: true, 13 | }, 14 | _fc_drag_tag: "input", 15 | hidden: false, 16 | display: true, 17 | validate: [ 18 | { 19 | trigger: "change", 20 | mode: "required", 21 | message: "请输入操作key", 22 | required: true, 23 | type: "string", 24 | }, 25 | { 26 | trigger: "change", 27 | mode: "min", 28 | message: "操作key不少于3个字符", 29 | min: 3, 30 | type: "string", 31 | }, 32 | ], 33 | }, 34 | { 35 | type: "input", 36 | field: "name", 37 | title: "操作名", 38 | info: "", 39 | props: { 40 | type: "text", 41 | maxlength: 50, 42 | minlength: 2, 43 | placeholder: "请输入操作名", 44 | clearable: true, 45 | showPassword: true, 46 | }, 47 | _fc_drag_tag: "input", 48 | hidden: false, 49 | display: true, 50 | validate: [ 51 | { 52 | trigger: "change", 53 | mode: "required", 54 | message: "请输入操作名", 55 | required: true, 56 | type: "string", 57 | }, 58 | { 59 | trigger: "change", 60 | mode: "min", 61 | message: "操作名长度在 2 到 50 个字符", 62 | min: 2, 63 | type: "string", 64 | }, 65 | { 66 | trigger: "change", 67 | mode: "max", 68 | message: "操作名长度在 2 到 50 个字符", 69 | max: 50, 70 | type: "string", 71 | }, 72 | ], 73 | }, 74 | ]; 75 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/request/NutzHttpRequestFactory.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.request; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | 6 | import org.nutz.http.ProxySwitcher; 7 | import org.nutz.log.Logs; 8 | import org.nutz.spring.boot.request.NutzHttpAutoConfigurationProperties.Http; 9 | import org.springframework.beans.factory.DisposableBean; 10 | import org.springframework.http.HttpMethod; 11 | import org.springframework.http.client.ClientHttpRequest; 12 | import org.springframework.http.client.ClientHttpRequestFactory; 13 | 14 | import lombok.AllArgsConstructor; 15 | import lombok.Data; 16 | import lombok.NoArgsConstructor; 17 | import lombok.experimental.SuperBuilder; 18 | 19 | /** 20 | * @author kerbores 21 | * 22 | */ 23 | @Data 24 | @SuperBuilder 25 | @AllArgsConstructor 26 | @NoArgsConstructor 27 | public class NutzHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean { 28 | 29 | ProxySwitcher proxySwitcher; 30 | 31 | Http http; 32 | 33 | /** 34 | * @param uri 35 | * @param httpMethod 36 | * @return 37 | * @throws IOException 38 | * @see org.springframework.http.client.ClientHttpRequestFactory#createRequest(java.net.URI, 39 | * org.springframework.http.HttpMethod) 40 | */ 41 | @Override 42 | public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { 43 | return new NutzHttpClientHttpRequest(uri, httpMethod, http, proxySwitcher); 44 | } 45 | 46 | /* (non-Javadoc) 47 | * @see org.springframework.beans.factory.DisposableBean#destroy() 48 | */ 49 | @Override 50 | public void destroy() throws Exception { 51 | Logs.get().debug("destroy"); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/acl/Role.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.acl; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Name; 6 | import org.nutz.dao.entity.annotation.Table; 7 | 8 | import io.swagger.v3.oas.annotations.media.Schema; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Data; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.NoArgsConstructor; 13 | import lombok.experimental.Accessors; 14 | import lombok.experimental.FieldNameConstants; 15 | import lombok.experimental.SuperBuilder; 16 | import tech.riemann.demo.entity.DemoEntity; 17 | 18 | /** 19 | *

20 | * 角色 21 | *

22 | * 23 | * @author Kerbores 24 | * @since 2021-08-12 25 | */ 26 | @Data 27 | @EqualsAndHashCode(callSuper = true) 28 | @SuperBuilder 29 | @NoArgsConstructor 30 | @AllArgsConstructor 31 | @FieldNameConstants 32 | @Accessors(chain = true) 33 | @Table("t_role") 34 | @Comment("角色") 35 | @Schema(name = "Role", description = "角色") 36 | public class Role extends DemoEntity { 37 | 38 | private static final long serialVersionUID = 1L; 39 | 40 | @Schema(description = "角色唯一键", required = true) 41 | @Column("r_key") 42 | @Comment("角色唯一键") 43 | @Name 44 | private String key; 45 | 46 | @Schema(description = "角色名称", required = true) 47 | @Column("r_name") 48 | @Comment("角色名称") 49 | private String name; 50 | 51 | @Schema(description = "角色描述") 52 | @Column("r_description") 53 | @Comment("角色描述") 54 | private String description; 55 | 56 | public static final String R_KEY = "r_key"; 57 | 58 | public static final String R_NAME = "r_name"; 59 | 60 | public static final String R_DESCR = "r_descr"; 61 | 62 | } 63 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/login/api.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | 4 | declare namespace login { 5 | export class AuthUser { 6 | /** 扩展信息 */ 7 | extInfo: ObjectMap; 8 | 9 | /** 密码 */ 10 | password: string; 11 | 12 | /** 权限列表 */ 13 | permissions: Array; 14 | 15 | /** jwt refreshToken */ 16 | refreshToken: string; 17 | 18 | /** 角色列表 */ 19 | roles: Array; 20 | 21 | /** jwt Token */ 22 | token: string; 23 | 24 | /** 用户名 */ 25 | userName: string; 26 | } 27 | 28 | export class Pagination { 29 | /** dataList */ 30 | dataList: Array; 31 | 32 | /** first */ 33 | first?: boolean; 34 | 35 | /** last */ 36 | last?: boolean; 37 | 38 | /** offset */ 39 | offset?: number; 40 | 41 | /** pageCount */ 42 | pageCount?: number; 43 | 44 | /** pageNumber */ 45 | pageNumber?: number; 46 | 47 | /** pageSize */ 48 | pageSize?: number; 49 | 50 | /** paras */ 51 | paras?: ObjectMap; 52 | 53 | /** recordCount */ 54 | recordCount?: number; 55 | } 56 | 57 | export class Result { 58 | /** 响应数据 */ 59 | data: T0; 60 | 61 | /** 错误信息列表 */ 62 | errors?: Array; 63 | 64 | /** 响应扩展数据 */ 65 | ext?: ObjectMap; 66 | 67 | /** 响应状态 */ 68 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 69 | 70 | /** success */ 71 | success?: boolean; 72 | } 73 | 74 | export class VXETableSaveDTO { 75 | /** 新增记录数据 */ 76 | insertRecords: Array; 77 | 78 | /** 删除记录数据 */ 79 | removeRecords: Array; 80 | 81 | /** 更新记录数据 */ 82 | updateRecords: Array; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/acl/Module.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.acl; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Name; 6 | import org.nutz.dao.entity.annotation.Table; 7 | 8 | import io.swagger.v3.oas.annotations.media.Schema; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Data; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.NoArgsConstructor; 13 | import lombok.experimental.Accessors; 14 | import lombok.experimental.FieldNameConstants; 15 | import lombok.experimental.SuperBuilder; 16 | import tech.riemann.demo.entity.DemoEntity; 17 | 18 | /** 19 | *

20 | * 功能模块 21 | *

22 | * 23 | * @author Kerbores 24 | * @since 2021-08-12 25 | */ 26 | @Data 27 | @EqualsAndHashCode(callSuper = true) 28 | @SuperBuilder 29 | @NoArgsConstructor 30 | @AllArgsConstructor 31 | @FieldNameConstants 32 | @Accessors(chain = true) 33 | @Table("t_module") 34 | @Comment("功能模块") 35 | @Schema(name = "Module", description = "功能模块") 36 | public class Module extends DemoEntity { 37 | 38 | private static final long serialVersionUID = 1L; 39 | 40 | @Schema(description = "模块key", required = true) 41 | @Column("m_key") 42 | @Comment("模块key") 43 | @Name 44 | private String key; 45 | 46 | @Schema(description = "模块描述") 47 | @Column("m_description") 48 | @Comment("模块描述") 49 | private String description; 50 | 51 | @Schema(description = "模块名称", required = true) 52 | @Column("m_name") 53 | @Comment("模块名称") 54 | private String name; 55 | 56 | public static final String M_KEY = "m_key"; 57 | 58 | public static final String M_DESCR = "m_descr"; 59 | 60 | public static final String M_NAME = "m_name"; 61 | 62 | } 63 | -------------------------------------------------------------------------------- /demo/src/frontend/src/global.less: -------------------------------------------------------------------------------- 1 | @import "./var.less"; 2 | 3 | html, 4 | body, 5 | #app, 6 | #root { 7 | height: 100%; 8 | } 9 | 10 | .colorWeak { 11 | filter: invert(80%); 12 | } 13 | 14 | .ant-layout.layout-basic { 15 | height: 100vh; 16 | min-height: 100vh; 17 | } 18 | 19 | canvas { 20 | display: block; 21 | } 22 | 23 | body { 24 | text-rendering: optimizeLegibility; 25 | -webkit-font-smoothing: antialiased; 26 | -moz-osx-font-smoothing: grayscale; 27 | } 28 | 29 | ul, 30 | ol { 31 | list-style: none; 32 | } 33 | 34 | // 数据列表 样式 35 | .table-alert { 36 | margin-bottom: 16px; 37 | } 38 | // 数据列表 操作 39 | .table-operator { 40 | margin-bottom: 18px; 41 | 42 | button { 43 | margin-right: 8px; 44 | } 45 | } 46 | // 数据列表 搜索条件 47 | .table-page-search-wrapper { 48 | .ant-form-inline { 49 | .ant-form-item { 50 | display: flex; 51 | margin-bottom: 24px; 52 | margin-right: 0; 53 | 54 | .ant-form-item-control-wrapper { 55 | flex: 1 1; 56 | display: inline-block; 57 | vertical-align: middle; 58 | } 59 | 60 | > .ant-form-item-label { 61 | line-height: 32px; 62 | padding-right: 8px; 63 | width: auto; 64 | } 65 | .ant-form-item-control { 66 | height: 32px; 67 | line-height: 32px; 68 | } 69 | } 70 | } 71 | 72 | .table-page-search-submitButtons { 73 | display: block; 74 | margin-bottom: 24px; 75 | white-space: nowrap; 76 | } 77 | } 78 | 79 | @media (max-width: @screen-xs) { 80 | .ant-table { 81 | width: 100%; 82 | overflow-x: auto; 83 | &-thead > tr, 84 | &-tbody > tr { 85 | > th, 86 | > td { 87 | white-space: pre; 88 | > span { 89 | display: block; 90 | } 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /demo/src/frontend/src/store/modules/user.ts: -------------------------------------------------------------------------------- 1 | import { 2 | VuexModule, 3 | Module, 4 | Mutation, 5 | Action, 6 | getModule, 7 | } from "vuex-module-decorators"; 8 | import store from "@/store"; 9 | import * as types from "@/store/mutation-types"; 10 | import storage from "store"; 11 | 12 | export interface UserState { 13 | /** 14 | * 用户名 15 | */ 16 | name: string; 17 | /** 18 | * 姓名 19 | */ 20 | fullName?: string; 21 | /** 22 | * 头像 23 | */ 24 | avatarUrl?: string; 25 | /** 26 | * 邮箱 27 | */ 28 | email?: string; 29 | /** 30 | * 电话 31 | */ 32 | mobile?: string; 33 | /** 34 | * jwtToken 35 | */ 36 | token: string; 37 | /** 38 | * 角色 39 | */ 40 | roles: Array; 41 | /** 42 | * 权限 43 | */ 44 | permissions: Array; 45 | } 46 | 47 | @Module({ dynamic: true, store, name: "user" }) 48 | class User extends VuexModule implements UserState { 49 | public name = ""; 50 | public fullName = ""; 51 | public avatarUrl = ""; 52 | public email = ""; 53 | public mobile = ""; 54 | public token = ""; 55 | public roles: Array = []; 56 | public permissions: Array = []; 57 | 58 | @Mutation 59 | public login(user: UserState) { 60 | storage.set(types.LOGIN_USER, user); 61 | Object.assign(this, user); 62 | } 63 | 64 | @Mutation 65 | public logout() { 66 | this.token = ""; 67 | } 68 | 69 | @Action 70 | public hasRole(role: string) { 71 | if (!role || role == null) return true; 72 | return this.roles.filter((r) => r === role).length > 0; 73 | } 74 | 75 | @Action 76 | public hasPermission(permission: string) { 77 | if (!permission || permission == null) return true; 78 | return this.permissions.filter((p) => p === permission).length > 0; 79 | } 80 | } 81 | 82 | export const UserModule = getModule(User); 83 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/acl/LoginChannel.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.acl; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Table; 6 | 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.NoArgsConstructor; 12 | import lombok.experimental.Accessors; 13 | import lombok.experimental.FieldNameConstants; 14 | import lombok.experimental.SuperBuilder; 15 | import tech.riemann.demo.entity.DemoEntity; 16 | 17 | /** 18 | * @author wkipy 19 | * 20 | */ 21 | @Data 22 | @EqualsAndHashCode(callSuper = true) 23 | @SuperBuilder 24 | @NoArgsConstructor 25 | @AllArgsConstructor 26 | @FieldNameConstants 27 | @Accessors(chain = true) 28 | @Table("t_login_channel") 29 | @Comment("角色") 30 | @Schema(name = "LoginChannel", description = "社会化登录渠道") 31 | public class LoginChannel extends DemoEntity { 32 | /** 33 | * 34 | */ 35 | private static final long serialVersionUID = 1L; 36 | 37 | @Schema(description = "openid", required = true) 38 | @Column("lc_openid") 39 | @Comment("openid") 40 | String openid; 41 | 42 | @Schema(description = "用户id", required = true) 43 | @Column("lc_user_id") 44 | @Comment("用户id") 45 | long userId; 46 | 47 | @Schema(description = "渠道", required = true) 48 | @Column("lc_channel") 49 | @Comment("渠道") 50 | Channel channel; 51 | 52 | public enum Channel { 53 | /** 54 | * 公众号网页授权 55 | */ 56 | MP, 57 | /** 58 | * 微信小程序 59 | */ 60 | MINIAPP, 61 | /** 62 | * 微信扫码登录 63 | */ 64 | WECHAT_SCAN, 65 | /** 66 | * 微信社会化登录(APP登录) 67 | */ 68 | WECHAT 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/sqltpl/impl/beetl/ClasspathStringResourceLoader.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao.sqltpl.impl.beetl; 2 | 3 | import org.beetl.core.Resource; 4 | import org.beetl.core.resource.ClasspathResourceLoader; 5 | import org.beetl.core.resource.FileResource; 6 | import org.beetl.core.resource.StringTemplateResource; 7 | 8 | /** 9 | * @author kerbores 10 | * 11 | */ 12 | public class ClasspathStringResourceLoader extends ClasspathResourceLoader { 13 | 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = 1L; 18 | 19 | public ClasspathStringResourceLoader() { 20 | super(); 21 | } 22 | 23 | public ClasspathStringResourceLoader(ClassLoader classLoader, String root, String charset) { 24 | super(classLoader, root, charset); 25 | } 26 | 27 | public ClasspathStringResourceLoader(ClassLoader classLoader, String root) { 28 | super(classLoader, root); 29 | } 30 | 31 | public ClasspathStringResourceLoader(ClassLoader classLoader) { 32 | super(classLoader); 33 | } 34 | 35 | public ClasspathStringResourceLoader(String root, String charset) { 36 | super(root, charset); 37 | } 38 | 39 | public ClasspathStringResourceLoader(String root) { 40 | super(root); 41 | } 42 | 43 | @Override 44 | public Resource getResource(String key) { 45 | if (key.startsWith("/")) 46 | return super.getResource(key); 47 | return new StringTemplateResource(key, this); 48 | } 49 | 50 | @Override 51 | public boolean isModified(Resource key) { 52 | if (key instanceof FileResource) 53 | return super.isModified(key); 54 | return false; 55 | } 56 | 57 | @Override 58 | public boolean exist(String key) { 59 | if (key.startsWith("/")) 60 | return super.exist(key); 61 | return true; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /demo/src/frontend/src/locales/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueI18n, { TranslateResult } from "vue-i18n"; 3 | import moment from "moment"; 4 | import vxezhCN from "vxe-table/lib/locale/lang/zh-CN"; 5 | import vxeenUS from "vxe-table/lib/locale/lang/en-US"; 6 | // default lang 7 | import zhCN from "./lang/zh-CN"; 8 | import enUS from "./lang/en-US"; 9 | 10 | Vue.use(VueI18n); 11 | 12 | export const defaultLang = "zh-CN"; 13 | 14 | const messages = { 15 | "zh-CN": { 16 | ...zhCN, 17 | ...vxezhCN, 18 | }, 19 | en_US: { 20 | ...enUS, 21 | ...vxeenUS, 22 | }, 23 | }; 24 | 25 | const i18n = new VueI18n({ 26 | silentTranslationWarn: true, 27 | locale: defaultLang, 28 | fallbackLocale: defaultLang, 29 | messages, 30 | }); 31 | 32 | const loadedLanguages = [defaultLang]; 33 | 34 | function setI18nLanguage(lang: string): string { 35 | i18n.locale = lang; 36 | // request.headers['Accept-Language'] = lang 37 | const html = document.querySelector("html"); 38 | html && html.setAttribute("lang", lang); 39 | return lang; 40 | } 41 | 42 | export function loadLanguageAsync(lang = defaultLang): Promise { 43 | return new Promise((resolve) => { 44 | // 缓存语言设置 45 | if (i18n.locale !== lang) { 46 | if (!loadedLanguages.includes(lang)) { 47 | return import( 48 | /* webpackChunkName: "lang-[request]" */ `./lang/${lang}` 49 | ).then((msg) => { 50 | const locale = msg.default; 51 | i18n.setLocaleMessage(lang, locale); 52 | loadedLanguages.push(lang); 53 | moment.updateLocale(locale.momentName, locale.momentLocale); 54 | return setI18nLanguage(lang); 55 | }); 56 | } 57 | return resolve(setI18nLanguage(lang)); 58 | } 59 | return resolve(lang); 60 | }); 61 | } 62 | 63 | export function i18nRender(key: string): TranslateResult { 64 | return i18n.t(`${key}`); 65 | } 66 | 67 | export default i18n; 68 | -------------------------------------------------------------------------------- /demo/src/frontend/src/components/NProgress/nprogress.less: -------------------------------------------------------------------------------- 1 | @import url('../index.less'); 2 | 3 | /* Make clicks pass-through */ 4 | #nprogress { 5 | pointer-events: none; 6 | } 7 | 8 | #nprogress .bar { 9 | background: @primary-color; 10 | 11 | position: fixed; 12 | z-index: 1031; 13 | top: 0; 14 | left: 0; 15 | 16 | width: 100%; 17 | height: 2px; 18 | } 19 | 20 | /* Fancy blur effect */ 21 | #nprogress .peg { 22 | display: block; 23 | position: absolute; 24 | right: 0px; 25 | width: 100px; 26 | height: 100%; 27 | box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color; 28 | opacity: 1.0; 29 | 30 | -webkit-transform: rotate(3deg) translate(0px, -4px); 31 | -ms-transform: rotate(3deg) translate(0px, -4px); 32 | transform: rotate(3deg) translate(0px, -4px); 33 | } 34 | 35 | /* Remove these to get rid of the spinner */ 36 | #nprogress .spinner { 37 | display: block; 38 | position: fixed; 39 | z-index: 1031; 40 | top: 15px; 41 | right: 15px; 42 | } 43 | 44 | #nprogress .spinner-icon { 45 | width: 18px; 46 | height: 18px; 47 | box-sizing: border-box; 48 | 49 | border: solid 2px transparent; 50 | border-top-color: @primary-color; 51 | border-left-color: @primary-color; 52 | border-radius: 50%; 53 | 54 | -webkit-animation: nprogress-spinner 400ms linear infinite; 55 | animation: nprogress-spinner 400ms linear infinite; 56 | } 57 | 58 | .nprogress-custom-parent { 59 | overflow: hidden; 60 | position: relative; 61 | } 62 | 63 | .nprogress-custom-parent #nprogress .spinner, 64 | .nprogress-custom-parent #nprogress .bar { 65 | position: absolute; 66 | } 67 | 68 | @-webkit-keyframes nprogress-spinner { 69 | 0% { 70 | -webkit-transform: rotate(0deg); 71 | } 72 | 73 | 100% { 74 | -webkit-transform: rotate(360deg); 75 | } 76 | } 77 | 78 | @keyframes nprogress-spinner { 79 | 0% { 80 | transform: rotate(0deg); 81 | } 82 | 83 | 100% { 84 | transform: rotate(360deg); 85 | } 86 | } -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.CYAN} 2 | 3 | +++++ +++++++++ 4 | ++++++++++++++++++++ 5 | +++++++++++++++++++ 6 | +++++++++++++++I7+++++ 7 | +++++++++++++++~ =++= 8 | ++++++++++++++ ~7=.==== 9 | ++++++++++++++I~~ ~====== 10 | +++++++++++++ ~~~:======== 11 | ++++++++++++ ~~~~~~~====== 12 | ++++++++++: ~~~~~~~~:~===== 13 | +++++++ == ~=++~~~=:~====== 14 | ++++ ======~~ ~~?:~======= 15 | + ==========7~~:~======== 16 | ========7~I:~========= 17 | ====== ~:~========== 18 | ======~~========== 19 | =============== 20 | =========== 21 | 22 | 23 | 24 | ' ███╗ ██╗██╗ ██╗████████╗███████╗ ███████╗████████╗ █████╗ ██████╗ ████████╗███████╗██████╗ 25 | ' ████╗ ██║██║ ██║╚══██╔══╝╚══███╔╝ ██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██╔══██╗ 26 | ' ██╔██╗ ██║██║ ██║ ██║ ███╔╝█████╗███████╗ ██║ ███████║██████╔╝ ██║ █████╗ ██████╔╝ 27 | ' ██║╚██╗██║██║ ██║ ██║ ███╔╝ ╚════╝╚════██║ ██║ ██╔══██║██╔══██╗ ██║ ██╔══╝ ██╔══██╗ 28 | ' ██║ ╚████║╚██████╔╝ ██║ ███████╗ ███████║ ██║ ██║ ██║██║ ██║ ██║ ███████╗██║ ██║ 29 | ' ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ 30 | ' 31 | ${AnsiColor.MAGENTA} 32 | 33 | powered by nutz.cn :: Spring Boot :: ${spring-boot.formatted-version} 34 | 35 | ${AnsiColor.DEFAULT} 36 | 37 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/dictionary/Group.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.dictionary; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Table; 6 | 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder.Default; 10 | import lombok.Data; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.NoArgsConstructor; 13 | import lombok.experimental.Accessors; 14 | import lombok.experimental.FieldNameConstants; 15 | import lombok.experimental.SuperBuilder; 16 | import tech.riemann.demo.entity.DemoEntity; 17 | 18 | /** 19 | *

20 | * 码本分组 21 | *

22 | * 23 | * @author Kerbores 24 | * @since 2021-08-12 25 | */ 26 | @Data 27 | @EqualsAndHashCode(callSuper = true) 28 | @SuperBuilder 29 | @NoArgsConstructor 30 | @AllArgsConstructor 31 | @FieldNameConstants 32 | @Accessors(chain = true) 33 | @Table("t_group") 34 | @Comment("码本分组") 35 | @Schema(name = "Group", description = "码本分组") 36 | public class Group extends DemoEntity { 37 | 38 | private static final long serialVersionUID = 1L; 39 | 40 | @Schema(description = "分组唯一键", required = true) 41 | @Column("g_key") 42 | @Comment("分组唯一键") 43 | private String key; 44 | 45 | @Schema(description = "分组名称", required = true) 46 | @Column("g_name") 47 | @Comment("分组名称") 48 | private String name; 49 | 50 | @Schema(description = "分组描述") 51 | @Column("g_description") 52 | @Comment("分组描述") 53 | private String description; 54 | 55 | @Schema(description = "禁用标识", required = true) 56 | @Column("g_disabled") 57 | @Comment("禁用标识") 58 | @Default 59 | private Boolean disabled = false; 60 | 61 | public static final String G_KEY = "g_key"; 62 | 63 | public static final String G_NAME = "g_name"; 64 | 65 | public static final String G_DESCR = "g_descr"; 66 | 67 | public static final String G_DISABLED = "g_disabled"; 68 | 69 | } 70 | -------------------------------------------------------------------------------- /demo/src/frontend/src/locales/lang/zhCN/page.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | user: { 3 | name: "用户名", 4 | mobile: "手机号码", 5 | createTime: "创建时间", 6 | action: "操作", 7 | add: "添加用户", 8 | delete: "删除", 9 | grant: "授权", 10 | role: "设置角色", 11 | edit: "编辑", 12 | search: { 13 | placeholder: "请输入关键字", 14 | }, 15 | confirm: { 16 | title: "确认删除这个用户?", 17 | textYes: "确认", 18 | textNo: "取消", 19 | }, 20 | message: { 21 | deleteSuccess: "删除用户成功", 22 | }, 23 | }, 24 | role: { 25 | key: "角色Key", 26 | name: "角色名", 27 | createdAt: "创建时间", 28 | action: "操作", 29 | add: "添加角色", 30 | delete: "删除", 31 | edit: "编辑", 32 | grant: "授权", 33 | search: { 34 | placeholder: "请输入关键字", 35 | }, 36 | confirm: { 37 | title: "确认删除这个角色?", 38 | textYes: "确认", 39 | textNo: "取消", 40 | }, 41 | message: { 42 | deleteSuccess: "删除角色成功", 43 | }, 44 | }, 45 | module: { 46 | key: "模块Key", 47 | name: "模块名称", 48 | createTime: "创建时间", 49 | action: "操作", 50 | add: "添加模块", 51 | delete: "删除", 52 | edit: "编辑", 53 | search: { 54 | placeholder: "请输入关键字", 55 | }, 56 | confirm: { 57 | title: "确认删除这个模块?", 58 | textYes: "确认", 59 | textNo: "取消", 60 | }, 61 | message: { 62 | deleteSuccess: "删除模块成功", 63 | }, 64 | }, 65 | innerProject: { 66 | name: "项目名称", 67 | createTime: "创建时间", 68 | description: "项目简介", 69 | category: "项目所属分类", 70 | principal: "项目负责人", 71 | action: "操作", 72 | add: "添加模块", 73 | delete: "删除", 74 | edit: "编辑", 75 | status: "状态", 76 | num: "项目成员数", 77 | search: { 78 | placeholder: "请输入项目名称", 79 | }, 80 | }, 81 | port: { 82 | name: "接口名称", 83 | url: "接口地址", 84 | method: "接口请求方法", 85 | action: "操作", 86 | sync: "同步", 87 | confirm: { 88 | title: "请确认是否同步接口?", 89 | textYes: "确认", 90 | textNo: "取消", 91 | }, 92 | }, 93 | }; 94 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/acl/mods/action/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @description 功能动作模块 4 | */ 5 | import add from './add'; 6 | import edit from './edit'; 7 | import get from './get'; 8 | import remove from './remove'; 9 | 10 | export class ActionApi { 11 | constructor( 12 | public add: ( 13 | action: acl.Action, 14 | 15 | success?: ({ 16 | data, 17 | ext, 18 | state, 19 | errors, 20 | }: { 21 | data: acl.Action; 22 | ext: ObjectMap; 23 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 24 | errors?: Array; 25 | }) => any, 26 | fail?: (error: string) => any, 27 | ) => void, 28 | 29 | public edit: ( 30 | action: acl.Action, 31 | 32 | success?: ({ 33 | data, 34 | ext, 35 | state, 36 | errors, 37 | }: { 38 | data: void; 39 | ext: ObjectMap; 40 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 41 | errors?: Array; 42 | }) => any, 43 | fail?: (error: string) => any, 44 | ) => void, 45 | 46 | public get: ( 47 | id: number, 48 | 49 | success?: ({ 50 | data, 51 | ext, 52 | state, 53 | errors, 54 | }: { 55 | data: acl.Action; 56 | ext: ObjectMap; 57 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 58 | errors?: Array; 59 | }) => any, 60 | fail?: (error: string) => any, 61 | ) => void, 62 | 63 | public remove: ( 64 | id: number, 65 | 66 | success?: ({ 67 | data, 68 | ext, 69 | state, 70 | errors, 71 | }: { 72 | data: void; 73 | ext: ObjectMap; 74 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 75 | errors?: Array; 76 | }) => any, 77 | fail?: (error: string) => any, 78 | ) => void, 79 | ) {} 80 | } 81 | 82 | export default { 83 | add, 84 | edit, 85 | get, 86 | remove, 87 | } as ActionApi; 88 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/acl/UserPermission.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.acl; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Table; 6 | 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.NoArgsConstructor; 12 | import lombok.experimental.Accessors; 13 | import lombok.experimental.FieldNameConstants; 14 | import lombok.experimental.SuperBuilder; 15 | import tech.riemann.demo.entity.DemoEntity; 16 | 17 | /** 18 | *

19 | * 用户权限关系 20 | *

21 | * 22 | * @author Kerbores 23 | * @since 2021-08-12 24 | */ 25 | @Data 26 | @EqualsAndHashCode(callSuper = true) 27 | @SuperBuilder 28 | @NoArgsConstructor 29 | @AllArgsConstructor 30 | @FieldNameConstants 31 | @Accessors(chain = true) 32 | @Table("t_user_permission") 33 | @Comment("用户权限关系") 34 | @Schema(name = "UserPermission", description = "用户权限关系") 35 | public class UserPermission extends DemoEntity { 36 | 37 | private static final long serialVersionUID = 1L; 38 | 39 | @Schema(description = "动作key", required = true) 40 | @Column("up_action_key") 41 | @Comment("动作key") 42 | private String actionKey; 43 | 44 | @Schema(description = "模块id", required = true) 45 | @Column("up_module_id") 46 | @Comment("模块id") 47 | private Long moduleId; 48 | 49 | @Schema(description = "模块key", required = true) 50 | @Column("up_module_key") 51 | @Comment("模块key") 52 | private String moduleKey; 53 | 54 | @Schema(description = "用户id", required = true) 55 | @Column("up_user_id") 56 | @Comment("用户id") 57 | private Long userId; 58 | 59 | public static final String UP_ACTION_KEY = "up_action_key"; 60 | 61 | public static final String UP_MODULE_ID = "up_module_id"; 62 | 63 | public static final String UP_MODULE_KEY = "up_module_key"; 64 | 65 | public static final String UP_USER_ID = "up_user_id"; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/acl/RolePermission.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.acl; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Table; 6 | 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.NoArgsConstructor; 12 | import lombok.experimental.Accessors; 13 | import lombok.experimental.FieldNameConstants; 14 | import lombok.experimental.SuperBuilder; 15 | import tech.riemann.demo.entity.DemoEntity; 16 | 17 | /** 18 | *

19 | * 角色权限关系表 20 | *

21 | * 22 | * @author Kerbores 23 | * @since 2021-08-12 24 | */ 25 | @Data 26 | @EqualsAndHashCode(callSuper = true) 27 | @SuperBuilder 28 | @NoArgsConstructor 29 | @AllArgsConstructor 30 | @FieldNameConstants 31 | @Accessors(chain = true) 32 | @Table("t_role_permission") 33 | @Comment("角色权限关系表") 34 | @Schema(name = "RolePermission", description = "角色权限关系表") 35 | public class RolePermission extends DemoEntity { 36 | 37 | private static final long serialVersionUID = 1L; 38 | 39 | @Schema(description = "动作key", required = true) 40 | @Column("rp_action_key") 41 | @Comment("动作key") 42 | private String actionKey; 43 | 44 | @Schema(description = "模块id", required = true) 45 | @Column("rp_module_id") 46 | @Comment("模块id") 47 | private Long moduleId; 48 | 49 | @Schema(description = "模块key", required = true) 50 | @Column("rp_module_key") 51 | @Comment("模块key") 52 | private String moduleKey; 53 | 54 | @Schema(description = "角色id", required = true) 55 | @Column("rp_role_id") 56 | @Comment("角色id") 57 | private Long roleId; 58 | 59 | public static final String RP_ACTION_KEY = "rp_action_key"; 60 | 61 | public static final String RP_MODULE_ID = "rp_module_id"; 62 | 63 | public static final String RP_MODULE_KEY = "rp_module_key"; 64 | 65 | public static final String RP_ROLE_ID = "rp_role_id"; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/hanlder/JsonLocalDateLikeHandler.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.hanlder; 2 | 3 | import java.io.IOException; 4 | import java.time.LocalDate; 5 | import java.time.ZoneId; 6 | import java.time.format.DateTimeFormatter; 7 | import java.time.temporal.TemporalAccessor; 8 | import java.util.Locale; 9 | 10 | import org.nutz.castor.Castors; 11 | import org.nutz.json.JsonFormat; 12 | import org.nutz.json.JsonRender; 13 | import org.nutz.json.JsonTypeHandler; 14 | import org.nutz.lang.Mirror; 15 | 16 | /** 17 | * @author wkipy 18 | * 19 | */ 20 | public class JsonLocalDateLikeHandler extends JsonTypeHandler { 21 | 22 | /** 23 | * @param mirror 24 | * @param obj 25 | * @return 26 | * @see org.nutz.json.JsonTypeHandler#supportFromJson(org.nutz.lang.Mirror, 27 | * java.lang.Object) 28 | */ 29 | @Override 30 | public boolean supportFromJson(Mirror mirror, Object obj) { 31 | return mirror.isLocalDateTimeLike(); 32 | } 33 | 34 | @Override 35 | public boolean supportToJson(Mirror mirror, Object obj, JsonFormat jf) { 36 | return mirror.isLocalDateTimeLike(); 37 | } 38 | 39 | @Override 40 | public void toJson(Mirror mirror, Object currentObj, JsonRender r, JsonFormat jf) throws IOException { 41 | String df = jf.getDateFormatRaw(); 42 | if (df == null) 43 | df = "yyyy-MM-dd HH:mm:ss.SSS"; 44 | if (mirror.getType().equals(LocalDate.class)) { 45 | df = "yyyy-MM-dd"; 46 | } 47 | Locale locale = null; 48 | String tmp = jf.getLocale(); 49 | if (tmp != null) 50 | locale = Locale.forLanguageTag(tmp); 51 | else 52 | locale = Locale.getDefault(); 53 | r.string2Json(DateTimeFormatter.ofPattern(df, locale).withZone(ZoneId.systemDefault()).format((TemporalAccessor) currentObj)); 54 | } 55 | 56 | @Override 57 | public Object fromJson(Object obj, Mirror mirror) throws Exception { 58 | return Castors.me().castTo(obj, mirror.getType()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/acl/Action.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.acl; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Name; 6 | import org.nutz.dao.entity.annotation.Table; 7 | 8 | import io.swagger.v3.oas.annotations.media.Schema; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder.Default; 11 | import lombok.Data; 12 | import lombok.EqualsAndHashCode; 13 | import lombok.NoArgsConstructor; 14 | import lombok.experimental.Accessors; 15 | import lombok.experimental.FieldNameConstants; 16 | import lombok.experimental.SuperBuilder; 17 | import tech.riemann.demo.entity.DemoEntity; 18 | 19 | /** 20 | *

21 | * 功能动作 22 | *

23 | * 24 | * @author Kerbores 25 | * @since 2021-08-12 26 | */ 27 | @Data 28 | @EqualsAndHashCode(callSuper = true) 29 | @SuperBuilder 30 | @NoArgsConstructor 31 | @AllArgsConstructor 32 | @FieldNameConstants 33 | @Accessors(chain = true) 34 | @Table("t_action") 35 | @Comment("功能动作") 36 | @Schema(name = "Action", description = "功能动作") 37 | public class Action extends DemoEntity { 38 | 39 | private static final long serialVersionUID = 1L; 40 | 41 | @Schema(description = "动作key", required = true) 42 | @Column("a_key") 43 | @Comment("动作key") 44 | @Name 45 | private String key; 46 | 47 | @Schema(description = "是否内置标识", required = true) 48 | @Column("a_installed") 49 | @Comment("是否内置标识") 50 | @Default 51 | private Boolean installed = false; 52 | 53 | @Schema(description = "归属的模块id", required = true) 54 | @Column("a_module_id") 55 | @Comment("归属的模块id") 56 | private Long moduleId; 57 | 58 | @Schema(description = "动作名称", required = true) 59 | @Column("a_name") 60 | @Comment("动作名称") 61 | private String name; 62 | 63 | public static final String A_KEY = "a_key"; 64 | 65 | public static final String A_INSTALLED = "a_installed"; 66 | 67 | public static final String A_MODULE_ID = "a_module_id"; 68 | 69 | public static final String A_NAME = "a_name"; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/sqlmanager/XmlSqlManager.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao.sqlmanager; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.List; 6 | 7 | import org.nutz.dao.impl.FileSqlManager; 8 | import org.nutz.lang.Xmls; 9 | import org.nutz.log.Log; 10 | import org.nutz.log.Logs; 11 | import org.nutz.resource.NutResource; 12 | import org.nutz.resource.Scans; 13 | import org.w3c.dom.Document; 14 | import org.w3c.dom.Element; 15 | import org.w3c.dom.Node; 16 | import org.w3c.dom.NodeList; 17 | 18 | /** 19 | * @author kerbores 20 | * 21 | */ 22 | public class XmlSqlManager extends FileSqlManager { 23 | 24 | private static final Log log = Logs.get(); 25 | 26 | public XmlSqlManager() {} 27 | 28 | public XmlSqlManager(String... paths) { 29 | super(paths); 30 | } 31 | 32 | @Override 33 | public void refresh() { 34 | if (paths != null) { 35 | for (String path : paths) { 36 | List res = Scans.me().scan(path, "^.+[.]xml$"); 37 | for (NutResource nr : res) { 38 | log.debug("add xml " + nr.getName()); 39 | try { 40 | add(nr.getInputStream()); 41 | } 42 | catch (IOException e) { 43 | log.debug("fail at " + nr.getName(), e); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | public void add(InputStream ins) { 51 | Document doc = Xmls.xml(ins); 52 | doc.normalize(); 53 | Element root = doc.getDocumentElement(); 54 | NodeList nodes = root.getChildNodes(); 55 | for (int i = 0; i < nodes.getLength(); i++) { 56 | Node node = nodes.item(i); 57 | if ("sql".equals(node.getNodeName())) { 58 | Element ele = (Element) node; 59 | String key = ele.getAttribute("key"); 60 | String value = Xmls.getText(ele); 61 | addSql(key, value); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/dto/response/Permission.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.dto.response; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Builder.Default; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * @author Kerbores(kerbores@gmail.com) 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @EqualsAndHashCode(of = {"actionId", "moduleId"}) 19 | public class Permission { 20 | 21 | @Schema(description = "操作key", required = true) 22 | String actionKey; 23 | 24 | @Schema(description = "操作名称", required = true) 25 | String actionName; 26 | 27 | @Schema(description = "操作Id", required = true) 28 | long actionId; 29 | 30 | @Schema(description = "模块key", required = true) 31 | String moduleKey; 32 | 33 | @Schema(description = "模块名称", required = true) 34 | String moduleName; 35 | 36 | @Schema(description = "模块描述") 37 | String moduleDescription; 38 | 39 | @Schema(description = "模块Id") 40 | long moduleId; 41 | 42 | @Default 43 | @Schema(description = "操作选中标志") 44 | boolean actionSelected = true; 45 | 46 | @Default 47 | @Schema(description = "模块选中标志") 48 | boolean moduleSelected = true; 49 | 50 | public ModuleInfo toModuleInfo() { 51 | return ModuleInfo.builder() 52 | .descr(getModuleDescription()) 53 | .moduleName(getModuleName()) 54 | .moduleKey(getModuleKey()) 55 | .moduleId(getModuleId()) 56 | .selected(moduleSelected) 57 | .build(); 58 | } 59 | 60 | public ActionInfo toActionInfo() { 61 | return ActionInfo.builder() 62 | .name(getActionName()) 63 | .key(getActionKey()) 64 | .id(getActionId()) 65 | .selected(actionSelected) 66 | .build(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/sqltpl/impl/velocity/VelocitySqlTpl.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao.sqltpl.impl.velocity; 2 | 3 | import java.io.StringWriter; 4 | 5 | import org.apache.velocity.VelocityContext; 6 | import org.apache.velocity.app.Velocity; 7 | import org.apache.velocity.app.VelocityEngine; 8 | import org.apache.velocity.runtime.RuntimeSingleton; 9 | import org.nutz.dao.DaoException; 10 | import org.nutz.dao.sql.Sql; 11 | import org.nutz.spring.boot.dao.sqltpl.NutSqlTpl; 12 | import org.nutz.spring.boot.dao.sqltpl.VarSetMap; 13 | 14 | /** 15 | * @author kerbores 16 | * 17 | */ 18 | public class VelocitySqlTpl extends NutSqlTpl { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | public VelocitySqlTpl(String source) { 23 | super(source); 24 | } 25 | 26 | /** 27 | * 自定义VelocityEngine 28 | */ 29 | protected static VelocityEngine engine; 30 | 31 | /** 32 | * 渲染一个Sql对象 33 | * 34 | * @param sql 35 | * 需要渲染的Sql实例 36 | * @return 原对象,用于链式调用 37 | */ 38 | public static Sql c(Sql sql) { 39 | VelocityContext context = new VelocityContext(VarSetMap.asCtx(sql)); 40 | StringWriter sw = new StringWriter(); 41 | try { 42 | if (engine == null) { 43 | if (!RuntimeSingleton.isInitialized()) 44 | Velocity.init(); 45 | Velocity.evaluate(context, sw, "sqltpl", sql.getSourceSql()); 46 | } else 47 | engine.evaluate(context, sw, "sqltpl", sql.getSourceSql()); 48 | } 49 | catch (Exception e) { 50 | throw new DaoException(e); 51 | } 52 | sql.setSourceSql(sw.toString()); 53 | return sql; 54 | } 55 | 56 | /** 57 | * 可自定义VelocityEngine,默认使用Velocity.evaluate进行渲染 58 | * 59 | * @param engine 60 | * 自定义的VelocityEngine 61 | */ 62 | public static void setEngine(VelocityEngine engine) { 63 | VelocitySqlTpl.engine = engine; 64 | } 65 | 66 | @Override 67 | protected void render() { 68 | c(this); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/entity/acl/User.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.entity.acl; 2 | 3 | import org.nutz.dao.entity.annotation.Column; 4 | import org.nutz.dao.entity.annotation.Comment; 5 | import org.nutz.dao.entity.annotation.Name; 6 | import org.nutz.dao.entity.annotation.Table; 7 | 8 | import club.zhcs.auth.AuthUser; 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | import lombok.AllArgsConstructor; 11 | import lombok.Data; 12 | import lombok.EqualsAndHashCode; 13 | import lombok.NoArgsConstructor; 14 | import lombok.experimental.Accessors; 15 | import lombok.experimental.FieldNameConstants; 16 | import lombok.experimental.SuperBuilder; 17 | import tech.riemann.demo.entity.DemoEntity; 18 | 19 | /** 20 | *

21 | * 用户 22 | *

23 | * 24 | * @author Kerbores 25 | * @since 2021-08-12 26 | */ 27 | @Data 28 | @EqualsAndHashCode(callSuper = true) 29 | @SuperBuilder 30 | @NoArgsConstructor 31 | @AllArgsConstructor 32 | @FieldNameConstants 33 | @Accessors(chain = true) 34 | @Table("t_user") 35 | @Comment("用户") 36 | @Schema(name = "User", description = "用户") 37 | public class User extends DemoEntity { 38 | 39 | private static final long serialVersionUID = 1L; 40 | 41 | @Schema(description = "用户名", required = true) 42 | @Column("u_name") 43 | @Comment("用户名") 44 | @Name 45 | private String name; 46 | 47 | @Schema(description = "密码", required = true) 48 | @Column("u_password") 49 | @Comment("密码") 50 | private String password; 51 | 52 | @Schema(description = "手机号", required = true) 53 | @Column("u_mobile") 54 | @Comment("手机号") 55 | private String mobile; 56 | 57 | /** 58 | * @return 59 | */ 60 | public AuthUser toUser() { 61 | return AuthUser.builder() 62 | .userName(getName()) 63 | .password("nutz-demo") 64 | .build() 65 | .token() 66 | .addExt("user", this); 67 | } 68 | 69 | public static final String U_NAME = "u_name"; 70 | 71 | public static final String U_PWD = "u_pwd"; 72 | 73 | public static final String U_MOBILE = "u_mobile"; 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/sqltpl/impl/beetl/BeetlSqlTpl.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao.sqltpl.impl.beetl; 2 | 3 | import java.io.IOException; 4 | 5 | import org.beetl.core.Configuration; 6 | import org.beetl.core.GroupTemplate; 7 | import org.beetl.core.Template; 8 | import org.nutz.dao.sql.Sql; 9 | import org.nutz.lang.Lang; 10 | import org.nutz.spring.boot.dao.sqltpl.NutSqlTpl; 11 | import org.nutz.spring.boot.dao.sqltpl.VarSetMap; 12 | 13 | /** 14 | * @author kerbores 15 | * 16 | */ 17 | public class BeetlSqlTpl extends NutSqlTpl { 18 | 19 | private static final long serialVersionUID = 1L; 20 | 21 | public BeetlSqlTpl(String source) { 22 | super(source); 23 | } 24 | 25 | /** 26 | * 自定义GroupTemplate 27 | */ 28 | protected static GroupTemplate gt; 29 | 30 | /** 31 | * 渲染一个Sql对象 32 | * 33 | * @param sql 34 | * 需要渲染的Sql实例 35 | * @return 原对象,用于链式调用 36 | */ 37 | public static Sql c(Sql sql) { 38 | Object source = sql.getSourceSql(); 39 | Template t = gt().getTemplate(source); 40 | t.binding(VarSetMap.asCtx(sql)); 41 | String n = t.render(); 42 | sql.setSourceSql(n); 43 | return sql; 44 | } 45 | 46 | /** 47 | * 自定义设置GroupTemplate 48 | * 49 | * @param gt 50 | * 自定义的GroupTemplate 51 | */ 52 | public static void setGroupTemplate(GroupTemplate gt) { 53 | BeetlSqlTpl.gt = gt; 54 | } 55 | 56 | /** 57 | * 获取GroupTemplate 58 | * 59 | * @return GroupTemplate实例,如果没有自定义,就生成一个默认的 60 | */ 61 | public static GroupTemplate gt() { 62 | if (gt == null) { 63 | Configuration cfg; 64 | try (ClasspathStringResourceLoader resourceLoader = new ClasspathStringResourceLoader()) { 65 | cfg = Configuration.defaultConfiguration(); 66 | gt = new GroupTemplate(resourceLoader, cfg); 67 | } 68 | catch (IOException e) { 69 | throw Lang.wrapThrow(e); 70 | } 71 | } 72 | return gt; 73 | } 74 | 75 | @Override 76 | protected void render() { 77 | c(this); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/wechat/api.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | 4 | declare namespace wechat { 5 | export class TieSocialLoginUserDataObject { 6 | /** 渠道 */ 7 | channel: 'MP' | 'MINIAPP' | 'WECHAT_SCAN' | 'WECHAT'; 8 | 9 | /** 验证码 */ 10 | code: string; 11 | 12 | /** 手机号 */ 13 | mobile: string; 14 | 15 | /** openid */ 16 | openid: string; 17 | } 18 | 19 | export class WxLogin { 20 | /** appid */ 21 | appid: string; 22 | 23 | /** href */ 24 | href?: string; 25 | 26 | /** id */ 27 | id: string; 28 | 29 | /** redirect_uri */ 30 | redirect_uri: string; 31 | 32 | /** scope */ 33 | scope: string; 34 | 35 | /** self_redirect */ 36 | self_redirect: boolean; 37 | 38 | /** state */ 39 | state: string; 40 | 41 | /** style */ 42 | style?: 'black' | 'white'; 43 | } 44 | 45 | export class Pagination { 46 | /** dataList */ 47 | dataList: Array; 48 | 49 | /** first */ 50 | first?: boolean; 51 | 52 | /** last */ 53 | last?: boolean; 54 | 55 | /** offset */ 56 | offset?: number; 57 | 58 | /** pageCount */ 59 | pageCount?: number; 60 | 61 | /** pageNumber */ 62 | pageNumber?: number; 63 | 64 | /** pageSize */ 65 | pageSize?: number; 66 | 67 | /** paras */ 68 | paras?: ObjectMap; 69 | 70 | /** recordCount */ 71 | recordCount?: number; 72 | } 73 | 74 | export class Result { 75 | /** 响应数据 */ 76 | data: T0; 77 | 78 | /** 错误信息列表 */ 79 | errors?: Array; 80 | 81 | /** 响应扩展数据 */ 82 | ext?: ObjectMap; 83 | 84 | /** 响应状态 */ 85 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 86 | 87 | /** success */ 88 | success?: boolean; 89 | } 90 | 91 | export class VXETableSaveDTO { 92 | /** 新增记录数据 */ 93 | insertRecords: Array; 94 | 95 | /** 删除记录数据 */ 96 | removeRecords: Array; 97 | 98 | /** 更新记录数据 */ 99 | updateRecords: Array; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /demo/src/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "antdv-ts-vue-cli-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@ant-design-vue/pro-layout": "^1.0.8", 12 | "@form-create/ant-design-vue": "^2.5.11", 13 | "ant-design-vue": "^1.7.8", 14 | "axios": "^0.21.4", 15 | "core-js": "^3.18.3", 16 | "exceljs": "^4.3.0", 17 | "nprogress": "^0.2.0", 18 | "qs": "^6.10.1", 19 | "store": "^2.0.12", 20 | "vue": "^2.6.14", 21 | "vue-class-component": "^7.2.6", 22 | "vue-i18n": "^8.26.5", 23 | "vue-particles": "^1.0.9", 24 | "vue-property-decorator": "^9.1.2", 25 | "vue-router": "^3.5.2", 26 | "vue-svg-component-runtime": "^1.0.1", 27 | "vuex": "^3.6.2", 28 | "vuex-class": "^0.3.2", 29 | "vuex-module-decorators": "^1.0.1", 30 | "vxe-table": "^3.4.15", 31 | "vxe-table-plugin-antd": "^1.11.3", 32 | "vxe-table-plugin-export-xlsx": "^2.2.2", 33 | "xe-utils": "^3.5.4" 34 | }, 35 | "devDependencies": { 36 | "@typescript-eslint/eslint-plugin": "^4.33.0", 37 | "@typescript-eslint/parser": "^4.33.0", 38 | "@vue/cli-plugin-babel": "~4.5.14", 39 | "@vue/cli-plugin-eslint": "~4.5.14", 40 | "@vue/cli-plugin-router": "~4.5.14", 41 | "@vue/cli-plugin-typescript": "~4.5.14", 42 | "@vue/cli-plugin-vuex": "~4.5.14", 43 | "@vue/cli-service": "~4.5.14", 44 | "@vue/eslint-config-prettier": "^6.0.0", 45 | "@vue/eslint-config-typescript": "^7.0.0", 46 | "eslint": "^7.32.0", 47 | "eslint-plugin-prettier": "^3.4.1", 48 | "eslint-plugin-vue": "^7.16.0", 49 | "less": "^3.13.1", 50 | "less-loader": "^7.3.0", 51 | "lint-staged": "^10.5.4", 52 | "pont-engine": "1.0.13", 53 | "prettier": "^2.4.1", 54 | "typescript": "~4.4.4", 55 | "vue-svg-icon-loader": "^2.1.1", 56 | "vue-template-compiler": "^2.6.14", 57 | "webpack-theme-color-replacer": "^1.3.26" 58 | }, 59 | "gitHooks": { 60 | "pre-commit": "lint-staged" 61 | }, 62 | "lint-staged": { 63 | "*.{js,jsx,vue,ts,tsx}": [ 64 | "vue-cli-service lint", 65 | "git add" 66 | ] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /demo/src/frontend/src/api/wechat/mods/socialLogin/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /** 3 | * @description Social Login Controller 4 | */ 5 | import authUrl, { AuthUrlParams } from './authUrl'; 6 | import bind from './bind'; 7 | import qrOption, { QrOptionParams } from './qrOption'; 8 | import oauth, { OauthParams } from './oauth'; 9 | 10 | export class SocialLoginApi { 11 | constructor( 12 | public authUrl: ( 13 | params: AuthUrlParams, 14 | success?: ({ 15 | data, 16 | ext, 17 | state, 18 | errors, 19 | }: { 20 | data: string; 21 | ext: ObjectMap; 22 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 23 | errors?: Array; 24 | }) => any, 25 | fail?: (error: string) => any, 26 | ) => void, 27 | 28 | public bind: ( 29 | socialLoginBindDTO: wechat.TieSocialLoginUserDataObject, 30 | 31 | success?: ({ 32 | data, 33 | ext, 34 | state, 35 | errors, 36 | }: { 37 | data: string; 38 | ext: ObjectMap; 39 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 40 | errors?: Array; 41 | }) => any, 42 | fail?: (error: string) => any, 43 | ) => void, 44 | 45 | public qrOption: ( 46 | params: QrOptionParams, 47 | success?: ({ 48 | data, 49 | ext, 50 | state, 51 | errors, 52 | }: { 53 | data: wechat.WxLogin; 54 | ext: ObjectMap; 55 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 56 | errors?: Array; 57 | }) => any, 58 | fail?: (error: string) => any, 59 | ) => void, 60 | 61 | public oauth: ( 62 | channel: 'MP' | 'MINIAPP' | 'WECHAT_SCAN' | 'WECHAT', 63 | 64 | params: OauthParams, 65 | success?: ({ 66 | data, 67 | ext, 68 | state, 69 | errors, 70 | }: { 71 | data: string; 72 | ext: ObjectMap; 73 | state: 'SUCCESS' | 'FAIL' | 'EXCEPTION'; 74 | errors?: Array; 75 | }) => any, 76 | fail?: (error: string) => any, 77 | ) => void, 78 | ) {} 79 | } 80 | 81 | export default { 82 | authUrl, 83 | bind, 84 | qrOption, 85 | oauth, 86 | } as SocialLoginApi; 87 | -------------------------------------------------------------------------------- /demo/src/main/java/tech/riemann/demo/controller/acl/ActionController.java: -------------------------------------------------------------------------------- 1 | package tech.riemann.demo.controller.acl; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.DeleteMapping; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.PutMapping; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import club.zhcs.Result; 13 | import club.zhcs.apm.APM; 14 | import io.swagger.v3.oas.annotations.Operation; 15 | import io.swagger.v3.oas.annotations.Parameter; 16 | import io.swagger.v3.oas.annotations.tags.Tag; 17 | import tech.riemann.demo.entity.acl.Action; 18 | import tech.riemann.demo.service.acl.ActionService; 19 | 20 | /** 21 | * @author Kerbores(kerbores@gmail.com) 22 | */ 23 | @RestController 24 | @Tag(name = "Action", description = "功能动作模块") 25 | @APM 26 | public class ActionController { 27 | 28 | @Autowired 29 | ActionService actionService; 30 | 31 | @GetMapping("action/{id}") 32 | @Operation(summary = "根据id获取功能动作详情") 33 | public Result get(@Parameter(description = "功能动作id") @PathVariable("id") long id) { 34 | return Result.success(actionService.fetch(id)); 35 | } 36 | 37 | @PostMapping("action") 38 | @Operation(summary = "新增功能动作") 39 | public Result add(@Parameter(description = "功能动作数据") @RequestBody Action action) { 40 | return Result.success(actionService.insert(action)); 41 | } 42 | 43 | @PutMapping("action") 44 | @Operation(summary = "根据id更新功能动作") 45 | public Result edit(@Parameter(description = "功能动作数据") @RequestBody Action action) { 46 | return actionService.update(action, "name", "key") ? Result.success() : Result.fail("更新操作失败"); 47 | } 48 | 49 | @DeleteMapping("action/{id}") 50 | @Operation(summary = "根据id获取功能动作详情") 51 | public Result delete(@Parameter(description = "功能动作id") @PathVariable("id") long id) { 52 | return actionService.delete(id) == 1 ? Result.success() : Result.fail("删除操作失败"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /demo/src/frontend/src/locales/lang/zh-CN.ts: -------------------------------------------------------------------------------- 1 | import antd from "ant-design-vue/lib/locale-provider/zh_CN"; 2 | import momentCN from "moment/locale/zh-cn"; 3 | import config from "@/core/config"; 4 | 5 | const components = { 6 | antLocale: antd, 7 | momentName: "zh-cn", 8 | momentLocale: momentCN, 9 | }; 10 | import page from "./zhCN/page"; 11 | const locale: Record = { 12 | message: "-", 13 | "acl.user": "用户管理", 14 | "acl.role": "角色管理", 15 | "acl.permission": "权限管理", 16 | acl: "访问控制", 17 | dictionary: "数据字典", 18 | dashboard: "仪表盘", 19 | 20 | "usercenter.logout": "退出登录", 21 | usercenter: "用户中心", 22 | "usercenter.setting": "个人设置", 23 | 24 | "layouts.usermenu.dialog.title": "提示", 25 | "layouts.usermenu.dialog.content": "您确定要退出登录?", 26 | 27 | "app.setting.pagestyle": "页面样式", 28 | "app.setting.pagestyle.light": "明亮模式", 29 | "app.setting.pagestyle.dark": "黑暗模式", 30 | "app.setting.pagestyle.realdark": "纯黑模式", 31 | "app.setting.themecolor": "主题色", 32 | "app.setting.navigationmode": "导航模式", 33 | "app.setting.content-width": "内容宽度", 34 | "app.setting.fixedheader": "固定头部", 35 | "app.setting.fixedsidebar": "固定侧栏", 36 | "app.setting.sidemenu": "侧面菜单", 37 | "app.setting.topmenu": "顶部菜单", 38 | "app.setting.content-width.fixed": "固定", 39 | "app.setting.content-width.fluid": "流式", 40 | "app.setting.othersettings": "其他设置", 41 | "app.setting.weakmode": "色弱模式", 42 | "app.setting.copy": "复制设置", 43 | "app.setting.loading": "正在加载主题", 44 | "app.setting.copyinfo": "复制成功,请替换 src/models/setting.js 中的设置内容", 45 | "app.setting.production.hint": "设置面板仅在开发环境显示,请自行修改", 46 | "app.setting.fixedsidebar.hint": "固定侧栏", 47 | 48 | "app.setting.themecolor.daybreak": "拂晓蓝", 49 | "app.setting.themecolor.dust": "薄暮", 50 | "app.setting.themecolor.volcano": "火山", 51 | "app.setting.themecolor.sunset": "日暮", 52 | "app.setting.themecolor.cyan": "明青", 53 | "app.setting.themecolor.green": "极光绿", 54 | "app.setting.themecolor.geekblue": "极客蓝", 55 | "app.setting.themecolor.purple": "酱紫", 56 | 57 | "global.http.error": "错误", 58 | "global.http.forbidden": "没有权限", 59 | "global.http.notFount": "资源不存在", 60 | "global.http.configError": "配置错误", 61 | }; 62 | // eslint-disable-next-line 63 | (locale as any)["app.setting.themecolor." + config.primaryColor] = config.primaryColorName; 64 | 65 | export default { 66 | ...components, 67 | ...locale, 68 | page, 69 | }; 70 | -------------------------------------------------------------------------------- /demo/src/frontend/src/core/index.ts: -------------------------------------------------------------------------------- 1 | import * as types from "@/store/mutation-types"; 2 | import { AppModule } from "@/store/modules/app"; 3 | import { UserModule } from "@/store/modules/user"; 4 | import config from "@/core/config"; 5 | import storage from "store"; 6 | import { updateTheme } from "@ant-design-vue/pro-layout"; 7 | 8 | export const colorList = [ 9 | { 10 | key: "dustRed", 11 | color: "#F5222D", 12 | }, 13 | { 14 | key: "volcano", 15 | color: "#FA541C", 16 | }, 17 | { 18 | key: "sunsetOrange", 19 | color: "#FAAD14", 20 | }, 21 | { 22 | key: "cyan", 23 | color: "#13C2C2", 24 | }, 25 | { 26 | key: "polarGreen", 27 | color: "#52C41A", 28 | }, 29 | { 30 | key: "daybreakBlue", 31 | color: "#19ACE9", 32 | }, 33 | { 34 | key: "geekBlue", 35 | color: "#2F54EB", 36 | }, 37 | { 38 | key: "goldenPurple", 39 | color: "#722ED1", 40 | }, 41 | ]; 42 | 43 | export default function Initializer(): void { 44 | updateTheme( 45 | storage.get( 46 | types.TOGGLE_COLOR, 47 | storage.get(types.TOGGLE_COLOR, config.primaryColor) 48 | ) 49 | ); 50 | AppModule.SIDEBAR_TYPE(storage.get(types.SIDEBAR_TYPE, true)); 51 | AppModule.TOGGLE_COLOR(storage.get(types.TOGGLE_COLOR, config.primaryColor)); 52 | AppModule.TOGGLE_NAV_THEME(storage.get(types.NAV_THEME, config.navTheme)); 53 | AppModule.TOGGLE_LAYOUT(storage.get(types.TOGGLE_LAYOUT, config.layout)); 54 | AppModule.TOGGLE_FIXED_HEADER( 55 | storage.get(types.TOGGLE_FIXED_HEADER, config.fixedHeader) 56 | ); 57 | AppModule.TOGGLE_FIXED_SIDEBAR( 58 | storage.get(types.TOGGLE_FIXED_SIDEBAR, config.autoHideHeader) 59 | ); 60 | AppModule.TOGGLE_CONTENT_WIDTH( 61 | storage.get(types.CONTENT_WIDTH_TYPE, config.contentWidth) 62 | ); 63 | AppModule.TOGGLE_HIDE_HEADER( 64 | storage.get(types.TOGGLE_HIDE_HEADER, config.autoHideHeader) 65 | ); 66 | AppModule.TOGGLE_WEAK(storage.get(types.TOGGLE_WEAK, config.colorWeak)); 67 | AppModule.TOGGLE_MULTI_TAB( 68 | storage.get(types.TOGGLE_MULTI_TAB, config.multiTab) 69 | ); 70 | AppModule.APP_LANGUAGE(storage.get(types.APP_LANGUAGE, config.language)); 71 | UserModule.login(storage.get(types.LOGIN_USER, {})); 72 | } 73 | 74 | export const updateColorWeak = (colorWeak: boolean): void => { 75 | colorWeak 76 | ? document.body.classList.add("colorWeak") 77 | : document.body.classList.remove("colorWeak"); 78 | }; 79 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/NutzDatabaseInitializer.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao; 2 | 3 | import java.util.Arrays; 4 | 5 | import javax.annotation.PostConstruct; 6 | 7 | import org.nutz.dao.Dao; 8 | import org.nutz.dao.util.Daos; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.autoconfigure.AutoConfiguration; 11 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 13 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 14 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 15 | 16 | /** 17 | * @author kerbores(kerbores@gmail.com) 18 | * 19 | */ 20 | @AutoConfiguration 21 | @ConditionalOnBean({Dao.class}) 22 | @ConditionalOnExpression("${nutz.dao.enabled:true}") 23 | @EnableConfigurationProperties(NutzDaoAutoConfigurationProperties.class) 24 | @AutoConfigureAfter({NutzDaoAutoConfiguration.class}) 25 | public class NutzDatabaseInitializer { 26 | 27 | @Autowired 28 | private Dao dao; 29 | 30 | @Autowired 31 | NutzDaoAutoConfigurationProperties properties; 32 | 33 | @PostConstruct 34 | public void create() { 35 | Daos.CHECK_COLUMN_NAME_KEYWORD = properties.getGlobal().isCheckColumnNameKeyword(); 36 | Daos.FORCE_HUMP_COLUMN_NAME = properties.getGlobal().isForceHumpColumnName(); 37 | Daos.FORCE_UPPER_COLUMN_NAME = properties.getGlobal().isForceUpperColumnName(); 38 | Daos.FORCE_WRAP_COLUMN_NAME = properties.getGlobal().isForceWrapColumnName(); 39 | Daos.DEFAULT_VARCHAR_WIDTH = properties.getGlobal().getDefaultVarcharWidth(); 40 | boolean create = properties.getRuntime().isCreate(); 41 | boolean migration = properties.getRuntime().isMigration(); 42 | Arrays.stream(properties.getRuntime().getBasepackage()).forEach(pkg -> { 43 | if (create) { 44 | Daos.createTablesInPackage(dao, pkg, properties.getRuntime().isFoceCreate()); 45 | } 46 | if (migration) { 47 | Daos.migration(dao, 48 | pkg, 49 | properties.getRuntime().isAddColumn(), 50 | properties.getRuntime().isDeleteColumn(), 51 | properties.getRuntime().isCheckIndex()); 52 | } 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/request/NutzClientHttpResponse.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.request; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | import org.nutz.http.Response; 8 | import org.nutz.lang.Strings; 9 | import org.springframework.http.HttpHeaders; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.client.AbstractClientHttpResponse; 12 | 13 | import lombok.AllArgsConstructor; 14 | import lombok.Data; 15 | import lombok.EqualsAndHashCode; 16 | 17 | /** 18 | * @author kerbores 19 | * 20 | */ 21 | @Data 22 | @AllArgsConstructor 23 | @EqualsAndHashCode(callSuper = true) 24 | public class NutzClientHttpResponse extends AbstractClientHttpResponse { 25 | 26 | Response response; 27 | 28 | /** 29 | * @return 30 | * @throws IOException 31 | * @see org.springframework.http.client.ClientHttpResponse#getRawStatusCode() 32 | */ 33 | @Override 34 | public int getRawStatusCode() throws IOException { 35 | return response.getStatus(); 36 | } 37 | 38 | /** 39 | * @return 40 | * @throws IOException 41 | * @see org.springframework.http.client.ClientHttpResponse#getStatusText() 42 | */ 43 | @Override 44 | public String getStatusText() throws IOException { 45 | return HttpStatus.valueOf(response.getStatus()).getReasonPhrase(); 46 | } 47 | 48 | /** 49 | * 50 | * @see org.springframework.http.client.ClientHttpResponse#close() 51 | */ 52 | @Override 53 | public void close() { 54 | // DO NOTHING 55 | } 56 | 57 | /** 58 | * @return 59 | * @throws IOException 60 | * @see org.springframework.http.HttpInputMessage#getBody() 61 | */ 62 | @Override 63 | public InputStream getBody() throws IOException { 64 | return new ByteArrayInputStream(response.getContent().getBytes()); 65 | } 66 | 67 | /** 68 | * @return 69 | * @see org.springframework.http.HttpMessage#getHeaders() 70 | */ 71 | @Override 72 | public HttpHeaders getHeaders() { 73 | HttpHeaders headers = new HttpHeaders(); 74 | response.getHeader().keys().stream().forEach(key -> { 75 | if (Strings.isNotBlank(key)) { 76 | response.getHeader().getValues(key).stream().forEach(item -> headers.add(key, item)); 77 | } 78 | }); 79 | return headers; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/nutz/spring/boot/dao/sqltpl/impl/freemarker/FreeMarkerSqlTpl.java: -------------------------------------------------------------------------------- 1 | package org.nutz.spring.boot.dao.sqltpl.impl.freemarker; 2 | 3 | import java.io.IOException; 4 | import java.io.StringReader; 5 | import java.io.StringWriter; 6 | import java.util.Map; 7 | 8 | import org.nutz.dao.sql.Sql; 9 | import org.nutz.lang.Lang; 10 | import org.nutz.spring.boot.dao.sqltpl.NutSqlTpl; 11 | import org.nutz.spring.boot.dao.sqltpl.VarSetMap; 12 | 13 | import freemarker.template.Configuration; 14 | import freemarker.template.Template; 15 | import freemarker.template.TemplateException; 16 | import freemarker.template.TemplateExceptionHandler; 17 | 18 | /** 19 | * @author kerbores 20 | * 21 | */ 22 | public class FreeMarkerSqlTpl extends NutSqlTpl { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | public FreeMarkerSqlTpl(String source) { 27 | super(source); 28 | } 29 | 30 | /** 31 | * 自定义Configuration 32 | */ 33 | protected static Configuration cfg; 34 | 35 | /** 36 | * 渲染一个Sql对象 37 | * 38 | * @param sql 39 | * 需要渲染的Sql实例 40 | * @return 原对象,用于链式调用 41 | */ 42 | public static Sql c(Sql sql) { 43 | try { 44 | Template t = new Template("sqltpl", new StringReader(sql.getSourceSql()), cfg); 45 | Map ctx = VarSetMap.asCtx(sql); 46 | StringWriter sw = new StringWriter(); 47 | t.process(ctx, sw); 48 | sql.setSourceSql(sw.toString()); 49 | } 50 | catch (IOException | TemplateException e) { 51 | throw Lang.wrapThrow(e); 52 | } 53 | 54 | return sql; 55 | } 56 | 57 | /** 58 | * 获取Configuration 59 | * 60 | * @return Configuration实例,如果没有自定义的,就生成一个默认的 61 | */ 62 | public static Configuration cfg() { 63 | if (cfg == null) { 64 | Configuration cfg = new Configuration(Configuration.VERSION_2_3_26); 65 | cfg.setDefaultEncoding("UTF-8"); 66 | cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); 67 | FreeMarkerSqlTpl.cfg = cfg; 68 | } 69 | return cfg; 70 | } 71 | 72 | /** 73 | * 设置Configuration 74 | * 75 | * @param cfg 76 | * 自定义Configuration 77 | */ 78 | public static void setConfiguration(Configuration cfg) { 79 | FreeMarkerSqlTpl.cfg = cfg; 80 | } 81 | 82 | @Override 83 | protected void render() { 84 | c(this); 85 | } 86 | } 87 | --------------------------------------------------------------------------------