Compare commits
11 Commits
57120c002a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| fe89520621 | |||
| de55ddbc48 | |||
| 78e2dbcd65 | |||
| 76ad4c7fa5 | |||
| 878b5f144c | |||
| c73a7b48fa | |||
| e615aefaee | |||
| dd9f966597 | |||
| 7c6c387a07 | |||
| bbf7a2a5d8 | |||
| 6c33113092 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -82,3 +82,4 @@ Icon
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
*.db
|
||||
|
||||
20
.idea/compiler.xml
generated
20
.idea/compiler.xml
generated
@@ -7,8 +7,28 @@
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
</profile>
|
||||
<profile name="Annotation profile for spring-boot-server-template" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<option name="mapstruct.defaultComponentModel" value="spring" />
|
||||
<option name="mapstruct.defaultInjectionStrategy" value="constructor" />
|
||||
<processorPath useClasspath="false">
|
||||
<entry name="$MAVEN_REPOSITORY$/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/mapstruct/mapstruct/1.6.3/mapstruct-1.6.3.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/unknown/lombok-unknown.jar" />
|
||||
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok-mapstruct-binding/0.2.0/lombok-mapstruct-binding-0.2.0.jar" />
|
||||
</processorPath>
|
||||
<module name="spring-boot-server-template" />
|
||||
<module name="spring-boot-apijson-server" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="spring-boot-apijson-server" options="-Amapstruct.defaultComponentModel=spring -Amapstruct.defaultInjectionStrategy=constructor" />
|
||||
<module name="spring-boot-server-template" options="-Amapstruct.defaultComponentModel=spring -Amapstruct.defaultInjectionStrategy=constructor" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/dataSources.xml
generated
12
.idea/dataSources.xml
generated
@@ -26,5 +26,17 @@
|
||||
</jdbc-additional-properties>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="database" uuid="894c61bb-b6fb-4153-94dc-97175dd7fd95">
|
||||
<driver-ref>h2.unified</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.h2.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:h2:$PROJECT_DIR$/database</jdbc-url>
|
||||
<jdbc-additional-properties>
|
||||
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||
</jdbc-additional-properties>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
15
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
15
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="NullableProblems" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_GETTER" value="true" />
|
||||
<option name="REPORT_NOT_ANNOTATED_SETTER_PARAMETER" value="true" />
|
||||
<option name="REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS" value="true" />
|
||||
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
10
.idea/jarRepositories.xml
generated
10
.idea/jarRepositories.xml
generated
@@ -11,6 +11,11 @@
|
||||
<option name="name" value="jitpack.io" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="lanyuanxiaoyao-maven-central" />
|
||||
<option name="name" value="lanyuanxiaoyao-maven-central" />
|
||||
<option name="url" value="https://maven.lanyuanxiaoyao.com/central" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="central" />
|
||||
@@ -26,5 +31,10 @@
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://maven.lanyuanxiaoyao.com/central" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/modules.xml
generated
2
.idea/modules.xml
generated
@@ -2,7 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/spring-boot-apijson-server.iml" filepath="$PROJECT_DIR$/spring-boot-apijson-server.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/spring-boot-server-template.iml" filepath="$PROJECT_DIR$/spring-boot-server-template.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"ant-design-vue": "4.x",
|
||||
"licia": "^1.48.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "4"
|
||||
},
|
||||
|
||||
8
client/pnpm-lock.yaml
generated
8
client/pnpm-lock.yaml
generated
@@ -14,6 +14,9 @@ importers:
|
||||
ant-design-vue:
|
||||
specifier: 4.x
|
||||
version: 4.2.6(vue@3.5.13)
|
||||
licia:
|
||||
specifier: ^1.48.0
|
||||
version: 1.48.0
|
||||
vue:
|
||||
specifier: ^3.5.13
|
||||
version: 3.5.13
|
||||
@@ -681,6 +684,9 @@ packages:
|
||||
libphonenumber-js@1.12.5:
|
||||
resolution: {integrity: sha512-DOjiaVjjSmap12ztyb4QgoFmUe/GbgnEXHu+R7iowk0lzDIjScvPAm8cK9RYTEobbRb0OPlwlZUGTTJPJg13Kw==}
|
||||
|
||||
licia@1.48.0:
|
||||
resolution: {integrity: sha512-bBWiT5CSdEtwuAHiYTJ74yItCjIFdHi4xiFk6BRDfKa+sdCpkUHp69YKb5udNOJlHDzFjNjcMgNZ/+wQIHrB8A==}
|
||||
|
||||
lodash-es@4.17.21:
|
||||
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
||||
|
||||
@@ -1489,6 +1495,8 @@ snapshots:
|
||||
|
||||
libphonenumber-js@1.12.5: {}
|
||||
|
||||
licia@1.48.0: {}
|
||||
|
||||
lodash-es@4.17.21: {}
|
||||
|
||||
lodash@4.17.21: {}
|
||||
|
||||
19
client/src/components/Flow.vue
Normal file
19
client/src/components/Flow.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {Button} from "ant-design-vue";
|
||||
|
||||
const message = ref('Vue Flow')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flow"></div>
|
||||
<Button>{{ message}}</Button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.flow {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
import {createApp} from 'vue'
|
||||
import {createMemoryHistory, createRouter, createWebHistory} from 'vue-router'
|
||||
import App from './App.vue'
|
||||
import {createRouter, createWebHistory} from 'vue-router'
|
||||
import App from '@/App.vue'
|
||||
import {Layout, Menu} from 'ant-design-vue'
|
||||
import 'ant-design-vue/dist/reset.css'
|
||||
|
||||
@@ -14,23 +14,28 @@ createApp(App)
|
||||
{
|
||||
name: 'home',
|
||||
path: '/home',
|
||||
component: () => import('./views/Home.vue'),
|
||||
component: () => import('@/views/Home.vue'),
|
||||
},
|
||||
{
|
||||
name: 'management',
|
||||
path: '/management',
|
||||
component: () => import('./views/management/Index.vue'),
|
||||
component: () => import('@/views/management/Index.vue'),
|
||||
redirect: '/management/overview',
|
||||
children: [
|
||||
{
|
||||
name: 'overview',
|
||||
path: 'overview',
|
||||
component: () => import('./views/management/Overview.vue'),
|
||||
component: () => import('@/views/management/Overview.vue'),
|
||||
},
|
||||
{
|
||||
name: 'organization',
|
||||
path: 'organization',
|
||||
component: () => import('@/views/management/Organization.vue'),
|
||||
},
|
||||
{
|
||||
name: 'setting',
|
||||
path: 'setting',
|
||||
component: () => import('./views/management/Setting.vue'),
|
||||
component: () => import('@/views/management/Setting.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,19 +1,54 @@
|
||||
import {isArr} from "licia";
|
||||
|
||||
const information = {
|
||||
debug: true,
|
||||
baseUrl: 'http://localhost'
|
||||
debug: true,
|
||||
baseUrl: 'http://localhost:8080',
|
||||
}
|
||||
|
||||
export function amisRender(target, amisJson) {
|
||||
let amisJsonObject = amisJson(information)
|
||||
if (information.debug) {
|
||||
console.log(amisJsonObject)
|
||||
}
|
||||
amisRequire('amis/embed').embed(
|
||||
target,
|
||||
amisJsonObject,
|
||||
information,
|
||||
{
|
||||
theme: 'antd'
|
||||
}
|
||||
)
|
||||
let amisJsonObject = amisJson(information)
|
||||
if (information.debug) {
|
||||
console.log(amisJsonObject)
|
||||
}
|
||||
amisRequire('amis/embed').embed(
|
||||
target,
|
||||
amisJsonObject,
|
||||
information,
|
||||
{
|
||||
theme: 'antd',
|
||||
enableAMISDebug: information.debug,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
function parseEdges(edges) {
|
||||
if (isArr(edges)) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export function amisElideGraphQLAdaptor(payload, response, api, context) {
|
||||
// console.log(payload, response, api, context)
|
||||
let result = []
|
||||
console.log(payload)
|
||||
if (payload.data) {
|
||||
let items = payload.data[Object.keys(payload.data)[0]]['edges']
|
||||
for (let item of items) {
|
||||
result.push(item.node)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function amisElideJsonapiAdaptor(payload, response, api, context) {
|
||||
let result = []
|
||||
if (payload.data && isArr(payload.data)) {
|
||||
for (let item of payload.data) {
|
||||
result.push({
|
||||
...item,
|
||||
...item['attributes'],
|
||||
})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import {onMounted} from 'vue'
|
||||
import {amisRender} from '../utils.js'
|
||||
import {createApp, onMounted} from 'vue'
|
||||
import {amisRender} from '@/utils.js'
|
||||
import Flow from "@/components/Flow.vue";
|
||||
|
||||
const toastMessage = () => {
|
||||
alert('click in vue')
|
||||
@@ -29,6 +30,12 @@ onMounted(() => {
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'custom',
|
||||
onMount: (dom, value, onChange, props) => {
|
||||
createApp(Flow).mount(dom)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<script setup>
|
||||
import {ref, watch} from 'vue'
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
|
||||
const route = useRoute();
|
||||
@@ -13,13 +16,18 @@ const menus = [
|
||||
key: 'system',
|
||||
name: '系统管理',
|
||||
children: [
|
||||
{
|
||||
key: 'organization',
|
||||
path: '/management/organization',
|
||||
name: '组织架构',
|
||||
},
|
||||
{
|
||||
key: 'setting',
|
||||
path: '/management/setting',
|
||||
name: '设置',
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
const sideNavSelected = ref(['overview'])
|
||||
const openKeys = ref([]) // 控制展开的子菜单
|
||||
@@ -65,19 +73,21 @@ watch(() => route.path, () => {
|
||||
v-model:openKeys="openKeys"
|
||||
mode="inline"
|
||||
>
|
||||
<a-menu-item key="overview">
|
||||
<router-link to="/management/overview">概览</router-link>
|
||||
</a-menu-item>
|
||||
<a-sub-menu key="system">
|
||||
<template #title>
|
||||
<div v-for="menu in menus" :key="menu.key">
|
||||
<a-sub-menu v-if="menu.children" :key="menu.key">
|
||||
<template #title>
|
||||
<span>
|
||||
系统管理
|
||||
{{ menu.name }}
|
||||
</span>
|
||||
</template>
|
||||
<a-menu-item key="setting">
|
||||
<router-link to="/management/setting">设置</router-link>
|
||||
</template>
|
||||
<a-menu-item v-for="submenu in menu.children" :key="submenu.key">
|
||||
<router-link :to="submenu.path">{{ submenu.name }}</router-link>
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
<a-menu-item v-else :key="menu.key">
|
||||
<router-link :to="menu.path">{{ menu.name }}</router-link>
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
</div>
|
||||
</a-menu>
|
||||
</a-layout-sider>
|
||||
<div class="p-3 h-full w-full">
|
||||
|
||||
121
client/src/views/management/Organization.vue
Normal file
121
client/src/views/management/Organization.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<script setup>
|
||||
import {onMounted} from 'vue'
|
||||
import {amisElideJsonapiAdaptor, amisRender,} from '@/utils.js'
|
||||
|
||||
onMounted(() => {
|
||||
amisRender(
|
||||
'#amis-organization',
|
||||
information => {
|
||||
return {
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'tpl',
|
||||
tpl: '${baseUrl}1',
|
||||
},
|
||||
{
|
||||
type: 'crud',
|
||||
syncLocation: false,
|
||||
headerToolbar: [
|
||||
'reload',
|
||||
{
|
||||
type: 'action',
|
||||
icon: 'fa fa-plus',
|
||||
label: '',
|
||||
actionType: 'dialog',
|
||||
dialog: {
|
||||
title: '新增组织',
|
||||
body: {
|
||||
type: 'form',
|
||||
api: {
|
||||
method: 'post',
|
||||
url: `${information.baseUrl}/organization/save`,
|
||||
data: {
|
||||
code: '${code|default:undefined}',
|
||||
name: '${name|default:undefined}',
|
||||
},
|
||||
},
|
||||
body: [
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'code',
|
||||
label: '组织编号',
|
||||
placeholder: '不填则自动生成',
|
||||
},
|
||||
{
|
||||
type: 'input-text',
|
||||
name: 'name',
|
||||
label: '组织名称',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
api: {
|
||||
method: 'get',
|
||||
url: `${information.baseUrl}/jsonapi/organization`,
|
||||
data: {
|
||||
fields: {
|
||||
organization: 'code,name'
|
||||
},
|
||||
page: {
|
||||
size: '${perPage|default:undefined}',
|
||||
number: '${page|default:undefined}',
|
||||
},
|
||||
},
|
||||
adaptor: amisElideJsonapiAdaptor,
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
width: 150,
|
||||
name: 'code',
|
||||
label: '组织编号',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: '组织名称',
|
||||
},
|
||||
{
|
||||
width: 100,
|
||||
label: '操作',
|
||||
type: 'operation',
|
||||
buttons: [
|
||||
{
|
||||
type: 'action',
|
||||
icon: 'fa fa-trash',
|
||||
label: '删除',
|
||||
level: 'danger',
|
||||
size: 'xs',
|
||||
actionType: 'ajax',
|
||||
api: {
|
||||
method: 'delete',
|
||||
url: `${information.baseUrl}/jsonapi/organization/\${id}`,
|
||||
},
|
||||
messages: {
|
||||
success: '删除成功',
|
||||
failed: '删除失败',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="amis-organization"></div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import {onMounted} from 'vue'
|
||||
import {amisRender} from '../../utils.js'
|
||||
import {amisRender} from '@/utils.js'
|
||||
|
||||
onMounted(() => {
|
||||
amisRender(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import {onMounted} from 'vue'
|
||||
import {amisRender} from '../../utils.js'
|
||||
import {amisRender} from '@/utils.js'
|
||||
|
||||
onMounted(() => {
|
||||
amisRender(
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import {defineConfig} from 'vite'
|
||||
import obfuscatorPlugin from "vite-plugin-javascript-obfuscator";
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import {fileURLToPath, URL} from 'node:url'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'vue': 'vue/dist/vue.esm-bundler.js',
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
obfuscatorPlugin({
|
||||
|
||||
61
pom.xml
61
pom.xml
@@ -5,7 +5,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.lanyuanxiaoyao</groupId>
|
||||
<artifactId>spring-boot-apijson-server</artifactId>
|
||||
<artifactId>spring-boot-server-template</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
@@ -16,13 +16,23 @@
|
||||
<spring-boot.version>3.4.0</spring-boot.version>
|
||||
<spring-cloud.version>2024.0.0</spring-cloud.version>
|
||||
<hutool.version>5.8.32</hutool.version>
|
||||
<elide.version>7.1.4</elide.version>
|
||||
<mapstruct.version>1.6.3</mapstruct.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jetty</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -35,7 +45,12 @@
|
||||
<dependency>
|
||||
<groupId>com.yahoo.elide</groupId>
|
||||
<artifactId>elide-spring-boot-starter</artifactId>
|
||||
<version>${elide.version}</version>
|
||||
<version>7.1.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.blinkfox</groupId>
|
||||
<artifactId>fenix-spring-boot-starter</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -43,14 +58,23 @@
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -76,6 +100,33 @@
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.12.1</version>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>-Amapstruct.defaultComponentModel=spring</arg>
|
||||
<arg>-Amapstruct.defaultInjectionStrategy=constructor</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-build</artifactId>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.lanyuanxiaoyao.server;
|
||||
|
||||
import com.blinkfox.fenix.EnableFenix;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@@ -12,6 +16,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
* @since 2025-03-03
|
||||
*/
|
||||
@Slf4j
|
||||
@EnableFenix
|
||||
@EnableConfigurationProperties
|
||||
@SpringBootApplication
|
||||
public class ServerApplication {
|
||||
public static void main(String[] args) {
|
||||
@@ -31,4 +37,10 @@ public class ServerApplication {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationRunner initialRunner() {
|
||||
return args -> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.lanyuanxiaoyao.server.configuration;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250327
|
||||
*/
|
||||
public interface Constants {
|
||||
String DATABASE_TABLE_PREFIX = "platform_";
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.lanyuanxiaoyao.server.configuration.database;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.time.Instant;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.annotations.IdGeneratorType;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 使用雪花算法作为ID生成器
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @date 2024-11-14
|
||||
*/
|
||||
@Slf4j
|
||||
public class SnowflakeId {
|
||||
@IdGeneratorType(IdGenerator.class)
|
||||
@Retention(RUNTIME)
|
||||
@Target({FIELD, METHOD})
|
||||
public @interface Generator {
|
||||
}
|
||||
|
||||
public static final class IdGenerator implements IdentifierGenerator {
|
||||
@Override
|
||||
public Object generate(SharedSessionContractImplementor session, Object object) {
|
||||
try {
|
||||
return Snowflake.next();
|
||||
} catch (Exception e) {
|
||||
log.error("Generate snowflake id failed", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static long nextId() {
|
||||
return Snowflake.next();
|
||||
}
|
||||
|
||||
public static String nextStrId() {
|
||||
return String.valueOf(Snowflake.next());
|
||||
}
|
||||
|
||||
private static class Snowflake {
|
||||
/**
|
||||
* 起始的时间戳
|
||||
*/
|
||||
private final static long START_TIMESTAMP = 1;
|
||||
|
||||
/**
|
||||
* 序列号占用的位数
|
||||
*/
|
||||
private final static long SEQUENCE_BIT = 11;
|
||||
|
||||
/**
|
||||
* 序列号最大值
|
||||
*/
|
||||
private final static long MAX_SEQUENCE_BIT = ~(-1 << SEQUENCE_BIT);
|
||||
|
||||
/**
|
||||
* 时间戳值向左位移
|
||||
*/
|
||||
private final static long TIMESTAMP_OFFSET = SEQUENCE_BIT;
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private static long sequence = 0;
|
||||
/**
|
||||
* 上一次时间戳
|
||||
*/
|
||||
private static long lastTimestamp = -1;
|
||||
|
||||
public static synchronized long next() {
|
||||
long currentTimestamp = nowTimestamp();
|
||||
if (currentTimestamp < lastTimestamp) {
|
||||
throw new RuntimeException("Clock have moved backwards.");
|
||||
}
|
||||
|
||||
if (currentTimestamp == lastTimestamp) {
|
||||
// 相同毫秒内, 序列号自增
|
||||
sequence = (sequence + 1) & MAX_SEQUENCE_BIT;
|
||||
// 同一毫秒的序列数已经达到最大
|
||||
if (sequence == 0) {
|
||||
currentTimestamp = nextTimestamp();
|
||||
}
|
||||
} else {
|
||||
// 不同毫秒内, 序列号置为0
|
||||
sequence = 0;
|
||||
}
|
||||
|
||||
lastTimestamp = currentTimestamp;
|
||||
return (currentTimestamp - START_TIMESTAMP) << TIMESTAMP_OFFSET | sequence;
|
||||
}
|
||||
|
||||
private static long nextTimestamp() {
|
||||
long milli = nowTimestamp();
|
||||
while (milli <= lastTimestamp) {
|
||||
milli = nowTimestamp();
|
||||
}
|
||||
return milli;
|
||||
}
|
||||
|
||||
private static long nowTimestamp() {
|
||||
return Instant.now().toEpochMilli();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.lanyuanxiaoyao.server.controller;
|
||||
|
||||
import com.lanyuanxiaoyao.server.controller.base.AbstractController;
|
||||
import com.lanyuanxiaoyao.server.entity.Organization;
|
||||
import com.lanyuanxiaoyao.server.entity.mapper.EntityMapper;
|
||||
import com.lanyuanxiaoyao.server.entity.mapper.OrganizationMapper;
|
||||
import com.lanyuanxiaoyao.server.service.OrganizationService;
|
||||
import com.lanyuanxiaoyao.server.service.base.AbstractService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("organization")
|
||||
public class OrganizationController extends AbstractController<Organization, Long, Organization.SaveVO> {
|
||||
private final OrganizationService organizationService;
|
||||
private final OrganizationMapper organizationMapper;
|
||||
|
||||
public OrganizationController(OrganizationService organizationService, OrganizationMapper organizationMapper) {
|
||||
this.organizationService = organizationService;
|
||||
this.organizationMapper = organizationMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractService<Organization, Long> getService() {
|
||||
return organizationService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMapper<Organization, Organization.SaveVO> getEntityMapper() {
|
||||
return organizationMapper;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.lanyuanxiaoyao.server.controller.base;
|
||||
|
||||
import com.lanyuanxiaoyao.server.entity.mapper.EntityMapper;
|
||||
import com.lanyuanxiaoyao.server.service.base.AbstractService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
public abstract class AbstractController<ENTITY, ID, SAVE_VO> {
|
||||
public abstract AbstractService<ENTITY, ID> getService();
|
||||
|
||||
public abstract EntityMapper<ENTITY, SAVE_VO> getEntityMapper();
|
||||
|
||||
@GetMapping("/get/{id}")
|
||||
public ENTITY get(@PathVariable("id") ID id) {
|
||||
return getService().get(id);
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
public ID save(@RequestBody SAVE_VO entity) {
|
||||
return getService().save(getEntityMapper().fromVO(entity));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.lanyuanxiaoyao.server.controller.base;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 处理错误信息
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @date 2024-01-02
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class ErrorController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ErrorController.class);
|
||||
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(Throwable.class)
|
||||
public String errorHandler(Throwable throwable) {
|
||||
logger.error("Error", throwable);
|
||||
return throwable.getMessage();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.lanyuanxiaoyao.server.entity;
|
||||
|
||||
import com.lanyuanxiaoyao.server.configuration.Constants;
|
||||
import com.lanyuanxiaoyao.server.configuration.database.SnowflakeId;
|
||||
import com.yahoo.elide.annotation.Include;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
/**
|
||||
* 组织
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250327
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Entity
|
||||
@Table(name = Constants.DATABASE_TABLE_PREFIX + "department")
|
||||
@Include
|
||||
public class Department {
|
||||
@Id
|
||||
@GeneratedValue(generator = "snowflakeId")
|
||||
@GenericGenerator(name = "snowflakeId", type = SnowflakeId.IdGenerator.class)
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
private Organization organization;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
private Department parent;
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
|
||||
@ToString.Exclude
|
||||
private Set<Department> children;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public static class SaveVO {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long organizationId;
|
||||
private Long parentId;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.lanyuanxiaoyao.server.entity;
|
||||
|
||||
import com.yahoo.elide.annotation.LifeCycleHookBinding;
|
||||
import com.yahoo.elide.core.lifecycle.LifeCycleHook;
|
||||
import com.yahoo.elide.core.security.ChangeSpec;
|
||||
import com.yahoo.elide.core.security.RequestScope;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250304
|
||||
*/
|
||||
@Slf4j
|
||||
public class LogicDeletedHook implements LifeCycleHook<User> {
|
||||
@Override
|
||||
public void execute(LifeCycleHookBinding.Operation operation, LifeCycleHookBinding.TransactionPhase phase, User user, RequestScope scope, Optional<ChangeSpec> changes) {
|
||||
log.info("Operation: {}, Phase: {}, User: {}, Scope: {}, Changes: {}", operation, phase, user, scope, changes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.lanyuanxiaoyao.server.entity;
|
||||
|
||||
import com.lanyuanxiaoyao.server.configuration.Constants;
|
||||
import com.lanyuanxiaoyao.server.configuration.database.SnowflakeId;
|
||||
import com.yahoo.elide.annotation.Include;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
/**
|
||||
* 组织
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250327
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Entity
|
||||
@Table(name = Constants.DATABASE_TABLE_PREFIX + "organization")
|
||||
@Include
|
||||
public class Organization {
|
||||
@Id
|
||||
@GeneratedValue(generator = "snowflakeId")
|
||||
@GenericGenerator(name = "snowflakeId", type = SnowflakeId.IdGenerator.class)
|
||||
private Long id;
|
||||
@Column(unique = true, nullable = false)
|
||||
private String code;
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "organization")
|
||||
@ToString.Exclude
|
||||
private Set<Department> departments;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public static final class SaveVO {
|
||||
private Long id;
|
||||
private String code;
|
||||
private String name;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.lanyuanxiaoyao.server.entity;
|
||||
|
||||
import com.yahoo.elide.annotation.Include;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250304
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Entity
|
||||
@DynamicUpdate
|
||||
@Include
|
||||
public class School {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private String id;
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "school")
|
||||
@ToString.Exclude
|
||||
private List<User> users;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.lanyuanxiaoyao.server.entity;
|
||||
|
||||
import com.yahoo.elide.annotation.Include;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
|
||||
/**
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250304
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Entity
|
||||
@DynamicUpdate
|
||||
@Include
|
||||
@SoftDelete
|
||||
public class User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private String id;
|
||||
@Column(unique = true, nullable = false)
|
||||
private String username;
|
||||
@Column(nullable = false)
|
||||
private String password;
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||
@ToString.Exclude
|
||||
private School school;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.lanyuanxiaoyao.server.entity.mapper;
|
||||
|
||||
import com.lanyuanxiaoyao.server.entity.Department;
|
||||
import com.lanyuanxiaoyao.server.service.DepartmentService;
|
||||
import com.lanyuanxiaoyao.server.service.OrganizationService;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
/**
|
||||
* 部门bean转换
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250327
|
||||
*/
|
||||
@Mapper(
|
||||
uses = {
|
||||
OrganizationService.class,
|
||||
DepartmentService.class,
|
||||
}
|
||||
)
|
||||
public interface DepartmentMapper extends EntityMapper<Department, Department.SaveVO> {
|
||||
@Mapping(target = "organization", source = "organizationId")
|
||||
@Mapping(target = "parent", source = "parentId")
|
||||
@Override
|
||||
Department fromVO(Department.SaveVO saveVO);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.lanyuanxiaoyao.server.entity.mapper;
|
||||
|
||||
public interface EntityMapper<SE, CE> {
|
||||
SE fromVO(CE creationVO);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.lanyuanxiaoyao.server.entity.mapper;
|
||||
|
||||
import com.lanyuanxiaoyao.server.configuration.database.SnowflakeId;
|
||||
import com.lanyuanxiaoyao.server.entity.Organization;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
/**
|
||||
* 组织bean转换
|
||||
*
|
||||
* @author lanyuanxiaoyao
|
||||
* @version 20250327
|
||||
*/
|
||||
@Mapper(
|
||||
imports = {
|
||||
SnowflakeId.class
|
||||
}
|
||||
)
|
||||
public interface OrganizationMapper extends EntityMapper<Organization, Organization.SaveVO> {
|
||||
@Mapping(target = "code", defaultExpression = "java(SnowflakeId.nextStrId())")
|
||||
@Override
|
||||
Organization fromVO(Organization.SaveVO creationVO);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lanyuanxiaoyao.server.repository;
|
||||
|
||||
import com.blinkfox.fenix.jpa.FenixJpaRepository;
|
||||
import com.blinkfox.fenix.specification.FenixJpaSpecificationExecutor;
|
||||
import com.lanyuanxiaoyao.server.entity.Department;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface DepartmentRepository extends FenixJpaRepository<Department, Long>, FenixJpaSpecificationExecutor<Department> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.lanyuanxiaoyao.server.repository;
|
||||
|
||||
import com.blinkfox.fenix.jpa.FenixJpaRepository;
|
||||
import com.blinkfox.fenix.specification.FenixJpaSpecificationExecutor;
|
||||
import com.lanyuanxiaoyao.server.entity.Organization;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OrganizationRepository extends FenixJpaRepository<Organization, Long>, FenixJpaSpecificationExecutor<Organization> {
|
||||
@Override
|
||||
Organization getReferenceById(Long aLong);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.lanyuanxiaoyao.server.service;
|
||||
|
||||
import com.blinkfox.fenix.jpa.FenixJpaRepository;
|
||||
import com.lanyuanxiaoyao.server.entity.Department;
|
||||
import com.lanyuanxiaoyao.server.repository.DepartmentRepository;
|
||||
import com.lanyuanxiaoyao.server.service.base.AbstractService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DepartmentService extends AbstractService<Department, Long> {
|
||||
private final DepartmentRepository departmentRepository;
|
||||
|
||||
public DepartmentService(DepartmentRepository departmentRepository) {
|
||||
this.departmentRepository = departmentRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FenixJpaRepository<Department, Long> getRepository() {
|
||||
return departmentRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId(Department department) {
|
||||
return department.getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.lanyuanxiaoyao.server.service;
|
||||
|
||||
import com.blinkfox.fenix.jpa.FenixJpaRepository;
|
||||
import com.lanyuanxiaoyao.server.entity.Organization;
|
||||
import com.lanyuanxiaoyao.server.repository.OrganizationRepository;
|
||||
import com.lanyuanxiaoyao.server.service.base.AbstractService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OrganizationService extends AbstractService<Organization, Long> {
|
||||
private final OrganizationRepository organizationRepository;
|
||||
|
||||
public OrganizationService(OrganizationRepository organizationRepository) {
|
||||
this.organizationRepository = organizationRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FenixJpaRepository<Organization, Long> getRepository() {
|
||||
return organizationRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId(Organization organization) {
|
||||
return organization.getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.lanyuanxiaoyao.server.service.base;
|
||||
|
||||
import com.blinkfox.fenix.jpa.FenixJpaRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
public abstract class AbstractService<ENTITY, ID> {
|
||||
public abstract FenixJpaRepository<ENTITY, ID> getRepository();
|
||||
|
||||
public abstract ID getId(ENTITY entity);
|
||||
|
||||
public ENTITY get(ID id) {
|
||||
return getRepository().getReferenceById(id);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ID save(ENTITY entity) {
|
||||
ENTITY saved = getRepository().saveOrUpdateByNotNullProperties(entity);
|
||||
return getId(saved);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3307/main?useSSL=false&allowPublicKeyRetrieval=true
|
||||
url: jdbc:h2:file:./database;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: test
|
||||
password: test
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
driver-class-name: org.h2.Driver
|
||||
jpa:
|
||||
show-sql: true
|
||||
generate-ddl: true
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 20MB
|
||||
jackson:
|
||||
date-format: 'yyyy-MM-dd HH:mm:ss'
|
||||
elide:
|
||||
json-api:
|
||||
enabled: true
|
||||
@@ -23,3 +29,5 @@ logging:
|
||||
jpql:
|
||||
query:
|
||||
DefaultQueryLogger: debug
|
||||
fenix:
|
||||
print-banner: false
|
||||
20
src/test/resources/organization.http
Normal file
20
src/test/resources/organization.http
Normal file
@@ -0,0 +1,20 @@
|
||||
### Create
|
||||
POST http://localhost:8080/jsonapi/organization
|
||||
Content-Type: application/vnd.api+json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"type": "organization",
|
||||
"attributes": {
|
||||
"organizationName": "苹果公司"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
### Create
|
||||
POST http://localhost:8080/graphql
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"query": "mutation {organization(op: UPSERT, data: {organizationName: \"苹果\"}) {edges {node {organizationId organizationName}}}}"
|
||||
}
|
||||
Reference in New Issue
Block a user