Browse Source

初始导入

yingp 5 years ago
parent
commit
b3f0339878
100 changed files with 1885 additions and 617 deletions
  1. 7 18
      README.md
  2. 14 0
      applications/README.md
  3. 2 15
      applications/base/build.gradle
  4. 0 47
      applications/base/src/main/java/com/usoftchina/uas/base/enum/Status.enum.json
  5. 0 37
      applications/base/src/main/java/com/usoftchina/uas/base/list/Filter.plan.json
  6. 0 22
      applications/base/src/main/java/com/usoftchina/uas/base/operation/Audit.opt.json
  7. 0 8
      applications/base/src/main/java/com/usoftchina/uas/base/operation/AuditOperationHandler.java
  8. 0 58
      applications/base/src/main/java/com/usoftchina/uas/base/permission/Base.perm.json
  9. 0 12
      applications/base/src/main/java/com/usoftchina/uas/base/validation/ChinaIDCard.vdt.json
  10. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/ChinaIDCardValidation.java
  11. 0 12
      applications/base/src/main/java/com/usoftchina/uas/base/validation/DefaultData.vdt.json
  12. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/DefaultDataValidation.java
  13. 0 12
      applications/base/src/main/java/com/usoftchina/uas/base/validation/Email.vdt.json
  14. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/EmailValidation.java
  15. 0 29
      applications/base/src/main/java/com/usoftchina/uas/base/validation/EntryCount.vdt.json
  16. 0 19
      applications/base/src/main/java/com/usoftchina/uas/base/validation/Legal.vdt.json
  17. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/LegalValidation.java
  18. 0 12
      applications/base/src/main/java/com/usoftchina/uas/base/validation/OneEntry.vdt.json
  19. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/OneEntryValidation.java
  20. 0 48
      applications/base/src/main/java/com/usoftchina/uas/base/validation/PriceLimit.vdt.json
  21. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/PriceLimitValidation.java
  22. 0 16
      applications/base/src/main/java/com/usoftchina/uas/base/validation/Regexp.vdt.json
  23. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/RegexpValidation.java
  24. 0 12
      applications/base/src/main/java/com/usoftchina/uas/base/validation/Required.vdt.json
  25. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/RequiredValidation.java
  26. 0 21
      applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueEntryFields.vdt.json
  27. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueEntryFieldsValidation.java
  28. 0 12
      applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueField.vdt.json
  29. 0 8
      applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueFieldValidation.java
  30. 0 17
      applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueFields.vdt.json
  31. 0 8
      applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueFieldsValidation.java
  32. 0 5
      applications/base/src/main/java/com/usoftchina/uas/base/validation/Used.vdt.json
  33. 0 10
      applications/base/src/main/java/com/usoftchina/uas/base/validation/UsedValidation.java
  34. 1 1
      build.gradle
  35. 18 0
      framework/README.md
  36. 25 12
      framework/framework-core/src/main/java/com/usoftchina/uas/metadata/DefaultMetadataScanner.java
  37. 5 1
      framework/framework-core/src/main/java/com/usoftchina/uas/metadata/MetadataScanner.java
  38. 31 14
      framework/framework-core/src/main/java/com/usoftchina/uas/metadata/MetadataScannerRegistrar.java
  39. 72 0
      framework/framework-core/src/main/java/com/usoftchina/uas/util/FileUtils.java
  40. 49 0
      framework/framework-core/src/main/java/com/usoftchina/uas/util/JarUtils.java
  41. 45 1
      framework/framework-core/src/main/java/com/usoftchina/uas/util/JsonUtils.java
  42. 6 6
      framework/framework-entity/src/main/java/com/usoftchina/uas/entity/EntityInitialize.java
  43. 71 0
      framework/framework-entity/src/main/java/com/usoftchina/uas/entity/util/EntityDelete.java
  44. 9 0
      framework/framework-entity/src/main/java/com/usoftchina/uas/entity/util/EntityQuery.java
  45. 1 1
      framework/framework-entity/src/main/java/com/usoftchina/uas/entity/util/EntityStructureUtils.java
  46. 3 0
      framework/framework-plugin/build.gradle
  47. 238 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/AbstractPluginManager.java
  48. 94 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/DefaultPluginRepository.java
  49. 44 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginClassLoader.java
  50. 113 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginDescriptor.java
  51. 35 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginDescriptorFinder.java
  52. 28 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginException.java
  53. 30 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginFactory.java
  54. 42 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginInstance.java
  55. 115 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginManager.java
  56. 55 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginRepository.java
  57. 38 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginRunner.java
  58. 28 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginState.java
  59. 39 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginStateEvent.java
  60. 17 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginStateListener.java
  61. 73 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/json/JsonPluginDescriptorFinder.java
  62. 31 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/spring/DefaultPluginRunner.java
  63. 52 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/spring/SpringPluginFactory.java
  64. 41 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/spring/SpringPluginManager.java
  65. 69 0
      framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/spring/SpringPluginRunner.java
  66. 49 0
      framework/framework-plugin/src/main/resources/metadata/Plugin.form.json
  67. 1 1
      framework/framework-web/build.gradle
  68. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/base/HomeController.java
  69. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/base/MainApplication.java
  70. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/base/MainController.java
  71. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/base/MainMenuController.java
  72. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/config/WebConfig.java
  73. 6 8
      framework/framework-web/src/main/java/com/usoftchina/uas/web/controller/WebController.java
  74. 2 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/dto/EventParams.java
  75. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/dto/ViewParams.java
  76. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/error/DefaultErrorConfig.java
  77. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/error/GlobalExceptionHandler.java
  78. 1 1
      framework/framework-web/src/main/java/com/usoftchina/uas/web/error/ServletErrorUtils.java
  79. 3 2
      framework/framework-web/src/main/java/com/usoftchina/uas/web/error/UnCaughtErrorFilter.java
  80. 0 0
      framework/framework-web/src/main/resources/metadata/Home.form.json
  81. 0 3
      framework/framework-web/src/main/resources/metadata/Main.form.json
  82. 0 0
      framework/framework-web/src/main/resources/metadata/MainMenu.form.json
  83. 12 0
      runtime/README.md
  84. 3 1
      runtime/debug-starter/build.gradle
  85. 24 0
      runtime/debug-starter/src/main/java/com/usoftchina/uas/runtime/DebugStarter.java
  86. 0 0
      runtime/debug-starter/src/main/resources/application.yml
  87. 1 1
      runtime/debug-starter/src/main/resources/banner.txt
  88. 0 0
      runtime/debug-starter/src/main/resources/config/application-prod.yml
  89. 0 0
      runtime/debug-starter/src/main/resources/config/application-test.yml
  90. 82 0
      runtime/linux/uas.sh
  91. 10 0
      runtime/saas-starter/build.gradle
  92. 3 3
      runtime/saas-starter/src/main/java/com/usoftchina/uas/runtime/SaasStarter.java
  93. 22 0
      runtime/saas-starter/src/main/resources/application.yml
  94. 10 0
      runtime/saas-starter/src/main/resources/banner.txt
  95. 11 0
      runtime/standalone-starter/build.gradle
  96. 24 0
      runtime/standalone-starter/src/main/java/com/usoftchina/uas/runtime/StandaloneStarter.java
  97. 30 0
      runtime/standalone-starter/src/main/java/com/usoftchina/uas/runtime/config/PluginConfig.java
  98. 22 0
      runtime/standalone-starter/src/main/resources/application.yml
  99. 10 0
      runtime/standalone-starter/src/main/resources/banner.txt
  100. 10 4
      settings.gradle

+ 7 - 18
README.md

@@ -1,17 +1,14 @@
-## UAS2.0框架
+## UAS2.0框架与基础应用
 
 ### 项目结构
 
 ```
-├─uu-platform
+├─uas-platform
 │  │  
-│  ├─apis-------------------------------------接口定义
-│  ├─db---------------------------------------数据库脚本
-│  ├─external---------------------------------第三方对接
+│  ├─applications-----------------------------领域应用
+│  ├─framework--------------------------------服务端框架
 │  ├─fronted----------------------------------前端
 │  ├─runtime----------------------------------服务运行
-│  ├─services---------------------------------服务实现
-│  ├─shared-----------------------------------公共模块
 │  │
 ```
 
@@ -35,22 +32,14 @@ gradlew build -x test
 
 | IP      |  账号  |  密码  |  环境  |  资源  |
 | --------   | :----:  | :----: | :------:  | :------:  |
-| 10.1.81.81 |  root  | select123*** |  CentOS7, Tigase, MySQL  | Cpu: 8, Mem: 16G, Disk: 60G |
-| 10.1.81.82 |  root  | select123*** |  CentOS7, MySQL  | Cpu: 8, Mem: 16G, Disk: 60G |
-| 10.1.81.83 |  root  | select123*** |  CentOS7, Docker  | Cpu: 8, Mem: 16G, Disk: 60G |
+| 10.1.81.80 |  root  | select123*** |  CentOS7, JAVA8  | Cpu: 8, Mem: 16G, Disk: 60G |
 
 > 数据库
 
 | 数据库      | 类型   | 地址   |  账号  |  密码  |  说明  |
 | --------   | :-----: | :-----:  | :----:  | :----: | :------  |
-| uu_base | mysql | 10.1.81.82:3306 |  root  | select111*** |  基础库  |
-
-> 中间件
-
-| 类型 | 地址 | 账号 | 密码 | 说明 |
-| ---- | :----: | :----: | :----: | ---- |
-| redis | 10.1.81.82:6379 | | | 内存数据库 |
+| UAS_2 | oracle | 10.1.81.2:1521 |  UAS_2  | select!#%*( |  测试数据中心  |
 
 > 部署
 
-* [jenkins](http://10.1.81.61:8080/job/uu/view/test) **账号** admin **密码** select123***
+* [jenkins](http://10.1.81.61:8080/job/uas) **账号** admin **密码** select123***

+ 14 - 0
applications/README.md

@@ -0,0 +1,14 @@
+## 领域应用
+
+### 项目结构
+
+```
+├─applications
+│  │  
+│  ├─base-------------------------------------基础
+│  ├─scm--------------------------------------供应链
+│  ├───scm-base-------------------------------供应链基础
+│  ├───scm-purchase---------------------------采购管理
+│  ├───scm-sale-------------------------------销售管理
+│  │
+```

+ 2 - 15
applications/base/build.gradle

@@ -1,16 +1,3 @@
-plugins {
-    id 'java'
-}
-
-group 'com.usoftchina.uas'
-version '2.0.0-SNAPSHOT'
-
-sourceCompatibility = 1.8
-
-repositories {
-    mavenCentral()
-}
-
 dependencies {
-    testCompile group: 'junit', name: 'junit', version: '4.12'
-}
+    compile project(':framework:framework-form')
+}

+ 0 - 47
applications/base/src/main/java/com/usoftchina/uas/base/enum/Status.enum.json

@@ -1,47 +0,0 @@
-[{
-  "id": "status",
-  "name": "数据状态",
-  "entries": [{
-    "name": "在录入",
-    "value": "E"
-  }, {
-    "name": "已提交",
-    "value": "S"
-  }, {
-    "name": "已审核",
-    "value": "A"
-  }]
-}, {
-  "id": "permStatus",
-  "name": "权限状态",
-  "entries": [{
-    "name": "无权",
-    "value": "N"
-  }, {
-    "name": "有权",
-    "value": "Y"
-  }]
-}, {
-  "id": "execStatus",
-  "name": "执行状态",
-  "entries": [{
-    "name": "成功",
-    "value": "S"
-  }, {
-    "name": "失败",
-    "value": "F"
-  }, {
-    "name": "部分成功",
-    "value": "P"
-  }]
-}, {
-  "id": "enableStatus",
-  "name": "禁用状态",
-  "entries": [{
-    "name": "禁用",
-    "value": "N"
-  }, {
-    "name": "启用",
-    "value": "Y"
-  }]
-}]

+ 0 - 37
applications/base/src/main/java/com/usoftchina/uas/base/list/Filter.plan.json

@@ -1,37 +0,0 @@
-[{
-  "id": "default",
-  "name": "缺省方案",
-  "filter": []
-}, {
-  "id": "today",
-  "name": "今天",
-  "filter": [{
-    "fieldFn": "billDateField",
-    "compare": "=",
-    "valueFn": "today"
-  }]
-}, {
-  "id": "thisWeek",
-  "name": "本周",
-  "filter": [{
-    "fieldFn": "billDateField",
-    "compare": ">=",
-    "valueFn": "thisWeekBegin"
-  }, {
-    "fieldFn": "billDateField",
-    "compare": "<=",
-    "valueFn": "thisWeekEnd"
-  }]
-}, {
-  "id": "thisMonth",
-  "name": "本月",
-  "filter": [{
-    "fieldFn": "billDateField",
-    "compare": ">=",
-    "valueFn": "thisMonthBegin"
-  }, {
-    "fieldFn": "billDateField",
-    "compare": "<=",
-    "valueFn": "thisMonthEnd"
-  }]
-}]

+ 0 - 22
applications/base/src/main/java/com/usoftchina/uas/base/operation/Audit.opt.json

@@ -1,22 +0,0 @@
-[{
-  "id": "audit",
-  "name": "审核",
-  "className": "com.usoftchina.uas.base.operation.AuditOperationHandler",
-  "form": {
-    "layout": "vbox",
-    "items": [{
-      "name": "statusField",
-      "label": "审核状态字段",
-      "xtype": "statusFieldCombo"
-    }, {
-      "name": "statusValue",
-      "label": "审核通过名称",
-      "xtype": "statusCombo"
-    }, {
-      "name": "commentField",
-      "label": "审核意见字段",
-      "xtype": "textFieldCombo"
-    }]
-  },
-  "log": true
-}]

+ 0 - 8
applications/base/src/main/java/com/usoftchina/uas/base/operation/AuditOperationHandler.java

@@ -1,8 +0,0 @@
-package com.usoftchina.uas.base.operation;
-
-/**
- * @author yingp
- * @date 2019/8/9
- */
-public class AuditOperationHandler {
-}

+ 0 - 58
applications/base/src/main/java/com/usoftchina/uas/base/permission/Base.perm.json

@@ -1,58 +0,0 @@
-[{
-  "id": "track",
-  "name": "业务流程查询"
-}, {
-  "id": "import",
-  "name": "导入"
-}, {
-  "id": "export",
-  "name": "导出"
-}, {
-  "id": "delete",
-  "name": "删除"
-}, {
-  "id": "freeze",
-  "name": "冻结"
-}, {
-  "id": "unFreeze",
-  "name": "取消冻结"
-}, {
-  "id": "close",
-  "name": "关闭"
-}, {
-  "id": "unClose",
-  "name": "反关闭"
-}, {
-  "id": "new",
-  "name": "新增"
-}, {
-  "id": "modify",
-  "name": "修改"
-}, {
-  "id": "submit",
-  "name": "提交"
-}, {
-  "id": "audit",
-  "name": "审核"
-}, {
-  "id": "unAudit",
-  "name": "反审核"
-}, {
-  "id": "view",
-  "name": "查看"
-}, {
-  "id": "trackUp",
-  "name": "上查"
-}, {
-  "id": "trackDown",
-  "name": "下查"
-}, {
-  "id": "print",
-  "name": "打印"
-}, {
-  "id": "cancel",
-  "name": "作废"
-}, {
-  "id": "accessory",
-  "name": "附件"
-}]

+ 0 - 12
applications/base/src/main/java/com/usoftchina/uas/base/validation/ChinaIDCard.vdt.json

@@ -1,12 +0,0 @@
-[{
-  "id": "chinaIDCard",
-  "name": "合法性检查,检查大陆居民身份证格式合法性",
-  "className": "com.usoftchina.uas.base.validation.ChinaIDCardValidation",
-  "form": {
-    "items": [{
-      "name": "idCardField",
-      "label": "选择需要进行大陆居民身份证号码检查的字段",
-      "xtype": "fieldTree"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/ChinaIDCardValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 合法性检查,检查大陆居民身份证格式合法性
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class ChinaIDCardValidation {
-}

+ 0 - 12
applications/base/src/main/java/com/usoftchina/uas/base/validation/DefaultData.vdt.json

@@ -1,12 +0,0 @@
-[{
-  "id": "defaultData",
-  "name": "合法性检查,默认数据设置",
-  "className": "com.usoftchina.uas.base.validation.DefaultDataValidation",
-  "form": {
-    "items": [{
-      "name": "selectField",
-      "label": "选择需要进行检查的复选框字段",
-      "xtype": "fieldTree"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/DefaultDataValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 合法性检查,默认数据设置
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class DefaultDataValidation {
-}

+ 0 - 12
applications/base/src/main/java/com/usoftchina/uas/base/validation/Email.vdt.json

@@ -1,12 +0,0 @@
-[{
-  "id": "email",
-  "name": "合法性检查,检查邮件格式合法性",
-  "className": "com.usoftchina.uas.base.validation.EmailValidation",
-  "form": {
-    "items": [{
-      "name": "emailField",
-      "label": "选择需要进行邮箱合法性检查的字段",
-      "xtype": "fieldTree"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/EmailValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 合法性检查,检查邮件格式合法性
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class EmailValidation {
-}

+ 0 - 29
applications/base/src/main/java/com/usoftchina/uas/base/validation/EntryCount.vdt.json

@@ -1,29 +0,0 @@
-[{
-  "id": "entryCount",
-  "name": "合法性检查,检查分录列合计",
-  "className": "com.usoftchina.uas.base.validation.EntryCountValidation",
-  "form": {
-    "layout": "vbox",
-    "items": [{
-      "name": "entryField",
-      "label": "分录项",
-      "xtype": "entryFieldCombo"
-    }, {
-      "name": "groupField",
-      "label": "分组列",
-      "xtype": "fieldCombo"
-    }, {
-      "name": "countField",
-      "label": "合计列",
-      "xtype": "numberFieldCombo"
-    }, {
-      "name": "compare",
-      "label": "操作符",
-      "xtype": "compareCombo"
-    }, {
-      "name": "compareValue",
-      "label": "比较值",
-      "xtype": "numberfield"
-    }]
-  }
-}]

+ 0 - 19
applications/base/src/main/java/com/usoftchina/uas/base/validation/Legal.vdt.json

@@ -1,19 +0,0 @@
-[{
-  "id": "legal",
-  "name": "单据合法性校验",
-  "className": "com.usoftchina.uas.base.validation.LegalValidation",
-  "form": {
-    "items": [{
-      "name": "expression",
-      "xtype": "fieldExpTree"
-    }, {
-      "name": "triggerOnTrue",
-      "label": "校验表达式为真时提示",
-      "xtype": "checkbox"
-    }, {
-      "name": "message",
-      "label": "提示信息",
-      "xtype": "textfield"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/LegalValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 单据合法性校验
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class LegalValidation {
-}

+ 0 - 12
applications/base/src/main/java/com/usoftchina/uas/base/validation/OneEntry.vdt.json

@@ -1,12 +0,0 @@
-[{
-  "id": "oneEntry",
-  "name": "有且只能选择一条分录",
-  "className": "com.usoftchina.uas.base.validation.OneEntryValidation",
-  "form": {
-    "items": [{
-      "name": "selectField",
-      "label": "选择需要进行检查的复选框字段",
-      "xtype": "fieldTree"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/OneEntryValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 有且只能选择一条分录
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class OneEntryValidation {
-}

+ 0 - 48
applications/base/src/main/java/com/usoftchina/uas/base/validation/PriceLimit.vdt.json

@@ -1,48 +0,0 @@
-[{
-  "id": "priceLimit",
-  "name": "限价控制检查",
-  "className": "com.usoftchina.uas.base.validation.PriceLimitValidation",
-  "form": {
-    "layout": {
-      "type": "table",
-      "columns": 2
-    },
-    "items": [{
-      "name": "productField",
-      "label": "物料字段",
-      "xtype": "productFieldCombo"
-    }, {
-      "name": "priceCateField",
-      "label": "价目表字段",
-      "xtype": "priceCateFieldCombo"
-    }, {
-      "name": "priceField",
-      "label": "单价字段",
-      "xtype": "priceFieldCombo"
-    }, {
-      "name": "taxPriceField",
-      "label": "含税单价字段",
-      "xtype": "priceFieldCombo"
-    }, {
-      "name": "maxPriceField",
-      "label": "价格上限字段",
-      "xtype": "priceFieldCombo"
-    }, {
-      "name": "minPriceField",
-      "label": "价格下限字段",
-      "xtype": "priceFieldCombo"
-    }, {
-      "name": "unitField",
-      "label": "单位字段",
-      "xtype": "unitFieldCombo"
-    }, {
-      "name": "hasTaxField",
-      "label": "是否含税字段",
-      "xtype": "checkboxFieldCombo"
-    }, {
-      "name": "isGiftField",
-      "label": "是否赠品字段",
-      "xtype": "checkboxFieldCombo"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/PriceLimitValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 限价控制检查
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class PriceLimitValidation {
-}

+ 0 - 16
applications/base/src/main/java/com/usoftchina/uas/base/validation/Regexp.vdt.json

@@ -1,16 +0,0 @@
-[{
-  "id": "regexp",
-  "name": "正则表达式验证",
-  "className": "com.usoftchina.uas.base.validation.RegexpValidation",
-  "form": {
-    "items": [{
-      "name": "textField",
-      "label": "选择需要校验字符串格式的字段",
-      "xtype": "textfieldTree"
-    }, {
-      "name": "expression",
-      "label": "正则表达式",
-      "xtype": "textfield"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/RegexpValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 正则表达式验证
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class RegexpValidation {
-}

+ 0 - 12
applications/base/src/main/java/com/usoftchina/uas/base/validation/Required.vdt.json

@@ -1,12 +0,0 @@
-[{
-  "id": "required",
-  "name": "合法性检查,字段必录",
-  "className": "com.usoftchina.uas.base.validation.RequiredValidation",
-  "form": {
-    "items": [{
-      "name": "requiredFields",
-      "label": "选择需要校验必录的字段列表",
-      "xtype": "fieldTree"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/RequiredValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 合法性检查,字段必录
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class RequiredValidation {
-}

+ 0 - 21
applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueEntryFields.vdt.json

@@ -1,21 +0,0 @@
-[{
-  "id": "uniqueField",
-  "name": "合法性检查,检查分录组合字段唯一性",
-  "className": "com.usoftchina.uas.base.validation.UniqueEntryFieldsValidation",
-  "form": {
-    "items": [{
-      "name": "uniqueFields",
-      "label": "选择需要进行唯一性检查的组合字段",
-      "xtype": "fieldTree",
-      "selectModel": "multi"
-    }, {
-      "name": "onlyCurrentForm",
-      "label": "仅检查当前表单内数据",
-      "xtype": "checkbox"
-    }, {
-      "name": "ignoreNull",
-      "label": "空值不参与校验",
-      "xtype": "checkbox"
-    }]
-  }
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueEntryFieldsValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 合法性检查,检查分录组合字段唯一性
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class UniqueEntryFieldsValidation {
-}

+ 0 - 12
applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueField.vdt.json

@@ -1,12 +0,0 @@
-[{
-  "id": "uniqueField",
-  "name": "合法性检查,检查字段唯一性",
-  "className": "com.usoftchina.uas.base.validation.UniqueFieldValidation",
-  "form": {
-    "items": [{
-      "name": "uniqueField",
-      "label": "选择需要进行唯一性检查的字段",
-      "xtype": "fieldTree"
-    }]
-  }
-}]

+ 0 - 8
applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueFieldValidation.java

@@ -1,8 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * @author yingp
- * @date 2019/8/9
- */
-public class UniqueFieldValidation {
-}

+ 0 - 17
applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueFields.vdt.json

@@ -1,17 +0,0 @@
-[{
-  "id": "uniqueField",
-  "name": "合法性检查,检查组合字段唯一性",
-  "className": "com.usoftchina.uas.base.validation.UniqueFieldsValidation",
-  "form": {
-    "items": [{
-      "name": "uniqueFields",
-      "label": "选择需要进行唯一性检查的组合字段",
-      "xtype": "fieldTree",
-      "selectModel": "multi"
-    }, {
-      "name": "ignoreNull",
-      "label": "空值不参与校验",
-      "xtype": "checkbox"
-    }]
-  }
-}]

+ 0 - 8
applications/base/src/main/java/com/usoftchina/uas/base/validation/UniqueFieldsValidation.java

@@ -1,8 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * @author yingp
- * @date 2019/8/9
- */
-public class UniqueFieldsValidation {
-}

+ 0 - 5
applications/base/src/main/java/com/usoftchina/uas/base/validation/Used.vdt.json

@@ -1,5 +0,0 @@
-[{
-  "id": "used",
-  "name": "检查当前基础资料是否被其他业务对象使用",
-  "className": "com.usoftchina.uas.base.validation.UsedValidation"
-}]

+ 0 - 10
applications/base/src/main/java/com/usoftchina/uas/base/validation/UsedValidation.java

@@ -1,10 +0,0 @@
-package com.usoftchina.uas.base.validation;
-
-/**
- * 检查当前基础资料是否被其他业务对象使用
- *
- * @author yingp
- * @date 2019/8/9
- */
-public class UsedValidation {
-}

+ 1 - 1
build.gradle

@@ -9,7 +9,7 @@ allprojects {
 }
 
 subprojects { Project subproject ->
-    if (subproject.name in ["framework", "applications", "scm", "cloud"]) {
+    if (subproject.name in ["framework", "applications", "runtime", "scm", "cloud"]) {
         return
     }
     apply plugin: 'java'

+ 18 - 0
framework/README.md

@@ -0,0 +1,18 @@
+## 服务端框架
+
+### 项目结构
+
+```
+├─framework
+│  │  
+│  ├─framework-core---------------------------基础
+│  ├─framework-domain-------------------------领域
+│  ├─framework-entity-------------------------实体
+│  ├─framework-form---------------------------表单
+│  ├─framework-jdbc---------------------------数据库操作
+│  ├─framework-plugin-------------------------插件
+│  ├─framework-security-----------------------安全
+│  ├─framework-view---------------------------视图
+│  ├─framework-web----------------------------web服务
+│  │
+```

+ 25 - 12
framework/framework-core/src/main/java/com/usoftchina/uas/metadata/DefaultMetadataScanner.java

@@ -3,14 +3,14 @@ package com.usoftchina.uas.metadata;
 import com.usoftchina.uas.io.UasFactoriesLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
 import org.springframework.core.env.Environment;
 import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.core.io.support.ResourcePatternResolver;
-import org.springframework.core.io.support.ResourcePatternUtils;
 
 import java.io.IOException;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author yingp
@@ -23,11 +23,11 @@ public class DefaultMetadataScanner implements MetadataScanner {
 
     private final Environment environment;
 
-    private final ResourcePatternResolver resourcePatternResolver;
+    private final Set<String> resolvedResources;
 
-    public DefaultMetadataScanner(Environment environment, ResourceLoader resourceLoader) {
+    public DefaultMetadataScanner(Environment environment) {
         this.environment = environment;
-        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
+        this.resolvedResources = new HashSet<>(0);
     }
 
     /**
@@ -55,12 +55,17 @@ public class DefaultMetadataScanner implements MetadataScanner {
     }
 
     @Override
-    public void scan(String... locations) {
+    public boolean scan(ApplicationContext context, String... locations) {
+        boolean scanned = false;
         if (null != locations || locations.length > 0) {
             for (String location : locations) {
-                scanResources(resolveLocation(location));
+                boolean hasResource = scanResources(context, resolveLocation(location));
+                if (hasResource) {
+                    scanned = true;
+                }
             }
         }
+        return scanned;
     }
 
     /**
@@ -70,8 +75,8 @@ public class DefaultMetadataScanner implements MetadataScanner {
      * @return
      * @throws IOException
      */
-    private Resource[] findCandidateResources(String location) throws IOException {
-        return this.resourcePatternResolver.getResources(location);
+    private Resource[] findCandidateResources(ApplicationContext context, String location) throws IOException {
+        return context.getResources(location);
     }
 
     /**
@@ -79,11 +84,18 @@ public class DefaultMetadataScanner implements MetadataScanner {
      *
      * @param location
      */
-    private void scanResources(String location) {
+    private boolean scanResources(ApplicationContext context, String location) {
         logger.trace("Start scan: " + location);
+        boolean hasResource = false;
         try {
-            Resource[] resources = findCandidateResources(location);
+            Resource[] resources = findCandidateResources(context, location);
             for (Resource resource : resources) {
+                // 多次扫描防重复
+                if (resolvedResources.contains(resource.toString())) {
+                    continue;
+                }
+                hasResource = true;
+                resolvedResources.add(resource.toString());
                 if (logger.isTraceEnabled()) {
                     logger.trace("Scanning " + resource);
                 }
@@ -103,6 +115,7 @@ public class DefaultMetadataScanner implements MetadataScanner {
         } catch (IOException ex) {
             throw new MetadataDefinitionException("I/O failure during scanning", ex);
         }
+        return hasResource;
     }
 
     private String resolveLocation(String location) {

+ 5 - 1
framework/framework-core/src/main/java/com/usoftchina/uas/metadata/MetadataScanner.java

@@ -1,5 +1,7 @@
 package com.usoftchina.uas.metadata;
 
+import org.springframework.context.ApplicationContext;
+
 /**
  * 扫描解析元数据
  *
@@ -11,7 +13,9 @@ public interface MetadataScanner {
     /**
      * 扫描
      *
+     * @param context
      * @param locations
+     * @return
      */
-    void scan(String... locations);
+    boolean scan(ApplicationContext context, String... locations);
 }

+ 31 - 14
framework/framework-core/src/main/java/com/usoftchina/uas/metadata/MetadataScannerRegistrar.java

@@ -4,13 +4,13 @@ import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationListener;
 import org.springframework.context.EnvironmentAware;
-import org.springframework.context.ResourceLoaderAware;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.ImportAware;
+import org.springframework.context.event.ContextRefreshedEvent;
 import org.springframework.core.annotation.AnnotationAttributes;
 import org.springframework.core.env.Environment;
-import org.springframework.core.io.ResourceLoader;
 import org.springframework.core.type.AnnotationMetadata;
 
 /**
@@ -18,16 +18,16 @@ import org.springframework.core.type.AnnotationMetadata;
  * @date 2019/8/23
  */
 @Configuration
-public class MetadataScannerRegistrar implements ImportAware, InitializingBean, EnvironmentAware,
-        ResourceLoaderAware, ApplicationContextAware {
+public class MetadataScannerRegistrar implements ImportAware, InitializingBean, EnvironmentAware, ApplicationContextAware,
+        ApplicationListener<ContextRefreshedEvent> {
     private Environment environment;
 
-    private ResourceLoader resourceLoader;
-
-    private ApplicationContext applicationContext;
+    private ApplicationContext parentContext;
 
     private String[] locations;
 
+    private MetadataScanner scanner;
+
     @Override
     public void setImportMetadata(AnnotationMetadata metadata) {
         AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(MetadataScan.class.getName()));
@@ -36,9 +36,24 @@ public class MetadataScannerRegistrar implements ImportAware, InitializingBean,
 
     @Override
     public void afterPropertiesSet() throws Exception {
-        MetadataScanner scanner = new DefaultMetadataScanner(environment, resourceLoader);
-        scanner.scan(locations);
-        this.applicationContext.publishEvent(new MetadataScannedEvent(this));
+        this.scanner = new DefaultMetadataScanner(environment);
+        scan();
+    }
+
+    public void scan() {
+        scan(parentContext);
+    }
+
+    /**
+     * 扫描指定上下文
+     *
+     * @param applicationContext
+     */
+    public void scan(ApplicationContext applicationContext) {
+        boolean scanned = scanner.scan(applicationContext, locations);
+        if (scanned) {
+            applicationContext.publishEvent(new MetadataScannedEvent(this));
+        }
     }
 
     @Override
@@ -47,12 +62,14 @@ public class MetadataScannerRegistrar implements ImportAware, InitializingBean,
     }
 
     @Override
-    public void setResourceLoader(ResourceLoader resourceLoader) {
-        this.resourceLoader = resourceLoader;
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        this.parentContext = applicationContext;
     }
 
     @Override
-    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-        this.applicationContext = applicationContext;
+    public void onApplicationEvent(ContextRefreshedEvent event) {
+        if (event.getApplicationContext() != parentContext) {
+            scan(event.getApplicationContext());
+        }
     }
 }

+ 72 - 0
framework/framework-core/src/main/java/com/usoftchina/uas/util/FileUtils.java

@@ -0,0 +1,72 @@
+package com.usoftchina.uas.util;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.*;
+import java.util.Collections;
+
+/**
+ * @author yingp
+ * @date 2018/7/31
+ */
+public class FileUtils {
+    /**
+     * 是否jar文件
+     * @param path
+     * @return
+     */
+    public static boolean isJarFile(Path path) {
+        return Files.isRegularFile(path) && path.toString().toLowerCase().endsWith(".jar");
+    }
+
+    public static Path getPath(Path path, String first, String... more) throws IOException {
+        if (Files.isDirectory(path)) {
+            return path.resolve(Paths.get(first, more));
+        }
+        URI uri = path.toUri();
+        if (isJarFile(path)) {
+            String pathString = path.toAbsolutePath().toString();
+            // Windows系统路径
+            pathString = pathString.replace("\\", "/");
+            if (!pathString.startsWith("/")) {
+                pathString = "/" + pathString;
+            }
+            // 替换空格
+            pathString = pathString.replaceAll(" ","%20");
+            uri = URI.create("jar:file:" + pathString);
+        }
+        return getPath(uri, first, more);
+    }
+
+    public static Path getPath(URI uri, String first, String... more) throws IOException {
+        FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, String>emptyMap());
+        return fileSystem.getPath(first, more);
+    }
+
+    public static void createDirectory(Path path) throws IOException {
+        if (!Files.exists(path) || !Files.isDirectory(path)) {
+            Files.createDirectory(path);
+        }
+    }
+
+    public static void createFile(Path path) throws IOException {
+        if (!Files.exists(path) || !Files.isRegularFile(path)) {
+            Files.createFile(path);
+        }
+    }
+
+    public static void deleteFile(Path path) throws IOException {
+        if (Files.exists(path) && Files.isRegularFile(path)) {
+            Files.delete(path);
+        }
+    }
+
+    public static URL toURL(Path path) throws IOException{
+        return path.toFile().getCanonicalFile().toURI().toURL();
+    }
+
+    public static URL toURL(Path path, String first, String... more) throws IOException {
+        return toURL(getPath(path, first, more));
+    }
+}

+ 49 - 0
framework/framework-core/src/main/java/com/usoftchina/uas/util/JarUtils.java

@@ -0,0 +1,49 @@
+package com.usoftchina.uas.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * @author yingp
+ * @date 2019/10/9
+ */
+public class JarUtils {
+
+    public static Set<String> getPackages(String url) throws IOException{
+        return getPackages(new JarFile(new URL(url).getFile()));
+    }
+
+    public static Set<String> getPackages(File file) throws IOException{
+        return getPackages(new JarFile(file));
+    }
+
+    public static Set<String> getPackages(JarFile file) {
+        Set<String> packageNames = new HashSet<>();
+        Enumeration<JarEntry> jarEntryEnumeration = file.entries();
+        while (jarEntryEnumeration.hasMoreElements()) {
+            JarEntry jarEntry = jarEntryEnumeration.nextElement();
+            String jarEntryName = jarEntry.getName();
+            if (jarEntryName.endsWith(".class")) {
+                if (jarEntryName.contains("/")) {
+                    String packageName =
+                            jarEntryName.
+                                    substring(0, jarEntryName.lastIndexOf("/")).
+                                    replaceAll("/", ".");
+                    packageNames.add(packageName);
+                } else {
+                    String packageName =
+                            jarEntryName.
+                                    substring(0, jarEntryName.length() - ".class".length());
+                    packageNames.add(packageName);
+                }
+            }
+        }
+        return packageNames;
+    }
+}

+ 45 - 1
framework/framework-core/src/main/java/com/usoftchina/uas/util/JsonUtils.java

@@ -7,6 +7,7 @@ import org.springframework.core.io.Resource;
 import org.springframework.util.StreamUtils;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.Charset;
 import java.text.SimpleDateFormat;
 import java.util.List;
@@ -93,6 +94,36 @@ public class JsonUtils {
         }
     }
 
+    /**
+     * 从资源文件转换
+     *
+     * @param resource
+     * @param targetClass
+     * @param <T>
+     * @return
+     * @throws IOException
+     */
+    public static <T> T load(Resource resource, Class<T> targetClass) throws IOException {
+        return load(resource.getInputStream(), targetClass);
+    }
+
+    /**
+     * 从资源文件转换
+     *
+     * @param in
+     * @param targetClass
+     * @param <T>
+     * @return
+     * @throws IOException
+     */
+    public static <T> T load(InputStream in, Class<T> targetClass) throws IOException {
+        String dataStr = StreamUtils.copyToString(in, Charset.defaultCharset());
+        if (!StringUtils.isEmpty(dataStr)) {
+            return fromJsonString(dataStr, targetClass);
+        }
+        return null;
+    }
+
     /**
      * 从资源文件转换
      *
@@ -103,7 +134,20 @@ public class JsonUtils {
      * @throws IOException
      */
     public static <T> List<T> loadArray(Resource resource, Class<T> targetClass) throws IOException {
-        String dataStr = StreamUtils.copyToString(resource.getInputStream(), Charset.defaultCharset());
+        return loadArray(resource.getInputStream(), targetClass);
+    }
+
+    /**
+     * 从资源文件转换
+     *
+     * @param in
+     * @param targetClass
+     * @param <T>
+     * @return
+     * @throws IOException
+     */
+    public static <T> List<T> loadArray(InputStream in, Class<T> targetClass) throws IOException {
+        String dataStr = StreamUtils.copyToString(in, Charset.defaultCharset());
         if (!StringUtils.isEmpty(dataStr)) {
             return fromJsonArray(dataStr, targetClass);
         }

+ 6 - 6
framework/framework-entity/src/main/java/com/usoftchina/uas/entity/EntityInitialize.java

@@ -1,7 +1,7 @@
 package com.usoftchina.uas.entity;
 
 import com.usoftchina.uas.context.ContextHolder;
-import com.usoftchina.uas.entity.util.EntityUtils;
+import com.usoftchina.uas.entity.util.EntityStructureUtils;
 import com.usoftchina.uas.jdbc.DatabaseUtils;
 import com.usoftchina.uas.jdbc.JdbcUtils;
 import com.usoftchina.uas.metadata.MetadataScannedEvent;
@@ -42,10 +42,10 @@ public class EntityInitialize implements ApplicationListener<MetadataScannedEven
             List<EntityMetadata> existEntities = new ArrayList<>(1);
             List<String> clauses = new ArrayList<>(1);
             // check for add
-            entities.parallelStream().forEach(entity -> {
+            entities.forEach(entity -> {
                 if (!tableNames.contains(entity.getTableName())) {
                     logger.debug("Create database table {}", entity.getTableName());
-                    clauses.addAll(EntityUtils.makeCreateTableClause(entity));
+                    clauses.addAll(EntityStructureUtils.makeCreateTableClause(entity));
                 } else {
                     tableNames.remove(entity.getTableName());
                     existTableNames.add(entity.getTableName());
@@ -56,18 +56,18 @@ public class EntityInitialize implements ApplicationListener<MetadataScannedEven
             if (!existEntities.isEmpty()) {
                 Map<String, Map<String, DatabaseUtils.ColumnInfo>> colInfo = DatabaseUtils.getColumnInfo(existTableNames);
                 logger.debug("Database column metadata loaded");
-                existEntities.parallelStream().forEach(entity -> {
+                existEntities.forEach(entity -> {
                     Map<String, DatabaseUtils.ColumnInfo> tableColInfo = colInfo.get(entity.getTableName());
                     entity.getFields().forEach(field -> {
                         if (!field.isVirtualField()) {
                             if (!tableColInfo.containsKey(field.getColumnName())) {
                                 logger.debug("Create database column {}.{}", entity.getTableName(), field.getColumnName());
-                                clauses.addAll(EntityUtils.makeAddColumnClause(entity, field));
+                                clauses.addAll(EntityStructureUtils.makeAddColumnClause(entity, field));
                             } else {
                                 DatabaseUtils.ColumnInfo info = tableColInfo.get(field.getColumnName());
                                 if (isFieldModified(entity, field, info)) {
                                     logger.debug("Modify database column {}.{}", entity.getTableName(), field.getColumnName());
-                                    clauses.addAll(EntityUtils.makeModifyColumnClause(entity, field));
+                                    clauses.addAll(EntityStructureUtils.makeModifyColumnClause(entity, field));
                                 }
                             }
                         }

+ 71 - 0
framework/framework-entity/src/main/java/com/usoftchina/uas/entity/util/EntityDelete.java

@@ -0,0 +1,71 @@
+package com.usoftchina.uas.entity.util;
+
+import com.usoftchina.uas.entity.EntityMetadata;
+import com.usoftchina.uas.entity.EntityMetadataService;
+import com.usoftchina.uas.jdbc.Filter;
+import com.usoftchina.uas.util.Record;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author yingp
+ * @date 2019/10/8
+ */
+public class EntityDelete {
+    /**
+     * 按ID删除
+     *
+     * @param record
+     * @param tableName
+     */
+    public static void delete(Record record, String tableName) {
+        delete(record, EntityMetadataService.get(tableName));
+    }
+
+    /**
+     * 按ID删除
+     *
+     * @param record
+     * @param entity
+     */
+    public static void delete(Record record, EntityMetadata entity) {
+        if (null != entity.getIdColumnName()) {
+            Object id = record.get(entity.getIdColumnName());
+            if (null != id) {
+                Filter.equalTo(entity.getIdColumnName(), id)
+                        .delete(entity.getTableName());
+            }
+        }
+    }
+
+    /**
+     * 按ID批量删除
+     *
+     * @param records
+     * @param tableName
+     */
+    public static void deleteAll(List<Record> records, String tableName) {
+        deleteAll(records, EntityMetadataService.get(tableName));
+    }
+
+    /**
+     * 按ID批量删除
+     *
+     * @param records
+     * @param entity
+     */
+    public static void deleteAll(List<Record> records, EntityMetadata entity) {
+        if (null != entity.getIdColumnName() && !CollectionUtils.isEmpty(records)) {
+            Set<Object> idSet = records.stream()
+                    .map(record -> record.get(entity.getIdColumnName()))
+                    .collect(Collectors.toSet());
+            if (!CollectionUtils.isEmpty(idSet)) {
+                Filter.in(entity.getIdColumnName(), idSet)
+                        .delete(entity.getTableName());
+            }
+        }
+    }
+}

+ 9 - 0
framework/framework-entity/src/main/java/com/usoftchina/uas/entity/util/EntityQuery.java

@@ -236,6 +236,15 @@ public class EntityQuery {
             return this;
         }
 
+        /**
+         * 查找全部
+         *
+         * @return
+         */
+        public List<Record> where() {
+            return where(Condition.EMPTY);
+        }
+
         /**
          * 条件查找
          *

+ 1 - 1
framework/framework-entity/src/main/java/com/usoftchina/uas/entity/util/EntityUtils.java → framework/framework-entity/src/main/java/com/usoftchina/uas/entity/util/EntityStructureUtils.java

@@ -15,7 +15,7 @@ import java.util.List;
  * @author yingp
  * @date 2019/8/25
  */
-public class EntityUtils {
+public class EntityStructureUtils {
 
     public static List<String> makeCreateTableClause(EntityMetadata entity) {
         List<String> clauses = new ArrayList<>(1);

+ 3 - 0
framework/framework-plugin/build.gradle

@@ -0,0 +1,3 @@
+dependencies {
+    compile project(':framework:framework-form')
+}

+ 238 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/AbstractPluginManager.java

@@ -0,0 +1,238 @@
+package com.usoftchina.uas.plugin;
+
+import com.usoftchina.uas.plugin.json.JsonPluginDescriptorFinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * @author yingp
+ * @date 2018/7/31
+ */
+public abstract class AbstractPluginManager implements PluginManager {
+
+    protected static final Logger log = LoggerFactory.getLogger(AbstractPluginManager.class);
+
+    private PluginRepository pluginRepository;
+
+    private PluginFactory pluginFactory;
+
+    private PluginDescriptorFinder pluginDescriptorFinder;
+
+    private Map<String, PluginRunner> runners;
+
+    private List<PluginStateListener> pluginStateListeners;
+
+    public AbstractPluginManager(Path pluginInstallPath, PluginFactory pluginFactory) {
+        this.pluginRepository = new DefaultPluginRepository(pluginInstallPath.toAbsolutePath());
+        this.pluginFactory = pluginFactory;
+        this.pluginDescriptorFinder = new JsonPluginDescriptorFinder();
+    }
+
+    @Override
+    public synchronized void addPluginStateListener(PluginStateListener listener) {
+        if (null == pluginStateListeners) {
+            pluginStateListeners = new ArrayList<>();
+        }
+        pluginStateListeners.add(listener);
+    }
+
+    private synchronized void changePluginState(PluginInstance instance, PluginState newState, boolean fireEvent) {
+        PluginState oldState = instance.getPluginState();
+        instance.setPluginState(newState);
+        // fire event
+        if (fireEvent && null != pluginStateListeners) {
+            PluginStateEvent event = new PluginStateEvent(this, this, instance, oldState);
+            pluginStateListeners.forEach(listener -> listener.pluginStateChanged(event));
+        }
+    }
+
+    @Override
+    public List<PluginInstance> getPlugins() {
+        if (null != runners) {
+            return runners.entrySet().stream().map(entry -> entry.getValue().getPluginInstance())
+                    .collect(Collectors.toList());
+        }
+        return null;
+    }
+
+    @Override
+    public PluginInstance getPlugin(String name) {
+        if (runners.containsKey(name)) {
+            return runners.get(name).getPluginInstance();
+        }
+        return null;
+    }
+
+    private void startPlugin(PluginDescriptor descriptor) throws PluginException {
+        try {
+            PluginRunner runner = loadPlugin(descriptor);
+            if (null != runner) {
+                if (descriptor.isEnabled()) {
+                    runner.start();
+                    log.info("Plugin {} started", descriptor.getName());
+                    changePluginState(runner.getPluginInstance(), PluginState.STARTED, false);
+                } else {
+                    log.info("Plugin {} disabled", descriptor.getName());
+                    changePluginState(runner.getPluginInstance(), PluginState.DISABLED, false);
+                }
+            }
+        } catch (Exception e) {
+            log.error(descriptor.getName() + " start error", e);
+            throw new PluginException(e);
+        }
+    }
+
+    @Override
+    public void startPlugins() throws PluginException {
+        List<PluginDescriptor> descriptors = pluginRepository.listPlugins();
+        if (!CollectionUtils.isEmpty(descriptors)) {
+            runners = new ConcurrentHashMap<>(descriptors.size());
+            for (PluginDescriptor descriptor : descriptors) {
+                startPlugin(descriptor);
+            }
+        }
+    }
+
+    private PluginRunner loadPlugin(PluginDescriptor descriptor) throws Exception {
+        ClassLoader pluginClassLoader = new PluginClassLoader(new URL(descriptor.getUrl()), getClass().getClassLoader());
+        PluginInstance instance = new PluginInstance(this, descriptor, pluginClassLoader);
+        PluginRunner runner = pluginFactory.create(instance);
+        if (null != runner) {
+            if (null == runners) {
+                runners = new ConcurrentHashMap<>(1);
+            }
+            runners.put(descriptor.getName(), runner);
+        }
+        return runner;
+    }
+
+    @Override
+    public void enablePlugin(String name) throws PluginException {
+        if (null != runners && runners.containsKey(name)) {
+            PluginRunner runner = runners.get(name);
+            if (PluginState.DISABLED == runner.getPluginInstance().getPluginState()) {
+                try {
+                    runner.start();
+                    log.info("Plugin {} started", name);
+                    changePluginState(runner.getPluginInstance(), PluginState.STARTED, true);
+                    // 持久化启用状态
+                    pluginRepository.enablePlugin(runner.getPluginInstance().getPluginDescriptor());
+                } catch (Exception e) {
+                    log.error(name + " start error", e);
+                    throw new PluginException(e);
+                }
+            }
+        }
+    }
+
+    private void stopPlugin(PluginRunner runner) throws PluginException {
+        PluginDescriptor descriptor = runner.getPluginInstance().getPluginDescriptor();
+        try {
+            runner.stop();
+            log.info("Plugin {} stopped", descriptor.getName());
+            changePluginState(runner.getPluginInstance(), PluginState.STOPPED, false);
+        } catch (Exception e) {
+            log.error(descriptor.getName() + " stop error", e);
+            throw new PluginException(e);
+        }
+    }
+
+    @Override
+    public void stopPlugins() throws PluginException {
+        if (null != runners) {
+            for (String name : runners.keySet()) {
+                stopPlugin(runners.get(name));
+            }
+        }
+    }
+
+    @Override
+    public void disablePlugin(String name) throws PluginException {
+        if (null != runners && runners.containsKey(name)) {
+            PluginRunner runner = runners.get(name);
+            if (PluginState.STARTED == runner.getPluginInstance().getPluginState()) {
+                try {
+                    runner.stop();
+                    log.info("Plugin {} stopped", name);
+                    changePluginState(runner.getPluginInstance(), PluginState.DISABLED, true);
+                    // 持久化禁用状态
+                    pluginRepository.disablePlugin(runner.getPluginInstance().getPluginDescriptor());
+                } catch (Exception e) {
+                    log.error(name + " stop error", e);
+                    throw new PluginException(e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void installPlugin(Path pluginPath) throws PluginException {
+        PluginDescriptor descriptor = pluginDescriptorFinder.find(pluginPath);
+        if (null != descriptor) {
+            installPlugin(pluginPath, descriptor);
+        }
+    }
+
+    @Override
+    public void installPlugin(Path pluginPath, PluginDescriptor descriptor) throws PluginException {
+        try {
+            PluginRunner runner = loadPlugin(descriptor);
+            if (null != runner) {
+                pluginRepository.savePluginDescriptor(descriptor);
+                log.info("Plugin {} added", descriptor.getName());
+                runner.start();
+                log.info("Plugin {} started", descriptor.getName());
+                runner.install();
+                log.info("Plugin {} installed", descriptor.getName());
+            }
+        } catch (Exception e) {
+            log.error(descriptor.getName() + " start error", e);
+            throw new PluginException(e);
+        }
+    }
+
+    @Override
+    public void installPlugin(byte[] fileBytes, String fileName) throws PluginException {
+        Path pluginFile = pluginRepository.savePluginFile(fileBytes, fileName);
+        if (null != pluginFile) {
+            installPlugin(pluginFile);
+        }
+    }
+
+    @Override
+    public void installPlugin(byte[] fileBytes, String fileName, PluginDescriptor descriptor) throws PluginException {
+        Path pluginFile = pluginRepository.savePluginFile(fileBytes, fileName);
+        if (null != pluginFile) {
+            installPlugin(pluginFile, descriptor);
+        }
+    }
+
+    @Override
+    public void uninstallPlugin(String name) throws PluginException {
+        if (null != runners && runners.containsKey(name)) {
+            PluginRunner runner = runners.get(name);
+            try {
+                runner.uninstall();
+                log.info("Plugin {} uninstalled", name);
+                runner.stop();
+                log.info("Plugin {} stopped ", name);
+                pluginRepository.deletePlugin(runner.getPluginInstance().getPluginDescriptor());
+                log.info("Plugin {} removed ", name);
+                changePluginState(runner.getPluginInstance(), PluginState.REMOVED, true);
+                runners.remove(name);
+            } catch (Exception e) {
+                log.error(name + " uninstall error", e);
+                throw new PluginException(e);
+            }
+        }
+    }
+}

+ 94 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/DefaultPluginRepository.java

@@ -0,0 +1,94 @@
+package com.usoftchina.uas.plugin;
+
+import com.usoftchina.uas.entity.util.EntityDelete;
+import com.usoftchina.uas.entity.util.EntityInsert;
+import com.usoftchina.uas.entity.util.EntityQuery;
+import com.usoftchina.uas.entity.util.EntityUpdate;
+import com.usoftchina.uas.util.FileUtils;
+import com.usoftchina.uas.util.Record;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.CollectionUtils;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author yingp
+ * @date 2018/7/31
+ */
+public class DefaultPluginRepository implements PluginRepository {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultPluginRepository.class);
+    /**
+     * 插件安装目录
+     */
+    private final Path pluginInstallPath;
+
+    public DefaultPluginRepository(Path pluginInstallPath) {
+        this.pluginInstallPath = pluginInstallPath;
+    }
+
+    @Override
+    public List<PluginDescriptor> listPlugins() {
+        List<Record> records = EntityQuery.selectAll("BASE_PLUGIN").where();
+        if (!CollectionUtils.isEmpty(records)) {
+            return records.stream().map(PluginDescriptor::fromRecord).collect(Collectors.toList());
+        }
+        return null;
+    }
+
+    @Override
+    public void savePluginDescriptor(PluginDescriptor descriptor) {
+        Record record = descriptor.toRecord();
+        if (null == descriptor.getId()) {
+            EntityInsert.insert(record, "BASE_PLUGIN");
+        } else {
+            EntityUpdate.update(record, "BASE_PLUGIN");
+        }
+    }
+
+    @Override
+    public Path savePluginFile(byte[] fileBytes, String fileName) {
+        try {
+            FileUtils.createDirectory(pluginInstallPath);
+            Path pluginPath = pluginInstallPath.resolve(fileName);
+            FileUtils.createFile(pluginPath);
+            Files.write(pluginPath, fileBytes);
+            return pluginPath;
+        } catch (IOException e) {
+            log.error("Can not save plugin file " + fileName, e);
+        }
+        return null;
+    }
+
+    @Override
+    public void deletePlugin(PluginDescriptor descriptor) {
+        Record record = descriptor.toRecord();
+        EntityDelete.delete(record, "BASE_PLUGIN");
+        // 删除插件文件
+        try {
+            Path modulePath = Paths.get(new URL(descriptor.getUrl()).toURI());
+            FileUtils.deleteFile(modulePath);
+        } catch (Exception e) {
+            log.error("Can not delete plugin file", e);
+        }
+    }
+
+    @Override
+    public void enablePlugin(PluginDescriptor descriptor) {
+        descriptor.setEnabled(true);
+        savePluginDescriptor(descriptor);
+    }
+
+    @Override
+    public void disablePlugin(PluginDescriptor descriptor) {
+        descriptor.setEnabled(false);
+        savePluginDescriptor(descriptor);
+    }
+}

+ 44 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginClassLoader.java

@@ -0,0 +1,44 @@
+package com.usoftchina.uas.plugin;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * 每个插件有独立的ClassLoader
+ *
+ * @author yingp
+ * @date 2018/7/26
+ */
+public class PluginClassLoader extends URLClassLoader {
+
+    /**
+     * 是否优先从parentClassLoader查找
+     */
+    private boolean parentFirst;
+
+    /**
+     * @param url 插件URL
+     * @param parent
+     */
+    public PluginClassLoader(URL url, ClassLoader parent) {
+        this(url, parent, false);
+    }
+
+    /**
+     * @param urls 插件URL
+     * @param parent
+     */
+    public PluginClassLoader(URL[] urls, ClassLoader parent) {
+        this(urls, parent, false);
+    }
+
+    public PluginClassLoader(URL url, ClassLoader parent, boolean parentFirst) {
+        this(new URL[]{url}, parent, parentFirst);
+    }
+
+    public PluginClassLoader(URL[] urls, ClassLoader parent, boolean parentFirst) {
+        super(urls, parent);
+        this.parentFirst = parentFirst;
+    }
+
+}

+ 113 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginDescriptor.java

@@ -0,0 +1,113 @@
+package com.usoftchina.uas.plugin;
+
+import com.usoftchina.uas.util.Record;
+
+/**
+ * @author yingp
+ * @date 2018/7/31
+ */
+public class PluginDescriptor {
+    private Long id;
+    /**
+     * 插件名(唯一)
+     */
+    private String name;
+    /**
+     * 版本
+     */
+    private String version;
+    /**
+     * 描述
+     */
+    private String description;
+    /**
+     * 插件地址
+     */
+    private String url;
+    /**
+     * 插件初始化启动类
+     */
+    private String runnerClass;
+    /**
+     * 是否启用
+     */
+    private boolean enabled;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getRunnerClass() {
+        return runnerClass;
+    }
+
+    public void setRunnerClass(String runnerClass) {
+        this.runnerClass = runnerClass;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public static PluginDescriptor fromRecord(Record record) {
+        PluginDescriptor descriptor = new PluginDescriptor();
+        descriptor.setId(record.getLong("PL_ID"));
+        descriptor.setName(record.getString("PL_NAME"));
+        descriptor.setDescription(record.getString("PL_DESC"));
+        descriptor.setVersion(record.getString("PL_VERSION"));
+        descriptor.setUrl(record.getString("PL_URL"));
+        descriptor.setRunnerClass(record.getString("PL_RUNNER"));
+        descriptor.setEnabled(record.getIntValue("PL_ENABLE") == 1);
+        return descriptor;
+    }
+
+    public Record toRecord() {
+        return new Record("PL_ID", id)
+                .set("PL_NAME", name)
+                .set("PL_DESC", description)
+                .set("PL_VERSION", version)
+                .set("PL_URL", url)
+                .set("PL_RUNNER", runnerClass)
+                .set("PL_ENABLE", enabled ? 1 : 0);
+    }
+}

+ 35 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginDescriptorFinder.java

@@ -0,0 +1,35 @@
+package com.usoftchina.uas.plugin;
+
+import com.usoftchina.uas.util.FileUtils;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+/**
+ * @author yingp
+ * @date 2018/8/7
+ */
+public interface PluginDescriptorFinder {
+
+    /**
+     * 通过插件下的配置文件查找PluginDescriptor
+     *
+     * @param pluginPath
+     * @return
+     */
+    PluginDescriptor find(Path pluginPath);
+
+    /**
+     * 没有配置文件的情况下,默认描述
+     *
+     * @param pluginPath
+     * @return
+     * @throws IOException
+     */
+    default PluginDescriptor getDefault(Path pluginPath) throws IOException {
+        PluginDescriptor descriptor = new PluginDescriptor();
+        descriptor.setUrl(FileUtils.toURL(pluginPath).toString());
+        descriptor.setName(pluginPath.toFile().getName());
+        return descriptor;
+    }
+}

+ 28 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginException.java

@@ -0,0 +1,28 @@
+package com.usoftchina.uas.plugin;
+
+/**
+ * @author yingp
+ * @date 2018/8/7
+ */
+public class PluginException extends Exception {
+
+    public PluginException() {
+        super();
+    }
+
+    public PluginException(String message) {
+        super(message);
+    }
+
+    public PluginException(Throwable cause) {
+        super(cause);
+    }
+
+    public PluginException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public PluginException(String message, Object... args) {
+        super(String.format(message, args));
+    }
+}

+ 30 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginFactory.java

@@ -0,0 +1,30 @@
+package com.usoftchina.uas.plugin;
+
+/**
+ * @author yingp
+ * @date 2018/8/1
+ */
+public interface PluginFactory {
+    /**
+     * 创建插件启动对象
+     *
+     * @param pluginInstance
+     * @return
+     */
+    PluginRunner create(PluginInstance pluginInstance);
+
+    /**
+     * 加载插件启动类
+     *
+     * @param pluginInstance
+     * @return
+     * @throws ClassNotFoundException
+     */
+    default Class<?> loadRunnerClass(PluginInstance pluginInstance) throws ClassNotFoundException {
+        String className = pluginInstance.getPluginDescriptor().getRunnerClass();
+        if (null != className) {
+            return pluginInstance.getPluginClassLoader().loadClass(className);
+        }
+        return null;
+    }
+}

+ 42 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginInstance.java

@@ -0,0 +1,42 @@
+package com.usoftchina.uas.plugin;
+
+/**
+ * 插件运行实例
+ *
+ * @author yingp
+ * @date 2018/7/31
+ */
+public class PluginInstance {
+
+    private final PluginManager pluginManager;
+    private final PluginDescriptor pluginDescriptor;
+    private final ClassLoader pluginClassLoader;
+    private PluginState pluginState;
+
+    public PluginInstance(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader pluginClassLoader) {
+        this.pluginManager = pluginManager;
+        this.pluginDescriptor = pluginDescriptor;
+        this.pluginClassLoader = pluginClassLoader;
+        this.pluginState = PluginState.READY;
+    }
+
+    public PluginManager getPluginManager() {
+        return pluginManager;
+    }
+
+    public PluginDescriptor getPluginDescriptor() {
+        return pluginDescriptor;
+    }
+
+    public ClassLoader getPluginClassLoader() {
+        return pluginClassLoader;
+    }
+
+    public PluginState getPluginState() {
+        return pluginState;
+    }
+
+    public void setPluginState(PluginState pluginState) {
+        this.pluginState = pluginState;
+    }
+}

+ 115 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginManager.java

@@ -0,0 +1,115 @@
+package com.usoftchina.uas.plugin;
+
+import java.nio.file.Path;
+import java.util.List;
+
+/**
+ * 管理所有插件
+ *
+ * @author yingp
+ * @date 2018/7/31
+ */
+public interface PluginManager {
+
+    /**
+     * 获取所有插件实例
+     *
+     * @return
+     */
+    List<PluginInstance> getPlugins();
+
+    /**
+     * 按插件名获取插件
+     *
+     * @param name
+     * @return
+     */
+    PluginInstance getPlugin(String name);
+
+    /**
+     * 启动所有(启用的)插件
+     *
+     * @throws PluginException
+     */
+    void startPlugins() throws PluginException;
+
+    /**
+     * 启用(启动)插件
+     *
+     * @param name
+     * @throws PluginException
+     */
+    void enablePlugin(String name) throws PluginException;
+
+    /**
+     * 停止所有插件
+     *
+     * @throws PluginException
+     */
+    void stopPlugins() throws PluginException;
+
+    /**
+     * 禁用(停止)插件
+     *
+     * @param name
+     * @throws PluginException
+     */
+    void disablePlugin(String name) throws PluginException;
+
+    /**
+     * 安装插件
+     * <pre>
+     *     插件的描述信息打包在jar文件里面
+     * </pre>
+     *
+     * @param pluginPath
+     * @throws PluginException
+     */
+    void installPlugin(Path pluginPath) throws PluginException;
+
+    /**
+     * 安装插件
+     *
+     * @param pluginPath
+     * @param descriptor 指定插件的描述信息
+     * @throws PluginException
+     */
+    void installPlugin(Path pluginPath, PluginDescriptor descriptor) throws PluginException;
+
+    /**
+     * 上传插件文件,并安装
+     * <pre>
+     *     插件的描述信息打包在jar文件里面
+     * </pre>
+     *
+     * @param fileBytes
+     * @param fileName
+     * @throws PluginException
+     */
+    void installPlugin(byte[] fileBytes, String fileName) throws PluginException;
+
+    /**
+     * 上传插件文件,并安装
+     *
+     * @param fileBytes
+     * @param fileName
+     * @param descriptor 指定插件的描述信息
+     * @throws PluginException
+     */
+    void installPlugin(byte[] fileBytes, String fileName, PluginDescriptor descriptor) throws PluginException;
+
+    /**
+     * 卸载插件
+     *
+     * @param name
+     * @throws PluginException
+     */
+    void uninstallPlugin(String name) throws PluginException;
+
+    /**
+     * 添加监听
+     *
+     * @param listener
+     */
+    void addPluginStateListener(PluginStateListener listener);
+}

+ 55 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginRepository.java

@@ -0,0 +1,55 @@
+package com.usoftchina.uas.plugin;
+
+import java.nio.file.Path;
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2018/7/31
+ */
+public interface PluginRepository {
+
+    /**
+     * 列出所有插件
+     *
+     * @return
+     */
+    List<PluginDescriptor> listPlugins();
+
+    /**
+     * 保存插件配置
+     *
+     * @param descriptor
+     */
+    void savePluginDescriptor(PluginDescriptor descriptor);
+
+    /**
+     * 保存插件文件
+     *
+     * @param fileBytes
+     * @param fileName
+     * @return
+     */
+    Path savePluginFile(byte[] fileBytes, String fileName);
+
+    /**
+     * 删除插件
+     *
+     * @param descriptor
+     */
+    void deletePlugin(PluginDescriptor descriptor);
+
+    /**
+     * 启用插件
+     *
+     * @param descriptor
+     */
+    void enablePlugin(PluginDescriptor descriptor);
+
+    /**
+     * 禁用插件
+     *
+     * @param descriptor
+     */
+    void disablePlugin(PluginDescriptor descriptor);
+}

+ 38 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginRunner.java

@@ -0,0 +1,38 @@
+package com.usoftchina.uas.plugin;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author yingp
+ * @date 2018/8/1
+ */
+public class PluginRunner {
+    private static final Logger log = LoggerFactory.getLogger(PluginRunner.class);
+
+    private final PluginInstance pluginInstance;
+
+    public PluginRunner(PluginInstance pluginInstance) {
+        this.pluginInstance = pluginInstance;
+    }
+
+    public PluginInstance getPluginInstance() {
+        return pluginInstance;
+    }
+
+    public void install() throws PluginException {
+        log.info("Installing plugin {}", pluginInstance.getPluginDescriptor().getName());
+    }
+
+    public void start() throws PluginException {
+        log.info("Starting plugin {}", pluginInstance.getPluginDescriptor().getName());
+    }
+
+    public void stop() throws PluginException {
+        log.info("Stopping plugin {}", pluginInstance.getPluginDescriptor().getName());
+    }
+
+    public void uninstall() throws PluginException {
+        log.info("Removing plugin {}", pluginInstance.getPluginDescriptor().getName());
+    }
+}

+ 28 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginState.java

@@ -0,0 +1,28 @@
+package com.usoftchina.uas.plugin;
+
+/**
+ * @author yingp
+ * @date 2018/8/7
+ */
+public enum PluginState {
+    /**
+     * 已置备状态
+     */
+    READY,
+    /**
+     * 已启用状态
+     */
+    STARTED,
+    /**
+     * 已禁用状态
+     */
+    DISABLED,
+    /**
+     * 已停止状态
+     */
+    STOPPED,
+    /**
+     * 移除状态
+     */
+    REMOVED;
+}

+ 39 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginStateEvent.java

@@ -0,0 +1,39 @@
+package com.usoftchina.uas.plugin;
+
+import java.util.EventObject;
+
+/**
+ * @author yingp
+ * @date 2018/8/7
+ */
+public class PluginStateEvent extends EventObject {
+
+    private final PluginManager pluginManager;
+
+    private final PluginInstance pluginInstance;
+
+    private final PluginState oldState;
+
+    public PluginStateEvent(Object source, PluginManager pluginManager, PluginInstance pluginInstance, PluginState oldState) {
+        super(source);
+        this.pluginManager = pluginManager;
+        this.pluginInstance = pluginInstance;
+        this.oldState = oldState;
+    }
+
+    public PluginManager getPluginManager() {
+        return pluginManager;
+    }
+
+    public PluginInstance getPluginInstance() {
+        return pluginInstance;
+    }
+
+    public PluginState getOldState() {
+        return oldState;
+    }
+
+    public PluginState getState() {
+        return getPluginInstance().getPluginState();
+    }
+}

+ 17 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/PluginStateListener.java

@@ -0,0 +1,17 @@
+package com.usoftchina.uas.plugin;
+
+import java.util.EventListener;
+
+/**
+ * @author yingp
+ * @date 2018/8/7
+ */
+public interface PluginStateListener extends EventListener {
+
+    /**
+     * 监听插件状态改变事件
+     *
+     * @param event
+     */
+    void pluginStateChanged(PluginStateEvent event);
+}

+ 73 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/json/JsonPluginDescriptorFinder.java

@@ -0,0 +1,73 @@
+package com.usoftchina.uas.plugin.json;
+
+import com.usoftchina.uas.plugin.PluginDescriptor;
+import com.usoftchina.uas.plugin.PluginDescriptorFinder;
+import com.usoftchina.uas.util.FileUtils;
+import com.usoftchina.uas.util.JsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * @author yingp
+ * @date 2019/10/8
+ */
+public class JsonPluginDescriptorFinder implements PluginDescriptorFinder {
+
+    private static final Logger log = LoggerFactory.getLogger(JsonPluginDescriptorFinder.class);
+
+    private static final String DEFAULT_PLUGIN_FILE = "plugin.json";
+
+    private String pluginFile;
+
+    public JsonPluginDescriptorFinder() {
+        this(DEFAULT_PLUGIN_FILE);
+    }
+
+    public JsonPluginDescriptorFinder(String pluginFile) {
+        this.pluginFile = pluginFile;
+    }
+
+    @Override
+    public PluginDescriptor find(Path pluginPath) {
+        if (isApplicable(pluginPath)) {
+            Path pluginFilePath = getPluginFilePath(pluginPath, pluginFile);
+            if (null != pluginFilePath && Files.exists(pluginFilePath)) {
+                try (InputStream input = Files.newInputStream(pluginFilePath)) {
+                    PluginDescriptor descriptor = JsonUtils.load(input, PluginDescriptor.class);
+                    descriptor.setUrl(FileUtils.toURL(pluginPath).toString());
+                    return descriptor;
+                } catch (IOException e) {
+                    log.error("can not resolve " + pluginFilePath, e);
+                }
+            } else {
+                log.warn("No plugin file " + pluginFilePath.toUri() + ", using default");
+                try {
+                    return getDefault(pluginPath);
+                } catch (IOException e) {
+                    log.error("can not resolve " + pluginPath, e);
+                }
+            }
+        } else {
+            log.error("illegal plugin path " + pluginPath);
+        }
+        return null;
+    }
+
+    private boolean isApplicable(Path pluginPath) {
+        return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isJarFile(pluginPath));
+    }
+
+    protected Path getPluginFilePath(Path pluginPath, String pluginFile) {
+        try {
+            return FileUtils.getPath(pluginPath, pluginFile);
+        } catch (IOException e) {
+            log.error("Can not get " + pluginFile, e);
+            return null;
+        }
+    }
+}

+ 31 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/spring/DefaultPluginRunner.java

@@ -0,0 +1,31 @@
+package com.usoftchina.uas.plugin.spring;
+
+import com.usoftchina.uas.plugin.PluginInstance;
+import com.usoftchina.uas.util.JarUtils;
+import org.springframework.context.ApplicationContext;
+
+import java.util.Set;
+
+/**
+ * @author yingp
+ * @date 2018/8/1
+ */
+public class DefaultPluginRunner extends SpringPluginRunner {
+
+    public DefaultPluginRunner(PluginInstance pluginInstance, ApplicationContext parentContext) {
+        super(pluginInstance, parentContext);
+    }
+
+    @Override
+    protected String[] basePackages() {
+        try {
+            Set<String> packageNames = JarUtils.getPackages(getPluginInstance().getPluginDescriptor().getUrl());
+            if (!packageNames.isEmpty()) {
+                return packageNames.toArray(new String[]{});
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new String[0];
+    }
+}

+ 52 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/spring/SpringPluginFactory.java

@@ -0,0 +1,52 @@
+package com.usoftchina.uas.plugin.spring;
+
+import com.usoftchina.uas.plugin.PluginFactory;
+import com.usoftchina.uas.plugin.PluginInstance;
+import com.usoftchina.uas.plugin.PluginRunner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * @author yingp
+ * @date 2019/3/25
+ */
+public class SpringPluginFactory implements PluginFactory {
+    private static final Logger log = LoggerFactory.getLogger(SpringPluginFactory.class);
+    private final ApplicationContext parentContext;
+
+    public SpringPluginFactory(ApplicationContext parentContext) {
+        this.parentContext = parentContext;
+    }
+
+    @Override
+    public PluginRunner create(PluginInstance pluginInstance) {
+        String className = pluginInstance.getPluginDescriptor().getRunnerClass();
+        Class<?> runnerClass;
+        try {
+            runnerClass = loadRunnerClass(pluginInstance);
+            if (null == runnerClass) {
+                // 没有插件描述文件,或没有配置启动类情况下,使用默认启动类
+                return new DefaultPluginRunner(pluginInstance, parentContext);
+            }
+        } catch (ClassNotFoundException e) {
+            log.error("no class found " + className, e);
+            return null;
+        }
+        try {
+            Constructor<?> constructor = runnerClass.getConstructor(PluginInstance.class, ApplicationContext.class);
+            return (PluginRunner) constructor.newInstance(pluginInstance, parentContext);
+        } catch (Exception e) {
+            log.warn(className + " is not a spring module");
+            try {
+                Constructor<?> constructor = runnerClass.getConstructor(PluginInstance.class);
+                return (PluginRunner) constructor.newInstance(pluginInstance);
+            } catch (Exception e1) {
+                log.error("can not create instance for " + className, e1);
+                return null;
+            }
+        }
+    }
+}

+ 41 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/spring/SpringPluginManager.java

@@ -0,0 +1,41 @@
+package com.usoftchina.uas.plugin.spring;
+
+import com.usoftchina.uas.entity.EntityInitializedEvent;
+import com.usoftchina.uas.plugin.AbstractPluginManager;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.scheduling.annotation.Async;
+
+import java.nio.file.Path;
+
+/**
+ * @author yingp
+ * @date 2019/3/25
+ */
+@Async
+public class SpringPluginManager extends AbstractPluginManager implements ApplicationListener<EntityInitializedEvent>, DisposableBean {
+
+    private boolean started;
+
+    public SpringPluginManager(Path pluginInstallPath, ApplicationContext applicationContext) {
+        super(pluginInstallPath, new SpringPluginFactory(applicationContext));
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        stopPlugins();
+    }
+
+    @Override
+    public void onApplicationEvent(EntityInitializedEvent event) {
+        if (!started) {
+            try {
+                started = true;
+                startPlugins();
+            } catch (Exception e) {
+                log.error("Start plugins error", e);
+            }
+        }
+    }
+}

+ 69 - 0
framework/framework-plugin/src/main/java/com/usoftchina/uas/plugin/spring/SpringPluginRunner.java

@@ -0,0 +1,69 @@
+package com.usoftchina.uas.plugin.spring;
+
+import com.usoftchina.uas.plugin.PluginRunner;
+import com.usoftchina.uas.plugin.PluginException;
+import com.usoftchina.uas.plugin.PluginInstance;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/**
+ * @author yingp
+ * @date 2018/8/1
+ */
+public abstract class SpringPluginRunner extends PluginRunner {
+
+    private ApplicationContext applicationContext;
+    private ApplicationContext parentContext;
+
+    public SpringPluginRunner(PluginInstance pluginInstance, ApplicationContext parentContext) {
+        super(pluginInstance);
+        this.parentContext = parentContext;
+    }
+
+    /**
+     * spring 上下文
+     *
+     * @return
+     */
+    private ApplicationContext createApplicationContext() {
+        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
+        if (null != parentContext) {
+            applicationContext.setParent(parentContext);
+        }
+        applicationContext.setClassLoader(getPluginInstance().getPluginClassLoader());
+        String[] basePackages = basePackages();
+        if (basePackages.length > 0) {
+            applicationContext.scan(basePackages);
+        }
+        applicationContext.refresh();
+        return applicationContext;
+    }
+
+    /**
+     * 指定扫描包
+     *
+     * @return
+     */
+    protected abstract String[] basePackages();
+
+    public ApplicationContext getApplicationContext() {
+        if (applicationContext == null) {
+            applicationContext = createApplicationContext();
+        }
+        return applicationContext;
+    }
+
+    @Override
+    public void start() throws PluginException {
+        super.start();
+        this.applicationContext = createApplicationContext();
+    }
+
+    @Override
+    public void stop() throws PluginException {
+        super.stop();
+        if (null != applicationContext) {
+            ((AnnotationConfigApplicationContext) applicationContext).stop();
+        }
+    }
+}

+ 49 - 0
framework/framework-plugin/src/main/resources/metadata/Plugin.form.json

@@ -0,0 +1,49 @@
+[{
+  "id": "base_plugin",
+  "name": "插件",
+  "idColumnName": "PL_ID",
+  "entities": [
+    {
+      "name": "基本信息",
+      "tableName": "BASE_PLUGIN",
+      "type": "head",
+      "fields": [
+        {
+          "name": "名称",
+          "columnName": "PL_NAME",
+          "type": "text",
+          "length": 50
+        },
+        {
+          "name": "版本",
+          "columnName": "PL_VERSION",
+          "type": "text",
+          "length": 50
+        },
+        {
+          "name": "描述",
+          "columnName": "PL_DESC",
+          "type": "textarea",
+          "length": 255
+        },
+        {
+          "name": "文件路径",
+          "columnName": "PL_URL",
+          "type": "text",
+          "length": 255
+        },
+        {
+          "name": "启动程序",
+          "columnName": "PL_RUNNER",
+          "type": "text",
+          "length": 255
+        },
+        {
+          "name": "启用",
+          "columnName": "PL_ENABLE",
+          "type": "checkbox"
+        }
+      ]
+    }
+  ]
+}]

+ 1 - 1
framework/framework-app/build.gradle → framework/framework-web/build.gradle

@@ -1,6 +1,6 @@
 dependencies {
     compile project(':framework:framework-security')
     compile project(':framework:framework-domain')
-    compile 'org.springframework:spring-web'
+    compile 'org.springframework:spring-webmvc'
     compileOnly 'javax.servlet:javax.servlet-api'
 }

+ 1 - 1
framework/framework-app/src/main/java/com/usoftchina/uas/app/view/HomeController.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/base/HomeController.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.app.view;
+package com.usoftchina.uas.web.base;
 
 import com.usoftchina.uas.view.virtual.ViewController;
 import org.springframework.stereotype.Component;

+ 1 - 1
framework/framework-app/src/main/java/com/usoftchina/uas/app/view/MainApplication.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/base/MainApplication.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.app.view;
+package com.usoftchina.uas.web.base;
 
 import com.usoftchina.uas.security.SecurityApplication;
 import com.usoftchina.uas.view.ViewMetadata;

+ 1 - 1
framework/framework-app/src/main/java/com/usoftchina/uas/app/view/MainController.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/base/MainController.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.app.view;
+package com.usoftchina.uas.web.base;
 
 import com.usoftchina.uas.security.SecurityApplication;
 import com.usoftchina.uas.view.ViewMetadata;

+ 1 - 1
framework/framework-app/src/main/java/com/usoftchina/uas/app/view/MainMenuController.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/base/MainMenuController.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.app.view;
+package com.usoftchina.uas.web.base;
 
 import com.usoftchina.uas.domain.DomainMetadata;
 import com.usoftchina.uas.domain.DomainMetadataService;

+ 1 - 1
applications/starter/src/main/java/com/usoftchina/uas/config/WebConfig.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/config/WebConfig.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.config;
+package com.usoftchina.uas.web.config;
 
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.CorsRegistry;

+ 6 - 8
framework/framework-app/src/main/java/com/usoftchina/uas/app/web/WebEventAdapter.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/controller/WebController.java

@@ -1,14 +1,12 @@
-package com.usoftchina.uas.app.web;
+package com.usoftchina.uas.web.controller;
 
-import com.usoftchina.uas.app.view.MainApplication;
-import com.usoftchina.uas.app.web.dto.EventParams;
-import com.usoftchina.uas.app.web.dto.ViewParams;
 import com.usoftchina.uas.base.Result;
 import com.usoftchina.uas.exception.ExceptionCode;
-import com.usoftchina.uas.view.ViewMetadata;
-import com.usoftchina.uas.view.ViewMetadataService;
 import com.usoftchina.uas.view.virtual.Application;
 import com.usoftchina.uas.view.virtual.View;
+import com.usoftchina.uas.web.base.MainApplication;
+import com.usoftchina.uas.web.dto.EventParams;
+import com.usoftchina.uas.web.dto.ViewParams;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.transaction.annotation.Transactional;
@@ -26,9 +24,9 @@ import java.util.Map;
  */
 @RestController
 @RequestMapping(path = "/web")
-public class WebEventAdapter {
+public class WebController {
 
-    private final Logger logger = LoggerFactory.getLogger(WebEventAdapter.class);
+    private final Logger logger = LoggerFactory.getLogger(WebController.class);
     private final static String WEB_UID_HEADER = "uuid";
 
     /**

+ 2 - 1
framework/framework-app/src/main/java/com/usoftchina/uas/app/web/dto/EventParams.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/dto/EventParams.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.app.web.dto;
+package com.usoftchina.uas.web.dto;
 
 import java.io.Serializable;
 import java.util.Arrays;
@@ -45,3 +45,4 @@ public class EventParams implements Serializable {
                 '}';
     }
 }
+

+ 1 - 1
framework/framework-app/src/main/java/com/usoftchina/uas/app/web/dto/ViewParams.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/dto/ViewParams.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.app.web.dto;
+package com.usoftchina.uas.web.dto;
 
 import java.io.Serializable;
 

+ 1 - 1
applications/starter/src/main/java/com/usoftchina/uas/error/DefaultErrorConfig.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/error/DefaultErrorConfig.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.error;
+package com.usoftchina.uas.web.error;
 
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;

+ 1 - 1
applications/starter/src/main/java/com/usoftchina/uas/error/GlobalExceptionHandler.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/error/GlobalExceptionHandler.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.error;
+package com.usoftchina.uas.web.error;
 
 import com.usoftchina.uas.base.Result;
 import com.usoftchina.uas.exception.BizException;

+ 1 - 1
applications/starter/src/main/java/com/usoftchina/uas/error/ServletErrorUtils.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/error/ServletErrorUtils.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas.error;
+package com.usoftchina.uas.web.error;
 
 import com.usoftchina.uas.base.Result;
 import com.usoftchina.uas.util.JsonUtils;

+ 3 - 2
applications/starter/src/main/java/com/usoftchina/uas/error/UnCaughtErrorFilter.java → framework/framework-web/src/main/java/com/usoftchina/uas/web/error/UnCaughtErrorFilter.java

@@ -1,11 +1,12 @@
-package com.usoftchina.uas.error;
+package com.usoftchina.uas.web.error;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.web.filter.OncePerRequestFilter;
 import org.springframework.web.util.NestedServletException;
 
-import javax.servlet.*;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;

+ 0 - 0
framework/framework-app/src/main/resources/metadata/Home.form.json → framework/framework-web/src/main/resources/metadata/Home.form.json


+ 0 - 3
framework/framework-app/src/main/resources/metadata/Main.form.json → framework/framework-web/src/main/resources/metadata/Main.form.json

@@ -3,9 +3,6 @@
     "id": "base_main",
     "name": "主页",
     "type": "dynamicForm",
-    "plugins": [
-      "com.usoftchina.uas.plugin.MainPlugin"
-    ],
     "views": [
       {
         "viewType": "form",

+ 0 - 0
framework/framework-app/src/main/resources/metadata/MainMenu.form.json → framework/framework-web/src/main/resources/metadata/MainMenu.form.json


+ 12 - 0
runtime/README.md

@@ -0,0 +1,12 @@
+## 服务运行
+
+### 项目结构
+
+```
+├─runtime
+│  │  
+│  ├─debug-starter----------------------------本地开发调试启动程序
+│  ├─saas-starter-----------------------------多租户模式微服务启动程序,支持插件扩展
+│  ├─standalone-starter-----------------------单客户启动程序,支持插件扩展
+│  │
+```

+ 3 - 1
applications/starter/build.gradle → runtime/debug-starter/build.gradle

@@ -1,9 +1,11 @@
 apply plugin: 'org.springframework.boot'
 
+archivesBaseName = 'uas-debug'
+
 dependencies {
     compile 'org.springframework.boot:spring-boot-starter-web'
     compile 'org.springframework.boot:spring-boot-starter-jdbc'
-    compile project(':framework:framework-app')
+    compile project(':framework:framework-web')
     compile project(':applications:scm:scm-base')
     compile project(':applications:scm:scm-purchase')
     compile "$ojdbc"

+ 24 - 0
runtime/debug-starter/src/main/java/com/usoftchina/uas/runtime/DebugStarter.java

@@ -0,0 +1,24 @@
+package com.usoftchina.uas.runtime;
+
+import com.usoftchina.uas.jdbc.support.EnableDynamicDataSource;
+import com.usoftchina.uas.metadata.MetadataScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * @author yingp
+ * @date 2019/8/22
+ */
+@SpringBootApplication(scanBasePackages = "com.usoftchina.uas")
+@EnableTransactionManagement
+@EnableAsync
+@EnableDynamicDataSource
+@MetadataScan(locations = "classpath*:metadata/**/*.*.json")
+public class DebugStarter {
+
+    public static void main(String[] args) {
+        SpringApplication.run(DebugStarter.class, args);
+    }
+}

+ 0 - 0
applications/starter/src/main/resources/application.yml → runtime/debug-starter/src/main/resources/application.yml


+ 1 - 1
applications/starter/src/main/resources/banner.txt → runtime/debug-starter/src/main/resources/banner.txt

@@ -1,4 +1,4 @@
-${AnsiColor.BRIGHT_YELLOW}
+${AnsiColor.BRIGHT_RED}
  _____  _____   _       ______
 |_   _||_   _| / \    .' ____ \
   | |    | |  / _ \   | (___ \_|

+ 0 - 0
applications/starter/src/main/resources/config/application-prod.yml → runtime/debug-starter/src/main/resources/config/application-prod.yml


+ 0 - 0
applications/starter/src/main/resources/config/application-test.yml → runtime/debug-starter/src/main/resources/config/application-test.yml


+ 82 - 0
runtime/linux/uas.sh

@@ -0,0 +1,82 @@
+#!/bin/sh
+
+APP_NAME=uas
+JAR_NAME=$APP_NAME\.jar
+PID=$APP_NAME\.pid
+
+usage() {
+    echo "Usage: sh uas.sh [start|stop|restart|status]"
+    exit 1
+}
+
+is_exist(){
+  pid=`ps -ef|grep $JAR_NAME|grep -v grep|awk '{print $2}' `
+  if [ -z "${pid}" ]; then
+   return 1
+  else
+    return 0
+  fi
+}
+
+start(){
+  is_exist
+  if [ $? -eq "0" ]; then
+    echo ">>> ${JAR_NAME} is already running PID=${pid} <<<"
+  else
+    nohup $JRE_HOME/bin/java -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx2048m -XX:NewSize=256m -XX:MaxNewSize=512m -XX:-UseGCOverheadLimit -XX:+UseConcMarkSweepGC -jar $JAR_NAME >/dev/null 2>&1 &
+    echo $! > $PID
+    echo ">>> start $JAR_NAME successed PID=$! <<<"
+   fi
+  }
+
+stop(){
+  #is_exist
+  pidf=$(cat $PID)
+  #echo "$pidf"
+  echo ">>> app PID = $pidf begin kill $pidf <<<"
+  kill $pidf
+  rm -rf $PID
+  sleep 2
+  is_exist
+  if [ $? -eq "0" ]; then
+    echo ">>> app 2 PID = $pid begin kill -9 $pid  <<<"
+    kill -9  $pid
+    sleep 2
+    echo ">>> $JAR_NAME process stopped <<<"
+  else
+    echo ">>> ${JAR_NAME} is not running <<<"
+  fi
+}
+
+status(){
+  is_exist
+  if [ $? -eq "0" ]; then
+    echo ">>> ${JAR_NAME} is running PID is ${pid} <<<"
+  else
+    echo ">>> ${JAR_NAME} is not running <<<"
+  fi
+}
+
+restart(){
+  stop
+  start
+}
+
+case "$1" in
+  "start")
+    start
+    ;;
+  "stop")
+    stop
+    ;;
+  "status")
+    status
+    ;;
+  "restart")
+    restart
+    ;;
+  *)
+    usage
+    ;;
+esac
+exit 0

+ 10 - 0
runtime/saas-starter/build.gradle

@@ -0,0 +1,10 @@
+apply plugin: 'org.springframework.boot'
+
+archivesBaseName = 'uas-saas'
+
+dependencies {
+    compile 'org.springframework.boot:spring-boot-starter-web'
+    compile 'org.springframework.boot:spring-boot-starter-jdbc'
+    compile project(':framework:framework-web')
+    compile "$ojdbc"
+}

+ 3 - 3
applications/starter/src/main/java/com/usoftchina/uas/ApplicationStarter.java → runtime/saas-starter/src/main/java/com/usoftchina/uas/runtime/SaasStarter.java

@@ -1,4 +1,4 @@
-package com.usoftchina.uas;
+package com.usoftchina.uas.runtime;
 
 import com.usoftchina.uas.jdbc.support.EnableDynamicDataSource;
 import com.usoftchina.uas.metadata.MetadataScan;
@@ -16,9 +16,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 @EnableAsync
 @EnableDynamicDataSource
 @MetadataScan(locations = "classpath*:metadata/**/*.*.json")
-public class ApplicationStarter {
+public class SaasStarter {
 
     public static void main(String[] args) {
-        SpringApplication.run(ApplicationStarter.class, args);
+        SpringApplication.run(SaasStarter.class, args);
     }
 }

+ 22 - 0
runtime/saas-starter/src/main/resources/application.yml

@@ -0,0 +1,22 @@
+spring:
+  datasource:
+    driver-class-name: oracle.jdbc.driver.OracleDriver
+    url: jdbc:oracle:thin:@10.1.81.2:1521:orcl
+    username: UAS_2
+    password: select!#%*(
+    hikari:
+      minimum-idle: 5
+      maximum-pool-size: 50
+      idle-timeout: 30000
+      max-lifetime: 1800000
+      connection-timeout: 30000
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+
+logging:
+  level:
+    com.usoftchina: trace
+
+server:
+  servlet:
+    context-path: /api

+ 10 - 0
runtime/saas-starter/src/main/resources/banner.txt

@@ -0,0 +1,10 @@
+${AnsiColor.BRIGHT_RED}
+ _____  _____   _       ______
+|_   _||_   _| / \    .' ____ \
+  | |    | |  / _ \   | (___ \_|
+  | '    ' | / ___ \   _.____`.
+   \ \__/ /_/ /   \ \_| \____) |
+    `.__.'|____| |____|\______.'
+
+@Copyright 2019 Shenzhen Usoft Information Technology Co., Ltd.
+${AnsiColor.DEFAULT}

+ 11 - 0
runtime/standalone-starter/build.gradle

@@ -0,0 +1,11 @@
+apply plugin: 'org.springframework.boot'
+
+archivesBaseName = 'uas'
+
+dependencies {
+    compile 'org.springframework.boot:spring-boot-starter-web'
+    compile 'org.springframework.boot:spring-boot-starter-jdbc'
+    compile project(':framework:framework-web')
+    compile project(':framework:framework-plugin')
+    compile "$ojdbc"
+}

+ 24 - 0
runtime/standalone-starter/src/main/java/com/usoftchina/uas/runtime/StandaloneStarter.java

@@ -0,0 +1,24 @@
+package com.usoftchina.uas.runtime;
+
+import com.usoftchina.uas.jdbc.support.EnableDynamicDataSource;
+import com.usoftchina.uas.metadata.MetadataScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * @author yingp
+ * @date 2019/8/22
+ */
+@SpringBootApplication(scanBasePackages = "com.usoftchina.uas")
+@EnableTransactionManagement
+@EnableAsync
+@EnableDynamicDataSource
+@MetadataScan(locations = "classpath*:metadata/**/*.*.json")
+public class StandaloneStarter {
+
+    public static void main(String[] args) {
+        SpringApplication.run(StandaloneStarter.class, args);
+    }
+}

+ 30 - 0
runtime/standalone-starter/src/main/java/com/usoftchina/uas/runtime/config/PluginConfig.java

@@ -0,0 +1,30 @@
+package com.usoftchina.uas.runtime.config;
+
+import com.usoftchina.uas.plugin.PluginManager;
+import com.usoftchina.uas.plugin.spring.SpringPluginManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.nio.file.Paths;
+
+/**
+ * @author yingp
+ * @date 2019/3/26
+ */
+@Configuration
+public class PluginConfig {
+
+    @Autowired
+    private ApplicationContext applicationContext;
+
+    @Value("${uas.plugins.path:plugins}")
+    public String pluginsPath;
+
+    @Bean
+    public PluginManager pluginManager() {
+        return new SpringPluginManager(Paths.get(pluginsPath), applicationContext);
+    }
+}

+ 22 - 0
runtime/standalone-starter/src/main/resources/application.yml

@@ -0,0 +1,22 @@
+spring:
+  datasource:
+    driver-class-name: oracle.jdbc.driver.OracleDriver
+    url: jdbc:oracle:thin:@10.1.81.2:1521:orcl
+    username: UAS_2
+    password: select!#%*(
+    hikari:
+      minimum-idle: 5
+      maximum-pool-size: 50
+      idle-timeout: 30000
+      max-lifetime: 1800000
+      connection-timeout: 30000
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+
+logging:
+  level:
+    com.usoftchina: trace
+
+server:
+  servlet:
+    context-path: /api

+ 10 - 0
runtime/standalone-starter/src/main/resources/banner.txt

@@ -0,0 +1,10 @@
+${AnsiColor.BRIGHT_RED}
+ _____  _____   _       ______
+|_   _||_   _| / \    .' ____ \
+  | |    | |  / _ \   | (___ \_|
+  | '    ' | / ___ \   _.____`.
+   \ \__/ /_/ /   \ \_| \____) |
+    `.__.'|____| |____|\______.'
+
+@Copyright 2019 Shenzhen Usoft Information Technology Co., Ltd.
+${AnsiColor.DEFAULT}

+ 10 - 4
settings.gradle

@@ -17,14 +17,20 @@ include 'framework:framework-entity'
 findProject(':framework:framework-entity')?.name = 'framework-entity'
 include 'framework:framework-service'
 findProject(':framework:framework-service')?.name = 'framework-service'
-include 'applications:starter'
-findProject(':applications:starter')?.name = 'starter'
+include 'runtime:debug-starter'
+findProject(':runtime:debug-starter')?.name = 'debug-starter'
 include 'framework:framework-domain'
 findProject(':framework:framework-domain')?.name = 'framework-domain'
-include 'framework:framework-app'
-findProject(':framework:framework-app')?.name = 'framework-app'
+include 'framework:framework-web'
+findProject(':framework:framework-web')?.name = 'framework-web'
 include 'framework:framework-view'
 findProject(':framework:framework-view')?.name = 'framework-view'
 include 'framework:framework-security'
 findProject(':framework:framework-security')?.name = 'framework-security'
+include 'framework:framework-plugin'
+findProject(':framework:framework-plugin')?.name = 'framework-plugin'
+include 'runtime:standalone-starter'
+findProject(':runtime:standalone-starter')?.name = 'standalone-starter'
+include 'runtime:saas-starter'
+findProject(':runtime:saas-starter')?.name = 'saas-starter'