Compare commits

...

16 Commits

43 changed files with 1796 additions and 194 deletions

1
.gitignore vendored
View File

@@ -82,3 +82,4 @@ Icon
Network Trash Folder
Temporary Items
.apdisk
*.db

20
.idea/compiler.xml generated
View File

@@ -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
View File

@@ -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>

View 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>

View File

@@ -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
View File

@@ -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>

View File

@@ -2,6 +2,7 @@
<html lang="zh">
<head>
<meta charset="UTF-8"/>
<link rel="icon" href="/icon.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>API Client</title>
<style>
@@ -16,10 +17,10 @@
<link rel="stylesheet" href="/jssdk/antd.css"/>
<link rel="stylesheet" href="/jssdk/helper.css"/>
<link rel="stylesheet" href="/jssdk/iconfont.css"/>
<script src="/jssdk/sdk.js"></script>
</head>
<body>
<div id="app"></div>
<script src="/jssdk/sdk.js"></script>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@@ -11,11 +11,13 @@
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"ant-design-vue": "4.x",
"licia": "^1.48.0",
"vue": "^3.5.13",
"vue-router": "4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.2.0"
"vite": "^6.2.0",
"vite-plugin-javascript-obfuscator": "^3.1.0"
}
}

746
client/pnpm-lock.yaml generated
View File

@@ -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
@@ -27,6 +30,9 @@ importers:
vite:
specifier: ^6.2.0
version: 6.2.0
vite-plugin-javascript-obfuscator:
specifier: ^3.1.0
version: 3.1.0
packages:
@@ -222,6 +228,14 @@ packages:
cpu: [x64]
os: [win32]
'@javascript-obfuscator/escodegen@2.3.0':
resolution: {integrity: sha512-QVXwMIKqYMl3KwtTirYIA6gOCiJ0ZDtptXqAv/8KWLG9uQU2fZqTVy7a/A5RvcoZhbDoFfveTxuGxJ5ibzQtkw==}
engines: {node: '>=6.0'}
'@javascript-obfuscator/estraverse@5.4.0':
resolution: {integrity: sha512-CZFX7UZVN9VopGbjTx4UXaXsi9ewoM1buL0kY7j1ftYdSs7p2spv9opxFjHlQ/QGTgh4UqufYqJJ0WKLml7b6w==}
engines: {node: '>=4.0'}
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
@@ -336,6 +350,12 @@ packages:
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
'@types/minimatch@3.0.5':
resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
'@types/validator@13.12.2':
resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==}
'@vitejs/plugin-vue@5.2.1':
resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -375,60 +395,298 @@ packages:
'@vue/shared@3.5.13':
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
acorn@8.8.2:
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
engines: {node: '>=0.4.0'}
hasBin: true
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
ant-design-vue@4.2.6:
resolution: {integrity: sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==}
engines: {node: '>=12.22.0'}
peerDependencies:
vue: '>=3.2.0'
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
array-differ@3.0.0:
resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==}
engines: {node: '>=8'}
array-tree-filter@2.1.0:
resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==}
array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
arrify@2.0.1:
resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==}
engines: {node: '>=8'}
assert@2.0.0:
resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==}
async-validator@4.2.5:
resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
call-bind@1.0.8:
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
engines: {node: '>= 0.4'}
call-bound@1.0.4:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
chance@1.1.9:
resolution: {integrity: sha512-TfxnA/DcZXRTA4OekA2zL9GH8qscbbl6X0ZqU4tXhGveVY/mXWvEQLt5GwZcYXTEyEFflVtj+pG8nc8EwSm1RQ==}
char-regex@1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
charenc@0.0.2:
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
class-validator@0.14.1:
resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
commander@10.0.0:
resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==}
engines: {node: '>=14'}
compute-scroll-into-view@1.0.20:
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
core-js@3.41.0:
resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
crypt@0.0.2:
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
define-properties@1.2.1:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
dom-align@1.12.4:
resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==}
dom-scroll-into-view@2.0.1:
resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es6-object-assign@1.1.0:
resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==}
esbuild@0.25.0:
resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==}
engines: {node: '>=18'}
hasBin: true
eslint-scope@7.1.1:
resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
eslint-visitor-keys@3.3.0:
resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'}
hasBin: true
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
estraverse@5.3.0:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
for-each@0.3.5:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
inversify@6.0.1:
resolution: {integrity: sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ==}
is-arguments@1.2.0:
resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==}
engines: {node: '>= 0.4'}
is-buffer@1.1.6:
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
is-callable@1.2.7:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
is-generator-function@1.1.0:
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
engines: {node: '>= 0.4'}
is-nan@1.3.2:
resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
engines: {node: '>= 0.4'}
is-plain-object@3.0.1:
resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==}
engines: {node: '>=0.10.0'}
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
is-typed-array@1.1.15:
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
engines: {node: '>= 0.4'}
javascript-obfuscator@4.1.1:
resolution: {integrity: sha512-gt+KZpIIrrxXHEQGD8xZrL8mTRwRY0U76/xz/YX0gZdPrSqQhT/c7dYLASlLlecT3r+FxE7je/+C0oLnTDCx4A==}
engines: {node: '>=12.22.0'}
hasBin: true
js-string-escape@1.0.1:
resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==}
engines: {node: '>= 0.8'}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
levn@0.3.0:
resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
engines: {node: '>= 0.8.0'}
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==}
@@ -442,6 +700,25 @@ packages:
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
md5@2.3.0:
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
mkdirp@2.1.3:
resolution: {integrity: sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw==}
engines: {node: '>=10'}
hasBin: true
multimatch@5.0.0:
resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==}
engines: {node: '>=10'}
nanoid@3.3.8:
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -450,13 +727,52 @@ packages:
nanopop@2.4.2:
resolution: {integrity: sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
object-is@1.1.6:
resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
engines: {node: '>= 0.4'}
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
opencollective-postinstall@2.0.3:
resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==}
hasBin: true
optionator@0.8.3:
resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
engines: {node: '>= 0.8.0'}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
postcss@8.5.3:
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.1.2:
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
engines: {node: '>= 0.8.0'}
process@0.11.10:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
engines: {node: '>= 0.6.0'}
reflect-metadata@0.1.13:
resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
@@ -468,9 +784,17 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
safe-regex-test@1.1.0:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
scroll-into-view-if-needed@2.2.31:
resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
shallow-equal@1.2.1:
resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==}
@@ -478,13 +802,47 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
string-template@1.0.0:
resolution: {integrity: sha512-SLqR3GBUXuoPP5MmYtD7ompvXiG87QjT6lzOszyXjTM86Uu7At7vNnt2xgyTLq5o9T4IxTYFyGxcULqpsmsfdg==}
stringz@2.1.0:
resolution: {integrity: sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==}
stylis@4.3.6:
resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
throttle-debounce@5.0.2:
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
engines: {node: '>=12.22'}
tslib@2.5.0:
resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
type-check@0.3.2:
resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
engines: {node: '>= 0.8.0'}
util@0.12.5:
resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
validator@13.12.0:
resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==}
engines: {node: '>= 0.10'}
vite-plugin-javascript-obfuscator@3.1.0:
resolution: {integrity: sha512-sf4JFlG1iUPl7bLXHGOy+bKWOQUFyXzJFWa+n2S2xMMvyfM+V9R40HhpZoIF1eAjifArM1SF7fbSFIaTuUIbPA==}
vite@6.2.0:
resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -547,6 +905,14 @@ packages:
warning@4.0.3:
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
which-typed-array@1.1.18:
resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==}
engines: {node: '>= 0.4'}
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
snapshots:
'@ant-design/colors@6.0.0':
@@ -659,6 +1025,17 @@ snapshots:
'@esbuild/win32-x64@0.25.0':
optional: true
'@javascript-obfuscator/escodegen@2.3.0':
dependencies:
'@javascript-obfuscator/estraverse': 5.4.0
esprima: 4.0.1
esutils: 2.0.3
optionator: 0.8.3
optionalDependencies:
source-map: 0.6.1
'@javascript-obfuscator/estraverse@5.4.0': {}
'@jridgewell/sourcemap-codec@1.5.0': {}
'@rollup/rollup-android-arm-eabi@4.34.9':
@@ -725,6 +1102,10 @@ snapshots:
'@types/estree@1.0.6': {}
'@types/minimatch@3.0.5': {}
'@types/validator@13.12.2': {}
'@vitejs/plugin-vue@5.2.1(vite@6.2.0)(vue@3.5.13)':
dependencies:
vite: 6.2.0
@@ -786,6 +1167,12 @@ snapshots:
'@vue/shared@3.5.13': {}
acorn@8.8.2: {}
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
ant-design-vue@4.2.6(vue@3.5.13):
dependencies:
'@ant-design/colors': 6.0.0
@@ -812,24 +1199,131 @@ snapshots:
vue-types: 3.0.2(vue@3.5.13)
warning: 4.0.3
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
array-differ@3.0.0: {}
array-tree-filter@2.1.0: {}
array-union@2.1.0: {}
arrify@2.0.1: {}
assert@2.0.0:
dependencies:
es6-object-assign: 1.1.0
is-nan: 1.3.2
object-is: 1.1.6
util: 0.12.5
async-validator@4.2.5: {}
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.1.0
balanced-match@1.0.2: {}
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
buffer-from@1.1.2: {}
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
call-bind@1.0.8:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
get-intrinsic: 1.3.0
set-function-length: 1.2.2
call-bound@1.0.4:
dependencies:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
chance@1.1.9: {}
char-regex@1.0.2: {}
charenc@0.0.2: {}
class-validator@0.14.1:
dependencies:
'@types/validator': 13.12.2
libphonenumber-js: 1.12.5
validator: 13.12.0
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
commander@10.0.0: {}
compute-scroll-into-view@1.0.20: {}
concat-map@0.0.1: {}
core-js@3.41.0: {}
crypt@0.0.2: {}
csstype@3.1.3: {}
dayjs@1.11.13: {}
deep-is@0.1.4: {}
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.1
es-errors: 1.3.0
gopd: 1.2.0
define-properties@1.2.1:
dependencies:
define-data-property: 1.1.4
has-property-descriptors: 1.0.2
object-keys: 1.1.1
dom-align@1.12.4: {}
dom-scroll-into-view@2.0.1: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
entities@4.5.0: {}
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es6-object-assign@1.1.0: {}
esbuild@0.25.0:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.0
@@ -858,15 +1352,151 @@ snapshots:
'@esbuild/win32-ia32': 0.25.0
'@esbuild/win32-x64': 0.25.0
eslint-scope@7.1.1:
dependencies:
esrecurse: 4.3.0
estraverse: 5.3.0
eslint-visitor-keys@3.3.0: {}
esprima@4.0.1: {}
esrecurse@4.3.0:
dependencies:
estraverse: 5.3.0
estraverse@5.3.0: {}
estree-walker@2.0.2: {}
esutils@2.0.3: {}
fast-deep-equal@3.1.3: {}
fast-levenshtein@2.0.6: {}
for-each@0.3.5:
dependencies:
is-callable: 1.2.7
fsevents@2.3.3:
optional: true
function-bind@1.1.2: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
gopd@1.2.0: {}
has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
dependencies:
es-define-property: 1.0.1
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.1.0
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
inherits@2.0.4: {}
inversify@6.0.1: {}
is-arguments@1.2.0:
dependencies:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-buffer@1.1.6: {}
is-callable@1.2.7: {}
is-generator-function@1.1.0:
dependencies:
call-bound: 1.0.4
get-proto: 1.0.1
has-tostringtag: 1.0.2
safe-regex-test: 1.1.0
is-nan@1.3.2:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
is-plain-object@3.0.1: {}
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
gopd: 1.2.0
has-tostringtag: 1.0.2
hasown: 2.0.2
is-typed-array@1.1.15:
dependencies:
which-typed-array: 1.1.18
javascript-obfuscator@4.1.1:
dependencies:
'@javascript-obfuscator/escodegen': 2.3.0
'@javascript-obfuscator/estraverse': 5.4.0
acorn: 8.8.2
assert: 2.0.0
chalk: 4.1.2
chance: 1.1.9
class-validator: 0.14.1
commander: 10.0.0
eslint-scope: 7.1.1
eslint-visitor-keys: 3.3.0
fast-deep-equal: 3.1.3
inversify: 6.0.1
js-string-escape: 1.0.1
md5: 2.3.0
mkdirp: 2.1.3
multimatch: 5.0.0
opencollective-postinstall: 2.0.3
process: 0.11.10
reflect-metadata: 0.1.13
source-map-support: 0.5.21
string-template: 1.0.0
stringz: 2.1.0
tslib: 2.5.0
js-string-escape@1.0.1: {}
js-tokens@4.0.0: {}
levn@0.3.0:
dependencies:
prelude-ls: 1.1.2
type-check: 0.3.2
libphonenumber-js@1.12.5: {}
licia@1.48.0: {}
lodash-es@4.17.21: {}
lodash@4.17.21: {}
@@ -879,18 +1509,70 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
math-intrinsics@1.1.0: {}
md5@2.3.0:
dependencies:
charenc: 0.0.2
crypt: 0.0.2
is-buffer: 1.1.6
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.11
mkdirp@2.1.3: {}
multimatch@5.0.0:
dependencies:
'@types/minimatch': 3.0.5
array-differ: 3.0.0
array-union: 2.1.0
arrify: 2.0.1
minimatch: 3.1.2
nanoid@3.3.8: {}
nanopop@2.4.2: {}
normalize-path@3.0.0: {}
object-is@1.1.6:
dependencies:
call-bind: 1.0.8
define-properties: 1.2.1
object-keys@1.1.1: {}
opencollective-postinstall@2.0.3: {}
optionator@0.8.3:
dependencies:
deep-is: 0.1.4
fast-levenshtein: 2.0.6
levn: 0.3.0
prelude-ls: 1.1.2
type-check: 0.3.2
word-wrap: 1.2.5
picocolors@1.1.1: {}
picomatch@2.3.1: {}
possible-typed-array-names@1.1.0: {}
postcss@8.5.3:
dependencies:
nanoid: 3.3.8
picocolors: 1.1.1
source-map-js: 1.2.1
prelude-ls@1.1.2: {}
process@0.11.10: {}
reflect-metadata@0.1.13: {}
regenerator-runtime@0.14.1: {}
resize-observer-polyfill@1.5.1: {}
@@ -920,18 +1602,71 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.34.9
fsevents: 2.3.3
safe-regex-test@1.1.0:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
is-regex: 1.2.1
scroll-into-view-if-needed@2.2.31:
dependencies:
compute-scroll-into-view: 1.0.20
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
es-errors: 1.3.0
function-bind: 1.1.2
get-intrinsic: 1.3.0
gopd: 1.2.0
has-property-descriptors: 1.0.2
shallow-equal@1.2.1: {}
source-map-js@1.2.1: {}
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
source-map@0.6.1: {}
string-template@1.0.0: {}
stringz@2.1.0:
dependencies:
char-regex: 1.0.2
stylis@4.3.6: {}
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
throttle-debounce@5.0.2: {}
tslib@2.5.0: {}
type-check@0.3.2:
dependencies:
prelude-ls: 1.1.2
util@0.12.5:
dependencies:
inherits: 2.0.4
is-arguments: 1.2.0
is-generator-function: 1.1.0
is-typed-array: 1.1.15
which-typed-array: 1.1.18
validator@13.12.0: {}
vite-plugin-javascript-obfuscator@3.1.0:
dependencies:
anymatch: 3.1.3
javascript-obfuscator: 4.1.1
vite@6.2.0:
dependencies:
esbuild: 0.25.0
@@ -961,3 +1696,14 @@ snapshots:
warning@4.0.3:
dependencies:
loose-envify: 1.4.0
which-typed-array@1.1.18:
dependencies:
available-typed-arrays: 1.0.7
call-bind: 1.0.8
call-bound: 1.0.4
for-each: 0.3.5
gopd: 1.2.0
has-tostringtag: 1.0.2
word-wrap@1.2.5: {}

BIN
client/public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,27 +1,55 @@
<script setup>
import {ref} from 'vue'
import {useRouter} from 'vue-router'
import {ref, watch} from "vue";
import {useRoute} from "vue-router";
const router = useRouter()
const headerNav = ref(['home'])
const route = useRoute();
const menus = ref([
{
key: 'home',
path: '/home',
name: '首页'
},
{
key: 'management',
path: '/management',
name: '管理台'
},
])
const headerNavSelected = ref(['home']);
// 根据路由路径设置选中的菜单项
const updateSelectedMenu = () => {
const path = route.path;
// 去除路径中的'/',获取路由名称
const routeName = path.split('/')[1] || 'home';
headerNavSelected.value = [routeName];
};
// 监听路由变化
watch(() => route.path, () => {
updateSelectedMenu();
}, {immediate: true}); // immediate: true 确保组件加载时立即执行一次
function handleHeaderNavSelect({item, key, selectedKeys}) {
router.push({name: key})
}
</script>
<template>
<a-layout class="container">
<a-layout-header class="header">
<div class="float-left">
<img class="icon w-12 h-12" src="/icon.png" alt="icon">
<span class="title mx-3 text-white text-xl">智能服务</span>
</div>
<a-menu
v-model:selectedKeys="headerNav"
@select="handleHeaderNavSelect"
v-model:selected-keys="headerNavSelected"
theme="dark"
mode="horizontal"
>
<a-menu-item key="home">首页</a-menu-item>
<a-menu-item key="management">管理台</a-menu-item>
<a-menu-item v-for="menu in menus" :key="menu.key">
<router-link :to="menu.path">{{ menu.name }}</router-link>
</a-menu-item>
</a-menu>
<div class="float-right">
</div>
</a-layout-header>
<router-view/>
</a-layout>
@@ -31,4 +59,8 @@ function handleHeaderNavSelect({item, key, selectedKeys}) {
.container {
min-height: 100vh;
}
.header {
padding: 0 20px 0 20px;
}
</style>

View 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>

View File

@@ -1,39 +1,44 @@
import {createApp} from 'vue'
import {createRouter, createWebHistory} from 'vue-router'
import App from './App.vue'
import App from '@/App.vue'
import {Layout, Menu} from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'
createApp(App)
.use(Layout)
.use(Menu)
.use(createRouter({
history: createWebHistory(),
routes: [
{path: '/', redirect: 'home'},
{
name: 'home',
path: '/home',
component: () => import('./views/Home.vue'),
},
{
name: 'management',
path: '/management',
component: () => import('./views/management/Index.vue'),
redirect: 'overview',
children: [
{
name: 'overview',
path: '/overview',
component: () => import('./views/management/Overview.vue'),
},
{
name: 'setting',
path: '/setting',
component: () => import('./views/management/Setting.vue'),
},
],
},
.use(Layout)
.use(Menu)
.use(createRouter({
history: createWebHistory(),
routes: [
{path: '/', redirect: 'home'},
{
name: 'home',
path: '/home',
component: () => import('@/views/Home.vue'),
},
{
name: 'management',
path: '/management',
component: () => import('@/views/management/Index.vue'),
redirect: '/management/overview',
children: [
{
name: 'overview',
path: 'overview',
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'),
},
],
}))
.mount('#app')
},
],
}))
.mount('#app')

View File

@@ -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)
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)
}
amisRequire('amis/embed').embed(
target,
amisJsonObject,
information,
{
theme: 'antd'
}
)
}
}
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
}

View File

@@ -1,6 +1,11 @@
<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')
}
onMounted(() => {
amisRender(
@@ -8,7 +13,31 @@ onMounted(() => {
information => {
return {
type: 'page',
body: 'Home'
body: [
"Home",
{
type: 'action',
label: '点击',
onEvent: {
click: {
actions: [
{
actionType: 'custom',
script: (context, doAction, event) => {
toastMessage()
}
}
]
}
}
},
{
type: 'custom',
onMount: (dom, value, onChange, props) => {
createApp(Flow).mount(dom)
}
}
]
}
},
)

View File

@@ -1,13 +1,67 @@
<script setup>
import {ref} from 'vue'
import {useRouter} from 'vue-router'
import {
ref,
watch,
} from 'vue'
import {useRoute} from 'vue-router'
const router = useRouter()
const sideNav = ref(['overview'])
const route = useRoute();
const menus = [
{
key: 'overview',
path: '/management/overview',
name: '概览',
},
{
key: 'system',
name: '系统管理',
children: [
{
key: 'organization',
path: '/management/organization',
name: '组织架构',
},
{
key: 'setting',
path: '/management/setting',
name: '设置',
},
],
},
]
const sideNavSelected = ref(['overview'])
const openKeys = ref([]) // 控制展开的子菜单
function handleSideNavSelect({item, key, selectedKeys}) {
router.push({name: key})
// 菜单项与其父菜单的映射关系
const menuItemToParent = ref({})
for (let index in menus) {
let menu = menus[index]
if (menu?.children) {
for (let subIndex in menu.children) {
let submenu = menu.children[subIndex]
menuItemToParent.value[submenu.key] = menu.key
}
}
}
// 更新侧边栏选中状态
const updateSideMenu = () => {
const pathSegments = route.path.split('/');
// 获取最后一个路径段作为当前页面的标识
const currentPage = pathSegments[pathSegments.length - 1];
sideNavSelected.value = [currentPage];
// 如果当前选中的菜单项在子菜单中,则自动展开父菜单
if (menuItemToParent.value[currentPage]) {
openKeys.value = [menuItemToParent.value[currentPage]];
}
};
// 监听路由变化
watch(() => route.path, () => {
updateSideMenu();
}, {immediate: true});
</script>
<template>
@@ -15,19 +69,25 @@ function handleSideNavSelect({item, key, selectedKeys}) {
<a-layout-sider>
<a-menu
class="h-full"
v-model:selectedKeys="sideNav"
@select="handleSideNavSelect"
v-model:selectedKeys="sideNavSelected"
v-model:openKeys="openKeys"
mode="inline"
>
<a-menu-item key="overview">概览</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">设置</a-menu-item>
</a-sub-menu>
</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>
</div>
</a-menu>
</a-layout-sider>
<div class="p-3 h-full w-full">

View 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>

View File

@@ -1,6 +1,6 @@
<script setup>
import {onMounted} from 'vue'
import {amisRender} from '../../utils.js'
import {amisRender} from '@/utils.js'
onMounted(() => {
amisRender(

View File

@@ -1,6 +1,6 @@
<script setup>
import {onMounted} from 'vue'
import {amisRender} from '../../utils.js'
import {amisRender} from '@/utils.js'
onMounted(() => {
amisRender(

View File

@@ -1,7 +1,38 @@
import { defineConfig } from 'vite'
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({
plugins: [vue()],
resolve: {
alias: {
'vue': 'vue/dist/vue.esm-bundler.js',
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
plugins: [
vue(),
obfuscatorPlugin({
apply: config => config['mode'] === 'production',
options: {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.75,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.4,
debugProtection: false,
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
renameGlobals: false,
stringArrayRotate: true,
selfDefending: true,
stringArray: true,
stringArrayEncoding: ['base64'],
stringArrayThreshold: 0.75,
transformObjectKeys: true,
unicodeEscapeSequence: false,
}
})
],
})

61
pom.xml
View File

@@ -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>

View File

@@ -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 -> {
};
}
}

View File

@@ -0,0 +1,9 @@
package com.lanyuanxiaoyao.server.configuration;
/**
* @author lanyuanxiaoyao
* @version 20250327
*/
public interface Constants {
String DATABASE_TABLE_PREFIX = "platform_";
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -1,21 +0,0 @@
package com.lanyuanxiaoyao.server.entity;
import com.yahoo.elide.annotation.LifeCycleHookBinding;
import com.yahoo.elide.core.lifecycle.CRUDEvent;
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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -1,46 +0,0 @@
package com.lanyuanxiaoyao.server.entity;
import com.yahoo.elide.annotation.Include;
import com.yahoo.elide.annotation.LifeCycleHookBinding;
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.SQLRestriction;
import org.hibernate.annotations.SoftDelete;
import org.hibernate.annotations.Where;
/**
* @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;
}

View File

@@ -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);
}

View File

@@ -0,0 +1,5 @@
package com.lanyuanxiaoyao.server.entity.mapper;
public interface EntityMapper<SE, CE> {
SE fromVO(CE creationVO);
}

View File

@@ -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);
}

View File

@@ -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> {
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -1,16 +1,25 @@
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
path: /json
path: /jsonapi
graphql:
enabled: true
path: /graphql
logging:
level:
com:
@@ -19,4 +28,6 @@ logging:
datastores:
jpql:
query:
DefaultQueryLogger: debug
DefaultQueryLogger: debug
fenix:
print-banner: false

View 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}}}}"
}