├── .dockerignore ├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE.txt ├── MuYun轻代码平台能力一览.pdf ├── README.md ├── README_QUARKUS.md ├── build.gradle.kts ├── compose.yaml ├── config └── checkstyle │ └── checkstyle.xml ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── muyun-authorization ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── authorization │ └── AuthorizationService.java ├── muyun-boot ├── build.gradle.kts └── src │ ├── main │ ├── docker │ │ ├── Dockerfile.jvm │ │ ├── Dockerfile.legacy-jar │ │ ├── Dockerfile.native │ │ └── Dockerfile.native-micro │ ├── java │ │ └── net │ │ │ └── ximatai │ │ │ └── muyun │ │ │ └── boot │ │ │ └── MainApp.java │ └── resources │ │ ├── META-INF │ │ └── resources │ │ │ └── eventbus.html │ │ ├── application.yml │ │ └── banner.txt │ ├── native-test │ └── java │ │ └── net │ │ └── ximatai │ │ └── muyun │ │ └── test │ │ └── GreetingResourceIT.java │ └── test │ └── java │ └── net │ └── ximatai │ └── muyun │ └── test │ ├── JdbiTest.java │ ├── auth │ ├── TestAuth.java │ └── TestDataAuth.java │ ├── core │ ├── TestArchiveWhenDelete.java │ ├── TestAuthAbility.java │ ├── TestBaseColumns.java │ ├── TestBaseScaffold.java │ ├── TestBasicCURD.java │ ├── TestComplexityCreate.java │ ├── TestCustomSqlQuery.java │ ├── TestDataBroadcastAbility.java │ ├── TestDataCheckAbility.java │ ├── TestDesensitizationAbility.java │ ├── TestFileAbility.java │ ├── TestGroupQuery.java │ ├── TestMainAndChildren.java │ ├── TestNaked.java │ ├── TestQuery.java │ ├── TestRange.java │ ├── TestReference.java │ ├── TestSMEncryptor.java │ ├── TestSecurityAbility.java │ ├── TestSerialCodeDemo.java │ ├── TestSetTypeWithEnum.java │ ├── TestSoftDelete.java │ ├── TestSortAbility.java │ ├── TestTimeFormatCR.java │ ├── TestTreeAbility.java │ └── TestWildcardPath.java │ ├── database │ ├── TableBuilderTest.java │ └── TestDatabaseOperations.java │ ├── fileserver │ ├── TestFileCRUD.java │ ├── TestFileDownload.java │ ├── TestFileGet.java │ ├── TestFileServer.java │ └── TestGetByName.java │ ├── plaform │ ├── TestAppConf.java │ ├── TestDictController.java │ ├── TestMenu.java │ ├── TestMessage.java │ ├── TestModuleAliasUpdate.java │ ├── TestModuleAndAction.java │ ├── TestNoticeController.java │ ├── TestOnlineUser.java │ ├── TestOrgAndDept.java │ ├── TestRegionController.java │ ├── TestUser.java │ ├── TestUserAndMenu.java │ ├── TestUserAndRole.java │ └── TestUserPasswordComplexity.java │ ├── testcontainers │ └── PostgresTestResource.java │ └── util │ └── TreeBuilderTest.java ├── muyun-core-uni ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── ability │ └── uni │ ├── IDatabaseUniAbility.java │ └── curd │ ├── ICURDUniAbility.java │ └── ICreateAbilityUni.java ├── muyun-core ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ ├── MuYunConst.java │ ├── RouterFilterPriority.java │ ├── ability │ ├── IArchiveWhenDelete.java │ ├── IAuthAbility.java │ ├── IChildAbility.java │ ├── IChildrenAbility.java │ ├── ICodeGenerateAbility.java │ ├── IDataBroadcastAbility.java │ ├── IDatabaseAbility.java │ ├── IDatabaseAbilityStd.java │ ├── IDesensitizationAbility.java │ ├── IFileAbility.java │ ├── ILabelAbility.java │ ├── IMetadataAbility.java │ ├── IReferableAbility.java │ ├── IReferenceAbility.java │ ├── IRuntimeAbility.java │ ├── ISecurityAbility.java │ ├── ISoftDeleteAbility.java │ ├── ISortAbility.java │ ├── ITableCreateAbility.java │ ├── ITreeAbility.java │ └── curd │ │ └── std │ │ ├── ICURDAbility.java │ │ ├── ICreateAbility.java │ │ ├── ICustomSelectSqlAbility.java │ │ ├── IDataCheckAbility.java │ │ ├── IDeleteAbility.java │ │ ├── IQueryAbility.java │ │ ├── ISelectAbility.java │ │ └── IUpdateAbility.java │ ├── base │ ├── BaseBusinessTable.java │ └── BaseScaffold.java │ ├── core │ ├── ObjectMapperConfig.java │ ├── Scaffold.java │ ├── config │ │ ├── FrontendConfig.java │ │ ├── FrontendItem.java │ │ ├── IProfile.java │ │ ├── MuYunConfig.java │ │ ├── ProfileMode.java │ │ ├── Redirect.java │ │ └── WebConfig.java │ ├── desensitization │ │ ├── Desensitizer.java │ │ ├── IDesensitizationAlgorithm.java │ │ ├── MaskEmailAlgorithm.java │ │ ├── MaskMiddleAlgorithm.java │ │ └── MaskPhoneNumberAlgorithm.java │ ├── exception │ │ ├── IToFrontendException.java │ │ ├── InvalidSignatureException.java │ │ ├── MuYunException.java │ │ ├── MyException.java │ │ ├── PermsException.java │ │ └── QueryException.java │ ├── global │ │ ├── DatabindExceptionMapper.java │ │ └── GlobalExceptionHandler.java │ └── security │ │ ├── AbstractEncryptor.java │ │ └── SMEncryptor.java │ ├── http │ ├── AuthorizationFilter.java │ ├── LogFilter.java │ ├── RedirectRouter.java │ ├── RootFilter.java │ ├── SockJsBridgeRouter.java │ └── StaticResourcesRouter.java │ ├── model │ ├── ApiRequest.java │ ├── BatchResult.java │ ├── CheckConfig.java │ ├── ChildTableInfo.java │ ├── DataChangeChannel.java │ ├── IRuntimeUser.java │ ├── PageResult.java │ ├── QueryGroup.java │ ├── QueryItem.java │ ├── ReferenceInfo.java │ ├── SortColumn.java │ ├── TreeNode.java │ ├── code │ │ ├── CodeGenerateConfig.java │ │ ├── DateCodePart.java │ │ ├── ICodePart.java │ │ ├── SerialCodePart.java │ │ ├── SimpleCodePart.java │ │ └── TransformCodePart.java │ └── log │ │ ├── LogAccessItem.java │ │ ├── LogItem.java │ │ └── LogLoginItem.java │ ├── service │ ├── IAuthorizationService.java │ ├── ILogAccess.java │ ├── ILogError.java │ ├── ILogLogin.java │ └── IRuntimeProvider.java │ └── util │ ├── StringUtil.java │ ├── TreeBuilder.java │ └── UserAgentParser.java ├── muyun-database-std ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── database │ └── std │ ├── DBInfoProvider.java │ ├── DataAccessStd.java │ ├── JdbiProducer.java │ ├── argument │ ├── List2JsonArgumentFactory.java │ ├── Map2JsonArgumentFactory.java │ ├── PgArrayArgumentFactory.java │ └── StringArrayArgumentFactory.java │ └── mapper │ ├── MyPgMapMapper.java │ ├── PgArrayToListMapper.java │ └── PgArrayToListMapperFactory.java ├── muyun-database-uni ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── database │ └── uni │ ├── DataAccessUni.java │ ├── IDatabaseAccessUni.java │ └── tool │ └── TupleTool.java ├── muyun-database ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── database │ ├── IDBInfoProvider.java │ ├── IDatabaseOperations.java │ ├── IDatabaseOperationsStd.java │ ├── builder │ ├── Column.java │ ├── ColumnType.java │ ├── IColumnTypeTransform.java │ ├── Index.java │ ├── TableBase.java │ ├── TableBuilder.java │ └── TableWrapper.java │ ├── exception │ └── MyDatabaseException.java │ ├── metadata │ ├── DBColumn.java │ ├── DBIndex.java │ ├── DBInfo.java │ ├── DBSchema.java │ └── DBTable.java │ └── tool │ └── DateTool.java ├── muyun-fileserver ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── fileserver │ ├── FileInfoEntity.java │ ├── FileServer.java │ ├── FileServerConfig.java │ ├── FileServerRegister.java │ ├── FileService.java │ ├── IFileService.java │ └── exception │ └── FileException.java ├── muyun-log ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── log │ ├── LogAccessController.java │ ├── LogBaseController.java │ ├── LogErrorController.java │ └── LogLoginController.java ├── muyun-platform ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── platform │ ├── PlatformConst.java │ ├── ScaffoldForPlatform.java │ ├── ability │ └── IModuleRegisterAbility.java │ ├── controller │ ├── AppConfController.java │ ├── AuthorizationController.java │ ├── DepartmentController.java │ ├── DictCategoryController.java │ ├── DictController.java │ ├── InboxController.java │ ├── MenuController.java │ ├── MenuSchemaController.java │ ├── MessageController.java │ ├── ModuleActionController.java │ ├── ModuleController.java │ ├── MsgConfigController.java │ ├── NoticeController.java │ ├── NoticeReadController.java │ ├── OnlineController.java │ ├── OrganizationController.java │ ├── OutboxController.java │ ├── ReceiveController.java │ ├── RegionController.java │ ├── RoleActionController.java │ ├── RoleController.java │ ├── RuntimeController.java │ ├── SsoController.java │ ├── SupervisionRegionController.java │ ├── UserController.java │ ├── UserInfoController.java │ └── UserRoleController.java │ ├── model │ ├── Dict.java │ ├── DictCategory.java │ ├── DictTreeNode.java │ ├── LoginUser.java │ ├── ModuleAction.java │ ├── ModuleConfig.java │ ├── MuYunMessage.java │ ├── OnlineDevice.java │ ├── OnlineUser.java │ └── RuntimeUser.java │ └── service │ └── MessageCenter.java ├── muyun-proxy ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── proxy │ ├── MuYunProxy.java │ ├── Upstream.java │ └── model │ ├── ProxyConfig.java │ └── UpstreamItem.java ├── muyun-runtime-gateway ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── runtime │ └── gateway │ └── GatewayRuntimeProvider.java ├── muyun-runtime-session ├── build.gradle.kts └── src │ └── main │ └── java │ └── net │ └── ximatai │ └── muyun │ └── runtime │ └── session │ └── SessionRuntimeProvider.java ├── settings.gradle.kts └── test.http /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !build/*-runner 3 | !build/*-runner.jar 4 | !build/lib/* 5 | !build/quarkus-app/* -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 120 10 | tab_width = 4 11 | 12 | [*.{yml,yaml}] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**/*.md' 7 | pull_request: 8 | paths-ignore: 9 | - '**/*.md' 10 | 11 | jobs: 12 | check: 13 | runs-on: 'ubuntu-latest' 14 | steps: 15 | - name: Set environment variable 16 | run: | 17 | echo "MUYUN_USERNAME=admin" >> $GITHUB_ENV 18 | echo "MUYUN_PASSWORD=admin@muyun" >> $GITHUB_ENV 19 | - uses: actions/checkout@v4 20 | - name: Set up JDK 21 21 | uses: actions/setup-java@v4 22 | with: 23 | distribution: 'zulu' 24 | java-version: 21 25 | - name: Setup Gradle 26 | uses: gradle/actions/setup-gradle@v4 27 | - name: Checkstyle 28 | run: ./gradlew checkstyleMain checkstyleTest --scan 29 | - name: Test with Gradle 30 | run: ./gradlew test 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build 3 | .idea 4 | .DS_store 5 | -------------------------------------------------------------------------------- /MuYun轻代码平台能力一览.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ximatai/MuYun/777fbe299bde0201fb7ef3d3cd1036419e344145/MuYun轻代码平台能力一览.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MuYun 轻代码开发平台 2 | 3 | [![Maven Central Version](https://img.shields.io/maven-central/v/net.ximatai.muyun/muyun-core)](https://central.sonatype.com/artifact/net.ximatai.muyun/muyun-core/overview) 4 | [![ci](https://github.com/ximatai/MuYun/actions/workflows/ci.yml/badge.svg)](https://github.com/ximatai/MuYun/actions/workflows/ci.yml) 5 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/ximatai/MuYun) 6 | 7 | ## 特点 8 | 9 | * 云原生 10 | * 同步/异步双轨支持 11 | * JVM系第一梯队性能 12 | * 分层次构建,按需插拔(意味着可以按需依赖Jar包) 13 | * 程序员友好(口号:“一切为了开发人员不加班”) 14 | * 前后端分离(不仅仅是技术层面,从业务设计就是全面分离的,以后端优先为出发点) 15 | * 测试驱动开发 16 | 17 | ## 架构设计 18 | 19 | ![CleanShot 2024-10-28 at 14 48 22@2x](https://github.com/user-attachments/assets/4cd95a86-8099-4df7-97a1-97fafbd3aed5) 20 | 21 | [MuYun轻代码平台能力一览.pdf](https://github.com/ximatai/MuYun/blob/master/MuYun轻代码平台能力一览.pdf) 22 | 23 | ## 开发进度 24 | 25 | ### 后端 26 | 27 | * [x] 主体框架搭建 28 | * [x] 同步数据库访问接入 29 | * [ ] 异步数据库访问接入 30 | * [x] 标准增删改查能力接入 31 | - [x] 数据新增 32 | - [x] 数据修改 33 | - [x] 数据删除 34 | - [x] 数据排序 35 | - [x] 数据查询 36 | - [x] 数据查询-单条 37 | - [x] 数据查询-多行-分页 38 | - [x] 数据查询-多行-分页-条件查询 39 | - [x] 数据查询-多表关联 40 | - [x] 数据查询-主子表关联 41 | - [x] 数据查询-树形构建 42 | - [x] 数据查询-数据脱敏 43 | * [x] 扩展能力接入 44 | - [x] 代码内创建表 45 | - [x] 软删除 46 | - [x] 删除数据时对数据归档 47 | - [x] 通用业务字段自动创建 48 | - [x] 数据加密 49 | - [x] 数据校验 50 | - [x] 数据签名(数据完整性校验) 51 | - [x] 内部数据变动广播 52 | - [x] 数据变动广播到SockJS 53 | - [x] 文件管理 54 | - [x] 内嵌反向代理服务器 55 | * [x] 日志记录 56 | - [x] 操作日志 57 | - [x] 异常日志 58 | - [x] 登录日志 59 | * [ ] UI渲染接口 60 | - [ ] 列表配置 61 | - [ ] 查询配置 62 | - [ ] 表单配置 63 | * [ ] 平台业务 64 | - [x] 数据字典 65 | - [x] 机构 66 | - [x] 部门 67 | - [x] 人员 68 | - [x] 角色 69 | - [x] 模块 70 | - [x] 菜单 71 | - [x] 权限 72 | - [x] 功能权限 73 | - [x] 数据权限 74 | - [x] 单据编码 75 | - [x] 通知公告 76 | - [x] 在线用户 77 | - [ ] 工作流 78 | - [ ] 站内信 79 | * [ ] 微服务 80 | - [ ] 通信 81 | 82 | ### 前端 83 | 84 | **暂无,目前精力都放在对后端的持续推进上** 85 | 86 | ## 开发指南 87 | 88 | 如果你是需要使用 MuYun 平台实现自己的项目,可以移步 https://github.com/ximatai/MuYunStarter , 89 | 这是我们准备好的专门用来使用打包好的 MuYun Jar 包进行业务开发的起步项目。如果你想了解 MuYun 本身是怎么开发的可以继续往下看。 90 | 91 | 本项目使用 Java 21 开发。 92 | 93 | 本项目目前对 [PostgreSQL](https://www.postgresql.org/) 做了完整适配,其他数据库需要做轻微改造才能兼容。 94 | 95 | 可以使用 [Docker Compose](https://docs.docker.com/compose/) 启动数据库: 96 | 97 | ```shell 98 | docker compose up -d 99 | ``` 100 | 101 | 或者 docker 命令: 102 | 103 | ```shell 104 | docker run --rm -p 54324:5432 -e POSTGRES_PASSWORD=muyun2024 -e POSTGRES_DB=muyun postgres:17-alpine 105 | ``` 106 | 107 | 后端开发环境启动(首次启动会引导你设置管理员账号密码): 108 | 109 | ```shell 110 | ./gradlew --console=plain :muyun-boot:quarkusDev 111 | ``` 112 | ## 一些视频教程 113 | * https://www.bilibili.com/video/BV1w6zGYkEPo/ 114 | * https://www.bilibili.com/video/BV1W2z8YhEqV/ 115 | * https://www.bilibili.com/video/BV1DHiUYSEHD/ 116 | -------------------------------------------------------------------------------- /README_QUARKUS.md: -------------------------------------------------------------------------------- 1 | # muyun 2 | 3 | This project uses Quarkus, the Supersonic Subatomic Java Framework. 4 | 5 | If you want to learn more about Quarkus, please visit its website: . 6 | 7 | ## Running the application in dev mode 8 | 9 | You can run your application in dev mode that enables live coding using: 10 | 11 | ```shell script 12 | ./gradlew quarkusDev 13 | ``` 14 | 15 | > **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at . 16 | 17 | ## Packaging and running the application 18 | 19 | The application can be packaged using: 20 | 21 | ```shell script 22 | ./gradlew build 23 | ``` 24 | 25 | It produces the `quarkus-run.jar` file in the `build/quarkus-app/` directory. 26 | Be aware that it’s not an _über-jar_ as the dependencies are copied into the `build/quarkus-app/lib/` directory. 27 | 28 | The application is now runnable using `java -jar build/quarkus-app/quarkus-run.jar`. 29 | 30 | If you want to build an _über-jar_, execute the following command: 31 | 32 | ```shell script 33 | ./gradlew build -Dquarkus.package.jar.type=uber-jar 34 | ``` 35 | 36 | The application, packaged as an _über-jar_, is now runnable using `java -jar build/*-runner.jar`. 37 | 38 | ## Creating a native executable 39 | 40 | You can create a native executable using: 41 | 42 | ```shell script 43 | ./gradlew build -Dquarkus.native.enabled=true 44 | ``` 45 | 46 | Or, if you don't have GraalVM installed, you can run the native executable build in a container using: 47 | 48 | ```shell script 49 | ./gradlew build -Dquarkus.native.enabled=true -Dquarkus.native.container-build=true 50 | ``` 51 | 52 | You can then execute your native executable with: `./build/muyun-1.0.0-SNAPSHOT-runner` 53 | 54 | If you want to learn more about building native executables, please consult . 55 | 56 | ## Related Guides 57 | 58 | - REST ([guide](https://quarkus.io/guides/rest)): A Jakarta REST implementation utilizing build time processing and Vert.x. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. 59 | 60 | ## Provided Code 61 | 62 | ### REST 63 | 64 | Easily start your REST Web Services 65 | 66 | [Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) 67 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17-alpine' 4 | environment: 5 | - 'POSTGRES_DB=muyun' 6 | - 'POSTGRES_USER=postgres' 7 | - 'POSTGRES_PASSWORD=muyun2024' 8 | ports: 9 | - '54324:5432' 10 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | testcontainers = "1.20.3" 3 | quarkus = "3.19.1" 4 | jdbi = "3.48.0" 5 | yasson = "3.0.4" 6 | bcprov = "1.80" 7 | caffeine = "3.2.0" 8 | codec = "1.18.0" 9 | 10 | [libraries] 11 | testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "testcontainers" } 12 | quarkus-platform-bom = { module = "io.quarkus.platform:quarkus-bom", version.ref = "quarkus" } 13 | jdbi-core = { module = "org.jdbi:jdbi3-core", version.ref = "jdbi" } 14 | jdbi-postgres = { module = "org.jdbi:jdbi3-postgres", version.ref = "jdbi" } 15 | yasson = { module = "org.eclipse:yasson", version.ref = "yasson" } 16 | bcprov = { module = "org.bouncycastle:bcprov-jdk18on", version.ref = "bcprov" } 17 | caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine" } 18 | commons-codes = { module = "commons-codec:commons-codec", version.ref = "codec" } 19 | easyCaptcha = { module = "com.github.whvcse:easy-captcha", version = "1.6.2" } 20 | 21 | [plugins] 22 | quarkus = { id = "io.quarkus", version.ref = "quarkus" } 23 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ximatai/MuYun/777fbe299bde0201fb7ef3d3cd1036419e344145/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.8-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /muyun-authorization/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(project(":muyun-core")) 8 | api(project(":muyun-database-std")) 9 | } 10 | -------------------------------------------------------------------------------- /muyun-boot/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.quarkus) 3 | } 4 | 5 | tasks.named("sourcesJar") { 6 | dependsOn(tasks.named("compileQuarkusGeneratedSourcesJava")) 7 | } 8 | 9 | tasks.named("compileJava") { 10 | dependsOn(tasks.named("compileQuarkusGeneratedSourcesJava")) 11 | } 12 | 13 | tasks.named("quarkusDependenciesBuild") { 14 | mustRunAfter(tasks.named("jandex")) 15 | } 16 | 17 | dependencies { 18 | implementation(enforcedPlatform(libs.quarkus.platform.bom)) 19 | implementation("io.quarkus:quarkus-config-yaml") 20 | 21 | implementation(project(":muyun-core")) 22 | implementation(project(":muyun-runtime-session")) 23 | // implementation(project(":muyun-runtime-gateway")) 24 | 25 | implementation(project(":muyun-database-std")) 26 | implementation(project(":muyun-platform")) 27 | implementation(project(":muyun-proxy")) 28 | implementation(project(":muyun-log")) 29 | implementation(project(":muyun-fileserver")) 30 | 31 | testImplementation("io.quarkus:quarkus-junit5") 32 | testImplementation("io.rest-assured:rest-assured") 33 | 34 | testImplementation("io.quarkus:quarkus-agroal") 35 | testImplementation("io.quarkus:quarkus-jdbc-postgresql") 36 | testImplementation(libs.testcontainers.postgresql) 37 | } 38 | -------------------------------------------------------------------------------- /muyun-boot/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./gradlew build -Dquarkus.native.enabled=true 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/muyun . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/muyun 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root build/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] 28 | -------------------------------------------------------------------------------- /muyun-boot/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./gradlew build -Dquarkus.native.enabled=true 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/muyun . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/muyun 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:2.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root build/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /muyun-boot/src/main/java/net/ximatai/muyun/boot/MainApp.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.boot; 2 | 3 | import io.quarkus.runtime.Quarkus; 4 | import io.quarkus.runtime.QuarkusApplication; 5 | import io.quarkus.runtime.annotations.QuarkusMain; 6 | import jakarta.inject.Inject; 7 | import net.ximatai.muyun.core.config.IProfile; 8 | import net.ximatai.muyun.core.config.MuYunConfig; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | @QuarkusMain 13 | public class MainApp implements QuarkusApplication, IProfile { 14 | 15 | private final Logger logger = LoggerFactory.getLogger(MainApp.class); 16 | 17 | @Inject 18 | MuYunConfig config; 19 | 20 | @Override 21 | public int run(String... args) { 22 | logger.info("PROFILE ON {}", profile()); 23 | Quarkus.waitForExit(); 24 | return 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /muyun-boot/src/main/resources/META-INF/resources/eventbus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vert.x EventBus with SockJS 7 | 8 | 9 | 10 | 11 |

EventBus Message Listener

12 |
13 |

Messages from server:

14 |

15 | 
16 | 17 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /muyun-boot/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | muyun: 2 | use-session: true 3 | file-server: 4 | upload-path: ./fileUploads/ 5 | web: 6 | redirects: 7 | - from: / 8 | to: /web/ 9 | frontend: 10 | resources: 11 | - prefix: /web/ 12 | path: /Users/aruis/develop/workspace-bsy/unicom_xinan_project/dist 13 | proxy: 14 | upstreams: 15 | - prefix: /web1/ 16 | # url: http://192.168.3.9:8089/web/ 17 | url: http://127.0.0.1:8001/web/ 18 | quarkus: 19 | http: 20 | port: 8080 21 | # root-path: /api 22 | rest: 23 | path: /api 24 | 25 | datasource: 26 | db-kind: postgresql 27 | username: postgres 28 | password: muyun2024 29 | jdbc: 30 | url: jdbc:postgresql://localhost:54324/muyun 31 | 32 | banner: 33 | path: banner.txt 34 | 35 | log: 36 | category: 37 | "org.jdbi": 38 | level: DEBUG 39 | 40 | level: INFO 41 | console: 42 | enable: true 43 | format: "%d{yyyy-MM-dd HH:mm:ss} %-5p [%c{3.}] (%t) %s%e%n" 44 | file: 45 | enable: true 46 | path: logs/muyun.log 47 | level: DEBUG 48 | format: "%d{yyyy-MM-dd HH:mm:ss} %-5p [%c{3.}] (%t) %s%e%n" 49 | rotation: 50 | file-suffix: .yyyy-MM-dd 51 | rotate-on-boot: true 52 | max-backup-index: 7 53 | # profile: test 54 | -------------------------------------------------------------------------------- /muyun-boot/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ███╗ ███╗██╗ ██╗██╗ ██╗██╗ ██╗███╗ ██╗ 2 | ████╗ ████║██║ ██║╚██╗ ██╔╝██║ ██║████╗ ██║ 3 | ██╔████╔██║██║ ██║ ╚████╔╝ ██║ ██║██╔██╗ ██║ 4 | ██║╚██╔╝██║██║ ██║ ╚██╔╝ ██║ ██║██║╚██╗██║ 5 | ██║ ╚═╝ ██║╚██████╔╝ ██║ ╚██████╔╝██║ ╚████║ 6 | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ 7 | -------------------------------------------------------------------------------- /muyun-boot/src/native-test/java/net/ximatai/muyun/test/GreetingResourceIT.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test; 2 | 3 | import io.quarkus.test.junit.QuarkusIntegrationTest; 4 | 5 | @QuarkusIntegrationTest 6 | class GreetingResourceIT { 7 | // Execute the same tests but in packaged mode. 8 | } 9 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/JdbiTest.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test; 2 | 3 | import net.ximatai.muyun.database.std.argument.List2JsonArgumentFactory; 4 | import net.ximatai.muyun.database.std.argument.Map2JsonArgumentFactory; 5 | import net.ximatai.muyun.database.std.mapper.MyPgMapMapper; 6 | import org.jdbi.v3.core.Handle; 7 | import org.jdbi.v3.core.Jdbi; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class JdbiTest { 14 | public static void main(String[] args) { 15 | // 创建 Jdbi 实例并安装 Postgres 插件 16 | Jdbi jdbi = Jdbi.create("jdbc:postgresql://localhost:54324/muyun", "postgres", "muyun2024") 17 | // .installPlugin(new PostgresPlugin()) 18 | .registerArgument(new Map2JsonArgumentFactory()) 19 | .registerArgument(new List2JsonArgumentFactory()); 20 | // .registerRowMapper(new MyPgMapMapper()) 21 | // .registerArrayType(String.class, "varchar") 22 | // .registerColumnMapper(PgArray.class, new PgArrayToListMapper()); 23 | 24 | // 使用 try-with-resources 确保资源自动释放 25 | try (Handle handle = jdbi.open()) { 26 | // 如果表尚未创建,则执行表创建 SQL(可选) 27 | String createTableSql = """ 28 | CREATE TABLE IF NOT EXISTS your_table ( 29 | id SERIAL PRIMARY KEY, 30 | name VARCHAR(100), 31 | j_text jsonb, 32 | colors VARCHAR[])"""; 33 | handle.execute(createTableSql); 34 | 35 | // 插入操作 36 | String insertSql = "INSERT INTO your_table (name, colors,j_text) VALUES (:name, :colors,:j_text)"; 37 | 38 | // 准备数据 39 | String name = "John Doe"; 40 | List colors = Arrays.asList("red", "blue"); 41 | 42 | String[] colors2 = {"a", "b", "c"}; 43 | // 执行插入 44 | handle.createUpdate(insertSql) 45 | .bind("name", name) // 自动绑定 name 参数 46 | // .bindByType("colors", colors, new GenericType>() { 47 | // }) // 显式指定类型 48 | .bind("colors", colors.toArray(new String[0])) 49 | // .bind("j_text","{\"a\":1,\"b\":2}") 50 | .bind("j_text", List.of("a", "b", "x", 1)) 51 | .execute(); // 执行 SQL 语句 52 | 53 | System.out.println("数据插入成功!"); 54 | 55 | // 查询操作,结果映射为 Map 56 | String selectSql = "SELECT id, name, colors,j_text FROM your_table"; 57 | 58 | // 执行查询 59 | List> results = handle.createQuery(selectSql) 60 | // .mapToMap() 61 | .map(new MyPgMapMapper()) 62 | // 将结果直接映射为 List> 63 | .list(); 64 | 65 | // 打印查询结果 66 | for (Map row : results) { 67 | System.out.println(row.get("colors").getClass()); 68 | } 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/core/TestBaseColumns.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.core; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import io.restassured.common.mapper.TypeRef; 6 | import jakarta.ws.rs.Path; 7 | import net.ximatai.muyun.ability.ISoftDeleteAbility; 8 | import net.ximatai.muyun.ability.ITableCreateAbility; 9 | import net.ximatai.muyun.ability.curd.std.ICURDAbility; 10 | import net.ximatai.muyun.base.BaseBusinessTable; 11 | import net.ximatai.muyun.core.Scaffold; 12 | import net.ximatai.muyun.database.builder.Column; 13 | import net.ximatai.muyun.database.builder.TableWrapper; 14 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 15 | import org.junit.jupiter.api.Assertions; 16 | import org.junit.jupiter.api.DisplayName; 17 | import org.junit.jupiter.api.Test; 18 | 19 | import java.util.Map; 20 | 21 | import static io.restassured.RestAssured.given; 22 | 23 | @QuarkusTest 24 | @QuarkusTestResource(value = PostgresTestResource.class) 25 | public class TestBaseColumns { 26 | private String path = "/TestBaseColumns"; 27 | 28 | @Test 29 | @DisplayName("验证创建记录时自动生成的审计列存在") 30 | void test() { 31 | String id = given() 32 | .contentType("application/json") 33 | .body(Map.of( 34 | "v_test", "test" 35 | )) 36 | .post("/api%s/create".formatted(path)) 37 | .then() 38 | .statusCode(200) 39 | .extract() 40 | .asString(); 41 | 42 | Map row = given() 43 | .get("/api%s/view/%s".formatted(path, id)) 44 | .then() 45 | .statusCode(200) 46 | .extract() 47 | .as(new TypeRef<>() { 48 | 49 | }); 50 | 51 | Assertions.assertTrue(row.containsKey("id_at_auth_user__create")); 52 | Assertions.assertTrue(row.containsKey("id_at_auth_user__update")); 53 | Assertions.assertTrue(row.containsKey("id_at_auth_user__delete")); 54 | Assertions.assertTrue(row.containsKey("t_delete")); 55 | } 56 | 57 | } 58 | 59 | @Path("/TestBaseColumns") 60 | class TestBaseColumnsController extends Scaffold implements ICURDAbility, ITableCreateAbility, ISoftDeleteAbility { 61 | 62 | @Override 63 | public String getSchemaName() { 64 | return "test"; 65 | } 66 | 67 | @Override 68 | public String getMainTable() { 69 | return "testbasecolumns"; 70 | } 71 | 72 | @Override 73 | public void fitOut(TableWrapper wrapper) { 74 | wrapper 75 | .setInherit(BaseBusinessTable.TABLE) 76 | .setPrimaryKey(Column.ID_POSTGRES) 77 | .addColumn(Column.of("v_test")); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/core/TestBaseScaffold.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.core; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import io.restassured.common.mapper.TypeRef; 6 | import jakarta.ws.rs.Path; 7 | import net.ximatai.muyun.base.BaseBusinessTable; 8 | import net.ximatai.muyun.base.BaseScaffold; 9 | import net.ximatai.muyun.database.builder.TableWrapper; 10 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 11 | import org.junit.jupiter.api.DisplayName; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import java.util.Map; 15 | 16 | import static io.restassured.RestAssured.given; 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | import static org.junit.jupiter.api.Assertions.assertNotNull; 19 | 20 | @QuarkusTest 21 | @QuarkusTestResource(value = PostgresTestResource.class) 22 | public class TestBaseScaffold { 23 | 24 | @Test 25 | @DisplayName("验证创建和更新记录时时间列的行为") 26 | void testCreateAndUpdateHasTimeColumn() { 27 | String id = given() 28 | .contentType("application/json") 29 | .body(Map.of("v_name", "test")) 30 | .when() 31 | .post("/api/TestBaseScaffold/create") 32 | .then() 33 | .statusCode(200) 34 | .extract() 35 | .asString(); 36 | 37 | Map row = given() 38 | .get("/api/TestBaseScaffold/view/%s".formatted(id)) 39 | .then() 40 | .statusCode(200) 41 | .extract() 42 | .as(new TypeRef<>() { 43 | }); 44 | 45 | assertNotNull(row.get("t_create")); 46 | 47 | String createString = "1988-10-12 13:58:06"; 48 | 49 | given() 50 | .contentType("application/json") 51 | .body(Map.of("v_name", "test", "t_create", createString)) 52 | .when() 53 | .post("/api/TestBaseScaffold/update/%s".formatted(id)) 54 | .then() 55 | .statusCode(200) 56 | .extract() 57 | .asString(); 58 | 59 | Map row2 = given() 60 | .get("/api/TestBaseScaffold/view/%s".formatted(id)) 61 | .then() 62 | .statusCode(200) 63 | .extract() 64 | .as(new TypeRef<>() { 65 | }); 66 | 67 | assertEquals(createString, row2.get("t_create")); 68 | } 69 | 70 | } 71 | 72 | @Path("/TestBaseScaffold") 73 | class TestBaseScaffoldController extends BaseScaffold { 74 | 75 | @Override 76 | public void fitOut(TableWrapper wrapper) { 77 | wrapper 78 | .setInherit(BaseBusinessTable.TABLE) 79 | .addColumn("v_name") 80 | .addColumn("v_remark"); 81 | } 82 | 83 | @Override 84 | public String getMainTable() { 85 | return "testbasescaffold"; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/core/TestDesensitizationAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.core; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import jakarta.inject.Inject; 6 | import jakarta.ws.rs.Path; 7 | import net.ximatai.muyun.ability.IDesensitizationAbility; 8 | import net.ximatai.muyun.ability.ITableCreateAbility; 9 | import net.ximatai.muyun.ability.curd.std.ICURDAbility; 10 | import net.ximatai.muyun.core.Scaffold; 11 | import net.ximatai.muyun.core.desensitization.Desensitizer; 12 | import net.ximatai.muyun.core.desensitization.MaskMiddleAlgorithm; 13 | import net.ximatai.muyun.core.security.SMEncryptor; 14 | import net.ximatai.muyun.database.IDatabaseOperations; 15 | import net.ximatai.muyun.database.builder.Column; 16 | import net.ximatai.muyun.database.builder.TableWrapper; 17 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 18 | import org.junit.jupiter.api.DisplayName; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.util.Map; 22 | 23 | import static org.junit.jupiter.api.Assertions.*; 24 | 25 | @QuarkusTest 26 | @QuarkusTestResource(value = PostgresTestResource.class) 27 | class TestDesensitizationAbility { 28 | 29 | private String path = "/TestSecurityAbility"; 30 | 31 | @Inject 32 | SMEncryptor smEncryptor; 33 | 34 | @Inject 35 | IDatabaseOperations databaseOperations; 36 | 37 | @Inject 38 | TestDesensitizationAbilityController testController; 39 | 40 | @Test 41 | @DisplayName("测试创建和查看数据时v_name字段是否被脱敏") 42 | void test() { 43 | String text = "hello world!"; 44 | String id = testController.create(Map.of( 45 | "v_name", text 46 | )); 47 | Map response = testController.view(id); 48 | 49 | String responseVName = (String) response.get("v_name"); 50 | assertEquals(text.length(), responseVName.length()); 51 | assertNotEquals(text, responseVName); 52 | assertEquals("h**********!", responseVName); 53 | assertNull(response.get("v_name2")); 54 | } 55 | 56 | } 57 | 58 | @Path("/TestDesensitizationAbility") 59 | class TestDesensitizationAbilityController extends Scaffold implements ICURDAbility, ITableCreateAbility, IDesensitizationAbility { 60 | 61 | @Override 62 | public String getSchemaName() { 63 | return "test"; 64 | } 65 | 66 | @Override 67 | public String getMainTable() { 68 | return "testdesensitizationability"; 69 | } 70 | 71 | @Override 72 | public void fitOut(TableWrapper wrapper) { 73 | wrapper 74 | .setPrimaryKey(Column.ID_POSTGRES) 75 | .addColumn(Column.of("v_name")) 76 | .addColumn(Column.of("v_name2")) 77 | .addColumn(Column.of("t_create").setDefaultValue("now()")); 78 | } 79 | 80 | @Override 81 | public Desensitizer getDesensitizer() { 82 | return new Desensitizer() 83 | .registerAlgorithm("v_name", MaskMiddleAlgorithm.INSTANCE); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/core/TestNaked.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.core; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import io.restassured.common.mapper.TypeRef; 6 | import jakarta.annotation.PostConstruct; 7 | import jakarta.inject.Inject; 8 | import jakarta.ws.rs.Path; 9 | import net.ximatai.muyun.ability.IDatabaseAbility; 10 | import net.ximatai.muyun.ability.IMetadataAbility; 11 | import net.ximatai.muyun.ability.ITableCreateAbility; 12 | import net.ximatai.muyun.ability.curd.std.ISelectAbility; 13 | import net.ximatai.muyun.database.IDatabaseOperations; 14 | import net.ximatai.muyun.database.builder.Column; 15 | import net.ximatai.muyun.database.builder.TableWrapper; 16 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 17 | import org.junit.jupiter.api.DisplayName; 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static io.restassured.RestAssured.given; 21 | 22 | @QuarkusTest 23 | @QuarkusTestResource(value = PostgresTestResource.class) 24 | public class TestNaked { 25 | 26 | @Test 27 | @DisplayName("测试以创建时间降序查询纯实现接口的controller的表") 28 | void test() { 29 | given() 30 | .queryParam("sort", "t_create,desc") 31 | .get("/api/demo/view") 32 | .then() 33 | .statusCode(200) 34 | .extract() 35 | .as(new TypeRef<>() { 36 | }); 37 | } 38 | } 39 | 40 | @Path("/demo") 41 | class Demo implements IDatabaseAbility, IMetadataAbility, ITableCreateAbility, ISelectAbility { 42 | 43 | @Inject 44 | IDatabaseOperations databaseOperations; 45 | 46 | @PostConstruct 47 | void init() { 48 | create(getDatabaseOperations()); 49 | } 50 | 51 | @Override 52 | public String getSchemaName() { 53 | return "public"; 54 | } 55 | 56 | @Override 57 | public String getMainTable() { 58 | return "demo"; 59 | } 60 | 61 | @Override 62 | public IDatabaseOperations getDatabaseOperations() { 63 | return databaseOperations; 64 | } 65 | 66 | @Override 67 | public void fitOut(TableWrapper wrapper) { 68 | wrapper 69 | .setPrimaryKey(Column.ID_POSTGRES) 70 | .addColumn("v_name") 71 | .addColumn("i_age"); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/core/TestSMEncryptor.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.core; 2 | 3 | import net.ximatai.muyun.core.security.SMEncryptor; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | public class TestSMEncryptor { 11 | 12 | SMEncryptor smEncryptor = new SMEncryptor(); 13 | 14 | @BeforeEach 15 | void beforeEach() throws Exception { 16 | smEncryptor.init(); 17 | } 18 | 19 | @Test 20 | @DisplayName("测试签名功能") 21 | void sign() { 22 | assertEquals("80b737c798f5bb0d826a987b0289e110d2283bb13d124aba4ec183644a05bb65", smEncryptor.sign("hello world!")); 23 | } 24 | 25 | @Test 26 | @DisplayName("测试加密和解密功能") 27 | void encryptAndDecrypt() { 28 | String source = "hello world!"; 29 | String encodeText = smEncryptor.encrypt(source); 30 | String decodeText = smEncryptor.decrypt(encodeText); 31 | 32 | assertEquals(source, decodeText); 33 | } 34 | 35 | @Test 36 | @DisplayName("测试解密功能") 37 | void testDecrypt() { 38 | String source = "hello world!"; 39 | String encodeText = "04863727249ca5f6256879937445552f513999b04fe0d46eac089006c9fd958c6416b8929989a8fbd6c24c4fc0ac1458931c9c00ff8502124162445cd4985e09f54518c5d15332143cd01fde65febb84f315a1c8e2328b0d32f3c452bd7f51f7a76dae0532eeb1c31d9dead16b"; 40 | String decodeText = smEncryptor.decrypt(encodeText); 41 | assertEquals(source, decodeText); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/core/TestTimeFormatCR.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.core; 2 | 3 | import io.quarkus.runtime.Startup; 4 | import io.quarkus.test.common.QuarkusTestResource; 5 | import io.quarkus.test.junit.QuarkusTest; 6 | import io.vertx.core.json.JsonObject; 7 | import jakarta.inject.Inject; 8 | import jakarta.ws.rs.Path; 9 | import net.ximatai.muyun.ability.ITableCreateAbility; 10 | import net.ximatai.muyun.ability.curd.std.ICURDAbility; 11 | import net.ximatai.muyun.core.Scaffold; 12 | import net.ximatai.muyun.database.builder.Column; 13 | import net.ximatai.muyun.database.builder.TableWrapper; 14 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 15 | import org.junit.jupiter.api.DisplayName; 16 | import org.junit.jupiter.api.Test; 17 | 18 | import java.util.Map; 19 | 20 | import static io.restassured.RestAssured.given; 21 | import static org.junit.jupiter.api.Assertions.assertEquals; 22 | 23 | @QuarkusTest 24 | @QuarkusTestResource(value = PostgresTestResource.class) 25 | class TestTimeFormatCR { 26 | 27 | @Inject 28 | TestTimeFormatCRController testController; 29 | 30 | @Test 31 | @DisplayName("测试时间格式创建和查看") 32 | void testCreate() { 33 | String id = testController.create(Map.of( 34 | "d_test", "1988-01-01", 35 | "t_test2", "2001-01-01" 36 | )); 37 | 38 | Map map = testController.view(id); 39 | 40 | assertEquals("1988-01-01", map.get("d_test").toString()); 41 | assertEquals("2001-01-01 00:00:00.0", map.get("t_test2").toString()); 42 | 43 | String string = given() 44 | .get("/api/TestTimeFormatCRController/view/" + id) 45 | .then() 46 | .extract() 47 | .asString(); 48 | 49 | JsonObject entries = new JsonObject(string); 50 | String dataInJson = entries.getString("d_test"); 51 | String dataInJson2 = entries.getString("t_test2"); 52 | 53 | assertEquals("1988-01-01", dataInJson); 54 | assertEquals("2001-01-01 00:00:00", dataInJson2); 55 | 56 | } 57 | 58 | } 59 | 60 | @Startup 61 | @Path("/TestTimeFormatCRController") 62 | class TestTimeFormatCRController extends Scaffold implements ICURDAbility, ITableCreateAbility { 63 | 64 | @Override 65 | public String getSchemaName() { 66 | return "test"; 67 | } 68 | 69 | @Override 70 | public String getMainTable() { 71 | return "test_table"; 72 | } 73 | 74 | @Override 75 | public void fitOut(TableWrapper wrapper) { 76 | wrapper 77 | .setPrimaryKey(Column.ID_POSTGRES) 78 | .addColumn(Column.of("d_test")) 79 | .addColumn(Column.of("t_test2")); 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/core/TestWildcardPath.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.core; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import jakarta.ws.rs.GET; 6 | import jakarta.ws.rs.Path; 7 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 8 | import org.junit.jupiter.api.DisplayName; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import static io.restassured.RestAssured.given; 12 | 13 | @QuarkusTest 14 | @QuarkusTestResource(value = PostgresTestResource.class) 15 | public class TestWildcardPath { 16 | 17 | @Test 18 | @DisplayName("测试通配符路径的视图接口") 19 | void test() { 20 | String result = given() 21 | .get("/api/commondoc/wildcard/wangpan/view") 22 | .then() 23 | .statusCode(200) 24 | .extract() 25 | .asString(); 26 | 27 | System.out.println(result); 28 | } 29 | } 30 | 31 | @Path("/commondoc/wildcard/{type}") 32 | class WildcardPathController { 33 | 34 | @GET 35 | @Path("view") 36 | public String view() { 37 | return getPath(); 38 | } 39 | 40 | String getPath() { 41 | Path annotation = this.getClass().getAnnotation(Path.class); 42 | if (annotation != null) { 43 | return annotation.value(); 44 | } 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/database/TestDatabaseOperations.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.database; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import jakarta.inject.Inject; 6 | import net.ximatai.muyun.database.IDatabaseOperationsStd; 7 | import net.ximatai.muyun.database.builder.TableBuilder; 8 | import net.ximatai.muyun.database.builder.TableWrapper; 9 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 10 | import org.junit.jupiter.api.BeforeAll; 11 | import org.junit.jupiter.api.DisplayName; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.TestInstance; 14 | 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | import static net.ximatai.muyun.database.builder.Column.ID_POSTGRES; 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | 21 | @QuarkusTest 22 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 23 | @QuarkusTestResource(value = PostgresTestResource.class) 24 | public class TestDatabaseOperations { 25 | 26 | @Inject 27 | IDatabaseOperationsStd db; 28 | 29 | @BeforeAll 30 | void setUp() { 31 | TableWrapper basic = TableWrapper.withName("basic") 32 | .setSchema("public") 33 | .setPrimaryKey(ID_POSTGRES) 34 | .addColumn("v_name") 35 | .addColumn("i_age"); 36 | 37 | new TableBuilder(db).build(basic); 38 | } 39 | 40 | @Test 41 | void testExecute() { 42 | db.execute("select 'test',?,?", "x", "y"); 43 | db.execute("select 'test',?,?", List.of("x", "y")); 44 | } 45 | 46 | @Test 47 | @DisplayName("验证单条记录插入操作") 48 | void testInsert() { 49 | String id = db.insertItem("public", "basic", Map.of( 50 | "v_name", "test", 51 | "i_age", 1 52 | )); 53 | 54 | Map row = db.row("select * from public.basic where id = ?", id); 55 | assertEquals("test", row.get("v_name")); 56 | assertEquals(1, row.get("i_age")); 57 | } 58 | 59 | @Test 60 | @DisplayName("验证批量记录插入操作") 61 | void testBatchInsert() { 62 | List list = List.of( 63 | Map.of( 64 | "v_name", "test1", 65 | "i_age", 1 66 | ), 67 | Map.of( 68 | "v_name", "test2", 69 | "i_age", 2 70 | ), 71 | Map.of( 72 | "v_name", "test3", 73 | "i_age", 3 74 | ) 75 | 76 | ); 77 | 78 | List idList = db.insertList("public", "basic", list); 79 | 80 | Map row = db.row("select * from public.basic where id = ?", idList.get(0)); 81 | assertEquals("test1", row.get("v_name")); 82 | assertEquals(1, row.get("i_age")); 83 | 84 | Map row2 = db.row("select * from public.basic where id = ?", idList.get(1)); 85 | assertEquals("test2", row2.get("v_name")); 86 | assertEquals(2, row2.get("i_age")); 87 | 88 | Map row3 = db.row("select * from public.basic where id = ?", idList.get(2)); 89 | assertEquals("test3", row3.get("v_name")); 90 | assertEquals(3, row3.get("i_age")); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/fileserver/TestFileDownload.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.fileserver; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import jakarta.inject.Inject; 6 | import net.ximatai.muyun.fileserver.FileServerConfig; 7 | import net.ximatai.muyun.fileserver.IFileService; 8 | import net.ximatai.muyun.fileserver.exception.FileException; 9 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.DisplayName; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import java.io.File; 15 | import java.io.FileOutputStream; 16 | import java.io.IOException; 17 | 18 | @QuarkusTest 19 | @QuarkusTestResource(value = PostgresTestResource.class) 20 | public class TestFileDownload { 21 | 22 | @Inject 23 | IFileService service; 24 | 25 | @Inject 26 | FileServerConfig config; 27 | 28 | String fileName; 29 | String fileContent; 30 | File tempFile; 31 | 32 | @BeforeEach 33 | public void setup() throws IOException { 34 | fileName = "ZZZZZZ.txt"; 35 | fileContent = "hello world"; 36 | tempFile = File.createTempFile(fileName.split("\\.")[0], fileName.split("\\.")[1]); 37 | FileOutputStream fos = new FileOutputStream(tempFile); 38 | fos.write(fileContent.getBytes()); 39 | fos.close(); 40 | } 41 | 42 | @Test 43 | @DisplayName("测试文件下载功能,确保多次下载同一个文件时能够正确处理") 44 | void testFileDownload() throws FileException { 45 | String id = service.save(tempFile, fileName).split("@")[0]; 46 | File file = service.get(id); 47 | String filePath = file.getPath(); 48 | System.out.println(filePath); 49 | // 错误原因: 目标目录中已经存在了同名文件,导致无法创建目标文件的副本 50 | File file2 = service.get(id); 51 | String filePath2 = file2.getPath(); 52 | System.out.println(filePath2); 53 | service.delete(id); 54 | file.deleteOnExit(); 55 | file2.deleteOnExit(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/plaform/TestAppConf.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.plaform; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import io.restassured.common.mapper.TypeRef; 6 | import jakarta.inject.Inject; 7 | import net.ximatai.muyun.core.config.MuYunConfig; 8 | import net.ximatai.muyun.platform.PlatformConst; 9 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 10 | import org.junit.jupiter.api.DisplayName; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | import static io.restassured.RestAssured.given; 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | import static org.junit.jupiter.api.Assertions.assertFalse; 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | @QuarkusTest 22 | @QuarkusTestResource(value = PostgresTestResource.class) 23 | public class TestAppConf { 24 | 25 | @Inject 26 | MuYunConfig config; 27 | 28 | String base = PlatformConst.BASE_PATH; 29 | 30 | @Test 31 | @DisplayName("验证配置的获取和设置操作") 32 | void testGetAndSet() { 33 | 34 | Map conf = Map.of( 35 | "test", 1, 36 | "test2", List.of(1, 2, 3), 37 | "ok", true 38 | ); 39 | 40 | Map confFromHTTP = given() 41 | .header("userID", config.superUserId()) 42 | .when() 43 | .get("/api%s/conf/get".formatted(base)) 44 | .then() 45 | .statusCode(200) 46 | .extract() 47 | .as(new TypeRef<>() { 48 | }); 49 | 50 | assertTrue(confFromHTTP.isEmpty()); 51 | 52 | given() 53 | .header("userID", config.superUserId()) 54 | .contentType("application/json") 55 | .body(conf) 56 | .when() 57 | .post("/api%s/conf/set".formatted(base)) 58 | .then() 59 | .statusCode(200) 60 | .extract() 61 | .asString(); 62 | 63 | confFromHTTP = given() 64 | .header("userID", config.superUserId()) 65 | .when() 66 | .get("/api%s/conf/get".formatted(base)) 67 | .then() 68 | .statusCode(200) 69 | .extract() 70 | .as(new TypeRef<>() { 71 | }); 72 | 73 | assertFalse(confFromHTTP.isEmpty()); 74 | assertEquals(conf.get("test"), confFromHTTP.get("test")); 75 | assertEquals(conf.get("test2"), confFromHTTP.get("test2")); 76 | assertEquals(conf.get("ok"), confFromHTTP.get("ok")); 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/plaform/TestModuleAliasUpdate.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.plaform; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import jakarta.inject.Inject; 6 | import net.ximatai.muyun.platform.controller.AuthorizationController; 7 | import net.ximatai.muyun.platform.controller.ModuleActionController; 8 | import net.ximatai.muyun.platform.controller.ModuleController; 9 | import net.ximatai.muyun.platform.controller.RoleActionController; 10 | import net.ximatai.muyun.platform.controller.RoleController; 11 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 12 | import org.junit.jupiter.api.DisplayName; 13 | import org.junit.jupiter.api.Test; 14 | import org.junit.jupiter.api.TestInstance; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | import static org.junit.jupiter.api.Assertions.assertNotNull; 21 | import static org.junit.jupiter.api.Assertions.assertTrue; 22 | 23 | @QuarkusTest 24 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 25 | @QuarkusTestResource(value = PostgresTestResource.class) 26 | @DisplayName("演示分配权限后,更改模块及功能别名(alias)") 27 | public class TestModuleAliasUpdate { 28 | 29 | @Inject 30 | ModuleController moduleController; 31 | 32 | @Inject 33 | RoleController roleController; 34 | 35 | @Inject 36 | ModuleActionController moduleActionController; 37 | 38 | @Inject 39 | RoleActionController roleActionController; 40 | 41 | @Inject 42 | AuthorizationController authorizationController; 43 | 44 | @Test 45 | @DisplayName("测试模块别名和操作别名更新后授权信息的一致性") 46 | void test() { 47 | String moduleID = moduleController.create(Map.of("v_name", "test", "v_alias", "test_alias", "v_table", "test_table")); 48 | List actions = moduleController.getChildTableList(moduleID, "app_module_action", null); 49 | assertTrue(!actions.isEmpty()); 50 | 51 | Map viewAction = actions.stream().filter(it -> it.get("v_alias").equals("view")).findFirst().get(); 52 | 53 | assertNotNull(viewAction); 54 | 55 | String roleID = roleController.create(Map.of("v_name", "test")); 56 | 57 | String roleActionID = authorizationController.grant(roleID, viewAction.get("id").toString()); 58 | 59 | Map roleAction = roleActionController.view(roleActionID); 60 | 61 | assertEquals("test_alias", roleAction.get("v_alias_at_app_module")); 62 | assertEquals("view", roleAction.get("v_alias_at_app_module_action")); 63 | 64 | moduleController.update(moduleID, Map.of("v_alias", "test_alias2")); 65 | 66 | Map roleAction2 = roleActionController.view(roleActionID); 67 | 68 | assertEquals("test_alias2", roleAction2.get("v_alias_at_app_module")); 69 | 70 | moduleActionController.update(viewAction.get("id").toString(), Map.of("v_alias", "view2")); 71 | 72 | Map roleAction3 = roleActionController.view(roleActionID); 73 | 74 | assertEquals("view2", roleAction3.get("v_alias_at_app_module_action")); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/plaform/TestNoticeController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.plaform; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import jakarta.inject.Inject; 6 | import net.ximatai.muyun.core.config.MuYunConfig; 7 | import net.ximatai.muyun.model.PageResult; 8 | import net.ximatai.muyun.platform.controller.NoticeController; 9 | import net.ximatai.muyun.platform.controller.ReceiveController; 10 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 11 | import org.junit.jupiter.api.DisplayName; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Optional; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.junit.jupiter.api.Assertions.assertTrue; 20 | 21 | @QuarkusTest 22 | @QuarkusTestResource(value = PostgresTestResource.class) 23 | public class TestNoticeController { 24 | 25 | @Inject 26 | MuYunConfig config; 27 | 28 | @Inject 29 | NoticeController noticeController; 30 | 31 | @Inject 32 | ReceiveController receiveController; 33 | 34 | @Test 35 | @DisplayName("测试发布通知并验证通知是否可见") 36 | public void publish() { 37 | String id = noticeController.create(Map.of( 38 | "v_title", "Chinese table tenni", 39 | "v_context", "Malone fades from center stage", 40 | "dict_notice_access_scope", "open" 41 | )); 42 | int release = noticeController.release(id); 43 | PageResult view = receiveController.view(10, 10L, true, null); 44 | List list = view.getList(); 45 | Optional optional = list.stream().filter(ele -> ele.get("id").equals(id)).findFirst(); 46 | assertTrue(optional.isPresent()); 47 | 48 | long unreadCount = receiveController.unreadCount(); 49 | assertEquals(1L, unreadCount); 50 | 51 | receiveController.view(id); 52 | 53 | assertEquals(0L, receiveController.unreadCount()); 54 | } 55 | 56 | @Test 57 | @DisplayName("测试向多个机构发送通知") 58 | public void publishToOrganizations() { 59 | String id = noticeController.create(Map.of( 60 | "v_title", "Chinese table tenni", 61 | "v_context", "Malone fades from center stage", 62 | "dict_notice_access_scope", "organization", 63 | "ids_at_org_organization", List.of("1", "2", "3") 64 | )); 65 | int release = noticeController.release(id); 66 | 67 | assertEquals(1, release); 68 | } 69 | 70 | @Test 71 | @DisplayName("测试向多个部门发送通知") 72 | public void publishToDepartments() { 73 | String id = noticeController.create(Map.of( 74 | "v_title", "Chinese table tenni", 75 | "v_context", "Malone fades from center stage", 76 | "dict_notice_access_scope", "department", 77 | "ids_at_org_department", List.of("1", "2", "3") 78 | )); 79 | int release = noticeController.release(id); 80 | 81 | assertEquals(1, release); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/plaform/TestOnlineUser.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.plaform; 2 | 3 | import io.quarkus.test.common.QuarkusTestResource; 4 | import io.quarkus.test.junit.QuarkusTest; 5 | import jakarta.inject.Inject; 6 | import net.ximatai.muyun.core.config.MuYunConfig; 7 | import net.ximatai.muyun.test.testcontainers.PostgresTestResource; 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.DisplayName; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import static io.restassured.RestAssured.given; 13 | 14 | @QuarkusTest 15 | @QuarkusTestResource(value = PostgresTestResource.class) 16 | public class TestOnlineUser { 17 | 18 | @Inject 19 | MuYunConfig config; 20 | 21 | @Test 22 | @DisplayName("测试在线用户接口并验证返回的设备ID不为空") 23 | void testOnline() { 24 | String deviceID = given() 25 | .header("userID", config.superUserId()) 26 | .get("/api/platform/online/iAmHere") 27 | .then() 28 | .statusCode(200) 29 | .extract() 30 | .asString(); 31 | 32 | Assertions.assertNotNull(deviceID); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /muyun-boot/src/test/java/net/ximatai/muyun/test/testcontainers/PostgresTestResource.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.test.testcontainers; 2 | 3 | import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; 4 | import org.testcontainers.containers.PostgreSQLContainer; 5 | 6 | import java.util.Map; 7 | 8 | public class PostgresTestResource implements QuarkusTestResourceLifecycleManager { 9 | 10 | private static final String POSTGRES_IMAGE = "postgres:17-alpine"; 11 | 12 | // 使用静态变量确保所有测试类共享一个实例 13 | private static final PostgreSQLContainer POSTGRES = new PostgreSQLContainer<>(POSTGRES_IMAGE).withReuse(true); 14 | 15 | @Override 16 | public Map start() { 17 | if (!POSTGRES.isRunning()) { 18 | POSTGRES.start(); 19 | } 20 | return Map.of( 21 | "quarkus.datasource.jdbc.url", POSTGRES.getJdbcUrl(), 22 | "quarkus.datasource.username", POSTGRES.getUsername(), 23 | "quarkus.datasource.password", POSTGRES.getPassword() 24 | ); 25 | } 26 | 27 | @Override 28 | public void stop() { 29 | // 不停止容器,让其他测试类可以继续使用 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /muyun-core-uni/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | checkstyle 5 | `configure-jandex` 6 | } 7 | 8 | dependencies { 9 | api(project(":muyun-core")) 10 | api(project(":muyun-database-uni")) 11 | } 12 | -------------------------------------------------------------------------------- /muyun-core-uni/src/main/java/net/ximatai/muyun/ability/uni/IDatabaseUniAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability.uni; 2 | 3 | import net.ximatai.muyun.database.IDatabaseOperations; 4 | import net.ximatai.muyun.database.uni.IDatabaseOperationsUni; 5 | 6 | public interface IDatabaseUniAbility { 7 | 8 | IDatabaseOperations getDatabaseOperations(); 9 | 10 | default IDatabaseOperationsUni getDatabase() { 11 | return (IDatabaseOperationsUni) getDatabaseOperations(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /muyun-core-uni/src/main/java/net/ximatai/muyun/ability/uni/curd/ICURDUniAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability.uni.curd; 2 | 3 | public interface ICURDUniAbility extends ICreateAbilityUni { 4 | } 5 | -------------------------------------------------------------------------------- /muyun-core-uni/src/main/java/net/ximatai/muyun/ability/uni/curd/ICreateAbilityUni.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability.uni.curd; 2 | 3 | import io.smallrye.mutiny.Uni; 4 | import jakarta.ws.rs.POST; 5 | import jakarta.ws.rs.Path; 6 | import net.ximatai.muyun.ability.IMetadataAbility; 7 | import net.ximatai.muyun.ability.uni.IDatabaseUniAbility; 8 | 9 | import java.util.Map; 10 | 11 | public interface ICreateAbilityUni extends IDatabaseUniAbility, IMetadataAbility { 12 | 13 | @POST 14 | @Path("/create") 15 | default Uni create(Map body) { 16 | return getDatabase().insertItem(getSchemaName(), getMainTable(), body); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /muyun-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(enforcedPlatform(libs.quarkus.platform.bom)) 8 | 9 | api(project(":muyun-database")) 10 | api(project(":muyun-fileserver")) 11 | 12 | api("io.quarkus:quarkus-rest") 13 | api("io.quarkus:quarkus-arc") 14 | api("io.quarkus:quarkus-vertx") 15 | api("io.quarkus:quarkus-reactive-routes") 16 | api("io.quarkus:quarkus-rest-jackson") 17 | api("io.quarkus:quarkus-smallrye-openapi") 18 | api("io.quarkus:quarkus-scheduler") 19 | 20 | api(libs.commons.codes) 21 | api(libs.bcprov) 22 | api(libs.caffeine) 23 | 24 | testImplementation("io.quarkus:quarkus-junit5") 25 | testImplementation("io.rest-assured:rest-assured") 26 | } 27 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/MuYunConst.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun; 2 | 3 | public class MuYunConst { 4 | public static final String CONTEXT_KEY_API_REQUEST = "apiRequest"; 5 | public static final String CONTEXT_KEY_RUNTIME_USER = "runtimeUser"; 6 | 7 | public static final String SESSION_USER_KEY = "user"; 8 | public static final String SSO_MODULE_NAME = "SSO"; 9 | } 10 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/RouterFilterPriority.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun; 2 | 3 | public class RouterFilterPriority { 4 | 5 | public static final int SESSION_BUILDER = 5001; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IArchiveWhenDelete.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import net.ximatai.muyun.database.IDatabaseOperations; 4 | 5 | import java.util.Map; 6 | import java.util.Objects; 7 | 8 | public interface IArchiveWhenDelete extends ITableCreateAbility { 9 | 10 | default String getArchiveTableName() { 11 | return getMainTable() + "__archive"; 12 | } 13 | 14 | default void restore(String id) { 15 | IDatabaseOperations databaseOperations = this.getDatabaseOperations(); 16 | Object item = databaseOperations.getItem(getSchemaName(), getArchiveTableName(), id); 17 | Objects.requireNonNull(item, "要恢复的数据ID[%s]不存在".formatted(id)); 18 | 19 | databaseOperations.insertItem(getSchemaName(), getMainTable(), (Map) item); 20 | databaseOperations.deleteItem(getSchemaName(), getArchiveTableName(), id); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IAuthAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import io.quarkus.arc.Arc; 4 | import jakarta.ws.rs.GET; 5 | import jakarta.ws.rs.Path; 6 | import jakarta.ws.rs.PathParam; 7 | import net.ximatai.muyun.service.IAuthorizationService; 8 | 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | /** 13 | * 参与权限管理的能力 14 | */ 15 | public interface IAuthAbility extends IRuntimeAbility { 16 | 17 | default IAuthorizationService getAuthorizationService() { 18 | return Arc.container().instance(IAuthorizationService.class).get(); 19 | } 20 | 21 | @GET 22 | @Path("/actions") 23 | default List actions() { 24 | IAuthorizationService authorizationService = getAuthorizationService(); 25 | if (authorizationService == null) { 26 | return List.of(); 27 | } 28 | 29 | String userID = getUser().getId(); 30 | String module = getApiRequest().getModule(); 31 | 32 | return authorizationService.getAllowedActions(userID, module); 33 | } 34 | 35 | @GET 36 | @Path("/actions/{id}") 37 | default List actions(@PathParam("id") String id) { 38 | IAuthorizationService authorizationService = getAuthorizationService(); 39 | if (authorizationService == null) { 40 | return List.of(); 41 | } 42 | 43 | String userID = getUser().getId(); 44 | String module = getApiRequest().getModule(); 45 | 46 | return authorizationService.getAllowedActions(userID, module) 47 | .stream().filter(it -> authorizationService.isDataAuthorized(userID, module, it, id)) 48 | .collect(Collectors.toList()); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IChildAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import jakarta.ws.rs.Path; 4 | import net.ximatai.muyun.ability.curd.std.ICURDAbility; 5 | import net.ximatai.muyun.model.ChildTableInfo; 6 | 7 | /** 8 | * 成为子表的能力 9 | */ 10 | public interface IChildAbility extends ICURDAbility, IMetadataAbility { 11 | 12 | default ChildTableInfo toChildTable(String foreignKey) { 13 | if (checkColumn(foreignKey)) { 14 | return new ChildTableInfo(foreignKey, this); 15 | } else { 16 | throw new RuntimeException("foreignKey not found"); 17 | } 18 | } 19 | 20 | @Path("/placeholder") 21 | default String placeholder() { 22 | return "placeholder"; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IDataBroadcastAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import io.quarkus.arc.Arc; 4 | import io.vertx.core.eventbus.EventBus; 5 | import io.vertx.core.json.JsonObject; 6 | import net.ximatai.muyun.model.DataChangeChannel; 7 | 8 | /** 9 | * 数据变动时通过 EventBus 向外广播的能力 10 | */ 11 | public interface IDataBroadcastAbility extends IMetadataAbility { 12 | 13 | default EventBus getEventBus() { 14 | return Arc.container().instance(EventBus.class).get(); 15 | } 16 | 17 | default DataChangeChannel getDataChangeChannel() { 18 | return new DataChangeChannel(this); 19 | } 20 | 21 | default boolean msgToFrontEnd() { 22 | return true; 23 | } 24 | 25 | default void broadcast(DataChangeChannel.Type type, String id) { 26 | EventBus eventBus = getEventBus(); 27 | DataChangeChannel channel = getDataChangeChannel(); 28 | String address = channel.getAddress(); 29 | String addressWithType = channel.getAddressWithType(type); 30 | 31 | JsonObject body = new JsonObject(); 32 | body.put("type", type.name()); 33 | body.put("id", id); 34 | if (msgToFrontEnd()) { 35 | body.put("toFrontEnd", true); 36 | } 37 | 38 | eventBus.publish(address, body); 39 | eventBus.publish(addressWithType, body); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IDatabaseAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import io.quarkus.arc.Arc; 4 | import net.ximatai.muyun.database.IDatabaseOperations; 5 | 6 | /** 7 | * 数据库操作能力 8 | */ 9 | public interface IDatabaseAbility { 10 | 11 | default IDatabaseOperations getDatabaseOperations() { 12 | return Arc.container().instance(IDatabaseOperations.class).get(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IDatabaseAbilityStd.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import net.ximatai.muyun.database.IDatabaseOperationsStd; 4 | 5 | /** 6 | * 数据库操作能力(标准JDBC同步版) 7 | */ 8 | public interface IDatabaseAbilityStd extends IDatabaseAbility { 9 | default IDatabaseOperationsStd getDB() { 10 | return (IDatabaseOperationsStd) getDatabaseOperations(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IDesensitizationAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import net.ximatai.muyun.core.desensitization.Desensitizer; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * 数据脱敏的能力 9 | */ 10 | public interface IDesensitizationAbility { 11 | 12 | Desensitizer getDesensitizer(); 13 | 14 | default void desensitize(Map map) { 15 | Desensitizer desensitizer = getDesensitizer(); 16 | if (desensitizer == null) return; 17 | 18 | map.replaceAll((k, v) -> v != null ? desensitizer.desensitize(k, v.toString()) : null); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/ILabelAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | /** 4 | * 数据行标签化的能力(树、被关联自动翻译、日志记录时展示字段) 5 | */ 6 | public interface ILabelAbility extends IMetadataAbility { 7 | default String getLabelColumn() { 8 | if (checkColumn("v_name")) { 9 | return "v_name"; 10 | } 11 | 12 | if (checkColumn("v_label")) { 13 | return "v_label"; 14 | } 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IMetadataAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import net.ximatai.muyun.database.builder.TableBase; 4 | import net.ximatai.muyun.database.metadata.DBTable; 5 | 6 | /** 7 | * 元数据信息能力 8 | */ 9 | public interface IMetadataAbility extends IDatabaseAbility { 10 | 11 | String getMainTable(); 12 | 13 | default String getSchemaName() { 14 | return "public"; 15 | } 16 | 17 | default TableBase getTableBase() { 18 | return new TableBase(getSchemaName(), getMainTable()); 19 | } 20 | 21 | default String getSchemaDotTable() { 22 | return getSchemaName() + "." + getMainTable(); 23 | } 24 | 25 | default String getPK() { 26 | return "id"; 27 | } 28 | 29 | default DBTable getDBTable() { 30 | return getDatabaseOperations().getDBInfo().getSchema(getSchemaName()).getTable(getMainTable()); 31 | } 32 | 33 | default boolean checkColumn(String column) { 34 | return getDBTable().contains(column); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IReferableAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import net.ximatai.muyun.ability.curd.std.ICustomSelectSqlAbility; 4 | import net.ximatai.muyun.ability.curd.std.ISelectAbility; 5 | import net.ximatai.muyun.database.exception.MyDatabaseException; 6 | import net.ximatai.muyun.model.ReferenceInfo; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 可以被关联的能力 12 | */ 13 | public interface IReferableAbility extends IMetadataAbility, ILabelAbility, ISelectAbility { 14 | 15 | default String getKeyColumn() { 16 | return getPK(); 17 | } 18 | 19 | default List getOpenColumns() { 20 | return List.of(); 21 | } 22 | 23 | default boolean checkColumnExist(String column) { 24 | if (!(this instanceof ICustomSelectSqlAbility) && !checkColumn(column)) { 25 | throw new MyDatabaseException("根据引用关系,要求 %s 必须含有 %s 字段".formatted(getMainTable(), column)); 26 | } 27 | return true; 28 | } 29 | 30 | default ReferenceInfo toReferenceInfo(String foreignKey) { 31 | return new ReferenceInfo(foreignKey, this).autoPackage(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IReferenceAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import net.ximatai.muyun.model.ReferenceInfo; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 关联别人的能力(可以自动扩展 view 的查询字段) 9 | */ 10 | public interface IReferenceAbility { 11 | 12 | List getReferenceList(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/IRuntimeAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import io.quarkus.arc.Arc; 4 | import io.vertx.ext.web.RoutingContext; 5 | import net.ximatai.muyun.MuYunConst; 6 | import net.ximatai.muyun.core.config.IProfile; 7 | import net.ximatai.muyun.model.ApiRequest; 8 | import net.ximatai.muyun.model.IRuntimeUser; 9 | 10 | /** 11 | * 获取运行时上下文的能力 12 | */ 13 | public interface IRuntimeAbility extends IProfile { 14 | default RoutingContext getRoutingContext() { 15 | return Arc.container().instance(RoutingContext.class).get(); 16 | } 17 | 18 | default ApiRequest getApiRequest() { 19 | try { 20 | return getRoutingContext().get(MuYunConst.CONTEXT_KEY_API_REQUEST); 21 | } catch (Exception e) { 22 | return ApiRequest.BLANK; 23 | } 24 | } 25 | 26 | default IRuntimeUser getUser() { 27 | try { 28 | return getRoutingContext().get(MuYunConst.CONTEXT_KEY_RUNTIME_USER); 29 | } catch (Exception e) { 30 | return IRuntimeUser.WHITE; 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/ISecurityAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import net.ximatai.muyun.core.security.AbstractEncryptor; 4 | import net.ximatai.muyun.database.builder.Column; 5 | import net.ximatai.muyun.database.builder.ColumnType; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * 安全能力(数据加密、数据签名验证完整性) 12 | */ 13 | public interface ISecurityAbility { 14 | 15 | String SIGN_SUFFIX = "_sign_"; 16 | 17 | List getColumnsForSigning(); 18 | 19 | List getColumnsForEncryption(); 20 | 21 | AbstractEncryptor getAEncryptor(); 22 | 23 | default String column2SignColumn(String column) { 24 | return column + SIGN_SUFFIX; 25 | } 26 | 27 | /** 28 | * 获取因为存在签名字段所以需要追加的列 29 | * 30 | * @return 额外的签名校验列 31 | */ 32 | default List getSignColumns() { 33 | if (getColumnsForSigning() == null) return List.of(); 34 | 35 | return getColumnsForSigning().stream() 36 | .map(this::column2SignColumn) 37 | .map(Column::of) 38 | .map(c -> c.setType(ColumnType.VARCHAR)) 39 | .toList(); 40 | } 41 | 42 | /** 43 | * 该签名签名、该加密加密 44 | * 45 | * @param map 46 | */ 47 | default void signAndEncrypt(Map map) { 48 | AbstractEncryptor encryptor = getAEncryptor(); 49 | if (encryptor == null) return; 50 | 51 | getColumnsForSigning().forEach(s -> { 52 | if (map.containsKey(s)) { 53 | map.put(column2SignColumn(s), encryptor.sign(map.get(s).toString())); 54 | } 55 | }); 56 | 57 | getColumnsForEncryption().forEach(s -> { 58 | if (map.containsKey(s)) { 59 | map.put(s, encryptor.encrypt(map.get(s).toString())); 60 | } 61 | }); 62 | } 63 | 64 | default void decrypt(Map map) { 65 | AbstractEncryptor encryptor = getAEncryptor(); 66 | if (encryptor == null) return; 67 | 68 | getColumnsForEncryption().forEach(s -> { 69 | if (map.containsKey(s)) { 70 | map.put(s, encryptor.decrypt(map.get(s).toString())); 71 | } 72 | }); 73 | } 74 | 75 | default void checkSign(Map map) { 76 | AbstractEncryptor encryptor = getAEncryptor(); 77 | if (encryptor == null) return; 78 | 79 | getColumnsForSigning().forEach(s -> { 80 | if (map.containsKey(s)) { 81 | encryptor.checkSign(map.get(s).toString(), map.get(column2SignColumn(s)).toString()); 82 | } 83 | }); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/ISoftDeleteAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import net.ximatai.muyun.database.builder.Column; 4 | 5 | /** 6 | * 软删除的能力 7 | */ 8 | public interface ISoftDeleteAbility { 9 | 10 | default Column getSoftDeleteColumn() { 11 | return Column.DELETE_FLAG; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/ITableCreateAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import jakarta.transaction.Transactional; 4 | import net.ximatai.muyun.core.exception.MuYunException; 5 | import net.ximatai.muyun.database.IDatabaseOperations; 6 | import net.ximatai.muyun.database.builder.TableBuilder; 7 | import net.ximatai.muyun.database.builder.TableWrapper; 8 | 9 | /** 10 | * 创建表能力 11 | */ 12 | public interface ITableCreateAbility extends IMetadataAbility { 13 | 14 | /** 15 | * 装配表信息 16 | * 17 | * @param wrapper 18 | */ 19 | void fitOut(TableWrapper wrapper); 20 | 21 | default void onTableCreated(boolean isFirst) { 22 | } 23 | 24 | @Transactional 25 | default void create(IDatabaseOperations db) { 26 | String mainTable = getMainTable(); 27 | if (!mainTable.toLowerCase().equals(mainTable)) { 28 | throw new MuYunException("%s表名不合法,不允许使用大写字母".formatted(mainTable)); 29 | } 30 | 31 | TableWrapper wrapper = TableWrapper.withName(mainTable) 32 | .setSchema(getSchemaName()); 33 | 34 | fitOut(wrapper); 35 | 36 | if (this instanceof ITreeAbility ability) { 37 | wrapper.addColumn(ability.getParentKeyColumn()); 38 | } 39 | if (this instanceof ISortAbility ability) { 40 | wrapper.addColumn(ability.getSortColumn().getColumn()); 41 | } 42 | if (this instanceof ISecurityAbility ability) { 43 | ability.getSignColumns().forEach(wrapper::addColumn); 44 | } 45 | 46 | if (this instanceof ISoftDeleteAbility ability) { 47 | if (this instanceof IArchiveWhenDelete) { 48 | throw new MuYunException("ISoftDeleteAbility 能力与 IArchiveWhenDelete 能力互斥,不可同时采用"); 49 | } 50 | 51 | wrapper.addColumn(ability.getSoftDeleteColumn()); 52 | wrapper.addColumn("t_delete"); 53 | wrapper.addColumn("id_at_auth_user__delete"); 54 | } 55 | 56 | boolean build = new TableBuilder(db).build(wrapper); 57 | 58 | if (this instanceof IArchiveWhenDelete ability) { 59 | wrapper.setName(ability.getArchiveTableName()); 60 | wrapper.addColumn("t_archive", "归档时间", "now()"); 61 | wrapper.addColumn("id_at_auth_user__archive", "归档人"); 62 | new TableBuilder(db).build(wrapper); 63 | } 64 | 65 | this.onTableCreated(build); 66 | } 67 | 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/ITreeAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability; 2 | 3 | import jakarta.ws.rs.GET; 4 | import jakarta.ws.rs.Path; 5 | import jakarta.ws.rs.QueryParam; 6 | import net.ximatai.muyun.ability.curd.std.ISelectAbility; 7 | import net.ximatai.muyun.database.builder.Column; 8 | import net.ximatai.muyun.model.TreeNode; 9 | import net.ximatai.muyun.util.TreeBuilder; 10 | import org.eclipse.microprofile.openapi.annotations.Operation; 11 | import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; 12 | 13 | import java.util.List; 14 | import java.util.Objects; 15 | 16 | /** 17 | * 树型数据能力 18 | */ 19 | public interface ITreeAbility extends ISelectAbility, ISortAbility, IMetadataAbility, ILabelAbility { 20 | 21 | default Column getParentKeyColumn() { 22 | return Column.TREE_PID.setDefaultValue(TreeBuilder.ROOT_PID); 23 | } 24 | 25 | @GET 26 | @Path("/tree") 27 | @Operation(summary = "按树结构获取数据") 28 | default List tree(@Parameter(description = "指定根节点id") @QueryParam("rootID") String rootID, 29 | @Parameter(description = "是否展示指定的根节点") @QueryParam("showMe") Boolean showMe, 30 | @Parameter(description = "指定作为 Label 的列") @QueryParam("labelColumn") String labelColumn, 31 | @Parameter(description = "树的最大层级") @QueryParam("maxLevel") Integer maxLevel 32 | ) { 33 | if (showMe == null) { 34 | showMe = true; 35 | } 36 | if (rootID == null) { 37 | showMe = false; 38 | } 39 | if (maxLevel == null) { 40 | maxLevel = Integer.MAX_VALUE; 41 | } 42 | if (labelColumn == null) { 43 | labelColumn = getLabelColumn(); 44 | } 45 | Objects.requireNonNull(labelColumn); 46 | 47 | List list = this.view(null, null, true, null).getList(); 48 | 49 | if (list.isEmpty()) { 50 | return List.of(); 51 | } 52 | 53 | List tree = TreeBuilder.build(getPK(), getParentKeyColumn().getName(), list, rootID, showMe, labelColumn, maxLevel); 54 | 55 | if (tree == null) { 56 | return List.of(); 57 | } else { 58 | return tree; 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/curd/std/ICURDAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability.curd.std; 2 | 3 | /** 4 | * 增删改查能力集合 5 | */ 6 | public interface ICURDAbility extends ICreateAbility, IUpdateAbility, IDeleteAbility, ISelectAbility { 7 | } 8 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/curd/std/ICustomSelectSqlAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability.curd.std; 2 | 3 | /** 4 | * 自定义SQL查询数据的能力(代替默认的主表查询) 5 | */ 6 | public interface ICustomSelectSqlAbility extends ISelectAbility { 7 | 8 | String getCustomSql(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/ability/curd/std/IQueryAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.ability.curd.std; 2 | 3 | import jakarta.ws.rs.GET; 4 | import jakarta.ws.rs.POST; 5 | import jakarta.ws.rs.Path; 6 | import jakarta.ws.rs.QueryParam; 7 | import net.ximatai.muyun.model.PageResult; 8 | import net.ximatai.muyun.model.QueryGroup; 9 | import net.ximatai.muyun.model.QueryItem; 10 | import org.eclipse.microprofile.openapi.annotations.Operation; 11 | import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; 12 | import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.stream.Collectors; 17 | 18 | /** 19 | * 根据条件对数据进行复杂查询的能力 20 | */ 21 | public interface IQueryAbility extends ISelectAbility { 22 | 23 | List queryItemList(); 24 | 25 | default QueryGroup queryGroup() { 26 | return QueryGroup.of(queryItemList()); 27 | } 28 | 29 | @GET 30 | @Path("/queryColumns") 31 | @Operation(summary = "已配置可供查询的字段") 32 | default List queryColumns() { 33 | return queryItemList().stream().filter(item -> !item.isHide()).collect(Collectors.toList()); 34 | } 35 | 36 | @POST 37 | @Path("/view") 38 | @Operation(summary = "分页查询(带查询条件)") 39 | default PageResult view(@Parameter(description = "页码") @QueryParam("page") Integer page, 40 | @Parameter(description = "分页大小") @QueryParam("size") Long size, 41 | @Parameter(description = "是否分页") @QueryParam("noPage") Boolean noPage, 42 | @Parameter(description = "排序", example = "t_create,desc") @QueryParam("sort") List sort, 43 | @RequestBody(description = "查询条件信息") Map queryBody) { 44 | return this.view(page, size, noPage, sort, queryBody, queryGroup()); 45 | } 46 | 47 | default PageResult query(Map queryBody) { 48 | return this.view(null, null, true, null, queryBody, queryGroup()); 49 | } 50 | 51 | default PageResult query(Map queryBody, String authCondition) { 52 | return this.view(null, null, true, null, queryBody, queryGroup(), authCondition); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/base/BaseBusinessTable.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.base; 2 | 3 | import io.quarkus.runtime.Startup; 4 | import jakarta.inject.Singleton; 5 | import net.ximatai.muyun.ability.ITableCreateAbility; 6 | import net.ximatai.muyun.core.Scaffold; 7 | import net.ximatai.muyun.database.builder.Column; 8 | import net.ximatai.muyun.database.builder.TableBase; 9 | import net.ximatai.muyun.database.builder.TableWrapper; 10 | 11 | @Startup(1000) 12 | @Singleton 13 | public class BaseBusinessTable extends Scaffold implements ITableCreateAbility { 14 | 15 | public final static String SCHEMA_NAME = "base"; 16 | public final static String TABLE_NAME = "base_business"; 17 | public final static TableBase TABLE = new TableBase(SCHEMA_NAME, TABLE_NAME); 18 | 19 | @Override 20 | public String getSchemaName() { 21 | return SCHEMA_NAME; 22 | } 23 | 24 | @Override 25 | public String getMainTable() { 26 | return TABLE_NAME; 27 | } 28 | 29 | @Override 30 | public void fitOut(TableWrapper wrapper) { 31 | wrapper 32 | .setPrimaryKey(Column.ID_POSTGRES) 33 | .addColumn("t_create") 34 | .addColumn("t_update") 35 | .addColumn("id_at_app_region") // 数据归属行政区划 36 | .addColumn("id_at_auth_user__create") 37 | .addColumn("id_at_auth_user__update") 38 | .addColumn("id_at_auth_user__perms") //权限所有人 39 | .addColumn("id_at_org_department__perms") //权限所有部门 40 | .addColumn("id_at_org_organization__perms") //权限所有机构 41 | .addColumn("id_at_app_module__perms"); //权限所有模块(对应:权限隔离) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/base/BaseScaffold.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.base; 2 | 3 | import net.ximatai.muyun.ability.IAuthAbility; 4 | import net.ximatai.muyun.ability.IMetadataAbility; 5 | import net.ximatai.muyun.ability.ITableCreateAbility; 6 | import net.ximatai.muyun.ability.curd.std.ICURDAbility; 7 | import net.ximatai.muyun.core.Scaffold; 8 | 9 | public abstract class BaseScaffold extends Scaffold implements IMetadataAbility, ICURDAbility, ITableCreateAbility, IAuthAbility { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/ObjectMapperConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | import com.fasterxml.jackson.databind.module.SimpleModule; 7 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 8 | import jakarta.enterprise.context.ApplicationScoped; 9 | import jakarta.enterprise.inject.Produces; 10 | 11 | import java.io.IOException; 12 | import java.text.SimpleDateFormat; 13 | import java.time.Duration; 14 | import java.time.LocalDateTime; 15 | import java.time.format.DateTimeFormatter; 16 | import java.util.Date; 17 | 18 | @ApplicationScoped 19 | public class ObjectMapperConfig { 20 | 21 | @Produces 22 | public ObjectMapper objectMapper() { 23 | ObjectMapper objectMapper = new ObjectMapper(); 24 | 25 | // 创建一个自定义的 SimpleModule 并注册自定义的序列化器和反序列化器 26 | SimpleModule customModule = new SimpleModule(); 27 | customModule.addSerializer(Date.class, new CustomDateSerializer()); 28 | customModule.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer()); 29 | customModule.addSerializer(Duration.class, new CustomDurationSerializer()); 30 | 31 | objectMapper.registerModule(customModule); 32 | return objectMapper; 33 | } 34 | } 35 | 36 | // 自定义 Date 序列化器 37 | class CustomDateSerializer extends StdSerializer { 38 | private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 39 | private static final SimpleDateFormat CALENDAR_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); 40 | 41 | public CustomDateSerializer() { 42 | this(null); 43 | } 44 | 45 | public CustomDateSerializer(Class t) { 46 | super(t); 47 | } 48 | 49 | @Override 50 | public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException { 51 | if (value instanceof java.sql.Date) { 52 | gen.writeString(CALENDAR_FORMAT.format(value)); 53 | } else { 54 | gen.writeString(DATE_FORMAT.format(value)); 55 | } 56 | } 57 | } 58 | 59 | // 自定义 LocalDateTime 序列化器 60 | class CustomLocalDateTimeSerializer extends StdSerializer { 61 | private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 62 | 63 | public CustomLocalDateTimeSerializer() { 64 | super(LocalDateTime.class); 65 | } 66 | 67 | @Override 68 | public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { 69 | gen.writeString(value.format(FORMATTER)); 70 | } 71 | } 72 | 73 | // 自定义 Duration 序列化器 74 | class CustomDurationSerializer extends StdSerializer { 75 | 76 | public CustomDurationSerializer() { 77 | super(Duration.class); 78 | } 79 | 80 | @Override 81 | public void serialize(Duration value, JsonGenerator gen, SerializerProvider provider) throws IOException { 82 | gen.writeNumber(value.getSeconds()); 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/Scaffold.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core; 2 | 3 | import jakarta.annotation.PostConstruct; 4 | import net.ximatai.muyun.ability.IDatabaseAbility; 5 | import net.ximatai.muyun.ability.IRuntimeAbility; 6 | import net.ximatai.muyun.ability.ITableCreateAbility; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public abstract class Scaffold implements IDatabaseAbility, IRuntimeAbility { 11 | 12 | protected final Logger logger = LoggerFactory.getLogger(getClass()); 13 | 14 | @PostConstruct 15 | protected void init() { 16 | if (this instanceof ITableCreateAbility ability) { 17 | ability.create(getDatabaseOperations()); 18 | } 19 | afterInit(); 20 | logger.info("{} initialized", this.getClass().getSimpleName()); 21 | } 22 | 23 | protected void afterInit() { 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/config/FrontendConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.config; 2 | 3 | import io.smallrye.config.ConfigMapping; 4 | 5 | import java.util.List; 6 | 7 | @ConfigMapping(prefix = "frontend") 8 | public interface FrontendConfig { 9 | List resources(); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/config/FrontendItem.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.config; 2 | 3 | public interface FrontendItem { 4 | String prefix(); 5 | 6 | String path(); 7 | } 8 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/config/IProfile.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.config; 2 | 3 | import org.eclipse.microprofile.config.Config; 4 | import org.eclipse.microprofile.config.ConfigProvider; 5 | 6 | public interface IProfile { 7 | default ProfileMode profile() { 8 | Config config = ConfigProvider.getConfig(); 9 | String upperCase = config.getValue("quarkus.profile", String.class).toUpperCase(); 10 | return ProfileMode.valueOf(upperCase); 11 | } 12 | 13 | default boolean isTestMode() { 14 | return profile().equals(ProfileMode.TEST); 15 | } 16 | 17 | default boolean isProdMode() { 18 | return profile().equals(ProfileMode.PROD); 19 | } 20 | 21 | default boolean isDevMode() { 22 | return profile().equals(ProfileMode.DEV); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/config/MuYunConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.config; 2 | 3 | import io.smallrye.config.ConfigMapping; 4 | import io.smallrye.config.WithDefault; 5 | 6 | import java.util.Objects; 7 | 8 | @ConfigMapping(prefix = "muyun") 9 | public interface MuYunConfig extends IProfile { 10 | 11 | @WithDefault("1") 12 | String superUserId(); 13 | 14 | @WithDefault("24") 15 | int sessionTimeoutHour(); 16 | 17 | @WithDefault("false") 18 | boolean useSession(); 19 | 20 | default boolean isSuperUser(String userID) { 21 | Objects.requireNonNull(userID, "请提供测试用户ID"); 22 | return userID.equals(superUserId()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/config/ProfileMode.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.config; 2 | 3 | public enum ProfileMode { 4 | DEV, PROD, TEST 5 | } 6 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/config/Redirect.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.config; 2 | 3 | public interface Redirect { 4 | String from(); 5 | 6 | String to(); 7 | } 8 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.config; 2 | 3 | import io.smallrye.config.ConfigMapping; 4 | 5 | import java.util.List; 6 | 7 | @ConfigMapping(prefix = "web") 8 | public interface WebConfig { 9 | List redirects(); 10 | } 11 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/desensitization/Desensitizer.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.desensitization; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class Desensitizer { 7 | private final Map columnAlgorithmMap = new HashMap<>(); 8 | 9 | /** 10 | * 注册列名及对应的脱敏算法 11 | * 12 | * @param columnName 列名 13 | * @param algorithm 脱敏算法 14 | */ 15 | public Desensitizer registerAlgorithm(String columnName, IDesensitizationAlgorithm algorithm) { 16 | columnAlgorithmMap.put(columnName, algorithm); 17 | return this; 18 | } 19 | 20 | /** 21 | * 根据列名获取对应的脱敏算法 22 | * 23 | * @param columnName 列名 24 | * @return 对应的脱敏算法 25 | */ 26 | public IDesensitizationAlgorithm getAlgorithm(String columnName) { 27 | return columnAlgorithmMap.get(columnName); 28 | } 29 | 30 | /** 31 | * 根据列名和原始值进行脱敏 32 | * 33 | * @param columnName 列名 34 | * @param source 原始数据 35 | * @return 脱敏后的数据 36 | */ 37 | public String desensitize(String columnName, String source) { 38 | IDesensitizationAlgorithm algorithm = getAlgorithm(columnName); 39 | if (algorithm != null) { 40 | return algorithm.desensitize(source); 41 | } 42 | return source; // 没有注册脱敏算法则返回原始数据 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/desensitization/IDesensitizationAlgorithm.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.desensitization; 2 | 3 | public interface IDesensitizationAlgorithm { 4 | 5 | String desensitize(String source); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/desensitization/MaskEmailAlgorithm.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.desensitization; 2 | 3 | public enum MaskEmailAlgorithm implements IDesensitizationAlgorithm { 4 | INSTANCE; 5 | 6 | @Override 7 | public String desensitize(String source) { 8 | if (source == null || !source.contains("@")) { 9 | return source; // 非法邮箱不进行脱敏 10 | } 11 | String[] parts = source.split("@"); 12 | String localPart = parts[0]; 13 | String domainPart = parts[1]; 14 | 15 | if (localPart.length() <= 1) { 16 | return source; // 短邮箱不进行脱敏 17 | } 18 | 19 | // 只显示首尾字符,中间使用 * 号代替 20 | return localPart.charAt(0) + "*".repeat(localPart.length() - 2) + localPart.charAt(localPart.length() - 1) + "@" + domainPart; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/desensitization/MaskMiddleAlgorithm.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.desensitization; 2 | 3 | public enum MaskMiddleAlgorithm implements IDesensitizationAlgorithm { 4 | INSTANCE; 5 | 6 | @Override 7 | public String desensitize(String source) { 8 | if (source == null || source.length() <= 2) { 9 | return source; // 短字符串不进行脱敏 10 | } 11 | // 只显示首尾字符,中间使用 * 号代替 12 | int length = source.length(); 13 | return source.charAt(0) + "*".repeat(length - 2) + source.charAt(length - 1); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/desensitization/MaskPhoneNumberAlgorithm.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.desensitization; 2 | 3 | public enum MaskPhoneNumberAlgorithm implements IDesensitizationAlgorithm { 4 | INSTANCE; 5 | 6 | @Override 7 | public String desensitize(String source) { 8 | if (source == null || source.length() != 11) { 9 | return source; // 非法手机号不进行脱敏 10 | } 11 | // 只显示前3位和后4位,中间使用 * 号代替 12 | return source.substring(0, 3) + "****" + source.substring(7); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/exception/IToFrontendException.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.exception; 2 | 3 | public interface IToFrontendException { 4 | 5 | String getMessage(); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/exception/InvalidSignatureException.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.exception; 2 | 3 | public class InvalidSignatureException extends RuntimeException { 4 | 5 | public InvalidSignatureException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/exception/MuYunException.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.exception; 2 | 3 | public class MuYunException extends RuntimeException implements IToFrontendException { 4 | public MuYunException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/exception/MyException.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.exception; 2 | 3 | @Deprecated 4 | public class MyException extends RuntimeException { 5 | public MyException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/exception/PermsException.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.exception; 2 | 3 | public class PermsException extends RuntimeException implements IToFrontendException { 4 | 5 | public PermsException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/exception/QueryException.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.exception; 2 | 3 | public class QueryException extends RuntimeException { 4 | 5 | public QueryException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/global/DatabindExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.global; 2 | 3 | import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; 4 | import jakarta.ws.rs.core.Response; 5 | import jakarta.ws.rs.ext.ExceptionMapper; 6 | import jakarta.ws.rs.ext.Provider; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | @Provider 11 | public class DatabindExceptionMapper implements ExceptionMapper { 12 | 13 | private static final Logger LOGGER = LoggerFactory.getLogger(DatabindExceptionMapper.class); 14 | 15 | @Override 16 | public Response toResponse(UnrecognizedPropertyException exception) { 17 | 18 | LOGGER.error(exception.getMessage(), exception); 19 | 20 | return Response.status(Response.Status.BAD_REQUEST) 21 | .entity("请求错误") 22 | .build(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/core/security/AbstractEncryptor.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.core.security; 2 | 3 | import net.ximatai.muyun.core.exception.InvalidSignatureException; 4 | import net.ximatai.muyun.util.StringUtil; 5 | 6 | public abstract class AbstractEncryptor { 7 | 8 | public void checkSign(String source, String sign) { 9 | if (StringUtil.isBlank(sign)) return; 10 | if (StringUtil.isBlank(source)) return; 11 | if (!sign.equals(sign(source))) { 12 | throw new InvalidSignatureException("数据「%s」已被篡改".formatted(source)); 13 | } 14 | } 15 | 16 | abstract public String sign(String source); 17 | 18 | abstract public String encrypt(String source); 19 | 20 | abstract public String decrypt(String encodeText); 21 | } 22 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/http/AuthorizationFilter.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.http; 2 | 3 | import io.vertx.ext.web.RoutingContext; 4 | import jakarta.annotation.Priority; 5 | import jakarta.enterprise.context.ApplicationScoped; 6 | import jakarta.enterprise.inject.Instance; 7 | import jakarta.inject.Inject; 8 | import jakarta.ws.rs.Priorities; 9 | import jakarta.ws.rs.container.ContainerRequestContext; 10 | import jakarta.ws.rs.container.ContainerRequestFilter; 11 | import jakarta.ws.rs.ext.Provider; 12 | import net.ximatai.muyun.ability.IRuntimeAbility; 13 | import net.ximatai.muyun.model.ApiRequest; 14 | import net.ximatai.muyun.service.IAuthorizationService; 15 | import org.eclipse.microprofile.config.inject.ConfigProperty; 16 | 17 | import java.io.IOException; 18 | 19 | @Provider 20 | @Priority(Priorities.AUTHENTICATION) 21 | @ApplicationScoped 22 | public class AuthorizationFilter implements ContainerRequestFilter, IRuntimeAbility { 23 | 24 | @ConfigProperty(name = "quarkus.rest.path") 25 | String restPath; 26 | 27 | @Inject 28 | RoutingContext routingContext; 29 | 30 | @Inject 31 | Instance authorizationService; 32 | 33 | @Override 34 | public void filter(ContainerRequestContext requestContext) throws IOException { 35 | String path = requestContext.getUriInfo().getRequestUri().getPath(); 36 | 37 | if (path.startsWith(restPath)) { //仅对 /api开头的请求做权限拦截 38 | ApiRequest apiRequest = getApiRequest(); 39 | 40 | if (authorizationService.isResolvable() && !authorizationService.get().isAuthorized(apiRequest)) { 41 | throw apiRequest.getError(); 42 | } 43 | } 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/http/RedirectRouter.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.http; 2 | 3 | import io.quarkus.runtime.StartupEvent; 4 | import io.vertx.ext.web.Router; 5 | import jakarta.enterprise.event.Observes; 6 | import jakarta.inject.Inject; 7 | import net.ximatai.muyun.core.config.WebConfig; 8 | 9 | public class RedirectRouter { 10 | 11 | @Inject 12 | WebConfig webConfig; 13 | 14 | void installRoute(@Observes StartupEvent startupEvent, Router router) { 15 | webConfig.redirects().forEach(r -> { 16 | router.route(r.from()) 17 | .handler(rc -> { 18 | rc.response().setStatusCode(301) 19 | .putHeader("Location", r.to()) 20 | .end(); 21 | }); 22 | 23 | }); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/http/SockJsBridgeRouter.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.http; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.json.JsonObject; 5 | import io.vertx.ext.bridge.PermittedOptions; 6 | import io.vertx.ext.web.Router; 7 | import io.vertx.ext.web.handler.sockjs.SockJSBridgeOptions; 8 | import io.vertx.ext.web.handler.sockjs.SockJSHandler; 9 | import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions; 10 | import jakarta.enterprise.context.ApplicationScoped; 11 | import jakarta.enterprise.event.Observes; 12 | 13 | @ApplicationScoped 14 | public class SockJsBridgeRouter { 15 | 16 | void init(@Observes Router router, Vertx vertx) { 17 | SockJSHandlerOptions options = new SockJSHandlerOptions(); 18 | SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options); 19 | 20 | router.route("/api/eventbus/*") 21 | .subRouter(sockJSHandler.bridge(createBridgeOptions())); 22 | } 23 | 24 | private SockJSBridgeOptions createBridgeOptions() { 25 | SockJSBridgeOptions options = new SockJSBridgeOptions(); 26 | options.addInboundPermitted(new PermittedOptions().setAddressRegex("web\\..+")); 27 | options.addOutboundPermitted(new PermittedOptions().setAddressRegex("web\\..+")); 28 | options.addOutboundPermitted(new PermittedOptions().setAddressRegex("data.change\\..+").setMatch(new JsonObject().put("toFrontEnd", true))); 29 | return options; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/http/StaticResourcesRouter.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.http; 2 | 3 | import io.quarkus.runtime.StartupEvent; 4 | import io.vertx.ext.web.Router; 5 | import io.vertx.ext.web.handler.FileSystemAccess; 6 | import io.vertx.ext.web.handler.StaticHandler; 7 | import jakarta.enterprise.event.Observes; 8 | import jakarta.inject.Inject; 9 | import net.ximatai.muyun.core.config.FrontendConfig; 10 | 11 | public class StaticResourcesRouter { 12 | 13 | @Inject 14 | FrontendConfig frontendConfig; 15 | 16 | void installRoute(@Observes StartupEvent startupEvent, Router router) { 17 | 18 | frontendConfig.resources() 19 | .forEach(item -> { 20 | String prefix = item.prefix(); 21 | 22 | if (item.prefix().endsWith("/")) { 23 | prefix = item.prefix() + "*"; 24 | } 25 | 26 | router.route(prefix) 27 | .handler(StaticHandler.create(FileSystemAccess.ROOT, item.path())); 28 | }); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/BatchResult.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import org.eclipse.microprofile.openapi.annotations.media.Schema; 4 | 5 | @Schema(description = "数据变更结果") 6 | public class BatchResult { 7 | @Schema(description = "新增数量") 8 | private int create; 9 | @Schema(description = "修改数量") 10 | private int update; 11 | @Schema(description = "删除数量") 12 | private int delete; 13 | 14 | public BatchResult() { 15 | } 16 | 17 | public BatchResult(int create, int update, int delete) { 18 | this.create = create; 19 | this.update = update; 20 | this.delete = delete; 21 | } 22 | 23 | public int getCreate() { 24 | return create; 25 | } 26 | 27 | public void setCreate(int create) { 28 | this.create = create; 29 | } 30 | 31 | public int getUpdate() { 32 | return update; 33 | } 34 | 35 | public void setUpdate(int update) { 36 | this.update = update; 37 | } 38 | 39 | public int getDelete() { 40 | return delete; 41 | } 42 | 43 | public void setDelete(int delete) { 44 | this.delete = delete; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/CheckConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class CheckConfig { 7 | 8 | private final Map nonEmptyMap = new HashMap<>(); 9 | private final Map uniqueMap = new HashMap<>(); 10 | 11 | public void addNonEmpty(String column, String tipWhenEmpty) { 12 | nonEmptyMap.put(column, tipWhenEmpty); 13 | } 14 | 15 | public void addUnique(String column, String tipWhenNonUnique) { 16 | uniqueMap.put(column, tipWhenNonUnique); 17 | } 18 | 19 | public Map getNonEmptyMap() { 20 | return nonEmptyMap; 21 | } 22 | 23 | public Map getUniqueMap() { 24 | return uniqueMap; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/ChildTableInfo.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import net.ximatai.muyun.ability.IChildAbility; 4 | 5 | public class ChildTableInfo { 6 | private final IChildAbility ctrl; 7 | private final String foreignKey; 8 | private boolean isAutoDelete = false; 9 | private String childAlias; 10 | 11 | public ChildTableInfo(String foreignKey, IChildAbility ctrl) { 12 | this.foreignKey = foreignKey; 13 | this.ctrl = ctrl; 14 | this.childAlias = ctrl.getMainTable(); 15 | } 16 | 17 | public ChildTableInfo setAutoDelete() { 18 | this.isAutoDelete = true; 19 | return this; 20 | } 21 | 22 | public ChildTableInfo setChildAlias(String childAlias) { 23 | this.childAlias = childAlias; 24 | return this; 25 | } 26 | 27 | public IChildAbility getCtrl() { 28 | return ctrl; 29 | } 30 | 31 | public String getForeignKey() { 32 | return foreignKey; 33 | } 34 | 35 | public boolean isAutoDelete() { 36 | return isAutoDelete; 37 | } 38 | 39 | public String getChildAlias() { 40 | return childAlias; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/DataChangeChannel.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import net.ximatai.muyun.ability.IMetadataAbility; 4 | 5 | public class DataChangeChannel { 6 | 7 | private final String schema; 8 | private final String table; 9 | 10 | public DataChangeChannel(String schema, String table) { 11 | this.schema = schema; 12 | this.table = table; 13 | } 14 | 15 | public DataChangeChannel(IMetadataAbility metadataAbility) { 16 | this.schema = metadataAbility.getSchemaName(); 17 | this.table = metadataAbility.getMainTable(); 18 | } 19 | 20 | public String getAddress() { 21 | return "data.change.%s.%s".formatted(schema, table).toLowerCase(); 22 | } 23 | 24 | public String getAddressWithType(Type type) { 25 | return "data.%s.%s.%s".formatted(type, schema, table).toLowerCase(); 26 | } 27 | 28 | public enum Type { 29 | CREATE, UPDATE, DELETE 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/IRuntimeUser.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | 5 | public interface IRuntimeUser { 6 | 7 | IRuntimeUser WHITE = new IRuntimeUser() { 8 | @Override 9 | public String getId() { 10 | return "0"; 11 | } 12 | 13 | @Override 14 | public String getName() { 15 | return "白名单用户"; 16 | } 17 | 18 | @Override 19 | public String getUsername() { 20 | return "white"; 21 | } 22 | 23 | @Override 24 | public String getOrganizationId() { 25 | return "0"; 26 | } 27 | 28 | @Override 29 | public String getDepartmentId() { 30 | return "0"; 31 | } 32 | }; 33 | 34 | String getId(); 35 | 36 | String getName(); 37 | 38 | String getUsername(); 39 | 40 | String getOrganizationId(); 41 | 42 | String getDepartmentId(); 43 | 44 | static IRuntimeUser build(String id) { 45 | return new IRuntimeUser() { 46 | 47 | @Override 48 | public String getId() { 49 | return id; 50 | } 51 | 52 | @Override 53 | public String getName() { 54 | return ""; 55 | } 56 | 57 | @Override 58 | public String getUsername() { 59 | return ""; 60 | } 61 | 62 | @Override 63 | public String getOrganizationId() { 64 | return ""; 65 | } 66 | 67 | @Override 68 | public String getDepartmentId() { 69 | return ""; 70 | } 71 | }; 72 | } 73 | 74 | static IRuntimeUser build(JsonObject object) { 75 | return new IRuntimeUser() { 76 | 77 | @Override 78 | public String getId() { 79 | return object.getString("id"); 80 | } 81 | 82 | @Override 83 | public String getName() { 84 | return object.getString("name"); 85 | } 86 | 87 | @Override 88 | public String getUsername() { 89 | return object.getString("username"); 90 | } 91 | 92 | @Override 93 | public String getOrganizationId() { 94 | return object.getString("organizationId"); 95 | } 96 | 97 | @Override 98 | public String getDepartmentId() { 99 | return object.getString("departmentId"); 100 | } 101 | }; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/PageResult.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import org.eclipse.microprofile.openapi.annotations.media.Schema; 4 | 5 | import java.util.List; 6 | 7 | @Schema(description = "分页结果") 8 | public class PageResult { 9 | @Schema(description = "数据列表") 10 | private List list; 11 | @Schema(description = "总数") 12 | private long total; 13 | @Schema(description = "分页大小") 14 | private long size; 15 | @Schema(description = "页码") 16 | private int page; 17 | 18 | public PageResult() { 19 | } 20 | 21 | public PageResult(List list, long total, long size, int page) { 22 | this.list = list; 23 | this.total = total; 24 | this.size = size; 25 | this.page = page; 26 | } 27 | 28 | public List getList() { 29 | return list; 30 | } 31 | 32 | public long getTotal() { 33 | return total; 34 | } 35 | 36 | public long getSize() { 37 | return size; 38 | } 39 | 40 | public int getPage() { 41 | return page; 42 | } 43 | 44 | public void setList(List list) { 45 | this.list = list; 46 | } 47 | 48 | public void setTotal(long total) { 49 | this.total = total; 50 | } 51 | 52 | public void setSize(long size) { 53 | this.size = size; 54 | } 55 | 56 | public void setPage(int page) { 57 | this.page = page; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/QueryGroup.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class QueryGroup { 7 | 8 | private final QueryItem queryItem; 9 | private List andGroups = new ArrayList<>(); 10 | private List orGroups = new ArrayList<>(); 11 | 12 | private QueryGroup() { 13 | this.queryItem = null; 14 | } 15 | 16 | private QueryGroup(QueryItem column) { 17 | this.queryItem = column; 18 | } 19 | 20 | public static QueryGroup of(List columns) { 21 | if (columns == null || columns.isEmpty()) { 22 | return null; 23 | } 24 | 25 | QueryGroup first = null; 26 | for (int i = 0; i < columns.size(); i++) { 27 | QueryGroup group = new QueryGroup(columns.get(i)); 28 | if (i == 0) { 29 | first = group; 30 | } else { 31 | first.and(group); 32 | } 33 | } 34 | 35 | return first; 36 | } 37 | 38 | public static QueryGroup ofBlank() { 39 | return new QueryGroup(); 40 | } 41 | 42 | public static QueryGroup of(QueryItem column) { 43 | return new QueryGroup(column); 44 | } 45 | 46 | public QueryGroup and(QueryGroup group) { 47 | this.andGroups.add(group); 48 | return this; 49 | } 50 | 51 | public QueryGroup or(QueryGroup group) { 52 | this.orGroups.add(group); 53 | return this; 54 | } 55 | 56 | public QueryItem getQueryItem() { 57 | return queryItem; 58 | } 59 | 60 | public List getAndGroups() { 61 | return andGroups; 62 | } 63 | 64 | public List getOrGroups() { 65 | return orGroups; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/SortColumn.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import net.ximatai.muyun.database.builder.Column; 4 | 5 | public class SortColumn { 6 | 7 | public static final SortColumn CREATE = new SortColumn(Column.CREATE, Type.DESC); 8 | public static final SortColumn ORDER = new SortColumn(Column.ORDER, Type.ASC); 9 | public static final SortColumn CODE = new SortColumn(Column.CODE, Type.ASC); 10 | 11 | private Column column; 12 | private final String columnName; 13 | private final Type type; 14 | 15 | public enum Type { 16 | DESC, ASC; 17 | 18 | public boolean isASC() { 19 | return this == ASC; 20 | } 21 | } 22 | 23 | public SortColumn(Column column, Type type) { 24 | this.column = column; 25 | this.columnName = column.getName(); 26 | this.type = type; 27 | } 28 | 29 | public SortColumn(String columnName, Type type) { 30 | this.columnName = columnName; 31 | this.type = type; 32 | } 33 | 34 | public SortColumn(String columnName, String typeString) { 35 | this.columnName = columnName; 36 | if (typeString != null) { 37 | this.type = Type.valueOf(typeString.toUpperCase()); 38 | } else { 39 | this.type = Type.ASC; 40 | } 41 | } 42 | 43 | public SortColumn(String columnName) { 44 | this.columnName = columnName; 45 | this.type = Type.ASC; 46 | } 47 | 48 | public String getColumnName() { 49 | return columnName; 50 | } 51 | 52 | public Type getType() { 53 | return type; 54 | } 55 | 56 | public Column getColumn() { 57 | if (column == null) { 58 | column = Column.of(columnName); 59 | } 60 | return column; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/TreeNode.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model; 2 | 3 | import org.eclipse.microprofile.openapi.annotations.media.Schema; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | @Schema(description = "树节点") 9 | public class TreeNode { 10 | @Schema(description = "数据id") 11 | private String id; 12 | @Schema(description = "数据标题") 13 | private String label; 14 | @Schema(description = "数据内容") 15 | private Map data; 16 | @Schema(description = "子节点") 17 | private List children; 18 | 19 | public TreeNode() { 20 | } 21 | 22 | public String getId() { 23 | return id; 24 | } 25 | 26 | public String getLabel() { 27 | return label; 28 | } 29 | 30 | public Map getData() { 31 | return data; 32 | } 33 | 34 | public List getChildren() { 35 | return children; 36 | } 37 | 38 | public TreeNode setId(String id) { 39 | this.id = id; 40 | return this; 41 | } 42 | 43 | public TreeNode setLabel(String label) { 44 | this.label = label; 45 | return this; 46 | } 47 | 48 | public TreeNode setData(Map data) { 49 | this.data = data; 50 | return this; 51 | } 52 | 53 | public TreeNode setChildren(List children) { 54 | this.children = children; 55 | return this; 56 | } 57 | 58 | @Override 59 | public int hashCode() { 60 | return getId().hashCode(); 61 | } 62 | 63 | @Override 64 | public boolean equals(Object obj) { 65 | if (obj instanceof TreeNode node) { 66 | return this.getId().equals(node.getId()); 67 | } else { 68 | return false; 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/code/CodeGenerateConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model.code; 2 | 3 | import net.ximatai.muyun.core.exception.MuYunException; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class CodeGenerateConfig { 9 | 10 | private List codePartList = new ArrayList<>(); 11 | 12 | public List getCodePartList() { 13 | return codePartList; 14 | } 15 | 16 | // 构造函数:初始化 codePartList 17 | public CodeGenerateConfig(List codePartList) { 18 | int serialIndex = -1; 19 | 20 | // 查找 SerialCodePart 在列表中的位置 21 | for (int i = 0; i < codePartList.size(); i++) { 22 | if (codePartList.get(i) instanceof SerialCodePart) { 23 | serialIndex = i; 24 | break; 25 | } 26 | } 27 | 28 | // 验证 SerialCodePart 必须是列表中的最后一部分 29 | if (serialIndex > -1 && serialIndex != codePartList.size() - 1) { 30 | throw new MuYunException("流水号必须放置与单号生成器的最后一部分"); 31 | } 32 | 33 | this.codePartList = codePartList; 34 | } 35 | 36 | public CodeGenerateConfig(int serialWidth) { 37 | this.codePartList.add(new SerialCodePart(serialWidth)); 38 | } 39 | 40 | public CodeGenerateConfig(String prefix, int serialWidth) { 41 | this.codePartList.add(new SimpleCodePart(prefix)); 42 | this.codePartList.add(new SerialCodePart(serialWidth)); 43 | } 44 | 45 | public CodeGenerateConfig(String prefix, boolean useDate, int serialWidth) { 46 | this.codePartList.add(new SimpleCodePart(prefix)); 47 | if (useDate) { 48 | this.codePartList.add(new DateCodePart()); 49 | } 50 | this.codePartList.add(new SerialCodePart(serialWidth)); 51 | } 52 | 53 | public CodeGenerateConfig(String prefix, TransformCodePart transformCodePart, int serialWidth) { 54 | this.codePartList.add(new SimpleCodePart(prefix)); 55 | this.codePartList.add(transformCodePart); 56 | this.codePartList.add(new SerialCodePart(serialWidth)); 57 | } 58 | 59 | public CodeGenerateConfig(String prefix, TransformCodePart transformCodePart, boolean useDate, int serialWidth) { 60 | this.codePartList.add(new SimpleCodePart(prefix)); 61 | this.codePartList.add(transformCodePart); 62 | if (useDate) { 63 | this.codePartList.add(new DateCodePart()); 64 | } 65 | this.codePartList.add(new SerialCodePart(serialWidth)); 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/code/DateCodePart.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model.code; 2 | 3 | import java.time.LocalDate; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | public class DateCodePart implements ICodePart { 7 | private final String formatString; 8 | 9 | public DateCodePart() { 10 | this.formatString = "yyyyMMdd"; 11 | } 12 | 13 | public DateCodePart(String formatString) { 14 | this.formatString = formatString; 15 | } 16 | 17 | @Override 18 | public String varchar() { 19 | return LocalDate.now().format(DateTimeFormatter.ofPattern(formatString)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/code/ICodePart.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model.code; 2 | 3 | public interface ICodePart { 4 | String varchar(); 5 | } 6 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/code/SerialCodePart.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model.code; 2 | 3 | public class SerialCodePart implements ICodePart { 4 | private final int width; 5 | private long base; 6 | 7 | public SerialCodePart setBase(long base) { 8 | this.base = base; 9 | return this; 10 | } 11 | 12 | public SerialCodePart(int width) { 13 | this.width = width; 14 | } 15 | 16 | @Override 17 | public String varchar() { 18 | return String.format("%0" + width + "d", ++base); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/code/SimpleCodePart.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model.code; 2 | 3 | public class SimpleCodePart implements ICodePart { 4 | private final String prefix; 5 | 6 | public SimpleCodePart(String prefix) { 7 | this.prefix = prefix; 8 | } 9 | 10 | @Override 11 | public String varchar() { 12 | return prefix; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/code/TransformCodePart.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model.code; 2 | 3 | import java.util.Map; 4 | import java.util.function.Function; 5 | 6 | public class TransformCodePart implements ICodePart { 7 | private final int width; 8 | private final Function transform; 9 | private Map data; 10 | 11 | public void setData(Map data) { 12 | this.data = data; 13 | } 14 | 15 | public TransformCodePart(int width, Function transform) { 16 | this.width = width; 17 | this.transform = transform; 18 | } 19 | 20 | @Override 21 | public String varchar() { 22 | if (data != null) { 23 | return transform.apply(data); 24 | } else { 25 | return "X".repeat(width); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/log/LogAccessItem.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model.log; 2 | 3 | public class LogAccessItem extends LogItem { 4 | 5 | 6 | } 7 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/model/log/LogLoginItem.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.model.log; 2 | 3 | public class LogLoginItem extends LogItem { 4 | 5 | 6 | } 7 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/service/IAuthorizationService.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.service; 2 | 3 | import net.ximatai.muyun.model.ApiRequest; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | public interface IAuthorizationService { 10 | 11 | /** 12 | * 校验权限 13 | * 14 | * @param request 15 | * @return 16 | */ 17 | boolean isAuthorized(ApiRequest request); 18 | 19 | /** 20 | * 校验功能权限 21 | * 22 | * @param userID 23 | * @param module 24 | * @param action 25 | * @return 26 | */ 27 | boolean isAuthorized(String userID, String module, String action); 28 | 29 | /** 30 | * 校验数据权限 31 | * 32 | * @param userID 33 | * @param module 34 | * @param action 35 | * @param dataID 36 | * @return 37 | */ 38 | boolean isDataAuthorized(String userID, String module, String action, String dataID); 39 | 40 | /** 41 | * 获取权限条件字符串 42 | * 43 | * @param userID 44 | * @param module 45 | * @param action 46 | * @return 47 | */ 48 | String getAuthCondition(String userID, String module, String action); 49 | 50 | /** 51 | * 获取可访问的功能列表 52 | * 53 | * @param userID 54 | * @param module 55 | * @return 56 | */ 57 | List getAllowedActions(String userID, String module); 58 | 59 | /** 60 | * 获取可访问的全量资源列表 61 | * 62 | * @param userID 63 | * @return 64 | */ 65 | Map> getAuthorizedResources(String userID); 66 | 67 | /** 68 | * 获取用户拥有的角色信息 69 | * 70 | * @param userID 71 | * @return 72 | */ 73 | Set getUserAvailableRoles(String userID); 74 | 75 | } 76 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/service/ILogAccess.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.service; 2 | 3 | import net.ximatai.muyun.model.log.LogItem; 4 | 5 | public interface ILogAccess { 6 | 7 | void log(LogItem logItem); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/service/ILogError.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.service; 2 | 3 | import net.ximatai.muyun.model.log.LogItem; 4 | 5 | public interface ILogError { 6 | 7 | void log(LogItem logItem); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/service/ILogLogin.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.service; 2 | 3 | import net.ximatai.muyun.model.log.LogItem; 4 | 5 | public interface ILogLogin { 6 | 7 | void log(LogItem logItem); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/service/IRuntimeProvider.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.service; 2 | 3 | import io.vertx.ext.web.RoutingContext; 4 | import net.ximatai.muyun.model.ApiRequest; 5 | import net.ximatai.muyun.model.IRuntimeUser; 6 | 7 | import java.util.Optional; 8 | 9 | public interface IRuntimeProvider { 10 | 11 | Optional getUser(RoutingContext context); 12 | 13 | default ApiRequest apiRequest(RoutingContext context) { 14 | return new ApiRequest(context.request().path()); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.util; 2 | 3 | public class StringUtil { 4 | 5 | public static boolean isBlank(Object x) { 6 | return switch (x) { 7 | case null -> true; 8 | case String str -> str.isBlank() || "NULL".equalsIgnoreCase(str.trim()); 9 | default -> false; 10 | }; 11 | } 12 | 13 | public static boolean isNotBlank(Object x) { 14 | return !isBlank(x); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /muyun-core/src/main/java/net/ximatai/muyun/util/UserAgentParser.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.util; 2 | 3 | public class UserAgentParser { 4 | 5 | /** 6 | * 从 User-Agent 头中获取操作系统信息 7 | * 8 | * @param userAgent User-Agent 字符串 9 | * @return 操作系统名称 10 | */ 11 | public static String getOS(String userAgent) { 12 | if (userAgent == null || userAgent.isEmpty()) { 13 | return "Unknown OS"; 14 | } 15 | 16 | // 检测常见的操作系统类型 17 | if (userAgent.contains("Windows NT")) { 18 | if (userAgent.contains("Windows NT 11.0")) { 19 | return "Windows 11"; 20 | } else if (userAgent.contains("Windows NT 10.0")) { 21 | return "Windows 10"; 22 | } else if (userAgent.contains("Windows NT 6.3")) { 23 | return "Windows 8.1"; 24 | } else if (userAgent.contains("Windows NT 6.2")) { 25 | return "Windows 8"; 26 | } else if (userAgent.contains("Windows NT 6.1")) { 27 | return "Windows 7"; 28 | } else { 29 | return "Windows (Unknown version)"; 30 | } 31 | } else if (userAgent.contains("Mac OS X")) { 32 | return "Mac OS X"; 33 | } else if (userAgent.contains("Linux")) { 34 | return "Linux"; 35 | } else if (userAgent.contains("Android")) { 36 | return "Android"; 37 | } else if (userAgent.contains("iPhone")) { 38 | return "iOS (iPhone)"; 39 | } else if (userAgent.contains("iPad")) { 40 | return "iOS (iPad)"; 41 | } else if (userAgent.contains("Unix")) { 42 | return "Unix"; 43 | } 44 | 45 | return "Unknown OS"; 46 | } 47 | 48 | /** 49 | * 从 User-Agent 头中获取浏览器信息 50 | * 51 | * @param userAgent User-Agent 字符串 52 | * @return 浏览器名称 53 | */ 54 | public static String getBrowser(String userAgent) { 55 | if (userAgent == null || userAgent.isEmpty()) { 56 | return "Unknown Browser"; 57 | } 58 | 59 | // 检测常见的浏览器类型 60 | if (userAgent.contains("Edge") || userAgent.contains("Edg/")) { 61 | return "Microsoft Edge"; 62 | } else if (userAgent.contains("Chrome")) { 63 | return "Google Chrome"; 64 | } else if (userAgent.contains("Safari") && !userAgent.contains("Chrome")) { 65 | return "Safari"; 66 | } else if (userAgent.contains("Firefox")) { 67 | return "Mozilla Firefox"; 68 | } else if (userAgent.contains("MSIE") || userAgent.contains("Trident")) { 69 | return "Internet Explorer"; 70 | } else if (userAgent.contains("Opera") || userAgent.contains("OPR")) { 71 | return "Opera"; 72 | } 73 | 74 | return "Unknown Browser"; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /muyun-database-std/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(enforcedPlatform(libs.quarkus.platform.bom)) 8 | api(project(":muyun-database")) 9 | 10 | api(libs.yasson) 11 | api("io.quarkus:quarkus-agroal") 12 | api("io.quarkus:quarkus-jdbc-postgresql") 13 | } 14 | -------------------------------------------------------------------------------- /muyun-database-std/src/main/java/net/ximatai/muyun/database/std/DBInfoProvider.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.std; 2 | 3 | import jakarta.inject.Inject; 4 | import net.ximatai.muyun.database.IDBInfoProvider; 5 | import net.ximatai.muyun.database.metadata.DBInfo; 6 | import org.jdbi.v3.core.Jdbi; 7 | 8 | public class DBInfoProvider implements IDBInfoProvider { 9 | 10 | private Jdbi jdbi; 11 | private DBInfo dbInfo; 12 | 13 | @Override 14 | public Jdbi getJdbi() { 15 | return jdbi; 16 | } 17 | 18 | @Inject 19 | @Override 20 | public void setJdbi(Jdbi jdbi) { 21 | this.jdbi = jdbi; 22 | } 23 | 24 | @Override 25 | public void resetDBInfo() { 26 | dbInfo = null; 27 | } 28 | 29 | @Override 30 | public DBInfo getDBInfo() { 31 | if (dbInfo == null) { 32 | dbInfo = IDBInfoProvider.super.getDBInfo(); 33 | } 34 | return dbInfo; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /muyun-database-std/src/main/java/net/ximatai/muyun/database/std/JdbiProducer.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.std; 2 | 3 | import io.agroal.api.AgroalDataSource; 4 | import jakarta.enterprise.context.ApplicationScoped; 5 | import jakarta.enterprise.inject.Produces; 6 | import jakarta.inject.Inject; 7 | import net.ximatai.muyun.database.std.argument.List2JsonArgumentFactory; 8 | import net.ximatai.muyun.database.std.argument.Map2JsonArgumentFactory; 9 | import net.ximatai.muyun.database.std.argument.PgArrayArgumentFactory; 10 | import org.jdbi.v3.core.Jdbi; 11 | import org.jdbi.v3.core.statement.Slf4JSqlLogger; 12 | 13 | @ApplicationScoped 14 | public class JdbiProducer { 15 | 16 | @Inject 17 | AgroalDataSource dataSource; 18 | 19 | @Produces 20 | @ApplicationScoped 21 | public Jdbi createJdbi() { 22 | return Jdbi.create(dataSource) 23 | .setSqlLogger(new Slf4JSqlLogger()) 24 | .registerArgument(new PgArrayArgumentFactory()) 25 | .registerArgument(new Map2JsonArgumentFactory()) 26 | .registerArgument(new List2JsonArgumentFactory()); 27 | // .installPlugin(new PostgresPlugin()) 28 | // .registerArrayType(String.class, "varchar") 29 | // .registerArgument(new PgArrayArgumentFactory()) 30 | // .registerColumnMapper(new SqlArrayMapperFactory()) 31 | // .registerColumnMapper(new PgArrayToListMapperFactory()) 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /muyun-database-std/src/main/java/net/ximatai/muyun/database/std/argument/List2JsonArgumentFactory.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.std.argument; 2 | 3 | import jakarta.json.bind.Jsonb; 4 | import jakarta.json.bind.JsonbBuilder; 5 | import org.jdbi.v3.core.argument.AbstractArgumentFactory; 6 | import org.jdbi.v3.core.argument.Argument; 7 | import org.jdbi.v3.core.config.ConfigRegistry; 8 | import org.postgresql.util.PGobject; 9 | 10 | import java.sql.SQLException; 11 | import java.sql.Types; 12 | import java.util.List; 13 | 14 | public class List2JsonArgumentFactory extends AbstractArgumentFactory { 15 | 16 | Jsonb jsonb = JsonbBuilder.create(); 17 | 18 | public List2JsonArgumentFactory() { 19 | super(Types.OTHER); 20 | } 21 | 22 | @Override 23 | protected Argument build(List value, ConfigRegistry config) { 24 | return (position, statement, ctx) -> { 25 | try { 26 | PGobject jsonObject = new PGobject(); 27 | jsonObject.setType("json"); 28 | jsonObject.setValue(jsonb.toJson(value)); 29 | statement.setObject(position, jsonObject); 30 | } catch (SQLException e) { 31 | throw new RuntimeException("Error setting PgArray argument", e); 32 | } 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /muyun-database-std/src/main/java/net/ximatai/muyun/database/std/argument/Map2JsonArgumentFactory.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.std.argument; 2 | 3 | import jakarta.json.bind.Jsonb; 4 | import jakarta.json.bind.JsonbBuilder; 5 | import org.jdbi.v3.core.argument.AbstractArgumentFactory; 6 | import org.jdbi.v3.core.argument.Argument; 7 | import org.jdbi.v3.core.config.ConfigRegistry; 8 | import org.postgresql.util.PGobject; 9 | 10 | import java.sql.SQLException; 11 | import java.sql.Types; 12 | import java.util.Map; 13 | 14 | public class Map2JsonArgumentFactory extends AbstractArgumentFactory { 15 | 16 | Jsonb jsonb = JsonbBuilder.create(); 17 | 18 | public Map2JsonArgumentFactory() { 19 | super(Types.OTHER); 20 | } 21 | 22 | @Override 23 | protected Argument build(Map value, ConfigRegistry config) { 24 | return (position, statement, ctx) -> { 25 | try { 26 | PGobject jsonObject = new PGobject(); 27 | jsonObject.setType("json"); 28 | jsonObject.setValue(jsonb.toJson(value)); 29 | statement.setObject(position, jsonObject); 30 | } catch (SQLException e) { 31 | throw new RuntimeException("Error setting PgArray argument", e); 32 | } 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /muyun-database-std/src/main/java/net/ximatai/muyun/database/std/argument/PgArrayArgumentFactory.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.std.argument; 2 | 3 | import org.jdbi.v3.core.argument.AbstractArgumentFactory; 4 | import org.jdbi.v3.core.argument.Argument; 5 | import org.jdbi.v3.core.config.ConfigRegistry; 6 | import org.postgresql.jdbc.PgArray; 7 | 8 | import java.sql.SQLException; 9 | import java.sql.Types; 10 | 11 | public class PgArrayArgumentFactory extends AbstractArgumentFactory { 12 | 13 | public PgArrayArgumentFactory() { 14 | super(Types.ARRAY); // 指定数据库类型为 ARRAY 15 | } 16 | 17 | @Override 18 | protected Argument build(PgArray value, ConfigRegistry config) { 19 | return (position, statement, ctx) -> { 20 | try { 21 | statement.setArray(position, value); 22 | } catch (SQLException e) { 23 | throw new RuntimeException("Error setting PgArray argument", e); 24 | } 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /muyun-database-std/src/main/java/net/ximatai/muyun/database/std/argument/StringArrayArgumentFactory.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.std.argument; 2 | 3 | import org.jdbi.v3.core.argument.AbstractArgumentFactory; 4 | import org.jdbi.v3.core.argument.Argument; 5 | import org.jdbi.v3.core.config.ConfigRegistry; 6 | 7 | import java.sql.Array; 8 | import java.sql.SQLException; 9 | import java.sql.Types; 10 | 11 | public class StringArrayArgumentFactory extends AbstractArgumentFactory { 12 | 13 | public StringArrayArgumentFactory() { 14 | super(Types.ARRAY); // 指定数据库类型为 ARRAY 15 | } 16 | 17 | @Override 18 | protected Argument build(String[] value, ConfigRegistry config) { 19 | return (position, statement, ctx) -> { 20 | try { 21 | Array array = statement.getConnection().createArrayOf("VARCHAR", value); 22 | statement.setArray(position, array); 23 | } catch (SQLException e) { 24 | throw new RuntimeException("Error setting PgArray argument", e); 25 | } 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /muyun-database-std/src/main/java/net/ximatai/muyun/database/std/mapper/PgArrayToListMapper.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.std.mapper; 2 | 3 | import org.jdbi.v3.core.mapper.ColumnMapper; 4 | import org.jdbi.v3.core.statement.StatementContext; 5 | 6 | import java.sql.Array; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | public class PgArrayToListMapper implements ColumnMapper> { 13 | 14 | @Override 15 | public List map(ResultSet r, int columnNumber, StatementContext ctx) throws SQLException { 16 | Array pgArray = r.getArray(columnNumber); 17 | 18 | if (pgArray == null) { 19 | return null; 20 | } 21 | 22 | // 处理结果集中的数组 23 | Object array = pgArray.getArray(); 24 | if (array instanceof Object[]) { 25 | return Arrays.asList((Object[]) array); // 转换为 List 26 | } else if (array instanceof int[]) { 27 | return Arrays.asList(Arrays.stream((int[]) array).boxed().toArray()); 28 | } else if (array instanceof long[]) { 29 | return Arrays.asList(Arrays.stream((long[]) array).boxed().toArray()); 30 | } else if (array instanceof double[]) { 31 | return Arrays.asList(Arrays.stream((double[]) array).boxed().toArray()); 32 | } else { 33 | throw new SQLException("Unsupported array type: " + array.getClass().getName()); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /muyun-database-std/src/main/java/net/ximatai/muyun/database/std/mapper/PgArrayToListMapperFactory.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.std.mapper; 2 | 3 | import org.jdbi.v3.core.config.ConfigRegistry; 4 | import org.jdbi.v3.core.mapper.ColumnMapper; 5 | import org.jdbi.v3.core.mapper.ColumnMapperFactory; 6 | 7 | import java.lang.reflect.ParameterizedType; 8 | import java.lang.reflect.Type; 9 | import java.sql.Array; 10 | import java.sql.SQLException; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Optional; 14 | 15 | public class PgArrayToListMapperFactory implements ColumnMapperFactory { 16 | 17 | @Override 18 | public Optional> build(Type type, ConfigRegistry config) { 19 | // 检查类型是否是泛型,并且是否是 List 类型 20 | if (type instanceof ParameterizedType) { 21 | ParameterizedType parameterizedType = (ParameterizedType) type; 22 | if (parameterizedType.getRawType() == List.class) { 23 | return Optional.of((rs, columnNumber, ctx) -> { 24 | Array pgArray = rs.getArray(columnNumber); 25 | 26 | if (pgArray == null) { 27 | return null; 28 | } 29 | 30 | Object array = pgArray.getArray(); 31 | if (array instanceof Object[]) { 32 | return Arrays.asList((Object[]) array); // 转换为 List 33 | } else if (array instanceof int[] || array instanceof double[] || array instanceof float[]) { 34 | // 处理其他原生类型数组 35 | Object[] objectArray = (Object[]) pgArray.getArray(); 36 | return Arrays.asList(objectArray); 37 | } else { 38 | throw new SQLException("Unsupported array type: " + array.getClass().getName()); 39 | } 40 | }); 41 | } 42 | } 43 | return Optional.empty(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /muyun-database-uni/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | checkstyle 5 | `configure-jandex` 6 | } 7 | 8 | dependencies { 9 | api(enforcedPlatform(libs.quarkus.platform.bom)) 10 | api(project(":muyun-database")) 11 | api(project(":muyun-database-std")) 12 | 13 | api("io.quarkus:quarkus-hibernate-reactive-panache") 14 | api("io.quarkus:quarkus-reactive-pg-client") 15 | } 16 | -------------------------------------------------------------------------------- /muyun-database-uni/src/main/java/net/ximatai/muyun/database/uni/IDatabaseAccessUni.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.uni; 2 | 3 | import io.smallrye.mutiny.Uni; 4 | import net.ximatai.muyun.database.IDatabaseOperations; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public interface IDatabaseOperationsUni extends IDatabaseOperations { 10 | 11 | @Override 12 | default Uni insertItem(String schema, String tableName, Map params) { 13 | return (Uni) IDatabaseOperations.super.insertItem(schema, tableName, params); 14 | } 15 | 16 | default Uni updateItem(String schema, String tableName, Map params) { 17 | Uni updated = (Uni) IDatabaseOperations.super.updateItem(schema, tableName, params); 18 | return updated.onItem().transform(rowsUpdated -> rowsUpdated == 1); 19 | } 20 | 21 | Uni insert(String sql, Map params, String pk, Class idType); 22 | 23 | Uni> row(String sql, Map params); 24 | 25 | Uni> row(String sql, List params); 26 | 27 | Uni> row(String sql); 28 | 29 | Uni>> query(String sql, Map params); 30 | 31 | Uni>> query(String sql, List params); 32 | 33 | Uni>> query(String sql); 34 | 35 | Uni update(String sql, Map params); 36 | 37 | Uni delete(String sql, Map params); 38 | 39 | Uni execute(String sql); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /muyun-database-uni/src/main/java/net/ximatai/muyun/database/uni/tool/TupleTool.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.uni.tool; 2 | 3 | import jakarta.persistence.Tuple; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class TupleTool { 9 | public static Map toMap(Tuple tuple) { 10 | Map result = new HashMap<>(); 11 | if (tuple == null) { 12 | return result; 13 | } 14 | tuple.getElements().forEach(tupleElement -> { 15 | String alias = tupleElement.getAlias(); 16 | Object value = tuple.get(alias); 17 | result.put(alias, value); 18 | }); 19 | 20 | return result; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /muyun-database/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(libs.jdbi.core) 8 | // api(libs.jdbi.postgres) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/IDBInfoProvider.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database; 2 | 3 | import net.ximatai.muyun.database.exception.MyDatabaseException; 4 | import net.ximatai.muyun.database.metadata.DBInfo; 5 | import net.ximatai.muyun.database.metadata.DBSchema; 6 | import net.ximatai.muyun.database.metadata.DBTable; 7 | import org.jdbi.v3.core.Jdbi; 8 | 9 | import java.sql.Connection; 10 | import java.sql.DatabaseMetaData; 11 | import java.sql.ResultSet; 12 | 13 | import static net.ximatai.muyun.database.exception.MyDatabaseException.Type.READ_METADATA_ERROR; 14 | 15 | public interface IDBInfoProvider { 16 | 17 | Jdbi getJdbi(); 18 | 19 | void setJdbi(Jdbi jdbi); 20 | 21 | void resetDBInfo(); 22 | 23 | default DBInfo getDBInfo() { 24 | return getJdbi().withHandle(handle -> { 25 | Connection connection = handle.getConnection(); 26 | try { 27 | DatabaseMetaData metaData = connection.getMetaData(); 28 | 29 | DBInfo info = new DBInfo(metaData.getDatabaseProductName()); 30 | 31 | try (ResultSet schemasRs = metaData.getSchemas()) { 32 | while (schemasRs.next()) { 33 | info.addSchema(new DBSchema(schemasRs.getString("TABLE_SCHEM"))); 34 | } 35 | } 36 | 37 | try (ResultSet tablesRs = metaData.getTables(null, null, null, new String[]{"TABLE"})) { 38 | while (tablesRs.next()) { 39 | String tableName = tablesRs.getString("TABLE_NAME"); 40 | String schema = tablesRs.getString("TABLE_SCHEM"); 41 | DBTable table = new DBTable(getJdbi()).setName(tableName).setSchema(schema); 42 | info.getSchema(schema).addTable(table); 43 | } 44 | } 45 | 46 | return info; 47 | } catch (Exception e) { 48 | throw new MyDatabaseException(e.getMessage(), READ_METADATA_ERROR); 49 | } 50 | }); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/IDatabaseOperationsStd.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database; 2 | 3 | import net.ximatai.muyun.database.exception.MyDatabaseException; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public interface IDatabaseOperationsStd extends IDatabaseOperations { 10 | 11 | default String insertItem(String schema, String tableName, Map params) { 12 | return (String) IDatabaseOperations.super.insertItem(schema, tableName, params); 13 | } 14 | 15 | default List insertList(String schema, String tableName, List list) { 16 | return (List) IDatabaseOperations.super.insertList(schema, tableName, list); 17 | } 18 | 19 | default Integer updateItem(String schema, String tableName, Map params) { 20 | Integer num = (Integer) IDatabaseOperations.super.updateItem(schema, tableName, params); 21 | if (num == 0) { 22 | throw new MyDatabaseException(MyDatabaseException.Type.DATA_NOT_FOUND); 23 | } 24 | return num; 25 | } 26 | 27 | default Map getItem(String schema, String tableName, String id) { 28 | return (Map) IDatabaseOperations.super.getItem(schema, tableName, id); 29 | } 30 | 31 | default Integer deleteItem(String schema, String tableName, String id) { 32 | Integer num = (Integer) IDatabaseOperations.super.deleteItem(schema, tableName, id); 33 | if (num == 0) { 34 | throw new MyDatabaseException(MyDatabaseException.Type.DATA_NOT_FOUND); 35 | } 36 | return num; 37 | } 38 | 39 | T insert(String sql, Map params, String pk, Class idType); 40 | 41 | List batchInsert(String sql, List paramsList, String pk, Class idType); 42 | 43 | Map row(String sql, List params); 44 | 45 | default Map row(String sql, Object... params) { 46 | return this.row(sql, Arrays.stream(params).toList()); 47 | } 48 | 49 | Map row(String sql, Map params); 50 | 51 | Map row(String sql); 52 | 53 | List> query(String sql, Map params); 54 | 55 | List> query(String sql, List params); 56 | 57 | default List> query(String sql, Object... params) { 58 | return this.query(sql, Arrays.stream(params).toList()); 59 | } 60 | 61 | List> query(String sql); 62 | 63 | Integer update(String sql, Map params); 64 | 65 | default Integer update(String sql, Object... params) { 66 | return this.update(sql, Arrays.stream(params).toList()); 67 | } 68 | 69 | Integer update(String sql, List params); 70 | 71 | default Integer delete(String sql, Map params) { 72 | return this.update(sql, params); 73 | } 74 | 75 | default Integer delete(String sql, Object... params) { 76 | return this.update(sql, params); 77 | } 78 | 79 | default Integer delete(String sql, List params) { 80 | return this.update(sql, params); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/builder/ColumnType.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.builder; 2 | 3 | public enum ColumnType { 4 | VARCHAR, 5 | INT, 6 | BOOLEAN, 7 | TIMESTAMP, 8 | DATE, 9 | NUMERIC, 10 | JSON, 11 | VARCHAR_ARRAY, 12 | INT_ARRAY; 13 | } 14 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/builder/IColumnTypeTransform.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.builder; 2 | 3 | public interface IColumnTypeTransform { 4 | 5 | IColumnTypeTransform POSTGRES = type -> switch (type) { 6 | case VARCHAR_ARRAY -> "varchar[]"; 7 | case INT_ARRAY -> "int[]"; 8 | case JSON -> "jsonb"; 9 | default -> type.name(); 10 | }; 11 | 12 | String transform(ColumnType type); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/builder/Index.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.builder; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | class Index { 7 | private List columns; 8 | private boolean unique; 9 | 10 | public Index(String columnName, boolean unique) { 11 | this.columns = new ArrayList<>(); 12 | this.columns.add(columnName); 13 | this.unique = unique; 14 | } 15 | 16 | public Index(List columns, boolean unique) { 17 | this.columns = columns; 18 | this.unique = unique; 19 | } 20 | 21 | public boolean isUnique() { 22 | return unique; 23 | } 24 | 25 | public List getColumns() { 26 | return columns; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/builder/TableBase.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.builder; 2 | 3 | public class TableBase { 4 | protected String schema; 5 | protected String name; 6 | 7 | public TableBase() { 8 | } 9 | 10 | public TableBase(String schema, String name) { 11 | this.schema = schema; 12 | this.name = name; 13 | } 14 | 15 | public TableBase setSchema(String schema) { 16 | this.schema = schema; 17 | return this; 18 | } 19 | 20 | public TableBase setName(String name) { 21 | this.name = name; 22 | return this; 23 | } 24 | 25 | public String getSchema() { 26 | return schema; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public String getSchemaDotTable() { 34 | return schema + "." + name; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/exception/MyDatabaseException.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.exception; 2 | 3 | public class MyDatabaseException extends RuntimeException { 4 | 5 | private final Type type; 6 | 7 | public enum Type { 8 | DEFAULT, 9 | DATA_NOT_FOUND, 10 | READ_METADATA_ERROR, 11 | } 12 | 13 | @Override 14 | public String getMessage() { 15 | 16 | switch (type) { 17 | case DATA_NOT_FOUND -> { 18 | return "操作的数据不存在"; 19 | } 20 | case READ_METADATA_ERROR -> { 21 | return "元数据读取失败," + super.getMessage(); 22 | } 23 | } 24 | 25 | return super.getMessage(); 26 | } 27 | 28 | public MyDatabaseException(Type type) { 29 | this.type = type; 30 | } 31 | 32 | public MyDatabaseException(String message) { 33 | super(message); 34 | this.type = Type.DEFAULT; 35 | } 36 | 37 | public MyDatabaseException(String message, Type type) { 38 | super(message); 39 | this.type = type; 40 | } 41 | 42 | public Type getType() { 43 | return type; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/metadata/DBColumn.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.metadata; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class DBColumn { 7 | private String name; 8 | private String description; 9 | private String type; 10 | private String defaultValue; 11 | private boolean nullable; 12 | private boolean primaryKey; 13 | private boolean sequence; 14 | 15 | // 使用正则表达式来匹配单引号之间的内容 16 | private static String regex = "'([^']*)'"; 17 | private static Pattern pattern = Pattern.compile(regex); 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | this.name = name; 25 | } 26 | 27 | public String getDescription() { 28 | return description; 29 | } 30 | 31 | public void setDescription(String description) { 32 | this.description = description; 33 | } 34 | 35 | public String getType() { 36 | return type; 37 | } 38 | 39 | public void setType(String type) { 40 | this.type = type; 41 | } 42 | 43 | public Object getDefaultValue() { 44 | return extractDefaultContent(defaultValue); 45 | } 46 | 47 | public void setDefaultValue(String defaultValue) { 48 | this.defaultValue = defaultValue; 49 | } 50 | 51 | public boolean isNullable() { 52 | return nullable; 53 | } 54 | 55 | public void setNullable(boolean nullable) { 56 | this.nullable = nullable; 57 | } 58 | 59 | public boolean isPrimaryKey() { 60 | return primaryKey; 61 | } 62 | 63 | public void setPrimaryKey(boolean primaryKey) { 64 | this.primaryKey = primaryKey; 65 | } 66 | 67 | public boolean isSequence() { 68 | return sequence; 69 | } 70 | 71 | public void setSequence() { 72 | this.sequence = true; 73 | } 74 | 75 | public void setNullable() { 76 | this.nullable = true; 77 | } 78 | 79 | public void setPrimaryKey() { 80 | this.primaryKey = true; 81 | } 82 | 83 | public String getLabel() { 84 | if (getDescription() != null) { 85 | return getDescription(); 86 | } 87 | return getName(); 88 | } 89 | 90 | public Object extractDefaultContent(String input) { 91 | if (input == null) { 92 | return null; 93 | } 94 | 95 | if (this.getType().equalsIgnoreCase("bool")) { 96 | return Boolean.parseBoolean(input); 97 | } else if (this.getType().startsWith("int")) { 98 | return Integer.parseInt(input); 99 | } 100 | 101 | Matcher matcher = pattern.matcher(input); 102 | // 查找并返回匹配的内容 103 | if (matcher.find()) { 104 | return matcher.group(1); // 返回第一个括号中的匹配结果 105 | } 106 | return input; // 如果没有匹配的内容,返回 null 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/metadata/DBIndex.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.metadata; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class DBIndex { 7 | private String name; 8 | private boolean unique = false; 9 | private final List columns = new ArrayList<>(); 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | public DBIndex setName(String name) { 16 | this.name = name; 17 | return this; 18 | } 19 | 20 | public boolean isUnique() { 21 | return unique; 22 | } 23 | 24 | public DBIndex setUnique(boolean unique) { 25 | this.unique = unique; 26 | return this; 27 | } 28 | 29 | public List getColumns() { 30 | return columns; 31 | } 32 | 33 | public DBIndex addColumn(String columns) { 34 | this.columns.add(columns); 35 | return this; 36 | } 37 | 38 | public boolean isMulti() { 39 | return columns.size() > 1; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/metadata/DBInfo.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.metadata; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class DBInfo { 7 | private String name; 8 | 9 | private Set schemas = new HashSet<>(); 10 | 11 | public DBInfo(String name) { 12 | this.name = name; 13 | } 14 | 15 | public DBInfo addSchema(DBSchema schema) { 16 | this.schemas.add(schema); 17 | return this; 18 | } 19 | 20 | public DBSchema getSchema(String schemaName) { 21 | return schemas.stream() 22 | .filter(schema -> schemaName.equals(schema.getName())) 23 | .findFirst() 24 | .orElse(null); 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/metadata/DBSchema.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.metadata; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class DBSchema { 7 | 8 | private String name; 9 | private Map tables = new HashMap<>(); 10 | 11 | public DBSchema(String name) { 12 | this.name = name; 13 | } 14 | 15 | public void addTable(DBTable table) { 16 | this.tables.put(table.getName(), table); 17 | } 18 | 19 | public DBSchema setTables(Map tables) { 20 | this.tables = tables; 21 | return this; 22 | } 23 | 24 | public DBTable getTable(String name) { 25 | return this.tables.get(name); 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | return name.hashCode(); 35 | } 36 | 37 | @Override 38 | public boolean equals(Object obj) { 39 | return obj instanceof DBSchema && name.equals(((DBSchema) obj).name); 40 | } 41 | 42 | public Map getTables() { 43 | return tables; 44 | } 45 | 46 | public boolean containsTable(String tableName) { 47 | return getTables().containsKey(tableName); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /muyun-database/src/main/java/net/ximatai/muyun/database/tool/DateTool.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.database.tool; 2 | 3 | import java.sql.Date; 4 | import java.sql.Timestamp; 5 | import java.time.LocalDate; 6 | import java.time.LocalDateTime; 7 | import java.time.format.DateTimeFormatter; 8 | import java.time.format.DateTimeParseException; 9 | 10 | public class DateTool { 11 | private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 12 | private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 13 | 14 | public static Date stringToSqlDate(String dateString) { 15 | if (dateString == null || dateString.isEmpty()) { 16 | throw new IllegalArgumentException("Date string cannot be null or empty."); 17 | } 18 | 19 | try { 20 | LocalDate localDate = LocalDate.parse(dateString.substring(0, 10), DATE_FORMATTER); 21 | return Date.valueOf(localDate); 22 | } catch (DateTimeParseException e) { 23 | throw new IllegalArgumentException("Invalid date format: " + dateString); 24 | } 25 | } 26 | 27 | public static Timestamp stringToSqlTimestamp(String dateString) { 28 | if (dateString == null || dateString.isEmpty()) { 29 | return null; 30 | } 31 | 32 | try { 33 | if (dateString.length() == 10) { 34 | dateString += " 00:00:00"; 35 | } 36 | LocalDateTime localDateTime = LocalDateTime.parse(dateString, DATE_TIME_FORMATTER); 37 | return Timestamp.valueOf(localDateTime); 38 | } catch (DateTimeParseException e) { 39 | throw new IllegalArgumentException("Invalid datetime format: " + dateString); 40 | } 41 | } 42 | 43 | public static Timestamp handleDateTimestamp(Object value) { 44 | if (value instanceof Timestamp) { 45 | return (Timestamp) value; 46 | } else if ("".equals(value)) { 47 | return null; 48 | } else if (value instanceof LocalDateTime localDateTime) { 49 | return Timestamp.valueOf(localDateTime); 50 | } else if (value instanceof Date date) { 51 | return new Timestamp(date.getTime()); 52 | } else if (value instanceof String str) { 53 | return stringToSqlTimestamp(str); 54 | } else { 55 | throw new IllegalArgumentException("Unsupported type: " + value.getClass().getName()); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /muyun-fileserver/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(enforcedPlatform(libs.quarkus.platform.bom)) 8 | api("io.quarkus:quarkus-rest") 9 | api("io.quarkus:quarkus-arc") 10 | api("io.quarkus:quarkus-vertx") 11 | api("io.quarkus:quarkus-reactive-routes") 12 | } 13 | -------------------------------------------------------------------------------- /muyun-fileserver/src/main/java/net/ximatai/muyun/fileserver/FileInfoEntity.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.fileserver; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | 5 | public class FileInfoEntity { 6 | String name; 7 | long size; 8 | String suffix; 9 | String id; 10 | String time; 11 | 12 | public FileInfoEntity() { 13 | } 14 | 15 | public FileInfoEntity(String name, long size, String suffix, String id, String time) { 16 | this.name = name; 17 | this.size = size; 18 | this.suffix = suffix; 19 | this.id = id; 20 | this.time = time; 21 | } 22 | 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public void setName(String name) { 28 | this.name = name; 29 | } 30 | 31 | public long getSize() { 32 | return size; 33 | } 34 | 35 | public void setSize(long size) { 36 | this.size = size; 37 | } 38 | 39 | public String getSuffix() { 40 | return suffix; 41 | } 42 | 43 | public void setSuffix(String suffix) { 44 | this.suffix = suffix; 45 | } 46 | 47 | public String getId() { 48 | return id; 49 | } 50 | 51 | public void setId(String id) { 52 | this.id = id; 53 | } 54 | 55 | public String getTime() { 56 | return time; 57 | } 58 | 59 | public void setTime(String time) { 60 | this.time = time; 61 | } 62 | 63 | public JsonObject toJson() { 64 | JsonObject jsonObject = new JsonObject(); 65 | jsonObject.put("name", name) 66 | .put("size", size) 67 | .put("suffix", suffix) 68 | .put("id", id) 69 | .put("time", time); 70 | return jsonObject; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /muyun-fileserver/src/main/java/net/ximatai/muyun/fileserver/FileServerConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.fileserver; 2 | 3 | import io.smallrye.config.ConfigMapping; 4 | import io.smallrye.config.WithDefault; 5 | 6 | @ConfigMapping(prefix = "file-server") 7 | public interface FileServerConfig { 8 | String uploadPath(); 9 | 10 | @WithDefault("fileServer") 11 | String pagePath(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /muyun-fileserver/src/main/java/net/ximatai/muyun/fileserver/FileServerRegister.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.fileserver; 2 | 3 | import io.quarkus.runtime.Startup; 4 | import io.quarkus.runtime.configuration.MemorySizeConverter; 5 | import io.quarkus.vertx.web.RouteFilter; 6 | import io.vertx.ext.web.RoutingContext; 7 | import io.vertx.ext.web.handler.BodyHandler; 8 | import jakarta.enterprise.context.ApplicationScoped; 9 | import jakarta.inject.Inject; 10 | import org.eclipse.microprofile.config.inject.ConfigProperty; 11 | 12 | @Startup 13 | @ApplicationScoped 14 | public class FileServerRegister { 15 | 16 | @Inject 17 | FileServerConfig config; 18 | 19 | @ConfigProperty(name = "quarkus.http.limits.max-body-size") 20 | String maxBodySize; 21 | 22 | MemorySizeConverter converter = new MemorySizeConverter(); 23 | 24 | private String getUploadPath() { 25 | String uploadPath = config.uploadPath(); 26 | if (!uploadPath.endsWith("/") && !uploadPath.endsWith("\\")) { 27 | uploadPath = uploadPath + "/"; 28 | } 29 | return uploadPath; 30 | } 31 | 32 | @RouteFilter(10) 33 | // 该注解中有route 34 | void filter(RoutingContext context) { 35 | BodyHandler.create() 36 | .setUploadsDirectory(getUploadPath()) 37 | .setBodyLimit(converter.convert(maxBodySize).asLongValue()) 38 | .handle(context); 39 | // BodyHandler是一个类,对象handler可以作为route的参数 40 | // create()函数返回一个BodyHandlerImpl 41 | // BodyHandlerImpl中有handle方法 42 | // handle方法接收context来处理 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /muyun-fileserver/src/main/java/net/ximatai/muyun/fileserver/IFileService.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.fileserver; 2 | 3 | import net.ximatai.muyun.fileserver.exception.FileException; 4 | 5 | import java.io.File; 6 | 7 | public interface IFileService { 8 | String save(File file); 9 | 10 | String save(File file, String assignName); 11 | 12 | File get(String idOrName) throws FileException; 13 | 14 | boolean delete(String id); 15 | 16 | FileInfoEntity info(String id) throws FileException; 17 | 18 | // uid文件名处理方法 19 | default String suffixFileNameWithN(String fileName) { 20 | return fileName + "-n"; 21 | } 22 | 23 | default String suffixFileNameWithO(String fileName) { 24 | return fileName + "-o"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /muyun-fileserver/src/main/java/net/ximatai/muyun/fileserver/exception/FileException.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.fileserver.exception; 2 | 3 | public class FileException extends Exception { 4 | 5 | public FileException(String message) { 6 | super(message); 7 | } 8 | 9 | public FileException(Exception e) { 10 | super(e); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /muyun-log/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(project(":muyun-core")) 8 | api(project(":muyun-database-std")) 9 | } 10 | -------------------------------------------------------------------------------- /muyun-log/src/main/java/net/ximatai/muyun/log/LogAccessController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.log; 2 | 3 | import jakarta.ws.rs.Path; 4 | import net.ximatai.muyun.service.ILogAccess; 5 | 6 | @Path("/log/access") 7 | public class LogAccessController extends LogBaseController implements ILogAccess { 8 | 9 | @Override 10 | public String getMainTable() { 11 | return "log_access"; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /muyun-log/src/main/java/net/ximatai/muyun/log/LogBaseController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.log; 2 | 3 | import jakarta.annotation.PostConstruct; 4 | import jakarta.inject.Inject; 5 | import net.ximatai.muyun.ability.IMetadataAbility; 6 | import net.ximatai.muyun.ability.curd.std.IQueryAbility; 7 | import net.ximatai.muyun.ability.curd.std.ISelectAbility; 8 | import net.ximatai.muyun.database.IDatabaseOperations; 9 | import net.ximatai.muyun.database.builder.Column; 10 | import net.ximatai.muyun.database.builder.TableBuilder; 11 | import net.ximatai.muyun.database.builder.TableWrapper; 12 | import net.ximatai.muyun.model.QueryItem; 13 | import net.ximatai.muyun.model.log.LogItem; 14 | 15 | import java.util.List; 16 | 17 | public abstract class LogBaseController implements IMetadataAbility, ISelectAbility, IQueryAbility { 18 | 19 | @Inject 20 | IDatabaseOperations databaseOperations; 21 | 22 | @Override 23 | public String getSchemaName() { 24 | return "log"; 25 | } 26 | 27 | @PostConstruct 28 | protected void init() { 29 | 30 | TableWrapper wrapper = new TableWrapper(getMainTable()) 31 | .setSchema(getSchemaName()) 32 | .setPrimaryKey(Column.ID_POSTGRES) 33 | .addColumn("v_uri") 34 | .addColumn("v_method") 35 | .addColumn("j_params") 36 | .addColumn("v_host") 37 | .addColumn("v_useragent") 38 | .addColumn("v_os") 39 | .addColumn("v_browser") 40 | .addColumn("id_at_auth_user") 41 | .addColumn("v_username") 42 | .addColumn("v_module") 43 | .addColumn("v_action") 44 | .addColumn("v_data_id") 45 | .addColumn("i_cost") 46 | .addColumn("b_success") 47 | .addColumn("i_status_code") 48 | .addColumn("v_error") 49 | .addColumn(Column.of("t_create").setDefaultValue("now()")); 50 | 51 | new TableBuilder(databaseOperations).build(wrapper); 52 | } 53 | 54 | public void log(LogItem logItem) { 55 | 56 | databaseOperations.insertItem(getSchemaName(), getMainTable(), logItem.toMap()); 57 | } 58 | 59 | @Override 60 | public List queryItemList() { 61 | return List.of( 62 | QueryItem.of("v_username"), 63 | QueryItem.of("t_create").setSymbolType(QueryItem.SymbolType.RANGE) 64 | ); 65 | } 66 | 67 | @Override 68 | public IDatabaseOperations getDatabaseOperations() { 69 | return databaseOperations; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /muyun-log/src/main/java/net/ximatai/muyun/log/LogErrorController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.log; 2 | 3 | import jakarta.ws.rs.Path; 4 | import net.ximatai.muyun.service.ILogError; 5 | 6 | @Path("/log/error") 7 | public class LogErrorController extends LogBaseController implements ILogError { 8 | 9 | @Override 10 | public String getMainTable() { 11 | return "log_error"; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /muyun-log/src/main/java/net/ximatai/muyun/log/LogLoginController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.log; 2 | 3 | import jakarta.ws.rs.Path; 4 | import net.ximatai.muyun.service.ILogLogin; 5 | 6 | @Path("/log/login") 7 | public class LogLoginController extends LogBaseController implements ILogLogin { 8 | 9 | @Override 10 | public String getMainTable() { 11 | return "log_login"; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /muyun-platform/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(project(":muyun-core")) 8 | api(project(":muyun-database-std")) 9 | api(project(":muyun-authorization")) 10 | 11 | api(libs.easyCaptcha) 12 | // api(project(":muyun-database-uni")) 13 | } 14 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/PlatformConst.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform; 2 | 3 | public class PlatformConst { 4 | 5 | public static final String BASE_PATH = "/platform"; 6 | public static final String DB_SCHEMA = "platform"; 7 | } 8 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/ScaffoldForPlatform.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform; 2 | 3 | import com.github.benmanes.caffeine.cache.Caffeine; 4 | import com.github.benmanes.caffeine.cache.LoadingCache; 5 | import net.ximatai.muyun.base.BaseScaffold; 6 | import net.ximatai.muyun.platform.ability.IModuleRegisterAbility; 7 | 8 | import java.util.Map; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import static net.ximatai.muyun.platform.PlatformConst.DB_SCHEMA; 12 | 13 | public abstract class ScaffoldForPlatform extends BaseScaffold { 14 | 15 | private final LoadingCache> myCache = Caffeine.newBuilder() 16 | .expireAfterWrite(1, TimeUnit.MINUTES) 17 | .build(this::view); 18 | 19 | @Override 20 | public String getSchemaName() { 21 | return DB_SCHEMA; 22 | } 23 | 24 | @Override 25 | protected void afterInit() { 26 | super.afterInit(); 27 | if (this instanceof IModuleRegisterAbility ability) { 28 | ability.registerModule(); 29 | } 30 | } 31 | 32 | public String idToName(String id) { 33 | if (id == null) { 34 | return null; 35 | } 36 | 37 | Map user = myCache.get(id); 38 | if (user != null) { 39 | return (String) user.get("v_name"); 40 | } 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/ability/IModuleRegisterAbility.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.ability; 2 | 3 | import net.ximatai.muyun.ability.IFileAbility; 4 | import net.ximatai.muyun.ability.ISortAbility; 5 | import net.ximatai.muyun.ability.curd.std.ICreateAbility; 6 | import net.ximatai.muyun.ability.curd.std.IDeleteAbility; 7 | import net.ximatai.muyun.ability.curd.std.ISelectAbility; 8 | import net.ximatai.muyun.ability.curd.std.IUpdateAbility; 9 | import net.ximatai.muyun.platform.controller.ModuleController; 10 | import net.ximatai.muyun.platform.model.ModuleAction; 11 | import net.ximatai.muyun.platform.model.ModuleConfig; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public interface IModuleRegisterAbility { 17 | 18 | ModuleController getModuleController(); 19 | 20 | ModuleConfig getModuleConfig(); 21 | 22 | default List defaultActions() { 23 | List actions = new ArrayList<>(); 24 | actions.add(ModuleAction.MENU); 25 | if (this instanceof ICreateAbility) { 26 | actions.add(ModuleAction.CREATE); 27 | } 28 | if (this instanceof ISelectAbility) { 29 | actions.add(ModuleAction.VIEW); 30 | } 31 | if (this instanceof IUpdateAbility) { 32 | actions.add(ModuleAction.UPDATE); 33 | } 34 | if (this instanceof IDeleteAbility) { 35 | actions.add(ModuleAction.DELETE); 36 | } 37 | if (this instanceof ISortAbility) { 38 | actions.add(ModuleAction.SORT); 39 | } 40 | if (this instanceof IFileAbility) { 41 | actions.add(new ModuleAction("download", "下载", ModuleAction.TypeLike.VIEW)); 42 | } 43 | return actions; 44 | } 45 | 46 | default void registerModule() { 47 | ModuleConfig config = getModuleConfig(); 48 | ModuleController moduleController = getModuleController(); 49 | if (config != null && moduleController != null) { 50 | config.addActions(defaultActions()); 51 | moduleController.register(config); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/AppConfController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import jakarta.inject.Inject; 4 | import jakarta.ws.rs.GET; 5 | import jakarta.ws.rs.POST; 6 | import jakarta.ws.rs.Path; 7 | import net.ximatai.muyun.ability.ITableCreateAbility; 8 | import net.ximatai.muyun.ability.curd.std.ICreateAbility; 9 | import net.ximatai.muyun.ability.curd.std.ISelectAbility; 10 | import net.ximatai.muyun.ability.curd.std.IUpdateAbility; 11 | import net.ximatai.muyun.core.Scaffold; 12 | import net.ximatai.muyun.database.builder.Column; 13 | import net.ximatai.muyun.database.builder.TableWrapper; 14 | import net.ximatai.muyun.platform.ability.IModuleRegisterAbility; 15 | import net.ximatai.muyun.platform.model.ModuleAction; 16 | import net.ximatai.muyun.platform.model.ModuleConfig; 17 | import org.eclipse.microprofile.openapi.annotations.tags.Tag; 18 | 19 | import java.util.Map; 20 | 21 | import static net.ximatai.muyun.platform.PlatformConst.BASE_PATH; 22 | import static net.ximatai.muyun.platform.controller.AppConfController.MODULE_ALIAS; 23 | 24 | @Tag(description = "全局程序配置(面向前端平台级业务)") 25 | @Path(BASE_PATH + "/" + MODULE_ALIAS) 26 | public class AppConfController extends Scaffold implements ICreateAbility, ISelectAbility, IUpdateAbility, ITableCreateAbility, IModuleRegisterAbility { 27 | public final static String MODULE_ALIAS = "conf"; 28 | 29 | private final static String CONF_ID = "1"; 30 | 31 | @Inject 32 | ModuleController moduleController; 33 | 34 | @Override 35 | public String getMainTable() { 36 | return "app_conf"; 37 | } 38 | 39 | @Override 40 | public void fitOut(TableWrapper wrapper) { 41 | wrapper 42 | .setPrimaryKey(Column.ID_POSTGRES) 43 | .addColumn("j_conf"); 44 | } 45 | 46 | @Override 47 | protected void afterInit() { 48 | super.afterInit(); 49 | this.registerModule(); 50 | 51 | Map view = this.view(CONF_ID); 52 | if (view == null) { 53 | this.create(Map.of( 54 | "id", CONF_ID, 55 | "j_conf", Map.of() 56 | )); 57 | } 58 | } 59 | 60 | @GET 61 | @Path("/get") 62 | public Map getConf() { 63 | Map conf = (Map) this.view(CONF_ID).get("j_conf"); 64 | if (conf == null) { 65 | return Map.of(); 66 | } 67 | return conf; 68 | } 69 | 70 | @POST 71 | @Path("/set") 72 | public Integer setConf(Map conf) { 73 | return this.update(CONF_ID, Map.of( 74 | "j_conf", conf 75 | )); 76 | } 77 | 78 | @Override 79 | public ModuleController getModuleController() { 80 | return moduleController; 81 | } 82 | 83 | @Override 84 | public ModuleConfig getModuleConfig() { 85 | return ModuleConfig.ofName("平台设置") 86 | .setAlias(MODULE_ALIAS) 87 | .setTable(getMainTable()) 88 | .setUrl("platform/conf/index") 89 | .addAction(new ModuleAction("set", "写入配置", ModuleAction.TypeLike.UPDATE)); 90 | // .addAction(new ModuleAction("get", "获取配置", ModuleAction.TypeLike.UPDATE)); 不参与权限,所以注释掉 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/InboxController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import jakarta.ws.rs.Path; 4 | import net.ximatai.muyun.core.exception.MuYunException; 5 | import org.eclipse.microprofile.openapi.annotations.tags.Tag; 6 | 7 | import java.util.Map; 8 | 9 | import static net.ximatai.muyun.platform.PlatformConst.BASE_PATH; 10 | 11 | @Tag(description = "站内信收件箱") 12 | @Path(BASE_PATH + "/inbox") 13 | public class InboxController extends MessageController { 14 | @Override 15 | public String getAuthCondition() { 16 | return """ 17 | and id in ( 18 | select id_at_app_message from %s.app_message_person where b_delete = false and id_at_auth_user__to = '%s' 19 | ) 20 | """.formatted(getSchemaName(), getUser().getId()); 21 | } 22 | 23 | @Override 24 | public Map view(String id) { 25 | Map view = super.view(id); 26 | getDB().update("update %s.app_message_person set b_read = true,t_read = now() where id_at_app_message = ? and id_at_auth_user__to = ?" 27 | .formatted(getSchemaName()), id, getUser().getId()); 28 | return view; 29 | } 30 | 31 | @Override 32 | public Integer delete(String id) { 33 | return getDB().update("update %s.app_message_person set b_delete = true where id_at_app_message = ? and id_at_auth_user__to = ?" 34 | .formatted(getSchemaName()), id, getUser().getId()); 35 | } 36 | 37 | @Override 38 | public Integer update(String id, Map body) { 39 | throw new MuYunException("收件箱内容不可修改"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/MenuController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import jakarta.inject.Inject; 4 | import jakarta.ws.rs.Path; 5 | import net.ximatai.muyun.ability.IChildAbility; 6 | import net.ximatai.muyun.ability.IReferenceAbility; 7 | import net.ximatai.muyun.ability.ITreeAbility; 8 | import net.ximatai.muyun.base.BaseBusinessTable; 9 | import net.ximatai.muyun.database.builder.Column; 10 | import net.ximatai.muyun.database.builder.TableWrapper; 11 | import net.ximatai.muyun.model.ReferenceInfo; 12 | import net.ximatai.muyun.platform.ScaffoldForPlatform; 13 | import net.ximatai.muyun.platform.model.Dict; 14 | import net.ximatai.muyun.platform.model.DictCategory; 15 | import org.eclipse.microprofile.openapi.annotations.tags.Tag; 16 | 17 | import java.util.List; 18 | 19 | import static net.ximatai.muyun.platform.PlatformConst.BASE_PATH; 20 | 21 | @Tag(description = "菜单管理") 22 | @Path(BASE_PATH + "/menu") 23 | public class MenuController extends ScaffoldForPlatform implements ITreeAbility, IChildAbility, IReferenceAbility { 24 | 25 | @Inject 26 | ModuleController moduleController; 27 | 28 | @Inject 29 | DictCategoryController dictCategoryController; 30 | 31 | @Override 32 | public String getMainTable() { 33 | return "app_menu"; 34 | } 35 | 36 | @Override 37 | protected void afterInit() { 38 | super.afterInit(); 39 | dictCategoryController.putDictCategory( 40 | new DictCategory("menu_opentype", "platform_dir", "菜单打开方式", 1).setDictList( 41 | new Dict("tab", "内嵌TAB"), 42 | new Dict("window", "新窗口") 43 | ), false); 44 | } 45 | 46 | @Override 47 | public void fitOut(TableWrapper wrapper) { 48 | wrapper 49 | .setPrimaryKey(Column.ID_POSTGRES) 50 | .setInherit(BaseBusinessTable.TABLE) 51 | .addColumn("v_name") 52 | .addColumn("v_icon") 53 | .addColumn("v_remark") 54 | .addColumn("dict_menu_opentype") 55 | .addColumn("id_at_app_menu_schema") 56 | .addColumn("id_at_app_module") 57 | .addColumn(Column.of("b_enable").setDefaultValue(true)) 58 | .addColumn(Column.of("b_homepage").setDefaultValue(false)) 59 | .addIndex("id_at_app_menu_schema"); 60 | 61 | } 62 | 63 | @Override 64 | public List getReferenceList() { 65 | return List.of( 66 | moduleController.toReferenceInfo("id_at_app_module") 67 | .add("v_alias", "v_alias") 68 | .add("v_url", "v_url") 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/ModuleActionController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import jakarta.enterprise.context.ApplicationScoped; 4 | import jakarta.inject.Inject; 5 | import net.ximatai.muyun.ability.IChildAbility; 6 | import net.ximatai.muyun.ability.IChildrenAbility; 7 | import net.ximatai.muyun.database.builder.Column; 8 | import net.ximatai.muyun.database.builder.TableWrapper; 9 | import net.ximatai.muyun.model.ChildTableInfo; 10 | import net.ximatai.muyun.model.SortColumn; 11 | import net.ximatai.muyun.platform.ScaffoldForPlatform; 12 | 13 | import java.util.List; 14 | 15 | @ApplicationScoped 16 | public class ModuleActionController extends ScaffoldForPlatform implements IChildAbility, IChildrenAbility { 17 | 18 | @Inject 19 | RoleActionController roleActionController; 20 | 21 | @Override 22 | public SortColumn getDefatultSortColumn() { 23 | return new SortColumn("i_order"); 24 | } 25 | 26 | @Override 27 | public String getMainTable() { 28 | return "app_module_action"; 29 | } 30 | 31 | @Override 32 | public void fitOut(TableWrapper wrapper) { 33 | wrapper 34 | .setPrimaryKey(Column.ID_POSTGRES) 35 | .addColumn("id_at_app_module", "模块id") 36 | .addColumn("v_name", "功能名称") 37 | .addColumn("v_alias", "功能标识") 38 | .addColumn("v_remark", "备注") 39 | .addColumn("b_white", "是否白名单", false) 40 | .addColumn("i_order", "序号") 41 | .addIndex(List.of("id_at_app_module", "v_alias"), true); 42 | } 43 | 44 | @Override 45 | public List getChildren() { 46 | return List.of( 47 | roleActionController.toChildTable("id_at_app_module_action").setAutoDelete() 48 | ); 49 | } 50 | 51 | @Override 52 | public void afterUpdate(String id) { 53 | getDB().update(""" 54 | UPDATE platform.auth_role_action 55 | SET v_alias_at_app_module_action = ( 56 | SELECT app_module_action.v_alias 57 | FROM platform.app_module_action 58 | WHERE platform.app_module_action.id = platform.auth_role_action.id_at_app_module_action 59 | ) 60 | WHERE platform.auth_role_action.id_at_app_module_action = ? 61 | """, id); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/NoticeReadController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import io.quarkus.runtime.Startup; 4 | import jakarta.enterprise.context.ApplicationScoped; 5 | import net.ximatai.muyun.ability.IChildAbility; 6 | import net.ximatai.muyun.ability.curd.std.IQueryAbility; 7 | import net.ximatai.muyun.database.builder.Column; 8 | import net.ximatai.muyun.database.builder.TableWrapper; 9 | import net.ximatai.muyun.model.QueryItem; 10 | import net.ximatai.muyun.platform.ScaffoldForPlatform; 11 | 12 | import java.util.List; 13 | 14 | @Startup 15 | @ApplicationScoped 16 | public class NoticeReadController extends ScaffoldForPlatform implements IChildAbility, IQueryAbility { 17 | @Override 18 | public String getMainTable() { 19 | return "app_notice_read"; 20 | } 21 | 22 | @Override 23 | public void fitOut(TableWrapper wrapper) { 24 | wrapper 25 | .setComment("通知公告已读记录") 26 | .setPrimaryKey(Column.ID_POSTGRES) 27 | .addColumn("id_at_app_notice") 28 | .addColumn("id_at_auth_user") 29 | .addColumn("t_create") 30 | .addIndex("t_create") 31 | .addIndex(List.of("id_at_app_notice", "id_at_auth_user"), true); 32 | } 33 | 34 | @Override 35 | public List queryItemList() { 36 | return List.of( 37 | QueryItem.of("id_at_app_notice"), 38 | QueryItem.of("id_at_auth_user") 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/OutboxController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import jakarta.ws.rs.Path; 4 | import net.ximatai.muyun.core.exception.MuYunException; 5 | import net.ximatai.muyun.model.PageResult; 6 | import org.eclipse.microprofile.openapi.annotations.tags.Tag; 7 | 8 | import java.util.Map; 9 | import java.util.Optional; 10 | 11 | import static net.ximatai.muyun.platform.PlatformConst.BASE_PATH; 12 | 13 | @Tag(description = "站内信发件箱") 14 | @Path(BASE_PATH + "/outbox") 15 | public class OutboxController extends MessageController { 16 | @Override 17 | public String getAuthCondition() { //发件箱查看只能看自己发的信件(不含回帖) 18 | return "and id_at_app_message__root isnull and id_at_auth_user__create = '%s'".formatted(getUser().getId()); 19 | } 20 | 21 | @Override 22 | public void beforeUpdate(String id, Optional body) { 23 | PageResult query = this.query(Map.of( 24 | "id_at_app_message__root", id 25 | )); 26 | if (query.getTotal() > 0) { 27 | throw new MuYunException("该信件已产生了回复,不允许修改或删除"); 28 | } 29 | } 30 | 31 | @Override 32 | public void beforeDelete(String id) { 33 | this.beforeUpdate(id, Optional.empty()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/SupervisionRegionController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import io.quarkus.runtime.Startup; 4 | import jakarta.enterprise.context.ApplicationScoped; 5 | import net.ximatai.muyun.ability.IChildAbility; 6 | import net.ximatai.muyun.base.BaseBusinessTable; 7 | import net.ximatai.muyun.database.builder.Column; 8 | import net.ximatai.muyun.database.builder.TableWrapper; 9 | import net.ximatai.muyun.platform.ScaffoldForPlatform; 10 | 11 | @Startup 12 | @ApplicationScoped 13 | public class SupervisionRegionController extends ScaffoldForPlatform implements IChildAbility { 14 | 15 | @Override 16 | public String getMainTable() { 17 | return "org_organization_supervision"; 18 | } 19 | 20 | @Override 21 | public void fitOut(TableWrapper wrapper) { 22 | wrapper 23 | .setPrimaryKey(Column.ID_POSTGRES) 24 | .setInherit(BaseBusinessTable.TABLE) 25 | .addColumn("id_at_org_organization") 26 | .addColumn("id_at_app_region"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import jakarta.enterprise.context.ApplicationScoped; 4 | import jakarta.inject.Inject; 5 | import net.ximatai.muyun.ability.IChildrenAbility; 6 | import net.ximatai.muyun.ability.IReferableAbility; 7 | import net.ximatai.muyun.ability.ISecurityAbility; 8 | import net.ximatai.muyun.ability.curd.std.IQueryAbility; 9 | import net.ximatai.muyun.core.security.AbstractEncryptor; 10 | import net.ximatai.muyun.core.security.SMEncryptor; 11 | import net.ximatai.muyun.database.builder.Column; 12 | import net.ximatai.muyun.database.builder.TableWrapper; 13 | import net.ximatai.muyun.model.ChildTableInfo; 14 | import net.ximatai.muyun.model.QueryItem; 15 | import net.ximatai.muyun.platform.ScaffoldForPlatform; 16 | 17 | import java.util.List; 18 | 19 | @ApplicationScoped 20 | public class UserController extends ScaffoldForPlatform implements IQueryAbility, ISecurityAbility, IReferableAbility, IChildrenAbility { 21 | 22 | @Override 23 | public String getMainTable() { 24 | return "auth_user"; 25 | } 26 | 27 | @Inject 28 | SMEncryptor smEncryptor; 29 | 30 | @Inject 31 | UserRoleController userRoleController; 32 | 33 | @Override 34 | public void fitOut(TableWrapper wrapper) { 35 | wrapper 36 | .setPrimaryKey(Column.ID_POSTGRES) 37 | .addColumn("v_username") 38 | .addColumn("v_password") 39 | .addColumn("t_create") 40 | .addColumn("t_update") 41 | .addColumn("t_last_login") 42 | .addColumn("t_this_login") 43 | .addColumn(Column.of("b_enabled").setDefaultValue(true)) 44 | .addIndex("v_username", true); 45 | } 46 | 47 | @Override 48 | public List queryItemList() { 49 | return List.of( 50 | QueryItem.of("v_username"), 51 | QueryItem.of("b_enabled") 52 | ); 53 | } 54 | 55 | /** 56 | * 登录成功标记 t_this_login \ t_last_login 57 | */ 58 | public void checkIn(String id) { 59 | getDB().update(""" 60 | update platform.auth_user set t_last_login = t_this_login,t_this_login = now() where id = ? 61 | """, id); 62 | } 63 | 64 | @Override 65 | public List getColumnsForSigning() { 66 | return List.of(); 67 | } 68 | 69 | @Override 70 | public List getColumnsForEncryption() { 71 | return List.of("v_password"); 72 | } 73 | 74 | @Override 75 | public AbstractEncryptor getAEncryptor() { 76 | return smEncryptor; 77 | } 78 | 79 | @Override 80 | public String getLabelColumn() { 81 | return "v_username"; 82 | } 83 | 84 | @Override 85 | public List getChildren() { 86 | return List.of( 87 | userRoleController.toChildTable("id_at_auth_user").setAutoDelete() 88 | ); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/controller/UserRoleController.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.controller; 2 | 3 | import io.quarkus.runtime.Startup; 4 | import jakarta.enterprise.context.ApplicationScoped; 5 | import net.ximatai.muyun.ability.IChildAbility; 6 | import net.ximatai.muyun.ability.curd.std.IQueryAbility; 7 | import net.ximatai.muyun.database.builder.Column; 8 | import net.ximatai.muyun.database.builder.TableWrapper; 9 | import net.ximatai.muyun.model.QueryItem; 10 | import net.ximatai.muyun.platform.ScaffoldForPlatform; 11 | 12 | import java.util.List; 13 | 14 | @Startup 15 | @ApplicationScoped 16 | public class UserRoleController extends ScaffoldForPlatform implements IChildAbility, IQueryAbility { 17 | 18 | @Override 19 | public String getMainTable() { 20 | return "auth_user_role"; 21 | } 22 | 23 | @Override 24 | public void fitOut(TableWrapper wrapper) { 25 | wrapper 26 | .setPrimaryKey(Column.ID_POSTGRES) 27 | .addColumn(Column.of("id_at_auth_user").setNullable(false).setComment("用户id")) 28 | .addColumn(Column.of("id_at_auth_role").setNullable(false).setComment("角色id")) 29 | .addIndex(List.of("id_at_auth_user", "id_at_auth_role"), true); 30 | } 31 | 32 | @Override 33 | public List queryItemList() { 34 | return List.of( 35 | QueryItem.of("id_at_auth_user"), 36 | QueryItem.of("id_at_auth_role") 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/Dict.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class Dict { 7 | private String categoryID; 8 | private String value; 9 | private String name; 10 | private int order; 11 | 12 | private String remark; 13 | 14 | public Dict(String value, String name) { 15 | this.value = value; 16 | this.name = name; 17 | } 18 | 19 | public Dict(String value, String name, String remark) { 20 | this.value = value; 21 | this.name = name; 22 | this.remark = remark; 23 | } 24 | 25 | public Dict setOrder(int order) { 26 | this.order = order; 27 | return this; 28 | } 29 | 30 | public Dict setCategoryID(String categoryID) { 31 | this.categoryID = categoryID; 32 | return this; 33 | } 34 | 35 | public Map toMap() { 36 | Map map = new HashMap(); 37 | map.put("id_at_app_dictcategory", categoryID); 38 | map.put("v_value", value); 39 | map.put("v_name", name); 40 | map.put("n_order", order); 41 | map.put("v_remark", remark); 42 | return map; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/DictCategory.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class DictCategory { 9 | private String id; 10 | private String pid; 11 | private String name; 12 | private int order; 13 | private Dict[] dictList; 14 | 15 | public DictCategory(String id, String name) { 16 | this.id = id; 17 | this.name = name; 18 | } 19 | 20 | public DictCategory(String id, String name, int order) { 21 | this.id = id; 22 | this.name = name; 23 | this.order = order; 24 | } 25 | 26 | public DictCategory(String id, String pid, String name, int order) { 27 | this.id = id; 28 | this.pid = pid; 29 | this.name = name; 30 | this.order = order; 31 | } 32 | 33 | public String getId() { 34 | return id; 35 | } 36 | 37 | public String getPid() { 38 | return pid; 39 | } 40 | 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public int getOrder() { 46 | return order; 47 | } 48 | 49 | public DictCategory setDictList(Dict... dictList) { 50 | for (int i = 0; i < dictList.length; i++) { 51 | Dict dict = dictList[i]; 52 | dict.setCategoryID(this.id); 53 | dict.setOrder(i); 54 | } 55 | 56 | this.dictList = dictList; 57 | return this; 58 | } 59 | 60 | public DictCategory setDictList(List dictList) { 61 | return this.setDictList(dictList.toArray(new Dict[0])); 62 | } 63 | 64 | public Map toMap() { 65 | HashMap map = new HashMap<>(); 66 | map.put("id", this.id); 67 | map.put("pid", this.pid); 68 | map.put("v_name", this.name); 69 | map.put("n_order", this.order); 70 | if (this.dictList != null) { 71 | map.put("app_dict", Arrays.stream(this.dictList).map(Dict::toMap).toList()); 72 | } 73 | 74 | return map; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/DictTreeNode.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | import net.ximatai.muyun.model.TreeNode; 4 | import org.eclipse.microprofile.openapi.annotations.media.Schema; 5 | 6 | import java.util.List; 7 | 8 | public class DictTreeNode extends TreeNode { 9 | @Schema(description = "字典值") 10 | private String value; 11 | 12 | public static DictTreeNode from(TreeNode node) { 13 | return (DictTreeNode) new DictTreeNode() 14 | .setId(node.getId()) 15 | .setLabel(node.getLabel()) 16 | .setChildren(node.getChildren()) 17 | .setData(node.getData()); 18 | } 19 | 20 | public String getValue() { 21 | return value; 22 | } 23 | 24 | public DictTreeNode setValue(String value) { 25 | this.value = value; 26 | return this; 27 | } 28 | 29 | @Override 30 | public List getChildren() { 31 | return (List) super.getChildren(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/LoginUser.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | public record LoginUser(String username, String password, String code) { 4 | } 5 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/ModuleAction.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | public class ModuleAction { 8 | 9 | public static final ModuleAction MENU = new ModuleAction("menu", "菜单", 0); 10 | public static final ModuleAction VIEW = new ModuleAction("view", "浏览", 10); 11 | public static final ModuleAction CREATE = new ModuleAction("create", "新增", 20); 12 | public static final ModuleAction SORT = new ModuleAction("sort", "排序", 29); 13 | public static final ModuleAction UPDATE = new ModuleAction("update", "修改", 30); 14 | public static final ModuleAction DELETE = new ModuleAction("delete", "删除", 40); 15 | 16 | public static final List DEFAULT_ACTIONS = List.of( 17 | MENU, VIEW, CREATE, UPDATE, DELETE 18 | ); 19 | 20 | private int order = 0; 21 | private final String alias; 22 | private final String name; 23 | 24 | public enum TypeLike { 25 | VIEW, UPDATE, CREATE, DELETE 26 | } 27 | 28 | public ModuleAction(String alias, String name, int order) { 29 | this.alias = alias; 30 | this.name = name; 31 | this.order = order; 32 | } 33 | 34 | public ModuleAction(String alias, String name, TypeLike typeLike) { 35 | this.alias = alias; 36 | this.name = name; 37 | this.order = switch (typeLike) { 38 | case VIEW -> 11; 39 | case UPDATE -> 31; 40 | case CREATE -> 21; 41 | case DELETE -> 41; 42 | }; 43 | } 44 | 45 | public String getAlias() { 46 | return alias; 47 | } 48 | 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | public int getOrder() { 54 | return order; 55 | } 56 | 57 | public Map toMap() { 58 | Map map = new HashMap<>(); 59 | map.put("v_alias", alias); 60 | map.put("v_name", name); 61 | map.put("i_order", order); 62 | return map; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/ModuleConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class ModuleConfig { 7 | private String name; 8 | private String alias = "void"; 9 | private String url; 10 | private String table; 11 | private String remark; 12 | private boolean bSystem; 13 | private List actions = new ArrayList<>(); 14 | 15 | private ModuleConfig() { 16 | } 17 | 18 | public static ModuleConfig ofName(String name) { 19 | return new ModuleConfig().setName(name); 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public ModuleConfig setName(String name) { 27 | this.name = name; 28 | return this; 29 | } 30 | 31 | public String getAlias() { 32 | return alias; 33 | } 34 | 35 | public ModuleConfig setAlias(String alias) { 36 | this.alias = alias; 37 | return this; 38 | } 39 | 40 | public String getUrl() { 41 | return url; 42 | } 43 | 44 | public ModuleConfig setUrl(String url) { 45 | this.url = url; 46 | return this; 47 | } 48 | 49 | public String getTable() { 50 | return table; 51 | } 52 | 53 | public ModuleConfig setTable(String table) { 54 | this.table = table; 55 | return this; 56 | } 57 | 58 | public String getRemark() { 59 | return remark; 60 | } 61 | 62 | public ModuleConfig setRemark(String remark) { 63 | this.remark = remark; 64 | return this; 65 | } 66 | 67 | public boolean isbSystem() { 68 | return bSystem; 69 | } 70 | 71 | public ModuleConfig setbSystem(boolean bSystem) { 72 | this.bSystem = bSystem; 73 | return this; 74 | } 75 | 76 | public List getActions() { 77 | return actions; 78 | } 79 | 80 | public ModuleConfig addAction(ModuleAction action) { 81 | this.actions.add(action); 82 | return this; 83 | } 84 | 85 | public ModuleConfig addActions(List action) { 86 | this.actions.addAll(action); 87 | return this; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/MuYunMessage.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | 5 | import java.time.LocalDateTime; 6 | import java.time.format.DateTimeFormatter; 7 | 8 | public class MuYunMessage { 9 | private final String title; 10 | private final String content; 11 | private final LocalDateTime createdAt; 12 | private final String url; 13 | 14 | public MuYunMessage(String title, String content, String url) { 15 | this(title, content, url, LocalDateTime.now()); 16 | } 17 | 18 | public MuYunMessage(String title, String content, String url, LocalDateTime createdAt) { 19 | this.title = title; 20 | this.content = content; 21 | this.url = url; 22 | this.createdAt = createdAt; 23 | } 24 | 25 | public JsonObject toJson() { 26 | JsonObject json = new JsonObject(); 27 | json.put("title", title); 28 | json.put("content", content); 29 | json.put("url", url); 30 | json.put("createdAt", createdAt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 31 | return json; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/OnlineDevice.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | 5 | import java.time.Duration; 6 | import java.time.LocalDateTime; 7 | 8 | public class OnlineDevice { 9 | private String id; 10 | private String os; 11 | private String browser; 12 | private boolean isActive = true; 13 | 14 | private LocalDateTime checkInTime; 15 | private LocalDateTime lastActiveTime; 16 | private OnlineUser onlineUser; 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public OnlineDevice setId(String id) { 23 | this.id = id; 24 | return this; 25 | } 26 | 27 | public String getOs() { 28 | return os; 29 | } 30 | 31 | public OnlineDevice setOs(String os) { 32 | this.os = os; 33 | return this; 34 | } 35 | 36 | public String getBrowser() { 37 | return browser; 38 | } 39 | 40 | public OnlineDevice setBrowser(String browser) { 41 | this.browser = browser; 42 | return this; 43 | } 44 | 45 | public boolean isActive() { 46 | return isActive; 47 | } 48 | 49 | public OnlineDevice setActive(boolean active) { 50 | isActive = active; 51 | return this; 52 | } 53 | 54 | public LocalDateTime getCheckInTime() { 55 | return checkInTime; 56 | } 57 | 58 | public OnlineDevice setCheckInTime(LocalDateTime checkInTime) { 59 | this.checkInTime = checkInTime; 60 | return this; 61 | } 62 | 63 | public LocalDateTime getLastActiveTime() { 64 | return lastActiveTime; 65 | } 66 | 67 | public OnlineDevice setLastActiveTime(LocalDateTime lastActiveTime) { 68 | this.lastActiveTime = lastActiveTime; 69 | return this; 70 | } 71 | 72 | public Duration getDuration() { 73 | return Duration.between(checkInTime, lastActiveTime); 74 | } 75 | 76 | @JsonIgnore 77 | public OnlineUser getOnlineUser() { 78 | return onlineUser; 79 | } 80 | 81 | public OnlineDevice setOnlineUser(OnlineUser onlineUser) { 82 | this.onlineUser = onlineUser; 83 | return this; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/model/RuntimeUser.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.model; 2 | 3 | import net.ximatai.muyun.model.IRuntimeUser; 4 | 5 | public class RuntimeUser implements IRuntimeUser { 6 | 7 | private String id; 8 | private String name; 9 | private String username; 10 | private String organizationId; 11 | private String departmentId; 12 | 13 | public String getId() { 14 | return id; 15 | } 16 | 17 | public RuntimeUser setId(String id) { 18 | this.id = id; 19 | return this; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public RuntimeUser setName(String name) { 27 | this.name = name; 28 | return this; 29 | } 30 | 31 | public String getUsername() { 32 | return username; 33 | } 34 | 35 | public RuntimeUser setUsername(String username) { 36 | this.username = username; 37 | return this; 38 | } 39 | 40 | public String getOrganizationId() { 41 | return organizationId; 42 | } 43 | 44 | public RuntimeUser setOrganizationId(String organizationId) { 45 | this.organizationId = organizationId; 46 | return this; 47 | } 48 | 49 | public String getDepartmentId() { 50 | return departmentId; 51 | } 52 | 53 | public RuntimeUser setDepartmentId(String departmentId) { 54 | this.departmentId = departmentId; 55 | return this; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /muyun-platform/src/main/java/net/ximatai/muyun/platform/service/MessageCenter.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.platform.service; 2 | 3 | import io.vertx.core.eventbus.EventBus; 4 | import jakarta.enterprise.context.ApplicationScoped; 5 | import jakarta.inject.Inject; 6 | import net.ximatai.muyun.platform.model.MuYunMessage; 7 | 8 | @ApplicationScoped 9 | public class MessageCenter { 10 | 11 | @Inject 12 | EventBus eventBus; 13 | 14 | public void send(String userID, MuYunMessage message) { 15 | eventBus.publish(channelForUser(userID), message.toJson()); 16 | } 17 | 18 | public void channelChanged(String channel) { 19 | eventBus.publish("web.channel.%s".formatted(channel), true); 20 | } 21 | 22 | private static String channelForUser(String userID) { 23 | return "web.user.%s".formatted(userID); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /muyun-proxy/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(enforcedPlatform(libs.quarkus.platform.bom)) 8 | api("io.quarkus:quarkus-vertx") 9 | api("io.quarkus:quarkus-reactive-routes") 10 | } 11 | 12 | -------------------------------------------------------------------------------- /muyun-proxy/src/main/java/net/ximatai/muyun/proxy/Upstream.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.proxy; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.http.HttpClient; 5 | import io.vertx.core.http.HttpClientOptions; 6 | import io.vertx.core.json.JsonObject; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.net.MalformedURLException; 11 | import java.net.URI; 12 | import java.net.URISyntaxException; 13 | import java.net.URL; 14 | 15 | public class Upstream { 16 | private static final Logger LOGGER = LoggerFactory.getLogger(Upstream.class); 17 | 18 | private final String url; 19 | private final int weight; 20 | private String path; 21 | 22 | private String host; 23 | private int port; 24 | 25 | public String getPath() { 26 | return path; 27 | } 28 | 29 | public String getUrl() { 30 | return url; 31 | } 32 | 33 | public HttpClient getClient() { 34 | return client; 35 | } 36 | 37 | public int getWeight() { 38 | return weight; 39 | } 40 | 41 | public String getHost() { 42 | return host; 43 | } 44 | 45 | public int getPort() { 46 | return port; 47 | } 48 | 49 | HttpClient client; 50 | 51 | public Upstream(JsonObject json, Vertx vertx) { 52 | this(json.getString("url"), json.getInteger("weight", 1), vertx); 53 | } 54 | 55 | public Upstream(String url, int weight, Vertx vertx) { 56 | 57 | this.url = url; 58 | this.weight = weight; 59 | 60 | try { 61 | URL realURL = new URI(this.url).toURL(); 62 | host = realURL.getHost(); 63 | port = realURL.getPort(); 64 | this.path = realURL.getPath(); 65 | 66 | HttpClientOptions clientOptions = new HttpClientOptions(); 67 | clientOptions.setDefaultHost(host); 68 | clientOptions.setDefaultPort(port); 69 | clientOptions.setKeepAlive(true); 70 | clientOptions.setTryUsePerMessageWebSocketCompression(true); 71 | 72 | if (realURL.getProtocol().equals("https")) { 73 | clientOptions.setSsl(true); 74 | clientOptions.setTrustAll(true); 75 | clientOptions.setDefaultPort(443); 76 | } 77 | 78 | this.client = vertx.createHttpClient(clientOptions); 79 | } catch (MalformedURLException e) { 80 | LOGGER.error("Malformed URL: {}", this.url, e); 81 | } catch (URISyntaxException e) { 82 | LOGGER.error("Invalid URL: {}", this.url, e); 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /muyun-proxy/src/main/java/net/ximatai/muyun/proxy/model/ProxyConfig.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.proxy.model; 2 | 3 | import io.smallrye.config.ConfigMapping; 4 | 5 | import java.util.List; 6 | 7 | @ConfigMapping(prefix = "proxy") 8 | public interface ProxyConfig { 9 | List upstreams(); 10 | } 11 | -------------------------------------------------------------------------------- /muyun-proxy/src/main/java/net/ximatai/muyun/proxy/model/UpstreamItem.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.proxy.model; 2 | 3 | public interface UpstreamItem { 4 | 5 | String prefix(); 6 | 7 | String url(); 8 | } 9 | -------------------------------------------------------------------------------- /muyun-runtime-gateway/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(project(":muyun-core")) 8 | api(project(":muyun-database-std")) 9 | } 10 | -------------------------------------------------------------------------------- /muyun-runtime-gateway/src/main/java/net/ximatai/muyun/runtime/gateway/GatewayRuntimeProvider.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.runtime.gateway; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | import io.vertx.ext.web.RoutingContext; 5 | import jakarta.enterprise.context.ApplicationScoped; 6 | import net.ximatai.muyun.model.IRuntimeUser; 7 | import net.ximatai.muyun.service.IRuntimeProvider; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.Base64; 12 | import java.util.Optional; 13 | 14 | @ApplicationScoped 15 | public class GatewayRuntimeProvider implements IRuntimeProvider { 16 | private static final Logger LOGGER = LoggerFactory.getLogger(GatewayRuntimeProvider.class); 17 | 18 | @Override 19 | public Optional getUser(RoutingContext context) { 20 | return Optional.ofNullable(context.request().getHeader("gw-user")) 21 | .flatMap(gwUser -> { 22 | try { 23 | // 尝试构建用户对象 24 | return Optional.of(IRuntimeUser.build(new JsonObject(new String(Base64.getDecoder().decode(gwUser))))); 25 | } catch (Exception e) { 26 | // 记录解析失败的日志 27 | LOGGER.warn("Failed to parse gw-user header: {}", gwUser, e); 28 | return Optional.empty(); 29 | } 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /muyun-runtime-session/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | `java-library` 4 | } 5 | 6 | dependencies { 7 | api(project(":muyun-core")) 8 | api(project(":muyun-database-std")) 9 | } 10 | -------------------------------------------------------------------------------- /muyun-runtime-session/src/main/java/net/ximatai/muyun/runtime/session/SessionRuntimeProvider.java: -------------------------------------------------------------------------------- 1 | package net.ximatai.muyun.runtime.session; 2 | 3 | import io.vertx.ext.web.RoutingContext; 4 | import jakarta.enterprise.context.ApplicationScoped; 5 | import net.ximatai.muyun.MuYunConst; 6 | import net.ximatai.muyun.model.IRuntimeUser; 7 | import net.ximatai.muyun.service.IRuntimeProvider; 8 | 9 | import java.util.Optional; 10 | 11 | @ApplicationScoped 12 | public class SessionRuntimeProvider implements IRuntimeProvider { 13 | @Override 14 | public Optional getUser(RoutingContext context) { 15 | return Optional.ofNullable(context.session()) 16 | .map(s -> s.get(MuYunConst.SESSION_USER_KEY)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | // maven { url = uri("https://mirrors.cloud.tencent.com/repository/gradle-plugins/") } 4 | // maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") } 5 | gradlePluginPortal() // 保留官方 Gradle 插件门户 6 | } 7 | } 8 | 9 | rootProject.name = "MuYun" 10 | 11 | include("muyun-core") 12 | include("muyun-runtime-session") 13 | include("muyun-runtime-gateway") 14 | //include("muyun-core-uni") 15 | include("muyun-database") 16 | include("muyun-database-std") 17 | //include("muyun-database-uni") 18 | include("muyun-platform") 19 | include("muyun-authorization") 20 | include("muyun-proxy") 21 | include("muyun-log") 22 | include("muyun-fileserver") 23 | 24 | include("muyun-boot") 25 | -------------------------------------------------------------------------------- /test.http: -------------------------------------------------------------------------------- 1 | POST http://127.0.0.1:8080/api/sso/login 2 | Content-Type: application/json 3 | 4 | { 5 | "username":"admin", 6 | "password":"admin", 7 | "code":"xxx" 8 | } 9 | 10 | ### 11 | --------------------------------------------------------------------------------