diff --git a/admin/package-lock.json b/admin/package-lock.json
index 5385d0c..9bfda42 100644
--- a/admin/package-lock.json
+++ b/admin/package-lock.json
@@ -19,10 +19,55 @@
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
+ "@vue/test-utils": "^2.4.6",
+ "jsdom": "^27.2.0",
"sass": "^1.83.0",
- "vite": "^5.4.2"
+ "vite": "^5.4.2",
+ "vitest": "^4.0.15"
}
},
+ "node_modules/@acemir/cssom": {
+ "version": "0.9.24",
+ "resolved": "https://registry.npmmirror.com/@acemir/cssom/-/cssom-0.9.24.tgz",
+ "integrity": "sha512-5YjgMmAiT2rjJZU7XK1SNI7iqTy92DpaYVgG6x63FxkJ11UpYfLndHJATtinWJClAXiOlW9XWaUyAQf8pMrQPg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@asamuzakjp/css-color": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-4.1.0.tgz",
+ "integrity": "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "lru-cache": "^11.2.2"
+ }
+ },
+ "node_modules/@asamuzakjp/dom-selector": {
+ "version": "6.7.5",
+ "resolved": "https://registry.npmmirror.com/@asamuzakjp/dom-selector/-/dom-selector-6.7.5.tgz",
+ "integrity": "sha512-Eks6dY8zau4m4wNRQjRVaKQRTalNcPcBvU1ZQ35w5kKRk1gUeNCkVLsRiATurjASTp3TKM4H10wsI50nx3NZdw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@asamuzakjp/nwsapi": "^2.3.9",
+ "bidi-js": "^1.0.3",
+ "css-tree": "^3.1.0",
+ "is-potential-custom-element-name": "^1.0.1",
+ "lru-cache": "^11.2.2"
+ }
+ },
+ "node_modules/@asamuzakjp/nwsapi": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmmirror.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
+ "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
@@ -87,6 +132,141 @@
"vue": "^3.0.11"
}
},
+ "node_modules/@csstools/color-helpers": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmmirror.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+ "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@csstools/css-calc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmmirror.com/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-color-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+ "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.1.0",
+ "@csstools/css-calc": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-parser-algorithms": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmmirror.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-syntax-patches-for-csstree": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmmirror.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.20.tgz",
+ "integrity": "sha512-8BHsjXfSciZxjmHQOuVdW2b8WLUPts9a+mfL13/PzEviufUEW2xnvQuOlKs9dRBHgRqJ53SF/DUoK9+MZk72oQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@csstools/css-tokenizer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmmirror.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@emotion/hash": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
@@ -382,6 +562,23 @@
"node": ">=12"
}
},
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
@@ -399,6 +596,23 @@
"node": ">=12"
}
},
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
@@ -416,6 +630,23 @@
"node": ">=12"
}
},
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
@@ -505,6 +736,24 @@
"vue": ">=3"
}
},
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
@@ -517,6 +766,13 @@
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==",
"license": "Apache-2.0"
},
+ "node_modules/@one-ini/wasm": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmmirror.com/@one-ini/wasm/-/wasm-0.1.1.tgz",
+ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
@@ -827,6 +1083,17 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
@@ -1135,6 +1402,31 @@
"win32"
]
},
+ "node_modules/@standard-schema/spec": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.0.0.tgz",
+ "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmmirror.com/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmmirror.com/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -1177,6 +1469,90 @@
"vue": "^3.2.25"
}
},
+ "node_modules/@vitest/expect": {
+ "version": "4.0.15",
+ "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-4.0.15.tgz",
+ "integrity": "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.0.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.0.15",
+ "@vitest/utils": "4.0.15",
+ "chai": "^6.2.1",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.0.15",
+ "resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-4.0.15.tgz",
+ "integrity": "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.0.15",
+ "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-4.0.15.tgz",
+ "integrity": "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.0.15",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.0.15",
+ "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-4.0.15.tgz",
+ "integrity": "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.0.15",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.0.15",
+ "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-4.0.15.tgz",
+ "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.0.15",
+ "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-4.0.15.tgz",
+ "integrity": "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.0.15",
+ "tinyrainbow": "^3.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
"node_modules/@vue/compiler-core": {
"version": "3.5.22",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz",
@@ -1283,6 +1659,73 @@
"integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==",
"license": "MIT"
},
+ "node_modules/@vue/test-utils": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmmirror.com/@vue/test-utils/-/test-utils-2.4.6.tgz",
+ "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-beautify": "^1.14.9",
+ "vue-component-type-helpers": "^2.0.0"
+ }
+ },
+ "node_modules/abbrev": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-2.0.0.tgz",
+ "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
@@ -1306,21 +1749,48 @@
"proxy-from-env": "^1.1.0"
}
},
- "node_modules/braces": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
- "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bidi-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.0.3.tgz",
+ "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
"dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
- "fill-range": "^7.1.1"
- },
- "engines": {
- "node": ">=8"
+ "require-from-string": "^2.0.2"
}
},
- "node_modules/call-bind-apply-helpers": {
+ "node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
@@ -1333,6 +1803,16 @@
"node": ">= 0.4"
}
},
+ "node_modules/chai": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmmirror.com/chai/-/chai-6.2.1.tgz",
+ "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
@@ -1349,6 +1829,26 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1361,6 +1861,42 @@
"node": ">= 0.8"
}
},
+ "node_modules/commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmmirror.com/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/css-render": {
"version": "0.15.14",
"resolved": "https://registry.npmjs.org/css-render/-/css-render-0.15.14.tgz",
@@ -1377,12 +1913,55 @@
"integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
"license": "MIT"
},
+ "node_modules/css-tree": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-3.1.0.tgz",
+ "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.12.2",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/cssstyle": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmmirror.com/cssstyle/-/cssstyle-5.3.3.tgz",
+ "integrity": "sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@asamuzakjp/css-color": "^4.0.3",
+ "@csstools/css-syntax-patches-for-csstree": "^1.0.14",
+ "css-tree": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
+ "node_modules/data-urls": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/data-urls/-/data-urls-6.0.0.tgz",
+ "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^15.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
"node_modules/date-fns": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
@@ -1408,6 +1987,31 @@
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
"license": "MIT"
},
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -1445,6 +2049,39 @@
"node": ">= 0.4"
}
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/editorconfig": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/editorconfig/-/editorconfig-1.0.4.tgz",
+ "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@one-ini/wasm": "0.1.1",
+ "commander": "^10.0.0",
+ "minimatch": "9.0.1",
+ "semver": "^7.5.3"
+ },
+ "bin": {
+ "editorconfig": "bin/editorconfig"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@@ -1475,6 +2112,13 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
@@ -1553,6 +2197,16 @@
"integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==",
"license": "MIT"
},
+ "node_modules/expect-type": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmmirror.com/expect-type/-/expect-type-1.2.2.tgz",
+ "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -1587,6 +2241,23 @@
}
}
},
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
@@ -1664,6 +2335,43 @@
"node": ">= 0.4"
}
},
+ "node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmmirror.com/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -1724,6 +2432,60 @@
"node": ">=12.0.0"
}
},
+ "node_modules/html-encoding-sniffer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-encoding": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/immutable": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz",
@@ -1731,6 +2493,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -1742,6 +2511,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -1767,6 +2546,108 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/js-beautify": {
+ "version": "1.15.4",
+ "resolved": "https://registry.npmmirror.com/js-beautify/-/js-beautify-1.15.4.tgz",
+ "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "config-chain": "^1.1.13",
+ "editorconfig": "^1.0.4",
+ "glob": "^10.4.2",
+ "js-cookie": "^3.0.5",
+ "nopt": "^7.2.1"
+ },
+ "bin": {
+ "css-beautify": "js/bin/css-beautify.js",
+ "html-beautify": "js/bin/html-beautify.js",
+ "js-beautify": "js/bin/js-beautify.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/jsdom": {
+ "version": "27.2.0",
+ "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-27.2.0.tgz",
+ "integrity": "sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@acemir/cssom": "^0.9.23",
+ "@asamuzakjp/dom-selector": "^6.7.4",
+ "cssstyle": "^5.3.3",
+ "data-urls": "^6.0.0",
+ "decimal.js": "^10.6.0",
+ "html-encoding-sniffer": "^4.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.6",
+ "is-potential-custom-element-name": "^1.0.1",
+ "parse5": "^8.0.0",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^6.0.0",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^8.0.0",
+ "whatwg-encoding": "^3.1.1",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^15.1.0",
+ "ws": "^8.18.3",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "canvas": "^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -1779,6 +2660,16 @@
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
+ "node_modules/lru-cache": {
+ "version": "11.2.4",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.4.tgz",
+ "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -1797,6 +2688,13 @@
"node": ">= 0.4"
}
},
+ "node_modules/mdn-data": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.12.2.tgz",
+ "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -1833,6 +2731,39 @@
"node": ">= 0.6"
}
},
+ "node_modules/minimatch": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.1.tgz",
+ "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/naive-ui": {
"version": "2.43.1",
"resolved": "https://registry.npmjs.org/naive-ui/-/naive-ui-2.43.1.tgz",
@@ -1889,25 +2820,126 @@
"license": "MIT",
"optional": true
},
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "license": "ISC"
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "node_modules/nopt": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmmirror.com/nopt/-/nopt-7.2.1.tgz",
+ "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=8.6"
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^2.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/obug": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/obug/-/obug-2.1.1.tgz",
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/parse5": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmmirror.com/parse5/-/parse5-8.0.0.tgz",
+ "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
},
"node_modules/pinia": {
"version": "2.3.1",
@@ -1959,12 +2991,29 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmmirror.com/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@@ -1979,6 +3028,16 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/rollup": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz",
@@ -2021,6 +3080,13 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/sass": {
"version": "1.93.3",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.93.3.tgz",
@@ -2042,161 +3108,1137 @@
"@parcel/watcher": "^2.4.1"
}
},
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
"node_modules/seemly": {
"version": "0.3.10",
"resolved": "https://registry.npmjs.org/seemly/-/seemly-0.3.10.tgz",
"integrity": "sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==",
"license": "MIT"
},
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "license": "BSD-3-Clause",
+ "node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
"engines": {
- "node": ">=0.10.0"
+ "node": ">=10"
}
},
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
- "optional": true,
"dependencies": {
- "is-number": "^7.0.0"
+ "shebang-regex": "^3.0.0"
},
"engines": {
- "node": ">=8.0"
+ "node": ">=8"
}
},
- "node_modules/treemate": {
- "version": "0.3.11",
- "resolved": "https://registry.npmjs.org/treemate/-/treemate-0.3.11.tgz",
- "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==",
- "license": "MIT"
- },
- "node_modules/vdirs": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/vdirs/-/vdirs-0.1.8.tgz",
- "integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==",
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "evtd": "^0.2.2"
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
},
- "peerDependencies": {
- "vue": "^3.0.11"
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/vfonts": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/vfonts/-/vfonts-0.0.3.tgz",
- "integrity": "sha512-nguyw8L6Un8eelg1vQ31vIU2ESxqid7EYmy8V+MDeMaHBqaRSkg3dTBToC1PR00D89UzS/SLkfYPnx0Wf23IQQ==",
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
"license": "MIT"
},
- "node_modules/vite": {
- "version": "5.4.21",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
- "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "node_modules/std-env": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz",
+ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.21.3",
- "postcss": "^8.4.43",
- "rollup": "^4.20.0"
- },
- "bin": {
- "vite": "bin/vite.js"
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": ">=12"
},
"funding": {
- "url": "https://github.com/vitejs/vite?sponsor=1"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- },
- "peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
- "lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "sass-embedded": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- }
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/vooks": {
- "version": "0.2.12",
- "resolved": "https://registry.npmjs.org/vooks/-/vooks-0.2.12.tgz",
- "integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==",
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "evtd": "^0.2.2"
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
},
- "peerDependencies": {
- "vue": "^3.0.0"
+ "engines": {
+ "node": ">=8"
}
},
- "node_modules/vue": {
- "version": "3.5.22",
- "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz",
- "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==",
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.22",
- "@vue/compiler-sfc": "3.5.22",
- "@vue/runtime-dom": "3.5.22",
- "@vue/server-renderer": "3.5.22",
- "@vue/shared": "3.5.22"
- },
- "peerDependencies": {
- "typescript": "*"
+ "ansi-regex": "^5.0.1"
},
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
+ "engines": {
+ "node": ">=8"
}
},
- "node_modules/vue-demi": {
- "version": "0.14.10",
- "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
- "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
- "hasInstallScript": true,
+ "node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "dev": true,
"license": "MIT",
- "bin": {
- "vue-demi-fix": "bin/vue-demi-fix.js",
- "vue-demi-switch": "bin/vue-demi-switch.js"
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.2.tgz",
+ "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
+ "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tldts": {
+ "version": "7.0.19",
+ "resolved": "https://registry.npmmirror.com/tldts/-/tldts-7.0.19.tgz",
+ "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tldts-core": "^7.0.19"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
+ }
+ },
+ "node_modules/tldts-core": {
+ "version": "7.0.19",
+ "resolved": "https://registry.npmmirror.com/tldts-core/-/tldts-core-7.0.19.tgz",
+ "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-6.0.0.tgz",
+ "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tldts": "^7.0.5"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/tr46/-/tr46-6.0.0.tgz",
+ "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/treemate": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/treemate/-/treemate-0.3.11.tgz",
+ "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==",
+ "license": "MIT"
+ },
+ "node_modules/vdirs": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/vdirs/-/vdirs-0.1.8.tgz",
+ "integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==",
+ "license": "MIT",
+ "dependencies": {
+ "evtd": "^0.2.2"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.11"
+ }
+ },
+ "node_modules/vfonts": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/vfonts/-/vfonts-0.0.3.tgz",
+ "integrity": "sha512-nguyw8L6Un8eelg1vQ31vIU2ESxqid7EYmy8V+MDeMaHBqaRSkg3dTBToC1PR00D89UzS/SLkfYPnx0Wf23IQQ==",
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.0.15",
+ "resolved": "https://registry.npmmirror.com/vitest/-/vitest-4.0.15.tgz",
+ "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.0.15",
+ "@vitest/mocker": "4.0.15",
+ "@vitest/pretty-format": "4.0.15",
+ "@vitest/runner": "4.0.15",
+ "@vitest/snapshot": "4.0.15",
+ "@vitest/spy": "4.0.15",
+ "@vitest/utils": "4.0.15",
+ "es-module-lexer": "^1.7.0",
+ "expect-type": "^1.2.2",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^3.10.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.0.3",
+ "vite": "^6.0.0 || ^7.0.0",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.0.15",
+ "@vitest/browser-preview": "4.0.15",
+ "@vitest/browser-webdriverio": "4.0.15",
+ "@vitest/ui": "4.0.15",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vitest/node_modules/@vitest/mocker": {
+ "version": "4.0.15",
+ "resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-4.0.15.tgz",
+ "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.0.15",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/vitest/node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/vitest/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/vitest/node_modules/vite": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.6.tgz",
+ "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vooks": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/vooks/-/vooks-0.2.12.tgz",
+ "integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==",
+ "license": "MIT",
+ "dependencies": {
+ "evtd": "^0.2.2"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/vue": {
+ "version": "3.5.22",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz",
+ "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.22",
+ "@vue/compiler-sfc": "3.5.22",
+ "@vue/runtime-dom": "3.5.22",
+ "@vue/server-renderer": "3.5.22",
+ "@vue/shared": "3.5.22"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-component-type-helpers": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz",
+ "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
@@ -2246,6 +4288,236 @@
"peerDependencies": {
"vue": "^3.0.11"
}
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.0.tgz",
+ "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-15.1.0.tgz",
+ "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "^6.0.0",
+ "webidl-conversions": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.18.3",
+ "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz",
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true,
+ "license": "MIT"
}
}
}
diff --git a/admin/package.json b/admin/package.json
index ababe21..378e5b5 100644
--- a/admin/package.json
+++ b/admin/package.json
@@ -6,7 +6,9 @@
"scripts": {
"dev": "vite",
"build": "vite build",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "test": "vitest",
+ "test:run": "vitest run"
},
"dependencies": {
"@iconify/vue": "^4.1.2",
@@ -20,7 +22,10 @@
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
+ "@vue/test-utils": "^2.4.6",
+ "jsdom": "^27.2.0",
"sass": "^1.83.0",
- "vite": "^5.4.2"
+ "vite": "^5.4.2",
+ "vitest": "^4.0.15"
}
}
diff --git a/admin/src/setupTests.js b/admin/src/setupTests.js
new file mode 100644
index 0000000..bb58519
--- /dev/null
+++ b/admin/src/setupTests.js
@@ -0,0 +1,41 @@
+// src/setupTests.js
+import { describe, expect, it, vi, beforeEach } from 'vitest'
+import { createApp } from 'vue'
+
+// 全局测试设置
+globalThis.describe = describe
+globalThis.it = it
+globalThis.expect = expect
+globalThis.vi = vi
+globalThis.beforeEach = beforeEach
+
+// 模拟常用的全局函数或对象
+vi.mock('axios', () => ({
+ default: {
+ get: vi.fn(() => Promise.resolve({ data: {} })),
+ post: vi.fn(() => Promise.resolve({ data: {} })),
+ put: vi.fn(() => Promise.resolve({ data: {} })),
+ delete: vi.fn(() => Promise.resolve({ data: {} }))
+ }
+}))
+
+// 模拟Vue Router
+vi.mock('vue-router', () => ({
+ useRouter: vi.fn(() => ({
+ push: vi.fn(),
+ replace: vi.fn()
+ })),
+ useRoute: vi.fn(() => ({
+ params: {},
+ query: {}
+ }))
+}))
+
+// 模拟Pinia
+vi.mock('pinia', () => ({
+ defineStore: vi.fn(() => ({
+ state: vi.fn(() => ({})),
+ getters: {},
+ actions: {}
+ }))
+}))
diff --git a/admin/src/utils/__tests__/request.test.js b/admin/src/utils/__tests__/request.test.js
new file mode 100644
index 0000000..337d586
--- /dev/null
+++ b/admin/src/utils/__tests__/request.test.js
@@ -0,0 +1,29 @@
+// src/utils/__tests__/request.test.js
+import { describe, it, expect, vi } from 'vitest'
+import request from '../request'
+
+describe('request 工具函数', () => {
+ it('应该存在', () => {
+ expect(request).toBeDefined()
+ })
+
+ it('应该有 get 方法', () => {
+ expect(request.get).toBeDefined()
+ expect(typeof request.get).toBe('function')
+ })
+
+ it('应该有 post 方法', () => {
+ expect(request.post).toBeDefined()
+ expect(typeof request.post).toBe('function')
+ })
+
+ it('应该有 put 方法', () => {
+ expect(request.put).toBeDefined()
+ expect(typeof request.put).toBe('function')
+ })
+
+ it('应该有 delete 方法', () => {
+ expect(request.delete).toBeDefined()
+ expect(typeof request.delete).toBe('function')
+ })
+})
diff --git a/admin/vite.config.js b/admin/vite.config.js
index 1f0e884..da7780f 100644
--- a/admin/vite.config.js
+++ b/admin/vite.config.js
@@ -1,6 +1,6 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
-import { resolve } from 'path' // 👈 添加这一行
+import { resolve } from 'path'
export default defineConfig({
plugins: [
@@ -31,5 +31,13 @@ export default defineConfig({
changeOrigin: true
}
}
+ },
+ test: {
+ environment: 'jsdom',
+ include: ['src/**/*.spec.js', 'src/**/*.test.js'],
+ setupFiles: ['src/setupTests.js'],
+ coverage: {
+ reporter: ['text', 'json', 'html']
+ }
}
})
\ No newline at end of file
diff --git a/miniprogram/project.config.json b/miniprogram/project.config.json
index d3ed3bc..5ed1fd3 100644
--- a/miniprogram/project.config.json
+++ b/miniprogram/project.config.json
@@ -1,33 +1,52 @@
{
- "miniprogramRoot": "unpackage/dist/dev/mp-weixin/",
- "projectname": "CampusGuide",
- "description": "校园导览小程序",
- "appid": "wxe6084d4844b6b9cc",
- "setting": {
- "urlCheck": false,
- "es6": true,
- "enhance": true,
- "postcss": true,
- "minified": true,
- "newFeature": true,
- "coverView": true,
- "nodeModules": false,
- "autoAudits": false,
- "showShadowRootInWxmlPanel": true,
- "scopeDataCheck": false,
- "checkInvalidKey": true,
- "checkSiteMap": true,
- "uploadWithSourceMap": true,
- "compileHotReLoad": true,
- "useMultiFrameRuntime": true,
- "useApiHook": true,
- "babelSetting": {
- "ignore": [],
- "disablePlugins": [],
- "outputPath": ""
+ "miniprogramRoot": "unpackage/dist/dev/mp-weixin/",
+ "projectname": "CampusGuide",
+ "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
+ "appid": "touristappid",
+ "setting": {
+ "urlCheck": false,
+ "es6": true,
+ "enhance": true,
+ "postcss": true,
+ "minified": true,
+ "newFeature": true,
+ "coverView": true,
+ "nodeModules": false,
+ "autoAudits": false,
+ "showShadowRootInWxmlPanel": true,
+ "scopeDataCheck": false,
+ "checkInvalidKey": true,
+ "checkSiteMap": true,
+ "uploadWithSourceMap": true,
+ "compileHotReLoad": true,
+ "useMultiFrameRuntime": true,
+ "useApiHook": true,
+ "babelSetting": {
+ "ignore": [],
+ "disablePlugins": [],
+ "outputPath": ""
+ },
+ "lazyloadPlaceholderEnable": false,
+ "preloadBackgroundData": false,
+ "uglifyFileName": false,
+ "packNpmManually": false,
+ "packNpmRelationList": [],
+ "minifyWXSS": true,
+ "useStaticServer": true,
+ "showES6CompileOption": false,
+ "disableUseStrict": false,
+ "useCompilerPlugins": false
+ },
+ "compileType": "miniprogram",
+ "libVersion": "3.7.7",
+ "condition": {},
+ "srcMiniprogramRoot": "unpackage/dist/dev/mp-weixin/",
+ "packOptions": {
+ "ignore": [],
+ "include": []
+ },
+ "editorSetting": {
+ "tabIndent": "insertSpaces",
+ "tabSize": 4
}
- },
- "compileType": "miniprogram",
- "libVersion": "3.9.3",
- "condition": {}
-}
+}
\ No newline at end of file
diff --git a/miniprogram/project.private.config.json b/miniprogram/project.private.config.json
new file mode 100644
index 0000000..5d292e7
--- /dev/null
+++ b/miniprogram/project.private.config.json
@@ -0,0 +1,7 @@
+{
+ "projectname": "miniprogram",
+ "setting": {
+ "compileHotReLoad": true
+ },
+ "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html"
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 8ace9f2..5e0a168 100644
--- a/pom.xml
+++ b/pom.xml
@@ -271,6 +271,7 @@
1.18.30
+ true
diff --git a/src/main/java/cn/cxdproject/coder/common/aspect/ServiceLogAspect.java b/src/main/java/cn/cxdproject/coder/common/aspect/ServiceLogAspect.java
index a642dd8..8b0232b 100644
--- a/src/main/java/cn/cxdproject/coder/common/aspect/ServiceLogAspect.java
+++ b/src/main/java/cn/cxdproject/coder/common/aspect/ServiceLogAspect.java
@@ -1,45 +1,207 @@
package cn.cxdproject.coder.common.aspect;
+import cn.cxdproject.coder.common.anno.Loggable;
+import cn.cxdproject.coder.common.context.AuthContext;
+import cn.cxdproject.coder.common.enums.LogType;
+import cn.cxdproject.coder.model.entity.OperationLog;
+import cn.cxdproject.coder.model.entity.User;
+import cn.cxdproject.coder.service.OperationLogService;
+import cn.cxdproject.coder.utils.IpUtils;
+import cn.cxdproject.coder.utils.JsonUtils;
+import cn.cxdproject.coder.utils.StringUtils;
+import cn.hutool.core.date.LocalDateTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.aop.framework.AopProxyUtils;
+import org.springframework.context.expression.MethodBasedEvaluationContext;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
+import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.util.Arrays;
import java.util.Date;
+import java.util.concurrent.CompletableFuture;
+
-/**
- * 服务层日志切面
- * 记录所有Service方法的调用信息
- *
- * @author heathcetide
- */
-@Slf4j
@Aspect
@Component
+@Slf4j
public class ServiceLogAspect {
- @Pointcut("execution(* cn.cxdproject.coder.service.*.*(..))")
- public void pointcut() {
+ private OperationLogService operationLogService;
+ private final ExpressionParser spelParser = new SpelExpressionParser();
+
+ public ServiceLogAspect(OperationLogService operationLogService) {
+ this.operationLogService = operationLogService;
}
- @Before("pointcut()")
- public void before(JoinPoint joinPoint) {
- // 用户[1.2.3.4],在[xxx],访问了[com.nowcoder.community.service.xxx()].
- ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- if (attributes == null) {
- return;
+ @Pointcut("@annotation(cn.cxdproject.coder.common.anno.Loggable)")
+ public void loggablePointcut() {
+
+ }
+
+ @Around("loggablePointcut()")
+ public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ Method method = signature.getMethod();
+ Class> targetClass = AopProxyUtils.ultimateTargetClass(joinPoint.getTarget());
+ method = targetClass.getMethod(method.getName(), method.getParameterTypes());
+ Loggable loggable = method.getAnnotation(Loggable.class);
+
+ OperationLog logEntity = new OperationLog();
+ logEntity.setTimestamp(LocalDateTime.now());
+ logEntity.setDeleted(false);
+
+ // 1. 操作类型
+ LogType logType = loggable.type();
+ logEntity.setType(logType.getCode());
+
+ // 2. 操作描述
+ String desc = resolveSpel(loggable.value(), method, joinPoint.getArgs());
+ logEntity.setDescription(StringUtils.hasText(desc) ? desc : logType.getDescription());
+
+ // 3. 操作人信息
+ User currentUser = AuthContext.getCurrentUser();
+
+ if (currentUser == null){
+ log.warn("未登录");
+ logEntity.setUserId(0L);
+ }else {
+ logEntity.setUserId(currentUser.getId());
+ }
+ String clientIp = IpUtils.getIpAddr();
+ logEntity.setOperator(clientIp);
+
+
+
+ // ===== 4. 请求参数快照(使用你的 JsonUtils)=====
+ try {
+ logEntity.setParams(JsonUtils.toJson(joinPoint.getArgs()));
+ } catch (Exception e) {
+ log.warn("序列化操作日志参数失败", e);
+ logEntity.setParams("[PARAM_SERIALIZE_ERROR]");
}
- HttpServletRequest request = attributes.getRequest();
- String ip = request.getRemoteHost();
- String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
- String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
- log.info("用户[{}],在[{}],访问了[{}]", ip, now, target);
+
+ // ===== 5. 执行目标方法并记录结果 =====
+ Object result;
+ try {
+ result = joinPoint.proceed();
+ logEntity.setSuccess(true);
+
+ // 避免记录大数据量结果
+ if (result == null) {
+ logEntity.setResult("null");
+ } else if (isLargeResult(result)) {
+ // 是大结果
+ logEntity.setResult("数据量过大,暂不记录");
+ } else {
+ // 小结果
+ logEntity.setResult(safeToJson(result));
+ }
+ } catch (Exception ex) {
+ logEntity.setSuccess(false);
+ logEntity.setResult("{\"error\": \"" + escapeJson(ex.getMessage()) + "\"}");
+ throw ex;
+ }
+
+ // ===== 6. 异步保存日志 =====
+ saveLogAsync(logEntity);
+
+ return result;
+ }
+
+ private String resolveSpel(String expression, Method method, Object[] args) {
+ if (!StringUtils.hasText(expression) || !expression.contains("#{")) {
+ return expression;
+ }
+
+ try {
+ // 使用 Spring 的 MethodBasedEvaluationContext
+ EvaluationContext context = new MethodBasedEvaluationContext(null, method, args,
+ new LocalVariableTableParameterNameDiscoverer());
+
+ ExpressionParser parser = new SpelExpressionParser();
+ return parser.parseExpression(expression, new TemplateParserContext())
+ .getValue(context, String.class);
+ } catch (Exception e) {
+ log.warn("SpEL 解析失败: {}", expression, e);
+ return expression; // 降级
+ }
+ }
+
+ /**
+ * 获取方法参数名(需编译时保留参数名:-parameters)
+ */
+ private String[] getParameterNames(Method method) {
+ return Arrays.stream(method.getParameters())
+ .map(Parameter::getName)
+ .toArray(String[]::new);
+ }
+
+ /**
+ * 判断结果是否过大(避免记录分页、列表等)
+ */
+ private boolean isLargeResult(Object result) {
+ if (result instanceof Iterable>) {
+ return true;
+ }
+ if (result.getClass().isArray()) {
+ return true;
+ }
+ // 可根据需要扩展,例如 Map size > 100
+ return false;
+ }
+
+ /**
+ * 安全地将对象转为 JSON 字符串
+ */
+ private String safeToJson(Object obj) {
+ try {
+ return JsonUtils.toJson(obj);
+ } catch (Exception e) {
+ log.warn("结果序列化失败", e);
+ return "[RESULT_SERIALIZE_ERROR]";
+ }
+ }
+
+ /**
+ * 转义双引号,用于嵌入 JSON 字符串
+ */
+ private String escapeJson(String str) {
+ if (str == null) return "";
+ return str.replace("\"", "\\\"");
+ }
+
+
+ protected void saveLogAsync(OperationLog operationLog) {
+ CompletableFuture.runAsync(() -> {
+ try {
+ operationLogService.save(operationLog);
+ } catch (Exception e) {
+ log.error("异步保存操作日志失败", e);
+ }
+ });
}
}
diff --git a/src/main/java/cn/cxdproject/coder/common/enums/LogType.java b/src/main/java/cn/cxdproject/coder/common/enums/LogType.java
index 10337cf..82b4cf5 100644
--- a/src/main/java/cn/cxdproject/coder/common/enums/LogType.java
+++ b/src/main/java/cn/cxdproject/coder/common/enums/LogType.java
@@ -1,54 +1,107 @@
package cn.cxdproject.coder.common.enums;
+import lombok.Getter;
+
/**
* 通用操作日志类型枚举
*
* @author heathcetide
*/
+@Getter
public enum LogType {
- USER_LOGIN("用户登录"),
-
- USER_REGISTER("用户注册"),
-
- USER_UPDATE("用户信息更新"),
-
- USER_DELETE("用户删除"),
-
- USER_LOGOUT("用户退出登录"),
-
- AUTH("认证与授权"),
-
- SYSTEM_MESSAGE("系统管理"),
-
- USER_MANAGEMENT("用户管理"),
-
- DATA_MANAGEMENT("数据管理"),
-
- CONFIGURATION("系统配置"),
-
- MONITORING("系统监控"),
-
- SECURITY("安全审计"),
-
- API_CALL("接口调用"),
-
- ORG_MODULE("组织接口调用"),
-
- TASK_EXECUTION("任务执行"),
-
- THIRD_PARTY("第三方服务"),
-
- OTHER("其他");
-
+ // 用户相关
+ USER_LOGIN("USER_LOGIN", "用户登录"),
+ USER_LOGOUT("USER_LOGOUT", "用户登出"),
+ USER_CREATE("USER_CREATE", "创建用户"),
+ USER_UPDATE("USER_UPDATE", "更新用户信息"),
+ USER_DELETE("USER_DELETE", "删除用户"),
+ AVATAR_UPDATE("AVATAR_UPDATE","用户更新头像"),
+
+ // 文章内容相关
+ ARTICLE_GET("ARTICLE_GET", "根据id查找文章"),
+ ARTICLE_ADMIN_GET_PAGE("ARTICLE_ADMIN_GET_PAGE", "管理员分页查询"),
+ ARTICLE_USER_GET_PAGE("ARTICLE_USER_GET_PAGE", "用户分页查询"),
+ ARTICLE_UPDATE("ARTICLE_UPDATE", "更新文章"),
+ ARTICLE_DELETE("ARTICLE_DELETE","删除文章"),
+ ARTICLE_CREATE("ARTICLE_CREATE","新建文章"),
+
+ // 系统操作
+ FILE_UPLOAD("FILE_UPLOAD", "上传文件"),
+
+ //影视相关操作
+ DRAMA_GET("DRAMA_GET", "根据ID查找影视"),
+ DRAMA_ADMIN_GET_PAGE("DRAMA_ADMIN_GET_PAGE", "管理员分页查询影视"),
+ DRAMA_USER_GET_PAGE("DRAMA_USER_GET_PAGE", "用户分页查询影视"),
+ DRAMA_UPDATE("DRAMA_UPDATE", "更新影视"),
+ DRAMA_DELETE("DRAMA_DELETE", "删除影视"),
+ DRAMA_CREATE("DRAMA_CREATE", "新建影视"),
+
+ //酒店相关操作
+ HOTEL_GET("HOTEL_GET", "根据ID查找酒店"),
+ HOTEL_ADMIN_GET_PAGE("HOTEL_ADMIN_GET_PAGE", "管理员分页查询酒店"),
+ HOTEL_USER_GET_PAGE("HOTEL_USER_GET_PAGE", "用户分页查询酒店"),
+ HOTEL_UPDATE("HOTEL_UPDATE", "更新酒店"),
+ HOTEL_DELETE("HOTEL_DELETE", "删除酒店"),
+ HOTEL_CREATE("HOTEL_CREATE", "新建酒店"),
+
+ //场地相关操作
+ LOCATION_GET("LOCATION_GET", "根据ID查找场地"),
+ LOCATION_ADMIN_GET_PAGE("LOCATION_ADMIN_GET_PAGE", "管理员分页查询场地"),
+ LOCATION_USER_GET_PAGE("LOCATION_USER_GET_PAGE", "用户分页查询场地"),
+ LOCATION_UPDATE("LOCATION_UPDATE", "更新场地"),
+ LOCATION_DELETE("LOCATION_DELETE", "删除场地"),
+ LOCATION_CREATE("LOCATION_CREATE", "新建场地"),
+
+ //政策相关操作
+ POLICY_GET("POLICY_GET", "根据ID查找政策"),
+ POLICY_ADMIN_GET_PAGE("POLICY_ADMIN_GET_PAGE", "管理员分页查询政策"),
+ POLICY_USER_GET_PAGE("POLICY_USER_GET_PAGE", "用户分页查询政策"),
+ POLICY_UPDATE("POLICY_UPDATE", "更新政策"),
+ POLICY_DELETE("POLICY_DELETE", "删除政策"),
+ POLICY_CREATE("POLICY_CREATE", "新建政策"),
+
+ //申请相关操作
+ REPORT_USER_CREATE("REPORT_USER_CREATE", "用户提交申请"),
+ REPORT_USER_UPDATE("REPORT_USER_UPDATE", "用户更新申请"),
+ REPORT_USER_DELETE("REPORT_USER_DELETE", "用户撤销申请"),
+ REPORT_USER_GET("REPORT_USER_GET", "用户根据ID查询申请"),
+ REPORT_USER_GET_PAGE("REPORT_USER_GET_PAGE", "查询自己的全部申请"),
+ REPORT_ADMIN_UPDATE("REPORT_ADMIN_UPDATE", "管理员处理申请"),
+ REPORT_ADMIN_GET("REPORT_ADMIN_GET", "管理员根据ID查询申请"),
+ REPORT_ADMIN_GET_PAGE("REPORT_ADMIN_GET_PAGE", "管理员分页查询申请"),
+
+ //协拍服务相关操作
+ SHOOT_GET("SHOOT_GET", "根据ID查找协拍"),
+ SHOOT_ADMIN_GET_PAGE("SHOOT_ADMIN_GET_PAGE", "管理员分页查询协拍"),
+ SHOOT_USER_GET_PAGE("SHOOT_USER_GET_PAGE", "用户分页查询协拍"),
+ SHOOT_UPDATE("SHOOT_UPDATE", "更新协拍"),
+ SHOOT_DELETE("SHOOT_DELETE", "删除协拍"),
+ SHOOT_CREATE("SHOOT_CREATE", "新建协拍"),
+
+ //旅游路线相关服务
+ TOUR_GET("TOUR_GET", "根据ID查找旅游路线"),
+ TOUR_ADMIN_GET_PAGE("TOUR_ADMIN_GET_PAGE", "管理员分页查询旅游路线"),
+ TOUR_USER_GET_PAGE("TOUR_USER_GET_PAGE", "用户分页查询旅游路线"),
+ TOUR_UPDATE("TOUR_UPDATE", "更新旅游路线"),
+ TOUR_DELETE("TOUR_DELETE", "删除旅游路线"),
+ TOUR_CREATE("TOUR_CREATE", "新建旅游路线"),
+ ;
+
+ /**
+ * 存入数据库的 code(建议用大写下划线或驼峰,保持一致性)
+ */
+ private final String code;
+
+ /**
+ * 默认描述(当 @Loggable.value() 未指定时使用)
+ */
private final String description;
- LogType(String description) {
+ LogType(String code, String description) {
+ this.code = code;
this.description = description;
}
- public String getDescription() {
- return description;
- }
}
diff --git a/src/main/java/cn/cxdproject/coder/config/WebConfig.java b/src/main/java/cn/cxdproject/coder/config/WebConfig.java
index fcf36cd..811ec94 100644
--- a/src/main/java/cn/cxdproject/coder/config/WebConfig.java
+++ b/src/main/java/cn/cxdproject/coder/config/WebConfig.java
@@ -93,9 +93,10 @@ public void addInterceptors(InterceptorRegistry registry) {
// 政策模块管理员接口
.addPathPatterns("/api/policy/admin/**")
//banner模块接口
- . addPathPatterns("/api/banner/**");
- registry.addInterceptor(registrationInterceptor)
- .addPathPatterns("/api/admin/register/email");
+ .addPathPatterns("/api/banner/**")
+ //操作日志相关接口
+ .addPathPatterns("/api/operationlog/admin/**");
+ registry.addInterceptor(registrationInterceptor);
}
@Bean
diff --git a/src/main/java/cn/cxdproject/coder/controller/ArticleController.java b/src/main/java/cn/cxdproject/coder/controller/ArticleController.java
index 296a489..3301e93 100644
--- a/src/main/java/cn/cxdproject/coder/controller/ArticleController.java
+++ b/src/main/java/cn/cxdproject/coder/controller/ArticleController.java
@@ -95,6 +95,7 @@ public ApiResponse createArticleByAdmin(@Valid @RequestBody CreateArt
return ApiResponse.success(articleVO);
}
+ //管理员文章分页
@GetMapping("/admin/page")
public PageResponse getArticlePageAdmin(
@RequestParam(defaultValue = "1") Integer current,
@@ -102,7 +103,7 @@ public PageResponse getArticlePageAdmin(
@RequestParam(required = false) String keyword) {
Page page = new Page<>(current, size);
- Page articlePage = articleService.getArticlePagAdmine(page, keyword);
+ Page articlePage = articleService.getArticlePagAdmin(page, keyword);
return PageResponse.of(
(int) articlePage.getCurrent(),
diff --git a/src/main/java/cn/cxdproject/coder/controller/DramaController.java b/src/main/java/cn/cxdproject/coder/controller/DramaController.java
index eed0712..682d331 100644
--- a/src/main/java/cn/cxdproject/coder/controller/DramaController.java
+++ b/src/main/java/cn/cxdproject/coder/controller/DramaController.java
@@ -81,9 +81,6 @@ public CursorPageResponseVO page(
return new CursorPageResponseVO<>(list, nextCursor);
}
- // ==================== 普通用户接口 ====================
-
-
// ==================== 管理员接口 ====================
/**
@@ -97,6 +94,7 @@ public ApiResponse updateDramaByAdmin(
return ApiResponse.success(dramaVO);
}
+ //管理员分页
@GetMapping("/admin/page")
public PageResponse getDramaPageAdmin(
@RequestParam(defaultValue = "1") Integer current,
diff --git a/src/main/java/cn/cxdproject/coder/controller/HotelController.java b/src/main/java/cn/cxdproject/coder/controller/HotelController.java
index 0363659..dc6af4a 100644
--- a/src/main/java/cn/cxdproject/coder/controller/HotelController.java
+++ b/src/main/java/cn/cxdproject/coder/controller/HotelController.java
@@ -73,21 +73,7 @@ public CursorPageResponseVO page(
return new CursorPageResponseVO<>(list, nextCursor);
}
- // ==================== 管理员接口 ====================
-
- /**
- * 管理员创建旅店信息
- */
- @PostMapping("/admin/create")
- public ApiResponse createHotelByAdmin(@Valid @RequestBody CreateHotelDTO createDTO) {
- User currentUser = AuthContext.getCurrentUser();
- if (currentUser == null) {
- return ApiResponse.error(401, "未登录");
- }
- HotelVO hotelVO = hotelService.createHotelByAdmin(currentUser.getId(),createDTO);
- return ApiResponse.success(hotelVO);
- }
-
+ //用户分页查询(游标分页)
@GetMapping("/admin/page")
public PageResponse getHotelPageAdmin(
@RequestParam(defaultValue = "1") Integer current,
@@ -105,6 +91,23 @@ public PageResponse getHotelPageAdmin(
);
}
+
+ // ==================== 管理员接口 ====================
+
+ /**
+ * 管理员创建旅店信息
+ */
+ @PostMapping("/admin/create")
+ public ApiResponse createHotelByAdmin(@Valid @RequestBody CreateHotelDTO createDTO) {
+ User currentUser = AuthContext.getCurrentUser();
+ if (currentUser == null) {
+ return ApiResponse.error(401, "未登录");
+ }
+ HotelVO hotelVO = hotelService.createHotelByAdmin(currentUser.getId(),createDTO);
+ return ApiResponse.success(hotelVO);
+ }
+
+
/**
* 管理员更新旅店
*/
diff --git a/src/main/java/cn/cxdproject/coder/controller/LocationController.java b/src/main/java/cn/cxdproject/coder/controller/LocationController.java
index 817f90d..68fdc98 100644
--- a/src/main/java/cn/cxdproject/coder/controller/LocationController.java
+++ b/src/main/java/cn/cxdproject/coder/controller/LocationController.java
@@ -81,7 +81,6 @@ public CursorPageResponseVO page(
return new CursorPageResponseVO<>(list, nextCursor);
}
- // ==================== 普通用户接口 ====================
// ==================== 管理员接口 ====================
@@ -99,6 +98,7 @@ public ApiResponse createLocation(@Valid @RequestBody CreateLocation
return ApiResponse.success(locationVO);
}
+ //管理员分页
@GetMapping("/admin/page")
public PageResponse getLocationPageAdmin(
@RequestParam(defaultValue = "1") Integer current,
diff --git a/src/main/java/cn/cxdproject/coder/controller/OperationLogController.java b/src/main/java/cn/cxdproject/coder/controller/OperationLogController.java
index a09ca9a..d174308 100644
--- a/src/main/java/cn/cxdproject/coder/controller/OperationLogController.java
+++ b/src/main/java/cn/cxdproject/coder/controller/OperationLogController.java
@@ -1,6 +1,12 @@
package cn.cxdproject.coder.controller;
+import cn.cxdproject.coder.common.PageResponse;
+import cn.cxdproject.coder.common.anno.PublicAccess;
+import cn.cxdproject.coder.model.dto.UpdateArticleDTO;
+import cn.cxdproject.coder.model.entity.Article;
import cn.cxdproject.coder.model.entity.OperationLog;
+import cn.cxdproject.coder.model.vo.ArticleVO;
+import cn.cxdproject.coder.model.vo.OperationLogVO;
import cn.cxdproject.coder.service.OperationLogService;
import cn.cxdproject.coder.common.ApiResponse;
import cn.cxdproject.coder.common.PageRequest;
@@ -8,6 +14,8 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
import java.util.List;
/**
@@ -24,74 +32,41 @@ public OperationLogController(OperationLogService operationLogService) {
this.operationLogService = operationLogService;
}
- /**
- * 新增 OperationLog 记录
- * @param entity 实体对象
- * @return 是否新增成功
- */
- @PostMapping
- public ApiResponse add(@RequestBody OperationLog entity) {
- return ApiResponse.success(operationLogService.save(entity));
- }
+ //分页获取操作信息
+ @GetMapping("/admin/page")
+ public PageResponse getOperationPageAdmin(
+ @RequestParam(defaultValue = "1") Integer current,
+ @RequestParam(defaultValue = "10") Integer size,
+ @RequestParam(required = false) String keyword) {
- /**
- * 更新 OperationLog 记录
- * @param entity 实体对象(必须包含主键 ID)
- * @return 是否更新成功
- */
- @PutMapping
- public ApiResponse update(@RequestBody OperationLog entity) {
- return ApiResponse.success(operationLogService.updateById(entity));
- }
+ Page page = new Page<>(current, size);
+ Page operationPage = operationLogService.getOperationPage(page, keyword);
- /**
- * 删除指定 ID 的 OperationLog 记录
- * @param id 主键 ID
- * @return 是否删除成功
- */
- @DeleteMapping("/{id}")
- public ApiResponse delete(@PathVariable("id") Integer id) {
- return ApiResponse.success(operationLogService.removeById(id));
+ return PageResponse.of(
+ (int) operationPage.getCurrent(),
+ (int) operationPage.getSize(),
+ operationPage.getTotal(),
+ operationPage.getRecords()
+ );
}
- /**
- * 根据 ID 获取 OperationLog 详情
- * @param id 主键 ID
- * @return 匹配的实体对象
- */
- @GetMapping("/{id}")
- public ApiResponse getById(@PathVariable("id") Integer id) {
- return ApiResponse.success(operationLogService.getById(id));
+ //根据id获取操作信息
+ @GetMapping("/admin/{id}")
+ @PublicAccess
+ public ApiResponse getOperationById(@PathVariable @NotNull(message = "记录ID不能为空") Long id) {
+ OperationLogVO operationLogVO = operationLogService.getOperationById(id);
+ return ApiResponse.success(operationLogVO);
}
- /**
- * 获取所有 OperationLog 列表(不分页)
- * @return 实体列表
- */
- @GetMapping
- public ApiResponse> list() {
- return ApiResponse.success(operationLogService.list());
- }
/**
- * 分页查询 OperationLog 列表
- * 支持关键字模糊搜索与排序
- * @param pageRequest 分页与筛选请求参数
- * @return 分页结果
+ * 管理员删除文章
*/
- @PostMapping("/page")
- public ApiResponse> getPage(@RequestBody PageRequest pageRequest) {
- Page page = new Page<>(pageRequest.getPage(), pageRequest.getSize());
- QueryWrapper wrapper = new QueryWrapper<>();
-
- if (pageRequest.getKeyword() != null && !pageRequest.getKeyword().isEmpty()) {
- wrapper.like("name", pageRequest.getKeyword()); // 可自定义字段
- }
+ @DeleteMapping("/admin/delete/{id}")
+ public ApiResponse deleteOperation(@PathVariable @NotNull(message = "记录ID不能为空") Long id) {
+ operationLogService.deleteArticle(id);
+ return ApiResponse.success();
+ }
- if (pageRequest.getSortBy() != null && !pageRequest.getSortBy().isEmpty()) {
- wrapper.orderBy(true, "asc".equalsIgnoreCase(pageRequest.getSortOrder()), pageRequest.getSortBy());
- }
- return ApiResponse.success(operationLogService.page(page, wrapper));
- }
}
diff --git a/src/main/java/cn/cxdproject/coder/controller/PolicyController.java b/src/main/java/cn/cxdproject/coder/controller/PolicyController.java
index 0382d4c..4b2c796 100644
--- a/src/main/java/cn/cxdproject/coder/controller/PolicyController.java
+++ b/src/main/java/cn/cxdproject/coder/controller/PolicyController.java
@@ -77,7 +77,6 @@ public CursorPageResponseVO page(
return new CursorPageResponseVO<>(list, nextCursor);
}
- // ==================== 普通用户接口 ====================
// ==================== 管理员接口 ====================
@@ -95,6 +94,7 @@ public ApiResponse createPolicy(@Valid @RequestBody CreatePolicyDTO cr
return ApiResponse.success(policyVO);
}
+ //管理员分页查询
@GetMapping("/admin/page")
public PageResponse getPolicyPageAdmin(
@RequestParam(defaultValue = "1") Integer current,
diff --git a/src/main/java/cn/cxdproject/coder/controller/ReportController.java b/src/main/java/cn/cxdproject/coder/controller/ReportController.java
index 3cf1b49..b0041e4 100644
--- a/src/main/java/cn/cxdproject/coder/controller/ReportController.java
+++ b/src/main/java/cn/cxdproject/coder/controller/ReportController.java
@@ -2,10 +2,10 @@
import cn.cxdproject.coder.common.ApiResponse;
import cn.cxdproject.coder.common.PageResponse;
-import cn.cxdproject.coder.common.anno.PublicAccess;
import cn.cxdproject.coder.common.context.AuthContext;
import cn.cxdproject.coder.model.dto.CreateReportDTO;
-import cn.cxdproject.coder.model.dto.UpdateReportDTO;
+import cn.cxdproject.coder.model.dto.UpdateReportAdminDTO;
+import cn.cxdproject.coder.model.dto.UpdateReportUserDTO;
import cn.cxdproject.coder.model.entity.Report;
import cn.cxdproject.coder.model.entity.User;
import cn.cxdproject.coder.model.vo.ReportVO;
@@ -69,7 +69,7 @@ public ApiResponse createReport(@Valid @RequestBody CreateReportDTO cr
@PutMapping("/update/{id}")
public ApiResponse updateReport(
@PathVariable @NotNull(message = "ID不能为空") Long id,
- @Valid @RequestBody UpdateReportDTO updateDTO) {
+ @Valid @RequestBody UpdateReportUserDTO updateDTO) {
User currentUser = AuthContext.getCurrentUser();
if (currentUser == null) {
return ApiResponse.error(401, "未登录");
@@ -128,7 +128,7 @@ public PageResponse getMyReportPage(
@PutMapping("/admin/update/{id}")
public ApiResponse updateReportByAdmin(
@PathVariable @NotNull(message = "ID不能为空") Long id,
- @Valid @RequestBody UpdateReportDTO updateDTO) {
+ @Valid @RequestBody UpdateReportAdminDTO updateDTO) {
ReportVO reportVO = reportService.updateReportByAdmin(id, updateDTO);
return ApiResponse.success(reportVO);
}
diff --git a/src/main/java/cn/cxdproject/coder/mapper/ArticleMapper.java b/src/main/java/cn/cxdproject/coder/mapper/ArticleMapper.java
index 5ef1323..a01b214 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/ArticleMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/ArticleMapper.java
@@ -1,5 +1,6 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.dto.UpdateArticleDTO;
import cn.cxdproject.coder.model.entity.Banner;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.cxdproject.coder.model.entity.Article;
@@ -30,4 +31,9 @@ List getAdminPage(
List selectLatest10();
+ int updateArticle(
+ @Param("articleId") Long articleId,
+ @Param("dto") UpdateArticleDTO updateArticleDTO
+ );
+
}
\ No newline at end of file
diff --git a/src/main/java/cn/cxdproject/coder/mapper/DramaMapper.java b/src/main/java/cn/cxdproject/coder/mapper/DramaMapper.java
index 4c8aac7..b9b82b5 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/DramaMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/DramaMapper.java
@@ -1,5 +1,6 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.dto.UpdateDramaDTO;
import cn.cxdproject.coder.model.entity.Article;
import cn.cxdproject.coder.model.entity.Banner;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@@ -27,7 +28,11 @@ List getAdminPage(
);
-
List selectLatest10();
+ int updateDrama(
+ @Param("dramaId") Long dramaId,
+ @Param("dto") UpdateDramaDTO dto
+ );
+
}
\ No newline at end of file
diff --git a/src/main/java/cn/cxdproject/coder/mapper/HotelMapper.java b/src/main/java/cn/cxdproject/coder/mapper/HotelMapper.java
index e441d48..cbf8bdc 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/HotelMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/HotelMapper.java
@@ -1,5 +1,6 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.dto.UpdateHotelDTO;
import cn.cxdproject.coder.model.entity.Banner;
import cn.cxdproject.coder.model.entity.Hotel;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@@ -21,4 +22,8 @@ List getAdminPage(
);
List selectLatest10();
+
+ int updateHotel(@Param("id") Long id,
+ @Param("dto") UpdateHotelDTO dto
+ );
}
diff --git a/src/main/java/cn/cxdproject/coder/mapper/LocationMapper.java b/src/main/java/cn/cxdproject/coder/mapper/LocationMapper.java
index e05b5f9..847e248 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/LocationMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/LocationMapper.java
@@ -1,5 +1,6 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.dto.UpdateLocationDTO;
import cn.cxdproject.coder.model.entity.Article;
import cn.cxdproject.coder.model.entity.Banner;
import cn.cxdproject.coder.model.entity.Drama;
@@ -27,8 +28,10 @@ List getAdminPage(
@Param("size") long size
);
-
-
-
List selectLatest10();
+
+ int updateLocation(
+ @Param("id") Long id,
+ @Param("dto") UpdateLocationDTO dto
+ );
}
\ No newline at end of file
diff --git a/src/main/java/cn/cxdproject/coder/mapper/OperationLogMapper.java b/src/main/java/cn/cxdproject/coder/mapper/OperationLogMapper.java
index 9afbe9e..59a9331 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/OperationLogMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/OperationLogMapper.java
@@ -1,8 +1,12 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.entity.Banner;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.cxdproject.coder.model.entity.OperationLog;
import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
/**
* OperationLog Mapper 接口
@@ -11,4 +15,10 @@
@Mapper
public interface OperationLogMapper extends BaseMapper {
+ List getPage(
+ @Param("keyword") String keyword,
+ @Param("offset") long offset,
+ @Param("size") long size
+ );
+
}
\ No newline at end of file
diff --git a/src/main/java/cn/cxdproject/coder/mapper/PolicyMapper.java b/src/main/java/cn/cxdproject/coder/mapper/PolicyMapper.java
index 8c28e6a..274a579 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/PolicyMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/PolicyMapper.java
@@ -1,5 +1,6 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.dto.UpdatePolicyDTO;
import cn.cxdproject.coder.model.entity.Banner;
import cn.cxdproject.coder.model.entity.Location;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@@ -25,7 +26,11 @@ List getAdminPage(
@Param("size") long size
);
-
List selectLatest10();
+ int updatePolicy(
+ @Param("id") Long id,
+ @Param("dto") UpdatePolicyDTO dto
+ );
+
}
\ No newline at end of file
diff --git a/src/main/java/cn/cxdproject/coder/mapper/ReportMapper.java b/src/main/java/cn/cxdproject/coder/mapper/ReportMapper.java
index 5a239d0..7995a2b 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/ReportMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/ReportMapper.java
@@ -1,5 +1,6 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.dto.UpdateReportUserDTO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.cxdproject.coder.model.entity.Report;
import org.apache.ibatis.annotations.Mapper;
@@ -26,5 +27,15 @@ List selectPageByAdmin(
@Param("size") long size
);
+ int updateReportByUser(
+ @Param("id") Long id,
+ @Param("dto") UpdateReportUserDTO dto
+ );
+
+ int updateReportByAdmin(
+ @Param("id") Long id,
+ @Param("status") String status
+ );
+
}
\ No newline at end of file
diff --git a/src/main/java/cn/cxdproject/coder/mapper/ShootMapper.java b/src/main/java/cn/cxdproject/coder/mapper/ShootMapper.java
index c0de075..5e31730 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/ShootMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/ShootMapper.java
@@ -1,5 +1,6 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.dto.UpdateShootDTO;
import cn.cxdproject.coder.model.entity.Banner;
import cn.cxdproject.coder.model.entity.Drama;
import cn.cxdproject.coder.model.entity.Location;
@@ -27,7 +28,11 @@ List getAdminPage(
@Param("size") long size
);
-
List selectLatest10();
+ int updateShoot(
+ @Param("id") Long id,
+ @Param("dto") UpdateShootDTO dto
+ );
+
}
\ No newline at end of file
diff --git a/src/main/java/cn/cxdproject/coder/mapper/TourMapper.java b/src/main/java/cn/cxdproject/coder/mapper/TourMapper.java
index a4fca5c..d0ad1a4 100644
--- a/src/main/java/cn/cxdproject/coder/mapper/TourMapper.java
+++ b/src/main/java/cn/cxdproject/coder/mapper/TourMapper.java
@@ -1,5 +1,6 @@
package cn.cxdproject.coder.mapper;
+import cn.cxdproject.coder.model.dto.UpdateTourDTO;
import cn.cxdproject.coder.model.entity.Banner;
import cn.cxdproject.coder.model.entity.Shoot;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@@ -28,4 +29,9 @@ List getAdminPage(
List selectLatest10();
+ int updateTour(
+ @Param("id") Long id,
+ @Param("dto") UpdateTourDTO dto
+ );
+
}
\ No newline at end of file
diff --git a/src/main/java/cn/cxdproject/coder/model/dto/UpdateReportAdminDTO.java b/src/main/java/cn/cxdproject/coder/model/dto/UpdateReportAdminDTO.java
new file mode 100644
index 0000000..e4ec377
--- /dev/null
+++ b/src/main/java/cn/cxdproject/coder/model/dto/UpdateReportAdminDTO.java
@@ -0,0 +1,8 @@
+package cn.cxdproject.coder.model.dto;
+
+import lombok.Data;
+
+@Data
+public class UpdateReportAdminDTO {
+ private String status;
+}
diff --git a/src/main/java/cn/cxdproject/coder/model/dto/UpdateReportDTO.java b/src/main/java/cn/cxdproject/coder/model/dto/UpdateReportUserDTO.java
similarity index 93%
rename from src/main/java/cn/cxdproject/coder/model/dto/UpdateReportDTO.java
rename to src/main/java/cn/cxdproject/coder/model/dto/UpdateReportUserDTO.java
index 4c5b443..629060e 100644
--- a/src/main/java/cn/cxdproject/coder/model/dto/UpdateReportDTO.java
+++ b/src/main/java/cn/cxdproject/coder/model/dto/UpdateReportUserDTO.java
@@ -11,7 +11,7 @@
* @author heathcetide
*/
@Data
-public class UpdateReportDTO {
+public class UpdateReportUserDTO {
private String name;
private String type;
@@ -27,7 +27,6 @@ public class UpdateReportDTO {
private String contact;
private String phoneNumber;
private String crewPosition;
- private String status;
private String shootPermit;
private String thumbShootPermit;
private String approvalFile;
diff --git a/src/main/java/cn/cxdproject/coder/model/vo/OperationLogVO.java b/src/main/java/cn/cxdproject/coder/model/vo/OperationLogVO.java
new file mode 100644
index 0000000..f145663
--- /dev/null
+++ b/src/main/java/cn/cxdproject/coder/model/vo/OperationLogVO.java
@@ -0,0 +1,60 @@
+package cn.cxdproject.coder.model.vo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class OperationLogVO {
+
+ private Long id;
+
+ /**
+ * 操作类型(如:LOGIN、DOC_EDIT)
+ */
+ private String type;
+
+ /**
+ * 操作描述
+ */
+ private String description;
+
+ /**
+ * 执行用户ID
+ */
+ private Long userId;
+
+ /**
+ * 操作人标识(用户名或IP)
+ */
+ private String operator;
+
+ /**
+ * 是否成功
+ */
+ private Boolean success;
+
+ /**
+ * 输入参数快照
+ */
+ private String params;
+
+ /**
+ * 输出结果快照
+ */
+ private String result;
+
+ /**
+ * 操作时间
+ */
+ private LocalDateTime timestamp;
+
+}
diff --git a/src/main/java/cn/cxdproject/coder/service/ArticleService.java b/src/main/java/cn/cxdproject/coder/service/ArticleService.java
index c7dbc71..9ab0784 100644
--- a/src/main/java/cn/cxdproject/coder/service/ArticleService.java
+++ b/src/main/java/cn/cxdproject/coder/service/ArticleService.java
@@ -70,5 +70,5 @@ public interface ArticleService extends IService {
List getPageFallback(Long lastId, int size, String keyword, Throwable e);
- Page getArticlePagAdmine(Page page, String keyword);
+ Page getArticlePagAdmin(Page page, String keyword);
}
diff --git a/src/main/java/cn/cxdproject/coder/service/OperationLogService.java b/src/main/java/cn/cxdproject/coder/service/OperationLogService.java
index b602817..227a2c7 100644
--- a/src/main/java/cn/cxdproject/coder/service/OperationLogService.java
+++ b/src/main/java/cn/cxdproject/coder/service/OperationLogService.java
@@ -1,5 +1,9 @@
package cn.cxdproject.coder.service;
+import cn.cxdproject.coder.model.entity.Location;
+import cn.cxdproject.coder.model.vo.LocationVO;
+import cn.cxdproject.coder.model.vo.OperationLogVO;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import cn.cxdproject.coder.model.entity.OperationLog;
@@ -9,4 +13,11 @@
*/
public interface OperationLogService extends IService {
+ OperationLogVO getOperationById(Long id);
+
+ void deleteArticle(Long id);
+
+ Page getOperationPage(Page page, String keyword);
+
+ OperationLogVO toOperationVO(OperationLog operationLog);
}
diff --git a/src/main/java/cn/cxdproject/coder/service/ReportService.java b/src/main/java/cn/cxdproject/coder/service/ReportService.java
index 8b93098..ac81cc6 100644
--- a/src/main/java/cn/cxdproject/coder/service/ReportService.java
+++ b/src/main/java/cn/cxdproject/coder/service/ReportService.java
@@ -1,7 +1,8 @@
package cn.cxdproject.coder.service;
import cn.cxdproject.coder.model.dto.CreateReportDTO;
-import cn.cxdproject.coder.model.dto.UpdateReportDTO;
+import cn.cxdproject.coder.model.dto.UpdateReportAdminDTO;
+import cn.cxdproject.coder.model.dto.UpdateReportUserDTO;
import cn.cxdproject.coder.model.entity.Report;
import cn.cxdproject.coder.model.vo.ReportVO;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -14,11 +15,11 @@
public interface ReportService extends IService {
ReportVO createReport(Long userId, CreateReportDTO createDTO);
- ReportVO updateReport(Long userId, Long reportId, UpdateReportDTO updateDTO);
+ ReportVO updateReport(Long userId, Long reportId, UpdateReportUserDTO updateDTO);
void deleteReport(Long userId, Long reportId);
ReportVO getReportById(Long reportId,Long userId);
Page getMyReportPage(Long userId, Page page);
- ReportVO updateReportByAdmin(Long reportId, UpdateReportDTO updateDTO);
+ ReportVO updateReportByAdmin(Long reportId, UpdateReportAdminDTO updateDTO);
Page getReportPageByAdmin(Page page, String keyword);
ReportVO getReportByIdByAdmin(Long reportId);
ReportVO toReportVO(Report report);
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/ArticleServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/ArticleServiceImpl.java
index 46a35bf..89ed4a1 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/ArticleServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/ArticleServiceImpl.java
@@ -1,7 +1,9 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
import cn.cxdproject.coder.common.constants.*;
import cn.cxdproject.coder.common.context.AuthContext;
+import cn.cxdproject.coder.common.enums.LogType;
import cn.cxdproject.coder.exception.BusinessException;
import cn.cxdproject.coder.exception.NotFoundException;
import cn.cxdproject.coder.model.dto.CreateArticleDTO;
@@ -59,6 +61,10 @@ public ArticleServiceImpl(
@Override
@CircuitBreaker(name = "articleGetById", fallbackMethod = "getByIdFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.ARTICLE_GET,
+ value = "Get article by ID: #{#articleId}"
+ )
public ArticleVO getArticleById(Long articleId) {
Object store = cache.getIfPresent(CaffeineConstants.ARTICLE + articleId);
if (store != null) {
@@ -68,7 +74,7 @@ public ArticleVO getArticleById(Long articleId) {
if (article == null || Boolean.TRUE.equals(article.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- cache.asMap().put(CaffeineConstants.ARTICLE + articleId, article);
+ cache.put(CaffeineConstants.ARTICLE + articleId, article);
return toArticleVO(article);
}
}
@@ -77,6 +83,10 @@ public ArticleVO getArticleById(Long articleId) {
@Override
@CircuitBreaker(name = "articleGetPage", fallbackMethod = "getPageFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.ARTICLE_USER_GET_PAGE,
+ value = "User get page "
+ )
public List getArticlePage(Long lastId, int size, String keyword) {
List ids = articleMapper.selectIds(lastId, size, keyword);
if (ids.isEmpty()) {
@@ -96,7 +106,10 @@ public List getArticlePage(Long lastId, int size, String keyword) {
}
@Override
- @Bulkhead(name = "add", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.ARTICLE_CREATE,
+ value = "Create article "
+ )
public ArticleVO createArticleByAdmin(CreateArticleDTO createDTO) {
// 获取当前管理员用户
User currentUser = AuthContext.getCurrentUser();
@@ -124,26 +137,40 @@ public ArticleVO createArticleByAdmin(CreateArticleDTO createDTO) {
}
@Override
- @Bulkhead(name = "update", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.ARTICLE_UPDATE,
+ value = "update article ID: #{#articleId}"
+ )
public ArticleVO updateArticleByAdmin(Long articleId, UpdateArticleDTO updateDTO) {
- Article article = this.getById(articleId);
- if (article == null || Boolean.TRUE.equals(article.getDeleted())) {
+ // 1. 先校验文章是否存在且未删除(必须做,用于抛出 NotFoundException)
+ Article existing = this.getById(articleId);
+ if (existing == null || Boolean.TRUE.equals(existing.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- if (updateDTO.getTitle() != null) article.setTitle(updateDTO.getTitle());
- if (updateDTO.getIssueUnit() != null) article.setIssueUnit(updateDTO.getIssueUnit());
- if (updateDTO.getContent() != null) article.setContent(updateDTO.getContent());
- if (updateDTO.getThumbImage() != null) article.setThumbImage(updateDTO.getThumbImage());
+ // 2. 执行动态更新(只更新非空字段)
+ int updated = articleMapper.updateArticle(articleId,updateDTO);
+ // 3. 如果更新影响行数为 0,说明条件不满足(比如已被删除),也可视为未找到
+ if (updated == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
- cache.asMap().put(CaffeineConstants.ARTICLE + articleId, article);
- this.updateById(article);
- return toArticleVO(article);
+ // 4. 重新加载最新数据
+ Article updatedArticle = this.getById(articleId);
+
+ // 5. 更新缓存
+ cache.put(CaffeineConstants.ARTICLE + articleId, updatedArticle);
+
+ // 6. 返回 VO
+ return toArticleVO(updatedArticle);
}
@Override
- @Bulkhead(name = "delete", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.ARTICLE_DELETE,
+ value = "Delete article ID: #{#articleId}"
+ )
public void deleteArticleByAdmin(Long articleId) {
boolean updated = articleMapper.update(null,
Wrappers.lambdaUpdate()
@@ -183,22 +210,26 @@ public ArticleVO toArticleVO(Article article) {
@Override
public ArticleVO getByIdFallback(Long articleId,Throwable e) {
- Object store;
- store = cache.getIfPresent(CaffeineConstants.ARTICLE + articleId);
- if (store != null) {
- return toArticleVO((Article) store);
+
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
}
- store = redisUtils.get(RedisKeyConstants.ARTICLE+articleId);
+
+ Object store = cache.getIfPresent(CaffeineConstants.ARTICLE + articleId);
if (store != null) {
- Article article = JsonUtils.fromJson((String) store, Article.class);
- return toArticleVO(article);
+ return toArticleVO((Article) store);
}
+
return null;
}
@Override
public List getPageFallback(Long lastId, int size, String keyword, Throwable e) {
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
+ }
+
try {
// 从 Redis 获取缓存的全量文章(假设是 ArticleVO[] 的 JSON)
String json = (String) redisUtils.get(TaskConstants.ARTICLE);
@@ -221,7 +252,12 @@ public List getPageFallback(Long lastId, int size, String keyword, Th
}
@Override
- public Page getArticlePagAdmine(Page page, String keyword) {
+ @Loggable(
+ type = LogType.ARTICLE_ADMIN_GET_PAGE,
+ value = "Admin get article page"
+ )
+ public Page getArticlePagAdmin(Page page, String keyword) {
+
long current = page.getCurrent();
long size = page.getSize();
long offset = (current - 1) * size;
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/BannerServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/BannerServiceImpl.java
index 2686934..6e84b3d 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/BannerServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/BannerServiceImpl.java
@@ -85,7 +85,6 @@ public BannerVO createImage(CreateBannerDTO createDTO) {
.status(true)
.build();
- // 保存文章
this.save(banner);
return toBannerVO(banner);
}
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/DramaServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/DramaServiceImpl.java
index 8abbcb0..831a701 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/DramaServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/DramaServiceImpl.java
@@ -1,6 +1,8 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
import cn.cxdproject.coder.common.constants.*;
+import cn.cxdproject.coder.common.enums.LogType;
import cn.cxdproject.coder.exception.BusinessException;
import cn.cxdproject.coder.exception.NotFoundException;
import cn.cxdproject.coder.model.dto.CreateDramaDTO;
@@ -51,7 +53,10 @@ public DramaServiceImpl(DramaMapper dramaMapper,
@Override
- @Bulkhead(name = "add", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.DRAMA_CREATE,
+ value = "create drama"
+ )
public DramaVO createDramaByAdmin(Long userId, CreateDramaDTO createDTO) {
if (createDTO.getImage() == null) {
createDTO.setImage(Constants.DEFAULT_COVER);
@@ -81,6 +86,10 @@ public DramaVO createDramaByAdmin(Long userId, CreateDramaDTO createDTO) {
@Override
@CircuitBreaker(name = "dramaGetById", fallbackMethod = "getByIdFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.DRAMA_GET,
+ value = "Get drama by ID: #{#dramaId}"
+ )
public DramaVO getDramaById(Long dramaId) {
Object store = cache.asMap().get(CaffeineConstants.DRAMA + dramaId);
if (store != null) {
@@ -90,7 +99,7 @@ public DramaVO getDramaById(Long dramaId) {
if (drama == null || Boolean.TRUE.equals(drama.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- cache.asMap().put(CaffeineConstants.DRAMA + dramaId, drama);
+ cache.put(CaffeineConstants.DRAMA + dramaId, drama);
return toDramaVO(drama);
}
}
@@ -98,6 +107,10 @@ public DramaVO getDramaById(Long dramaId) {
@Override
@CircuitBreaker(name = "dramaGetPage", fallbackMethod = "getPageFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.DRAMA_USER_GET_PAGE,
+ value = "User get drama page"
+ )
public List getDramaPage(Long lastId, int size, String keyword) {
List ids = dramaMapper.selectIds(lastId, size, keyword);
if (ids.isEmpty()) {
@@ -118,32 +131,40 @@ public List getDramaPage(Long lastId, int size, String keyword) {
}
@Override
+ @Loggable(
+ type = LogType.DRAMA_UPDATE,
+ value = "Update drama ID: #{#dramaId}"
+ )
public DramaVO updateDramaByAdmin(Long dramaId, UpdateDramaDTO updateDTO) {
- Drama drama = this.getById(dramaId);
- if (drama == null || Boolean.TRUE.equals(drama.getDeleted())) {
+ // 先校验文章是否存在且未被删除
+ Drama existing = this.getById(dramaId);
+ if (existing == null || Boolean.TRUE.equals(existing.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- if (updateDTO.getName() != null) drama.setName(updateDTO.getName());
- if (updateDTO.getFilingNum() != null) drama.setFilingNum(updateDTO.getFilingNum());
- if (updateDTO.getProdCompany() != null) drama.setProdCompany(updateDTO.getProdCompany());
- if (updateDTO.getCrewDescription() != null) drama.setCrewDescription(updateDTO.getCrewDescription());
- if (updateDTO.getDramaDescription() != null) drama.setDramaDescription(updateDTO.getDramaDescription());
- if (updateDTO.getCast() != null) drama.setCast(updateDTO.getCast());
- if (updateDTO.getShootLocation() != null) drama.setShootLocation(updateDTO.getShootLocation());
- if (updateDTO.getLocationId() != null) drama.setLocationId(updateDTO.getLocationId());
- if (updateDTO.getService() != null) drama.setService(updateDTO.getService());
- if (updateDTO.getServiceId() != null) drama.setServiceId(updateDTO.getServiceId());
- if(updateDTO.getImage() != null) drama.setImage(updateDTO.getImage());
- if(updateDTO.getThumbImage() != null) drama.setThumbImage(updateDTO.getThumbImage());
-
- drama.setUpdatedAt(LocalDateTime.now());
- cache.asMap().put(CaffeineConstants.DRAMA + dramaId, dramaId);
- this.updateById(drama);
- return toDramaVO(drama);
+ // 执行动态更新(只更新非空字段)
+ int updated = dramaMapper.updateDrama(dramaId, updateDTO);
+
+ // 如果更新影响行数为 0,说明条件不满足(比如已被删除),也可视为未找到
+ if (updated == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+
+ // 重新加载最新数据(保证数据一致性)
+ Drama updatedDrama = this.getById(dramaId);
+
+ // 更新缓存
+ cache.put(CaffeineConstants.DRAMA + dramaId, updatedDrama);
+
+ // 返回 VO
+ return toDramaVO(updatedDrama);
}
@Override
+ @Loggable(
+ type = LogType.DRAMA_DELETE,
+ value = "delete drama ID: #{#dramaId}"
+ )
public void deleteDramaByAdmin(Long dramaId) {
boolean updated = dramaMapper.update(null,
Wrappers.lambdaUpdate()
@@ -188,22 +209,26 @@ public DramaVO toDramaVO(Drama drama) {
@Override
public DramaVO getByIdFallback(Long id,Throwable e) {
- Object store;
- store = cache.getIfPresent(CaffeineConstants.DRAMA + id);
- if (store != null) {
- return toDramaVO((Drama) store);
+
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
}
- store = redisUtils.get(RedisKeyConstants.DRAMA+id);
+
+ Object store = cache.getIfPresent(CaffeineConstants.DRAMA + id);
if (store != null) {
- Drama drama = JsonUtils.fromJson((String) store, Drama.class);
- return toDramaVO(drama);
+ return toDramaVO((Drama) store);
}
+
return null;
}
@Override
public List getPageFallback(Long lastId, int size, String keyword, Throwable e) {
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
+ }
+
try {
// 从 Redis 获取缓存的全量文章(假设是 ArticleVO[] 的 JSON)
String json = (String) redisUtils.get(TaskConstants.DRAMA);
@@ -226,6 +251,10 @@ public List getPageFallback(Long lastId, int size, String keyword, Thro
}
@Override
+ @Loggable(
+ type = LogType.DRAMA_ADMIN_GET_PAGE,
+ value = "Admin get drama page"
+ )
public Page getDramaPageAdmin(Page page, String keyword) {
long current = page.getCurrent();
long size = page.getSize();
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/FileServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/FileServiceImpl.java
index 401cf5d..b7d8959 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/FileServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/FileServiceImpl.java
@@ -1,5 +1,7 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
+import cn.cxdproject.coder.common.enums.LogType;
import cn.cxdproject.coder.common.storage.LocalStorageService;
import cn.cxdproject.coder.common.storage.MinioStorageService;
import cn.cxdproject.coder.model.vo.FileVO;
@@ -20,6 +22,10 @@ public FileServiceImpl(MinioStorageService minioStorageService, LocalStorageServ
@Override
+ @Loggable(
+ type = LogType.FILE_UPLOAD,
+ value = "upload file"
+ )
public FileVO imageUpload(MultipartFile file) {
FileVO fileVO = minioStorageService.upload(file);
return fileVO;
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/HotelServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/HotelServiceImpl.java
index 6139e15..fa57b37 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/HotelServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/HotelServiceImpl.java
@@ -1,6 +1,9 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
import cn.cxdproject.coder.common.constants.*;
+import cn.cxdproject.coder.common.enums.LogType;
+import cn.cxdproject.coder.exception.BusinessException;
import cn.cxdproject.coder.exception.NotFoundException;
import cn.cxdproject.coder.mapper.HotelMapper;
import cn.cxdproject.coder.mapper.LocationMapper;
@@ -54,7 +57,10 @@ public HotelServiceImpl(
@Override
- @Bulkhead(name = "add", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.HOTEL_CREATE,
+ value = "Create hotel"
+ )
public HotelVO createHotelByAdmin(Long userId, CreateHotelDTO createDTO) {
if (createDTO.getImage() == null) {
createDTO.setImage(Constants.DEFAULT_COVER);
@@ -80,6 +86,10 @@ public HotelVO createHotelByAdmin(Long userId, CreateHotelDTO createDTO) {
@Override
@CircuitBreaker(name = "hotelGetById", fallbackMethod = "getByIdFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.HOTEL_GET,
+ value = "Get hotel by ID: #{#hotelId}"
+ )
public HotelVO getHotelById(Long hotelId) {
Object store = cache.asMap().get(CaffeineConstants.HOTEL + hotelId);
if (store != null) {
@@ -89,7 +99,7 @@ public HotelVO getHotelById(Long hotelId) {
if (hotel == null || Boolean.TRUE.equals(hotel.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- cache.asMap().put(CaffeineConstants.HOTEL + hotelId, hotel);
+ cache.put(CaffeineConstants.HOTEL + hotelId, hotel);
return toHotelVO(hotel);
}
}
@@ -97,6 +107,10 @@ public HotelVO getHotelById(Long hotelId) {
@Override
@CircuitBreaker(name = "hotelGetPage", fallbackMethod = "getPageFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.ARTICLE_USER_GET_PAGE,
+ value = "User get hotel"
+ )
public List getHotelPage(Long lastId, int size, String keyword) {
List ids = hotelMapper.selectIds(lastId, size, keyword);
if (ids.isEmpty()) {
@@ -117,31 +131,41 @@ public List getHotelPage(Long lastId, int size, String keyword) {
@Override
- @Bulkhead(name = "update", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.DRAMA_UPDATE,
+ value = "Update hotel ID: #{#dramaId}"
+ )
public HotelVO updateHotelByAdmin(Long hotelId, UpdateHotelDTO updateDTO) {
- Hotel hotel = this.getById(hotelId);
- if (hotel == null || Boolean.TRUE.equals(hotel.getDeleted())) {
+ // 1. 校验是否存在且未删除
+ Hotel existing = this.getById(hotelId);
+ if (existing == null || Boolean.TRUE.equals(existing.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- if (updateDTO.getName() != null) hotel.setName(updateDTO.getName());
- if (updateDTO.getDescription() != null) hotel.setDescription(updateDTO.getDescription());
- if (updateDTO.getAddress() != null) hotel.setAddress(updateDTO.getAddress());
- if (updateDTO.getManagerName() != null) hotel.setManagerName(updateDTO.getManagerName());
- if (updateDTO.getManagerPhone() != null) hotel.setManagerPhone(updateDTO.getManagerPhone());
- if (updateDTO.getImage() != null) hotel.setImage(updateDTO.getImage());
- if (updateDTO.getThumbImage() != null) hotel.setThumbImage(updateDTO.getThumbImage());
- if (updateDTO.getLongitude() != null) hotel.setLongitude(updateDTO.getLongitude());
- if (updateDTO.getLatitude() != null) hotel.setLatitude(updateDTO.getLatitude());
-
- cache.asMap().put(CaffeineConstants.HOTEL + hotelId, hotel);
- this.updateById(hotel);
- return toHotelVO(hotel);
+ // 2. 执行动态更新
+ int updatedRows = hotelMapper.updateHotel(hotelId, updateDTO);
+
+ // 3. 若无记录被更新
+ if (updatedRows == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+
+ // 4. 重新加载最新数据(保证返回的是 DB 最新状态)
+ Hotel updatedHotel = this.getById(hotelId);
+
+ // 5. 更新缓存(注意:存的是对象,不是 ID!)
+ cache.put(CaffeineConstants.HOTEL + hotelId, updatedHotel);
+
+ // 6. 返回 VO
+ return toHotelVO(updatedHotel);
}
@Override
- @Bulkhead(name = "delete", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.HOTEL_DELETE,
+ value = "Delete hotel by ID: #{#hotelId}"
+ )
public void deleteHotelByAdmin(Long hotelId) {
boolean updated = hotelMapper.update(null,
Wrappers.lambdaUpdate()
@@ -183,15 +207,14 @@ public HotelVO toHotelVO(Hotel hotel) {
@Override
public HotelVO getByIdFallback(Long id,Throwable e) {
- Object store;
- store = cache.getIfPresent(CaffeineConstants.HOTEL + id);
- if (store != null) {
- return toHotelVO((Hotel) store);
+
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
}
- store = redisUtils.get(RedisKeyConstants.HOTEL+id);
+
+ Object store = cache.getIfPresent(CaffeineConstants.HOTEL + id);
if (store != null) {
- Hotel hotel = JsonUtils.fromJson((String) store, Hotel.class);
- return toHotelVO(hotel);
+ return toHotelVO((Hotel) store);
}
return null;
}
@@ -199,6 +222,10 @@ public HotelVO getByIdFallback(Long id,Throwable e) {
@Override
public List getPageFallback(Long lastId, int size, String keyword, Throwable e) {
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
+ }
+
try {
// 从 Redis 获取缓存的全量文章(假设是 ArticleVO[] 的 JSON)
String json = (String) redisUtils.get(TaskConstants.HOTEL);
@@ -222,6 +249,10 @@ public List getPageFallback(Long lastId, int size, String keyword, Thro
}
@Override
+ @Loggable(
+ type = LogType.DRAMA_ADMIN_GET_PAGE,
+ value = "Admin get hotel Page"
+ )
public Page getHotelPageAdmin(Page page, String keyword) {
long current = page.getCurrent();
long size = page.getSize();
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/LocationServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/LocationServiceImpl.java
index 804d6be..1639be2 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/LocationServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/LocationServiceImpl.java
@@ -1,6 +1,8 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
import cn.cxdproject.coder.common.constants.*;
+import cn.cxdproject.coder.common.enums.LogType;
import cn.cxdproject.coder.exception.BusinessException;
import cn.cxdproject.coder.exception.NotFoundException;
import cn.cxdproject.coder.model.dto.CreateLocationDTO;
@@ -53,7 +55,10 @@ public LocationServiceImpl(
}
@Override
- @Bulkhead(name = "add", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.LOCATION_CREATE,
+ value = "Create location"
+ )
public LocationVO createLocationByAdmin(Long userId, CreateLocationDTO createDTO) {
if (createDTO.getImage() == null) {
createDTO.setImage(Constants.DEFAULT_COVER);
@@ -85,6 +90,10 @@ public LocationVO createLocationByAdmin(Long userId, CreateLocationDTO createDTO
@Override
@CircuitBreaker(name = "locationGetById", fallbackMethod = "getByIdFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.LOCATION_GET,
+ value = "Get location by ID: #{#locationId}"
+ )
public LocationVO getLocationById(Long locationId) {
Object store = cache.asMap().get(CaffeineConstants.LOCATION + locationId);
if (store != null) {
@@ -94,7 +103,7 @@ public LocationVO getLocationById(Long locationId) {
if (location == null || Boolean.TRUE.equals(location.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- cache.asMap().put(CaffeineConstants.LOCATION + locationId, location);
+ cache.put(CaffeineConstants.LOCATION + locationId, location);
return toLocationVO(location);
}
}
@@ -102,6 +111,10 @@ public LocationVO getLocationById(Long locationId) {
@Override
@CircuitBreaker(name = "locationGetPage", fallbackMethod = "getPageFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.LOCATION_USER_GET_PAGE,
+ value = "User get location"
+ )
public List getLocationPage(Long lastId, int size, String keyword) {
List ids = locationMapper.selectIds(lastId, size, keyword);
if (ids.isEmpty()) {
@@ -122,35 +135,40 @@ public List getLocationPage(Long lastId, int size, String keyword) {
@Override
- @Bulkhead(name = "update", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.LOCATION_UPDATE,
+ value = "Update location ID: #{#locationId}"
+ )
public LocationVO updateLocationByAdmin(Long locationId, UpdateLocationDTO updateDTO) {
- Location location = this.getById(locationId);
- if (location == null || Boolean.TRUE.equals(location.getDeleted())) {
+ // 1. 校验是否存在且未删除
+ Location existing = this.getById(locationId);
+ if (existing == null || Boolean.TRUE.equals(existing.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- if (updateDTO.getName() != null) location.setName(updateDTO.getName());
- if (updateDTO.getType() != null) location.setType(updateDTO.getType());
- if (updateDTO.getStatus() != null) location.setStatus(updateDTO.getStatus());
- if (updateDTO.getLocationDescription() != null) location.setLocationDescription(updateDTO.getLocationDescription());
- if (updateDTO.getLocationPrincipalName() != null) location.setLocationPrincipalName(updateDTO.getLocationPrincipalName());
- if (updateDTO.getLocationPrincipalPhone() != null) location.setLocationPrincipalPhone(updateDTO.getLocationPrincipalPhone());
- if (updateDTO.getGovPrincipalName() != null) location.setGovPrincipalName(updateDTO.getGovPrincipalName());
- if (updateDTO.getGovPrincipalPhone() != null) location.setGovPrincipalPhone(updateDTO.getGovPrincipalPhone());
- if (updateDTO.getAddress() != null) location.setAddress(updateDTO.getAddress());
- if (updateDTO.getPrice() != null) location.setPrice(updateDTO.getPrice());
- if(updateDTO.getImage() != null) location.setImage(updateDTO.getImage());
- if(updateDTO.getThumbImage() != null) location.setThumbImage(updateDTO.getThumbImage());
- if(updateDTO.getLongitude() != null) location.setLongitude(updateDTO.getLongitude());
- if(updateDTO.getLatitude() != null) location.setLatitude(updateDTO.getLatitude());
-
- cache.asMap().put(CaffeineConstants.LOCATION + locationId, location);
- this.updateById(location);
- return toLocationVO(location);
+ // 2. 执行动态更新
+ int updatedRows = locationMapper.updateLocation(locationId, updateDTO);
+
+ // 3. 若无记录被更新(可能已被删除)
+ if (updatedRows == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+
+ // 4. 重新加载最新数据(保证缓存和返回的是最新状态)
+ Location updatedLocation = this.getById(locationId);
+
+ // 5. 更新缓存
+ cache.put(CaffeineConstants.LOCATION + locationId, updatedLocation);
+
+ // 6. 返回 VO
+ return toLocationVO(updatedLocation);
}
@Override
- @Bulkhead(name = "delete", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.LOCATION_DELETE,
+ value = "Delete location by ID: #{#locationId}"
+ )
public void deleteLocationByAdmin(Long locationId) {
boolean updated = locationMapper.update(null,
Wrappers.lambdaUpdate()
@@ -198,15 +216,14 @@ public LocationVO toLocationVO(Location location) {
@Override
public LocationVO getByIdFallback(Long id,Throwable e) {
- Object store;
- store = cache.getIfPresent(CaffeineConstants.LOCATION + id);
- if (store != null) {
- return toLocationVO((Location) store);
+
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
}
- store = redisUtils.get(RedisKeyConstants.LOCATION+id);
+
+ Object store = cache.getIfPresent(CaffeineConstants.LOCATION + id);
if (store != null) {
- Location location = JsonUtils.fromJson((String) store, Location.class);
- return toLocationVO(location);
+ return toLocationVO((Location) store);
}
return null;
}
@@ -214,6 +231,10 @@ public LocationVO getByIdFallback(Long id,Throwable e) {
@Override
public List getPageFallback(Long lastId, int size, String keyword, Throwable e) {
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
+ }
+
try {
// 从 Redis 获取缓存的全量文章(假设是 ArticleVO[] 的 JSON)
String json = (String) redisUtils.get(TaskConstants.LOCATION);
@@ -236,6 +257,10 @@ public List getPageFallback(Long lastId, int size, String keyword, T
}
@Override
+ @Loggable(
+ type = LogType.LOCATION_ADMIN_GET_PAGE,
+ value = "Admin get location Page"
+ )
public Page getLocationPageAdmin(Page page, String keyword) {
long current = page.getCurrent();
long size = page.getSize();
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/OperationLogServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/OperationLogServiceImpl.java
index e43c95b..f254220 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/OperationLogServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/OperationLogServiceImpl.java
@@ -1,11 +1,23 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.constants.ResponseConstants;
+import cn.cxdproject.coder.exception.NotFoundException;
+import cn.cxdproject.coder.model.entity.Banner;
+import cn.cxdproject.coder.model.vo.BannerVO;
+import cn.cxdproject.coder.model.vo.OperationLogVO;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.cxdproject.coder.model.entity.OperationLog;
import cn.cxdproject.coder.mapper.OperationLogMapper;
import cn.cxdproject.coder.service.OperationLogService;
import org.springframework.stereotype.Service;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static cn.cxdproject.coder.common.enums.ResponseCodeEnum.NOT_FOUND;
+
/**
* OperationLog 服务实现类
* @author Hibiscus-code-generate
@@ -13,4 +25,73 @@
@Service
public class OperationLogServiceImpl extends ServiceImpl implements OperationLogService {
+ private final OperationLogMapper operationLogMapper;
+
+ public OperationLogServiceImpl(OperationLogMapper operationLogMapper) {
+ this.operationLogMapper = operationLogMapper;
+ }
+
+ @Override
+ public OperationLogVO getOperationById(Long id) {
+ OperationLog operationLog = this.getById(id);
+ if (operationLog == null || Boolean.TRUE.equals(operationLog.getDeleted())) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+ return toOperationVO(operationLog);
+ }
+
+ @Override
+ public void deleteArticle(Long id) {
+ boolean updated = operationLogMapper.update(null,
+ Wrappers.lambdaUpdate()
+ .set(OperationLog::getDeleted, true)
+ .eq(OperationLog::getId, id)
+ .eq(OperationLog::getDeleted, false)
+ ) > 0;
+
+ if (!updated) {
+ OperationLog operationLog = this.getById(id);
+ if (operationLog == null || Boolean.TRUE.equals(operationLog.getDeleted())) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+ }
+
+ }
+
+ @Override
+ public Page getOperationPage(Page page, String keyword) {
+ long current = page.getCurrent();
+ long size = page.getSize();
+ long offset = (current - 1) * size;
+
+
+ List operationLogs = operationLogMapper.getPage(keyword, offset, size);
+
+ List voList = operationLogs.stream()
+ .map(this::toOperationVO)
+ .collect(Collectors.toList());
+
+ return new Page()
+ .setCurrent(current)
+ .setSize(size)
+ .setRecords(voList);
+ }
+
+ @Override
+ public OperationLogVO toOperationVO(OperationLog operationLog) {
+ if (log == null) {
+ return null;
+ }
+ return OperationLogVO.builder()
+ .id(operationLog.getId())
+ .type(operationLog.getType())
+ .description(operationLog.getDescription())
+ .userId(operationLog.getUserId())
+ .operator(operationLog.getOperator())
+ .success(operationLog.getSuccess())
+ .params(operationLog.getParams())
+ .result(operationLog.getResult())
+ .timestamp(operationLog.getTimestamp())
+ .build();
+ }
}
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/PolicyServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/PolicyServiceImpl.java
index 7ebca52..a626ebd 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/PolicyServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/PolicyServiceImpl.java
@@ -1,6 +1,9 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
import cn.cxdproject.coder.common.constants.*;
+import cn.cxdproject.coder.common.enums.LogType;
+import cn.cxdproject.coder.exception.BusinessException;
import cn.cxdproject.coder.exception.NotFoundException;
import cn.cxdproject.coder.mapper.LocationMapper;
import cn.cxdproject.coder.model.dto.CreatePolicyDTO;
@@ -48,6 +51,10 @@ public PolicyServiceImpl(PolicyMapper policyMapper, Cache cache,
}
@Override
+ @Loggable(
+ type = LogType.POLICY_CREATE,
+ value = "Create policy"
+ )
public PolicyVO createPolicyByAdmin(CreatePolicyDTO createDTO) {
if (createDTO.getImage() == null) {
createDTO.setImage(Constants.DEFAULT_COVER);
@@ -71,6 +78,10 @@ public PolicyVO createPolicyByAdmin(CreatePolicyDTO createDTO) {
@Override
@CircuitBreaker(name = "policyGetById", fallbackMethod = "getByIdFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.POLICY_GET,
+ value = "Get policy by ID: #{#policyId}"
+ )
public PolicyVO getPolicyById(Long policyId) {
Object store = cache.asMap().get(CaffeineConstants.POLICY + policyId);
if (store != null) {
@@ -88,6 +99,10 @@ public PolicyVO getPolicyById(Long policyId) {
@Override
@CircuitBreaker(name = "policyGetPage", fallbackMethod = "getPageFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.POLICY_USER_GET_PAGE,
+ value = "User get policy"
+ )
public List getPolicyPage(Long lastId, int size, String keyword) {
List ids = policyMapper.selectIds(lastId, size, keyword);
if (ids.isEmpty()) {
@@ -107,28 +122,40 @@ public List getPolicyPage(Long lastId, int size, String keyword) {
}
@Override
- @Bulkhead(name = "update", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.POLICY_UPDATE,
+ value = "Update policy ID: #{#policyId}"
+ )
public PolicyVO updatePolicyByAdmin(Long policyId, UpdatePolicyDTO updateDTO) {
- Policy policy = this.getById(policyId);
- if (policy == null || Boolean.TRUE.equals(policy.getDeleted())) {
+ // 1. 校验是否存在且未被删除
+ Policy existing = this.getById(policyId);
+ if (existing == null || Boolean.TRUE.equals(existing.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- if (updateDTO.getTitle() != null) policy.setTitle(updateDTO.getTitle());
- if (updateDTO.getType() != null) policy.setType(updateDTO.getType());
- if (updateDTO.getIssueUnit() != null) policy.setIssueUnit(updateDTO.getIssueUnit());
- if (updateDTO.getIssueTime() != null) policy.setIssueTime(updateDTO.getIssueTime());
- if (updateDTO.getContent() != null) policy.setContent(updateDTO.getContent());
- if (updateDTO.getImage() != null) policy.setImage(updateDTO.getImage());
- if (updateDTO.getThumbImage() != null) policy.setThumbImage(updateDTO.getThumbImage());
+ // 2. 执行动态更新
+ int updatedRows = policyMapper.updatePolicy(policyId, updateDTO);
- cache.asMap().put(CaffeineConstants.POLICY + policyId, policy);
- this.updateById(policy);
- return toPolicyVO(policy);
+ // 3. 若无记录被更新(可能已被删除或并发修改)
+ if (updatedRows == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+
+ // 4. 重新加载最新数据(保证一致性)
+ Policy updatedPolicy = this.getById(policyId);
+
+ // 5. 更新缓存(注意:缓存的是对象,不是 ID)
+ cache.put(CaffeineConstants.POLICY + policyId, updatedPolicy);
+
+ // 6. 返回 VO
+ return toPolicyVO(updatedPolicy);
}
@Override
- @Bulkhead(name = "delete", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.POLICY_DELETE,
+ value = "Delete policy by ID: #{#policyId}"
+ )
public void deletePolicyByAdmin(Long policyId) {
boolean updated = policyMapper.update(null,
Wrappers.lambdaUpdate()
@@ -167,15 +194,14 @@ public PolicyVO toPolicyVO(Policy policy) {
@Override
public PolicyVO getByIdFallback(Long id, Throwable e) {
- Object store;
- store = cache.getIfPresent(CaffeineConstants.POLICY + id);
- if (store != null) {
- return toPolicyVO((Policy) store);
+
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
}
- store = redisUtils.get(RedisKeyConstants.POLICY + id);
+
+ Object store = cache.getIfPresent(CaffeineConstants.POLICY + id);
if (store != null) {
- Policy policy = JsonUtils.fromJson((String) store, Policy.class);
- return toPolicyVO(policy);
+ return toPolicyVO((Policy) store);
}
return null;
}
@@ -183,6 +209,10 @@ public PolicyVO getByIdFallback(Long id, Throwable e) {
@Override
public List getPageFallback(Long lastId, int size, String keyword, Throwable e) {
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
+ }
+
try {
// 从 Redis 获取缓存的全量文章(假设是 ArticleVO[] 的 JSON)
String json = (String) redisUtils.get(TaskConstants.POLICY);
@@ -205,6 +235,10 @@ public List getPageFallback(Long lastId, int size, String keyword, Thr
}
@Override
+ @Loggable(
+ type = LogType.POLICY_ADMIN_GET_PAGE,
+ value = "Admin get policy Page"
+ )
public Page getPolicyPageAdmin(Page page, String keyword) {
long current = page.getCurrent();
long size = page.getSize();
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/ReportServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/ReportServiceImpl.java
index 5bcd9a6..5b458b1 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/ReportServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/ReportServiceImpl.java
@@ -1,17 +1,19 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
import cn.cxdproject.coder.common.constants.CaffeineConstants;
import cn.cxdproject.coder.common.constants.ResponseConstants;
import cn.cxdproject.coder.common.context.AuthContext;
+import cn.cxdproject.coder.common.enums.LogType;
import cn.cxdproject.coder.exception.BusinessException;
import cn.cxdproject.coder.exception.NotFoundException;
import cn.cxdproject.coder.model.dto.CreateReportDTO;
-import cn.cxdproject.coder.model.dto.UpdateReportDTO;
+import cn.cxdproject.coder.model.dto.UpdateReportAdminDTO;
+import cn.cxdproject.coder.model.dto.UpdateReportUserDTO;
import cn.cxdproject.coder.model.entity.Report;
import cn.cxdproject.coder.model.entity.User;
import cn.cxdproject.coder.model.vo.ReportVO;
import cn.cxdproject.coder.mapper.ReportMapper;
-import cn.cxdproject.coder.model.vo.ShootVO;
import cn.cxdproject.coder.service.ReportService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -28,6 +30,7 @@
import static cn.cxdproject.coder.common.enums.ResponseCodeEnum.*;
+
/**
* Report 服务实现类
* @author Hibiscus-code-generate
@@ -46,6 +49,10 @@ public ReportServiceImpl(
}
@Override
+ @Loggable(
+ type = LogType.REPORT_USER_CREATE,
+ value = "User submit report application"
+ )
public ReportVO createReport(Long userId, CreateReportDTO createDTO) {
Report report = Report.builder()
@@ -78,43 +85,34 @@ public ReportVO createReport(Long userId, CreateReportDTO createDTO) {
}
@Override
- public ReportVO updateReport(Long userId, Long reportId, UpdateReportDTO updateDTO) {
+ @Loggable(
+ type = LogType.REPORT_USER_UPDATE,
+ value = "User update report application ID: #{#reportId}"
+ )
+ public ReportVO updateReport(Long userId, Long reportId, UpdateReportUserDTO updateDTO) {
Report report = this.getById(reportId);
if (report == null || Boolean.TRUE.equals(report.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
-
if (!report.getUserId().equals(userId)) {
throw new BusinessException(FORBIDDEN.code(), ResponseConstants.NO_PERMISSION);
}
- if (updateDTO.getName() != null) report.setName(updateDTO.getName());
- if (updateDTO.getType() != null) report.setType(updateDTO.getType());
- if (updateDTO.getGenre() != null) report.setGenre(updateDTO.getGenre());
- if (updateDTO.getEpisodes() != null) report.setEpisodes(updateDTO.getEpisodes());
- if (updateDTO.getInvestAmount() != null) report.setInvestAmount(updateDTO.getInvestAmount());
- if (updateDTO.getMainCreators() != null) report.setMainCreators(updateDTO.getMainCreators());
- if (updateDTO.getLeadProducer() != null) report.setLeadProducer(updateDTO.getLeadProducer());
- if (updateDTO.getProducerUnit() != null) report.setProducerUnit(updateDTO.getProducerUnit());
- if (updateDTO.getStartDate() != null) report.setStartDate(updateDTO.getStartDate());
- if (updateDTO.getEndDate() != null) report.setEndDate(updateDTO.getEndDate());
- if (updateDTO.getCrewScale() != null) report.setCrewScale(updateDTO.getCrewScale());
- if (updateDTO.getContact() != null) report.setContact(updateDTO.getContact());
- if (updateDTO.getPhoneNumber() != null) report.setPhoneNumber(updateDTO.getPhoneNumber());
- if (updateDTO.getCrewPosition() != null) report.setCrewPosition(updateDTO.getCrewPosition());
- if (updateDTO.getShootPermit() != null) report.setShootPermit(updateDTO.getShootPermit());
- if (updateDTO.getThumbShootPermit() != null) report.setThumbShootPermit(updateDTO.getThumbShootPermit());
- if (updateDTO.getApprovalFile() != null) report.setApprovalFile(updateDTO.getApprovalFile());
- if (updateDTO.getThumbApprovalFile() != null) report.setThumbApprovalFile(updateDTO.getThumbApprovalFile());
- if (updateDTO.getShootApply() != null) report.setShootApply(updateDTO.getShootApply());
- if (updateDTO.getThumbShootApply() != null) report.setThumbShootApply(updateDTO.getThumbShootApply());
- if (updateDTO.getStatus() != null) throw new BusinessException(FORBIDDEN.code(), "无权修改申请状态");;
-
- this.updateById(report);
- return toReportVO(report);
+ int rows = reportMapper.updateReportByUser(reportId, updateDTO);
+ if (rows == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+
+ Report updated = this.getById(reportId);
+ cache.asMap().put(CaffeineConstants.REPORT + reportId, updated);
+ return toReportVO(updated);
}
@Override
+ @Loggable(
+ type = LogType.REPORT_USER_DELETE,
+ value = "User cancel report application ID: #{#reportId}"
+ )
public void deleteReport(Long userId, Long reportId) {
boolean updated = reportMapper.update(null,
Wrappers.lambdaUpdate()
@@ -136,6 +134,10 @@ public void deleteReport(Long userId, Long reportId) {
}
@Override
+ @Loggable(
+ type = LogType.REPORT_USER_GET,
+ value = "User get own report application by ID: #{#reportId}"
+ )
public ReportVO getReportById(Long reportId,Long userId) {
Object store = cache.asMap().get(CaffeineConstants.REPORT + reportId);
Report report;
@@ -157,6 +159,10 @@ public ReportVO getReportById(Long reportId,Long userId) {
@Override
+ @Loggable(
+ type = LogType.REPORT_USER_GET_PAGE,
+ value = "User get all own report applications"
+ )
public Page getMyReportPage(Long userId, Page page) {
long current = page.getCurrent();
long size = page.getSize();
@@ -178,23 +184,36 @@ public Page getMyReportPage(Long userId, Page page) {
@Override
- public ReportVO updateReportByAdmin(Long reportId, UpdateReportDTO updateDTO) {
- User currentUser = AuthContext.getCurrentUser();
+ @Loggable(
+ type = LogType.REPORT_ADMIN_UPDATE,
+ value = "Admin process report application ID: #{#reportId}"
+ )
+ public ReportVO updateReportByAdmin(Long reportId, UpdateReportAdminDTO updateDTO) {
+ if (updateDTO.getStatus() == null || updateDTO.getStatus().trim().isEmpty()) {
+ throw new BusinessException(MISSING_PARAMETER.code(),"审核状态不能为空");
+ }
+
Report report = this.getById(reportId);
if (report == null || Boolean.TRUE.equals(report.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- if (updateDTO.getStatus() != null) report.setStatus(updateDTO.getStatus());
+ int rows = reportMapper.updateReportByAdmin(reportId, updateDTO.getStatus());
+ if (rows == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
- report.setUpdatedAt(LocalDateTime.now());
- cache.asMap().put(CaffeineConstants.REPORT + reportId, report);
- this.updateById(report);
- return toReportVO(report);
+ Report updated = this.getById(reportId);
+ cache.asMap().put(CaffeineConstants.REPORT + reportId, updated);
+ return toReportVO(updated);
}
@Override
+ @Loggable(
+ type = LogType.REPORT_ADMIN_GET_PAGE,
+ value = "Admin get paginated report applications"
+ )
public Page getReportPageByAdmin(Page page, String keyword) {
long current = page.getCurrent();
long size = page.getSize();
@@ -221,6 +240,10 @@ public Page getReportPageByAdmin(Page page, String keyword) {
}
@Override
+ @Loggable(
+ type = LogType.REPORT_ADMIN_GET,
+ value = "Admin get report application by ID: #{#reportId}"
+ )
public ReportVO getReportByIdByAdmin(Long reportId) {
Object store = cache.asMap().get(CaffeineConstants.REPORT + reportId);
if (store != null) {
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/ShootServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/ShootServiceImpl.java
index fb5d203..f7ea56f 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/ShootServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/ShootServiceImpl.java
@@ -1,6 +1,8 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
import cn.cxdproject.coder.common.constants.*;
+import cn.cxdproject.coder.common.enums.LogType;
import cn.cxdproject.coder.exception.BusinessException;
import cn.cxdproject.coder.exception.NotFoundException;
import cn.cxdproject.coder.model.dto.CreateShootDTO;
@@ -53,7 +55,10 @@ public ShootServiceImpl(
}
@Override
- @Bulkhead(name = "add", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.SHOOT_CREATE,
+ value = "Create shoot"
+ )
public ShootVO createShootByAdmin(Long userId, CreateShootDTO createDTO) {
if (createDTO.getImage() == null) {
createDTO.setImage(Constants.DEFAULT_COVER);
@@ -80,6 +85,10 @@ public ShootVO createShootByAdmin(Long userId, CreateShootDTO createDTO) {
@Override
@CircuitBreaker(name = "shootGetById", fallbackMethod = "getByIdFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.SHOOT_GET,
+ value = "Get shoot by ID: #{#shootId}"
+ )
public ShootVO getShootById(Long shootId) {
Object store = cache.asMap().get(CaffeineConstants.SHOOT + shootId);
if (store != null) {
@@ -89,7 +98,7 @@ public ShootVO getShootById(Long shootId) {
if (shoot == null || Boolean.TRUE.equals(shoot.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- cache.asMap().put(CaffeineConstants.SHOOT + shootId, shoot);
+ cache.put(CaffeineConstants.SHOOT + shootId, shoot);
return toShootVO(shoot);
}
}
@@ -97,6 +106,10 @@ public ShootVO getShootById(Long shootId) {
@Override
@CircuitBreaker(name = "shootGetPage", fallbackMethod = "getPageFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.SHOOT_USER_GET_PAGE,
+ value = "User get shoot"
+ )
public List getShootPage(Long lastId, int size, String keyword) {
List ids = shootMapper.selectIds(lastId, size, keyword);
if (ids.isEmpty()) {
@@ -117,30 +130,40 @@ public List getShootPage(Long lastId, int size, String keyword) {
@Override
@Bulkhead(name = "update", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.SHOOT_UPDATE,
+ value = "Update shoot ID: #{#shootId}"
+ )
public ShootVO updateShootByAdmin(Long shootId, UpdateShootDTO updateDTO) {
- Shoot shoot = this.getById(shootId);
- if (shoot == null || Boolean.TRUE.equals(shoot.getDeleted())) {
+ // 1. 校验是否存在且未被删除
+ Shoot existing = this.getById(shootId);
+ if (existing == null || Boolean.TRUE.equals(existing.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- if (updateDTO.getName() != null) shoot.setName(updateDTO.getName());
- if (updateDTO.getDescription() != null) shoot.setDescription(updateDTO.getDescription());
- if (updateDTO.getPrice() != null) shoot.setPrice(updateDTO.getPrice());
- if (updateDTO.getStatus() != null) shoot.setStatus(updateDTO.getStatus());
- if (updateDTO.getAddress() != null) shoot.setAddress(updateDTO.getAddress());
- if (updateDTO.getPhone() != null) shoot.setPhone(updateDTO.getPhone());
- if (updateDTO.getContactName() != null) shoot.setContactName(updateDTO.getContactName());
- if (updateDTO.getImage() != null) shoot.setImage(updateDTO.getImage());
- if (updateDTO.getThumbImage() != null) shoot.setThumbImage(updateDTO.getThumbImage());
-
- shoot.setUpdatedAt(LocalDateTime.now());
- cache.asMap().put(CaffeineConstants.SHOOT + shootId, shoot);
- this.updateById(shoot);
- return toShootVO(shoot);
+ // 3. 执行动态更新
+ int updatedRows = shootMapper.updateShoot(shootId, updateDTO);
+
+ // 4. 若无记录被更新(可能已被删除)
+ if (updatedRows == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+
+ // 5. 重新加载最新数据(保证缓存和返回的是最新状态)
+ Shoot updatedShoot = this.getById(shootId);
+
+ // 6. 更新缓存
+ cache.put(CaffeineConstants.SHOOT + shootId, updatedShoot);
+
+ // 7. 返回 VO
+ return toShootVO(updatedShoot);
}
@Override
- @Bulkhead(name = "delete", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.SHOOT_DELETE,
+ value = "Delete shoot by ID: #{#shootId}"
+ )
public void deleteShootByAdmin(Long shootId) {
boolean updated = shootMapper.update(null,
Wrappers.lambdaUpdate()
@@ -182,15 +205,14 @@ public ShootVO toShootVO(Shoot shoot) {
@Override
public ShootVO getByIdFallback(Long id,Throwable e) {
- Object store;
- store = cache.getIfPresent(CaffeineConstants.SHOOT + id);
- if (store != null) {
- return toShootVO((Shoot) store);
+
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
}
- store = redisUtils.get(RedisKeyConstants.SHOOT+id);
+
+ Object store = cache.getIfPresent(CaffeineConstants.SHOOT + id);
if (store != null) {
- Shoot shoot = JsonUtils.fromJson((String) store, Shoot.class);
- return toShootVO(shoot);
+ return toShootVO((Shoot) store);
}
return null;
}
@@ -198,6 +220,10 @@ public ShootVO getByIdFallback(Long id,Throwable e) {
@Override
public List getPageFallback(Long lastId, int size, String keyword, Throwable e) {
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
+ }
+
try {
// 从 Redis 获取缓存的全量文章(假设是 ArticleVO[] 的 JSON)
String json = (String) redisUtils.get(TaskConstants.SHOOT);
@@ -220,6 +246,10 @@ public List getPageFallback(Long lastId, int size, String keyword, Thro
}
@Override
+ @Loggable(
+ type = LogType.SHOOT_ADMIN_GET_PAGE,
+ value = "Admin get shoot Page"
+ )
public Page getShootPageAdmin(Page page, String keyword) {
long current = page.getCurrent();
long size = page.getSize();
diff --git a/src/main/java/cn/cxdproject/coder/service/impl/TourServiceImpl.java b/src/main/java/cn/cxdproject/coder/service/impl/TourServiceImpl.java
index fceeb52..68f513b 100644
--- a/src/main/java/cn/cxdproject/coder/service/impl/TourServiceImpl.java
+++ b/src/main/java/cn/cxdproject/coder/service/impl/TourServiceImpl.java
@@ -1,6 +1,9 @@
package cn.cxdproject.coder.service.impl;
+import cn.cxdproject.coder.common.anno.Loggable;
import cn.cxdproject.coder.common.constants.*;
+import cn.cxdproject.coder.common.enums.LogType;
+import cn.cxdproject.coder.exception.BusinessException;
import cn.cxdproject.coder.exception.NotFoundException;
import cn.cxdproject.coder.model.dto.CreateTourDTO;
import cn.cxdproject.coder.model.dto.UpdateTourDTO;
@@ -46,6 +49,10 @@ public TourServiceImpl(TourMapper tourMapper, Cache cache, Redis
}
@Override
+ @Loggable(
+ type = LogType.TOUR_CREATE,
+ value = "Create tour"
+ )
public TourVO createTourByAdmin(CreateTourDTO createDTO) {
if (createDTO.getImage() == null) {
createDTO.setImage(Constants.DEFAULT_COVER);
@@ -76,6 +83,10 @@ public TourVO createTourByAdmin(CreateTourDTO createDTO) {
@Override
@CircuitBreaker(name = "tourGetById", fallbackMethod = "getByIdFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.TOUR_GET,
+ value = "Get tour by ID: #{#tourId}"
+ )
public TourVO getTourById(Long tourId) {
Object store = cache.asMap().get(CaffeineConstants.TOUR + tourId);
if (store != null) {
@@ -85,7 +96,7 @@ public TourVO getTourById(Long tourId) {
if (tour == null || Boolean.TRUE.equals(tour.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- cache.asMap().put(CaffeineConstants.TOUR + tourId, tour);
+ cache.put(CaffeineConstants.TOUR + tourId, tour);
return toTourVO(tour);
}
}
@@ -93,6 +104,10 @@ public TourVO getTourById(Long tourId) {
@Override
@CircuitBreaker(name = "tourGetPage", fallbackMethod = "getPageFallback")
@Bulkhead(name = "get", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.TOUR_USER_GET_PAGE,
+ value = "User get tour"
+ )
public List getTourPage(Long lastId, int size, String keyword) {
List ids = tourMapper.selectIds(lastId, size, keyword);
if (ids.isEmpty()) {
@@ -113,37 +128,40 @@ public List getTourPage(Long lastId, int size, String keyword) {
@Override
- @Bulkhead(name = "update", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.TOUR_UPDATE,
+ value = "Update tour ID: #{#tourId}"
+ )
public TourVO updateTourByAdmin(Long tourId, UpdateTourDTO updateDTO) {
- Tour tour = this.getById(tourId);
- if (tour == null || Boolean.TRUE.equals(tour.getDeleted())) {
+ // 1. 校验是否存在且未被删除
+ Tour existing = this.getById(tourId);
+ if (existing == null || Boolean.TRUE.equals(existing.getDeleted())) {
throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
}
- if (updateDTO.getName() != null) tour.setName(updateDTO.getName());
- if (updateDTO.getDescription() != null) tour.setDescription(updateDTO.getDescription());
- if (updateDTO.getTheme() != null) tour.setTheme(updateDTO.getTheme());
- if (updateDTO.getFeatures() != null) tour.setFeatures(updateDTO.getFeatures());
- if (updateDTO.getTransport() != null) tour.setTransport(updateDTO.getTransport());
- if (updateDTO.getHotel() != null) tour.setHotel(updateDTO.getHotel());
- if (updateDTO.getFood() != null) tour.setFood(updateDTO.getFood());
- if (updateDTO.getImage() != null) tour.setImage(updateDTO.getImage());
- if (updateDTO.getThumbImage() != null) tour.setThumbImage(updateDTO.getThumbImage());
- if (updateDTO.getLongitude() != null) tour.setLongitude(updateDTO.getLongitude());
- if (updateDTO.getLatitude() != null) tour.setLatitude(updateDTO.getLatitude());
- if (updateDTO.getLocationId() != null) tour.setLocationId(updateDTO.getLocationId());
- if (updateDTO.getDramaId() != null) tour.setDramaId(updateDTO.getDramaId());
-
-
-
- tour.setUpdatedAt(LocalDateTime.now());
- cache.asMap().put(CaffeineConstants.TOUR + tourId, tour);
- this.updateById(tour);
- return toTourVO(tour);
+ // 3. 执行动态更新
+ int updatedRows = tourMapper.updateTour(tourId, updateDTO);
+
+ // 4. 若无记录被更新(可能已被删除或并发修改)
+ if (updatedRows == 0) {
+ throw new NotFoundException(NOT_FOUND.code(), ResponseConstants.NOT_FIND);
+ }
+
+ // 5. 重新加载最新数据(保证一致性)
+ Tour updatedTour = this.getById(tourId);
+
+ // 6. 更新缓存(注意:缓存的是对象,不是旧引用)
+ cache.put(CaffeineConstants.TOUR + tourId, updatedTour);
+
+ // 7. 返回 VO
+ return toTourVO(updatedTour);
}
@Override
- @Bulkhead(name = "delete", type = Bulkhead.Type.SEMAPHORE)
+ @Loggable(
+ type = LogType.TOUR_DELETE,
+ value = "Delete tour by ID: #{#tourId}"
+ )
public void deleteTourByAdmin(Long tourId) {
boolean updated = tourMapper.update(null,
Wrappers.lambdaUpdate()
@@ -190,15 +208,14 @@ public TourVO toTourVO(Tour tour) {
@Override
public TourVO getByIdFallback(Long id,Throwable e) {
- Object store;
- store = cache.getIfPresent(CaffeineConstants.TOUR + id);
- if (store != null) {
- return toTourVO((Tour) store);
+
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
}
- store = redisUtils.get(RedisKeyConstants.TOUR+id);
+
+ Object store = cache.getIfPresent(CaffeineConstants.TOUR + id);
if (store != null) {
- Tour tour = JsonUtils.fromJson((String) store, Tour.class);
- return toTourVO(tour);
+ return toTourVO((Tour) store);
}
return null;
}
@@ -206,6 +223,10 @@ public TourVO getByIdFallback(Long id,Throwable e) {
@Override
public List getPageFallback(Long lastId, int size, String keyword, Throwable e) {
+ if (e instanceof NotFoundException || e instanceof BusinessException) {
+ throw (RuntimeException) e;
+ }
+
try {
String json = (String) redisUtils.get(TaskConstants.TOUR);
@@ -228,6 +249,10 @@ public List getPageFallback(Long lastId, int size, String keyword, Throw
}
@Override
+ @Loggable(
+ type = LogType.TOUR_ADMIN_GET_PAGE,
+ value = "Admin get tour Page"
+ )
public Page getTourPageAdmin(Page page, String keyword) {
long current = page.getCurrent();
long size = page.getSize();
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index dc3659c..3b9712f 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -125,21 +125,6 @@ resilience4j:
max-wait-duration: 100ms
instances:
- add:
- base-config: default
- max-concurrent-calls: 10
- max-wait-duration: 100ms # 写操作可以稍微等一下
-
- delete:
- base-config: default
- max-concurrent-calls: 20
- max-wait-duration: 50ms
-
- update:
- base-config: default
- max-concurrent-calls: 20
- max-wait-duration: 50ms
-
get:
base-config: default
max-concurrent-calls: 50
diff --git a/src/main/resources/mapper/ArticleMapper.xml b/src/main/resources/mapper/ArticleMapper.xml
index 3a5951a..b6bcdca 100644
--- a/src/main/resources/mapper/ArticleMapper.xml
+++ b/src/main/resources/mapper/ArticleMapper.xml
@@ -77,4 +77,25 @@
LIMIT 10
+
+
+ UPDATE fi_articles
+
+
+ title = #{dto.title},
+
+
+ issue_unit = #{dto.issueUnit},
+
+
+ content = #{dto.content},
+
+
+ thumb_image = #{dto.thumbImage},
+
+
+ WHERE id = #{articleId}
+ AND deleted = false
+
+
diff --git a/src/main/resources/mapper/DramaMapper.xml b/src/main/resources/mapper/DramaMapper.xml
index 1d59006..8c08015 100644
--- a/src/main/resources/mapper/DramaMapper.xml
+++ b/src/main/resources/mapper/DramaMapper.xml
@@ -89,4 +89,48 @@
LIMIT 10
+
+ UPDATE fi_dramas
+
+
+ name = #{dto.name},
+
+
+ filing_num = #{dto.filingNum},
+
+
+ prod_company = #{dto.prodCompany},
+
+
+ crew_description = #{dto.crewDescription},
+
+
+ drama_description = #{dto.dramaDescription},
+
+
+ cast = #{dto.cast},
+
+
+ shoot_location = #{dto.shootLocation},
+
+
+ service = #{dto.service},
+
+
+ image = #{dto.image},
+
+
+ thumb_image = #{dto.thumbImage},
+
+
+ location_id = #{dto.locationId},
+
+
+ service_id = #{dto.serviceId},
+
+
+ WHERE id = #{dramaId}
+ AND deleted = false
+
+
diff --git a/src/main/resources/mapper/HotelMapper.xml b/src/main/resources/mapper/HotelMapper.xml
index c33dcc3..c21affa 100644
--- a/src/main/resources/mapper/HotelMapper.xml
+++ b/src/main/resources/mapper/HotelMapper.xml
@@ -81,4 +81,39 @@
LIMIT 10
+
+ UPDATE fi_hotel
+
+
+ name = #{dto.name},
+
+
+ description = #{dto.description},
+
+
+ address = #{dto.address},
+
+
+ manager_name = #{dto.managerName},
+
+
+ manager_phone = #{dto.managerPhone},
+
+
+ image = #{dto.image},
+
+
+ thumb_image = #{dto.thumbImage},
+
+
+ longitude = #{dto.longitude},
+
+
+ latitude = #{dto.latitude},
+
+
+ WHERE id = #{id}
+ AND deleted = false
+
+
\ No newline at end of file
diff --git a/src/main/resources/mapper/LocationMapper.xml b/src/main/resources/mapper/LocationMapper.xml
index dfda343..17bd0b3 100644
--- a/src/main/resources/mapper/LocationMapper.xml
+++ b/src/main/resources/mapper/LocationMapper.xml
@@ -59,6 +59,7 @@
AND (
name LIKE CONCAT('%', #{keyword}, '%')
OR location_description LIKE CONCAT('%', #{keyword}, '%')
+ OR type LIKE CONCAT('%', #{keyword}, '%')
)
@@ -79,7 +80,11 @@
FROM fi_locations
WHERE deleted = 0
- AND title LIKE CONCAT('%', #{keyword}, '%')
+ AND (
+ name LIKE CONCAT('%', #{keyword}, '%')
+ OR location_description LIKE CONCAT('%', #{keyword}, '%')
+ OR type LIKE CONCAT('%', #{keyword}, '%')
+ )
ORDER BY created_at DESC
LIMIT #{offset}, #{size}
@@ -94,4 +99,55 @@
LIMIT 10
+
+
+ UPDATE fi_locations
+
+
+ name = #{dto.name},
+
+
+ type = #{dto.type},
+
+
+ status = #{dto.status},
+
+
+ location_description = #{dto.locationDescription},
+
+
+ location_principal_name = #{dto.locationPrincipalName},
+
+
+ location_principal_phone = #{dto.locationPrincipalPhone},
+
+
+ gov_principal_name = #{dto.govPrincipalName},
+
+
+ gov_principal_phone = #{dto.govPrincipalPhone},
+
+
+ address = #{dto.address},
+
+
+ price = #{dto.price},
+
+
+ image = #{dto.image},
+
+
+ thumb_image = #{dto.thumbImage},
+
+
+ longitude = #{dto.longitude},
+
+
+ latitude = #{dto.latitude},
+
+
+ WHERE id = #{id}
+ AND deleted = false
+
+
diff --git a/src/main/resources/mapper/OperationLogMapper.xml b/src/main/resources/mapper/OperationLogMapper.xml
index a25d5df..8bebc05 100644
--- a/src/main/resources/mapper/OperationLogMapper.xml
+++ b/src/main/resources/mapper/OperationLogMapper.xml
@@ -34,4 +34,15 @@
`updated_at`
+
+
diff --git a/src/main/resources/mapper/PolicyMapper.xml b/src/main/resources/mapper/PolicyMapper.xml
index b45c915..5f3956d 100644
--- a/src/main/resources/mapper/PolicyMapper.xml
+++ b/src/main/resources/mapper/PolicyMapper.xml
@@ -76,5 +76,34 @@
LIMIT 10
+
+ UPDATE fi_policy
+
+
+ title = #{dto.title},
+
+
+ type = #{dto.type},
+
+
+ issue_unit = #{dto.issueUnit},
+
+
+ issue_time = #{dto.issueTime},
+
+
+ content = #{dto.content},
+
+
+ image = #{dto.image},
+
+
+ thumb_image = #{dto.thumbImage},
+
+
+ WHERE id = #{id}
+ AND deleted = false
+
+
diff --git a/src/main/resources/mapper/ReportMapper.xml b/src/main/resources/mapper/ReportMapper.xml
index 55bc8de..7f657b5 100644
--- a/src/main/resources/mapper/ReportMapper.xml
+++ b/src/main/resources/mapper/ReportMapper.xml
@@ -84,7 +84,10 @@
FROM fi_reports
WHERE deleted = 0
- AND name LIKE CONCAT('%', #{keyword}, '%')
+ AND (
+ name LIKE CONCAT('%', #{keyword}, '%')
+ OR type LIKE CONCAT('%', #{keyword}, '%')
+ )
ORDER BY created_at DESC
LIMIT #{offset}, #{size}
@@ -92,5 +95,81 @@
ORDER BY created_at DESC
+
+ UPDATE fi_reports
+
+
+ name = #{dto.name},
+
+
+ type = #{dto.type},
+
+
+ genre = #{dto.genre},
+
+
+ episodes = #{dto.episodes},
+
+
+ invest_amount = #{dto.investAmount},
+
+
+ main_creators = #{dto.mainCreators},
+
+
+ lead_producer = #{dto.leadProducer},
+
+
+ producer_unit = #{dto.producerUnit},
+
+
+ start_date = #{dto.startDate},
+
+
+ end_date = #{dto.endDate},
+
+
+ crew_scale = #{dto.crewScale},
+
+
+ contact = #{dto.contact},
+
+
+ phone_number = #{dto.phoneNumber},
+
+
+ crew_position = #{dto.crewPosition},
+
+
+ shoot_permit = #{dto.shootPermit},
+
+
+ thumb_shoot_permit = #{dto.thumbShootPermit},
+
+
+ approval_file = #{dto.approvalFile},
+
+
+ thumb_approval_file = #{dto.thumbApprovalFile},
+
+
+ shoot_apply = #{dto.shootApply},
+
+
+ thumb_shoot_apply = #{dto.thumbShootApply},
+
+
+ WHERE id = #{id}
+ AND deleted = false
+
+
+
+ UPDATE fi_reports
+ SET
+ status = #{status}
+ WHERE id = #{id}
+ AND deleted = false
+
+
diff --git a/src/main/resources/mapper/ShootMapper.xml b/src/main/resources/mapper/ShootMapper.xml
index 2749bf2..78466e2 100644
--- a/src/main/resources/mapper/ShootMapper.xml
+++ b/src/main/resources/mapper/ShootMapper.xml
@@ -83,4 +83,38 @@
LIMIT 10
+
+ UPDATE fi_shoots
+
+
+ name = #{dto.name},
+
+
+ description = #{dto.description},
+
+
+ price = #{dto.price},
+
+
+ status = #{dto.status},
+
+
+ address = #{dto.address},
+
+
+ phone = #{dto.phone},
+
+
+ contact_name = #{dto.contactName},
+
+
+ image = #{dto.image},
+
+
+ thumb_image = #{dto.thumbImage},
+
+
+ WHERE id = #{id}
+ AND deleted = false
+
diff --git a/src/main/resources/mapper/TourMapper.xml b/src/main/resources/mapper/TourMapper.xml
index 8a3d7c9..77b0df3 100644
--- a/src/main/resources/mapper/TourMapper.xml
+++ b/src/main/resources/mapper/TourMapper.xml
@@ -54,6 +54,7 @@
AND (
name LIKE CONCAT('%', #{keyword}, '%')
OR description LIKE CONCAT('%', #{keyword}, '%')
+ OR theme LIKE CONCAT('%', #{keyword}, '%')
)
@@ -67,14 +68,18 @@
+
+ UPDATE fi_tour
+
+
+ name = #{dto.name},
+
+
+ description = #{dto.description},
+
+
+ theme = #{dto.theme},
+
+
+ features = #{dto.features},
+
+
+ transport = #{dto.transport},
+
+
+ hotel = #{dto.hotel},
+
+
+ food = #{dto.food},
+
+
+ image = #{dto.image},
+
+
+ thumb_image = #{dto.thumbImage},
+
+
+ longitude = #{dto.longitude},
+
+
+ latitude = #{dto.latitude},
+
+
+ location_id = #{dto.locationId},
+
+
+ drama_id = #{dto.dramaId},
+
+
+ WHERE id = #{id}
+ AND deleted = false
+
+