Browse Source

Merge remote-tracking branch 'origin/main'

main
likunming 1 year ago
parent
commit
ba438f1314
  1. 4
      gradle.properties
  2. 2
      io.sc.platform.core.frontend/package.json
  3. 9
      io.sc.platform.core.frontend/src/platform/components-ext/formater/BooleanFormater.ts
  4. 6
      io.sc.platform.core.frontend/src/platform/components-ext/formater/EnumFormater.ts
  5. 6
      io.sc.platform.core.frontend/src/platform/components-ext/formater/index.ts
  6. 17
      io.sc.platform.core.frontend/src/platform/components-ext/options/BooleanOptions.ts
  7. 6
      io.sc.platform.core.frontend/src/platform/components-ext/options/index.ts
  8. 11
      io.sc.platform.core.frontend/src/platform/components/form/elements/WSelectUserText.vue
  9. 3
      io.sc.platform.core.frontend/src/platform/components/index.ts
  10. 13
      io.sc.platform.core.frontend/src/platform/components/tag/WSuccessFailedTag.vue
  11. 1
      io.sc.platform.core.frontend/src/platform/index.ts
  12. 100
      io.sc.platform.core.frontend/src/views/TreeGrid.vue
  13. 4
      io.sc.platform.core.frontend/template-project/package.json
  14. 100
      io.sc.platform.core.frontend/template-project/src/views/TreeGrid.vue
  15. 3
      io.sc.platform.core/src/main/java/io/sc/platform/core/annotation/AuditLog.java
  16. 2
      io.sc.platform.core/src/main/java/io/sc/platform/core/audit/AuditLog.java
  17. 17
      io.sc.platform.core/src/main/java/io/sc/platform/core/audit/AuditLogAction.java
  18. 23
      io.sc.platform.core/src/main/java/io/sc/platform/core/enums/AuditLogAction.java
  19. 2
      io.sc.platform.core/src/main/java/io/sc/platform/core/enums/AuditLogStatus.java
  20. 3
      io.sc.platform.core/src/main/java/io/sc/platform/core/enums/LogLevel.java
  21. 35
      io.sc.platform.core/src/main/java/io/sc/platform/core/support/FileWrapper.java
  22. 12
      io.sc.platform.core/src/main/java/io/sc/platform/core/util/EnumUtil.java
  23. 32
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/enums.properties
  24. 32
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/enums_tw_CN.properties
  25. 32
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/enums_zh_CN.properties
  26. 4
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties
  27. 4
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties
  28. 4
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties
  29. 4
      io.sc.platform.developer.frontend/package.json
  30. 79
      io.sc.platform.developer.frontend/src/views/plugin/Menus.vue
  31. 4
      io.sc.platform.gradle/templates/pgp/app/build.gradle
  32. 3
      io.sc.platform.gradle/templates/pgp/app/src/main/java/app/platform/Application.java.txt
  33. 4
      io.sc.platform.gradle/templates/pgp/setup/build.gradle.txt
  34. 8
      io.sc.platform.gradle/templates/pgp/setup/gradle.properties
  35. 4
      io.sc.platform.lcdp.frontend/package.json
  36. 4
      io.sc.platform.mvc.frontend/package.json
  37. 1
      io.sc.platform.mvc/build.gradle
  38. 14
      io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/autoconfigure/support/AuditLogAspectorBean.java
  39. 104
      io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/controller/support/RestCrudController.java
  40. 18
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/QueryResult.java
  41. 4
      io.sc.platform.security.frontend/package.json
  42. 29
      io.sc.platform.security.loginform/src/main/java/io/sc/platform/security/loginform/autoconfigure/WebSecurityAutoConfiguration.java
  43. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/PlatformOauth2AuthorizationServerAutoConfiguration.java
  44. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/PlatformWebSecurityAutoConfiguration.java
  45. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/SecurityAutoConfiguration.java
  46. 2
      io.sc.platform.security.oauth2.server.resource/src/main/java/io/sc/platform/security/oauth2/server/resource/configure/PlatformOauth2ResourceServerAutoConfiguration.java
  47. 1
      io.sc.platform.security/src/main/java/io/sc/platform/security/autoconfigure/PlatformWebSecurityAutoConfiguration.java
  48. 37
      io.sc.platform.security/src/main/java/io/sc/platform/security/handler/PlatformAuthenticationFailureHandler.java
  49. 30
      io.sc.platform.security/src/main/java/io/sc/platform/security/handler/PlatformAuthenticationSuccessHandler.java
  50. 48
      io.sc.platform.security/src/main/java/io/sc/platform/security/handler/PlatformLogoutSuccessHandler.java
  51. 2
      io.sc.platform.security/src/main/java/io/sc/platform/security/service/impl/UserDetailsServiceImpl.java
  52. 4
      io.sc.platform.system.frontend/package.json
  53. 22
      io.sc.platform.system.frontend/src/i18n/messages.json
  54. 22
      io.sc.platform.system.frontend/src/i18n/messages_tw_CN.json
  55. 22
      io.sc.platform.system.frontend/src/i18n/messages_zh_CN.json
  56. 20
      io.sc.platform.system.frontend/src/views/corporation/Corporation.vue
  57. 107
      io.sc.platform.system.frontend/src/views/dictionary/Dictionary.vue
  58. 158
      io.sc.platform.system.frontend/src/views/i18n/I18n.vue
  59. 135
      io.sc.platform.system.frontend/src/views/menu/ImportMenuPluginDialog.vue
  60. 54
      io.sc.platform.system.frontend/src/views/menu/Menu.vue
  61. 166
      io.sc.platform.system.frontend/src/views/monitor/AuditLog.vue
  62. 161
      io.sc.platform.system.frontend/src/views/monitor/Log.vue
  63. 154
      io.sc.platform.system.frontend/src/views/parameter/Parameter.vue
  64. 2
      io.sc.platform.system/src/main/java/io/sc/platform/system/dictionary/jpa/entity/DictionaryEntity.java
  65. 2
      io.sc.platform.system/src/main/java/io/sc/platform/system/i18n/jpa/entity/I18nEntity.java
  66. 4
      io.sc.platform.system/src/main/java/io/sc/platform/system/i18n/service/impl/I18nServiceImpl.java
  67. 16
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/controller/MenuWebController.java
  68. 7
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/MenuService.java
  69. 71
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/impl/MenuServiceImpl.java
  70. 165
      io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/controller/LogViewerWebController.java
  71. 36
      io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogFileLastModifyDateComparator.java
  72. 25
      io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogFileNameComparator.java
  73. 38
      io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogFileSizeComparator.java
  74. 25
      io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogLevelConfiguredLevelComparator.java
  75. 24
      io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogLevelEffectiveLevelComparator.java
  76. 26
      io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogLevelNameComparator.java
  77. 2
      io.sc.platform.system/src/main/java/io/sc/platform/system/parameter/jpa/entity/ParameterEntity.java
  78. 1
      io.sc.platform.system/src/main/java/io/sc/platform/system/plugins/PluginManager.java
  79. 11
      io.sc.platform.system/src/main/java/io/sc/platform/system/plugins/item/MenuItem.java
  80. 5
      io.sc.platform.system/src/main/resources/META-INF/platform/plugins/p6spy.json

4
gradle.properties

@ -36,9 +36,9 @@ application_version=1.0.0
# platform
###########################################################
platform_group=io.sc
platform_version=8.1.20
platform_version=8.1.22
platform_plugin_version=8.1.13
platform_core_frontend_version=8.1.83
platform_core_frontend_version=8.1.88
###########################################################
# dependencies version

2
io.sc.platform.core.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "platform-core",
"version": "8.1.87",
"version": "8.1.94",
"description": "前端核心包,用于快速构建前端的脚手架",
"//main": "库的主文件",
"main": "dist/platform-core.js",

9
io.sc.platform.core.frontend/src/platform/components-ext/formater/BooleanFormater.ts

@ -21,4 +21,11 @@ const enableTagFormater = (value) => {
};
};
export { yesNoFormater, trueFalseFormater, enableTagFormater };
const successFailedTagFormater = (value) => {
return {
componentType: 'WSuccessFailedTag',
attrs: { success: value },
};
};
export { yesNoFormater, trueFalseFormater, enableTagFormater, successFailedTagFormater };

6
io.sc.platform.core.frontend/src/platform/components-ext/formater/EnumFormater.ts

@ -1,19 +1,25 @@
import type { OptionItemType } from '@/platform/types';
import { i18n } from '@/platform/plugin';
import { Tools } from '@/platform/utils';
class EnumFormater {
#enumMap = {};
constructor(options: OptionItemType[]) {
for (const option of options) {
if (!Tools.isUndefinedOrNull(option.value)) {
this.#enumMap[option.value] = option.label;
}
}
}
public formater() {
const enumMap = this.#enumMap;
return (value) => {
if (!Tools.isUndefinedOrNull(value)) {
return i18n.global.t(enumMap[value]);
}
return '';
};
}
}

6
io.sc.platform.core.frontend/src/platform/components-ext/formater/index.ts

@ -1,5 +1,5 @@
import type { EnumType } from '@/platform/types';
import { yesNoFormater, trueFalseFormater, enableTagFormater } from './BooleanFormater';
import { yesNoFormater, trueFalseFormater, enableTagFormater, successFailedTagFormater } from './BooleanFormater';
import { dateOnlyFormater } from './DatetimeFormater';
import { menuTypeFormater } from './MenuTypeFormater';
import { EnumFormater } from './EnumFormater';
@ -11,6 +11,10 @@ class Formater {
return enableTagFormater;
}
public static successTag() {
return successFailedTagFormater;
}
public static yesNo() {
return yesNoFormater;
}

17
io.sc.platform.core.frontend/src/platform/components-ext/options/BooleanOptions.ts

@ -30,4 +30,19 @@ const trueFalse = (includeEmpty: boolean = true) => {
}
};
export { yesNo, trueFalse };
const successFailed = (includeEmpty: boolean = true) => {
if (includeEmpty) {
return [
{ value: null, label: '' },
{ value: 'success', label: i18n.global.t('success') },
{ value: 'failed', label: i18n.global.t('failed') },
];
} else {
return [
{ value: 'success', label: i18n.global.t('success') },
{ value: 'failed', label: i18n.global.t('failed') },
];
}
};
export { yesNo, trueFalse, successFailed };

6
io.sc.platform.core.frontend/src/platform/components-ext/options/index.ts

@ -1,5 +1,5 @@
import type { EnumType } from '@/platform/types';
import { yesNo, trueFalse } from './BooleanOptions';
import { yesNo, trueFalse, successFailed } from './BooleanOptions';
import { EnumOptions } from './EnumOptions';
class Options {
@ -13,6 +13,10 @@ class Options {
return trueFalse();
}
public static successFailed() {
return successFailed();
}
public static enum(enumType: EnumType, includeEmpty: boolean = true) {
if (enumType) {
let enumOptions = Options.#enumOptionsMap[enumType.name];

11
io.sc.platform.core.frontend/src/platform/components/form/elements/WEditor.vue → io.sc.platform.core.frontend/src/platform/components/form/elements/WSelectUserText.vue

@ -2,18 +2,19 @@
<div>
<q-input
v-show="!hideIfComputed"
ref="textareaRef"
ref="textRef"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="textarea"
type="text"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
</template>
</q-input>
</div>
</template>
@ -22,7 +23,7 @@
import { ref, computed, defineProps, useAttrs } from 'vue';
import { FormValidators } from '@/platform/components';
const textareaRef = ref();
const textRef = ref();
const attrs = useAttrs();
const inRules = attrs.rules;
const props = defineProps({
@ -65,8 +66,8 @@ const rulesComputed = computed(() => {
} else if (hideIfComputed.value) {
rules = [];
}
if (textareaRef?.value) {
textareaRef.value.resetValidation();
if (textRef?.value) {
textRef.value.resetValidation();
}
return rules;
});

3
io.sc.platform.core.frontend/src/platform/components/index.ts

@ -27,6 +27,7 @@ import WInfoPanel from './panel/WInfoPanel.vue';
import WGrid from './grid/WGrid.vue';
import WToolbar from './toolbar/WToolbar.vue';
import WEnableTag from './tag/WEnableTag.vue';
import WSuccessFailedTag from './tag/WSuccessFailedTag.vue';
export default {
install: (app: App) => {
@ -57,6 +58,7 @@ export default {
app.component('WGrid', WGrid);
app.component('WToolbar', WToolbar);
app.component('WEnableTag', WEnableTag);
app.component('WSuccessFailedTag', WSuccessFailedTag);
},
};
@ -83,6 +85,7 @@ export {
WGrid,
WToolbar,
WEnableTag,
WSuccessFailedTag,
};
export { PlatformIconEnum } from './utils';

13
io.sc.platform.core.frontend/src/platform/components/tag/WSuccessFailedTag.vue

@ -0,0 +1,13 @@
<template>
<div v-if="!Tools.isUndefinedOrNull(success)">
<q-chip v-if="success === true || success === 'success'" color="green" text-color="white" :label="$t('success')" dense></q-chip>
<q-chip v-if="success !== true && success !== 'success'" color="red" text-color="white" :label="$t('failed')" dense></q-chip>
</div>
</template>
<script setup lang="ts">
import { Tools } from '@/platform/utils';
const props = defineProps({
success: { type: [Boolean, String], default: true },
});
</script>

1
io.sc.platform.core.frontend/src/platform/index.ts

@ -120,6 +120,7 @@ export {
WGrid,
WTreeGrid,
WEnableTag,
WSuccessFailedTag,
} from './components';
export { PlatformIconEnum } from './components';

100
io.sc.platform.core.frontend/src/views/TreeGrid.vue

@ -1,56 +1,58 @@
<template>
<w-list-grid
:tree="true"
title="User List"
:no-action-icon="true"
:target-object-name="$t('菜单')"
:actions="[
'query',
'refresh',
'expandAll',
'selectAll',
'separator',
'add',
'clone',
'edit',
'remove',
'removeAll',
'separator',
'detail',
'addTop',
'addChild',
<w-grid
ref="auditLogGridRef"
:title="$t('system.user.grid.title')"
selection="multiple"
:data-url="Environment.apiContextPath('/api/system/monitor/auditlog')"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'text' },
{ name: 'message', label: $t('i18nMessage'), type: 'text' },
{ name: 'lang', label: $t('language'), type: 'select' },
{ name: 'dataComeFrom', label: $t('dataComeFrom'), type: 'select' },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'view', 'export']"
:columns="[
{ width: 100, name: 'status', label: $t('status'), format: Formater.successTag() },
{ width: 60, name: 'action', label: $t('action') },
{ width: 100, name: 'startDate', label: $t('startDate') },
{ width: 60, name: 'executeTime', label: $t('executeTime') },
{ width: 80, name: 'loginName', label: $t('loginName') },
{ width: 60, name: 'httpMethod', label: $t('httpMethod') },
{ width: 200, name: 'url', label: $t('url') },
{ width: 100, name: 'ip', label: $t('ip') },
{ width: 100, name: 'className', label: $t('className') },
{ width: 100, name: 'methodName', label: $t('methodName') },
{ width: 100, name: 'oldValue', label: $t('oldValue') },
{ width: 100, name: 'newValue', label: $t('newValue') },
{ width: 100, name: 'exception', label: $t('exception') },
{ width: 100, name: 'exceptionMessage', label: $t('exceptionMessage') },
{ width: 100, name: 'exceptionStackTrace', label: $t('exceptionStackTrace') },
]"
:columns="columns"
:auto-fetch-data="true"
:data-url="Environment.apiContextPath('/api/system/menu/allMenus')"
></w-list-grid>
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'code', label: $t('code') },
{ name: 'lang', label: $t('language') },
{ name: 'message', label: $t('i18nMessage') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
}"
@row-click="(evt, row, index) => {}"
>
</w-grid>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment } from '@/platform';
import { Environment, axios, Formater, Options } from '@/platform';
const { t } = useI18n();
const columns = [
{
name: 'name',
required: true,
label: t('name'),
align: 'left',
field: 'name',
sortable: true,
format: (value, data) => {
return t(data.titleI18nKey);
},
},
{ name: 'type', align: 'center', label: 'type', field: 'type', sortable: true },
{ name: 'enable', label: 'enable', field: 'enable', sortable: true },
{ name: 'dataComeFrom', label: 'dataComeFrom', field: 'dataComeFrom' },
{ name: 'lastModifier', label: 'lastModifier', field: 'lastModifier' },
{ name: 'lastModifyDate', label: 'lastModifyDate', field: 'lastModifyDate' },
{ name: 'corporationCode', label: 'corporationCode', field: 'corporationCode', sortable: true },
];
const auditLogGridRef = ref();
</script>

4
io.sc.platform.core.frontend/template-project/package.json

@ -1,6 +1,6 @@
{
"name": "platform-core",
"version": "8.1.87",
"version": "8.1.94",
"description": "前端核心包,用于快速构建前端的脚手架",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.87",
"platform-core": "8.1.94",
"quasar": "2.14.2",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

100
io.sc.platform.core.frontend/template-project/src/views/TreeGrid.vue

@ -1,56 +1,58 @@
<template>
<w-list-grid
:tree="true"
title="User List"
:no-action-icon="true"
:target-object-name="$t('菜单')"
:actions="[
'query',
'refresh',
'expandAll',
'selectAll',
'separator',
'add',
'clone',
'edit',
'remove',
'removeAll',
'separator',
'detail',
'addTop',
'addChild',
<w-grid
ref="auditLogGridRef"
:title="$t('system.user.grid.title')"
selection="multiple"
:data-url="Environment.apiContextPath('/api/system/monitor/auditlog')"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'text' },
{ name: 'message', label: $t('i18nMessage'), type: 'text' },
{ name: 'lang', label: $t('language'), type: 'select' },
{ name: 'dataComeFrom', label: $t('dataComeFrom'), type: 'select' },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'view', 'export']"
:columns="[
{ width: 100, name: 'status', label: $t('status'), format: Formater.successTag() },
{ width: 60, name: 'action', label: $t('action') },
{ width: 100, name: 'startDate', label: $t('startDate') },
{ width: 60, name: 'executeTime', label: $t('executeTime') },
{ width: 80, name: 'loginName', label: $t('loginName') },
{ width: 60, name: 'httpMethod', label: $t('httpMethod') },
{ width: 200, name: 'url', label: $t('url') },
{ width: 100, name: 'ip', label: $t('ip') },
{ width: 100, name: 'className', label: $t('className') },
{ width: 100, name: 'methodName', label: $t('methodName') },
{ width: 100, name: 'oldValue', label: $t('oldValue') },
{ width: 100, name: 'newValue', label: $t('newValue') },
{ width: 100, name: 'exception', label: $t('exception') },
{ width: 100, name: 'exceptionMessage', label: $t('exceptionMessage') },
{ width: 100, name: 'exceptionStackTrace', label: $t('exceptionStackTrace') },
]"
:columns="columns"
:auto-fetch-data="true"
:data-url="Environment.apiContextPath('/api/system/menu/allMenus')"
></w-list-grid>
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'code', label: $t('code') },
{ name: 'lang', label: $t('language') },
{ name: 'message', label: $t('i18nMessage') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
}"
@row-click="(evt, row, index) => {}"
>
</w-grid>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment } from '@/platform';
import { Environment, axios, Formater, Options } from '@/platform';
const { t } = useI18n();
const columns = [
{
name: 'name',
required: true,
label: t('name'),
align: 'left',
field: 'name',
sortable: true,
format: (value, data) => {
return t(data.titleI18nKey);
},
},
{ name: 'type', align: 'center', label: 'type', field: 'type', sortable: true },
{ name: 'enable', label: 'enable', field: 'enable', sortable: true },
{ name: 'dataComeFrom', label: 'dataComeFrom', field: 'dataComeFrom' },
{ name: 'lastModifier', label: 'lastModifier', field: 'lastModifier' },
{ name: 'lastModifyDate', label: 'lastModifyDate', field: 'lastModifyDate' },
{ name: 'corporationCode', label: 'corporationCode', field: 'corporationCode', sortable: true },
];
const auditLogGridRef = ref();
</script>

3
io.sc.platform.core/src/main/java/io/sc/platform/core/annotation/AuditLog.java

@ -1,5 +1,6 @@
package io.sc.platform.core.annotation;
import io.sc.platform.core.enums.AuditLogAction;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.Documented;
@ -25,4 +26,6 @@ public @interface AuditLog {
@AliasFor("value")
String category() default "";
AuditLogAction action() default AuditLogAction.UNKNOWN;
}

2
io.sc.platform.core/src/main/java/io/sc/platform/core/audit/AuditLog.java

@ -26,7 +26,7 @@ public class AuditLog {
private String action; //AuditLog 注解中指定的操作
private String status; //请求方法的执行状态
private Date startDate; //方法开始执行时间
private Date startDate =new Date();//方法开始执行时间
private Long executeTime; //方法执行的时间(单位:毫秒)
private String url; //请求的 URL

17
io.sc.platform.core/src/main/java/io/sc/platform/core/audit/AuditLogAction.java

@ -1,17 +0,0 @@
package io.sc.platform.core.audit;
/**
* 审计日志分类常量
*/
public class AuditLogAction {
public static final String ADD ="io.sc.platform.core.audit.AuditLogAction.ADD";
public static final String REMOVE ="io.sc.platform.core.audit.AuditLogAction.REMOVE";
public static final String UPDATE ="io.sc.platform.core.audit.AuditLogAction.UPDATE";
public static final String QUERY ="io.sc.platform.core.audit.AuditLogAction.QUERY";
public static final String FIND_ONE ="io.sc.platform.core.audit.AuditLogAction.FIND_ONE";
public static final String EXPORT ="io.sc.platform.core.audit.AuditLogAction.EXPORT";
public static final String LOGIN ="io.sc.platform.core.audit.AuditLogAction.LOGIN";
public static final String LOGOUT ="io.sc.platform.core.audit.AuditLogAction.LOGOUT";
private AuditLogAction(){}
}

23
io.sc.platform.core/src/main/java/io/sc/platform/core/enums/AuditLogAction.java

@ -0,0 +1,23 @@
package io.sc.platform.core.enums;
public enum AuditLogAction {
UNKNOWN("UNKNOWN"),
ADD("ADD"),
REMOVE("REMOVE"),
UPDATE("UPDATE"),
QUERY("QUERY"),
FIND_ONE("FIND_ONE"),
EXPORT("EXPORT"),
LOGIN("LOGIN"),
LOGOUT("LOGOUT");
private final String value;
private AuditLogAction(String value){
this.value =value;
}
public String getValue(){
return this.value;
}
}

2
io.sc.platform.core/src/main/java/io/sc/platform/core/audit/AuditLogStatus.java → io.sc.platform.core/src/main/java/io/sc/platform/core/enums/AuditLogStatus.java

@ -1,4 +1,4 @@
package io.sc.platform.core.audit;
package io.sc.platform.core.enums;
/**
* 审计日志状态枚举

3
io.sc.platform.core/src/main/java/io/sc/platform/core/support/LogLevel.java → io.sc.platform.core/src/main/java/io/sc/platform/core/enums/LogLevel.java

@ -1,10 +1,9 @@
package io.sc.platform.core.support;
package io.sc.platform.core.enums;
/**
* 日志级别枚举
*/
public enum LogLevel {
OFF, //关闭
ERROR, //错误
WARN, //警告
INFO, //信息

35
io.sc.platform.core/src/main/java/io/sc/platform/core/support/FileWrapper.java

@ -1,18 +1,22 @@
package io.sc.platform.core.support;
import io.sc.platform.core.util.StringUtil;
import java.util.Date;
public class FileWrapper {
private String name;
private Date lastModified;
private String size;
private Date lastModifyDate;
private long size;
private String commaSize;
public FileWrapper(){}
public FileWrapper(String name,Date lastModified,String size){
public FileWrapper(String name,Date lastModifyDate,long size){
this.name =name;
this.lastModified =lastModified;
this.lastModifyDate =lastModifyDate;
this.size =size;
this.commaSize = StringUtil.commaStyle(size);
}
public String getName() {
@ -21,16 +25,27 @@ public class FileWrapper {
public void setName(String name) {
this.name = name;
}
public Date getLastModified() {
return lastModified;
public Date getLastModifyDate() {
return lastModifyDate;
}
public void setLastModified(Date lastModified) {
this.lastModified = lastModified;
public void setLastModifyDate(Date lastModifyDate) {
this.lastModifyDate = lastModifyDate;
}
public String getSize() {
public long getSize() {
return size;
}
public void setSize(String size) {
public void setSize(long size) {
this.size = size;
}
public String getCommaSize() {
return commaSize;
}
public void setCommaSize(String commaSize) {
this.commaSize = commaSize;
}
}

12
io.sc.platform.core/src/main/java/io/sc/platform/core/util/EnumUtil.java

@ -140,6 +140,18 @@ public class EnumUtil {
return null;
}
public static <E extends Enum<E>> int compare(E o1, E o2){
if(o1!=null && o2==null){
return 1;
}else if(o1==null && o2!=null){
return -1;
}else if(o1==null && o2==null){
return 0;
}else{
return o1.compareTo(o2);
}
}
private static boolean contain(String[] arrays,String str) {
if(arrays==null || arrays.length==0) {
return false;

32
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/enums.properties

@ -1,21 +1,21 @@
io.sc.platform.core.support.LogLevel.OFF=OFF
io.sc.platform.core.support.LogLevel.ERROR=ERROR
io.sc.platform.core.support.LogLevel.WARN=WARN
io.sc.platform.core.support.LogLevel.INFO=INFO
io.sc.platform.core.support.LogLevel.DEBUG=DEBUG
io.sc.platform.core.support.LogLevel.TRACE=TRACE
io.sc.platform.core.enums.LogLevel.ERROR=ERROR
io.sc.platform.core.enums.LogLevel.WARN=WARN
io.sc.platform.core.enums.LogLevel.INFO=INFO
io.sc.platform.core.enums.LogLevel.DEBUG=DEBUG
io.sc.platform.core.enums.LogLevel.TRACE=TRACE
io.sc.platform.core.audit.AuditLogStatus.SUCCESS=Success
io.sc.platform.core.audit.AuditLogStatus.FAILED=Failed
io.sc.platform.core.enums.AuditLogStatus.SUCCESS=Success
io.sc.platform.core.enums.AuditLogStatus.FAILED=Failed
io.sc.platform.core.audit.AuditLogAction.ADD=Add
io.sc.platform.core.audit.AuditLogAction.REMOVE=Remove
io.sc.platform.core.audit.AuditLogAction.UPDATE=Update
io.sc.platform.core.audit.AuditLogAction.QUERY=Query
io.sc.platform.core.audit.AuditLogAction.FIND_ONE=Find One
io.sc.platform.core.audit.AuditLogAction.EXPORT=Export
io.sc.platform.core.audit.AuditLogAction.LOGIN=Login
io.sc.platform.core.audit.AuditLogAction.LOGOUT=Logout
io.sc.platform.core.enums.AuditLogAction.UNKNOWN=Unknown
io.sc.platform.core.enums.AuditLogAction.ADD=Add
io.sc.platform.core.enums.AuditLogAction.REMOVE=Remove
io.sc.platform.core.enums.AuditLogAction.UPDATE=Update
io.sc.platform.core.enums.AuditLogAction.QUERY=Query
io.sc.platform.core.enums.AuditLogAction.FIND_ONE=Find One
io.sc.platform.core.enums.AuditLogAction.EXPORT=Export
io.sc.platform.core.enums.AuditLogAction.LOGIN=Login
io.sc.platform.core.enums.AuditLogAction.LOGOUT=Logout
io.sc.platform.core.enums.Language.en=English
io.sc.platform.core.enums.Language.zh_CN=\u7B80\u4F53\u4E2D\u6587

32
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/enums_tw_CN.properties

@ -1,21 +1,21 @@
io.sc.platform.core.support.LogLevel.OFF=\u95DC\u9589
io.sc.platform.core.support.LogLevel.ERROR=\u932F\u8AA4
io.sc.platform.core.support.LogLevel.WARN=\u8B66\u544A
io.sc.platform.core.support.LogLevel.INFO=\u4FE1\u606F
io.sc.platform.core.support.LogLevel.DEBUG=\u8ABF\u8A66
io.sc.platform.core.support.LogLevel.TRACE=\u8DDF\u8E64
io.sc.platform.core.enums.LogLevel.ERROR=\u932F\u8AA4
io.sc.platform.core.enums.LogLevel.WARN=\u8B66\u544A
io.sc.platform.core.enums.LogLevel.INFO=\u4FE1\u606F
io.sc.platform.core.enums.LogLevel.DEBUG=\u8ABF\u8A66
io.sc.platform.core.enums.LogLevel.TRACE=\u8DDF\u8E64
io.sc.platform.core.audit.AuditLogStatus.SUCCESS=\u6210\u529F
io.sc.platform.core.audit.AuditLogStatus.FAILED=\u5931\u6557
io.sc.platform.core.enums.AuditLogStatus.SUCCESS=\u6210\u529F
io.sc.platform.core.enums.AuditLogStatus.FAILED=\u5931\u6557
io.sc.platform.core.audit.AuditLogAction.ADD=\u65B0\u589E
io.sc.platform.core.audit.AuditLogAction.REMOVE=\u522A\u9664
io.sc.platform.core.audit.AuditLogAction.UPDATE=\u66F4\u65B0
io.sc.platform.core.audit.AuditLogAction.QUERY=\u67E5\u8A62
io.sc.platform.core.audit.AuditLogAction.FIND_ONE=\u67E5\u627E\u55AE\u500B
io.sc.platform.core.audit.AuditLogAction.EXPORT=\u5C0E\u51FA
io.sc.platform.core.audit.AuditLogAction.LOGIN=\u767B\u9304
io.sc.platform.core.audit.AuditLogAction.LOGOUT=\u9000\u51FA
io.sc.platform.core.enums.AuditLogAction.UNKNOWN=\u672A\u77E5
io.sc.platform.core.enums.AuditLogAction.ADD=\u65B0\u589E
io.sc.platform.core.enums.AuditLogAction.REMOVE=\u522A\u9664
io.sc.platform.core.enums.AuditLogAction.UPDATE=\u66F4\u65B0
io.sc.platform.core.enums.AuditLogAction.QUERY=\u67E5\u8A62
io.sc.platform.core.enums.AuditLogAction.FIND_ONE=\u67E5\u627E\u55AE\u500B
io.sc.platform.core.enums.AuditLogAction.EXPORT=\u5C0E\u51FA
io.sc.platform.core.enums.AuditLogAction.LOGIN=\u767B\u9304
io.sc.platform.core.enums.AuditLogAction.LOGOUT=\u9000\u51FA
io.sc.platform.core.enums.Language.en=English
io.sc.platform.core.enums.Language.zh_CN=\u7B80\u4F53\u4E2D\u6587

32
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/enums_zh_CN.properties

@ -1,21 +1,21 @@
io.sc.platform.core.support.LogLevel.OFF=\u5173\u95ED
io.sc.platform.core.support.LogLevel.ERROR=\u9519\u8BEF
io.sc.platform.core.support.LogLevel.WARN=\u8B66\u544A
io.sc.platform.core.support.LogLevel.INFO=\u4FE1\u606F
io.sc.platform.core.support.LogLevel.DEBUG=\u8C03\u8BD5
io.sc.platform.core.support.LogLevel.TRACE=\u8DDF\u8E2A
io.sc.platform.core.enums.LogLevel.ERROR=\u9519\u8BEF
io.sc.platform.core.enums.LogLevel.WARN=\u8B66\u544A
io.sc.platform.core.enums.LogLevel.INFO=\u4FE1\u606F
io.sc.platform.core.enums.LogLevel.DEBUG=\u8C03\u8BD5
io.sc.platform.core.enums.LogLevel.TRACE=\u8DDF\u8E2A
io.sc.platform.core.audit.AuditLogStatus.SUCCESS=\u6210\u529F
io.sc.platform.core.audit.AuditLogStatus.FAILED=\u5931\u8D25
io.sc.platform.core.enums.AuditLogStatus.SUCCESS=\u6210\u529F
io.sc.platform.core.enums.AuditLogStatus.FAILED=\u5931\u8D25
io.sc.platform.core.audit.AuditLogAction.ADD=\u65B0\u589E
io.sc.platform.core.audit.AuditLogAction.REMOVE=\u522A\u9664
io.sc.platform.core.audit.AuditLogAction.UPDATE=\u66F4\u65B0
io.sc.platform.core.audit.AuditLogAction.QUERY=\u67E5\u8BE2
io.sc.platform.core.audit.AuditLogAction.FIND_ONE=\u67E5\u627E\u5355\u4E2A
io.sc.platform.core.audit.AuditLogAction.EXPORT=\u5BFC\u51FA
io.sc.platform.core.audit.AuditLogAction.LOGIN=\u767B\u5F55
io.sc.platform.core.audit.AuditLogAction.LOGOUT=\u9000\u51FA
io.sc.platform.core.enums.AuditLogAction.UNKNOWN=\u672A\u77E5
io.sc.platform.core.enums.AuditLogAction.ADD=\u65B0\u589E
io.sc.platform.core.enums.AuditLogAction.REMOVE=\u522A\u9664
io.sc.platform.core.enums.AuditLogAction.UPDATE=\u66F4\u65B0
io.sc.platform.core.enums.AuditLogAction.QUERY=\u67E5\u8BE2
io.sc.platform.core.enums.AuditLogAction.FIND_ONE=\u67E5\u627E\u5355\u4E2A
io.sc.platform.core.enums.AuditLogAction.EXPORT=\u5BFC\u51FA
io.sc.platform.core.enums.AuditLogAction.LOGIN=\u767B\u5F55
io.sc.platform.core.enums.AuditLogAction.LOGOUT=\u9000\u51FA
io.sc.platform.core.enums.Language.en=English
io.sc.platform.core.enums.Language.zh_CN=\u7B80\u4F53\u4E2D\u6587

4
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties

@ -115,6 +115,7 @@ query=Query
redirect=Redirect
refresh=Refresh
remove=Remove
removeAll=Remove All
reset=Reset
restore=Restore
reverse=Reverse
@ -156,4 +157,5 @@ confirmNewPassword=Confirm New Password
accountExpired=Expired
accountLocked=Locked
credentialsExpired=Credentials Expired
javascript=JavaScript
icon=Icon
i18nMessage=I18N Message

4
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties

@ -115,6 +115,7 @@ query=\u67E5\u8A62
redirect=\u91CD\u5B9A\u5411
refresh=\u5237\u65B0
remove=\u79FB\u9664
removeAll=\u79FB\u9664\u6240\u6709
reset=\u91CD\u7F6E
restore=\u6062\u5FA9
reverse=\u53CD\u8F49
@ -156,4 +157,5 @@ confirmNewPassword=\u78BA\u8A8D\u65B0\u5BC6\u78BC
accountExpired=\u904E\u671F
accountLocked=\u9396\u5B9A
credentialsExpired=\u5BC6\u78BC\u904E\u671F
javascript=JavaScript
icon=\u5716\u6A19
i18nMessage=\u591A\u8A9E\u8A00\u6D88\u606F

4
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties

@ -115,6 +115,7 @@ query=\u67E5\u8BE2
redirect=\u91CD\u5B9A\u5411
refresh=\u5237\u65B0
remove=\u79FB\u9664
removeAll=\u79FB\u9664\u6240\u6709
reset=\u91CD\u7F6E
restore=\u6062\u590D
reverse=\u53CD\u8F6C
@ -156,4 +157,5 @@ confirmNewPassword=\u786E\u8BA4\u65B0\u5BC6\u7801
accountExpired=\u8FC7\u671F
accountLocked=\u9501\u5B9A
credentialsExpired=\u5BC6\u7801\u8FC7\u671F
javascript=JavaScript
icon=\u56FE\u6807
i18nMessage=\u591A\u8BED\u8A00\u6D88\u606F

4
io.sc.platform.developer.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.developer.frontend",
"version": "8.1.20",
"version": "8.1.22",
"description": "",
"private": false,
"keywords": [],
@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.83",
"platform-core": "8.1.88",
"quasar": "2.14.2",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

79
io.sc.platform.developer.frontend/src/views/plugin/Menus.vue

@ -1,4 +1,79 @@
<template>
<div>Menus</div>
<w-grid
:tree="true"
dense-body
:title="$t('menu.developer.plugin.menus')"
:data-url="Environment.apiContextPath('/api/system/menu/getAllMenuPlugins')"
primary-key="id"
foreign-key="parentId"
selection="multiple"
:checkbox-selection="false"
:pageable="false"
:full-screen-button="false"
:tree-icon="
(row) => {
if (row.type === 'SEPARATOR') {
return { name: 'bi-dash-lg' };
} else if (row.type === 'ROUTE_ACTION') {
return { name: 'sym_o_crop_16_9' };
} else {
return { name: row.icon };
}
}
"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'view', 'export']"
:columns="[
{
width: 400,
name: 'titleI18nKey',
label: $t('name'),
sortable: false,
format: (value, row) => {
if (row.type === 'SEPARATOR') {
return `<hr style='width:100px'/>`;
} else if (row.type === 'ROUTE_ACTION') {
return $t(row.i18nKey);
} else {
return $t(value);
}
},
},
{ width: 100, name: 'order', label: $t('order'), align: 'right', sortable: false },
{ width: '100%', name: 'configurationFileUrl', label: $t('url'), sortable: false },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'parentId', label: $t('parentId') },
{
name: 'titleI18nKey',
label: $t('system.menu.grid.entity.titleI18nKey'),
format: (value) => {
return value;
},
},
{ name: 'icon', label: $t('icon') },
{ name: 'order', label: $t('order') },
{ name: 'javaScript', label: $t('system.menu.grid.entity.javaScript') },
{ name: 'url', label: $t('system.menu.grid.entity.url') },
{ name: 'urlOpenType', label: $t('system.menu.grid.entity.urlOpenType') },
{ name: 'routeName', label: $t('system.menu.grid.entity.routeName') },
{
name: 'routeQuery',
label: $t('system.menu.grid.entity.routeQuery'),
format: (value) => {
return JSON.stringify(value);
},
},
{ name: 'configurationFileUrl', label: $t('url') },
],
},
}"
></w-grid>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { Environment } from 'platform-core';
</script>

4
io.sc.platform.gradle/templates/pgp/app/build.gradle

@ -13,7 +13,11 @@ dependencies {
implementation (
project(":io.sc.platform.app"),
project(":io.sc.platform.developer"),
//project(":io.sc.platform.job.core"),
//project(":io.sc.platform.job.executor"),
//project(":io.sc.platform.job.manager"),
)
}

3
io.sc.platform.gradle/templates/pgp/app/src/main/java/app/platform/Application.java.txt

@ -2,12 +2,9 @@ package app.platform;
import io.sc.platform.core.ApplicationLauncher;
import io.sc.platform.core.PlatformSpringBootServletInitializer;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.WebApplicationInitializer;
import java.sql.Types;
/**
* 应用程序入口
*/

4
io.sc.platform.gradle/templates/pgp/setup/build.gradle.txt

@ -371,8 +371,6 @@ subprojects {
* pnpm sync
*----------------------------------------------------------------*/
task frontendNpmSync(type:Exec){
commandLine 'cd', '.'
/*
if(isFrontendProject(file('.')) && !project.name.contains("io.sc.platform.security.frontend")){
workingDir '.'
if(org.gradle.internal.os.OperatingSystem.current().isWindows()){
@ -386,7 +384,7 @@ subprojects {
}else{
commandLine 'cd', '.'
}
}*/
}
}
tasks.frontendNpmSync.doFirst {
if(isFrontendProject(file('.'))) {

8
io.sc.platform.gradle/templates/pgp/setup/gradle.properties

@ -36,9 +36,9 @@ application_version=1.0.0
# platform
###########################################################
platform_group=io.sc
platform_version=8.1.19
platform_version=8.1.22
platform_plugin_version=8.1.13
platform_core_frontend_version=8.1.45
platform_core_frontend_version=8.1.88
###########################################################
# dependencies version
@ -50,6 +50,7 @@ cxf_version=3.2.7
dm_hibernate_version=8.1.2.192
flowable_version=6.8.0
guava_version=31.1-jre
ipaddress_version=5.4.0
jackson_version=2.13.5
jasypt_version=2.1.2
jboss_logging_version=3.3.2.Final
@ -64,6 +65,7 @@ mybatis_version=3.5.10
opencsv_version=5.7.1
oshi_version=6.4.2
p6spy_version=3.9.1
pinyin4j_version=2.5.1
poi_ooxml_version=3.17
schemacrawler_version=16.19.11
#schemacrawler_version=14.21.02
@ -71,7 +73,7 @@ spring_boot_version=2.7.18
spring_cloud_alibaba_version=2021.0.4.0
spring_cloud_context_version=3.1.4
spring_cloud_version=2021.0.8
spring_security_oauth2_authorization_server_version=0.4.4
spring_security_oauth2_authorization_server_version=0.4.5
spring_statemachine_version=3.2.1
webjars_locator_weblogic_version=0.10
zip4j_version=2.11.5

4
io.sc.platform.lcdp.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.lcdp.frontend",
"version": "8.1.20",
"version": "8.1.22",
"description": "",
"private": false,
"keywords": [],
@ -91,7 +91,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.83",
"platform-core": "8.1.88",
"quasar": "2.14.2",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

4
io.sc.platform.mvc.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.mvc.frontend",
"version": "8.1.20",
"version": "8.1.22",
"description": "",
"private": false,
"keywords": [],
@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.83",
"platform-core": "8.1.88",
"quasar": "2.14.2",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

1
io.sc.platform.mvc/build.gradle

@ -1,6 +1,7 @@
dependencies {
api(
project(":io.sc.platform.orm"),
project(":io.sc.platform.security"),
project(":io.sc.platform.mvc.frontend"),
"org.webjars:webjars-locator-core",

14
io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/autoconfigure/support/AuditLogAspectorBean.java

@ -2,13 +2,15 @@ package io.sc.platform.mvc.autoconfigure.support;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.sc.platform.core.audit.AuditLog;
import io.sc.platform.core.audit.AuditLogStatus;
import io.sc.platform.core.enums.AuditLogAction;
import io.sc.platform.core.enums.AuditLogStatus;
import io.sc.platform.core.response.ValidationError;
import io.sc.platform.core.service.AuditLogPersistenter;
import io.sc.platform.core.service.AuditLogPersistenterManager;
import io.sc.platform.core.util.IpUtil;
import io.sc.platform.orm.entity.AuditorEntity;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.security.util.SecurityUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
@ -19,6 +21,7 @@ import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
@ -110,12 +113,17 @@ public class AuditLogAspectorBean {
if(httpServletRequest!=null) {
auditLog.setUrl(httpServletRequest.getRequestURL().toString());
auditLog.setLoginName(httpServletRequest.getRemoteUser());
//auditLog.setLoginName(httpServletRequest.getRemoteUser());
auditLog.setLoginName(SecurityUtil.getLoginName());
auditLog.setIp(IpUtil.getRemoteIp(httpServletRequest));
//审计日志注解
io.sc.platform.core.annotation.AuditLog annotation = method.getAnnotation(io.sc.platform.core.annotation.AuditLog.class);
String key =annotation.value();
String key = annotation.value();
if(!StringUtils.hasText(key)){
AuditLogAction action =annotation.action();
key =action.toString();
}
String action =messageSource.getMessage(key, null, key, httpServletRequest.getLocale());
auditLog.setAction(action);
}

104
io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/controller/support/RestCrudController.java

@ -1,7 +1,7 @@
package io.sc.platform.mvc.controller.support;
import io.sc.platform.core.annotation.AuditLog;
import io.sc.platform.core.audit.AuditLogAction;
import io.sc.platform.core.enums.AuditLogAction;
import io.sc.platform.core.response.ValidateException;
import io.sc.platform.core.util.ValidatorUtil;
import io.sc.platform.orm.api.vo.BaseVo;
@ -26,7 +26,7 @@ import java.util.List;
public abstract class RestCrudController<V extends BaseVo, E extends BaseEntity<V>, ID extends Serializable, R extends DaoRepository<E,ID>, S extends DaoService<E,ID,R>> {
@Autowired protected S service;
@AuditLog(AuditLogAction.ADD)
@AuditLog(action= AuditLogAction.ADD)
@PostMapping("")
@ResponseBody
public V _add(HttpServletRequest request,HttpServletResponse response,@RequestBody @Valid E entity,BindingResult bindingResult) throws Exception{
@ -45,7 +45,7 @@ public abstract class RestCrudController<V extends BaseVo, E extends BaseEntity<
return null;
}
@AuditLog(AuditLogAction.REMOVE)
@AuditLog(action=AuditLogAction.REMOVE)
@DeleteMapping("{id}")
@ResponseBody
public ID _remove(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id) throws Exception{
@ -56,7 +56,7 @@ public abstract class RestCrudController<V extends BaseVo, E extends BaseEntity<
return service.remove(id);
}
@AuditLog(AuditLogAction.UPDATE)
@AuditLog(action=AuditLogAction.UPDATE)
@PutMapping("{id}")
@ResponseBody
public V _update(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id,@RequestBody @Valid E entity,BindingResult bindingResult) throws Exception{
@ -75,7 +75,7 @@ public abstract class RestCrudController<V extends BaseVo, E extends BaseEntity<
return null;
}
@AuditLog(AuditLogAction.FIND_ONE)
@AuditLog(action=AuditLogAction.FIND_ONE)
@GetMapping("{id}")
@ResponseBody
public V _findById(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id) throws Exception{
@ -90,7 +90,7 @@ public abstract class RestCrudController<V extends BaseVo, E extends BaseEntity<
return null;
}
@AuditLog(AuditLogAction.QUERY)
@AuditLog(action=AuditLogAction.QUERY)
@GetMapping("")
@ResponseBody
public Page<V> _query(HttpServletRequest request,HttpServletResponse response,QueryParameter queryParameter) throws Exception{
@ -111,96 +111,4 @@ public abstract class RestCrudController<V extends BaseVo, E extends BaseEntity<
}
return new PageImpl<>(vos,page.getPageable(),page.getTotalElements());
}
/*
@AuditLog(AuditLogAction.ADD)
@PostMapping("")
@ResponseBody
public E _add(HttpServletRequest request,HttpServletResponse response,@RequestBody @Valid E entity,BindingResult bindingResult) throws Exception{
ValidateException result =validationError(bindingResult);
if(result!=null){
throw result;
}
return add(request,response,entity);
}
protected E add(HttpServletRequest request,HttpServletResponse response,@RequestBody @Valid E entity) throws Exception{
return service.add(entity);
}
@AuditLog(AuditLogAction.REMOVE)
@DeleteMapping("{id}")
@ResponseBody
public ID _remove(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id) throws Exception{
return remove(request,response,id);
}
protected ID remove(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id) throws Exception{
return service.remove(id);
}
@AuditLog(AuditLogAction.UPDATE)
@PutMapping("{id}")
@ResponseBody
public E _update(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id,@RequestBody @Valid E entity,BindingResult bindingResult) throws Exception{
ValidateException result =validationError(bindingResult);
if(result!=null){
throw result;
}
return update(request,response,id,entity);
}
protected E update(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id,@RequestBody @Valid E entity) throws Exception{
return service.update(id,entity);
}
@AuditLog(AuditLogAction.FIND_ONE)
@GetMapping("{id}")
@ResponseBody
public E _findById(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id) throws Exception{
return findById(request,response,id);
}
protected E findById(HttpServletRequest request,HttpServletResponse response,@PathVariable(name="id")ID id) throws Exception{
return service.findById(id);
}
@AuditLog(AuditLogAction.QUERY)
@GetMapping("")
@ResponseBody
public Page<E> _query(HttpServletRequest request,HttpServletResponse response,QueryParameter queryParameter) throws Exception{
return query(request,response,queryParameter);
}
protected Page<E> query(HttpServletRequest request,HttpServletResponse response,QueryParameter queryParameter) throws Exception{
return service.query(queryParameter);
}
private ValidateException validationError(BindingResult bindingResult) {
if(bindingResult==null || !bindingResult.hasErrors()){
return null;
}
List<FieldError> errors =bindingResult.getFieldErrors();
if(errors==null || errors.size()==0){
return null;
}
List<ValidationError> errorList =new ArrayList<ValidationError>();
for(FieldError error : errors){
errorList.add(new ValidationError(getFieldName(error.getField()),error.getDefaultMessage()));
}
return new ValidateException(errorList);
}
private String getFieldName(String field){
int index =field.lastIndexOf(".");
if(index>-1){
return field.substring(index+1);
}else{
return field;
}
}
*/
}

18
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/QueryResult.java

@ -2,7 +2,9 @@ package io.sc.platform.orm.service.support;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -23,4 +25,20 @@ public class QueryResult {
return new PageImpl<T>(Collections.emptyList());
}
}
public static <T> Page<T> page(List<T> list, Pageable pageable){
if(list!=null && !list.isEmpty()) {
int page =pageable.getPageNumber();
int pageSize =pageable.getPageSize();
int total =list.size();
int first =pageSize * page;
List<T> result =new ArrayList<>();
for(int i=first;i<first+pageSize && i<total;i++){
result.add(list.get(i));
}
return new PageImpl<T>(result,pageable,total);
}else{
return new PageImpl<T>(Collections.emptyList());
}
}
}

4
io.sc.platform.security.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.security.frontend",
"version": "8.1.20",
"version": "8.1.22",
"description": "",
"private": false,
"keywords": [
@ -99,6 +99,6 @@
"vue-dompurify-html": "5.0.1",
"vue-i18n": "9.8.0",
"vue-router": "4.2.5",
"platform-core": "8.1.83"
"platform-core": "8.1.88"
}
}

29
io.sc.platform.security.loginform/src/main/java/io/sc/platform/security/loginform/autoconfigure/WebSecurityAutoConfiguration.java

@ -1,13 +1,11 @@
package io.sc.platform.security.loginform.autoconfigure;
import io.sc.platform.core.service.AuditLogPersistenterManager;
import io.sc.platform.core.service.RuntimeService;
import io.sc.platform.security.handler.PlatformAccessDeniedHandler;
import io.sc.platform.security.handler.PlatformAuthenticationEntryPoint;
import io.sc.platform.security.handler.PlatformAuthenticationFailureHandler;
import io.sc.platform.security.handler.PlatformAuthenticationSuccessHandler;
import io.sc.platform.security.service.SecurityConfigureService;
import io.sc.platform.security.SecurityProperties;
import io.sc.platform.security.handler.*;
import io.sc.platform.security.service.SecurityConfigureService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -26,6 +24,7 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
/**
* 框架安全自动配置类
@ -41,6 +40,7 @@ public class WebSecurityAutoConfiguration {
@Autowired private UserDetailsService userDetailsService;
@Autowired private PasswordEncoder passwordEncoder;
@Autowired private AuthenticationProvider authenticationProvider;
@Autowired private AuditLogPersistenterManager auditLogPersistenterManager;
/**
* 认证成功处理器
@ -49,7 +49,7 @@ public class WebSecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AuthenticationSuccessHandler authenticationSuccessHandler(){
return new PlatformAuthenticationSuccessHandler();
return new PlatformAuthenticationSuccessHandler(securityProperties,auditLogPersistenterManager);
}
/**
@ -59,7 +59,17 @@ public class WebSecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AuthenticationFailureHandler authenticationFailureHandler(){
return new PlatformAuthenticationFailureHandler(securityProperties.getFormLogin().getFailureUrl());
return new PlatformAuthenticationFailureHandler(securityProperties,auditLogPersistenterManager);
}
/**
* 退出处理器
* @return 退出处理器
*/
@Bean
@ConditionalOnMissingBean
public LogoutSuccessHandler logoutSuccessHandler(){
return new PlatformLogoutSuccessHandler(auditLogPersistenterManager);
}
/**
@ -84,12 +94,13 @@ public class WebSecurityAutoConfiguration {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 300)
public SecurityFilterChain platformWebSecurityFilterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain loginFormWebSecurityFilterChain(HttpSecurity http) throws Exception {
if(!runtimeService.isReady()) {
return http.csrf(csrfConfigurer -> {
csrfConfigurer.disable();
}).build();
}
return http
// 在匹配了上述 SecurityFilterChain 规则的基础上进行权限控制
.authorizeRequests(authorizeRequests -> {
@ -127,6 +138,7 @@ public class WebSecurityAutoConfiguration {
.logout(logoutConfigurer -> {
logoutConfigurer.logoutUrl(securityProperties.getLogout().getLogoutUrl());
logoutConfigurer.logoutSuccessUrl(securityProperties.getLogout().getLogoutSuccessUrl());
logoutConfigurer.logoutSuccessHandler(logoutSuccessHandler());
})
.headers(headersConfigurer -> {
//接收从 iframe 发送的请求
@ -136,6 +148,7 @@ public class WebSecurityAutoConfiguration {
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint())
.accessDeniedHandler(accessDeniedHandler()).and()
.build();
}
}

2
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/PlatformOauth2AuthorizationServerAutoConfiguration.java

@ -62,7 +62,7 @@ public class PlatformOauth2AuthorizationServerAutoConfiguration {
}
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain auth2AuthorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
if(!runtimeService.isReady()) {
return http.csrf(csrfConfigurer -> {
csrfConfigurer.disable();

2
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/PlatformWebSecurityAutoConfiguration.java

@ -34,7 +34,7 @@ public class PlatformWebSecurityAutoConfiguration {
}
@Bean
public SecurityFilterChain platformWebSecurityFilterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain auth2WebSecurityFilterChain(HttpSecurity http) throws Exception {
if(!runtimeService.isReady()) {
return http.csrf(csrfConfigurer -> {
csrfConfigurer.disable();

2
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/SecurityAutoConfiguration.java

@ -39,7 +39,7 @@ public class SecurityAutoConfiguration {
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain auth2AuthorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
// Enable OpenID Connect 1.0
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());

2
io.sc.platform.security.oauth2.server.resource/src/main/java/io/sc/platform/security/oauth2/server/resource/configure/PlatformOauth2ResourceServerAutoConfiguration.java

@ -48,7 +48,7 @@ public class PlatformOauth2ResourceServerAutoConfiguration {
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
SecurityFilterChain frameworkResourceServerSecurityFilterChain(HttpSecurity http) throws Exception {
SecurityFilterChain oauth2ResourceServerSecurityFilterChain(HttpSecurity http) throws Exception {
if(!runtimeService.isReady()) {
return http.csrf(csrfConfigurer -> {
csrfConfigurer.disable();

1
io.sc.platform.security/src/main/java/io/sc/platform/security/autoconfigure/PlatformWebSecurityAutoConfiguration.java

@ -2,7 +2,6 @@ package io.sc.platform.security.autoconfigure;
import io.sc.platform.security.autoconfigure.support.EncodePasswordAuthenticationProvider;
import io.sc.platform.security.service.impl.UserDetailsServiceImpl;
import org.apache.catalina.security.SecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

37
io.sc.platform.security/src/main/java/io/sc/platform/security/handler/PlatformAuthenticationFailureHandler.java

@ -1,12 +1,18 @@
package io.sc.platform.security.handler;
import io.sc.platform.core.response.ResponseWrapper;
import io.sc.platform.core.response.ResponseWrapperBuilder;
import io.sc.platform.core.audit.AuditLog;
import io.sc.platform.core.enums.AuditLogAction;
import io.sc.platform.core.enums.AuditLogStatus;
import io.sc.platform.core.service.AuditLogPersistenter;
import io.sc.platform.core.service.AuditLogPersistenterManager;
import io.sc.platform.core.util.HttpServletRequestUtil;
import io.sc.platform.core.util.ObjectMapper4Json;
import io.sc.platform.core.util.IpUtil;
import io.sc.platform.security.SecurityProperties;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -14,12 +20,33 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class PlatformAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
public PlatformAuthenticationFailureHandler(String defaultFailureUrl) {
setDefaultFailureUrl(defaultFailureUrl);
private SecurityProperties securityProperties;
private AuditLogPersistenterManager auditLogPersistenterManager;
public PlatformAuthenticationFailureHandler(SecurityProperties securityProperties,AuditLogPersistenterManager auditLogPersistenterManager) {
this.auditLogPersistenterManager =auditLogPersistenterManager;
setDefaultFailureUrl(securityProperties.getFormLogin().getFailureUrl());
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
AuditLogPersistenter logger =auditLogPersistenterManager.getAuditLogPersister();
if(logger!=null){
AuditLog auditLog =new AuditLog();
auditLog.setAction(AuditLogAction.LOGIN.getValue());
auditLog.setExecuteTime(10L);
auditLog.setClassName(UsernamePasswordAuthenticationFilter.class.getName());
auditLog.setMethodName("attemptAuthentication");
auditLog.setHttpMethod("POST");
auditLog.setStatus(AuditLogStatus.FAILED.getValue());
auditLog.setUrl(request.getRequestURL().toString());
auditLog.setLoginName(request.getParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY));
auditLog.setIp(IpUtil.getRemoteIp(request));
auditLog.setException(exception.getClass().getName());
auditLog.setExceptionMessage(exception.getMessage());
auditLog.setExceptionStackTrace(ExceptionUtils.getStackTrace(exception));
logger.log(auditLog);
}
if(HttpServletRequestUtil.isTextHtmlMediaType(request)) {
super.onAuthenticationFailure(request,response,exception);
} else {

30
io.sc.platform.security/src/main/java/io/sc/platform/security/handler/PlatformAuthenticationSuccessHandler.java

@ -1,12 +1,20 @@
package io.sc.platform.security.handler;
import io.sc.platform.core.audit.AuditLog;
import io.sc.platform.core.enums.AuditLogAction;
import io.sc.platform.core.enums.AuditLogStatus;
import io.sc.platform.core.response.ResponseWrapper;
import io.sc.platform.core.response.ResponseWrapperBuilder;
import io.sc.platform.core.service.AuditLogPersistenter;
import io.sc.platform.core.service.AuditLogPersistenterManager;
import io.sc.platform.core.util.HttpServletRequestUtil;
import io.sc.platform.core.util.IpUtil;
import io.sc.platform.core.util.ObjectMapper4Json;
import io.sc.platform.security.SecurityProperties;
import io.sc.platform.security.util.SecurityUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -14,8 +22,30 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class PlatformAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private SecurityProperties securityProperties;
private AuditLogPersistenterManager auditLogPersistenterManager;
public PlatformAuthenticationSuccessHandler(SecurityProperties securityProperties,AuditLogPersistenterManager auditLogPersistenterManager){
this.securityProperties =securityProperties;
this.auditLogPersistenterManager =auditLogPersistenterManager;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
AuditLogPersistenter logger =auditLogPersistenterManager.getAuditLogPersister();
if(logger!=null){
AuditLog auditLog =new AuditLog();
auditLog.setAction(AuditLogAction.LOGIN.getValue());
auditLog.setExecuteTime(10L);
auditLog.setClassName(UsernamePasswordAuthenticationFilter.class.getName());
auditLog.setMethodName("attemptAuthentication");
auditLog.setHttpMethod("POST");
auditLog.setStatus(AuditLogStatus.SUCCESS.getValue());
auditLog.setUrl(request.getRequestURL().toString());
auditLog.setLoginName(request.getParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY));
auditLog.setIp(IpUtil.getRemoteIp(request));
logger.log(auditLog);
}
if(HttpServletRequestUtil.isTextHtmlMediaType(request)) {
super.onAuthenticationSuccess(request,response,authentication);
}else{

48
io.sc.platform.security/src/main/java/io/sc/platform/security/handler/PlatformLogoutSuccessHandler.java

@ -0,0 +1,48 @@
package io.sc.platform.security.handler;
import io.sc.platform.core.audit.AuditLog;
import io.sc.platform.core.enums.AuditLogAction;
import io.sc.platform.core.enums.AuditLogStatus;
import io.sc.platform.core.service.AuditLogPersistenter;
import io.sc.platform.core.service.AuditLogPersistenterManager;
import io.sc.platform.core.util.IpUtil;
import io.sc.platform.security.support.SecurityUser;
import io.sc.platform.security.util.SecurityUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class PlatformLogoutSuccessHandler implements LogoutSuccessHandler {
private AuditLogPersistenterManager auditLogPersistenterManager;
public PlatformLogoutSuccessHandler(AuditLogPersistenterManager auditLogPersistenterManager){
this.auditLogPersistenterManager =auditLogPersistenterManager;
}
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
AuditLogPersistenter logger =auditLogPersistenterManager.getAuditLogPersister();
if(logger!=null){
AuditLog auditLog =new AuditLog();
auditLog.setAction(AuditLogAction.LOGOUT.getValue());
auditLog.setExecuteTime(10L);
auditLog.setClassName(LogoutHandler.class.getName());
auditLog.setMethodName("logout");
auditLog.setHttpMethod("POST");
auditLog.setStatus(AuditLogStatus.SUCCESS.getValue());
auditLog.setUrl(request.getRequestURL().toString());
if(authentication!=null && authentication.getPrincipal() instanceof SecurityUser){
auditLog.setLoginName(((SecurityUser)authentication.getPrincipal()).getLoginName());
}
auditLog.setIp(IpUtil.getRemoteIp(request));
logger.log(auditLog);
}
}
}

2
io.sc.platform.security/src/main/java/io/sc/platform/security/service/impl/UserDetailsServiceImpl.java

@ -1,5 +1,7 @@
package io.sc.platform.security.service.impl;
import io.sc.platform.core.annotation.AuditLog;
import io.sc.platform.core.enums.AuditLogAction;
import io.sc.platform.security.service.support.*;
import io.sc.platform.security.support.SecurityRole;
import io.sc.platform.security.support.SecurityUser;

4
io.sc.platform.system.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.system.frontend",
"version": "8.1.20",
"version": "8.1.22",
"description": "",
"private": false,
"keywords": [],
@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.87",
"platform-core": "8.1.94",
"quasar": "2.14.2",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

22
io.sc.platform.system.frontend/src/i18n/messages.json

@ -51,6 +51,8 @@
"system.shared.selectMenu.grid.toolbar.save":"Save",
"system.shared.selectMenu.grid.toolbar.save.tip":"Are you sure to update menus?",
"system.shared.importMenuPlugin.dialog.title": "Import Menu Plugins",
"system.corporation.grid.title":"Corporation Tree",
"system.corporation.grid.toolbar.addTop":"Add Top Corporation",
"system.corporation.grid.toolbar.addChild":"Add Child Corporation",
@ -66,19 +68,29 @@
"system.role.grid.title":"Role List",
"system.org.grid.title":"Organization Tree",
"system.org.grid.toolbar.addTop":"Add Top Org",
"system.org.grid.toolbar.addChild":"Add Child Org",
"system.menu.grid.title":"Menu Tree",
"system.menu.grid.toolbar.addTop":"Add Top Menu",
"system.menu.grid.toolbar.addChild":"Add Child Menu",
"system.menu.grid.entity.titleI18nKey":"Title i18n Key",
"system.menu.grid.entity.routeName":"Title i18n Key",
"system.menu.grid.entity.javaScript":"JavaScript",
"system.menu.grid.entity.routeName":"Route Name",
"system.menu.grid.entity.routeQuery":"Route Query",
"system.menu.grid.entity.url":"URL",
"system.menu.grid.entity.urlOpenType":"URL Open Type",
"system.announcementManager.grid.title":"Announcement List",
"system.notification.grid.title":"Messages List",
"system.i18n.grid.toolbar.removeAll.tip":"Are you sure to remove ALL i18n messages?",
"system.i18n.grid.toolbar.importAll.tip":"Are you sure to import ALL i18n messages?",
"system.monitor.log.viewer.logRows":"The Last Row Count of Log File",
"system.monitor.log.viewer.autoRefresh":"Auto Refresh(1 / 2 sec, stop after 300 times auto refreshed)",
"system.monitor.log.viewer.action.refreshNow":"Refresh",
"system.monitor.log.download.grid.title":"Log File List",
"system.monitor.log.level.grid.title":"Log Level List",
"system.monitor.log.level.entity.configuredLevel":"Configured",
"system.monitor.log.level.entity.effectiveLevel":"Effective",
"system.monitor.log.tab.view":"Log View",
"system.monitor.log.tab.download":"Log Download",

22
io.sc.platform.system.frontend/src/i18n/messages_tw_CN.json

@ -87,17 +87,35 @@
"system.shared.selectMenu.grid.toolbar.save":"保存",
"system.shared.selectMenu.grid.toolbar.save.tip":"您确定要更新菜單吗?",
"system.shared.importMenuPlugin.dialog.title": "導入菜單插件",
"system.announcement.gridTitle":"公告列表",
"system.monitor.auditlog.gridTitle":"審計日誌列表",
"system.menu.action.addTop":"新增頂級菜單",
"system.menu.action.addChild":"新增子菜單",
"system.menu.grid.title":"菜單樹",
"system.menu.grid.entity.titleI18nKey":"標題多語言消息鍵",
"system.menu.grid.entity.javaScript":"JavaScript",
"system.menu.grid.entity.routeName":"路由名稱",
"system.menu.grid.entity.routeQuery":"路由查詢參數",
"system.menu.grid.entity.url":"URL",
"system.menu.grid.entity.urlOpenType":"URL 打開方式",
"system.announcementManager.grid.title":"公告列表",
"system.notification.grid.title":"消息列表",
"system.i18n.grid.toolbar.removeAll.tip":"您確定要刪除所有多語言消息嗎?",
"system.i18n.grid.toolbar.importAll.tip":"您確定要所導入所有多語言消息嗎?",
"system.monitor.log.viewer.logRows":"顯示日誌文件最後行數",
"system.monitor.log.viewer.autoRefresh":"自動刷新(頻率:1次/2秒,超過 300 次后停止自動刷新)",
"system.monitor.log.viewer.action.refreshNow":"刷新",
"system.monitor.log.download.grid.title":"日誌文件列表",
"system.monitor.log.level.grid.title":"日誌級別列表",
"system.monitor.log.level.entity.configuredLevel":"配置級別",
"system.monitor.log.level.entity.effectiveLevel":"生效級別",
"system.corporation.action.addTop":"新增頂級法人",
"system.corporation.action.addChild":"新增子法人",

22
io.sc.platform.system.frontend/src/i18n/messages_zh_CN.json

@ -51,6 +51,8 @@
"system.shared.selectMenu.grid.toolbar.save":"保存",
"system.shared.selectMenu.grid.toolbar.save.tip":"您确定要更新菜单吗?",
"system.shared.importMenuPlugin.dialog.title": "导入菜单插件",
"system.corporation.grid.title":"法人树",
"system.corporation.grid.toolbar.addTop":"新增顶级法人",
"system.corporation.grid.toolbar.addChild":"新增子法人",
@ -66,18 +68,30 @@
"system.role.grid.title":"角色列表",
"system.org.grid.title":"机构树",
"system.org.grid.toolbar.addTop":"新增顶级机构",
"system.org.grid.toolbar.addChild":"新增子机构",
"system.menu.grid.title":"菜单树",
"system.menu.grid.toolbar.addTop":"新增顶级菜单",
"system.menu.grid.toolbar.addChild":"新增子菜单",
"system.menu.grid.entity.titleI18nKey":"标题多语言消息键",
"system.menu.grid.entity.javaScript":"JavaScript",
"system.menu.grid.entity.routeName":"路由名称",
"system.menu.grid.entity.routeQuery":"路由查询参数",
"system.menu.grid.entity.url":"URL",
"system.menu.grid.entity.urlOpenType":"URL 打开方式",
"system.announcementManager.grid.title":"公告列表",
"system.notification.grid.title":"消息列表",
"system.i18n.grid.toolbar.removeAll.tip":"您确定要删除所有多语言消息吗?",
"system.i18n.grid.toolbar.importAll.tip":"您确定要导入所有多语言消息吗?",
"system.monitor.log.viewer.logRows":"显示日志文件最后行数",
"system.monitor.log.viewer.autoRefresh":"自动刷新(频率:1次/2秒,超过 300 次后停止自动刷新)",
"system.monitor.log.viewer.action.refreshNow":"刷新",
"system.monitor.log.download.grid.title":"日志文件列表",
"system.monitor.log.level.grid.title":"日志级别列表",
"system.monitor.log.level.entity.configuredLevel":"配置级别",
"system.monitor.log.level.entity.effectiveLevel":"生效级别",
"system.announcement.gridTitle":"公告列表",
"system.monitor.auditlog.gridTitle":"审计日志列表",

20
io.sc.platform.system.frontend/src/views/corporation/Corporation.vue

@ -5,7 +5,6 @@
<w-grid
ref="corporationTreeGridRef"
:tree="true"
dense-body
:title="$t('system.corporation.grid.title')"
:data-url="Environment.apiContextPath('/api/system/corporation')"
selection="multiple"
@ -20,12 +19,12 @@
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', ['addTop', 'addChild'], 'edit', 'remove', 'separator', 'view']"
:columns="[
{ width: 250, name: 'name', label: $t('name') },
{ name: 'code', label: $t('code') },
{ name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
{ width: 100, name: 'enable', label: $t('status'), format: Formater.enableTag() },
{ width: '100%', name: 'name', label: $t('name') },
{ width: 150, name: 'code', label: $t('code') },
{ width: 90, name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) },
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 110, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
{ width: 80, name: 'enable', label: $t('status'), format: Formater.enableTag() },
]"
:editor="{
dialog: {
@ -65,6 +64,13 @@
userGridRef?.refresh();
}
"
@request-data-before="
() => {
console.log('>>>>>');
currentSelectedCorporationCode = '';
userGridRef?.refresh();
}
"
></w-grid>
</div>
</template>

107
io.sc.platform.system.frontend/src/views/dictionary/Dictionary.vue

@ -1,62 +1,63 @@
<template>
<div>
<platform-grid
<w-grid
ref="dictionaryGridRef"
:table-props="{ borderded: false, flat: true }"
:table-title="dictionaryConfigure.tableTitle"
:table-init-load-data="dictionaryConfigure.tableInitLoadData"
:table-row-key="dictionaryConfigure.tableRowKey"
:table-data-url="dictionaryConfigure.tableDataUrl"
:table-columns="dictionaryConfigure.tableColumns"
:table-buttons="dictionaryConfigure.tableButtons"
:add-form-props="dictionaryConfigure.addFormProps"
:table-show-sort-no="false"
:table-dense="true"
:table-row-drag="true"
:table-pagination="dictionaryConfigure.tablePagination"
@row-drag-drop-after="rowDragDropAfter"
>
</platform-grid>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, axios } from 'platform-core';
const { t } = useI18n();
const dictionaryGridRef = ref();
const dictionaryConfigure = {
tableTitle: '数据字典列表',
tableInitLoadData: true,
tableRowKey: 'id',
tableDataUrl: Environment.apiContextPath('/api/system/dictionary'),
tablePagination: {
sortBy: 'order',
descending: false,
reqPageStart: 0,
rowsPerPage: 0,
:title="$t('system.user.grid.title')"
selection="multiple"
:data-url="Environment.apiContextPath('/api/system/dictionary')"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'text' },
{ name: 'dataComeFrom', label: $t('dataComeFrom'), type: 'select', options: Options.enum(DataComeFromEnum), queryOperator: 'equals' },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'export']"
:columns="[
{ width: 200, name: 'code', label: $t('code') },
{ width: 200, name: 'value', label: $t('value') },
{ width: '100%', name: 'value', label: $t('displayValue'), format: (value) => $t(value) },
{ width: 100, name: 'order', label: $t('order'), align: 'right' },
{ width: 100, name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) },
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 100, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
height: '300px',
},
tableColumns: [
{ name: 'code', label: t('code') },
{ name: 'value', label: t('value') },
{ name: 'value', label: t('displayValue'), format: (value) => t(value) },
{ name: 'order', label: t('order') },
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('code'), type: 'text', required: true },
{ name: 'value', label: $t('value'), type: 'text', required: true },
{ name: 'order', label: $t('order') },
],
tableButtons: ['refresh', 'add', 'edit', 'delete'],
addFormProps: {
dialogInitWidth: '50%',
dialogInitHeight: '90%',
formColsNumber: 1,
formColsAuto: false,
formFields: [
{ modelName: 'code', label: t('code'), type: 'text', required: true },
{ modelName: 'value', label: t('value'), type: 'text', required: true },
{ modelName: 'order', label: t('order'), type: 'text', required: true },
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'code', label: $t('code') },
{ name: 'value', label: $t('value') },
{ name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
};
}"
@row-click="(evt, row, index) => {}"
></w-grid>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Environment, axios, EnumTools, Formater, Options } from 'platform-core';
const dictionaryGridRef = ref();
const DataComeFromEnum = await EnumTools.fetch('io.sc.platform.orm.api.enums.DataComeFrom');
const rowDragDropAfter = () => {
const rows = dictionaryGridRef.value.getRowsFun();

158
io.sc.platform.system.frontend/src/views/i18n/I18n.vue

@ -1,70 +1,102 @@
<template>
<div>
<platform-grid
<w-grid
ref="i18nGridRef"
:table-props="{ borderded: false, flat: true }"
:table-title="i18nConfigure.tableTitle"
:table-init-load-data="i18nConfigure.tableInitLoadData"
:table-row-key="i18nConfigure.tableRowKey"
:table-data-url="i18nConfigure.tableDataUrl"
:table-columns="i18nConfigure.tableColumns"
:table-buttons="i18nConfigure.tableButtons"
:add-form-props="i18nConfigure.addFormProps"
:table-show-sort-no="false"
:table-dense="true"
:table-row-drag="true"
:table-pagination="i18nConfigure.tablePagination"
>
</platform-grid>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, axios } from 'platform-core';
const { t } = useI18n();
const Language = {
en: '英文',
zh_CN: '简体中文',
tw_CN: '繁体中文',
};
const LanguageOptions = [
{ label: '英文', value: 'en' },
{ label: '简体中文', value: 'zh_CN' },
{ label: '繁体中文', value: 'tw_CN' },
];
const i18nGridRef = ref();
const i18nConfigure = {
tableTitle: '多语言消息列表',
tableInitLoadData: true,
tableRowKey: 'id',
tableDataUrl: Environment.apiContextPath('/api/system/i18n'),
tablePagination: {
sortBy: 'code',
descending: false,
reqPageStart: 0,
rowsPerPage: 20,
:title="$t('system.user.grid.title')"
selection="multiple"
:data-url="Environment.apiContextPath('/api/system/i18n')"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'text' },
{ name: 'message', label: $t('i18nMessage'), type: 'text' },
{ name: 'lang', label: $t('language'), type: 'select', options: Options.enum(LanguageEnum), queryOperator: 'equals' },
{ name: 'dataComeFrom', label: $t('dataComeFrom'), type: 'select', options: Options.enum(DataComeFromEnum), queryOperator: 'equals' },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'query',
'refresh',
'separator',
'add',
'clone',
'edit',
'remove',
{
name: 'deleteAll',
label: $t('deleteAll'),
icon: 'bi-trash3',
click: () => {
DialogManager.confirm($t('system.i18n.grid.toolbar.removeAll.tip'), () => {
axios.post(Environment.apiContextPath('/api/system/i18n/removeMessages')).then(() => {
NotifyManager.info($t('operationSuccess'));
i18nGridRef.refresh();
});
});
},
},
tableColumns: [
{ name: 'code', label: t('code') },
{ name: 'lang', label: t('lang'), format: (value) => Language[value] },
{ name: 'message', label: t('message') },
'separator',
{
name: 'importAll',
label: $t('import'),
icon: 'bi-arrow-right-circle',
click: () => {
DialogManager.confirm($t('system.i18n.grid.toolbar.importAll.tip'), () => {
axios.post(Environment.apiContextPath('/api/system/i18n/importMessages')).then(() => {
NotifyManager.info($t('operationSuccess'));
i18nGridRef.refresh();
});
});
},
},
'separator',
'view',
'export',
]"
:columns="[
{ name: 'code', label: $t('code') },
{ name: 'lang', label: $t('language'), format: Formater.enum(LanguageEnum) },
{ name: 'message', label: $t('i18nMessage') },
{ width: 100, name: 'dataComeFrom', label: $t('dataComeFrom'), format: Formater.enum(DataComeFromEnum) },
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 100, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
height: '300px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('code'), type: 'text', required: true },
{ name: 'lang', label: $t('language'), type: 'select', required: true, options: Options.enum(LanguageEnum) },
{ name: 'message', label: $t('i18nMessage'), type: 'text', required: true },
],
tableButtons: ['refresh', 'add', 'edit', 'delete'],
addFormProps: {
dialogInitWidth: '50%',
dialogInitHeight: '90%',
formColsNumber: 1,
formColsAuto: false,
formFields: [
{ modelName: 'code', label: t('code'), type: 'text', required: true },
{ modelName: 'lang', label: t('lang'), type: 'select', required: true, options: LanguageOptions },
{ modelName: 'message', label: t('message'), type: 'text', required: true },
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'code', label: $t('code') },
{ name: 'lang', label: $t('language') },
{ name: 'message', label: $t('i18nMessage') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
};
}"
@row-click="(evt, row, index) => {}"
></w-grid>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment, EnumTools, Formater, Options, DialogManager, NotifyManager } from 'platform-core';
const i18nGridRef = ref();
const DataComeFromEnum = await EnumTools.fetch('io.sc.platform.orm.api.enums.DataComeFrom');
const LanguageEnum = await EnumTools.fetch('io.sc.platform.core.enums.Language');
</script>

135
io.sc.platform.system.frontend/src/views/menu/ImportMenuPluginDialog.vue

@ -0,0 +1,135 @@
<template>
<w-dialog
ref="dialogRef"
:title="$t('system.shared.importMenuPlugin.dialog.title')"
width="800px"
height="500px"
:can-maximize="false"
:buttons="[
{
label: $t('confirm'),
click: () => {
const ids = Tools.extractProperties(gridRef.getSelectedRows(), 'id');
emit('afterSelected', ids, dialogRef);
},
},
]"
>
<div class="px-2">
<w-grid
:tree="true"
dense-body
:data-url="Environment.apiContextPath('/api/system/menu/getAllMenuPlugins')"
primary-key="id"
foreign-key="parentId"
selection="multiple"
:checkbox-selection="true"
tick-strategy="strict"
:pageable="false"
:config-button="false"
:full-screen-button="false"
:tree-icon="
(row) => {
if (row.type === 'SEPARATOR') {
return { name: 'bi-dash-lg' };
} else if (row.type === 'ROUTE_ACTION') {
return { name: 'sym_o_crop_16_9' };
} else {
return { name: row.icon };
}
}
"
:columns="[
{
width: '100%',
name: 'titleI18nKey',
label: $t('name'),
sortable: false,
format: (value, row) => {
if (row.type === 'SEPARATOR') {
return `<hr style='width:100px'/>`;
} else if (row.type === 'ROUTE_ACTION') {
return $t(row.i18nKey);
} else {
return $t(value);
}
},
},
{ width: 100, name: 'order', label: $t('order'), align: 'right', sortable: false },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'parentId', label: $t('parentId') },
{
name: 'titleI18nKey',
label: $t('system.menu.grid.entity.titleI18nKey'),
format: (value) => {
return value;
},
},
{ name: 'icon', label: $t('icon') },
{ name: 'order', label: $t('order') },
{ name: 'javaScript', label: $t('system.menu.grid.entity.javaScript') },
{ name: 'url', label: $t('system.menu.grid.entity.url') },
{ name: 'urlOpenType', label: $t('system.menu.grid.entity.urlOpenType') },
{ name: 'routeName', label: $t('system.menu.grid.entity.routeName') },
{
name: 'routeQuery',
label: $t('system.menu.grid.entity.routeQuery'),
format: (value) => {
return JSON.stringify(value);
},
},
{ name: 'configurationFileUrl', label: $t('url') },
],
},
}"
></w-grid>
</div>
</w-dialog>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, Tools, EnumTools, Options, Formater } from 'platform-core';
const props = defineProps({
opener: { type: Object, default: undefined },
fetchDataUrl: { type: String, default: '' },
foreignKey: { type: String, default: '' },
foreignValue: { type: String, default: '' },
});
const emit = defineEmits<{
(e: 'afterSelected', ids: string[], dialogComponent: any): void;
}>();
const { t } = useI18n();
const dialogRef = ref();
const gridRef = ref();
const foreignKeyRef = ref();
const open = (foreignKey: string) => {
foreignKeyRef.value = foreignKey;
dialogRef.value.show();
nextTick(() => {
gridRef.value.refresh();
});
};
const close = () => {
dialogRef.value.hide();
};
defineExpose({
open,
close,
});
const DataComeFromEnum = await EnumTools.fetch('io.sc.platform.orm.api.enums.DataComeFrom');
</script>

54
io.sc.platform.system.frontend/src/views/menu/Menu.vue

@ -34,7 +34,10 @@
{
name: 'import',
label: $t('import'),
click: () => {},
icon: 'bi-arrow-right-circle',
click: () => {
importMenuPluginDialogRef.open();
},
},
'separator',
'view',
@ -44,7 +47,7 @@
{
width: '100%',
name: 'titleI18nKey',
label: $t('name'),
label: $t('system.menu.grid.entity.titleI18nKey'),
sortable: false,
format: (value, row) => {
if (row.type === 'SEPARATOR') {
@ -66,33 +69,57 @@
:editor="{
dialog: {
width: '600px',
height: '550px',
height: '450px',
},
form: {
colsNum: 1,
fields: [
{ name: 'type', label: $t('type'), type: 'select', required: true, options: Options.enum(MenuTypeEnum, false) },
{ name: 'titleI18nKey', label: $t('titleI18nKey'), type: 'text', required: true },
{ name: 'type', label: $t('type'), type: 'select', required: true, options: Options.enum(MenuTypeEnum, false), defaultValue: 'GROUP' },
{ name: 'titleI18nKey', label: $t('system.menu.grid.entity.titleI18nKey'), type: 'text', required: true },
{ name: 'icon', label: $t('icon'), type: 'text' },
{ name: 'enable', label: $t('enable'), type: 'checkbox', defaultValue: true },
{ name: 'order', label: $t('order'), type: 'text' },
{
name: 'javaScript',
label: $t('javascript'),
label: $t('system.menu.grid.entity.javaScript'),
type: 'textarea',
showIf: (form) => {
return form.getFieldValue('type') === 'JAVASCRIPT';
},
},
{
name: 'routeName',
label: $t('system.menu.grid.entity.routeName'),
type: 'text',
enableIf: (row) => {
console.log(row);
showIf: (form) => {
return form.getFieldValue('type') === 'ROUTE';
},
},
{
name: 'routeQuery',
label: $t('system.menu.grid.entity.routeQuery'),
type: 'text',
showIf: (form) => {
return form.getFieldValue('type') === 'ROUTE';
},
},
{
name: 'url',
label: $t('url'),
type: 'text',
showIf: (form) => {
return form.getFieldValue('type') === 'URL';
},
},
{ name: 'routeName', label: $t('routeName'), type: 'text' },
{ name: 'routeQuery', label: $t('routeQuery'), type: 'text' },
{ name: 'url', label: $t('url'), type: 'text' },
{
name: 'urlOpenType',
label: $t('urlOpenType'),
label: $t('system.menu.grid.entity.urlOpenType'),
type: 'select',
options: Options.enum(UrlOpenTypeEnum, false),
defaultValue: 'NEW_WINDOW',
showIf: (form) => {
return form.getFieldValue('type') === 'URL';
},
},
],
},
@ -167,6 +194,7 @@
</q-tab-panels>
</div>
</template>
<ImportMenuPluginDialog ref="importMenuPluginDialogRef"></ImportMenuPluginDialog>
<AddTopMenuDialog ref="addTopMenuDialog" title="菜单" :maximized="false" width="50%" height="340px"></AddTopMenuDialog>
<AddMenuDialog ref="addMenuDialog" title="菜单" :maximized="false" width="50%" height="440px"></AddMenuDialog>
<EditMenuDialog ref="editMenuDialog" title="菜单" :maximized="false" width="50%" height="440px"></EditMenuDialog>
@ -177,6 +205,7 @@ import { ref } from 'vue';
import { Environment, axios, EnumTools, Formater, Options } from 'platform-core';
import SelectRoleGrid from '../shared/SelectRoleGrid.vue';
import SelectOrgTreeGrid from '../shared/SelectOrgTreeGrid.vue';
import ImportMenuPluginDialog from './ImportMenuPluginDialog.vue';
import AddTopMenuDialog from './AddTopMenuDialog.vue';
import AddMenuDialog from './AddMenuDialog.vue';
import EditMenuDialog from './EditMenuDialog.vue';
@ -189,6 +218,7 @@ console.log(Options.enum(UrlOpenTypeEnum, false));
const menuTreeGridRef = ref();
const roleGridRef = ref();
const orgTreeGridRef = ref();
const importMenuPluginDialogRef = ref();
const selectedTabRef = ref('role');
const currentSelectedMenuId = ref('');

166
io.sc.platform.system.frontend/src/views/monitor/AuditLog.vue

@ -1,94 +1,88 @@
<template>
<div>
<platform-grid
ref="auditlogGridRef"
:table-props="{ borderded: false, flat: true }"
:query-form-cols-number="auditlogConfigure.queryFormColsNumber"
:query-form-cols-auto="auditlogConfigure.queryFormColsAuto"
:table-title="auditlogConfigure.tableTitle"
:table-row-key="auditlogConfigure.tableRowKey"
:table-init-load-data="auditlogConfigure.tableInitLoadData"
:table-data-url="auditlogConfigure.tableDataUrl"
:table-show-sort-no="false"
:table-columns="auditlogConfigure.tableColumns"
:table-left-column-sticky-number="auditlogConfigure.tableLeftColumnStickyNumber"
:table-buttons="auditlogConfigure.tableButtons"
:query-form-fields="auditlogConfigure.queryFormFields"
:table-pagination="auditlogConfigure.tablePagination"
:add-form-props="auditlogConfigure.addFormProps"
:table-dense="false"
<w-grid
ref="auditLogGridRef"
:title="$t('system.user.grid.title')"
selection="multiple"
:data-url="Environment.apiContextPath('/api/system/monitor/auditlog')"
:query-form-fields="[
{ name: 'status', label: $t('status'), type: 'select', queryOperator: 'equals', options: Options.successFailed() },
{ name: 'action', label: $t('action'), type: 'select', queryOperator: 'equals', options: Options.enum(AuditLogActionEnum) },
{ name: 'loginName', label: $t('loginName'), type: 'text' },
{ name: 'startDate', label: $t('startDate'), type: 'text' },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'view', 'export']"
:pagination="{
sortBy: 'startDate',
descending: true,
}"
:checkbox-selection="false"
:columns="[
{ width: 80, name: 'status', label: $t('status'), format: Formater.successTag() },
{
width: 80,
name: 'action',
label: $t('action'),
format: Formater.enum(AuditLogActionEnum),
},
{ width: 150, name: 'startDate', label: $t('startDate') },
{ width: 100, name: 'executeTime', label: $t('executeTime') },
{ width: 200, name: 'loginName', label: $t('loginName') },
{ width: 100, name: 'httpMethod', label: $t('httpMethod') },
{ width: 200, name: 'url', label: $t('url') },
{ width: 100, name: 'ip', label: $t('ip') },
{ width: 200, name: 'className', label: $t('className') },
{ width: 100, name: 'methodName', label: $t('methodName') },
{ width: 100, name: 'oldValue', label: $t('oldValue') },
{ width: 100, name: 'newValue', label: $t('newValue') },
{ width: 100, name: 'exception', label: $t('exception') },
{ width: 100, name: 'exceptionMessage', label: $t('exceptionMessage') },
{ width: 100, name: 'exceptionStackTrace', label: $t('exceptionStackTrace') },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{
name: 'status',
label: $t('status'),
format: (value) => {
return value;
},
},
{
name: 'action',
label: $t('action'),
format: (value) => {
return value;
},
},
{ name: 'startDate', label: $t('startDate') },
{ name: 'executeTime', label: $t('executeTime') },
{ name: 'loginName', label: $t('loginName') },
{ name: 'httpMethod', label: $t('httpMethod') },
{ name: 'url', label: $t('url') },
{ name: 'ip', label: $t('ip') },
{ name: 'className', label: $t('className') },
{ name: 'methodName', label: $t('methodName') },
{ name: 'oldValue', label: $t('oldValue') },
{ name: 'newValue', label: $t('newValue') },
{ name: 'exception', label: $t('exception') },
{ name: 'exceptionMessage', label: $t('exceptionMessage') },
{ name: 'exceptionStackTrace', label: $t('exceptionStackTrace') },
],
},
}"
@row-click="(evt, row, index) => {}"
>
</platform-grid>
</div>
</w-grid>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, axios } from 'platform-core';
import { Environment, EnumTools, Formater, Options } from 'platform-core';
const { t } = useI18n();
const AuditLogActionEnum = await EnumTools.fetch('io.sc.platform.core.enums.AuditLogAction');
const Status = {
success: t('success'),
failed: t('failed'),
};
const Actions = {
'io.sc.platform.core.audit.AuditLogAction.ADD': t('io.sc.platform.core.audit.AuditLogAction.ADD'),
'io.sc.platform.core.audit.AuditLogAction.REMOVE': t('io.sc.platform.core.audit.AuditLogAction.REMOVE'),
'io.sc.platform.core.audit.AuditLogAction.UPDATE': t('io.sc.platform.core.audit.AuditLogAction.UPDATE'),
'io.sc.platform.core.audit.AuditLogAction.QUERY': t('io.sc.platform.core.audit.AuditLogAction.QUERY'),
'io.sc.platform.core.audit.AuditLogAction.FIND_ONE': t('io.sc.platform.core.audit.AuditLogAction.FIND_ONE'),
'io.sc.platform.core.audit.AuditLogAction.EXPORT': t('io.sc.platform.core.audit.AuditLogAction.EXPORT'),
'io.sc.platform.core.audit.AuditLogAction.LOGIN': t('io.sc.platform.core.audit.AuditLogAction.LOGIN'),
'io.sc.platform.core.audit.AuditLogAction.LOGOUT': t('io.sc.platform.core.audit.AuditLogAction.LOGOUT'),
};
console.log(Status);
const auditlogConfigure = {
queryFormColsNumber: 4,
queryFormColsAuto: false,
hideBottom: false,
tableInitLoadData: true,
tableLeftColumnStickyNumber: 0,
tableTitle: t('system.monitor.auditlog.gridTitle'),
tableRowKey: 'id',
tableDataUrl: Environment.apiContextPath('/api/system/monitor/auditlog'),
tablePagination: {
sortBy: 'startDate',
descending: true,
reqPageStart: 0,
rowsPerPage: 10,
},
queryFormFields: [],
tableButtons: ['view', 'inFullscreen'],
tableColumns: [
{ width: 60, name: 'status', label: t('status'), format: (value) => Status[value] },
{ width: 60, name: 'action', label: t('action'), format: (value) => Actions[value] },
{ width: 100, name: 'startDate', label: t('startDate') },
{ width: 60, name: 'executeTime', label: t('executeTime') },
{ width: 80, name: 'loginName', label: t('loginName') },
{ width: 60, name: 'httpMethod', label: t('httpMethod') },
{ width: 200, name: 'url', label: t('url') },
{ width: 100, name: 'ip', label: t('ip') },
{ width: 100, name: 'className', label: t('className') },
{ width: 100, name: 'methodName', label: t('methodName') },
{ width: 100, name: 'oldValue', label: t('oldValue') },
{ width: 100, name: 'newValue', label: t('newValue') },
{ width: 100, name: 'exception', label: t('exception') },
{ width: 100, name: 'exceptionMessage', label: t('exceptionMessage') },
{ width: 100, name: 'exceptionStackTrace', label: t('exceptionStackTrace') },
],
addFormProps: {
dialogInitWidth: '50%',
dialogInitHeight: '90%',
formColsNumber: 1,
formColsAuto: false,
formFields: [
{ modelName: 'title', label: t('title'), type: 'text', required: true },
{ modelName: 'content', label: t('content'), type: 'textarea', required: true },
],
},
};
const auditLogGridRef = ref();
</script>

161
io.sc.platform.system.frontend/src/views/monitor/Log.vue

@ -3,21 +3,21 @@
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0">
<q-tab name="view" icon="bi-receipt" :label="$t('system.monitor.log.tab.view')" />
<q-tab name="download" icon="bi-download" :label="$t('system.monitor.log.tab.download')" />
<q-tab name="level" icon="bi-123" :label="$t('system.monitor.log.tab.level')" />
<q-tab name="level" icon="bi-sort-numeric-up" :label="$t('system.monitor.log.tab.level')" />
</q-tabs>
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive>
<q-tab-panel name="view">
<div class="row q-pt-sm">
<div class="col-2">
<q-input v-model="logRows" label="显示日志文件最后行数" outlined dense />
<q-input v-model="logRows" :label="$t('system.monitor.log.viewer.logRows')" outlined dense />
</div>
<div class="col-5 q-pl-sm">
<q-checkbox v-model="autoRefresh" label="自动刷新(频率:1次/2秒,超过 300 次后停止自动刷新)" outlined dense />
<q-checkbox v-model="autoRefresh" :label="$t('system.monitor.log.viewer.autoRefresh')" outlined dense />
</div>
<div class="col-4"></div>
<div class="col-1">
<q-btn label="立即刷新" @click="refreshLog" />
<q-btn :label="$t('system.monitor.log.viewer.action.refreshNow')" @click="refresh" />
</div>
</div>
<div class="row q-pt-sm">
@ -27,47 +27,93 @@
</div>
</q-tab-panel>
<q-tab-panel name="download">
<platform-grid
<w-grid
ref="downloadGridRef"
:table-props="{ borderded: false, flat: true }"
:table-title="downloadConfigure.tableTitle"
:table-init-load-data="downloadConfigure.tableInitLoadData"
:table-row-key="downloadConfigure.tableRowKey"
:table-data-url="downloadConfigure.tableDataUrl"
:table-columns="downloadConfigure.tableColumns"
:table-buttons="downloadConfigure.tableButtons"
:table-show-sort-no="false"
:table-pagination="downloadConfigure.tablePagination"
:table-dense="false"
:title="$t('system.monitor.log.download.grid.title')"
selection="multiple"
:data-url="Environment.apiContextPath('/api/monitor/logger/getLogFiles')"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'refresh',
'separator',
{
name: 'download',
label: t('download'),
icon: 'bi-download',
click: () => {
let url = Environment.apiContextPath('/api/monitor/logger/downloadLogFile?');
url += 'fileName=' + encodeURIComponent(downloadGridRef.getSelectedRows()[0].name);
downloadIframe.src = url;
},
},
]"
:pagination="{
sortBy: 'startDate',
descending: true,
}"
:columns="[
{ width: '100%', name: 'name', label: $t('name') },
{ width: 150, name: 'lastModifyDate', label: $t('lastModifyDate') },
{
width: 200,
name: 'size',
label: $t('size'),
align: 'right',
format: (value, row) => {
return row.commaSize;
},
},
]"
>
</platform-grid>
</w-grid>
</q-tab-panel>
<q-tab-panel name="level">
<platform-grid
<w-grid
ref="levelGridRef"
:table-props="{ borderded: false, flat: true }"
:table-title="levelConfigure.tableTitle"
:table-init-load-data="levelConfigure.tableInitLoadData"
:table-row-key="levelConfigure.tableRowKey"
:table-data-url="levelConfigure.tableDataUrl"
:table-columns="levelConfigure.tableColumns"
:table-buttons="levelConfigure.tableButtons"
:table-show-sort-no="false"
:table-pagination="levelConfigure.tablePagination"
:table-dense="true"
:title="$t('system.monitor.log.level.grid.title')"
selection="multiple"
:data-url="Environment.apiContextPath('/api/monitor/logger/getLogConfigurationLevels')"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'edit']"
:checkbox-selection="false"
:pagination="{
sortBy: 'name',
descending: false,
}"
:query-form-fields="[
{ name: 'name', label: $t('name'), type: 'text' },
{ name: 'configuredLevel', label: $t('system.monitor.log.level.entity.configuredLevel'), type: 'select', options: Options.enum(LogLevelEnum) },
{ name: 'effectiveLevel', label: $t('system.monitor.log.level.entity.effectiveLevel'), type: 'select', options: Options.enum(LogLevelEnum) },
]"
:columns="[
{ width: 700, name: 'name', label: $t('name') },
{ width: 100, name: 'configuredLevel', label: $t('system.monitor.log.level.entity.configuredLevel') },
{ width: 100, name: 'effectiveLevel', label: $t('system.monitor.log.level.entity.effectiveLevel') },
]"
:editor="{
dialog: {
width: '600px',
height: '210px',
},
form: {
colsNum: 1,
fields: [
{ name: 'effectiveLevel', label: $t('system.monitor.log.level.entity.effectiveLevel'), type: 'select', options: Options.enum(LogLevelEnum) },
],
},
}"
>
</platform-grid>
</w-grid>
</q-tab-panel>
</q-tab-panels>
<iframe ref="downloadIframe" src="javascript:;" style="width: 0px; height: 0px"></iframe>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, axios } from 'platform-core';
import { ref, onMounted } from 'vue';
import { Environment, axios, EnumTools, Options } from 'platform-core';
const { t } = useI18n();
const LogLevelEnum = await EnumTools.fetch('io.sc.platform.core.enums.LogLevel');
const selectedTabRef = ref('view');
const logRows = ref(20);
@ -78,54 +124,7 @@ const downloadIframe = ref();
const downloadGridRef = ref();
const levelGridRef = ref();
const downloadConfigure = {
tableTitle: '日志列表',
tableInitLoadData: true,
tablePagination: {
sortBy: 'lastModifyDate',
descending: true,
reqPageStart: 0,
rowsPerPage: 10,
},
tableRowKey: 'name',
tableDataUrl: Environment.apiContextPath('/api/monitor/logger/getLogFiles'),
tableColumns: [
{ width: 100, name: 'name', label: t('name') },
{ width: 100, name: 'lastModifyDate', label: t('lastModifyDate') },
{ width: 100, name: 'size', label: t('size') },
],
tableButtons: [
{
name: 'download',
label: t('download'),
click: () => {
let url = Environment.apiContextPath('/api/monitor/logger/downloadLogFile?');
url += 'fileName=' + encodeURIComponent(downloadGridRef.value.getSelectedRows()[0].name);
downloadIframe.value.src = url;
},
},
],
};
const levelConfigure = {
tableTitle: '日志级别列表',
tableInitLoadData: true,
tablePagination: {
sortBy: 'lastModifyDate',
descending: true,
reqPageStart: 0,
rowsPerPage: 10,
},
tableRowKey: 'name',
tableDataUrl: Environment.apiContextPath('/api/monitor/logger/getLogConfigurationLevels'),
tableColumns: [
{ width: 100, name: 'name', label: t('name') },
{ width: 100, name: 'configuredLevel', label: t('configuredLevel') },
{ width: 100, name: 'effectiveLevel', label: t('effectiveLevel') },
],
};
const refreshLog = () => {
const refresh = () => {
axios
.post(Environment.apiContextPath('/api/monitor/logger/getLogFileContents'), {
rows: logRows.value,
@ -136,6 +135,6 @@ const refreshLog = () => {
};
onMounted(() => {
refreshLog();
refresh();
});
</script>

154
io.sc.platform.system.frontend/src/views/parameter/Parameter.vue

@ -1,73 +1,91 @@
<template>
<q-splitter :model-value="70" class="w-full h-full">
<template #before>
<w-tree-grid ref="parameterTreeGridRef" title="系统参数树" label-key="code" label-i18n @update:selected="parameterSelected" />
</template>
<template #after>
<div class="row">
<div class="col-12">
<q-input v-model="code" label="代码" outlined class="p-1" />
</div>
</div>
<div class="row">
<div class="col-12">
<q-input v-model="value" label="值" outlined class="p-1" />
</div>
</div>
<div class="row">
<div class="col-12 flex justify-center q-gutter-md pt-5">
<q-btn label="保存" color="primary" outlined class="p-1" style="width: 100px" @click="save" />
</div>
</div>
<div class="row" style="height: 200px"></div>
</template>
</q-splitter>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, axios } from 'platform-core';
const { t } = useI18n();
const parameterTreeGridRef = ref();
const id = ref('');
const code = ref('');
const value = ref('');
const parameterSelected = (target) => {
if (target) {
const node = parameterTreeGridRef.value.getNodeById(target);
id.value = node.id;
code.value = node.code;
value.value = node.value;
} else {
id.value = '';
code.value = '';
value.value = '';
<w-grid
ref="parameterTreeGridRef"
:tree="true"
dense-body
:title="$t('system.menu.grid.title')"
:data-url="Environment.apiContextPath('/api/system/parameter')"
selection="multiple"
:checkbox-selection="false"
:pageable="false"
:full-screen-button="false"
default-expand-all
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'refresh',
'separator',
'expand',
{
extend: 'edit',
enableIf: (selecteds) => {
if (selecteds && selecteds.length > 0) {
return !Tools.isUndefinedOrNull(selecteds[0].value);
}
};
return false;
},
},
'separator',
'export',
]"
:columns="[
{
width: 300,
name: 'code',
label: $t('code'),
sortable: false,
format: (value) => {
return $t(value);
},
},
{ width: '100%', name: 'value', label: $t('value'), sortable: false },
{ width: 100, name: 'dataComeFrom', label: $t('dataComeFrom'), sortable: false, format: Formater.enum(DataComeFromEnum) },
{ width: 100, name: 'lastModifier', label: $t('lastModifier'), sortable: false },
{ width: 100, name: 'lastModifyDate', label: $t('lastModifyDate'), sortable: false, format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
height: '250px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('code'), type: 'text' },
{ name: 'value', label: $t('value'), type: 'text' },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'type', label: $t('type') },
{ name: 'name', label: $t('name') },
{ name: 'titleI18nKey', label: $t('titleI18nKey') },
{ name: 'icon', label: $t('icon') },
{ name: 'enable', label: $t('enable') },
{ name: 'order', label: $t('order') },
{ name: 'javaScript', label: $t('javaScript') },
{ name: 'url', label: $t('url') },
{ name: 'urlOpenType', label: $t('urlOpenType') },
{ name: 'routeName', label: $t('routeName') },
{ name: 'routeQuery', label: $t('routeQuery') },
const save = () => {
const node = parameterTreeGridRef.value.getNodeById(id.value);
const data = {
id: node.id,
code: node.code,
value: value.value,
parent: node.parentId,
corporationCode: node.corporationCode,
dataComeFrom: node.dataComeFrom,
creator: node.creator,
createDate: node.createDate,
lastModifier: node.lastModifier,
lastModifyDate: node.lastModifyDate,
};
axios.put(Environment.apiContextPath('/api/system/parameter/') + id.value, data).then((response) => {});
};
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
}"
@row-click="(evt, row, index) => {}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Tools, EnumTools, Formater, Options } from 'platform-core';
onMounted(() => {
axios.get(Environment.apiContextPath('/api/system/parameter?pageable=false&sortBy=code')).then((response) => {
parameterTreeGridRef.value.setNodes(response.data.content);
});
});
const DataComeFromEnum = await EnumTools.fetch('io.sc.platform.orm.api.enums.DataComeFrom');
</script>

2
io.sc.platform.system/src/main/java/io/sc/platform/system/dictionary/jpa/entity/DictionaryEntity.java

@ -47,7 +47,7 @@ public class DictionaryEntity extends CorporationAuditorEntity<DictionaryVo> {
@Override
public DictionaryVo toVo() {
DictionaryVo vo =new DictionaryVo();
CorporationAuditorEntity.toVo(vo,this);
super.toVo(vo);
vo.setId(this.getId());
vo.setCode(this.getCode());
vo.setValue(this.getValue());

2
io.sc.platform.system/src/main/java/io/sc/platform/system/i18n/jpa/entity/I18nEntity.java

@ -52,7 +52,7 @@ public class I18nEntity extends CorporationAuditorEntity<I18nVo> {
@Override
public I18nVo toVo() {
I18nVo vo =new I18nVo();
CorporationAuditorEntity.toVo(vo,this);
super.toVo(vo);
vo.setId(this.getId());
vo.setCode(this.getCode());
vo.setLang(this.getLang().toString());

4
io.sc.platform.system/src/main/java/io/sc/platform/system/i18n/service/impl/I18nServiceImpl.java

@ -42,6 +42,10 @@ public class I18nServiceImpl extends DaoServiceImpl<I18nEntity, String, I18nRepo
repository.save(obj);
}
}else{
obj =new I18nEntity();
obj.setCode(code);
obj.setLang(language);
obj.setMessage(message);
repository.save(obj);
}
}

16
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/controller/MenuWebController.java

@ -14,6 +14,7 @@ import io.sc.platform.system.api.menu.MenuVo;
import io.sc.platform.system.menu.jpa.entity.MenuEntity;
import io.sc.platform.system.menu.jpa.repository.MenuRepository;
import io.sc.platform.system.menu.service.MenuService;
import io.sc.platform.system.plugins.item.MenuItem;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Sort;
@ -65,16 +66,6 @@ public class MenuWebController extends RestCrudController<MenuVo,MenuEntity,Stri
return QueryResult.emptyPage();
}
@PostMapping("addMenu")
public void addMenu(@RequestBody Map<String,String> map) throws Exception{
service.addMenu(map);
}
@PostMapping("updateMenu")
public void updateMenu(@RequestBody Map<String,String> map) throws Exception{
service.updateMenu(map);
}
/**
* 列出所有菜单树,并且将角色所拥有的菜单列表作出标记
* @param roleId 角色ID
@ -157,6 +148,11 @@ public class MenuWebController extends RestCrudController<MenuVo,MenuEntity,Stri
}
}
@GetMapping("getAllMenuPlugins")
public List<MenuItem> getAllMenuPlugins() throws Exception{
return service.getAllMenuPlugins();
}
/**
* 将菜单插件导入到菜单表中
* @param pluginIds 需要导入的菜单插件ID集合

7
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/MenuService.java

@ -7,16 +7,12 @@ import io.sc.platform.system.api.menu.MenuVo;
import io.sc.platform.system.menu.jpa.entity.MenuEntity;
import io.sc.platform.system.menu.jpa.repository.MenuRepository;
import io.sc.platform.system.org.jpa.entity.OrgEntity;
import io.sc.platform.system.plugins.item.MenuItem;
import java.util.List;
import java.util.Map;
public interface MenuService extends DaoService<MenuEntity, String, MenuRepository> {
public void addMenu(Map<String,String> map) throws Exception;
public void updateMenu(Map<String,String> map) throws Exception;
public List<MenuEntity> getMenusByUser(String userId) throws Exception;
@ -91,6 +87,7 @@ public interface MenuService extends DaoService<MenuEntity, String, MenuReposito
*/
public void updateOrgs(CascadeMany2Many<String,String> wrapper) throws Exception;
public List<MenuItem> getAllMenuPlugins() throws Exception;
/**
* 导入菜单插件
* @param pluginIds 需要导入的菜单插件ID集合

71
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/impl/MenuServiceImpl.java

@ -38,63 +38,6 @@ public class MenuServiceImpl extends DaoServiceImpl<MenuEntity, String, MenuRepo
private TreeBuilder<MenuEntity,String> treeBuilder =new MenuEntityTreeBuilder();
@Autowired private JdbcTemplate jdbcTemplate;
@Transactional
public void addMenu(Map<String,String> map) throws Exception{
String parentId =map.get("parentId");
if(!StringUtils.hasText(parentId)){
MenuGroupEntity entity = new MenuGroupEntity();
entity.setName(map.get("name"));
entity.setTitleI18nKey(map.get("name"));
entity.setIcon(map.get("icon"));
entity.setEnable(Boolean.parseBoolean(map.get("enable")));
entity.setOrder(Integer.parseInt(map.get("order")));
this.add(entity);
return;
}
MenuEntity parentEntity =this.findById(parentId);
if(parentEntity!=null && parentEntity instanceof MenuGroupEntity) {
MenuGroupEntity parent = (MenuGroupEntity) parentEntity;
MenuRouteEntity entity = new MenuRouteEntity();
entity.setName(map.get("name"));
entity.setTitleI18nKey(map.get("name"));
entity.setIcon(map.get("icon"));
entity.setEnable(Boolean.parseBoolean(map.get("enable")));
entity.setOrder(Integer.parseInt(map.get("order")));
entity.setRouteName(map.get("routeName"));
String routeQuery =map.get("routeQuery");
if(StringUtils.hasText(routeQuery)) {
entity.setRouteQuery(ObjectMapper4Json.getMapper().readValue(map.get("routeQuery"), new TypeReference<Map<String, String>>() {}));
}
entity.setParent(parent);
this.add(entity);
}
}
@Transactional
public void updateMenu(Map<String,String> map) throws Exception{
String id =map.get("id");
if(StringUtils.hasText(id)){
MenuEntity menuEntity =this.findById(id);
if(menuEntity!=null && menuEntity instanceof MenuRouteEntity) {
MenuRouteEntity entity = (MenuRouteEntity) menuEntity;
entity.setName(map.get("name"));
entity.setTitleI18nKey(map.get("name"));
entity.setIcon(map.get("icon"));
entity.setEnable(Boolean.parseBoolean(map.get("enable")));
entity.setOrder(Integer.parseInt(map.get("order")));
entity.setRouteName(map.get("routeName"));
String routeQuery = map.get("routeQuery");
System.out.println(routeQuery);
if (StringUtils.hasText(routeQuery)) {
entity.setRouteQuery(ObjectMapper4Json.getMapper().readValue(routeQuery, new TypeReference<Map<String, String>>() {
}));
}
repository.save(entity);
}
}
}
@Override
public List<MenuEntity> getMenusByUser(String userId) throws Exception {
if(!StringUtils.hasText(userId)){
@ -375,6 +318,20 @@ public class MenuServiceImpl extends DaoServiceImpl<MenuEntity, String, MenuRepo
repository.saveAll(entities);
}
@Override
public List<MenuItem> getAllMenuPlugins() throws Exception {
List<MenuItem> result =PluginManager.getInstance().getMenuItems();
if(result!=null && !result.isEmpty()){
result.sort(new Comparator<MenuItem>() {
@Override
public int compare(MenuItem o1, MenuItem o2) {
return o1.getOrder()-o2.getOrder();
}
});
}
return result;
}
@Override
public List<MenuVo> getAllMenus() {
List<MenuEntity> entities =jdbcTemplate.query("select * from SYS_MENU where ENABLE_=1 order by ORDER_",new MenuEntityMapper());

165
io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/controller/LogViewerWebController.java

@ -1,24 +1,30 @@
package io.sc.platform.system.monitor.controller;
import io.sc.platform.core.DirectoryManager;
import io.sc.platform.core.support.FileWrapper;
import io.sc.platform.core.support.LoggerLevelInfo;
import io.sc.platform.core.util.FileUtil;
import io.sc.platform.core.util.StringUtil;
import io.sc.platform.mvc.support.FileDownloader;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.QueryResult;
import io.sc.platform.system.monitor.support.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.logging.LoggersEndpoint;
import org.springframework.boot.actuate.logging.LoggersEndpoint.LoggerLevels;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.data.domain.Page;
import org.springframework.util.StringUtils;
import org.springframework.data.domain.Sort.Order;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/monitor/logger")
@ -32,130 +38,103 @@ public class LogViewerWebController {
}
@GetMapping("getLogFiles")
public Page<FileWrapper> getLogFiles() throws IOException {
public Page<FileWrapper> getLogFiles(QueryParameter queryParameter) throws IOException {
File dir =new File(DirectoryManager.getInstance().getByName("dir.log"));
File[] files =dir.listFiles();
if(files!=null && files.length>0){
List<FileWrapper> result =new ArrayList<FileWrapper>();
for(File file : files){
FileWrapper wrapper =new FileWrapper(file.getName(),new Date(file.lastModified()), StringUtil.commaStyle(file.length()));
FileWrapper wrapper =new FileWrapper(file.getName(),new Date(file.lastModified()), file.length());
result.add(wrapper);
}
return QueryResult.page(result);
if(queryParameter!=null){
Order order =queryParameter.getFirstSort();
if(order!=null){
String fieldName =order.getProperty();
boolean asc =order.isAscending();
if("name".equalsIgnoreCase(fieldName)){
if(asc){
result.sort(new LogFileNameComparator(true));
}else{
result.sort(new LogFileNameComparator(false));
}
return QueryResult.emptyPage();
}
@GetMapping("getLogConfigurationLevels")
public Page<LoggerLevelInfo> getLogConfigurationLevels(LoggerLevelInfo levelInfo) throws Exception{
return QueryResult.page(findLevelsFromActuator(levelInfo));
}
/**
* 下载日志文件
* @param fileName 文件名
* @param request HttpServletRequest 对象
* @param response HttpServletResponse 对象
* @throws Exception 违例
*/
@RequestMapping(value="downloadLogFile/{fileName:.+}",method=RequestMethod.GET)
public void downloadLogFile(@PathVariable("fileName") String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception{
Resource resource =new DefaultResourceLoader().getResource("file://" + DirectoryManager.getInstance().getByName("dir.log") + "/" + fileName);
FileDownloader.download(request, response, fileName, resource);
}
private static class FileWrapper{
private String name;
private Date lastModifyDate;
private String size;
public FileWrapper(String name,Date lastModifyDate,String size){
this.name =name;
this.lastModifyDate =lastModifyDate;
this.size =size;
}else if("size".equalsIgnoreCase(fieldName)){
if(asc){
result.sort(new LogFileSizeComparator(true));
}else{
result.sort(new LogFileSizeComparator(false));
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}else if("lastModifyDate".equalsIgnoreCase(fieldName)){
if(asc){
result.sort(new LogFileLastModifyDateComparator(true));
}else{
result.sort(new LogFileLastModifyDateComparator(false));
}
public Date getLastModifyDate() {
return lastModifyDate;
}
public void setLastModifyDate(Date lastModifyDate) {
this.lastModifyDate = lastModifyDate;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
return QueryResult.page(result,queryParameter.getJpaPageable());
}
return QueryResult.emptyPage();
}
public List<LoggerLevelInfo> findLevelsFromActuator(LoggerLevelInfo levelInfo) throws Exception {
@GetMapping("getLogConfigurationLevels")
public Page<LoggerLevelInfo> getLogConfigurationLevels(QueryParameter queryParameter) throws Exception{
Map<String,Object> configurations =loggersEndpoint.loggers();
if(configurations==null){
return Collections.emptyList();
return QueryResult.emptyPage();
}
Object o =configurations.get("loggers");
if(o==null){
return Collections.emptyList();
return QueryResult.emptyPage();
}
List<LoggerLevelInfo> result =new ArrayList<LoggerLevelInfo>();
List<LoggerLevelInfo> result =new ArrayList<LoggerLevelInfo>(10000);
if(o instanceof Map){
Map<String, LoggersEndpoint.LoggerLevels> map =(Map<String, LoggersEndpoint.LoggerLevels>)o;
Map<String, LoggerLevels> map =(Map<String, LoggerLevels>)o;
for(String key : map.keySet()){
LoggersEndpoint.LoggerLevels loggerLevels =map.get(key);
LoggerLevels loggerLevels =map.get(key);
result.add(new LoggerLevelInfo(key,loggerLevels.getConfiguredLevel(),"INFO"));
}
}
if(
!StringUtils.hasText(levelInfo.getName())
&& levelInfo.getConfiguredLevel()!=null
&& levelInfo.getEffectiveLevel()!=null
){
return result;
if(queryParameter!=null){
Order order =queryParameter.getFirstSort();
if(order!=null){
String fieldName =order.getProperty();
boolean asc =order.isAscending();
if("name".equalsIgnoreCase(fieldName)){
if(asc){
result.sort(new LogLevelNameComparator(true));
}else{
result.sort(new LogLevelNameComparator(false));
}
//filter
List<LoggerLevelInfo> filter =new ArrayList<LoggerLevelInfo>();
if(StringUtils.hasText(levelInfo.getName())){
for(LoggerLevelInfo info : result){
if(info.getName().toLowerCase().contains(levelInfo.getName().trim().toLowerCase())){
filter.add(info);
}else if("configuredLevel".equalsIgnoreCase(fieldName)){
if(asc){
result.sort(new LogLevelConfiguredLevelComparator(true));
}else{
result.sort(new LogLevelConfiguredLevelComparator(false));
}
}else if("effectiveLevel".equalsIgnoreCase(fieldName)){
if(asc){
result.sort(new LogLevelEffectiveLevelComparator(true));
}else{
result.sort(new LogLevelEffectiveLevelComparator(false));
}
result =filter;
}
filter =new ArrayList<LoggerLevelInfo>();
if(levelInfo.getConfiguredLevel()!=null){
for(LoggerLevelInfo info : result){
if(info.getConfiguredLevel()!=null && info.getConfiguredLevel().equals(levelInfo.getConfiguredLevel())){
filter.add(info);
}
}
result =filter;
return QueryResult.page(result,queryParameter.getJpaPageable());
}
filter =new ArrayList<LoggerLevelInfo>();
if(levelInfo.getEffectiveLevel()!=null){
for(LoggerLevelInfo info : result){
if(info.getEffectiveLevel()!=null && info.getEffectiveLevel().equals(levelInfo.getEffectiveLevel())){
filter.add(info);
}
}
result =filter;
}
return result;
/**
* 下载日志文件
* @param fileName 文件名
* @param request HttpServletRequest 对象
* @param response HttpServletResponse 对象
* @throws Exception 违例
*/
@RequestMapping(value="downloadLogFile/{fileName:.+}",method=RequestMethod.GET)
public void downloadLogFile(@PathVariable("fileName") String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception{
Resource resource =new DefaultResourceLoader().getResource("file://" + DirectoryManager.getInstance().getByName("dir.log") + "/" + fileName);
FileDownloader.download(request, response, fileName, resource);
}
}

36
io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogFileLastModifyDateComparator.java

@ -0,0 +1,36 @@
package io.sc.platform.system.monitor.support;
import io.sc.platform.core.support.FileWrapper;
import java.util.Comparator;
public class LogFileLastModifyDateComparator implements Comparator<FileWrapper> {
private boolean asc =true;
public LogFileLastModifyDateComparator(){}
public LogFileLastModifyDateComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(FileWrapper o1, FileWrapper o2) {
if(asc) {
if(o1.getLastModifyDate().getTime()>o2.getLastModifyDate().getTime()){
return 1;
}else if(o1.getLastModifyDate().getTime()==o2.getLastModifyDate().getTime()){
return 0;
}else{
return -1;
}
}else{
if(o2.getLastModifyDate().getTime()>o1.getLastModifyDate().getTime()){
return 1;
}else if(o2.getLastModifyDate().getTime()==o1.getLastModifyDate().getTime()){
return 0;
}else{
return -1;
}
}
}
}

25
io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogFileNameComparator.java

@ -0,0 +1,25 @@
package io.sc.platform.system.monitor.support;
import io.sc.platform.core.support.FileWrapper;
import io.sc.platform.core.util.PinyinUtil;
import java.util.Comparator;
public class LogFileNameComparator implements Comparator<FileWrapper> {
private boolean asc =true;
public LogFileNameComparator(){}
public LogFileNameComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(FileWrapper o1, FileWrapper o2) {
if(asc) {
return PinyinUtil.compare(o1.getName(), o2.getName());
}else{
return PinyinUtil.compare(o2.getName(), o1.getName());
}
}
}

38
io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogFileSizeComparator.java

@ -0,0 +1,38 @@
package io.sc.platform.system.monitor.support;
import io.sc.platform.core.support.FileWrapper;
import io.sc.platform.core.util.PinyinUtil;
import io.sc.platform.system.api.org.OrgVo;
import java.util.Comparator;
public class LogFileSizeComparator implements Comparator<FileWrapper> {
private boolean asc =true;
public LogFileSizeComparator(){}
public LogFileSizeComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(FileWrapper o1, FileWrapper o2) {
if(asc) {
if(o1.getSize()>o2.getSize()){
return 1;
}else if(o1.getSize()==o2.getSize()){
return 0;
}else{
return -1;
}
}else{
if(o2.getSize()>o1.getSize()){
return 1;
}else if(o2.getSize()==o1.getSize()){
return 0;
}else{
return -1;
}
}
}
}

25
io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogLevelConfiguredLevelComparator.java

@ -0,0 +1,25 @@
package io.sc.platform.system.monitor.support;
import io.sc.platform.core.support.LoggerLevelInfo;
import io.sc.platform.core.util.EnumUtil;
import java.util.Comparator;
public class LogLevelConfiguredLevelComparator implements Comparator<LoggerLevelInfo> {
private boolean asc =true;
public LogLevelConfiguredLevelComparator(){}
public LogLevelConfiguredLevelComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(LoggerLevelInfo o1, LoggerLevelInfo o2) {
if(asc) {
return EnumUtil.compare(o1.getConfiguredLevel(),o2.getConfiguredLevel());
}else{
return EnumUtil.compare(o2.getConfiguredLevel(),o1.getConfiguredLevel());
}
}
}

24
io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogLevelEffectiveLevelComparator.java

@ -0,0 +1,24 @@
package io.sc.platform.system.monitor.support;
import io.sc.platform.core.support.LoggerLevelInfo;
import java.util.Comparator;
public class LogLevelEffectiveLevelComparator implements Comparator<LoggerLevelInfo> {
private boolean asc =true;
public LogLevelEffectiveLevelComparator(){}
public LogLevelEffectiveLevelComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(LoggerLevelInfo o1, LoggerLevelInfo o2) {
if(asc) {
return o1.getEffectiveLevel().compareTo(o2.getEffectiveLevel());
}else{
return o2.getEffectiveLevel().compareTo(o1.getEffectiveLevel());
}
}
}

26
io.sc.platform.system/src/main/java/io/sc/platform/system/monitor/support/LogLevelNameComparator.java

@ -0,0 +1,26 @@
package io.sc.platform.system.monitor.support;
import io.sc.platform.core.support.FileWrapper;
import io.sc.platform.core.support.LoggerLevelInfo;
import io.sc.platform.core.util.PinyinUtil;
import java.util.Comparator;
public class LogLevelNameComparator implements Comparator<LoggerLevelInfo> {
private boolean asc =true;
public LogLevelNameComparator(){}
public LogLevelNameComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(LoggerLevelInfo o1, LoggerLevelInfo o2) {
if(asc) {
return o1.getName().compareTo(o2.getName());
}else{
return o2.getName().compareTo(o1.getName());
}
}
}

2
io.sc.platform.system/src/main/java/io/sc/platform/system/parameter/jpa/entity/ParameterEntity.java

@ -43,7 +43,7 @@ public class ParameterEntity extends CorporationAuditorEntity<ParameterVo> {
@Override
public ParameterVo toVo() {
ParameterVo vo =new ParameterVo();
CorporationAuditorEntity.toVo(vo,this);
super.toVo(vo);
vo.setId(this.getId());
vo.setCode(this.getCode());
vo.setValue(this.getValue());

1
io.sc.platform.system/src/main/java/io/sc/platform/system/plugins/PluginManager.java

@ -80,6 +80,7 @@ public class PluginManager {
if(menu.getId()==null){
menu.setId(UUID.randomUUID().toString());
}
menu.setConfigurationFileUrl(plugin.getFileUrl());
menuItemMap.put(menu.getId(),menu);
items.add(menu);
}

11
io.sc.platform.system/src/main/java/io/sc/platform/system/plugins/item/MenuItem.java

@ -20,6 +20,9 @@ public abstract class MenuItem {
private String icon;
private int order;
//附加属性
private String configurationFileUrl; //菜单贡献项配置文件位置
public String getId() {
return id;
}
@ -59,4 +62,12 @@ public abstract class MenuItem {
public void setOrder(int order) {
this.order = order;
}
public String getConfigurationFileUrl() {
return configurationFileUrl;
}
public void setConfigurationFileUrl(String configurationFileUrl) {
this.configurationFileUrl = configurationFileUrl;
}
}

5
io.sc.platform.system/src/main/resources/META-INF/platform/plugins/p6spy.json

@ -0,0 +1,5 @@
{
"ignoredPatterns":[
"^insert into sys_audit_log"
]
}
Loading…
Cancel
Save