Compare commits
2 Commits
e592b9fc42
...
b04eb52da4
| Author | SHA1 | Date | |
|---|---|---|---|
| b04eb52da4 | |||
| 71f71b74d1 |
418
.gitignore
vendored
Normal file
418
.gitignore
vendored
Normal file
@@ -0,0 +1,418 @@
|
||||
/.vscode/mcp.json
|
||||
/.vscode/settings.json
|
||||
/.qwen/settings.json.orig
|
||||
/.playwright-mcp/console-2026-03-31T08-27-30-883Z.log
|
||||
/.playwright-mcp/console-2026-05-19T03-10-43-600Z.log
|
||||
/.playwright-mcp/console-2026-05-19T03-18-23-396Z.log
|
||||
/.playwright-mcp/console-2026-05-19T03-18-51-946Z.log
|
||||
/.playwright-mcp/page-2026-05-11T02-56-22-027Z.yml
|
||||
/.playwright-mcp/page-2026-05-11T02-56-30-095Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-10-44-171Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-11-20-520Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-11-40-168Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-12-10-968Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-18-23-610Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-18-52-634Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-19-19-472Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-19-36-669Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-20-04-342Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-21-08-820Z.yml
|
||||
/.playwright-mcp/page-2026-05-19T03-21-43-735Z.yml
|
||||
/.idea/compiler.xml
|
||||
/.idea/encodings.xml
|
||||
/.idea/jarRepositories.xml
|
||||
/.idea/misc.xml
|
||||
/.idea/vcs.xml
|
||||
/.idea/workspace.xml
|
||||
/node_modules/.bin/husky
|
||||
/node_modules/.bin/husky.cmd
|
||||
/node_modules/.bin/husky.ps1
|
||||
/node_modules/asynckit/lib/abort.js
|
||||
/node_modules/asynckit/lib/async.js
|
||||
/node_modules/asynckit/lib/defer.js
|
||||
/node_modules/asynckit/lib/iterate.js
|
||||
/node_modules/asynckit/lib/readable_asynckit.js
|
||||
/node_modules/asynckit/lib/readable_parallel.js
|
||||
/node_modules/asynckit/lib/readable_serial.js
|
||||
/node_modules/asynckit/lib/readable_serial_ordered.js
|
||||
/node_modules/asynckit/lib/state.js
|
||||
/node_modules/asynckit/lib/streamify.js
|
||||
/node_modules/asynckit/lib/terminator.js
|
||||
/node_modules/asynckit/bench.js
|
||||
/node_modules/asynckit/index.js
|
||||
/node_modules/asynckit/LICENSE
|
||||
/node_modules/asynckit/package.json
|
||||
/node_modules/asynckit/parallel.js
|
||||
/node_modules/asynckit/README.md
|
||||
/node_modules/asynckit/serial.js
|
||||
/node_modules/asynckit/serialOrdered.js
|
||||
/node_modules/asynckit/stream.js
|
||||
/node_modules/axios/dist/browser/axios.cjs
|
||||
/node_modules/axios/dist/esm/axios.js
|
||||
/node_modules/axios/dist/esm/axios.min.js
|
||||
/node_modules/axios/dist/esm/axios.min.js.map
|
||||
/node_modules/axios/dist/node/axios.cjs
|
||||
/node_modules/axios/dist/axios.js
|
||||
/node_modules/axios/dist/axios.min.js
|
||||
/node_modules/axios/dist/axios.min.js.map
|
||||
/node_modules/axios/lib/adapters/adapters.js
|
||||
/node_modules/axios/lib/adapters/fetch.js
|
||||
/node_modules/axios/lib/adapters/http.js
|
||||
/node_modules/axios/lib/adapters/README.md
|
||||
/node_modules/axios/lib/adapters/xhr.js
|
||||
/node_modules/axios/lib/cancel/CanceledError.js
|
||||
/node_modules/axios/lib/cancel/CancelToken.js
|
||||
/node_modules/axios/lib/cancel/isCancel.js
|
||||
/node_modules/axios/lib/core/Axios.js
|
||||
/node_modules/axios/lib/core/AxiosError.js
|
||||
/node_modules/axios/lib/core/AxiosHeaders.js
|
||||
/node_modules/axios/lib/core/buildFullPath.js
|
||||
/node_modules/axios/lib/core/dispatchRequest.js
|
||||
/node_modules/axios/lib/core/InterceptorManager.js
|
||||
/node_modules/axios/lib/core/mergeConfig.js
|
||||
/node_modules/axios/lib/core/README.md
|
||||
/node_modules/axios/lib/core/settle.js
|
||||
/node_modules/axios/lib/core/transformData.js
|
||||
/node_modules/axios/lib/defaults/index.js
|
||||
/node_modules/axios/lib/defaults/transitional.js
|
||||
/node_modules/axios/lib/env/classes/FormData.js
|
||||
/node_modules/axios/lib/env/data.js
|
||||
/node_modules/axios/lib/env/README.md
|
||||
/node_modules/axios/lib/helpers/AxiosTransformStream.js
|
||||
/node_modules/axios/lib/helpers/AxiosURLSearchParams.js
|
||||
/node_modules/axios/lib/helpers/bind.js
|
||||
/node_modules/axios/lib/helpers/buildURL.js
|
||||
/node_modules/axios/lib/helpers/callbackify.js
|
||||
/node_modules/axios/lib/helpers/combineURLs.js
|
||||
/node_modules/axios/lib/helpers/composeSignals.js
|
||||
/node_modules/axios/lib/helpers/cookies.js
|
||||
/node_modules/axios/lib/helpers/deprecatedMethod.js
|
||||
/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js
|
||||
/node_modules/axios/lib/helpers/formDataToJSON.js
|
||||
/node_modules/axios/lib/helpers/formDataToStream.js
|
||||
/node_modules/axios/lib/helpers/fromDataURI.js
|
||||
/node_modules/axios/lib/helpers/HttpStatusCode.js
|
||||
/node_modules/axios/lib/helpers/isAbsoluteURL.js
|
||||
/node_modules/axios/lib/helpers/isAxiosError.js
|
||||
/node_modules/axios/lib/helpers/isURLSameOrigin.js
|
||||
/node_modules/axios/lib/helpers/null.js
|
||||
/node_modules/axios/lib/helpers/parseHeaders.js
|
||||
/node_modules/axios/lib/helpers/parseProtocol.js
|
||||
/node_modules/axios/lib/helpers/progressEventReducer.js
|
||||
/node_modules/axios/lib/helpers/readBlob.js
|
||||
/node_modules/axios/lib/helpers/README.md
|
||||
/node_modules/axios/lib/helpers/resolveConfig.js
|
||||
/node_modules/axios/lib/helpers/speedometer.js
|
||||
/node_modules/axios/lib/helpers/spread.js
|
||||
/node_modules/axios/lib/helpers/throttle.js
|
||||
/node_modules/axios/lib/helpers/toFormData.js
|
||||
/node_modules/axios/lib/helpers/toURLEncodedForm.js
|
||||
/node_modules/axios/lib/helpers/trackStream.js
|
||||
/node_modules/axios/lib/helpers/validator.js
|
||||
/node_modules/axios/lib/helpers/ZlibHeaderTransformStream.js
|
||||
/node_modules/axios/lib/platform/browser/classes/Blob.js
|
||||
/node_modules/axios/lib/platform/browser/classes/FormData.js
|
||||
/node_modules/axios/lib/platform/browser/classes/URLSearchParams.js
|
||||
/node_modules/axios/lib/platform/browser/index.js
|
||||
/node_modules/axios/lib/platform/common/utils.js
|
||||
/node_modules/axios/lib/platform/node/classes/FormData.js
|
||||
/node_modules/axios/lib/platform/node/classes/URLSearchParams.js
|
||||
/node_modules/axios/lib/platform/node/index.js
|
||||
/node_modules/axios/lib/platform/index.js
|
||||
/node_modules/axios/lib/axios.js
|
||||
/node_modules/axios/lib/utils.js
|
||||
/node_modules/axios/CHANGELOG.md
|
||||
/node_modules/axios/index.d.cts
|
||||
/node_modules/axios/index.d.ts
|
||||
/node_modules/axios/index.js
|
||||
/node_modules/axios/LICENSE
|
||||
/node_modules/axios/MIGRATION_GUIDE.md
|
||||
/node_modules/axios/package.json
|
||||
/node_modules/axios/README.md
|
||||
/node_modules/bignumber.js/doc/API.html
|
||||
/node_modules/bignumber.js/bignumber.d.mts
|
||||
/node_modules/bignumber.js/bignumber.d.ts
|
||||
/node_modules/bignumber.js/bignumber.js
|
||||
/node_modules/bignumber.js/bignumber.mjs
|
||||
/node_modules/bignumber.js/CHANGELOG.md
|
||||
/node_modules/bignumber.js/LICENCE.md
|
||||
/node_modules/bignumber.js/package.json
|
||||
/node_modules/bignumber.js/README.md
|
||||
/node_modules/bignumber.js/types.d.ts
|
||||
/node_modules/call-bind-apply-helpers/.github/FUNDING.yml
|
||||
/node_modules/call-bind-apply-helpers/test/index.js
|
||||
/node_modules/call-bind-apply-helpers/.eslintrc
|
||||
/node_modules/call-bind-apply-helpers/.nycrc
|
||||
/node_modules/call-bind-apply-helpers/actualApply.d.ts
|
||||
/node_modules/call-bind-apply-helpers/actualApply.js
|
||||
/node_modules/call-bind-apply-helpers/applyBind.d.ts
|
||||
/node_modules/call-bind-apply-helpers/applyBind.js
|
||||
/node_modules/call-bind-apply-helpers/CHANGELOG.md
|
||||
/node_modules/call-bind-apply-helpers/functionApply.d.ts
|
||||
/node_modules/call-bind-apply-helpers/functionApply.js
|
||||
/node_modules/call-bind-apply-helpers/functionCall.d.ts
|
||||
/node_modules/call-bind-apply-helpers/functionCall.js
|
||||
/node_modules/call-bind-apply-helpers/index.d.ts
|
||||
/node_modules/call-bind-apply-helpers/index.js
|
||||
/node_modules/call-bind-apply-helpers/LICENSE
|
||||
/node_modules/call-bind-apply-helpers/package.json
|
||||
/node_modules/call-bind-apply-helpers/README.md
|
||||
/node_modules/call-bind-apply-helpers/reflectApply.d.ts
|
||||
/node_modules/call-bind-apply-helpers/reflectApply.js
|
||||
/node_modules/call-bind-apply-helpers/tsconfig.json
|
||||
/node_modules/combined-stream/lib/combined_stream.js
|
||||
/node_modules/combined-stream/License
|
||||
/node_modules/combined-stream/package.json
|
||||
/node_modules/combined-stream/Readme.md
|
||||
/node_modules/combined-stream/yarn.lock
|
||||
/node_modules/delayed-stream/lib/delayed_stream.js
|
||||
/node_modules/delayed-stream/.npmignore
|
||||
/node_modules/delayed-stream/License
|
||||
/node_modules/delayed-stream/Makefile
|
||||
/node_modules/delayed-stream/package.json
|
||||
/node_modules/delayed-stream/Readme.md
|
||||
/node_modules/dunder-proto/.github/FUNDING.yml
|
||||
/node_modules/dunder-proto/test/get.js
|
||||
/node_modules/dunder-proto/test/index.js
|
||||
/node_modules/dunder-proto/test/set.js
|
||||
/node_modules/dunder-proto/.eslintrc
|
||||
/node_modules/dunder-proto/.nycrc
|
||||
/node_modules/dunder-proto/CHANGELOG.md
|
||||
/node_modules/dunder-proto/get.d.ts
|
||||
/node_modules/dunder-proto/get.js
|
||||
/node_modules/dunder-proto/LICENSE
|
||||
/node_modules/dunder-proto/package.json
|
||||
/node_modules/dunder-proto/README.md
|
||||
/node_modules/dunder-proto/set.d.ts
|
||||
/node_modules/dunder-proto/set.js
|
||||
/node_modules/dunder-proto/tsconfig.json
|
||||
/node_modules/es-define-property/.github/FUNDING.yml
|
||||
/node_modules/es-define-property/test/index.js
|
||||
/node_modules/es-define-property/.eslintrc
|
||||
/node_modules/es-define-property/.nycrc
|
||||
/node_modules/es-define-property/CHANGELOG.md
|
||||
/node_modules/es-define-property/index.d.ts
|
||||
/node_modules/es-define-property/index.js
|
||||
/node_modules/es-define-property/LICENSE
|
||||
/node_modules/es-define-property/package.json
|
||||
/node_modules/es-define-property/README.md
|
||||
/node_modules/es-define-property/tsconfig.json
|
||||
/node_modules/es-errors/.github/FUNDING.yml
|
||||
/node_modules/es-errors/test/index.js
|
||||
/node_modules/es-errors/.eslintrc
|
||||
/node_modules/es-errors/CHANGELOG.md
|
||||
/node_modules/es-errors/eval.d.ts
|
||||
/node_modules/es-errors/eval.js
|
||||
/node_modules/es-errors/index.d.ts
|
||||
/node_modules/es-errors/index.js
|
||||
/node_modules/es-errors/LICENSE
|
||||
/node_modules/es-errors/package.json
|
||||
/node_modules/es-errors/range.d.ts
|
||||
/node_modules/es-errors/range.js
|
||||
/node_modules/es-errors/README.md
|
||||
/node_modules/es-errors/ref.d.ts
|
||||
/node_modules/es-errors/ref.js
|
||||
/node_modules/es-errors/syntax.d.ts
|
||||
/node_modules/es-errors/syntax.js
|
||||
/node_modules/es-errors/tsconfig.json
|
||||
/node_modules/es-errors/type.d.ts
|
||||
/node_modules/es-errors/type.js
|
||||
/node_modules/es-errors/uri.d.ts
|
||||
/node_modules/es-errors/uri.js
|
||||
/node_modules/es-object-atoms/.github/FUNDING.yml
|
||||
/node_modules/es-object-atoms/test/index.js
|
||||
/node_modules/es-object-atoms/.eslintrc
|
||||
/node_modules/es-object-atoms/CHANGELOG.md
|
||||
/node_modules/es-object-atoms/index.d.ts
|
||||
/node_modules/es-object-atoms/index.js
|
||||
/node_modules/es-object-atoms/isObject.d.ts
|
||||
/node_modules/es-object-atoms/isObject.js
|
||||
/node_modules/es-object-atoms/LICENSE
|
||||
/node_modules/es-object-atoms/package.json
|
||||
/node_modules/es-object-atoms/README.md
|
||||
/node_modules/es-object-atoms/RequireObjectCoercible.d.ts
|
||||
/node_modules/es-object-atoms/RequireObjectCoercible.js
|
||||
/node_modules/es-object-atoms/ToObject.d.ts
|
||||
/node_modules/es-object-atoms/ToObject.js
|
||||
/node_modules/es-object-atoms/tsconfig.json
|
||||
/node_modules/es-set-tostringtag/test/index.js
|
||||
/node_modules/es-set-tostringtag/.eslintrc
|
||||
/node_modules/es-set-tostringtag/.nycrc
|
||||
/node_modules/es-set-tostringtag/CHANGELOG.md
|
||||
/node_modules/es-set-tostringtag/index.d.ts
|
||||
/node_modules/es-set-tostringtag/index.js
|
||||
/node_modules/es-set-tostringtag/LICENSE
|
||||
/node_modules/es-set-tostringtag/package.json
|
||||
/node_modules/es-set-tostringtag/README.md
|
||||
/node_modules/es-set-tostringtag/tsconfig.json
|
||||
/node_modules/follow-redirects/debug.js
|
||||
/node_modules/follow-redirects/http.js
|
||||
/node_modules/follow-redirects/https.js
|
||||
/node_modules/follow-redirects/index.js
|
||||
/node_modules/follow-redirects/LICENSE
|
||||
/node_modules/follow-redirects/package.json
|
||||
/node_modules/follow-redirects/README.md
|
||||
/node_modules/form-data/lib/browser.js
|
||||
/node_modules/form-data/lib/form_data.js
|
||||
/node_modules/form-data/lib/populate.js
|
||||
/node_modules/form-data/CHANGELOG.md
|
||||
/node_modules/form-data/index.d.ts
|
||||
/node_modules/form-data/License
|
||||
/node_modules/form-data/package.json
|
||||
/node_modules/form-data/README.md
|
||||
/node_modules/function-bind/.github/FUNDING.yml
|
||||
/node_modules/function-bind/.github/SECURITY.md
|
||||
/node_modules/function-bind/test/.eslintrc
|
||||
/node_modules/function-bind/test/index.js
|
||||
/node_modules/function-bind/.eslintrc
|
||||
/node_modules/function-bind/.nycrc
|
||||
/node_modules/function-bind/CHANGELOG.md
|
||||
/node_modules/function-bind/implementation.js
|
||||
/node_modules/function-bind/index.js
|
||||
/node_modules/function-bind/LICENSE
|
||||
/node_modules/function-bind/package.json
|
||||
/node_modules/function-bind/README.md
|
||||
/node_modules/get-intrinsic/.github/FUNDING.yml
|
||||
/node_modules/get-intrinsic/test/GetIntrinsic.js
|
||||
/node_modules/get-intrinsic/.eslintrc
|
||||
/node_modules/get-intrinsic/.nycrc
|
||||
/node_modules/get-intrinsic/CHANGELOG.md
|
||||
/node_modules/get-intrinsic/index.js
|
||||
/node_modules/get-intrinsic/LICENSE
|
||||
/node_modules/get-intrinsic/package.json
|
||||
/node_modules/get-intrinsic/README.md
|
||||
/node_modules/get-proto/.github/FUNDING.yml
|
||||
/node_modules/get-proto/test/index.js
|
||||
/node_modules/get-proto/.eslintrc
|
||||
/node_modules/get-proto/.nycrc
|
||||
/node_modules/get-proto/CHANGELOG.md
|
||||
/node_modules/get-proto/index.d.ts
|
||||
/node_modules/get-proto/index.js
|
||||
/node_modules/get-proto/LICENSE
|
||||
/node_modules/get-proto/Object.getPrototypeOf.d.ts
|
||||
/node_modules/get-proto/Object.getPrototypeOf.js
|
||||
/node_modules/get-proto/package.json
|
||||
/node_modules/get-proto/README.md
|
||||
/node_modules/get-proto/Reflect.getPrototypeOf.d.ts
|
||||
/node_modules/get-proto/Reflect.getPrototypeOf.js
|
||||
/node_modules/get-proto/tsconfig.json
|
||||
/node_modules/gopd/.github/FUNDING.yml
|
||||
/node_modules/gopd/test/index.js
|
||||
/node_modules/gopd/.eslintrc
|
||||
/node_modules/gopd/CHANGELOG.md
|
||||
/node_modules/gopd/gOPD.d.ts
|
||||
/node_modules/gopd/gOPD.js
|
||||
/node_modules/gopd/index.d.ts
|
||||
/node_modules/gopd/index.js
|
||||
/node_modules/gopd/LICENSE
|
||||
/node_modules/gopd/package.json
|
||||
/node_modules/gopd/README.md
|
||||
/node_modules/gopd/tsconfig.json
|
||||
/node_modules/has-symbols/.github/FUNDING.yml
|
||||
/node_modules/has-symbols/test/shams/core-js.js
|
||||
/node_modules/has-symbols/test/shams/get-own-property-symbols.js
|
||||
/node_modules/has-symbols/test/index.js
|
||||
/node_modules/has-symbols/test/tests.js
|
||||
/node_modules/has-symbols/.eslintrc
|
||||
/node_modules/has-symbols/.nycrc
|
||||
/node_modules/has-symbols/CHANGELOG.md
|
||||
/node_modules/has-symbols/index.d.ts
|
||||
/node_modules/has-symbols/index.js
|
||||
/node_modules/has-symbols/LICENSE
|
||||
/node_modules/has-symbols/package.json
|
||||
/node_modules/has-symbols/README.md
|
||||
/node_modules/has-symbols/shams.d.ts
|
||||
/node_modules/has-symbols/shams.js
|
||||
/node_modules/has-symbols/tsconfig.json
|
||||
/node_modules/has-tostringtag/.github/FUNDING.yml
|
||||
/node_modules/has-tostringtag/test/shams/core-js.js
|
||||
/node_modules/has-tostringtag/test/shams/get-own-property-symbols.js
|
||||
/node_modules/has-tostringtag/test/index.js
|
||||
/node_modules/has-tostringtag/test/tests.js
|
||||
/node_modules/has-tostringtag/.eslintrc
|
||||
/node_modules/has-tostringtag/.nycrc
|
||||
/node_modules/has-tostringtag/CHANGELOG.md
|
||||
/node_modules/has-tostringtag/index.d.ts
|
||||
/node_modules/has-tostringtag/index.js
|
||||
/node_modules/has-tostringtag/LICENSE
|
||||
/node_modules/has-tostringtag/package.json
|
||||
/node_modules/has-tostringtag/README.md
|
||||
/node_modules/has-tostringtag/shams.d.ts
|
||||
/node_modules/has-tostringtag/shams.js
|
||||
/node_modules/has-tostringtag/tsconfig.json
|
||||
/node_modules/hasown/.github/FUNDING.yml
|
||||
/node_modules/hasown/.nycrc
|
||||
/node_modules/hasown/CHANGELOG.md
|
||||
/node_modules/hasown/index.d.ts
|
||||
/node_modules/hasown/index.js
|
||||
/node_modules/hasown/LICENSE
|
||||
/node_modules/hasown/package.json
|
||||
/node_modules/hasown/README.md
|
||||
/node_modules/hasown/tsconfig.json
|
||||
/node_modules/husky/bin.js
|
||||
/node_modules/husky/husky
|
||||
/node_modules/husky/index.d.ts
|
||||
/node_modules/husky/index.js
|
||||
/node_modules/husky/LICENSE
|
||||
/node_modules/husky/package.json
|
||||
/node_modules/husky/README.md
|
||||
/node_modules/json-bigint/lib/parse.js
|
||||
/node_modules/json-bigint/lib/stringify.js
|
||||
/node_modules/json-bigint/index.js
|
||||
/node_modules/json-bigint/LICENSE
|
||||
/node_modules/json-bigint/package.json
|
||||
/node_modules/json-bigint/README.md
|
||||
/node_modules/math-intrinsics/.github/FUNDING.yml
|
||||
/node_modules/math-intrinsics/constants/maxArrayLength.d.ts
|
||||
/node_modules/math-intrinsics/constants/maxArrayLength.js
|
||||
/node_modules/math-intrinsics/constants/maxSafeInteger.d.ts
|
||||
/node_modules/math-intrinsics/constants/maxSafeInteger.js
|
||||
/node_modules/math-intrinsics/constants/maxValue.d.ts
|
||||
/node_modules/math-intrinsics/constants/maxValue.js
|
||||
/node_modules/math-intrinsics/test/index.js
|
||||
/node_modules/math-intrinsics/.eslintrc
|
||||
/node_modules/math-intrinsics/abs.d.ts
|
||||
/node_modules/math-intrinsics/abs.js
|
||||
/node_modules/math-intrinsics/CHANGELOG.md
|
||||
/node_modules/math-intrinsics/floor.d.ts
|
||||
/node_modules/math-intrinsics/floor.js
|
||||
/node_modules/math-intrinsics/isFinite.d.ts
|
||||
/node_modules/math-intrinsics/isFinite.js
|
||||
/node_modules/math-intrinsics/isInteger.d.ts
|
||||
/node_modules/math-intrinsics/isInteger.js
|
||||
/node_modules/math-intrinsics/isNaN.d.ts
|
||||
/node_modules/math-intrinsics/isNaN.js
|
||||
/node_modules/math-intrinsics/isNegativeZero.d.ts
|
||||
/node_modules/math-intrinsics/isNegativeZero.js
|
||||
/node_modules/math-intrinsics/LICENSE
|
||||
/node_modules/math-intrinsics/max.d.ts
|
||||
/node_modules/math-intrinsics/max.js
|
||||
/node_modules/math-intrinsics/min.d.ts
|
||||
/node_modules/math-intrinsics/min.js
|
||||
/node_modules/math-intrinsics/mod.d.ts
|
||||
/node_modules/math-intrinsics/mod.js
|
||||
/node_modules/math-intrinsics/package.json
|
||||
/node_modules/math-intrinsics/pow.d.ts
|
||||
/node_modules/math-intrinsics/pow.js
|
||||
/node_modules/math-intrinsics/README.md
|
||||
/node_modules/math-intrinsics/round.d.ts
|
||||
/node_modules/math-intrinsics/round.js
|
||||
/node_modules/math-intrinsics/sign.d.ts
|
||||
/node_modules/math-intrinsics/sign.js
|
||||
/node_modules/math-intrinsics/tsconfig.json
|
||||
/node_modules/mime-db/db.json
|
||||
/node_modules/mime-db/HISTORY.md
|
||||
/node_modules/mime-db/index.js
|
||||
/node_modules/mime-db/LICENSE
|
||||
/node_modules/mime-db/package.json
|
||||
/node_modules/mime-db/README.md
|
||||
/node_modules/mime-types/HISTORY.md
|
||||
/node_modules/mime-types/index.js
|
||||
/node_modules/mime-types/LICENSE
|
||||
/node_modules/mime-types/package.json
|
||||
/node_modules/mime-types/README.md
|
||||
/node_modules/proxy-from-env/index.js
|
||||
/node_modules/proxy-from-env/LICENSE
|
||||
/node_modules/proxy-from-env/package.json
|
||||
/node_modules/proxy-from-env/README.md
|
||||
/node_modules/.package-lock.json
|
||||
171
MD/healthlink-his-promotion-article.md
Normal file
171
MD/healthlink-his-promotion-article.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# HealthLink-HIS:新一代智慧医院信息管理系统的实践与突破
|
||||
|
||||
## 引言
|
||||
|
||||
在医疗信息化高速发展的今天,一套稳定、高效、可扩展的医院信息系统(HIS)是医疗机构数字化转型的基石。HealthLink-HIS 是一款面向现代化医疗机构的综合信息管理系统,覆盖门诊、住院、手术、药房、检验检查、医保对接等核心业务场景。过去半年,我们的开发团队完成了超过 2200 次代码提交,发布了 111 项新功能,修复了 1400 余项问题,系统在技术架构、功能覆盖和工程质量三个维度实现了质的飞跃。
|
||||
|
||||
---
|
||||
|
||||
## 一、技术架构全面升级
|
||||
|
||||
### 1.1 后端:Spring Boot 4.0 + JDK 25
|
||||
|
||||
HealthLink-HIS 在业内率先完成了 **Spring Boot 2.x → 4.0.6** 的全链路升级,并同步落地 **JDK 25**,走在了 Java 生态的技术前沿。这次升级涵盖了:
|
||||
|
||||
- **Spring Boot 4.0.6** 全量适配,包括自动配置、安全框架、数据访问层的全面重构
|
||||
- **HttpClient 4.x → 5.x 完整迁移**,拥抱 Apache HttpComponents 5 的异步与 HTTP/2 能力
|
||||
- **MyBatis Plus 3.5.16** 升级,优化数据访问性能
|
||||
- **JWT 认证体系重构**,升级至 0.12.6 版本,强化令牌安全机制
|
||||
- **BouncyCastle 1.69 → 1.80** 安全加密库升级
|
||||
- **Spring Security 白名单机制完善**,适配 Springdoc OpenAPI 1.8.0 路径
|
||||
|
||||
### 1.2 前端:Vue 3 + Vite + RuoYi 3.9.2
|
||||
|
||||
前端技术栈同步完成了深度升级:
|
||||
|
||||
- **合入 RuoYi 3.9.2 前端框架**,获得更成熟的路由管理、权限控制和组件体系
|
||||
- **VxeTable 全面替代 el-table**,在数据字典管理、价格调整、医嘱列表等大数据量表格场景中,显著提升了渲染性能和交互体验
|
||||
- **lodash 迁移至 lodash-es**,支持 Tree Shaking,减小打包体积
|
||||
- **Vue 3 兼容性补丁插件**,解决了 Vite 预打包与 Vue 3 Proxy 对象的兼容性问题
|
||||
- **D3.js 体温单重绘**,使用 d3.symbol 替代自定义绘制函数,医疗图表更精准
|
||||
|
||||
### 1.3 工程化:从"能跑"到"跑得好"
|
||||
|
||||
- **引入 Flyway 数据库迁移管理**,所有表结构变更通过版本化脚本管理,告别手动 SQL
|
||||
- **配置 Husky pre-commit 钩子**,提交前自动执行前端构建检查,阻断低级错误
|
||||
- **启用 ESLint import 规则**,实时检测缺失导出,防止构建失败
|
||||
- **Playwright E2E 自动化测试方案**,覆盖门诊医生站、手术计费、并发场景等核心流程
|
||||
- **Swagger → Springdoc OpenAPI 1.8.0**,API 文档自动生成交互更流畅
|
||||
- **系统品牌重塑**:完成 openhis → healthlink-his 的全面重命名,清除历史残留
|
||||
|
||||
---
|
||||
|
||||
## 二、核心业务功能持续深化
|
||||
|
||||
### 2.1 门诊全流程闭环
|
||||
|
||||
系统围绕门诊诊疗场景,实现了从挂号预约到完诊结算的完整闭环:
|
||||
|
||||
- **预约挂号**:支持多渠道预约、签到状态流转(已预约→已签到→已完成)、退号流程优化、费用性质自动识别
|
||||
- **门诊医生站**:诊断录入(含中医诊断体系及证候关联)、检验检查申请、处方开立、手术申请、医嘱签发
|
||||
- **门诊划价收费**:自动填充、收费项目联动、结算单打印
|
||||
- **分诊排队**:队列核心功能实现,支持叫号、状态追踪、日志记录
|
||||
|
||||
### 2.2 住院管理深度拓展
|
||||
|
||||
住院业务是本轮开发的重点攻坚领域:
|
||||
|
||||
- **住院医生工作站**:临床医嘱录入(长期/临时)、医嘱校对与退回机制、诊断录入(西医+中医双体系)、手术申请与排程
|
||||
- **住院护士工作站**:医嘱执行、住院记账、发退药管理、护理记录
|
||||
- **医嘱闭环管理**:皮试确认、用药频次配置、执行科室自动匹配、医嘱退回原因反馈机制
|
||||
- **病历系统**:住院病历模板、待写病历管理、病历数据关联获取
|
||||
|
||||
### 2.3 手术管理全流程
|
||||
|
||||
- **手术申请**:支持手术单号生成、手术状态追踪、穿梭框组件优化
|
||||
- **手术安排**:重复校验、日期范围查询、费用类别管理
|
||||
- **手术计费**:门诊/住院手术费用管理,追溯术中产生的费用
|
||||
- **手术室排班**:与手术申请联动,支持排程优化
|
||||
|
||||
### 2.4 医技工作站(新增)
|
||||
|
||||
全新开发的医技工作站模块,实现检查检验功能的统一管理:
|
||||
|
||||
- 检验申请单号自动生成
|
||||
- 检验套餐管理(项目树形展开、懒加载明细、套餐价格查询)
|
||||
- 检查申请分类联动
|
||||
- 执行科室智能匹配
|
||||
- 医嘱签发与费用状态同步
|
||||
|
||||
### 2.5 会诊管理
|
||||
|
||||
- 会诊申请与审批流程
|
||||
- 会诊意见列表与自动填充
|
||||
- 参会医师确认/签名状态管理
|
||||
- 紧急程度标识与筛选
|
||||
|
||||
### 2.6 传染病报告管理(新增)
|
||||
|
||||
- 传染病报卡的新增、查询、审核全流程
|
||||
- 审核记录追溯
|
||||
- 工作单位等必填字段完善
|
||||
|
||||
---
|
||||
|
||||
## 三、用户体验显著提升
|
||||
|
||||
### 3.1 首页仪表板
|
||||
|
||||
全新设计的首页仪表板,为不同角色提供数据驾驶舱:
|
||||
|
||||
- **处方统计**:实时展示处方数据趋势
|
||||
- **收入统计**:门诊/住院收入可视化分析
|
||||
- **医生专属患者统计**:按医生维度展示患者数据
|
||||
- **菜单快捷跳转**:高频功能一键直达
|
||||
|
||||
### 3.2 交互体验优化
|
||||
|
||||
- **混合菜单布局**:优化顶部导航实现逻辑,支持多种菜单模式
|
||||
- **标签页持久化**:视图状态按用户独立存储,刷新不丢失
|
||||
- **锁屏功能**:保护医生工作站数据安全
|
||||
- **消息中心**:通知公告重构,支持优先级标识、未读状态、详情查看
|
||||
- **UI 统一规范**:全面梳理界面样式标准,按钮、表单、弹窗风格一致
|
||||
|
||||
### 3.3 打印与报表
|
||||
|
||||
- 门诊收费结算单打印配置优化
|
||||
- 住院体温单 D3.js 重绘
|
||||
- PDF 生成能力升级(iTextPDF 5.5.13.4)
|
||||
|
||||
---
|
||||
|
||||
## 四、系统安全与稳定性
|
||||
|
||||
### 4.1 安全加固
|
||||
|
||||
- JWT 认证体系重构,令牌密钥更新
|
||||
- BouncyCastle 加密库升级至 1.80
|
||||
- Security 白名单与 API 路径精细化管控
|
||||
- 登录验证码机制完善
|
||||
- 多租户数据隔离(租户 ID 全链路透传)
|
||||
|
||||
### 4.2 稳定性保障
|
||||
|
||||
- **1400+ Bug 修复**:涵盖门诊、住院、手术、药房、检验等全部模块
|
||||
- **数据一致性**:乐观锁防并发、状态流转校验、多表事务保障
|
||||
- **异常处理完善**:Promise 异常捕获、NPE 防护、空值安全处理
|
||||
- **性能优化**:数据库索引优化(分诊队列联合索引)、接口响应优化
|
||||
|
||||
---
|
||||
|
||||
## 五、多团队协同开发
|
||||
|
||||
过去半年,来自 40+ 位开发者的 2265 次提交,体现了 HealthLink-HIS 项目高效的团队协作能力:
|
||||
|
||||
- **标准化提交规范**:feat/fix/refactor/chore 前缀分类清晰
|
||||
- **发布检查清单**:建立后端发布前标准化检查流程
|
||||
- **代码质量门禁**:ESLint + Husky + 构建验证三重保障
|
||||
- **Bug 跟踪闭环**:每个 Bug 从发现、分析、修复到验证归档,形成完整记录
|
||||
|
||||
---
|
||||
|
||||
## 六、系统优势总结
|
||||
|
||||
| 维度 | 核心优势 |
|
||||
|------|---------|
|
||||
| **技术先进性** | Spring Boot 4.0 + JDK 25,走在行业技术前沿 |
|
||||
| **架构可扩展性** | DDD 领域驱动设计 + Maven 多模块,业务模块独立演进 |
|
||||
| **功能完整性** | 35+ 功能模块,覆盖门诊-住院-手术-药房-检验全流程 |
|
||||
| **工程质量** | Flyway 迁移 + E2E 测试 + CI 门禁,变更可追溯可验证 |
|
||||
| **用户体验** | Vue 3 + VxeTable 高性能表格,医生操作效率显著提升 |
|
||||
| **安全合规** | JWT + 多租户隔离 + 数据加密,满足医疗数据安全要求 |
|
||||
|
||||
---
|
||||
|
||||
## 结语
|
||||
|
||||
HealthLink-HIS 正在从一套传统的医院信息系统,演进为一个**技术领先、功能完备、持续迭代**的智慧医疗平台。过去半年的密集迭代证明,我们不仅有能力跟上技术浪潮,更有能力将前沿技术转化为实实在在的业务价值。
|
||||
|
||||
未来,我们将继续深化 AI 辅助诊疗、移动端扩展(小程序模块已就绪)、数据智能分析等方向的探索,为医疗机构提供更智能、更高效的信息化支撑。
|
||||
|
||||
**HealthLink-HIS —— 让医疗信息化更简单、更可靠、更智能。**
|
||||
36
SOUL.md
36
SOUL.md
@@ -1,36 +0,0 @@
|
||||
# SOUL.md - Who You Are
|
||||
|
||||
_You're not a chatbot. You're becoming someone._
|
||||
|
||||
## Core Truths
|
||||
|
||||
**Be genuinely helpful, not performatively helpful.** Skip the "Great question!" and "I'd be happy to help!" — just help. Actions speak louder than filler words.
|
||||
|
||||
**Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.
|
||||
|
||||
**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. _Then_ ask if you're stuck. The goal is to come back with answers, not questions.
|
||||
|
||||
**Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning).
|
||||
|
||||
**Remember you're a guest.** You have access to someone's life — their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- Private things stay private. Period.
|
||||
- When in doubt, ask before acting externally.
|
||||
- Never send half-baked replies to messaging surfaces.
|
||||
- You're not the user's voice — be careful in group chats.
|
||||
|
||||
## Vibe
|
||||
|
||||
Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
|
||||
|
||||
## Continuity
|
||||
|
||||
Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
|
||||
|
||||
If you change this file, tell the user — it's your soul, and they should know.
|
||||
|
||||
---
|
||||
|
||||
_This file is yours to evolve. As you learn who you are, update it._
|
||||
@@ -449,7 +449,7 @@ import {localPatientInfo as patientInfo} from '../../store/localPatient.js';
|
||||
import OrderGroupDrawer from '@/views/doctorstation/components/prescription/orderGroupDrawer.vue';
|
||||
import PrescriptionHistory from '@/views/doctorstation/components/prescription/prescriptionHistory.vue';
|
||||
import Decimal from 'decimal.js';
|
||||
import {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
|
||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import ApplicationFormBottomBtn from './applicationForm/applicationFormBottomBtn.vue';
|
||||
import LeaveHospitalDialog from './applicationForm/leaveHospitalDialog.vue';
|
||||
@@ -643,7 +643,7 @@ const statusOption = [
|
||||
},
|
||||
];
|
||||
|
||||
let loadingInstance = undefined;
|
||||
// loadingInstance removed - using loading ref instead
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', escKeyListener);
|
||||
});
|
||||
@@ -697,10 +697,7 @@ function refresh() {
|
||||
}
|
||||
// 获取列表信息
|
||||
function getListInfo(addNewRow) {
|
||||
loadingInstance = ElLoading.service({ fullscreen: true });
|
||||
setTimeout(() => {
|
||||
loadingInstance.close();
|
||||
}, 180);
|
||||
loading.value = true;
|
||||
isAdding.value = false;
|
||||
collapseAllExpanded();
|
||||
// 🔧 修复:先加载科室树,再处理处方数据
|
||||
@@ -712,7 +709,7 @@ function getListInfo(addNewRow) {
|
||||
getPrescriptionList(patientInfo.value.encounterId).then((res) => {
|
||||
// 等待科室树加载完成后再处理处方数据,确保 resolveOrgId 能正确匹配
|
||||
orgTreePromise.then(() => {
|
||||
loadingInstance.close();
|
||||
loading.value = false;
|
||||
prescriptionList.value = res.data
|
||||
.map((item) => {
|
||||
const parsedContent = JSON.parse(item.contentJson);
|
||||
@@ -772,7 +769,7 @@ function getListInfo(addNewRow) {
|
||||
handleAddPrescription();
|
||||
}
|
||||
});
|
||||
});
|
||||
}).catch(() => { loading.value = false; });
|
||||
getContract({ encounterId: patientInfo.value.encounterId }).then((res) => {
|
||||
contractList.value = res.data;
|
||||
});
|
||||
|
||||
@@ -252,31 +252,37 @@ const queryTemplateTree = async () => {
|
||||
// 处理节点点击,根据后台返回的路径加载组件
|
||||
const handleNodeClick = (data, node) => {
|
||||
if (node.isLeaf) {
|
||||
const newRouter = data.document?.vueRouter;
|
||||
const oldRouter = currentSelectTemplate.value?.vueRouter;
|
||||
const isSameTemplate = newRouter && oldRouter && newRouter === oldRouter;
|
||||
|
||||
// 存储当前节点数据
|
||||
currentSelectTemplate.value = data.document;
|
||||
|
||||
// 在切换组件前先重置表单数据,避免显示之前的数据
|
||||
editForm.value = {
|
||||
id: '',
|
||||
definitionId: '',
|
||||
definitionBusNo: '',
|
||||
contentJson: '',
|
||||
statusEnum: 1,
|
||||
organizationId: 0,
|
||||
encounterId: '',
|
||||
patientId: '',
|
||||
recordTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
createBy: '',
|
||||
source: '',
|
||||
};
|
||||
|
||||
// 先清空当前组件,再设置新组件,确保组件完全重新渲染
|
||||
currentComponent.value = undefined;
|
||||
|
||||
// 使用 nextTick 确保 DOM 更新后再设置新组件
|
||||
nextTick(() => {
|
||||
currentComponent.value = currentSelectTemplate.value.vueRouter;
|
||||
});
|
||||
if (isSameTemplate) {
|
||||
// 同一模板(仅患者切换):不卸载组件、不清空表单,避免闪烁
|
||||
// loading 遮罩覆盖过渡,loadLatestMedicalRecord 会原子替换表单数据
|
||||
loading.value = true;
|
||||
} else {
|
||||
// 不同模板:先清空再设置,确保组件完全重新渲染
|
||||
editForm.value = {
|
||||
id: '',
|
||||
definitionId: '',
|
||||
definitionBusNo: '',
|
||||
contentJson: '',
|
||||
statusEnum: 1,
|
||||
organizationId: 0,
|
||||
encounterId: '',
|
||||
patientId: '',
|
||||
recordTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
createBy: '',
|
||||
source: '',
|
||||
};
|
||||
currentComponent.value = undefined;
|
||||
nextTick(() => {
|
||||
currentComponent.value = newRouter;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
currentSelectTemplate.value = {
|
||||
id: '',
|
||||
@@ -624,8 +630,8 @@ const selectOutpatientMedicalRecordTemplate = async () => {
|
||||
// 加载最新的病历数据并回显
|
||||
const loadLatestMedicalRecord = async () => {
|
||||
if (!patientInfo.value?.encounterId || !currentSelectTemplate.value.id) return;
|
||||
editForm.value.id = '';
|
||||
loading.value = true;
|
||||
editForm.value.id = '';
|
||||
try {
|
||||
// 获取患者的历史病历记录
|
||||
const res = await getRecordByEncounterIdList({
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# Bug #444 分析报告
|
||||
|
||||
## Bug 描述
|
||||
生成临时医嘱界面,"已引用计费药品"列表未正常显示药品详细名称信息。具体表现为:
|
||||
- 列表中出现了"小腿烧伤扩创交腿皮瓣修复术"(属于手术诊疗项目)
|
||||
- 列表中出现了"心脏彩色多普勒超声"(属于检查/诊疗项目)
|
||||
- 非药品类计费信息错误地混入"已引用计费药品"列表
|
||||
|
||||
## 根因定位
|
||||
**文件**: `healthlink-his-ui/src/views/surgicalschedule/index.vue`
|
||||
**行号**: 1580 (handleMedicalAdvice), 1864 (handleQuoteBilling), 1850 (handleTemporaryMedicalRefresh)
|
||||
|
||||
三处过滤逻辑均使用:
|
||||
```javascript
|
||||
if (item.adviceType !== 1) return false;
|
||||
```
|
||||
|
||||
**问题1(主因)**: `adviceType` 字段命名兼容不完整。代码在 `insuranceType`、`contentJson` 等字段上做了 camelCase + snake_case 双兼容(如 `item.insuranceType || item.insurance_type`),但 `adviceType` 只检查了 camelCase。若后端返回 snake_case 数据(`advice_type`),`item.adviceType` 为 `undefined`,`undefined !== 1` 为 `true`,导致所有非药品项目全部放行。
|
||||
|
||||
**问题2(次因)**: 即使 `adviceType` 正确返回,后端可能存在数据标注错误的情况(非药品项目被标为 adviceType=1),缺乏基于药品名称的二次验证。
|
||||
|
||||
## 修复方案
|
||||
1. `adviceType` 检查增加 snake_case 回退:`const at = item.adviceType ?? item.advice_type; if (at !== 1) return false;`
|
||||
2. 增加药品名称关键字二次过滤:排除名称中包含"术"、"检查"、"超声"、"多普勒"等关键词的非药品项目
|
||||
|
||||
## 验收标准
|
||||
1. "已引用计费药品"列表中只显示药品类项目
|
||||
2. 不显示手术诊疗项目(如"小腿烧伤扩创交腿皮瓣修复术")
|
||||
3. 不显示检查项目(如"心脏彩色多普勒超声")
|
||||
4. 药品名称正常显示
|
||||
@@ -1,153 +0,0 @@
|
||||
# Bug #445 分析报告
|
||||
|
||||
## Bug 描述
|
||||
在"门诊手术临时医嘱"界面,生成医嘱成功后,已生成的计费项目仍然保留在"一、已引用计费药品(待生成医嘱)"列表中,导致上下两个列表数据完全一致,用户无法区分哪些已处理、哪些未处理。
|
||||
|
||||
## 根因定位
|
||||
|
||||
### 核心问题:`handleTemporaryMedicalSubmit` 中过滤逻辑匹配字段路径错误
|
||||
|
||||
**文件**: `healthlink-his-ui/src/views/surgicalschedule/index.vue`
|
||||
**行号**: 第 1791-1793 行
|
||||
|
||||
```js
|
||||
// 第 1776-1788 行:构建已提交项目的匹配键(从 originalMedicine 中取字段)
|
||||
const submittedKeys = new Set(
|
||||
(data.temporaryAdvices || [])
|
||||
.map(a => {
|
||||
const om = a.originalMedicine || {}
|
||||
const name = om.medicineName || om.adviceName || om.advice_name || a.adviceName || ''
|
||||
const spec = om.specification || om.volume || ''
|
||||
const qty = om.quantity || 0
|
||||
return `${name}|||${spec}|||${qty}`
|
||||
})
|
||||
.filter(k => k !== '|||0')
|
||||
)
|
||||
|
||||
// 第 1791-1794 行:过滤待生成列表(错误:直接从顶层取字段)
|
||||
temporaryBillingMedicines.value = (temporaryBillingMedicines.value || []).filter(m => {
|
||||
const key = `${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity || 0}` // ❌ BUG: 字段路径错误
|
||||
return !submittedKeys.has(key)
|
||||
})
|
||||
```
|
||||
|
||||
### 为什么匹配不上?
|
||||
|
||||
`temporaryBillingMedicines` 中的数据来自 `handleMedicalAdvice`(第 1605-1651 行),转换后的对象结构为:
|
||||
|
||||
```js
|
||||
{
|
||||
medicineName: 'xxx', // 顶层有(从原始 item 映射来的)
|
||||
specification: 'xxx', // 顶层有
|
||||
quantity: xxx, // 顶层有
|
||||
originalMedicine: { // 嵌套也有
|
||||
medicineName: 'xxx', // ...spread 复制了所有字段
|
||||
specification: 'xxx',
|
||||
quantity: xxx,
|
||||
encounterId: xxx
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
但问题在于 **`handleQuoteBilling`**(第 1878-1916 行)刷新数据时的结构不同:
|
||||
|
||||
```js
|
||||
// handleQuoteBilling 中的数据映射(第 1878-1897 行)
|
||||
{
|
||||
medicineName: 'xxx', // 顶层有
|
||||
specification: 'xxx', // 顶层有
|
||||
quantity: xxx, // 顶层有
|
||||
// ❌ 没有 originalMedicine 嵌套!
|
||||
}
|
||||
```
|
||||
|
||||
等等,让我再仔细看...实际上 `handleMedicalAdvice` 第一次加载时,数据是有顶层字段的(第 1611-1627 行直接映射了 `medicineName`、`specification`、`quantity` 到顶层)。所以匹配键应该能对上。
|
||||
|
||||
让我重新审视...
|
||||
|
||||
### 重新分析:真正的问题
|
||||
|
||||
再看 `handleMedicalAdvice` 第 1560-1562 行:
|
||||
|
||||
```js
|
||||
// 先清空旧数据
|
||||
temporaryBillingMedicines.value = []
|
||||
temporaryAdvices.value = []
|
||||
```
|
||||
|
||||
**这是问题的关键!** 当用户第二次打开同一个手术记录的医嘱界面时:
|
||||
|
||||
1. `isSameEncounter` 检查(第 1543-1556 行):
|
||||
- `temporaryAdvices.value.length > 0` → 此时已被清空为 0,所以 `isSameEncounter = false`
|
||||
- 不会走 early return
|
||||
|
||||
2. 第 1560-1562 行清空了 `temporaryAdvices`
|
||||
3. 调用 `getPrescriptionList` 从后端拉取最新数据
|
||||
4. 第 1587-1588 行过滤:`if (item.requestId) return false;`
|
||||
- **后端新创建的医嘱记录,返回的数据中 `requestId` 可能为空/null**
|
||||
- 因为这些记录刚被创建,后端的计费数据表(adm_charge_item)可能还没同步 requestId
|
||||
|
||||
5. 结果:已生成的医嘱项目因为 `requestId` 为空,没有被过滤掉,重新出现在"待生成"列表中
|
||||
|
||||
### 另一个问题:提交后的本地过滤也不可靠
|
||||
|
||||
`handleTemporaryMedicalSubmit`(第 1791 行)的本地过滤:
|
||||
```js
|
||||
const key = `${m.medicineName || ''}|||${m.specification || ''}|||${m.quantity || 0}`
|
||||
```
|
||||
|
||||
这里的 `m` 是 `temporaryBillingMedicines` 中的对象。在 `handleMedicalAdvice` 首次加载时(第 1605-1651 行),确实映射了顶层字段,所以这个匹配**应该能工作**。
|
||||
|
||||
但问题在于:提交成功后,如果用户点击了"刷新"按钮或"引用计费"按钮:
|
||||
- `handleQuoteBilling` 从后端重新拉取数据
|
||||
- 后端返回的数据中,新生成的医嘱项 `requestId` 仍然可能为空
|
||||
- 虽然 `handleQuoteBilling` 有第 1977-1999 行的过滤逻辑,但它依赖于 `temporaryAdvices` 中已有的 `requestId`/`chargeItemId`/`id`
|
||||
- 如果这些 ID 在后端返回的新数据中不存在,就匹配不上
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 方案:在 `handleMedicalAdvice` 中,提交后再次打开时保留 `temporaryAdvices` 数据
|
||||
|
||||
核心修复点:**不要在打开医嘱时清空 `temporaryAdvices`**,而是复用已提交的数据,避免从后端重复拉取已生成的项目。
|
||||
|
||||
具体修改 `handleMedicalAdvice` 函数:
|
||||
|
||||
1. 将清空数据的逻辑移到 `isSameEncounter` 检查**之后**,并且只在非同一 encounter 时才清空
|
||||
2. 或者,在从后端拉取数据后,用已提交的 `temporaryAdvices` 中的 requestId 来过滤后端返回的数据
|
||||
|
||||
最简洁的修复:在 `handleMedicalAdvice` 第 1559-1562 行,**不要无条件清空 `temporaryAdvices`**。改为:
|
||||
|
||||
```js
|
||||
// 修复前:
|
||||
temporaryBillingMedicines.value = []
|
||||
temporaryAdvices.value = []
|
||||
|
||||
// 修复后:
|
||||
temporaryBillingMedicines.value = []
|
||||
// 不清空 temporaryAdvices,保留已提交的医嘱数据
|
||||
// 但需要清空未提交的自动转换数据(避免重复)
|
||||
const submittedAdvices = temporaryAdvices.value.filter(a => a.originalMedicine?.requestId)
|
||||
temporaryAdvices.value = submittedAdvices
|
||||
```
|
||||
|
||||
同时,在 `getPrescriptionList` 回调中(第 1571 行之后),用已提交的 requestId 过滤后端返回的数据。
|
||||
|
||||
## 修复结果
|
||||
|
||||
### 实际根因
|
||||
`handleQuoteBilling` 函数中:
|
||||
1. **第1856行**:在调用 `getPrescriptionList` 之前先清空了 `temporaryAdvices.value = []`
|
||||
2. **第1997-2019行(旧代码)**:ID 匹配过滤逻辑依赖已被清空的 `temporaryAdvices.value`,因此过滤形同虚设
|
||||
3. 即使 `temporaryAdvices` 未被清空,ID 匹配也不可靠(新生成的医嘱可能没有 `requestId`/`chargeItemId`/`id`)
|
||||
|
||||
### 修复方案
|
||||
1. 在清空 `temporaryAdvices` **之前**,提取已提交项目的复合键(名称+规格+数量)保存到 `submittedKeysBeforeClear`
|
||||
2. 用 `submittedKeysBeforeClear` 替换原有的 ID 匹配过滤逻辑,确保即使后端未返回 `requestId` 也能正确过滤
|
||||
3. 复合键匹配策略与 `handleTemporaryMedicalSubmit` 中使用的策略一致
|
||||
|
||||
### 修改文件
|
||||
- `healthlink-his-ui/src/views/surgicalschedule/index.vue`
|
||||
- 第1853-1864行:新增 `submittedKeysBeforeClear` 提取逻辑
|
||||
- 第1997-2004行:替换 ID 匹配为复合键匹配
|
||||
|
||||
### 修复结果:✅ 成功,~20行改动(+20/-21)
|
||||
@@ -1,33 +0,0 @@
|
||||
## Bug #470: 住院医生工作站-手术申请单加载手术项目耗时过长
|
||||
|
||||
### 根因分析
|
||||
|
||||
点击"手术"按钮后,前端调用 `GET /doctor-station/advice/surgery-page` 加载手术项目列表。
|
||||
|
||||
**性能瓶颈链路**:
|
||||
1. 后端 `DoctorStationAdviceAppServiceImpl.getSurgeryPage()` 使用 MyBatis Plus 分页查询
|
||||
2. MyBatis Plus `PaginationInnerInterceptor` 会**先执行一次 COUNT 查询**获取 total,再执行数据查询
|
||||
3. COUNT 查询需要扫描 `wor_activity_definition` 全表 10,102 条手术项目记录(~4ms)
|
||||
4. 数据查询(LIMIT 100)仅需 0.3ms,但 COUNT 查询是主要开销来源
|
||||
5. 虽然 Redis 缓存已配置(24小时过期),但首次调用/缓存失效时仍需执行完整查询
|
||||
|
||||
**关键问题**:前端 el-transfer 组件**不需要精确的 total 总数**(无分页控件),MyBatis Plus 的 COUNT 查询完全是多余开销。
|
||||
|
||||
### 修复方案
|
||||
|
||||
将手术项目查询从 MyBatis Plus 分页模式改为直接 LIMIT/OFFSET 查询:
|
||||
|
||||
1. **Mapper 接口**:`getSurgeryPage()` 返回值从 `IPage<SurgeryItemDto>` 改为 `List<SurgeryItemDto>`
|
||||
2. **XML SQL**:添加 `LIMIT #{page.size} OFFSET ${(page.current - 1) * page.size}`
|
||||
3. **Service 层**:手动构造 `Page` 对象,`total` 设为 `records.size()`(前端 el-transfer 只用作显示)
|
||||
4. **Controller 层**:无需修改,仍返回 `R.ok(IPage)`
|
||||
|
||||
**效果**:消除了 COUNT 查询开销,首次加载从 ~5ms 降至 ~0.3ms(数据库层面),叠加 Redis 缓存后后续调用几乎瞬时。
|
||||
|
||||
### 修改文件
|
||||
|
||||
- `healthlink-his-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationAdviceAppMapper.java`
|
||||
- `healthlink-his-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml`
|
||||
- `healthlink-his-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java`
|
||||
|
||||
修复结果:✅ 成功,~15行改动
|
||||
Reference in New Issue
Block a user