From c0fe67ff19c0f17bac8600103ecbd62d3d9bf864 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 28 Feb 2024 08:32:35 +0800 Subject: [PATCH 01/64] =?UTF-8?q?refactor:=20=E5=8D=8E=E4=BF=A1OA=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E6=9E=B6=E6=9E=84=E6=90=AD=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cz-config.js | 94 + .dockerignore | 46 + .env | 32 + .env.development | 35 + .env.production | 37 + .gitattributes | 11 + .gitignore | 77 + .husky/commit-msg | 4 + .husky/pre-commit | 7 + .npmrc | 6 + .versionrc.js | 19 + .vscode/launch.json | 17 + .vscode/settings.json | 41 + CHANGELOG.md | 0 Dockerfile | 53 + LICENSE | 21 + README.md | 112 + commitlint.config.cjs | 24 + deploy/sql/nest_admin.sql | 744 + deploy/web/default.conf | 54 + docker-compose.yml | 64 + ecosystem.config.js | 22 + eslint.config.js | 36 + nest-cli.json | 17 + package.json | 174 + pnpm-lock.yaml | 12758 ++++++++++++++++ scripts/genEnvTypes.ts | 49 + scripts/resetScheduler.ts | 26 + src/app.module.ts | 67 + src/assets/templates/verification-code-zh.hbs | 4 + src/assets/templates/verification-code.hbs | 5 + src/common/adapters/fastify.adapter.ts | 46 + src/common/adapters/socket.adapter.ts | 26 + src/common/decorators/api-result.decorator.ts | 83 + src/common/decorators/bypass.decorator.ts | 10 + src/common/decorators/cookie.decorator.ts | 8 + src/common/decorators/cron-once.decorator.ts | 19 + src/common/decorators/field.decorator.ts | 137 + src/common/decorators/http.decorator.ts | 22 + src/common/decorators/id-param.decorator.ts | 7 + .../decorators/idempotence.decorator.ts | 15 + src/common/decorators/swagger.decorator.ts | 11 + src/common/decorators/transform.decorator.ts | 146 + src/common/dto/cursor.dto.ts | 26 + src/common/dto/delete.dto.ts | 8 + src/common/dto/id.dto.ts | 6 + src/common/dto/pager.dto.ts | 45 + src/common/entity/common.entity.ts | 55 + src/common/exceptions/biz.exception.ts | 40 + src/common/exceptions/not-found.exception.ts | 10 + src/common/exceptions/socket.exception.ts | 38 + src/common/filters/any-exception.filter.ts | 89 + .../interceptors/idempotence.interceptor.ts | 148 + .../interceptors/logging.interceptor.ts | 35 + .../interceptors/timeout.interceptor.ts | 26 + .../interceptors/transform.interceptor.ts | 46 + src/common/model/response.model.ts | 42 + src/common/pipes/parse-int.pipe.ts | 18 + src/config/app.config.ts | 20 + src/config/database.config.ts | 40 + src/config/index.ts | 37 + src/config/mailer.config.ts | 18 + src/config/oss.config.ts | 32 + src/config/redis.config.ts | 14 + src/config/security.config.ts | 14 + src/config/swagger.config.ts | 12 + src/constants/cache.constant.ts | 8 + src/constants/error-code.constant.ts | 49 + src/constants/event-bus.constant.ts | 4 + src/constants/oss.constant.ts | 8 + src/constants/response.constant.ts | 15 + src/constants/system.constant.ts | 6 + src/global/env.ts | 62 + src/helper/catchError.ts | 5 + src/helper/crud/base.service.ts | 40 + src/helper/crud/crud.factory.ts | 81 + src/helper/genRedisKey.ts | 19 + src/helper/paginate/create-pagination.ts | 27 + src/helper/paginate/index.ts | 147 + src/helper/paginate/interface.ts | 27 + src/helper/paginate/pagination.ts | 14 + src/main.ts | 101 + src/migrations/1707996695540-initData.ts | 15 + src/modules/auth/auth.constant.ts | 26 + src/modules/auth/auth.controller.ts | 47 + src/modules/auth/auth.module.ts | 64 + src/modules/auth/auth.service.ts | 156 + .../auth/controllers/account.controller.ts | 75 + .../auth/controllers/captcha.controller.ts | 50 + .../auth/controllers/email.controller.ts | 41 + .../auth/decorators/allow-anon.decorator.ts | 8 + .../auth/decorators/auth-user.decorator.ts | 17 + .../auth/decorators/permission.decorator.ts | 58 + .../auth/decorators/public.decorator.ts | 8 + .../auth/decorators/resource.decorator.ts | 12 + src/modules/auth/dto/account.dto.ts | 64 + src/modules/auth/dto/auth.dto.ts | 43 + src/modules/auth/dto/captcha.dto.ts | 53 + .../auth/entities/access-token.entity.ts | 40 + .../auth/entities/refresh-token.entity.ts | 32 + src/modules/auth/guards/jwt-auth.guard.ts | 105 + src/modules/auth/guards/local.guard.ts | 11 + src/modules/auth/guards/rbac.guard.ts | 76 + src/modules/auth/guards/resource.guard.ts | 87 + src/modules/auth/models/auth.model.ts | 14 + src/modules/auth/services/captcha.service.ts | 40 + src/modules/auth/services/token.service.ts | 160 + src/modules/auth/strategies/jwt.strategy.ts | 24 + src/modules/auth/strategies/local.strategy.ts | 24 + src/modules/health/health.controller.ts | 71 + src/modules/health/health.module.ts | 11 + src/modules/netdisk/manager/manage.class.ts | 68 + .../netdisk/manager/manage.controller.ts | 153 + src/modules/netdisk/manager/manage.dto.ts | 162 + src/modules/netdisk/manager/manage.service.ts | 930 ++ src/modules/netdisk/netdisk.module.ts | 22 + .../netdisk/overview/overview.controller.ts | 49 + src/modules/netdisk/overview/overview.dto.ts | 53 + .../netdisk/overview/overview.service.ts | 156 + src/modules/sse/sse.controller.ts | 54 + src/modules/sse/sse.module.ts | 12 + src/modules/sse/sse.service.ts | 85 + src/modules/system/dept/dept.controller.ts | 83 + src/modules/system/dept/dept.dto.ts | 70 + src/modules/system/dept/dept.entity.ts | 36 + src/modules/system/dept/dept.module.ts | 19 + src/modules/system/dept/dept.service.ts | 134 + .../system/dict-item/dict-item.controller.ts | 68 + src/modules/system/dict-item/dict-item.dto.ts | 48 + .../system/dict-item/dict-item.entity.ts | 32 + .../system/dict-item/dict-item.module.ts | 16 + .../system/dict-item/dict-item.service.ts | 97 + .../system/dict-type/dict-type.controller.ts | 76 + src/modules/system/dict-type/dict-type.dto.ts | 40 + .../system/dict-type/dict-type.entity.ts | 23 + .../system/dict-type/dict-type.module.ts | 16 + .../system/dict-type/dict-type.service.ts | 84 + src/modules/system/log/dto/log.dto.ts | 57 + .../system/log/entities/captcha-log.entity.ts | 23 + src/modules/system/log/entities/index.ts | 0 .../system/log/entities/login-log.entity.ts | 29 + .../system/log/entities/task-log.entity.ts | 25 + src/modules/system/log/log.controller.ts | 64 + src/modules/system/log/log.module.ts | 25 + src/modules/system/log/models/log.model.ts | 47 + .../log/services/captcha-log.service.ts | 50 + .../system/log/services/login-log.service.ts | 100 + .../system/log/services/task-log.service.ts | 52 + src/modules/system/menu/menu.controller.ts | 104 + src/modules/system/menu/menu.dto.ts | 88 + src/modules/system/menu/menu.entity.ts | 55 + src/modules/system/menu/menu.model.ts | 8 + src/modules/system/menu/menu.module.ts | 24 + src/modules/system/menu/menu.service.ts | 259 + .../system/online/online.controller.ts | 46 + src/modules/system/online/online.dto.ts | 8 + src/modules/system/online/online.model.ts | 27 + src/modules/system/online/online.module.ts | 27 + src/modules/system/online/online.service.ts | 134 + .../param-config/param-config.controller.ts | 65 + .../system/param-config/param-config.dto.ts | 31 + .../param-config/param-config.entity.ts | 23 + .../param-config/param-config.module.ts | 16 + .../param-config/param-config.service.ts | 91 + src/modules/system/role/role.controller.ts | 85 + src/modules/system/role/role.dto.ts | 38 + src/modules/system/role/role.entity.ts | 43 + src/modules/system/role/role.model.ts | 8 + src/modules/system/role/role.module.ts | 23 + src/modules/system/role/role.service.ts | 152 + src/modules/system/serve/serve.controller.ts | 31 + src/modules/system/serve/serve.model.ts | 86 + src/modules/system/serve/serve.module.ts | 16 + src/modules/system/serve/serve.service.ts | 63 + src/modules/system/system.module.ts | 45 + src/modules/system/task/constant.ts | 12 + src/modules/system/task/task.controller.ts | 98 + src/modules/system/task/task.dto.ts | 105 + src/modules/system/task/task.entity.ts | 55 + src/modules/system/task/task.module.ts | 37 + src/modules/system/task/task.processor.ts | 44 + src/modules/system/task/task.service.ts | 369 + src/modules/system/task/task.ts | 2 + src/modules/tasks/jobs/email.job.ts | 29 + src/modules/tasks/jobs/http-request.job.ts | 32 + src/modules/tasks/jobs/log-clear.job.ts | 26 + src/modules/tasks/mission.decorator.ts | 8 + src/modules/tasks/tasks.module.ts | 46 + src/modules/todo/todo.controller.ts | 79 + src/modules/todo/todo.dto.ts | 14 + src/modules/todo/todo.entity.ts | 20 + src/modules/todo/todo.module.ts | 16 + src/modules/todo/todo.service.ts | 46 + src/modules/tools/email/email.controller.ts | 22 + src/modules/tools/email/email.dto.ts | 19 + src/modules/tools/email/email.module.ts | 9 + .../tools/storage/storage.controller.ts | 41 + src/modules/tools/storage/storage.dto.ts | 68 + src/modules/tools/storage/storage.entity.ts | 40 + src/modules/tools/storage/storage.modal.ts | 27 + src/modules/tools/storage/storage.module.ts | 18 + src/modules/tools/storage/storage.service.ts | 98 + src/modules/tools/tools.module.ts | 21 + src/modules/tools/upload/file.constraint.ts | 64 + src/modules/tools/upload/upload.controller.ts | 53 + src/modules/tools/upload/upload.dto.ts | 27 + src/modules/tools/upload/upload.module.ts | 16 + src/modules/tools/upload/upload.service.ts | 53 + src/modules/user/constant.ts | 4 + src/modules/user/dto/password.dto.ts | 39 + src/modules/user/dto/user.dto.ts | 98 + src/modules/user/user.controller.ts | 82 + src/modules/user/user.entity.ts | 69 + src/modules/user/user.model.ts | 21 + src/modules/user/user.module.ts | 26 + src/modules/user/user.service.ts | 372 + src/repl.ts | 12 + src/setup-swagger.ts | 44 + .../constraints/entity-exist.constraint.ts | 85 + .../database/constraints/unique.constraint.ts | 97 + src/shared/database/database.module.ts | 51 + src/shared/database/typeorm-logger.ts | 118 + src/shared/helper/cron.service.ts | 48 + src/shared/helper/helper.module.ts | 17 + src/shared/helper/qq.service.ts | 19 + src/shared/logger/logger.module.ts | 15 + src/shared/logger/logger.service.ts | 115 + src/shared/mailer/mailer.module.ts | 42 + src/shared/mailer/mailer.service.ts | 151 + src/shared/redis/cache.service.ts | 68 + src/shared/redis/redis-subpub.ts | 68 + src/shared/redis/redis.constant.ts | 1 + src/shared/redis/redis.module.ts | 60 + src/shared/redis/subpub.service.ts | 21 + src/shared/shared.module.ts | 49 + src/socket/base.gateway.ts | 33 + src/socket/business-event.constant.ts | 11 + src/socket/events/admin.gateway.ts | 37 + src/socket/events/web.gateway.ts | 36 + src/socket/shared/auth.gateway.ts | 120 + src/socket/socket.module.ts | 16 + src/utils/captcha.util.ts | 19 + src/utils/crypto.util.ts | 30 + src/utils/date.util.ts | 23 + src/utils/file.util.ts | 89 + src/utils/index.ts | 11 + src/utils/ip.util.ts | 66 + src/utils/is.util.ts | 4 + src/utils/list2tree.util.ts | 86 + src/utils/permission.util.ts | 158 + src/utils/redis.util.ts | 10 + src/utils/schedule.util.ts | 99 + src/utils/tool.util.ts | 60 + tsconfig.build.json | 4 + tsconfig.json | 27 + types/global.d.ts | 21 + types/module.d.ts | 7 + types/utils.d.ts | 104 + vercel.json | 22 + wait-for-it.sh | 182 + 260 files changed, 27385 insertions(+) create mode 100644 .cz-config.js create mode 100644 .dockerignore create mode 100644 .env create mode 100644 .env.development create mode 100644 .env.production create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .husky/commit-msg create mode 100644 .husky/pre-commit create mode 100644 .npmrc create mode 100644 .versionrc.js create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 CHANGELOG.md create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 commitlint.config.cjs create mode 100644 deploy/sql/nest_admin.sql create mode 100644 deploy/web/default.conf create mode 100644 docker-compose.yml create mode 100644 ecosystem.config.js create mode 100644 eslint.config.js create mode 100644 nest-cli.json create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 scripts/genEnvTypes.ts create mode 100644 scripts/resetScheduler.ts create mode 100644 src/app.module.ts create mode 100644 src/assets/templates/verification-code-zh.hbs create mode 100644 src/assets/templates/verification-code.hbs create mode 100644 src/common/adapters/fastify.adapter.ts create mode 100644 src/common/adapters/socket.adapter.ts create mode 100644 src/common/decorators/api-result.decorator.ts create mode 100644 src/common/decorators/bypass.decorator.ts create mode 100644 src/common/decorators/cookie.decorator.ts create mode 100644 src/common/decorators/cron-once.decorator.ts create mode 100644 src/common/decorators/field.decorator.ts create mode 100644 src/common/decorators/http.decorator.ts create mode 100644 src/common/decorators/id-param.decorator.ts create mode 100644 src/common/decorators/idempotence.decorator.ts create mode 100644 src/common/decorators/swagger.decorator.ts create mode 100644 src/common/decorators/transform.decorator.ts create mode 100644 src/common/dto/cursor.dto.ts create mode 100644 src/common/dto/delete.dto.ts create mode 100644 src/common/dto/id.dto.ts create mode 100644 src/common/dto/pager.dto.ts create mode 100644 src/common/entity/common.entity.ts create mode 100644 src/common/exceptions/biz.exception.ts create mode 100644 src/common/exceptions/not-found.exception.ts create mode 100644 src/common/exceptions/socket.exception.ts create mode 100644 src/common/filters/any-exception.filter.ts create mode 100644 src/common/interceptors/idempotence.interceptor.ts create mode 100644 src/common/interceptors/logging.interceptor.ts create mode 100644 src/common/interceptors/timeout.interceptor.ts create mode 100644 src/common/interceptors/transform.interceptor.ts create mode 100644 src/common/model/response.model.ts create mode 100644 src/common/pipes/parse-int.pipe.ts create mode 100644 src/config/app.config.ts create mode 100644 src/config/database.config.ts create mode 100644 src/config/index.ts create mode 100644 src/config/mailer.config.ts create mode 100644 src/config/oss.config.ts create mode 100644 src/config/redis.config.ts create mode 100644 src/config/security.config.ts create mode 100644 src/config/swagger.config.ts create mode 100644 src/constants/cache.constant.ts create mode 100644 src/constants/error-code.constant.ts create mode 100644 src/constants/event-bus.constant.ts create mode 100644 src/constants/oss.constant.ts create mode 100644 src/constants/response.constant.ts create mode 100644 src/constants/system.constant.ts create mode 100644 src/global/env.ts create mode 100644 src/helper/catchError.ts create mode 100644 src/helper/crud/base.service.ts create mode 100644 src/helper/crud/crud.factory.ts create mode 100644 src/helper/genRedisKey.ts create mode 100644 src/helper/paginate/create-pagination.ts create mode 100644 src/helper/paginate/index.ts create mode 100644 src/helper/paginate/interface.ts create mode 100644 src/helper/paginate/pagination.ts create mode 100644 src/main.ts create mode 100644 src/migrations/1707996695540-initData.ts create mode 100644 src/modules/auth/auth.constant.ts create mode 100644 src/modules/auth/auth.controller.ts create mode 100644 src/modules/auth/auth.module.ts create mode 100644 src/modules/auth/auth.service.ts create mode 100644 src/modules/auth/controllers/account.controller.ts create mode 100644 src/modules/auth/controllers/captcha.controller.ts create mode 100644 src/modules/auth/controllers/email.controller.ts create mode 100644 src/modules/auth/decorators/allow-anon.decorator.ts create mode 100644 src/modules/auth/decorators/auth-user.decorator.ts create mode 100644 src/modules/auth/decorators/permission.decorator.ts create mode 100644 src/modules/auth/decorators/public.decorator.ts create mode 100644 src/modules/auth/decorators/resource.decorator.ts create mode 100644 src/modules/auth/dto/account.dto.ts create mode 100644 src/modules/auth/dto/auth.dto.ts create mode 100644 src/modules/auth/dto/captcha.dto.ts create mode 100644 src/modules/auth/entities/access-token.entity.ts create mode 100644 src/modules/auth/entities/refresh-token.entity.ts create mode 100644 src/modules/auth/guards/jwt-auth.guard.ts create mode 100644 src/modules/auth/guards/local.guard.ts create mode 100644 src/modules/auth/guards/rbac.guard.ts create mode 100644 src/modules/auth/guards/resource.guard.ts create mode 100644 src/modules/auth/models/auth.model.ts create mode 100644 src/modules/auth/services/captcha.service.ts create mode 100644 src/modules/auth/services/token.service.ts create mode 100644 src/modules/auth/strategies/jwt.strategy.ts create mode 100644 src/modules/auth/strategies/local.strategy.ts create mode 100644 src/modules/health/health.controller.ts create mode 100644 src/modules/health/health.module.ts create mode 100644 src/modules/netdisk/manager/manage.class.ts create mode 100644 src/modules/netdisk/manager/manage.controller.ts create mode 100644 src/modules/netdisk/manager/manage.dto.ts create mode 100644 src/modules/netdisk/manager/manage.service.ts create mode 100644 src/modules/netdisk/netdisk.module.ts create mode 100644 src/modules/netdisk/overview/overview.controller.ts create mode 100644 src/modules/netdisk/overview/overview.dto.ts create mode 100644 src/modules/netdisk/overview/overview.service.ts create mode 100644 src/modules/sse/sse.controller.ts create mode 100644 src/modules/sse/sse.module.ts create mode 100644 src/modules/sse/sse.service.ts create mode 100644 src/modules/system/dept/dept.controller.ts create mode 100644 src/modules/system/dept/dept.dto.ts create mode 100644 src/modules/system/dept/dept.entity.ts create mode 100644 src/modules/system/dept/dept.module.ts create mode 100644 src/modules/system/dept/dept.service.ts create mode 100644 src/modules/system/dict-item/dict-item.controller.ts create mode 100644 src/modules/system/dict-item/dict-item.dto.ts create mode 100644 src/modules/system/dict-item/dict-item.entity.ts create mode 100644 src/modules/system/dict-item/dict-item.module.ts create mode 100644 src/modules/system/dict-item/dict-item.service.ts create mode 100644 src/modules/system/dict-type/dict-type.controller.ts create mode 100644 src/modules/system/dict-type/dict-type.dto.ts create mode 100644 src/modules/system/dict-type/dict-type.entity.ts create mode 100644 src/modules/system/dict-type/dict-type.module.ts create mode 100644 src/modules/system/dict-type/dict-type.service.ts create mode 100644 src/modules/system/log/dto/log.dto.ts create mode 100644 src/modules/system/log/entities/captcha-log.entity.ts create mode 100644 src/modules/system/log/entities/index.ts create mode 100644 src/modules/system/log/entities/login-log.entity.ts create mode 100644 src/modules/system/log/entities/task-log.entity.ts create mode 100644 src/modules/system/log/log.controller.ts create mode 100644 src/modules/system/log/log.module.ts create mode 100644 src/modules/system/log/models/log.model.ts create mode 100644 src/modules/system/log/services/captcha-log.service.ts create mode 100644 src/modules/system/log/services/login-log.service.ts create mode 100644 src/modules/system/log/services/task-log.service.ts create mode 100644 src/modules/system/menu/menu.controller.ts create mode 100644 src/modules/system/menu/menu.dto.ts create mode 100644 src/modules/system/menu/menu.entity.ts create mode 100644 src/modules/system/menu/menu.model.ts create mode 100644 src/modules/system/menu/menu.module.ts create mode 100644 src/modules/system/menu/menu.service.ts create mode 100644 src/modules/system/online/online.controller.ts create mode 100644 src/modules/system/online/online.dto.ts create mode 100644 src/modules/system/online/online.model.ts create mode 100644 src/modules/system/online/online.module.ts create mode 100644 src/modules/system/online/online.service.ts create mode 100644 src/modules/system/param-config/param-config.controller.ts create mode 100644 src/modules/system/param-config/param-config.dto.ts create mode 100644 src/modules/system/param-config/param-config.entity.ts create mode 100644 src/modules/system/param-config/param-config.module.ts create mode 100644 src/modules/system/param-config/param-config.service.ts create mode 100644 src/modules/system/role/role.controller.ts create mode 100644 src/modules/system/role/role.dto.ts create mode 100644 src/modules/system/role/role.entity.ts create mode 100644 src/modules/system/role/role.model.ts create mode 100644 src/modules/system/role/role.module.ts create mode 100644 src/modules/system/role/role.service.ts create mode 100644 src/modules/system/serve/serve.controller.ts create mode 100644 src/modules/system/serve/serve.model.ts create mode 100644 src/modules/system/serve/serve.module.ts create mode 100644 src/modules/system/serve/serve.service.ts create mode 100644 src/modules/system/system.module.ts create mode 100644 src/modules/system/task/constant.ts create mode 100644 src/modules/system/task/task.controller.ts create mode 100644 src/modules/system/task/task.dto.ts create mode 100644 src/modules/system/task/task.entity.ts create mode 100644 src/modules/system/task/task.module.ts create mode 100644 src/modules/system/task/task.processor.ts create mode 100644 src/modules/system/task/task.service.ts create mode 100644 src/modules/system/task/task.ts create mode 100644 src/modules/tasks/jobs/email.job.ts create mode 100644 src/modules/tasks/jobs/http-request.job.ts create mode 100644 src/modules/tasks/jobs/log-clear.job.ts create mode 100644 src/modules/tasks/mission.decorator.ts create mode 100644 src/modules/tasks/tasks.module.ts create mode 100644 src/modules/todo/todo.controller.ts create mode 100644 src/modules/todo/todo.dto.ts create mode 100644 src/modules/todo/todo.entity.ts create mode 100644 src/modules/todo/todo.module.ts create mode 100644 src/modules/todo/todo.service.ts create mode 100644 src/modules/tools/email/email.controller.ts create mode 100644 src/modules/tools/email/email.dto.ts create mode 100644 src/modules/tools/email/email.module.ts create mode 100644 src/modules/tools/storage/storage.controller.ts create mode 100644 src/modules/tools/storage/storage.dto.ts create mode 100644 src/modules/tools/storage/storage.entity.ts create mode 100644 src/modules/tools/storage/storage.modal.ts create mode 100644 src/modules/tools/storage/storage.module.ts create mode 100644 src/modules/tools/storage/storage.service.ts create mode 100644 src/modules/tools/tools.module.ts create mode 100644 src/modules/tools/upload/file.constraint.ts create mode 100644 src/modules/tools/upload/upload.controller.ts create mode 100644 src/modules/tools/upload/upload.dto.ts create mode 100644 src/modules/tools/upload/upload.module.ts create mode 100644 src/modules/tools/upload/upload.service.ts create mode 100644 src/modules/user/constant.ts create mode 100644 src/modules/user/dto/password.dto.ts create mode 100644 src/modules/user/dto/user.dto.ts create mode 100644 src/modules/user/user.controller.ts create mode 100644 src/modules/user/user.entity.ts create mode 100644 src/modules/user/user.model.ts create mode 100644 src/modules/user/user.module.ts create mode 100644 src/modules/user/user.service.ts create mode 100644 src/repl.ts create mode 100644 src/setup-swagger.ts create mode 100644 src/shared/database/constraints/entity-exist.constraint.ts create mode 100644 src/shared/database/constraints/unique.constraint.ts create mode 100644 src/shared/database/database.module.ts create mode 100644 src/shared/database/typeorm-logger.ts create mode 100644 src/shared/helper/cron.service.ts create mode 100644 src/shared/helper/helper.module.ts create mode 100644 src/shared/helper/qq.service.ts create mode 100644 src/shared/logger/logger.module.ts create mode 100644 src/shared/logger/logger.service.ts create mode 100644 src/shared/mailer/mailer.module.ts create mode 100644 src/shared/mailer/mailer.service.ts create mode 100644 src/shared/redis/cache.service.ts create mode 100644 src/shared/redis/redis-subpub.ts create mode 100644 src/shared/redis/redis.constant.ts create mode 100644 src/shared/redis/redis.module.ts create mode 100644 src/shared/redis/subpub.service.ts create mode 100644 src/shared/shared.module.ts create mode 100644 src/socket/base.gateway.ts create mode 100644 src/socket/business-event.constant.ts create mode 100644 src/socket/events/admin.gateway.ts create mode 100644 src/socket/events/web.gateway.ts create mode 100644 src/socket/shared/auth.gateway.ts create mode 100644 src/socket/socket.module.ts create mode 100644 src/utils/captcha.util.ts create mode 100644 src/utils/crypto.util.ts create mode 100644 src/utils/date.util.ts create mode 100644 src/utils/file.util.ts create mode 100644 src/utils/index.ts create mode 100644 src/utils/ip.util.ts create mode 100644 src/utils/is.util.ts create mode 100644 src/utils/list2tree.util.ts create mode 100644 src/utils/permission.util.ts create mode 100644 src/utils/redis.util.ts create mode 100644 src/utils/schedule.util.ts create mode 100644 src/utils/tool.util.ts create mode 100644 tsconfig.build.json create mode 100644 tsconfig.json create mode 100644 types/global.d.ts create mode 100644 types/module.d.ts create mode 100644 types/utils.d.ts create mode 100644 vercel.json create mode 100644 wait-for-it.sh diff --git a/.cz-config.js b/.cz-config.js new file mode 100644 index 0000000..57d4806 --- /dev/null +++ b/.cz-config.js @@ -0,0 +1,94 @@ +// 请使用npm run c提交代码。遵循代码提交规范 +module.exports = { + types: [ + { value: 'feat', name: '功能: ✨ 新增功能', emoji: ':sparkles:' }, + { value: 'fix', name: '修复: 🐛 修复缺陷', emoji: ':bug:' }, + { value: 'docs', name: '文档: 📝 文档变更', emoji: ':memo:' }, + { + value: 'style', + name: '格式: 🌈 代码格式(不影响功能,例如空格、分号等格式修正)', + emoji: ':lipstick:', + }, + { + value: 'refactor', + name: '重构: 🔄 代码重构(不包括 bug 修复、功能新增)', + emoji: ':recycle:', + }, + { value: 'perf', name: '性能: 🚀 性能优化', emoji: ':zap:' }, + { + value: 'test', + name: '测试: 🧪 添加疏漏测试或已有测试改动', + emoji: ':white_check_mark:', + }, + { + value: 'build', + name: '构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)', + emoji: ':package:', + }, + { + value: 'ci', + name: '集成: ⚙️ 修改 CI 配置、脚本', + emoji: ':ferris_wheel:', + }, + { value: 'revert', name: '回退: ↩️ 回滚 commit', emoji: ':rewind:' }, + { + value: 'chore', + name: '其他: 🛠️ 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)', + emoji: ':hammer:', + }, + ], + useEmoji: true, + emojiAlign: 'center', + useAI: false, + aiNumber: 1, + themeColorCode: '', + scopes: [], + allowCustomScopes: true, + allowEmptyScopes: true, + customScopesAlign: 'bottom', + customScopesAlias: 'custom', + emptyScopesAlias: 'empty', + upperCaseSubject: false, + markBreakingChangeMode: false, + breaklineNumber: 100, + breaklineChar: '|', + issuePrefixes: [ + { value: 'closed', name: 'closed: ISSUES has been processed' }, + ], + customIssuePrefixAlign: 'top', + emptyIssuePrefixAlias: 'skip', + customIssuePrefixAlias: 'custom', + allowCustomIssuePrefix: true, + allowEmptyIssuePrefix: true, + confirmColorize: true, + maxHeaderLength: Infinity, + maxSubjectLength: Infinity, + minSubjectLength: 0, + scopeOverrides: undefined, + defaultBody: '', + defaultIssues: '', + defaultScope: '', + defaultSubject: '', + messages: { + type: '选择一种你期望的提交类型(type):', + // scope: '选择一个更改的范围(scope) (可选):', + // used if allowCustomScopes is true + // customScope: 'Denote the SCOPE of this change:', + subject: '输入本次commit记录说明:\n', + // body: '长说明,使用"|"换行(可选):\n', + // breaking: '非兼容性说明 (可选):\n', + // footer: '关联关闭的issue,例如:#31, #34(可选):\n', + confirmCommit: '确定提交说明?', + }, + skipQuestions: ['scope', 'body', 'breaking', 'footer'], + allowBreakingChanges: [ + 'fix', + 'feat', + 'update', + 'refactor', + 'perf', + 'build', + 'revert', + ], + subjectLimit: 500, // 提交长度限制500 +}; diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..84cee7b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,46 @@ +# compiled output +/dist +/node_modules +# package-lock.json +# yarn.lock + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# Code +src/config/config.development.* +docs/* +# sql/* +test/* +README.md + +# Dev data +/__data/ \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..6f19ae2 --- /dev/null +++ b/.env @@ -0,0 +1,32 @@ +# app +APP_NAME = Nest Admin +APP_PORT = 7001 +APP_BASE_URL = http://localhost:${APP_PORT} +APP_LOCALE = zh-CN + +# logger +LOGGER_LEVEL = verbose +LOGGER_MAX_FILES = 31 + +TZ = Asia/Shanghai + +# OSS(qiniu) +OSS_ACCESSKEY=xxx +OSS_SECRETKEY=xxx +OSS_DOMAIN=https://cdn.buqiyuan.site +OSS_BUCKET=nest-admin +OSS_ZONE=Zone_z2 # Zone_as0 | Zone_na0 | Zone_z0 | Zone_z1 | Zone_z2 +OSS_ACCESS_TYPE=public # or private + +DB_HOST = host.docker.internal +DB_PORT = 13307 +DB_DATABASE = nest_admin +DB_USERNAME = root +DB_PASSWORD = root +DB_SYNCHRONIZE = false +DB_LOGGING = ["error"] + +REDIS_PORT = 6379 +REDIS_HOST = host.docker.internal +REDIS_PASSWORD = 123456 +REDIS_DB = 0 \ No newline at end of file diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..6662f21 --- /dev/null +++ b/.env.development @@ -0,0 +1,35 @@ +# logger +LOGGER_LEVEL = debug + +# security +JWT_SECRET = admin!@#123 +JWT_EXPIRE = 86400 # 单位秒 +REFRESH_TOKEN_SECRET = admin!@#123 +REFRESH_TOKEN_EXPIRE = 2592000 + +# swagger +SWAGGER_ENABLE = true +SWAGGER_PATH = api-docs +SWAGGER_VERSION = 1.0 + +# db +DB_HOST = 127.0.0.1 +DB_PORT = 13307 +DB_DATABASE = nest_admin +DB_USERNAME = root +DB_PASSWORD = root +DB_SYNCHRONIZE = true +DB_LOGGING = ["error"] + +# redis +REDIS_PORT = 6379 +REDIS_HOST = 127.0.0.1 +REDIS_PASSWORD = 123456 +REDIS_DB = 0 + +# smtp +SMTP_HOST = smtp.163.com +SMTP_PORT = 465 +SMTP_USER = nest_admin@163.com +SMTP_PASS = VIPLLOIPMETTROYU + \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..c6a2b1d --- /dev/null +++ b/.env.production @@ -0,0 +1,37 @@ +# logger +LOGGER_LEVEL = debug + +# security +JWT_SECRET = admin!@#123 +JWT_EXPIRE = 86400 # 单位秒 +REFRESH_TOKEN_SECRET = admin!@#123 +REFRESH_TOKEN_EXPIRE = 2592000 + +# swagger +SWAGGER_ENABLE = true +SWAGGER_PATH = api-docs +SWAGGER_VERSION = 1.0 + +# db +DB_HOST = host.docker.internal +DB_PORT = 13307 +DB_DATABASE = nest_admin +DB_USERNAME = root +DB_PASSWORD = root +DB_SYNCHRONIZE = false +DB_LOGGING = ["error"] + +# redis +REDIS_PORT = 6379 +REDIS_HOST = host.docker.internal +REDIS_PASSWORD = 123456 +REDIS_DB = 0 + +# smtp +SMTP_HOST = smtp.163.com +SMTP_PORT = 465 +SMTP_USER = nest_admin@163.com +SMTP_PASS = VIPLLOIPMETTROYU + +# 是否为演示模式(在演示模式下,会拦截除 GET 方法以外的所有请求) +IS_DEMO = false \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d4e5bd3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Automatically normalize line endings (to LF) for all text-based files. +* text=auto eol=lf + +# Declare files that will always have CRLF line endings on checkout. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7fbd170 --- /dev/null +++ b/.gitignore @@ -0,0 +1,77 @@ +node_modules +.DS_Store +dist +*-dist +.cache +.history +.vercel/ + +.turbo +.local + +# local env files +#.env.development +#.env.production +.env.local + +.eslintcache + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# Editor directories and files +.idea +# .vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# compiled output +/dist +/node_modules + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +.nestjs_repl_history +out + +# temp data +__data + +public/upload +types/env.d.ts \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..35ed753 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run commitlint diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..3bb6316 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +#推送之前运行eslint检查 +npx lint-staged +##推送之前运行单元测试检查 +#npm run test:unit diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..eb950ed --- /dev/null +++ b/.npmrc @@ -0,0 +1,6 @@ +shamefully-hoist=true +strict-peer-dependencies=false + +# 使用淘宝镜像源 +registry = https://registry.npmmirror.com +# registry = https://registry.npmjs.org \ No newline at end of file diff --git a/.versionrc.js b/.versionrc.js new file mode 100644 index 0000000..e4ad6c6 --- /dev/null +++ b/.versionrc.js @@ -0,0 +1,19 @@ +//发布应用 生成commit日志记录。 + +module.exports = { + types: [ + { type: 'feat', section: '✨ Features | 新功能' }, + { type: 'fix', section: '🐛 Bug Fixes | Bug 修复' }, + { type: 'init', section: '📦️ Init | 初始化' }, + { type: 'docs', section: '📝 Documentation | 文档' }, + { type: 'style', section: '🌈 Styles | 风格' }, + { type: 'refactor', section: '🔄 Code Refactoring | 代码重构' }, + { type: 'perf', section: '🚀 Performance Improvements | 性能优化' }, + { type: 'test', section: '🧪 Tests | 测试' }, + { type: 'revert', section: '↩️ Revert | 回退' }, + { type: 'build', section: '📦️ Build System | 打包构建' }, + { type: 'update', section: '⚙️ update | 构建/工程依赖/工具升级' }, + { type: 'tool', section: '🛠️ tool | 工具升级' }, + { type: 'ci', section: '⚙️ Continuous Integration | CI 配置' }, + ], +}; diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d879beb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug Nest Framework", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "start:debug", "--", "--inspect-brk"], + "autoAttachChildProcesses": true, + "restart": true, + "sourceMaps": true, + "stopOnEntry": false, + "console": "integratedTerminal" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..56ee0fc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,41 @@ +{ + // Enable the ESlint flat config support + "eslint.experimental.useFlatConfig": true, + + // Disable the default formatter, use eslint instead + "prettier.enable": false, + "editor.formatOnSave": false, + + // Auto fix + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never" + }, + + // Silent the stylistic rules in you IDE, but still auto fix them + "eslint.rules.customizations": [ + { "rule": "style/*", "severity": "off" }, + { "rule": "*-indent", "severity": "off" }, + { "rule": "*-spacing", "severity": "off" }, + { "rule": "*-spaces", "severity": "off" }, + { "rule": "*-order", "severity": "off" }, + { "rule": "*-dangle", "severity": "off" }, + { "rule": "*-newline", "severity": "off" }, + { "rule": "*quotes", "severity": "off" }, + { "rule": "*semi", "severity": "off" } + ], + + // Enable eslint for all supported languages + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "yaml" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..31e8e78 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,53 @@ +# 遇到网络问题可以配置镜像加速:https://gist.github.com/y0ngb1n/7e8f16af3242c7815e7ca2f0833d3ea6 +# FROM 表示设置要制作的镜像基于哪个镜像,FROM指令必须是整个Dockerfile的第一个指令,如果指定的镜像不存在默认会自动从Docker Hub上下载。 +# 指定我们的基础镜像是node,latest表示版本是最新, 如果要求空间极致,可以选择lts-alpine +# 使用 as 来为某一阶段命名 +FROM node:20-slim AS base + +ENV PROJECT_DIR=/nest-admin \ + DB_HOST=mysql \ + APP_PORT=7001 \ + PNPM_HOME="/pnpm" \ + PATH="$PNPM_HOME:$PATH" + + +RUN corepack enable \ + && yarn global add pm2 + +# WORKDIR指令用于设置Dockerfile中的RUN、CMD和ENTRYPOINT指令执行命令的工作目录(默认为/目录),该指令在Dockerfile文件中可以出现多次, +# 如果使用相对路径则为相对于WORKDIR上一次的值, +# 例如WORKDIR /data,WORKDIR logs,RUN pwd最终输出的当前目录是/data/logs。 +# cd 到 /nest-admin +WORKDIR $PROJECT_DIR +COPY ./ $PROJECT_DIR +RUN chmod +x ./wait-for-it.sh + +# set timezone +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo 'Asia/Shanghai' > /etc/timezone + +# see https://pnpm.io/docker +FROM base AS prod-deps +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile + +FROM base AS build +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN pnpm run build + + +# mirror acceleration +# RUN npm config set registry https://registry.npmmirror.com +# RUN pnpm config set registry https://registry.npmmirror.com +# RUN npm config rm proxy && npm config rm https-proxy + +FROM base +COPY --from=prod-deps $PROJECT_DIR/node_modules $PROJECT_DIR/node_modules +COPY --from=build $PROJECT_DIR/dist $PROJECT_DIR/dist + +# EXPOSE port +EXPOSE $APP_PORT + +# 容器启动时执行的命令,类似npm run start +# CMD ["pnpm", "start:prod"] +# CMD ["pm2-runtime", "ecosystem.config.js"] +ENTRYPOINT ./wait-for-it.sh $DB_HOST:$DB_PORT -- pm2-runtime ecosystem.config.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5c341e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present buqiyuan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ce302ae --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ + +## 环境要求 + +- `nodejs` `16.20.2`+ +- `docker` `20.x`+ ,其中 `docker compose`版本需要 `2.17.0`+ +- `mysql` `8.x`+ +- 使用 [`pnpm`](https://pnpm.io/zh/) 包管理器安装项目依赖 + +演示环境账号密码: + +| 账号 | 密码 | 权限 | +| :-------: | :----: | :--------: | +| admin | a123456 | 超级管理员 | + +> 所有新建的用户初始密码都为 a123456 + +本地部署账号密码: + +| 账号 | 密码 | 权限 | +| :-------: | :----: | :--------: | +| admin | a123456 | 超级管理员 | + + + +## 本地开发 + +- 【可选】如果你是新手,还不太会搭建`mysql/redis`,你可以使用 `Docker` 启动指定服务供本地开发时使用, 例如: + +```bash +# 启动MySql服务 +docker compose --env-file .env --env-file .env.development run -d --service-ports mysql +# 启动Redis服务 +docker compose --env-file .env --env-file .env.development run -d --service-ports redis +``` + +- 安装依赖 + +```bash + +pnpm install + +``` + +- 运行 + 启动成功后,通过 访问。 + +```bash +pnpm dev +``` + +- 打包 + +```bash +pnpm build +``` + +2.使用docker运行 + +```bash +docker compose up -d +``` + +停止并删除所有容器 + +```bash +pnpm docker:down +# or +docker compose --env-file .env --env-file .env.production down +``` + +删除镜像 + +```bash +pnpm docker:rmi +# or +docker rmi buqiyuan/nest-admin-server:stable +``` + +查看实时日志输出 + +```bash +pnpm docker:logs +# or +docker compose --env-file .env --env-file .env.production logs -f + +``` + +## 数据库迁移 + +1. 更新数据库(或初始化数据) + +```bash +pnpm migration:run +``` + +2. 生成迁移 + +```bash +pnpm migration:generate +``` + +3. 回滚到最后一次更新 + +```bash +pnpm migration:revert +``` + +更多细节,请移步至[官方文档](https://typeorm.io/migrations) + +> [!TIP] +> 如果你的`实体类`或`数据库配置`有更新,请执行`npm run build`后再进行数据库迁移相关操作。 + diff --git a/commitlint.config.cjs b/commitlint.config.cjs new file mode 100644 index 0000000..7576451 --- /dev/null +++ b/commitlint.config.cjs @@ -0,0 +1,24 @@ +// 请使用npm run c/yarn c提交代码。 +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'type-enum': [ + 2, + 'always', + [ + 'feat', // 新增功能 + 'fix', // 修复缺陷 + 'docs', // 文档变更 + 'style', // 代码格式(不影响功能,例如空格、分号等格式修正) + 'refactor', // 代码重构(不包括 bug 修复、功能新增) + 'perf', // 性能优化 + 'test', // 添加疏漏测试或已有测试改动 + 'build', // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等) + 'ci', // 修改 CI 配置、脚本 + 'revert', // 回滚 commit + 'chore', // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例) + ], + ], + 'subject-case': [0], // subject大小写不做校验 + }, +}; diff --git a/deploy/sql/nest_admin.sql b/deploy/sql/nest_admin.sql new file mode 100644 index 0000000..fc3386e --- /dev/null +++ b/deploy/sql/nest_admin.sql @@ -0,0 +1,744 @@ +/* + Navicat Premium Data Transfer + + Source Server : nest-admin + Source Server Type : MySQL + Source Server Version : 80030 (8.0.30) + Source Host : localhost:13307 + Source Schema : nest_admin + + Target Server Type : MySQL + Target Server Version : 80030 (8.0.30) + File Encoding : 65001 + + Date: 10/02/2024 09:43:40 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for sys_captcha_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_captcha_log`; +CREATE TABLE `sys_captcha_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, + `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_captcha_log +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for sys_config +-- ---------------------------- +DROP TABLE IF EXISTS `sys_config`; +CREATE TABLE `sys_config` ( + `id` int NOT NULL AUTO_INCREMENT, + `key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `IDX_2c363c25cf99bcaab3a7f389ba` (`key`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_config +-- ---------------------------- +BEGIN; +INSERT INTO `sys_config` (`id`, `key`, `name`, `value`, `remark`, `created_at`, `updated_at`) VALUES (1, 'sys_user_initPassword', '初始密码', '123456', '创建管理员账号的初始密码', '2023-11-10 00:31:44.154921', '2023-11-10 00:31:44.161263'); +INSERT INTO `sys_config` (`id`, `key`, `name`, `value`, `remark`, `created_at`, `updated_at`) VALUES (2, 'sys_api_token', 'API Token', 'nest-admin', '用于请求 @ApiToken 的控制器', '2023-11-10 00:31:44.154921', '2024-01-29 09:52:27.000000'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_dept +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dept`; +CREATE TABLE `sys_dept` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `orderNo` int DEFAULT '0', + `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT '', + `parentId` int DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + KEY `FK_c75280b01c49779f2323536db67` (`parentId`) USING BTREE, + CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_dept +-- ---------------------------- +BEGIN; +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (1, '华东分部', 1, '1.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (2, '研发部', 1, '1.2.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (3, '市场部', 2, '1.3.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (4, '商务部', 3, '1.4.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (5, '财务部', 4, '1.5.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (6, '华南分部', 2, '6.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (7, '西北分部', 3, '7.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (8, '研发部', 1, '6.8.', 6, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (9, '市场部', 1, '6.9.', 6, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_dict +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict`; +CREATE TABLE `sys_dict` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint NOT NULL DEFAULT '1', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `IDX_d112365748f740ee260b65ce91` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of sys_dict +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for sys_dict_item +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_item`; +CREATE TABLE `sys_dict_item` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `order` int DEFAULT NULL COMMENT '字典项排序', + `status` tinyint NOT NULL DEFAULT '1', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `type_id` int DEFAULT NULL, + `orderNo` int DEFAULT NULL COMMENT '字典项排序', + PRIMARY KEY (`id`), + KEY `FK_d68ea74fcb041c8cfd1fd659844` (`type_id`), + CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of sys_dict_item +-- ---------------------------- +BEGIN; +INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (1, '2024-01-29 01:24:51.846135', '2024-01-29 02:23:19.000000', 1, 1, '男', '1', 0, 1, '性别男', 1, 3); +INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (2, '2024-01-29 01:32:58.458741', '2024-01-29 01:58:20.000000', 1, 1, '女', '0', 1, 1, '性别女', 1, 2); +INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (3, '2024-01-29 01:59:17.805394', '2024-01-29 14:37:18.000000', 1, 1, '人妖王', '3', NULL, 1, '安布里奥·伊万科夫', 1, 0); +INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (5, '2024-01-29 02:13:01.782466', '2024-01-29 02:13:01.782466', 1, 1, '显示', '1', NULL, 1, '显示菜单', 2, 0); +INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (6, '2024-01-29 02:13:31.134721', '2024-01-29 02:13:31.134721', 1, 1, '隐藏', '0', NULL, 1, '隐藏菜单', 2, 0); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_dict_type +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_type`; +CREATE TABLE `sys_dict_type` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint NOT NULL DEFAULT '1', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `IDX_74d0045ff7fab9f67adc0b1bda` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- ---------------------------- +-- Records of sys_dict_type +-- ---------------------------- +BEGIN; +INSERT INTO `sys_dict_type` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `name`, `status`, `remark`, `code`) VALUES (1, '2024-01-28 08:19:12.777447', '2024-02-08 13:05:10.000000', 1, 1, '性别', 1, '性别单选', 'sys_user_gender'); +INSERT INTO `sys_dict_type` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `name`, `status`, `remark`, `code`) VALUES (2, '2024-01-28 08:38:41.235185', '2024-01-29 02:11:33.000000', 1, 1, '菜单显示状态', 1, '菜单显示状态', 'sys_show_hide'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_login_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_login_log`; +CREATE TABLE `sys_login_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `ua` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `user_id` int DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + KEY `FK_3029712e0df6a28edaee46fd470` (`user_id`), + CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_login_log +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for sys_menu +-- ---------------------------- +DROP TABLE IF EXISTS `sys_menu`; +CREATE TABLE `sys_menu` ( + `id` int NOT NULL AUTO_INCREMENT, + `parent_id` int DEFAULT NULL, + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `type` tinyint NOT NULL DEFAULT '0', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `order_no` int DEFAULT '0', + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `keep_alive` tinyint NOT NULL DEFAULT '1', + `show` tinyint NOT NULL DEFAULT '1', + `status` tinyint NOT NULL DEFAULT '1', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `is_ext` tinyint NOT NULL DEFAULT '0', + `ext_open_mode` tinyint NOT NULL DEFAULT '1', + `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=128 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_menu +-- ---------------------------- +BEGIN; +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (1, NULL, '/system', '系统管理', '', 0, 'ant-design:setting-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:46.668745', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (2, 1, '/system/user', '用户管理', 'system:user:list', 1, 'ant-design:user-outlined', 0, 'system/user/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:10:30.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (3, 1, '/system/role', '角色管理', 'system:role:list', 1, 'ep:user', 1, 'system/role/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:02.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (4, 1, '/system/menu', '菜单管理', 'system:menu:list', 1, 'ep:menu', 2, 'system/menu/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:18.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:44.567023', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (6, 5, '/system/monitor/online', '在线用户', 'system:online:list', 1, '', 0, 'system/monitor/online/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:13:59.519267', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (7, 5, '/sys/monitor/login-log', '登录日志', 'system:log:login:list', 1, '', 0, 'system/monitor/log/login/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:02.610719', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (8, 5, '/system/monitor/serve', '服务监控', 'system:serve:stat', 1, '', 4, 'system/monitor/serve/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:05.606355', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:52.967983', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (10, 9, '/system/task', '任务管理', '', 1, '', 0, 'system/schedule/task/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:14:39.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (11, 9, '/system/task/log', '任务日志', 'system:task:list', 1, '', 0, 'system/schedule/log/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:15:01.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (12, NULL, '/document', '文档', '', 0, 'ion:tv-outline', 2, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:42.514264', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (14, 12, 'https://www.typeorm.org/', 'Typeorm中文文档(外链)', NULL, 1, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:39:53.000000', 1, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (15, 12, 'https://docs.nestjs.cn/', 'Nest.js中文文档(内嵌)', '', 1, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:40:43.000000', 1, 2, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (20, 2, NULL, '新增', 'system:user:create', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (21, 2, '', '删除', 'system:user:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (22, 2, '', '更新', 'system:user:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (23, 2, '', '查询', 'system:user:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (24, 3, '', '新增', 'system:role:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (25, 3, '', '删除', 'system:role:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (26, 3, '', '修改', 'system:role:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (27, 3, '', '查询', 'system:role:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (28, 4, NULL, '新增', 'system:menu:create', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (29, 4, NULL, '删除', 'system:menu:delete', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (30, 4, '', '修改', 'system:menu:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (31, 4, NULL, '查询', 'system:menu:read', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (32, 6, '', '下线', 'system:online:kick', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (34, 10, '', '新增', 'system:task:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (35, 10, '', '删除', 'system:task:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (36, 10, '', '执行一次', 'system:task:once', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (37, 10, '', '查询', 'system:task:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (38, 10, '', '运行', 'system:task:start', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (39, 10, '', '暂停', 'system:task:stop', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (40, 10, '', '更新', 'system:task:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (41, 7, '', '查询登录日志', 'system:log:login:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (42, 7, '', '查询任务日志', 'system:log:task:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (43, NULL, '/about', '关于', '', 1, 'ant-design:info-circle-outlined', 260, 'account/about', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-10 09:35:41.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (48, NULL, '/tool', '系统工具', NULL, 0, 'ant-design:tool-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:28.327223', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (49, 48, '/tool/email', '邮件工具', 'system:tools:email', 1, 'ant-design:send-outlined', 1, 'tool/email/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:59:07.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (50, 49, NULL, '发送邮件', 'tools:email:send', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (51, 48, '/tool/storage', '存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:59:17.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (52, 51, NULL, '文件上传', 'upload:upload', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 01:04:08.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (53, 51, NULL, '文件删除', 'tool:storage:delete', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:56:01.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (54, 2, NULL, '修改密码', 'system:user:password', 2, '', 5, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (56, 1, '/system/dict-type', '字典管理', 'system:dict-type:list', 1, 'ant-design:book-outlined', 4, 'system/dict-type/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:12.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (57, 56, NULL, '新增', 'system:dict-type:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:20.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (58, 56, NULL, '更新', 'system:dict-type:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:26.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (59, 56, NULL, '删除', 'system:dict-type:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:42.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (60, 56, NULL, '查询', 'system:dict-type:info', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (61, 1, '/system/dept', '部门管理', 'system:dept:list', 1, 'ant-design:deployment-unit-outlined', 3, 'system/dept/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:55.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (62, 61, NULL, '新增', 'system:dept:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (63, 61, NULL, '更新', 'system:dept:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (64, 61, NULL, '删除', 'system:dept:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (65, 61, NULL, '查询', 'system:dept:read', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (68, 5, '/health', '健康检查', '', 1, '', 4, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:33.352155', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (69, 68, NULL, '网络', 'app:health:network', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (70, 68, NULL, '数据库', 'app:health: database', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (86, 1, '/param-config', '参数配置', 'system:param-config:list', 1, 'ep:edit', 255, 'system/param-config/index', 0, 1, 1, '2024-01-10 17:34:52.569663', '2024-01-19 02:11:27.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (87, 86, NULL, '查询', 'system:param-config:read', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:20.983241', '2024-01-10 17:39:20.983241', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (88, 86, NULL, '新增', 'system:param-config:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:57.543510', '2024-01-10 17:39:57.543510', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (89, 86, NULL, '更新', 'system:param-config:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:40:27.355944', '2024-01-10 17:40:27.355944', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (92, 86, NULL, '删除', 'system:param-config:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:57:32.059887', '2024-01-10 17:57:32.059887', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (107, 1, 'system/dict-item/:id', '字典项管理', 'system:dict-item:list', 1, 'ant-design:facebook-outlined', 255, 'system/dict-item/index', 0, 0, 1, '2024-01-28 09:21:17.409532', '2024-01-30 13:09:47.000000', 0, 1, '字典管理'); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (108, 107, NULL, '新增', 'system:dict-item:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:22:39.401758', '2024-01-28 22:38:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (109, 107, NULL, '更新', 'system:dict-item:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:26:43.911886', '2024-01-28 09:26:43.911886', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (110, 107, NULL, '删除', 'system:dict-item:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:28.535225', '2024-01-28 09:27:28.535225', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 1, 1, '2024-02-10 08:00:02.394616', '2024-02-10 09:35:34.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (116, 115, 'manage', '文件管理', 'netdisk:manage:list', 1, '', 252, 'netdisk/manage', 0, 1, 1, '2024-02-10 08:03:49.837348', '2024-02-10 09:34:41.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (120, 116, NULL, '删除', 'netdisk:manage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:09.480601', '2024-02-10 08:42:09.480601', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (121, 116, NULL, '获取文件上传token', 'netdisk:manage:token', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:57.688104', '2024-02-10 08:42:57.688104', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (122, 116, NULL, '添加文件备注', 'netdisk:manage:mark', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:43:40.117321', '2024-02-10 08:43:40.117321', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (123, 116, NULL, '下载文件', 'netdisk:manage:download', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:01.338984', '2024-02-10 08:44:01.338984', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (124, 116, NULL, '重命名文件或文件夹', 'netdisk:manage:rename', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:27.233379', '2024-02-10 08:45:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (125, 116, NULL, '复制文件或文件夹', 'netdisk:manage:copy', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:44.725391', '2024-02-10 08:45:48.000000', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (126, 116, NULL, '剪切文件或文件夹', 'netdisk:manage:cut', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:45:21.660511', '2024-02-10 08:45:21.660511', 0, 1, NULL); +INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (127, 115, 'overview', '网盘概览', 'netdisk:overview:desc', 1, '', 254, 'netdisk/overview', 0, 1, 1, '2024-02-10 09:32:56.981190', '2024-02-10 09:34:18.000000', 0, 1, NULL); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_role +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role`; +CREATE TABLE `sys_role` ( + `id` int NOT NULL AUTO_INCREMENT, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `status` tinyint DEFAULT '1', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `default` tinyint DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `IDX_223de54d6badbe43a5490450c3` (`name`) USING BTREE, + UNIQUE KEY `IDX_05edc0a51f41bb16b7d8137da9` (`value`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_role +-- ---------------------------- +BEGIN; +INSERT INTO `sys_role` (`id`, `value`, `name`, `remark`, `status`, `created_at`, `updated_at`, `default`) VALUES (1, 'admin', '管理员', '超级管理员', 1, '2023-11-10 00:31:44.058463', '2024-01-28 21:08:39.000000', NULL); +INSERT INTO `sys_role` (`id`, `value`, `name`, `remark`, `status`, `created_at`, `updated_at`, `default`) VALUES (2, 'user', '用户', '', 1, '2023-11-10 00:31:44.058463', '2024-01-30 18:44:45.000000', 1); +INSERT INTO `sys_role` (`id`, `value`, `name`, `remark`, `status`, `created_at`, `updated_at`, `default`) VALUES (9, 'test', '测试', NULL, 1, '2024-01-23 22:46:52.408827', '2024-01-30 01:04:52.000000', NULL); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_role_menus +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role_menus`; +CREATE TABLE `sys_role_menus` ( + `role_id` int NOT NULL, + `menu_id` int NOT NULL, + PRIMARY KEY (`role_id`,`menu_id`), + KEY `IDX_35ce749b04d57e226d059e0f63` (`role_id`), + KEY `IDX_2b95fdc95b329d66c18f5baed6` (`menu_id`), + CONSTRAINT `FK_2b95fdc95b329d66c18f5baed6d` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_35ce749b04d57e226d059e0f633` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ---------------------------- +-- Records of sys_role_menus +-- ---------------------------- +BEGIN; +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 1); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 2); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 3); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 4); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 5); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 6); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 7); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 8); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 9); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 10); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 11); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 12); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 14); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 15); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 20); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 21); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 22); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 23); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 24); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 25); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 26); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 27); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 28); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 29); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 30); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 31); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 32); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 34); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 35); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 36); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 37); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 38); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 39); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 40); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 41); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 42); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 43); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 48); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 49); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 50); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 51); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 52); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 53); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 54); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 56); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 57); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 58); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 59); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 60); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 61); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 62); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 63); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 64); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 65); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 68); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 69); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 70); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 86); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 87); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 88); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 89); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 92); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 107); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 108); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 109); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 110); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 111); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 1); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 5); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 6); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 7); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 8); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 9); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 10); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 11); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 12); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 14); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 15); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 32); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 34); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 35); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 36); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 37); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 38); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 39); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 40); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 41); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 42); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 43); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 48); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 49); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 50); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 51); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 52); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 53); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 56); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 57); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 58); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 59); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 60); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 68); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 69); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 70); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 86); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 87); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 88); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 89); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 92); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 107); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 108); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 109); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 110); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 111); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 112); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 1); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 2); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 3); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 4); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 5); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 6); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 7); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 8); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 9); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 10); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 11); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 20); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 21); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 22); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 23); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 24); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 25); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 26); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 27); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 28); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 29); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 30); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 31); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 32); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 34); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 35); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 36); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 37); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 38); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 39); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 40); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 41); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 42); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 54); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 56); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 57); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 58); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 59); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 60); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 61); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 62); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 63); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 64); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 65); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 68); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 69); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 70); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 86); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 87); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 88); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 89); +INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 92); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_task +-- ---------------------------- +DROP TABLE IF EXISTS `sys_task`; +CREATE TABLE `sys_task` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `service` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `type` tinyint NOT NULL DEFAULT '0', + `status` tinyint NOT NULL DEFAULT '1', + `start_time` datetime DEFAULT NULL, + `end_time` datetime DEFAULT NULL, + `limit` int DEFAULT '0', + `cron` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `every` int DEFAULT NULL, + `data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `job_opts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `IDX_ef8e5ab5ef2fe0ddb1428439ef` (`name`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_task +-- ---------------------------- +BEGIN; +INSERT INTO `sys_task` (`id`, `name`, `service`, `type`, `status`, `start_time`, `end_time`, `limit`, `cron`, `every`, `data`, `job_opts`, `remark`, `created_at`, `updated_at`) VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-02-10 09:43:14.000000'); +INSERT INTO `sys_task` (`id`, `name`, `service`, `type`, `status`, `start_time`, `end_time`, `limit`, `cron`, `every`, `data`, `job_opts`, `remark`, `created_at`, `updated_at`) VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-02-10 09:43:14.000000'); +INSERT INTO `sys_task` (`id`, `name`, `service`, `type`, `status`, `start_time`, `end_time`, `limit`, `cron`, `every`, `data`, `job_opts`, `remark`, `created_at`, `updated_at`) VALUES (4, '访问百度首页', 'HttpRequestJob.handle', 0, 0, NULL, NULL, 1, '* * * * * ?', NULL, '{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}', NULL, '访问百度首页', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); +INSERT INTO `sys_task` (`id`, `name`, `service`, `type`, `status`, `start_time`, `end_time`, `limit`, `cron`, `every`, `data`, `job_opts`, `remark`, `created_at`, `updated_at`) VALUES (5, '发送邮箱', 'EmailJob.send', 0, 0, NULL, NULL, -1, '0 0 0 1 * ?', NULL, '{\"subject\":\"这是标题\",\"to\":\"zeyu57@163.com\",\"content\":\"这是正文\"}', NULL, '每月发送邮箱', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_task_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_task_log`; +CREATE TABLE `sys_task_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `task_id` int DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT '0', + `detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `consume_time` int DEFAULT '0', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + KEY `FK_f4d9c36052fdb188ff5c089454b` (`task_id`), + CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_task_log +-- ---------------------------- +BEGIN; +INSERT INTO `sys_task_log` (`id`, `task_id`, `status`, `detail`, `consume_time`, `created_at`, `updated_at`) VALUES (1, 3, 1, NULL, 0, '2024-02-05 03:06:22.037448', '2024-02-05 03:06:22.037448'); +INSERT INTO `sys_task_log` (`id`, `task_id`, `status`, `detail`, `consume_time`, `created_at`, `updated_at`) VALUES (2, 2, 1, NULL, 0, '2024-02-10 09:42:21.738712', '2024-02-10 09:42:21.738712'); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `psalt` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint DEFAULT '1', + `qq` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `dept_id` int DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `IDX_9e7164b2f1ea1348bc0eb0a7da` (`username`) USING BTREE, + KEY `FK_96bde34263e2ae3b46f011124ac` (`dept_id`), + CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- ---------------------------- +-- Records of sys_user +-- ---------------------------- +BEGIN; +INSERT INTO `sys_user` (`id`, `username`, `password`, `avatar`, `email`, `phone`, `remark`, `psalt`, `status`, `qq`, `created_at`, `updated_at`, `nickname`, `dept_id`) VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-01-29 09:49:43.000000', 'bqy', 1); +INSERT INTO `sys_user` (`id`, `username`, `password`, `avatar`, `email`, `phone`, `remark`, `psalt`, `status`, `qq`, `created_at`, `updated_at`, `nickname`, `dept_id`) VALUES (2, 'user', 'dbd89546dec743f82bb9073d6ac39361', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', 'luffy@qq.com', '10010', '王路飞', 'qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-01-29 09:49:57.000000', 'luffy', 8); +INSERT INTO `sys_user` (`id`, `username`, `password`, `avatar`, `email`, `phone`, `remark`, `psalt`, `status`, `qq`, `created_at`, `updated_at`, `nickname`, `dept_id`) VALUES (8, 'developer', 'f03fa2a99595127b9a39587421d471f6', '/upload/cfd0d14459bc1a47-202402032141838.jpeg', 'nami@qq.com', '10000', '小贼猫', 'NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-02-03 21:41:18.000000', '娜美', 7); +COMMIT; + +-- ---------------------------- +-- Table structure for sys_user_roles +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user_roles`; +CREATE TABLE `sys_user_roles` ( + `user_id` int NOT NULL, + `role_id` int NOT NULL, + PRIMARY KEY (`user_id`,`role_id`), + KEY `IDX_96311d970191a044ec048011f4` (`user_id`), + KEY `IDX_6d61c5b3f76a3419d93a421669` (`role_id`), + CONSTRAINT `FK_6d61c5b3f76a3419d93a4216695` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`), + CONSTRAINT `FK_96311d970191a044ec048011f44` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ---------------------------- +-- Records of sys_user_roles +-- ---------------------------- +BEGIN; +INSERT INTO `sys_user_roles` (`user_id`, `role_id`) VALUES (1, 1); +INSERT INTO `sys_user_roles` (`user_id`, `role_id`) VALUES (2, 2); +INSERT INTO `sys_user_roles` (`user_id`, `role_id`) VALUES (8, 2); +COMMIT; + +-- ---------------------------- +-- Table structure for todo +-- ---------------------------- +DROP TABLE IF EXISTS `todo`; +CREATE TABLE `todo` ( + `id` int NOT NULL AUTO_INCREMENT, + `value` varchar(255) NOT NULL, + `user_id` int DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT '0', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`), + KEY `FK_9cb7989853c4cb7fe427db4b260` (`user_id`), + CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- ---------------------------- +-- Records of todo +-- ---------------------------- +BEGIN; +INSERT INTO `todo` (`id`, `value`, `user_id`, `status`, `created_at`, `updated_at`) VALUES (1, 'nest.js', NULL, 0, '2023-11-10 00:31:44.139730', '2023-11-10 00:31:44.147629'); +COMMIT; + +-- ---------------------------- +-- Table structure for tool_storage +-- ---------------------------- +DROP TABLE IF EXISTS `tool_storage`; +CREATE TABLE `tool_storage` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名', + `fileName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '真实文件名', + `ext_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `user_id` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=79 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ---------------------------- +-- Records of tool_storage +-- ---------------------------- +BEGIN; +INSERT INTO `tool_storage` (`id`, `created_at`, `updated_at`, `name`, `fileName`, `ext_name`, `path`, `type`, `size`, `user_id`) VALUES (78, '2024-02-03 21:41:16.851178', '2024-02-03 21:41:16.851178', 'cfd0d14459bc1a47-202402032141838.jpeg', 'cfd0d14459bc1a47.jpeg', 'jpeg', '/upload/cfd0d14459bc1a47-202402032141838.jpeg', '图片', '33.92 KB', 1); +COMMIT; + +-- ---------------------------- +-- Table structure for user_access_tokens +-- ---------------------------- +DROP TABLE IF EXISTS `user_access_tokens`; +CREATE TABLE `user_access_tokens` ( + `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `expired_at` datetime NOT NULL COMMENT '令牌过期时间', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', + `user_id` int DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FK_e9d9d0c303432e4e5e48c1c3e90` (`user_id`), + CONSTRAINT `FK_e9d9d0c303432e4e5e48c1c3e90` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ---------------------------- +-- Records of user_access_tokens +-- ---------------------------- +BEGIN; +INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('09cf7b0a-62e0-45ee-96b0-e31de32361e0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1MDkxNTd9.0gtKlcxrxQ-TarEai2lsBnfMc852ZDYHeSjjhpo5Fn8', '2024-02-11 04:05:58', '2024-02-10 04:05:57.696509', 1); +INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('3f7dffae-db1f-47dc-9677-5c956c3de39e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczMTEzMDJ9.D5Qpht1RquKor8WtgfGAcCp8LwG7z3FZhIwbyQzhDmE', '2024-02-08 21:08:22', '2024-02-07 21:08:22.130066', 1); +INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('40342c3e-194c-42eb-adee-189389839195', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxNjF9.tRQOxhB-01Pcut5MXm4L5D1OrbMJfS4LfUys0XB4kWs', '2024-02-09 14:02:41', '2024-02-08 14:02:41.081164', 1); +INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('9d1ba8e9-dffc-4b15-b21f-4a90f196e39c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1Mjc5MDV9.7LeiS3LBBdiAc7YrULWpmnI1oNSvR79K-qjEOlBYOnI', '2024-02-11 09:18:26', '2024-02-10 09:18:25.656695', 1); +INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxMjd9.VRuJHGca2IPrdfTyW09wfhht4x8JX207pKG-0aZyF60', '2024-02-09 14:02:07', '2024-02-08 14:02:07.390658', 1); +COMMIT; + +-- ---------------------------- +-- Table structure for user_refresh_tokens +-- ---------------------------- +DROP TABLE IF EXISTS `user_refresh_tokens`; +CREATE TABLE `user_refresh_tokens` ( + `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `expired_at` datetime NOT NULL COMMENT '令牌过期时间', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', + `accessTokenId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `REL_1dfd080c2abf42198691b60ae3` (`accessTokenId`), + CONSTRAINT `FK_1dfd080c2abf42198691b60ae39` FOREIGN KEY (`accessTokenId`) REFERENCES `user_access_tokens` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- ---------------------------- +-- Records of user_refresh_tokens +-- ---------------------------- +BEGIN; +INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('202d0969-6721-4f6f-bf34-f0d1931d4d01', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRpOXVYei1TdldjdWRnclFXVmFXIiwiaWF0IjoxNzA3MzcyMTYxfQ.NOQufR5EAPE2uZoyenmAj9H7S7qo4d6W1aW2ojDxZQc', '2024-03-09 14:02:41', '2024-02-08 14:02:41.091492', '40342c3e-194c-42eb-adee-189389839195'); +INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('461f9b7c-e500-4762-a6d9-f9ea47163064', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicXJvTWNYMnhNRW5uRmZGWkQtaUx0IiwiaWF0IjoxNzA3MzExMzAyfQ.dFIWCePZnn2z2Qv1D5PKBKXUwVDI0Gp091MIOi9jiIo', '2024-03-08 21:08:22', '2024-02-07 21:08:22.145464', '3f7dffae-db1f-47dc-9677-5c956c3de39e'); +INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('b375e623-2d82-48f0-9b7a-9058e3850cc6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicDhUMzdGNFFaUDJHLU5yNGVha21wIiwiaWF0IjoxNzA3MzcyMTI3fQ.fn3It6RKIxXlKmqixg0BMmY_YsQmAxtetueqW-0y1IM', '2024-03-09 14:02:07', '2024-02-08 14:02:07.410008', 'edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb'); +INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('e620ccc1-9e40-4387-9f21-f0722e535a63', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNE5WdmFIc2hWaU05ZFh0QnVBaHNsIiwiaWF0IjoxNzA3NTI3OTA1fQ.zzyGX0mOJe6KWpTzIi7We9d9c0MRuDeGC86DMB0Vubs', '2024-03-11 09:18:26', '2024-02-10 09:18:25.664251', '9d1ba8e9-dffc-4b15-b21f-4a90f196e39c'); +INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('f9a003e8-91b7-41ee-979e-e39cca3534ec', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGJQdl9SVjFtUl80N0o0TGF0QlV5IiwiaWF0IjoxNzA3NTA5MTU3fQ.oEVdWSigTpAQY7F8MlwBnedldH0sJT1YF1Mt0ZUbIw4', '2024-03-11 04:05:58', '2024-02-10 04:05:57.706763', '09cf7b0a-62e0-45ee-96b0-e31de32361e0'); +COMMIT; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/deploy/web/default.conf b/deploy/web/default.conf new file mode 100644 index 0000000..c4f24e0 --- /dev/null +++ b/deploy/web/default.conf @@ -0,0 +1,54 @@ +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 80; + absolute_redirect off; #取消绝对路径的重定向 + sendfile on; + default_type application/octet-stream; + + gzip on; + gzip_http_version 1.1; + gzip_disable "MSIE [1-6]\."; + gzip_min_length 256; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_comp_level 9; + + root /usr/share/nginx/html; + + location / { + # same docker config + root /usr/share/nginx/html; + index index.html; + # support history mode + try_files $uri $uri/ /index.html; + } + + # 后端服务 + location ^~ /api/ { + proxy_pass http://nest-admin-server:7001/; # 转发规则 + proxy_set_header Host $proxy_host; # 修改转发请求头,让目标应用可以受到真实的请求 + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # websocket服务 + location ^~ /ws-api/ { + proxy_pass http://nest-admin-server:7002/; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + + proxy_set_header Host $host; + proxy_set_header X-real-ip $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + } + +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4139438 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,64 @@ +version: '3' + +services: + mysql: + image: mysql:latest + container_name: nest-admin-mysql + restart: always + env_file: + - .env + - .env.production + environment: + - MYSQL_HOST=${DB_HOST} + - MYSQL_PORT=${DB_PORT} + - MYSQL_DATABASE=${DB_DATABASE} + - MYSQL_USERNAME=${DB_USERNAME} + - MYSQL_PASSWORD=${DB_PASSWORD} + - MYSQL_ROOT_PASSWORD=${DB_PASSWORD} + ports: + - '${DB_PORT}:3306' + command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci #设置utf8字符集 + volumes: + - ./__data/mysql/:/var/lib/mysql/ # ./__data/mysql/ 路径可以替换成自己的路径 + - ./deploy/sql/:/docker-entrypoint-initdb.d/ # 初始化的脚本,若 ./__data/mysql/ 文件夹存在数据,则不会执行初始化脚本 + networks: + - nest_admin_net + + redis: + image: redis:alpine + container_name: nest-admin-redis + restart: always + env_file: + - .env + - .env.production + ports: + - '${REDIS_PORT}:6379' + command: > + --requirepass ${REDIS_PASSWORD} + networks: + - nest_admin_net + + nest-admin-server: + # build: 从当前路径构建镜像 + build: + context: . + dockerfile: Dockerfile + container_name: nest-admin-server + restart: always + env_file: + - .env + - .env.production + extra_hosts: + - 'host.docker.internal:host-gateway' + ports: + - '${APP_PORT}:${APP_PORT}' + # 当前服务启动之前先要把depends_on指定的服务启动起来才行 + depends_on: + - mysql + - redis + networks: + - nest_admin_net + +networks: + nest_admin_net: + name: nest_admin_net diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..8f2eb78 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,22 @@ +const { cpus } = require('os') + +const cpuLen = cpus().length + +module.exports = { + apps: [ + { + name: 'nest-admin', + script: './dist/main.js', + autorestart: true, + exec_mode: 'cluster', + watch: false, + instances: cpuLen, + max_memory_restart: '520M', + args: '', + env: { + NODE_ENV: 'production', + PORT: process.env.APP_PORT, + }, + }, + ], +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..c3ca7c3 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,36 @@ +const antfu = require('@antfu/eslint-config').default + +module.exports = antfu({ + stylistic: { + indent: 2, + quotes: 'single', + }, + typescript: true, +}, { + rules: { + 'no-console': 'off', + 'unused-imports/no-unused-vars': 'off', + 'unused-imports/no-unused-imports': 2, + + 'ts/consistent-type-imports': 'off', + 'node/prefer-global/process': 'off', + 'node/prefer-global/buffer': 'off', + + 'import/order': [ + 2, + { + 'pathGroups': [ + { + pattern: '~/**', + group: 'external', + position: 'after', + }, + ], + 'alphabetize': { order: 'asc', caseInsensitive: false }, + 'newlines-between': 'always-and-inside-groups', + 'warnOnUnassignedImports': true, + }, + ], + + }, +}) diff --git a/nest-cli.json b/nest-cli.json new file mode 100644 index 0000000..86963b2 --- /dev/null +++ b/nest-cli.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true, + "assets": [ + { "include": "assets/**/*", "outDir": "dist", "watchAssets": true } + ], + "plugins": [{ + "name": "@nestjs/swagger", + "options": { + "introspectComments": true + } + }] + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e137183 --- /dev/null +++ b/package.json @@ -0,0 +1,174 @@ +{ + "name": "nest-admin", + "version": "2.0.0", + "private": true, + "packageManager": "pnpm@8.10.2", + "license": "MIT", + "engines": { + "node": ">=18", + "pnpm": ">=8.1.0" + }, + "scripts": { + "postinstall": "npm run gen-env-types", + "prebuild": "rimraf dist", + "build": "nest build", + "dev": "npm run start", + "dev:debug": "npm run start:debug", + "repl": "npm run start -- --entryFile repl", + "bundle": "rimraf out && npm run build && ncc build dist/main.js -o out -m -t && chmod +x out/index.js", + "start": "cross-env NODE_ENV=development nest start -w --path tsconfig.json", + "start:debug": "cross-env NODE_ENV=development nest start --debug --watch", + "start:prod": "cross-env NODE_ENV=production node dist/main", + "prod": "cross-env NODE_ENV=production pm2-runtime start ecosystem.config.js", + "prod:pm2": "cross-env NODE_ENV=production pm2 restart ecosystem.config.js", + "prod:stop": "pm2 stop ecosystem.config.js", + "prod:debug": "cross-env NODE_ENV=production nest start --debug --watch", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "doc": "compodoc -p tsconfig.json -s", + "gen-env-types": "npx tsx scripts/genEnvTypes.ts", + "typeorm": "NODE_ENV=development typeorm-ts-node-esm -d ./dist/config/database.config.js", + "migration:create": "npm run typeorm migration:create ./src/migrations/initData", + "migration:generate": "npm run typeorm migration:generate ./src/migrations/update-table_$(echo $npm_package_version | sed 's/\\./_/g')", + "migration:run": "npm run typeorm -- migration:run", + "migration:revert": "npm run typeorm -- migration:revert", + "cleanlog": "rimraf logs", + "docker:build:dev": "docker compose --env-file .env --env-file .env.development up --build", + "docker:build": "docker compose --env-file .env --env-file .env.production up --build", + "docker:up": "docker compose --env-file .env --env-file .env.production up -d --no-build", + "docker:down": "docker compose --env-file .env --env-file .env.production down", + "docker:rmi": "docker compose --env-file .env --env-file .env.production stop nest-admin-server && docker container rm nest-admin-server && docker rmi nest-admin-server", + "docker:logs": "docker compose --env-file .env --env-file .env.production logs -f", + "c": "git add . && git cz && git push", + "release": "standard-version", + "commitlint": "commitlint --config commitlint.config.cjs -e -V", + "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,md}\"" + }, + "dependencies": { + "@fastify/cookie": "^9.3.1", + "@fastify/multipart": "^8.1.0", + "@fastify/static": "^7.0.1", + "@liaoliaots/nestjs-redis": "^9.0.5", + "@nestjs-modules/mailer": "^1.10.3", + "@nestjs/axios": "^3.0.2", + "@nestjs/bull": "^10.1.0", + "@nestjs/cache-manager": "^2.2.1", + "@nestjs/common": "^10.3.3", + "@nestjs/config": "^3.2.0", + "@nestjs/core": "^10.3.3", + "@nestjs/event-emitter": "^2.0.4", + "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.3", + "@nestjs/platform-fastify": "^10.3.3", + "@nestjs/platform-socket.io": "^10.3.3", + "@nestjs/schedule": "^4.0.1", + "@nestjs/swagger": "^7.3.0", + "@nestjs/terminus": "^10.2.2", + "@nestjs/throttler": "^5.1.2", + "@nestjs/typeorm": "^10.0.2", + "@nestjs/websockets": "^10.3.3", + "@socket.io/redis-adapter": "^8.2.1", + "@socket.io/redis-emitter": "^5.1.0", + "@types/lodash": "^4.14.202", + "axios": "^1.6.7", + "bull": "^4.12.2", + "cache-manager": "^5.4.0", + "cache-manager-ioredis-yet": "^1.2.2", + "chalk": "^5.3.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "cron": "^3.1.6", + "cron-parser": "^4.9.0", + "crypto-js": "^4.2.0", + "dayjs": "^1.11.10", + "dotenv": "16.4.4", + "handlebars": "^4.7.8", + "helmet": "^7.1.0", + "ioredis": "^5.3.2", + "lodash": "^4.17.21", + "mysql2": "^3.9.1", + "nanoid": "^3.3.7", + "nodemailer": "^6.9.9", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", + "passport-jwt": "^4.0.1", + "passport-local": "^1.0.0", + "qiniu": "^7.11.0", + "reflect-metadata": "^0.2.1", + "rimraf": "^5.0.5", + "rxjs": "^7.8.1", + "socket.io": "^4.7.4", + "stacktrace-js": "^2.0.2", + "svg-captcha": "^1.4.0", + "systeminformation": "^5.22.0", + "typeorm": "0.3.17", + "ua-parser-js": "^1.0.37", + "winston": "^3.11.0", + "winston-daily-rotate-file": "^5.0.0" + }, + "devDependencies": { + "@antfu/eslint-config": "^2.6.4", + "@compodoc/compodoc": "^1.1.23", + "@nestjs/cli": "^10.3.2", + "@nestjs/schematics": "^10.1.1", + "@nestjs/testing": "^10.3.2", + "@types/cache-manager": "^4.0.6", + "@types/jest": "29.5.12", + "@types/multer": "^1.4.11", + "@types/node": "^20.11.16", + "@types/supertest": "^6.0.2", + "@types/ua-parser-js": "^0.7.39", + "cross-env": "^7.0.3", + "eslint": "^8.56.0", + "jest": "^29.7.0", + "lint-staged": "^15.2.2", + "source-map-support": "^0.5.21", + "supertest": "^6.3.4", + "ts-jest": "^29.1.2", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.3.3", + "cliui": "^8.0.1", + "commitizen": "^4.3.0", + "cz-customizable": "^7.0.0", + "standard-version": "^9.5.0", + "husky": "^8.0.0" + }, + "pnpm": { + "overrides": { + "@liaoliaots/nestjs-redis": "npm:@songkeys/nestjs-redis" + } + }, + "config": { + "commitizen": { + "path": "cz-customizable" + } + }, + "lint-staged": { + "*": [ + "npm run lint" + ] + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "moduleNameMapper": { + "^~/(.*)$": "/$1" + }, + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..f084228 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,12758 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + '@liaoliaots/nestjs-redis': npm:@songkeys/nestjs-redis + +dependencies: + '@fastify/cookie': + specifier: ^9.3.1 + version: 9.3.1 + '@fastify/multipart': + specifier: ^8.1.0 + version: 8.1.0 + '@fastify/static': + specifier: ^7.0.1 + version: 7.0.1 + '@liaoliaots/nestjs-redis': + specifier: npm:@songkeys/nestjs-redis + version: /@songkeys/nestjs-redis@10.0.0(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(ioredis@5.3.2) + '@nestjs-modules/mailer': + specifier: ^1.10.3 + version: 1.10.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(nodemailer@6.9.9) + '@nestjs/axios': + specifier: ^3.0.2 + version: 3.0.2(@nestjs/common@10.3.3)(axios@1.6.7)(rxjs@7.8.1) + '@nestjs/bull': + specifier: ^10.1.0 + version: 10.1.0(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(bull@4.12.2) + '@nestjs/cache-manager': + specifier: ^2.2.1 + version: 2.2.1(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(cache-manager@5.4.0)(rxjs@7.8.1) + '@nestjs/common': + specifier: ^10.3.3 + version: 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/config': + specifier: ^3.2.0 + version: 3.2.0(@nestjs/common@10.3.3)(rxjs@7.8.1) + '@nestjs/core': + specifier: ^10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/event-emitter': + specifier: ^2.0.4 + version: 2.0.4(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) + '@nestjs/jwt': + specifier: ^10.2.0 + version: 10.2.0(@nestjs/common@10.3.3) + '@nestjs/passport': + specifier: ^10.0.3 + version: 10.0.3(@nestjs/common@10.3.3)(passport@0.7.0) + '@nestjs/platform-fastify': + specifier: ^10.3.3 + version: 10.3.3(@fastify/static@7.0.1)(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) + '@nestjs/platform-socket.io': + specifier: ^10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(rxjs@7.8.1) + '@nestjs/schedule': + specifier: ^4.0.1 + version: 4.0.1(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) + '@nestjs/swagger': + specifier: ^7.3.0 + version: 7.3.0(@fastify/static@7.0.1)(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1) + '@nestjs/terminus': + specifier: ^10.2.2 + version: 10.2.2(@nestjs/axios@3.0.2)(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/typeorm@10.0.2)(reflect-metadata@0.2.1)(rxjs@7.8.1)(typeorm@0.3.17) + '@nestjs/throttler': + specifier: ^5.1.2 + version: 5.1.2(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(reflect-metadata@0.2.1) + '@nestjs/typeorm': + specifier: ^10.0.2 + version: 10.0.2(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)(typeorm@0.3.17) + '@nestjs/websockets': + specifier: ^10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-socket.io@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@socket.io/redis-adapter': + specifier: ^8.2.1 + version: 8.2.1(socket.io-adapter@2.5.2) + '@socket.io/redis-emitter': + specifier: ^5.1.0 + version: 5.1.0 + '@types/lodash': + specifier: ^4.14.202 + version: 4.14.202 + axios: + specifier: ^1.6.7 + version: 1.6.7 + bull: + specifier: ^4.12.2 + version: 4.12.2 + cache-manager: + specifier: ^5.4.0 + version: 5.4.0 + cache-manager-ioredis-yet: + specifier: ^1.2.2 + version: 1.2.2 + chalk: + specifier: ^5.3.0 + version: 5.3.0 + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + class-validator: + specifier: ^0.14.1 + version: 0.14.1 + cron: + specifier: ^3.1.6 + version: 3.1.6 + cron-parser: + specifier: ^4.9.0 + version: 4.9.0 + crypto-js: + specifier: ^4.2.0 + version: 4.2.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.10 + dotenv: + specifier: 16.4.4 + version: 16.4.4 + handlebars: + specifier: ^4.7.8 + version: 4.7.8 + helmet: + specifier: ^7.1.0 + version: 7.1.0 + ioredis: + specifier: ^5.3.2 + version: 5.3.2 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + mysql2: + specifier: ^3.9.1 + version: 3.9.1 + nanoid: + specifier: ^3.3.7 + version: 3.3.7 + nodemailer: + specifier: ^6.9.9 + version: 6.9.9 + passport: + specifier: ^0.7.0 + version: 0.7.0 + passport-google-oauth20: + specifier: ^2.0.0 + version: 2.0.0 + passport-jwt: + specifier: ^4.0.1 + version: 4.0.1 + passport-local: + specifier: ^1.0.0 + version: 1.0.0 + qiniu: + specifier: ^7.11.0 + version: 7.11.0 + reflect-metadata: + specifier: ^0.2.1 + version: 0.2.1 + rimraf: + specifier: ^5.0.5 + version: 5.0.5 + rxjs: + specifier: ^7.8.1 + version: 7.8.1 + socket.io: + specifier: ^4.7.4 + version: 4.7.4 + stacktrace-js: + specifier: ^2.0.2 + version: 2.0.2 + svg-captcha: + specifier: ^1.4.0 + version: 1.4.0 + systeminformation: + specifier: ^5.22.0 + version: 5.22.0 + typeorm: + specifier: 0.3.17 + version: 0.3.17(ioredis@5.3.2)(mysql2@3.9.1)(ts-node@10.9.2) + ua-parser-js: + specifier: ^1.0.37 + version: 1.0.37 + winston: + specifier: ^3.11.0 + version: 3.11.0 + winston-daily-rotate-file: + specifier: ^5.0.0 + version: 5.0.0(winston@3.11.0) + +devDependencies: + '@antfu/eslint-config': + specifier: ^2.6.4 + version: 2.6.4(@vue/compiler-sfc@3.4.19)(eslint@8.56.0)(typescript@5.3.3) + '@compodoc/compodoc': + specifier: ^1.1.23 + version: 1.1.23(typescript@5.3.3) + '@nestjs/cli': + specifier: ^10.3.2 + version: 10.3.2 + '@nestjs/schematics': + specifier: ^10.1.1 + version: 10.1.1(chokidar@3.6.0)(typescript@5.3.3) + '@nestjs/testing': + specifier: ^10.3.2 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) + '@types/cache-manager': + specifier: ^4.0.6 + version: 4.0.6 + '@types/jest': + specifier: 29.5.12 + version: 29.5.12 + '@types/multer': + specifier: ^1.4.11 + version: 1.4.11 + '@types/node': + specifier: ^20.11.16 + version: 20.11.18 + '@types/supertest': + specifier: ^6.0.2 + version: 6.0.2 + '@types/ua-parser-js': + specifier: ^0.7.39 + version: 0.7.39 + cliui: + specifier: ^8.0.1 + version: 8.0.1 + commitizen: + specifier: ^4.3.0 + version: 4.3.0(@types/node@20.11.18)(typescript@5.3.3) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + cz-customizable: + specifier: ^7.0.0 + version: 7.0.0 + eslint: + specifier: ^8.56.0 + version: 8.56.0 + husky: + specifier: ^8.0.0 + version: 8.0.3 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@20.11.18)(ts-node@10.9.2) + lint-staged: + specifier: ^15.2.2 + version: 15.2.2 + source-map-support: + specifier: ^0.5.21 + version: 0.5.21 + standard-version: + specifier: ^9.5.0 + version: 9.5.0 + supertest: + specifier: ^6.3.4 + version: 6.3.4 + ts-jest: + specifier: ^29.1.2 + version: 29.1.2(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.3.3) + ts-loader: + specifier: ^9.5.1 + version: 9.5.1(typescript@5.3.3)(webpack@5.90.1) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.11.18)(typescript@5.3.3) + tsconfig-paths: + specifier: ^4.2.0 + version: 4.2.0 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@aduh95/viz.js@3.4.0: + resolution: {integrity: sha512-KI2nVf9JdwWCXqK6RVf+9/096G7VWN4Z84mnynlyZKao2xQENW8WNEjLmvdlxS5X8PNWXFC1zqwm7tveOXw/4A==} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.22 + dev: true + + /@angular-devkit/core@14.2.12(chokidar@3.6.0): + resolution: {integrity: sha512-tg1+deEZdm3fgk2BQ6y7tujciL6qhtN5Ums266lX//kAZeZ4nNNXTBT+oY5xgfjvmLbW+xKg0XZrAS0oIRKY5g==} + engines: {node: ^14.15.0 || >=16.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + ajv: 8.11.0 + ajv-formats: 2.1.1(ajv@8.11.0) + chokidar: 3.6.0 + jsonc-parser: 3.1.0 + rxjs: 6.6.7 + source-map: 0.7.4 + dev: true + + /@angular-devkit/core@17.1.2(chokidar@3.6.0): + resolution: {integrity: sha512-ku+/W/HMCBacSWFppenr9y6Lx8mDuTuQvn1IkTyBLiJOpWnzgVbx9kHDeaDchGa1PwLlJUBBrv27t3qgJOIDPw==} + engines: {node: ^18.13.0 || >=20.9.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + chokidar: 3.6.0 + jsonc-parser: 3.2.0 + picomatch: 3.0.1 + rxjs: 7.8.1 + source-map: 0.7.4 + dev: true + + /@angular-devkit/schematics-cli@17.1.2(chokidar@3.6.0): + resolution: {integrity: sha512-bvXykYzSST05qFdlgIzUguNOb3z0hCa8HaTwtqdmQo9aFPf+P+/AC56I64t1iTchMjQtf3JrBQhYM25gUdcGbg==} + engines: {node: ^18.13.0 || >=20.9.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + hasBin: true + dependencies: + '@angular-devkit/core': 17.1.2(chokidar@3.6.0) + '@angular-devkit/schematics': 17.1.2(chokidar@3.6.0) + ansi-colors: 4.1.3 + inquirer: 9.2.12 + symbol-observable: 4.0.0 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@angular-devkit/schematics@14.2.12(chokidar@3.6.0): + resolution: {integrity: sha512-MN5yGR+SSSPPBBVMf4cifDJn9u0IYvxiHst+HWokH2AkBYy+vB1x8jYES2l1wkiISD7nvjTixfqX+Y95oMBoLg==} + engines: {node: ^14.15.0 || >=16.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + dependencies: + '@angular-devkit/core': 14.2.12(chokidar@3.6.0) + jsonc-parser: 3.1.0 + magic-string: 0.26.2 + ora: 5.4.1 + rxjs: 6.6.7 + transitivePeerDependencies: + - chokidar + dev: true + + /@angular-devkit/schematics@17.1.2(chokidar@3.6.0): + resolution: {integrity: sha512-8S9RuM8olFN/gwN+mjbuF1CwHX61f0i59EGXz9tXLnKRUTjsRR+8vVMTAmX0dvVAT5fJTG/T69X+HX7FeumdqA==} + engines: {node: ^18.13.0 || >=20.9.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + dependencies: + '@angular-devkit/core': 17.1.2(chokidar@3.6.0) + jsonc-parser: 3.2.0 + magic-string: 0.30.5 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@antfu/eslint-config@2.6.4(@vue/compiler-sfc@3.4.19)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-dMD/QC5KWS1OltdpKLhfZM7W7y7zils85opk8d4lyNr7yn0OFjZs7eMYtcC6DrrN2kQ1JrFvBM7uB0QdWn5PUQ==} + hasBin: true + peerDependencies: + '@unocss/eslint-plugin': '>=0.50.0' + eslint: '>=8.40.0' + eslint-plugin-format: '>=0.1.0' + eslint-plugin-react: ^7.33.2 + eslint-plugin-react-hooks: ^4.6.0 + eslint-plugin-react-refresh: ^0.4.4 + eslint-plugin-svelte: ^2.34.1 + svelte-eslint-parser: ^0.33.1 + peerDependenciesMeta: + '@unocss/eslint-plugin': + optional: true + eslint-plugin-format: + optional: true + eslint-plugin-react: + optional: true + eslint-plugin-react-hooks: + optional: true + eslint-plugin-react-refresh: + optional: true + eslint-plugin-svelte: + optional: true + svelte-eslint-parser: + optional: true + dependencies: + '@antfu/eslint-define-config': 1.23.0-2 + '@antfu/install-pkg': 0.3.1 + '@eslint-types/jsdoc': 46.8.2-1 + '@eslint-types/typescript-eslint': 6.21.0 + '@eslint-types/unicorn': 50.0.1 + '@stylistic/eslint-plugin': 1.6.2(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-flat-gitignore: 0.1.3 + eslint-merge-processors: 0.1.0(eslint@8.56.0) + eslint-plugin-antfu: 2.1.2(eslint@8.56.0) + eslint-plugin-eslint-comments: 3.2.0(eslint@8.56.0) + eslint-plugin-i: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint@8.56.0) + eslint-plugin-jsdoc: 48.1.0(eslint@8.56.0) + eslint-plugin-jsonc: 2.13.0(eslint@8.56.0) + eslint-plugin-markdown: 3.0.1(eslint@8.56.0) + eslint-plugin-n: 16.6.2(eslint@8.56.0) + eslint-plugin-no-only-tests: 3.1.0 + eslint-plugin-perfectionist: 2.5.0(eslint@8.56.0)(typescript@5.3.3)(vue-eslint-parser@9.4.2) + eslint-plugin-toml: 0.9.2(eslint@8.56.0) + eslint-plugin-unicorn: 50.0.1(eslint@8.56.0) + eslint-plugin-unused-imports: 3.1.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0) + eslint-plugin-vitest: 0.3.22(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-vue: 9.21.1(eslint@8.56.0) + eslint-plugin-yml: 1.12.2(eslint@8.56.0) + eslint-processor-vue-blocks: 0.1.1(@vue/compiler-sfc@3.4.19)(eslint@8.56.0) + globals: 13.24.0 + jsonc-eslint-parser: 2.4.0 + local-pkg: 0.5.0 + parse-gitignore: 2.0.0 + picocolors: 1.0.0 + prompts: 2.4.2 + toml-eslint-parser: 0.9.3 + vue-eslint-parser: 9.4.2(eslint@8.56.0) + yaml-eslint-parser: 1.2.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@vue/compiler-sfc' + - astro-eslint-parser + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - svelte + - typescript + - vitest + dev: true + + /@antfu/eslint-define-config@1.23.0-2: + resolution: {integrity: sha512-LvxY21+ZhpuBf/aHeBUtGQhSEfad4PkNKXKvDOSvukaM3XVTfBhwmHX2EKwAsdq5DlfjbT3qqYyMiueBIO5iDQ==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>= 8.6.0'} + dev: true + + /@antfu/install-pkg@0.3.1: + resolution: {integrity: sha512-A3zWY9VeTPnxlMiZtsGHw2lSd3ghwvL8s9RiGOtqvDxhhFfZ781ynsGBa/iUnDJ5zBrmTFQrJDud3TGgRISaxw==} + dependencies: + execa: 8.0.1 + dev: true + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.23.5: + resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.9: + resolution: {integrity: sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9) + '@babel/helpers': 7.23.9 + '@babel/parser': 7.23.9 + '@babel/template': 7.23.9 + '@babel/traverse': 7.23.9 + '@babel/types': 7.23.9 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.6: + resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.22 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: + resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.23.0 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-create-class-features-plugin@7.23.10(@babel/core@7.23.9): + resolution: {integrity: sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.9) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + semver: 6.3.1 + dev: true + + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.9): + resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-annotate-as-pure': 7.22.5 + regexpu-core: 5.3.2 + semver: 6.3.1 + dev: true + + /@babel/helper-define-polyfill-provider@0.5.0(@babel/core@7.23.9): + resolution: {integrity: sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.23.9 + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-optimise-call-expression@7.22.5: + resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.9): + resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-wrap-function': 7.22.20 + dev: true + + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.9): + resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-skip-transparent-expression-wrappers@7.22.5: + resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-wrap-function@7.22.20: + resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-function-name': 7.23.0 + '@babel/template': 7.23.9 + '@babel/types': 7.23.9 + dev: true + + /@babel/helpers@7.23.9: + resolution: {integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.23.9 + '@babel/traverse': 7.23.9 + '@babel/types': 7.23.9 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.23.9: + resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.9 + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.9) + dev: true + + /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.7(@babel/core@7.23.9): + resolution: {integrity: sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.23.9): + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-class-features-plugin': 7.23.10(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.9): + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.9): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.9): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.9): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.9): + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.9): + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.9): + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.9): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.9): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.9): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.9): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.9): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.9): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.9): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.9): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.9): + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.9): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.9): + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-async-generator-functions@7.23.9(@babel/core@7.23.9): + resolution: {integrity: sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.9) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-class-features-plugin': 7.23.10(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-class-features-plugin': 7.23.10(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-classes@7.23.8(@babel/core@7.23.9): + resolution: {integrity: sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.9) + '@babel/helper-split-export-declaration': 7.22.6 + globals: 11.12.0 + dev: true + + /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/template': 7.23.9 + dev: true + + /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-for-of@7.23.6(@babel/core@7.23.9): + resolution: {integrity: sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + dev: true + + /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-simple-access': 7.22.5 + dev: true + + /@babel/plugin-transform-modules-systemjs@7.23.9(@babel/core@7.23.9): + resolution: {integrity: sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.9): + resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/core': 7.23.9 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-class-features-plugin': 7.23.10(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.9): + resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.23.10(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.9) + dev: true + + /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + regenerator-transform: 0.15.2 + dev: true + + /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + dev: true + + /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.9): + resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.9) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/preset-env@7.23.9(@babel/core@7.23.9): + resolution: {integrity: sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/core': 7.23.9 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.23.5 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.7(@babel/core@7.23.9) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.9) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.9) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.9) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.9) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.9) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.9) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.9) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.9) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.9) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.9) + '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-async-generator-functions': 7.23.9(@babel/core@7.23.9) + '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-classes': 7.23.8(@babel/core@7.23.9) + '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-for-of': 7.23.6(@babel/core@7.23.9) + '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-modules-systemjs': 7.23.9(@babel/core@7.23.9) + '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.9) + '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.9) + '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.9) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.9) + babel-plugin-polyfill-corejs2: 0.4.8(@babel/core@7.23.9) + babel-plugin-polyfill-corejs3: 0.9.0(@babel/core@7.23.9) + babel-plugin-polyfill-regenerator: 0.5.5(@babel/core@7.23.9) + core-js-compat: 3.36.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.9): + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/types': 7.23.9 + esutils: 2.0.3 + dev: true + + /@babel/regjsgen@0.8.0: + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} + dev: true + + /@babel/runtime@7.23.9: + resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + + /@babel/template@7.23.9: + resolution: {integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.23.9 + '@babel/types': 7.23.9 + dev: true + + /@babel/traverse@7.23.9: + resolution: {integrity: sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.9 + '@babel/types': 7.23.9 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.23.9: + resolution: {integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@colors/colors@1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + requiresBuild: true + dev: true + optional: true + + /@colors/colors@1.6.0: + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + dev: false + + /@commitlint/config-validator@18.6.1: + resolution: {integrity: sha512-05uiToBVfPhepcQWE1ZQBR/Io3+tb3gEotZjnI4tTzzPk16NffN6YABgwFQCLmzZefbDcmwWqJWc2XT47q7Znw==} + engines: {node: '>=v18'} + requiresBuild: true + dependencies: + '@commitlint/types': 18.6.1 + ajv: 8.12.0 + dev: true + optional: true + + /@commitlint/execute-rule@18.6.1: + resolution: {integrity: sha512-7s37a+iWyJiGUeMFF6qBlyZciUkF8odSAnHijbD36YDctLhGKoYltdvuJ/AFfRm6cBLRtRk9cCVPdsEFtt/2rg==} + engines: {node: '>=v18'} + requiresBuild: true + dev: true + optional: true + + /@commitlint/load@18.6.1(@types/node@20.11.18)(typescript@5.3.3): + resolution: {integrity: sha512-p26x8734tSXUHoAw0ERIiHyW4RaI4Bj99D8YgUlVV9SedLf8hlWAfyIFhHRIhfPngLlCe0QYOdRKYFt8gy56TA==} + engines: {node: '>=v18'} + requiresBuild: true + dependencies: + '@commitlint/config-validator': 18.6.1 + '@commitlint/execute-rule': 18.6.1 + '@commitlint/resolve-extends': 18.6.1 + '@commitlint/types': 18.6.1 + chalk: 4.1.2 + cosmiconfig: 8.3.6(typescript@5.3.3) + cosmiconfig-typescript-loader: 5.0.0(@types/node@20.11.18)(cosmiconfig@8.3.6)(typescript@5.3.3) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + resolve-from: 5.0.0 + transitivePeerDependencies: + - '@types/node' + - typescript + dev: true + optional: true + + /@commitlint/resolve-extends@18.6.1: + resolution: {integrity: sha512-ifRAQtHwK+Gj3Bxj/5chhc4L2LIc3s30lpsyW67yyjsETR6ctHAHRu1FSpt0KqahK5xESqoJ92v6XxoDRtjwEQ==} + engines: {node: '>=v18'} + requiresBuild: true + dependencies: + '@commitlint/config-validator': 18.6.1 + '@commitlint/types': 18.6.1 + import-fresh: 3.3.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + dev: true + optional: true + + /@commitlint/types@18.6.1: + resolution: {integrity: sha512-gwRLBLra/Dozj2OywopeuHj2ac26gjGkz2cZ+86cTJOdtWfiRRr4+e77ZDAGc6MDWxaWheI+mAV5TLWWRwqrFg==} + engines: {node: '>=v18'} + requiresBuild: true + dependencies: + chalk: 4.1.2 + dev: true + optional: true + + /@compodoc/compodoc@1.1.23(typescript@5.3.3): + resolution: {integrity: sha512-5Zfx+CHKTxLD+TxCGt1U8krnEBCWPVxCLt3jCJEN55AzhTluo8xlMenaXlJsuVqL4Lmo/OTTzEXrm9zoQKh/3w==} + engines: {node: '>= 14.0.0'} + hasBin: true + requiresBuild: true + dependencies: + '@angular-devkit/schematics': 14.2.12(chokidar@3.6.0) + '@babel/core': 7.23.9 + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.23.9) + '@babel/preset-env': 7.23.9(@babel/core@7.23.9) + '@compodoc/live-server': 1.2.3 + '@compodoc/ngd-transformer': 2.1.3 + bootstrap.native: 5.0.11 + chalk: 4.1.2 + cheerio: 1.0.0-rc.12 + chokidar: 3.6.0 + colors: 1.4.0 + commander: 11.1.0 + cosmiconfig: 8.3.6(typescript@5.3.3) + decache: 4.6.2 + es6-shim: 0.35.8 + fancy-log: 2.0.0 + fast-glob: 3.3.2 + fs-extra: 11.2.0 + glob: 10.3.10 + handlebars: 4.7.8 + html-entities: 2.4.0 + i18next: 23.8.2 + json5: 2.2.3 + lodash: 4.17.21 + loglevel: 1.9.1 + loglevel-plugin-prefix: 0.8.4 + lunr: 2.3.9 + marked: 7.0.3 + minimist: 1.2.8 + opencollective-postinstall: 2.0.3 + os-name: 4.0.1 + pdfjs-dist: 2.12.313 + pdfmake: 0.2.9 + prismjs: 1.29.0 + semver: 7.6.0 + svg-pan-zoom: 3.6.1 + tablesort: 5.3.0 + traverse: 0.6.8 + ts-morph: 20.0.0 + uuid: 9.0.1 + vis: 4.21.0-EOL + zepto: 1.2.0 + transitivePeerDependencies: + - supports-color + - typescript + - worker-loader + dev: true + + /@compodoc/live-server@1.2.3: + resolution: {integrity: sha512-hDmntVCyjjaxuJzPzBx68orNZ7TW4BtHWMnXlIVn5dqhK7vuFF/11hspO1cMmc+2QTYgqde1TBcb3127S7Zrow==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + chokidar: 3.6.0 + colors: 1.4.0 + connect: 3.7.0 + cors: 2.8.5 + event-stream: 4.0.1 + faye-websocket: 0.11.4 + http-auth: 4.1.9 + http-auth-connect: 1.0.6 + morgan: 1.10.0 + object-assign: 4.1.1 + open: 8.4.0 + proxy-middleware: 0.15.0 + send: 0.18.0 + serve-index: 1.9.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@compodoc/ngd-core@2.1.1: + resolution: {integrity: sha512-Z+wE6wWZYVnudRYg6qunDlyh3Orw39Ib66Gvrz5kX5u7So+iu3tr6sQJdqH6yGS3hAjig5avlfhWLlgsb6/x1Q==} + engines: {node: '>= 10.0.0'} + dependencies: + ansi-colors: 4.1.3 + fancy-log: 2.0.0 + typescript: 5.3.3 + dev: true + + /@compodoc/ngd-transformer@2.1.3: + resolution: {integrity: sha512-oWxJza7CpWR8/FeWYfE6j+jgncnGBsTWnZLt5rD2GUpsGSQTuGrsFPnmbbaVLgRS5QIVWBJYke7QFBr/7qVMWg==} + engines: {node: '>= 10.0.0'} + dependencies: + '@aduh95/viz.js': 3.4.0 + '@compodoc/ngd-core': 2.1.1 + dot: 2.0.0-beta.1 + fs-extra: 11.2.0 + dev: true + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + /@dabh/diagnostics@2.0.3: + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + dev: false + + /@es-joy/jsdoccomment@0.42.0: + resolution: {integrity: sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==} + engines: {node: '>=16'} + dependencies: + comment-parser: 1.4.1 + esquery: 1.5.0 + jsdoc-type-pratt-parser: 4.0.0 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.56.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint-types/jsdoc@46.8.2-1: + resolution: {integrity: sha512-FwD7V0xX0jyaqj8Ul5ZY+TAAPohDfVqtbuXJNHb+OIv1aTIqZi5+Zn3F2UwQ5O3BnQd2mTduyK0+HjGx3/AMFg==} + dev: true + + /@eslint-types/typescript-eslint@6.21.0: + resolution: {integrity: sha512-ao4TdMLw+zFdAJ9q6iBBxC5GSrJ14Hpv0VKaergr++jRTDaGgoYiAq84tx1FYqUJzQgzJC7dm6s52IAQP7EiHA==} + dev: true + + /@eslint-types/unicorn@50.0.1: + resolution: {integrity: sha512-nuJuipTNcg9f+oxZ+3QZw4tuDLmir4RJOPfM/oujgToiy1s+tePDZhwg5jUGc3q8OzTtPbVpsFSYX7QApjO3EA==} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.56.0: + resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@fastify/accept-negotiator@1.1.0: + resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==} + engines: {node: '>=14'} + dev: false + + /@fastify/ajv-compiler@3.5.0: + resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==} + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + fast-uri: 2.3.0 + dev: false + + /@fastify/busboy@1.2.1: + resolution: {integrity: sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==} + engines: {node: '>=14'} + dependencies: + text-decoding: 1.0.0 + dev: false + + /@fastify/cookie@9.3.1: + resolution: {integrity: sha512-h1NAEhB266+ZbZ0e9qUE6NnNR07i7DnNXWG9VbbZ8uC6O/hxHpl+Zoe5sw1yfdZ2U6XhToUGDnzQtWJdCaPwfg==} + dependencies: + cookie-signature: 1.2.1 + fastify-plugin: 4.5.1 + dev: false + + /@fastify/cors@9.0.1: + resolution: {integrity: sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==} + dependencies: + fastify-plugin: 4.5.1 + mnemonist: 0.39.6 + dev: false + + /@fastify/deepmerge@1.3.0: + resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==} + dev: false + + /@fastify/error@3.4.1: + resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} + dev: false + + /@fastify/fast-json-stringify-compiler@4.3.0: + resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + dependencies: + fast-json-stringify: 5.12.0 + dev: false + + /@fastify/formbody@7.4.0: + resolution: {integrity: sha512-H3C6h1GN56/SMrZS8N2vCT2cZr7mIHzBHzOBa5OPpjfB/D6FzP9mMpE02ZzrFX0ANeh0BAJdoXKOF2e7IbV+Og==} + dependencies: + fast-querystring: 1.1.2 + fastify-plugin: 4.5.1 + dev: false + + /@fastify/merge-json-schemas@0.1.1: + resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + dependencies: + fast-deep-equal: 3.1.3 + dev: false + + /@fastify/middie@8.3.0: + resolution: {integrity: sha512-h+zBxCzMlkEkh4fM7pZaSGzqS7P9M0Z6rXnWPdUEPfe7x1BCj++wEk/pQ5jpyYY4pF8AknFqb77n7uwh8HdxEA==} + dependencies: + '@fastify/error': 3.4.1 + fastify-plugin: 4.5.1 + path-to-regexp: 6.2.1 + reusify: 1.0.4 + dev: false + + /@fastify/multipart@8.1.0: + resolution: {integrity: sha512-sRX9X4ZhAqRbe2kDvXY2NK7i6Wf1Rm2g/CjpGYYM7+Np8E6uWQXcj761j08qPfPO8PJXM+vJ7yrKbK1GPB+OeQ==} + dependencies: + '@fastify/busboy': 1.2.1 + '@fastify/deepmerge': 1.3.0 + '@fastify/error': 3.4.1 + fastify-plugin: 4.5.1 + secure-json-parse: 2.7.0 + stream-wormhole: 1.1.0 + dev: false + + /@fastify/send@2.1.0: + resolution: {integrity: sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==} + dependencies: + '@lukeed/ms': 2.0.2 + escape-html: 1.0.3 + fast-decode-uri-component: 1.0.1 + http-errors: 2.0.0 + mime: 3.0.0 + dev: false + + /@fastify/static@7.0.1: + resolution: {integrity: sha512-i1p/nELMknAisNfnjo7yhfoUOdKzA+n92QaMirv2NkZrJ1Wl12v2nyTYlDwPN8XoStMBAnRK/Kx6zKmfrXUPXw==} + dependencies: + '@fastify/accept-negotiator': 1.1.0 + '@fastify/send': 2.1.0 + content-disposition: 0.5.4 + fastify-plugin: 4.5.1 + fastq: 1.17.1 + glob: 10.3.10 + dev: false + + /@foliojs-fork/fontkit@1.9.1: + resolution: {integrity: sha512-U589voc2/ROnvx1CyH9aNzOQWJp127JGU1QAylXGQ7LoEAF6hMmahZLQ4eqAcgHUw+uyW4PjtCItq9qudPkK3A==} + dependencies: + '@foliojs-fork/restructure': 2.0.2 + brfs: 2.0.2 + brotli: 1.3.3 + browserify-optional: 1.0.1 + clone: 1.0.4 + deep-equal: 1.1.2 + dfa: 1.2.0 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + dev: true + + /@foliojs-fork/linebreak@1.1.1: + resolution: {integrity: sha512-pgY/+53GqGQI+mvDiyprvPWgkTlVBS8cxqee03ejm6gKAQNsR1tCYCIvN9FHy7otZajzMqCgPOgC4cHdt4JPig==} + dependencies: + base64-js: 1.3.1 + brfs: 2.0.2 + unicode-trie: 2.0.0 + dev: true + + /@foliojs-fork/pdfkit@0.14.0: + resolution: {integrity: sha512-nMOiQAv6id89MT3tVTCgc7HxD5ZMANwio2o5yvs5sexQkC0KI3BLaLakpsrHmFfeGFAhqPmZATZGbJGXTUebpg==} + dependencies: + '@foliojs-fork/fontkit': 1.9.1 + '@foliojs-fork/linebreak': 1.1.1 + crypto-js: 4.2.0 + png-js: 1.0.0 + dev: true + + /@foliojs-fork/restructure@2.0.2: + resolution: {integrity: sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==} + dev: true + + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.2 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + dev: true + + /@hutson/parse-repository-url@3.0.2: + resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} + engines: {node: '>=6.9.0'} + dev: true + + /@ioredis/commands@1.2.0: + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + dev: false + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/console@29.7.0: + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/core@29.7.0(ts-node@10.9.2): + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.11.18)(ts-node@10.9.2) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /@jest/environment@29.7.0: + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + jest-mock: 29.7.0 + dev: true + + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + dev: true + + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers@29.7.0: + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 20.11.18 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.7.0: + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.22 + '@types/node': 20.11.18 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.6 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jest/source-map@29.6.3: + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.22 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.7.0: + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@29.7.0: + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.9 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.22 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.11.18 + '@types/yargs': 17.0.32 + chalk: 4.1.2 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.22 + dev: true + + /@jridgewell/resolve-uri@3.1.2: + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map@0.3.5: + resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.22 + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + /@jridgewell/trace-mapping@0.3.22: + resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + /@ljharb/through@2.3.12: + resolution: {integrity: sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + dev: true + + /@lukeed/csprng@1.1.0: + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + + /@lukeed/ms@2.0.2: + resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} + engines: {node: '>=8'} + dev: false + + /@microsoft/tsdoc@0.14.2: + resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + dev: false + + /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2: + resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2: + resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2: + resolution: {integrity: sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2: + resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2: + resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2: + resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@nestjs-modules/mailer@1.10.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(nodemailer@6.9.9): + resolution: {integrity: sha512-k2gs2NH8Ygq4JnETX+EDBXixLAS8DDZEI/Wbr9LGL3HwO3Qz8zVh8dBJ4ESpySuWniW+a8rARzGXtTUHC4KFlw==} + peerDependencies: + '@nestjs/common': '>=7.0.9' + '@nestjs/core': '>=7.0.9' + nodemailer: '>=6.4.6' + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + css-inline: 0.11.2 + glob: 10.3.10 + mjml: 4.14.1 + nodemailer: 6.9.9 + preview-email: 3.0.19 + optionalDependencies: + '@types/ejs': 3.1.5 + '@types/pug': 2.0.10 + ejs: 3.1.9 + handlebars: 4.7.8 + pug: 3.0.2 + transitivePeerDependencies: + - encoding + dev: false + + /@nestjs/axios@3.0.2(@nestjs/common@10.3.3)(axios@1.6.7)(rxjs@7.8.1): + resolution: {integrity: sha512-Z6GuOUdNQjP7FX+OuV2Ybyamse+/e0BFdTWBX5JxpBDKA+YkdLynDgG6HTF04zy6e9zPa19UX0WA2VDoehwhXQ==} + peerDependencies: + '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + axios: ^1.3.1 + rxjs: ^6.0.0 || ^7.0.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + axios: 1.6.7 + rxjs: 7.8.1 + dev: false + + /@nestjs/bull-shared@10.1.0(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-E1lAvVTCwbtBXySElkVrleXzr1bNuTCOLaQ1GmLSQGGlzXIvrXFXEIS1Dh1JCULICC25b7rGOfD3yL7uKRaMzw==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + tslib: 2.6.2 + dev: false + + /@nestjs/bull@10.1.0(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(bull@4.12.2): + resolution: {integrity: sha512-JEw4eFCtgECg1A9UGxa8eJtaxjwSk2XPLAG1xahZGnoozAYlDzvO6W6mFpCbKvoBbNSh1p+p+lccUbrbQnUd8w==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + bull: ^3.3 || ^4.0.0 + dependencies: + '@nestjs/bull-shared': 10.1.0(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + bull: 4.12.2 + tslib: 2.6.2 + dev: false + + /@nestjs/cache-manager@2.2.1(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(cache-manager@5.4.0)(rxjs@7.8.1): + resolution: {integrity: sha512-mXj0zenuyMPJICokwVud4Kjh0+pzBNBAgfpx3I48LozNkd8Qfv/MAhZsb15GihGpbFRxafUo3p6XvtAqRm8GRw==} + peerDependencies: + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + cache-manager: <=5 + rxjs: ^7.0.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + cache-manager: 5.4.0 + rxjs: 7.8.1 + dev: false + + /@nestjs/cli@10.3.2: + resolution: {integrity: sha512-aWmD1GLluWrbuC4a1Iz/XBk5p74Uj6nIVZj6Ov03JbTfgtWqGFLtXuMetvzMiHxfrHehx/myt2iKAPRhKdZvTg==} + engines: {node: '>= 16.14'} + hasBin: true + peerDependencies: + '@swc/cli': ^0.1.62 || ^0.3.0 + '@swc/core': ^1.3.62 + peerDependenciesMeta: + '@swc/cli': + optional: true + '@swc/core': + optional: true + dependencies: + '@angular-devkit/core': 17.1.2(chokidar@3.6.0) + '@angular-devkit/schematics': 17.1.2(chokidar@3.6.0) + '@angular-devkit/schematics-cli': 17.1.2(chokidar@3.6.0) + '@nestjs/schematics': 10.1.1(chokidar@3.6.0)(typescript@5.3.3) + chalk: 4.1.2 + chokidar: 3.6.0 + cli-table3: 0.6.3 + commander: 4.1.1 + fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.3.3)(webpack@5.90.1) + glob: 10.3.10 + inquirer: 8.2.6 + node-emoji: 1.11.0 + ora: 5.4.1 + rimraf: 4.4.1 + shelljs: 0.8.5 + source-map-support: 0.5.21 + tree-kill: 1.2.2 + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.1.0 + typescript: 5.3.3 + webpack: 5.90.1 + webpack-node-externals: 3.0.0 + transitivePeerDependencies: + - esbuild + - uglify-js + - webpack-cli + dev: true + + /@nestjs/common@10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + class-transformer: 0.5.1 + class-validator: 0.14.1 + iterare: 1.2.1 + reflect-metadata: 0.2.1 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + + /@nestjs/config@3.2.0(@nestjs/common@10.3.3)(rxjs@7.8.1): + resolution: {integrity: sha512-BpYRn57shg7CH35KGT6h+hT7ZucB6Qn2B3NBNdvhD4ApU8huS5pX/Wc2e/aO5trIha606Bz2a9t9/vbiuTBTww==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + rxjs: ^7.1.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + dotenv: 16.4.1 + dotenv-expand: 10.0.0 + lodash: 4.17.21 + rxjs: 7.8.1 + uuid: 9.0.1 + dev: false + + /@nestjs/core@10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==} + requiresBuild: true + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/websockets': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-socket.io@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.2.0 + reflect-metadata: 0.2.1 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + transitivePeerDependencies: + - encoding + + /@nestjs/event-emitter@2.0.4(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-quMiw8yOwoSul0pp3mOonGz8EyXWHSBTqBy8B0TbYYgpnG1Ix2wGUnuTksLWaaBiiOTDhciaZ41Y5fJZsSJE1Q==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + eventemitter2: 6.4.9 + dev: false + + /@nestjs/jwt@10.2.0(@nestjs/common@10.3.3): + resolution: {integrity: sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@types/jsonwebtoken': 9.0.5 + jsonwebtoken: 9.0.2 + dev: false + + /@nestjs/mapped-types@2.0.5(@nestjs/common@10.3.3)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1): + resolution: {integrity: sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + class-transformer: ^0.4.0 || ^0.5.0 + class-validator: ^0.13.0 || ^0.14.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + class-transformer: 0.5.1 + class-validator: 0.14.1 + reflect-metadata: 0.2.1 + dev: false + + /@nestjs/passport@10.0.3(@nestjs/common@10.3.3)(passport@0.7.0): + resolution: {integrity: sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + passport: ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + passport: 0.7.0 + dev: false + + /@nestjs/platform-fastify@10.3.3(@fastify/static@7.0.1)(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-OTKcKGnWWrSk/nDl5bFmv2gcPhbF6nsU/EHxkh6tguc0YY4aopQR9GaodseJn8isEOtZzcx8UUBsnLTtqWKxaA==} + peerDependencies: + '@fastify/static': ^6.0.0 || ^7.0.0 + '@fastify/view': ^7.0.0 || ^8.0.0 + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + peerDependenciesMeta: + '@fastify/static': + optional: true + '@fastify/view': + optional: true + dependencies: + '@fastify/cors': 9.0.1 + '@fastify/formbody': 7.4.0 + '@fastify/middie': 8.3.0 + '@fastify/static': 7.0.1 + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + fastify: 4.26.0 + light-my-request: 5.11.0 + path-to-regexp: 3.2.0 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: false + + /@nestjs/platform-socket.io@10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(rxjs@7.8.1): + resolution: {integrity: sha512-QqM9BMTdYPvXOqx3oWrv130HOtc2krPvfgqgDsPWkBLfR+TssrA5QDaTW8HSjEQAfmugvHwhEAAU4+yXRl6tKg==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + rxjs: ^7.1.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/websockets': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-socket.io@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + rxjs: 7.8.1 + socket.io: 4.7.4 + tslib: 2.6.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + /@nestjs/schedule@4.0.1(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-cz2FNjsuoma+aGsG0cMmG6Dqg/BezbBWet1UTHtAuu6d2mXNTVcmoEQM2DIVG5Lfwb2hfSE2yZt8Moww+7y+mA==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + cron: 3.1.6 + uuid: 9.0.1 + dev: false + + /@nestjs/schematics@10.1.1(chokidar@3.6.0)(typescript@5.3.3): + resolution: {integrity: sha512-o4lfCnEeIkfJhGBbLZxTuVWcGuqDCFwg5OrvpgRUBM7vI/vONvKKiB5riVNpO+JqXoH0I42NNeDb0m4V5RREig==} + peerDependencies: + typescript: '>=4.8.2' + dependencies: + '@angular-devkit/core': 17.1.2(chokidar@3.6.0) + '@angular-devkit/schematics': 17.1.2(chokidar@3.6.0) + comment-json: 4.2.3 + jsonc-parser: 3.2.1 + pluralize: 8.0.0 + typescript: 5.3.3 + transitivePeerDependencies: + - chokidar + dev: true + + /@nestjs/swagger@7.3.0(@fastify/static@7.0.1)(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1): + resolution: {integrity: sha512-zLkfKZ+ioYsIZ3dfv7Bj8YHnZMNAGWFUmx2ZDuLp/fBE4P8BSjB7hldzDueFXsmwaPL90v7lgyd82P+s7KME1Q==} + peerDependencies: + '@fastify/static': ^6.0.0 || ^7.0.0 + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + '@fastify/static': + optional: true + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + '@fastify/static': 7.0.1 + '@microsoft/tsdoc': 0.14.2 + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.3.3)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1) + class-transformer: 0.5.1 + class-validator: 0.14.1 + js-yaml: 4.1.0 + lodash: 4.17.21 + path-to-regexp: 3.2.0 + reflect-metadata: 0.2.1 + swagger-ui-dist: 5.11.2 + dev: false + + /@nestjs/terminus@10.2.2(@nestjs/axios@3.0.2)(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/typeorm@10.0.2)(reflect-metadata@0.2.1)(rxjs@7.8.1)(typeorm@0.3.17): + resolution: {integrity: sha512-tZdTSqgHyxekN8PJmJJ1ptZG97q/1nBIBwLdMcmB7Dsz4XDTQvYuhs20F1qkEgFuQwarNkb/2AF5Qib31g2bmA==} + peerDependencies: + '@grpc/grpc-js': '*' + '@grpc/proto-loader': '*' + '@mikro-orm/core': '*' + '@mikro-orm/nestjs': '*' + '@nestjs/axios': ^1.0.0 || ^2.0.0 || ^3.0.0 + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + '@nestjs/microservices': ^9.0.0 || ^10.0.0 + '@nestjs/mongoose': ^9.0.0 || ^10.0.0 + '@nestjs/sequelize': ^9.0.0 || ^10.0.0 + '@nestjs/typeorm': ^9.0.0 || ^10.0.0 + '@prisma/client': '*' + mongoose: '*' + reflect-metadata: 0.1.x || 0.2.x + rxjs: 7.x + sequelize: '*' + typeorm: '*' + peerDependenciesMeta: + '@grpc/grpc-js': + optional: true + '@grpc/proto-loader': + optional: true + '@mikro-orm/core': + optional: true + '@mikro-orm/nestjs': + optional: true + '@nestjs/axios': + optional: true + '@nestjs/microservices': + optional: true + '@nestjs/mongoose': + optional: true + '@nestjs/sequelize': + optional: true + '@nestjs/typeorm': + optional: true + '@prisma/client': + optional: true + mongoose: + optional: true + sequelize: + optional: true + typeorm: + optional: true + dependencies: + '@nestjs/axios': 3.0.2(@nestjs/common@10.3.3)(axios@1.6.7)(rxjs@7.8.1) + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/typeorm': 10.0.2(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)(typeorm@0.3.17) + boxen: 5.1.2 + check-disk-space: 3.4.0 + reflect-metadata: 0.2.1 + rxjs: 7.8.1 + typeorm: 0.3.17(ioredis@5.3.2)(mysql2@3.9.1)(ts-node@10.9.2) + dev: false + + /@nestjs/testing@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + tslib: 2.6.2 + dev: true + + /@nestjs/throttler@5.1.2(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(reflect-metadata@0.2.1): + resolution: {integrity: sha512-60MqhSLYUqWOgc38P6C6f76JIpf6mVjly7gpuPBCKtVd0p5e8Fq855j7bJuO4/v25vgaOo1OdVs0U1qtgYioGw==} + peerDependencies: + '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + reflect-metadata: ^0.1.13 || ^0.2.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + reflect-metadata: 0.2.1 + dev: false + + /@nestjs/typeorm@10.0.2(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)(typeorm@0.3.17): + resolution: {integrity: sha512-H738bJyydK4SQkRCTeh1aFBxoO1E9xdL/HaLGThwrqN95os5mEyAtK7BLADOS+vldP4jDZ2VQPLj4epWwRqCeQ==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + reflect-metadata: ^0.1.13 || ^0.2.0 + rxjs: ^7.2.0 + typeorm: ^0.3.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + reflect-metadata: 0.2.1 + rxjs: 7.8.1 + typeorm: 0.3.17(ioredis@5.3.2)(mysql2@3.9.1)(ts-node@10.9.2) + uuid: 9.0.1 + dev: false + + /@nestjs/websockets@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-socket.io@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-cR5cB0bLS87vd0iu7Nud/4x2EH1Vs0aIgwGWd0eH/5SAw0rrDNU81PiOde+rnMXETbxvSVfOZuLRyn7/WQtGUg==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + '@nestjs/platform-socket.io': ^10.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/platform-socket.io': + optional: true + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/platform-socket.io': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(rxjs@7.8.1) + iterare: 1.2.1 + object-hash: 3.0.0 + reflect-metadata: 0.2.1 + rxjs: 7.8.1 + tslib: 2.6.2 + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + dev: true + + /@nuxtjs/opencollective@0.3.2: + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + /@one-ini/wasm@0.1.1: + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + dev: false + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + optional: true + + /@selderee/plugin-htmlparser2@0.11.0: + resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + dependencies: + domhandler: 5.0.3 + selderee: 0.11.0 + dev: false + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@sinonjs/commons@3.0.1: + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.3.0: + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + dependencies: + '@sinonjs/commons': 3.0.1 + dev: true + + /@socket.io/component-emitter@3.1.0: + resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + + /@socket.io/redis-adapter@8.2.1(socket.io-adapter@2.5.2): + resolution: {integrity: sha512-6Dt7EZgGSBP0qvXeOKGx7NnSr2tPMbVDfDyL97zerZo+v69hMfL99skMCL3RKZlWVqLyRme2T0wcy3udHhtOsg==} + engines: {node: '>=10.0.0'} + peerDependencies: + socket.io-adapter: ^2.4.0 + dependencies: + debug: 4.3.4 + notepack.io: 3.0.1 + socket.io-adapter: 2.5.2 + uid2: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@socket.io/redis-emitter@5.1.0: + resolution: {integrity: sha512-QQUFPBq6JX7JIuM/X1811ymKlAfwufnQ8w6G2/59Jaqp09hdF1GJ/+e8eo/XdcmT0TqkvcSa2TT98ggTXa5QYw==} + dependencies: + debug: 4.3.4 + notepack.io: 3.0.1 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - supports-color + dev: false + + /@songkeys/nestjs-redis@10.0.0(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(ioredis@5.3.2): + resolution: {integrity: sha512-s56+NECuJXzcaPLYzpvA2xjL0e/1Zy55UE0q6b1UqpbQSKI06TFPFCWCMUadJigiuB26O1hxi+lmDbzahKvcLg==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + ioredis: ^5.0.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + ioredis: 5.3.2 + tslib: 2.6.0 + dev: false + + /@sqltools/formatter@1.2.5: + resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + dev: false + + /@stylistic/eslint-plugin-js@1.6.2(eslint@8.56.0): + resolution: {integrity: sha512-ndT6X2KgWGxv8101pdMOxL8pihlYIHcOv3ICd70cgaJ9exwkPn8hJj4YQwslxoAlre1TFHnXd/G1/hYXgDrjIA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: '>=8.40.0' + dependencies: + '@types/eslint': 8.56.2 + acorn: 8.11.3 + escape-string-regexp: 4.0.0 + eslint: 8.56.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + dev: true + + /@stylistic/eslint-plugin-jsx@1.6.2(eslint@8.56.0): + resolution: {integrity: sha512-hbbouazSJbHD/fshBIOLh9JgtSphKNoTCfHLSNBjAkXLK+GR4i2jhEZZF9P0mtXrNuy2WWInmpq/g0pfWBmSBA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: '>=8.40.0' + dependencies: + '@stylistic/eslint-plugin-js': 1.6.2(eslint@8.56.0) + '@types/eslint': 8.56.2 + eslint: 8.56.0 + estraverse: 5.3.0 + picomatch: 4.0.1 + dev: true + + /@stylistic/eslint-plugin-plus@1.6.2(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-EDMwa6gzKw4bXRqdIAUvZDfIgwotbjJs8o+vYE22chAYtVAnA0Pcq+cPx0Uk35t2gvJWb5OaLDjqA6oy1tD0jg==} + peerDependencies: + eslint: '*' + dependencies: + '@types/eslint': 8.56.2 + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@stylistic/eslint-plugin-ts@1.6.2(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-FizV58em0OjO/xFHRIy/LJJVqzxCNmYC/xVtKDf8aGDRgZpLo+lkaBKfBrbMkAGzhBKbYj+iLEFI4WEl6aVZGQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: '>=8.40.0' + dependencies: + '@stylistic/eslint-plugin-js': 1.6.2(eslint@8.56.0) + '@types/eslint': 8.56.2 + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@stylistic/eslint-plugin@1.6.2(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-EFnVcKOE5HTiMlVwisL9hHjz8a69yBbJRscWF/z+/vl6M4ew8NVrBlY8ea7KdV8QtyCY4Yapmsbg5ZDfhWlEgg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: '>=8.40.0' + dependencies: + '@stylistic/eslint-plugin-js': 1.6.2(eslint@8.56.0) + '@stylistic/eslint-plugin-jsx': 1.6.2(eslint@8.56.0) + '@stylistic/eslint-plugin-plus': 1.6.2(eslint@8.56.0)(typescript@5.3.3) + '@stylistic/eslint-plugin-ts': 1.6.2(eslint@8.56.0)(typescript@5.3.3) + '@types/eslint': 8.56.2 + eslint: 8.56.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@thednp/event-listener@2.0.4: + resolution: {integrity: sha512-sc4B7AzYAIvnGnivirq0XyR7LfzEDhGiiB70Q0qdNn8wSJ2pL1buVAsEZxrlc47qRJiBV4YIP+BFkyMm2r3NLg==} + engines: {node: '>=16', pnpm: '>=8.6.0'} + dev: true + + /@thednp/shorty@2.0.0: + resolution: {integrity: sha512-kwtLivCxYIoFfGIVU4NlZtfdA/zxZ6X8UcWaJrb7XqU3WQ4Q1p5IaZlLBfOVAO06WH5oWE87QUdK/dS56Wnfjg==} + engines: {node: '>=16', pnpm: '>=8.6.0'} + dev: true + + /@ts-morph/common@0.21.0: + resolution: {integrity: sha512-ES110Mmne5Vi4ypUKrtVQfXFDtCsDXiUiGxF6ILVlE90dDD4fdpC1LSjydl/ml7xJWKSDZwUYD2zkOePMSrPBA==} + dependencies: + fast-glob: 3.3.2 + minimatch: 7.4.6 + mkdirp: 2.1.6 + path-browserify: 1.0.1 + dev: true + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + /@types/babel__core@7.20.5: + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + dependencies: + '@babel/parser': 7.23.9 + '@babel/types': 7.23.9 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.5 + dev: true + + /@types/babel__generator@7.6.8: + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + dependencies: + '@babel/parser': 7.23.9 + '@babel/types': 7.23.9 + dev: true + + /@types/babel__traverse@7.20.5: + resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} + dependencies: + '@babel/types': 7.23.9 + dev: true + + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.11.18 + dev: true + + /@types/cache-manager@4.0.6: + resolution: {integrity: sha512-8qL93MF05/xrzFm/LSPtzNEOE1eQF3VwGHAcQEylgp5hDSTe41jtFwbSYAPfyYcVa28y1vYSjIt0c1fLLUiC/Q==} + dev: true + + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 20.11.18 + dev: true + + /@types/cookie@0.4.1: + resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + + /@types/cookiejar@2.1.5: + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + dev: true + + /@types/cors@2.8.17: + resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + dependencies: + '@types/node': 20.11.18 + + /@types/ejs@3.1.5: + resolution: {integrity: sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==} + dev: false + optional: true + + /@types/eslint-scope@3.7.7: + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + dependencies: + '@types/eslint': 8.56.2 + '@types/estree': 1.0.5 + dev: true + + /@types/eslint@8.56.2: + resolution: {integrity: sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==} + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + + /@types/express-serve-static-core@4.17.43: + resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} + dependencies: + '@types/node': 20.11.18 + '@types/qs': 6.9.11 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true + + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.17.43 + '@types/qs': 6.9.11 + '@types/serve-static': 1.15.5 + dev: true + + /@types/graceful-fs@4.1.9: + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + dependencies: + '@types/node': 20.11.18 + dev: true + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + + /@types/istanbul-lib-coverage@2.0.6: + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + dev: true + + /@types/istanbul-lib-report@3.0.3: + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + dev: true + + /@types/istanbul-reports@3.0.4: + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + dependencies: + '@types/istanbul-lib-report': 3.0.3 + dev: true + + /@types/jest@29.5.12: + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/jsonwebtoken@9.0.5: + resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} + dependencies: + '@types/node': 20.11.18 + dev: false + + /@types/lodash@4.14.202: + resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} + dev: false + + /@types/luxon@3.3.8: + resolution: {integrity: sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ==} + dev: false + + /@types/mdast@3.0.15: + resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + dependencies: + '@types/unist': 2.0.10 + dev: true + + /@types/methods@1.1.4: + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + dev: true + + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + + /@types/mime@3.0.4: + resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + dev: true + + /@types/minimist@1.2.5: + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + dev: true + + /@types/multer@1.4.11: + resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} + dependencies: + '@types/express': 4.17.21 + dev: true + + /@types/node@20.11.18: + resolution: {integrity: sha512-ABT5VWnnYneSBcNWYSCuR05M826RoMyMSGiFivXGx6ZUIsXb9vn4643IEwkg2zbEOSgAiSogtapN2fgc4mAPlw==} + dependencies: + undici-types: 5.26.5 + + /@types/normalize-package-data@2.4.4: + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + dev: true + + /@types/pug@2.0.10: + resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} + dev: false + optional: true + + /@types/qs@6.9.11: + resolution: {integrity: sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + + /@types/semver@7.5.7: + resolution: {integrity: sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==} + dev: true + + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.11.18 + dev: true + + /@types/serve-static@1.15.5: + resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/mime': 3.0.4 + '@types/node': 20.11.18 + dev: true + + /@types/stack-utils@2.0.3: + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + dev: true + + /@types/superagent@8.1.3: + resolution: {integrity: sha512-R/CfN6w2XsixLb1Ii8INfn+BT9sGPvw74OavfkW4SwY+jeUcAwLZv2+bXLJkndnimxjEBm0RPHgcjW9pLCa8cw==} + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 20.11.18 + dev: true + + /@types/supertest@6.0.2: + resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==} + dependencies: + '@types/methods': 1.1.4 + '@types/superagent': 8.1.3 + dev: true + + /@types/triple-beam@1.3.5: + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + dev: false + + /@types/ua-parser-js@0.7.39: + resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} + dev: true + + /@types/unist@2.0.10: + resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + dev: true + + /@types/validator@13.11.9: + resolution: {integrity: sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==} + + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + dev: true + + /@types/yargs@17.0.32: + resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} + dependencies: + '@types/yargs-parser': 21.0.3 + dev: true + + /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/type-utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.4 + eslint: 8.56.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + semver: 7.6.0 + ts-api-utils: 1.2.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.4 + eslint: 8.56.0 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.21.0: + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + dev: true + + /@typescript-eslint/type-utils@6.21.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + debug: 4.3.4 + eslint: 8.56.0 + ts-api-utils: 1.2.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.21.0: + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3): + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.0 + ts-api-utils: 1.2.1(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.21.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.7 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + eslint: 8.56.0 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.21.0: + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.21.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vue/compiler-core@3.4.19: + resolution: {integrity: sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==} + dependencies: + '@babel/parser': 7.23.9 + '@vue/shared': 3.4.19 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + dev: true + + /@vue/compiler-dom@3.4.19: + resolution: {integrity: sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==} + dependencies: + '@vue/compiler-core': 3.4.19 + '@vue/shared': 3.4.19 + dev: true + + /@vue/compiler-sfc@3.4.19: + resolution: {integrity: sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==} + dependencies: + '@babel/parser': 7.23.9 + '@vue/compiler-core': 3.4.19 + '@vue/compiler-dom': 3.4.19 + '@vue/compiler-ssr': 3.4.19 + '@vue/shared': 3.4.19 + estree-walker: 2.0.2 + magic-string: 0.30.7 + postcss: 8.4.35 + source-map-js: 1.0.2 + dev: true + + /@vue/compiler-ssr@3.4.19: + resolution: {integrity: sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==} + dependencies: + '@vue/compiler-dom': 3.4.19 + '@vue/shared': 3.4.19 + dev: true + + /@vue/shared@3.4.19: + resolution: {integrity: sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==} + dev: true + + /@webassemblyjs/ast@1.11.6: + resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} + dependencies: + '@webassemblyjs/helper-numbers': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + dev: true + + /@webassemblyjs/floating-point-hex-parser@1.11.6: + resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} + dev: true + + /@webassemblyjs/helper-api-error@1.11.6: + resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} + dev: true + + /@webassemblyjs/helper-buffer@1.11.6: + resolution: {integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==} + dev: true + + /@webassemblyjs/helper-numbers@1.11.6: + resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@xtuc/long': 4.2.2 + dev: true + + /@webassemblyjs/helper-wasm-bytecode@1.11.6: + resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} + dev: true + + /@webassemblyjs/helper-wasm-section@1.11.6: + resolution: {integrity: sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + dev: true + + /@webassemblyjs/ieee754@1.11.6: + resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} + dependencies: + '@xtuc/ieee754': 1.2.0 + dev: true + + /@webassemblyjs/leb128@1.11.6: + resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} + dependencies: + '@xtuc/long': 4.2.2 + dev: true + + /@webassemblyjs/utf8@1.11.6: + resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} + dev: true + + /@webassemblyjs/wasm-edit@1.11.6: + resolution: {integrity: sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + '@webassemblyjs/wasm-opt': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + '@webassemblyjs/wast-printer': 1.11.6 + dev: true + + /@webassemblyjs/wasm-gen@1.11.6: + resolution: {integrity: sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + dev: true + + /@webassemblyjs/wasm-opt@1.11.6: + resolution: {integrity: sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/wasm-gen': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + dev: true + + /@webassemblyjs/wasm-parser@1.11.6: + resolution: {integrity: sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + dev: true + + /@webassemblyjs/wast-printer@1.11.6: + resolution: {integrity: sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==} + dependencies: + '@webassemblyjs/ast': 1.11.6 + '@xtuc/long': 4.2.2 + dev: true + + /@xtuc/ieee754@1.2.0: + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + dev: true + + /@xtuc/long@4.2.2: + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + dev: true + + /JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + dev: true + + /abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: false + + /abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: false + + /abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + dev: false + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + /acorn-import-assertions@1.9.0(acorn@8.11.3): + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + peerDependencies: + acorn: ^8 + dependencies: + acorn: 8.11.3 + dev: true + + /acorn-jsx@5.3.2(acorn@8.11.3): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.3 + dev: true + + /acorn-node@1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 + dev: true + + /acorn-walk@7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + + /acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + + /add-stream@1.0.0: + resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} + dev: true + + /agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + dependencies: + humanize-ms: 1.2.1 + dev: false + + /ajv-formats@2.1.1(ajv@8.11.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.11.0 + dev: true + + /ajv-formats@2.1.1(ajv@8.12.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.12.0 + + /ajv-keywords@3.5.2(ajv@6.12.6): + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + dependencies: + ajv: 6.12.6 + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv@8.11.0: + resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + /alce@1.2.0: + resolution: {integrity: sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w==} + engines: {node: '>=0.8.0'} + dependencies: + esprima: 1.2.5 + estraverse: 1.9.3 + dev: false + + /amdefine@1.0.1: + resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==} + engines: {node: '>=0.4.2'} + requiresBuild: true + dev: true + optional: true + + /ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + dependencies: + string-width: 4.2.3 + dev: false + + /ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + /ansi-escapes@3.2.0: + resolution: {integrity: sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==} + engines: {node: '>=4'} + dev: true + + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-escapes@6.2.0: + resolution: {integrity: sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==} + engines: {node: '>=14.16'} + dependencies: + type-fest: 3.13.1 + dev: true + + /ansi-regex@3.0.1: + resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} + engines: {node: '>=4'} + dev: true + + /ansi-regex@4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: false + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + /apache-crypt@1.2.6: + resolution: {integrity: sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==} + engines: {node: '>=8'} + dependencies: + unix-crypt-td-js: 1.1.4 + dev: true + + /apache-md5@1.1.8: + resolution: {integrity: sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==} + engines: {node: '>=8'} + dev: true + + /app-root-path@3.1.0: + resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} + engines: {node: '>= 6.0.0'} + dev: false + + /archy@1.0.0: + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + dev: false + + /are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + dev: true + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + /array-from@2.1.1: + resolution: {integrity: sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==} + dev: true + + /array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + dev: true + + /array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + dev: true + + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + /assert-never@1.2.1: + resolution: {integrity: sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==} + requiresBuild: true + dev: false + + /ast-transform@0.0.0: + resolution: {integrity: sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A==} + dependencies: + escodegen: 1.2.0 + esprima: 1.0.4 + through: 2.3.8 + dev: true + + /ast-types@0.7.8: + resolution: {integrity: sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q==} + engines: {node: '>= 0.6'} + dev: true + + /async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + dev: false + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + /at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + dev: true + + /atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + dev: false + + /avvio@8.3.0: + resolution: {integrity: sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==} + dependencies: + '@fastify/error': 3.4.1 + archy: 1.0.0 + debug: 4.3.4 + fastq: 1.17.1 + transitivePeerDependencies: + - supports-color + dev: false + + /axios@1.6.7: + resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==} + dependencies: + follow-redirects: 1.15.5 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + + /babel-jest@29.7.0(@babel/core@7.23.9): + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.23.9 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.23.9) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.22.5 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.23.9 + '@babel/types': 7.23.9 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.5 + dev: true + + /babel-plugin-polyfill-corejs2@0.4.8(@babel/core@7.23.9): + resolution: {integrity: sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/core': 7.23.9 + '@babel/helper-define-polyfill-provider': 0.5.0(@babel/core@7.23.9) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-corejs3@0.9.0(@babel/core@7.23.9): + resolution: {integrity: sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-define-polyfill-provider': 0.5.0(@babel/core@7.23.9) + core-js-compat: 3.36.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-regenerator@0.5.5(@babel/core@7.23.9): + resolution: {integrity: sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/helper-define-polyfill-provider': 0.5.0(@babel/core@7.23.9) + transitivePeerDependencies: + - supports-color + dev: true + + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.9): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.9) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.9) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.9) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.9) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.9) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.9) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.9) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.23.9): + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.9 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.9) + dev: true + + /babel-walk@3.0.0-canary-5: + resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + '@babel/types': 7.23.9 + dev: false + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + /base64-js@1.3.1: + resolution: {integrity: sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==} + dev: true + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + /base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + + /base64url@3.0.1: + resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} + engines: {node: '>=6.0.0'} + dev: false + + /basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + dev: true + + /bcryptjs@2.4.3: + resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==} + dev: true + + /before@0.0.1: + resolution: {integrity: sha512-1J5SWbkoVJH9DTALN8igB4p+nPKZzPrJ/HomqBDLpfUvDXCdjdBmBUcH5McZfur0lftVssVU6BZug5NYh87zTw==} + dev: false + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /block-stream2@2.1.0: + resolution: {integrity: sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==} + dependencies: + readable-stream: 3.6.2 + dev: false + + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + /bootstrap.native@5.0.11: + resolution: {integrity: sha512-bk2i4sQcQk2KuCTs1yygTa+JGjZOpKzIZ/It6TZZOO/Q+PmVGuKuIbrznXF64BUFxXaPNy7gO9LnE7vjGdauSQ==} + engines: {node: '>=16', pnpm: '>=8.6.0'} + dependencies: + '@thednp/event-listener': 2.0.4 + '@thednp/shorty': 2.0.0 + dev: true + + /boxen@5.1.2: + resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} + engines: {node: '>=10'} + dependencies: + ansi-align: 3.0.1 + camelcase: 6.3.0 + chalk: 4.1.2 + cli-boxes: 2.2.1 + string-width: 4.2.3 + type-fest: 0.20.2 + widest-line: 3.1.0 + wrap-ansi: 7.0.0 + dev: false + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + + /brfs@2.0.2: + resolution: {integrity: sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ==} + hasBin: true + dependencies: + quote-stream: 1.0.2 + resolve: 1.22.8 + static-module: 3.0.4 + through2: 2.0.5 + dev: true + + /brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + dependencies: + base64-js: 1.5.1 + dev: true + + /browser-resolve@1.11.3: + resolution: {integrity: sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==} + dependencies: + resolve: 1.1.7 + dev: true + + /browserify-optional@1.0.1: + resolution: {integrity: sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ==} + dependencies: + ast-transform: 0.0.0 + ast-types: 0.7.8 + browser-resolve: 1.11.3 + dev: true + + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001587 + electron-to-chromium: 1.4.670 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.23.0) + dev: true + + /bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + dependencies: + fast-json-stable-stringify: 2.1.0 + dev: true + + /bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: true + + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + + /buffer-equal@0.0.1: + resolution: {integrity: sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==} + engines: {node: '>=0.4.0'} + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + + /builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /builtins@5.0.1: + resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + dependencies: + semver: 7.6.0 + dev: true + + /bull@4.12.2: + resolution: {integrity: sha512-WPuc0VCYx+cIVMiZtPwRpWyyJFBrj4/OgKJ6n9Jf4tIw7rQNV+HAKQv15UDkcTvfpGFehvod7Fd1YztbYSJIDQ==} + engines: {node: '>=12'} + dependencies: + cron-parser: 4.9.0 + get-port: 5.1.1 + ioredis: 5.3.2 + lodash: 4.17.21 + msgpackr: 1.10.1 + semver: 7.6.0 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + dev: false + + /cache-manager-ioredis-yet@1.2.2: + resolution: {integrity: sha512-o03N/tQxfFONZ1XLGgIxOFHuQQpjpRdnSAL1THG1YWZIVp1JMUfjU3ElSAjFN1LjbJXa55IpC8waG+VEoLUCUw==} + engines: {node: '>= 16.17.0'} + dependencies: + cache-manager: 5.4.0 + ioredis: 5.3.2 + transitivePeerDependencies: + - supports-color + dev: false + + /cache-manager@5.4.0: + resolution: {integrity: sha512-FS7o8vqJosnLpu9rh2gQTo8EOzCRJLF1BJ4XDEUDMqcfvs7SJZs5iuoFTXLauzQ3S5v8sBAST1pCwMaurpyi1A==} + dependencies: + lodash.clonedeep: 4.5.0 + lru-cache: 10.2.0 + promise-coalesce: 1.1.2 + dev: false + + /cachedir@2.3.0: + resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==} + engines: {node: '>=6'} + dev: true + + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.1 + + /callsite@1.0.0: + resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camel-case@3.0.0: + resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} + dependencies: + no-case: 2.3.2 + upper-case: 1.1.3 + dev: false + + /camelcase-keys@6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + dev: true + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + /caniuse-lite@1.0.30001587: + resolution: {integrity: sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: false + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + + /character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + dev: true + + /character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + dev: true + + /character-parser@2.2.0: + resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==} + requiresBuild: true + dependencies: + is-regex: 1.1.4 + dev: false + + /character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + dev: true + + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + + /check-disk-space@3.4.0: + resolution: {integrity: sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==} + engines: {node: '>=16'} + dev: false + + /cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + + /cheerio@1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.1.0 + htmlparser2: 8.0.2 + parse5: 7.1.2 + parse5-htmlparser2-tree-adapter: 7.0.0 + + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + /chrome-trace-event@1.0.3: + resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + engines: {node: '>=6.0'} + dev: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + /ci-info@4.0.0: + resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} + engines: {node: '>=8'} + dev: true + + /cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + dev: true + + /class-transformer@0.5.1: + resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} + + /class-validator@0.14.1: + resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==} + dependencies: + '@types/validator': 13.11.9 + libphonenumber-js: 1.10.56 + validator: 13.11.0 + + /clean-css@4.2.4: + resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} + engines: {node: '>= 4.0'} + dependencies: + source-map: 0.6.1 + dev: false + + /clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /cli-boxes@2.2.1: + resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} + engines: {node: '>=6'} + dev: false + + /cli-cursor@2.1.0: + resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} + engines: {node: '>=4'} + dependencies: + restore-cursor: 2.0.0 + dev: true + + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: true + + /cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + dev: false + + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + dev: true + + /cli-table3@0.6.3: + resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} + engines: {node: 10.* || >= 12.*} + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + dev: true + + /cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + dependencies: + slice-ansi: 5.0.0 + string-width: 7.1.0 + dev: true + + /cli-width@2.2.1: + resolution: {integrity: sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==} + dev: true + + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: true + + /cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + dev: true + + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true + + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + dev: false + + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /code-block-writer@12.0.0: + resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} + dev: true + + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + dev: true + + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + dev: false + + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + dev: true + + /colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + dev: true + + /colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + dev: false + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: false + + /commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + dev: true + + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + + /commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + dev: false + + /comment-json@4.2.3: + resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==} + engines: {node: '>= 6'} + dependencies: + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 + has-own-prop: 2.0.0 + repeat-string: 1.6.1 + dev: true + + /comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + dev: true + + /commitizen@4.3.0(@types/node@20.11.18)(typescript@5.3.3): + resolution: {integrity: sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw==} + engines: {node: '>= 12'} + hasBin: true + dependencies: + cachedir: 2.3.0 + cz-conventional-changelog: 3.3.0(@types/node@20.11.18)(typescript@5.3.3) + dedent: 0.7.0 + detect-indent: 6.1.0 + find-node-modules: 2.1.3 + find-root: 1.1.0 + fs-extra: 9.1.0 + glob: 7.2.3 + inquirer: 8.2.5 + is-utf8: 0.2.1 + lodash: 4.17.21 + minimist: 1.2.7 + strip-bom: 4.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - '@types/node' + - typescript + dev: true + + /compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + dev: true + + /component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + /concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + dev: true + + /concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + typedarray: 0.0.6 + dev: true + + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: false + + /connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + + /constantinople@4.0.1: + resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} + requiresBuild: true + dependencies: + '@babel/parser': 7.23.9 + '@babel/types': 7.23.9 + dev: false + + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + /conventional-changelog-angular@5.0.13: + resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==} + engines: {node: '>=10'} + dependencies: + compare-func: 2.0.0 + q: 1.5.1 + dev: true + + /conventional-changelog-atom@2.0.8: + resolution: {integrity: sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-codemirror@2.0.8: + resolution: {integrity: sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-config-spec@2.1.0: + resolution: {integrity: sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==} + dev: true + + /conventional-changelog-conventionalcommits@4.6.3: + resolution: {integrity: sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==} + engines: {node: '>=10'} + dependencies: + compare-func: 2.0.0 + lodash: 4.17.21 + q: 1.5.1 + dev: true + + /conventional-changelog-core@4.2.4: + resolution: {integrity: sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==} + engines: {node: '>=10'} + dependencies: + add-stream: 1.0.0 + conventional-changelog-writer: 5.0.1 + conventional-commits-parser: 3.2.4 + dateformat: 3.0.3 + get-pkg-repo: 4.2.1 + git-raw-commits: 2.0.11 + git-remote-origin-url: 2.0.0 + git-semver-tags: 4.1.1 + lodash: 4.17.21 + normalize-package-data: 3.0.3 + q: 1.5.1 + read-pkg: 3.0.0 + read-pkg-up: 3.0.0 + through2: 4.0.2 + dev: true + + /conventional-changelog-ember@2.0.9: + resolution: {integrity: sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-eslint@3.0.9: + resolution: {integrity: sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-express@2.0.6: + resolution: {integrity: sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-jquery@3.0.11: + resolution: {integrity: sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-jshint@2.0.9: + resolution: {integrity: sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==} + engines: {node: '>=10'} + dependencies: + compare-func: 2.0.0 + q: 1.5.1 + dev: true + + /conventional-changelog-preset-loader@2.3.4: + resolution: {integrity: sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==} + engines: {node: '>=10'} + dev: true + + /conventional-changelog-writer@5.0.1: + resolution: {integrity: sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + conventional-commits-filter: 2.0.7 + dateformat: 3.0.3 + handlebars: 4.7.8 + json-stringify-safe: 5.0.1 + lodash: 4.17.21 + meow: 8.1.2 + semver: 6.3.1 + split: 1.0.1 + through2: 4.0.2 + dev: true + + /conventional-changelog@3.1.25: + resolution: {integrity: sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==} + engines: {node: '>=10'} + dependencies: + conventional-changelog-angular: 5.0.13 + conventional-changelog-atom: 2.0.8 + conventional-changelog-codemirror: 2.0.8 + conventional-changelog-conventionalcommits: 4.6.3 + conventional-changelog-core: 4.2.4 + conventional-changelog-ember: 2.0.9 + conventional-changelog-eslint: 3.0.9 + conventional-changelog-express: 2.0.6 + conventional-changelog-jquery: 3.0.11 + conventional-changelog-jshint: 2.0.9 + conventional-changelog-preset-loader: 2.3.4 + dev: true + + /conventional-commit-types@3.0.0: + resolution: {integrity: sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==} + dev: true + + /conventional-commits-filter@2.0.7: + resolution: {integrity: sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==} + engines: {node: '>=10'} + dependencies: + lodash.ismatch: 4.4.0 + modify-values: 1.0.1 + dev: true + + /conventional-commits-parser@3.2.4: + resolution: {integrity: sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==} + engines: {node: '>=10'} + hasBin: true + dependencies: + JSONStream: 1.3.5 + is-text-path: 1.0.1 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + dev: true + + /conventional-recommended-bump@6.1.0: + resolution: {integrity: sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + concat-stream: 2.0.0 + conventional-changelog-preset-loader: 2.3.4 + conventional-commits-filter: 2.0.7 + conventional-commits-parser: 3.2.4 + git-raw-commits: 2.0.11 + git-semver-tags: 4.1.1 + meow: 8.1.2 + q: 1.5.1 + dev: true + + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cookie-signature@1.2.1: + resolution: {integrity: sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==} + engines: {node: '>=6.6.0'} + dev: false + + /cookie@0.4.2: + resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + engines: {node: '>= 0.6'} + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + + /cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + dev: true + + /copy-to@2.0.1: + resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==} + dev: false + + /core-js-compat@3.36.0: + resolution: {integrity: sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==} + dependencies: + browserslist: 4.23.0 + dev: true + + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true + + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + /cosmiconfig-typescript-loader@5.0.0(@types/node@20.11.18)(cosmiconfig@8.3.6)(typescript@5.3.3): + resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} + engines: {node: '>=v16'} + requiresBuild: true + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=8.2' + typescript: '>=4' + dependencies: + '@types/node': 20.11.18 + cosmiconfig: 8.3.6(typescript@5.3.3) + jiti: 1.21.0 + typescript: 5.3.3 + dev: true + optional: true + + /cosmiconfig@8.3.6(typescript@5.3.3): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.3.3 + dev: true + + /crc32@0.2.2: + resolution: {integrity: sha512-PFZEGbDUeoNbL2GHIEpJRQGheXReDody/9axKTxhXtQqIL443wnNigtVZO9iuCIMPApKZRv7k2xr8euXHqNxQQ==} + engines: {node: '>= 0.4.0'} + hasBin: true + dev: false + + /create-jest@29.7.0(@types/node@20.11.18)(ts-node@10.9.2): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.11.18)(ts-node@10.9.2) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + /cron-parser@4.9.0: + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} + engines: {node: '>=12.0.0'} + dependencies: + luxon: 3.4.4 + dev: false + + /cron@3.1.6: + resolution: {integrity: sha512-cvFiQCeVzsA+QPM6fhjBtlKGij7tLLISnTSvFxVdnFGLdz+ZdXN37kNe0i2gefmdD17XuZA6n2uPVwzl4FxW/w==} + dependencies: + '@types/luxon': 3.3.8 + luxon: 3.4.4 + dev: false + + /cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + + /cross-spawn@6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + /crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + + /css-inline@0.11.2: + resolution: {integrity: sha512-c/oie5Yqa2lVRwUO7A8nd3c3r0x7yE6MQH2PPB/R1LaUb6ohZD7vNXj23fod5y4QNsNhsQi98/AWfUwo1K6R7g==} + dev: false + + /css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + /css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /cz-conventional-changelog@3.3.0(@types/node@20.11.18)(typescript@5.3.3): + resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==} + engines: {node: '>= 10'} + dependencies: + chalk: 2.4.2 + commitizen: 4.3.0(@types/node@20.11.18)(typescript@5.3.3) + conventional-commit-types: 3.0.0 + lodash.map: 4.6.0 + longest: 2.0.1 + word-wrap: 1.2.5 + optionalDependencies: + '@commitlint/load': 18.6.1(@types/node@20.11.18)(typescript@5.3.3) + transitivePeerDependencies: + - '@types/node' + - typescript + dev: true + + /cz-customizable@7.0.0: + resolution: {integrity: sha512-pQKkGSm+8SY9VY/yeJqDOla1MjrGaG7WG4EYLLEV4VNctGO7WdzdGtWEr2ydKSkrpmTs7f8fmBksg/FaTrUAyw==} + hasBin: true + dependencies: + editor: 1.0.0 + find-config: 1.0.0 + inquirer: 6.5.2 + lodash: 4.17.21 + temp: 0.9.4 + word-wrap: 1.2.5 + dev: true + + /d@1.0.1: + resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} + dependencies: + es5-ext: 0.10.62 + type: 1.2.0 + dev: true + + /dargs@7.0.0: + resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} + engines: {node: '>=8'} + dev: true + + /dash-ast@2.0.1: + resolution: {integrity: sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==} + dev: true + + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.23.9 + dev: false + + /dateformat@3.0.3: + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + dev: true + + /dayjs@1.11.10: + resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dev: false + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /decache@4.6.2: + resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==} + dependencies: + callsite: 1.0.0 + dev: true + + /decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + dev: true + + /decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /dedent@0.7.0: + resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + dev: true + + /dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + + /deep-equal@1.1.2: + resolution: {integrity: sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==} + engines: {node: '>= 0.4'} + dependencies: + is-arguments: 1.1.1 + is-date-object: 1.0.5 + is-regex: 1.1.4 + object-is: 1.1.5 + object-keys: 1.1.1 + regexp.prototype.flags: 1.5.2 + dev: true + + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: false + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + /default-user-agent@1.0.0: + resolution: {integrity: sha512-bDF7bg6OSNcSwFWPu4zYKpVkJZQYVrAANMYB8bc9Szem1D0yKdm4sa/rOCs2aC9+2GMqQ7KnwtZRvDhmLF0dXw==} + engines: {node: '>= 0.10.0'} + dependencies: + os-name: 1.0.3 + dev: false + + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + dependencies: + clone: 1.0.4 + dev: true + + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + /define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + dev: true + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + dev: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + /denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + dev: false + + /depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + dev: true + + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + /detect-file@1.0.0: + resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} + engines: {node: '>=0.10.0'} + dev: true + + /detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + /detect-node@2.0.4: + resolution: {integrity: sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==} + dev: false + + /detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + dev: false + + /dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + dev: true + + /dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + /digest-header@1.1.0: + resolution: {integrity: sha512-glXVh42vz40yZb9Cq2oMOt70FIoWiv+vxNvdKdU8CwjLad25qHM3trLxhl9bVjdr6WaslIXhWpn0NO8T/67Qjg==} + engines: {node: '>= 8.0.0'} + dev: false + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /display-notification@2.0.0: + resolution: {integrity: sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw==} + engines: {node: '>=4'} + dependencies: + escape-string-applescript: 1.0.0 + run-applescript: 3.2.0 + dev: false + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctypes@1.1.0: + resolution: {integrity: sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==} + requiresBuild: true + dev: false + + /dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + dev: false + + /dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + /domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + /domhandler@3.3.0: + resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: false + + /domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: false + + /domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + + /domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dev: false + + /domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + /dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dependencies: + is-obj: 2.0.0 + dev: true + + /dot@2.0.0-beta.1: + resolution: {integrity: sha512-kxM7fSnNQTXOmaeGuBSXM8O3fEsBb7XSDBllkGbRwa0lJSJTxxDE/4eSNGLKZUmlFw0f1vJ5qSV2BljrgQtgIA==} + dev: true + + /dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + dev: false + + /dotenv@16.4.1: + resolution: {integrity: sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==} + engines: {node: '>=12'} + dev: false + + /dotenv@16.4.4: + resolution: {integrity: sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==} + engines: {node: '>=12'} + dev: false + + /dotgitignore@2.1.0: + resolution: {integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==} + engines: {node: '>=6'} + dependencies: + find-up: 3.0.0 + minimatch: 3.1.2 + dev: true + + /duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + dependencies: + readable-stream: 2.3.8 + dev: true + + /duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + dev: true + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /editor@1.0.0: + resolution: {integrity: sha512-SoRmbGStwNYHgKfjOrX2L0mUvp9bUVv0uPppZSOMAntEbcFtoC3MKF5b3T6HQPXKIV+QGY3xPO3JK5it5lVkuw==} + dev: true + + /editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.6.0 + dev: false + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + /ejs@3.1.9: + resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.8.7 + dev: false + optional: true + + /electron-to-chromium@1.4.670: + resolution: {integrity: sha512-hcijYOWjOtjKrKPtNA6tuLlA/bTLO3heFG8pQA6mLpq7dRydSWicXova5lyxDzp1iVJaYhK7J2OQlGE52KYn7A==} + dev: true + + /emitter-component@1.1.2: + resolution: {integrity: sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==} + dev: true + + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + + /emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + /enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + dev: false + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + /encoding-japanese@2.0.0: + resolution: {integrity: sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==} + engines: {node: '>=8.10.0'} + dev: false + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + + /engine.io-parser@5.2.2: + resolution: {integrity: sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==} + engines: {node: '>=10.0.0'} + + /engine.io@6.5.4: + resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==} + engines: {node: '>=10.2.0'} + dependencies: + '@types/cookie': 0.4.1 + '@types/cors': 2.8.17 + '@types/node': 20.11.18 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.4.2 + cors: 2.8.5 + debug: 4.3.4 + engine.io-parser: 5.2.2 + ws: 8.11.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + /enhanced-resolve@5.15.0: + resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + + /entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: false + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + dependencies: + stackframe: 1.3.4 + dev: false + + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + /es-module-lexer@1.4.1: + resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} + dev: true + + /es5-ext@0.10.62: + resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} + engines: {node: '>=0.10'} + requiresBuild: true + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + next-tick: 1.1.0 + dev: true + + /es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-symbol: 3.1.3 + dev: true + + /es6-map@0.1.5: + resolution: {integrity: sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-iterator: 2.0.3 + es6-set: 0.1.6 + es6-symbol: 3.1.3 + event-emitter: 0.3.5 + dev: true + + /es6-set@0.1.6: + resolution: {integrity: sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==} + engines: {node: '>=0.12'} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + event-emitter: 0.3.5 + type: 2.7.2 + dev: true + + /es6-shim@0.35.8: + resolution: {integrity: sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==} + dev: true + + /es6-symbol@3.1.3: + resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} + dependencies: + d: 1.0.1 + ext: 1.7.0 + dev: true + + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + /escape-goat@3.0.0: + resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} + engines: {node: '>=10'} + dev: false + + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + /escape-string-applescript@1.0.0: + resolution: {integrity: sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==} + engines: {node: '>=0.10.0'} + dev: false + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: true + + /escodegen@1.14.3: + resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} + engines: {node: '>=4.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 4.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + + /escodegen@1.2.0: + resolution: {integrity: sha512-yLy3Cc+zAC0WSmoT2fig3J87TpQ8UaZGx8ahCAs9FL8qNbyV7CVyPKS74DG4bsHiL5ew9sxdYx131OkBQMFnvA==} + engines: {node: '>=0.4.0'} + hasBin: true + dependencies: + esprima: 1.0.4 + estraverse: 1.5.1 + esutils: 1.0.0 + optionalDependencies: + source-map: 0.1.43 + dev: true + + /escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + + /eslint-compat-utils@0.1.2(eslint@8.56.0): + resolution: {integrity: sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-compat-utils@0.4.1(eslint@8.56.0): + resolution: {integrity: sha512-5N7ZaJG5pZxUeNNJfUchurLVrunD1xJvyg5kYOIVF8kg1f3ajTikmAu/5fZ9w100omNPOoMjngRszh/Q/uFGMg==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + eslint: 8.56.0 + semver: 7.6.0 + dev: true + + /eslint-config-flat-gitignore@0.1.3: + resolution: {integrity: sha512-oQD+dEZv3RThN60tFqGFt+NJcO1DmssUcP+T/nlX+ZzEoEvVUYH0GU9X/VlmDXsbMsS9mONI1HrlxLgtKojw7w==} + dependencies: + find-up: 7.0.0 + parse-gitignore: 2.0.0 + dev: true + + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-merge-processors@0.1.0(eslint@8.56.0): + resolution: {integrity: sha512-IvRXXtEajLeyssvW4wJcZ2etxkR9mUf4zpNwgI+m/Uac9RfXHskuJefkHUcawVzePnd6xp24enp5jfgdHzjRdQ==} + peerDependencies: + eslint: '*' + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + debug: 3.2.7 + eslint: 8.56.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-antfu@2.1.2(eslint@8.56.0): + resolution: {integrity: sha512-s7ZTOM3uq0iqpp6gF0UEotnvup7f2PHBUftCytLZX0+6C9j9KadKZQh6bVVngAyFgsmeD9+gcBopOYLClb2oDg==} + peerDependencies: + eslint: '*' + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-plugin-es-x@7.5.0(eslint@8.56.0): + resolution: {integrity: sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/regexpp': 4.10.0 + eslint: 8.56.0 + eslint-compat-utils: 0.1.2(eslint@8.56.0) + dev: true + + /eslint-plugin-eslint-comments@3.2.0(eslint@8.56.0): + resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} + engines: {node: '>=6.5.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + escape-string-regexp: 1.0.5 + eslint: 8.56.0 + ignore: 5.3.1 + dev: true + + /eslint-plugin-i@2.29.1(@typescript-eslint/parser@6.21.0)(eslint@8.56.0): + resolution: {integrity: sha512-ORizX37MelIWLbMyqI7hi8VJMf7A0CskMmYkB+lkCX3aF4pkGV7kwx5bSEb4qx7Yce2rAf9s34HqDRPjGRZPNQ==} + engines: {node: '>=12'} + peerDependencies: + eslint: ^7.2.0 || ^8 + dependencies: + debug: 4.3.4 + doctrine: 3.0.0 + eslint: 8.56.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0) + get-tsconfig: 4.7.2 + is-glob: 4.0.3 + minimatch: 3.1.2 + semver: 7.6.0 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-jsdoc@48.1.0(eslint@8.56.0): + resolution: {integrity: sha512-g9S8ukmTd1DVcV/xeBYPPXOZ6rc8WJ4yi0+MVxJ1jBOrz5kmxV9gJJQ64ltCqIWFnBChLIhLVx3tbTSarqVyFA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + dependencies: + '@es-joy/jsdoccomment': 0.42.0 + are-docs-informative: 0.0.2 + comment-parser: 1.4.1 + debug: 4.3.4 + escape-string-regexp: 4.0.0 + eslint: 8.56.0 + esquery: 1.5.0 + is-builtin-module: 3.2.1 + semver: 7.6.0 + spdx-expression-parse: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-jsonc@2.13.0(eslint@8.56.0): + resolution: {integrity: sha512-2wWdJfpO/UbZzPDABuUVvlUQjfMJa2p2iQfYt/oWxOMpXCcjuiMUSaA02gtY/Dbu82vpaSqc+O7Xq6ECHwtIxA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + eslint: 8.56.0 + eslint-compat-utils: 0.4.1(eslint@8.56.0) + espree: 9.6.1 + graphemer: 1.4.0 + jsonc-eslint-parser: 2.4.0 + natural-compare: 1.4.0 + synckit: 0.6.2 + dev: true + + /eslint-plugin-markdown@3.0.1(eslint@8.56.0): + resolution: {integrity: sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.56.0 + mdast-util-from-markdown: 0.8.5 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-n@16.6.2(eslint@8.56.0): + resolution: {integrity: sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + eslint: '>=7.0.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + builtins: 5.0.1 + eslint: 8.56.0 + eslint-plugin-es-x: 7.5.0(eslint@8.56.0) + get-tsconfig: 4.7.2 + globals: 13.24.0 + ignore: 5.3.1 + is-builtin-module: 3.2.1 + is-core-module: 2.13.1 + minimatch: 3.1.2 + resolve: 1.22.8 + semver: 7.6.0 + dev: true + + /eslint-plugin-no-only-tests@3.1.0: + resolution: {integrity: sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==} + engines: {node: '>=5.0.0'} + dev: true + + /eslint-plugin-perfectionist@2.5.0(eslint@8.56.0)(typescript@5.3.3)(vue-eslint-parser@9.4.2): + resolution: {integrity: sha512-F6XXcq4mKKUe/SREoMGQqzgw6cgCgf3pFzkFfQVIGtqD1yXVpQjnhTepzhBeZfxZwgMzR9HO4yH4CUhIQ2WBcQ==} + peerDependencies: + astro-eslint-parser: ^0.16.0 + eslint: '>=8.0.0' + svelte: '>=3.0.0' + svelte-eslint-parser: ^0.33.0 + vue-eslint-parser: '>=9.0.0' + peerDependenciesMeta: + astro-eslint-parser: + optional: true + svelte: + optional: true + svelte-eslint-parser: + optional: true + vue-eslint-parser: + optional: true + dependencies: + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + minimatch: 9.0.3 + natural-compare-lite: 1.4.0 + vue-eslint-parser: 9.4.2(eslint@8.56.0) + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-toml@0.9.2(eslint@8.56.0): + resolution: {integrity: sha512-ri0xf63PYf3pIq/WY9BIwrqxZmGTIwSkAO0bHddI0ajUwN4KGz6W8vOvdXFHOpRdRfzxlmXze/vfsY/aTEXESg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + debug: 4.3.4 + eslint: 8.56.0 + eslint-compat-utils: 0.4.1(eslint@8.56.0) + lodash: 4.17.21 + toml-eslint-parser: 0.9.3 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-unicorn@50.0.1(eslint@8.56.0): + resolution: {integrity: sha512-KxenCZxqSYW0GWHH18okDlOQcpezcitm5aOSz6EnobyJ6BIByiPDviQRjJIUAjG/tMN11958MxaQ+qCoU6lfDA==} + engines: {node: '>=16'} + peerDependencies: + eslint: '>=8.56.0' + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint/eslintrc': 2.1.4 + ci-info: 4.0.0 + clean-regexp: 1.0.0 + core-js-compat: 3.36.0 + eslint: 8.56.0 + esquery: 1.5.0 + indent-string: 4.0.0 + is-builtin-module: 3.2.1 + jsesc: 3.0.2 + pluralize: 8.0.0 + read-pkg-up: 7.0.1 + regexp-tree: 0.1.27 + regjsparser: 0.10.0 + semver: 7.6.0 + strip-indent: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-unused-imports@3.1.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0): + resolution: {integrity: sha512-9l1YFCzXKkw1qtAru1RWUtG2EVDZY0a0eChKXcL+EZ5jitG7qxdctu4RnvhOJHv4xfmUf7h+JJPINlVpGhZMrw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': 6 - 7 + eslint: '8' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-rule-composer: 0.3.0 + dev: true + + /eslint-plugin-vitest@0.3.22(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-atkFGQ7aVgcuSeSMDqnyevIyUpfBPMnosksgEPrKE7Y8xQlqG/5z2IQ6UDau05zXaaFv7Iz8uzqvIuKshjZ0Zw==} + engines: {node: ^18.0.0 || >= 20.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': '*' + eslint: '>=8.0.0' + vitest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + vitest: + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-vue@9.21.1(eslint@8.56.0): + resolution: {integrity: sha512-XVtI7z39yOVBFJyi8Ljbn7kY9yHzznKXL02qQYn+ta63Iy4A9JFBw6o4OSB9hyD2++tVT+su9kQqetUyCCwhjw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + eslint: 8.56.0 + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.0.15 + semver: 7.6.0 + vue-eslint-parser: 9.4.2(eslint@8.56.0) + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-yml@1.12.2(eslint@8.56.0): + resolution: {integrity: sha512-hvS9p08FhPT7i/ynwl7/Wt7ke7Rf4P2D6fT8lZlL43peZDTsHtH2A0SIFQ7Kt7+mJ6if6P+FX3iJhMkdnxQwpg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + debug: 4.3.4 + eslint: 8.56.0 + eslint-compat-utils: 0.4.1(eslint@8.56.0) + lodash: 4.17.21 + natural-compare: 1.4.0 + yaml-eslint-parser: 1.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-processor-vue-blocks@0.1.1(@vue/compiler-sfc@3.4.19)(eslint@8.56.0): + resolution: {integrity: sha512-9+dU5lU881log570oBwpelaJmOfOzSniben7IWEDRYQPPWwlvaV7NhOtsTuUWDqpYT+dtKKWPsgz4OkOi+aZnA==} + peerDependencies: + '@vue/compiler-sfc': ^3.3.0 + eslint: ^8.50.0 + dependencies: + '@vue/compiler-sfc': 3.4.19 + eslint: 8.56.0 + dev: true + + /eslint-rule-composer@0.3.0: + resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} + engines: {node: '>=4.0.0'} + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.56.0: + resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.56.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 + dev: true + + /esprima@1.0.4: + resolution: {integrity: sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /esprima@1.2.5: + resolution: {integrity: sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@1.5.1: + resolution: {integrity: sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==} + engines: {node: '>=0.4.0'} + dev: true + + /estraverse@1.9.3: + resolution: {integrity: sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==} + engines: {node: '>=0.10.0'} + dev: false + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /estree-is-function@1.0.0: + resolution: {integrity: sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA==} + dev: true + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + + /esutils@1.0.0: + resolution: {integrity: sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==} + engines: {node: '>=0.10.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: true + + /event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.62 + dev: true + + /event-stream@4.0.1: + resolution: {integrity: sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==} + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.0.7 + pause-stream: 0.0.11 + split: 1.0.1 + stream-combiner: 0.2.2 + through: 2.3.8 + dev: true + + /event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + dev: false + + /eventemitter2@6.4.9: + resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} + dev: false + + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: true + + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + /execa@0.10.0: + resolution: {integrity: sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==} + engines: {node: '>=4'} + dependencies: + cross-spawn: 6.0.5 + get-stream: 3.0.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + dev: false + + /execa@4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.2.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expand-tilde@2.0.2: + resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} + engines: {node: '>=0.10.0'} + dependencies: + homedir-polyfill: 1.0.3 + dev: true + + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + + /ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + dependencies: + type: 2.7.2 + dev: true + + /extend-object@1.0.0: + resolution: {integrity: sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==} + dev: false + + /extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + dev: false + + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + + /fancy-log@2.0.0: + resolution: {integrity: sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==} + engines: {node: '>=10.13.0'} + dependencies: + color-support: 1.1.3 + dev: true + + /fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + dev: false + + /fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + dev: false + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-json-stringify@5.12.0: + resolution: {integrity: sha512-7Nnm9UPa7SfHRbHVA1kJQrGXCRzB7LMlAAqHXQFkEQqueJm1V8owm0FsE/2Do55/4CcdhwiLQERaKomOnKQkyA==} + dependencies: + '@fastify/merge-json-schemas': 0.1.1 + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + fast-deep-equal: 3.1.3 + fast-uri: 2.3.0 + json-schema-ref-resolver: 1.0.1 + rfdc: 1.3.1 + dev: false + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + dependencies: + fast-decode-uri-component: 1.0.1 + dev: false + + /fast-redact@3.3.0: + resolution: {integrity: sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==} + engines: {node: '>=6'} + dev: false + + /fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + /fast-uri@2.3.0: + resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} + dev: false + + /fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + dev: false + + /fastify@4.26.0: + resolution: {integrity: sha512-Fq/7ziWKc6pYLYLIlCRaqJqEVTIZ5tZYfcW/mDK2AQ9v/sqjGFpj0On0/7hU50kbPVjLO4de+larPA1WwPZSfw==} + dependencies: + '@fastify/ajv-compiler': 3.5.0 + '@fastify/error': 3.4.1 + '@fastify/fast-json-stringify-compiler': 4.3.0 + abstract-logging: 2.0.1 + avvio: 8.3.0 + fast-content-type-parse: 1.1.0 + fast-json-stringify: 5.12.0 + find-my-way: 8.1.0 + light-my-request: 5.11.0 + pino: 8.18.0 + process-warning: 3.0.0 + proxy-addr: 2.0.7 + rfdc: 1.3.1 + secure-json-parse: 2.7.0 + semver: 7.6.0 + toad-cache: 3.7.0 + transitivePeerDependencies: + - supports-color + dev: false + + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + dependencies: + reusify: 1.0.4 + + /faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + dependencies: + websocket-driver: 0.7.4 + dev: true + + /fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: true + + /fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + dev: false + + /figures@2.0.0: + resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} + engines: {node: '>=4'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /figures@5.0.0: + resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} + engines: {node: '>=14'} + dependencies: + escape-string-regexp: 5.0.0 + is-unicode-supported: 1.3.0 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.2.0 + dev: true + + /file-stream-rotator@0.6.1: + resolution: {integrity: sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==} + dependencies: + moment: 2.30.1 + dev: false + + /filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + requiresBuild: true + dependencies: + minimatch: 5.1.6 + dev: false + optional: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + + /finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /find-config@1.0.0: + resolution: {integrity: sha512-Z+suHH+7LSE40WfUeZPIxSxypCWvrzdVc60xAjUShZeT5eMWM0/FQUduq3HjluyfAHWvC/aOBkT1pTZktyF/jg==} + engines: {node: '>= 0.12'} + dependencies: + user-home: 2.0.0 + dev: true + + /find-my-way@8.1.0: + resolution: {integrity: sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==} + engines: {node: '>=14'} + dependencies: + fast-deep-equal: 3.1.3 + fast-querystring: 1.1.2 + safe-regex2: 2.0.0 + dev: false + + /find-node-modules@2.1.3: + resolution: {integrity: sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==} + dependencies: + findup-sync: 4.0.0 + merge: 2.1.1 + dev: true + + /find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + dev: true + + /find-up@2.1.0: + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} + dependencies: + locate-path: 2.0.0 + dev: true + + /find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + dev: true + + /findup-sync@4.0.0: + resolution: {integrity: sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==} + engines: {node: '>= 8'} + dependencies: + detect-file: 1.0.0 + is-glob: 4.0.3 + micromatch: 4.0.5 + resolve-dir: 1.0.1 + dev: true + + /fixpack@4.0.0: + resolution: {integrity: sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==} + hasBin: true + dependencies: + alce: 1.2.0 + chalk: 3.0.0 + detect-indent: 6.1.0 + detect-newline: 3.1.0 + extend-object: 1.0.0 + rc: 1.2.8 + dev: false + + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + dev: false + + /follow-redirects@1.15.5: + resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + /fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.90.1): + resolution: {integrity: sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==} + engines: {node: '>=12.13.0', yarn: '>=1.0.0'} + peerDependencies: + typescript: '>3.6.0' + webpack: ^5.11.0 + dependencies: + '@babel/code-frame': 7.23.5 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 8.3.6(typescript@5.3.3) + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.2 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.6.0 + tapable: 2.2.1 + typescript: 5.3.3 + webpack: 5.90.1 + dev: true + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + /formidable@2.1.2: + resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} + dependencies: + dezalgo: 1.0.4 + hexoid: 1.0.0 + once: 1.4.0 + qs: 6.11.2 + dev: true + + /formstream@1.3.1: + resolution: {integrity: sha512-FkW++ub+VbE5dpwukJVDizNWhSgp8FhmhI65pF7BZSVStBqe6Wgxe2Z9/Vhsn7l7nXCPwP+G1cyYlX8VwWOf0g==} + dependencies: + destroy: 1.2.0 + mime: 2.6.0 + pause-stream: 0.0.11 + dev: false + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: true + + /from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + dev: true + + /fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + + /fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + + /fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + + /fs-monkey@1.0.5: + resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + dependencies: + is-property: 1.0.2 + dev: false + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-assigned-identifiers@1.2.0: + resolution: {integrity: sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + /get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + dev: true + + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.1 + + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + + /get-pkg-repo@4.2.1: + resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} + engines: {node: '>=6.9.0'} + hasBin: true + dependencies: + '@hutson/parse-repository-url': 3.0.2 + hosted-git-info: 4.1.0 + through2: 2.0.5 + yargs: 16.2.0 + dev: true + + /get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + dev: false + + /get-stream@3.0.0: + resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} + engines: {node: '>=4'} + dev: false + + /get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /git-raw-commits@2.0.11: + resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + dargs: 7.0.0 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + dev: true + + /git-remote-origin-url@2.0.0: + resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==} + engines: {node: '>=4'} + dependencies: + gitconfiglocal: 1.0.0 + pify: 2.3.0 + dev: true + + /git-semver-tags@4.1.1: + resolution: {integrity: sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + meow: 8.1.2 + semver: 6.3.1 + dev: true + + /gitconfiglocal@1.0.0: + resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} + dependencies: + ini: 1.3.8 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: true + + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.1 + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: false + + /glob@9.3.5: + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.10.1 + dev: true + + /global-dirs@0.1.1: + resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + ini: 1.3.8 + dev: true + optional: true + + /global-modules@1.0.0: + resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} + engines: {node: '>=0.10.0'} + dependencies: + global-prefix: 1.0.2 + is-windows: 1.0.2 + resolve-dir: 1.0.1 + dev: true + + /global-prefix@1.0.2: + resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} + engines: {node: '>=0.10.0'} + dependencies: + expand-tilde: 2.0.2 + homedir-polyfill: 1.0.3 + ini: 1.3.8 + is-windows: 1.0.2 + which: 1.3.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /hammerjs@2.0.8: + resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==} + engines: {node: '>=0.8.0'} + dev: true + + /handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.17.4 + + /hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + /has-own-prop@2.0.0: + resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + + /has@1.0.4: + resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} + engines: {node: '>= 0.4.0'} + dev: true + + /hasown@2.0.1: + resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: false + + /helmet@7.1.0: + resolution: {integrity: sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==} + engines: {node: '>=16.0.0'} + dev: false + + /hexoid@1.0.0: + resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} + engines: {node: '>=8'} + dev: true + + /highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + dev: false + + /homedir-polyfill@1.0.3: + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + engines: {node: '>=0.10.0'} + dependencies: + parse-passwd: 1.0.0 + dev: true + + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + + /hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: true + + /html-entities@2.4.0: + resolution: {integrity: sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==} + dev: true + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /html-minifier@4.0.0: + resolution: {integrity: sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==} + engines: {node: '>=6'} + hasBin: true + dependencies: + camel-case: 3.0.0 + clean-css: 4.2.4 + commander: 2.20.3 + he: 1.2.0 + param-case: 2.1.1 + relateurl: 0.2.7 + uglify-js: 3.17.4 + dev: false + + /html-to-text@9.0.5: + resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} + engines: {node: '>=14'} + dependencies: + '@selderee/plugin-htmlparser2': 0.11.0 + deepmerge: 4.3.1 + dom-serializer: 2.0.0 + htmlparser2: 8.0.2 + selderee: 0.11.0 + dev: false + + /htmlparser2@5.0.1: + resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} + dependencies: + domelementtype: 2.3.0 + domhandler: 3.3.0 + domutils: 2.8.0 + entities: 2.2.0 + dev: false + + /htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + + /http-auth-connect@1.0.6: + resolution: {integrity: sha512-yaO0QSCPqGCjPrl3qEEHjJP+lwZ6gMpXLuCBE06eWwcXomkI5TARtu0kxf9teFuBj6iaV3Ybr15jaWUvbzNzHw==} + engines: {node: '>=8'} + dev: true + + /http-auth@4.1.9: + resolution: {integrity: sha512-kvPYxNGc9EKGTXvOMnTBQw2RZfuiSihK/mLw/a4pbtRueTE45S55Lw/3k5CktIf7Ak0veMKEIteDj4YkNmCzmQ==} + engines: {node: '>=8'} + dependencies: + apache-crypt: 1.2.6 + apache-md5: 1.1.8 + bcryptjs: 2.4.3 + uuid: 8.3.2 + dev: true + + /http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + dev: true + + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + /http-parser-js@0.5.8: + resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + dev: true + + /human-signals@1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + + /humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + dependencies: + ms: 2.1.3 + dev: false + + /husky@8.0.3: + resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /i18next@23.8.2: + resolution: {integrity: sha512-Z84zyEangrlERm0ZugVy4bIt485e/H8VecGUZkZWrH7BDePG6jT73QdL9EA1tRTTVVMpry/MgWIP1FjEn0DRXA==} + dependencies: + '@babel/runtime': 7.23.9 + dev: true + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + /inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + /inquirer@6.5.2: + resolution: {integrity: sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==} + engines: {node: '>=6.0.0'} + dependencies: + ansi-escapes: 3.2.0 + chalk: 2.4.2 + cli-cursor: 2.1.0 + cli-width: 2.2.1 + external-editor: 3.1.0 + figures: 2.0.0 + lodash: 4.17.21 + mute-stream: 0.0.7 + run-async: 2.4.1 + rxjs: 6.6.7 + string-width: 2.1.1 + strip-ansi: 5.2.0 + through: 2.3.8 + dev: true + + /inquirer@8.2.5: + resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + + /inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + dev: true + + /inquirer@9.2.12: + resolution: {integrity: sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==} + engines: {node: '>=14.18.0'} + dependencies: + '@ljharb/through': 2.3.12 + ansi-escapes: 4.3.2 + chalk: 5.3.0 + cli-cursor: 3.1.0 + cli-width: 4.1.0 + external-editor: 3.1.0 + figures: 5.0.0 + lodash: 4.17.21 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: true + + /interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + dev: true + + /ioredis@5.3.2: + resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==} + engines: {node: '>=12.22.0'} + dependencies: + '@ioredis/commands': 1.2.0 + cluster-key-slot: 1.1.2 + debug: 4.3.4 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /ip@1.1.8: + resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} + dev: false + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + + /is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + dev: true + + /is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + dev: true + + /is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + + /is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.1 + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + dev: true + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + /is-expression@4.0.0: + resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==} + requiresBuild: true + dependencies: + acorn: 7.4.1 + object-assign: 4.1.1 + dev: false + + /is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: false + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + /is-fullwidth-code-point@2.0.0: + resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} + engines: {node: '>=4'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + /is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + dev: true + + /is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + dependencies: + get-east-asian-width: 1.2.0 + dev: true + + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + + /is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + dev: true + + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + /is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + dev: true + + /is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + requiresBuild: true + dev: false + + /is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + dev: false + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + /is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + dev: false + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-text-path@1.0.1: + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} + dependencies: + text-extensions: 1.9.0 + dev: true + + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: true + + /is-utf8@0.2.1: + resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} + dev: true + + /is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + dev: true + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + /istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.23.9 + '@babel/parser': 7.23.9 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-instrument@6.0.1: + resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} + engines: {node: '>=10'} + dependencies: + '@babel/core': 7.23.9 + '@babel/parser': 7.23.9 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + /jake@10.8.7: + resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} + engines: {node: '>=10'} + hasBin: true + requiresBuild: true + dependencies: + async: 3.2.5 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: false + optional: true + + /jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.1 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.0.4 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-cli@29.7.0(@types/node@20.11.18)(ts-node@10.9.2): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.11.18)(ts-node@10.9.2) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.11.18)(ts-node@10.9.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jest-config@29.7.0(@types/node@20.11.18)(ts-node@10.9.2): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.23.9 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + babel-jest: 29.7.0(@babel/core@7.23.9) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.2(@types/node@20.11.18)(typescript@5.3.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 20.11.18 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.23.5 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + + /jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + jest-util: 29.7.0 + dev: true + + /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.7.0 + dev: true + + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + chalk: 4.1.2 + cjs-module-lexer: 1.2.3 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.9 + '@babel/generator': 7.23.6 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.9) + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.9) + '@babel/types': 7.23.9 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.9) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + dev: true + + /jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.18 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + dev: true + + /jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 20.11.18 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 20.11.18 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest@29.7.0(@types/node@20.11.18)(ts-node@10.9.2): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@20.11.18)(ts-node@10.9.2) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + requiresBuild: true + dev: true + optional: true + + /js-beautify@1.14.11: + resolution: {integrity: sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw==} + engines: {node: '>=14'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.3.10 + nopt: 7.2.0 + dev: false + + /js-stringify@1.0.2: + resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==} + requiresBuild: true + dev: false + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + + /jsdoc-type-pratt-parser@4.0.0: + resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} + engines: {node: '>=12.0.0'} + dev: true + + /jsesc@0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + dependencies: + fast-deep-equal: 3.1.3 + dev: false + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonc-eslint-parser@2.4.0: + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.3 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.6.0 + dev: true + + /jsonc-parser@3.1.0: + resolution: {integrity: sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==} + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /jsonc-parser@3.2.1: + resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + dev: true + + /jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.6.0 + dev: false + + /jstransformer@1.0.0: + resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==} + requiresBuild: true + dependencies: + is-promise: 2.2.2 + promise: 7.3.1 + dev: false + + /juice@9.1.0: + resolution: {integrity: sha512-odblShmPrUoHUwRuC8EmLji5bPP2MLO1GL+gt4XU3tT2ECmbSrrMjtMQaqg3wgMFP2zvUzdPZGfxc5Trk3Z+fQ==} + engines: {node: '>=10.0.0'} + hasBin: true + dependencies: + cheerio: 1.0.0-rc.12 + commander: 6.2.1 + mensch: 0.3.4 + slick: 1.12.2 + web-resource-inliner: 6.0.1 + transitivePeerDependencies: + - encoding + dev: false + + /jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + dev: false + + /keycharm@0.2.0: + resolution: {integrity: sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: true + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + dev: false + + /leac@0.6.0: + resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} + dev: false + + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn@0.3.0: + resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + type-check: 0.3.2 + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /libbase64@1.2.1: + resolution: {integrity: sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==} + dev: false + + /libmime@5.2.0: + resolution: {integrity: sha512-X2U5Wx0YmK0rXFbk67ASMeqYIkZ6E5vY7pNWRKtnNzqjvdYYG8xtPDpCnuUEnPU9vlgNev+JoSrcaKSUaNvfsw==} + dependencies: + encoding-japanese: 2.0.0 + iconv-lite: 0.6.3 + libbase64: 1.2.1 + libqp: 2.0.1 + dev: false + + /libmime@5.2.1: + resolution: {integrity: sha512-A0z9O4+5q+ZTj7QwNe/Juy1KARNb4WaviO4mYeFC4b8dBT2EEqK2pkM+GC8MVnkOjqhl5nYQxRgnPYRRTNmuSQ==} + dependencies: + encoding-japanese: 2.0.0 + iconv-lite: 0.6.3 + libbase64: 1.2.1 + libqp: 2.0.1 + dev: false + + /libphonenumber-js@1.10.56: + resolution: {integrity: sha512-d0GdKshNnyfl5gM7kZ9rXjGiAbxT/zCXp0k+EAzh8H4zrb2R7GXtMCrULrX7UQxtfx6CLy/vz/lomvW79FAFdA==} + + /libqp@2.0.1: + resolution: {integrity: sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==} + dev: false + + /light-my-request@5.11.0: + resolution: {integrity: sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==} + dependencies: + cookie: 0.5.0 + process-warning: 2.3.2 + set-cookie-parser: 2.6.0 + dev: false + + /lilconfig@3.0.0: + resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} + engines: {node: '>=14'} + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + dependencies: + uc.micro: 2.0.0 + dev: false + + /lint-staged@15.2.2: + resolution: {integrity: sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==} + engines: {node: '>=18.12.0'} + hasBin: true + dependencies: + chalk: 5.3.0 + commander: 11.1.0 + debug: 4.3.4 + execa: 8.0.1 + lilconfig: 3.0.0 + listr2: 8.0.1 + micromatch: 4.0.5 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /listr2@8.0.1: + resolution: {integrity: sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==} + engines: {node: '>=18.0.0'} + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.0.0 + rfdc: 1.3.1 + wrap-ansi: 9.0.0 + dev: true + + /load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + dependencies: + graceful-fs: 4.2.11 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + dev: true + + /loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + dev: true + + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.5.0 + pkg-types: 1.0.3 + dev: true + + /locate-path@2.0.0: + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} + dependencies: + p-locate: 2.0.0 + path-exists: 3.0.0 + dev: true + + /locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-locate: 6.0.0 + dev: true + + /lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + dev: false + + /lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + dev: true + + /lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + dev: false + + /lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: false + + /lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + dev: false + + /lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: false + + /lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: false + + /lodash.ismatch@4.4.0: + resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} + dev: true + + /lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: false + + /lodash.map@4.6.0: + resolution: {integrity: sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==} + dev: true + + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + requiresBuild: true + dev: true + optional: true + + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false + + /lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + requiresBuild: true + dev: true + optional: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + + /log-update@6.0.0: + resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} + engines: {node: '>=18'} + dependencies: + ansi-escapes: 6.2.0 + cli-cursor: 4.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + dev: true + + /logform@2.6.0: + resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} + engines: {node: '>= 12.0.0'} + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.4.3 + triple-beam: 1.4.1 + dev: false + + /loglevel-plugin-prefix@0.8.4: + resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} + dev: true + + /loglevel@1.9.1: + resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} + engines: {node: '>= 0.6.0'} + dev: true + + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + dev: false + + /longest@2.0.1: + resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==} + engines: {node: '>=0.10.0'} + dev: true + + /lower-case@1.1.4: + resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} + dev: false + + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: false + + /lru-cache@8.0.5: + resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} + engines: {node: '>=16.14'} + dev: false + + /lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + dev: true + + /luxon@3.4.4: + resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} + engines: {node: '>=12'} + dev: false + + /macos-release@2.5.1: + resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} + engines: {node: '>=6'} + dev: true + + /magic-string@0.25.1: + resolution: {integrity: sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /magic-string@0.26.2: + resolution: {integrity: sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==} + engines: {node: '>=12'} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /magic-string@0.30.7: + resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /mailparser@3.6.7: + resolution: {integrity: sha512-/3x8HW70DNehw+3vdOPKdlLuxOHoWcGB5jfx5vJ5XUbY9/2jUJbrrhda5Si8Dj/3w08U0y5uGAkqs5+SPTPKoA==} + dependencies: + encoding-japanese: 2.0.0 + he: 1.2.0 + html-to-text: 9.0.5 + iconv-lite: 0.6.3 + libmime: 5.2.1 + linkify-it: 5.0.0 + mailsplit: 5.4.0 + nodemailer: 6.9.9 + tlds: 1.248.0 + dev: false + + /mailsplit@5.4.0: + resolution: {integrity: sha512-wnYxX5D5qymGIPYLwnp6h8n1+6P6vz/MJn5AzGjZ8pwICWssL+CCQjWBIToOVHASmATot4ktvlLo6CyLfOXWYA==} + dependencies: + libbase64: 1.2.1 + libmime: 5.2.0 + libqp: 2.0.1 + dev: false + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.6.0 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + /makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + dependencies: + tmpl: 1.0.5 + dev: true + + /map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + dev: true + + /map-stream@0.0.7: + resolution: {integrity: sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==} + dev: true + + /marked@7.0.3: + resolution: {integrity: sha512-ev2uM40p0zQ/GbvqotfKcSWEa59fJwluGZj5dcaUOwDRrB1F3dncdXy8NWUApk4fi8atU3kTBOwjyjZ0ud0dxw==} + engines: {node: '>= 16'} + hasBin: true + dev: true + + /mdast-util-from-markdown@0.8.5: + resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-string: 2.0.0 + micromark: 2.11.4 + parse-entities: 2.0.0 + unist-util-stringify-position: 2.0.3 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-to-string@2.0.0: + resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + dev: true + + /memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + dependencies: + fs-monkey: 1.0.5 + dev: true + + /mensch@0.3.4: + resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} + dev: false + + /meow@8.1.2: + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} + dependencies: + '@types/minimist': 1.2.5 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + dev: true + + /merge-source-map@1.0.4: + resolution: {integrity: sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA==} + dependencies: + source-map: 0.5.7 + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /merge@2.1.1: + resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==} + dev: true + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: true + + /micromark@2.11.4: + resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} + dependencies: + debug: 4.3.4 + parse-entities: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + /mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: false + + /mimic-fn@1.2.0: + resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} + engines: {node: '>=4'} + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + brace-expansion: 2.0.1 + dev: false + + /minimatch@7.4.6: + resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: false + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + + /minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + dev: true + + /minimist@1.2.7: + resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + /minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + dev: true + + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + + /mjml-accordion@4.14.1: + resolution: {integrity: sha512-dpNXyjnhYwhM75JSjD4wFUa9JgHm86M2pa0CoTzdv1zOQz67ilc4BoK5mc2S0gOjJpjBShM5eOJuCyVIuAPC6w==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-body@4.14.1: + resolution: {integrity: sha512-YpXcK3o2o1U+fhI8f60xahrhXuHmav6BZez9vIN3ZEJOxPFSr+qgr1cT2iyFz50L5+ZsLIVj2ZY+ALQjdsg8ig==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-button@4.14.1: + resolution: {integrity: sha512-V1Tl1vQ3lXYvvqHJHvGcc8URr7V1l/ZOsv7iLV4QRrh7kjKBXaRS7uUJtz6/PzEbNsGQCiNtXrODqcijLWlgaw==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-carousel@4.14.1: + resolution: {integrity: sha512-Ku3MUWPk/TwHxVgKEUtzspy/ePaWtN/3z6/qvNik0KIn0ZUIZ4zvR2JtaVL5nd30LHSmUaNj30XMPkCjYiKkFA==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-cli@4.14.1: + resolution: {integrity: sha512-Gy6MnSygFXs0U1qOXTHqBg2vZX2VL/fAacgQzD4MHq4OuybWaTNSzXRwxBXYCxT3IJB874n2Q0Mxp+Xka+tnZg==} + hasBin: true + dependencies: + '@babel/runtime': 7.23.9 + chokidar: 3.6.0 + glob: 7.2.3 + html-minifier: 4.0.0 + js-beautify: 1.14.11 + lodash: 4.17.21 + mjml-core: 4.14.1 + mjml-migrate: 4.14.1 + mjml-parser-xml: 4.14.1 + mjml-validator: 4.13.0 + yargs: 16.2.0 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-column@4.14.1: + resolution: {integrity: sha512-iixVCIX1YJtpQuwG2WbDr7FqofQrlTtGQ4+YAZXGiLThs0En3xNIJFQX9xJ8sgLEGGltyooHiNICBRlzSp9fDg==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-core@4.14.1: + resolution: {integrity: sha512-di88rSfX+8r4r+cEqlQCO7CRM4mYZrfe2wSCu2je38i+ujjkLpF72cgLnjBlSG5aOUCZgYvlsZ85stqIz9LQfA==} + dependencies: + '@babel/runtime': 7.23.9 + cheerio: 1.0.0-rc.12 + detect-node: 2.1.0 + html-minifier: 4.0.0 + js-beautify: 1.14.11 + juice: 9.1.0 + lodash: 4.17.21 + mjml-migrate: 4.14.1 + mjml-parser-xml: 4.14.1 + mjml-validator: 4.13.0 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-divider@4.14.1: + resolution: {integrity: sha512-agqWY0aW2xaMiUOhYKDvcAAfOLalpbbtjKZAl1vWmNkURaoK4L7MgDilKHSJDFUlHGm2ZOArTrq8i6K0iyThBQ==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-group@4.14.1: + resolution: {integrity: sha512-dJt5batgEJ7wxlxzqOfHOI94ABX+8DZBvAlHuddYO4CsLFHYv6XRIArLAMMnAKU76r6p3X8JxYeOjKZXdv49kg==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-head-attributes@4.14.1: + resolution: {integrity: sha512-XdUNOp2csK28kBDSistInOyzWNwmu5HDNr4y1Z7vSQ1PfkmiuS6jWG7jHUjdoMhs27e6Leuyyc6a8gWSpqSWrg==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-head-breakpoint@4.14.1: + resolution: {integrity: sha512-Qw9l/W/I5Z9p7I4ShgnEpAL9if4472ejcznbBnp+4Gq+sZoPa7iYoEPsa9UCGutlaCh3N3tIi2qKhl9qD8DFxA==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-head-font@4.14.1: + resolution: {integrity: sha512-oBYm1gaOdEMjE5BoZouRRD4lCNZ1jcpz92NR/F7xDyMaKCGN6T/+r4S5dq1gOLm9zWqClRHaECdFJNEmrDpZqA==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-head-html-attributes@4.14.1: + resolution: {integrity: sha512-vlJsJc1Sm4Ml2XvLmp01zsdmWmzm6+jNCO7X3eYi9ngEh8LjMCLIQOncnOgjqm9uGpQu2EgUhwvYFZP2luJOVg==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-head-preview@4.14.1: + resolution: {integrity: sha512-89gQtt3fhl2dkYpHLF5HDQXz/RLpzecU6wmAIT7Dz6etjLGE1dgq2Ay6Bu/OeHjDcT1gbM131zvBwuXw8OydNw==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-head-style@4.14.1: + resolution: {integrity: sha512-XryOuf32EDuUCBT2k99C1+H87IOM919oY6IqxKFJCDkmsbywKIum7ibhweJdcxiYGONKTC6xjuibGD3fQTTYNQ==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-head-title@4.14.1: + resolution: {integrity: sha512-aIfpmlQdf1eJZSSrFodmlC4g5GudBti2eMyG42M7/3NeLM6anEWoe+UkF/6OG4Zy0tCQ40BDJ5iBZlMsjQICzw==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-head@4.14.1: + resolution: {integrity: sha512-KoCbtSeTAhx05Ugn9TB2UYt5sQinSCb7RGRer5iPQ3CrXj8hT5B5Svn6qvf/GACPkWl4auExHQh+XgLB+r3OEA==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-hero@4.14.1: + resolution: {integrity: sha512-TQJ3yfjrKYGkdEWjHLHhL99u/meKFYgnfJvlo9xeBvRjSM696jIjdqaPHaunfw4CP6d2OpCIMuacgOsvqQMWOA==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-image@4.14.1: + resolution: {integrity: sha512-jfKLPHXuFq83okwlNM1Um/AEWeVDgs2JXIOsWp2TtvXosnRvGGMzA5stKLYdy1x6UfKF4c1ovpMS162aYGp+xQ==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-migrate@4.14.1: + resolution: {integrity: sha512-d+9HKQOhZi3ZFAaFSDdjzJX9eDQGjMf3BArLWNm2okC4ZgfJSpOc77kgCyFV8ugvwc8fFegPnSV60Jl4xtvK2A==} + hasBin: true + dependencies: + '@babel/runtime': 7.23.9 + js-beautify: 1.14.11 + lodash: 4.17.21 + mjml-core: 4.14.1 + mjml-parser-xml: 4.14.1 + yargs: 16.2.0 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-navbar@4.14.1: + resolution: {integrity: sha512-rNy1Kw8CR3WQ+M55PFBAUDz2VEOjz+sk06OFnsnmNjoMVCjo1EV7OFLDAkmxAwqkC8h4zQWEOFY0MBqqoAg7+A==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-parser-xml@4.14.1: + resolution: {integrity: sha512-9WQVeukbXfq9DUcZ8wOsHC6BTdhaVwTAJDYMIQglXLwKwN7I4pTCguDDHy5d0kbbzK5OCVxCdZe+bfVI6XANOQ==} + dependencies: + '@babel/runtime': 7.23.9 + detect-node: 2.0.4 + htmlparser2: 8.0.2 + lodash: 4.17.21 + dev: false + + /mjml-preset-core@4.14.1: + resolution: {integrity: sha512-uUCqK9Z9d39rwB/+JDV2KWSZGB46W7rPQpc9Xnw1DRP7wD7qAfJwK6AZFCwfTgWdSxw0PwquVNcrUS9yBa9uhw==} + dependencies: + '@babel/runtime': 7.23.9 + mjml-accordion: 4.14.1 + mjml-body: 4.14.1 + mjml-button: 4.14.1 + mjml-carousel: 4.14.1 + mjml-column: 4.14.1 + mjml-divider: 4.14.1 + mjml-group: 4.14.1 + mjml-head: 4.14.1 + mjml-head-attributes: 4.14.1 + mjml-head-breakpoint: 4.14.1 + mjml-head-font: 4.14.1 + mjml-head-html-attributes: 4.14.1 + mjml-head-preview: 4.14.1 + mjml-head-style: 4.14.1 + mjml-head-title: 4.14.1 + mjml-hero: 4.14.1 + mjml-image: 4.14.1 + mjml-navbar: 4.14.1 + mjml-raw: 4.14.1 + mjml-section: 4.14.1 + mjml-social: 4.14.1 + mjml-spacer: 4.14.1 + mjml-table: 4.14.1 + mjml-text: 4.14.1 + mjml-wrapper: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-raw@4.14.1: + resolution: {integrity: sha512-9+4wzoXnCtfV6QPmjfJkZ50hxFB4Z8QZnl2Ac0D1Cn3dUF46UkmO5NLMu7UDIlm5DdFyycZrMOwvZS4wv9ksPw==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-section@4.14.1: + resolution: {integrity: sha512-Ik5pTUhpT3DOfB3hEmAWp8rZ0ilWtIivnL8XdUJRfgYE9D+MCRn+reIO+DAoJHxiQoI6gyeKkIP4B9OrQ7cHQw==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-social@4.14.1: + resolution: {integrity: sha512-G44aOZXgZHukirjkeQWTTV36UywtE2YvSwWGNfo/8d+k5JdJJhCIrlwaahyKEAyH63G1B0Zt8b2lEWx0jigYUw==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-spacer@4.14.1: + resolution: {integrity: sha512-5SfQCXTd3JBgRH1pUy6NVZ0lXBiRqFJPVHBdtC3OFvUS3q1w16eaAXlIUWMKTfy8CKhQrCiE6m65kc662ZpYxA==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-table@4.14.1: + resolution: {integrity: sha512-aVBdX3WpyKVGh/PZNn2KgRem+PQhWlvnD00DKxDejRBsBSKYSwZ0t3EfFvZOoJ9DzfHsN0dHuwd6Z18Ps44NFQ==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-text@4.14.1: + resolution: {integrity: sha512-yZuvf5z6qUxEo5CqOhCUltJlR6oySKVcQNHwoV5sneMaKdmBiaU4VDnlYFera9gMD9o3KBHIX6kUg7EHnCwBRQ==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml-validator@4.13.0: + resolution: {integrity: sha512-uURYfyQYtHJ6Qz/1A7/+E9ezfcoISoLZhYK3olsxKRViwaA2Mm8gy/J3yggZXnsUXWUns7Qymycm5LglLEIiQg==} + dependencies: + '@babel/runtime': 7.23.9 + dev: false + + /mjml-wrapper@4.14.1: + resolution: {integrity: sha512-aA5Xlq6d0hZ5LY+RvSaBqmVcLkvPvdhyAv3vQf3G41Gfhel4oIPmkLnVpHselWhV14A0KwIOIAKVxHtSAxyOTQ==} + dependencies: + '@babel/runtime': 7.23.9 + lodash: 4.17.21 + mjml-core: 4.14.1 + mjml-section: 4.14.1 + transitivePeerDependencies: + - encoding + dev: false + + /mjml@4.14.1: + resolution: {integrity: sha512-f/wnWWIVbeb/ge3ff7c/KYYizI13QbGIp03odwwkCThsJsacw4gpZZAU7V4gXY3HxSXP2/q3jxOfaHVbkfNpOQ==} + hasBin: true + dependencies: + '@babel/runtime': 7.23.9 + mjml-cli: 4.14.1 + mjml-core: 4.14.1 + mjml-migrate: 4.14.1 + mjml-preset-core: 4.14.1 + mjml-validator: 4.13.0 + transitivePeerDependencies: + - encoding + dev: false + + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + + /mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} + engines: {node: '>=10'} + hasBin: true + + /mlly@1.5.0: + resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==} + dependencies: + acorn: 8.11.3 + pathe: 1.1.2 + pkg-types: 1.0.3 + ufo: 1.4.0 + dev: true + + /mnemonist@0.39.6: + resolution: {integrity: sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==} + dependencies: + obliterator: 2.0.4 + dev: false + + /mockdate@3.0.5: + resolution: {integrity: sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==} + dev: false + + /modify-values@1.0.1: + resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} + engines: {node: '>=0.10.0'} + dev: true + + /moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + + /morgan@1.10.0: + resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} + engines: {node: '>= 0.8.0'} + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + /msgpackr-extract@3.0.2: + resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==} + hasBin: true + requiresBuild: true + dependencies: + node-gyp-build-optional-packages: 5.0.7 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.2 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.2 + dev: false + optional: true + + /msgpackr@1.10.1: + resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==} + optionalDependencies: + msgpackr-extract: 3.0.2 + dev: false + + /mute-stream@0.0.7: + resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==} + dev: true + + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + + /mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + + /mysql2@3.9.1: + resolution: {integrity: sha512-3njoWAAhGBYy0tWBabqUQcLtczZUxrmmtc2vszQUekg3kTJyZ5/IeLC3Fo04u6y6Iy5Sba7pIIa2P/gs8D3ZeQ==} + engines: {node: '>= 8.0'} + dependencies: + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.2.3 + lru-cache: 8.0.5 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + dev: false + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: false + + /named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + dependencies: + lru-cache: 7.18.3 + dev: false + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + /neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + /next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + dev: true + + /nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + dev: false + + /no-case@2.3.2: + resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} + dependencies: + lower-case: 1.1.4 + dev: false + + /node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + dev: true + + /node-emoji@1.11.0: + resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} + dependencies: + lodash: 4.17.21 + dev: true + + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + + /node-gyp-build-optional-packages@5.0.7: + resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} + hasBin: true + requiresBuild: true + dev: false + optional: true + + /node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: true + + /nodemailer@6.9.9: + resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==} + engines: {node: '>=6.0.0'} + dev: false + + /nopt@7.2.0: + resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + dependencies: + abbrev: 2.0.0 + dev: false + + /normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.13.1 + semver: 7.6.0 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + /notepack.io@3.0.1: + resolution: {integrity: sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg==} + dev: false + + /npm-run-path@2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} + dependencies: + path-key: 2.0.1 + dev: false + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /npm-run-path@5.2.0: + resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + + /oauth@0.10.0: + resolution: {integrity: sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==} + dev: false + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + + /object-is@1.1.5: + resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /obliterator@2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + dev: false + + /on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + dev: false + + /on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: true + + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: true + + /on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + + /one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + dependencies: + fn.name: 1.1.0 + dev: false + + /onetime@2.0.1: + resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} + engines: {node: '>=4'} + dependencies: + mimic-fn: 1.2.0 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /open@7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: false + + /open@8.4.0: + resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} + engines: {node: '>=12'} + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + + /opencollective-postinstall@2.0.3: + resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==} + hasBin: true + dev: true + + /opentype.js@0.7.3: + resolution: {integrity: sha512-Veui5vl2bLonFJ/SjX/WRWJT3SncgiZNnKUyahmXCc2sa1xXW15u3R/3TN5+JFiP7RsjK5ER4HA5eWaEmV9deA==} + hasBin: true + dependencies: + tiny-inflate: 1.0.3 + dev: false + + /optionator@0.8.3: + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.5 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + + /os-homedir@1.0.2: + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} + engines: {node: '>=0.10.0'} + dev: true + + /os-name@1.0.3: + resolution: {integrity: sha512-f5estLO2KN8vgtTRaILIgEGBoBrMnZ3JQ7W9TMZCnOIGwHe8TRGSpcagnWDo+Dfhd/z08k9Xe75hvciJJ8Qaew==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + osx-release: 1.1.0 + win-release: 1.1.1 + dev: false + + /os-name@4.0.1: + resolution: {integrity: sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==} + engines: {node: '>=10'} + dependencies: + macos-release: 2.5.1 + windows-release: 4.0.0 + dev: true + + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true + + /osx-release@1.1.0: + resolution: {integrity: sha512-ixCMMwnVxyHFQLQnINhmIpWqXIfS2YOXchwQrk+OFzmo6nDjQ0E4KXAyyUh0T0MZgV4bUhkRrAbVqlE4yLVq4A==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: false + + /p-event@4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + dependencies: + p-timeout: 3.2.0 + dev: false + + /p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + dev: false + + /p-limit@1.3.0: + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} + dependencies: + p-try: 1.0.0 + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /p-locate@2.0.0: + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} + dependencies: + p-limit: 1.3.0 + dev: true + + /p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-limit: 4.0.0 + dev: true + + /p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + dependencies: + p-finally: 1.0.0 + dev: false + + /p-try@1.0.0: + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /p-wait-for@3.2.0: + resolution: {integrity: sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==} + engines: {node: '>=8'} + dependencies: + p-timeout: 3.2.0 + dev: false + + /pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + dev: true + + /param-case@2.1.1: + resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} + dependencies: + no-case: 2.3.2 + dev: false + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + dev: true + + /parse-gitignore@2.0.0: + resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} + engines: {node: '>=14'} + dev: true + + /parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.23.5 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /parse-passwd@1.0.0: + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + engines: {node: '>=0.10.0'} + dev: true + + /parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + dependencies: + parse5: 6.0.1 + dev: false + + /parse5-htmlparser2-tree-adapter@7.0.0: + resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + dependencies: + domhandler: 5.0.3 + parse5: 7.1.2 + + /parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + dev: false + + /parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + dev: false + + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + + /parseley@0.12.1: + resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} + dependencies: + leac: 0.6.0 + peberminta: 0.9.0 + dev: false + + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: true + + /passport-google-oauth20@2.0.0: + resolution: {integrity: sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==} + engines: {node: '>= 0.4.0'} + dependencies: + passport-oauth2: 1.8.0 + dev: false + + /passport-jwt@4.0.1: + resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==} + dependencies: + jsonwebtoken: 9.0.2 + passport-strategy: 1.0.0 + dev: false + + /passport-local@1.0.0: + resolution: {integrity: sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==} + engines: {node: '>= 0.4.0'} + dependencies: + passport-strategy: 1.0.0 + dev: false + + /passport-oauth2@1.8.0: + resolution: {integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==} + engines: {node: '>= 0.4.0'} + dependencies: + base64url: 3.0.1 + oauth: 0.10.0 + passport-strategy: 1.0.0 + uid2: 0.0.4 + utils-merge: 1.0.1 + dev: false + + /passport-strategy@1.0.0: + resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} + engines: {node: '>= 0.4.0'} + dev: false + + /passport@0.7.0: + resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==} + engines: {node: '>= 0.4.0'} + dependencies: + passport-strategy: 1.0.0 + pause: 0.0.1 + utils-merge: 1.0.1 + dev: false + + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: true + + /path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + /path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + dev: false + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.2.0 + minipass: 7.0.4 + + /path-to-regexp@3.2.0: + resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} + + /path-to-regexp@6.2.1: + resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} + dev: false + + /path-type@3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + dependencies: + pify: 3.0.0 + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true + + /pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + dependencies: + through: 2.3.8 + + /pause@0.0.1: + resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} + dev: false + + /pdfjs-dist@2.12.313: + resolution: {integrity: sha512-1x6iXO4Qnv6Eb+YFdN5JdUzt4pAkxSp3aLAYPX93eQCyg/m7QFzXVWJHJVtoW48CI8HCXju4dSkhQZwoheL5mA==} + peerDependencies: + worker-loader: ^3.0.8 + peerDependenciesMeta: + worker-loader: + optional: true + dev: true + + /pdfmake@0.2.9: + resolution: {integrity: sha512-LAtYwlR8cCQqbxESK2d50DYaVAzAC9Id9NjilRte6Tb9pyHUB+Z50nhD0imuBL0eDyXQKvEYSNjo3P5AOc2ZCg==} + engines: {node: '>=12'} + dependencies: + '@foliojs-fork/linebreak': 1.1.1 + '@foliojs-fork/pdfkit': 0.14.0 + iconv-lite: 0.6.3 + xmldoc: 1.3.0 + dev: true + + /peberminta@0.9.0: + resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} + dev: false + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + /picomatch@3.0.1: + resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==} + engines: {node: '>=10'} + dev: true + + /picomatch@4.0.1: + resolution: {integrity: sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==} + engines: {node: '>=12'} + dev: true + + /pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + + /pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + dev: true + + /pino-abstract-transport@1.1.0: + resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==} + dependencies: + readable-stream: 4.5.2 + split2: 4.2.0 + dev: false + + /pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + dev: false + + /pino@8.18.0: + resolution: {integrity: sha512-Mz/gKiRyuXu4HnpHgi1YWdHQCoWMufapzooisvFn78zl4dZciAxS+YeRkUxXl1ee/SzU80YCz1zpECCh4oC6Aw==} + hasBin: true + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.3.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.1.0 + pino-std-serializers: 6.2.2 + process-warning: 3.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.4.3 + sonic-boom: 3.8.0 + thread-stream: 2.4.1 + dev: false + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.1 + mlly: 1.5.0 + pathe: 1.1.2 + dev: true + + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + + /png-js@1.0.0: + resolution: {integrity: sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==} + dev: true + + /postcss-selector-parser@6.0.15: + resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss@8.4.35: + resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls@1.1.2: + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} + engines: {node: '>= 0.8.0'} + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /preview-email@3.0.19: + resolution: {integrity: sha512-DBS3Nir18YtKc8loYCCOGitmiaQ0vTdahPoiXxwNweJDpmVZo+w3tppufOhoK0m8skpRxT56llYLs3VrORnmNQ==} + engines: {node: '>=14'} + dependencies: + ci-info: 3.9.0 + display-notification: 2.0.0 + fixpack: 4.0.0 + get-port: 5.1.1 + mailparser: 3.6.7 + nodemailer: 6.9.9 + open: 7.4.2 + p-event: 4.2.0 + p-wait-for: 3.2.0 + pug: 3.0.2 + uuid: 9.0.1 + dev: false + + /prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + dev: true + + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + dev: false + + /process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + dev: false + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: false + + /promise-coalesce@1.1.2: + resolution: {integrity: sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg==} + engines: {node: '>=16'} + dev: false + + /promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + requiresBuild: true + dependencies: + asap: 2.0.6 + dev: false + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /propagating-hammerjs@1.5.0: + resolution: {integrity: sha512-3PUXWmomwutoZfydC+lJwK1bKCh6sK6jZGB31RUX6+4EXzsbkDZrK4/sVR7gBrvJaEIwpTVyxQUAd29FKkmVdw==} + dependencies: + hammerjs: 2.0.8 + dev: true + + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: false + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + + /proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false + + /proxy-middleware@0.15.0: + resolution: {integrity: sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==} + engines: {node: '>=0.8.0'} + dev: true + + /pug-attrs@3.0.0: + resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==} + requiresBuild: true + dependencies: + constantinople: 4.0.1 + js-stringify: 1.0.2 + pug-runtime: 3.0.1 + dev: false + + /pug-code-gen@3.0.2: + resolution: {integrity: sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==} + requiresBuild: true + dependencies: + constantinople: 4.0.1 + doctypes: 1.1.0 + js-stringify: 1.0.2 + pug-attrs: 3.0.0 + pug-error: 2.0.0 + pug-runtime: 3.0.1 + void-elements: 3.1.0 + with: 7.0.2 + dev: false + + /pug-error@2.0.0: + resolution: {integrity: sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==} + requiresBuild: true + dev: false + + /pug-filters@4.0.0: + resolution: {integrity: sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==} + requiresBuild: true + dependencies: + constantinople: 4.0.1 + jstransformer: 1.0.0 + pug-error: 2.0.0 + pug-walk: 2.0.0 + resolve: 1.22.8 + dev: false + + /pug-lexer@5.0.1: + resolution: {integrity: sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==} + requiresBuild: true + dependencies: + character-parser: 2.2.0 + is-expression: 4.0.0 + pug-error: 2.0.0 + dev: false + + /pug-linker@4.0.0: + resolution: {integrity: sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==} + requiresBuild: true + dependencies: + pug-error: 2.0.0 + pug-walk: 2.0.0 + dev: false + + /pug-load@3.0.0: + resolution: {integrity: sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==} + requiresBuild: true + dependencies: + object-assign: 4.1.1 + pug-walk: 2.0.0 + dev: false + + /pug-parser@6.0.0: + resolution: {integrity: sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==} + requiresBuild: true + dependencies: + pug-error: 2.0.0 + token-stream: 1.0.0 + dev: false + + /pug-runtime@3.0.1: + resolution: {integrity: sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==} + requiresBuild: true + dev: false + + /pug-strip-comments@2.0.0: + resolution: {integrity: sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==} + requiresBuild: true + dependencies: + pug-error: 2.0.0 + dev: false + + /pug-walk@2.0.0: + resolution: {integrity: sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==} + requiresBuild: true + dev: false + + /pug@3.0.2: + resolution: {integrity: sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==} + dependencies: + pug-code-gen: 3.0.2 + pug-filters: 4.0.0 + pug-lexer: 5.0.1 + pug-linker: 4.0.0 + pug-load: 3.0.0 + pug-parser: 6.0.0 + pug-runtime: 3.0.1 + pug-strip-comments: 2.0.0 + dev: false + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + dev: true + + /q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + dev: true + + /qiniu@7.11.0: + resolution: {integrity: sha512-Pdux9AxQR5V8IrlkSWDBUIrBRoxyK98sfmdGm19R0jZyxBMM2+KMwB0zhjAJhb6+lxEzjyHO3EfsVRz0JeTj7A==} + engines: {node: '>= 6'} + dependencies: + agentkeepalive: 4.5.0 + before: 0.0.1 + block-stream2: 2.1.0 + crc32: 0.2.2 + destroy: 1.2.0 + encodeurl: 1.0.2 + formstream: 1.3.1 + mime: 2.6.0 + mockdate: 3.0.5 + tunnel-agent: 0.6.0 + urllib: 2.41.0 + transitivePeerDependencies: + - proxy-agent + - supports-color + dev: false + + /qs@6.11.2: + resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.5 + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + dev: false + + /quick-lru@4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + dev: true + + /quote-stream@1.0.2: + resolution: {integrity: sha512-kKr2uQ2AokadPjvTyKJQad9xELbZwYzWlNfI3Uz2j/ib5u6H9lDP7fUUR//rMycd0gv4Z5P1qXMfXR8YpIxrjQ==} + hasBin: true + dependencies: + buffer-equal: 0.0.1 + minimist: 1.2.8 + through2: 2.0.5 + dev: true + + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: true + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: false + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /read-pkg-up@3.0.0: + resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==} + engines: {node: '>=4'} + dependencies: + find-up: 2.1.0 + read-pkg: 3.0.0 + dev: true + + /read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg@3.0.0: + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} + dependencies: + load-json-file: 4.0.0 + normalize-package-data: 2.5.0 + path-type: 3.0.0 + dev: true + + /read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + /readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + dev: false + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + + /real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + dev: false + + /rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + dependencies: + resolve: 1.22.8 + dev: true + + /redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: true + + /redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + dev: false + + /redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + dependencies: + redis-errors: 1.2.0 + dev: false + + /reflect-metadata@0.1.14: + resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} + dev: false + + /reflect-metadata@0.2.1: + resolution: {integrity: sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==} + + /regenerate-unicode-properties@10.1.1: + resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} + engines: {node: '>=4'} + dependencies: + regenerate: 1.4.2 + dev: true + + /regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + dev: true + + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + /regenerator-transform@0.15.2: + resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + dependencies: + '@babel/runtime': 7.23.9 + dev: true + + /regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + dev: true + + /regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.1 + dev: true + + /regexpu-core@5.3.2: + resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} + engines: {node: '>=4'} + dependencies: + '@babel/regjsgen': 0.8.0 + regenerate: 1.4.2 + regenerate-unicode-properties: 10.1.1 + regjsparser: 0.9.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.1.0 + dev: true + + /regjsparser@0.10.0: + resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /regjsparser@0.9.1: + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + dev: false + + /repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-dir@1.0.1: + resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} + engines: {node: '>=0.10.0'} + dependencies: + expand-tilde: 2.0.2 + global-modules: 1.0.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve-global@1.0.0: + resolution: {integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + global-dirs: 0.1.1 + dev: true + optional: true + + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + + /resolve@1.1.7: + resolution: {integrity: sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + /restore-cursor@2.0.0: + resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} + engines: {node: '>=4'} + dependencies: + onetime: 2.0.1 + signal-exit: 3.0.7 + dev: true + + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + dev: false + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + /rfdc@1.3.1: + resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} + + /rimraf@2.6.3: + resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf@4.4.1: + resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 9.3.5 + dev: true + + /rimraf@5.0.5: + resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 10.3.10 + dev: false + + /run-applescript@3.2.0: + resolution: {integrity: sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg==} + engines: {node: '>=4'} + dependencies: + execa: 0.10.0 + dev: false + + /run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: true + + /run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + dependencies: + tslib: 1.14.1 + dev: true + + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + dependencies: + tslib: 2.6.2 + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + /safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + dependencies: + ret: 0.2.2 + dev: false + + /safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + dev: false + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + /sax@1.3.0: + resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} + dev: true + + /schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + dev: true + + /scope-analyzer@2.1.2: + resolution: {integrity: sha512-5cfCmsTYV/wPaRIItNxatw02ua/MThdIUNnUOCYp+3LSEJvnG804ANw2VLaavNILIfWXF1D1G2KNANkBBvInwQ==} + dependencies: + array-from: 2.1.1 + dash-ast: 2.0.1 + es6-map: 0.1.5 + es6-set: 0.1.6 + es6-symbol: 3.1.3 + estree-is-function: 1.0.0 + get-assigned-identifiers: 1.2.0 + dev: true + + /secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + dev: false + + /selderee@0.11.0: + resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} + dependencies: + parseley: 0.12.1 + dev: false + + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + dev: false + + /serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + dependencies: + randombytes: 2.1.0 + dev: true + + /serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + dev: false + + /set-function-length@1.2.1: + resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + dev: true + + /setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + dev: true + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + + /shallow-copy@0.0.1: + resolution: {integrity: sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==} + dev: true + + /shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + + /shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + dev: false + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + /shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + dependencies: + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + dev: true + + /side-channel@1.0.5: + resolution: {integrity: sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + dev: true + + /slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + dev: true + + /slick@1.12.2: + resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} + dev: false + + /socket.io-adapter@2.5.2: + resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==} + dependencies: + ws: 8.11.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + /socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + + /socket.io@4.7.4: + resolution: {integrity: sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==} + engines: {node: '>=10.2.0'} + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.4 + engine.io: 6.5.4 + socket.io-adapter: 2.5.2 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + /sonic-boom@3.8.0: + resolution: {integrity: sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==} + dependencies: + atomic-sleep: 1.0.0 + dev: false + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.1.43: + resolution: {integrity: sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==} + engines: {node: '>=0.8.0'} + requiresBuild: true + dependencies: + amdefine: 1.0.1 + dev: true + optional: true + + /source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + dev: false + + /source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + + /sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + dev: true + + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.17 + dev: true + + /spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.17 + dev: true + + /spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.17 + dev: true + + /spdx-license-ids@3.0.17: + resolution: {integrity: sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==} + dev: true + + /split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + dependencies: + readable-stream: 3.6.2 + dev: true + + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: false + + /split@1.0.1: + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + dependencies: + through: 2.3.8 + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + dev: false + + /stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + dependencies: + stackframe: 1.3.4 + dev: false + + /stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + dev: false + + /stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + dev: false + + /stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + dependencies: + source-map: 0.5.6 + stackframe: 1.3.4 + dev: false + + /stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + dependencies: + error-stack-parser: 2.1.4 + stack-generator: 2.0.10 + stacktrace-gps: 3.1.2 + dev: false + + /standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + dev: false + + /standard-version@9.5.0: + resolution: {integrity: sha512-3zWJ/mmZQsOaO+fOlsa0+QK90pwhNd042qEcw6hKFNoLFs7peGyvPffpEBbK/DSGPbyOvli0mUIFv5A4qTjh2Q==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chalk: 2.4.2 + conventional-changelog: 3.1.25 + conventional-changelog-config-spec: 2.1.0 + conventional-changelog-conventionalcommits: 4.6.3 + conventional-recommended-bump: 6.1.0 + detect-indent: 6.1.0 + detect-newline: 3.1.0 + dotgitignore: 2.1.0 + figures: 3.2.0 + find-up: 5.0.0 + git-semver-tags: 4.1.1 + semver: 7.6.0 + stringify-package: 1.0.1 + yargs: 16.2.0 + dev: true + + /static-eval@2.1.1: + resolution: {integrity: sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==} + dependencies: + escodegen: 2.1.0 + dev: true + + /static-module@3.0.4: + resolution: {integrity: sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw==} + dependencies: + acorn-node: 1.8.2 + concat-stream: 1.6.2 + convert-source-map: 1.9.0 + duplexer2: 0.1.4 + escodegen: 1.14.3 + has: 1.0.4 + magic-string: 0.25.1 + merge-source-map: 1.0.4 + object-inspect: 1.13.1 + readable-stream: 2.3.8 + scope-analyzer: 2.1.2 + shallow-copy: 0.0.1 + static-eval: 2.1.1 + through2: 2.0.5 + dev: true + + /statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + /stream-combiner@0.2.2: + resolution: {integrity: sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==} + dependencies: + duplexer: 0.1.2 + through: 2.3.8 + dev: true + + /stream-wormhole@1.1.0: + resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==} + engines: {node: '>=4.0.0'} + dev: false + + /string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + dev: true + + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-width@2.1.1: + resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} + engines: {node: '>=4'} + dependencies: + is-fullwidth-code-point: 2.0.0 + strip-ansi: 4.0.0 + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + /string-width@7.1.0: + resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} + engines: {node: '>=18'} + dependencies: + emoji-regex: 10.3.0 + get-east-asian-width: 1.2.0 + strip-ansi: 7.1.0 + dev: true + + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + + /stringify-package@1.0.1: + resolution: {integrity: sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==} + deprecated: This module is not used anymore, and has been replaced by @npmcli/package-json + dev: true + + /strip-ansi@4.0.0: + resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==} + engines: {node: '>=4'} + dependencies: + ansi-regex: 3.0.1 + dev: true + + /strip-ansi@5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + dependencies: + ansi-regex: 4.1.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + + /strip-eof@1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + dev: false + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: false + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /superagent@8.1.2: + resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} + engines: {node: '>=6.4.0 <13 || >=14'} + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.3.4 + fast-safe-stringify: 2.1.1 + form-data: 4.0.0 + formidable: 2.1.2 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.11.2 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + + /supertest@6.3.4: + resolution: {integrity: sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==} + engines: {node: '>=6.4.0'} + dependencies: + methods: 1.1.2 + superagent: 8.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + /svg-captcha@1.4.0: + resolution: {integrity: sha512-/fkkhavXPE57zRRCjNqAP3txRCSncpMx3NnNZL7iEoyAtYwUjPhJxW6FQTQPG5UPEmCrbFoXS10C3YdJlW7PDg==} + engines: {node: '>=4.x'} + dependencies: + opentype.js: 0.7.3 + dev: false + + /svg-pan-zoom@3.6.1: + resolution: {integrity: sha512-JaKkGHHfGvRrcMPdJWkssLBeWqM+Isg/a09H7kgNNajT1cX5AztDTNs+C8UzpCxjCTRrG34WbquwaovZbmSk9g==} + dev: true + + /swagger-ui-dist@5.11.2: + resolution: {integrity: sha512-jQG0cRgJNMZ7aCoiFofnoojeSaa/+KgWaDlfgs8QN+BXoGMpxeMVY5OEnjq4OlNvF3yjftO8c9GRAgcHlO+u7A==} + dev: false + + /symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + dev: true + + /synckit@0.6.2: + resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} + engines: {node: '>=12.20'} + dependencies: + tslib: 2.6.2 + dev: true + + /systeminformation@5.22.0: + resolution: {integrity: sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==} + engines: {node: '>=8.0.0'} + os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] + hasBin: true + dev: false + + /tablesort@5.3.0: + resolution: {integrity: sha512-WkfcZBHsp47gVH9CBHG0ZXopriG01IA87arGrchvIe868d4RiXVvoYPS1zMq9IdW05kBs5iGsqxTABqLyWonbg==} + dev: true + + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + + /temp@0.9.4: + resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==} + engines: {node: '>=6.0.0'} + dependencies: + mkdirp: 0.5.6 + rimraf: 2.6.3 + dev: true + + /terser-webpack-plugin@5.3.10(webpack@5.90.1): + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.22 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.27.1 + webpack: 5.90.1 + dev: true + + /terser@5.27.1: + resolution: {integrity: sha512-29wAr6UU/oQpnTw5HoadwjUZnFQXGdOfj0LjZ4sVxzqwHh/QVkvr7m8y9WoR4iN3FRitVduTc6KdjcW38Npsug==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.5 + acorn: 8.11.3 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-decoding@1.0.0: + resolution: {integrity: sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==} + dev: false + + /text-extensions@1.9.0: + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} + dev: true + + /text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + dev: false + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: false + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: false + + /thread-stream@2.4.1: + resolution: {integrity: sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==} + dependencies: + real-require: 0.2.0 + dev: false + + /through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + dev: true + + /through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + dependencies: + readable-stream: 3.6.2 + dev: true + + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + /tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + /tlds@1.248.0: + resolution: {integrity: sha512-noj0KdpWTBhwsKxMOXk0rN9otg4kTgLm4WohERRHbJ9IY+kSDKr3RmjitaQ3JFzny+DyvBOQKlFZhp0G0qNSfg==} + hasBin: true + dev: false + + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + + /tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + + /toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + dev: false + + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + /token-stream@1.0.0: + resolution: {integrity: sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==} + requiresBuild: true + dev: false + + /toml-eslint-parser@0.9.3: + resolution: {integrity: sha512-moYoCvkNUAPCxSW9jmHmRElhm4tVJpHL8ItC/+uYD0EpPSFXbck7yREz9tNdJVTSpHVod8+HoipcpbQ0oE6gsw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + eslint-visitor-keys: 3.4.3 + dev: true + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + /traverse@0.6.8: + resolution: {integrity: sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==} + engines: {node: '>= 0.4'} + dev: true + + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true + + /trim-newlines@3.0.1: + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} + dev: true + + /triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + dev: false + + /ts-api-utils@1.2.1(typescript@5.3.3): + resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.3.3 + dev: true + + /ts-jest@29.1.2(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.3.3): + resolution: {integrity: sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==} + engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.23.9 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@20.11.18)(ts-node@10.9.2) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.0 + typescript: 5.3.3 + yargs-parser: 21.1.1 + dev: true + + /ts-loader@9.5.1(typescript@5.3.3)(webpack@5.90.1): + resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.15.0 + micromatch: 4.0.5 + semver: 7.6.0 + source-map: 0.7.4 + typescript: 5.3.3 + webpack: 5.90.1 + dev: true + + /ts-morph@20.0.0: + resolution: {integrity: sha512-JVmEJy2Wow5n/84I3igthL9sudQ8qzjh/6i4tmYCm6IqYyKFlNbJZi7oBdjyqcWSWYRu3CtL0xbT6fS03ESZIg==} + dependencies: + '@ts-morph/common': 0.21.0 + code-block-writer: 12.0.0 + dev: true + + /ts-node@10.9.2(@types/node@20.11.18)(typescript@5.3.3): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.11.18 + acorn: 8.11.3 + acorn-walk: 8.3.2 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.3.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + /tsconfig-paths-webpack-plugin@4.1.0: + resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==} + engines: {node: '>=10.13.0'} + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.15.0 + tsconfig-paths: 4.2.0 + dev: true + + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.6.0: + resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} + dev: false + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /type-check@0.3.2: + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.18.1: + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + + /type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + dev: true + + /type@1.2.0: + resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} + dev: true + + /type@2.7.2: + resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} + dev: true + + /typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + dev: true + + /typeorm@0.3.17(ioredis@5.3.2)(mysql2@3.9.1)(ts-node@10.9.2): + resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==} + engines: {node: '>= 12.9.0'} + hasBin: true + peerDependencies: + '@google-cloud/spanner': ^5.18.0 + '@sap/hana-client': ^2.12.25 + better-sqlite3: ^7.1.2 || ^8.0.0 + hdb-pool: ^0.1.6 + ioredis: ^5.0.4 + mongodb: ^5.2.0 + mssql: ^9.1.1 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^5.1.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 + peerDependenciesMeta: + '@google-cloud/spanner': + optional: true + '@sap/hana-client': + optional: true + better-sqlite3: + optional: true + hdb-pool: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + dependencies: + '@sqltools/formatter': 1.2.5 + app-root-path: 3.1.0 + buffer: 6.0.3 + chalk: 4.1.2 + cli-highlight: 2.1.11 + date-fns: 2.30.0 + debug: 4.3.4 + dotenv: 16.4.4 + glob: 8.1.0 + ioredis: 5.3.2 + mkdirp: 2.1.6 + mysql2: 3.9.1 + reflect-metadata: 0.1.14 + sha.js: 2.4.11 + ts-node: 10.9.2(@types/node@20.11.18)(typescript@5.3.3) + tslib: 2.6.2 + uuid: 9.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + dev: false + + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + + /ua-parser-js@1.0.37: + resolution: {integrity: sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==} + dev: false + + /uc.micro@2.0.0: + resolution: {integrity: sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==} + dev: false + + /ufo@1.4.0: + resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==} + dev: true + + /uglify-js@3.17.4: + resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} + engines: {node: '>=0.8.0'} + hasBin: true + + /uid2@0.0.4: + resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==} + dev: false + + /uid2@1.0.0: + resolution: {integrity: sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==} + engines: {node: '>= 4.0.0'} + dev: false + + /uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + dependencies: + '@lukeed/csprng': 1.1.0 + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + /unescape@1.0.1: + resolution: {integrity: sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==} + engines: {node: '>=0.10.0'} + dependencies: + extend-shallow: 2.0.1 + dev: false + + /unicode-canonical-property-names-ecmascript@2.0.0: + resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + engines: {node: '>=4'} + dev: true + + /unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-property-aliases-ecmascript: 2.1.0 + dev: true + + /unicode-match-property-value-ecmascript@2.1.0: + resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} + engines: {node: '>=4'} + dev: true + + /unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + dev: true + + /unicode-property-aliases-ecmascript@2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + dev: true + + /unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + dev: true + + /unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + dev: true + + /unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + dependencies: + '@types/unist': 2.0.10 + dev: true + + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: true + + /unix-crypt-td-js@1.1.4: + resolution: {integrity: sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==} + dev: true + + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.23.0): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.23.0 + escalade: 3.1.2 + picocolors: 1.0.0 + dev: true + + /upper-case@1.1.3: + resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} + dev: false + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + + /urllib@2.41.0: + resolution: {integrity: sha512-pNXdxEv52L67jahLT+/7QE+Fup1y2Gc6EdmrAhQ6OpQIC2rl14oWwv9hvk1GXOZqEnJNwRXHABuwgPOs1CtL7g==} + engines: {node: '>= 0.10.0'} + peerDependencies: + proxy-agent: ^5.0.0 + peerDependenciesMeta: + proxy-agent: + optional: true + dependencies: + any-promise: 1.3.0 + content-type: 1.0.5 + debug: 2.6.9 + default-user-agent: 1.0.0 + digest-header: 1.1.0 + ee-first: 1.1.1 + formstream: 1.3.1 + humanize-ms: 1.2.1 + iconv-lite: 0.4.24 + ip: 1.1.8 + pump: 3.0.0 + qs: 6.11.2 + statuses: 1.5.0 + utility: 1.18.0 + transitivePeerDependencies: + - supports-color + dev: false + + /user-home@2.0.0: + resolution: {integrity: sha512-KMWqdlOcjCYdtIJpicDSFBQ8nFwS2i9sslAd6f4+CBGcU4gist2REnr2fxj2YocvJFxSF3ZOHLYLVZnUxv4BZQ==} + engines: {node: '>=0.10.0'} + dependencies: + os-homedir: 1.0.2 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + /utility@1.18.0: + resolution: {integrity: sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==} + engines: {node: '>= 0.12.0'} + dependencies: + copy-to: 2.0.1 + escape-html: 1.0.3 + mkdirp: 0.5.6 + mz: 2.7.0 + unescape: 1.0.1 + dev: false + + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + /v8-to-istanbul@9.2.0: + resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.22 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + dev: true + + /valid-data-url@3.0.1: + resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} + engines: {node: '>=10'} + dev: false + + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + + /validator@13.11.0: + resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + engines: {node: '>= 0.10'} + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + /vis@4.21.0-EOL: + resolution: {integrity: sha512-JVS1mywKg5S88XbkDJPfCb3n+vlg5fMA8Ae2hzs3KHAwD4ryM5qwlbFZ6ReDfY8te7I4NLCpuCoywJQEehvJlQ==} + deprecated: Please consider using https://github.com/visjs + dependencies: + emitter-component: 1.1.2 + hammerjs: 2.0.8 + keycharm: 0.2.0 + moment: 2.30.1 + propagating-hammerjs: 1.5.0 + dev: true + + /void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: false + + /vue-eslint-parser@9.4.2(eslint@8.56.0): + resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + debug: 4.3.4 + eslint: 8.56.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + lodash: 4.17.21 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + + /walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + dependencies: + makeerror: 1.0.12 + dev: true + + /watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + dev: true + + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + dependencies: + defaults: 1.0.4 + dev: true + + /web-resource-inliner@6.0.1: + resolution: {integrity: sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==} + engines: {node: '>=10.0.0'} + dependencies: + ansi-colors: 4.1.3 + escape-goat: 3.0.0 + htmlparser2: 5.0.1 + mime: 2.6.0 + node-fetch: 2.7.0 + valid-data-url: 3.0.1 + transitivePeerDependencies: + - encoding + dev: false + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + /webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + dev: true + + /webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + dev: true + + /webpack@5.90.1: + resolution: {integrity: sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/wasm-edit': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + acorn: 8.11.3 + acorn-import-assertions: 1.9.0(acorn@8.11.3) + browserslist: 4.23.0 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.15.0 + es-module-lexer: 1.4.1 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(webpack@5.90.1) + watchpack: 2.4.0 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + + /websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + dependencies: + http-parser-js: 0.5.8 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + dev: true + + /websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + dev: true + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + + /widest-line@3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} + dependencies: + string-width: 4.2.3 + dev: false + + /win-release@1.1.1: + resolution: {integrity: sha512-iCRnKVvGxOQdsKhcQId2PXV1vV3J/sDPXKA4Oe9+Eti2nb2ESEsYHRYls/UjoUW3bIc5ZDO8dTH50A/5iVN+bw==} + engines: {node: '>=0.10.0'} + dependencies: + semver: 5.7.2 + dev: false + + /windows-release@4.0.0: + resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==} + engines: {node: '>=10'} + dependencies: + execa: 4.1.0 + dev: true + + /winston-daily-rotate-file@5.0.0(winston@3.11.0): + resolution: {integrity: sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==} + engines: {node: '>=8'} + peerDependencies: + winston: ^3 + dependencies: + file-stream-rotator: 0.6.1 + object-hash: 3.0.0 + triple-beam: 1.4.1 + winston: 3.11.0 + winston-transport: 4.7.0 + dev: false + + /winston-transport@4.7.0: + resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} + engines: {node: '>= 12.0.0'} + dependencies: + logform: 2.6.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + dev: false + + /winston@3.11.0: + resolution: {integrity: sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==} + engines: {node: '>= 12.0.0'} + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.5 + is-stream: 2.0.1 + logform: 2.6.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.7.0 + dev: false + + /with@7.0.2: + resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + '@babel/parser': 7.23.9 + '@babel/types': 7.23.9 + assert-never: 1.2.1 + babel-walk: 3.0.0-canary-5 + dev: false + + /word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + /wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + dependencies: + ansi-styles: 6.2.1 + string-width: 7.1.0 + strip-ansi: 7.1.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /ws@8.11.0: + resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} + 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 + + /xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: true + + /xmldoc@1.3.0: + resolution: {integrity: sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==} + dependencies: + sax: 1.3.0 + dev: true + + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + /yaml-eslint-parser@1.2.2: + resolution: {integrity: sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==} + engines: {node: ^14.17.0 || >=16.0.0} + dependencies: + eslint-visitor-keys: 3.4.3 + lodash: 4.17.21 + yaml: 2.3.4 + dev: true + + /yaml@2.3.4: + resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} + engines: {node: '>= 14'} + dev: true + + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + + /zepto@1.2.0: + resolution: {integrity: sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==} + dev: true diff --git a/scripts/genEnvTypes.ts b/scripts/genEnvTypes.ts new file mode 100644 index 0000000..1e90f3a --- /dev/null +++ b/scripts/genEnvTypes.ts @@ -0,0 +1,49 @@ +import fs from 'node:fs' +import path from 'node:path' + +import dotenv from 'dotenv' + +const directoryPath = path.resolve(__dirname, '..') + +const targets = ['.env', `.env.${process.env.NODE_ENV || 'development'}`] + +const envObj = targets.reduce((prev, file) => { + const result = dotenv.parse(fs.readFileSync(path.join(directoryPath, file))) + return { ...prev, ...result } +}, {}) + +const envType = Object.entries(envObj).reduce((prev, [key, value]) => { + return `${prev} + ${key}: '${value}';` +}, '').trim() + +fs.writeFile(path.join(directoryPath, 'types/env.d.ts'), ` +// generate by ./scripts/generateEnvTypes.ts +declare global { + namespace NodeJS { + interface ProcessEnv { + ${envType} + } + } +} +export {}; + `, (err) => { + if (err) + console.log('生成 env.d.ts 文件失败') + else + console.log('成功生成 env.d.ts 文件') +}) + +// console.log('envObj:', envObj) + +function formatValue(value) { + let _value + try { + const res = JSON.parse(value) + _value = typeof res === 'object' ? value : res + } + catch (error) { + _value = `'${value}'` + } + return _value +} diff --git a/scripts/resetScheduler.ts b/scripts/resetScheduler.ts new file mode 100644 index 0000000..2a85291 --- /dev/null +++ b/scripts/resetScheduler.ts @@ -0,0 +1,26 @@ +import { exec } from 'node:child_process' + +import { CronJob } from 'cron' + +/** 此文件仅供演示时使用 */ + +const runMigrationGenerate = async function () { + exec('npm run migration:revert && npm run migration:run', (error, stdout, stderr) => { + if (!error) + console.log('操作成功', error) + + else + console.log('操作失败', error) + }) +} + +const job = CronJob.from({ + /** 每天凌晨 4.30 恢复初始数据 */ + cronTime: '30 4 * * *', + timeZone: 'Asia/Shanghai', + start: true, + onTick() { + runMigrationGenerate() + console.log('Task executed daily at 4.30 AM:', new Date().toLocaleTimeString()) + }, +}) diff --git a/src/app.module.ts b/src/app.module.ts new file mode 100644 index 0000000..34b38f7 --- /dev/null +++ b/src/app.module.ts @@ -0,0 +1,67 @@ +import { ClassSerializerInterceptor, Module } from '@nestjs/common' + +import { ConfigModule } from '@nestjs/config' +import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core' + +import config from '~/config' +import { SharedModule } from '~/shared/shared.module' + +import { AllExceptionsFilter } from './common/filters/any-exception.filter' + +import { IdempotenceInterceptor } from './common/interceptors/idempotence.interceptor' +import { TimeoutInterceptor } from './common/interceptors/timeout.interceptor' +import { TransformInterceptor } from './common/interceptors/transform.interceptor' +import { AuthModule } from './modules/auth/auth.module' +import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard' +import { RbacGuard } from './modules/auth/guards/rbac.guard' +import { HealthModule } from './modules/health/health.module' +import { NetdiskModule } from './modules/netdisk/netdisk.module' +import { SseModule } from './modules/sse/sse.module' +import { SystemModule } from './modules/system/system.module' +import { TasksModule } from './modules/tasks/tasks.module' +import { TodoModule } from './modules/todo/todo.module' +import { ToolsModule } from './modules/tools/tools.module' +import { DatabaseModule } from './shared/database/database.module' + +import { SocketModule } from './socket/socket.module' + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + expandVariables: true, + // 指定多个 env 文件时,第一个优先级最高 + envFilePath: ['.env.local', `.env.${process.env.NODE_ENV}`, '.env'], + load: [...Object.values(config)], + }), + SharedModule, + DatabaseModule, + + AuthModule, + SystemModule, + TasksModule.forRoot(), + ToolsModule, + SocketModule, + HealthModule, + SseModule, + NetdiskModule, + + // biz + + // end biz + + TodoModule, + ], + providers: [ + { provide: APP_FILTER, useClass: AllExceptionsFilter }, + + { provide: APP_INTERCEPTOR, useClass: ClassSerializerInterceptor }, + { provide: APP_INTERCEPTOR, useClass: TransformInterceptor }, + { provide: APP_INTERCEPTOR, useFactory: () => new TimeoutInterceptor(15 * 1000) }, + { provide: APP_INTERCEPTOR, useClass: IdempotenceInterceptor }, + + { provide: APP_GUARD, useClass: JwtAuthGuard }, + { provide: APP_GUARD, useClass: RbacGuard }, + ], +}) +export class AppModule {} diff --git a/src/assets/templates/verification-code-zh.hbs b/src/assets/templates/verification-code-zh.hbs new file mode 100644 index 0000000..df3702a --- /dev/null +++ b/src/assets/templates/verification-code-zh.hbs @@ -0,0 +1,4 @@ +

你的验证码是:

+

{{code}}

+

该验证码 10 分钟内有效,请勿将验证码告知给他人!

+本邮件由系统自动发出,请勿回复。 \ No newline at end of file diff --git a/src/assets/templates/verification-code.hbs b/src/assets/templates/verification-code.hbs new file mode 100644 index 0000000..6545c27 --- /dev/null +++ b/src/assets/templates/verification-code.hbs @@ -0,0 +1,5 @@ +

Your verification code is:

+

{{verificationCode}}

+

This code will expire in 10 minutes.

+This email is sent automatically by the system, please do not + reply. \ No newline at end of file diff --git a/src/common/adapters/fastify.adapter.ts b/src/common/adapters/fastify.adapter.ts new file mode 100644 index 0000000..e25d0ca --- /dev/null +++ b/src/common/adapters/fastify.adapter.ts @@ -0,0 +1,46 @@ +import FastifyCookie from '@fastify/cookie' +import FastifyMultipart from '@fastify/multipart' +import { FastifyAdapter } from '@nestjs/platform-fastify' + +const app: FastifyAdapter = new FastifyAdapter({ + trustProxy: true, + logger: false, + // forceCloseConnections: true, +}) +export { app as fastifyApp } + +app.register(FastifyMultipart, { + limits: { + fields: 10, // Max number of non-file fields + fileSize: 1024 * 1024 * 6, // limit size 6M + files: 5, // Max number of file fields + }, +}) + +app.register(FastifyCookie, { + secret: 'cookie-secret', // 这个 secret 不太重要,不存鉴权相关,无关紧要 +}) + +app.getInstance().addHook('onRequest', (request, reply, done) => { + // set undefined origin + const { origin } = request.headers + if (!origin) + request.headers.origin = request.headers.host + + // forbidden php + + const { url } = request + + if (url.endsWith('.php')) { + reply.raw.statusMessage + = 'Eh. PHP is not support on this machine. Yep, I also think PHP is bestest programming language. But for me it is beyond my reach.' + + return reply.code(418).send() + } + + // skip favicon request + if (url.match(/favicon.ico$/) || url.match(/manifest.json$/)) + return reply.code(204).send() + + done() +}) diff --git a/src/common/adapters/socket.adapter.ts b/src/common/adapters/socket.adapter.ts new file mode 100644 index 0000000..d766592 --- /dev/null +++ b/src/common/adapters/socket.adapter.ts @@ -0,0 +1,26 @@ +import { INestApplication } from '@nestjs/common' +import { IoAdapter } from '@nestjs/platform-socket.io' +import { createAdapter } from '@socket.io/redis-adapter' + +import { REDIS_PUBSUB } from '~/shared/redis/redis.constant' + +export const RedisIoAdapterKey = 'm-shop-socket' + +export class RedisIoAdapter extends IoAdapter { + constructor(private readonly app: INestApplication) { + super(app) + } + + createIOServer(port: number, options?: any) { + const server = super.createIOServer(port, options) + + const { pubClient, subClient } = this.app.get(REDIS_PUBSUB) + + const redisAdapter = createAdapter(pubClient, subClient, { + key: RedisIoAdapterKey, + requestsTimeout: 10000, + }) + server.adapter(redisAdapter) + return server + } +} diff --git a/src/common/decorators/api-result.decorator.ts b/src/common/decorators/api-result.decorator.ts new file mode 100644 index 0000000..1892762 --- /dev/null +++ b/src/common/decorators/api-result.decorator.ts @@ -0,0 +1,83 @@ +import { HttpStatus, Type, applyDecorators } from '@nestjs/common' +import { ApiExtraModels, ApiResponse, getSchemaPath } from '@nestjs/swagger' + +import { ResOp } from '~/common/model/response.model' + +const baseTypeNames = ['String', 'Number', 'Boolean'] + +function genBaseProp(type: Type) { + if (baseTypeNames.includes(type.name)) + return { type: type.name.toLocaleLowerCase() } + else + return { $ref: getSchemaPath(type) } +} + +/** + * @description: 生成返回结果装饰器 + */ +export function ApiResult>({ + type, + isPage, + status, +}: { + type?: TModel | TModel[] + isPage?: boolean + status?: HttpStatus +}) { + let prop = null + + if (Array.isArray(type)) { + if (isPage) { + prop = { + type: 'object', + properties: { + items: { + type: 'array', + items: { $ref: getSchemaPath(type[0]) }, + }, + meta: { + type: 'object', + properties: { + itemCount: { type: 'number', default: 0 }, + totalItems: { type: 'number', default: 0 }, + itemsPerPage: { type: 'number', default: 0 }, + totalPages: { type: 'number', default: 0 }, + currentPage: { type: 'number', default: 0 }, + }, + }, + }, + } + } + else { + prop = { + type: 'array', + items: genBaseProp(type[0]), + } + } + } + else if (type) { + prop = genBaseProp(type) + } + else { + prop = { type: 'null', default: null } + } + + const model = Array.isArray(type) ? type[0] : type + + return applyDecorators( + ApiExtraModels(model), + ApiResponse({ + status, + schema: { + allOf: [ + { $ref: getSchemaPath(ResOp) }, + { + properties: { + data: prop, + }, + }, + ], + }, + }), + ) +} diff --git a/src/common/decorators/bypass.decorator.ts b/src/common/decorators/bypass.decorator.ts new file mode 100644 index 0000000..8c7562d --- /dev/null +++ b/src/common/decorators/bypass.decorator.ts @@ -0,0 +1,10 @@ +import { SetMetadata } from '@nestjs/common' + +export const BYPASS_KEY = '__bypass_key__' + +/** + * 当不需要转换成基础返回格式时添加该装饰器 + */ +export function Bypass() { + return SetMetadata(BYPASS_KEY, true) +} diff --git a/src/common/decorators/cookie.decorator.ts b/src/common/decorators/cookie.decorator.ts new file mode 100644 index 0000000..102a527 --- /dev/null +++ b/src/common/decorators/cookie.decorator.ts @@ -0,0 +1,8 @@ +import type { ExecutionContext } from '@nestjs/common' +import { createParamDecorator } from '@nestjs/common' +import type { FastifyRequest } from 'fastify' + +export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest() + return data ? request.cookies?.[data] : request.cookies +}) diff --git a/src/common/decorators/cron-once.decorator.ts b/src/common/decorators/cron-once.decorator.ts new file mode 100644 index 0000000..39eab20 --- /dev/null +++ b/src/common/decorators/cron-once.decorator.ts @@ -0,0 +1,19 @@ +import cluster from 'node:cluster' + +import { Cron } from '@nestjs/schedule' + +import { isMainProcess } from '~/global/env' + +export const CronOnce: typeof Cron = (...rest): MethodDecorator => { + // If not in cluster mode, and PM2 main worker + if (isMainProcess) + // eslint-disable-next-line no-useless-call + return Cron.call(null, ...rest) + + if (cluster.isWorker && cluster.worker?.id === 1) + // eslint-disable-next-line no-useless-call + return Cron.call(null, ...rest) + + const returnNothing: MethodDecorator = () => {} + return returnNothing +} diff --git a/src/common/decorators/field.decorator.ts b/src/common/decorators/field.decorator.ts new file mode 100644 index 0000000..68fa483 --- /dev/null +++ b/src/common/decorators/field.decorator.ts @@ -0,0 +1,137 @@ +import { applyDecorators } from '@nestjs/common' +import { + IsBoolean, + IsDate, + IsInt, + IsNotEmpty, + IsNumber, + IsOptional, + IsPositive, + IsString, + Max, + MaxLength, + Min, + MinLength, +} from 'class-validator' +import { isNumber } from 'lodash' + +import { + ToArray, + ToBoolean, + ToDate, + ToLowerCase, + ToNumber, + ToTrim, + ToUpperCase, +} from './transform.decorator' + +interface IOptionalOptions { + required?: boolean +} + +interface INumberFieldOptions extends IOptionalOptions { + each?: boolean + int?: boolean + min?: number + max?: number + positive?: boolean +} + +interface IStringFieldOptions extends IOptionalOptions { + each?: boolean + minLength?: number + maxLength?: number + lowerCase?: boolean + upperCase?: boolean +} + +export function NumberField( + options: INumberFieldOptions = {}, +): PropertyDecorator { + const { each, min, max, int, positive, required = true } = options + + const decorators = [ToNumber()] + + if (each) + decorators.push(ToArray()) + + if (int) + decorators.push(IsInt({ each })) + else + decorators.push(IsNumber({}, { each })) + + if (isNumber(min)) + decorators.push(Min(min, { each })) + + if (isNumber(max)) + decorators.push(Max(max, { each })) + + if (positive) + decorators.push(IsPositive({ each })) + + if (!required) + decorators.push(IsOptional()) + + return applyDecorators(...decorators) +} + +export function StringField( + options: IStringFieldOptions = {}, +): PropertyDecorator { + const { + each, + minLength, + maxLength, + lowerCase, + upperCase, + required = true, + } = options + + const decorators = [IsString({ each }), ToTrim()] + + if (each) + decorators.push(ToArray()) + + if (isNumber(minLength)) + decorators.push(MinLength(minLength, { each })) + + if (isNumber(maxLength)) + decorators.push(MaxLength(maxLength, { each })) + + if (lowerCase) + decorators.push(ToLowerCase()) + + if (upperCase) + decorators.push(ToUpperCase()) + + if (!required) + decorators.push(IsOptional()) + else + decorators.push(IsNotEmpty({ each })) + + return applyDecorators(...decorators) +} + +export function BooleanField( + options: IOptionalOptions = {}, +): PropertyDecorator { + const decorators = [ToBoolean(), IsBoolean()] + + const { required = true } = options + + if (!required) + decorators.push(IsOptional()) + + return applyDecorators(...decorators) +} + +export function DateField(options: IOptionalOptions = {}): PropertyDecorator { + const decorators = [ToDate(), IsDate()] + + const { required = true } = options + + if (!required) + decorators.push(IsOptional()) + + return applyDecorators(...decorators) +} diff --git a/src/common/decorators/http.decorator.ts b/src/common/decorators/http.decorator.ts new file mode 100644 index 0000000..85ed449 --- /dev/null +++ b/src/common/decorators/http.decorator.ts @@ -0,0 +1,22 @@ +import type { ExecutionContext } from '@nestjs/common' + +import { createParamDecorator } from '@nestjs/common' +import type { FastifyRequest } from 'fastify' + +import { getIp } from '~/utils/ip.util' + +/** + * 快速获取IP + */ +export const Ip = createParamDecorator((_, context: ExecutionContext) => { + const request = context.switchToHttp().getRequest() + return getIp(request) +}) + +/** + * 快速获取request path,并不包括url params + */ +export const Uri = createParamDecorator((_, context: ExecutionContext) => { + const request = context.switchToHttp().getRequest() + return request.routerPath +}) diff --git a/src/common/decorators/id-param.decorator.ts b/src/common/decorators/id-param.decorator.ts new file mode 100644 index 0000000..fade33c --- /dev/null +++ b/src/common/decorators/id-param.decorator.ts @@ -0,0 +1,7 @@ +import { HttpStatus, NotAcceptableException, Param, ParseIntPipe } from '@nestjs/common' + +export function IdParam() { + return Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE, exceptionFactory: (_error) => { + throw new NotAcceptableException('id 格式不正确') + } })) +} diff --git a/src/common/decorators/idempotence.decorator.ts b/src/common/decorators/idempotence.decorator.ts new file mode 100644 index 0000000..d1142b3 --- /dev/null +++ b/src/common/decorators/idempotence.decorator.ts @@ -0,0 +1,15 @@ +import { SetMetadata } from '@nestjs/common' + +import { IdempotenceOption } from '../interceptors/idempotence.interceptor' + +export const HTTP_IDEMPOTENCE_KEY = '__idempotence_key__' +export const HTTP_IDEMPOTENCE_OPTIONS = '__idempotence_options__' + +/** + * 幂等 + */ +export function Idempotence(options?: IdempotenceOption): MethodDecorator { + return function (target, key, descriptor: PropertyDescriptor) { + SetMetadata(HTTP_IDEMPOTENCE_OPTIONS, options || {})(descriptor.value) + } +} diff --git a/src/common/decorators/swagger.decorator.ts b/src/common/decorators/swagger.decorator.ts new file mode 100644 index 0000000..4809e5c --- /dev/null +++ b/src/common/decorators/swagger.decorator.ts @@ -0,0 +1,11 @@ +import { applyDecorators } from '@nestjs/common' +import { ApiSecurity } from '@nestjs/swagger' + +export const API_SECURITY_AUTH = 'auth' + +/** + * like to @ApiSecurity('auth') + */ +export function ApiSecurityAuth(): ClassDecorator & MethodDecorator { + return applyDecorators(ApiSecurity(API_SECURITY_AUTH)) +} diff --git a/src/common/decorators/transform.decorator.ts b/src/common/decorators/transform.decorator.ts new file mode 100644 index 0000000..e86cf75 --- /dev/null +++ b/src/common/decorators/transform.decorator.ts @@ -0,0 +1,146 @@ +import { Transform } from 'class-transformer' +import { castArray, isArray, isNil, trim } from 'lodash' + +/** + * convert string to number + */ +export function ToNumber(): PropertyDecorator { + return Transform( + (params) => { + const value = params.value as string[] | string + + if (isArray(value)) + return value.map(v => Number(v)) + + return Number(value) + }, + { toClassOnly: true }, + ) +} + +/** + * convert string to int + */ +export function ToInt(): PropertyDecorator { + return Transform( + (params) => { + const value = params.value as string[] | string + + if (isArray(value)) + return value.map(v => Number.parseInt(v)) + + return Number.parseInt(value) + }, + { toClassOnly: true }, + ) +} + +/** + * convert string to boolean + */ +export function ToBoolean(): PropertyDecorator { + return Transform( + (params) => { + switch (params.value) { + case 'true': + return true + case 'false': + return false + default: + return params.value + } + }, + { toClassOnly: true }, + ) +} + +/** + * convert string to Date + */ +export function ToDate(): PropertyDecorator { + return Transform( + (params) => { + const { value } = params + + if (!value) + return + + return new Date(value) + }, + { toClassOnly: true }, + ) +} + +/** + * transforms to array, specially for query params + */ +export function ToArray(): PropertyDecorator { + return Transform( + (params) => { + const { value } = params + + if (isNil(value)) + return [] + + return castArray(value) + }, + { toClassOnly: true }, + ) +} + +/** + * trim spaces from start and end, replace multiple spaces with one. + */ +export function ToTrim(): PropertyDecorator { + return Transform( + (params) => { + const value = params.value as string[] | string + + if (isArray(value)) + return value.map(v => trim(v)) + + return trim(value) + }, + { toClassOnly: true }, + ) +} + +/** + * lowercase value + */ +export function ToLowerCase(): PropertyDecorator { + return Transform( + (params) => { + const value = params.value as string[] | string + + if (!value) + return + + if (isArray(value)) + return value.map(v => v.toLowerCase()) + + return value.toLowerCase() + }, + { toClassOnly: true }, + ) +} + +/** + * uppercase value + */ +export function ToUpperCase(): PropertyDecorator { + return Transform( + (params) => { + const value = params.value as string[] | string + + if (!value) + return + + if (isArray(value)) + return value.map(v => v.toUpperCase()) + + return value.toUpperCase() + }, + { toClassOnly: true }, + ) +} diff --git a/src/common/dto/cursor.dto.ts b/src/common/dto/cursor.dto.ts new file mode 100644 index 0000000..50c816c --- /dev/null +++ b/src/common/dto/cursor.dto.ts @@ -0,0 +1,26 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Expose, Transform } from 'class-transformer' +import { IsInt, IsOptional, Max, Min } from 'class-validator' + +export class CursorDto { + @ApiProperty({ minimum: 0, default: 0 }) + @Min(0) + @IsInt() + @Expose() + @IsOptional({ always: true }) + @Transform(({ value: val }) => (val ? Number.parseInt(val) : 0), { + toClassOnly: true, + }) + cursor?: number + + @ApiProperty({ minimum: 1, maximum: 100, default: 10 }) + @Min(1) + @Max(100) + @IsInt() + @IsOptional({ always: true }) + @Expose() + @Transform(({ value: val }) => (val ? Number.parseInt(val) : 10), { + toClassOnly: true, + }) + limit?: number +} diff --git a/src/common/dto/delete.dto.ts b/src/common/dto/delete.dto.ts new file mode 100644 index 0000000..5157aae --- /dev/null +++ b/src/common/dto/delete.dto.ts @@ -0,0 +1,8 @@ +import { IsDefined, IsNotEmpty, IsNumber } from 'class-validator' + +export class BatchDeleteDto { + @IsDefined() + @IsNotEmpty() + @IsNumber({}, { each: true }) + ids: number[] +} diff --git a/src/common/dto/id.dto.ts b/src/common/dto/id.dto.ts new file mode 100644 index 0000000..65898b1 --- /dev/null +++ b/src/common/dto/id.dto.ts @@ -0,0 +1,6 @@ +import { IsNumber } from 'class-validator' + +export class IdDto { + @IsNumber() + id: number +} diff --git a/src/common/dto/pager.dto.ts b/src/common/dto/pager.dto.ts new file mode 100644 index 0000000..95abdaf --- /dev/null +++ b/src/common/dto/pager.dto.ts @@ -0,0 +1,45 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Expose, Transform } from 'class-transformer' +import { Allow, IsEnum, IsInt, IsOptional, IsString, Max, Min } from 'class-validator' + +export enum Order { + ASC = 'ASC', + DESC = 'DESC', +} + +export class PagerDto { + @ApiProperty({ minimum: 1, default: 1 }) + @Min(1) + @IsInt() + @Expose() + @IsOptional({ always: true }) + @Transform(({ value: val }) => (val ? Number.parseInt(val) : 1), { + toClassOnly: true, + }) + page?: number + + @ApiProperty({ minimum: 1, maximum: 100, default: 10 }) + @Min(1) + @Max(100) + @IsInt() + @IsOptional({ always: true }) + @Expose() + @Transform(({ value: val }) => (val ? Number.parseInt(val) : 10), { + toClassOnly: true, + }) + pageSize?: number + + @ApiProperty() + @IsString() + @IsOptional() + field?: string // | keyof T + + @ApiProperty({ enum: Order }) + @IsEnum(Order) + @IsOptional() + @Transform(({ value }) => (value === 'asc' ? Order.ASC : Order.DESC)) + order?: Order + + @Allow() + _t?: number +} diff --git a/src/common/entity/common.entity.ts b/src/common/entity/common.entity.ts new file mode 100644 index 0000000..b6a9e32 --- /dev/null +++ b/src/common/entity/common.entity.ts @@ -0,0 +1,55 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger' +import { Exclude } from 'class-transformer' +import { + BaseEntity, + Column, + CreateDateColumn, + PrimaryGeneratedColumn, + UpdateDateColumn, + VirtualColumn, +} from 'typeorm' + +// 如果觉得前端转换时间太麻烦,并且不考虑通用性的话,可以在服务端进行转换,eg: @UpdateDateColumn({ name: 'updated_at', transformer }) +// const transformer: ValueTransformer = { +// to(value) { +// return value +// }, +// from(value) { +// return dayjs(value).format('YYYY-MM-DD HH:mm:ss') +// }, +// } + +export abstract class CommonEntity extends BaseEntity { + @PrimaryGeneratedColumn() + id: number + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date +} + +export abstract class CompleteEntity extends CommonEntity { + @ApiHideProperty() + @Exclude() + @Column({ name: 'create_by', update: false, comment: '创建者' }) + createBy: number + + @ApiHideProperty() + @Exclude() + @Column({ name: 'update_by', comment: '更新者' }) + updateBy: number + + /** + * 不会保存到数据库中的虚拟列,数据量大时可能会有性能问题,有性能要求请考虑在 service 层手动实现 + * @see https://typeorm.io/decorator-reference#virtualcolumn + */ + @ApiProperty({ description: '创建者' }) + @VirtualColumn({ query: alias => `SELECT username FROM sys_user WHERE id = ${alias}.create_by` }) + creator: string + + @ApiProperty({ description: '更新者' }) + @VirtualColumn({ query: alias => `SELECT username FROM sys_user WHERE id = ${alias}.update_by` }) + updater: string +} diff --git a/src/common/exceptions/biz.exception.ts b/src/common/exceptions/biz.exception.ts new file mode 100644 index 0000000..f33ee3a --- /dev/null +++ b/src/common/exceptions/biz.exception.ts @@ -0,0 +1,40 @@ +import { HttpException, HttpStatus } from '@nestjs/common' + +import { ErrorEnum } from '~/constants/error-code.constant' +import { RESPONSE_SUCCESS_CODE } from '~/constants/response.constant' + +export class BusinessException extends HttpException { + private errorCode: number + + constructor(error: ErrorEnum | string) { + // 如果是非 ErrorEnum + if (!error.includes(':')) { + super( + HttpException.createBody({ + code: RESPONSE_SUCCESS_CODE, + message: error, + }), + HttpStatus.OK, + ) + this.errorCode = RESPONSE_SUCCESS_CODE + return + } + + const [code, message] = error.split(':') + super( + HttpException.createBody({ + code, + message, + }), + HttpStatus.OK, + ) + + this.errorCode = Number(code) + } + + getErrorCode(): number { + return this.errorCode + } +} + +export { BusinessException as BizException } diff --git a/src/common/exceptions/not-found.exception.ts b/src/common/exceptions/not-found.exception.ts new file mode 100644 index 0000000..c6cb536 --- /dev/null +++ b/src/common/exceptions/not-found.exception.ts @@ -0,0 +1,10 @@ +import { NotFoundException } from '@nestjs/common' +import { sample } from 'lodash' + +export const NotFoundMessage = ['404, Not Found'] + +export class CannotFindException extends NotFoundException { + constructor() { + super(sample(NotFoundMessage)) + } +} diff --git a/src/common/exceptions/socket.exception.ts b/src/common/exceptions/socket.exception.ts new file mode 100644 index 0000000..e790844 --- /dev/null +++ b/src/common/exceptions/socket.exception.ts @@ -0,0 +1,38 @@ +import { HttpException } from '@nestjs/common' +import { WsException } from '@nestjs/websockets' + +import { ErrorEnum } from '~/constants/error-code.constant' + +export class SocketException extends WsException { + private errorCode: number + + constructor(message: string) + constructor(error: ErrorEnum) + constructor(...args: any) { + const error = args[0] + if (typeof error === 'string') { + super( + HttpException.createBody({ + code: 0, + message: error, + }), + ) + this.errorCode = 0 + return + } + + const [code, message] = error.split(':') + super( + HttpException.createBody({ + code, + message, + }), + ) + + this.errorCode = Number(code) + } + + getErrorCode(): number { + return this.errorCode + } +} diff --git a/src/common/filters/any-exception.filter.ts b/src/common/filters/any-exception.filter.ts new file mode 100644 index 0000000..8bf04cd --- /dev/null +++ b/src/common/filters/any-exception.filter.ts @@ -0,0 +1,89 @@ +import { + ArgumentsHost, + Catch, + ExceptionFilter, + HttpException, + HttpStatus, + Logger, +} from '@nestjs/common' +import { FastifyReply, FastifyRequest } from 'fastify' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' + +import { isDev } from '~/global/env' + +interface myError { + readonly status: number + readonly statusCode?: number + + readonly message?: string +} + +@Catch() +export class AllExceptionsFilter implements ExceptionFilter { + private readonly logger = new Logger(AllExceptionsFilter.name) + + constructor() { + this.registerCatchAllExceptionsHook() + } + + catch(exception: unknown, host: ArgumentsHost) { + const ctx = host.switchToHttp() + const request = ctx.getRequest() + const response = ctx.getResponse() + + const url = request.raw.url! + + const status + = exception instanceof HttpException + ? exception.getStatus() + : (exception as myError)?.status + || (exception as myError)?.statusCode + || HttpStatus.INTERNAL_SERVER_ERROR + + let message + = (exception as any)?.response?.message + || (exception as myError)?.message + || `${exception}` + + // 系统内部错误时 + if ( + status === HttpStatus.INTERNAL_SERVER_ERROR + && !(exception instanceof BusinessException) + ) { + Logger.error(exception, undefined, 'Catch') + + // 生产环境下隐藏错误信息 + if (!isDev) + message = ErrorEnum.SERVER_ERROR?.split(':')[1] + } + else { + this.logger.warn( + `错误信息:(${status}) ${message} Path: ${decodeURI(url)}`, + ) + } + + const apiErrorCode: number + = exception instanceof BusinessException ? exception.getErrorCode() : status + + // 返回基础响应结果 + const resBody: IBaseResponse = { + code: apiErrorCode, + message, + data: null, + } + + response.status(status).send(resBody) + } + + registerCatchAllExceptionsHook() { + process.on('unhandledRejection', (reason) => { + console.error('unhandledRejection: ', reason) + }) + + process.on('uncaughtException', (err) => { + console.error('uncaughtException: ', err) + }) + } +} diff --git a/src/common/interceptors/idempotence.interceptor.ts b/src/common/interceptors/idempotence.interceptor.ts new file mode 100644 index 0000000..a1f11d3 --- /dev/null +++ b/src/common/interceptors/idempotence.interceptor.ts @@ -0,0 +1,148 @@ +import type { + CallHandler, + ExecutionContext, + NestInterceptor, +} from '@nestjs/common' + +import { + ConflictException, + Injectable, + SetMetadata, +} from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import type { FastifyRequest } from 'fastify' +import { catchError, tap } from 'rxjs' + +import { CacheService } from '~/shared/redis/cache.service' +import { hashString } from '~/utils' +import { getIp } from '~/utils/ip.util' +import { getRedisKey } from '~/utils/redis.util' + +import { HTTP_IDEMPOTENCE_KEY, HTTP_IDEMPOTENCE_OPTIONS } from '../decorators/idempotence.decorator' + +const IdempotenceHeaderKey = 'x-idempotence' + +export interface IdempotenceOption { + errorMessage?: string + pendingMessage?: string + + /** + * 如果重复请求的话,手动处理异常 + */ + handler?: (req: FastifyRequest) => any + + /** + * 记录重复请求的时间 + * @default 60 + */ + expired?: number + + /** + * 如果 header 没有幂等 key,根据 request 生成 key,如何生成这个 key 的方法 + */ + generateKey?: (req: FastifyRequest) => string + + /** + * 仅读取 header 的 key,不自动生成 + * @default false + */ + disableGenerateKey?: boolean +} + +@Injectable() +export class IdempotenceInterceptor implements NestInterceptor { + constructor( + private readonly reflector: Reflector, + private readonly cacheService: CacheService, + ) {} + + async intercept(context: ExecutionContext, next: CallHandler) { + const request = context.switchToHttp().getRequest() + + // skip Get 请求 + if (request.method.toUpperCase() === 'GET') + return next.handle() + + const handler = context.getHandler() + const options: IdempotenceOption | undefined = this.reflector.get( + HTTP_IDEMPOTENCE_OPTIONS, + handler, + ) + + if (!options) + return next.handle() + + const { + errorMessage = '相同请求成功后在 60 秒内只能发送一次', + pendingMessage = '相同请求正在处理中...', + handler: errorHandler, + expired = 60, + disableGenerateKey = false, + } = options + const redis = this.cacheService.getClient() + + const idempotence = request.headers[IdempotenceHeaderKey] as string + const key = disableGenerateKey + ? undefined + : options.generateKey + ? options.generateKey(request) + : this.generateKey(request) + + const idempotenceKey + = !!(idempotence || key) && getRedisKey(`idempotence:${idempotence || key}`) + + SetMetadata(HTTP_IDEMPOTENCE_KEY, idempotenceKey)(handler) + + if (idempotenceKey) { + const resultValue: '0' | '1' | null = (await redis.get( + idempotenceKey, + )) as any + if (resultValue !== null) { + if (errorHandler) + return await errorHandler(request) + + const message = { + 1: errorMessage, + 0: pendingMessage, + }[resultValue] + throw new ConflictException(message) + } + else { + await redis.set(idempotenceKey, '0', 'EX', expired) + } + } + return next.handle().pipe( + tap(async () => { + idempotenceKey && (await redis.set(idempotenceKey, '1', 'KEEPTTL')) + }), + catchError(async (err) => { + if (idempotenceKey) + await redis.del(idempotenceKey) + + throw err + }), + ) + } + + private generateKey(req: FastifyRequest) { + const { body, params, query = {}, headers, url } = req + + const obj = { body, url, params, query } as any + + const uuid = headers['x-uuid'] + if (uuid) { + obj.uuid = uuid + } + else { + const ua = headers['user-agent'] + const ip = getIp(req) + + if (!ua && !ip) + return undefined + + Object.assign(obj, { ua, ip }) + } + + return hashString(JSON.stringify(obj)) + } +} diff --git a/src/common/interceptors/logging.interceptor.ts b/src/common/interceptors/logging.interceptor.ts new file mode 100644 index 0000000..de54623 --- /dev/null +++ b/src/common/interceptors/logging.interceptor.ts @@ -0,0 +1,35 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + Logger, + NestInterceptor, +} from '@nestjs/common' +import { Observable, tap } from 'rxjs' + +@Injectable() +export class LoggingInterceptor implements NestInterceptor { + private logger = new Logger(LoggingInterceptor.name, { timestamp: false }) + + intercept( + context: ExecutionContext, + next: CallHandler, + ): Observable { + const call$ = next.handle() + const request = context.switchToHttp().getRequest() + const content = `${request.method} -> ${request.url}` + const isSse = request.headers.accept === 'text/event-stream' + this.logger.debug(`+++ 请求:${content}`) + const now = Date.now() + + return call$.pipe( + tap(() => { + if (isSse) + return + + this.logger.debug(`--- 响应:${content}${` +${Date.now() - now}ms`}`) + }, + ), + ) + } +} diff --git a/src/common/interceptors/timeout.interceptor.ts b/src/common/interceptors/timeout.interceptor.ts new file mode 100644 index 0000000..8508c58 --- /dev/null +++ b/src/common/interceptors/timeout.interceptor.ts @@ -0,0 +1,26 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, + RequestTimeoutException, +} from '@nestjs/common' +import { Observable, TimeoutError, throwError } from 'rxjs' +import { catchError, timeout } from 'rxjs/operators' + +@Injectable() +export class TimeoutInterceptor implements NestInterceptor { + constructor(private readonly time: number = 10000) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + timeout(this.time), + catchError((err) => { + if (err instanceof TimeoutError) + return throwError(new RequestTimeoutException('请求超时')) + + return throwError(err) + }), + ) + } +} diff --git a/src/common/interceptors/transform.interceptor.ts b/src/common/interceptors/transform.interceptor.ts new file mode 100644 index 0000000..4b59fbe --- /dev/null +++ b/src/common/interceptors/transform.interceptor.ts @@ -0,0 +1,46 @@ +import { + CallHandler, + ExecutionContext, + HttpStatus, + Injectable, + NestInterceptor, +} from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { Observable } from 'rxjs' +import { map } from 'rxjs/operators' + +import { ResOp } from '~/common/model/response.model' + +import { BYPASS_KEY } from '../decorators/bypass.decorator' + +/** + * 统一处理返回接口结果,如果不需要则添加 @Bypass 装饰器 + */ +@Injectable() +export class TransformInterceptor implements NestInterceptor { + constructor(private readonly reflector: Reflector) {} + + intercept( + context: ExecutionContext, + next: CallHandler, + ): Observable { + const bypass = this.reflector.get( + BYPASS_KEY, + context.getHandler(), + ) + + if (bypass) + return next.handle() + + return next.handle().pipe( + map((data) => { + // if (typeof data === 'undefined') { + // context.switchToHttp().getResponse().status(HttpStatus.NO_CONTENT); + // return data; + // } + + return new ResOp(HttpStatus.OK, data ?? null) + }), + ) + } +} diff --git a/src/common/model/response.model.ts b/src/common/model/response.model.ts new file mode 100644 index 0000000..0336456 --- /dev/null +++ b/src/common/model/response.model.ts @@ -0,0 +1,42 @@ +import { ApiProperty } from '@nestjs/swagger' + +import { + RESPONSE_SUCCESS_CODE, + RESPONSE_SUCCESS_MSG, +} from '~/constants/response.constant' + +export class ResOp { + @ApiProperty({ type: 'object' }) + data?: T + + @ApiProperty({ type: 'number', default: RESPONSE_SUCCESS_CODE }) + code: number + + @ApiProperty({ type: 'string', default: RESPONSE_SUCCESS_MSG }) + message: string + + constructor(code: number, data: T, message = RESPONSE_SUCCESS_MSG) { + this.code = code + this.data = data + this.message = message + } + + static success(data?: T, message?: string) { + return new ResOp(RESPONSE_SUCCESS_CODE, data, message) + } + + static error(code: number, message) { + return new ResOp(code, {}, message) + } +} + +export class TreeResult { + @ApiProperty() + id: number + + @ApiProperty() + parentId: number + + @ApiProperty() + children?: TreeResult[] +} diff --git a/src/common/pipes/parse-int.pipe.ts b/src/common/pipes/parse-int.pipe.ts new file mode 100644 index 0000000..514d67c --- /dev/null +++ b/src/common/pipes/parse-int.pipe.ts @@ -0,0 +1,18 @@ +import { + ArgumentMetadata, + BadRequestException, + Injectable, + PipeTransform, +} from '@nestjs/common' + +@Injectable() +export class ParseIntPipe implements PipeTransform { + transform(value: string, metadata: ArgumentMetadata): number { + const val = Number.parseInt(value, 10) + + if (Number.isNaN(val)) + throw new BadRequestException('id validation failed') + + return val + } +} diff --git a/src/config/app.config.ts b/src/config/app.config.ts new file mode 100644 index 0000000..25f2a14 --- /dev/null +++ b/src/config/app.config.ts @@ -0,0 +1,20 @@ +import { ConfigType, registerAs } from '@nestjs/config' + +import { env, envNumber } from '~/global/env' + +export const appRegToken = 'app' + +export const AppConfig = registerAs(appRegToken, () => ({ + name: env('APP_NAME'), + port: envNumber('APP_PORT', 3000), + baseUrl: env('APP_BASE_URL'), + globalPrefix: env('GLOBAL_PREFIX', 'api'), + locale: env('APP_LOCALE', 'zh-CN'), + + logger: { + level: env('LOGGER_LEVEL'), + maxFiles: envNumber('LOGGER_MAX_FILES'), + }, +})) + +export type IAppConfig = ConfigType diff --git a/src/config/database.config.ts b/src/config/database.config.ts new file mode 100644 index 0000000..2d1b34f --- /dev/null +++ b/src/config/database.config.ts @@ -0,0 +1,40 @@ +import { ConfigType, registerAs } from '@nestjs/config' + +import { DataSource, DataSourceOptions } from 'typeorm' + +import { env, envBoolean, envNumber } from '~/global/env' + +// eslint-disable-next-line import/order +import dotenv from 'dotenv' + +dotenv.config({ path: `.env.${process.env.NODE_ENV}` }) + +// 当前通过 npm scripts 执行的命令 +const currentScript = process.env.npm_lifecycle_event + +const dataSourceOptions: DataSourceOptions = { + type: 'mysql', + host: env('DB_HOST', '127.0.0.1'), + port: envNumber('DB_PORT', 3306), + username: env('DB_USERNAME'), + password: env('DB_PASSWORD'), + database: env('DB_DATABASE'), + synchronize: envBoolean('DB_SYNCHRONIZE', false), + // 解决通过 pnpm migration:run 初始化数据时,遇到的 SET FOREIGN_KEY_CHECKS = 0; 等语句报错问题, 仅在执行数据迁移操作时设为 true + multipleStatements: currentScript === 'typeorm', + entities: ['dist/modules/**/*.entity{.ts,.js}'], + migrations: ['dist/migrations/*{.ts,.js}'], + subscribers: ['dist/modules/**/*.subscriber{.ts,.js}'], +} +export const dbRegToken = 'database' + +export const DatabaseConfig = registerAs( + dbRegToken, + (): DataSourceOptions => dataSourceOptions, +) + +export type IDatabaseConfig = ConfigType + +const dataSource = new DataSource(dataSourceOptions) + +export default dataSource diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..7cca557 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,37 @@ +import { AppConfig, IAppConfig, appRegToken } from './app.config' +import { DatabaseConfig, IDatabaseConfig, dbRegToken } from './database.config' +import { IMailerConfig, MailerConfig, mailerRegToken } from './mailer.config' +import { IOssConfig, OssConfig, ossRegToken } from './oss.config' +import { IRedisConfig, RedisConfig, redisRegToken } from './redis.config' +import { ISecurityConfig, SecurityConfig, securityRegToken } from './security.config' +import { ISwaggerConfig, SwaggerConfig, swaggerRegToken } from './swagger.config' + +export * from './app.config' +export * from './redis.config' +export * from './database.config' +export * from './swagger.config' +export * from './security.config' +export * from './mailer.config' +export * from './oss.config' + +export interface AllConfigType { + [appRegToken]: IAppConfig + [dbRegToken]: IDatabaseConfig + [mailerRegToken]: IMailerConfig + [redisRegToken]: IRedisConfig + [securityRegToken]: ISecurityConfig + [swaggerRegToken]: ISwaggerConfig + [ossRegToken]: IOssConfig +} + +export type ConfigKeyPaths = RecordNamePaths + +export default { + AppConfig, + DatabaseConfig, + MailerConfig, + OssConfig, + RedisConfig, + SecurityConfig, + SwaggerConfig, +} diff --git a/src/config/mailer.config.ts b/src/config/mailer.config.ts new file mode 100644 index 0000000..27763d1 --- /dev/null +++ b/src/config/mailer.config.ts @@ -0,0 +1,18 @@ +import { ConfigType, registerAs } from '@nestjs/config' + +import { env, envNumber } from '~/global/env' + +export const mailerRegToken = 'mailer' + +export const MailerConfig = registerAs(mailerRegToken, () => ({ + host: env('SMTP_HOST'), + port: envNumber('SMTP_PORT'), + ignoreTLS: true, + secure: true, + auth: { + user: env('SMTP_USER'), + pass: env('SMTP_PASS'), + }, +})) + +export type IMailerConfig = ConfigType diff --git a/src/config/oss.config.ts b/src/config/oss.config.ts new file mode 100644 index 0000000..ae1f563 --- /dev/null +++ b/src/config/oss.config.ts @@ -0,0 +1,32 @@ +import { ConfigType, registerAs } from '@nestjs/config' +import * as qiniu from 'qiniu' + +import { env } from '~/global/env' + +function parseZone(zone: string) { + switch (zone) { + case 'Zone_as0': + return qiniu.zone.Zone_as0 + case 'Zone_na0': + return qiniu.zone.Zone_na0 + case 'Zone_z0': + return qiniu.zone.Zone_z0 + case 'Zone_z1': + return qiniu.zone.Zone_z1 + case 'Zone_z2': + return qiniu.zone.Zone_z2 + } +} + +export const ossRegToken = 'oss' + +export const OssConfig = registerAs(ossRegToken, () => ({ + accessKey: env('OSS_ACCESSKEY'), + secretKey: env('OSS_SECRETKEY'), + domain: env('OSS_DOMAIN'), + bucket: env('OSS_BUCKET'), + zone: parseZone(env('OSS_ZONE') || 'Zone_z2'), + access: (env('OSS_ACCESS_TYPE') as any) || 'public', +})) + +export type IOssConfig = ConfigType diff --git a/src/config/redis.config.ts b/src/config/redis.config.ts new file mode 100644 index 0000000..749c772 --- /dev/null +++ b/src/config/redis.config.ts @@ -0,0 +1,14 @@ +import { ConfigType, registerAs } from '@nestjs/config' + +import { env, envNumber } from '~/global/env' + +export const redisRegToken = 'redis' + +export const RedisConfig = registerAs(redisRegToken, () => ({ + host: env('REDIS_HOST', '127.0.0.1'), + port: envNumber('REDIS_PORT', 6379), + password: env('REDIS_PASSWORD'), + db: envNumber('REDIS_DB'), +})) + +export type IRedisConfig = ConfigType diff --git a/src/config/security.config.ts b/src/config/security.config.ts new file mode 100644 index 0000000..024cccb --- /dev/null +++ b/src/config/security.config.ts @@ -0,0 +1,14 @@ +import { ConfigType, registerAs } from '@nestjs/config' + +import { env, envNumber } from '~/global/env' + +export const securityRegToken = 'security' + +export const SecurityConfig = registerAs(securityRegToken, () => ({ + jwtSecret: env('JWT_SECRET'), + jwtExprire: envNumber('JWT_EXPIRE'), + refreshSecret: env('REFRESH_TOKEN_SECRET'), + refreshExpire: envNumber('REFRESH_TOKEN_EXPIRE'), +})) + +export type ISecurityConfig = ConfigType diff --git a/src/config/swagger.config.ts b/src/config/swagger.config.ts new file mode 100644 index 0000000..0424fcf --- /dev/null +++ b/src/config/swagger.config.ts @@ -0,0 +1,12 @@ +import { ConfigType, registerAs } from '@nestjs/config' + +import { env, envBoolean } from '~/global/env' + +export const swaggerRegToken = 'swagger' + +export const SwaggerConfig = registerAs(swaggerRegToken, () => ({ + enable: envBoolean('SWAGGER_ENABLE'), + path: env('SWAGGER_PATH'), +})) + +export type ISwaggerConfig = ConfigType diff --git a/src/constants/cache.constant.ts b/src/constants/cache.constant.ts new file mode 100644 index 0000000..c4852b8 --- /dev/null +++ b/src/constants/cache.constant.ts @@ -0,0 +1,8 @@ +export enum RedisKeys { + AccessIp = 'access_ip', + CAPTCHA_IMG_PREFIX = 'captcha:img:', + AUTH_TOKEN_PREFIX = 'auth:token:', + AUTH_PERM_PREFIX = 'auth:permission:', + AUTH_PASSWORD_V_PREFIX = 'auth:passwordVersion:', +} +export const API_CACHE_PREFIX = 'api-cache:' diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts new file mode 100644 index 0000000..4046758 --- /dev/null +++ b/src/constants/error-code.constant.ts @@ -0,0 +1,49 @@ +export enum ErrorEnum { + DEFAULT = '0:未知错误', + SERVER_ERROR = '500:服务繁忙,请稍后再试', + + SYSTEM_USER_EXISTS = '1001:系统用户已存在', + INVALID_VERIFICATION_CODE = '1002:验证码填写有误', + INVALID_USERNAME_PASSWORD = '1003:用户名密码有误', + NODE_ROUTE_EXISTS = '1004:节点路由已存在', + PERMISSION_REQUIRES_PARENT = '1005:权限必须包含父节点', + ILLEGAL_OPERATION_DIRECTORY_PARENT = '1006:非法操作:该节点仅支持目录类型父节点', + ILLEGAL_OPERATION_CANNOT_CONVERT_NODE_TYPE = '1007:非法操作:节点类型无法直接转换', + ROLE_HAS_ASSOCIATED_USERS = '1008:该角色存在关联用户,请先删除关联用户', + DEPARTMENT_HAS_ASSOCIATED_USERS = '1009:该部门存在关联用户,请先删除关联用户', + DEPARTMENT_HAS_ASSOCIATED_ROLES = '1010:该部门存在关联角色,请先删除关联角色', + PASSWORD_MISMATCH = '1011:旧密码与原密码不一致', + LOGOUT_OWN_SESSION = '1012:如想下线自身可右上角退出', + NOT_ALLOWED_TO_LOGOUT_USER = '1013:不允许下线该用户', + PARENT_MENU_NOT_FOUND = '1014:父级菜单不存在', + DEPARTMENT_HAS_CHILD_DEPARTMENTS = '1015:该部门存在子部门,请先删除子部门', + SYSTEM_BUILTIN_FUNCTION_NOT_ALLOWED = '1016:系统内置功能不允许操作', + USER_NOT_FOUND = '1017:用户不存在', + UNABLE_TO_FIND_DEPARTMENT_FOR_USER = '1018:无法查找当前用户所属部门', + DEPARTMENT_NOT_FOUND = '1019:部门不存在', + DICT_NAME_EXISTS = '1020: 已存在相同名称的字典', + PARAMETER_CONFIG_KEY_EXISTS = '1021:参数配置键值对已存在', + DEFAULT_ROLE_NOT_FOUND = '1022:所分配的默认角色不存在', + + INVALID_LOGIN = '1101:登录无效,请重新登录', + NO_PERMISSION = '1102:无权限访问', + ONLY_ADMIN_CAN_LOGIN = '1103:不是管理员,无法登录', + REQUEST_INVALIDATED = '1104:当前请求已失效', + ACCOUNT_LOGGED_IN_ELSEWHERE = '1105:您的账号已在其他地方登录', + GUEST_ACCOUNT_RESTRICTED_OPERATION = '1106:游客账号不允许操作', + REQUESTED_RESOURCE_NOT_FOUND = '1107:所请求的资源不存在', + + TOO_MANY_REQUESTS = '1201:请求频率过快,请一分钟后再试', + MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY = '1202:一天最多发送5条验证码', + VERIFICATION_CODE_SEND_FAILED = '1203:验证码发送失败', + + INSECURE_MISSION = '1301:不安全的任务,确保执行的加入@Mission注解', + EXECUTED_MISSION_NOT_FOUND = '1302:所执行的任务不存在', + MISSION_EXECUTION_FAILED = '1303:任务执行失败', + MISSION_NOT_FOUND = '1304:任务不存在', + + // OSS相关 + OSS_FILE_OR_DIR_EXIST = '1401:当前创建的文件或目录已存在', + OSS_NO_OPERATION_REQUIRED = '1402:无需操作', + OSS_EXCEE_MAXIMUM_QUANTITY = '1403:已超出支持的最大处理数量', +} diff --git a/src/constants/event-bus.constant.ts b/src/constants/event-bus.constant.ts new file mode 100644 index 0000000..7d35e8a --- /dev/null +++ b/src/constants/event-bus.constant.ts @@ -0,0 +1,4 @@ +export enum EventBusEvents { + TokenExpired = 'token.expired', + SystemException = 'system.exception', +} diff --git a/src/constants/oss.constant.ts b/src/constants/oss.constant.ts new file mode 100644 index 0000000..a56336e --- /dev/null +++ b/src/constants/oss.constant.ts @@ -0,0 +1,8 @@ +export const OSS_CONFIG = 'admin_module:qiniu_config' +export const OSS_API = 'http://api.qiniu.com' + +// 目录分隔符 +export const NETDISK_DELIMITER = '/' +export const NETDISK_LIMIT = 100 +export const NETDISK_HANDLE_MAX_ITEM = 1000 +export const NETDISK_COPY_SUFFIX = '的副本' diff --git a/src/constants/response.constant.ts b/src/constants/response.constant.ts new file mode 100644 index 0000000..229f70f --- /dev/null +++ b/src/constants/response.constant.ts @@ -0,0 +1,15 @@ +export const RESPONSE_SUCCESS_CODE = 200 + +export const RESPONSE_SUCCESS_MSG = 'success' + +/** + * @description: contentType + */ +export enum ContentTypeEnum { + // json + JSON = 'application/json;charset=UTF-8', + // form-data qs + FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', + // form-data upload + FORM_DATA = 'multipart/form-data;charset=UTF-8', +} diff --git a/src/constants/system.constant.ts b/src/constants/system.constant.ts new file mode 100644 index 0000000..eef03a5 --- /dev/null +++ b/src/constants/system.constant.ts @@ -0,0 +1,6 @@ +export const SYS_USER_INITPASSWORD = 'sys_user_initPassword' +export const SYS_API_TOKEN = 'sys_api_token' +/** 超级管理员用户 id */ +export const ROOT_USER_ID = 1 +/** 超级管理员角色 id */ +export const ROOT_ROLE_ID = 1 diff --git a/src/global/env.ts b/src/global/env.ts new file mode 100644 index 0000000..8f7b82b --- /dev/null +++ b/src/global/env.ts @@ -0,0 +1,62 @@ +import cluster from 'node:cluster' + +export const isMainCluster + = process.env.NODE_APP_INSTANCE && Number.parseInt(process.env.NODE_APP_INSTANCE) === 0 +export const isMainProcess = cluster.isPrimary || isMainCluster + +export const isDev = process.env.NODE_ENV === 'development' + +export const isTest = !!process.env.TEST +export const cwd = process.cwd() + +/** + * 基础类型接口 + */ +export type BaseType = boolean | number | string | undefined | null + +/** + * 格式化环境变量 + * @param key 环境变量的键值 + * @param defaultValue 默认值 + * @param callback 格式化函数 + */ +function fromatValue(key: string, defaultValue: T, callback?: (value: string) => T): T { + const value: string | undefined = process.env[key] + if (typeof value === 'undefined') + return defaultValue + + if (!callback) + return value as unknown as T + + return callback(value) +} + +export function env(key: string, defaultValue: string = '') { + return fromatValue(key, defaultValue) +} + +export function envString(key: string, defaultValue: string = '') { + return fromatValue(key, defaultValue) +} + +export function envNumber(key: string, defaultValue: number = 0) { + return fromatValue(key, defaultValue, (value) => { + try { + return Number(value) + } + catch { + throw new Error(`${key} environment variable is not a number`) + } + }) +} + +export function envBoolean(key: string, defaultValue: boolean = false) { + return fromatValue(key, defaultValue, (value) => { + try { + return Boolean(JSON.parse(value)) + } + catch { + throw new Error(`${key} environment variable is not a boolean`) + } + }) +} diff --git a/src/helper/catchError.ts b/src/helper/catchError.ts new file mode 100644 index 0000000..d21789c --- /dev/null +++ b/src/helper/catchError.ts @@ -0,0 +1,5 @@ +export function catchError() { + process.on('unhandledRejection', (reason, p) => { + console.log('Promise: ', p, 'Reason: ', reason) + }) +} diff --git a/src/helper/crud/base.service.ts b/src/helper/crud/base.service.ts new file mode 100644 index 0000000..daa67a4 --- /dev/null +++ b/src/helper/crud/base.service.ts @@ -0,0 +1,40 @@ +import { NotFoundException } from '@nestjs/common' +import { ObjectLiteral, Repository } from 'typeorm' + +import { PagerDto } from '~/common/dto/pager.dto' + +import { paginate } from '../paginate' +import { Pagination } from '../paginate/pagination' + +export class BaseService = Repository> { + constructor(private repository: R) { + } + + async list({ + page, + pageSize, + }: PagerDto): Promise> { + return paginate(this.repository, { page, pageSize }) + } + + async findOne(id: number): Promise { + const item = await this.repository.createQueryBuilder().where({ id }).getOne() + if (!item) + throw new NotFoundException('未找到该记录') + + return item + } + + async create(dto: any): Promise { + return await this.repository.save(dto) + } + + async update(id: number, dto: any): Promise { + await this.repository.update(id, dto) + } + + async delete(id: number): Promise { + const item = await this.findOne(id) + await this.repository.remove(item) + } +} diff --git a/src/helper/crud/crud.factory.ts b/src/helper/crud/crud.factory.ts new file mode 100644 index 0000000..7ae1fc8 --- /dev/null +++ b/src/helper/crud/crud.factory.ts @@ -0,0 +1,81 @@ +import type { Type } from '@nestjs/common' + +import { + Body, + Controller, + Delete, + Get, + Patch, + Post, + Put, + Query, +} from '@nestjs/common' +import { ApiBody, IntersectionType, PartialType } from '@nestjs/swagger' +import pluralize from 'pluralize' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { PagerDto } from '~/common/dto/pager.dto' + +import { BaseService } from './base.service' + +export function BaseCrudFactory< + E extends new (...args: any[]) => any, +>({ entity, dto, permissions }: { entity: E, dto?: Type, permissions?: Record }): Type { + const prefix = entity.name.toLowerCase().replace(/entity$/, '') + const pluralizeName = pluralize(prefix) as string + + dto = dto ?? class extends entity {} + + class Dto extends dto {} + class UpdateDto extends PartialType(Dto) {} + class QueryDto extends IntersectionType(PagerDto, PartialType(Dto)) {} + + permissions = permissions ?? { + LIST: `${prefix}:list`, + CREATE: `${prefix}:create`, + READ: `${prefix}:read`, + UPDATE: `${prefix}:update`, + DELETE: `${prefix}:delete`, + } as const + + @Controller(pluralizeName) + class BaseController> { + constructor(private service: S) { } + + @Get() + @ApiResult({ type: [entity], isPage: true }) + async list(@Query() pager: QueryDto) { + return await this.service.list(pager) + } + + @Get(':id') + @ApiResult({ type: entity }) + async get(@IdParam() id: number) { + return await this.service.findOne(id) + } + + @Post() + @ApiBody({ type: dto }) + async create(@Body() dto: Dto) { + return await this.service.create(dto) + } + + @Put(':id') + async update(@IdParam() id: number, @Body() dto: UpdateDto) { + return await this.service.update(id, dto) + } + + @Patch(':id') + async patch(@IdParam() id: number, @Body() dto: UpdateDto) { + await this.service.update(id, dto) + } + + @Delete(':id') + async delete(@IdParam() id: number) { + await this.service.delete(id) + } + } + + return BaseController +} diff --git a/src/helper/genRedisKey.ts b/src/helper/genRedisKey.ts new file mode 100644 index 0000000..1dab9a3 --- /dev/null +++ b/src/helper/genRedisKey.ts @@ -0,0 +1,19 @@ +import { RedisKeys } from '~/constants/cache.constant' + +/** 生成验证码 redis key */ +export function genCaptchaImgKey(val: string | number) { + return `${RedisKeys.CAPTCHA_IMG_PREFIX}${String(val)}` as const +} + +/** 生成 auth token redis key */ +export function genAuthTokenKey(val: string | number) { + return `${RedisKeys.AUTH_TOKEN_PREFIX}${String(val)}` as const +} +/** 生成 auth permission redis key */ +export function genAuthPermKey(val: string | number) { + return `${RedisKeys.AUTH_PERM_PREFIX}${String(val)}` as const +} +/** 生成 auth passwordVersion redis key */ +export function genAuthPVKey(val: string | number) { + return `${RedisKeys.AUTH_PASSWORD_V_PREFIX}${String(val)}` as const +} diff --git a/src/helper/paginate/create-pagination.ts b/src/helper/paginate/create-pagination.ts new file mode 100644 index 0000000..5c1182f --- /dev/null +++ b/src/helper/paginate/create-pagination.ts @@ -0,0 +1,27 @@ +import { IPaginationMeta } from './interface' +import { Pagination } from './pagination' + +export function createPaginationObject({ + items, + totalItems, + currentPage, + limit, +}: { + items: T[] + totalItems?: number + currentPage: number + limit: number +}): Pagination { + const totalPages + = totalItems !== undefined ? Math.ceil(totalItems / limit) : undefined + + const meta: IPaginationMeta = { + totalItems, + itemCount: items.length, + itemsPerPage: limit, + totalPages, + currentPage, + } + + return new Pagination(items, meta) +} diff --git a/src/helper/paginate/index.ts b/src/helper/paginate/index.ts new file mode 100644 index 0000000..fa8b487 --- /dev/null +++ b/src/helper/paginate/index.ts @@ -0,0 +1,147 @@ +import { + FindManyOptions, + FindOptionsWhere, + ObjectLiteral, + Repository, + SelectQueryBuilder, +} from 'typeorm' + +import { createPaginationObject } from './create-pagination' +import { IPaginationOptions, PaginationTypeEnum } from './interface' +import { Pagination } from './pagination' + +const DEFAULT_LIMIT = 10 +const DEFAULT_PAGE = 1 + +function resolveOptions( + options: IPaginationOptions, +): [number, number, PaginationTypeEnum] { + const { page, pageSize, paginationType } = options + + return [ + page || DEFAULT_PAGE, + pageSize || DEFAULT_LIMIT, + paginationType || PaginationTypeEnum.TAKE_AND_SKIP, + ] +} + +async function paginateRepository( + repository: Repository, + options: IPaginationOptions, + searchOptions?: FindOptionsWhere | FindManyOptions, +): Promise> { + const [page, limit] = resolveOptions(options) + + const promises: [Promise, Promise | undefined] = [ + repository.find({ + skip: limit * (page - 1), + take: limit, + ...searchOptions, + }), + undefined, + ] + + const [items, total] = await Promise.all(promises) + + return createPaginationObject({ + items, + totalItems: total, + currentPage: page, + limit, + }) +} + +async function paginateQueryBuilder( + queryBuilder: SelectQueryBuilder, + options: IPaginationOptions, +): Promise> { + const [page, limit, paginationType] = resolveOptions(options) + + if (paginationType === PaginationTypeEnum.TAKE_AND_SKIP) + queryBuilder.take(limit).skip((page - 1) * limit) + else + queryBuilder.limit(limit).offset((page - 1) * limit) + + const [items, total] = await queryBuilder.getManyAndCount() + + return createPaginationObject({ + items, + totalItems: total, + currentPage: page, + limit, + }) +} + +export async function paginateRaw( + queryBuilder: SelectQueryBuilder, + options: IPaginationOptions, +): Promise> { + const [page, limit, paginationType] = resolveOptions(options) + + const promises: [Promise, Promise | undefined] = [ + (paginationType === PaginationTypeEnum.LIMIT_AND_OFFSET + ? queryBuilder.limit(limit).offset((page - 1) * limit) + : queryBuilder.take(limit).skip((page - 1) * limit) + ).getRawMany(), + queryBuilder.getCount(), + ] + + const [items, total] = await Promise.all(promises) + + return createPaginationObject({ + items, + totalItems: total, + currentPage: page, + limit, + }) +} + +export async function paginateRawAndEntities( + queryBuilder: SelectQueryBuilder, + options: IPaginationOptions, +): Promise<[Pagination, Partial[]]> { + const [page, limit, paginationType] = resolveOptions(options) + + const promises: [ + Promise<{ entities: T[], raw: T[] }>, + Promise | undefined, + ] = [ + (paginationType === PaginationTypeEnum.LIMIT_AND_OFFSET + ? queryBuilder.limit(limit).offset((page - 1) * limit) + : queryBuilder.take(limit).skip((page - 1) * limit) + ).getRawAndEntities(), + queryBuilder.getCount(), + ] + + const [itemObject, total] = await Promise.all(promises) + + return [ + createPaginationObject({ + items: itemObject.entities, + totalItems: total, + currentPage: page, + limit, + }), + itemObject.raw, + ] +} + +export async function paginate( + repository: Repository, + options: IPaginationOptions, + searchOptions?: FindOptionsWhere | FindManyOptions, +): Promise> +export async function paginate( + queryBuilder: SelectQueryBuilder, + options: IPaginationOptions, +): Promise> + +export async function paginate( + repositoryOrQueryBuilder: Repository | SelectQueryBuilder, + options: IPaginationOptions, + searchOptions?: FindOptionsWhere | FindManyOptions, +) { + return repositoryOrQueryBuilder instanceof Repository + ? paginateRepository(repositoryOrQueryBuilder, options, searchOptions) + : paginateQueryBuilder(repositoryOrQueryBuilder, options) +} diff --git a/src/helper/paginate/interface.ts b/src/helper/paginate/interface.ts new file mode 100644 index 0000000..e7c6ee2 --- /dev/null +++ b/src/helper/paginate/interface.ts @@ -0,0 +1,27 @@ +import { ObjectLiteral } from 'typeorm' + +export enum PaginationTypeEnum { + LIMIT_AND_OFFSET = 'limit', + TAKE_AND_SKIP = 'take', +} + +export interface IPaginationOptions { + page: number + pageSize: number + paginationType?: PaginationTypeEnum +} + +export interface IPaginationMeta extends ObjectLiteral { + itemCount: number + totalItems?: number + itemsPerPage: number + totalPages?: number + currentPage: number +} + +export interface IPaginationLinks { + first?: string + previous?: string + next?: string + last?: string +} diff --git a/src/helper/paginate/pagination.ts b/src/helper/paginate/pagination.ts new file mode 100644 index 0000000..5bcf90f --- /dev/null +++ b/src/helper/paginate/pagination.ts @@ -0,0 +1,14 @@ +import { ObjectLiteral } from 'typeorm' + +import { IPaginationMeta } from './interface' + +export class Pagination< + PaginationObject, + T extends ObjectLiteral = IPaginationMeta, +> { + constructor( + public readonly items: PaginationObject[], + + public readonly meta: T, + ) {} +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..4fd1732 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,101 @@ +import cluster from 'node:cluster' +import path from 'node:path' + +import { + HttpStatus, + Logger, + UnprocessableEntityException, + ValidationPipe, +} from '@nestjs/common' +import { ConfigService } from '@nestjs/config' +import { NestFactory } from '@nestjs/core' +import { NestFastifyApplication } from '@nestjs/platform-fastify' + +import { useContainer } from 'class-validator' + +import { AppModule } from './app.module' + +import { fastifyApp } from './common/adapters/fastify.adapter' +import { RedisIoAdapter } from './common/adapters/socket.adapter' +import { LoggingInterceptor } from './common/interceptors/logging.interceptor' +import type { ConfigKeyPaths } from './config' +import { isDev, isMainProcess } from './global/env' +import { setupSwagger } from './setup-swagger' +import { LoggerService } from './shared/logger/logger.service' + +declare const module: any + +async function bootstrap() { + const app = await NestFactory.create( + AppModule, + fastifyApp, + { + bufferLogs: true, + snapshot: true, + // forceCloseConnections: true, + }, + ) + + const configService = app.get(ConfigService) + + const { port, globalPrefix } = configService.get('app', { infer: true }) + + // class-validator 的 DTO 类中注入 nest 容器的依赖 (用于自定义验证器) + useContainer(app.select(AppModule), { fallbackOnErrors: true }) + + app.enableCors({ origin: '*', credentials: true }) + app.setGlobalPrefix(globalPrefix) + app.useStaticAssets({ root: path.join(__dirname, '..', 'public') }) + // Starts listening for shutdown hooks + !isDev && app.enableShutdownHooks() + + if (isDev) + app.useGlobalInterceptors(new LoggingInterceptor()) + + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + transformOptions: { enableImplicitConversion: true }, + // forbidNonWhitelisted: true, // 禁止 无装饰器验证的数据通过 + errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, + stopAtFirstError: true, + exceptionFactory: errors => + new UnprocessableEntityException( + errors.map((e) => { + const rule = Object.keys(e.constraints!)[0] + const msg = e.constraints![rule] + return msg + })[0], + ), + }), + ) + + app.useWebSocketAdapter(new RedisIoAdapter(app)) + + setupSwagger(app, configService) + + await app.listen(port, '0.0.0.0', async () => { + app.useLogger(app.get(LoggerService)) + const url = await app.getUrl() + const { pid } = process + const env = cluster.isPrimary + const prefix = env ? 'P' : 'W' + + if (!isMainProcess) + return + + const logger = new Logger('NestApplication') + logger.log(`[${prefix + pid}] Server running on ${url}`) + + if (isDev) + logger.log(`[${prefix + pid}] OpenAPI: ${url}/api-docs`) + }) + + if (module.hot) { + module.hot.accept() + module.hot.dispose(() => app.close()) + } +} + +bootstrap() diff --git a/src/migrations/1707996695540-initData.ts b/src/migrations/1707996695540-initData.ts new file mode 100644 index 0000000..c4c3e6e --- /dev/null +++ b/src/migrations/1707996695540-initData.ts @@ -0,0 +1,15 @@ +import fs from 'node:fs' +import path from 'node:path' + +import { MigrationInterface, QueryRunner } from 'typeorm' + +const sql = fs.readFileSync(path.join(__dirname, '../../deploy/sql/nest_admin.sql'), 'utf8') + +export class InitData1707996695540 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(sql) + } + + public async down(queryRunner: QueryRunner): Promise { + } +} diff --git a/src/modules/auth/auth.constant.ts b/src/modules/auth/auth.constant.ts new file mode 100644 index 0000000..08d48e6 --- /dev/null +++ b/src/modules/auth/auth.constant.ts @@ -0,0 +1,26 @@ +export const PUBLIC_KEY = '__public_key__' + +export const PERMISSION_KEY = '__permission_key__' + +export const RESOURCE_KEY = '__resource_key__' + +export const ALLOW_ANON_KEY = '__allow_anon_permission_key__' + +export const AuthStrategy = { + LOCAL: 'local', + LOCAL_EMAIL: 'local_email', + LOCAL_PHONE: 'local_phone', + + JWT: 'jwt', + + GITHUB: 'github', + GOOGLE: 'google', +} as const + +export const Roles = { + ADMIN: 'admin', + USER: 'user', + // GUEST: 'guest', +} as const + +export type Role = (typeof Roles)[keyof typeof Roles] diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts new file mode 100644 index 0000000..cb1bed7 --- /dev/null +++ b/src/modules/auth/auth.controller.ts @@ -0,0 +1,47 @@ +import { Body, Controller, Headers, Post, UseGuards } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { Ip } from '~/common/decorators/http.decorator' + +import { UserService } from '../user/user.service' + +import { AuthService } from './auth.service' +import { Public } from './decorators/public.decorator' +import { LoginDto, RegisterDto } from './dto/auth.dto' +import { LocalGuard } from './guards/local.guard' +import { LoginToken } from './models/auth.model' +import { CaptchaService } from './services/captcha.service' + +@ApiTags('Auth - 认证模块') +@UseGuards(LocalGuard) +@Public() +@Controller('auth') +export class AuthController { + constructor( + private authService: AuthService, + private userService: UserService, + private captchaService: CaptchaService, + ) {} + + @Post('login') + @ApiOperation({ summary: '登录' }) + @ApiResult({ type: LoginToken }) + async login( + @Body() dto: LoginDto, @Ip() ip: string, @Headers('user-agent') ua: string): Promise { + await this.captchaService.checkImgCaptcha(dto.captchaId, dto.verifyCode) + const token = await this.authService.login( + dto.username, + dto.password, + ip, + ua, + ) + return { token } + } + + @Post('register') + @ApiOperation({ summary: '注册' }) + async register(@Body() dto: RegisterDto): Promise { + await this.userService.register(dto) + } +} diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts new file mode 100644 index 0000000..edaf919 --- /dev/null +++ b/src/modules/auth/auth.module.ts @@ -0,0 +1,64 @@ +import { Module } from '@nestjs/common' + +import { ConfigModule, ConfigService } from '@nestjs/config' +import { JwtModule } from '@nestjs/jwt' +import { PassportModule } from '@nestjs/passport' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { ConfigKeyPaths, ISecurityConfig } from '~/config' +import { isDev } from '~/global/env' + +import { LogModule } from '../system/log/log.module' +import { MenuModule } from '../system/menu/menu.module' +import { RoleModule } from '../system/role/role.module' +import { UserModule } from '../user/user.module' + +import { AuthController } from './auth.controller' +import { AuthService } from './auth.service' +import { AccountController } from './controllers/account.controller' +import { CaptchaController } from './controllers/captcha.controller' +import { EmailController } from './controllers/email.controller' +import { AccessTokenEntity } from './entities/access-token.entity' +import { RefreshTokenEntity } from './entities/refresh-token.entity' +import { CaptchaService } from './services/captcha.service' +import { TokenService } from './services/token.service' +import { JwtStrategy } from './strategies/jwt.strategy' +import { LocalStrategy } from './strategies/local.strategy' + +const controllers = [ + AuthController, + AccountController, + CaptchaController, + EmailController, +] +const providers = [AuthService, TokenService, CaptchaService] +const strategies = [LocalStrategy, JwtStrategy] + +@Module({ + imports: [ + TypeOrmModule.forFeature([AccessTokenEntity, RefreshTokenEntity]), + PassportModule, + JwtModule.registerAsync({ + imports: [ConfigModule], + useFactory: (configService: ConfigService) => { + const { jwtSecret, jwtExprire } + = configService.get('security') + + return { + secret: jwtSecret, + expires: jwtExprire, + ignoreExpiration: isDev, + } + }, + inject: [ConfigService], + }), + UserModule, + RoleModule, + MenuModule, + LogModule, + ], + controllers: [...controllers], + providers: [...providers, ...strategies], + exports: [TypeOrmModule, JwtModule, ...providers], +}) +export class AuthModule {} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts new file mode 100644 index 0000000..b734e27 --- /dev/null +++ b/src/modules/auth/auth.service.ts @@ -0,0 +1,156 @@ +import { InjectRedis } from '@liaoliaots/nestjs-redis' +import { Injectable } from '@nestjs/common' + +import Redis from 'ioredis' +import { isEmpty } from 'lodash' + +import { BusinessException } from '~/common/exceptions/biz.exception' + +import { ErrorEnum } from '~/constants/error-code.constant' +import { genAuthPVKey, genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey' + +import { UserService } from '~/modules/user/user.service' + +import { md5 } from '~/utils' + +import { LoginLogService } from '../system/log/services/login-log.service' +import { MenuService } from '../system/menu/menu.service' +import { RoleService } from '../system/role/role.service' + +import { TokenService } from './services/token.service' + +@Injectable() +export class AuthService { + constructor( + @InjectRedis() private readonly redis: Redis, + private menuService: MenuService, + private roleService: RoleService, + private userService: UserService, + private loginLogService: LoginLogService, + private tokenService: TokenService, + ) {} + + async validateUser(credential: string, password: string): Promise { + const user = await this.userService.findUserByUserName(credential) + + if (isEmpty(user)) + throw new BusinessException(ErrorEnum.USER_NOT_FOUND) + + const comparePassword = md5(`${password}${user.psalt}`) + if (user.password !== comparePassword) + throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD) + + if (user) { + const { password, ...result } = user + return result + } + + return null + } + + /** + * 获取登录JWT + * 返回null则账号密码有误,不存在该用户 + */ + async login( + username: string, + password: string, + ip: string, + ua: string, + ): Promise { + const user = await this.userService.findUserByUserName(username) + if (isEmpty(user)) + throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD) + + const comparePassword = md5(`${password}${user.psalt}`) + if (user.password !== comparePassword) + throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD) + + const roleIds = await this.roleService.getRoleIdsByUser(user.id) + + const roles = await this.roleService.getRoleValues(roleIds) + + // 包含access_token和refresh_token + const token = await this.tokenService.generateAccessToken(user.id, roles) + + await this.redis.set(genAuthTokenKey(user.id), token.accessToken) + + // 设置密码版本号 当密码修改时,版本号+1 + await this.redis.set(genAuthPVKey(user.id), 1) + + // 设置菜单权限 + const permissions = await this.menuService.getPermissions(user.id) + await this.setPermissionsCache(user.id, permissions) + + await this.loginLogService.create(user.id, ip, ua) + + return token.accessToken + } + + /** + * 效验账号密码 + */ + async checkPassword(username: string, password: string) { + const user = await this.userService.findUserByUserName(username) + + const comparePassword = md5(`${password}${user.psalt}`) + if (user.password !== comparePassword) + throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD) + } + + async loginLog(uid: number, ip: string, ua: string) { + await this.loginLogService.create(uid, ip, ua) + } + + async logout(uid: number) { + // 删除token + await this.userService.forbidden(uid) + } + + /** + * 重置密码 + */ + async resetPassword(username: string, password: string) { + const user = await this.userService.findUserByUserName(username) + + await this.userService.forceUpdatePassword(user.id, password) + } + + /** + * 清除登录状态信息 + */ + async clearLoginStatus(uid: number): Promise { + await this.userService.forbidden(uid) + } + + /** + * 获取菜单列表 + */ + async getMenus(uid: number): Promise { + return this.menuService.getMenus(uid) + } + + /** + * 获取权限列表 + */ + async getPermissions(uid: number): Promise { + return this.menuService.getPermissions(uid) + } + + async getPermissionsCache(uid: number): Promise { + const permissionString = await this.redis.get(genAuthPermKey(uid)) + return permissionString ? JSON.parse(permissionString) : [] + } + + async setPermissionsCache(uid: number, permissions: string[]): Promise { + await this.redis.set(genAuthPermKey(uid), JSON.stringify(permissions)) + } + + async getPasswordVersionByUid(uid: number): Promise { + return this.redis.get(genAuthPVKey(uid)) + } + + async getTokenByUid(uid: number): Promise { + return this.redis.get(genAuthTokenKey(uid)) + } +} diff --git a/src/modules/auth/controllers/account.controller.ts b/src/modules/auth/controllers/account.controller.ts new file mode 100644 index 0000000..d6072a1 --- /dev/null +++ b/src/modules/auth/controllers/account.controller.ts @@ -0,0 +1,75 @@ +import { Body, Controller, Get, Post, Put, UseGuards } from '@nestjs/common' +import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' + +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { AllowAnon } from '~/modules/auth/decorators/allow-anon.decorator' +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' + +import { PasswordUpdateDto } from '~/modules/user/dto/password.dto' + +import { AccountInfo } from '../../user/user.model' +import { UserService } from '../../user/user.service' +import { AuthService } from '../auth.service' +import { AccountMenus, AccountUpdateDto } from '../dto/account.dto' +import { JwtAuthGuard } from '../guards/jwt-auth.guard' + +@ApiTags('Account - 账户模块') +@ApiSecurityAuth() +@ApiExtraModels(AccountInfo) +@UseGuards(JwtAuthGuard) +@Controller('account') +export class AccountController { + constructor( + private userService: UserService, + private authService: AuthService, + ) {} + + @Get('profile') + @ApiOperation({ summary: '获取账户资料' }) + @ApiResult({ type: AccountInfo }) + @AllowAnon() + async profile(@AuthUser() user: IAuthUser): Promise { + return this.userService.getAccountInfo(user.uid) + } + + @Get('logout') + @ApiOperation({ summary: '账户登出' }) + @AllowAnon() + async logout(@AuthUser() user: IAuthUser): Promise { + await this.authService.clearLoginStatus(user.uid) + } + + @Get('menus') + @ApiOperation({ summary: '获取菜单列表' }) + @ApiResult({ type: [AccountMenus] }) + @AllowAnon() + async menu(@AuthUser() user: IAuthUser): Promise { + return this.authService.getMenus(user.uid) + } + + @Get('permissions') + @ApiOperation({ summary: '获取权限列表' }) + @ApiResult({ type: [String] }) + @AllowAnon() + async permissions(@AuthUser() user: IAuthUser): Promise { + return this.authService.getPermissions(user.uid) + } + + @Put('update') + @ApiOperation({ summary: '更改账户资料' }) + @AllowAnon() + async update( + @AuthUser() user: IAuthUser, @Body() dto: AccountUpdateDto): Promise { + await this.userService.updateAccountInfo(user.uid, dto) + } + + @Post('password') + @ApiOperation({ summary: '更改账户密码' }) + @AllowAnon() + async password( + @AuthUser() user: IAuthUser, @Body() dto: PasswordUpdateDto): Promise { + await this.userService.updatePassword(user.uid, dto) + } +} diff --git a/src/modules/auth/controllers/captcha.controller.ts b/src/modules/auth/controllers/captcha.controller.ts new file mode 100644 index 0000000..154236c --- /dev/null +++ b/src/modules/auth/controllers/captcha.controller.ts @@ -0,0 +1,50 @@ +import { InjectRedis } from '@liaoliaots/nestjs-redis' +import { Controller, Get, Query } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import Redis from 'ioredis' +import { isEmpty } from 'lodash' +import * as svgCaptcha from 'svg-captcha' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { genCaptchaImgKey } from '~/helper/genRedisKey' +import { generateUUID } from '~/utils' + +import { Public } from '../decorators/public.decorator' + +import { ImageCaptchaDto } from '../dto/captcha.dto' +import { ImageCaptcha } from '../models/auth.model' + +@ApiTags('Captcha - 验证码模块') +// @UseGuards(ThrottlerGuard) +@Controller('auth/captcha') +export class CaptchaController { + constructor(@InjectRedis() private redis: Redis) {} + + @Get('img') + @ApiOperation({ summary: '获取登录图片验证码' }) + @ApiResult({ type: ImageCaptcha }) + @Public() + // @Throttle({ default: { limit: 2, ttl: 600000 } }) + async captchaByImg(@Query() dto: ImageCaptchaDto): Promise { + const { width, height } = dto + + const svg = svgCaptcha.create({ + size: 4, + color: true, + noise: 4, + width: isEmpty(width) ? 100 : width, + height: isEmpty(height) ? 50 : height, + charPreset: '1234567890', + }) + const result = { + img: `data:image/svg+xml;base64,${Buffer.from(svg.data).toString( + 'base64', + )}`, + id: generateUUID(), + } + // 5分钟过期时间 + await this.redis.set(genCaptchaImgKey(result.id), svg.text, 'EX', 60 * 5) + return result + } +} diff --git a/src/modules/auth/controllers/email.controller.ts b/src/modules/auth/controllers/email.controller.ts new file mode 100644 index 0000000..99ce81a --- /dev/null +++ b/src/modules/auth/controllers/email.controller.ts @@ -0,0 +1,41 @@ +import { Body, Controller, Post, UseGuards } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { Throttle, ThrottlerGuard } from '@nestjs/throttler' + +import { Ip } from '~/common/decorators/http.decorator' + +import { MailerService } from '~/shared/mailer/mailer.service' + +import { Public } from '../decorators/public.decorator' + +import { SendEmailCodeDto } from '../dto/captcha.dto' + +@ApiTags('Auth - 认证模块') +@UseGuards(ThrottlerGuard) +@Controller('auth/email') +export class EmailController { + constructor(private mailerService: MailerService) {} + + @Post('send') + @ApiOperation({ summary: '发送邮箱验证码' }) + @Public() + @Throttle({ default: { limit: 2, ttl: 600000 } }) + async sendEmailCode( + @Body() dto: SendEmailCodeDto, + @Ip() ip: string, + ): Promise { + // await this.authService.checkImgCaptcha(dto.captchaId, dto.verifyCode); + const { email } = dto + + await this.mailerService.checkLimit(email, ip) + const { code } = await this.mailerService.sendVerificationCode(email) + + await this.mailerService.log(email, code, ip) + } + + // @Post() + // async authWithEmail(@AuthUser() user: IAuthUser) { + // // TODO: + // } +} diff --git a/src/modules/auth/decorators/allow-anon.decorator.ts b/src/modules/auth/decorators/allow-anon.decorator.ts new file mode 100644 index 0000000..21954f6 --- /dev/null +++ b/src/modules/auth/decorators/allow-anon.decorator.ts @@ -0,0 +1,8 @@ +import { SetMetadata } from '@nestjs/common' + +import { ALLOW_ANON_KEY } from '../auth.constant' + +/** + * 当接口不需要检测用户是否具有操作权限时添加该装饰器 + */ +export const AllowAnon = () => SetMetadata(ALLOW_ANON_KEY, true) diff --git a/src/modules/auth/decorators/auth-user.decorator.ts b/src/modules/auth/decorators/auth-user.decorator.ts new file mode 100644 index 0000000..ef0733d --- /dev/null +++ b/src/modules/auth/decorators/auth-user.decorator.ts @@ -0,0 +1,17 @@ +import { ExecutionContext, createParamDecorator } from '@nestjs/common' +import { FastifyRequest } from 'fastify' + +type Payload = keyof IAuthUser + +/** + * @description 获取当前登录用户信息, 并挂载到request上 + */ +export const AuthUser = createParamDecorator( + (data: Payload, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest() + // auth guard will mount this + const user = request.user as IAuthUser + + return data ? user?.[data] : user + }, +) diff --git a/src/modules/auth/decorators/permission.decorator.ts b/src/modules/auth/decorators/permission.decorator.ts new file mode 100644 index 0000000..943679f --- /dev/null +++ b/src/modules/auth/decorators/permission.decorator.ts @@ -0,0 +1,58 @@ +import { SetMetadata, applyDecorators } from '@nestjs/common' + +import { isPlainObject } from 'lodash' + +import { PERMISSION_KEY } from '../auth.constant' + + type TupleToObject> = { + [K in Uppercase]: `${T}:${Lowercase}` + } + type AddPrefixToObjectValue> = { + [K in keyof P]: K extends string ? `${T}:${P[K]}` : never + } + +/** 资源操作需要特定的权限 */ +export function Perm(permission: string | string[]) { + return applyDecorators(SetMetadata(PERMISSION_KEY, permission)) +} + +/** (此举非必需)保存通过 definePermission 定义的所有权限,可用于前端开发人员开发阶段的 ts 类型提示,避免前端权限定义与后端定义不匹配 */ +let permissions: string[] = [] +/** + * 定义权限,同时收集所有被定义的权限 + * + * - 通过对象形式定义, eg: + * ```ts + * definePermission('app:health', { + * NETWORK: 'network' + * }; + * ``` + * + * - 通过字符串数组形式定义, eg: + * ```ts + * definePermission('app:health', ['network']); + * ``` + */ +export function definePermission>(modulePrefix: T, actionMap: U): AddPrefixToObjectValue +export function definePermission>(modulePrefix: T, actions: U): TupleToObject +export function definePermission(modulePrefix: string, actions) { + if (isPlainObject(actions)) { + Object.entries(actions).forEach(([key, action]) => { + actions[key] = `${modulePrefix}:${action}` + }) + permissions = [...new Set([...permissions, ...Object.values(actions)])] + return actions + } + else if (Array.isArray(actions)) { + const permissionFormats = actions.map(action => `${modulePrefix}:${action}`) + permissions = [...new Set([...permissions, ...permissionFormats])] + + return actions.reduce((prev, action) => { + prev[action.toUpperCase()] = `${modulePrefix}:${action}` + return prev + }, {}) + } +} + +/** 获取所有通过 definePermission 定义的权限 */ +export const getDefinePermissions = () => permissions diff --git a/src/modules/auth/decorators/public.decorator.ts b/src/modules/auth/decorators/public.decorator.ts new file mode 100644 index 0000000..c3409ca --- /dev/null +++ b/src/modules/auth/decorators/public.decorator.ts @@ -0,0 +1,8 @@ +import { SetMetadata } from '@nestjs/common' + +import { PUBLIC_KEY } from '../auth.constant' + +/** + * 当接口不需要检测用户登录时添加该装饰器 + */ +export const Public = () => SetMetadata(PUBLIC_KEY, true) diff --git a/src/modules/auth/decorators/resource.decorator.ts b/src/modules/auth/decorators/resource.decorator.ts new file mode 100644 index 0000000..73143b0 --- /dev/null +++ b/src/modules/auth/decorators/resource.decorator.ts @@ -0,0 +1,12 @@ +import { SetMetadata, applyDecorators } from '@nestjs/common' + +import { ObjectLiteral, ObjectType, Repository } from 'typeorm' + +import { RESOURCE_KEY } from '../auth.constant' + +export type Condition = (Repository: Repository, items: number[], user: IAuthUser) => Promise + +export interface ResourceObject { entity: ObjectType, condition: Condition } +export function Resource(entity: ObjectType, condition?: Condition) { + return applyDecorators(SetMetadata(RESOURCE_KEY, { entity, condition })) +} diff --git a/src/modules/auth/dto/account.dto.ts b/src/modules/auth/dto/account.dto.ts new file mode 100644 index 0000000..8b3a5ee --- /dev/null +++ b/src/modules/auth/dto/account.dto.ts @@ -0,0 +1,64 @@ +import { ApiProperty, OmitType, PartialType, PickType } from '@nestjs/swagger' +import { + IsEmail, + IsOptional, + IsString, + Matches, + MaxLength, + MinLength, +} from 'class-validator' + +import { MenuEntity } from '~/modules/system/menu/menu.entity' + +export class AccountUpdateDto { + @ApiProperty({ description: '用户呢称' }) + @IsString() + @IsOptional() + nickname: string + + @ApiProperty({ description: '用户邮箱' }) + @IsEmail() + email: string + + @ApiProperty({ description: '用户QQ' }) + @IsOptional() + @IsString() + @Matches(/^[0-9]+$/) + @MinLength(5) + @MaxLength(11) + qq: string + + @ApiProperty({ description: '用户手机号' }) + @IsOptional() + @IsString() + phone: string + + @ApiProperty({ description: '用户头像' }) + @IsOptional() + @IsString() + avatar: string + + @ApiProperty({ description: '用户备注' }) + @IsOptional() + @IsString() + remark: string +} + +export class ResetPasswordDto { + @ApiProperty({ description: '临时token', example: 'uuid' }) + @IsString() + accessToken: string + + @ApiProperty({ description: '密码', example: 'a123456' }) + @IsString() + @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/) + @MinLength(6) + password: string +} + +export class MenuMeta extends PartialType(OmitType(MenuEntity, ['parentId', 'createdAt', 'updatedAt', 'id', 'roles', 'path', 'name'] as const)) { + title: string +} +export class AccountMenus extends PickType(MenuEntity, ['id', 'path', 'name', 'component'] as const) { + meta: MenuMeta +} diff --git a/src/modules/auth/dto/auth.dto.ts b/src/modules/auth/dto/auth.dto.ts new file mode 100644 index 0000000..abaf379 --- /dev/null +++ b/src/modules/auth/dto/auth.dto.ts @@ -0,0 +1,43 @@ +import { ApiProperty } from '@nestjs/swagger' + +import { IsString, Matches, MaxLength, MinLength } from 'class-validator' + +export class LoginDto { + @ApiProperty({ description: '手机号/邮箱' }) + @IsString() + @MinLength(4) + username: string + + @ApiProperty({ description: '密码', example: 'a123456' }) + @IsString() + @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/) + @MinLength(6) + password: string + + @ApiProperty({ description: '验证码标识' }) + @IsString() + captchaId: string + + @ApiProperty({ description: '用户输入的验证码' }) + @IsString() + @MinLength(4) + @MaxLength(4) + verifyCode: string +} + +export class RegisterDto { + @ApiProperty({ description: '账号' }) + @IsString() + username: string + + @ApiProperty({ description: '密码' }) + @IsString() + @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/) + @MinLength(6) + @MaxLength(16) + password: string + + @ApiProperty({ description: '语言', examples: ['EN', 'ZH'] }) + @IsString() + lang: string +} diff --git a/src/modules/auth/dto/captcha.dto.ts b/src/modules/auth/dto/captcha.dto.ts new file mode 100644 index 0000000..c24d3d8 --- /dev/null +++ b/src/modules/auth/dto/captcha.dto.ts @@ -0,0 +1,53 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Type } from 'class-transformer' +import { + IsEmail, + IsInt, + IsMobilePhone, + IsOptional, + IsString, +} from 'class-validator' + +export class ImageCaptchaDto { + @ApiProperty({ + required: false, + default: 100, + description: '验证码宽度', + }) + @Type(() => Number) + @IsInt() + @IsOptional() + readonly width: number = 100 + + @ApiProperty({ + required: false, + default: 50, + description: '验证码宽度', + }) + @Type(() => Number) + @IsInt() + @IsOptional() + readonly height: number = 50 +} + +export class SendEmailCodeDto { + @ApiProperty({ description: '邮箱' }) + @IsEmail({}, { message: '邮箱格式不正确' }) + email: string +} + +export class SendSmsCodeDto { + @ApiProperty({ description: '手机号' }) + @IsMobilePhone('zh-CN', {}, { message: '手机号格式不正确' }) + phone: string +} + +export class CheckCodeDto { + @ApiProperty({ description: '手机号/邮箱' }) + @IsString() + account: string + + @ApiProperty({ description: '验证码' }) + @IsString() + code: string +} diff --git a/src/modules/auth/entities/access-token.entity.ts b/src/modules/auth/entities/access-token.entity.ts new file mode 100644 index 0000000..d56afc8 --- /dev/null +++ b/src/modules/auth/entities/access-token.entity.ts @@ -0,0 +1,40 @@ +import { + BaseEntity, + Column, + CreateDateColumn, + Entity, + JoinColumn, + ManyToOne, + OneToOne, + PrimaryGeneratedColumn, +} from 'typeorm' + +import { UserEntity } from '~/modules/user/user.entity' + +import { RefreshTokenEntity } from './refresh-token.entity' + +@Entity('user_access_tokens') +export class AccessTokenEntity extends BaseEntity { + @PrimaryGeneratedColumn('uuid') + id!: string + + @Column({ length: 500 }) + value!: string + + @Column({ comment: '令牌过期时间' }) + expired_at!: Date + + @CreateDateColumn({ comment: '令牌创建时间' }) + created_at!: Date + + @OneToOne(() => RefreshTokenEntity, refreshToken => refreshToken.accessToken, { + cascade: true, + }) + refreshToken!: RefreshTokenEntity + + @ManyToOne(() => UserEntity, user => user.accessTokens, { + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'user_id' }) + user!: UserEntity +} diff --git a/src/modules/auth/entities/refresh-token.entity.ts b/src/modules/auth/entities/refresh-token.entity.ts new file mode 100644 index 0000000..cec190b --- /dev/null +++ b/src/modules/auth/entities/refresh-token.entity.ts @@ -0,0 +1,32 @@ +import { + BaseEntity, + Column, + CreateDateColumn, + Entity, + JoinColumn, + OneToOne, + PrimaryGeneratedColumn, +} from 'typeorm' + +import { AccessTokenEntity } from './access-token.entity' + +@Entity('user_refresh_tokens') +export class RefreshTokenEntity extends BaseEntity { + @PrimaryGeneratedColumn('uuid') + id!: string + + @Column({ length: 500 }) + value!: string + + @Column({ comment: '令牌过期时间' }) + expired_at!: Date + + @CreateDateColumn({ comment: '令牌创建时间' }) + created_at!: Date + + @OneToOne(() => AccessTokenEntity, accessToken => accessToken.refreshToken, { + onDelete: 'CASCADE', + }) + @JoinColumn() + accessToken!: AccessTokenEntity +} diff --git a/src/modules/auth/guards/jwt-auth.guard.ts b/src/modules/auth/guards/jwt-auth.guard.ts new file mode 100644 index 0000000..0cbe88c --- /dev/null +++ b/src/modules/auth/guards/jwt-auth.guard.ts @@ -0,0 +1,105 @@ +import { + ExecutionContext, + Injectable, + UnauthorizedException, +} from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { AuthGuard } from '@nestjs/passport' +import { FastifyRequest } from 'fastify' +import { isEmpty, isNil } from 'lodash' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { AuthService } from '~/modules/auth/auth.service' + +import { checkIsDemoMode } from '~/utils' + +import { AuthStrategy, PUBLIC_KEY } from '../auth.constant' +import { TokenService } from '../services/token.service' + +// https://docs.nestjs.com/recipes/passport#implement-protected-route-and-jwt-strategy-guards +@Injectable() +export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) { + constructor( + private reflector: Reflector, + private authService: AuthService, + private tokenService: TokenService, + ) { + super() + } + + async canActivate(context: ExecutionContext): Promise { + const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]) + const request = context.switchToHttp().getRequest() + // const response = context.switchToHttp().getResponse() + + // TODO 此处代码的作用是判断如果在演示环境下,则拒绝用户的增删改操作,去掉此代码不影响正常的业务逻辑 + if (request.method !== 'GET' && !request.url.includes('/auth/login')) + checkIsDemoMode() + + const isSse = request.headers.accept === 'text/event-stream' + + if (isSse && !request.headers.authorization?.startsWith('Bearer')) { + const { token } = request.query as Record + if (token) + request.headers.authorization = `Bearer ${token}` + } + + const Authorization = request.headers.authorization + + let result: any = false + try { + result = await super.canActivate(context) + } + catch (e) { + // 需要后置判断 这样携带了 token 的用户就能够解析到 request.user + if (isPublic) + return true + + if (isEmpty(Authorization)) + throw new UnauthorizedException('未登录') + + // 判断 token 是否存在, 如果不存在则认证失败 + const accessToken = isNil(Authorization) + ? undefined + : await this.tokenService.checkAccessToken(Authorization!) + + if (!accessToken) + throw new UnauthorizedException('令牌无效') + } + + // SSE 请求 + if (isSse) { + const { uid } = request.params as Record + + if (Number(uid) !== request.user.uid) + throw new UnauthorizedException('路径参数 uid 与当前 token 登录的用户 uid 不一致') + } + + const pv = await this.authService.getPasswordVersionByUid(request.user.uid) + if (pv !== `${request.user.pv}`) { + // 密码版本不一致,登录期间已更改过密码 + throw new BusinessException(ErrorEnum.INVALID_LOGIN) + } + + // 不允许多端登录 + // const cacheToken = await this.authService.getTokenByUid(request.user.uid); + // if (Authorization !== cacheToken) { + // // 与redis保存不一致 即二次登录 + // throw new ApiException(ErrorEnum.CODE_1106); + // } + + return result + } + + handleRequest(err, user, info) { + // You can throw an exception based on either "info" or "err" arguments + if (err || !user) + throw err || new UnauthorizedException() + + return user + } +} diff --git a/src/modules/auth/guards/local.guard.ts b/src/modules/auth/guards/local.guard.ts new file mode 100644 index 0000000..c2de171 --- /dev/null +++ b/src/modules/auth/guards/local.guard.ts @@ -0,0 +1,11 @@ +import { ExecutionContext, Injectable } from '@nestjs/common' +import { AuthGuard } from '@nestjs/passport' + +import { AuthStrategy } from '../auth.constant' + +@Injectable() +export class LocalGuard extends AuthGuard(AuthStrategy.LOCAL) { + async canActivate(context: ExecutionContext) { + return true + } +} diff --git a/src/modules/auth/guards/rbac.guard.ts b/src/modules/auth/guards/rbac.guard.ts new file mode 100644 index 0000000..8545adb --- /dev/null +++ b/src/modules/auth/guards/rbac.guard.ts @@ -0,0 +1,76 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { FastifyRequest } from 'fastify' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { AuthService } from '~/modules/auth/auth.service' + +import { ALLOW_ANON_KEY, PERMISSION_KEY, PUBLIC_KEY, Roles } from '../auth.constant' + +@Injectable() +export class RbacGuard implements CanActivate { + constructor( + private reflector: Reflector, + private authService: AuthService, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]) + + if (isPublic) + return true + + const request = context.switchToHttp().getRequest() + + const { user } = request + if (!user) + throw new UnauthorizedException('登录无效') + + // allowAnon 是需要登录后可访问(无需权限), Public 则是无需登录也可访问. + const allowAnon = this.reflector.get( + ALLOW_ANON_KEY, + context.getHandler(), + ) + if (allowAnon) + return true + + const payloadPermission = this.reflector.getAllAndOverride< + string | string[] + >(PERMISSION_KEY, [context.getHandler(), context.getClass()]) + + // 控制器没有设置接口权限,则默认通过 + if (!payloadPermission) + return true + + // 管理员放开所有权限 + if (user.roles.includes(Roles.ADMIN)) + return true + + const allPermissions = await this.authService.getPermissionsCache(user.uid) ?? await this.authService.getPermissions(user.uid) + // console.log(allPermissions) + let canNext = false + + // handle permission strings + if (Array.isArray(payloadPermission)) { + // 只要有一个权限满足即可 + canNext = payloadPermission.every(i => allPermissions.includes(i)) + } + + if (typeof payloadPermission === 'string') + canNext = allPermissions.includes(payloadPermission) + + if (!canNext) + throw new BusinessException(ErrorEnum.NO_PERMISSION) + + return true + } +} diff --git a/src/modules/auth/guards/resource.guard.ts b/src/modules/auth/guards/resource.guard.ts new file mode 100644 index 0000000..b78da0c --- /dev/null +++ b/src/modules/auth/guards/resource.guard.ts @@ -0,0 +1,87 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { FastifyRequest } from 'fastify' + +import { isArray, isEmpty, isNil } from 'lodash' + +import { DataSource, In, Repository } from 'typeorm' + +import { BusinessException } from '~/common/exceptions/biz.exception' + +import { ErrorEnum } from '~/constants/error-code.constant' + +import { PUBLIC_KEY, RESOURCE_KEY, Roles } from '../auth.constant' +import { ResourceObject } from '../decorators/resource.decorator' + +@Injectable() +export class ResourceGuard implements CanActivate { + constructor( + private reflector: Reflector, + private dataSource: DataSource, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]) + + const request = context.switchToHttp().getRequest() + const isSse = request.headers.accept === 'text/event-stream' + // 忽略 sse 请求 + if (isPublic || isSse) + return true + + const { user } = request + + if (!user) + return false + + // 如果是检查资源所属,且不是超级管理员,还需要进一步判断是否是自己的数据 + const { entity, condition } = this.reflector.get( + RESOURCE_KEY, + context.getHandler(), + ) ?? { entity: null, condition: null } + + if (entity && !user.roles.includes(Roles.ADMIN)) { + const repo: Repository = this.dataSource.getRepository(entity) + + /** + * 获取请求中的 items (ids) 验证数据拥有者 + * @param request + */ + const getRequestItems = (request?: FastifyRequest): number[] => { + const { params = {}, body = {}, query = {} } = (request ?? {}) as any + const id = params.id ?? body.id ?? query.id + + if (id) + return [id] + + const { items } = body + return !isNil(items) && isArray(items) ? items : [] + } + + const items = getRequestItems(request) + if (isEmpty(items)) + throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND) + + if (condition) + return condition(repo, items, user) + + const recordQuery = { + where: { + id: In(items), + user: { id: user.uid }, + }, + relations: ['user'], + } + + const records = await repo.find(recordQuery) + + if (isEmpty(records)) + throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND) + } + + return true + } +} diff --git a/src/modules/auth/models/auth.model.ts b/src/modules/auth/models/auth.model.ts new file mode 100644 index 0000000..faeada1 --- /dev/null +++ b/src/modules/auth/models/auth.model.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger' + +export class ImageCaptcha { + @ApiProperty({ description: 'base64格式的svg图片' }) + img: string + + @ApiProperty({ description: '验证码对应的唯一ID' }) + id: string +} + +export class LoginToken { + @ApiProperty({ description: 'JWT身份Token' }) + token: string +} diff --git a/src/modules/auth/services/captcha.service.ts b/src/modules/auth/services/captcha.service.ts new file mode 100644 index 0000000..612a6b7 --- /dev/null +++ b/src/modules/auth/services/captcha.service.ts @@ -0,0 +1,40 @@ +import { InjectRedis } from '@liaoliaots/nestjs-redis' +import { Injectable } from '@nestjs/common' + +import Redis from 'ioredis' +import { isEmpty } from 'lodash' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { genCaptchaImgKey } from '~/helper/genRedisKey' +import { CaptchaLogService } from '~/modules/system/log/services/captcha-log.service' + +@Injectable() +export class CaptchaService { + constructor( + @InjectRedis() private redis: Redis, + + private captchaLogService: CaptchaLogService, + ) {} + + /** + * 校验图片验证码 + */ + async checkImgCaptcha(id: string, code: string): Promise { + const result = await this.redis.get(genCaptchaImgKey(id)) + if (isEmpty(result) || code.toLowerCase() !== result.toLowerCase()) + throw new BusinessException(ErrorEnum.INVALID_VERIFICATION_CODE) + + // 校验成功后移除验证码 + await this.redis.del(genCaptchaImgKey(id)) + } + + async log( + account: string, + code: string, + provider: 'sms' | 'email', + uid?: number, + ): Promise { + await this.captchaLogService.create(account, code, provider, uid) + } +} diff --git a/src/modules/auth/services/token.service.ts b/src/modules/auth/services/token.service.ts new file mode 100644 index 0000000..e680ecc --- /dev/null +++ b/src/modules/auth/services/token.service.ts @@ -0,0 +1,160 @@ +import { Inject, Injectable } from '@nestjs/common' +import { JwtService } from '@nestjs/jwt' +import dayjs from 'dayjs' + +import { ISecurityConfig, SecurityConfig } from '~/config' +import { RoleService } from '~/modules/system/role/role.service' +import { UserEntity } from '~/modules/user/user.entity' +import { generateUUID } from '~/utils' + +import { AccessTokenEntity } from '../entities/access-token.entity' +import { RefreshTokenEntity } from '../entities/refresh-token.entity' + +/** + * 令牌服务 + */ +@Injectable() +export class TokenService { + constructor( + private jwtService: JwtService, + private roleService: RoleService, + @Inject(SecurityConfig.KEY) private securityConfig: ISecurityConfig, + ) {} + + /** + * 根据accessToken刷新AccessToken与RefreshToken + * @param accessTokenSign + * @param response + */ + async refreshToken(accessToken: AccessTokenEntity) { + const { user, refreshToken } = accessToken + + if (refreshToken) { + const now = dayjs() + // 判断refreshToken是否过期 + if (now.isAfter(refreshToken.expired_at)) + return null + + const roleIds = await this.roleService.getRoleIdsByUser(user.id) + const roleValues = await this.roleService.getRoleValues(roleIds) + + // 如果没过期则生成新的access_token和refresh_token + const token = await this.generateAccessToken(user.id, roleValues) + + await accessToken.remove() + return token + } + return null + } + + generateJwtSign(payload: any) { + const jwtSign = this.jwtService.sign(payload) + + return jwtSign + } + + async generateAccessToken(uid: number, roles: string[] = []) { + const payload: IAuthUser = { + uid, + pv: 1, + roles, + } + + const jwtSign = this.jwtService.sign(payload) + + // 生成accessToken + const accessToken = new AccessTokenEntity() + accessToken.value = jwtSign + accessToken.user = { id: uid } as UserEntity + accessToken.expired_at = dayjs() + .add(this.securityConfig.jwtExprire, 'second') + .toDate() + + await accessToken.save() + + // 生成refreshToken + const refreshToken = await this.generateRefreshToken(accessToken, dayjs()) + + return { + accessToken: jwtSign, + refreshToken, + } + } + + /** + * 生成新的RefreshToken并存入数据库 + * @param accessToken + * @param now + */ + async generateRefreshToken( + accessToken: AccessTokenEntity, + now: dayjs.Dayjs, + ): Promise { + const refreshTokenPayload = { + uuid: generateUUID(), + } + + const refreshTokenSign = this.jwtService.sign(refreshTokenPayload, { + secret: this.securityConfig.refreshSecret, + }) + + const refreshToken = new RefreshTokenEntity() + refreshToken.value = refreshTokenSign + refreshToken.expired_at = now + .add(this.securityConfig.refreshExpire, 'second') + .toDate() + refreshToken.accessToken = accessToken + + await refreshToken.save() + + return refreshTokenSign + } + + /** + * 检查accessToken是否存在 + * @param value + */ + async checkAccessToken(value: string) { + return AccessTokenEntity.findOne({ + where: { value }, + relations: ['user', 'refreshToken'], + cache: true, + }) + } + + /** + * 移除AccessToken且自动移除关联的RefreshToken + * @param value + */ + async removeAccessToken(value: string) { + const accessToken = await AccessTokenEntity.findOne({ + where: { value }, + }) + if (accessToken) + await accessToken.remove() + } + + /** + * 移除RefreshToken + * @param value + */ + async removeRefreshToken(value: string) { + const refreshToken = await RefreshTokenEntity.findOne({ + where: { value }, + relations: ['accessToken'], + }) + if (refreshToken) { + if (refreshToken.accessToken) + await refreshToken.accessToken.remove() + await refreshToken.remove() + } + } + + /** + * 验证Token是否正确,如果正确则返回所属用户对象 + * @param token + */ + async verifyAccessToken(token: string): Promise { + return this.jwtService.verify(token) + } +} diff --git a/src/modules/auth/strategies/jwt.strategy.ts b/src/modules/auth/strategies/jwt.strategy.ts new file mode 100644 index 0000000..1fc00fd --- /dev/null +++ b/src/modules/auth/strategies/jwt.strategy.ts @@ -0,0 +1,24 @@ +import { Inject, Injectable } from '@nestjs/common' +import { PassportStrategy } from '@nestjs/passport' +import { ExtractJwt, Strategy } from 'passport-jwt' + +import { ISecurityConfig, SecurityConfig } from '~/config' + +import { AuthStrategy } from '../auth.constant' + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy, AuthStrategy.JWT) { + constructor( + @Inject(SecurityConfig.KEY) private securityConfig: ISecurityConfig, + ) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: securityConfig.jwtSecret, + }) + } + + async validate(payload: IAuthUser) { + return payload + } +} diff --git a/src/modules/auth/strategies/local.strategy.ts b/src/modules/auth/strategies/local.strategy.ts new file mode 100644 index 0000000..69c59a9 --- /dev/null +++ b/src/modules/auth/strategies/local.strategy.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@nestjs/common' +import { PassportStrategy } from '@nestjs/passport' +import { Strategy } from 'passport-local' + +import { AuthStrategy } from '../auth.constant' +import { AuthService } from '../auth.service' + +@Injectable() +export class LocalStrategy extends PassportStrategy( + Strategy, + AuthStrategy.LOCAL, +) { + constructor(private authService: AuthService) { + super({ + usernameField: 'credential', + passwordField: 'password', + }) + } + + async validate(username: string, password: string): Promise { + const user = await this.authService.validateUser(username, password) + return user + } +} diff --git a/src/modules/health/health.controller.ts b/src/modules/health/health.controller.ts new file mode 100644 index 0000000..ad81caa --- /dev/null +++ b/src/modules/health/health.controller.ts @@ -0,0 +1,71 @@ +import { Controller, Get } from '@nestjs/common' +import { ApiTags } from '@nestjs/swagger' +import { + DiskHealthIndicator, + HealthCheck, + HttpHealthIndicator, + MemoryHealthIndicator, + TypeOrmHealthIndicator, +} from '@nestjs/terminus' + +import { Perm, definePermission } from '../auth/decorators/permission.decorator' + +export const PermissionHealth = definePermission('app:health', { + NETWORK: 'network', + DB: 'database', + MH: 'memory-heap', + MR: 'memory-rss', + DISK: 'disk', +} as const) + +@ApiTags('Health - 健康检查') +@Controller('health') +export class HealthController { + constructor( + private http: HttpHealthIndicator, + private db: TypeOrmHealthIndicator, + private memory: MemoryHealthIndicator, + private disk: DiskHealthIndicator, + ) {} + + @Get('network') + @HealthCheck() + @Perm(PermissionHealth.NETWORK) + async checkNetwork() { + return this.http.pingCheck('buqiyuan', 'https://buqiyuan.gitee.io/') + } + + @Get('database') + @HealthCheck() + @Perm(PermissionHealth.DB) + async checkDatabase() { + return this.db.pingCheck('database') + } + + @Get('memory-heap') + @HealthCheck() + @Perm(PermissionHealth.MH) + async checkMemoryHeap() { + // the process should not use more than 200MB memory + return this.memory.checkHeap('memory-heap', 200 * 1024 * 1024) + } + + @Get('memory-rss') + @HealthCheck() + @Perm(PermissionHealth.MR) + async checkMemoryRSS() { + // the process should not have more than 200MB RSS memory allocated + return this.memory.checkRSS('memory-rss', 200 * 1024 * 1024) + } + + @Get('disk') + @HealthCheck() + @Perm(PermissionHealth.DISK) + async checkDisk() { + return this.disk.checkStorage('disk', { + // The used disk storage should not exceed 75% of the full disk size + thresholdPercent: 0.75, + path: '/', + }) + } +} diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts new file mode 100644 index 0000000..141fadc --- /dev/null +++ b/src/modules/health/health.module.ts @@ -0,0 +1,11 @@ +import { HttpModule } from '@nestjs/axios' +import { Module } from '@nestjs/common' +import { TerminusModule } from '@nestjs/terminus' + +import { HealthController } from './health.controller' + +@Module({ + imports: [TerminusModule, HttpModule], + controllers: [HealthController], +}) +export class HealthModule {} diff --git a/src/modules/netdisk/manager/manage.class.ts b/src/modules/netdisk/manager/manage.class.ts new file mode 100644 index 0000000..ef774c1 --- /dev/null +++ b/src/modules/netdisk/manager/manage.class.ts @@ -0,0 +1,68 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export type FileType = 'file' | 'dir'; + +export class SFileInfo { + @ApiProperty({ description: '文件id' }) + id: string; + + @ApiProperty({ description: '文件类型', enum: ['file', 'dir'] }) + type: FileType; + + @ApiProperty({ description: '文件名称' }) + name: string; + + @ApiProperty({ description: '存入时间', type: Date }) + putTime?: Date; + + @ApiProperty({ description: '文件大小, byte单位' }) + fsize?: string; + + @ApiProperty({ description: '文件的mime-type' }) + mimeType?: string; + + @ApiProperty({ description: '所属目录' }) + belongTo?: string; +} + +export class SFileList { + @ApiProperty({ description: '文件列表', type: [SFileInfo] }) + list: SFileInfo[]; + + @ApiProperty({ description: '分页标志,空则代表加载完毕' }) + marker?: string; +} + +export class UploadToken { + @ApiProperty({ description: '上传token' }) + token: string; +} + +export class SFileInfoDetail { + @ApiProperty({ description: '文件大小,int64类型,单位为字节(Byte)' }) + fsize: number; + + @ApiProperty({ description: '文件HASH值' }) + hash: string; + + @ApiProperty({ description: '文件MIME类型,string类型' }) + mimeType: string; + + @ApiProperty({ + description: + '文件存储类型,2 表示归档存储,1 表示低频存储,0表示普通存储。', + }) + type: number; + + @ApiProperty({ description: '文件上传时间', type: Date }) + putTime: Date; + + @ApiProperty({ description: '文件md5值' }) + md5: string; + + @ApiProperty({ description: '上传人' }) + uploader: string; + + @ApiProperty({ description: '文件备注' }) + mark?: string; +} diff --git a/src/modules/netdisk/manager/manage.controller.ts b/src/modules/netdisk/manager/manage.controller.ts new file mode 100644 index 0000000..652d6aa --- /dev/null +++ b/src/modules/netdisk/manager/manage.controller.ts @@ -0,0 +1,153 @@ +import { Body, Controller, Get, Post, Query } from '@nestjs/common' +import { + ApiOkResponse, + ApiOperation, + ApiTags, +} from '@nestjs/swagger' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' + +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' + +import { checkIsDemoMode } from '~/utils' + +import { SFileInfoDetail, SFileList, UploadToken } from './manage.class' +import { + DeleteDto, + FileInfoDto, + FileOpDto, + GetFileListDto, + MKDirDto, + MarkFileDto, + RenameDto, +} from './manage.dto' +import { NetDiskManageService } from './manage.service' + +export const permissions = definePermission('netdisk:manage', { + LIST: 'list', + CREATE: 'create', + INFO: 'info', + UPDATE: 'update', + DELETE: 'delete', + MKDIR: 'mkdir', + TOKEN: 'token', + MARK: 'mark', + DOWNLOAD: 'download', + RENAME: 'rename', + CUT: 'cut', + COPY: 'copy', +} as const) + +@ApiTags('NetDiskManage - 网盘管理模块') +@Controller('manage') +export class NetDiskManageController { + constructor(private manageService: NetDiskManageService) {} + + @Get('list') + @ApiOperation({ summary: '获取文件列表' }) + @ApiOkResponse({ type: SFileList }) + @Perm(permissions.LIST) + async list(@Query() dto: GetFileListDto): Promise { + return await this.manageService.getFileList(dto.path, dto.marker, dto.key) + } + + @Post('mkdir') + @ApiOperation({ summary: '创建文件夹,支持多级' }) + @Perm(permissions.MKDIR) + async mkdir(@Body() dto: MKDirDto): Promise { + const result = await this.manageService.checkFileExist( + `${dto.path}${dto.dirName}/`, + ) + if (result) + throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST) + + await this.manageService.createDir(`${dto.path}${dto.dirName}`) + } + + @Get('token') + @ApiOperation({ summary: '获取上传Token,无Token前端无法上传' }) + @ApiOkResponse({ type: UploadToken }) + @Perm(permissions.TOKEN) + async token(@AuthUser() user: IAuthUser): Promise { + checkIsDemoMode() + + return { + token: this.manageService.createUploadToken(`${user.uid}`), + } + } + + @Get('info') + @ApiOperation({ summary: '获取文件详细信息' }) + @ApiOkResponse({ type: SFileInfoDetail }) + @Perm(permissions.INFO) + async info(@Query() dto: FileInfoDto): Promise { + return await this.manageService.getFileInfo(dto.name, dto.path) + } + + @Post('mark') + @ApiOperation({ summary: '添加文件备注' }) + @Perm(permissions.MARK) + async mark(@Body() dto: MarkFileDto): Promise { + await this.manageService.changeFileHeaders(dto.name, dto.path, { + mark: dto.mark, + }) + } + + @Get('download') + @ApiOperation({ summary: '获取下载链接,不支持下载文件夹' }) + @ApiOkResponse({ type: String }) + @Perm(permissions.DOWNLOAD) + async download(@Query() dto: FileInfoDto): Promise { + return this.manageService.getDownloadLink(`${dto.path}${dto.name}`) + } + + @Post('rename') + @ApiOperation({ summary: '重命名文件或文件夹' }) + @Perm(permissions.RENAME) + async rename(@Body() dto: RenameDto): Promise { + const result = await this.manageService.checkFileExist( + `${dto.path}${dto.toName}${dto.type === 'dir' ? '/' : ''}`, + ) + if (result) + throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST) + + if (dto.type === 'file') + await this.manageService.renameFile(dto.path, dto.name, dto.toName) + else + await this.manageService.renameDir(dto.path, dto.name, dto.toName) + } + + @Post('delete') + @ApiOperation({ summary: '删除文件或文件夹' }) + @Perm(permissions.DELETE) + async delete(@Body() dto: DeleteDto): Promise { + await this.manageService.deleteMultiFileOrDir(dto.files, dto.path) + } + + @Post('cut') + @ApiOperation({ summary: '剪切文件或文件夹,支持批量' }) + @Perm(permissions.CUT) + async cut(@Body() dto: FileOpDto): Promise { + if (dto.originPath === dto.toPath) + throw new BusinessException(ErrorEnum.OSS_NO_OPERATION_REQUIRED) + + await this.manageService.moveMultiFileOrDir( + dto.files, + dto.originPath, + dto.toPath, + ) + } + + @Post('copy') + @ApiOperation({ summary: '复制文件或文件夹,支持批量' }) + @Perm(permissions.COPY) + async copy(@Body() dto: FileOpDto): Promise { + await this.manageService.copyMultiFileOrDir( + dto.files, + dto.originPath, + dto.toPath, + ) + } +} diff --git a/src/modules/netdisk/manager/manage.dto.ts b/src/modules/netdisk/manager/manage.dto.ts new file mode 100644 index 0000000..984ef9b --- /dev/null +++ b/src/modules/netdisk/manager/manage.dto.ts @@ -0,0 +1,162 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' +import { Type } from 'class-transformer' +import { + ArrayMaxSize, + IsNotEmpty, + IsOptional, + IsString, + Matches, + Validate, + ValidateIf, + ValidateNested, + ValidationArguments, + ValidatorConstraint, + ValidatorConstraintInterface, +} from 'class-validator' +import { isEmpty } from 'lodash' + +import { NETDISK_HANDLE_MAX_ITEM } from '~/constants/oss.constant' + +@ValidatorConstraint({ name: 'IsLegalNameExpression', async: false }) +export class IsLegalNameExpression implements ValidatorConstraintInterface { + validate(value: string, args: ValidationArguments) { + try { + if (isEmpty(value)) + throw new Error('dir name is empty') + + if (value.includes('/')) + throw new Error('dir name not allow /') + + return true + } + catch (e) { + return false + } + } + + defaultMessage(_args: ValidationArguments) { + // here you can provide default error message if validation failed + return 'file or dir name invalid' + } +} + +export class FileOpItem { + @ApiProperty({ description: '文件类型', enum: ['file', 'dir'] }) + @IsString() + @Matches(/(^file$)|(^dir$)/) + type: string + + @ApiProperty({ description: '文件名称' }) + @IsString() + @IsNotEmpty() + @Validate(IsLegalNameExpression) + name: string +} + +export class GetFileListDto { + @ApiProperty({ description: '分页标识' }) + @IsOptional() + @IsString() + marker: string + + @ApiProperty({ description: '当前路径' }) + @IsString() + path: string + + @ApiPropertyOptional({ description: '搜索关键字' }) + @Validate(IsLegalNameExpression) + @ValidateIf(o => !isEmpty(o.key)) + @IsString() + key: string +} + +export class MKDirDto { + @ApiProperty({ description: '文件夹名称' }) + @IsNotEmpty() + @IsString() + @Validate(IsLegalNameExpression) + dirName: string + + @ApiProperty({ description: '所属路径' }) + @IsString() + path: string +} + +export class RenameDto { + @ApiProperty({ description: '文件类型' }) + @IsString() + @Matches(/(^file$)|(^dir$)/) + type: string + + @ApiProperty({ description: '更改的名称' }) + @IsString() + @IsNotEmpty() + @Validate(IsLegalNameExpression) + toName: string + + @ApiProperty({ description: '原来的名称' }) + @IsString() + @IsNotEmpty() + @Validate(IsLegalNameExpression) + name: string + + @ApiProperty({ description: '路径' }) + @IsString() + path: string +} + +export class FileInfoDto { + @ApiProperty({ description: '文件名' }) + @IsString() + @IsNotEmpty() + @Validate(IsLegalNameExpression) + name: string + + @ApiProperty({ description: '文件所在路径' }) + @IsString() + path: string +} + +export class DeleteDto { + @ApiProperty({ description: '需要操作的文件或文件夹', type: [FileOpItem] }) + @Type(() => FileOpItem) + @ArrayMaxSize(NETDISK_HANDLE_MAX_ITEM) + @ValidateNested({ each: true }) + files: FileOpItem[] + + @ApiProperty({ description: '所在目录' }) + @IsString() + path: string +} + +export class MarkFileDto { + @ApiProperty({ description: '文件名' }) + @IsString() + @IsNotEmpty() + @Validate(IsLegalNameExpression) + name: string + + @ApiProperty({ description: '文件所在路径' }) + @IsString() + path: string + + @ApiProperty({ description: '备注信息' }) + @IsString() + mark: string +} + +export class FileOpDto { + @ApiProperty({ description: '需要操作的文件或文件夹', type: [FileOpItem] }) + @Type(() => FileOpItem) + @ArrayMaxSize(NETDISK_HANDLE_MAX_ITEM) + @ValidateNested({ each: true }) + files: FileOpItem[] + + @ApiProperty({ description: '操作前的目录' }) + @IsString() + originPath: string + + @ApiProperty({ description: '操作后的目录' }) + @IsString() + toPath: string +} diff --git a/src/modules/netdisk/manager/manage.service.ts b/src/modules/netdisk/manager/manage.service.ts new file mode 100644 index 0000000..3574a65 --- /dev/null +++ b/src/modules/netdisk/manager/manage.service.ts @@ -0,0 +1,930 @@ +import { basename, extname } from 'node:path' + +import { Injectable } from '@nestjs/common' +import { ConfigService } from '@nestjs/config' +import { isEmpty } from 'lodash' +import * as qiniu from 'qiniu' +import { auth, conf, rs } from 'qiniu' + +import { ConfigKeyPaths } from '~/config' +import { NETDISK_COPY_SUFFIX, NETDISK_DELIMITER, NETDISK_HANDLE_MAX_ITEM, NETDISK_LIMIT } from '~/constants/oss.constant' + +import { AccountInfo } from '~/modules/user/user.model' +import { UserService } from '~/modules/user/user.service' + +import { generateRandomValue } from '~/utils' + +import { SFileInfo, SFileInfoDetail, SFileList } from './manage.class' +import { FileOpItem } from './manage.dto' + +@Injectable() +export class NetDiskManageService { + private config: conf.ConfigOptions + private mac: auth.digest.Mac + private bucketManager: rs.BucketManager + + private get qiniuConfig() { + return this.configService.get('oss', { infer: true }) + } + + constructor( + private configService: ConfigService, + private userService: UserService, + ) { + this.mac = new qiniu.auth.digest.Mac( + this.qiniuConfig.accessKey, + this.qiniuConfig.secretKey, + ) + this.config = new qiniu.conf.Config({ + zone: this.qiniuConfig.zone, + }) + // bucket manager + this.bucketManager = new qiniu.rs.BucketManager(this.mac, this.config) + } + + /** + * 获取文件列表 + * @param prefix 当前文件夹路径,搜索模式下会被忽略 + * @param marker 下一页标识 + * @returns iFileListResult + */ + async getFileList(prefix = '', marker = '', skey = ''): Promise { + // 是否需要搜索 + const searching = !isEmpty(skey) + return new Promise((resolve, reject) => { + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: searching ? '' : prefix, + limit: NETDISK_LIMIT, + delimiter: searching ? '' : NETDISK_DELIMITER, + marker, + }, + (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + // 如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候, + // 指定options里面的marker为这个值 + const fileList: SFileInfo[] = [] + // 处理目录,但只有非搜索模式下可用 + if (!searching && !isEmpty(respBody.commonPrefixes)) { + // dir + for (const dirPath of respBody.commonPrefixes) { + const name = (dirPath as string) + .substr(0, dirPath.length - 1) + .replace(prefix, '') + if (isEmpty(skey) || name.includes(skey)) { + fileList.push({ + name: (dirPath as string) + .substr(0, dirPath.length - 1) + .replace(prefix, ''), + type: 'dir', + id: generateRandomValue(10), + }) + } + } + } + // handle items + if (!isEmpty(respBody.items)) { + // file + for (const item of respBody.items) { + // 搜索模式下处理 + if (searching) { + const pathList: string[] = item.key.split(NETDISK_DELIMITER) + // dir is empty stirng, file is key string + const name = pathList.pop() + if ( + item.key.endsWith(NETDISK_DELIMITER) + && pathList[pathList.length - 1].includes(skey) + ) { + // 结果是目录 + const ditName = pathList.pop() + fileList.push({ + id: generateRandomValue(10), + name: ditName, + type: 'dir', + belongTo: pathList.join(NETDISK_DELIMITER), + }) + } + else if (name.includes(skey)) { + // 文件 + fileList.push({ + id: generateRandomValue(10), + name, + type: 'file', + fsize: item.fsize, + mimeType: item.mimeType, + putTime: new Date(Number.parseInt(item.putTime) / 10000), + belongTo: pathList.join(NETDISK_DELIMITER), + }) + } + } + else { + // 正常获取列表 + const fileKey = item.key.replace(prefix, '') as string + if (!isEmpty(fileKey)) { + fileList.push({ + id: generateRandomValue(10), + name: fileKey, + type: 'file', + fsize: item.fsize, + mimeType: item.mimeType, + putTime: new Date(Number.parseInt(item.putTime) / 10000), + }) + } + } + } + } + resolve({ + list: fileList, + marker: respBody.marker || null, + }) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + + /** + * 获取文件信息 + */ + async getFileInfo(name: string, path: string): Promise { + return new Promise((resolve, reject) => { + this.bucketManager.stat( + this.qiniuConfig.bucket, + `${path}${name}`, + (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + const detailInfo: SFileInfoDetail = { + fsize: respBody.fsize, + hash: respBody.hash, + md5: respBody.md5, + mimeType: respBody.mimeType.split('/x-qn-meta')[0], + putTime: new Date(Number.parseInt(respBody.putTime) / 10000), + type: respBody.type, + uploader: '', + mark: respBody?.['x-qn-meta']?.['!mark'] ?? '', + } + if (!respBody.endUser) { + resolve(detailInfo) + } + else { + this.userService + .getAccountInfo(Number.parseInt(respBody.endUser)) + .then((user: AccountInfo) => { + if (isEmpty(user)) { + resolve(detailInfo) + } + else { + detailInfo.uploader = user.username + resolve(detailInfo) + } + }) + } + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + + /** + * 修改文件MimeType + */ + async changeFileHeaders( + name: string, + path: string, + headers: { [k: string]: string }, + ): Promise { + return new Promise((resolve, reject) => { + this.bucketManager.changeHeaders( + this.qiniuConfig.bucket, + `${path}${name}`, + headers, + (err, _, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + + /** + * 创建文件夹 + * @returns true创建成功 + */ + async createDir(dirName: string): Promise { + const safeDirName = dirName.endsWith('/') ? dirName : `${dirName}/` + return new Promise((resolve, reject) => { + // 上传一个空文件以用于显示文件夹效果 + const formUploader = new qiniu.form_up.FormUploader(this.config) + const putExtra = new qiniu.form_up.PutExtra() + formUploader.put( + this.createUploadToken(''), + safeDirName, + ' ', + putExtra, + (respErr, respBody, respInfo) => { + if (respErr) { + reject(respErr) + return + } + if (respInfo.statusCode === 200) { + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + + /** + * 检查文件是否存在,同可检查目录 + */ + async checkFileExist(filePath: string): Promise { + return new Promise((resolve, reject) => { + // fix path end must a / + + // 检测文件夹是否存在 + this.bucketManager.stat( + this.qiniuConfig.bucket, + filePath, + (respErr, respBody, respInfo) => { + if (respErr) { + reject(respErr) + return + } + if (respInfo.statusCode === 200) { + // 文件夹存在 + resolve(true) + } + else if (respInfo.statusCode === 612) { + // 文件夹不存在 + resolve(false) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + + /** + * 创建Upload Token, 默认过期时间一小时 + * @returns upload token + */ + createUploadToken(endUser: string): string { + const policy = new qiniu.rs.PutPolicy({ + scope: this.qiniuConfig.bucket, + insertOnly: 1, + fsizeLimit: 1024 ** 2 * 10, + endUser, + }) + const uploadToken = policy.uploadToken(this.mac) + return uploadToken + } + + /** + * 重命名文件 + * @param dir 文件路径 + * @param name 文件名称 + */ + async renameFile(dir: string, name: string, toName: string): Promise { + const fileName = `${dir}${name}` + const toFileName = `${dir}${toName}` + const op = { + force: true, + } + return new Promise((resolve, reject) => { + this.bucketManager.move( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op, + (err, respBody, respInfo) => { + if (err) { + reject(err) + } + else { + if (respInfo.statusCode === 200) { + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + } + }, + ) + }) + } + + /** + * 移动文件 + */ + async moveFile(dir: string, toDir: string, name: string): Promise { + const fileName = `${dir}${name}` + const toFileName = `${toDir}${name}` + const op = { + force: true, + } + return new Promise((resolve, reject) => { + this.bucketManager.move( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op, + (err, respBody, respInfo) => { + if (err) { + reject(err) + } + else { + if (respInfo.statusCode === 200) { + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + } + }, + ) + }) + } + + /** + * 复制文件 + */ + async copyFile(dir: string, toDir: string, name: string): Promise { + const fileName = `${dir}${name}` + // 拼接文件名 + const ext = extname(name) + const bn = basename(name, ext) + const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}` + const op = { + force: true, + } + return new Promise((resolve, reject) => { + this.bucketManager.copy( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op, + (err, respBody, respInfo) => { + if (err) { + reject(err) + } + else { + if (respInfo.statusCode === 200) { + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + } + }, + ) + }) + } + + /** + * 重命名文件夹 + */ + async renameDir(path: string, name: string, toName: string): Promise { + const dirName = `${path}${name}` + const toDirName = `${path}${toName}` + let hasFile = true + let marker = '' + const op = { + force: true, + } + const bucketName = this.qiniuConfig.bucket + while (hasFile) { + await new Promise((resolve, reject) => { + // 列举当前目录下的所有文件 + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: dirName, + limit: NETDISK_HANDLE_MAX_ITEM, + marker, + }, + (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + const moveOperations = respBody.items.map((item) => { + const { key } = item + const destKey = key.replace(dirName, toDirName) + return qiniu.rs.moveOp( + bucketName, + key, + bucketName, + destKey, + op, + ) + }) + this.bucketManager.batch( + moveOperations, + (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2) + return + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) + hasFile = false + else + marker = respBody.marker + + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}`, + ), + ) + } + }, + ) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + } + + /** + * 获取七牛下载的文件url链接 + * @param key 文件路径 + * @returns 连接 + */ + getDownloadLink(key: string): string { + if (this.qiniuConfig.access === 'public') { + return this.bucketManager.publicDownloadUrl(this.qiniuConfig.domain, key) + } + else if (this.qiniuConfig.access === 'private') { + return this.bucketManager.privateDownloadUrl( + this.qiniuConfig.domain, + key, + Date.now() / 1000 + 36000, + ) + } + throw new Error('qiniu config access type not support') + } + + /** + * 删除文件 + * @param dir 删除的文件夹目录 + * @param name 文件名 + */ + async deleteFile(dir: string, name: string): Promise { + return new Promise((resolve, reject) => { + this.bucketManager.delete( + this.qiniuConfig.bucket, + `${dir}${name}`, + (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + + /** + * 删除文件夹 + * @param dir 文件夹所在的上级目录 + * @param name 文件目录名称 + */ + async deleteMultiFileOrDir( + fileList: FileOpItem[], + dir: string, + ): Promise { + const files = fileList.filter(item => item.type === 'file') + if (files.length > 0) { + // 批处理文件 + const copyOperations = files.map((item) => { + const fileName = `${dir}${item.name}` + return qiniu.rs.deleteOp(this.qiniuConfig.bucket, fileName) + }) + await new Promise((resolve, reject) => { + this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + resolve() + } + else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }) + }) + } + // 处理文件夹 + const dirs = fileList.filter(item => item.type === 'dir') + if (dirs.length > 0) { + // 处理文件夹的复制 + for (let i = 0; i < dirs.length; i++) { + const dirName = `${dir}${dirs[i].name}/` + let hasFile = true + let marker = '' + while (hasFile) { + await new Promise((resolve, reject) => { + // 列举当前目录下的所有文件 + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: dirName, + limit: NETDISK_HANDLE_MAX_ITEM, + marker, + }, + (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + const moveOperations = respBody.items.map((item) => { + const { key } = item + return qiniu.rs.deleteOp(this.qiniuConfig.bucket, key) + }) + this.bucketManager.batch( + moveOperations, + (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2) + return + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) + hasFile = false + else + marker = respBody.marker + + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}`, + ), + ) + } + }, + ) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + } + } + } + + /** + * 复制文件,含文件夹 + */ + async copyMultiFileOrDir( + fileList: FileOpItem[], + dir: string, + toDir: string, + ): Promise { + const files = fileList.filter(item => item.type === 'file') + const op = { + force: true, + } + if (files.length > 0) { + // 批处理文件 + const copyOperations = files.map((item) => { + const fileName = `${dir}${item.name}` + // 拼接文件名 + const ext = extname(item.name) + const bn = basename(item.name, ext) + const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}` + return qiniu.rs.copyOp( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op, + ) + }) + await new Promise((resolve, reject) => { + this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + resolve() + } + else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }) + }) + } + // 处理文件夹 + const dirs = fileList.filter(item => item.type === 'dir') + if (dirs.length > 0) { + // 处理文件夹的复制 + for (let i = 0; i < dirs.length; i++) { + const dirName = `${dir}${dirs[i].name}/` + const copyDirName = `${toDir}${dirs[i].name}${NETDISK_COPY_SUFFIX}/` + let hasFile = true + let marker = '' + while (hasFile) { + await new Promise((resolve, reject) => { + // 列举当前目录下的所有文件 + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: dirName, + limit: NETDISK_HANDLE_MAX_ITEM, + marker, + }, + (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + const moveOperations = respBody.items.map((item) => { + const { key } = item + const destKey = key.replace(dirName, copyDirName) + return qiniu.rs.copyOp( + this.qiniuConfig.bucket, + key, + this.qiniuConfig.bucket, + destKey, + op, + ) + }) + this.bucketManager.batch( + moveOperations, + (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2) + return + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) + hasFile = false + else + marker = respBody.marker + + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}`, + ), + ) + } + }, + ) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + } + } + } + + /** + * 移动文件,含文件夹 + */ + async moveMultiFileOrDir( + fileList: FileOpItem[], + dir: string, + toDir: string, + ): Promise { + const files = fileList.filter(item => item.type === 'file') + const op = { + force: true, + } + if (files.length > 0) { + // 批处理文件 + const copyOperations = files.map((item) => { + const fileName = `${dir}${item.name}` + const toFileName = `${toDir}${item.name}` + return qiniu.rs.moveOp( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op, + ) + }) + await new Promise((resolve, reject) => { + this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + resolve() + } + else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }) + }) + } + // 处理文件夹 + const dirs = fileList.filter(item => item.type === 'dir') + if (dirs.length > 0) { + // 处理文件夹的复制 + for (let i = 0; i < dirs.length; i++) { + const dirName = `${dir}${dirs[i].name}/` + const toDirName = `${toDir}${dirs[i].name}/` + // 移动的目录不是是自己 + if (toDirName.startsWith(dirName)) + continue + + let hasFile = true + let marker = '' + while (hasFile) { + await new Promise((resolve, reject) => { + // 列举当前目录下的所有文件 + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: dirName, + limit: NETDISK_HANDLE_MAX_ITEM, + marker, + }, + (err, respBody, respInfo) => { + if (err) { + reject(err) + return + } + if (respInfo.statusCode === 200) { + const moveOperations = respBody.items.map((item) => { + const { key } = item + const destKey = key.replace(dirName, toDirName) + return qiniu.rs.moveOp( + this.qiniuConfig.bucket, + key, + this.qiniuConfig.bucket, + destKey, + op, + ) + }) + this.bucketManager.batch( + moveOperations, + (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2) + return + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) + hasFile = false + else + marker = respBody.marker + + resolve() + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}`, + ), + ) + } + }, + ) + } + else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, + ), + ) + } + }, + ) + }) + } + } + } + } +} diff --git a/src/modules/netdisk/netdisk.module.ts b/src/modules/netdisk/netdisk.module.ts new file mode 100644 index 0000000..aa5a3f7 --- /dev/null +++ b/src/modules/netdisk/netdisk.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common' + +import { RouterModule } from '@nestjs/core' + +import { UserModule } from '../user/user.module' + +import { NetDiskManageController } from './manager/manage.controller' +import { NetDiskManageService } from './manager/manage.service' +import { NetDiskOverviewController } from './overview/overview.controller' +import { NetDiskOverviewService } from './overview/overview.service' + +@Module({ + imports: [UserModule, RouterModule.register([ + { + path: 'netdisk', + module: NetdiskModule, + }, + ])], + controllers: [NetDiskManageController, NetDiskOverviewController], + providers: [NetDiskManageService, NetDiskOverviewService], +}) +export class NetdiskModule {} diff --git a/src/modules/netdisk/overview/overview.controller.ts b/src/modules/netdisk/overview/overview.controller.ts new file mode 100644 index 0000000..a7b7df9 --- /dev/null +++ b/src/modules/netdisk/overview/overview.controller.ts @@ -0,0 +1,49 @@ +import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager' +import { + Controller, + Get, + UseInterceptors, +} from '@nestjs/common' +import { + ApiOkResponse, + ApiOperation, + ApiTags, +} from '@nestjs/swagger' + +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' + +import { OverviewSpaceInfo } from './overview.dto' +import { NetDiskOverviewService } from './overview.service' + +export const permissions = definePermission('netdisk:overview', { + DESC: 'desc', +} as const) + +@ApiTags('NetDiskOverview - 网盘概览模块') +@Controller('overview') +export class NetDiskOverviewController { + constructor(private overviewService: NetDiskOverviewService) {} + + @Get('desc') + @CacheKey('netdisk_overview_desc') + @CacheTTL(3600) + @UseInterceptors(CacheInterceptor) + @ApiOperation({ summary: '获取网盘空间数据统计' }) + @ApiOkResponse({ type: OverviewSpaceInfo }) + @Perm(permissions.DESC) + async space(): Promise { + const date = this.overviewService.getZeroHourAnd1Day(new Date()) + const hit = await this.overviewService.getHit(date) + const flow = await this.overviewService.getFlow(date) + const space = await this.overviewService.getSpace(date) + const count = await this.overviewService.getCount(date) + return { + fileSize: count.datas[count.datas.length - 1], + flowSize: flow.datas[flow.datas.length - 1], + hitSize: hit.datas[hit.datas.length - 1], + spaceSize: space.datas[space.datas.length - 1], + flowTrend: flow, + sizeTrend: space, + } + } +} diff --git a/src/modules/netdisk/overview/overview.dto.ts b/src/modules/netdisk/overview/overview.dto.ts new file mode 100644 index 0000000..9d77aff --- /dev/null +++ b/src/modules/netdisk/overview/overview.dto.ts @@ -0,0 +1,53 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class SpaceInfo { + @ApiProperty({ description: '当月的X号', type: [Number] }) + times: number[]; + + @ApiProperty({ description: '对应天数的容量, byte单位', type: [Number] }) + datas: number[]; +} + +export class CountInfo { + @ApiProperty({ description: '当月的X号', type: [Number] }) + times: number[]; + + @ApiProperty({ description: '对应天数的文件数量', type: [Number] }) + datas: number[]; +} + +export class FlowInfo { + @ApiProperty({ description: '当月的X号', type: [Number] }) + times: number[]; + + @ApiProperty({ description: '对应天数的耗费流量', type: [Number] }) + datas: number[]; +} + +export class HitInfo { + @ApiProperty({ description: '当月的X号', type: [Number] }) + times: number[]; + + @ApiProperty({ description: '对应天数的Get请求次数', type: [Number] }) + datas: number[]; +} + +export class OverviewSpaceInfo { + @ApiProperty({ description: '当前使用容量' }) + spaceSize: number; + + @ApiProperty({ description: '当前文件数量' }) + fileSize: number; + + @ApiProperty({ description: '当天使用流量' }) + flowSize: number; + + @ApiProperty({ description: '当天请求次数' }) + hitSize: number; + + @ApiProperty({ description: '流量趋势,从当月1号开始计算', type: FlowInfo }) + flowTrend: FlowInfo; + + @ApiProperty({ description: '容量趋势,从当月1号开始计算', type: SpaceInfo }) + sizeTrend: SpaceInfo; +} diff --git a/src/modules/netdisk/overview/overview.service.ts b/src/modules/netdisk/overview/overview.service.ts new file mode 100644 index 0000000..40ff410 --- /dev/null +++ b/src/modules/netdisk/overview/overview.service.ts @@ -0,0 +1,156 @@ +import { HttpService } from '@nestjs/axios' +import { Injectable } from '@nestjs/common' +import { ConfigService } from '@nestjs/config' +import dayjs from 'dayjs' +import * as qiniu from 'qiniu' + +import { ConfigKeyPaths } from '~/config' +import { OSS_API } from '~/constants/oss.constant' + +import { CountInfo, FlowInfo, HitInfo, SpaceInfo } from './overview.dto' + +@Injectable() +export class NetDiskOverviewService { + private mac: qiniu.auth.digest.Mac + private readonly FORMAT = 'YYYYMMDDHHmmss' + private get qiniuConfig() { + return this.configService.get('oss', { infer: true }) + } + + constructor( + private configService: ConfigService, + private readonly httpService: HttpService, + ) { + this.mac = new qiniu.auth.digest.Mac( + this.qiniuConfig.accessKey, + this.qiniuConfig.secretKey, + ) + } + + /** 获取格式化后的起始和结束时间 */ + getStartAndEndDate(start: Date, end = new Date()) { + return [dayjs(start).format(this.FORMAT), dayjs(end).format(this.FORMAT)] + } + + /** + * 获取数据统计接口路径 + * @see: https://developer.qiniu.com/kodo/3906/statistic-interface + */ + getStatisticUrl(type: string, queryParams = {}) { + const defaultParams = { + $bucket: this.qiniuConfig.bucket, + g: 'day', + } + const searchParams = new URLSearchParams({ ...defaultParams, ...queryParams }) + return decodeURIComponent(`${OSS_API}/v6/${type}?${searchParams}`) + } + + /** 获取统计数据 */ + getStatisticData(url: string) { + const accessToken = qiniu.util.generateAccessTokenV2( + this.mac, + url, + 'GET', + 'application/x-www-form-urlencoded', + ) + return this.httpService.axiosRef.get(url, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': `${accessToken}`, + }, + }) + } + + /** + * 获取当天零时 + */ + getZeroHourToDay(current: Date): Date { + const year = dayjs(current).year() + const month = dayjs(current).month() + const date = dayjs(current).date() + return new Date(year, month, date, 0) + } + + /** + * 获取当月1号零时 + */ + getZeroHourAnd1Day(current: Date): Date { + const year = dayjs(current).year() + const month = dayjs(current).month() + return new Date(year, month, 1, 0) + } + + /** + * 该接口可以获取标准存储的当前存储量。可查询当天计量,统计延迟大概 5 分钟。 + * https://developer.qiniu.com/kodo/3908/statistic-space + */ + async getSpace(beginDate: Date, endDate = new Date()): Promise { + const [begin, end] = this.getStartAndEndDate(beginDate, endDate) + const url = this.getStatisticUrl('space', { begin, end }) + const { data } = await this.getStatisticData(url) + return { + datas: data.datas, + times: data.times.map((e) => { + return dayjs.unix(e).date() + }), + } + } + + /** + * 该接口可以获取标准存储的文件数量。可查询当天计量,统计延迟大概 5 分钟。 + * https://developer.qiniu.com/kodo/3914/count + */ + async getCount(beginDate: Date, endDate = new Date()): Promise { + const [begin, end] = this.getStartAndEndDate(beginDate, endDate) + const url = this.getStatisticUrl('count', { begin, end }) + const { data } = await this.getStatisticData(url) + return { + times: data.times.map((e) => { + return dayjs.unix(e).date() + }), + datas: data.datas, + } + } + + /** + * 外网流出流量统计 + * 该接口可以获取外网流出流量、CDN回源流量统计和 GET 请求次数。可查询当天计量,统计延迟大概 5 分钟。 + * https://developer.qiniu.com/kodo/3820/blob-io + */ + async getFlow(beginDate: Date, endDate = new Date()): Promise { + const [begin, end] = this.getStartAndEndDate(beginDate, endDate) + const url = this.getStatisticUrl('blob_io', { begin, end, $ftype: 0, $src: 'origin', select: 'flow' }) + const { data } = await this.getStatisticData(url) + const times = [] + const datas = [] + data.forEach((e) => { + times.push(dayjs(e.time).date()) + datas.push(e.values.flow) + }) + return { + times, + datas, + } + } + + /** + * GET 请求次数统计 + * 该接口可以获取外网流出流量、CDN回源流量统计和 GET 请求次数。可查询当天计量,统计延迟大概 5 分钟。 + * https://developer.qiniu.com/kodo/3820/blob-io + */ + async getHit(beginDate: Date, endDate = new Date()): Promise { + const [begin, end] = this.getStartAndEndDate(beginDate, endDate) + const url = this.getStatisticUrl('blob_io', { begin, end, $ftype: 0, $src: 'inner', select: 'hit' }) + const { data } = await this.getStatisticData(url) + const times = [] + const datas = [] + data.forEach((e) => { + times.push(dayjs(e.time).date()) + datas.push(e.values.hit) + }) + return { + times, + datas, + } + } +} diff --git a/src/modules/sse/sse.controller.ts b/src/modules/sse/sse.controller.ts new file mode 100644 index 0000000..1e475b7 --- /dev/null +++ b/src/modules/sse/sse.controller.ts @@ -0,0 +1,54 @@ +import { BeforeApplicationShutdown, Controller, Param, ParseIntPipe, Req, Res, Sse } from '@nestjs/common' +import { ApiTags } from '@nestjs/swagger' +import { FastifyReply, FastifyRequest } from 'fastify' +import { Observable, interval } from 'rxjs' + +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' + +import { MessageEvent, SseService } from './sse.service' + +@ApiTags('System - sse模块') +@ApiSecurityAuth() +@Controller('sse') +export class SseController implements BeforeApplicationShutdown { + private replyMap: Map = new Map() + + constructor(private readonly sseService: SseService) {} + + private closeAllConnect() { + this.sseService.sendToAll({ + type: 'close', + data: 'bye~', + }) + this.replyMap.forEach((reply) => { + reply.raw.end().destroy() + }) + } + + // 通过控制台关闭程序时触发 + beforeApplicationShutdown() { + // console.log('beforeApplicationShutdown') + this.closeAllConnect() + } + + @Sse(':uid') + sse(@Param('uid', ParseIntPipe) uid: number, @Req() req: FastifyRequest, @Res() res: FastifyReply): Observable { + this.replyMap.set(uid, res) + + const subscription = interval(10000).subscribe(() => { + this.sseService.sendToClient(uid, { type: 'ping' }) + }) + + // 当客户端断开连接时 + req.raw.on('close', () => { + subscription.unsubscribe() + this.sseService.removeClient(uid) + this.replyMap.delete(uid) + // console.log(`user-${uid}已关闭`) + }) + + return new Observable((subscriber) => { + this.sseService.addClient(uid, subscriber) + }) + } +} diff --git a/src/modules/sse/sse.module.ts b/src/modules/sse/sse.module.ts new file mode 100644 index 0000000..e6af2b1 --- /dev/null +++ b/src/modules/sse/sse.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common' + +import { SseController } from './sse.controller' +import { SseService } from './sse.service' + +@Module({ + imports: [], + controllers: [SseController], + providers: [SseService], + exports: [SseService], +}) +export class SseModule {} diff --git a/src/modules/sse/sse.service.ts b/src/modules/sse/sse.service.ts new file mode 100644 index 0000000..076e415 --- /dev/null +++ b/src/modules/sse/sse.service.ts @@ -0,0 +1,85 @@ +import { Injectable } from '@nestjs/common' +import { Subscriber } from 'rxjs' +import { In } from 'typeorm' + +import { ROOT_ROLE_ID } from '~/constants/system.constant' + +import { RoleEntity } from '~/modules/system/role/role.entity' +import { UserEntity } from '~/modules/user/user.entity' + +export interface MessageEvent { + data?: string | object + id?: string + type?: 'ping' | 'close' | 'updatePermsAndMenus' + retry?: number +} + +const clientMap: Map> = new Map() + +@Injectable() +export class SseService { + addClient(uid: number, subscriber: Subscriber) { + clientMap.set(uid, subscriber) + } + + removeClient(uid: number): void { + const client = clientMap.get(uid) + client?.complete() + clientMap.delete(uid) + } + + sendToClient(uid: number, data: MessageEvent): void { + const client = clientMap.get(uid) + client?.next?.(data) + } + + sendToAll(data: MessageEvent): void { + clientMap.forEach((client) => { + client.next(data) + }) + } + + /** + * 通知前端重新获取权限菜单 + * @param uid + * @constructor + */ + async noticeClientToUpdateMenusByUserIds(uid: number | number[]) { + const userIds = [].concat(uid) as number[] + userIds.forEach((uid) => { + this.sendToClient(uid, { type: 'updatePermsAndMenus' }) + }) + } + + /** + * 通过menuIds通知用户更新权限菜单 + */ + async noticeClientToUpdateMenusByMenuIds(menuIds: number[]): Promise { + const roleMenus = await RoleEntity.find({ + where: { + menus: { + id: In(menuIds), + }, + }, + }) + const roleIds = roleMenus.map(n => n.id).concat(ROOT_ROLE_ID) + await this.noticeClientToUpdateMenusByRoleIds(roleIds) + } + + /** + * 通过roleIds通知用户更新权限菜单 + */ + async noticeClientToUpdateMenusByRoleIds(roleIds: number[]): Promise { + const users = await UserEntity.find({ + where: { + roles: { + id: In(roleIds), + }, + }, + }) + if (users) { + const userIds = users.map(n => n.id) + await this.noticeClientToUpdateMenusByUserIds(userIds) + } + } +} diff --git a/src/modules/system/dept/dept.controller.ts b/src/modules/system/dept/dept.controller.ts new file mode 100644 index 0000000..81b6ef9 --- /dev/null +++ b/src/modules/system/dept/dept.controller.ts @@ -0,0 +1,83 @@ +import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { DeptEntity } from '~/modules/system/dept/dept.entity' + +import { DeptDto, DeptQueryDto } from './dept.dto' +import { DeptService } from './dept.service' + +export const permissions = definePermission('system:dept', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', +} as const) + +@ApiSecurityAuth() +@ApiTags('System - 部门模块') +@Controller('depts') +export class DeptController { + constructor(private deptService: DeptService) {} + + @Get() + @ApiOperation({ summary: '获取部门列表' }) + @ApiResult({ type: [DeptEntity] }) + @Perm(permissions.LIST) + async list( + @Query() dto: DeptQueryDto, @AuthUser('uid') uid: number): Promise { + return this.deptService.getDeptTree(uid, dto) + } + + @Post() + @ApiOperation({ summary: '创建部门' }) + @Perm(permissions.CREATE) + async create(@Body() dto: DeptDto): Promise { + await this.deptService.create(dto) + } + + @Get(':id') + @ApiOperation({ summary: '查询部门信息' }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.deptService.info(id) + } + + @Put(':id') + @ApiOperation({ summary: '更新部门' }) + @Perm(permissions.UPDATE) + async update( + @IdParam() id: number, @Body() updateDeptDto: DeptDto): Promise { + await this.deptService.update(id, updateDeptDto) + } + + @Delete(':id') + @ApiOperation({ summary: '删除部门' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + // 查询是否有关联用户或者部门,如果含有则无法删除 + const count = await this.deptService.countUserByDeptId(id) + if (count > 0) + throw new BusinessException(ErrorEnum.DEPARTMENT_HAS_ASSOCIATED_USERS) + + const count2 = await this.deptService.countChildDept(id) + console.log('count2', count2) + if (count2 > 0) + throw new BusinessException(ErrorEnum.DEPARTMENT_HAS_CHILD_DEPARTMENTS) + + await this.deptService.delete(id) + } + + // @Post('move') + // @ApiOperation({ summary: '部门移动排序' }) + // async move(@Body() dto: MoveDeptDto): Promise { + // await this.deptService.move(dto.depts); + // } +} diff --git a/src/modules/system/dept/dept.dto.ts b/src/modules/system/dept/dept.dto.ts new file mode 100644 index 0000000..5d79782 --- /dev/null +++ b/src/modules/system/dept/dept.dto.ts @@ -0,0 +1,70 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Type } from 'class-transformer' +import { + ArrayNotEmpty, + IsArray, + IsInt, + IsOptional, + IsString, + Min, + MinLength, + ValidateNested, +} from 'class-validator' + +export class DeptDto { + @ApiProperty({ description: '部门名称' }) + @IsString() + @MinLength(1) + name: string + + @ApiProperty({ description: '父级部门id' }) + @Type(() => Number) + @IsInt() + @IsOptional() + parentId: number + + @ApiProperty({ description: '排序编号', required: false }) + @IsInt() + @Min(0) + @IsOptional() + orderNo: number +} + +export class TransferDeptDto { + @ApiProperty({ description: '需要转移的管理员列表编号', type: [Number] }) + @IsArray() + @ArrayNotEmpty() + userIds: number[] + + @ApiProperty({ description: '需要转移过去的系统部门ID' }) + @IsInt() + @Min(0) + deptId: number +} + +export class MoveDept { + @ApiProperty({ description: '当前部门ID' }) + @IsInt() + @Min(0) + id: number + + @ApiProperty({ description: '移动到指定父级部门的ID' }) + @IsInt() + @Min(0) + @IsOptional() + parentId: number +} + +export class MoveDeptDto { + @ApiProperty({ description: '部门列表', type: [MoveDept] }) + @ValidateNested({ each: true }) + @Type(() => MoveDept) + depts: MoveDept[] +} + +export class DeptQueryDto { + @ApiProperty({ description: '部门名称' }) + @IsString() + @IsOptional() + name?: string +} diff --git a/src/modules/system/dept/dept.entity.ts b/src/modules/system/dept/dept.entity.ts new file mode 100644 index 0000000..3a69396 --- /dev/null +++ b/src/modules/system/dept/dept.entity.ts @@ -0,0 +1,36 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger' +import { + Column, + Entity, + OneToMany, + Relation, + Tree, + TreeChildren, + TreeParent, +} from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +import { UserEntity } from '../../user/user.entity' + +@Entity({ name: 'sys_dept' }) +@Tree('materialized-path') +export class DeptEntity extends CommonEntity { + @Column() + @ApiProperty({ description: '部门名称' }) + name: string + + @Column({ nullable: true, default: 0 }) + @ApiProperty({ description: '排序' }) + orderNo: number + + @TreeChildren({ cascade: true }) + children: DeptEntity[] + + @TreeParent({ onDelete: 'SET NULL' }) + parent?: DeptEntity + + @ApiHideProperty() + @OneToMany(() => UserEntity, user => user.dept) + users: Relation +} diff --git a/src/modules/system/dept/dept.module.ts b/src/modules/system/dept/dept.module.ts new file mode 100644 index 0000000..d936ed9 --- /dev/null +++ b/src/modules/system/dept/dept.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { UserModule } from '../../user/user.module' +import { RoleModule } from '../role/role.module' + +import { DeptController } from './dept.controller' +import { DeptEntity } from './dept.entity' +import { DeptService } from './dept.service' + +const services = [DeptService] + +@Module({ + imports: [TypeOrmModule.forFeature([DeptEntity]), UserModule, RoleModule], + controllers: [DeptController], + providers: [...services], + exports: [TypeOrmModule, ...services], +}) +export class DeptModule {} diff --git a/src/modules/system/dept/dept.service.ts b/src/modules/system/dept/dept.service.ts new file mode 100644 index 0000000..1eae39b --- /dev/null +++ b/src/modules/system/dept/dept.service.ts @@ -0,0 +1,134 @@ +import { Injectable } from '@nestjs/common' +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm' +import { isEmpty } from 'lodash' +import { EntityManager, Repository, TreeRepository } from 'typeorm' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { DeptEntity } from '~/modules/system/dept/dept.entity' +import { UserEntity } from '~/modules/user/user.entity' + +import { deleteEmptyChildren } from '~/utils/list2tree.util' + +import { DeptDto, DeptQueryDto, MoveDept } from './dept.dto' + +@Injectable() +export class DeptService { + constructor( + @InjectRepository(UserEntity) + private userRepository: Repository, + @InjectRepository(DeptEntity) + private deptRepository: TreeRepository, + @InjectEntityManager() private entityManager: EntityManager, + ) {} + + async list(): Promise { + return this.deptRepository.find({ order: { orderNo: 'DESC' } }) + } + + async info(id: number): Promise { + const dept = await this.deptRepository + .createQueryBuilder('dept') + .leftJoinAndSelect('dept.parent', 'parent') + .where({ id }) + .getOne() + + if (isEmpty(dept)) + throw new BusinessException(ErrorEnum.DEPARTMENT_NOT_FOUND) + + return dept + } + + async create({ parentId, ...data }: DeptDto): Promise { + const parent = await this.deptRepository + .createQueryBuilder('dept') + .where({ id: parentId }) + .getOne() + + await this.deptRepository.save({ + ...data, + parent, + }) + } + + async update(id: number, { parentId, ...data }: DeptDto): Promise { + const item = await this.deptRepository + .createQueryBuilder('dept') + .where({ id }) + .getOne() + + const parent = await this.deptRepository + .createQueryBuilder('dept') + .where({ id: parentId }) + .getOne() + + await this.deptRepository.save({ + ...item, + ...data, + parent, + }) + } + + async delete(id: number): Promise { + await this.deptRepository.delete(id) + } + + /** + * 移动排序 + */ + async move(depts: MoveDept[]): Promise { + await this.entityManager.transaction(async (manager) => { + await manager.save(depts) + }) + } + + /** + * 根据部门查询关联的用户数量 + */ + async countUserByDeptId(id: number): Promise { + return this.userRepository.countBy({ dept: { id } }) + } + + /** + * 查找当前部门下的子部门数量 + */ + async countChildDept(id: number): Promise { + const item = await this.deptRepository.findOneBy({ id }) + return (await this.deptRepository.countDescendants(item)) - 1 + } + + /** + * 获取部门列表树结构 + */ + async getDeptTree( + uid: number, + { name }: DeptQueryDto, + ): Promise { + const tree: DeptEntity[] = [] + + if (name) { + const deptList = await this.deptRepository + .createQueryBuilder('dept') + .where('dept.name like :name', { name: `%${name}%` }) + .getMany() + + for (const dept of deptList) { + const deptTree = await this.deptRepository.findDescendantsTree(dept) + tree.push(deptTree) + } + + deleteEmptyChildren(tree) + + return tree + } + + const deptTree = await this.deptRepository.findTrees({ + depth: 2, + relations: ['parent'], + }) + + deleteEmptyChildren(deptTree) + + return deptTree + } +} diff --git a/src/modules/system/dict-item/dict-item.controller.ts b/src/modules/system/dict-item/dict-item.controller.ts new file mode 100644 index 0000000..6906aa6 --- /dev/null +++ b/src/modules/system/dict-item/dict-item.controller.ts @@ -0,0 +1,68 @@ +import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { Pagination } from '~/helper/paginate/pagination' +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity' + +import { DictItemDto, DictItemQueryDto } from './dict-item.dto' +import { DictItemService } from './dict-item.service' + +export const permissions = definePermission('system:dict-item', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', +} as const) + +@ApiTags('System - 字典项模块') +@ApiSecurityAuth() +@Controller('dict-item') +export class DictItemController { + constructor(private dictItemService: DictItemService) {} + + @Get() + @ApiOperation({ summary: '获取字典项列表' }) + @ApiResult({ type: [DictItemEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: DictItemQueryDto): Promise> { + return this.dictItemService.page(dto) + } + + @Post() + @ApiOperation({ summary: '新增字典项' }) + @Perm(permissions.CREATE) + async create(@Body() dto: DictItemDto, @AuthUser() user: IAuthUser): Promise { + await this.dictItemService.isExistKey(dto) + dto.createBy = dto.updateBy = user.uid + await this.dictItemService.create(dto) + } + + @Get(':id') + @ApiOperation({ summary: '查询字典项信息' }) + @ApiResult({ type: DictItemEntity }) + @Perm(permissions.READ) + async info(@IdParam() id: number): Promise { + return this.dictItemService.findOne(id) + } + + @Post(':id') + @ApiOperation({ summary: '更新字典项' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: DictItemDto, @AuthUser() user: IAuthUser): Promise { + dto.updateBy = user.uid + await this.dictItemService.update(id, dto) + } + + @Delete(':id') + @ApiOperation({ summary: '删除指定的字典项' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.dictItemService.delete(id) + } +} diff --git a/src/modules/system/dict-item/dict-item.dto.ts b/src/modules/system/dict-item/dict-item.dto.ts new file mode 100644 index 0000000..fa5cf65 --- /dev/null +++ b/src/modules/system/dict-item/dict-item.dto.ts @@ -0,0 +1,48 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger' +import { IsInt, IsOptional, IsString, MinLength } from 'class-validator' + +import { PagerDto } from '~/common/dto/pager.dto' + +import { DictItemEntity } from './dict-item.entity' + +export class DictItemDto extends PartialType(DictItemEntity) { + @ApiProperty({ description: '字典类型 ID' }) + @IsInt() + typeId: number + + @ApiProperty({ description: '字典项键名' }) + @IsString() + @MinLength(1) + label: string + + @ApiProperty({ description: '字典项值' }) + @IsString() + @MinLength(1) + value: string + + @ApiProperty({ description: '状态' }) + @IsOptional() + @IsInt() + status?: number + + @ApiProperty({ description: '备注' }) + @IsOptional() + @IsString() + remark?: string +} + +export class DictItemQueryDto extends PagerDto { + @ApiProperty({ description: '字典类型 ID', required: true }) + @IsInt() + typeId: number + + @ApiProperty({ description: '字典项键名' }) + @IsString() + @IsOptional() + label?: string + + @ApiProperty({ description: '字典项值' }) + @IsString() + @IsOptional() + value?: string +} diff --git a/src/modules/system/dict-item/dict-item.entity.ts b/src/modules/system/dict-item/dict-item.entity.ts new file mode 100644 index 0000000..d4885a8 --- /dev/null +++ b/src/modules/system/dict-item/dict-item.entity.ts @@ -0,0 +1,32 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm' + +import { CompleteEntity } from '~/common/entity/common.entity' + +import { DictTypeEntity } from '../dict-type/dict-type.entity' + +@Entity({ name: 'sys_dict_item' }) +export class DictItemEntity extends CompleteEntity { + @ManyToOne(() => DictTypeEntity, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'type_id' }) + type: DictTypeEntity + + @Column({ type: 'varchar', length: 50 }) + @ApiProperty({ description: '字典项键名' }) + label: string + + @Column({ type: 'varchar', length: 50 }) + @ApiProperty({ description: '字典项值' }) + value: string + + @Column({ nullable: true, comment: '字典项排序' }) + orderNo: number + + @Column({ type: 'tinyint', default: 1 }) + @ApiProperty({ description: ' 状态' }) + status: number + + @Column({ type: 'varchar', nullable: true }) + @ApiProperty({ description: '备注' }) + remark: string +} diff --git a/src/modules/system/dict-item/dict-item.module.ts b/src/modules/system/dict-item/dict-item.module.ts new file mode 100644 index 0000000..e5ee832 --- /dev/null +++ b/src/modules/system/dict-item/dict-item.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { DictItemController } from './dict-item.controller' +import { DictItemEntity } from './dict-item.entity' +import { DictItemService } from './dict-item.service' + +const services = [DictItemService] + +@Module({ + imports: [TypeOrmModule.forFeature([DictItemEntity])], + controllers: [DictItemController], + providers: [...services], + exports: [TypeOrmModule, ...services], +}) +export class DictItemModule {} diff --git a/src/modules/system/dict-item/dict-item.service.ts b/src/modules/system/dict-item/dict-item.service.ts new file mode 100644 index 0000000..ae5f0a0 --- /dev/null +++ b/src/modules/system/dict-item/dict-item.service.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' + +import { Like, Repository } from 'typeorm' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { paginate } from '~/helper/paginate' +import { Pagination } from '~/helper/paginate/pagination' +import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity' + +import { DictItemDto, DictItemQueryDto } from './dict-item.dto' + +@Injectable() +export class DictItemService { + constructor( + @InjectRepository(DictItemEntity) + private dictItemRepository: Repository, + ) {} + + /** + * 罗列所有配置 + */ + async page({ + page, + pageSize, + label, + value, + typeId, + }: DictItemQueryDto): Promise> { + const queryBuilder = this.dictItemRepository.createQueryBuilder('dict_item') + .orderBy({ orderNo: 'ASC' }) + .where({ + ...(label && { label: Like(`%${label}%`) }), + ...(value && { value: Like(`%${value}%`) }), + type: { + id: typeId, + }, + }) + + return paginate(queryBuilder, { page, pageSize }) + } + + /** + * 获取参数总数 + */ + async countConfigList(): Promise { + return this.dictItemRepository.count() + } + + /** + * 新增 + */ + async create(dto: DictItemDto): Promise { + const { typeId, ...rest } = dto + await this.dictItemRepository.insert({ + ...rest, + type: { + id: typeId, + }, + }) + } + + /** + * 更新 + */ + async update(id: number, dto: Partial): Promise { + const { typeId, ...rest } = dto + await this.dictItemRepository.update(id, { + ...rest, + type: { + id: typeId, + }, + }) + } + + /** + * 删除 + */ + async delete(id: number): Promise { + await this.dictItemRepository.delete(id) + } + + /** + * 查询单个 + */ + async findOne(id: number): Promise { + return this.dictItemRepository.findOneBy({ id }) + } + + async isExistKey(dto: DictItemDto): Promise { + const { value, typeId } = dto + const result = await this.dictItemRepository.findOneBy({ value, type: { id: typeId } }) + if (result) + throw new BusinessException(ErrorEnum.DICT_NAME_EXISTS) + } +} diff --git a/src/modules/system/dict-type/dict-type.controller.ts b/src/modules/system/dict-type/dict-type.controller.ts new file mode 100644 index 0000000..652a94b --- /dev/null +++ b/src/modules/system/dict-type/dict-type.controller.ts @@ -0,0 +1,76 @@ +import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { Pagination } from '~/helper/paginate/pagination' +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity' + +import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto' +import { DictTypeService } from './dict-type.service' + +export const permissions = definePermission('system:dict-type', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', +} as const) + +@ApiTags('System - 字典类型模块') +@ApiSecurityAuth() +@Controller('dict-type') +export class DictTypeController { + constructor(private dictTypeService: DictTypeService) {} + + @Get() + @ApiOperation({ summary: '获取字典类型列表' }) + @ApiResult({ type: [DictTypeEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: DictTypeQueryDto): Promise> { + return this.dictTypeService.page(dto) + } + + @Get('select-options') + @ApiOperation({ summary: '一次性获取所有的字典类型(不分页)' }) + @ApiResult({ type: [DictTypeEntity] }) + @Perm(permissions.LIST) + async getAll(): Promise { + return this.dictTypeService.getAll() + } + + @Post() + @ApiOperation({ summary: '新增字典类型' }) + @Perm(permissions.CREATE) + async create(@Body() dto: DictTypeDto, @AuthUser() user: IAuthUser): Promise { + await this.dictTypeService.isExistKey(dto.name) + dto.createBy = dto.updateBy = user.uid + await this.dictTypeService.create(dto) + } + + @Get(':id') + @ApiOperation({ summary: '查询字典类型信息' }) + @ApiResult({ type: DictTypeEntity }) + @Perm(permissions.READ) + async info(@IdParam() id: number): Promise { + return this.dictTypeService.findOne(id) + } + + @Post(':id') + @ApiOperation({ summary: '更新字典类型' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: DictTypeDto, @AuthUser() user: IAuthUser): Promise { + dto.updateBy = user.uid + await this.dictTypeService.update(id, dto) + } + + @Delete(':id') + @ApiOperation({ summary: '删除指定的字典类型' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.dictTypeService.delete(id) + } +} diff --git a/src/modules/system/dict-type/dict-type.dto.ts b/src/modules/system/dict-type/dict-type.dto.ts new file mode 100644 index 0000000..c3809b4 --- /dev/null +++ b/src/modules/system/dict-type/dict-type.dto.ts @@ -0,0 +1,40 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger' +import { IsInt, IsOptional, IsString, MinLength } from 'class-validator' + +import { PagerDto } from '~/common/dto/pager.dto' + +import { DictTypeEntity } from './dict-type.entity' + +export class DictTypeDto extends PartialType(DictTypeEntity) { + @ApiProperty({ description: '字典类型名称' }) + @IsString() + @MinLength(1) + name: string + + @ApiProperty({ description: '字典类型code' }) + @IsString() + @MinLength(3) + code: string + + @ApiProperty({ description: '状态' }) + @IsOptional() + @IsInt() + status?: number + + @ApiProperty({ description: '备注' }) + @IsOptional() + @IsString() + remark?: string +} + +export class DictTypeQueryDto extends PagerDto { + @ApiProperty({ description: '字典类型名称' }) + @IsString() + @IsOptional() + name: string + + @ApiProperty({ description: '字典类型code' }) + @IsString() + @IsOptional() + code: string +} diff --git a/src/modules/system/dict-type/dict-type.entity.ts b/src/modules/system/dict-type/dict-type.entity.ts new file mode 100644 index 0000000..8d2608f --- /dev/null +++ b/src/modules/system/dict-type/dict-type.entity.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity } from 'typeorm' + +import { CompleteEntity } from '~/common/entity/common.entity' + +@Entity({ name: 'sys_dict_type' }) +export class DictTypeEntity extends CompleteEntity { + @Column({ type: 'varchar', length: 50 }) + @ApiProperty({ description: '字典名称' }) + name: string + + @Column({ type: 'varchar', length: 50, unique: true }) + @ApiProperty({ description: '字典类型' }) + code: string + + @Column({ type: 'tinyint', default: 1 }) + @ApiProperty({ description: ' 状态' }) + status: number + + @Column({ type: 'varchar', nullable: true }) + @ApiProperty({ description: '备注' }) + remark: string +} diff --git a/src/modules/system/dict-type/dict-type.module.ts b/src/modules/system/dict-type/dict-type.module.ts new file mode 100644 index 0000000..5f8f238 --- /dev/null +++ b/src/modules/system/dict-type/dict-type.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { DictTypeController } from './dict-type.controller' +import { DictTypeEntity } from './dict-type.entity' +import { DictTypeService } from './dict-type.service' + +const services = [DictTypeService] + +@Module({ + imports: [TypeOrmModule.forFeature([DictTypeEntity])], + controllers: [DictTypeController], + providers: [...services], + exports: [TypeOrmModule, ...services], +}) +export class DictTypeModule {} diff --git a/src/modules/system/dict-type/dict-type.service.ts b/src/modules/system/dict-type/dict-type.service.ts new file mode 100644 index 0000000..04e6c47 --- /dev/null +++ b/src/modules/system/dict-type/dict-type.service.ts @@ -0,0 +1,84 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' + +import { Like, Repository } from 'typeorm' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { paginate } from '~/helper/paginate' +import { Pagination } from '~/helper/paginate/pagination' +import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity' + +import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto' + +@Injectable() +export class DictTypeService { + constructor( + @InjectRepository(DictTypeEntity) + private dictTypeRepository: Repository, + ) {} + + /** + * 罗列所有配置 + */ + async page({ + page, + pageSize, + name, + code, + }: DictTypeQueryDto): Promise> { + const queryBuilder = this.dictTypeRepository.createQueryBuilder('dict_type') + .where({ + ...(name && { name: Like(`%${name}%`) }), + ...(code && { code: Like(`%${code}%`) }), + }) + + return paginate(queryBuilder, { page, pageSize }) + } + + /** 一次性获取所有的字典类型 */ + async getAll() { + return this.dictTypeRepository.find() + } + + /** + * 获取参数总数 + */ + async countConfigList(): Promise { + return this.dictTypeRepository.count() + } + + /** + * 新增 + */ + async create(dto: DictTypeDto): Promise { + await this.dictTypeRepository.insert(dto) + } + + /** + * 更新 + */ + async update(id: number, dto: Partial): Promise { + await this.dictTypeRepository.update(id, dto) + } + + /** + * 删除 + */ + async delete(id: number): Promise { + await this.dictTypeRepository.delete(id) + } + + /** + * 查询单个 + */ + async findOne(id: number): Promise { + return this.dictTypeRepository.findOneBy({ id }) + } + + async isExistKey(name: string): Promise { + const result = await this.dictTypeRepository.findOneBy({ name }) + if (result) + throw new BusinessException(ErrorEnum.DICT_NAME_EXISTS) + } +} diff --git a/src/modules/system/log/dto/log.dto.ts b/src/modules/system/log/dto/log.dto.ts new file mode 100644 index 0000000..5cf0ff3 --- /dev/null +++ b/src/modules/system/log/dto/log.dto.ts @@ -0,0 +1,57 @@ +import { ApiProperty } from '@nestjs/swagger' +import { IsOptional, IsString } from 'class-validator' + +import { PagerDto } from '~/common/dto/pager.dto' + +export class LoginLogQueryDto extends PagerDto { + @ApiProperty({ description: '用户名' }) + @IsString() + @IsOptional() + username: string + + @ApiProperty({ description: '登录IP' }) + @IsOptional() + @IsString() + ip?: string + + @ApiProperty({ description: '登录地点' }) + @IsOptional() + @IsString() + address?: string + + @ApiProperty({ description: '登录时间' }) + @IsOptional() + time?: string[] +} + +export class TaskLogQueryDto extends PagerDto { + @ApiProperty({ description: '用户名' }) + @IsOptional() + @IsString() + username: string + + @ApiProperty({ description: '登录IP' }) + @IsString() + @IsOptional() + ip?: string + + @ApiProperty({ description: '登录时间' }) + @IsOptional() + time?: string[] +} + +export class CaptchaLogQueryDto extends PagerDto { + @ApiProperty({ description: '用户名' }) + @IsOptional() + @IsString() + username: string + + @ApiProperty({ description: '验证码' }) + @IsString() + @IsOptional() + code?: string + + @ApiProperty({ description: '发送时间' }) + @IsOptional() + time?: string[] +} diff --git a/src/modules/system/log/entities/captcha-log.entity.ts b/src/modules/system/log/entities/captcha-log.entity.ts new file mode 100644 index 0000000..048bec8 --- /dev/null +++ b/src/modules/system/log/entities/captcha-log.entity.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +@Entity({ name: 'sys_captcha_log' }) +export class CaptchaLogEntity extends CommonEntity { + @Column({ name: 'user_id', nullable: true }) + @ApiProperty({ description: '用户ID' }) + userId: number + + @Column({ nullable: true }) + @ApiProperty({ description: '账号' }) + account: string + + @Column({ nullable: true }) + @ApiProperty({ description: '验证码' }) + code: string + + @Column({ nullable: true }) + @ApiProperty({ description: '验证码提供方' }) + provider: 'sms' | 'email' +} diff --git a/src/modules/system/log/entities/index.ts b/src/modules/system/log/entities/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/system/log/entities/login-log.entity.ts b/src/modules/system/log/entities/login-log.entity.ts new file mode 100644 index 0000000..7a56568 --- /dev/null +++ b/src/modules/system/log/entities/login-log.entity.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +import { UserEntity } from '../../../user/user.entity' + +@Entity({ name: 'sys_login_log' }) +export class LoginLogEntity extends CommonEntity { + @Column({ nullable: true }) + @ApiProperty({ description: 'IP' }) + ip: string + + @Column({ nullable: true }) + @ApiProperty({ description: '地址' }) + address: string + + @Column({ nullable: true }) + @ApiProperty({ description: '登录方式' }) + provider: string + + @Column({ length: 500, nullable: true }) + @ApiProperty({ description: '浏览器ua' }) + ua: string + + @ManyToOne(() => UserEntity, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'user_id' }) + user: Relation +} diff --git a/src/modules/system/log/entities/task-log.entity.ts b/src/modules/system/log/entities/task-log.entity.ts new file mode 100644 index 0000000..1721d68 --- /dev/null +++ b/src/modules/system/log/entities/task-log.entity.ts @@ -0,0 +1,25 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +import { TaskEntity } from '../../task/task.entity' + +@Entity({ name: 'sys_task_log' }) +export class TaskLogEntity extends CommonEntity { + @Column({ type: 'tinyint', default: 0 }) + @ApiProperty({ description: '任务状态:0失败,1成功' }) + status: number + + @Column({ type: 'text', nullable: true }) + @ApiProperty({ description: '任务日志信息' }) + detail: string + + @Column({ type: 'int', nullable: true, name: 'consume_time', default: 0 }) + @ApiProperty({ description: '任务耗时' }) + consumeTime: number + + @ManyToOne(() => TaskEntity) + @JoinColumn({ name: 'task_id' }) + task: Relation +} diff --git a/src/modules/system/log/log.controller.ts b/src/modules/system/log/log.controller.ts new file mode 100644 index 0000000..489f3cb --- /dev/null +++ b/src/modules/system/log/log.controller.ts @@ -0,0 +1,64 @@ +import { Controller, Get, Query } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { Pagination } from '~/helper/paginate/pagination' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' + +import { + CaptchaLogQueryDto, + LoginLogQueryDto, + TaskLogQueryDto, +} from './dto/log.dto' +import { CaptchaLogEntity } from './entities/captcha-log.entity' +import { TaskLogEntity } from './entities/task-log.entity' +import { LoginLogInfo } from './models/log.model' +import { CaptchaLogService } from './services/captcha-log.service' +import { LoginLogService } from './services/login-log.service' +import { TaskLogService } from './services/task-log.service' + +export const permissions = definePermission('system:log', { + TaskList: 'task:list', + LogList: 'login:list', + CaptchaList: 'captcha:list', +} as const) + +@ApiSecurityAuth() +@ApiTags('System - 日志模块') +@Controller('log') +export class LogController { + constructor( + private loginLogService: LoginLogService, + private taskService: TaskLogService, + private captchaLogService: CaptchaLogService, + ) {} + + @Get('login/list') + @ApiOperation({ summary: '查询登录日志列表' }) + @ApiResult({ type: [LoginLogInfo], isPage: true }) + @Perm(permissions.TaskList) + async loginLogPage( + @Query() dto: LoginLogQueryDto, + ): Promise> { + return this.loginLogService.list(dto) + } + + @Get('task/list') + @ApiOperation({ summary: '查询任务日志列表' }) + @ApiResult({ type: [TaskLogEntity], isPage: true }) + @Perm(permissions.LogList) + async taskList(@Query() dto: TaskLogQueryDto) { + return this.taskService.list(dto) + } + + @Get('captcha/list') + @ApiOperation({ summary: '查询验证码日志列表' }) + @ApiResult({ type: [CaptchaLogEntity], isPage: true }) + @Perm(permissions.CaptchaList) + async captchaList( + @Query() dto: CaptchaLogQueryDto, + ): Promise> { + return this.captchaLogService.paginate(dto) + } +} diff --git a/src/modules/system/log/log.module.ts b/src/modules/system/log/log.module.ts new file mode 100644 index 0000000..27f9f04 --- /dev/null +++ b/src/modules/system/log/log.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { UserModule } from '../../user/user.module' + +import { CaptchaLogEntity } from './entities/captcha-log.entity' +import { LoginLogEntity } from './entities/login-log.entity' +import { TaskLogEntity } from './entities/task-log.entity' +import { LogController } from './log.controller' +import { CaptchaLogService } from './services/captcha-log.service' +import { LoginLogService } from './services/login-log.service' +import { TaskLogService } from './services/task-log.service' + +const providers = [LoginLogService, TaskLogService, CaptchaLogService] + +@Module({ + imports: [ + TypeOrmModule.forFeature([LoginLogEntity, CaptchaLogEntity, TaskLogEntity]), + UserModule, + ], + controllers: [LogController], + providers: [...providers], + exports: [TypeOrmModule, ...providers], +}) +export class LogModule {} diff --git a/src/modules/system/log/models/log.model.ts b/src/modules/system/log/models/log.model.ts new file mode 100644 index 0000000..f128be0 --- /dev/null +++ b/src/modules/system/log/models/log.model.ts @@ -0,0 +1,47 @@ +import { ApiProperty } from '@nestjs/swagger' + +export class LoginLogInfo { + @ApiProperty({ description: '日志编号' }) + id: number + + @ApiProperty({ description: '登录ip', example: '1.1.1.1' }) + ip: string + + @ApiProperty({ description: '登录地址' }) + address: string + + @ApiProperty({ description: '系统', example: 'Windows 10' }) + os: string + + @ApiProperty({ description: '浏览器', example: 'Chrome' }) + browser: string + + @ApiProperty({ description: '登录用户名', example: 'admin' }) + username: string + + @ApiProperty({ description: '登录时间', example: '2023-12-22 16:46:20.333843' }) + time: string +} + +export class TaskLogInfo { + @ApiProperty({ description: '日志编号' }) + id: number + + @ApiProperty({ description: '任务编号' }) + taskId: number + + @ApiProperty({ description: '任务名称' }) + name: string + + @ApiProperty({ description: '创建时间' }) + createdAt: string + + @ApiProperty({ description: '耗时' }) + consumeTime: number + + @ApiProperty({ description: '执行信息' }) + detail: string + + @ApiProperty({ description: '任务执行状态' }) + status: number +} diff --git a/src/modules/system/log/services/captcha-log.service.ts b/src/modules/system/log/services/captcha-log.service.ts new file mode 100644 index 0000000..5bc01e1 --- /dev/null +++ b/src/modules/system/log/services/captcha-log.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' + +import { LessThan, Repository } from 'typeorm' + +import { paginate } from '~/helper/paginate' + +import { CaptchaLogQueryDto } from '../dto/log.dto' +import { CaptchaLogEntity } from '../entities/captcha-log.entity' + +@Injectable() +export class CaptchaLogService { + constructor( + @InjectRepository(CaptchaLogEntity) + private captchaLogRepository: Repository, + ) {} + + async create( + account: string, + code: string, + provider: 'sms' | 'email', + uid?: number, + ): Promise { + await this.captchaLogRepository.save({ + account, + code, + provider, + userId: uid, + }) + } + + async paginate({ page, pageSize }: CaptchaLogQueryDto) { + const queryBuilder = await this.captchaLogRepository + .createQueryBuilder('captcha_log') + .orderBy('captcha_log.id', 'DESC') + + return paginate(queryBuilder, { + page, + pageSize, + }) + } + + async clearLog(): Promise { + await this.captchaLogRepository.clear() + } + + async clearLogBeforeTime(time: Date): Promise { + await this.captchaLogRepository.delete({ createdAt: LessThan(time) }) + } +} diff --git a/src/modules/system/log/services/login-log.service.ts b/src/modules/system/log/services/login-log.service.ts new file mode 100644 index 0000000..b3420de --- /dev/null +++ b/src/modules/system/log/services/login-log.service.ts @@ -0,0 +1,100 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' + +import { Between, LessThan, Like, Repository } from 'typeorm' + +import UAParser from 'ua-parser-js' + +import { paginateRaw } from '~/helper/paginate' + +import { getIpAddress } from '~/utils/ip.util' + +import { LoginLogQueryDto } from '../dto/log.dto' +import { LoginLogEntity } from '../entities/login-log.entity' +import { LoginLogInfo } from '../models/log.model' + +async function parseLoginLog(e: any, parser: UAParser): Promise { + const uaResult = parser.setUA(e.login_log_ua).getResult() + + return { + id: e.login_log_id, + ip: e.login_log_ip, + address: e.login_log_address, + os: `${`${uaResult.os.name ?? ''} `}${uaResult.os.version}`, + browser: `${`${uaResult.browser.name ?? ''} `}${uaResult.browser.version}`, + username: e.user_username, + time: e.login_log_created_at, + } +} + +@Injectable() +export class LoginLogService { + constructor( + @InjectRepository(LoginLogEntity) + private loginLogRepository: Repository, + + ) {} + + async create(uid: number, ip: string, ua: string): Promise { + try { + const address = await getIpAddress(ip) + + await this.loginLogRepository.save({ + ip, + ua, + address, + user: { id: uid }, + }) + } + catch (e) { + console.error(e) + } + } + + async list({ + page, + pageSize, + username, + ip, + address, + time, + }: LoginLogQueryDto) { + const queryBuilder = await this.loginLogRepository + .createQueryBuilder('login_log') + .innerJoinAndSelect('login_log.user', 'user') + .where({ + ...(ip && { ip: Like(`%${ip}%`) }), + ...(address && { address: Like(`%${address}%`) }), + ...(time && { createdAt: Between(time[0], time[1]) }), + ...(username && { + user: { + username: Like(`%${username}%`), + }, + }), + }) + .orderBy('login_log.created_at', 'DESC') + + const { items, ...rest } = await paginateRaw(queryBuilder, { + page, + pageSize, + }) + + const parser = new UAParser() + const loginLogInfos = await Promise.all( + items.map(item => parseLoginLog(item, parser)), + ) + + return { + items: loginLogInfos, + ...rest, + } + } + + async clearLog(): Promise { + await this.loginLogRepository.clear() + } + + async clearLogBeforeTime(time: Date): Promise { + await this.loginLogRepository.delete({ createdAt: LessThan(time) }) + } +} diff --git a/src/modules/system/log/services/task-log.service.ts b/src/modules/system/log/services/task-log.service.ts new file mode 100644 index 0000000..b57410f --- /dev/null +++ b/src/modules/system/log/services/task-log.service.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' + +import { LessThan, Repository } from 'typeorm' + +import { paginate } from '~/helper/paginate' + +import { TaskLogQueryDto } from '../dto/log.dto' +import { TaskLogEntity } from '../entities/task-log.entity' + +@Injectable() +export class TaskLogService { + constructor( + @InjectRepository(TaskLogEntity) + private taskLogRepository: Repository, + ) {} + + async create( + tid: number, + status: number, + time?: number, + err?: string, + ): Promise { + const result = await this.taskLogRepository.save({ + status, + detail: err, + time, + task: { id: tid }, + }) + return result.id + } + + async list({ page, pageSize }: TaskLogQueryDto) { + const queryBuilder = await this.taskLogRepository + .createQueryBuilder('task_log') + .leftJoinAndSelect('task_log.task', 'task') + .orderBy('task_log.id', 'DESC') + + return paginate(queryBuilder, { + page, + pageSize, + }) + } + + async clearLog(): Promise { + await this.taskLogRepository.clear() + } + + async clearLogBeforeTime(time: Date): Promise { + await this.taskLogRepository.delete({ createdAt: LessThan(time) }) + } +} diff --git a/src/modules/system/menu/menu.controller.ts b/src/modules/system/menu/menu.controller.ts new file mode 100644 index 0000000..2de0370 --- /dev/null +++ b/src/modules/system/menu/menu.controller.ts @@ -0,0 +1,104 @@ +import { + BadRequestException, + Body, + Controller, + Delete, + Get, + Post, + Put, + Query, +} from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { flattenDeep } from 'lodash' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { Perm, definePermission, getDefinePermissions } from '~/modules/auth/decorators/permission.decorator' + +import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto' +import { MenuItemInfo } from './menu.model' +import { MenuService } from './menu.service' + +export const permissions = definePermission('system:menu', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', +} as const) + +@ApiTags('System - 菜单权限模块') +@ApiSecurityAuth() +@Controller('menus') +export class MenuController { + constructor(private menuService: MenuService) {} + + @Get() + @ApiOperation({ summary: '获取所有菜单列表' }) + @ApiResult({ type: [MenuItemInfo] }) + @Perm(permissions.LIST) + async list(@Query() dto: MenuQueryDto) { + return this.menuService.list(dto) + } + + @Get(':id') + @ApiOperation({ summary: '获取菜单或权限信息' }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.menuService.getMenuItemAndParentInfo(id) + } + + @Post() + @ApiOperation({ summary: '新增菜单或权限' }) + @Perm(permissions.CREATE) + async create(@Body() dto: MenuDto): Promise { + // check + await this.menuService.check(dto) + if (!dto.parentId) + dto.parentId = null + + await this.menuService.create(dto) + if (dto.type === 2) { + // 如果是权限发生更改,则刷新所有在线用户的权限 + await this.menuService.refreshOnlineUserPerms() + } + } + + @Put(':id') + @ApiOperation({ summary: '更新菜单或权限' }) + @Perm(permissions.UPDATE) + async update( + @IdParam() id: number, @Body() dto: MenuUpdateDto): Promise { + // check + await this.menuService.check(dto) + if (dto.parentId === -1 || !dto.parentId) + dto.parentId = null + + await this.menuService.update(id, dto) + if (dto.type === 2) { + // 如果是权限发生更改,则刷新所有在线用户的权限 + await this.menuService.refreshOnlineUserPerms() + } + } + + @Delete(':id') + @ApiOperation({ summary: '删除菜单或权限' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + if (await this.menuService.checkRoleByMenuId(id)) + throw new BadRequestException('该菜单存在关联角色,无法删除') + + // 如果有子目录,一并删除 + const childMenus = await this.menuService.findChildMenus(id) + await this.menuService.deleteMenuItem(flattenDeep([id, childMenus])) + // 刷新在线用户权限 + await this.menuService.refreshOnlineUserPerms() + } + + @Get('permissions') + @ApiOperation({ summary: '获取后端定义的所有权限集' }) + async getPermissions(): Promise { + return getDefinePermissions() + } +} diff --git a/src/modules/system/menu/menu.dto.ts b/src/modules/system/menu/menu.dto.ts new file mode 100644 index 0000000..5a24aab --- /dev/null +++ b/src/modules/system/menu/menu.dto.ts @@ -0,0 +1,88 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger' +import { + IsBoolean, + IsIn, + IsInt, + IsOptional, + IsString, + Min, + MinLength, + ValidateIf, +} from 'class-validator' + +export class MenuDto { + @ApiProperty({ description: '菜单类型' }) + @IsIn([0, 1, 2]) + type: number + + @ApiProperty({ description: '父级菜单' }) + @IsOptional() + parentId: number + + @ApiProperty({ description: '菜单或权限名称' }) + @IsString() + @MinLength(2) + name: string + + @ApiProperty({ description: '排序' }) + @IsInt() + @Min(0) + orderNo: number + + @ApiProperty({ description: '前端路由地址' }) + // @Matches(/^[/]$/) + @ValidateIf(o => o.type !== 2) + path: string + + @ApiProperty({ description: '是否外链', default: false }) + @ValidateIf(o => o.type !== 2) + @IsBoolean() + isExt: boolean + + @ApiProperty({ description: '外链打开方式', default: 1 }) + @ValidateIf((o: MenuDto) => o.isExt) + @IsIn([1, 2]) + extOpenMode: number + + @ApiProperty({ description: '菜单是否显示', default: 1 }) + @ValidateIf((o: MenuDto) => o.type !== 2) + @IsIn([0, 1]) + show: number + + @ApiProperty({ description: '设置当前路由高亮的菜单项,一般用于详情页' }) + @ValidateIf((o: MenuDto) => o.type !== 2 && o.show === 0) + @IsString() + @IsOptional() + activeMenu?: string + + @ApiProperty({ description: '是否开启页面缓存', default: 1 }) + @ValidateIf((o: MenuDto) => o.type === 1) + @IsIn([0, 1]) + keepAlive: number + + @ApiProperty({ description: '状态', default: 1 }) + @IsIn([0, 1]) + status: number + + @ApiProperty({ description: '菜单图标' }) + @IsOptional() + @ValidateIf((o: MenuDto) => o.type !== 2) + @IsString() + icon?: string + + @ApiProperty({ description: '对应权限' }) + @ValidateIf((o: MenuDto) => o.type === 2) + @IsString() + @IsOptional() + permission: string + + @ApiProperty({ description: '菜单路由路径或外链' }) + @ValidateIf((o: MenuDto) => o.type !== 2) + @IsString() + @IsOptional() + component?: string +} + +export class MenuUpdateDto extends PartialType(MenuDto) {} + +export class MenuQueryDto extends PartialType(MenuDto) {} diff --git a/src/modules/system/menu/menu.entity.ts b/src/modules/system/menu/menu.entity.ts new file mode 100644 index 0000000..950d057 --- /dev/null +++ b/src/modules/system/menu/menu.entity.ts @@ -0,0 +1,55 @@ +import { Column, Entity, ManyToMany, Relation } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +import { RoleEntity } from '../role/role.entity' + +@Entity({ name: 'sys_menu' }) +export class MenuEntity extends CommonEntity { + @Column({ name: 'parent_id', nullable: true }) + parentId: number + + @Column() + name: string + + @Column({ nullable: true }) + path: string + + @Column({ nullable: true }) + permission: string + + @Column({ type: 'tinyint', default: 0 }) + type: number + + @Column({ nullable: true, default: '' }) + icon: string + + @Column({ name: 'order_no', type: 'int', nullable: true, default: 0 }) + orderNo: number + + @Column({ name: 'component', nullable: true }) + component: string + + @Column({ name: 'is_ext', type: 'boolean', default: false }) + isExt: boolean + + @Column({ name: 'ext_open_mode', type: 'tinyint', default: 1 }) + extOpenMode: number + + @Column({ name: 'keep_alive', type: 'tinyint', default: 1 }) + keepAlive: number + + @Column({ type: 'tinyint', default: 1 }) + show: number + + @Column({ name: 'active_menu', nullable: true }) + activeMenu: string + + @Column({ type: 'tinyint', default: 1 }) + status: number + + @ManyToMany(() => RoleEntity, role => role.menus, { + onDelete: 'CASCADE', + }) + roles: Relation +} diff --git a/src/modules/system/menu/menu.model.ts b/src/modules/system/menu/menu.model.ts new file mode 100644 index 0000000..36ca084 --- /dev/null +++ b/src/modules/system/menu/menu.model.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger' + +import { MenuEntity } from './menu.entity' + +export class MenuItemInfo extends MenuEntity { + @ApiProperty({ type: [MenuItemInfo] }) + children: MenuItemInfo[] +} diff --git a/src/modules/system/menu/menu.module.ts b/src/modules/system/menu/menu.module.ts new file mode 100644 index 0000000..cdb6ead --- /dev/null +++ b/src/modules/system/menu/menu.module.ts @@ -0,0 +1,24 @@ +import { Module, forwardRef } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { SseService } from '~/modules/sse/sse.service' + +import { RoleModule } from '../role/role.module' + +import { MenuController } from './menu.controller' +import { MenuEntity } from './menu.entity' +import { MenuService } from './menu.service' + +const providers = [MenuService, SseService] + +@Module({ + imports: [ + TypeOrmModule.forFeature([MenuEntity]), + + forwardRef(() => RoleModule), + ], + controllers: [MenuController], + providers: [...providers], + exports: [TypeOrmModule, ...providers], +}) +export class MenuModule {} diff --git a/src/modules/system/menu/menu.service.ts b/src/modules/system/menu/menu.service.ts new file mode 100644 index 0000000..aa288cd --- /dev/null +++ b/src/modules/system/menu/menu.service.ts @@ -0,0 +1,259 @@ +import { InjectRedis } from '@liaoliaots/nestjs-redis' +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' +import Redis from 'ioredis' +import { concat, isEmpty, uniq } from 'lodash' + +import { In, IsNull, Like, Not, Repository } from 'typeorm' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { RedisKeys } from '~/constants/cache.constant' +import { ErrorEnum } from '~/constants/error-code.constant' +import { genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey' +import { SseService } from '~/modules/sse/sse.service' +import { MenuEntity } from '~/modules/system/menu/menu.entity' + +import { deleteEmptyChildren, generatorMenu, generatorRouters } from '~/utils' + +import { RoleService } from '../role/role.service' + +import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto' + +@Injectable() +export class MenuService { + constructor( + @InjectRedis() private redis: Redis, + @InjectRepository(MenuEntity) + private menuRepository: Repository, + private roleService: RoleService, + private sseService: SseService, + ) {} + + /** + * 获取所有菜单以及权限 + */ + async list({ + name, + path, + permission, + component, + status, + }: MenuQueryDto): Promise { + const menus = await this.menuRepository.find({ + where: { + ...(name && { name: Like(`%${name}%`) }), + ...(path && { path: Like(`%${path}%`) }), + ...(permission && { permission: Like(`%${permission}%`) }), + ...(component && { component: Like(`%${component}%`) }), + ...(status && { status }), + }, + order: { orderNo: 'ASC' }, + }) + const menuList = generatorMenu(menus) + + if (!isEmpty(menuList)) { + deleteEmptyChildren(menuList) + return menuList + } + // 如果生产树形结构为空,则返回原始菜单列表 + return menus + } + + async create(menu: MenuDto): Promise { + const result = await this.menuRepository.save(menu) + this.sseService.noticeClientToUpdateMenusByMenuIds([result.id]) + } + + async update(id: number, menu: MenuUpdateDto): Promise { + await this.menuRepository.update(id, menu) + this.sseService.noticeClientToUpdateMenusByMenuIds([id]) + } + + /** + * 根据角色获取所有菜单 + */ + async getMenus(uid: number): Promise { + const roleIds = await this.roleService.getRoleIdsByUser(uid) + let menus: MenuEntity[] = [] + + if (isEmpty(roleIds)) + return generatorRouters([]) + + if (this.roleService.hasAdminRole(roleIds)) { + menus = await this.menuRepository.find({ order: { orderNo: 'ASC' } }) + } + else { + menus = await this.menuRepository + .createQueryBuilder('menu') + .innerJoinAndSelect('menu.roles', 'role') + .andWhere('role.id IN (:...roleIds)', { roleIds }) + .orderBy('menu.order_no', 'ASC') + .getMany() + } + + const menuList = generatorRouters(menus) + return menuList + } + + /** + * 检查菜单创建规则是否符合 + */ + async check(dto: Partial): Promise { + if (dto.type === 2 && !dto.parentId) { + // 无法直接创建权限,必须有parent + throw new BusinessException(ErrorEnum.PERMISSION_REQUIRES_PARENT) + } + if (dto.type === 1 && dto.parentId) { + const parent = await this.getMenuItemInfo(dto.parentId) + if (isEmpty(parent)) + throw new BusinessException(ErrorEnum.PARENT_MENU_NOT_FOUND) + + if (parent && parent.type === 1) { + // 当前新增为菜单但父节点也为菜单时为非法操作 + throw new BusinessException( + ErrorEnum.ILLEGAL_OPERATION_DIRECTORY_PARENT, + ) + } + } + } + + /** + * 查找当前菜单下的子菜单,目录以及菜单 + */ + async findChildMenus(mid: number): Promise { + const allMenus: any = [] + const menus = await this.menuRepository.findBy({ parentId: mid }) + // if (_.isEmpty(menus)) { + // return allMenus; + // } + // const childMenus: any = []; + for (const menu of menus) { + if (menu.type !== 2) { + // 子目录下是菜单或目录,继续往下级查找 + const c = await this.findChildMenus(menu.id) + allMenus.push(c) + } + allMenus.push(menu.id) + } + return allMenus + } + + /** + * 获取某个菜单的信息 + * @param mid menu id + */ + async getMenuItemInfo(mid: number): Promise { + const menu = await this.menuRepository.findOneBy({ id: mid }) + return menu + } + + /** + * 获取某个菜单以及关联的父菜单的信息 + */ + async getMenuItemAndParentInfo(mid: number) { + const menu = await this.menuRepository.findOneBy({ id: mid }) + let parentMenu: MenuEntity | undefined + if (menu && menu.parentId) + parentMenu = await this.menuRepository.findOneBy({ id: menu.parentId }) + + return { menu, parentMenu } + } + + /** + * 查找节点路由是否存在 + */ + async findRouterExist(path: string): Promise { + const menus = await this.menuRepository.findOneBy({ path }) + return !isEmpty(menus) + } + + /** + * 获取当前用户的所有权限 + */ + async getPermissions(uid: number): Promise { + const roleIds = await this.roleService.getRoleIdsByUser(uid) + let permission: any[] = [] + let result: any = null + if (this.roleService.hasAdminRole(roleIds)) { + result = await this.menuRepository.findBy({ + permission: Not(IsNull()), + type: In([1, 2]), + }) + } + else { + if (isEmpty(roleIds)) + return permission + + result = await this.menuRepository + .createQueryBuilder('menu') + .innerJoinAndSelect('menu.roles', 'role') + .andWhere('role.id IN (:...roleIds)', { roleIds }) + .andWhere('menu.type IN (1,2)') + .andWhere('menu.permission IS NOT NULL') + .getMany() + } + if (!isEmpty(result)) { + result.forEach((e) => { + if (e.permission) + permission = concat(permission, e.permission.split(',')) + }) + permission = uniq(permission) + } + return permission + } + + /** + * 删除多项菜单 + */ + async deleteMenuItem(mids: number[]): Promise { + await this.menuRepository.delete(mids) + } + + /** + * 刷新指定用户ID的权限 + */ + async refreshPerms(uid: number): Promise { + const perms = await this.getPermissions(uid) + const online = await this.redis.get(genAuthTokenKey(uid)) + if (online) { + // 判断是否在线 + await this.redis.set(genAuthPermKey(uid), JSON.stringify(perms)) + console.log('refreshPerms') + + this.sseService.noticeClientToUpdateMenusByUserIds([uid]) + } + } + + /** + * 刷新所有在线用户的权限 + */ + async refreshOnlineUserPerms(): Promise { + const onlineUserIds: string[] = await this.redis.keys(genAuthTokenKey('*')) + if (onlineUserIds && onlineUserIds.length > 0) { + const promiseArr = onlineUserIds + .map(i => Number.parseInt(i.split(RedisKeys.AUTH_TOKEN_PREFIX)[1])) + .filter(i => i) + .map(async (uid) => { + const perms = await this.getPermissions(uid) + await this.redis.set(genAuthPermKey(uid), JSON.stringify(perms)) + return uid + }) + const uids = await Promise.all(promiseArr) + console.log('refreshOnlineUserPerms') + this.sseService.noticeClientToUpdateMenusByUserIds(uids) + } + } + + /** + * 根据菜单ID查找是否有关联角色 + */ + async checkRoleByMenuId(id: number): Promise { + return !!(await this.menuRepository.findOne({ + where: { + roles: { + id, + }, + }, + })) + } +} diff --git a/src/modules/system/online/online.controller.ts b/src/modules/system/online/online.controller.ts new file mode 100644 index 0000000..c484037 --- /dev/null +++ b/src/modules/system/online/online.controller.ts @@ -0,0 +1,46 @@ +import { Body, Controller, Get, Post } from '@nestjs/common' +import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' + +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' + +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' + +import { KickDto } from './online.dto' +import { OnlineUserInfo } from './online.model' +import { OnlineService } from './online.service' + +export const permissions = definePermission('system:online', { + LIST: 'list', + KICK: 'kick', +} as const) + +@ApiTags('System - 在线用户模块') +@ApiSecurityAuth() +@ApiExtraModels(OnlineUserInfo) +@Controller('online') +export class OnlineController { + constructor(private onlineService: OnlineService) {} + + @Get('list') + @ApiOperation({ summary: '查询当前在线用户' }) + @ApiResult({ type: [OnlineUserInfo] }) + @Perm(permissions.LIST) + async list(@AuthUser() user: IAuthUser): Promise { + return this.onlineService.listOnlineUser(user.uid) + } + + @Post('kick') + @ApiOperation({ summary: '下线指定在线用户' }) + @Perm(permissions.KICK) + async kick(@Body() dto: KickDto, @AuthUser() user: IAuthUser): Promise { + if (dto.id === user.uid) + throw new BusinessException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER) + + await this.onlineService.kickUser(dto.id, user.uid) + } +} diff --git a/src/modules/system/online/online.dto.ts b/src/modules/system/online/online.dto.ts new file mode 100644 index 0000000..fbe8f8b --- /dev/null +++ b/src/modules/system/online/online.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger' +import { IsInt } from 'class-validator' + +export class KickDto { + @ApiProperty({ description: '需要下线的角色ID' }) + @IsInt() + id: number +} diff --git a/src/modules/system/online/online.model.ts b/src/modules/system/online/online.model.ts new file mode 100644 index 0000000..a08f812 --- /dev/null +++ b/src/modules/system/online/online.model.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger' + +export class OnlineUserInfo { + @ApiProperty({ description: '最近的一条登录日志ID' }) + id: number + + @ApiProperty({ description: '登录IP' }) + ip: string + + @ApiProperty({ description: '登录地点' }) + address: string + + @ApiProperty({ description: '用户名' }) + username: string + + @ApiProperty({ description: '是否当前' }) + isCurrent: boolean + + @ApiProperty({ description: '系统' }) + os: string + + @ApiProperty({ description: '浏览器' }) + browser: string + + @ApiProperty({ description: '是否禁用' }) + disable: boolean +} diff --git a/src/modules/system/online/online.module.ts b/src/modules/system/online/online.module.ts new file mode 100644 index 0000000..4de292a --- /dev/null +++ b/src/modules/system/online/online.module.ts @@ -0,0 +1,27 @@ +import { Module, forwardRef } from '@nestjs/common' + +import { AuthModule } from '~/modules/auth/auth.module' +import { SocketModule } from '~/socket/socket.module' + +import { UserModule } from '../../user/user.module' +import { RoleModule } from '../role/role.module' +import { SystemModule } from '../system.module' + +import { OnlineController } from './online.controller' +import { OnlineService } from './online.service' + +const providers = [OnlineService] + +@Module({ + imports: [ + forwardRef(() => SystemModule), + forwardRef(() => SocketModule), + AuthModule, + UserModule, + RoleModule, + ], + controllers: [OnlineController], + providers, + exports: [...providers], +}) +export class OnlineModule {} diff --git a/src/modules/system/online/online.service.ts b/src/modules/system/online/online.service.ts new file mode 100644 index 0000000..31ad4c0 --- /dev/null +++ b/src/modules/system/online/online.service.ts @@ -0,0 +1,134 @@ +import { Injectable } from '@nestjs/common' +import { JwtService } from '@nestjs/jwt' +import { InjectEntityManager } from '@nestjs/typeorm' + +import { RemoteSocket } from 'socket.io' +import { EntityManager } from 'typeorm' + +import { UAParser } from 'ua-parser-js' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' + +import { BusinessEvents } from '~/socket/business-event.constant' +import { AdminEventsGateway } from '~/socket/events/admin.gateway' + +import { UserService } from '../../user/user.service' + +import { OnlineUserInfo } from './online.model' + +@Injectable() +export class OnlineService { + constructor( + @InjectEntityManager() private readonly entityManager: EntityManager, + private readonly userService: UserService, + private readonly adminEventsGateWay: AdminEventsGateway, + private readonly jwtService: JwtService, + ) {} + + /** + * 罗列在线用户列表 + */ + async listOnlineUser(currentUid: number): Promise { + const onlineSockets = await this.getOnlineSockets() + if (!onlineSockets || onlineSockets.length <= 0) + return [] + + const onlineIds = onlineSockets.map((socket) => { + const token = socket.handshake.query?.token as string + return this.jwtService.verify(token).uid + }) + return this.findLastLoginInfoList(onlineIds, currentUid) + } + + /** + * 下线当前用户 + */ + async kickUser(uid: number, currentUid: number): Promise { + const rootUserId = await this.userService.findRootUserId() + const currentUserInfo = await this.userService.getAccountInfo(currentUid) + if (uid === rootUserId) + throw new BusinessException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER) + + // reset redis keys + await this.userService.forbidden(uid) + // socket emit + const socket = await this.findSocketIdByUid(uid) + if (socket) { + // socket emit event + this.adminEventsGateWay.server + .to(socket.id) + .emit(BusinessEvents.USER_KICK, { operater: currentUserInfo.username }) + // close socket + socket.disconnect() + } + } + + /** + * 根据用户id列表查找最近登录信息和用户信息 + */ + async findLastLoginInfoList( + ids: number[], + currentUid: number, + ): Promise { + const rootUserId = await this.userService.findRootUserId() + const result = await this.entityManager.query( + ` + SELECT sys_login_log.created_at, sys_login_log.ip, sys_login_log.address, sys_login_log.ua, sys_user.id, sys_user.username, sys_user.nick_name + FROM sys_login_log + INNER JOIN sys_user ON sys_login_log.user_id = sys_user.id + WHERE sys_login_log.created_at IN (SELECT MAX(created_at) as createdAt FROM sys_login_log GROUP BY user_id) + AND sys_user.id IN (?) + `, + [ids], + ) + if (result) { + const parser = new UAParser() + return result.map((e) => { + const u = parser.setUA(e.ua).getResult() + return { + id: e.id, + ip: e.ip, + address: e.address, + username: `${e.nick_name}(${e.username})`, + isCurrent: currentUid === e.id, + time: e.created_at, + os: `${u.os.name} ${u.os.version}`, + browser: `${u.browser.name} ${u.browser.version}`, + disable: currentUid === e.id || e.id === rootUserId, + } + }) + } + return [] + } + + /** + * 根据uid查找socketid + */ + async findSocketIdByUid(uid: number): Promise> { + const onlineSockets = await this.getOnlineSockets() + const socket = onlineSockets.find((socket) => { + const token = socket.handshake.query?.token as string + const tokenUid = this.jwtService.verify(token).uid + return tokenUid === uid + }) + return socket + } + + async filterSocketIdByUidArr( + uids: number[], + ): Promise[]> { + const onlineSockets = await this.getOnlineSockets() + const sockets = onlineSockets.filter((socket) => { + const token = socket.handshake.query?.token as string + const tokenUid = this.jwtService.verify(token).uid + return uids.includes(tokenUid) + }) + return sockets + } + + async getOnlineSockets() { + const onlineSockets = await this.adminEventsGateWay.server.fetchSockets() + return onlineSockets + } +} diff --git a/src/modules/system/param-config/param-config.controller.ts b/src/modules/system/param-config/param-config.controller.ts new file mode 100644 index 0000000..b96ab9d --- /dev/null +++ b/src/modules/system/param-config/param-config.controller.ts @@ -0,0 +1,65 @@ +import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { Pagination } from '~/helper/paginate/pagination' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity' + +import { ParamConfigDto, ParamConfigQueryDto } from './param-config.dto' +import { ParamConfigService } from './param-config.service' + +export const permissions = definePermission('system:param-config', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', +} as const) + +@ApiTags('System - 参数配置模块') +@ApiSecurityAuth() +@Controller('param-config') +export class ParamConfigController { + constructor(private paramConfigService: ParamConfigService) {} + + @Get() + @ApiOperation({ summary: '获取参数配置列表' }) + @ApiResult({ type: [ParamConfigEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: ParamConfigQueryDto): Promise> { + return this.paramConfigService.page(dto) + } + + @Post() + @ApiOperation({ summary: '新增参数配置' }) + @Perm(permissions.CREATE) + async create(@Body() dto: ParamConfigDto): Promise { + await this.paramConfigService.isExistKey(dto.key) + await this.paramConfigService.create(dto) + } + + @Get(':id') + @ApiOperation({ summary: '查询参数配置信息' }) + @ApiResult({ type: ParamConfigEntity }) + @Perm(permissions.READ) + async info(@IdParam() id: number): Promise { + return this.paramConfigService.findOne(id) + } + + @Post(':id') + @ApiOperation({ summary: '更新参数配置' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: ParamConfigDto): Promise { + await this.paramConfigService.update(id, dto) + } + + @Delete(':id') + @ApiOperation({ summary: '删除指定的参数配置' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.paramConfigService.delete(id) + } +} diff --git a/src/modules/system/param-config/param-config.dto.ts b/src/modules/system/param-config/param-config.dto.ts new file mode 100644 index 0000000..5b720ba --- /dev/null +++ b/src/modules/system/param-config/param-config.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty } from '@nestjs/swagger' +import { IsOptional, IsString, MinLength } from 'class-validator' + +import { PagerDto } from '~/common/dto/pager.dto' + +export class ParamConfigDto { + @ApiProperty({ description: '参数名称' }) + @IsString() + name: string + + @ApiProperty({ description: '参数键名' }) + @IsString() + @MinLength(3) + key: string + + @ApiProperty({ description: '参数值' }) + @IsString() + value: string + + @ApiProperty({ description: '备注' }) + @IsOptional() + @IsString() + remark?: string +} + +export class ParamConfigQueryDto extends PagerDto { + @ApiProperty({ description: '参数名称' }) + @IsString() + @IsOptional() + name: string +} diff --git a/src/modules/system/param-config/param-config.entity.ts b/src/modules/system/param-config/param-config.entity.ts new file mode 100644 index 0000000..2b5c4e0 --- /dev/null +++ b/src/modules/system/param-config/param-config.entity.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +@Entity({ name: 'sys_config' }) +export class ParamConfigEntity extends CommonEntity { + @Column({ type: 'varchar', length: 50 }) + @ApiProperty({ description: '配置名' }) + name: string + + @Column({ type: 'varchar', length: 50, unique: true }) + @ApiProperty({ description: '配置键名' }) + key: string + + @Column({ type: 'varchar', nullable: true }) + @ApiProperty({ description: '配置值' }) + value: string + + @Column({ type: 'varchar', nullable: true }) + @ApiProperty({ description: '配置描述' }) + remark: string +} diff --git a/src/modules/system/param-config/param-config.module.ts b/src/modules/system/param-config/param-config.module.ts new file mode 100644 index 0000000..e1c7a9e --- /dev/null +++ b/src/modules/system/param-config/param-config.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { ParamConfigController } from './param-config.controller' +import { ParamConfigEntity } from './param-config.entity' +import { ParamConfigService } from './param-config.service' + +const services = [ParamConfigService] + +@Module({ + imports: [TypeOrmModule.forFeature([ParamConfigEntity])], + controllers: [ParamConfigController], + providers: [...services], + exports: [TypeOrmModule, ...services], +}) +export class ParamConfigModule {} diff --git a/src/modules/system/param-config/param-config.service.ts b/src/modules/system/param-config/param-config.service.ts new file mode 100644 index 0000000..36cbee3 --- /dev/null +++ b/src/modules/system/param-config/param-config.service.ts @@ -0,0 +1,91 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' + +import { Repository } from 'typeorm' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { paginate } from '~/helper/paginate' +import { Pagination } from '~/helper/paginate/pagination' +import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity' + +import { ParamConfigDto, ParamConfigQueryDto } from './param-config.dto' + +@Injectable() +export class ParamConfigService { + constructor( + @InjectRepository(ParamConfigEntity) + private paramConfigRepository: Repository, + ) {} + + /** + * 罗列所有配置 + */ + async page({ + page, + pageSize, + name, + }: ParamConfigQueryDto): Promise> { + const queryBuilder = this.paramConfigRepository.createQueryBuilder('config') + + if (name) { + queryBuilder.where('config.name LIKE :name', { + name: `%${name}%`, + }) + } + + return paginate(queryBuilder, { page, pageSize }) + } + + /** + * 获取参数总数 + */ + async countConfigList(): Promise { + return this.paramConfigRepository.count() + } + + /** + * 新增 + */ + async create(dto: ParamConfigDto): Promise { + await this.paramConfigRepository.insert(dto) + } + + /** + * 更新 + */ + async update(id: number, dto: Partial): Promise { + await this.paramConfigRepository.update(id, dto) + } + + /** + * 删除 + */ + async delete(id: number): Promise { + await this.paramConfigRepository.delete(id) + } + + /** + * 查询单个 + */ + async findOne(id: number): Promise { + return this.paramConfigRepository.findOneBy({ id }) + } + + async isExistKey(key: string): Promise { + const result = await this.paramConfigRepository.findOneBy({ key }) + if (result) + throw new BusinessException(ErrorEnum.PARAMETER_CONFIG_KEY_EXISTS) + } + + async findValueByKey(key: string): Promise { + const result = await this.paramConfigRepository.findOne({ + where: { key }, + select: ['value'], + }) + if (result) + return result.value + + return null + } +} diff --git a/src/modules/system/role/role.controller.ts b/src/modules/system/role/role.controller.ts new file mode 100644 index 0000000..c3ac95b --- /dev/null +++ b/src/modules/system/role/role.controller.ts @@ -0,0 +1,85 @@ +import { + BadRequestException, + Body, + Controller, + Delete, + Get, + Post, + Put, + Query, +} from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { PagerDto } from '~/common/dto/pager.dto' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { RoleEntity } from '~/modules/system/role/role.entity' + +import { MenuService } from '../menu/menu.service' + +import { RoleDto, RoleUpdateDto } from './role.dto' +import { RoleInfo } from './role.model' +import { RoleService } from './role.service' + +export const permissions = definePermission('system:role', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', +} as const) + +@ApiTags('System - 角色模块') +@ApiSecurityAuth() +@Controller('roles') +export class RoleController { + constructor( + private roleService: RoleService, + private menuService: MenuService, + ) {} + + @Get() + @ApiOperation({ summary: '获取角色列表' }) + @ApiResult({ type: [RoleEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: PagerDto) { + return this.roleService.findAll(dto) + } + + @Get(':id') + @ApiOperation({ summary: '获取角色信息' }) + @ApiResult({ type: RoleInfo }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.roleService.info(id) + } + + @Post() + @ApiOperation({ summary: '新增角色' }) + @Perm(permissions.CREATE) + async create(@Body() dto: RoleDto): Promise { + await this.roleService.create(dto) + } + + @Put(':id') + @ApiOperation({ summary: '更新角色' }) + @Perm(permissions.UPDATE) + async update( + @IdParam() id: number, @Body() dto: RoleUpdateDto): Promise { + await this.roleService.update(id, dto) + await this.menuService.refreshOnlineUserPerms() + } + + @Delete(':id') + @ApiOperation({ summary: '删除角色' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + if (await this.roleService.checkUserByRoleId(id)) + throw new BadRequestException('该角色存在关联用户,无法删除') + + await this.roleService.delete(id) + await this.menuService.refreshOnlineUserPerms() + } +} diff --git a/src/modules/system/role/role.dto.ts b/src/modules/system/role/role.dto.ts new file mode 100644 index 0000000..66d2178 --- /dev/null +++ b/src/modules/system/role/role.dto.ts @@ -0,0 +1,38 @@ +import { ApiProperty, PartialType } from '@nestjs/swagger' +import { + IsArray, + IsIn, + IsOptional, + IsString, + Matches, + MinLength, +} from 'class-validator' + +export class RoleDto { + @ApiProperty({ description: '角色名称' }) + @IsString() + @MinLength(2, { message: '角色名称长度不能小于2' }) + name: string + + @ApiProperty({ description: '角色值' }) + @IsString() + @Matches(/^[a-z0-9A-Z]+$/, { message: '角色值只能包含字母和数字' }) + @MinLength(2, { message: '角色值长度不能小于2' }) + value: string + + @ApiProperty({ description: '角色备注' }) + @IsString() + @IsOptional() + remark?: string + + @ApiProperty({ description: '状态' }) + @IsIn([0, 1]) + status: number + + @ApiProperty({ description: '关联菜单、权限编号' }) + @IsOptional() + @IsArray() + menuIds?: number[] +} + +export class RoleUpdateDto extends PartialType(RoleDto) {} diff --git a/src/modules/system/role/role.entity.ts b/src/modules/system/role/role.entity.ts new file mode 100644 index 0000000..1317697 --- /dev/null +++ b/src/modules/system/role/role.entity.ts @@ -0,0 +1,43 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger' +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +import { UserEntity } from '../../user/user.entity' +import { MenuEntity } from '../menu/menu.entity' + +@Entity({ name: 'sys_role' }) +export class RoleEntity extends CommonEntity { + @Column({ length: 50, unique: true }) + @ApiProperty({ description: '角色名' }) + name: string + + @Column({ unique: true }) + @ApiProperty({ description: '角色标识' }) + value: string + + @Column({ nullable: true }) + @ApiProperty({ description: '角色描述' }) + remark: string + + @Column({ type: 'tinyint', nullable: true, default: 1 }) + @ApiProperty({ description: '状态:1启用,0禁用' }) + status: number + + @Column({ nullable: true }) + @ApiProperty({ description: '是否默认用户' }) + default: boolean + + @ApiHideProperty() + @ManyToMany(() => UserEntity, user => user.roles) + users: Relation + + @ApiHideProperty() + @ManyToMany(() => MenuEntity, menu => menu.roles, {}) + @JoinTable({ + name: 'sys_role_menus', + joinColumn: { name: 'role_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'menu_id', referencedColumnName: 'id' }, + }) + menus: Relation +} diff --git a/src/modules/system/role/role.model.ts b/src/modules/system/role/role.model.ts new file mode 100644 index 0000000..2d11ab4 --- /dev/null +++ b/src/modules/system/role/role.model.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger' + +import { RoleEntity } from './role.entity' + +export class RoleInfo extends RoleEntity { + @ApiProperty({ type: [Number] }) + menuIds: number[] +} diff --git a/src/modules/system/role/role.module.ts b/src/modules/system/role/role.module.ts new file mode 100644 index 0000000..98c24e6 --- /dev/null +++ b/src/modules/system/role/role.module.ts @@ -0,0 +1,23 @@ +import { Module, forwardRef } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { SseService } from '~/modules/sse/sse.service' + +import { MenuModule } from '../menu/menu.module' + +import { RoleController } from './role.controller' +import { RoleEntity } from './role.entity' +import { RoleService } from './role.service' + +const providers = [RoleService, SseService] + +@Module({ + imports: [ + TypeOrmModule.forFeature([RoleEntity]), + forwardRef(() => MenuModule), + ], + controllers: [RoleController], + providers: [...providers], + exports: [TypeOrmModule, ...providers], +}) +export class RoleModule {} diff --git a/src/modules/system/role/role.service.ts b/src/modules/system/role/role.service.ts new file mode 100644 index 0000000..3eaba39 --- /dev/null +++ b/src/modules/system/role/role.service.ts @@ -0,0 +1,152 @@ +import { Injectable } from '@nestjs/common' +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm' +import { isEmpty } from 'lodash' +import { EntityManager, In, Repository } from 'typeorm' + +import { PagerDto } from '~/common/dto/pager.dto' +import { ROOT_ROLE_ID } from '~/constants/system.constant' +import { paginate } from '~/helper/paginate' +import { Pagination } from '~/helper/paginate/pagination' +import { SseService } from '~/modules/sse/sse.service' +import { MenuEntity } from '~/modules/system/menu/menu.entity' +import { RoleEntity } from '~/modules/system/role/role.entity' + +import { RoleDto, RoleUpdateDto } from './role.dto' + +@Injectable() +export class RoleService { + constructor( + @InjectRepository(RoleEntity) + private roleRepository: Repository, + @InjectRepository(MenuEntity) + private menuRepository: Repository, + @InjectEntityManager() private entityManager: EntityManager, + private sseService: SseService, + + ) {} + + /** + * 列举所有角色:除去超级管理员 + */ + async findAll({ + page, + pageSize, + }: PagerDto): Promise> { + return paginate(this.roleRepository, { page, pageSize }) + } + + /** + * 根据角色获取角色信息 + */ + async info(id: number) { + const info = await this.roleRepository + .createQueryBuilder('role') + .where({ + id, + }) + .getOne() + + const menus = await this.menuRepository.find({ + where: { roles: { id } }, + select: ['id'], + }) + + return { ...info, menuIds: menus.map(m => m.id) } + } + + async delete(id: number): Promise { + if (id === ROOT_ROLE_ID) + throw new Error('不能删除超级管理员') + await this.roleRepository.delete(id) + } + + /** + * 增加角色 + */ + async create({ menuIds, ...data }: RoleDto): Promise<{ roleId: number }> { + const role = await this.roleRepository.save({ + ...data, + menus: menuIds + ? await this.menuRepository.findBy({ id: In(menuIds) }) + : [], + }) + + return { roleId: role.id } + } + + /** + * 更新角色信息 + */ + async update(id, { menuIds, ...data }: RoleUpdateDto): Promise { + await this.roleRepository.update(id, data) + + if (!isEmpty(menuIds)) { + // using transaction + await this.entityManager.transaction(async (manager) => { + const menus = await this.menuRepository.find({ + where: { id: In(menuIds) }, + }) + + const role = await this.roleRepository.findOne({ where: { id } }) + role.menus = menus + await manager.save(role) + }) + } + } + + /** + * 根据用户id查找角色信息 + */ + async getRoleIdsByUser(id: number): Promise { + const roles = await this.roleRepository.find({ + where: { + users: { id }, + }, + }) + + if (!isEmpty(roles)) + return roles.map(r => r.id) + + return [] + } + + async getRoleValues(ids: number[]): Promise { + return ( + await this.roleRepository.findBy({ + id: In(ids), + }) + ).map(r => r.value) + } + + async isAdminRoleByUser(uid: number): Promise { + const roles = await this.roleRepository.find({ + where: { + users: { id: uid }, + }, + }) + + if (!isEmpty(roles)) { + return roles.some( + r => r.id === ROOT_ROLE_ID, + ) + } + return false + } + + hasAdminRole(rids: number[]): boolean { + return rids.includes(ROOT_ROLE_ID) + } + + /** + * 根据角色ID查找是否有关联用户 + */ + async checkUserByRoleId(id: number): Promise { + return this.roleRepository.exist({ + where: { + users: { + roles: { id }, + }, + }, + }) + } +} diff --git a/src/modules/system/serve/serve.controller.ts b/src/modules/system/serve/serve.controller.ts new file mode 100644 index 0000000..eb27465 --- /dev/null +++ b/src/modules/system/serve/serve.controller.ts @@ -0,0 +1,31 @@ +import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager' +import { Controller, Get, UseInterceptors } from '@nestjs/common' +import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' + +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' + +import { AllowAnon } from '~/modules/auth/decorators/allow-anon.decorator' + +import { ServeStatInfo } from './serve.model' +import { ServeService } from './serve.service' + +@ApiTags('System - 服务监控') +@ApiSecurityAuth() +@ApiExtraModels(ServeStatInfo) +@Controller('serve') +@UseInterceptors(CacheInterceptor) +@CacheKey('serve_stat') +@CacheTTL(10000) +export class ServeController { + constructor(private serveService: ServeService) {} + + @Get('stat') + @ApiOperation({ summary: '获取服务器运行信息' }) + @ApiResult({ type: ServeStatInfo }) + @AllowAnon() + async stat(): Promise { + return this.serveService.getServeStat() + } +} diff --git a/src/modules/system/serve/serve.model.ts b/src/modules/system/serve/serve.model.ts new file mode 100644 index 0000000..4c602cb --- /dev/null +++ b/src/modules/system/serve/serve.model.ts @@ -0,0 +1,86 @@ +import { ApiProperty } from '@nestjs/swagger' + +export class Runtime { + @ApiProperty({ description: '系统' }) + os?: string + + @ApiProperty({ description: '服务器架构' }) + arch?: string + + @ApiProperty({ description: 'Node版本' }) + nodeVersion?: string + + @ApiProperty({ description: 'Npm版本' }) + npmVersion?: string +} + +export class CoreLoad { + @ApiProperty({ description: '当前CPU资源消耗' }) + rawLoad?: number + + @ApiProperty({ description: '当前空闲CPU资源' }) + rawLoadIdle?: number +} + +// Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz +export class Cpu { + @ApiProperty({ description: '制造商' }) + manufacturer?: string + + @ApiProperty({ description: '品牌' }) + brand?: string + + @ApiProperty({ description: '物理核心数' }) + physicalCores?: number + + @ApiProperty({ description: '型号' }) + model?: string + + @ApiProperty({ description: '速度 in GHz' }) + speed?: number + + @ApiProperty({ description: 'CPU资源消耗 原始滴答' }) + rawCurrentLoad?: number + + @ApiProperty({ description: '空闲CPU资源 原始滴答' }) + rawCurrentLoadIdle?: number + + @ApiProperty({ description: 'cpu资源消耗', type: [CoreLoad] }) + coresLoad?: CoreLoad[] +} + +export class Disk { + @ApiProperty({ description: '磁盘空间大小 (bytes)' }) + size?: number + + @ApiProperty({ description: '已使用磁盘空间 (bytes)' }) + used?: number + + @ApiProperty({ description: '可用磁盘空间 (bytes)' }) + available?: number +} + +export class Memory { + @ApiProperty({ description: 'total memory in bytes' }) + total?: number + + @ApiProperty({ description: '可用内存' }) + available?: number +} + +/** + * 系统信息 + */ +export class ServeStatInfo { + @ApiProperty({ description: '运行环境', type: Runtime }) + runtime?: Runtime + + @ApiProperty({ description: 'CPU信息', type: Cpu }) + cpu?: Cpu + + @ApiProperty({ description: '磁盘信息', type: Disk }) + disk?: Disk + + @ApiProperty({ description: '内存信息', type: Memory }) + memory?: Memory +} diff --git a/src/modules/system/serve/serve.module.ts b/src/modules/system/serve/serve.module.ts new file mode 100644 index 0000000..db44177 --- /dev/null +++ b/src/modules/system/serve/serve.module.ts @@ -0,0 +1,16 @@ +import { Module, forwardRef } from '@nestjs/common' + +import { SystemModule } from '../system.module' + +import { ServeController } from './serve.controller' +import { ServeService } from './serve.service' + +const providers = [ServeService] + +@Module({ + imports: [forwardRef(() => SystemModule)], + controllers: [ServeController], + providers: [...providers], + exports: [...providers], +}) +export class ServeModule {} diff --git a/src/modules/system/serve/serve.service.ts b/src/modules/system/serve/serve.service.ts new file mode 100644 index 0000000..2b92f3a --- /dev/null +++ b/src/modules/system/serve/serve.service.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@nestjs/common' +import * as si from 'systeminformation' + +import { Disk, ServeStatInfo } from './serve.model' + +@Injectable() +export class ServeService { + /** + * 获取服务器信息 + */ + async getServeStat(): Promise { + const [versions, osinfo, cpuinfo, currentLoadinfo, meminfo] = ( + await Promise.allSettled([ + si.versions('node, npm'), + si.osInfo(), + si.cpu(), + si.currentLoad(), + si.mem(), + ]) + ).map((p: any) => p.value) + + // 计算总空间 + const diskListInfo = await si.fsSize() + const diskinfo = new Disk() + diskinfo.size = 0 + diskinfo.available = 0 + diskinfo.used = 0 + diskListInfo.forEach((d) => { + diskinfo.size += d.size + diskinfo.available += d.available + diskinfo.used += d.used + }) + + return { + runtime: { + npmVersion: versions.npm, + nodeVersion: versions.node, + os: osinfo.platform, + arch: osinfo.arch, + }, + cpu: { + manufacturer: cpuinfo.manufacturer, + brand: cpuinfo.brand, + physicalCores: cpuinfo.physicalCores, + model: cpuinfo.model, + speed: cpuinfo.speed, + rawCurrentLoad: currentLoadinfo.rawCurrentLoad, + rawCurrentLoadIdle: currentLoadinfo.rawCurrentLoadIdle, + coresLoad: currentLoadinfo.cpus.map((e) => { + return { + rawLoad: e.rawLoad, + rawLoadIdle: e.rawLoadIdle, + } + }), + }, + disk: diskinfo, + memory: { + total: meminfo.total, + available: meminfo.available, + }, + } + } +} diff --git a/src/modules/system/system.module.ts b/src/modules/system/system.module.ts new file mode 100644 index 0000000..ae4b12f --- /dev/null +++ b/src/modules/system/system.module.ts @@ -0,0 +1,45 @@ +import { Module } from '@nestjs/common' + +import { RouterModule } from '@nestjs/core' + +import { UserModule } from '../user/user.module' + +import { DeptModule } from './dept/dept.module' +import { DictItemModule } from './dict-item/dict-item.module' +import { DictTypeModule } from './dict-type/dict-type.module' +import { LogModule } from './log/log.module' +import { MenuModule } from './menu/menu.module' +import { OnlineModule } from './online/online.module' +import { ParamConfigModule } from './param-config/param-config.module' +import { RoleModule } from './role/role.module' +import { ServeModule } from './serve/serve.module' +import { TaskModule } from './task/task.module' + +const modules = [ + UserModule, + RoleModule, + MenuModule, + DeptModule, + DictTypeModule, + DictItemModule, + ParamConfigModule, + LogModule, + TaskModule, + OnlineModule, + ServeModule, +] + +@Module({ + imports: [ + ...modules, + RouterModule.register([ + { + path: 'system', + module: SystemModule, + children: [...modules], + }, + ]), + ], + exports: [...modules], +}) +export class SystemModule {} diff --git a/src/modules/system/task/constant.ts b/src/modules/system/task/constant.ts new file mode 100644 index 0000000..53f5d9b --- /dev/null +++ b/src/modules/system/task/constant.ts @@ -0,0 +1,12 @@ +export enum TaskStatus { + Disabled = 0, + Activited = 1, +} + +export enum TaskType { + Cron = 0, + Interval = 1, +} + +export const SYS_TASK_QUEUE_NAME = 'system:sys-task' +export const SYS_TASK_QUEUE_PREFIX = 'system:sys:task' diff --git a/src/modules/system/task/task.controller.ts b/src/modules/system/task/task.controller.ts new file mode 100644 index 0000000..6888664 --- /dev/null +++ b/src/modules/system/task/task.controller.ts @@ -0,0 +1,98 @@ +import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { Pagination } from '~/helper/paginate/pagination' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { TaskEntity } from '~/modules/system/task/task.entity' + +import { TaskDto, TaskQueryDto, TaskUpdateDto } from './task.dto' +import { TaskService } from './task.service' + +export const permissions = definePermission('system:task', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', + + ONCE: 'once', + START: 'start', + STOP: 'stop', +} as const) + +@ApiTags('System - 任务调度模块') +@ApiSecurityAuth() +@Controller('tasks') +export class TaskController { + constructor(private taskService: TaskService) {} + + @Get() + @ApiOperation({ summary: '获取任务列表' }) + @ApiResult({ type: [TaskEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: TaskQueryDto): Promise> { + return this.taskService.list(dto) + } + + @Post() + @ApiOperation({ summary: '添加任务' }) + @Perm(permissions.CREATE) + async create(@Body() dto: TaskDto): Promise { + const serviceCall = dto.service.split('.') + await this.taskService.checkHasMissionMeta(serviceCall[0], serviceCall[1]) + await this.taskService.create(dto) + } + + @Put(':id') + @ApiOperation({ summary: '更新任务' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: TaskUpdateDto): Promise { + const serviceCall = dto.service.split('.') + await this.taskService.checkHasMissionMeta(serviceCall[0], serviceCall[1]) + await this.taskService.update(id, dto) + } + + @Get(':id') + @ApiOperation({ summary: '查询任务详细信息' }) + @ApiResult({ type: TaskEntity }) + @Perm(permissions.READ) + async info(@IdParam() id: number): Promise { + return this.taskService.info(id) + } + + @Delete(':id') + @ApiOperation({ summary: '删除任务' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + const task = await this.taskService.info(id) + await this.taskService.delete(task) + } + + @Put(':id/once') + @ApiOperation({ summary: '手动执行一次任务' }) + @Perm(permissions.ONCE) + async once(@IdParam() id: number): Promise { + const task = await this.taskService.info(id) + await this.taskService.once(task) + } + + @Put(':id/stop') + @ApiOperation({ summary: '停止任务' }) + @Perm(permissions.STOP) + async stop(@IdParam() id: number): Promise { + const task = await this.taskService.info(id) + await this.taskService.stop(task) + } + + @Put(':id/start') + @ApiOperation({ summary: '启动任务' }) + @Perm(permissions.START) + async start(@IdParam() id: number): Promise { + const task = await this.taskService.info(id) + + await this.taskService.start(task) + } +} diff --git a/src/modules/system/task/task.dto.ts b/src/modules/system/task/task.dto.ts new file mode 100644 index 0000000..c02d24f --- /dev/null +++ b/src/modules/system/task/task.dto.ts @@ -0,0 +1,105 @@ +import { BadRequestException } from '@nestjs/common' +import { ApiProperty, ApiPropertyOptional, IntersectionType, PartialType } from '@nestjs/swagger' +import { + IsDateString, + IsIn, + IsInt, + IsOptional, + IsString, + MaxLength, + Min, + MinLength, + Validate, + ValidateIf, + ValidationArguments, + ValidatorConstraint, + ValidatorConstraintInterface, +} from 'class-validator' +import * as parser from 'cron-parser' +import { isEmpty } from 'lodash' + +import { PagerDto } from '~/common/dto/pager.dto' + +// cron 表达式验证,bull lib下引用了cron-parser +@ValidatorConstraint({ name: 'isCronExpression', async: false }) +export class IsCronExpression implements ValidatorConstraintInterface { + validate(value: string, _args: ValidationArguments) { + try { + if (isEmpty(value)) + throw new BadRequestException('cron expression is empty') + + parser.parseExpression(value) + return true + } + catch (e) { + return false + } + } + + defaultMessage(_args: ValidationArguments) { + return 'this cron expression ($value) invalid' + } +} + +export class TaskDto { + @ApiProperty({ description: '任务名称' }) + @IsString() + @MinLength(2) + @MaxLength(50) + name: string + + @ApiProperty({ description: '调用的服务' }) + @IsString() + @MinLength(1) + service: string + + @ApiProperty({ description: '任务类别:cron | interval' }) + @IsIn([0, 1]) + type: number + + @ApiProperty({ description: '任务状态' }) + @IsIn([0, 1]) + status: number + + @ApiPropertyOptional({ description: '开始时间', type: Date }) + @IsDateString() + @ValidateIf(o => !isEmpty(o.startTime)) + startTime: string + + @ApiPropertyOptional({ description: '结束时间', type: Date }) + @IsDateString() + @ValidateIf(o => !isEmpty(o.endTime)) + endTime: string + + @ApiPropertyOptional({ + description: '限制执行次数,负数则无限制', + }) + @IsOptional() + @IsInt() + limit?: number = -1 + + @ApiProperty({ description: 'cron表达式' }) + @Validate(IsCronExpression) + @ValidateIf(o => o.type === 0) + cron: string + + @ApiProperty({ description: '执行间隔,毫秒单位' }) + @IsInt() + @Min(100) + @ValidateIf(o => o.type === 1) + every?: number + + @ApiPropertyOptional({ description: '执行参数' }) + @IsOptional() + @IsString() + data?: string + + @ApiPropertyOptional({ description: '任务备注' }) + @IsOptional() + @IsString() + remark?: string +} + +export class TaskUpdateDto extends PartialType(TaskDto) {} + +export class TaskQueryDto extends IntersectionType(PagerDto, PartialType(TaskDto)) {} diff --git a/src/modules/system/task/task.entity.ts b/src/modules/system/task/task.entity.ts new file mode 100644 index 0000000..9ceb548 --- /dev/null +++ b/src/modules/system/task/task.entity.ts @@ -0,0 +1,55 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +@Entity({ name: 'sys_task' }) +export class TaskEntity extends CommonEntity { + @Column({ type: 'varchar', length: 50, unique: true }) + @ApiProperty({ description: '任务名' }) + name: string + + @Column() + @ApiProperty({ description: '任务标识' }) + service: string + + @Column({ type: 'tinyint', default: 0 }) + @ApiProperty({ description: '任务类型 0cron 1间隔' }) + type: number + + @Column({ type: 'tinyint', default: 1 }) + @ApiProperty({ description: '任务状态 0禁用 1启用' }) + status: number + + @Column({ name: 'start_time', type: 'datetime', nullable: true }) + @ApiProperty({ description: '开始时间' }) + startTime: Date + + @Column({ name: 'end_time', type: 'datetime', nullable: true }) + @ApiProperty({ description: '结束时间' }) + endTime: Date + + @Column({ type: 'int', nullable: true, default: 0 }) + @ApiProperty({ description: '间隔时间' }) + limit: number + + @Column({ nullable: true }) + @ApiProperty({ description: 'cron表达式' }) + cron: string + + @Column({ type: 'int', nullable: true }) + @ApiProperty({ description: '执行次数' }) + every: number + + @Column({ type: 'text', nullable: true }) + @ApiProperty({ description: '任务参数' }) + data: string + + @Column({ name: 'job_opts', type: 'text', nullable: true }) + @ApiProperty({ description: '任务配置' }) + jobOpts: string + + @Column({ nullable: true }) + @ApiProperty({ description: '任务描述' }) + remark: string +} diff --git a/src/modules/system/task/task.module.ts b/src/modules/system/task/task.module.ts new file mode 100644 index 0000000..1fed543 --- /dev/null +++ b/src/modules/system/task/task.module.ts @@ -0,0 +1,37 @@ +import { BullModule } from '@nestjs/bull' +import { Module } from '@nestjs/common' + +import { ConfigService } from '@nestjs/config' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { ConfigKeyPaths, IRedisConfig } from '~/config' + +import { LogModule } from '../log/log.module' + +import { SYS_TASK_QUEUE_NAME, SYS_TASK_QUEUE_PREFIX } from './constant' + +import { TaskController } from './task.controller' +import { TaskEntity } from './task.entity' +import { TaskConsumer } from './task.processor' +import { TaskService } from './task.service' + +const providers = [TaskService, TaskConsumer] + +@Module({ + imports: [ + TypeOrmModule.forFeature([TaskEntity]), + BullModule.registerQueueAsync({ + name: SYS_TASK_QUEUE_NAME, + useFactory: (configService: ConfigService) => ({ + redis: configService.get('redis'), + prefix: SYS_TASK_QUEUE_PREFIX, + }), + inject: [ConfigService], + }), + LogModule, + ], + controllers: [TaskController], + providers: [...providers], + exports: [TypeOrmModule, ...providers], +}) +export class TaskModule {} diff --git a/src/modules/system/task/task.processor.ts b/src/modules/system/task/task.processor.ts new file mode 100644 index 0000000..d45e24c --- /dev/null +++ b/src/modules/system/task/task.processor.ts @@ -0,0 +1,44 @@ +import { OnQueueCompleted, Process, Processor } from '@nestjs/bull' +import { Job } from 'bull' + +import { TaskLogService } from '../log/services/task-log.service' + +import { SYS_TASK_QUEUE_NAME } from './constant' + +import { TaskService } from './task.service' + +export interface ExecuteData { + id: number + args?: string | null + service: string +} + +@Processor(SYS_TASK_QUEUE_NAME) +export class TaskConsumer { + constructor( + private taskService: TaskService, + private taskLogService: TaskLogService, + ) {} + + @Process() + async handle(job: Job): Promise { + const startTime = Date.now() + const { data } = job + try { + await this.taskService.callService(data.service, data.args) + const timing = Date.now() - startTime + // 任务执行成功 + await this.taskLogService.create(data.id, 1, timing) + } + catch (e) { + const timing = Date.now() - startTime + // 执行失败 + await this.taskLogService.create(data.id, 0, timing, `${e}`) + } + } + + @OnQueueCompleted() + onCompleted(job: Job) { + this.taskService.updateTaskCompleteStatus(job.data.id) + } +} diff --git a/src/modules/system/task/task.service.ts b/src/modules/system/task/task.service.ts new file mode 100644 index 0000000..8cc0e28 --- /dev/null +++ b/src/modules/system/task/task.service.ts @@ -0,0 +1,369 @@ +import { InjectRedis } from '@liaoliaots/nestjs-redis' +import { InjectQueue } from '@nestjs/bull' +import { + BadRequestException, + Injectable, + Logger, + NotFoundException, + OnModuleInit, +} from '@nestjs/common' +import { ModuleRef, Reflector } from '@nestjs/core' +import { UnknownElementException } from '@nestjs/core/errors/exceptions/unknown-element.exception' +import { InjectRepository } from '@nestjs/typeorm' +import { Queue } from 'bull' +import Redis from 'ioredis' +import { isEmpty } from 'lodash' +import { Like, Repository } from 'typeorm' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' + +import { paginate } from '~/helper/paginate' +import { Pagination } from '~/helper/paginate/pagination' + +import { TaskEntity } from '~/modules/system/task/task.entity' +import { MISSION_DECORATOR_KEY } from '~/modules/tasks/mission.decorator' + +import { + SYS_TASK_QUEUE_NAME, + SYS_TASK_QUEUE_PREFIX, + TaskStatus, +} from './constant' +import { TaskDto, TaskQueryDto, TaskUpdateDto } from './task.dto' + +@Injectable() +export class TaskService implements OnModuleInit { + private logger = new Logger(TaskService.name) + + constructor( + @InjectRepository(TaskEntity) + private taskRepository: Repository, + @InjectQueue(SYS_TASK_QUEUE_NAME) private taskQueue: Queue, + private moduleRef: ModuleRef, + private reflector: Reflector, + @InjectRedis() private redis: Redis, + ) {} + + /** + * module init + */ + async onModuleInit() { + await this.initTask() + } + + /** + * 初始化任务,系统启动前调用 + */ + async initTask(): Promise { + const initKey = `${SYS_TASK_QUEUE_PREFIX}:init` + // 防止重复初始化 + const result = await this.redis + .multi() + .setnx(initKey, new Date().getTime()) + .expire(initKey, 60 * 30) + .exec() + if (result[0][1] === 0) { + // 存在锁则直接跳过防止重复初始化 + this.logger.log('Init task is lock', TaskService.name) + return + } + const jobs = await this.taskQueue.getJobs([ + 'active', + 'delayed', + 'failed', + 'paused', + 'waiting', + 'completed', + ]) + jobs.forEach((j) => { + j.remove() + }) + + // 查找所有需要运行的任务 + const tasks = await this.taskRepository.findBy({ status: 1 }) + if (tasks && tasks.length > 0) { + for (const t of tasks) + await this.start(t) + } + // 启动后释放锁 + await this.redis.del(initKey) + } + + async list({ + page, + pageSize, + name, + service, + type, + status, + }: TaskQueryDto): Promise> { + const queryBuilder = this.taskRepository + .createQueryBuilder('task') + .where({ + ...(name ? { name: Like(`%${name}%`) } : null), + ...(service ? { service: Like(`%${service}%`) } : null), + ...(type ? { type } : null), + ...(status ? { status } : null), + }) + .orderBy('task.id', 'ASC') + + return paginate(queryBuilder, { page, pageSize }) + } + + /** + * task info + */ + async info(id: number): Promise { + const task = this.taskRepository + .createQueryBuilder('task') + .where({ id }) + .getOne() + + if (!task) + throw new NotFoundException('Task Not Found') + + return task + } + + /** + * delete task + */ + async delete(task: TaskEntity): Promise { + if (!task) + throw new BadRequestException('Task is Empty') + + await this.stop(task) + await this.taskRepository.delete(task.id) + } + + /** + * 手动执行一次 + */ + async once(task: TaskEntity): Promise { + if (task) { + await this.taskQueue.add( + { id: task.id, service: task.service, args: task.data }, + { jobId: task.id, removeOnComplete: true, removeOnFail: true }, + ) + } + else { + throw new BadRequestException('Task is Empty') + } + } + + async create(dto: TaskDto): Promise { + const result = await this.taskRepository.save(dto) + const task = await this.info(result.id) + if (result.status === 0) + await this.stop(task) + else if (result.status === TaskStatus.Activited) + await this.start(task) + } + + async update(id: number, dto: TaskUpdateDto): Promise { + await this.taskRepository.update(id, dto) + const task = await this.info(id) + if (task.status === 0) + await this.stop(task) + else if (task.status === TaskStatus.Activited) + await this.start(task) + } + + /** + * 启动任务 + */ + async start(task: TaskEntity): Promise { + if (!task) + throw new BadRequestException('Task is Empty') + + // 先停掉之前存在的任务 + await this.stop(task) + let repeat: any + if (task.type === 1) { + // 间隔 Repeat every millis (cron setting cannot be used together with this setting.) + repeat = { + every: task.every, + } + } + else { + // cron + repeat = { + cron: task.cron, + } + // Start date when the repeat job should start repeating (only with cron). + if (task.startTime) + repeat.startDate = task.startTime + + if (task.endTime) + repeat.endDate = task.endTime + } + if (task.limit > 0) + repeat.limit = task.limit + + const job = await this.taskQueue.add( + { id: task.id, service: task.service, args: task.data }, + { jobId: task.id, removeOnComplete: true, removeOnFail: true, repeat }, + ) + if (job && job.opts) { + await this.taskRepository.update(task.id, { + jobOpts: JSON.stringify(job.opts.repeat), + status: 1, + }) + } + else { + // update status to 0,标识暂停任务,因为启动失败 + await job?.remove() + await this.taskRepository.update(task.id, { + status: TaskStatus.Disabled, + }) + throw new BadRequestException('Task Start failed') + } + } + + /** + * 停止任务 + */ + async stop(task: TaskEntity): Promise { + if (!task) + throw new BadRequestException('Task is Empty') + + const exist = await this.existJob(task.id.toString()) + if (!exist) { + await this.taskRepository.update(task.id, { + status: TaskStatus.Disabled, + }) + return + } + const jobs = await this.taskQueue.getJobs([ + 'active', + 'delayed', + 'failed', + 'paused', + 'waiting', + 'completed', + ]) + jobs + .filter(j => j.data.id === task.id) + .forEach(async (j) => { + await j.remove() + }) + + await this.taskRepository.update(task.id, { status: TaskStatus.Disabled }) + // if (task.jobOpts) { + // await this.app.queue.sys.removeRepeatable(JSON.parse(task.jobOpts)); + // // update status + // await this.getRepo().admin.sys.Task.update(task.id, { status: TaskStatus.Disabled, }); + // } + } + + /** + * 查看队列中任务是否存在 + */ + async existJob(jobId: string): Promise { + // https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#queueremoverepeatablebykey + const jobs = await this.taskQueue.getRepeatableJobs() + const ids = jobs.map((e) => { + return e.id + }) + return ids.includes(jobId) + } + + /** + * 更新是否已经完成,完成则移除该任务并修改状态 + */ + async updateTaskCompleteStatus(tid: number): Promise { + const jobs = await this.taskQueue.getRepeatableJobs() + const task = await this.taskRepository.findOneBy({ id: tid }) + // 如果下次执行时间小于当前时间,则表示已经执行完成。 + for (const job of jobs) { + const currentTime = new Date().getTime() + if (job.id === tid.toString() && job.next < currentTime) { + // 如果下次执行时间小于当前时间,则表示已经执行完成。 + await this.stop(task) + break + } + } + } + + /** + * 检测service是否有注解定义 + * @param serviceName service + */ + async checkHasMissionMeta( + nameOrInstance: string | unknown, + exec: string, + ): Promise { + try { + let service: any + if (typeof nameOrInstance === 'string') + service = await this.moduleRef.get(nameOrInstance, { strict: false }) + else + service = nameOrInstance + + // 所执行的任务不存在 + if (!service || !(exec in service)) + throw new NotFoundException('任务不存在') + + // 检测是否有Mission注解 + const hasMission = this.reflector.get( + MISSION_DECORATOR_KEY, + service.constructor, + ) + // 如果没有,则抛出错误 + if (!hasMission) + throw new BusinessException(ErrorEnum.INSECURE_MISSION) + } + catch (e) { + if (e instanceof UnknownElementException) { + // 任务不存在 + throw new NotFoundException('任务不存在') + } + else { + // 其余错误则不处理,继续抛出 + throw e + } + } + } + + /** + * 根据serviceName调用service,例如 LogService.clearReqLog + */ + async callService(name: string, args: string): Promise { + if (name) { + const [serviceName, methodName] = name.split('.') + if (!methodName) + throw new BadRequestException('serviceName define BadRequestException') + + const service = await this.moduleRef.get(serviceName, { + strict: false, + }) + + // 安全注解检查 + await this.checkHasMissionMeta(service, methodName) + if (isEmpty(args)) { + await service[methodName]() + } + else { + // 参数安全判断 + const parseArgs = this.safeParse(args) + + if (Array.isArray(parseArgs)) { + // 数组形式则自动扩展成方法参数回掉 + await service[methodName](...parseArgs) + } + else { + await service[methodName](parseArgs) + } + } + } + } + + safeParse(args: string): unknown | string { + try { + return JSON.parse(args) + } + catch (e) { + return args + } + } +} diff --git a/src/modules/system/task/task.ts b/src/modules/system/task/task.ts new file mode 100644 index 0000000..26d1e2c --- /dev/null +++ b/src/modules/system/task/task.ts @@ -0,0 +1,2 @@ +export const SYS_TASK_QUEUE_NAME = 'system:sys-task' +export const SYS_TASK_QUEUE_PREFIX = 'system:sys:task' diff --git a/src/modules/tasks/jobs/email.job.ts b/src/modules/tasks/jobs/email.job.ts new file mode 100644 index 0000000..1f923e4 --- /dev/null +++ b/src/modules/tasks/jobs/email.job.ts @@ -0,0 +1,29 @@ +import { BadRequestException, Injectable } from '@nestjs/common' + +import { LoggerService } from '~/shared/logger/logger.service' +import { MailerService } from '~/shared/mailer/mailer.service' + +import { Mission } from '../mission.decorator' + +/** + * Api接口请求类型任务 + */ +@Injectable() +@Mission() +export class EmailJob { + constructor( + private readonly emailService: MailerService, + private readonly logger: LoggerService, + ) {} + + async send(config: any): Promise { + if (config) { + const { to, subject, content } = config + const result = await this.emailService.send(to, subject, content) + this.logger.log(result, EmailJob.name) + } + else { + throw new BadRequestException('Email send job param is empty') + } + } +} diff --git a/src/modules/tasks/jobs/http-request.job.ts b/src/modules/tasks/jobs/http-request.job.ts new file mode 100644 index 0000000..359cb06 --- /dev/null +++ b/src/modules/tasks/jobs/http-request.job.ts @@ -0,0 +1,32 @@ +import { HttpService } from '@nestjs/axios' +import { BadRequestException, Injectable } from '@nestjs/common' + +import { LoggerService } from '~/shared/logger/logger.service' + +import { Mission } from '../mission.decorator' + +/** + * Api接口请求类型任务 + */ +@Injectable() +@Mission() +export class HttpRequestJob { + constructor( + private readonly httpService: HttpService, + private readonly logger: LoggerService, + ) {} + + /** + * 发起请求 + * @param config {AxiosRequestConfig} + */ + async handle(config: unknown): Promise { + if (config) { + const result = await this.httpService.request(config) + this.logger.log(result, HttpRequestJob.name) + } + else { + throw new BadRequestException('Http request job param is empty') + } + } +} diff --git a/src/modules/tasks/jobs/log-clear.job.ts b/src/modules/tasks/jobs/log-clear.job.ts new file mode 100644 index 0000000..fe6fa52 --- /dev/null +++ b/src/modules/tasks/jobs/log-clear.job.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common' + +import { LoginLogService } from '~/modules/system/log/services/login-log.service' +import { TaskLogService } from '~/modules/system/log/services/task-log.service' + +import { Mission } from '../mission.decorator' + +/** + * 管理后台日志清理任务 + */ +@Injectable() +@Mission() +export class LogClearJob { + constructor( + private loginLogService: LoginLogService, + private taskLogService: TaskLogService, + ) {} + + async clearLoginLog(): Promise { + await this.loginLogService.clearLog() + } + + async clearTaskLog(): Promise { + await this.taskLogService.clearLog() + } +} diff --git a/src/modules/tasks/mission.decorator.ts b/src/modules/tasks/mission.decorator.ts new file mode 100644 index 0000000..c14c7c9 --- /dev/null +++ b/src/modules/tasks/mission.decorator.ts @@ -0,0 +1,8 @@ +import { SetMetadata } from '@nestjs/common' + +export const MISSION_DECORATOR_KEY = 'decorator:mission' + +/** + * 定时任务标记,没有该任务标记的任务不会被执行,保证全局获取下的模块被安全执行 + */ +export const Mission = () => SetMetadata(MISSION_DECORATOR_KEY, true) diff --git a/src/modules/tasks/tasks.module.ts b/src/modules/tasks/tasks.module.ts new file mode 100644 index 0000000..9c6b1de --- /dev/null +++ b/src/modules/tasks/tasks.module.ts @@ -0,0 +1,46 @@ +import { DynamicModule, ExistingProvider, Module } from '@nestjs/common' + +import { LogModule } from '~/modules/system/log/log.module' +import { SystemModule } from '~/modules/system/system.module' + +import { EmailJob } from './jobs/email.job' +import { HttpRequestJob } from './jobs/http-request.job' +import { LogClearJob } from './jobs/log-clear.job' + +const providers = [LogClearJob, HttpRequestJob, EmailJob] + +/** + * auto create alias + * { + * provide: 'LogClearMissionService', + * useExisting: LogClearMissionService, + * } + */ +function createAliasProviders(): ExistingProvider[] { + const aliasProviders: ExistingProvider[] = [] + for (const p of providers) { + aliasProviders.push({ + provide: p.name, + useExisting: p, + }) + } + return aliasProviders +} + +/** + * 所有需要执行的定时任务都需要在这里注册 + */ +@Module({}) +export class TasksModule { + static forRoot(): DynamicModule { + // 使用Alias定义别名,使得可以通过字符串类型获取定义的Service,否则无法获取 + const aliasProviders = createAliasProviders() + return { + global: true, + module: TasksModule, + imports: [SystemModule, LogModule], + providers: [...providers, ...aliasProviders], + exports: aliasProviders, + } + } +} diff --git a/src/modules/todo/todo.controller.ts b/src/modules/todo/todo.controller.ts new file mode 100644 index 0000000..0cb95d6 --- /dev/null +++ b/src/modules/todo/todo.controller.ts @@ -0,0 +1,79 @@ +import { + Body, + Controller, + Delete, + Get, + Post, + Put, + Query, + UseGuards, +} from '@nestjs/common' +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' + +import { Pagination } from '~/helper/paginate/pagination' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { Resource } from '~/modules/auth/decorators/resource.decorator' + +import { ResourceGuard } from '~/modules/auth/guards/resource.guard' +import { TodoEntity } from '~/modules/todo/todo.entity' + +import { TodoDto, TodoQueryDto, TodoUpdateDto } from './todo.dto' +import { TodoService } from './todo.service' + +export const permissions = definePermission('todo', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', +} as const) + +@ApiTags('Business - Todo模块') +@UseGuards(ResourceGuard) +@Controller('todos') +export class TodoController { + constructor(private readonly todoService: TodoService) {} + + @Get() + @ApiOperation({ summary: '获取Todo列表' }) + @ApiResult({ type: [TodoEntity] }) + @Perm(permissions.LIST) + async list(@Query() dto: TodoQueryDto): Promise> { + return this.todoService.list(dto) + } + + @Get(':id') + @ApiOperation({ summary: '获取Todo详情' }) + @ApiResult({ type: TodoEntity }) + @Perm(permissions.READ) + async info(@IdParam() id: number): Promise { + return this.todoService.detail(id) + } + + @Post() + @ApiOperation({ summary: '创建Todo' }) + @Perm(permissions.CREATE) + async create(@Body() dto: TodoDto): Promise { + await this.todoService.create(dto) + } + + @Put(':id') + @ApiOperation({ summary: '更新Todo' }) + @Perm(permissions.UPDATE) + @Resource(TodoEntity) + async update( + @IdParam() id: number, @Body() dto: TodoUpdateDto): Promise { + await this.todoService.update(id, dto) + } + + @Delete(':id') + @ApiOperation({ summary: '删除Todo' }) + @Perm(permissions.DELETE) + @Resource(TodoEntity) + async delete(@IdParam() id: number): Promise { + await this.todoService.delete(id) + } +} diff --git a/src/modules/todo/todo.dto.ts b/src/modules/todo/todo.dto.ts new file mode 100644 index 0000000..8b222b3 --- /dev/null +++ b/src/modules/todo/todo.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger' +import { IsString } from 'class-validator' + +import { PagerDto } from '~/common/dto/pager.dto' + +export class TodoDto { + @ApiProperty({ description: '名称' }) + @IsString() + value: string +} + +export class TodoUpdateDto extends PartialType(TodoDto) {} + +export class TodoQueryDto extends IntersectionType(PagerDto, TodoDto) {} diff --git a/src/modules/todo/todo.entity.ts b/src/modules/todo/todo.entity.ts new file mode 100644 index 0000000..acc32ad --- /dev/null +++ b/src/modules/todo/todo.entity.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' +import { UserEntity } from '~/modules/user/user.entity' + +@Entity('todo') +export class TodoEntity extends CommonEntity { + @Column() + @ApiProperty({ description: 'todo' }) + value: string + + @ApiProperty({ description: 'todo' }) + @Column({ default: false }) + status: boolean + + @ManyToOne(() => UserEntity) + @JoinColumn({ name: 'user_id' }) + user: Relation +} diff --git a/src/modules/todo/todo.module.ts b/src/modules/todo/todo.module.ts new file mode 100644 index 0000000..5a3d417 --- /dev/null +++ b/src/modules/todo/todo.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { TodoController } from './todo.controller' +import { TodoEntity } from './todo.entity' +import { TodoService } from './todo.service' + +const services = [TodoService] + +@Module({ + imports: [TypeOrmModule.forFeature([TodoEntity])], + controllers: [TodoController], + providers: [...services], + exports: [TypeOrmModule, ...services], +}) +export class TodoModule {} diff --git a/src/modules/todo/todo.service.ts b/src/modules/todo/todo.service.ts new file mode 100644 index 0000000..4fbf35e --- /dev/null +++ b/src/modules/todo/todo.service.ts @@ -0,0 +1,46 @@ +import { Injectable, NotFoundException } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' +import { Repository } from 'typeorm' + +import { paginate } from '~/helper/paginate' +import { Pagination } from '~/helper/paginate/pagination' +import { TodoEntity } from '~/modules/todo/todo.entity' + +import { TodoDto, TodoQueryDto, TodoUpdateDto } from './todo.dto' + +@Injectable() +export class TodoService { + constructor( + @InjectRepository(TodoEntity) + private todoRepository: Repository, + ) {} + + async list({ + page, + pageSize, + }: TodoQueryDto): Promise> { + return paginate(this.todoRepository, { page, pageSize }) + } + + async detail(id: number): Promise { + const item = await this.todoRepository.findOneBy({ id }) + if (!item) + throw new NotFoundException('未找到该记录') + + return item + } + + async create(dto: TodoDto) { + await this.todoRepository.save(dto) + } + + async update(id: number, dto: TodoUpdateDto) { + await this.todoRepository.update(id, dto) + } + + async delete(id: number) { + const item = await this.detail(id) + + await this.todoRepository.remove(item) + } +} diff --git a/src/modules/tools/email/email.controller.ts b/src/modules/tools/email/email.controller.ts new file mode 100644 index 0000000..d3a1816 --- /dev/null +++ b/src/modules/tools/email/email.controller.ts @@ -0,0 +1,22 @@ +import { Body, Controller, Post } from '@nestjs/common' + +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { MailerService } from '~/shared/mailer/mailer.service' + +import { EmailSendDto } from './email.dto' + +@ApiTags('System - 邮箱模块') +@ApiSecurityAuth() +@Controller('email') +export class EmailController { + constructor(private emailService: MailerService) {} + + @ApiOperation({ summary: '发送邮件' }) + @Post('send') + async send(@Body() dto: EmailSendDto): Promise { + const { to, subject, content } = dto + await this.emailService.send(to, subject, content, 'html') + } +} diff --git a/src/modules/tools/email/email.dto.ts b/src/modules/tools/email/email.dto.ts new file mode 100644 index 0000000..8dcca35 --- /dev/null +++ b/src/modules/tools/email/email.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger' +import { IsEmail, IsString } from 'class-validator' + +/** + * 发送邮件 + */ +export class EmailSendDto { + @ApiProperty({ description: '收件人邮箱' }) + @IsEmail() + to: string + + @ApiProperty({ description: '标题' }) + @IsString() + subject: string + + @ApiProperty({ description: '正文' }) + @IsString() + content: string +} diff --git a/src/modules/tools/email/email.module.ts b/src/modules/tools/email/email.module.ts new file mode 100644 index 0000000..e9ba5b1 --- /dev/null +++ b/src/modules/tools/email/email.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common' + +import { EmailController } from './email.controller' + +@Module({ + imports: [], + controllers: [EmailController], +}) +export class EmailModule {} diff --git a/src/modules/tools/storage/storage.controller.ts b/src/modules/tools/storage/storage.controller.ts new file mode 100644 index 0000000..bdd92ba --- /dev/null +++ b/src/modules/tools/storage/storage.controller.ts @@ -0,0 +1,41 @@ +import { Body, Controller, Get, Post, Query } from '@nestjs/common' + +import { ApiOperation, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' + +import { Pagination } from '~/helper/paginate/pagination' + +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' + +import { StorageDeleteDto, StoragePageDto } from './storage.dto' +import { StorageInfo } from './storage.modal' +import { StorageService } from './storage.service' + +export const permissions = definePermission('tool:storage', { + LIST: 'list', + DELETE: 'delete', +} as const) + +@ApiTags('Tools - 存储模块') +@ApiSecurityAuth() +@Controller('storage') +export class StorageController { + constructor(private storageService: StorageService) {} + + @Get('list') + @ApiOperation({ summary: '获取本地存储列表' }) + @ApiResult({ type: [StorageInfo], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: StoragePageDto): Promise> { + return this.storageService.list(dto) + } + + @ApiOperation({ summary: '删除文件' }) + @Post('delete') + @Perm(permissions.DELETE) + async delete(@Body() dto: StorageDeleteDto): Promise { + await this.storageService.delete(dto.ids) + } +} diff --git a/src/modules/tools/storage/storage.dto.ts b/src/modules/tools/storage/storage.dto.ts new file mode 100644 index 0000000..5e50398 --- /dev/null +++ b/src/modules/tools/storage/storage.dto.ts @@ -0,0 +1,68 @@ +import { ApiProperty } from '@nestjs/swagger' +import { ArrayNotEmpty, IsArray, IsOptional, IsString } from 'class-validator' + +import { PagerDto } from '~/common/dto/pager.dto' + +export class StoragePageDto extends PagerDto { + @ApiProperty({ description: '文件名' }) + @IsOptional() + @IsString() + name: string + + @ApiProperty({ description: '文件后缀' }) + @IsString() + @IsOptional() + extName: string + + @ApiProperty({ description: '文件类型' }) + @IsString() + @IsOptional() + type: string + + @ApiProperty({ description: '大小' }) + @IsString() + @IsOptional() + size: string + + @ApiProperty({ description: '上传时间' }) + @IsOptional() + time: string[] + + @ApiProperty({ description: '上传者' }) + @IsString() + @IsOptional() + username: string +} + +export class StorageCreateDto { + @ApiProperty({ description: '文件名' }) + @IsString() + name: string + + @ApiProperty({ description: '真实文件名' }) + @IsString() + fileName: string + + @ApiProperty({ description: '文件扩展名' }) + @IsString() + extName: string + + @ApiProperty({ description: '文件路径' }) + @IsString() + path: string + + @ApiProperty({ description: '文件路径' }) + @IsString() + type: string + + @ApiProperty({ description: '文件大小' }) + @IsString() + size: string +} + +export class StorageDeleteDto { + @ApiProperty({ description: '需要删除的文件ID列表', type: [Number] }) + @IsArray() + @ArrayNotEmpty() + ids: number[] +} diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts new file mode 100644 index 0000000..c68dcf5 --- /dev/null +++ b/src/modules/tools/storage/storage.entity.ts @@ -0,0 +1,40 @@ +import { ApiProperty } from '@nestjs/swagger' +import { Column, Entity } from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +@Entity({ name: 'tool_storage' }) +export class Storage extends CommonEntity { + @Column({ type: 'varchar', length: 200, comment: '文件名' }) + @ApiProperty({ description: '文件名' }) + name: string + + @Column({ + type: 'varchar', + length: 200, + nullable: true, + comment: '真实文件名', + }) + @ApiProperty({ description: '真实文件名' }) + fileName: string + + @Column({ name: 'ext_name', type: 'varchar', nullable: true }) + @ApiProperty({ description: '扩展名' }) + extName: string + + @Column({ type: 'varchar' }) + @ApiProperty({ description: '文件类型' }) + path: string + + @Column({ type: 'varchar', nullable: true }) + @ApiProperty({ description: '文件类型' }) + type: string + + @Column({ type: 'varchar', nullable: true }) + @ApiProperty({ description: '文件大小' }) + size: string + + @Column({ nullable: true, name: 'user_id' }) + @ApiProperty({ description: '用户ID' }) + userId: number +} diff --git a/src/modules/tools/storage/storage.modal.ts b/src/modules/tools/storage/storage.modal.ts new file mode 100644 index 0000000..203e974 --- /dev/null +++ b/src/modules/tools/storage/storage.modal.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger' + +export class StorageInfo { + @ApiProperty({ description: '文件ID' }) + id: number + + @ApiProperty({ description: '文件名' }) + name: string + + @ApiProperty({ description: '文件扩展名' }) + extName: string + + @ApiProperty({ description: '文件路径' }) + path: string + + @ApiProperty({ description: '文件类型' }) + type: string + + @ApiProperty({ description: '大小' }) + size: string + + @ApiProperty({ description: '上传时间' }) + createdAt: string + + @ApiProperty({ description: '上传者' }) + username: string +} diff --git a/src/modules/tools/storage/storage.module.ts b/src/modules/tools/storage/storage.module.ts new file mode 100644 index 0000000..aed7f33 --- /dev/null +++ b/src/modules/tools/storage/storage.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { UserEntity } from '~/modules/user/user.entity' + +import { StorageController } from './storage.controller' +import { Storage } from './storage.entity' +import { StorageService } from './storage.service' + +const services = [StorageService] + +@Module({ + imports: [TypeOrmModule.forFeature([Storage, UserEntity])], + controllers: [StorageController], + providers: [...services], + exports: [TypeOrmModule, ...services], +}) +export class StorageModule {} diff --git a/src/modules/tools/storage/storage.service.ts b/src/modules/tools/storage/storage.service.ts new file mode 100644 index 0000000..e7ddefa --- /dev/null +++ b/src/modules/tools/storage/storage.service.ts @@ -0,0 +1,98 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' +import { Between, Like, Repository } from 'typeorm' + +import { paginateRaw } from '~/helper/paginate' +import { PaginationTypeEnum } from '~/helper/paginate/interface' +import { Pagination } from '~/helper/paginate/pagination' +import { Storage } from '~/modules/tools/storage/storage.entity' +import { UserEntity } from '~/modules/user/user.entity' +import { deleteFile } from '~/utils' + +import { StorageCreateDto, StoragePageDto } from './storage.dto' +import { StorageInfo } from './storage.modal' + +@Injectable() +export class StorageService { + constructor( + @InjectRepository(Storage) + private storageRepository: Repository, + @InjectRepository(UserEntity) + private userRepository: Repository, + ) {} + + async create(dto: StorageCreateDto, userId: number): Promise { + await this.storageRepository.save({ + ...dto, + userId, + }) + } + + /** + * 删除文件 + */ + async delete(fileIds: number[]): Promise { + const items = await this.storageRepository.findByIds(fileIds) + await this.storageRepository.delete(fileIds) + + items.forEach((el) => { + deleteFile(el.path) + }) + } + + async list({ + page, + pageSize, + name, + type, + size, + extName, + time, + username, + }: StoragePageDto): Promise> { + const queryBuilder = this.storageRepository + .createQueryBuilder('storage') + .leftJoinAndSelect('sys_user', 'user', 'storage.user_id = user.id') + .where({ + ...(name && { name: Like(`%${name}%`) }), + ...(type && { type }), + ...(extName && { extName }), + ...(size && { size: Between(size[0], size[1]) }), + ...(time && { createdAt: Between(time[0], time[1]) }), + ...(username && { + userId: await (await this.userRepository.findOneBy({ username })).id, + }), + }) + .orderBy('storage.created_at', 'DESC') + + const { items, ...rest } = await paginateRaw(queryBuilder, { + page, + pageSize, + paginationType: PaginationTypeEnum.LIMIT_AND_OFFSET, + }) + + function formatResult(result: Storage[]) { + return result.map((e: any) => { + return { + id: e.storage_id, + name: e.storage_name, + extName: e.storage_ext_name, + path: e.storage_path, + type: e.storage_type, + size: e.storage_size, + createdAt: e.storage_created_at, + username: e.user_username, + } + }) + } + + return { + items: formatResult(items), + ...rest, + } + } + + async count(): Promise { + return this.storageRepository.count() + } +} diff --git a/src/modules/tools/tools.module.ts b/src/modules/tools/tools.module.ts new file mode 100644 index 0000000..d22d131 --- /dev/null +++ b/src/modules/tools/tools.module.ts @@ -0,0 +1,21 @@ +import { Module } from '@nestjs/common' + +import { RouterModule } from '@nestjs/core' + +import { EmailModule } from './email/email.module' +import { StorageModule } from './storage/storage.module' +import { UploadModule } from './upload/upload.module' + +const modules = [StorageModule, EmailModule, UploadModule] + +@Module({ + imports: [...modules, RouterModule.register([ + { + path: 'tools', + module: ToolsModule, + children: [...modules], + }, + ])], + exports: [...modules], +}) +export class ToolsModule {} diff --git a/src/modules/tools/upload/file.constraint.ts b/src/modules/tools/upload/file.constraint.ts new file mode 100644 index 0000000..dfaa04b --- /dev/null +++ b/src/modules/tools/upload/file.constraint.ts @@ -0,0 +1,64 @@ +import { FastifyMultipartBaseOptions, MultipartFile } from '@fastify/multipart' +import { + ValidationArguments, + ValidationOptions, + ValidatorConstraint, + ValidatorConstraintInterface, + registerDecorator, +} from 'class-validator' +import { has, isArray } from 'lodash' + +type FileLimit = Pick< + FastifyMultipartBaseOptions['limits'], + 'fileSize' | 'files' +> & { + mimetypes?: string[] +} +function checkFileAndLimit(file: MultipartFile, limits: FileLimit = {}) { + if (!('mimetype' in file)) + return false + if (limits.mimetypes && !limits.mimetypes.includes(file.mimetype)) + return false + if ( + has(file, '_buf') + && Buffer.byteLength((file as any)._buf) > limits.fileSize + ) + return false + return true +} + +@ValidatorConstraint({ name: 'isFile' }) +export class FileConstraint implements ValidatorConstraintInterface { + validate(value: MultipartFile, args: ValidationArguments) { + const [limits = {}] = args.constraints + const values = (args.object as any)[args.property] + const filesLimit = (limits as FileLimit).files ?? 0 + if (filesLimit > 0 && isArray(values) && values.length > filesLimit) + return false + return checkFileAndLimit(value, limits) + } + + defaultMessage(_args: ValidationArguments) { + return `The file which to upload's conditions are not met` + } +} + +/** + * 图片验证规则 + * @param limits 限制选项 + * @param validationOptions class-validator选项 + */ +export function IsFile( + limits?: FileLimit, + validationOptions?: ValidationOptions, +) { + return (object: Record, propertyName: string) => { + registerDecorator({ + target: object.constructor, + propertyName, + options: validationOptions, + constraints: [limits], + validator: FileConstraint, + }) + } +} diff --git a/src/modules/tools/upload/upload.controller.ts b/src/modules/tools/upload/upload.controller.ts new file mode 100644 index 0000000..8a73704 --- /dev/null +++ b/src/modules/tools/upload/upload.controller.ts @@ -0,0 +1,53 @@ +import { BadRequestException, Controller, Post, Req } from '@nestjs/common' +import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger' +import { FastifyRequest } from 'fastify' + +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' + +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' + +import { FileUploadDto } from './upload.dto' +import { UploadService } from './upload.service' + +export const permissions = definePermission('upload', { + UPLOAD: 'upload', +} as const) + +@ApiSecurityAuth() +@ApiTags('Tools - 上传模块') +@Controller('upload') +export class UploadController { + constructor(private uploadService: UploadService) {} + + @Post() + @Perm(permissions.UPLOAD) + @ApiOperation({ summary: '上传' }) + @ApiConsumes('multipart/form-data') + @ApiBody({ + type: FileUploadDto, + }) + async upload(@Req() req: FastifyRequest, @AuthUser() user: IAuthUser) { + if (!req.isMultipart()) + throw new BadRequestException('Request is not multipart') + + const file = await req.file() + + // https://github.com/fastify/fastify-multipart + // const parts = req.files() + // for await (const part of parts) + // console.log(part.file) + + try { + const path = await this.uploadService.saveFile(file, user.uid) + + return { + filename: path, + } + } + catch (error) { + console.log(error) + throw new BadRequestException('上传失败') + } + } +} diff --git a/src/modules/tools/upload/upload.dto.ts b/src/modules/tools/upload/upload.dto.ts new file mode 100644 index 0000000..3ab9a01 --- /dev/null +++ b/src/modules/tools/upload/upload.dto.ts @@ -0,0 +1,27 @@ +import { MultipartFile } from '@fastify/multipart' +import { ApiProperty } from '@nestjs/swagger' + +import { IsDefined } from 'class-validator' + +import { IsFile } from './file.constraint' + +export class FileUploadDto { + @ApiProperty({ type: Buffer, format: 'binary', description: '文件' }) + @IsDefined() + @IsFile( + { + mimetypes: [ + 'image/png', + 'image/gif', + 'image/jpeg', + 'image/webp', + 'image/svg+xml', + ], + fileSize: 1024 * 1024 * 10, + }, + { + message: '文件类型不正确', + }, + ) + file: MultipartFile +} diff --git a/src/modules/tools/upload/upload.module.ts b/src/modules/tools/upload/upload.module.ts new file mode 100644 index 0000000..6674739 --- /dev/null +++ b/src/modules/tools/upload/upload.module.ts @@ -0,0 +1,16 @@ +import { Module, forwardRef } from '@nestjs/common' + +import { StorageModule } from '../storage/storage.module' + +import { UploadController } from './upload.controller' +import { UploadService } from './upload.service' + +const services = [UploadService] + +@Module({ + imports: [forwardRef(() => StorageModule)], + controllers: [UploadController], + providers: [...services], + exports: [...services], +}) +export class UploadModule {} diff --git a/src/modules/tools/upload/upload.service.ts b/src/modules/tools/upload/upload.service.ts new file mode 100644 index 0000000..792448f --- /dev/null +++ b/src/modules/tools/upload/upload.service.ts @@ -0,0 +1,53 @@ +import { MultipartFile } from '@fastify/multipart' +import { Injectable, NotFoundException } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' +import { isNil } from 'lodash' +import { Repository } from 'typeorm' + +import { Storage } from '~/modules/tools/storage/storage.entity' + +import { + fileRename, + getExtname, + getFilePath, + getFileType, + getSize, + saveLocalFile, +} from '~/utils/file.util' + +@Injectable() +export class UploadService { + constructor( + @InjectRepository(Storage) + private storageRepository: Repository, + ) {} + + /** + * 保存文件上传记录 + */ + async saveFile(file: MultipartFile, userId: number): Promise { + if (isNil(file)) + throw new NotFoundException('Have not any file to upload!') + + const fileName = file.filename + const size = getSize(file.file.bytesRead) + const extName = getExtname(fileName) + const type = getFileType(extName) + const name = fileRename(fileName) + const path = getFilePath(name) + + saveLocalFile(await file.toBuffer(), name) + + await this.storageRepository.save({ + name, + fileName, + extName, + path, + type, + size, + userId, + }) + + return path + } +} diff --git a/src/modules/user/constant.ts b/src/modules/user/constant.ts new file mode 100644 index 0000000..2510ff6 --- /dev/null +++ b/src/modules/user/constant.ts @@ -0,0 +1,4 @@ +export enum UserStatus { + Disable = 0, + Enabled = 1, +} diff --git a/src/modules/user/dto/password.dto.ts b/src/modules/user/dto/password.dto.ts new file mode 100644 index 0000000..3a6d97b --- /dev/null +++ b/src/modules/user/dto/password.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger' +import { IsString, Matches, MaxLength, MinLength } from 'class-validator' + +export class PasswordUpdateDto { + @ApiProperty({ description: '旧密码' }) + @IsString() + @Matches(/^[a-z0-9A-Z\W_]+$/) + @MinLength(6) + @MaxLength(20) + oldPassword: string + + @ApiProperty({ description: '新密码' }) + @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { + message: '密码必须包含数字、字母,长度为6-16', + }) + newPassword: string +} + +export class UserPasswordDto { + // @ApiProperty({ description: '管理员/用户ID' }) + // @IsEntityExist(UserEntity, { message: '用户不存在' }) + // @IsInt() + // id: number + + @ApiProperty({ description: '更改后的密码' }) + @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { + message: '密码格式不正确', + }) + password: string +} + +export class UserExistDto { + @ApiProperty({ description: '登录账号' }) + @IsString() + @Matches(/^[a-zA-Z0-9_-]{4,16}$/) + @MinLength(6) + @MaxLength(20) + username: string +} diff --git a/src/modules/user/dto/user.dto.ts b/src/modules/user/dto/user.dto.ts new file mode 100644 index 0000000..31e5ddc --- /dev/null +++ b/src/modules/user/dto/user.dto.ts @@ -0,0 +1,98 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger' +import { Type } from 'class-transformer' +import { + ArrayMaxSize, + ArrayMinSize, + ArrayNotEmpty, + IsEmail, + IsIn, + IsInt, + IsOptional, + IsString, + Matches, + MaxLength, + MinLength, + ValidateIf, +} from 'class-validator' +import { isEmpty } from 'lodash' + +import { PagerDto } from '~/common/dto/pager.dto' + +export class UserDto { + @ApiProperty({ description: '头像' }) + @IsOptional() + @IsString() + avatar?: string + + @ApiProperty({ description: '登录账号', example: 'admin' }) + @IsString() + @Matches(/^[a-z0-9A-Z\W_]+$/) + @MinLength(4) + @MaxLength(20) + username: string + + @ApiProperty({ description: '登录密码', example: 'a123456' }) + @IsOptional() + @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { + message: '密码必须包含数字、字母,长度为6-16', + }) + password: string + + @ApiProperty({ description: '归属角色', type: [Number] }) + @ArrayNotEmpty() + @ArrayMinSize(1) + @ArrayMaxSize(3) + roleIds: number[] + + @ApiProperty({ description: '归属大区', type: Number }) + @Type(() => Number) + @IsInt() + @IsOptional() + deptId?: number + + @ApiProperty({ description: '呢称', example: 'admin' }) + @IsOptional() + @IsString() + nickname: string + + @ApiProperty({ description: '邮箱', example: 'bqy.dev@qq.com' }) + @IsEmail() + @ValidateIf(o => !isEmpty(o.email)) + email: string + + @ApiProperty({ description: '手机号' }) + @IsOptional() + @IsString() + phone?: string + + @ApiProperty({ description: 'QQ' }) + @IsOptional() + @IsString() + @Matches(/^[1-9][0-9]{4,10}$/) + @MinLength(5) + @MaxLength(11) + qq?: string + + @ApiProperty({ description: '备注' }) + @IsOptional() + @IsString() + remark?: string + + @ApiProperty({ description: '状态' }) + @IsIn([0, 1]) + status: number +} + +export class UserUpdateDto extends PartialType(UserDto) {} + +export class UserQueryDto extends IntersectionType(PagerDto, PartialType(UserDto)) { + @ApiProperty({ description: '归属大区', example: 1, required: false }) + @IsInt() + @IsOptional() + deptId?: number + + @ApiProperty({ description: '状态', example: 0, required: false }) + @IsInt() + @IsOptional() + status?: number +} diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts new file mode 100644 index 0000000..7b4fd1f --- /dev/null +++ b/src/modules/user/user.controller.ts @@ -0,0 +1,82 @@ +import { Body, Controller, Delete, Get, Param, ParseArrayPipe, Post, Put, Query } from '@nestjs/common' +import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger' + +import { ApiResult } from '~/common/decorators/api-result.decorator' +import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { MenuService } from '~/modules/system/menu/menu.service' + +import { Perm, definePermission } from '../auth/decorators/permission.decorator' + +import { UserPasswordDto } from './dto/password.dto' +import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto' +import { UserEntity } from './user.entity' +import { UserService } from './user.service' + +export const permissions = definePermission('system:user', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', + + PASSWORD_UPDATE: 'password:update', + PASSWORD_RESET: 'pass:reset', +} as const) + +@ApiTags('System - 用户模块') +@ApiSecurityAuth() +@Controller('users') +export class UserController { + constructor( + private userService: UserService, + private menuService: MenuService, + ) {} + + @Get() + @ApiOperation({ summary: '获取用户列表' }) + @ApiResult({ type: [UserEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: UserQueryDto) { + return this.userService.list(dto) + } + + @Get(':id') + @ApiOperation({ summary: '查询用户' }) + @Perm(permissions.READ) + async read(@IdParam() id: number) { + return this.userService.info(id) + } + + @Post() + @ApiOperation({ summary: '新增用户' }) + @Perm(permissions.CREATE) + async create(@Body() dto: UserDto): Promise { + await this.userService.create(dto) + } + + @Put(':id') + @ApiOperation({ summary: '更新用户' }) + @Perm(permissions.UPDATE) + async update( + @IdParam() id: number, @Body() dto: UserUpdateDto): Promise { + await this.userService.update(id, dto) + await this.menuService.refreshPerms(id) + } + + @Delete(':id') + @ApiOperation({ summary: '删除用户' }) + @ApiParam({ name: 'id', type: String, schema: { oneOf: [{ type: 'string' }, { type: 'number' }] } }) + @Perm(permissions.DELETE) + async delete(@Param('id', new ParseArrayPipe({ items: Number, separator: ',' })) ids: number[]): Promise { + await this.userService.delete(ids) + await this.userService.multiForbidden(ids) + } + + @Post(':id/password') + @ApiOperation({ summary: '更改用户密码' }) + @Perm(permissions.PASSWORD_UPDATE) + async password(@IdParam() id: number, @Body() dto: UserPasswordDto): Promise { + await this.userService.forceUpdatePassword(id, dto.password) + } +} diff --git a/src/modules/user/user.entity.ts b/src/modules/user/user.entity.ts new file mode 100644 index 0000000..b1f66b8 --- /dev/null +++ b/src/modules/user/user.entity.ts @@ -0,0 +1,69 @@ +import { Exclude } from 'class-transformer' +import { + Column, + Entity, + JoinColumn, + JoinTable, + ManyToMany, + ManyToOne, + OneToMany, + Relation, +} from 'typeorm' + +import { CommonEntity } from '~/common/entity/common.entity' + +import { AccessTokenEntity } from '~/modules/auth/entities/access-token.entity' + +import { DeptEntity } from '~/modules/system/dept/dept.entity' +import { RoleEntity } from '~/modules/system/role/role.entity' + +@Entity({ name: 'sys_user' }) +export class UserEntity extends CommonEntity { + @Column({ unique: true }) + username: string + + @Exclude() + @Column() + password: string + + @Column({ length: 32 }) + psalt: string + + @Column({ nullable: true }) + nickname: string + + @Column({ name: 'avatar', nullable: true }) + avatar: string + + @Column({ nullable: true }) + qq: string + + @Column({ nullable: true }) + email: string + + @Column({ nullable: true }) + phone: string + + @Column({ nullable: true }) + remark: string + + @Column({ type: 'tinyint', nullable: true, default: 1 }) + status: number + + @ManyToMany(() => RoleEntity, role => role.users) + @JoinTable({ + name: 'sys_user_roles', + joinColumn: { name: 'user_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' }, + }) + roles: Relation + + @ManyToOne(() => DeptEntity, dept => dept.users) + @JoinColumn({ name: 'dept_id' }) + dept: Relation + + @OneToMany(() => AccessTokenEntity, accessToken => accessToken.user, { + cascade: true, + }) + accessTokens: Relation +} diff --git a/src/modules/user/user.model.ts b/src/modules/user/user.model.ts new file mode 100644 index 0000000..1493187 --- /dev/null +++ b/src/modules/user/user.model.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger' + +export class AccountInfo { + @ApiProperty({ description: '用户名' }) + username: string + + @ApiProperty({ description: '昵称' }) + nickname: string + + @ApiProperty({ description: '邮箱' }) + email: string + + @ApiProperty({ description: '手机号' }) + phone: string + + @ApiProperty({ description: '备注' }) + remark: string + + @ApiProperty({ description: '头像' }) + avatar: string +} diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts new file mode 100644 index 0000000..bd26eae --- /dev/null +++ b/src/modules/user/user.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { MenuModule } from '../system/menu/menu.module' +import { ParamConfigModule } from '../system/param-config/param-config.module' + +import { RoleModule } from '../system/role/role.module' + +import { UserController } from './user.controller' +import { UserEntity } from './user.entity' +import { UserService } from './user.service' + +const providers = [UserService] + +@Module({ + imports: [ + TypeOrmModule.forFeature([UserEntity]), + RoleModule, + MenuModule, + ParamConfigModule, + ], + controllers: [UserController], + providers: [...providers], + exports: [TypeOrmModule, ...providers], +}) +export class UserModule {} diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts new file mode 100644 index 0000000..806526c --- /dev/null +++ b/src/modules/user/user.service.ts @@ -0,0 +1,372 @@ +import { InjectRedis } from '@liaoliaots/nestjs-redis' +import { BadRequestException, Injectable } from '@nestjs/common' +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm' +import Redis from 'ioredis' +import { isEmpty, isNil } from 'lodash' + +import { EntityManager, In, Like, Repository } from 'typeorm' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { ErrorEnum } from '~/constants/error-code.constant' +import { ROOT_ROLE_ID, SYS_USER_INITPASSWORD } from '~/constants/system.constant' +import { genAuthPVKey, genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey' + +import { paginate } from '~/helper/paginate' +import { Pagination } from '~/helper/paginate/pagination' +import { AccountUpdateDto } from '~/modules/auth/dto/account.dto' +import { RegisterDto } from '~/modules/auth/dto/auth.dto' +import { QQService } from '~/shared/helper/qq.service' + +import { md5, randomValue } from '~/utils' + +import { DeptEntity } from '../system/dept/dept.entity' +import { ParamConfigService } from '../system/param-config/param-config.service' +import { RoleEntity } from '../system/role/role.entity' + +import { UserStatus } from './constant' +import { PasswordUpdateDto } from './dto/password.dto' +import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto' +import { UserEntity } from './user.entity' +import { AccountInfo } from './user.model' + +@Injectable() +export class UserService { + constructor( + @InjectRedis() + private readonly redis: Redis, + @InjectRepository(UserEntity) + private readonly userRepository: Repository, + @InjectRepository(RoleEntity) + private readonly roleRepository: Repository, + @InjectEntityManager() private entityManager: EntityManager, + private readonly paramConfigService: ParamConfigService, + private readonly qqService: QQService, + ) {} + + async findUserById(id: number): Promise { + return this.userRepository + .createQueryBuilder('user') + .where({ + id, + status: UserStatus.Enabled, + }) + .getOne() + } + + async findUserByUserName(username: string): Promise { + return this.userRepository + .createQueryBuilder('user') + .where({ + username, + status: UserStatus.Enabled, + }) + .getOne() + } + + /** + * 获取用户信息 + * @param uid user id + */ + async getAccountInfo(uid: number): Promise { + const user: UserEntity = await this.userRepository + .createQueryBuilder('user') + .leftJoinAndSelect('user.roles', 'role') + .where(`user.id = :uid`, { uid }) + .getOne() + + if (isEmpty(user)) + throw new BusinessException(ErrorEnum.USER_NOT_FOUND) + + delete user?.psalt + + return user + } + + /** + * 更新个人信息 + */ + async updateAccountInfo(uid: number, info: AccountUpdateDto): Promise { + const user = await this.userRepository.findOneBy({ id: uid }) + if (isEmpty(user)) + throw new BusinessException(ErrorEnum.USER_NOT_FOUND) + + const data = { + ...(info.nickname ? { nickname: info.nickname } : null), + ...(info.avatar ? { avatar: info.avatar } : null), + ...(info.email ? { email: info.email } : null), + ...(info.phone ? { phone: info.phone } : null), + ...(info.qq ? { qq: info.qq } : null), + ...(info.remark ? { remark: info.remark } : null), + } + + if (!info.avatar && info.qq) { + // 如果qq不等于原qq,则更新qq头像 + if (info.qq !== user.qq) + data.avatar = await this.qqService.getAvater(info.qq) + } + + await this.userRepository.update(uid, data) + } + + /** + * 更改密码 + */ + async updatePassword(uid: number, dto: PasswordUpdateDto): Promise { + const user = await this.userRepository.findOneBy({ id: uid }) + if (isEmpty(user)) + throw new BusinessException(ErrorEnum.USER_NOT_FOUND) + + const comparePassword = md5(`${dto.oldPassword}${user.psalt}`) + // 原密码不一致,不允许更改 + if (user.password !== comparePassword) + throw new BusinessException(ErrorEnum.PASSWORD_MISMATCH) + + const password = md5(`${dto.newPassword}${user.psalt}`) + await this.userRepository.update({ id: uid }, { password }) + await this.upgradePasswordV(user.id) + } + + /** + * 直接更改密码 + */ + async forceUpdatePassword(uid: number, password: string): Promise { + const user = await this.userRepository.findOneBy({ id: uid }) + + const newPassword = md5(`${password}${user.psalt}`) + await this.userRepository.update({ id: uid }, { password: newPassword }) + await this.upgradePasswordV(user.id) + } + + /** + * 增加系统用户,如果返回false则表示已存在该用户 + */ + async create({ + username, + password, + roleIds, + deptId, + ...data + }: UserDto): Promise { + const exists = await this.userRepository.findOneBy({ + username, + }) + if (!isEmpty(exists)) + throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS) + + await this.entityManager.transaction(async (manager) => { + const salt = randomValue(32) + + if (!password) { + const initPassword = await this.paramConfigService.findValueByKey( + SYS_USER_INITPASSWORD, + ) + password = md5(`${initPassword ?? '123456'}${salt}`) + } + else { + password = md5(`${password ?? '123456'}${salt}`) + } + const u = manager.create(UserEntity, { + username, + password, + ...data, + psalt: salt, + roles: await this.roleRepository.findBy({ id: In(roleIds) }), + dept: await DeptEntity.findOneBy({ id: deptId }), + }) + + const result = await manager.save(u) + return result + }) + } + + /** + * 更新用户信息 + */ + async update( + id: number, + { password, deptId, roleIds, status, ...data }: UserUpdateDto, + ): Promise { + await this.entityManager.transaction(async (manager) => { + if (password) + await this.forceUpdatePassword(id, password) + + await manager.update(UserEntity, id, { + ...data, + status, + }) + + const user = await this.userRepository + .createQueryBuilder('user') + .leftJoinAndSelect('user.roles', 'roles') + .leftJoinAndSelect('user.dept', 'dept') + .where('user.id = :id', { id }) + .getOne() + + await manager + .createQueryBuilder() + .relation(UserEntity, 'roles') + .of(id) + .addAndRemove(roleIds, user.roles) + + await manager + .createQueryBuilder() + .relation(UserEntity, 'dept') + .of(id) + .set(deptId) + + if (status === 0) { + // 禁用状态 + await this.forbidden(id) + } + }) + } + + /** + * 查找用户信息 + * @param id 用户id + */ + async info(id: number): Promise { + const user = await this.userRepository + .createQueryBuilder('user') + .leftJoinAndSelect('user.roles', 'roles') + .leftJoinAndSelect('user.dept', 'dept') + .where('user.id = :id', { id }) + .getOne() + + delete user.password + delete user.psalt + + return user + } + + /** + * 根据ID列表删除用户 + */ + async delete(userIds: number[]): Promise { + const rootUserId = await this.findRootUserId() + if (userIds.includes(rootUserId)) + throw new BadRequestException('不能删除root用户!') + + await this.userRepository.delete(userIds) + } + + /** + * 查找超管的用户ID + */ + async findRootUserId(): Promise { + const user = await this.userRepository.findOneBy({ + roles: { id: ROOT_ROLE_ID }, + }) + return user.id + } + + /** + * 查询用户列表 + */ + async list({ + page, + pageSize, + username, + nickname, + deptId, + email, + status, + }: UserQueryDto): Promise> { + const queryBuilder = this.userRepository + .createQueryBuilder('user') + .leftJoinAndSelect('user.dept', 'dept') + .leftJoinAndSelect('user.roles', 'role') + // .where('user.id NOT IN (:...ids)', { ids: [rootUserId, uid] }) + .where({ + ...(username ? { username: Like(`%${username}%`) } : null), + ...(nickname ? { nickname: Like(`%${nickname}%`) } : null), + ...(email ? { email: Like(`%${email}%`) } : null), + ...(status ? { status } : null), + }) + + if (deptId) + queryBuilder.andWhere('dept.id = :deptId', { deptId }) + + return paginate(queryBuilder, { + page, + pageSize, + }) + } + + /** + * 禁用用户 + */ + async forbidden(uid: number): Promise { + await this.redis.del(genAuthPVKey(uid)) + await this.redis.del(genAuthTokenKey(uid)) + await this.redis.del(genAuthPermKey(uid)) + } + + /** + * 禁用多个用户 + */ + async multiForbidden(uids: number[]): Promise { + if (uids) { + const pvs: string[] = [] + const ts: string[] = [] + const ps: string[] = [] + uids.forEach((uid) => { + pvs.push(genAuthPVKey(uid)) + ts.push(genAuthTokenKey(uid)) + ps.push(genAuthPermKey(uid)) + }) + await this.redis.del(pvs) + await this.redis.del(ts) + await this.redis.del(ps) + } + } + + /** + * 升级用户版本密码 + */ + async upgradePasswordV(id: number): Promise { + // admin:passwordVersion:${param.id} + const v = await this.redis.get(genAuthPVKey(id)) + if (!isEmpty(v)) + await this.redis.set(genAuthPVKey(id), Number.parseInt(v) + 1) + } + + /** + * 判断用户名是否存在 + */ + async exist(username: string) { + const user = await this.userRepository.findOneBy({ username }) + if (isNil(user)) + throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS) + + return true + } + + /** + * 注册 + */ + async register({ username, ...data }: RegisterDto): Promise { + const exists = await this.userRepository.findOneBy({ + username, + }) + if (!isEmpty(exists)) + throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS) + + await this.entityManager.transaction(async (manager) => { + const salt = randomValue(32) + + const password = md5(`${data.password ?? 'a123456'}${salt}`) + + const u = manager.create(UserEntity, { + username, + password, + status: 1, + psalt: salt, + }) + + const user = await manager.save(u) + + return user + }) + } +} diff --git a/src/repl.ts b/src/repl.ts new file mode 100644 index 0000000..649d7ba --- /dev/null +++ b/src/repl.ts @@ -0,0 +1,12 @@ +import { repl } from '@nestjs/core' + +import { AppModule } from './app.module' + +async function bootstrap() { + const replServer = await repl(AppModule) + replServer.setupHistory('.nestjs_repl_history', (err) => { + if (err) + console.error(err) + }) +} +bootstrap() diff --git a/src/setup-swagger.ts b/src/setup-swagger.ts new file mode 100644 index 0000000..4858584 --- /dev/null +++ b/src/setup-swagger.ts @@ -0,0 +1,44 @@ +import { INestApplication, Logger } from '@nestjs/common' +import { ConfigService } from '@nestjs/config' +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' + +import { API_SECURITY_AUTH } from './common/decorators/swagger.decorator' +import { CommonEntity } from './common/entity/common.entity' +import { ResOp, TreeResult } from './common/model/response.model' +import { ConfigKeyPaths, IAppConfig, ISwaggerConfig } from './config' +import { Pagination } from './helper/paginate/pagination' + +export function setupSwagger( + app: INestApplication, + configService: ConfigService, +): void { + const { name, port } = configService.get('app')! + const { enable, path } = configService.get('swagger')! + + if (!enable) + return + + const documentBuilder = new DocumentBuilder() + .setTitle(name) + .setDescription(`${name} API document`) + .setVersion('1.0') + + // auth security + documentBuilder.addSecurity(API_SECURITY_AUTH, { + description: 'Auth', + type: 'apiKey', + in: 'header', + name: 'Authorization', + }) + + const document = SwaggerModule.createDocument(app, documentBuilder.build(), { + ignoreGlobalPrefix: false, + extraModels: [CommonEntity, ResOp, Pagination, TreeResult], + }) + + SwaggerModule.setup(path, app, document) + + // started log + const logger = new Logger('SwaggerModule') + logger.log(`Document running on http://127.0.0.1:${port}/${path}`) +} diff --git a/src/shared/database/constraints/entity-exist.constraint.ts b/src/shared/database/constraints/entity-exist.constraint.ts new file mode 100644 index 0000000..2ce89cd --- /dev/null +++ b/src/shared/database/constraints/entity-exist.constraint.ts @@ -0,0 +1,85 @@ +import { Injectable } from '@nestjs/common' +import { + ValidationArguments, + ValidationOptions, + ValidatorConstraint, + ValidatorConstraintInterface, + registerDecorator, +} from 'class-validator' +import { DataSource, ObjectType, Repository } from 'typeorm' + +interface Condition { + entity: ObjectType + // 如果没有指定字段则使用当前验证的属性作为查询依据 + field?: string +} + +/** + * 查询某个字段的值是否在数据表中存在 + */ +@ValidatorConstraint({ name: 'entityItemExist', async: true }) +@Injectable() +export class EntityExistConstraint implements ValidatorConstraintInterface { + constructor(private dataSource: DataSource) {} + + async validate(value: string, args: ValidationArguments) { + let repo: Repository + + if (!value) + return true + // 默认对比字段是id + let field = 'id' + // 通过传入的 entity 获取其 repository + if ('entity' in args.constraints[0]) { + // 传入的是对象 可以指定对比字段 + field = args.constraints[0].field ?? 'id' + repo = this.dataSource.getRepository(args.constraints[0].entity) + } + else { + // 传入的是实体类 + repo = this.dataSource.getRepository(args.constraints[0]) + } + // 通过查询记录是否存在进行验证 + const item = await repo.findOne({ where: { [field]: value } }) + return !!item + } + + defaultMessage(args: ValidationArguments) { + if (!args.constraints[0]) + return 'Model not been specified!' + + return `All instance of ${args.constraints[0].name} must been exists in databse!` + } +} + +/** + * 数据存在性验证 + * @param params Entity类或验证条件对象 + * @param validationOptions + */ +function IsEntityExist( + entity: ObjectType, + validationOptions?: ValidationOptions, +): (object: Record, propertyName: string) => void + +function IsEntityExist( + condition: { entity: ObjectType, field?: string }, + validationOptions?: ValidationOptions, +): (object: Record, propertyName: string) => void + +function IsEntityExist( + condition: ObjectType | { entity: ObjectType, field?: string }, + validationOptions?: ValidationOptions, +): (object: Record, propertyName: string) => void { + return (object: Record, propertyName: string) => { + registerDecorator({ + target: object.constructor, + propertyName, + options: validationOptions, + constraints: [condition], + validator: EntityExistConstraint, + }) + } +} + +export { IsEntityExist } diff --git a/src/shared/database/constraints/unique.constraint.ts b/src/shared/database/constraints/unique.constraint.ts new file mode 100644 index 0000000..65fa562 --- /dev/null +++ b/src/shared/database/constraints/unique.constraint.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@nestjs/common' +import { + ValidationArguments, + ValidationOptions, + ValidatorConstraint, + ValidatorConstraintInterface, + registerDecorator, +} from 'class-validator' +import { isNil, merge } from 'lodash' +import { DataSource, ObjectType } from 'typeorm' + +interface Condition { + entity: ObjectType + // 如果没有指定字段则使用当前验证的属性作为查询依据 + field?: string +} + +/** + * 验证某个字段的唯一性 + */ +@ValidatorConstraint({ name: 'entityItemUnique', async: true }) +@Injectable() +export class UniqueConstraint implements ValidatorConstraintInterface { + constructor(private dataSource: DataSource) {} + + async validate(value: any, args: ValidationArguments) { + // 获取要验证的模型和字段 + const config: Omit = { + field: args.property, + } + const condition = ('entity' in args.constraints[0] + ? merge(config, args.constraints[0]) + : { + ...config, + entity: args.constraints[0], + }) as unknown as Required + if (!condition.entity) + return false + try { + // 查询是否存在数据,如果已经存在则验证失败 + const repo = this.dataSource.getRepository(condition.entity) + return isNil( + await repo.findOne({ + where: { [condition.field]: value }, + }), + ) + } + catch (err) { + // 如果数据库操作异常则验证失败 + return false + } + } + + defaultMessage(args: ValidationArguments) { + const { entity, property } = args.constraints[0] + const queryProperty = property ?? args.property + if (!(args.object as any).getManager) + return 'getManager function not been found!' + + if (!entity) + return 'Model not been specified!' + + return `${queryProperty} of ${entity.name} must been unique!` + } +} + +/** + * 数据唯一性验证 + * @param params Entity类或验证条件对象 + * @param validationOptions + */ +function IsUnique( + entity: ObjectType, + validationOptions?: ValidationOptions, +): (object: Record, propertyName: string) => void + +function IsUnique( + condition: Condition, + validationOptions?: ValidationOptions, +): (object: Record, propertyName: string) => void + +function IsUnique( + params: ObjectType | Condition, + validationOptions?: ValidationOptions, +) { + return (object: Record, propertyName: string) => { + registerDecorator({ + target: object.constructor, + propertyName, + options: validationOptions, + constraints: [params], + validator: UniqueConstraint, + }) + } +} + +export { IsUnique } diff --git a/src/shared/database/database.module.ts b/src/shared/database/database.module.ts new file mode 100644 index 0000000..acb5526 --- /dev/null +++ b/src/shared/database/database.module.ts @@ -0,0 +1,51 @@ +import { Module } from '@nestjs/common' + +import { ConfigService } from '@nestjs/config' +import { TypeOrmModule } from '@nestjs/typeorm' + +import { DataSource, LoggerOptions } from 'typeorm' + +import { ConfigKeyPaths, IDatabaseConfig } from '~/config' + +import { env } from '~/global/env' + +import { EntityExistConstraint } from './constraints/entity-exist.constraint' +import { UniqueConstraint } from './constraints/unique.constraint' +import { TypeORMLogger } from './typeorm-logger' + +const providers = [EntityExistConstraint, UniqueConstraint] + +@Module({ + imports: [ + TypeOrmModule.forRootAsync({ + inject: [ConfigService], + useFactory: (configService: ConfigService) => { + let loggerOptions: LoggerOptions = env('DB_LOGGING') as 'all' + + try { + // 解析成 js 数组 ['error'] + loggerOptions = JSON.parse(loggerOptions) + } + catch { + // ignore + } + + return { + ...configService.get('database'), + autoLoadEntities: true, + logging: loggerOptions, + logger: new TypeORMLogger(loggerOptions), + } + }, + // dataSource receives the configured DataSourceOptions + // and returns a Promise. + dataSourceFactory: async (options) => { + const dataSource = await new DataSource(options).initialize() + return dataSource + }, + }), + ], + providers, + exports: providers, +}) +export class DatabaseModule {} diff --git a/src/shared/database/typeorm-logger.ts b/src/shared/database/typeorm-logger.ts new file mode 100644 index 0000000..6db7638 --- /dev/null +++ b/src/shared/database/typeorm-logger.ts @@ -0,0 +1,118 @@ +import { Logger } from '@nestjs/common' +import { Logger as ITypeORMLogger, LoggerOptions, QueryRunner } from 'typeorm' + +export class TypeORMLogger implements ITypeORMLogger { + private logger = new Logger(TypeORMLogger.name) + + constructor(private options: LoggerOptions) {} + + logQuery(query: string, parameters?: any[], _queryRunner?: QueryRunner) { + if (!this.isEnable('query')) + return + + const sql + = query + + (parameters && parameters.length + ? ` -- PARAMETERS: ${this.stringifyParams(parameters)}` + : '') + + this.logger.log(`[QUERY]: ${sql}`) + } + + logQueryError( + error: string | Error, + query: string, + parameters?: any[], + _queryRunner?: QueryRunner, + ) { + if (!this.isEnable('error')) + return + + const sql + = query + + (parameters && parameters.length + ? ` -- PARAMETERS: ${this.stringifyParams(parameters)}` + : '') + + this.logger.error([`[FAILED QUERY]: ${sql}`, `[QUERY ERROR]: ${error}`]) + } + + logQuerySlow( + time: number, + query: string, + parameters?: any[], + _queryRunner?: QueryRunner, + ) { + const sql + = query + + (parameters && parameters.length + ? ` -- PARAMETERS: ${this.stringifyParams(parameters)}` + : '') + + this.logger.warn(`[SLOW QUERY: ${time} ms]: ${sql}`) + } + + logSchemaBuild(message: string, _queryRunner?: QueryRunner) { + if (!this.isEnable('schema')) + return + + this.logger.log(message) + } + + logMigration(message: string, _queryRunner?: QueryRunner) { + if (!this.isEnable('migration')) + return + + this.logger.log(message) + } + + log( + level: 'warn' | 'info' | 'log', + message: any, + _queryRunner?: QueryRunner, + ) { + if (!this.isEnable(level)) + return + + switch (level) { + case 'log': + this.logger.debug(message) + break + case 'info': + this.logger.log(message) + break + case 'warn': + this.logger.warn(message) + break + default: + break + } + } + + /** + * Converts parameters to a string. + * Sometimes parameters can have circular objects and therefor we are handle this case too. + */ + private stringifyParams(parameters: any[]) { + try { + return JSON.stringify(parameters) + } + catch (error) { + // most probably circular objects in parameters + return parameters + } + } + + /** + * check enbale log + */ + private isEnable( + level: 'query' | 'schema' | 'error' | 'warn' | 'info' | 'log' | 'migration', + ): boolean { + return ( + this.options === 'all' + || this.options === true + || (Array.isArray(this.options) && this.options.includes(level)) + ) + } +} diff --git a/src/shared/helper/cron.service.ts b/src/shared/helper/cron.service.ts new file mode 100644 index 0000000..94d031b --- /dev/null +++ b/src/shared/helper/cron.service.ts @@ -0,0 +1,48 @@ +import { Injectable, Logger } from '@nestjs/common' +import { ConfigService } from '@nestjs/config' +import { CronExpression } from '@nestjs/schedule' +import dayjs from 'dayjs' + +import { LessThan } from 'typeorm' + +import { CronOnce } from '~/common/decorators/cron-once.decorator' +import { ConfigKeyPaths } from '~/config' +import { AccessTokenEntity } from '~/modules/auth/entities/access-token.entity' + +@Injectable() +export class CronService { + private logger: Logger = new Logger(CronService.name) + constructor( + private readonly configService: ConfigService, + ) {} + + @CronOnce(CronExpression.EVERY_DAY_AT_MIDNIGHT) + async deleteExpiredJWT() { + this.logger.log('--> 开始扫表,清除过期的 token') + + const expiredTokens = await AccessTokenEntity.find({ + where: { + expired_at: LessThan(new Date()), + }, + }) + + let deleteCount = 0 + await Promise.all( + expiredTokens.map(async (token) => { + const { value, created_at } = token + + await AccessTokenEntity.remove(token) + + this.logger.debug( + `--> 删除过期的 token:${value}, 签发于 ${dayjs(created_at).format( + 'YYYY-MM-DD H:mm:ss', + )}`, + ) + + deleteCount += 1 + }), + ) + + this.logger.log(`--> 删除了 ${deleteCount} 个过期的 token`) + } +} diff --git a/src/shared/helper/helper.module.ts b/src/shared/helper/helper.module.ts new file mode 100644 index 0000000..1261c7b --- /dev/null +++ b/src/shared/helper/helper.module.ts @@ -0,0 +1,17 @@ +import { Global, Module, type Provider } from '@nestjs/common' + +import { CronService } from './cron.service' +import { QQService } from './qq.service' + +const providers: Provider[] = [ + CronService, + QQService, +] + +@Global() +@Module({ + imports: [], + providers, + exports: providers, +}) +export class HelperModule {} diff --git a/src/shared/helper/qq.service.ts b/src/shared/helper/qq.service.ts new file mode 100644 index 0000000..9c5f788 --- /dev/null +++ b/src/shared/helper/qq.service.ts @@ -0,0 +1,19 @@ +import { HttpService } from '@nestjs/axios' +import { Injectable } from '@nestjs/common' + +@Injectable() +export class QQService { + constructor(private readonly http: HttpService) {} + + async getNickname(qq: string | number) { + const { data } = await this.http.axiosRef.get( + `https://users.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins=${qq}`, + ) + return data + } + + async getAvater(qq: string | number) { + // https://thirdqq.qlogo.cn/headimg_dl?dst_uin=1743369777&spec=640&img_type=jpg + return `https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=${qq}` + } +} diff --git a/src/shared/logger/logger.module.ts b/src/shared/logger/logger.module.ts new file mode 100644 index 0000000..3757496 --- /dev/null +++ b/src/shared/logger/logger.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common' + +import { LoggerService } from './logger.service' + +@Module({}) +export class LoggerModule { + static forRoot() { + return { + global: true, + module: LoggerModule, + providers: [LoggerService], + exports: [LoggerService], + } + } +} diff --git a/src/shared/logger/logger.service.ts b/src/shared/logger/logger.service.ts new file mode 100644 index 0000000..f2856e1 --- /dev/null +++ b/src/shared/logger/logger.service.ts @@ -0,0 +1,115 @@ +import { ConsoleLogger, ConsoleLoggerOptions, Injectable } from '@nestjs/common' + +import { ConfigService } from '@nestjs/config' +import type { Logger as WinstonLogger } from 'winston' + +import { config, createLogger, format, transports } from 'winston' + +import 'winston-daily-rotate-file' + +import { ConfigKeyPaths } from '~/config' + +export enum LogLevel { + ERROR = 'error', + WARN = 'warn', + INFO = 'info', + DEBUG = 'debug', + VERBOSE = 'verbose', +} + +@Injectable() +export class LoggerService extends ConsoleLogger { + private winstonLogger: WinstonLogger + + constructor( + context: string, + options: ConsoleLoggerOptions, + private configService: ConfigService, + ) { + super(context, options) + this.initWinston() + } + + protected get level(): LogLevel { + return this.configService.get('app.logger.level', { infer: true }) as LogLevel + } + + protected get maxFiles(): number { + return this.configService.get('app.logger.maxFiles', { infer: true }) + } + + protected initWinston(): void { + this.winstonLogger = createLogger({ + levels: config.npm.levels, + format: format.combine( + format.errors({ stack: true }), + format.timestamp(), + format.json(), + ), + transports: [ + new transports.DailyRotateFile({ + level: this.level, + filename: 'logs/app.%DATE%.log', + datePattern: 'YYYY-MM-DD', + maxFiles: this.maxFiles, + format: format.combine(format.timestamp(), format.json()), + auditFile: 'logs/.audit/app.json', + }), + new transports.DailyRotateFile({ + level: LogLevel.ERROR, + filename: 'logs/app-error.%DATE%.log', + datePattern: 'YYYY-MM-DD', + maxFiles: this.maxFiles, + format: format.combine(format.timestamp(), format.json()), + auditFile: 'logs/.audit/app-error.json', + }), + ], + }) + + // if (isDev) { + // this.winstonLogger.add( + // new transports.Console({ + // level: this.level, + // format: format.combine( + // format.simple(), + // format.colorize({ all: true }), + // ), + // }), + // ); + // } + } + + verbose(message: any, context?: string): void { + super.verbose.apply(this, [message, context]) + + this.winstonLogger.log(LogLevel.VERBOSE, message, { context }) + } + + debug(message: any, context?: string): void { + super.debug.apply(this, [message, context]) + + this.winstonLogger.log(LogLevel.DEBUG, message, { context }) + } + + log(message: any, context?: string): void { + super.log.apply(this, [message, context]) + + this.winstonLogger.log(LogLevel.INFO, message, { context }) + } + + warn(message: any, context?: string): void { + super.warn.apply(this, [message, context]) + + this.winstonLogger.log(LogLevel.WARN, message) + } + + error(message: any, stack?: string, context?: string): void { + super.error.apply(this, [message, stack, context]) + + const hasStack = !!context + this.winstonLogger.log(LogLevel.ERROR, { + context: hasStack ? context : stack, + message: hasStack ? new Error(message) : message, + }) + } +} diff --git a/src/shared/mailer/mailer.module.ts b/src/shared/mailer/mailer.module.ts new file mode 100644 index 0000000..9ee4128 --- /dev/null +++ b/src/shared/mailer/mailer.module.ts @@ -0,0 +1,42 @@ +import { join } from 'node:path' + +import { Module, Provider } from '@nestjs/common' +import { ConfigModule, ConfigService } from '@nestjs/config' +import { MailerModule as NestMailerModule } from '@nestjs-modules/mailer' +import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter' + +import { ConfigKeyPaths, IAppConfig, IMailerConfig } from '~/config' + +import { MailerService } from './mailer.service' + +const providers: Provider[] = [ + MailerService, +] + +@Module({ + imports: [ + NestMailerModule.forRootAsync({ + imports: [ConfigModule], + useFactory: (configService: ConfigService) => ({ + transport: configService.get('mailer'), + defaults: { + from: { + name: configService.get('app').name, + address: configService.get('mailer').auth.user, + }, + }, + template: { + dir: join(__dirname, '..', '..', '/assets/templates'), + adapter: new HandlebarsAdapter(), + options: { + strict: true, + }, + }, + }), + inject: [ConfigService], + }), + ], + providers, + exports: providers, +}) +export class MailerModule {} diff --git a/src/shared/mailer/mailer.service.ts b/src/shared/mailer/mailer.service.ts new file mode 100644 index 0000000..16aba4a --- /dev/null +++ b/src/shared/mailer/mailer.service.ts @@ -0,0 +1,151 @@ +import { InjectRedis } from '@liaoliaots/nestjs-redis' +import { Inject, Injectable } from '@nestjs/common' + +import { MailerService as NestMailerService } from '@nestjs-modules/mailer' +import dayjs from 'dayjs' + +import Redis from 'ioredis' + +import { BusinessException } from '~/common/exceptions/biz.exception' +import { AppConfig, IAppConfig } from '~/config' +import { ErrorEnum } from '~/constants/error-code.constant' +import { randomValue } from '~/utils' + +@Injectable() +export class MailerService { + constructor( + @Inject(AppConfig.KEY) private appConfig: IAppConfig, + @InjectRedis() private redis: Redis, + private mailerService: NestMailerService, + ) {} + + async log(to: string, code: string, ip: string) { + const getRemainTime = () => { + const now = dayjs() + return now.endOf('day').diff(now, 'second') + } + + await this.redis.set(`captcha:${to}`, code, 'EX', 60 * 5) + + const limitCountOfDay = await this.redis.get(`captcha:${to}:limit-day`) + const ipLimitCountOfDay = await this.redis.get(`ip:${ip}:send:limit-day`) + + await this.redis.set(`ip:${ip}:send:limit`, 1, 'EX', 60) + await this.redis.set(`captcha:${to}:limit`, 1, 'EX', 60) + await this.redis.set( + `captcha:${to}:send:limit-count-day`, + limitCountOfDay, + 'EX', + getRemainTime(), + ) + await this.redis.set( + `ip:${ip}:send:limit-count-day`, + ipLimitCountOfDay, + 'EX', + getRemainTime(), + ) + } + + async checkCode(to, code) { + const ret = await this.redis.get(`captcha:${to}`) + if (ret !== code) + throw new BusinessException(ErrorEnum.INVALID_VERIFICATION_CODE) + + await this.redis.del(`captcha:${to}`) + } + + async checkLimit(to, ip) { + const LIMIT_TIME = 5 + + // ip限制 + const ipLimit = await this.redis.get(`ip:${ip}:send:limit`) + if (ipLimit) + throw new BusinessException(ErrorEnum.TOO_MANY_REQUESTS) + + // 1分钟最多接收1条 + const limit = await this.redis.get(`captcha:${to}:limit`) + if (limit) + throw new BusinessException(ErrorEnum.TOO_MANY_REQUESTS) + + // 1天一个邮箱最多接收5条 + let limitCountOfDay: string | number = await this.redis.get( + `captcha:${to}:limit-day`, + ) + limitCountOfDay = limitCountOfDay ? Number(limitCountOfDay) : 0 + if (limitCountOfDay > LIMIT_TIME) { + throw new BusinessException( + ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY, + ) + } + + // 1天一个ip最多发送5条 + let ipLimitCountOfDay: string | number = await this.redis.get( + `ip:${ip}:send:limit-day`, + ) + ipLimitCountOfDay = ipLimitCountOfDay ? Number(ipLimitCountOfDay) : 0 + if (ipLimitCountOfDay > LIMIT_TIME) { + throw new BusinessException( + ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY, + ) + } + } + + async send( + to, + subject, + content: string, + type: 'text' | 'html' = 'text', + ): Promise { + if (type === 'text') { + return this.mailerService.sendMail({ + to, + subject, + text: content, + }) + } + else { + return this.mailerService.sendMail({ + to, + subject, + html: content, + }) + } + } + + async sendVerificationCode(to, code = randomValue(4, '1234567890')) { + const subject = `[${this.appConfig.name}] 验证码` + + try { + await this.mailerService.sendMail({ + to, + subject, + template: './verification-code-zh', + context: { + code, + }, + }) + } + catch (error) { + console.log(error) + throw new BusinessException(ErrorEnum.VERIFICATION_CODE_SEND_FAILED) + } + + return { + to, + code, + } + } + + // async sendUserConfirmation(user: UserEntity, token: string) { + // const url = `example.com/auth/confirm?token=${token}` + // await this.mailerService.sendMail({ + // to: user.email, + // subject: 'Confirm your Email', + // template: './confirmation', + // context: { + // name: user.name, + // url, + // }, + // }) + // } +} diff --git a/src/shared/redis/cache.service.ts b/src/shared/redis/cache.service.ts new file mode 100644 index 0000000..90c8707 --- /dev/null +++ b/src/shared/redis/cache.service.ts @@ -0,0 +1,68 @@ +import { CACHE_MANAGER } from '@nestjs/cache-manager' +import { Inject, Injectable } from '@nestjs/common' +import { Emitter } from '@socket.io/redis-emitter' +import { Cache } from 'cache-manager' +import type { Redis } from 'ioredis' + +import { RedisIoAdapterKey } from '~/common/adapters/socket.adapter' + +import { API_CACHE_PREFIX } from '~/constants/cache.constant' +import { getRedisKey } from '~/utils/redis.util' + +// 获取器 +export type TCacheKey = string +export type TCacheResult = Promise + +@Injectable() +export class CacheService { + private cache!: Cache + + private ioRedis!: Redis + constructor(@Inject(CACHE_MANAGER) cache: Cache) { + this.cache = cache + } + + private get redisClient(): Redis { + // eslint-disable-next-line ts/ban-ts-comment + // @ts-expect-error + return this.cache.store.client + } + + public get(key: TCacheKey): TCacheResult { + return this.cache.get(key) + } + + public set(key: TCacheKey, value: any, milliseconds: number) { + return this.cache.set(key, value, milliseconds) + } + + public getClient() { + return this.redisClient + } + + private _emitter: Emitter + + public get emitter(): Emitter { + if (this._emitter) + return this._emitter + + this._emitter = new Emitter(this.redisClient, { + key: RedisIoAdapterKey, + }) + + return this._emitter + } + + public async cleanCatch() { + const redis = this.getClient() + const keys: string[] = await redis.keys(`${API_CACHE_PREFIX}*`) + await Promise.all(keys.map(key => redis.del(key))) + } + + public async cleanAllRedisKey() { + const redis = this.getClient() + const keys: string[] = await redis.keys(getRedisKey('*')) + + await Promise.all(keys.map(key => redis.del(key))) + } +} diff --git a/src/shared/redis/redis-subpub.ts b/src/shared/redis/redis-subpub.ts new file mode 100644 index 0000000..7634753 --- /dev/null +++ b/src/shared/redis/redis-subpub.ts @@ -0,0 +1,68 @@ +import { Logger } from '@nestjs/common' +import IORedis from 'ioredis' +import type { Redis, RedisOptions } from 'ioredis' + +export class RedisSubPub { + public pubClient: Redis + public subClient: Redis + constructor( + private redisConfig: RedisOptions, + private channelPrefix: string = 'm-shop-channel#', + ) { + this.init() + } + + public init() { + const redisOptions: RedisOptions = { + host: this.redisConfig.host, + port: this.redisConfig.port, + } + + if (this.redisConfig.password) + redisOptions.password = this.redisConfig.password + + const pubClient = new IORedis(redisOptions) + const subClient = pubClient.duplicate() + this.pubClient = pubClient + this.subClient = subClient + } + + public async publish(event: string, data: any) { + const channel = this.channelPrefix + event + const _data = JSON.stringify(data) + if (event !== 'log') + Logger.debug(`发布事件:${channel} <- ${_data}`, RedisSubPub.name) + + await this.pubClient.publish(channel, _data) + } + + private ctc = new WeakMap void>() + + public async subscribe(event: string, callback: (data: any) => void) { + const myChannel = this.channelPrefix + event + this.subClient.subscribe(myChannel) + + const cb = (channel, message) => { + if (channel === myChannel) { + if (event !== 'log') + Logger.debug(`接收事件:${channel} -> ${message}`, RedisSubPub.name) + + callback(JSON.parse(message)) + } + } + + this.ctc.set(callback, cb) + this.subClient.on('message', cb) + } + + public async unsubscribe(event: string, callback: (data: any) => void) { + const channel = this.channelPrefix + event + this.subClient.unsubscribe(channel) + const cb = this.ctc.get(callback) + if (cb) { + this.subClient.off('message', cb) + + this.ctc.delete(callback) + } + } +} diff --git a/src/shared/redis/redis.constant.ts b/src/shared/redis/redis.constant.ts new file mode 100644 index 0000000..9b82a3b --- /dev/null +++ b/src/shared/redis/redis.constant.ts @@ -0,0 +1 @@ +export const REDIS_PUBSUB = Symbol('REDIS_PUBSUB') diff --git a/src/shared/redis/redis.module.ts b/src/shared/redis/redis.module.ts new file mode 100644 index 0000000..5c19851 --- /dev/null +++ b/src/shared/redis/redis.module.ts @@ -0,0 +1,60 @@ +import { RedisModule as NestRedisModule } from '@liaoliaots/nestjs-redis' +import { CacheModule } from '@nestjs/cache-manager' +import { Global, Module, Provider } from '@nestjs/common' +import { ConfigModule, ConfigService } from '@nestjs/config' + +import { redisStore } from 'cache-manager-ioredis-yet' +import { RedisOptions } from 'ioredis' + +import { ConfigKeyPaths, IRedisConfig } from '~/config' + +import { CacheService } from './cache.service' +import { RedisSubPub } from './redis-subpub' +import { REDIS_PUBSUB } from './redis.constant' +import { RedisPubSubService } from './subpub.service' + +const providers: Provider[] = [ + CacheService, + { + provide: REDIS_PUBSUB, + useFactory: (configService: ConfigService) => { + const redisOptions: RedisOptions = configService.get('redis') + return new RedisSubPub(redisOptions) + }, + inject: [ConfigService], + }, + RedisPubSubService, +] + +@Global() +@Module({ + imports: [ + // cache + CacheModule.registerAsync({ + imports: [ConfigModule], + useFactory: (configService: ConfigService) => { + const redisOptions: RedisOptions = configService.get('redis') + + return { + isGlobal: true, + store: redisStore, + isCacheableValue: () => true, + ...redisOptions, + } + }, + inject: [ConfigService], + }), + // redis + NestRedisModule.forRootAsync({ + imports: [ConfigModule], + useFactory: (configService: ConfigService) => ({ + readyLog: true, + config: configService.get('redis'), + }), + inject: [ConfigService], + }), + ], + providers, + exports: [...providers, CacheModule], +}) +export class RedisModule {} diff --git a/src/shared/redis/subpub.service.ts b/src/shared/redis/subpub.service.ts new file mode 100644 index 0000000..2ba347f --- /dev/null +++ b/src/shared/redis/subpub.service.ts @@ -0,0 +1,21 @@ +import { Inject, Injectable } from '@nestjs/common' + +import { RedisSubPub } from './redis-subpub' +import { REDIS_PUBSUB } from './redis.constant' + +@Injectable() +export class RedisPubSubService { + constructor(@Inject(REDIS_PUBSUB) private readonly redisSubPub: RedisSubPub) {} + + public async publish(event: string, data: any) { + return this.redisSubPub.publish(event, data) + } + + public async subscribe(event: string, callback: (data: any) => void) { + return this.redisSubPub.subscribe(event, callback) + } + + public async unsubscribe(event: string, callback: (data: any) => void) { + return this.redisSubPub.unsubscribe(event, callback) + } +} diff --git a/src/shared/shared.module.ts b/src/shared/shared.module.ts new file mode 100644 index 0000000..59cf92e --- /dev/null +++ b/src/shared/shared.module.ts @@ -0,0 +1,49 @@ +import { HttpModule } from '@nestjs/axios' +import { Global, Module } from '@nestjs/common' +import { EventEmitterModule } from '@nestjs/event-emitter' +import { ScheduleModule } from '@nestjs/schedule' +import { ThrottlerModule } from '@nestjs/throttler' + +import { isDev } from '~/global/env' + +import { HelperModule } from './helper/helper.module' +import { LoggerModule } from './logger/logger.module' +import { MailerModule } from './mailer/mailer.module' + +import { RedisModule } from './redis/redis.module' + +@Global() +@Module({ + imports: [ + // logger + LoggerModule.forRoot(), + // http + HttpModule, + // schedule + ScheduleModule.forRoot(), + // rate limit + ThrottlerModule.forRoot([ + { + limit: 3, + ttl: 60000, + }, + ]), + EventEmitterModule.forRoot({ + wildcard: true, + delimiter: '.', + newListener: false, + removeListener: false, + maxListeners: 20, + verboseMemoryLeak: isDev, + ignoreErrors: false, + }), + // redis + RedisModule, + // mailer + MailerModule, + // helper + HelperModule, + ], + exports: [HttpModule, MailerModule, RedisModule, HelperModule], +}) +export class SharedModule {} diff --git a/src/socket/base.gateway.ts b/src/socket/base.gateway.ts new file mode 100644 index 0000000..a664892 --- /dev/null +++ b/src/socket/base.gateway.ts @@ -0,0 +1,33 @@ +import type { Socket } from 'socket.io' + +import { BusinessEvents } from './business-event.constant' + +export abstract class BaseGateway { + public gatewayMessageFormat( + type: BusinessEvents, + message: any, + code?: number, + ) { + return { + type, + data: message, + code, + } + } + + handleDisconnect(client: Socket) { + client.send( + this.gatewayMessageFormat(BusinessEvents.GATEWAY_CONNECT, 'WebSocket 断开'), + ) + } + + handleConnect(client: Socket) { + client.send( + this.gatewayMessageFormat(BusinessEvents.GATEWAY_CONNECT, 'WebSocket 已连接'), + ) + } +} + +export abstract class BroadcastBaseGateway extends BaseGateway { + broadcast(event: BusinessEvents, data: any) {} +} diff --git a/src/socket/business-event.constant.ts b/src/socket/business-event.constant.ts new file mode 100644 index 0000000..1864d6c --- /dev/null +++ b/src/socket/business-event.constant.ts @@ -0,0 +1,11 @@ +export enum BusinessEvents { + GATEWAY_CONNECT = 'GATEWAY_CONNECT', + GATEWAY_DISCONNECT = 'GATEWAY_DISCONNECT', + + AUTH_FAILED = 'AUTH_FAILED', + + // 用户上线事件 + USER_ONLINE = 'USER_ONLINE', + USER_OFFLINE = 'USER_OFFLINE', + USER_KICK = 'USER_KICK', +} diff --git a/src/socket/events/admin.gateway.ts b/src/socket/events/admin.gateway.ts new file mode 100644 index 0000000..60f930a --- /dev/null +++ b/src/socket/events/admin.gateway.ts @@ -0,0 +1,37 @@ +import { JwtService } from '@nestjs/jwt' +import { + GatewayMetadata, + OnGatewayConnection, + OnGatewayDisconnect, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets' + +import { Server } from 'socket.io' + +import { AuthService } from '~/modules/auth/auth.service' +import { CacheService } from '~/shared/redis/cache.service' + +import { createAuthGateway } from '../shared/auth.gateway' + +const AuthGateway = createAuthGateway({ namespace: 'admin' }) + +@WebSocketGateway({ namespace: 'admin' }) +export class AdminEventsGateway + extends AuthGateway + implements OnGatewayConnection, OnGatewayDisconnect { + constructor( + protected readonly jwtService: JwtService, + protected readonly authService: AuthService, + private readonly cacheService: CacheService, + ) { + super(jwtService, authService, cacheService) + } + + @WebSocketServer() + protected _server: Server + + get server() { + return this._server + } +} diff --git a/src/socket/events/web.gateway.ts b/src/socket/events/web.gateway.ts new file mode 100644 index 0000000..94b83a3 --- /dev/null +++ b/src/socket/events/web.gateway.ts @@ -0,0 +1,36 @@ +import { JwtService } from '@nestjs/jwt' +import { + GatewayMetadata, + OnGatewayConnection, + OnGatewayDisconnect, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets' + +import { Server } from 'socket.io' + +import { TokenService } from '~/modules/auth/services/token.service' +import { CacheService } from '~/shared/redis/cache.service' + +import { createAuthGateway } from '../shared/auth.gateway' + +const AuthGateway = createAuthGateway({ namespace: 'web' }) +@WebSocketGateway({ namespace: 'web' }) +export class WebEventsGateway + extends AuthGateway + implements OnGatewayConnection, OnGatewayDisconnect { + constructor( + protected readonly jwtService: JwtService, + protected readonly tokenService: TokenService, + private readonly cacheService: CacheService, + ) { + super(jwtService, tokenService, cacheService) + } + + @WebSocketServer() + protected _server: Server + + get server() { + return this._server + } +} diff --git a/src/socket/shared/auth.gateway.ts b/src/socket/shared/auth.gateway.ts new file mode 100644 index 0000000..8285338 --- /dev/null +++ b/src/socket/shared/auth.gateway.ts @@ -0,0 +1,120 @@ +import { } from '@nestjs/common' +import { OnEvent } from '@nestjs/event-emitter' +import { JwtService } from '@nestjs/jwt' +import type { + OnGatewayConnection, + OnGatewayDisconnect, +} from '@nestjs/websockets' +import { WebSocketServer } from '@nestjs/websockets' +import { Namespace } from 'socket.io' +import type { Socket } from 'socket.io' + +import { EventBusEvents } from '~/constants/event-bus.constant' + +import { TokenService } from '~/modules/auth/services/token.service' +import { CacheService } from '~/shared/redis/cache.service' + +import { BroadcastBaseGateway } from '../base.gateway' +import { BusinessEvents } from '../business-event.constant' + +export interface AuthGatewayOptions { + namespace: string +} + +// eslint-disable-next-line ts/ban-ts-comment +// @ts-expect-error +export interface IAuthGateway extends OnGatewayConnection, OnGatewayDisconnect, BroadcastBaseGateway {} + +export function createAuthGateway(options: AuthGatewayOptions): new (...args: any[]) => IAuthGateway { + const { namespace } = options + + class AuthGateway extends BroadcastBaseGateway implements IAuthGateway { + constructor( + protected readonly jwtService: JwtService, + protected readonly tokenService: TokenService, + private readonly cacheService: CacheService, + ) { + super() + } + + @WebSocketServer() + protected namespace: Namespace + + async authFailed(client: Socket) { + client.send( + this.gatewayMessageFormat(BusinessEvents.AUTH_FAILED, '认证失败'), + ) + client.disconnect() + } + + async authToken(token: string): Promise { + if (typeof token !== 'string') + return false + + const validJwt = async () => { + try { + const ok = await this.jwtService.verify(token) + + if (!ok) + return false + } + catch { + return false + } + // is not crash, is verify + return true + } + + return await validJwt() + } + + async handleConnection(client: Socket) { + const token + = client.handshake.query.token + || client.handshake.headers.authorization + || client.handshake.headers.Authorization + if (!token) + return this.authFailed(client) + + if (!(await this.authToken(token as string))) + return this.authFailed(client) + + super.handleConnect(client) + + const sid = client.id + this.tokenSocketIdMap.set(token.toString(), sid) + } + + handleDisconnect(client: Socket) { + super.handleDisconnect(client) + } + + tokenSocketIdMap = new Map() + + @OnEvent(EventBusEvents.TokenExpired) + handleTokenExpired(token: string) { + // consola.debug(`token expired: ${token}`) + + const server = this.namespace.server + const sid = this.tokenSocketIdMap.get(token) + if (!sid) + return false + + const socket = server.of(`/${namespace}`).sockets.get(sid) + if (socket) { + socket.disconnect() + super.handleDisconnect(socket) + return true + } + return false + } + + override broadcast(event: BusinessEvents, data: any) { + this.cacheService.emitter + .of(`/${namespace}`) + .emit('message', this.gatewayMessageFormat(event, data)) + } + } + + return AuthGateway +} diff --git a/src/socket/socket.module.ts b/src/socket/socket.module.ts new file mode 100644 index 0000000..a1a1de8 --- /dev/null +++ b/src/socket/socket.module.ts @@ -0,0 +1,16 @@ +import { Module, Provider, forwardRef } from '@nestjs/common' + +import { AuthModule } from '../modules/auth/auth.module' +import { SystemModule } from '../modules/system/system.module' + +import { AdminEventsGateway } from './events/admin.gateway' +import { WebEventsGateway } from './events/web.gateway' + +const providers: Provider[] = [AdminEventsGateway, WebEventsGateway] + +@Module({ + imports: [forwardRef(() => SystemModule), AuthModule], + providers, + exports: [...providers], +}) +export class SocketModule {} diff --git a/src/utils/captcha.util.ts b/src/utils/captcha.util.ts new file mode 100644 index 0000000..4f416bc --- /dev/null +++ b/src/utils/captcha.util.ts @@ -0,0 +1,19 @@ +import svgCaptcha from 'svg-captcha' + +export function createCaptcha() { + return svgCaptcha.createMathExpr({ + size: 4, + ignoreChars: '0o1iIl', + noise: 2, + color: true, + background: '#eee', + fontSize: 50, + width: 110, + height: 38, + }) +} + +export function createMathExpr() { + const options = {} + return svgCaptcha.createMathExpr(options) +} diff --git a/src/utils/crypto.util.ts b/src/utils/crypto.util.ts new file mode 100644 index 0000000..143938b --- /dev/null +++ b/src/utils/crypto.util.ts @@ -0,0 +1,30 @@ +import CryptoJS from 'crypto-js' + +const key = CryptoJS.enc.Utf8.parse('buqiyuanabcdefe9bc') +const iv = CryptoJS.enc.Utf8.parse('0123456789buqiyuan') + +export function aesEncrypt(data) { + if (!data) + return data + const enc = CryptoJS.AES.encrypt(data, key, { + iv, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }) + return enc.toString() +} + +export function aesDecrypt(data) { + if (!data) + return data + const dec = CryptoJS.AES.decrypt(data, key, { + iv, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }) + return dec.toString(CryptoJS.enc.Utf8) +} + +export function md5(str: string) { + return CryptoJS.MD5(str).toString() +} diff --git a/src/utils/date.util.ts b/src/utils/date.util.ts new file mode 100644 index 0000000..54a15d1 --- /dev/null +++ b/src/utils/date.util.ts @@ -0,0 +1,23 @@ +import dayjs from 'dayjs' +import { isDate } from 'lodash' + +const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss' +const DATE_FORMAT = 'YYYY-MM-DD' + +export function formatToDateTime( + date: string | number | Date | dayjs.Dayjs | null | undefined = undefined, + format = DATE_TIME_FORMAT, +): string { + return dayjs(date).format(format) +} + +export function formatToDate( + date: string | number | Date | dayjs.Dayjs | null | undefined = undefined, + format = DATE_FORMAT, +): string { + return dayjs(date).format(format) +} + +export function isDateObject(obj: unknown): boolean { + return isDate(obj) || dayjs.isDayjs(obj) +} diff --git a/src/utils/file.util.ts b/src/utils/file.util.ts new file mode 100644 index 0000000..6d6ba48 --- /dev/null +++ b/src/utils/file.util.ts @@ -0,0 +1,89 @@ +import fs from 'node:fs' +import path from 'node:path' + +import { MultipartFile } from '@fastify/multipart' + +import dayjs from 'dayjs' + +enum Type { + IMAGE = '图片', + TXT = '文档', + MUSIC = '音乐', + VIDEO = '视频', + OTHER = '其他', +} + +export function getFileType(extName: string) { + const documents = 'txt doc pdf ppt pps xlsx xls docx' + const music = 'mp3 wav wma mpa ram ra aac aif m4a' + const video = 'avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg' + const image + = 'bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg' + if (image.includes(extName)) + return Type.IMAGE + + if (documents.includes(extName)) + return Type.TXT + + if (music.includes(extName)) + return Type.MUSIC + + if (video.includes(extName)) + return Type.VIDEO + + return Type.OTHER +} + +export function getName(fileName: string) { + if (fileName.includes('.')) + return fileName.split('.')[0] + + return fileName +} + +export function getExtname(fileName: string) { + return path.extname(fileName).replace('.', '') +} + +export function getSize(bytes: number, decimals = 2) { + if (bytes === 0) + return '0 Bytes' + + const k = 1024 + const dm = decimals < 0 ? 0 : decimals + const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + + const i = Math.floor(Math.log(bytes) / Math.log(k)) + + return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}` +} + +export function fileRename(fileName: string) { + const name = fileName.split('.')[0] + const extName = path.extname(fileName) + const time = dayjs().format('YYYYMMDDHHmmSSS') + return `${name}-${time}${extName}` +} + +export function getFilePath(name: string) { + return `/upload/${name}` +} + +export function saveLocalFile(buffer: Buffer, name: string) { + const filePath = path.join(__dirname, '../../', 'public/upload', name) + const writeStream = fs.createWriteStream(filePath) + writeStream.write(buffer) +} + +export async function saveFile(file: MultipartFile, name: string) { + const filePath = path.join(__dirname, '../../', 'public/upload', name) + const writeStream = fs.createWriteStream(filePath) + const buffer = await file.toBuffer() + writeStream.write(buffer) +} + +export async function deleteFile(name: string) { + fs.unlink(path.join(__dirname, '../../', 'public', name), () => { + // console.log(error); + }) +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..b889238 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,11 @@ +export * from './captcha.util' +export * from './crypto.util' +export * from './date.util' +export * from './file.util' +export * from './ip.util' +export * from './is.util' +export * from './list2tree.util' +export * from './permission.util' +export * from './redis.util' +export * from './schedule.util' +export * from './tool.util' diff --git a/src/utils/ip.util.ts b/src/utils/ip.util.ts new file mode 100644 index 0000000..8c6ce06 --- /dev/null +++ b/src/utils/ip.util.ts @@ -0,0 +1,66 @@ +/** + * @module utils/ip + * @description IP utility functions + */ +import type { IncomingMessage } from 'node:http' + +import axios from 'axios' +import type { FastifyRequest } from 'fastify' + +/* 判断IP是不是内网 */ +function isLAN(ip: string) { + ip.toLowerCase() + if (ip === 'localhost') + return true + let a_ip = 0 + if (ip === '') + return false + const aNum = ip.split('.') + if (aNum.length !== 4) + return false + a_ip += Number.parseInt(aNum[0]) << 24 + a_ip += Number.parseInt(aNum[1]) << 16 + a_ip += Number.parseInt(aNum[2]) << 8 + a_ip += Number.parseInt(aNum[3]) << 0 + a_ip = (a_ip >> 16) & 0xFFFF + return ( + a_ip >> 8 === 0x7F + || a_ip >> 8 === 0xA + || a_ip === 0xC0A8 + || (a_ip >= 0xAC10 && a_ip <= 0xAC1F) + ) +} + +export function getIp(request: FastifyRequest | IncomingMessage) { + const req = request as any + + let ip: string + = request.headers['x-forwarded-for'] + || request.headers['X-Forwarded-For'] + || request.headers['X-Real-IP'] + || request.headers['x-real-ip'] + || req?.ip + || req?.raw?.connection?.remoteAddress + || req?.raw?.socket?.remoteAddress + || undefined + if (ip && ip.split(',').length > 0) + ip = ip.split(',')[0] + + return ip +} + +export async function getIpAddress(ip: string) { + if (isLAN(ip)) + return '内网IP' + try { + let { data } = await axios.get( + `https://whois.pconline.com.cn/ipJson.jsp?ip=${ip}&json=true`, + { responseType: 'arraybuffer' }, + ) + data = new TextDecoder('gbk').decode(data) + data = JSON.parse(data) + return data.addr.trim().split(' ').at(0) + } catch (error) { + return '第三方接口请求失败' + } +} diff --git a/src/utils/is.util.ts b/src/utils/is.util.ts new file mode 100644 index 0000000..ce9e048 --- /dev/null +++ b/src/utils/is.util.ts @@ -0,0 +1,4 @@ +export function isExternal(path: string): boolean { + const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/ + return reg.test(path) +} diff --git a/src/utils/list2tree.util.ts b/src/utils/list2tree.util.ts new file mode 100644 index 0000000..663a9f4 --- /dev/null +++ b/src/utils/list2tree.util.ts @@ -0,0 +1,86 @@ +export type TreeNode = T & { + id: number + parentId: number + children?: TreeNode[] +} + +export type ListNode = T & { + id: number + parentId: number +} + +export function list2Tree( + items: T, + parentId: number | null = null, +): TreeNode[] { + return items + .filter(item => item.parentId === parentId) + .map((item) => { + const children = list2Tree(items, item.id) + return { + ...item, + ...(children.length ? { children } : null), + } + }) +} + +/** + * 过滤树,返回列表数据 + * @param treeData + * @param key 用于过滤的字段 + * @param value 用于过滤的值 + * @returns + */ +export function filterTree2List(treeData, key, value) { + const filterChildrenTree = (resTree, treeItem) => { + if (treeItem[key].includes(value)) { + resTree.push(treeItem) + return resTree + } + if (Array.isArray(treeItem.children)) { + const children = treeItem.children.reduce(filterChildrenTree, []) + + const data = { ...treeItem, children } + + if (children.length) + resTree.push({ ...data }) + } + return resTree + } + return treeData.reduce(filterChildrenTree, []) +} + +/** + * 过滤树,并保留原有的结构 + * @param treeData + * @param predicate + * @returns + */ +export function filterTree( + treeData: TreeNode[], + predicate: (data: T) => boolean, +): TreeNode[] { + function filter(treeData: TreeNode[]): TreeNode[] { + if (!treeData?.length) + return treeData + + return treeData.filter((data) => { + if (!predicate(data)) + return false + + data.children = filter(data.children) + return true + }) + } + + return filter(treeData) || [] +} + +export function deleteEmptyChildren(arr: any) { + arr?.forEach((node) => { + if (node.children?.length === 0) + delete node.children + else + deleteEmptyChildren(node.children) + }) +} diff --git a/src/utils/permission.util.ts b/src/utils/permission.util.ts new file mode 100644 index 0000000..6aa34ce --- /dev/null +++ b/src/utils/permission.util.ts @@ -0,0 +1,158 @@ +import { ForbiddenException } from '@nestjs/common' + +import { envBoolean } from '~/global/env' +import { MenuEntity } from '~/modules/system/menu/menu.entity' +import { isExternal } from '~/utils/is.util' + +function createRoute(menu: MenuEntity, _isRoot) { + const commonMeta = { + title: menu.name, + icon: menu.icon, + isExt: menu.isExt, + extOpenMode: menu.extOpenMode, + type: menu.type, + orderNo: menu.orderNo, + show: menu.show, + activeMenu: menu.activeMenu, + status: menu.status, + keepAlive: menu.keepAlive, + } + + if (isExternal(menu.path)) { + return { + id: menu.id, + path: menu.path, + // component: 'IFrame', + name: menu.name, + meta: { ...commonMeta }, + } + } + + // 目录 + if (menu.type === 0) { + return { + id: menu.id, + path: menu.path, + component: menu.component, + name: menu.name, + meta: { ...commonMeta }, + } + } + + return { + id: menu.id, + path: menu.path, + name: menu.name, + component: menu.component, + meta: { + ...commonMeta, + }, + } +} + +function filterAsyncRoutes(menus: MenuEntity[], parentRoute) { + const res = [] + + menus.forEach((menu) => { + if (menu.type === 2 || !menu.status) { + // 如果是权限或禁用直接跳过 + return + } + // 根级别菜单渲染 + let realRoute + if (!parentRoute && !menu.parentId && menu.type === 1) { + // 根菜单 + realRoute = createRoute(menu, true) + } + else if (!parentRoute && !menu.parentId && menu.type === 0) { + // 目录 + const childRoutes = filterAsyncRoutes(menus, menu) + realRoute = createRoute(menu, true) + if (childRoutes && childRoutes.length > 0) { + realRoute.redirect = childRoutes[0].path + realRoute.children = childRoutes + } + } + else if ( + parentRoute + && parentRoute.id === menu.parentId + && menu.type === 1 + ) { + // 子菜单 + realRoute = createRoute(menu, false) + } + else if ( + parentRoute + && parentRoute.id === menu.parentId + && menu.type === 0 + ) { + // 如果还是目录,继续递归 + const childRoute = filterAsyncRoutes(menus, menu) + realRoute = createRoute(menu, false) + if (childRoute && childRoute.length > 0) { + realRoute.redirect = childRoute[0].path + realRoute.children = childRoute + } + } + // add curent route + if (realRoute) + res.push(realRoute) + }) + return res +} + +export function generatorRouters(menus: MenuEntity[]) { + return filterAsyncRoutes(menus, null) +} + +// 获取所有菜单以及权限 +function filterMenuToTable(menus: MenuEntity[], parentMenu) { + const res = [] + menus.forEach((menu) => { + // 根级别菜单渲染 + let realMenu + if (!parentMenu && !menu.parentId && menu.type === 1) { + // 根菜单,查找该跟菜单下子菜单,因为可能会包含权限 + const childMenu = filterMenuToTable(menus, menu) + realMenu = { ...menu } + realMenu.children = childMenu + } + else if (!parentMenu && !menu.parentId && menu.type === 0) { + // 根目录 + const childMenu = filterMenuToTable(menus, menu) + realMenu = { ...menu } + realMenu.children = childMenu + } + else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 1) { + // 子菜单下继续找是否有子菜单 + const childMenu = filterMenuToTable(menus, menu) + realMenu = { ...menu } + realMenu.children = childMenu + } + else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 0) { + // 如果还是目录,继续递归 + const childMenu = filterMenuToTable(menus, menu) + realMenu = { ...menu } + realMenu.children = childMenu + } + else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 2) { + realMenu = { ...menu } + } + // add curent route + if (realMenu) { + realMenu.pid = menu.id + res.push(realMenu) + } + }) + return res +} + +export function generatorMenu(menu: MenuEntity[]) { + return filterMenuToTable(menu, null) +} + +/** 检测是否为演示环境, 如果为演示环境,则拒绝该操作 */ +export function checkIsDemoMode() { + if (envBoolean('IS_DEMO')) + throw new ForbiddenException('演示模式下不允许操作') +} diff --git a/src/utils/redis.util.ts b/src/utils/redis.util.ts new file mode 100644 index 0000000..c09380c --- /dev/null +++ b/src/utils/redis.util.ts @@ -0,0 +1,10 @@ +import type { RedisKeys } from '~/constants/cache.constant' + +type Prefix = 'm-shop' +const prefix = 'm-shop' + +export function getRedisKey(key: T, ...concatKeys: string[]): `${Prefix}:${T}${string | ''}` { + return `${prefix}:${key}${ + concatKeys && concatKeys.length ? `:${concatKeys.join('_')}` : '' + }` +} diff --git a/src/utils/schedule.util.ts b/src/utils/schedule.util.ts new file mode 100644 index 0000000..184fede --- /dev/null +++ b/src/utils/schedule.util.ts @@ -0,0 +1,99 @@ +const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) + +export function scheduleMicrotask(callback: () => void) { + sleep(0).then(callback) +} + +type NotifyCallback = () => void + +type NotifyFunction = (callback: () => void) => void + +type BatchNotifyFunction = (callback: () => void) => void + +export function createNotifyManager() { + let queue: NotifyCallback[] = [] + let transactions = 0 + let notifyFn: NotifyFunction = (callback) => { + callback() + } + let batchNotifyFn: BatchNotifyFunction = (callback: () => void) => { + callback() + } + + const flush = (): void => { + const originalQueue = queue + queue = [] + if (originalQueue.length) { + scheduleMicrotask(() => { + batchNotifyFn(() => { + originalQueue.forEach((callback) => { + notifyFn(callback) + }) + }) + }) + } + } + + const batch = (callback: () => T): T => { + let result + transactions++ + try { + result = callback() + } + finally { + transactions-- + if (!transactions) + flush() + } + return result + } + + const schedule = (callback: NotifyCallback): void => { + if (transactions) { + queue.push(callback) + } + else { + scheduleMicrotask(() => { + notifyFn(callback) + }) + } + } + + /** + * All calls to the wrapped function will be batched. + */ + const batchCalls = (callback: T): T => { + return ((...args: any[]) => { + schedule(() => { + callback(...args) + }) + }) as any + } + + /** + * Use this method to set a custom notify function. + * This can be used to for example wrap notifications with `React.act` while running tests. + */ + const setNotifyFunction = (fn: NotifyFunction) => { + notifyFn = fn + } + + /** + * Use this method to set a custom function to batch notifications together into a single tick. + * By default React Query will use the batch function provided by ReactDOM or React Native. + */ + const setBatchNotifyFunction = (fn: BatchNotifyFunction) => { + batchNotifyFn = fn + } + + return { + batch, + batchCalls, + schedule, + setNotifyFunction, + setBatchNotifyFunction, + } as const +} + +// SINGLETON +export const scheduleManager = createNotifyManager() diff --git a/src/utils/tool.util.ts b/src/utils/tool.util.ts new file mode 100644 index 0000000..9982cf5 --- /dev/null +++ b/src/utils/tool.util.ts @@ -0,0 +1,60 @@ +import { customAlphabet, nanoid } from 'nanoid' + +import { md5 } from './crypto.util' + +export function getAvatar(mail: string | undefined) { + if (!mail) + return '' + + return `https://cravatar.cn/avatar/${md5(mail)}?d=retro` +} + +export function generateUUID(size: number = 21): string { + return nanoid(size) +} + +export function generateShortUUID(): string { + return nanoid(10) +} + +/** + * 生成一个随机的值 + */ +export function generateRandomValue( + length: number, + placeholder = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM', +): string { + const customNanoid = customAlphabet(placeholder, length) + return customNanoid() +} + +/** + * 生成一个随机的值 + */ +export function randomValue( + size = 16, + dict = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict', +): string { + let id = '' + let i = size + const len = dict.length + while (i--) id += dict[(Math.random() * len) | 0] + return id +} + +export const hashString = function (str, seed = 0) { + let h1 = 0xDEADBEEF ^ seed + let h2 = 0x41C6CE57 ^ seed + for (let i = 0, ch; i < str.length; i++) { + ch = str.charCodeAt(i) + h1 = Math.imul(h1 ^ ch, 2654435761) + h2 = Math.imul(h2 ^ ch, 1597334677) + } + h1 + = Math.imul(h1 ^ (h1 >>> 16), 2246822507) + ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909) + h2 + = Math.imul(h2 ^ (h2 >>> 16), 2246822507) + ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909) + return 4294967296 * (2097151 & h2) + (h1 >>> 0) +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..e1fc515 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "scripts", "test", "dist", "**/*spec.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c146e4a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "incremental": true, + "target": "ES2022", + "lib": ["ESNext"], + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "baseUrl": "./", + "module": "commonjs", + "paths": { + "~/*": ["src/*"] + }, + "strictBindCallApply": false, + "strictNullChecks": false, + "noFallthroughCasesInSwitch": false, + "noImplicitAny": false, + "declaration": true, + "outDir": "./dist", + "removeComments": true, + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": false, + "skipLibCheck": true + }, + "exclude": ["node_modules", "scripts", "dist"] +} diff --git a/types/global.d.ts b/types/global.d.ts new file mode 100644 index 0000000..2e5535e --- /dev/null +++ b/types/global.d.ts @@ -0,0 +1,21 @@ +declare global { + interface IAuthUser { + uid: number; + pv: number; + exp?: number; + iat?: number; + roles?: string[]; + } + + export interface IBaseResponse { + message: string; + code: number; + data?: T; + } + + export interface IListRespData { + items: T[]; + } +} + +export {}; diff --git a/types/module.d.ts b/types/module.d.ts new file mode 100644 index 0000000..b6cd022 --- /dev/null +++ b/types/module.d.ts @@ -0,0 +1,7 @@ +import 'fastify'; + +declare module 'fastify' { + interface FastifyRequest { + user?: IAuthUser; + } +} diff --git a/types/utils.d.ts b/types/utils.d.ts new file mode 100644 index 0000000..812c77e --- /dev/null +++ b/types/utils.d.ts @@ -0,0 +1,104 @@ +/** 提取Promise返回值 */ +type UnboxPromise> = T extends Promise + ? U + : never + +/** 将联合类型转为交叉类型 */ +declare type UnionToIntersection = ( + U extends any ? (k: U) => void : never +) extends (k: infer I) => void + ? I + : never + +/** eg: type result = StringToUnion<'abc'> 结果:'a'|'b'|'c' */ +type StringToUnion = S extends `${infer S1}${infer S2}` + ? S1 | StringToUnion + : never + +/** 字符串替换,类似js的字符串replace方法 */ +type Replace< + Str extends string, + From extends string, + To extends string, +> = Str extends `${infer Left}${From}${infer Right}` + ? `${Left}${To}${Right}` + : Str + +/** 字符串替换,类似js的字符串replaceAll方法 */ +type ReplaceAll< + Str extends string, + From extends string, + To extends string, +> = Str extends `${infer Left}${From}${infer Right}` + ? Replace, From, To> + : Str + +/** eg: type result = CamelCase<'foo-bar-baz'>, 结果:fooBarBaz */ +type CamelCase = S extends `${infer S1}-${infer S2}` + ? S2 extends Capitalize + ? `${S1}-${CamelCase}` + : `${S1}${CamelCase>}` + : S + +/** eg: type result = StringToArray<'abc'>, 结果:['a', 'b', 'c'] */ +type StringToArray< + S extends string, + T extends any[] = [], +> = S extends `${infer S1}${infer S2}` ? StringToArray : T + +/** `RequiredKeys`是用来获取所有必填字段,其中这些必填字段组合成一个联合类型 */ +type RequiredKeys = { + [P in keyof T]: T extends Record ? P : never; +}[keyof T] + +/** `OptionalKeys`是用来获取所有可选字段,其中这些可选字段组合成一个联合类型 */ +type OptionalKeys = { + [P in keyof T]: object extends Pick ? P : never; +}[keyof T] + +/** `GetRequired`是用来获取一个类型中,所有必填键及其类型所组成的一个新类型的 */ +type GetRequired = { + [P in RequiredKeys]-?: T[P]; +} + +/** `GetOptional`是用来获取一个类型中,所有可选键及其类型所组成的一个新类型的 */ +type GetOptional = { + [P in OptionalKeys]?: T[P]; +} + +/** type result1 = Includes<[1, 2, 3, 4], '4'> 结果: false; type result2 = Includes<[1, 2, 3, 4], 4> 结果: true */ +type Includes = K extends T[number] ? true : false + +/** eg:type result = MyConcat<[1, 2], [3, 4]> 结果:[1, 2, 3, 4] */ +type MyConcat = [...T, ...U] +/** eg: type result1 = MyPush<[1, 2, 3], 4> 结果:[1, 2, 3, 4] */ +type MyPush = [...T, K] +/** eg: type result3 = MyPop<[1, 2, 3]> 结果:[1, 2] */ +type MyPop = T extends [...infer L, infer R] ? L : never; // eslint-disable-line + +type PropType = string extends Path + ? unknown + : Path extends keyof T + ? T[Path] + : Path extends `${infer K}.${infer R}` + ? K extends keyof T + ? PropType + : unknown + : unknown + +/** + * NestedKeyOf + * Get all the possible paths of an object + * @example + * type Keys = NestedKeyOf<{ a: { b: { c: string } }> + * // 'a' | 'a.b' | 'a.b.c' + */ +type NestedKeyOf = { + [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object + ? `${Key}` | `${Key}.${NestedKeyOf}` + : `${Key}`; +}[keyof ObjectType & (string | number)] + + type RecordNamePaths = { + [K in NestedKeyOf]: PropType + } diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..90de8ed --- /dev/null +++ b/vercel.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "installCommand": "pnpm install", + "buildCommand": "pnpm build", + // 由于项目中使用了路径别名,暂时无法简单的部署到 vercel: https://github.com/vercel/vercel/issues/2832 + "builds": [ + { + "src": "src/main.ts", + "use": "@vercel/node" + } + ], + "routes": [ + { + "src": "/(.*)", + "dest": "src/main.ts", + "methods": ["GET", "POST", "PUT", "PATCH", "DELETE"] + } + ], + "env": { + "NODE_ENV": "development" + } +} diff --git a/wait-for-it.sh b/wait-for-it.sh new file mode 100644 index 0000000..3974640 --- /dev/null +++ b/wait-for-it.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi \ No newline at end of file From 4646d91172f075b0f3e0f1d2973aeb48e05bbf74 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 28 Feb 2024 11:53:50 +0800 Subject: [PATCH 02/64] =?UTF-8?q?refactor:=20=E9=A1=B9=E7=9B=AE=E6=90=AD?= =?UTF-8?q?=E5=BB=BA=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 10 +- .env.development | 4 +- .env.production | 4 +- Dockerfile | 4 +- README.md | 2 +- deploy/sql/hxoa.sql | 712 ++++++++++++++++++++++ deploy/sql/nest_admin.sql | 744 ----------------------- deploy/web/default.conf | 54 -- docker-compose.yml | 18 +- ecosystem.config.js | 2 +- package.json | 4 +- src/migrations/1707996695540-initData.ts | 2 +- 12 files changed, 737 insertions(+), 823 deletions(-) create mode 100644 deploy/sql/hxoa.sql delete mode 100644 deploy/sql/nest_admin.sql delete mode 100644 deploy/web/default.conf diff --git a/.env b/.env index 6f19ae2..68c4ac4 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ # app -APP_NAME = Nest Admin +APP_NAME = Huaxin OA APP_PORT = 7001 APP_BASE_URL = http://localhost:${APP_PORT} APP_LOCALE = zh-CN @@ -10,19 +10,19 @@ LOGGER_MAX_FILES = 31 TZ = Asia/Shanghai -# OSS(qiniu) +# OSS(minio) OSS_ACCESSKEY=xxx OSS_SECRETKEY=xxx OSS_DOMAIN=https://cdn.buqiyuan.site -OSS_BUCKET=nest-admin +OSS_BUCKET=hxoa OSS_ZONE=Zone_z2 # Zone_as0 | Zone_na0 | Zone_z0 | Zone_z1 | Zone_z2 OSS_ACCESS_TYPE=public # or private DB_HOST = host.docker.internal DB_PORT = 13307 -DB_DATABASE = nest_admin +DB_DATABASE = hxoa DB_USERNAME = root -DB_PASSWORD = root +DB_PASSWORD = huaxin123 DB_SYNCHRONIZE = false DB_LOGGING = ["error"] diff --git a/.env.development b/.env.development index 6662f21..ce6ed42 100644 --- a/.env.development +++ b/.env.development @@ -15,9 +15,9 @@ SWAGGER_VERSION = 1.0 # db DB_HOST = 127.0.0.1 DB_PORT = 13307 -DB_DATABASE = nest_admin +DB_DATABASE = hxoa DB_USERNAME = root -DB_PASSWORD = root +DB_PASSWORD = huaxin123 DB_SYNCHRONIZE = true DB_LOGGING = ["error"] diff --git a/.env.production b/.env.production index c6a2b1d..0a4e6bd 100644 --- a/.env.production +++ b/.env.production @@ -15,9 +15,9 @@ SWAGGER_VERSION = 1.0 # db DB_HOST = host.docker.internal DB_PORT = 13307 -DB_DATABASE = nest_admin +DB_DATABASE = hxoa DB_USERNAME = root -DB_PASSWORD = root +DB_PASSWORD = huaxin123 DB_SYNCHRONIZE = false DB_LOGGING = ["error"] diff --git a/Dockerfile b/Dockerfile index 31e8e78..58a6897 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ # 使用 as 来为某一阶段命名 FROM node:20-slim AS base -ENV PROJECT_DIR=/nest-admin \ +ENV PROJECT_DIR=/huaxin-admin \ DB_HOST=mysql \ APP_PORT=7001 \ PNPM_HOME="/pnpm" \ @@ -17,7 +17,7 @@ RUN corepack enable \ # WORKDIR指令用于设置Dockerfile中的RUN、CMD和ENTRYPOINT指令执行命令的工作目录(默认为/目录),该指令在Dockerfile文件中可以出现多次, # 如果使用相对路径则为相对于WORKDIR上一次的值, # 例如WORKDIR /data,WORKDIR logs,RUN pwd最终输出的当前目录是/data/logs。 -# cd 到 /nest-admin +# cd 到 /huaxin-admin WORKDIR $PROJECT_DIR COPY ./ $PROJECT_DIR RUN chmod +x ./wait-for-it.sh diff --git a/README.md b/README.md index ce302ae..6ff61a3 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ docker compose --env-file .env --env-file .env.production down ```bash pnpm docker:rmi # or -docker rmi buqiyuan/nest-admin-server:stable +docker rmi buqiyuan/huaxin-admin-server:stable ``` 查看实时日志输出 diff --git a/deploy/sql/hxoa.sql b/deploy/sql/hxoa.sql new file mode 100644 index 0000000..872e522 --- /dev/null +++ b/deploy/sql/hxoa.sql @@ -0,0 +1,712 @@ +/* + Navicat Premium Data Transfer + + Source Server : 13307 + Source Server Type : MySQL + Source Server Version : 80300 + Source Host : localhost:13307 + Source Schema : hxoa + + Target Server Type : MySQL + Target Server Version : 80300 + File Encoding : 65001 + + Date: 28/02/2024 11:52:46 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for sys_captcha_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_captcha_log`; +CREATE TABLE `sys_captcha_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NULL DEFAULT NULL, + `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_captcha_log +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sys_config +-- ---------------------------- +DROP TABLE IF EXISTS `sys_config`; +CREATE TABLE `sys_config` ( + `id` int NOT NULL AUTO_INCREMENT, + `key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_2c363c25cf99bcaab3a7f389ba`(`key`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_config +-- ---------------------------- +INSERT INTO `sys_config` VALUES (1, 'sys_user_initPassword', '初始密码', '123456', '创建管理员账号的初始密码', '2023-11-10 00:31:44.154921', '2023-11-10 00:31:44.161263'); +INSERT INTO `sys_config` VALUES (2, 'sys_api_token', 'API Token', 'huaxin-admin', '用于请求 @ApiToken 的控制器', '2023-11-10 00:31:44.154921', '2024-01-29 09:52:27.000000'); + +-- ---------------------------- +-- Table structure for sys_dept +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dept`; +CREATE TABLE `sys_dept` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `orderNo` int NULL DEFAULT 0, + `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '', + `parentId` int NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_c75280b01c49779f2323536db67`(`parentId`) USING BTREE, + CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_dept +-- ---------------------------- +INSERT INTO `sys_dept` VALUES (1, '华东分部', 1, '1.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` VALUES (2, '研发部', 1, '1.2.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` VALUES (3, '市场部', 2, '1.3.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` VALUES (4, '商务部', 3, '1.4.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` VALUES (5, '财务部', 4, '1.5.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` VALUES (6, '华南分部', 2, '6.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` VALUES (7, '西北分部', 3, '7.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` VALUES (8, '研发部', 1, '6.8.', 6, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +INSERT INTO `sys_dept` VALUES (9, '市场部', 1, '6.9.', 6, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); + +-- ---------------------------- +-- Table structure for sys_dict +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict`; +CREATE TABLE `sys_dict` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint NOT NULL DEFAULT 1, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_d112365748f740ee260b65ce91`(`name`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_dict +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sys_dict_item +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_item`; +CREATE TABLE `sys_dict_item` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `order` int NULL DEFAULT NULL COMMENT '字典项排序', + `status` tinyint NOT NULL DEFAULT 1, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `type_id` int NULL DEFAULT NULL, + `orderNo` int NULL DEFAULT NULL COMMENT '字典项排序', + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_d68ea74fcb041c8cfd1fd659844`(`type_id`) USING BTREE, + CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_dict_item +-- ---------------------------- +INSERT INTO `sys_dict_item` VALUES (1, '2024-01-29 01:24:51.846135', '2024-01-29 02:23:19.000000', 1, 1, '男', '1', 0, 1, '性别男', 1, 3); +INSERT INTO `sys_dict_item` VALUES (2, '2024-01-29 01:32:58.458741', '2024-01-29 01:58:20.000000', 1, 1, '女', '0', 1, 1, '性别女', 1, 2); +INSERT INTO `sys_dict_item` VALUES (3, '2024-01-29 01:59:17.805394', '2024-01-29 14:37:18.000000', 1, 1, '人妖王', '3', NULL, 1, '安布里奥·伊万科夫', 1, 0); +INSERT INTO `sys_dict_item` VALUES (5, '2024-01-29 02:13:01.782466', '2024-01-29 02:13:01.782466', 1, 1, '显示', '1', NULL, 1, '显示菜单', 2, 0); +INSERT INTO `sys_dict_item` VALUES (6, '2024-01-29 02:13:31.134721', '2024-01-29 02:13:31.134721', 1, 1, '隐藏', '0', NULL, 1, '隐藏菜单', 2, 0); + +-- ---------------------------- +-- Table structure for sys_dict_type +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_type`; +CREATE TABLE `sys_dict_type` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint NOT NULL DEFAULT 1, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_74d0045ff7fab9f67adc0b1bda`(`code`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_dict_type +-- ---------------------------- +INSERT INTO `sys_dict_type` VALUES (1, '2024-01-28 08:19:12.777447', '2024-02-08 13:05:10.000000', 1, 1, '性别', 1, '性别单选', 'sys_user_gender'); +INSERT INTO `sys_dict_type` VALUES (2, '2024-01-28 08:38:41.235185', '2024-01-29 02:11:33.000000', 1, 1, '菜单显示状态', 1, '菜单显示状态', 'sys_show_hide'); + +-- ---------------------------- +-- Table structure for sys_login_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_login_log`; +CREATE TABLE `sys_login_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `ua` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `user_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_3029712e0df6a28edaee46fd470`(`user_id`) USING BTREE, + CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_login_log +-- ---------------------------- +INSERT INTO `sys_login_log` VALUES (1, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 11:49:38.330842', '2024-02-28 11:49:38.330842', 1); + +-- ---------------------------- +-- Table structure for sys_menu +-- ---------------------------- +DROP TABLE IF EXISTS `sys_menu`; +CREATE TABLE `sys_menu` ( + `id` int NOT NULL AUTO_INCREMENT, + `parent_id` int NULL DEFAULT NULL, + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `type` tinyint NOT NULL DEFAULT 0, + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '', + `order_no` int NULL DEFAULT 0, + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `keep_alive` tinyint NOT NULL DEFAULT 1, + `show` tinyint NOT NULL DEFAULT 1, + `status` tinyint NOT NULL DEFAULT 1, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `is_ext` tinyint NOT NULL DEFAULT 0, + `ext_open_mode` tinyint NOT NULL DEFAULT 1, + `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 128 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_menu +-- ---------------------------- +INSERT INTO `sys_menu` VALUES (1, NULL, '/system', '系统管理', '', 0, 'ant-design:setting-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:46.668745', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (2, 1, '/system/user', '用户管理', 'system:user:list', 1, 'ant-design:user-outlined', 0, 'system/user/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:10:30.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (3, 1, '/system/role', '角色管理', 'system:role:list', 1, 'ep:user', 1, 'system/role/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:02.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (4, 1, '/system/menu', '菜单管理', 'system:menu:list', 1, 'ep:menu', 2, 'system/menu/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:18.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:44.567023', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (6, 5, '/system/monitor/online', '在线用户', 'system:online:list', 1, '', 0, 'system/monitor/online/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:13:59.519267', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (7, 5, '/sys/monitor/login-log', '登录日志', 'system:log:login:list', 1, '', 0, 'system/monitor/log/login/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:02.610719', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (8, 5, '/system/monitor/serve', '服务监控', 'system:serve:stat', 1, '', 4, 'system/monitor/serve/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:05.606355', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:52.967983', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (10, 9, '/system/task', '任务管理', '', 1, '', 0, 'system/schedule/task/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:14:39.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (11, 9, '/system/task/log', '任务日志', 'system:task:list', 1, '', 0, 'system/schedule/log/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:15:01.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (12, NULL, '/document', '文档', '', 0, 'ion:tv-outline', 2, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:51.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (14, 12, 'https://www.typeorm.org/', 'Typeorm中文文档(外链)', NULL, 1, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:39:53.000000', 1, 1, NULL); +INSERT INTO `sys_menu` VALUES (15, 12, 'https://docs.nestjs.cn/', 'Nest.js中文文档(内嵌)', '', 1, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:40:43.000000', 1, 2, NULL); +INSERT INTO `sys_menu` VALUES (20, 2, NULL, '新增', 'system:user:create', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (21, 2, '', '删除', 'system:user:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (22, 2, '', '更新', 'system:user:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (23, 2, '', '查询', 'system:user:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (24, 3, '', '新增', 'system:role:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (25, 3, '', '删除', 'system:role:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (26, 3, '', '修改', 'system:role:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (27, 3, '', '查询', 'system:role:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (28, 4, NULL, '新增', 'system:menu:create', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (29, 4, NULL, '删除', 'system:menu:delete', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (30, 4, '', '修改', 'system:menu:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (31, 4, NULL, '查询', 'system:menu:read', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (32, 6, '', '下线', 'system:online:kick', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (34, 10, '', '新增', 'system:task:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (35, 10, '', '删除', 'system:task:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (36, 10, '', '执行一次', 'system:task:once', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (37, 10, '', '查询', 'system:task:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (38, 10, '', '运行', 'system:task:start', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (39, 10, '', '暂停', 'system:task:stop', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (40, 10, '', '更新', 'system:task:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (41, 7, '', '查询登录日志', 'system:log:login:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (42, 7, '', '查询任务日志', 'system:log:task:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (43, NULL, '/about', '关于', '', 1, 'ant-design:info-circle-outlined', 260, 'account/about', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-10 09:35:41.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (48, NULL, '/tool', '系统工具', NULL, 0, 'ant-design:tool-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:28.327223', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (49, 48, '/tool/email', '邮件工具', 'system:tools:email', 1, 'ant-design:send-outlined', 1, 'tool/email/index', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:38.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (50, 49, NULL, '发送邮件', 'tools:email:send', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (51, 48, '/tool/storage', '存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:59:17.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (52, 51, NULL, '文件上传', 'upload:upload', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 01:04:08.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (53, 51, NULL, '文件删除', 'tool:storage:delete', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:56:01.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (54, 2, NULL, '修改密码', 'system:user:password', 2, '', 5, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (56, 1, '/system/dict-type', '字典管理', 'system:dict-type:list', 1, 'ant-design:book-outlined', 4, 'system/dict-type/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:12.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (57, 56, NULL, '新增', 'system:dict-type:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:20.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (58, 56, NULL, '更新', 'system:dict-type:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:26.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (59, 56, NULL, '删除', 'system:dict-type:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:42.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (60, 56, NULL, '查询', 'system:dict-type:info', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (61, 1, '/system/dept', '部门管理', 'system:dept:list', 1, 'ant-design:deployment-unit-outlined', 3, 'system/dept/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:55.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (62, 61, NULL, '新增', 'system:dept:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (63, 61, NULL, '更新', 'system:dept:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (64, 61, NULL, '删除', 'system:dept:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (65, 61, NULL, '查询', 'system:dept:read', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (68, 5, '/health', '健康检查', '', 1, '', 4, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:33.352155', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (69, 68, NULL, '网络', 'app:health:network', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (70, 68, NULL, '数据库', 'app:health: database', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (86, 1, '/param-config', '参数配置', 'system:param-config:list', 1, 'ep:edit', 255, 'system/param-config/index', 0, 1, 1, '2024-01-10 17:34:52.569663', '2024-01-19 02:11:27.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (87, 86, NULL, '查询', 'system:param-config:read', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:20.983241', '2024-01-10 17:39:20.983241', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (88, 86, NULL, '新增', 'system:param-config:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:57.543510', '2024-01-10 17:39:57.543510', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (89, 86, NULL, '更新', 'system:param-config:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:40:27.355944', '2024-01-10 17:40:27.355944', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (92, 86, NULL, '删除', 'system:param-config:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:57:32.059887', '2024-01-10 17:57:32.059887', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (107, 1, 'system/dict-item/:id', '字典项管理', 'system:dict-item:list', 1, 'ant-design:facebook-outlined', 255, 'system/dict-item/index', 0, 0, 1, '2024-01-28 09:21:17.409532', '2024-01-30 13:09:47.000000', 0, 1, '字典管理'); +INSERT INTO `sys_menu` VALUES (108, 107, NULL, '新增', 'system:dict-item:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:22:39.401758', '2024-01-28 22:38:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (109, 107, NULL, '更新', 'system:dict-item:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:26:43.911886', '2024-01-28 09:26:43.911886', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (110, 107, NULL, '删除', 'system:dict-item:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:28.535225', '2024-01-28 09:27:28.535225', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL); +INSERT INTO `sys_menu` VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 0, 1, '2024-02-10 08:00:02.394616', '2024-02-28 11:51:21.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (116, 115, 'manage', '文件管理', 'netdisk:manage:list', 1, '', 252, 'netdisk/manage', 0, 1, 1, '2024-02-10 08:03:49.837348', '2024-02-10 09:34:41.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (120, 116, NULL, '删除', 'netdisk:manage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:09.480601', '2024-02-10 08:42:09.480601', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (121, 116, NULL, '获取文件上传token', 'netdisk:manage:token', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:57.688104', '2024-02-10 08:42:57.688104', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (122, 116, NULL, '添加文件备注', 'netdisk:manage:mark', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:43:40.117321', '2024-02-10 08:43:40.117321', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (123, 116, NULL, '下载文件', 'netdisk:manage:download', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:01.338984', '2024-02-10 08:44:01.338984', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (124, 116, NULL, '重命名文件或文件夹', 'netdisk:manage:rename', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:27.233379', '2024-02-10 08:45:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (125, 116, NULL, '复制文件或文件夹', 'netdisk:manage:copy', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:44.725391', '2024-02-10 08:45:48.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (126, 116, NULL, '剪切文件或文件夹', 'netdisk:manage:cut', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:45:21.660511', '2024-02-10 08:45:21.660511', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (127, 115, 'overview', '网盘概览', 'netdisk:overview:desc', 1, '', 254, 'netdisk/overview', 0, 1, 1, '2024-02-10 09:32:56.981190', '2024-02-10 09:34:18.000000', 0, 1, NULL); + +-- ---------------------------- +-- Table structure for sys_role +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role`; +CREATE TABLE `sys_role` ( + `id` int NOT NULL AUTO_INCREMENT, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `status` tinyint NULL DEFAULT 1, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `default` tinyint NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_223de54d6badbe43a5490450c3`(`name`) USING BTREE, + UNIQUE INDEX `IDX_05edc0a51f41bb16b7d8137da9`(`value`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_role +-- ---------------------------- +INSERT INTO `sys_role` VALUES (1, 'admin', '管理员', '超级管理员', 1, '2023-11-10 00:31:44.058463', '2024-01-28 21:08:39.000000', NULL); +INSERT INTO `sys_role` VALUES (2, 'user', '用户', '', 1, '2023-11-10 00:31:44.058463', '2024-01-30 18:44:45.000000', 1); +INSERT INTO `sys_role` VALUES (9, 'test', '测试', NULL, 1, '2024-01-23 22:46:52.408827', '2024-01-30 01:04:52.000000', NULL); + +-- ---------------------------- +-- Table structure for sys_role_menus +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role_menus`; +CREATE TABLE `sys_role_menus` ( + `role_id` int NOT NULL, + `menu_id` int NOT NULL, + PRIMARY KEY (`role_id`, `menu_id`) USING BTREE, + INDEX `IDX_35ce749b04d57e226d059e0f63`(`role_id`) USING BTREE, + INDEX `IDX_2b95fdc95b329d66c18f5baed6`(`menu_id`) USING BTREE, + CONSTRAINT `FK_2b95fdc95b329d66c18f5baed6d` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `FK_35ce749b04d57e226d059e0f633` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_role_menus +-- ---------------------------- +INSERT INTO `sys_role_menus` VALUES (1, 1); +INSERT INTO `sys_role_menus` VALUES (1, 2); +INSERT INTO `sys_role_menus` VALUES (1, 3); +INSERT INTO `sys_role_menus` VALUES (1, 4); +INSERT INTO `sys_role_menus` VALUES (1, 5); +INSERT INTO `sys_role_menus` VALUES (1, 6); +INSERT INTO `sys_role_menus` VALUES (1, 7); +INSERT INTO `sys_role_menus` VALUES (1, 8); +INSERT INTO `sys_role_menus` VALUES (1, 9); +INSERT INTO `sys_role_menus` VALUES (1, 10); +INSERT INTO `sys_role_menus` VALUES (1, 11); +INSERT INTO `sys_role_menus` VALUES (1, 12); +INSERT INTO `sys_role_menus` VALUES (1, 14); +INSERT INTO `sys_role_menus` VALUES (1, 15); +INSERT INTO `sys_role_menus` VALUES (1, 20); +INSERT INTO `sys_role_menus` VALUES (1, 21); +INSERT INTO `sys_role_menus` VALUES (1, 22); +INSERT INTO `sys_role_menus` VALUES (1, 23); +INSERT INTO `sys_role_menus` VALUES (1, 24); +INSERT INTO `sys_role_menus` VALUES (1, 25); +INSERT INTO `sys_role_menus` VALUES (1, 26); +INSERT INTO `sys_role_menus` VALUES (1, 27); +INSERT INTO `sys_role_menus` VALUES (1, 28); +INSERT INTO `sys_role_menus` VALUES (1, 29); +INSERT INTO `sys_role_menus` VALUES (1, 30); +INSERT INTO `sys_role_menus` VALUES (1, 31); +INSERT INTO `sys_role_menus` VALUES (1, 32); +INSERT INTO `sys_role_menus` VALUES (1, 34); +INSERT INTO `sys_role_menus` VALUES (1, 35); +INSERT INTO `sys_role_menus` VALUES (1, 36); +INSERT INTO `sys_role_menus` VALUES (1, 37); +INSERT INTO `sys_role_menus` VALUES (1, 38); +INSERT INTO `sys_role_menus` VALUES (1, 39); +INSERT INTO `sys_role_menus` VALUES (1, 40); +INSERT INTO `sys_role_menus` VALUES (1, 41); +INSERT INTO `sys_role_menus` VALUES (1, 42); +INSERT INTO `sys_role_menus` VALUES (1, 43); +INSERT INTO `sys_role_menus` VALUES (1, 48); +INSERT INTO `sys_role_menus` VALUES (1, 49); +INSERT INTO `sys_role_menus` VALUES (1, 50); +INSERT INTO `sys_role_menus` VALUES (1, 51); +INSERT INTO `sys_role_menus` VALUES (1, 52); +INSERT INTO `sys_role_menus` VALUES (1, 53); +INSERT INTO `sys_role_menus` VALUES (1, 54); +INSERT INTO `sys_role_menus` VALUES (1, 56); +INSERT INTO `sys_role_menus` VALUES (1, 57); +INSERT INTO `sys_role_menus` VALUES (1, 58); +INSERT INTO `sys_role_menus` VALUES (1, 59); +INSERT INTO `sys_role_menus` VALUES (1, 60); +INSERT INTO `sys_role_menus` VALUES (1, 61); +INSERT INTO `sys_role_menus` VALUES (1, 62); +INSERT INTO `sys_role_menus` VALUES (1, 63); +INSERT INTO `sys_role_menus` VALUES (1, 64); +INSERT INTO `sys_role_menus` VALUES (1, 65); +INSERT INTO `sys_role_menus` VALUES (1, 68); +INSERT INTO `sys_role_menus` VALUES (1, 69); +INSERT INTO `sys_role_menus` VALUES (1, 70); +INSERT INTO `sys_role_menus` VALUES (1, 86); +INSERT INTO `sys_role_menus` VALUES (1, 87); +INSERT INTO `sys_role_menus` VALUES (1, 88); +INSERT INTO `sys_role_menus` VALUES (1, 89); +INSERT INTO `sys_role_menus` VALUES (1, 92); +INSERT INTO `sys_role_menus` VALUES (1, 107); +INSERT INTO `sys_role_menus` VALUES (1, 108); +INSERT INTO `sys_role_menus` VALUES (1, 109); +INSERT INTO `sys_role_menus` VALUES (1, 110); +INSERT INTO `sys_role_menus` VALUES (1, 111); +INSERT INTO `sys_role_menus` VALUES (2, 1); +INSERT INTO `sys_role_menus` VALUES (2, 5); +INSERT INTO `sys_role_menus` VALUES (2, 6); +INSERT INTO `sys_role_menus` VALUES (2, 7); +INSERT INTO `sys_role_menus` VALUES (2, 8); +INSERT INTO `sys_role_menus` VALUES (2, 9); +INSERT INTO `sys_role_menus` VALUES (2, 10); +INSERT INTO `sys_role_menus` VALUES (2, 11); +INSERT INTO `sys_role_menus` VALUES (2, 12); +INSERT INTO `sys_role_menus` VALUES (2, 14); +INSERT INTO `sys_role_menus` VALUES (2, 15); +INSERT INTO `sys_role_menus` VALUES (2, 32); +INSERT INTO `sys_role_menus` VALUES (2, 34); +INSERT INTO `sys_role_menus` VALUES (2, 35); +INSERT INTO `sys_role_menus` VALUES (2, 36); +INSERT INTO `sys_role_menus` VALUES (2, 37); +INSERT INTO `sys_role_menus` VALUES (2, 38); +INSERT INTO `sys_role_menus` VALUES (2, 39); +INSERT INTO `sys_role_menus` VALUES (2, 40); +INSERT INTO `sys_role_menus` VALUES (2, 41); +INSERT INTO `sys_role_menus` VALUES (2, 42); +INSERT INTO `sys_role_menus` VALUES (2, 43); +INSERT INTO `sys_role_menus` VALUES (2, 48); +INSERT INTO `sys_role_menus` VALUES (2, 49); +INSERT INTO `sys_role_menus` VALUES (2, 50); +INSERT INTO `sys_role_menus` VALUES (2, 51); +INSERT INTO `sys_role_menus` VALUES (2, 52); +INSERT INTO `sys_role_menus` VALUES (2, 53); +INSERT INTO `sys_role_menus` VALUES (2, 56); +INSERT INTO `sys_role_menus` VALUES (2, 57); +INSERT INTO `sys_role_menus` VALUES (2, 58); +INSERT INTO `sys_role_menus` VALUES (2, 59); +INSERT INTO `sys_role_menus` VALUES (2, 60); +INSERT INTO `sys_role_menus` VALUES (2, 68); +INSERT INTO `sys_role_menus` VALUES (2, 69); +INSERT INTO `sys_role_menus` VALUES (2, 70); +INSERT INTO `sys_role_menus` VALUES (2, 86); +INSERT INTO `sys_role_menus` VALUES (2, 87); +INSERT INTO `sys_role_menus` VALUES (2, 88); +INSERT INTO `sys_role_menus` VALUES (2, 89); +INSERT INTO `sys_role_menus` VALUES (2, 92); +INSERT INTO `sys_role_menus` VALUES (2, 107); +INSERT INTO `sys_role_menus` VALUES (2, 108); +INSERT INTO `sys_role_menus` VALUES (2, 109); +INSERT INTO `sys_role_menus` VALUES (2, 110); +INSERT INTO `sys_role_menus` VALUES (2, 111); +INSERT INTO `sys_role_menus` VALUES (2, 112); +INSERT INTO `sys_role_menus` VALUES (9, 1); +INSERT INTO `sys_role_menus` VALUES (9, 2); +INSERT INTO `sys_role_menus` VALUES (9, 3); +INSERT INTO `sys_role_menus` VALUES (9, 4); +INSERT INTO `sys_role_menus` VALUES (9, 5); +INSERT INTO `sys_role_menus` VALUES (9, 6); +INSERT INTO `sys_role_menus` VALUES (9, 7); +INSERT INTO `sys_role_menus` VALUES (9, 8); +INSERT INTO `sys_role_menus` VALUES (9, 9); +INSERT INTO `sys_role_menus` VALUES (9, 10); +INSERT INTO `sys_role_menus` VALUES (9, 11); +INSERT INTO `sys_role_menus` VALUES (9, 20); +INSERT INTO `sys_role_menus` VALUES (9, 21); +INSERT INTO `sys_role_menus` VALUES (9, 22); +INSERT INTO `sys_role_menus` VALUES (9, 23); +INSERT INTO `sys_role_menus` VALUES (9, 24); +INSERT INTO `sys_role_menus` VALUES (9, 25); +INSERT INTO `sys_role_menus` VALUES (9, 26); +INSERT INTO `sys_role_menus` VALUES (9, 27); +INSERT INTO `sys_role_menus` VALUES (9, 28); +INSERT INTO `sys_role_menus` VALUES (9, 29); +INSERT INTO `sys_role_menus` VALUES (9, 30); +INSERT INTO `sys_role_menus` VALUES (9, 31); +INSERT INTO `sys_role_menus` VALUES (9, 32); +INSERT INTO `sys_role_menus` VALUES (9, 34); +INSERT INTO `sys_role_menus` VALUES (9, 35); +INSERT INTO `sys_role_menus` VALUES (9, 36); +INSERT INTO `sys_role_menus` VALUES (9, 37); +INSERT INTO `sys_role_menus` VALUES (9, 38); +INSERT INTO `sys_role_menus` VALUES (9, 39); +INSERT INTO `sys_role_menus` VALUES (9, 40); +INSERT INTO `sys_role_menus` VALUES (9, 41); +INSERT INTO `sys_role_menus` VALUES (9, 42); +INSERT INTO `sys_role_menus` VALUES (9, 54); +INSERT INTO `sys_role_menus` VALUES (9, 56); +INSERT INTO `sys_role_menus` VALUES (9, 57); +INSERT INTO `sys_role_menus` VALUES (9, 58); +INSERT INTO `sys_role_menus` VALUES (9, 59); +INSERT INTO `sys_role_menus` VALUES (9, 60); +INSERT INTO `sys_role_menus` VALUES (9, 61); +INSERT INTO `sys_role_menus` VALUES (9, 62); +INSERT INTO `sys_role_menus` VALUES (9, 63); +INSERT INTO `sys_role_menus` VALUES (9, 64); +INSERT INTO `sys_role_menus` VALUES (9, 65); +INSERT INTO `sys_role_menus` VALUES (9, 68); +INSERT INTO `sys_role_menus` VALUES (9, 69); +INSERT INTO `sys_role_menus` VALUES (9, 70); +INSERT INTO `sys_role_menus` VALUES (9, 86); +INSERT INTO `sys_role_menus` VALUES (9, 87); +INSERT INTO `sys_role_menus` VALUES (9, 88); +INSERT INTO `sys_role_menus` VALUES (9, 89); +INSERT INTO `sys_role_menus` VALUES (9, 92); + +-- ---------------------------- +-- Table structure for sys_task +-- ---------------------------- +DROP TABLE IF EXISTS `sys_task`; +CREATE TABLE `sys_task` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `service` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `type` tinyint NOT NULL DEFAULT 0, + `status` tinyint NOT NULL DEFAULT 1, + `start_time` datetime NULL DEFAULT NULL, + `end_time` datetime NULL DEFAULT NULL, + `limit` int NULL DEFAULT 0, + `cron` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `every` int NULL DEFAULT NULL, + `data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `job_opts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_ef8e5ab5ef2fe0ddb1428439ef`(`name`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_task +-- ---------------------------- +INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-02-28 11:44:00.000000'); +INSERT INTO `sys_task` VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-02-28 11:44:00.000000'); +INSERT INTO `sys_task` VALUES (4, '访问百度首页', 'HttpRequestJob.handle', 0, 0, NULL, NULL, 1, '* * * * * ?', NULL, '{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}', NULL, '访问百度首页', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); +INSERT INTO `sys_task` VALUES (5, '发送邮箱', 'EmailJob.send', 0, 0, NULL, NULL, -1, '0 0 0 1 * ?', NULL, '{\"subject\":\"这是标题\",\"to\":\"zeyu57@163.com\",\"content\":\"这是正文\"}', NULL, '每月发送邮箱', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); + +-- ---------------------------- +-- Table structure for sys_task_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_task_log`; +CREATE TABLE `sys_task_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `task_id` int NULL DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT 0, + `detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `consume_time` int NULL DEFAULT 0, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_f4d9c36052fdb188ff5c089454b`(`task_id`) USING BTREE, + CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_task_log +-- ---------------------------- +INSERT INTO `sys_task_log` VALUES (1, 3, 1, NULL, 0, '2024-02-05 03:06:22.037448', '2024-02-05 03:06:22.037448'); +INSERT INTO `sys_task_log` VALUES (2, 2, 1, NULL, 0, '2024-02-10 09:42:21.738712', '2024-02-10 09:42:21.738712'); + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `psalt` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint NULL DEFAULT 1, + `qq` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `dept_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_9e7164b2f1ea1348bc0eb0a7da`(`username`) USING BTREE, + INDEX `FK_96bde34263e2ae3b46f011124ac`(`dept_id`) USING BTREE, + CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 27 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_user +-- ---------------------------- +INSERT INTO `sys_user` VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-01-29 09:49:43.000000', 'bqy', 1); +INSERT INTO `sys_user` VALUES (2, 'user', 'dbd89546dec743f82bb9073d6ac39361', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', 'luffy@qq.com', '10010', '王路飞', 'qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-01-29 09:49:57.000000', 'luffy', 8); +INSERT INTO `sys_user` VALUES (8, 'developer', 'f03fa2a99595127b9a39587421d471f6', '/upload/报名照片-202402281149824.jpg', 'nami@qq.com', '10000', '小贼猫', 'NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-02-28 11:49:54.000000', '娜美', 7); + +-- ---------------------------- +-- Table structure for sys_user_roles +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user_roles`; +CREATE TABLE `sys_user_roles` ( + `user_id` int NOT NULL, + `role_id` int NOT NULL, + PRIMARY KEY (`user_id`, `role_id`) USING BTREE, + INDEX `IDX_96311d970191a044ec048011f4`(`user_id`) USING BTREE, + INDEX `IDX_6d61c5b3f76a3419d93a421669`(`role_id`) USING BTREE, + CONSTRAINT `FK_6d61c5b3f76a3419d93a4216695` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_96311d970191a044ec048011f44` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of sys_user_roles +-- ---------------------------- +INSERT INTO `sys_user_roles` VALUES (1, 1); +INSERT INTO `sys_user_roles` VALUES (2, 2); +INSERT INTO `sys_user_roles` VALUES (8, 2); + +-- ---------------------------- +-- Table structure for todo +-- ---------------------------- +DROP TABLE IF EXISTS `todo`; +CREATE TABLE `todo` ( + `id` int NOT NULL AUTO_INCREMENT, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `user_id` int NULL DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT 0, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_9cb7989853c4cb7fe427db4b260`(`user_id`) USING BTREE, + CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of todo +-- ---------------------------- +INSERT INTO `todo` VALUES (1, 'nest.js', NULL, 0, '2023-11-10 00:31:44.139730', '2023-11-10 00:31:44.147629'); + +-- ---------------------------- +-- Table structure for tool_storage +-- ---------------------------- +DROP TABLE IF EXISTS `tool_storage`; +CREATE TABLE `tool_storage` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名', + `fileName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '真实文件名', + `ext_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `user_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 80 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of tool_storage +-- ---------------------------- +INSERT INTO `tool_storage` VALUES (78, '2024-02-03 21:41:16.851178', '2024-02-03 21:41:16.851178', 'cfd0d14459bc1a47-202402032141838.jpeg', 'cfd0d14459bc1a47.jpeg', 'jpeg', '/upload/cfd0d14459bc1a47-202402032141838.jpeg', '图片', '33.92 KB', 1); +INSERT INTO `tool_storage` VALUES (79, '2024-02-28 11:49:53.834125', '2024-02-28 11:49:53.834125', '报名照片-202402281149824.jpg', '报名照片.jpg', 'jpg', '/upload/报名照片-202402281149824.jpg', '图片', '8.7 KB', 1); + +-- ---------------------------- +-- Table structure for user_access_tokens +-- ---------------------------- +DROP TABLE IF EXISTS `user_access_tokens`; +CREATE TABLE `user_access_tokens` ( + `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `expired_at` datetime NOT NULL COMMENT '令牌过期时间', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', + `user_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_e9d9d0c303432e4e5e48c1c3e90`(`user_id`) USING BTREE, + CONSTRAINT `FK_e9d9d0c303432e4e5e48c1c3e90` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of user_access_tokens +-- ---------------------------- +INSERT INTO `user_access_tokens` VALUES ('09cf7b0a-62e0-45ee-96b0-e31de32361e0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1MDkxNTd9.0gtKlcxrxQ-TarEai2lsBnfMc852ZDYHeSjjhpo5Fn8', '2024-02-11 04:05:58', '2024-02-10 04:05:57.696509', 1); +INSERT INTO `user_access_tokens` VALUES ('31399cec-e506-4194-a9cc-47f7a1a953a9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTIxNzh9.Da3j0Ht_zAsPldEYJ18RFYZqTYNhgPxSefCEuXd-4T8', '2024-02-29 11:49:38', '2024-02-28 11:49:38.289991', 1); +INSERT INTO `user_access_tokens` VALUES ('3f7dffae-db1f-47dc-9677-5c956c3de39e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczMTEzMDJ9.D5Qpht1RquKor8WtgfGAcCp8LwG7z3FZhIwbyQzhDmE', '2024-02-08 21:08:22', '2024-02-07 21:08:22.130066', 1); +INSERT INTO `user_access_tokens` VALUES ('40342c3e-194c-42eb-adee-189389839195', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxNjF9.tRQOxhB-01Pcut5MXm4L5D1OrbMJfS4LfUys0XB4kWs', '2024-02-09 14:02:41', '2024-02-08 14:02:41.081164', 1); +INSERT INTO `user_access_tokens` VALUES ('9d1ba8e9-dffc-4b15-b21f-4a90f196e39c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1Mjc5MDV9.7LeiS3LBBdiAc7YrULWpmnI1oNSvR79K-qjEOlBYOnI', '2024-02-11 09:18:26', '2024-02-10 09:18:25.656695', 1); +INSERT INTO `user_access_tokens` VALUES ('edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxMjd9.VRuJHGca2IPrdfTyW09wfhht4x8JX207pKG-0aZyF60', '2024-02-09 14:02:07', '2024-02-08 14:02:07.390658', 1); + +-- ---------------------------- +-- Table structure for user_refresh_tokens +-- ---------------------------- +DROP TABLE IF EXISTS `user_refresh_tokens`; +CREATE TABLE `user_refresh_tokens` ( + `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `expired_at` datetime NOT NULL COMMENT '令牌过期时间', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', + `accessTokenId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `REL_1dfd080c2abf42198691b60ae3`(`accessTokenId`) USING BTREE, + CONSTRAINT `FK_1dfd080c2abf42198691b60ae39` FOREIGN KEY (`accessTokenId`) REFERENCES `user_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of user_refresh_tokens +-- ---------------------------- +INSERT INTO `user_refresh_tokens` VALUES ('045ad38e-ab82-4ea1-8b61-c2da89060999', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRWMxcEQwZ1VrYnFtZEhyWVdKTDVPIiwiaWF0IjoxNzA5MDkyMTc4fQ.K2nBZ_B8jneihHfs51LIp0fvkkgV7lFawe2cu4sOjN4', '2024-03-29 11:49:38', '2024-02-28 11:49:38.302821', '31399cec-e506-4194-a9cc-47f7a1a953a9'); +INSERT INTO `user_refresh_tokens` VALUES ('202d0969-6721-4f6f-bf34-f0d1931d4d01', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRpOXVYei1TdldjdWRnclFXVmFXIiwiaWF0IjoxNzA3MzcyMTYxfQ.NOQufR5EAPE2uZoyenmAj9H7S7qo4d6W1aW2ojDxZQc', '2024-03-09 14:02:41', '2024-02-08 14:02:41.091492', '40342c3e-194c-42eb-adee-189389839195'); +INSERT INTO `user_refresh_tokens` VALUES ('461f9b7c-e500-4762-a6d9-f9ea47163064', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicXJvTWNYMnhNRW5uRmZGWkQtaUx0IiwiaWF0IjoxNzA3MzExMzAyfQ.dFIWCePZnn2z2Qv1D5PKBKXUwVDI0Gp091MIOi9jiIo', '2024-03-08 21:08:22', '2024-02-07 21:08:22.145464', '3f7dffae-db1f-47dc-9677-5c956c3de39e'); +INSERT INTO `user_refresh_tokens` VALUES ('b375e623-2d82-48f0-9b7a-9058e3850cc6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicDhUMzdGNFFaUDJHLU5yNGVha21wIiwiaWF0IjoxNzA3MzcyMTI3fQ.fn3It6RKIxXlKmqixg0BMmY_YsQmAxtetueqW-0y1IM', '2024-03-09 14:02:07', '2024-02-08 14:02:07.410008', 'edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb'); +INSERT INTO `user_refresh_tokens` VALUES ('e620ccc1-9e40-4387-9f21-f0722e535a63', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNE5WdmFIc2hWaU05ZFh0QnVBaHNsIiwiaWF0IjoxNzA3NTI3OTA1fQ.zzyGX0mOJe6KWpTzIi7We9d9c0MRuDeGC86DMB0Vubs', '2024-03-11 09:18:26', '2024-02-10 09:18:25.664251', '9d1ba8e9-dffc-4b15-b21f-4a90f196e39c'); +INSERT INTO `user_refresh_tokens` VALUES ('f9a003e8-91b7-41ee-979e-e39cca3534ec', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGJQdl9SVjFtUl80N0o0TGF0QlV5IiwiaWF0IjoxNzA3NTA5MTU3fQ.oEVdWSigTpAQY7F8MlwBnedldH0sJT1YF1Mt0ZUbIw4', '2024-03-11 04:05:58', '2024-02-10 04:05:57.706763', '09cf7b0a-62e0-45ee-96b0-e31de32361e0'); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/deploy/sql/nest_admin.sql b/deploy/sql/nest_admin.sql deleted file mode 100644 index fc3386e..0000000 --- a/deploy/sql/nest_admin.sql +++ /dev/null @@ -1,744 +0,0 @@ -/* - Navicat Premium Data Transfer - - Source Server : nest-admin - Source Server Type : MySQL - Source Server Version : 80030 (8.0.30) - Source Host : localhost:13307 - Source Schema : nest_admin - - Target Server Type : MySQL - Target Server Version : 80030 (8.0.30) - File Encoding : 65001 - - Date: 10/02/2024 09:43:40 -*/ - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ---------------------------- --- Table structure for sys_captcha_log --- ---------------------------- -DROP TABLE IF EXISTS `sys_captcha_log`; -CREATE TABLE `sys_captcha_log` ( - `id` int NOT NULL AUTO_INCREMENT, - `user_id` int DEFAULT NULL, - `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, - `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, - `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_captcha_log --- ---------------------------- -BEGIN; -COMMIT; - --- ---------------------------- --- Table structure for sys_config --- ---------------------------- -DROP TABLE IF EXISTS `sys_config`; -CREATE TABLE `sys_config` ( - `id` int NOT NULL AUTO_INCREMENT, - `key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_2c363c25cf99bcaab3a7f389ba` (`key`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_config --- ---------------------------- -BEGIN; -INSERT INTO `sys_config` (`id`, `key`, `name`, `value`, `remark`, `created_at`, `updated_at`) VALUES (1, 'sys_user_initPassword', '初始密码', '123456', '创建管理员账号的初始密码', '2023-11-10 00:31:44.154921', '2023-11-10 00:31:44.161263'); -INSERT INTO `sys_config` (`id`, `key`, `name`, `value`, `remark`, `created_at`, `updated_at`) VALUES (2, 'sys_api_token', 'API Token', 'nest-admin', '用于请求 @ApiToken 的控制器', '2023-11-10 00:31:44.154921', '2024-01-29 09:52:27.000000'); -COMMIT; - --- ---------------------------- --- Table structure for sys_dept --- ---------------------------- -DROP TABLE IF EXISTS `sys_dept`; -CREATE TABLE `sys_dept` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, - `orderNo` int DEFAULT '0', - `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT '', - `parentId` int DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_c75280b01c49779f2323536db67` (`parentId`) USING BTREE, - CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL -) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_dept --- ---------------------------- -BEGIN; -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (1, '华东分部', 1, '1.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (2, '研发部', 1, '1.2.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (3, '市场部', 2, '1.3.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (4, '商务部', 3, '1.4.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (5, '财务部', 4, '1.5.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (6, '华南分部', 2, '6.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (7, '西北分部', 3, '7.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (8, '研发部', 1, '6.8.', 6, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` (`id`, `name`, `orderNo`, `mpath`, `parentId`, `created_at`, `updated_at`) VALUES (9, '市场部', 1, '6.9.', 6, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -COMMIT; - --- ---------------------------- --- Table structure for sys_dict --- ---------------------------- -DROP TABLE IF EXISTS `sys_dict`; -CREATE TABLE `sys_dict` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `create_by` int NOT NULL COMMENT '创建者', - `update_by` int NOT NULL COMMENT '更新者', - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint NOT NULL DEFAULT '1', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `IDX_d112365748f740ee260b65ce91` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; - --- ---------------------------- --- Records of sys_dict --- ---------------------------- -BEGIN; -COMMIT; - --- ---------------------------- --- Table structure for sys_dict_item --- ---------------------------- -DROP TABLE IF EXISTS `sys_dict_item`; -CREATE TABLE `sys_dict_item` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `create_by` int NOT NULL COMMENT '创建者', - `update_by` int NOT NULL COMMENT '更新者', - `label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `order` int DEFAULT NULL COMMENT '字典项排序', - `status` tinyint NOT NULL DEFAULT '1', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `type_id` int DEFAULT NULL, - `orderNo` int DEFAULT NULL COMMENT '字典项排序', - PRIMARY KEY (`id`), - KEY `FK_d68ea74fcb041c8cfd1fd659844` (`type_id`), - CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; - --- ---------------------------- --- Records of sys_dict_item --- ---------------------------- -BEGIN; -INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (1, '2024-01-29 01:24:51.846135', '2024-01-29 02:23:19.000000', 1, 1, '男', '1', 0, 1, '性别男', 1, 3); -INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (2, '2024-01-29 01:32:58.458741', '2024-01-29 01:58:20.000000', 1, 1, '女', '0', 1, 1, '性别女', 1, 2); -INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (3, '2024-01-29 01:59:17.805394', '2024-01-29 14:37:18.000000', 1, 1, '人妖王', '3', NULL, 1, '安布里奥·伊万科夫', 1, 0); -INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (5, '2024-01-29 02:13:01.782466', '2024-01-29 02:13:01.782466', 1, 1, '显示', '1', NULL, 1, '显示菜单', 2, 0); -INSERT INTO `sys_dict_item` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `label`, `value`, `order`, `status`, `remark`, `type_id`, `orderNo`) VALUES (6, '2024-01-29 02:13:31.134721', '2024-01-29 02:13:31.134721', 1, 1, '隐藏', '0', NULL, 1, '隐藏菜单', 2, 0); -COMMIT; - --- ---------------------------- --- Table structure for sys_dict_type --- ---------------------------- -DROP TABLE IF EXISTS `sys_dict_type`; -CREATE TABLE `sys_dict_type` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `create_by` int NOT NULL COMMENT '创建者', - `update_by` int NOT NULL COMMENT '更新者', - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint NOT NULL DEFAULT '1', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `IDX_74d0045ff7fab9f67adc0b1bda` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; - --- ---------------------------- --- Records of sys_dict_type --- ---------------------------- -BEGIN; -INSERT INTO `sys_dict_type` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `name`, `status`, `remark`, `code`) VALUES (1, '2024-01-28 08:19:12.777447', '2024-02-08 13:05:10.000000', 1, 1, '性别', 1, '性别单选', 'sys_user_gender'); -INSERT INTO `sys_dict_type` (`id`, `created_at`, `updated_at`, `create_by`, `update_by`, `name`, `status`, `remark`, `code`) VALUES (2, '2024-01-28 08:38:41.235185', '2024-01-29 02:11:33.000000', 1, 1, '菜单显示状态', 1, '菜单显示状态', 'sys_show_hide'); -COMMIT; - --- ---------------------------- --- Table structure for sys_login_log --- ---------------------------- -DROP TABLE IF EXISTS `sys_login_log`; -CREATE TABLE `sys_login_log` ( - `id` int NOT NULL AUTO_INCREMENT, - `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `ua` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `provider` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `user_id` int DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_3029712e0df6a28edaee46fd470` (`user_id`), - CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_login_log --- ---------------------------- -BEGIN; -COMMIT; - --- ---------------------------- --- Table structure for sys_menu --- ---------------------------- -DROP TABLE IF EXISTS `sys_menu`; -CREATE TABLE `sys_menu` ( - `id` int NOT NULL AUTO_INCREMENT, - `parent_id` int DEFAULT NULL, - `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `type` tinyint NOT NULL DEFAULT '0', - `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', - `order_no` int DEFAULT '0', - `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `keep_alive` tinyint NOT NULL DEFAULT '1', - `show` tinyint NOT NULL DEFAULT '1', - `status` tinyint NOT NULL DEFAULT '1', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `is_ext` tinyint NOT NULL DEFAULT '0', - `ext_open_mode` tinyint NOT NULL DEFAULT '1', - `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=128 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_menu --- ---------------------------- -BEGIN; -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (1, NULL, '/system', '系统管理', '', 0, 'ant-design:setting-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:46.668745', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (2, 1, '/system/user', '用户管理', 'system:user:list', 1, 'ant-design:user-outlined', 0, 'system/user/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:10:30.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (3, 1, '/system/role', '角色管理', 'system:role:list', 1, 'ep:user', 1, 'system/role/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:02.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (4, 1, '/system/menu', '菜单管理', 'system:menu:list', 1, 'ep:menu', 2, 'system/menu/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:18.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:44.567023', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (6, 5, '/system/monitor/online', '在线用户', 'system:online:list', 1, '', 0, 'system/monitor/online/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:13:59.519267', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (7, 5, '/sys/monitor/login-log', '登录日志', 'system:log:login:list', 1, '', 0, 'system/monitor/log/login/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:02.610719', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (8, 5, '/system/monitor/serve', '服务监控', 'system:serve:stat', 1, '', 4, 'system/monitor/serve/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:05.606355', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:52.967983', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (10, 9, '/system/task', '任务管理', '', 1, '', 0, 'system/schedule/task/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:14:39.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (11, 9, '/system/task/log', '任务日志', 'system:task:list', 1, '', 0, 'system/schedule/log/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:15:01.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (12, NULL, '/document', '文档', '', 0, 'ion:tv-outline', 2, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:42.514264', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (14, 12, 'https://www.typeorm.org/', 'Typeorm中文文档(外链)', NULL, 1, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:39:53.000000', 1, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (15, 12, 'https://docs.nestjs.cn/', 'Nest.js中文文档(内嵌)', '', 1, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:40:43.000000', 1, 2, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (20, 2, NULL, '新增', 'system:user:create', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (21, 2, '', '删除', 'system:user:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (22, 2, '', '更新', 'system:user:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (23, 2, '', '查询', 'system:user:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (24, 3, '', '新增', 'system:role:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (25, 3, '', '删除', 'system:role:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (26, 3, '', '修改', 'system:role:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (27, 3, '', '查询', 'system:role:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (28, 4, NULL, '新增', 'system:menu:create', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (29, 4, NULL, '删除', 'system:menu:delete', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (30, 4, '', '修改', 'system:menu:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (31, 4, NULL, '查询', 'system:menu:read', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (32, 6, '', '下线', 'system:online:kick', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (34, 10, '', '新增', 'system:task:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (35, 10, '', '删除', 'system:task:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (36, 10, '', '执行一次', 'system:task:once', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (37, 10, '', '查询', 'system:task:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (38, 10, '', '运行', 'system:task:start', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (39, 10, '', '暂停', 'system:task:stop', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (40, 10, '', '更新', 'system:task:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (41, 7, '', '查询登录日志', 'system:log:login:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (42, 7, '', '查询任务日志', 'system:log:task:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (43, NULL, '/about', '关于', '', 1, 'ant-design:info-circle-outlined', 260, 'account/about', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-10 09:35:41.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (48, NULL, '/tool', '系统工具', NULL, 0, 'ant-design:tool-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:28.327223', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (49, 48, '/tool/email', '邮件工具', 'system:tools:email', 1, 'ant-design:send-outlined', 1, 'tool/email/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:59:07.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (50, 49, NULL, '发送邮件', 'tools:email:send', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (51, 48, '/tool/storage', '存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:59:17.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (52, 51, NULL, '文件上传', 'upload:upload', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 01:04:08.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (53, 51, NULL, '文件删除', 'tool:storage:delete', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:56:01.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (54, 2, NULL, '修改密码', 'system:user:password', 2, '', 5, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (56, 1, '/system/dict-type', '字典管理', 'system:dict-type:list', 1, 'ant-design:book-outlined', 4, 'system/dict-type/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:12.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (57, 56, NULL, '新增', 'system:dict-type:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:20.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (58, 56, NULL, '更新', 'system:dict-type:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:26.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (59, 56, NULL, '删除', 'system:dict-type:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:42.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (60, 56, NULL, '查询', 'system:dict-type:info', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (61, 1, '/system/dept', '部门管理', 'system:dept:list', 1, 'ant-design:deployment-unit-outlined', 3, 'system/dept/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:55.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (62, 61, NULL, '新增', 'system:dept:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (63, 61, NULL, '更新', 'system:dept:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (64, 61, NULL, '删除', 'system:dept:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (65, 61, NULL, '查询', 'system:dept:read', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (68, 5, '/health', '健康检查', '', 1, '', 4, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:33.352155', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (69, 68, NULL, '网络', 'app:health:network', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (70, 68, NULL, '数据库', 'app:health: database', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (86, 1, '/param-config', '参数配置', 'system:param-config:list', 1, 'ep:edit', 255, 'system/param-config/index', 0, 1, 1, '2024-01-10 17:34:52.569663', '2024-01-19 02:11:27.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (87, 86, NULL, '查询', 'system:param-config:read', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:20.983241', '2024-01-10 17:39:20.983241', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (88, 86, NULL, '新增', 'system:param-config:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:57.543510', '2024-01-10 17:39:57.543510', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (89, 86, NULL, '更新', 'system:param-config:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:40:27.355944', '2024-01-10 17:40:27.355944', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (92, 86, NULL, '删除', 'system:param-config:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:57:32.059887', '2024-01-10 17:57:32.059887', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (107, 1, 'system/dict-item/:id', '字典项管理', 'system:dict-item:list', 1, 'ant-design:facebook-outlined', 255, 'system/dict-item/index', 0, 0, 1, '2024-01-28 09:21:17.409532', '2024-01-30 13:09:47.000000', 0, 1, '字典管理'); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (108, 107, NULL, '新增', 'system:dict-item:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:22:39.401758', '2024-01-28 22:38:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (109, 107, NULL, '更新', 'system:dict-item:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:26:43.911886', '2024-01-28 09:26:43.911886', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (110, 107, NULL, '删除', 'system:dict-item:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:28.535225', '2024-01-28 09:27:28.535225', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 1, 1, '2024-02-10 08:00:02.394616', '2024-02-10 09:35:34.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (116, 115, 'manage', '文件管理', 'netdisk:manage:list', 1, '', 252, 'netdisk/manage', 0, 1, 1, '2024-02-10 08:03:49.837348', '2024-02-10 09:34:41.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (120, 116, NULL, '删除', 'netdisk:manage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:09.480601', '2024-02-10 08:42:09.480601', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (121, 116, NULL, '获取文件上传token', 'netdisk:manage:token', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:57.688104', '2024-02-10 08:42:57.688104', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (122, 116, NULL, '添加文件备注', 'netdisk:manage:mark', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:43:40.117321', '2024-02-10 08:43:40.117321', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (123, 116, NULL, '下载文件', 'netdisk:manage:download', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:01.338984', '2024-02-10 08:44:01.338984', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (124, 116, NULL, '重命名文件或文件夹', 'netdisk:manage:rename', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:27.233379', '2024-02-10 08:45:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (125, 116, NULL, '复制文件或文件夹', 'netdisk:manage:copy', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:44.725391', '2024-02-10 08:45:48.000000', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (126, 116, NULL, '剪切文件或文件夹', 'netdisk:manage:cut', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:45:21.660511', '2024-02-10 08:45:21.660511', 0, 1, NULL); -INSERT INTO `sys_menu` (`id`, `parent_id`, `path`, `name`, `permission`, `type`, `icon`, `order_no`, `component`, `keep_alive`, `show`, `status`, `created_at`, `updated_at`, `is_ext`, `ext_open_mode`, `active_menu`) VALUES (127, 115, 'overview', '网盘概览', 'netdisk:overview:desc', 1, '', 254, 'netdisk/overview', 0, 1, 1, '2024-02-10 09:32:56.981190', '2024-02-10 09:34:18.000000', 0, 1, NULL); -COMMIT; - --- ---------------------------- --- Table structure for sys_role --- ---------------------------- -DROP TABLE IF EXISTS `sys_role`; -CREATE TABLE `sys_role` ( - `id` int NOT NULL AUTO_INCREMENT, - `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `status` tinyint DEFAULT '1', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `default` tinyint DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_223de54d6badbe43a5490450c3` (`name`) USING BTREE, - UNIQUE KEY `IDX_05edc0a51f41bb16b7d8137da9` (`value`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_role --- ---------------------------- -BEGIN; -INSERT INTO `sys_role` (`id`, `value`, `name`, `remark`, `status`, `created_at`, `updated_at`, `default`) VALUES (1, 'admin', '管理员', '超级管理员', 1, '2023-11-10 00:31:44.058463', '2024-01-28 21:08:39.000000', NULL); -INSERT INTO `sys_role` (`id`, `value`, `name`, `remark`, `status`, `created_at`, `updated_at`, `default`) VALUES (2, 'user', '用户', '', 1, '2023-11-10 00:31:44.058463', '2024-01-30 18:44:45.000000', 1); -INSERT INTO `sys_role` (`id`, `value`, `name`, `remark`, `status`, `created_at`, `updated_at`, `default`) VALUES (9, 'test', '测试', NULL, 1, '2024-01-23 22:46:52.408827', '2024-01-30 01:04:52.000000', NULL); -COMMIT; - --- ---------------------------- --- Table structure for sys_role_menus --- ---------------------------- -DROP TABLE IF EXISTS `sys_role_menus`; -CREATE TABLE `sys_role_menus` ( - `role_id` int NOT NULL, - `menu_id` int NOT NULL, - PRIMARY KEY (`role_id`,`menu_id`), - KEY `IDX_35ce749b04d57e226d059e0f63` (`role_id`), - KEY `IDX_2b95fdc95b329d66c18f5baed6` (`menu_id`), - CONSTRAINT `FK_2b95fdc95b329d66c18f5baed6d` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE, - CONSTRAINT `FK_35ce749b04d57e226d059e0f633` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- ---------------------------- --- Records of sys_role_menus --- ---------------------------- -BEGIN; -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 1); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 2); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 3); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 4); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 5); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 6); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 7); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 8); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 9); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 10); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 11); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 12); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 14); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 15); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 20); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 21); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 22); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 23); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 24); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 25); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 26); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 27); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 28); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 29); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 30); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 31); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 32); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 34); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 35); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 36); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 37); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 38); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 39); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 40); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 41); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 42); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 43); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 48); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 49); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 50); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 51); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 52); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 53); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 54); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 56); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 57); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 58); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 59); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 60); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 61); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 62); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 63); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 64); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 65); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 68); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 69); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 70); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 86); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 87); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 88); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 89); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 92); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 107); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 108); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 109); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 110); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (1, 111); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 1); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 5); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 6); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 7); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 8); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 9); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 10); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 11); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 12); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 14); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 15); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 32); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 34); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 35); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 36); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 37); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 38); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 39); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 40); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 41); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 42); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 43); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 48); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 49); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 50); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 51); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 52); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 53); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 56); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 57); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 58); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 59); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 60); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 68); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 69); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 70); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 86); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 87); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 88); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 89); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 92); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 107); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 108); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 109); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 110); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 111); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (2, 112); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 1); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 2); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 3); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 4); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 5); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 6); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 7); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 8); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 9); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 10); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 11); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 20); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 21); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 22); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 23); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 24); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 25); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 26); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 27); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 28); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 29); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 30); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 31); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 32); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 34); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 35); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 36); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 37); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 38); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 39); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 40); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 41); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 42); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 54); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 56); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 57); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 58); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 59); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 60); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 61); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 62); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 63); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 64); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 65); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 68); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 69); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 70); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 86); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 87); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 88); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 89); -INSERT INTO `sys_role_menus` (`role_id`, `menu_id`) VALUES (9, 92); -COMMIT; - --- ---------------------------- --- Table structure for sys_task --- ---------------------------- -DROP TABLE IF EXISTS `sys_task`; -CREATE TABLE `sys_task` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `service` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `type` tinyint NOT NULL DEFAULT '0', - `status` tinyint NOT NULL DEFAULT '1', - `start_time` datetime DEFAULT NULL, - `end_time` datetime DEFAULT NULL, - `limit` int DEFAULT '0', - `cron` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `every` int DEFAULT NULL, - `data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, - `job_opts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_ef8e5ab5ef2fe0ddb1428439ef` (`name`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_task --- ---------------------------- -BEGIN; -INSERT INTO `sys_task` (`id`, `name`, `service`, `type`, `status`, `start_time`, `end_time`, `limit`, `cron`, `every`, `data`, `job_opts`, `remark`, `created_at`, `updated_at`) VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-02-10 09:43:14.000000'); -INSERT INTO `sys_task` (`id`, `name`, `service`, `type`, `status`, `start_time`, `end_time`, `limit`, `cron`, `every`, `data`, `job_opts`, `remark`, `created_at`, `updated_at`) VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-02-10 09:43:14.000000'); -INSERT INTO `sys_task` (`id`, `name`, `service`, `type`, `status`, `start_time`, `end_time`, `limit`, `cron`, `every`, `data`, `job_opts`, `remark`, `created_at`, `updated_at`) VALUES (4, '访问百度首页', 'HttpRequestJob.handle', 0, 0, NULL, NULL, 1, '* * * * * ?', NULL, '{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}', NULL, '访问百度首页', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); -INSERT INTO `sys_task` (`id`, `name`, `service`, `type`, `status`, `start_time`, `end_time`, `limit`, `cron`, `every`, `data`, `job_opts`, `remark`, `created_at`, `updated_at`) VALUES (5, '发送邮箱', 'EmailJob.send', 0, 0, NULL, NULL, -1, '0 0 0 1 * ?', NULL, '{\"subject\":\"这是标题\",\"to\":\"zeyu57@163.com\",\"content\":\"这是正文\"}', NULL, '每月发送邮箱', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); -COMMIT; - --- ---------------------------- --- Table structure for sys_task_log --- ---------------------------- -DROP TABLE IF EXISTS `sys_task_log`; -CREATE TABLE `sys_task_log` ( - `id` int NOT NULL AUTO_INCREMENT, - `task_id` int DEFAULT NULL, - `status` tinyint NOT NULL DEFAULT '0', - `detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, - `consume_time` int DEFAULT '0', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_f4d9c36052fdb188ff5c089454b` (`task_id`), - CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_task_log --- ---------------------------- -BEGIN; -INSERT INTO `sys_task_log` (`id`, `task_id`, `status`, `detail`, `consume_time`, `created_at`, `updated_at`) VALUES (1, 3, 1, NULL, 0, '2024-02-05 03:06:22.037448', '2024-02-05 03:06:22.037448'); -INSERT INTO `sys_task_log` (`id`, `task_id`, `status`, `detail`, `consume_time`, `created_at`, `updated_at`) VALUES (2, 2, 1, NULL, 0, '2024-02-10 09:42:21.738712', '2024-02-10 09:42:21.738712'); -COMMIT; - --- ---------------------------- --- Table structure for sys_user --- ---------------------------- -DROP TABLE IF EXISTS `sys_user`; -CREATE TABLE `sys_user` ( - `id` int NOT NULL AUTO_INCREMENT, - `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `psalt` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint DEFAULT '1', - `qq` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `dept_id` int DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_9e7164b2f1ea1348bc0eb0a7da` (`username`) USING BTREE, - KEY `FK_96bde34263e2ae3b46f011124ac` (`dept_id`), - CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; - --- ---------------------------- --- Records of sys_user --- ---------------------------- -BEGIN; -INSERT INTO `sys_user` (`id`, `username`, `password`, `avatar`, `email`, `phone`, `remark`, `psalt`, `status`, `qq`, `created_at`, `updated_at`, `nickname`, `dept_id`) VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-01-29 09:49:43.000000', 'bqy', 1); -INSERT INTO `sys_user` (`id`, `username`, `password`, `avatar`, `email`, `phone`, `remark`, `psalt`, `status`, `qq`, `created_at`, `updated_at`, `nickname`, `dept_id`) VALUES (2, 'user', 'dbd89546dec743f82bb9073d6ac39361', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', 'luffy@qq.com', '10010', '王路飞', 'qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-01-29 09:49:57.000000', 'luffy', 8); -INSERT INTO `sys_user` (`id`, `username`, `password`, `avatar`, `email`, `phone`, `remark`, `psalt`, `status`, `qq`, `created_at`, `updated_at`, `nickname`, `dept_id`) VALUES (8, 'developer', 'f03fa2a99595127b9a39587421d471f6', '/upload/cfd0d14459bc1a47-202402032141838.jpeg', 'nami@qq.com', '10000', '小贼猫', 'NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-02-03 21:41:18.000000', '娜美', 7); -COMMIT; - --- ---------------------------- --- Table structure for sys_user_roles --- ---------------------------- -DROP TABLE IF EXISTS `sys_user_roles`; -CREATE TABLE `sys_user_roles` ( - `user_id` int NOT NULL, - `role_id` int NOT NULL, - PRIMARY KEY (`user_id`,`role_id`), - KEY `IDX_96311d970191a044ec048011f4` (`user_id`), - KEY `IDX_6d61c5b3f76a3419d93a421669` (`role_id`), - CONSTRAINT `FK_6d61c5b3f76a3419d93a4216695` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`), - CONSTRAINT `FK_96311d970191a044ec048011f44` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- ---------------------------- --- Records of sys_user_roles --- ---------------------------- -BEGIN; -INSERT INTO `sys_user_roles` (`user_id`, `role_id`) VALUES (1, 1); -INSERT INTO `sys_user_roles` (`user_id`, `role_id`) VALUES (2, 2); -INSERT INTO `sys_user_roles` (`user_id`, `role_id`) VALUES (8, 2); -COMMIT; - --- ---------------------------- --- Table structure for todo --- ---------------------------- -DROP TABLE IF EXISTS `todo`; -CREATE TABLE `todo` ( - `id` int NOT NULL AUTO_INCREMENT, - `value` varchar(255) NOT NULL, - `user_id` int DEFAULT NULL, - `status` tinyint NOT NULL DEFAULT '0', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`), - KEY `FK_9cb7989853c4cb7fe427db4b260` (`user_id`), - CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- ---------------------------- --- Records of todo --- ---------------------------- -BEGIN; -INSERT INTO `todo` (`id`, `value`, `user_id`, `status`, `created_at`, `updated_at`) VALUES (1, 'nest.js', NULL, 0, '2023-11-10 00:31:44.139730', '2023-11-10 00:31:44.147629'); -COMMIT; - --- ---------------------------- --- Table structure for tool_storage --- ---------------------------- -DROP TABLE IF EXISTS `tool_storage`; -CREATE TABLE `tool_storage` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名', - `fileName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '真实文件名', - `ext_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `user_id` int DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=79 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- ---------------------------- --- Records of tool_storage --- ---------------------------- -BEGIN; -INSERT INTO `tool_storage` (`id`, `created_at`, `updated_at`, `name`, `fileName`, `ext_name`, `path`, `type`, `size`, `user_id`) VALUES (78, '2024-02-03 21:41:16.851178', '2024-02-03 21:41:16.851178', 'cfd0d14459bc1a47-202402032141838.jpeg', 'cfd0d14459bc1a47.jpeg', 'jpeg', '/upload/cfd0d14459bc1a47-202402032141838.jpeg', '图片', '33.92 KB', 1); -COMMIT; - --- ---------------------------- --- Table structure for user_access_tokens --- ---------------------------- -DROP TABLE IF EXISTS `user_access_tokens`; -CREATE TABLE `user_access_tokens` ( - `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `expired_at` datetime NOT NULL COMMENT '令牌过期时间', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', - `user_id` int DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `FK_e9d9d0c303432e4e5e48c1c3e90` (`user_id`), - CONSTRAINT `FK_e9d9d0c303432e4e5e48c1c3e90` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- ---------------------------- --- Records of user_access_tokens --- ---------------------------- -BEGIN; -INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('09cf7b0a-62e0-45ee-96b0-e31de32361e0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1MDkxNTd9.0gtKlcxrxQ-TarEai2lsBnfMc852ZDYHeSjjhpo5Fn8', '2024-02-11 04:05:58', '2024-02-10 04:05:57.696509', 1); -INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('3f7dffae-db1f-47dc-9677-5c956c3de39e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczMTEzMDJ9.D5Qpht1RquKor8WtgfGAcCp8LwG7z3FZhIwbyQzhDmE', '2024-02-08 21:08:22', '2024-02-07 21:08:22.130066', 1); -INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('40342c3e-194c-42eb-adee-189389839195', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxNjF9.tRQOxhB-01Pcut5MXm4L5D1OrbMJfS4LfUys0XB4kWs', '2024-02-09 14:02:41', '2024-02-08 14:02:41.081164', 1); -INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('9d1ba8e9-dffc-4b15-b21f-4a90f196e39c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1Mjc5MDV9.7LeiS3LBBdiAc7YrULWpmnI1oNSvR79K-qjEOlBYOnI', '2024-02-11 09:18:26', '2024-02-10 09:18:25.656695', 1); -INSERT INTO `user_access_tokens` (`id`, `value`, `expired_at`, `created_at`, `user_id`) VALUES ('edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxMjd9.VRuJHGca2IPrdfTyW09wfhht4x8JX207pKG-0aZyF60', '2024-02-09 14:02:07', '2024-02-08 14:02:07.390658', 1); -COMMIT; - --- ---------------------------- --- Table structure for user_refresh_tokens --- ---------------------------- -DROP TABLE IF EXISTS `user_refresh_tokens`; -CREATE TABLE `user_refresh_tokens` ( - `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `expired_at` datetime NOT NULL COMMENT '令牌过期时间', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', - `accessTokenId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `REL_1dfd080c2abf42198691b60ae3` (`accessTokenId`), - CONSTRAINT `FK_1dfd080c2abf42198691b60ae39` FOREIGN KEY (`accessTokenId`) REFERENCES `user_access_tokens` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - --- ---------------------------- --- Records of user_refresh_tokens --- ---------------------------- -BEGIN; -INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('202d0969-6721-4f6f-bf34-f0d1931d4d01', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRpOXVYei1TdldjdWRnclFXVmFXIiwiaWF0IjoxNzA3MzcyMTYxfQ.NOQufR5EAPE2uZoyenmAj9H7S7qo4d6W1aW2ojDxZQc', '2024-03-09 14:02:41', '2024-02-08 14:02:41.091492', '40342c3e-194c-42eb-adee-189389839195'); -INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('461f9b7c-e500-4762-a6d9-f9ea47163064', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicXJvTWNYMnhNRW5uRmZGWkQtaUx0IiwiaWF0IjoxNzA3MzExMzAyfQ.dFIWCePZnn2z2Qv1D5PKBKXUwVDI0Gp091MIOi9jiIo', '2024-03-08 21:08:22', '2024-02-07 21:08:22.145464', '3f7dffae-db1f-47dc-9677-5c956c3de39e'); -INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('b375e623-2d82-48f0-9b7a-9058e3850cc6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicDhUMzdGNFFaUDJHLU5yNGVha21wIiwiaWF0IjoxNzA3MzcyMTI3fQ.fn3It6RKIxXlKmqixg0BMmY_YsQmAxtetueqW-0y1IM', '2024-03-09 14:02:07', '2024-02-08 14:02:07.410008', 'edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb'); -INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('e620ccc1-9e40-4387-9f21-f0722e535a63', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNE5WdmFIc2hWaU05ZFh0QnVBaHNsIiwiaWF0IjoxNzA3NTI3OTA1fQ.zzyGX0mOJe6KWpTzIi7We9d9c0MRuDeGC86DMB0Vubs', '2024-03-11 09:18:26', '2024-02-10 09:18:25.664251', '9d1ba8e9-dffc-4b15-b21f-4a90f196e39c'); -INSERT INTO `user_refresh_tokens` (`id`, `value`, `expired_at`, `created_at`, `accessTokenId`) VALUES ('f9a003e8-91b7-41ee-979e-e39cca3534ec', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGJQdl9SVjFtUl80N0o0TGF0QlV5IiwiaWF0IjoxNzA3NTA5MTU3fQ.oEVdWSigTpAQY7F8MlwBnedldH0sJT1YF1Mt0ZUbIw4', '2024-03-11 04:05:58', '2024-02-10 04:05:57.706763', '09cf7b0a-62e0-45ee-96b0-e31de32361e0'); -COMMIT; - -SET FOREIGN_KEY_CHECKS = 1; diff --git a/deploy/web/default.conf b/deploy/web/default.conf deleted file mode 100644 index c4f24e0..0000000 --- a/deploy/web/default.conf +++ /dev/null @@ -1,54 +0,0 @@ -map $http_upgrade $connection_upgrade { - default upgrade; - '' close; -} - -server { - listen 80; - absolute_redirect off; #取消绝对路径的重定向 - sendfile on; - default_type application/octet-stream; - - gzip on; - gzip_http_version 1.1; - gzip_disable "MSIE [1-6]\."; - gzip_min_length 256; - gzip_vary on; - gzip_proxied expired no-cache no-store private auth; - gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; - gzip_comp_level 9; - - root /usr/share/nginx/html; - - location / { - # same docker config - root /usr/share/nginx/html; - index index.html; - # support history mode - try_files $uri $uri/ /index.html; - } - - # 后端服务 - location ^~ /api/ { - proxy_pass http://nest-admin-server:7001/; # 转发规则 - proxy_set_header Host $proxy_host; # 修改转发请求头,让目标应用可以受到真实的请求 - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - # websocket服务 - location ^~ /ws-api/ { - proxy_pass http://nest-admin-server:7002/; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - - proxy_set_header Host $host; - proxy_set_header X-real-ip $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } - -} diff --git a/docker-compose.yml b/docker-compose.yml index 4139438..8d84b03 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: '3' services: mysql: image: mysql:latest - container_name: nest-admin-mysql + container_name: huaxin-admin-mysql restart: always env_file: - .env @@ -22,11 +22,11 @@ services: - ./__data/mysql/:/var/lib/mysql/ # ./__data/mysql/ 路径可以替换成自己的路径 - ./deploy/sql/:/docker-entrypoint-initdb.d/ # 初始化的脚本,若 ./__data/mysql/ 文件夹存在数据,则不会执行初始化脚本 networks: - - nest_admin_net + - huaxin_admin_net redis: image: redis:alpine - container_name: nest-admin-redis + container_name: huaxin-admin-redis restart: always env_file: - .env @@ -36,14 +36,14 @@ services: command: > --requirepass ${REDIS_PASSWORD} networks: - - nest_admin_net + - huaxin_admin_net - nest-admin-server: + huaxin-admin-server: # build: 从当前路径构建镜像 build: context: . dockerfile: Dockerfile - container_name: nest-admin-server + container_name: huaxin-admin-server restart: always env_file: - .env @@ -57,8 +57,8 @@ services: - mysql - redis networks: - - nest_admin_net + - huaxin_admin_net networks: - nest_admin_net: - name: nest_admin_net + huaxin_admin_net: + name: huaxin_admin_net diff --git a/ecosystem.config.js b/ecosystem.config.js index 8f2eb78..4b329d9 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -5,7 +5,7 @@ const cpuLen = cpus().length module.exports = { apps: [ { - name: 'nest-admin', + name: 'huaxin-admin', script: './dist/main.js', autorestart: true, exec_mode: 'cluster', diff --git a/package.json b/package.json index e137183..8126b51 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "nest-admin", + "name": "huaxin-admin", "version": "2.0.0", "private": true, "packageManager": "pnpm@8.10.2", @@ -38,7 +38,7 @@ "docker:build": "docker compose --env-file .env --env-file .env.production up --build", "docker:up": "docker compose --env-file .env --env-file .env.production up -d --no-build", "docker:down": "docker compose --env-file .env --env-file .env.production down", - "docker:rmi": "docker compose --env-file .env --env-file .env.production stop nest-admin-server && docker container rm nest-admin-server && docker rmi nest-admin-server", + "docker:rmi": "docker compose --env-file .env --env-file .env.production stop huaxin-admin-server && docker container rm huaxin-admin-server && docker rmi huaxin-admin-server", "docker:logs": "docker compose --env-file .env --env-file .env.production logs -f", "c": "git add . && git cz && git push", "release": "standard-version", diff --git a/src/migrations/1707996695540-initData.ts b/src/migrations/1707996695540-initData.ts index c4c3e6e..75f758c 100644 --- a/src/migrations/1707996695540-initData.ts +++ b/src/migrations/1707996695540-initData.ts @@ -3,7 +3,7 @@ import path from 'node:path' import { MigrationInterface, QueryRunner } from 'typeorm' -const sql = fs.readFileSync(path.join(__dirname, '../../deploy/sql/nest_admin.sql'), 'utf8') +const sql = fs.readFileSync(path.join(__dirname, '../../deploy/sql/hxoa.sql'), 'utf8') export class InitData1707996695540 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { From 5b562e1d3bd51507a9e90450f23529aaa5717fee Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 28 Feb 2024 12:59:45 +0800 Subject: [PATCH 03/64] =?UTF-8?q?build:=20=E4=BC=98=E5=8C=96=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 deploy.sh diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..b39ff4d --- /dev/null +++ b/deploy.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# 拉取最新的代码 +git pull + +docker compose --env-file .env --env-file .env.production up -d --build \ No newline at end of file From 2821dc6dadb11c0784bf460a2872571e235b4996 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 28 Feb 2024 15:07:47 +0800 Subject: [PATCH 04/64] =?UTF-8?q?fix:=20=E5=88=86=E9=A1=B5=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=97=B6=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6=E7=9A=84?= =?UTF-8?q?number=E7=B1=BB=E5=9E=8B=E7=A9=BA=E5=80=BC=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 2 +- src/modules/system/menu/menu.service.ts | 4 ++-- src/modules/system/role/role.controller.ts | 4 ++-- src/modules/system/role/role.dto.ts | 12 ++++++++++- src/modules/system/role/role.service.ts | 24 +++++++++++++++++----- src/modules/system/task/task.service.ts | 4 ++-- src/modules/user/dto/user.dto.ts | 3 ++- src/modules/user/user.service.ts | 4 ++-- src/shared/database/typeorm-logger.ts | 2 +- 9 files changed, 42 insertions(+), 17 deletions(-) diff --git a/.env.development b/.env.development index ce6ed42..b33890f 100644 --- a/.env.development +++ b/.env.development @@ -19,7 +19,7 @@ DB_DATABASE = hxoa DB_USERNAME = root DB_PASSWORD = huaxin123 DB_SYNCHRONIZE = true -DB_LOGGING = ["error"] +DB_LOGGING = "all" # redis REDIS_PORT = 6379 diff --git a/src/modules/system/menu/menu.service.ts b/src/modules/system/menu/menu.service.ts index aa288cd..85ce773 100644 --- a/src/modules/system/menu/menu.service.ts +++ b/src/modules/system/menu/menu.service.ts @@ -2,7 +2,7 @@ import { InjectRedis } from '@liaoliaots/nestjs-redis' import { Injectable } from '@nestjs/common' import { InjectRepository } from '@nestjs/typeorm' import Redis from 'ioredis' -import { concat, isEmpty, uniq } from 'lodash' +import { concat, isEmpty, isNumber, uniq } from 'lodash' import { In, IsNull, Like, Not, Repository } from 'typeorm' @@ -45,7 +45,7 @@ export class MenuService { ...(path && { path: Like(`%${path}%`) }), ...(permission && { permission: Like(`%${permission}%`) }), ...(component && { component: Like(`%${component}%`) }), - ...(status && { status }), + ...(isNumber(status) ? { status } : null), }, order: { orderNo: 'ASC' }, }) diff --git a/src/modules/system/role/role.controller.ts b/src/modules/system/role/role.controller.ts index c3ac95b..1f5b7a0 100644 --- a/src/modules/system/role/role.controller.ts +++ b/src/modules/system/role/role.controller.ts @@ -19,7 +19,7 @@ import { RoleEntity } from '~/modules/system/role/role.entity' import { MenuService } from '../menu/menu.service' -import { RoleDto, RoleUpdateDto } from './role.dto' +import { RoleDto, RoleQueryDto, RoleUpdateDto } from './role.dto' import { RoleInfo } from './role.model' import { RoleService } from './role.service' @@ -44,7 +44,7 @@ export class RoleController { @ApiOperation({ summary: '获取角色列表' }) @ApiResult({ type: [RoleEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: PagerDto) { + async list(@Query() dto: RoleQueryDto) { return this.roleService.findAll(dto) } diff --git a/src/modules/system/role/role.dto.ts b/src/modules/system/role/role.dto.ts index 66d2178..2e9d0e2 100644 --- a/src/modules/system/role/role.dto.ts +++ b/src/modules/system/role/role.dto.ts @@ -1,12 +1,14 @@ -import { ApiProperty, PartialType } from '@nestjs/swagger' +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger' import { IsArray, IsIn, + IsInt, IsOptional, IsString, Matches, MinLength, } from 'class-validator' +import { PagerDto } from '~/common/dto/pager.dto' export class RoleDto { @ApiProperty({ description: '角色名称' }) @@ -36,3 +38,11 @@ export class RoleDto { } export class RoleUpdateDto extends PartialType(RoleDto) {} +export class RoleQueryDto extends IntersectionType(PagerDto, PartialType(RoleDto)) { + + @ApiProperty({ description: '状态', example: 0, required: false }) + @IsInt() + @IsOptional() + status?: number + +} diff --git a/src/modules/system/role/role.service.ts b/src/modules/system/role/role.service.ts index 3eaba39..2f0c807 100644 --- a/src/modules/system/role/role.service.ts +++ b/src/modules/system/role/role.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common' import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm' -import { isEmpty } from 'lodash' -import { EntityManager, In, Repository } from 'typeorm' +import { isEmpty, isNumber } from 'lodash' +import { EntityManager, In, Like, Repository } from 'typeorm' import { PagerDto } from '~/common/dto/pager.dto' import { ROOT_ROLE_ID } from '~/constants/system.constant' @@ -11,7 +11,7 @@ import { SseService } from '~/modules/sse/sse.service' import { MenuEntity } from '~/modules/system/menu/menu.entity' import { RoleEntity } from '~/modules/system/role/role.entity' -import { RoleDto, RoleUpdateDto } from './role.dto' +import { RoleDto, RoleQueryDto, RoleUpdateDto } from './role.dto' @Injectable() export class RoleService { @@ -31,8 +31,22 @@ export class RoleService { async findAll({ page, pageSize, - }: PagerDto): Promise> { - return paginate(this.roleRepository, { page, pageSize }) + name, + value, + status, + }: RoleQueryDto): Promise> { + const queryBuilder = this.roleRepository + .createQueryBuilder('role') + .where({ + ...(name ? { name: Like(`%${name}%`) } : null), + ...(value ? { value: Like(`%${value}%`) } : null), + ...(isNumber(status) ? { status } : null), + }) + + return paginate(queryBuilder, { + page, + pageSize, + }) } /** diff --git a/src/modules/system/task/task.service.ts b/src/modules/system/task/task.service.ts index 8cc0e28..eaba96d 100644 --- a/src/modules/system/task/task.service.ts +++ b/src/modules/system/task/task.service.ts @@ -12,7 +12,7 @@ import { UnknownElementException } from '@nestjs/core/errors/exceptions/unknown- import { InjectRepository } from '@nestjs/typeorm' import { Queue } from 'bull' import Redis from 'ioredis' -import { isEmpty } from 'lodash' +import { isEmpty, isNumber } from 'lodash' import { Like, Repository } from 'typeorm' import { BusinessException } from '~/common/exceptions/biz.exception' @@ -103,7 +103,7 @@ export class TaskService implements OnModuleInit { ...(name ? { name: Like(`%${name}%`) } : null), ...(service ? { service: Like(`%${service}%`) } : null), ...(type ? { type } : null), - ...(status ? { status } : null), + ...(isNumber(status) ? { status } : null), }) .orderBy('task.id', 'ASC') diff --git a/src/modules/user/dto/user.dto.ts b/src/modules/user/dto/user.dto.ts index 31e5ddc..60a44fa 100644 --- a/src/modules/user/dto/user.dto.ts +++ b/src/modules/user/dto/user.dto.ts @@ -27,7 +27,7 @@ export class UserDto { @ApiProperty({ description: '登录账号', example: 'admin' }) @IsString() @Matches(/^[a-z0-9A-Z\W_]+$/) - @MinLength(4) + @MinLength(1) @MaxLength(20) username: string @@ -95,4 +95,5 @@ export class UserQueryDto extends IntersectionType(PagerDto, PartialTyp @IsInt() @IsOptional() status?: number + } diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 806526c..294c5ef 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -2,7 +2,7 @@ import { InjectRedis } from '@liaoliaots/nestjs-redis' import { BadRequestException, Injectable } from '@nestjs/common' import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm' import Redis from 'ioredis' -import { isEmpty, isNil } from 'lodash' +import { isEmpty, isNil, isNumber } from 'lodash' import { EntityManager, In, Like, Repository } from 'typeorm' @@ -281,7 +281,7 @@ export class UserService { ...(username ? { username: Like(`%${username}%`) } : null), ...(nickname ? { nickname: Like(`%${nickname}%`) } : null), ...(email ? { email: Like(`%${email}%`) } : null), - ...(status ? { status } : null), + ...(isNumber(status) ? { status } : null), }) if (deptId) diff --git a/src/shared/database/typeorm-logger.ts b/src/shared/database/typeorm-logger.ts index 6db7638..434932b 100644 --- a/src/shared/database/typeorm-logger.ts +++ b/src/shared/database/typeorm-logger.ts @@ -3,7 +3,7 @@ import { Logger as ITypeORMLogger, LoggerOptions, QueryRunner } from 'typeorm' export class TypeORMLogger implements ITypeORMLogger { private logger = new Logger(TypeORMLogger.name) - + constructor(private options: LoggerOptions) {} logQuery(query: string, parameters?: any[], _queryRunner?: QueryRunner) { From 78fa645b4c24454c26d50bbb36ed565d81e27d5a Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 28 Feb 2024 16:00:07 +0800 Subject: [PATCH 05/64] =?UTF-8?q?feat:=20=E6=96=B0=E5=BB=BAsql?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- README.md | 10 ---------- deploy/sql/contracts.sql | 11 +++++++++++ deploy/sql/vehicle_usage.sql | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 deploy/sql/contracts.sql create mode 100644 deploy/sql/vehicle_usage.sql diff --git a/.gitignore b/.gitignore index 7fbd170..b3626e6 100644 --- a/.gitignore +++ b/.gitignore @@ -73,5 +73,5 @@ out # temp data __data -public/upload +public/upload/* types/env.d.ts \ No newline at end of file diff --git a/README.md b/README.md index 6ff61a3..c62d18a 100644 --- a/README.md +++ b/README.md @@ -6,21 +6,11 @@ - `mysql` `8.x`+ - 使用 [`pnpm`](https://pnpm.io/zh/) 包管理器安装项目依赖 -演示环境账号密码: | 账号 | 密码 | 权限 | | :-------: | :----: | :--------: | | admin | a123456 | 超级管理员 | -> 所有新建的用户初始密码都为 a123456 - -本地部署账号密码: - -| 账号 | 密码 | 权限 | -| :-------: | :----: | :--------: | -| admin | a123456 | 超级管理员 | - - ## 本地开发 diff --git a/deploy/sql/contracts.sql b/deploy/sql/contracts.sql new file mode 100644 index 0000000..307c789 --- /dev/null +++ b/deploy/sql/contracts.sql @@ -0,0 +1,11 @@ +CREATE TABLE contracts ( + id INT AUTO_INCREMENT PRIMARY KEY COMMENT '合同编号', + contract_title VARCHAR(255) COMMENT '合同标题', + contract_type VARCHAR(255) COMMENT '合同类型', + party_a VARCHAR(255) COMMENT '甲方', + party_b VARCHAR(255) COMMENT '乙方', + signing_date DATE COMMENT '签订日期', + delivery_deadline DATE COMMENT '交付期限', + review_result VARCHAR(255) COMMENT '审核结果', + attachment_url VARCHAR(255) COMMENT '下载附件' +); \ No newline at end of file diff --git a/deploy/sql/vehicle_usage.sql b/deploy/sql/vehicle_usage.sql new file mode 100644 index 0000000..081289b --- /dev/null +++ b/deploy/sql/vehicle_usage.sql @@ -0,0 +1,15 @@ +CREATE TABLE vehicle_usage ( + id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键,用于唯一标识每条记录', + `year` INT COMMENT '年度', + vehicle_name VARCHAR(255) COMMENT '外出使用的车辆名称', + license_plate VARCHAR(255) COMMENT '车牌号', + applicant VARCHAR(255) COMMENT '申请人', + driver VARCHAR(255) COMMENT '出行司机', + current_mileage FLOAT COMMENT '当前车辆里程数(KM)', + expected_travel_time DATETIME COMMENT '预计出行时间', + purpose VARCHAR(255) COMMENT '使用事由', + actual_return_time DATETIME COMMENT '实际回司时间', + return_mileage FLOAT COMMENT '回城车辆里程数(KM)', + approval_by_leader VARCHAR(255) COMMENT '公司领导审批', + remarks VARCHAR(255) COMMENT '备注' +); \ No newline at end of file From aeb25ac816214a08dc667874d1cf89bc1e4a24ce Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 28 Feb 2024 16:12:34 +0800 Subject: [PATCH 06/64] =?UTF-8?q?feat:=20=E6=A1=86=E6=9E=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 +- LICENSE | 2 +- README.md | 2 +- src/utils/crypto.util.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.env b/.env index 68c4ac4..6a2b67a 100644 --- a/.env +++ b/.env @@ -13,7 +13,7 @@ TZ = Asia/Shanghai # OSS(minio) OSS_ACCESSKEY=xxx OSS_SECRETKEY=xxx -OSS_DOMAIN=https://cdn.buqiyuan.site +OSS_DOMAIN=https://cdn.louis.site OSS_BUCKET=hxoa OSS_ZONE=Zone_z2 # Zone_as0 | Zone_na0 | Zone_z0 | Zone_z1 | Zone_z2 OSS_ACCESS_TYPE=public # or private diff --git a/LICENSE b/LICENSE index 5c341e9..ea7c2f9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-present buqiyuan +Copyright (c) 2024-present Louis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index c62d18a..a2a3f7b 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ docker compose --env-file .env --env-file .env.production down ```bash pnpm docker:rmi # or -docker rmi buqiyuan/huaxin-admin-server:stable +docker rmi huaxin-admin-server:stable ``` 查看实时日志输出 diff --git a/src/utils/crypto.util.ts b/src/utils/crypto.util.ts index 143938b..83fd841 100644 --- a/src/utils/crypto.util.ts +++ b/src/utils/crypto.util.ts @@ -1,7 +1,7 @@ import CryptoJS from 'crypto-js' -const key = CryptoJS.enc.Utf8.parse('buqiyuanabcdefe9bc') -const iv = CryptoJS.enc.Utf8.parse('0123456789buqiyuan') +const key = CryptoJS.enc.Utf8.parse('louisabcdefe9bc') +const iv = CryptoJS.enc.Utf8.parse('0123456789louis') export function aesEncrypt(data) { if (!data) From a98e122fbc778045b3a9cd812173b1fda856f86b Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 28 Feb 2024 17:02:46 +0800 Subject: [PATCH 07/64] refactor: format --- .eslintignore | 7 + .eslintrc.cjs | 22 + .prettierignore | 10 + .prettierrc.cjs | 11 + deploy/sql/contracts.sql | 7 +- package.json | 16 +- pnpm-lock.yaml | 587 +++++++++++++ src/app.module.ts | 45 +- src/common/adapters/fastify.adapter.ts | 34 +- src/common/adapters/socket.adapter.ts | 22 +- src/common/decorators/api-result.decorator.ts | 45 +- src/common/decorators/bypass.decorator.ts | 6 +- src/common/decorators/cookie.decorator.ts | 12 +- src/common/decorators/cron-once.decorator.ts | 16 +- src/common/decorators/field.decorator.ts | 121 +-- src/common/decorators/http.decorator.ts | 20 +- src/common/decorators/id-param.decorator.ts | 14 +- .../decorators/idempotence.decorator.ts | 12 +- src/common/decorators/swagger.decorator.ts | 8 +- src/common/decorators/transform.decorator.ts | 113 ++- src/common/dto/cursor.dto.ts | 10 +- src/common/dto/delete.dto.ts | 4 +- src/common/dto/id.dto.ts | 4 +- src/common/dto/pager.dto.ts | 16 +- src/common/entity/common.entity.ts | 20 +- src/common/exceptions/biz.exception.ts | 28 +- src/common/exceptions/not-found.exception.ts | 8 +- src/common/exceptions/socket.exception.ts | 32 +- src/common/filters/any-exception.filter.ts | 81 +- .../interceptors/idempotence.interceptor.ts | 128 ++- .../interceptors/logging.interceptor.ts | 39 +- .../interceptors/timeout.interceptor.ts | 17 +- .../interceptors/transform.interceptor.ts | 33 +- src/common/model/response.model.ts | 29 +- src/common/pipes/parse-int.pipe.ts | 14 +- src/config/app.config.ts | 10 +- src/config/database.config.ts | 27 +- src/config/index.ts | 46 +- src/config/mailer.config.ts | 10 +- src/config/oss.config.ts | 22 +- src/config/redis.config.ts | 10 +- src/config/security.config.ts | 10 +- src/config/swagger.config.ts | 10 +- src/constants/cache.constant.ts | 2 +- src/constants/oss.constant.ts | 12 +- src/constants/response.constant.ts | 4 +- src/constants/system.constant.ts | 8 +- src/global/env.ts | 58 +- src/helper/catchError.ts | 4 +- src/helper/crud/base.service.ts | 35 +- src/helper/crud/crud.factory.ts | 75 +- src/helper/genRedisKey.ts | 10 +- src/helper/paginate/create-pagination.ts | 19 +- src/helper/paginate/index.ts | 80 +- src/helper/paginate/interface.ts | 26 +- src/helper/paginate/pagination.ts | 11 +- src/main.ts | 114 ++- src/migrations/1707996695540-initData.ts | 13 +- src/modules/auth/auth.constant.ts | 14 +- src/modules/auth/auth.controller.ts | 42 +- src/modules/auth/auth.module.ts | 60 +- src/modules/auth/auth.service.ts | 107 ++- .../auth/controllers/account.controller.ts | 44 +- .../auth/controllers/captcha.controller.ts | 38 +- .../auth/controllers/email.controller.ts | 27 +- .../auth/decorators/allow-anon.decorator.ts | 6 +- .../auth/decorators/auth-user.decorator.ts | 20 +- .../auth/decorators/permission.decorator.ts | 55 +- .../auth/decorators/public.decorator.ts | 6 +- .../auth/decorators/resource.decorator.ts | 24 +- src/modules/auth/dto/account.dto.ts | 52 +- src/modules/auth/dto/auth.dto.ts | 18 +- src/modules/auth/dto/captcha.dto.ts | 24 +- .../auth/entities/access-token.entity.ts | 18 +- .../auth/entities/refresh-token.entity.ts | 14 +- src/modules/auth/guards/jwt-auth.guard.ts | 79 +- src/modules/auth/guards/local.guard.ts | 8 +- src/modules/auth/guards/rbac.guard.ts | 70 +- src/modules/auth/guards/resource.guard.ts | 70 +- src/modules/auth/models/auth.model.ts | 8 +- src/modules/auth/services/captcha.service.ts | 33 +- src/modules/auth/services/token.service.ts | 102 +-- src/modules/auth/strategies/jwt.strategy.ts | 18 +- src/modules/auth/strategies/local.strategy.ts | 21 +- src/modules/contract/contract.controller.ts | 63 ++ src/modules/contract/contract.entity.ts | 39 + src/modules/contract/contract.module.ts | 9 + src/modules/contract/contract.service.ts | 4 + src/modules/health/health.controller.ts | 22 +- src/modules/health/health.module.ts | 8 +- src/modules/netdisk/manager/manage.class.ts | 3 +- .../netdisk/manager/manage.controller.ts | 76 +- src/modules/netdisk/manager/manage.dto.ts | 67 +- src/modules/netdisk/manager/manage.service.ts | 820 ++++++++---------- src/modules/netdisk/netdisk.module.ts | 29 +- .../netdisk/overview/overview.controller.ts | 34 +- .../netdisk/overview/overview.service.ts | 135 +-- src/modules/sse/sse.controller.ts | 58 +- src/modules/sse/sse.module.ts | 6 +- src/modules/sse/sse.service.ts | 60 +- src/modules/system/dept/dept.controller.ts | 54 +- src/modules/system/dept/dept.dto.ts | 24 +- src/modules/system/dept/dept.entity.ts | 26 +- src/modules/system/dept/dept.module.ts | 16 +- src/modules/system/dept/dept.service.ts | 81 +- .../system/dict-item/dict-item.controller.ts | 46 +- src/modules/system/dict-item/dict-item.dto.ts | 24 +- .../system/dict-item/dict-item.entity.ts | 20 +- .../system/dict-item/dict-item.module.ts | 12 +- .../system/dict-item/dict-item.service.ts | 48 +- .../system/dict-type/dict-type.controller.ts | 48 +- src/modules/system/dict-type/dict-type.dto.ts | 20 +- .../system/dict-type/dict-type.entity.ts | 14 +- .../system/dict-type/dict-type.module.ts | 12 +- .../system/dict-type/dict-type.service.ts | 48 +- src/modules/system/log/dto/log.dto.ts | 26 +- .../system/log/entities/captcha-log.entity.ts | 14 +- .../system/log/entities/login-log.entity.ts | 18 +- .../system/log/entities/task-log.entity.ts | 16 +- src/modules/system/log/log.controller.ts | 48 +- src/modules/system/log/log.module.ts | 22 +- src/modules/system/log/models/log.model.ts | 30 +- .../log/services/captcha-log.service.ts | 26 +- .../system/log/services/login-log.service.ts | 59 +- .../system/log/services/task-log.service.ts | 33 +- src/modules/system/menu/menu.controller.ts | 61 +- src/modules/system/menu/menu.dto.ts | 32 +- src/modules/system/menu/menu.entity.ts | 36 +- src/modules/system/menu/menu.model.ts | 6 +- src/modules/system/menu/menu.module.ts | 22 +- src/modules/system/menu/menu.service.ts | 174 ++-- .../system/online/online.controller.ts | 31 +- src/modules/system/online/online.dto.ts | 6 +- src/modules/system/online/online.model.ts | 18 +- src/modules/system/online/online.module.ts | 18 +- src/modules/system/online/online.service.ts | 115 ++- .../param-config/param-config.controller.ts | 34 +- .../system/param-config/param-config.dto.ts | 16 +- .../param-config/param-config.entity.ts | 14 +- .../param-config/param-config.module.ts | 12 +- .../param-config/param-config.service.ts | 48 +- src/modules/system/role/role.controller.ts | 47 +- src/modules/system/role/role.dto.ts | 28 +- src/modules/system/role/role.entity.ts | 24 +- src/modules/system/role/role.model.ts | 6 +- src/modules/system/role/role.module.ts | 21 +- src/modules/system/role/role.service.ts | 95 +- src/modules/system/serve/serve.controller.ts | 18 +- src/modules/system/serve/serve.model.ts | 48 +- src/modules/system/serve/serve.module.ts | 10 +- src/modules/system/serve/serve.service.ts | 34 +- src/modules/system/system.module.ts | 28 +- src/modules/system/task/constant.ts | 4 +- src/modules/system/task/task.controller.ts | 54 +- src/modules/system/task/task.dto.ts | 48 +- src/modules/system/task/task.entity.ts | 30 +- src/modules/system/task/task.module.ts | 24 +- src/modules/system/task/task.processor.ts | 37 +- src/modules/system/task/task.service.ts | 259 +++--- src/modules/system/task/task.ts | 4 +- src/modules/tasks/jobs/email.job.ts | 21 +- src/modules/tasks/jobs/http-request.job.ts | 19 +- src/modules/tasks/jobs/log-clear.job.ts | 14 +- src/modules/tasks/mission.decorator.ts | 6 +- src/modules/tasks/tasks.module.ts | 24 +- src/modules/todo/todo.controller.ts | 46 +- src/modules/todo/todo.dto.ts | 8 +- src/modules/todo/todo.entity.ts | 14 +- src/modules/todo/todo.module.ts | 12 +- src/modules/todo/todo.service.ts | 38 +- src/modules/tools/email/email.controller.ts | 14 +- src/modules/tools/email/email.dto.ts | 10 +- src/modules/tools/email/email.module.ts | 4 +- .../tools/storage/storage.controller.ts | 24 +- src/modules/tools/storage/storage.dto.ts | 32 +- src/modules/tools/storage/storage.entity.ts | 20 +- src/modules/tools/storage/storage.modal.ts | 18 +- src/modules/tools/storage/storage.module.ts | 14 +- src/modules/tools/storage/storage.service.ts | 48 +- src/modules/tools/tools.module.ts | 29 +- src/modules/tools/upload/file.constraint.ts | 51 +- src/modules/tools/upload/upload.controller.ts | 34 +- src/modules/tools/upload/upload.dto.ts | 20 +- src/modules/tools/upload/upload.module.ts | 10 +- src/modules/tools/upload/upload.service.ts | 37 +- src/modules/user/dto/password.dto.ts | 12 +- src/modules/user/dto/user.dto.ts | 37 +- src/modules/user/user.controller.ts | 65 +- src/modules/user/user.entity.ts | 38 +- src/modules/user/user.model.ts | 14 +- src/modules/user/user.module.ts | 25 +- src/modules/user/user.service.ts | 243 +++--- src/repl.ts | 15 +- src/setup-swagger.ts | 37 +- .../constraints/entity-exist.constraint.ts | 55 +- .../database/constraints/unique.constraint.ts | 61 +- src/shared/database/database.module.ts | 35 +- src/shared/database/typeorm-logger.ts | 103 +-- src/shared/helper/cron.service.ts | 48 +- src/shared/helper/helper.module.ts | 11 +- src/shared/helper/qq.service.ts | 12 +- src/shared/logger/logger.module.ts | 6 +- src/shared/logger/logger.service.ts | 54 +- src/shared/mailer/mailer.module.ts | 18 +- src/shared/mailer/mailer.service.ts | 113 +-- src/shared/redis/cache.service.ts | 55 +- src/shared/redis/redis-subpub.ts | 63 +- src/shared/redis/redis.constant.ts | 2 +- src/shared/redis/redis.module.ts | 32 +- src/shared/redis/subpub.service.ts | 12 +- src/shared/shared.module.ts | 20 +- src/socket/base.gateway.ts | 20 +- src/socket/events/admin.gateway.ts | 25 +- src/socket/events/web.gateway.ts | 25 +- src/socket/shared/auth.gateway.ts | 116 ++- src/socket/socket.module.ts | 12 +- src/utils/captcha.util.ts | 8 +- src/utils/crypto.util.ts | 22 +- src/utils/date.util.ts | 18 +- src/utils/file.util.ts | 77 +- src/utils/index.ts | 22 +- src/utils/ip.util.ts | 81 +- src/utils/is.util.ts | 5 +- src/utils/list2tree.util.ts | 69 +- src/utils/permission.util.ts | 127 ++- src/utils/redis.util.ts | 15 +- src/utils/schedule.util.ts | 89 +- src/utils/tool.util.ts | 53 +- 228 files changed, 4787 insertions(+), 4615 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.cjs create mode 100644 .prettierignore create mode 100644 .prettierrc.cjs create mode 100644 src/modules/contract/contract.controller.ts create mode 100644 src/modules/contract/contract.entity.ts create mode 100644 src/modules/contract/contract.module.ts create mode 100644 src/modules/contract/contract.service.ts diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..cc3ee7a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +node_modules +dist/ +config +build/ +.eslintrc.js +package.json +tsconfig**.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..0b84b6b --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,22 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin', 'prettier'], + extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 0, + }, +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..c4d6feb --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +/dist/* +.local +.output.js +/node_modules/** + +**/*.svg +**/*.sh + +/public/* +test/**/* \ No newline at end of file diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 0000000..bf858ce --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,11 @@ +module.exports = { + printWidth: 100, // 每行代码长度(默认80) + tabWidth: 2, // 每个tab相当于多少个空格(默认2) + useTabs: false, // 是否使用tab进行缩进(默认false) + singleQuote: true, // 使用单引号(默认false) + semi: true, // 声明结尾使用分号(默认true) + trailingComma: 'es5', // 多行使用拖尾逗号(默认none) + bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true) + arrowParens: 'avoid', // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid) + endOfLine: 'auto', // 文件换行格式 LF/CRLF +}; diff --git a/deploy/sql/contracts.sql b/deploy/sql/contracts.sql index 307c789..b010e9f 100644 --- a/deploy/sql/contracts.sql +++ b/deploy/sql/contracts.sql @@ -1,7 +1,8 @@ CREATE TABLE contracts ( - id INT AUTO_INCREMENT PRIMARY KEY COMMENT '合同编号', - contract_title VARCHAR(255) COMMENT '合同标题', - contract_type VARCHAR(255) COMMENT '合同类型', + id INT AUTO_INCREMENT PRIMARY KEY , + contract_number VARCHAR(255) COMMENT '合同编号', + title VARCHAR(255) COMMENT '合同标题', + type VARCHAR(255) COMMENT '合同类型', party_a VARCHAR(255) COMMENT '甲方', party_b VARCHAR(255) COMMENT '乙方', signing_date DATE COMMENT '签订日期', diff --git a/package.json b/package.json index 8126b51..711305a 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "c": "git add . && git cz && git push", "release": "standard-version", "commitlint": "commitlint --config commitlint.config.cjs -e -V", - "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,md}\"" + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"" }, "dependencies": { "@fastify/cookie": "^9.3.1", @@ -108,7 +108,6 @@ "winston-daily-rotate-file": "^5.0.0" }, "devDependencies": { - "@antfu/eslint-config": "^2.6.4", "@compodoc/compodoc": "^1.1.23", "@nestjs/cli": "^10.3.2", "@nestjs/schematics": "^10.1.1", @@ -119,10 +118,14 @@ "@types/node": "^20.11.16", "@types/supertest": "^6.0.2", "@types/ua-parser-js": "^0.7.39", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", "cross-env": "^7.0.3", - "eslint": "^8.56.0", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "prettier": "~3.2.5", "jest": "^29.7.0", - "lint-staged": "^15.2.2", "source-map-support": "^0.5.21", "supertest": "^6.3.4", "ts-jest": "^29.1.2", @@ -136,11 +139,6 @@ "standard-version": "^9.5.0", "husky": "^8.0.0" }, - "pnpm": { - "overrides": { - "@liaoliaots/nestjs-redis": "npm:@songkeys/nestjs-redis" - } - }, "config": { "commitizen": { "path": "cz-customizable" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f084228..e03c7c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -238,6 +238,18 @@ devDependencies: eslint: specifier: ^8.56.0 version: 8.56.0 + eslint-config-prettier: + specifier: ~9.1.0 + version: 9.1.0(eslint@8.56.0) + eslint-define-config: + specifier: ~2.1.0 + version: 2.1.0 + eslint-plugin-import: + specifier: ~2.29.1 + version: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint@8.56.0) + eslint-plugin-prettier: + specifier: ~5.1.3 + version: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.2.5) husky: specifier: ^8.0.0 version: 8.0.3 @@ -247,6 +259,9 @@ devDependencies: lint-staged: specifier: ^15.2.2 version: 15.2.2 + prettier: + specifier: ~3.2.5 + version: 3.2.5 source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -2921,6 +2936,11 @@ packages: requiresBuild: true optional: true + /@pkgr/core@0.1.1: + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dev: true + /@selderee/plugin-htmlparser2@0.11.0: resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} dependencies: @@ -3228,6 +3248,10 @@ packages: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + /@types/jsonwebtoken@9.0.5: resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} dependencies: @@ -3920,6 +3944,14 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + /array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + dev: true + /array-from@2.1.1: resolution: {integrity: sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==} dev: true @@ -3928,6 +3960,17 @@ packages: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} dev: true + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + dev: true + /array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} dev: true @@ -3937,6 +3980,62 @@ packages: engines: {node: '>=8'} dev: true + /array.prototype.filter@1.0.3: + resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + es-array-method-boxes-properly: 1.0.0 + is-string: 1.0.7 + dev: true + + /array.prototype.findlastindex@1.2.4: + resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + es-shim-unscopables: 1.0.2 + dev: true + + /arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + dev: true + /arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -3980,6 +4079,13 @@ packages: engines: {node: '>=8.0.0'} dev: false + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 + dev: true + /avvio@8.3.0: resolution: {integrity: sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==} dependencies: @@ -5452,6 +5558,13 @@ packages: run-applescript: 3.2.0 dev: false + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -5689,6 +5802,57 @@ packages: stackframe: 1.3.4 dev: false + /es-abstract@1.22.4: + resolution: {integrity: sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.1 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.0 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.14 + dev: true + + /es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + dev: true + /es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} @@ -5703,6 +5867,30 @@ packages: resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} dev: true + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.1 + dev: true + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.1 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + /es5-ext@0.10.62: resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} engines: {node: '>=0.10'} @@ -5855,6 +6043,20 @@ packages: parse-gitignore: 2.0.0 dev: true + /eslint-config-prettier@9.1.0(eslint@8.56.0): + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-define-config@2.1.0: + resolution: {integrity: sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>=8.6.0'} + dev: true + /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: @@ -5955,6 +6157,41 @@ packages: - supports-color dev: true + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0)(eslint@8.56.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.4 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.56.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0) + hasown: 2.0.1 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.2 + object.values: 1.1.7 + semver: 6.3.1 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + /eslint-plugin-jsdoc@48.1.0(eslint@8.56.0): resolution: {integrity: sha512-g9S8ukmTd1DVcV/xeBYPPXOZ6rc8WJ4yi0+MVxJ1jBOrz5kmxV9gJJQ64ltCqIWFnBChLIhLVx3tbTSarqVyFA==} engines: {node: '>=18'} @@ -6056,6 +6293,27 @@ packages: - typescript dev: true + /eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.2.5): + resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.56.0 + eslint-config-prettier: 9.1.0(eslint@8.56.0) + prettier: 3.2.5 + prettier-linter-helpers: 1.0.0 + synckit: 0.8.8 + dev: true + /eslint-plugin-toml@0.9.2(eslint@8.56.0): resolution: {integrity: sha512-ri0xf63PYf3pIq/WY9BIwrqxZmGTIwSkAO0bHddI0ajUwN4KGz6W8vOvdXFHOpRdRfzxlmXze/vfsY/aTEXESg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6496,6 +6754,10 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -6773,6 +7035,12 @@ packages: optional: true dev: false + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -6887,6 +7155,16 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + functions-have-names: 1.2.3 + dev: true + /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true @@ -6968,6 +7246,15 @@ packages: engines: {node: '>=16'} dev: true + /get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + dev: true + /get-tsconfig@4.7.2: resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} dependencies: @@ -7109,6 +7396,13 @@ packages: type-fest: 0.20.2 dev: true + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: true + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -7156,6 +7450,10 @@ packages: engines: {node: '>=6'} dev: true + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -7179,6 +7477,11 @@ packages: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} engines: {node: '>= 0.4'} + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + dev: true + /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} @@ -7504,6 +7807,15 @@ packages: wrap-ansi: 6.2.0 dev: true + /internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + hasown: 2.0.1 + side-channel: 1.0.5 + dev: true + /interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} @@ -7554,6 +7866,14 @@ packages: has-tostringtag: 1.0.2 dev: true + /is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + dev: true + /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true @@ -7562,12 +7882,26 @@ packages: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} dev: false + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + /is-builtin-module@3.2.1: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} @@ -7575,6 +7909,11 @@ packages: builtin-modules: 3.3.0 dev: true + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: @@ -7654,6 +7993,18 @@ packages: engines: {node: '>=8'} dev: true + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -7689,6 +8040,13 @@ packages: call-bind: 1.0.7 has-tostringtag: 1.0.2 + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + dev: true + /is-stream@1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} @@ -7703,6 +8061,20 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + /is-text-path@1.0.1: resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} engines: {node: '>=0.10.0'} @@ -7710,6 +8082,13 @@ packages: text-extensions: 1.9.0 dev: true + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.14 + dev: true + /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -7724,6 +8103,12 @@ packages: resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} dev: true + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.7 + dev: true + /is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -7739,6 +8124,10 @@ packages: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} dev: true + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -8342,6 +8731,13 @@ packages: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: true + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -9728,6 +10124,44 @@ packages: engines: {node: '>= 0.4'} dev: true + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + dev: true + + /object.groupby@1.0.2: + resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==} + dependencies: + array.prototype.filter: 1.0.3 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + es-errors: 1.3.0 + dev: true + + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + dev: true + /obliterator@2.0.4: resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} dev: false @@ -10322,6 +10756,11 @@ packages: resolution: {integrity: sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==} dev: true + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + dev: true + /postcss-selector-parser@6.0.15: resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} engines: {node: '>=4'} @@ -10349,6 +10788,19 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + + /prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + engines: {node: '>=14'} + hasBin: true + dev: true + /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -10985,6 +11437,16 @@ packages: dependencies: tslib: 2.6.2 + /safe-array-concat@1.1.0: + resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} dev: true @@ -10992,6 +11454,15 @@ packages: /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + /safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + dev: true + /safe-regex2@2.0.0: resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} dependencies: @@ -11527,6 +11998,31 @@ packages: strip-ansi: 7.1.0 dev: true + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + dev: true + + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + dev: true + + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.4 + dev: true + /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -11690,6 +12186,14 @@ packages: tslib: 2.6.2 dev: true + /synckit@0.8.8: + resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.6.2 + dev: true + /systeminformation@5.22.0: resolution: {integrity: sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==} engines: {node: '>=8.0.0'} @@ -11988,6 +12492,15 @@ packages: tsconfig-paths: 4.2.0 dev: true + /tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + /tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -12070,6 +12583,50 @@ packages: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} dev: true + /typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-length@1.0.5: + resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + dev: true + /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true @@ -12191,6 +12748,15 @@ packages: dependencies: '@lukeed/csprng': 1.1.0 + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -12517,6 +13083,27 @@ packages: tr46: 0.0.3 webidl-conversions: 3.0.1 + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-typed-array@1.1.14: + resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + dev: true + /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true diff --git a/src/app.module.ts b/src/app.module.ts index 34b38f7..7b5542d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,29 +1,30 @@ -import { ClassSerializerInterceptor, Module } from '@nestjs/common' +import { ClassSerializerInterceptor, Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config' -import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core' +import { ConfigModule } from '@nestjs/config'; +import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core'; -import config from '~/config' -import { SharedModule } from '~/shared/shared.module' +import config from '~/config'; +import { SharedModule } from '~/shared/shared.module'; -import { AllExceptionsFilter } from './common/filters/any-exception.filter' +import { AllExceptionsFilter } from './common/filters/any-exception.filter'; -import { IdempotenceInterceptor } from './common/interceptors/idempotence.interceptor' -import { TimeoutInterceptor } from './common/interceptors/timeout.interceptor' -import { TransformInterceptor } from './common/interceptors/transform.interceptor' -import { AuthModule } from './modules/auth/auth.module' -import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard' -import { RbacGuard } from './modules/auth/guards/rbac.guard' -import { HealthModule } from './modules/health/health.module' -import { NetdiskModule } from './modules/netdisk/netdisk.module' -import { SseModule } from './modules/sse/sse.module' -import { SystemModule } from './modules/system/system.module' -import { TasksModule } from './modules/tasks/tasks.module' -import { TodoModule } from './modules/todo/todo.module' -import { ToolsModule } from './modules/tools/tools.module' -import { DatabaseModule } from './shared/database/database.module' +import { IdempotenceInterceptor } from './common/interceptors/idempotence.interceptor'; +import { TimeoutInterceptor } from './common/interceptors/timeout.interceptor'; +import { TransformInterceptor } from './common/interceptors/transform.interceptor'; +import { AuthModule } from './modules/auth/auth.module'; +import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard'; +import { RbacGuard } from './modules/auth/guards/rbac.guard'; +import { HealthModule } from './modules/health/health.module'; +import { NetdiskModule } from './modules/netdisk/netdisk.module'; +import { SseModule } from './modules/sse/sse.module'; +import { SystemModule } from './modules/system/system.module'; +import { TasksModule } from './modules/tasks/tasks.module'; +import { TodoModule } from './modules/todo/todo.module'; +import { ToolsModule } from './modules/tools/tools.module'; +import { DatabaseModule } from './shared/database/database.module'; -import { SocketModule } from './socket/socket.module' +import { SocketModule } from './socket/socket.module'; +import { ContractModule } from './modules/contract/contract.module'; @Module({ imports: [ @@ -51,6 +52,8 @@ import { SocketModule } from './socket/socket.module' // end biz TodoModule, + + ContractModule, ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, diff --git a/src/common/adapters/fastify.adapter.ts b/src/common/adapters/fastify.adapter.ts index e25d0ca..67ecb8c 100644 --- a/src/common/adapters/fastify.adapter.ts +++ b/src/common/adapters/fastify.adapter.ts @@ -1,13 +1,13 @@ -import FastifyCookie from '@fastify/cookie' -import FastifyMultipart from '@fastify/multipart' -import { FastifyAdapter } from '@nestjs/platform-fastify' +import FastifyCookie from '@fastify/cookie'; +import FastifyMultipart from '@fastify/multipart'; +import { FastifyAdapter } from '@nestjs/platform-fastify'; const app: FastifyAdapter = new FastifyAdapter({ trustProxy: true, logger: false, // forceCloseConnections: true, -}) -export { app as fastifyApp } +}); +export { app as fastifyApp }; app.register(FastifyMultipart, { limits: { @@ -15,32 +15,30 @@ app.register(FastifyMultipart, { fileSize: 1024 * 1024 * 6, // limit size 6M files: 5, // Max number of file fields }, -}) +}); app.register(FastifyCookie, { secret: 'cookie-secret', // 这个 secret 不太重要,不存鉴权相关,无关紧要 -}) +}); app.getInstance().addHook('onRequest', (request, reply, done) => { // set undefined origin - const { origin } = request.headers - if (!origin) - request.headers.origin = request.headers.host + const { origin } = request.headers; + if (!origin) request.headers.origin = request.headers.host; // forbidden php - const { url } = request + const { url } = request; if (url.endsWith('.php')) { - reply.raw.statusMessage - = 'Eh. PHP is not support on this machine. Yep, I also think PHP is bestest programming language. But for me it is beyond my reach.' + reply.raw.statusMessage = + 'Eh. PHP is not support on this machine. Yep, I also think PHP is bestest programming language. But for me it is beyond my reach.'; - return reply.code(418).send() + return reply.code(418).send(); } // skip favicon request - if (url.match(/favicon.ico$/) || url.match(/manifest.json$/)) - return reply.code(204).send() + if (url.match(/favicon.ico$/) || url.match(/manifest.json$/)) return reply.code(204).send(); - done() -}) + done(); +}); diff --git a/src/common/adapters/socket.adapter.ts b/src/common/adapters/socket.adapter.ts index d766592..ce50f40 100644 --- a/src/common/adapters/socket.adapter.ts +++ b/src/common/adapters/socket.adapter.ts @@ -1,26 +1,26 @@ -import { INestApplication } from '@nestjs/common' -import { IoAdapter } from '@nestjs/platform-socket.io' -import { createAdapter } from '@socket.io/redis-adapter' +import { INestApplication } from '@nestjs/common'; +import { IoAdapter } from '@nestjs/platform-socket.io'; +import { createAdapter } from '@socket.io/redis-adapter'; -import { REDIS_PUBSUB } from '~/shared/redis/redis.constant' +import { REDIS_PUBSUB } from '~/shared/redis/redis.constant'; -export const RedisIoAdapterKey = 'm-shop-socket' +export const RedisIoAdapterKey = 'm-shop-socket'; export class RedisIoAdapter extends IoAdapter { constructor(private readonly app: INestApplication) { - super(app) + super(app); } createIOServer(port: number, options?: any) { - const server = super.createIOServer(port, options) + const server = super.createIOServer(port, options); - const { pubClient, subClient } = this.app.get(REDIS_PUBSUB) + const { pubClient, subClient } = this.app.get(REDIS_PUBSUB); const redisAdapter = createAdapter(pubClient, subClient, { key: RedisIoAdapterKey, requestsTimeout: 10000, - }) - server.adapter(redisAdapter) - return server + }); + server.adapter(redisAdapter); + return server; } } diff --git a/src/common/decorators/api-result.decorator.ts b/src/common/decorators/api-result.decorator.ts index 1892762..65887fa 100644 --- a/src/common/decorators/api-result.decorator.ts +++ b/src/common/decorators/api-result.decorator.ts @@ -1,15 +1,13 @@ -import { HttpStatus, Type, applyDecorators } from '@nestjs/common' -import { ApiExtraModels, ApiResponse, getSchemaPath } from '@nestjs/swagger' +import { HttpStatus, Type, applyDecorators } from '@nestjs/common'; +import { ApiExtraModels, ApiResponse, getSchemaPath } from '@nestjs/swagger'; -import { ResOp } from '~/common/model/response.model' +import { ResOp } from '~/common/model/response.model'; -const baseTypeNames = ['String', 'Number', 'Boolean'] +const baseTypeNames = ['String', 'Number', 'Boolean']; function genBaseProp(type: Type) { - if (baseTypeNames.includes(type.name)) - return { type: type.name.toLocaleLowerCase() } - else - return { $ref: getSchemaPath(type) } + if (baseTypeNames.includes(type.name)) return { type: type.name.toLocaleLowerCase() }; + else return { $ref: getSchemaPath(type) }; } /** @@ -20,11 +18,11 @@ export function ApiResult>({ isPage, status, }: { - type?: TModel | TModel[] - isPage?: boolean - status?: HttpStatus + type?: TModel | TModel[]; + isPage?: boolean; + status?: HttpStatus; }) { - let prop = null + let prop = null; if (Array.isArray(type)) { if (isPage) { @@ -46,23 +44,20 @@ export function ApiResult>({ }, }, }, - } - } - else { + }; + } else { prop = { type: 'array', items: genBaseProp(type[0]), - } + }; } - } - else if (type) { - prop = genBaseProp(type) - } - else { - prop = { type: 'null', default: null } + } else if (type) { + prop = genBaseProp(type); + } else { + prop = { type: 'null', default: null }; } - const model = Array.isArray(type) ? type[0] : type + const model = Array.isArray(type) ? type[0] : type; return applyDecorators( ApiExtraModels(model), @@ -78,6 +73,6 @@ export function ApiResult>({ }, ], }, - }), - ) + }) + ); } diff --git a/src/common/decorators/bypass.decorator.ts b/src/common/decorators/bypass.decorator.ts index 8c7562d..eb66876 100644 --- a/src/common/decorators/bypass.decorator.ts +++ b/src/common/decorators/bypass.decorator.ts @@ -1,10 +1,10 @@ -import { SetMetadata } from '@nestjs/common' +import { SetMetadata } from '@nestjs/common'; -export const BYPASS_KEY = '__bypass_key__' +export const BYPASS_KEY = '__bypass_key__'; /** * 当不需要转换成基础返回格式时添加该装饰器 */ export function Bypass() { - return SetMetadata(BYPASS_KEY, true) + return SetMetadata(BYPASS_KEY, true); } diff --git a/src/common/decorators/cookie.decorator.ts b/src/common/decorators/cookie.decorator.ts index 102a527..9b91ba5 100644 --- a/src/common/decorators/cookie.decorator.ts +++ b/src/common/decorators/cookie.decorator.ts @@ -1,8 +1,8 @@ -import type { ExecutionContext } from '@nestjs/common' -import { createParamDecorator } from '@nestjs/common' -import type { FastifyRequest } from 'fastify' +import type { ExecutionContext } from '@nestjs/common'; +import { createParamDecorator } from '@nestjs/common'; +import type { FastifyRequest } from 'fastify'; export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { - const request = ctx.switchToHttp().getRequest() - return data ? request.cookies?.[data] : request.cookies -}) + const request = ctx.switchToHttp().getRequest(); + return data ? request.cookies?.[data] : request.cookies; +}); diff --git a/src/common/decorators/cron-once.decorator.ts b/src/common/decorators/cron-once.decorator.ts index 39eab20..36267e9 100644 --- a/src/common/decorators/cron-once.decorator.ts +++ b/src/common/decorators/cron-once.decorator.ts @@ -1,19 +1,19 @@ -import cluster from 'node:cluster' +import cluster from 'node:cluster'; -import { Cron } from '@nestjs/schedule' +import { Cron } from '@nestjs/schedule'; -import { isMainProcess } from '~/global/env' +import { isMainProcess } from '~/global/env'; export const CronOnce: typeof Cron = (...rest): MethodDecorator => { // If not in cluster mode, and PM2 main worker if (isMainProcess) // eslint-disable-next-line no-useless-call - return Cron.call(null, ...rest) + return Cron.call(null, ...rest); if (cluster.isWorker && cluster.worker?.id === 1) // eslint-disable-next-line no-useless-call - return Cron.call(null, ...rest) + return Cron.call(null, ...rest); - const returnNothing: MethodDecorator = () => {} - return returnNothing -} + const returnNothing: MethodDecorator = () => {}; + return returnNothing; +}; diff --git a/src/common/decorators/field.decorator.ts b/src/common/decorators/field.decorator.ts index 68fa483..f3863f2 100644 --- a/src/common/decorators/field.decorator.ts +++ b/src/common/decorators/field.decorator.ts @@ -1,4 +1,4 @@ -import { applyDecorators } from '@nestjs/common' +import { applyDecorators } from '@nestjs/common'; import { IsBoolean, IsDate, @@ -12,8 +12,8 @@ import { MaxLength, Min, MinLength, -} from 'class-validator' -import { isNumber } from 'lodash' +} from 'class-validator'; +import { isNumber } from 'lodash'; import { ToArray, @@ -23,115 +23,86 @@ import { ToNumber, ToTrim, ToUpperCase, -} from './transform.decorator' +} from './transform.decorator'; interface IOptionalOptions { - required?: boolean + required?: boolean; } interface INumberFieldOptions extends IOptionalOptions { - each?: boolean - int?: boolean - min?: number - max?: number - positive?: boolean + each?: boolean; + int?: boolean; + min?: number; + max?: number; + positive?: boolean; } interface IStringFieldOptions extends IOptionalOptions { - each?: boolean - minLength?: number - maxLength?: number - lowerCase?: boolean - upperCase?: boolean + each?: boolean; + minLength?: number; + maxLength?: number; + lowerCase?: boolean; + upperCase?: boolean; } -export function NumberField( - options: INumberFieldOptions = {}, -): PropertyDecorator { - const { each, min, max, int, positive, required = true } = options +export function NumberField(options: INumberFieldOptions = {}): PropertyDecorator { + const { each, min, max, int, positive, required = true } = options; - const decorators = [ToNumber()] + const decorators = [ToNumber()]; - if (each) - decorators.push(ToArray()) + if (each) decorators.push(ToArray()); - if (int) - decorators.push(IsInt({ each })) - else - decorators.push(IsNumber({}, { each })) + if (int) decorators.push(IsInt({ each })); + else decorators.push(IsNumber({}, { each })); - if (isNumber(min)) - decorators.push(Min(min, { each })) + if (isNumber(min)) decorators.push(Min(min, { each })); - if (isNumber(max)) - decorators.push(Max(max, { each })) + if (isNumber(max)) decorators.push(Max(max, { each })); - if (positive) - decorators.push(IsPositive({ each })) + if (positive) decorators.push(IsPositive({ each })); - if (!required) - decorators.push(IsOptional()) + if (!required) decorators.push(IsOptional()); - return applyDecorators(...decorators) + return applyDecorators(...decorators); } -export function StringField( - options: IStringFieldOptions = {}, -): PropertyDecorator { - const { - each, - minLength, - maxLength, - lowerCase, - upperCase, - required = true, - } = options +export function StringField(options: IStringFieldOptions = {}): PropertyDecorator { + const { each, minLength, maxLength, lowerCase, upperCase, required = true } = options; - const decorators = [IsString({ each }), ToTrim()] + const decorators = [IsString({ each }), ToTrim()]; - if (each) - decorators.push(ToArray()) + if (each) decorators.push(ToArray()); - if (isNumber(minLength)) - decorators.push(MinLength(minLength, { each })) + if (isNumber(minLength)) decorators.push(MinLength(minLength, { each })); - if (isNumber(maxLength)) - decorators.push(MaxLength(maxLength, { each })) + if (isNumber(maxLength)) decorators.push(MaxLength(maxLength, { each })); - if (lowerCase) - decorators.push(ToLowerCase()) + if (lowerCase) decorators.push(ToLowerCase()); - if (upperCase) - decorators.push(ToUpperCase()) + if (upperCase) decorators.push(ToUpperCase()); - if (!required) - decorators.push(IsOptional()) - else - decorators.push(IsNotEmpty({ each })) + if (!required) decorators.push(IsOptional()); + else decorators.push(IsNotEmpty({ each })); - return applyDecorators(...decorators) + return applyDecorators(...decorators); } -export function BooleanField( - options: IOptionalOptions = {}, -): PropertyDecorator { - const decorators = [ToBoolean(), IsBoolean()] +export function BooleanField(options: IOptionalOptions = {}): PropertyDecorator { + const decorators = [ToBoolean(), IsBoolean()]; - const { required = true } = options + const { required = true } = options; - if (!required) - decorators.push(IsOptional()) + if (!required) decorators.push(IsOptional()); - return applyDecorators(...decorators) + return applyDecorators(...decorators); } export function DateField(options: IOptionalOptions = {}): PropertyDecorator { - const decorators = [ToDate(), IsDate()] + const decorators = [ToDate(), IsDate()]; - const { required = true } = options + const { required = true } = options; - if (!required) - decorators.push(IsOptional()) + if (!required) decorators.push(IsOptional()); - return applyDecorators(...decorators) + return applyDecorators(...decorators); } diff --git a/src/common/decorators/http.decorator.ts b/src/common/decorators/http.decorator.ts index 85ed449..22c13f7 100644 --- a/src/common/decorators/http.decorator.ts +++ b/src/common/decorators/http.decorator.ts @@ -1,22 +1,22 @@ -import type { ExecutionContext } from '@nestjs/common' +import type { ExecutionContext } from '@nestjs/common'; -import { createParamDecorator } from '@nestjs/common' -import type { FastifyRequest } from 'fastify' +import { createParamDecorator } from '@nestjs/common'; +import type { FastifyRequest } from 'fastify'; -import { getIp } from '~/utils/ip.util' +import { getIp } from '~/utils/ip.util'; /** * 快速获取IP */ export const Ip = createParamDecorator((_, context: ExecutionContext) => { - const request = context.switchToHttp().getRequest() - return getIp(request) -}) + const request = context.switchToHttp().getRequest(); + return getIp(request); +}); /** * 快速获取request path,并不包括url params */ export const Uri = createParamDecorator((_, context: ExecutionContext) => { - const request = context.switchToHttp().getRequest() - return request.routerPath -}) + const request = context.switchToHttp().getRequest(); + return request.routerPath; +}); diff --git a/src/common/decorators/id-param.decorator.ts b/src/common/decorators/id-param.decorator.ts index fade33c..4967ce2 100644 --- a/src/common/decorators/id-param.decorator.ts +++ b/src/common/decorators/id-param.decorator.ts @@ -1,7 +1,13 @@ -import { HttpStatus, NotAcceptableException, Param, ParseIntPipe } from '@nestjs/common' +import { HttpStatus, NotAcceptableException, Param, ParseIntPipe } from '@nestjs/common'; export function IdParam() { - return Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE, exceptionFactory: (_error) => { - throw new NotAcceptableException('id 格式不正确') - } })) + return Param( + 'id', + new ParseIntPipe({ + errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE, + exceptionFactory: _error => { + throw new NotAcceptableException('id 格式不正确'); + }, + }) + ); } diff --git a/src/common/decorators/idempotence.decorator.ts b/src/common/decorators/idempotence.decorator.ts index d1142b3..949b321 100644 --- a/src/common/decorators/idempotence.decorator.ts +++ b/src/common/decorators/idempotence.decorator.ts @@ -1,15 +1,15 @@ -import { SetMetadata } from '@nestjs/common' +import { SetMetadata } from '@nestjs/common'; -import { IdempotenceOption } from '../interceptors/idempotence.interceptor' +import { IdempotenceOption } from '../interceptors/idempotence.interceptor'; -export const HTTP_IDEMPOTENCE_KEY = '__idempotence_key__' -export const HTTP_IDEMPOTENCE_OPTIONS = '__idempotence_options__' +export const HTTP_IDEMPOTENCE_KEY = '__idempotence_key__'; +export const HTTP_IDEMPOTENCE_OPTIONS = '__idempotence_options__'; /** * 幂等 */ export function Idempotence(options?: IdempotenceOption): MethodDecorator { return function (target, key, descriptor: PropertyDescriptor) { - SetMetadata(HTTP_IDEMPOTENCE_OPTIONS, options || {})(descriptor.value) - } + SetMetadata(HTTP_IDEMPOTENCE_OPTIONS, options || {})(descriptor.value); + }; } diff --git a/src/common/decorators/swagger.decorator.ts b/src/common/decorators/swagger.decorator.ts index 4809e5c..92744d8 100644 --- a/src/common/decorators/swagger.decorator.ts +++ b/src/common/decorators/swagger.decorator.ts @@ -1,11 +1,11 @@ -import { applyDecorators } from '@nestjs/common' -import { ApiSecurity } from '@nestjs/swagger' +import { applyDecorators } from '@nestjs/common'; +import { ApiSecurity } from '@nestjs/swagger'; -export const API_SECURITY_AUTH = 'auth' +export const API_SECURITY_AUTH = 'auth'; /** * like to @ApiSecurity('auth') */ export function ApiSecurityAuth(): ClassDecorator & MethodDecorator { - return applyDecorators(ApiSecurity(API_SECURITY_AUTH)) + return applyDecorators(ApiSecurity(API_SECURITY_AUTH)); } diff --git a/src/common/decorators/transform.decorator.ts b/src/common/decorators/transform.decorator.ts index e86cf75..f69dc98 100644 --- a/src/common/decorators/transform.decorator.ts +++ b/src/common/decorators/transform.decorator.ts @@ -1,21 +1,20 @@ -import { Transform } from 'class-transformer' -import { castArray, isArray, isNil, trim } from 'lodash' +import { Transform } from 'class-transformer'; +import { castArray, isArray, isNil, trim } from 'lodash'; /** * convert string to number */ export function ToNumber(): PropertyDecorator { return Transform( - (params) => { - const value = params.value as string[] | string + params => { + const value = params.value as string[] | string; - if (isArray(value)) - return value.map(v => Number(v)) + if (isArray(value)) return value.map(v => Number(v)); - return Number(value) + return Number(value); }, - { toClassOnly: true }, - ) + { toClassOnly: true } + ); } /** @@ -23,16 +22,15 @@ export function ToNumber(): PropertyDecorator { */ export function ToInt(): PropertyDecorator { return Transform( - (params) => { - const value = params.value as string[] | string + params => { + const value = params.value as string[] | string; - if (isArray(value)) - return value.map(v => Number.parseInt(v)) + if (isArray(value)) return value.map(v => Number.parseInt(v)); - return Number.parseInt(value) + return Number.parseInt(value); }, - { toClassOnly: true }, - ) + { toClassOnly: true } + ); } /** @@ -40,18 +38,18 @@ export function ToInt(): PropertyDecorator { */ export function ToBoolean(): PropertyDecorator { return Transform( - (params) => { + params => { switch (params.value) { case 'true': - return true + return true; case 'false': - return false + return false; default: - return params.value + return params.value; } }, - { toClassOnly: true }, - ) + { toClassOnly: true } + ); } /** @@ -59,16 +57,15 @@ export function ToBoolean(): PropertyDecorator { */ export function ToDate(): PropertyDecorator { return Transform( - (params) => { - const { value } = params + params => { + const { value } = params; - if (!value) - return + if (!value) return; - return new Date(value) + return new Date(value); }, - { toClassOnly: true }, - ) + { toClassOnly: true } + ); } /** @@ -76,16 +73,15 @@ export function ToDate(): PropertyDecorator { */ export function ToArray(): PropertyDecorator { return Transform( - (params) => { - const { value } = params + params => { + const { value } = params; - if (isNil(value)) - return [] + if (isNil(value)) return []; - return castArray(value) + return castArray(value); }, - { toClassOnly: true }, - ) + { toClassOnly: true } + ); } /** @@ -93,16 +89,15 @@ export function ToArray(): PropertyDecorator { */ export function ToTrim(): PropertyDecorator { return Transform( - (params) => { - const value = params.value as string[] | string + params => { + const value = params.value as string[] | string; - if (isArray(value)) - return value.map(v => trim(v)) + if (isArray(value)) return value.map(v => trim(v)); - return trim(value) + return trim(value); }, - { toClassOnly: true }, - ) + { toClassOnly: true } + ); } /** @@ -110,19 +105,17 @@ export function ToTrim(): PropertyDecorator { */ export function ToLowerCase(): PropertyDecorator { return Transform( - (params) => { - const value = params.value as string[] | string + params => { + const value = params.value as string[] | string; - if (!value) - return + if (!value) return; - if (isArray(value)) - return value.map(v => v.toLowerCase()) + if (isArray(value)) return value.map(v => v.toLowerCase()); - return value.toLowerCase() + return value.toLowerCase(); }, - { toClassOnly: true }, - ) + { toClassOnly: true } + ); } /** @@ -130,17 +123,15 @@ export function ToLowerCase(): PropertyDecorator { */ export function ToUpperCase(): PropertyDecorator { return Transform( - (params) => { - const value = params.value as string[] | string + params => { + const value = params.value as string[] | string; - if (!value) - return + if (!value) return; - if (isArray(value)) - return value.map(v => v.toUpperCase()) + if (isArray(value)) return value.map(v => v.toUpperCase()); - return value.toUpperCase() + return value.toUpperCase(); }, - { toClassOnly: true }, - ) + { toClassOnly: true } + ); } diff --git a/src/common/dto/cursor.dto.ts b/src/common/dto/cursor.dto.ts index 50c816c..59e7912 100644 --- a/src/common/dto/cursor.dto.ts +++ b/src/common/dto/cursor.dto.ts @@ -1,6 +1,6 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Expose, Transform } from 'class-transformer' -import { IsInt, IsOptional, Max, Min } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { Expose, Transform } from 'class-transformer'; +import { IsInt, IsOptional, Max, Min } from 'class-validator'; export class CursorDto { @ApiProperty({ minimum: 0, default: 0 }) @@ -11,7 +11,7 @@ export class CursorDto { @Transform(({ value: val }) => (val ? Number.parseInt(val) : 0), { toClassOnly: true, }) - cursor?: number + cursor?: number; @ApiProperty({ minimum: 1, maximum: 100, default: 10 }) @Min(1) @@ -22,5 +22,5 @@ export class CursorDto { @Transform(({ value: val }) => (val ? Number.parseInt(val) : 10), { toClassOnly: true, }) - limit?: number + limit?: number; } diff --git a/src/common/dto/delete.dto.ts b/src/common/dto/delete.dto.ts index 5157aae..6f87b9e 100644 --- a/src/common/dto/delete.dto.ts +++ b/src/common/dto/delete.dto.ts @@ -1,8 +1,8 @@ -import { IsDefined, IsNotEmpty, IsNumber } from 'class-validator' +import { IsDefined, IsNotEmpty, IsNumber } from 'class-validator'; export class BatchDeleteDto { @IsDefined() @IsNotEmpty() @IsNumber({}, { each: true }) - ids: number[] + ids: number[]; } diff --git a/src/common/dto/id.dto.ts b/src/common/dto/id.dto.ts index 65898b1..271bece 100644 --- a/src/common/dto/id.dto.ts +++ b/src/common/dto/id.dto.ts @@ -1,6 +1,6 @@ -import { IsNumber } from 'class-validator' +import { IsNumber } from 'class-validator'; export class IdDto { @IsNumber() - id: number + id: number; } diff --git a/src/common/dto/pager.dto.ts b/src/common/dto/pager.dto.ts index 95abdaf..2467702 100644 --- a/src/common/dto/pager.dto.ts +++ b/src/common/dto/pager.dto.ts @@ -1,6 +1,6 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Expose, Transform } from 'class-transformer' -import { Allow, IsEnum, IsInt, IsOptional, IsString, Max, Min } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { Expose, Transform } from 'class-transformer'; +import { Allow, IsEnum, IsInt, IsOptional, IsString, Max, Min } from 'class-validator'; export enum Order { ASC = 'ASC', @@ -16,7 +16,7 @@ export class PagerDto { @Transform(({ value: val }) => (val ? Number.parseInt(val) : 1), { toClassOnly: true, }) - page?: number + page?: number; @ApiProperty({ minimum: 1, maximum: 100, default: 10 }) @Min(1) @@ -27,19 +27,19 @@ export class PagerDto { @Transform(({ value: val }) => (val ? Number.parseInt(val) : 10), { toClassOnly: true, }) - pageSize?: number + pageSize?: number; @ApiProperty() @IsString() @IsOptional() - field?: string // | keyof T + field?: string; // | keyof T @ApiProperty({ enum: Order }) @IsEnum(Order) @IsOptional() @Transform(({ value }) => (value === 'asc' ? Order.ASC : Order.DESC)) - order?: Order + order?: Order; @Allow() - _t?: number + _t?: number; } diff --git a/src/common/entity/common.entity.ts b/src/common/entity/common.entity.ts index b6a9e32..b726edf 100644 --- a/src/common/entity/common.entity.ts +++ b/src/common/entity/common.entity.ts @@ -1,5 +1,5 @@ -import { ApiHideProperty, ApiProperty } from '@nestjs/swagger' -import { Exclude } from 'class-transformer' +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Exclude } from 'class-transformer'; import { BaseEntity, Column, @@ -7,7 +7,7 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, VirtualColumn, -} from 'typeorm' +} from 'typeorm'; // 如果觉得前端转换时间太麻烦,并且不考虑通用性的话,可以在服务端进行转换,eg: @UpdateDateColumn({ name: 'updated_at', transformer }) // const transformer: ValueTransformer = { @@ -21,25 +21,25 @@ import { export abstract class CommonEntity extends BaseEntity { @PrimaryGeneratedColumn() - id: number + id: number; @CreateDateColumn({ name: 'created_at' }) - createdAt: Date + createdAt: Date; @UpdateDateColumn({ name: 'updated_at' }) - updatedAt: Date + updatedAt: Date; } export abstract class CompleteEntity extends CommonEntity { @ApiHideProperty() @Exclude() @Column({ name: 'create_by', update: false, comment: '创建者' }) - createBy: number + createBy: number; @ApiHideProperty() @Exclude() @Column({ name: 'update_by', comment: '更新者' }) - updateBy: number + updateBy: number; /** * 不会保存到数据库中的虚拟列,数据量大时可能会有性能问题,有性能要求请考虑在 service 层手动实现 @@ -47,9 +47,9 @@ export abstract class CompleteEntity extends CommonEntity { */ @ApiProperty({ description: '创建者' }) @VirtualColumn({ query: alias => `SELECT username FROM sys_user WHERE id = ${alias}.create_by` }) - creator: string + creator: string; @ApiProperty({ description: '更新者' }) @VirtualColumn({ query: alias => `SELECT username FROM sys_user WHERE id = ${alias}.update_by` }) - updater: string + updater: string; } diff --git a/src/common/exceptions/biz.exception.ts b/src/common/exceptions/biz.exception.ts index f33ee3a..9b51262 100644 --- a/src/common/exceptions/biz.exception.ts +++ b/src/common/exceptions/biz.exception.ts @@ -1,10 +1,10 @@ -import { HttpException, HttpStatus } from '@nestjs/common' +import { HttpException, HttpStatus } from '@nestjs/common'; -import { ErrorEnum } from '~/constants/error-code.constant' -import { RESPONSE_SUCCESS_CODE } from '~/constants/response.constant' +import { ErrorEnum } from '~/constants/error-code.constant'; +import { RESPONSE_SUCCESS_CODE } from '~/constants/response.constant'; export class BusinessException extends HttpException { - private errorCode: number + private errorCode: number; constructor(error: ErrorEnum | string) { // 如果是非 ErrorEnum @@ -14,27 +14,27 @@ export class BusinessException extends HttpException { code: RESPONSE_SUCCESS_CODE, message: error, }), - HttpStatus.OK, - ) - this.errorCode = RESPONSE_SUCCESS_CODE - return + HttpStatus.OK + ); + this.errorCode = RESPONSE_SUCCESS_CODE; + return; } - const [code, message] = error.split(':') + const [code, message] = error.split(':'); super( HttpException.createBody({ code, message, }), - HttpStatus.OK, - ) + HttpStatus.OK + ); - this.errorCode = Number(code) + this.errorCode = Number(code); } getErrorCode(): number { - return this.errorCode + return this.errorCode; } } -export { BusinessException as BizException } +export { BusinessException as BizException }; diff --git a/src/common/exceptions/not-found.exception.ts b/src/common/exceptions/not-found.exception.ts index c6cb536..95b034d 100644 --- a/src/common/exceptions/not-found.exception.ts +++ b/src/common/exceptions/not-found.exception.ts @@ -1,10 +1,10 @@ -import { NotFoundException } from '@nestjs/common' -import { sample } from 'lodash' +import { NotFoundException } from '@nestjs/common'; +import { sample } from 'lodash'; -export const NotFoundMessage = ['404, Not Found'] +export const NotFoundMessage = ['404, Not Found']; export class CannotFindException extends NotFoundException { constructor() { - super(sample(NotFoundMessage)) + super(sample(NotFoundMessage)); } } diff --git a/src/common/exceptions/socket.exception.ts b/src/common/exceptions/socket.exception.ts index e790844..c25de02 100644 --- a/src/common/exceptions/socket.exception.ts +++ b/src/common/exceptions/socket.exception.ts @@ -1,38 +1,38 @@ -import { HttpException } from '@nestjs/common' -import { WsException } from '@nestjs/websockets' +import { HttpException } from '@nestjs/common'; +import { WsException } from '@nestjs/websockets'; -import { ErrorEnum } from '~/constants/error-code.constant' +import { ErrorEnum } from '~/constants/error-code.constant'; export class SocketException extends WsException { - private errorCode: number + private errorCode: number; - constructor(message: string) - constructor(error: ErrorEnum) + constructor(message: string); + constructor(error: ErrorEnum); constructor(...args: any) { - const error = args[0] + const error = args[0]; if (typeof error === 'string') { super( HttpException.createBody({ code: 0, message: error, - }), - ) - this.errorCode = 0 - return + }) + ); + this.errorCode = 0; + return; } - const [code, message] = error.split(':') + const [code, message] = error.split(':'); super( HttpException.createBody({ code, message, - }), - ) + }) + ); - this.errorCode = Number(code) + this.errorCode = Number(code); } getErrorCode(): number { - return this.errorCode + return this.errorCode; } } diff --git a/src/common/filters/any-exception.filter.ts b/src/common/filters/any-exception.filter.ts index 8bf04cd..f31ab40 100644 --- a/src/common/filters/any-exception.filter.ts +++ b/src/common/filters/any-exception.filter.ts @@ -5,85 +5,76 @@ import { HttpException, HttpStatus, Logger, -} from '@nestjs/common' -import { FastifyReply, FastifyRequest } from 'fastify' +} from '@nestjs/common'; +import { FastifyReply, FastifyRequest } from 'fastify'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; -import { isDev } from '~/global/env' +import { isDev } from '~/global/env'; interface myError { - readonly status: number - readonly statusCode?: number + readonly status: number; + readonly statusCode?: number; - readonly message?: string + readonly message?: string; } @Catch() export class AllExceptionsFilter implements ExceptionFilter { - private readonly logger = new Logger(AllExceptionsFilter.name) + private readonly logger = new Logger(AllExceptionsFilter.name); constructor() { - this.registerCatchAllExceptionsHook() + this.registerCatchAllExceptionsHook(); } catch(exception: unknown, host: ArgumentsHost) { - const ctx = host.switchToHttp() - const request = ctx.getRequest() - const response = ctx.getResponse() + const ctx = host.switchToHttp(); + const request = ctx.getRequest(); + const response = ctx.getResponse(); - const url = request.raw.url! + const url = request.raw.url!; - const status - = exception instanceof HttpException + const status = + exception instanceof HttpException ? exception.getStatus() - : (exception as myError)?.status - || (exception as myError)?.statusCode - || HttpStatus.INTERNAL_SERVER_ERROR + : (exception as myError)?.status || + (exception as myError)?.statusCode || + HttpStatus.INTERNAL_SERVER_ERROR; - let message - = (exception as any)?.response?.message - || (exception as myError)?.message - || `${exception}` + let message = + (exception as any)?.response?.message || (exception as myError)?.message || `${exception}`; // 系统内部错误时 - if ( - status === HttpStatus.INTERNAL_SERVER_ERROR - && !(exception instanceof BusinessException) - ) { - Logger.error(exception, undefined, 'Catch') + if (status === HttpStatus.INTERNAL_SERVER_ERROR && !(exception instanceof BusinessException)) { + Logger.error(exception, undefined, 'Catch'); // 生产环境下隐藏错误信息 - if (!isDev) - message = ErrorEnum.SERVER_ERROR?.split(':')[1] - } - else { - this.logger.warn( - `错误信息:(${status}) ${message} Path: ${decodeURI(url)}`, - ) + if (!isDev) message = ErrorEnum.SERVER_ERROR?.split(':')[1]; + } else { + this.logger.warn(`错误信息:(${status}) ${message} Path: ${decodeURI(url)}`); } - const apiErrorCode: number - = exception instanceof BusinessException ? exception.getErrorCode() : status + const apiErrorCode: number = + exception instanceof BusinessException ? exception.getErrorCode() : status; // 返回基础响应结果 const resBody: IBaseResponse = { code: apiErrorCode, message, data: null, - } + }; - response.status(status).send(resBody) + response.status(status).send(resBody); } registerCatchAllExceptionsHook() { - process.on('unhandledRejection', (reason) => { - console.error('unhandledRejection: ', reason) - }) + process.on('unhandledRejection', reason => { + console.error('unhandledRejection: ', reason); + }); - process.on('uncaughtException', (err) => { - console.error('uncaughtException: ', err) - }) + process.on('uncaughtException', err => { + console.error('uncaughtException: ', err); + }); } } diff --git a/src/common/interceptors/idempotence.interceptor.ts b/src/common/interceptors/idempotence.interceptor.ts index a1f11d3..6fab029 100644 --- a/src/common/interceptors/idempotence.interceptor.ts +++ b/src/common/interceptors/idempotence.interceptor.ts @@ -1,76 +1,69 @@ -import type { - CallHandler, - ExecutionContext, - NestInterceptor, -} from '@nestjs/common' +import type { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common'; + +import { ConflictException, Injectable, SetMetadata } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import type { FastifyRequest } from 'fastify'; +import { catchError, tap } from 'rxjs'; + +import { CacheService } from '~/shared/redis/cache.service'; +import { hashString } from '~/utils'; +import { getIp } from '~/utils/ip.util'; +import { getRedisKey } from '~/utils/redis.util'; import { - ConflictException, - Injectable, - SetMetadata, -} from '@nestjs/common' -import { Reflector } from '@nestjs/core' -import type { FastifyRequest } from 'fastify' -import { catchError, tap } from 'rxjs' + HTTP_IDEMPOTENCE_KEY, + HTTP_IDEMPOTENCE_OPTIONS, +} from '../decorators/idempotence.decorator'; -import { CacheService } from '~/shared/redis/cache.service' -import { hashString } from '~/utils' -import { getIp } from '~/utils/ip.util' -import { getRedisKey } from '~/utils/redis.util' - -import { HTTP_IDEMPOTENCE_KEY, HTTP_IDEMPOTENCE_OPTIONS } from '../decorators/idempotence.decorator' - -const IdempotenceHeaderKey = 'x-idempotence' +const IdempotenceHeaderKey = 'x-idempotence'; export interface IdempotenceOption { - errorMessage?: string - pendingMessage?: string + errorMessage?: string; + pendingMessage?: string; /** * 如果重复请求的话,手动处理异常 */ - handler?: (req: FastifyRequest) => any + handler?: (req: FastifyRequest) => any; /** * 记录重复请求的时间 * @default 60 */ - expired?: number + expired?: number; /** * 如果 header 没有幂等 key,根据 request 生成 key,如何生成这个 key 的方法 */ - generateKey?: (req: FastifyRequest) => string + generateKey?: (req: FastifyRequest) => string; /** * 仅读取 header 的 key,不自动生成 * @default false */ - disableGenerateKey?: boolean + disableGenerateKey?: boolean; } @Injectable() export class IdempotenceInterceptor implements NestInterceptor { constructor( private readonly reflector: Reflector, - private readonly cacheService: CacheService, + private readonly cacheService: CacheService ) {} async intercept(context: ExecutionContext, next: CallHandler) { - const request = context.switchToHttp().getRequest() + const request = context.switchToHttp().getRequest(); // skip Get 请求 - if (request.method.toUpperCase() === 'GET') - return next.handle() + if (request.method.toUpperCase() === 'GET') return next.handle(); - const handler = context.getHandler() + const handler = context.getHandler(); const options: IdempotenceOption | undefined = this.reflector.get( HTTP_IDEMPOTENCE_OPTIONS, - handler, - ) + handler + ); - if (!options) - return next.handle() + if (!options) return next.handle(); const { errorMessage = '相同请求成功后在 60 秒内只能发送一次', @@ -78,71 +71,64 @@ export class IdempotenceInterceptor implements NestInterceptor { handler: errorHandler, expired = 60, disableGenerateKey = false, - } = options - const redis = this.cacheService.getClient() + } = options; + const redis = this.cacheService.getClient(); - const idempotence = request.headers[IdempotenceHeaderKey] as string + const idempotence = request.headers[IdempotenceHeaderKey] as string; const key = disableGenerateKey ? undefined : options.generateKey ? options.generateKey(request) - : this.generateKey(request) + : this.generateKey(request); - const idempotenceKey - = !!(idempotence || key) && getRedisKey(`idempotence:${idempotence || key}`) + const idempotenceKey = + !!(idempotence || key) && getRedisKey(`idempotence:${idempotence || key}`); - SetMetadata(HTTP_IDEMPOTENCE_KEY, idempotenceKey)(handler) + SetMetadata(HTTP_IDEMPOTENCE_KEY, idempotenceKey)(handler); if (idempotenceKey) { - const resultValue: '0' | '1' | null = (await redis.get( - idempotenceKey, - )) as any + const resultValue: '0' | '1' | null = (await redis.get(idempotenceKey)) as any; if (resultValue !== null) { - if (errorHandler) - return await errorHandler(request) + if (errorHandler) return await errorHandler(request); const message = { 1: errorMessage, 0: pendingMessage, - }[resultValue] - throw new ConflictException(message) - } - else { - await redis.set(idempotenceKey, '0', 'EX', expired) + }[resultValue]; + throw new ConflictException(message); + } else { + await redis.set(idempotenceKey, '0', 'EX', expired); } } return next.handle().pipe( tap(async () => { - idempotenceKey && (await redis.set(idempotenceKey, '1', 'KEEPTTL')) + idempotenceKey && (await redis.set(idempotenceKey, '1', 'KEEPTTL')); }), - catchError(async (err) => { - if (idempotenceKey) - await redis.del(idempotenceKey) + catchError(async err => { + if (idempotenceKey) await redis.del(idempotenceKey); - throw err - }), - ) + throw err; + }) + ); } private generateKey(req: FastifyRequest) { - const { body, params, query = {}, headers, url } = req + const { body, params, query = {}, headers, url } = req; - const obj = { body, url, params, query } as any + const obj = { body, url, params, query } as any; - const uuid = headers['x-uuid'] + const uuid = headers['x-uuid']; if (uuid) { - obj.uuid = uuid - } - else { - const ua = headers['user-agent'] - const ip = getIp(req) + obj.uuid = uuid; + } else { + const ua = headers['user-agent']; + const ip = getIp(req); - if (!ua && !ip) - return undefined + if (!ua && !ip) return undefined; - Object.assign(obj, { ua, ip }) + Object.assign(obj, { ua, ip }); } - return hashString(JSON.stringify(obj)) + return hashString(JSON.stringify(obj)); } } diff --git a/src/common/interceptors/logging.interceptor.ts b/src/common/interceptors/logging.interceptor.ts index de54623..d9595f7 100644 --- a/src/common/interceptors/logging.interceptor.ts +++ b/src/common/interceptors/logging.interceptor.ts @@ -1,35 +1,24 @@ -import { - CallHandler, - ExecutionContext, - Injectable, - Logger, - NestInterceptor, -} from '@nestjs/common' -import { Observable, tap } from 'rxjs' +import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common'; +import { Observable, tap } from 'rxjs'; @Injectable() export class LoggingInterceptor implements NestInterceptor { - private logger = new Logger(LoggingInterceptor.name, { timestamp: false }) + private logger = new Logger(LoggingInterceptor.name, { timestamp: false }); - intercept( - context: ExecutionContext, - next: CallHandler, - ): Observable { - const call$ = next.handle() - const request = context.switchToHttp().getRequest() - const content = `${request.method} -> ${request.url}` - const isSse = request.headers.accept === 'text/event-stream' - this.logger.debug(`+++ 请求:${content}`) - const now = Date.now() + intercept(context: ExecutionContext, next: CallHandler): Observable { + const call$ = next.handle(); + const request = context.switchToHttp().getRequest(); + const content = `${request.method} -> ${request.url}`; + const isSse = request.headers.accept === 'text/event-stream'; + this.logger.debug(`+++ 请求:${content}`); + const now = Date.now(); return call$.pipe( tap(() => { - if (isSse) - return + if (isSse) return; - this.logger.debug(`--- 响应:${content}${` +${Date.now() - now}ms`}`) - }, - ), - ) + this.logger.debug(`--- 响应:${content}${` +${Date.now() - now}ms`}`); + }) + ); } } diff --git a/src/common/interceptors/timeout.interceptor.ts b/src/common/interceptors/timeout.interceptor.ts index 8508c58..464d53c 100644 --- a/src/common/interceptors/timeout.interceptor.ts +++ b/src/common/interceptors/timeout.interceptor.ts @@ -4,9 +4,9 @@ import { Injectable, NestInterceptor, RequestTimeoutException, -} from '@nestjs/common' -import { Observable, TimeoutError, throwError } from 'rxjs' -import { catchError, timeout } from 'rxjs/operators' +} from '@nestjs/common'; +import { Observable, TimeoutError, throwError } from 'rxjs'; +import { catchError, timeout } from 'rxjs/operators'; @Injectable() export class TimeoutInterceptor implements NestInterceptor { @@ -15,12 +15,11 @@ export class TimeoutInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { return next.handle().pipe( timeout(this.time), - catchError((err) => { - if (err instanceof TimeoutError) - return throwError(new RequestTimeoutException('请求超时')) + catchError(err => { + if (err instanceof TimeoutError) return throwError(new RequestTimeoutException('请求超时')); - return throwError(err) - }), - ) + return throwError(err); + }) + ); } } diff --git a/src/common/interceptors/transform.interceptor.ts b/src/common/interceptors/transform.interceptor.ts index 4b59fbe..8ba5c95 100644 --- a/src/common/interceptors/transform.interceptor.ts +++ b/src/common/interceptors/transform.interceptor.ts @@ -4,14 +4,14 @@ import { HttpStatus, Injectable, NestInterceptor, -} from '@nestjs/common' -import { Reflector } from '@nestjs/core' -import { Observable } from 'rxjs' -import { map } from 'rxjs/operators' +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; -import { ResOp } from '~/common/model/response.model' +import { ResOp } from '~/common/model/response.model'; -import { BYPASS_KEY } from '../decorators/bypass.decorator' +import { BYPASS_KEY } from '../decorators/bypass.decorator'; /** * 统一处理返回接口结果,如果不需要则添加 @Bypass 装饰器 @@ -20,27 +20,20 @@ import { BYPASS_KEY } from '../decorators/bypass.decorator' export class TransformInterceptor implements NestInterceptor { constructor(private readonly reflector: Reflector) {} - intercept( - context: ExecutionContext, - next: CallHandler, - ): Observable { - const bypass = this.reflector.get( - BYPASS_KEY, - context.getHandler(), - ) + intercept(context: ExecutionContext, next: CallHandler): Observable { + const bypass = this.reflector.get(BYPASS_KEY, context.getHandler()); - if (bypass) - return next.handle() + if (bypass) return next.handle(); return next.handle().pipe( - map((data) => { + map(data => { // if (typeof data === 'undefined') { // context.switchToHttp().getResponse().status(HttpStatus.NO_CONTENT); // return data; // } - return new ResOp(HttpStatus.OK, data ?? null) - }), - ) + return new ResOp(HttpStatus.OK, data ?? null); + }) + ); } } diff --git a/src/common/model/response.model.ts b/src/common/model/response.model.ts index 0336456..b3f6330 100644 --- a/src/common/model/response.model.ts +++ b/src/common/model/response.model.ts @@ -1,42 +1,39 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; -import { - RESPONSE_SUCCESS_CODE, - RESPONSE_SUCCESS_MSG, -} from '~/constants/response.constant' +import { RESPONSE_SUCCESS_CODE, RESPONSE_SUCCESS_MSG } from '~/constants/response.constant'; export class ResOp { @ApiProperty({ type: 'object' }) - data?: T + data?: T; @ApiProperty({ type: 'number', default: RESPONSE_SUCCESS_CODE }) - code: number + code: number; @ApiProperty({ type: 'string', default: RESPONSE_SUCCESS_MSG }) - message: string + message: string; constructor(code: number, data: T, message = RESPONSE_SUCCESS_MSG) { - this.code = code - this.data = data - this.message = message + this.code = code; + this.data = data; + this.message = message; } static success(data?: T, message?: string) { - return new ResOp(RESPONSE_SUCCESS_CODE, data, message) + return new ResOp(RESPONSE_SUCCESS_CODE, data, message); } static error(code: number, message) { - return new ResOp(code, {}, message) + return new ResOp(code, {}, message); } } export class TreeResult { @ApiProperty() - id: number + id: number; @ApiProperty() - parentId: number + parentId: number; @ApiProperty() - children?: TreeResult[] + children?: TreeResult[]; } diff --git a/src/common/pipes/parse-int.pipe.ts b/src/common/pipes/parse-int.pipe.ts index 514d67c..c6d7227 100644 --- a/src/common/pipes/parse-int.pipe.ts +++ b/src/common/pipes/parse-int.pipe.ts @@ -1,18 +1,12 @@ -import { - ArgumentMetadata, - BadRequestException, - Injectable, - PipeTransform, -} from '@nestjs/common' +import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; @Injectable() export class ParseIntPipe implements PipeTransform { transform(value: string, metadata: ArgumentMetadata): number { - const val = Number.parseInt(value, 10) + const val = Number.parseInt(value, 10); - if (Number.isNaN(val)) - throw new BadRequestException('id validation failed') + if (Number.isNaN(val)) throw new BadRequestException('id validation failed'); - return val + return val; } } diff --git a/src/config/app.config.ts b/src/config/app.config.ts index 25f2a14..072be51 100644 --- a/src/config/app.config.ts +++ b/src/config/app.config.ts @@ -1,8 +1,8 @@ -import { ConfigType, registerAs } from '@nestjs/config' +import { ConfigType, registerAs } from '@nestjs/config'; -import { env, envNumber } from '~/global/env' +import { env, envNumber } from '~/global/env'; -export const appRegToken = 'app' +export const appRegToken = 'app'; export const AppConfig = registerAs(appRegToken, () => ({ name: env('APP_NAME'), @@ -15,6 +15,6 @@ export const AppConfig = registerAs(appRegToken, () => ({ level: env('LOGGER_LEVEL'), maxFiles: envNumber('LOGGER_MAX_FILES'), }, -})) +})); -export type IAppConfig = ConfigType +export type IAppConfig = ConfigType; diff --git a/src/config/database.config.ts b/src/config/database.config.ts index 2d1b34f..2f9f706 100644 --- a/src/config/database.config.ts +++ b/src/config/database.config.ts @@ -1,16 +1,16 @@ -import { ConfigType, registerAs } from '@nestjs/config' +import { ConfigType, registerAs } from '@nestjs/config'; -import { DataSource, DataSourceOptions } from 'typeorm' +import { DataSource, DataSourceOptions } from 'typeorm'; -import { env, envBoolean, envNumber } from '~/global/env' +import { env, envBoolean, envNumber } from '~/global/env'; // eslint-disable-next-line import/order -import dotenv from 'dotenv' +import dotenv from 'dotenv'; -dotenv.config({ path: `.env.${process.env.NODE_ENV}` }) +dotenv.config({ path: `.env.${process.env.NODE_ENV}` }); // 当前通过 npm scripts 执行的命令 -const currentScript = process.env.npm_lifecycle_event +const currentScript = process.env.npm_lifecycle_event; const dataSourceOptions: DataSourceOptions = { type: 'mysql', @@ -25,16 +25,13 @@ const dataSourceOptions: DataSourceOptions = { entities: ['dist/modules/**/*.entity{.ts,.js}'], migrations: ['dist/migrations/*{.ts,.js}'], subscribers: ['dist/modules/**/*.subscriber{.ts,.js}'], -} -export const dbRegToken = 'database' +}; +export const dbRegToken = 'database'; -export const DatabaseConfig = registerAs( - dbRegToken, - (): DataSourceOptions => dataSourceOptions, -) +export const DatabaseConfig = registerAs(dbRegToken, (): DataSourceOptions => dataSourceOptions); -export type IDatabaseConfig = ConfigType +export type IDatabaseConfig = ConfigType; -const dataSource = new DataSource(dataSourceOptions) +const dataSource = new DataSource(dataSourceOptions); -export default dataSource +export default dataSource; diff --git a/src/config/index.ts b/src/config/index.ts index 7cca557..4154c0a 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,30 +1,30 @@ -import { AppConfig, IAppConfig, appRegToken } from './app.config' -import { DatabaseConfig, IDatabaseConfig, dbRegToken } from './database.config' -import { IMailerConfig, MailerConfig, mailerRegToken } from './mailer.config' -import { IOssConfig, OssConfig, ossRegToken } from './oss.config' -import { IRedisConfig, RedisConfig, redisRegToken } from './redis.config' -import { ISecurityConfig, SecurityConfig, securityRegToken } from './security.config' -import { ISwaggerConfig, SwaggerConfig, swaggerRegToken } from './swagger.config' +import { AppConfig, IAppConfig, appRegToken } from './app.config'; +import { DatabaseConfig, IDatabaseConfig, dbRegToken } from './database.config'; +import { IMailerConfig, MailerConfig, mailerRegToken } from './mailer.config'; +import { IOssConfig, OssConfig, ossRegToken } from './oss.config'; +import { IRedisConfig, RedisConfig, redisRegToken } from './redis.config'; +import { ISecurityConfig, SecurityConfig, securityRegToken } from './security.config'; +import { ISwaggerConfig, SwaggerConfig, swaggerRegToken } from './swagger.config'; -export * from './app.config' -export * from './redis.config' -export * from './database.config' -export * from './swagger.config' -export * from './security.config' -export * from './mailer.config' -export * from './oss.config' +export * from './app.config'; +export * from './redis.config'; +export * from './database.config'; +export * from './swagger.config'; +export * from './security.config'; +export * from './mailer.config'; +export * from './oss.config'; export interface AllConfigType { - [appRegToken]: IAppConfig - [dbRegToken]: IDatabaseConfig - [mailerRegToken]: IMailerConfig - [redisRegToken]: IRedisConfig - [securityRegToken]: ISecurityConfig - [swaggerRegToken]: ISwaggerConfig - [ossRegToken]: IOssConfig + [appRegToken]: IAppConfig; + [dbRegToken]: IDatabaseConfig; + [mailerRegToken]: IMailerConfig; + [redisRegToken]: IRedisConfig; + [securityRegToken]: ISecurityConfig; + [swaggerRegToken]: ISwaggerConfig; + [ossRegToken]: IOssConfig; } -export type ConfigKeyPaths = RecordNamePaths +export type ConfigKeyPaths = RecordNamePaths; export default { AppConfig, @@ -34,4 +34,4 @@ export default { RedisConfig, SecurityConfig, SwaggerConfig, -} +}; diff --git a/src/config/mailer.config.ts b/src/config/mailer.config.ts index 27763d1..2f60052 100644 --- a/src/config/mailer.config.ts +++ b/src/config/mailer.config.ts @@ -1,8 +1,8 @@ -import { ConfigType, registerAs } from '@nestjs/config' +import { ConfigType, registerAs } from '@nestjs/config'; -import { env, envNumber } from '~/global/env' +import { env, envNumber } from '~/global/env'; -export const mailerRegToken = 'mailer' +export const mailerRegToken = 'mailer'; export const MailerConfig = registerAs(mailerRegToken, () => ({ host: env('SMTP_HOST'), @@ -13,6 +13,6 @@ export const MailerConfig = registerAs(mailerRegToken, () => ({ user: env('SMTP_USER'), pass: env('SMTP_PASS'), }, -})) +})); -export type IMailerConfig = ConfigType +export type IMailerConfig = ConfigType; diff --git a/src/config/oss.config.ts b/src/config/oss.config.ts index ae1f563..0f00e42 100644 --- a/src/config/oss.config.ts +++ b/src/config/oss.config.ts @@ -1,24 +1,24 @@ -import { ConfigType, registerAs } from '@nestjs/config' -import * as qiniu from 'qiniu' +import { ConfigType, registerAs } from '@nestjs/config'; +import * as qiniu from 'qiniu'; -import { env } from '~/global/env' +import { env } from '~/global/env'; function parseZone(zone: string) { switch (zone) { case 'Zone_as0': - return qiniu.zone.Zone_as0 + return qiniu.zone.Zone_as0; case 'Zone_na0': - return qiniu.zone.Zone_na0 + return qiniu.zone.Zone_na0; case 'Zone_z0': - return qiniu.zone.Zone_z0 + return qiniu.zone.Zone_z0; case 'Zone_z1': - return qiniu.zone.Zone_z1 + return qiniu.zone.Zone_z1; case 'Zone_z2': - return qiniu.zone.Zone_z2 + return qiniu.zone.Zone_z2; } } -export const ossRegToken = 'oss' +export const ossRegToken = 'oss'; export const OssConfig = registerAs(ossRegToken, () => ({ accessKey: env('OSS_ACCESSKEY'), @@ -27,6 +27,6 @@ export const OssConfig = registerAs(ossRegToken, () => ({ bucket: env('OSS_BUCKET'), zone: parseZone(env('OSS_ZONE') || 'Zone_z2'), access: (env('OSS_ACCESS_TYPE') as any) || 'public', -})) +})); -export type IOssConfig = ConfigType +export type IOssConfig = ConfigType; diff --git a/src/config/redis.config.ts b/src/config/redis.config.ts index 749c772..5cbdbff 100644 --- a/src/config/redis.config.ts +++ b/src/config/redis.config.ts @@ -1,14 +1,14 @@ -import { ConfigType, registerAs } from '@nestjs/config' +import { ConfigType, registerAs } from '@nestjs/config'; -import { env, envNumber } from '~/global/env' +import { env, envNumber } from '~/global/env'; -export const redisRegToken = 'redis' +export const redisRegToken = 'redis'; export const RedisConfig = registerAs(redisRegToken, () => ({ host: env('REDIS_HOST', '127.0.0.1'), port: envNumber('REDIS_PORT', 6379), password: env('REDIS_PASSWORD'), db: envNumber('REDIS_DB'), -})) +})); -export type IRedisConfig = ConfigType +export type IRedisConfig = ConfigType; diff --git a/src/config/security.config.ts b/src/config/security.config.ts index 024cccb..f69ac11 100644 --- a/src/config/security.config.ts +++ b/src/config/security.config.ts @@ -1,14 +1,14 @@ -import { ConfigType, registerAs } from '@nestjs/config' +import { ConfigType, registerAs } from '@nestjs/config'; -import { env, envNumber } from '~/global/env' +import { env, envNumber } from '~/global/env'; -export const securityRegToken = 'security' +export const securityRegToken = 'security'; export const SecurityConfig = registerAs(securityRegToken, () => ({ jwtSecret: env('JWT_SECRET'), jwtExprire: envNumber('JWT_EXPIRE'), refreshSecret: env('REFRESH_TOKEN_SECRET'), refreshExpire: envNumber('REFRESH_TOKEN_EXPIRE'), -})) +})); -export type ISecurityConfig = ConfigType +export type ISecurityConfig = ConfigType; diff --git a/src/config/swagger.config.ts b/src/config/swagger.config.ts index 0424fcf..c29a9c3 100644 --- a/src/config/swagger.config.ts +++ b/src/config/swagger.config.ts @@ -1,12 +1,12 @@ -import { ConfigType, registerAs } from '@nestjs/config' +import { ConfigType, registerAs } from '@nestjs/config'; -import { env, envBoolean } from '~/global/env' +import { env, envBoolean } from '~/global/env'; -export const swaggerRegToken = 'swagger' +export const swaggerRegToken = 'swagger'; export const SwaggerConfig = registerAs(swaggerRegToken, () => ({ enable: envBoolean('SWAGGER_ENABLE'), path: env('SWAGGER_PATH'), -})) +})); -export type ISwaggerConfig = ConfigType +export type ISwaggerConfig = ConfigType; diff --git a/src/constants/cache.constant.ts b/src/constants/cache.constant.ts index c4852b8..34d9b26 100644 --- a/src/constants/cache.constant.ts +++ b/src/constants/cache.constant.ts @@ -5,4 +5,4 @@ export enum RedisKeys { AUTH_PERM_PREFIX = 'auth:permission:', AUTH_PASSWORD_V_PREFIX = 'auth:passwordVersion:', } -export const API_CACHE_PREFIX = 'api-cache:' +export const API_CACHE_PREFIX = 'api-cache:'; diff --git a/src/constants/oss.constant.ts b/src/constants/oss.constant.ts index a56336e..ceecb80 100644 --- a/src/constants/oss.constant.ts +++ b/src/constants/oss.constant.ts @@ -1,8 +1,8 @@ -export const OSS_CONFIG = 'admin_module:qiniu_config' -export const OSS_API = 'http://api.qiniu.com' +export const OSS_CONFIG = 'admin_module:qiniu_config'; +export const OSS_API = 'http://api.qiniu.com'; // 目录分隔符 -export const NETDISK_DELIMITER = '/' -export const NETDISK_LIMIT = 100 -export const NETDISK_HANDLE_MAX_ITEM = 1000 -export const NETDISK_COPY_SUFFIX = '的副本' +export const NETDISK_DELIMITER = '/'; +export const NETDISK_LIMIT = 100; +export const NETDISK_HANDLE_MAX_ITEM = 1000; +export const NETDISK_COPY_SUFFIX = '的副本'; diff --git a/src/constants/response.constant.ts b/src/constants/response.constant.ts index 229f70f..5b5395b 100644 --- a/src/constants/response.constant.ts +++ b/src/constants/response.constant.ts @@ -1,6 +1,6 @@ -export const RESPONSE_SUCCESS_CODE = 200 +export const RESPONSE_SUCCESS_CODE = 200; -export const RESPONSE_SUCCESS_MSG = 'success' +export const RESPONSE_SUCCESS_MSG = 'success'; /** * @description: contentType diff --git a/src/constants/system.constant.ts b/src/constants/system.constant.ts index eef03a5..d1e191c 100644 --- a/src/constants/system.constant.ts +++ b/src/constants/system.constant.ts @@ -1,6 +1,6 @@ -export const SYS_USER_INITPASSWORD = 'sys_user_initPassword' -export const SYS_API_TOKEN = 'sys_api_token' +export const SYS_USER_INITPASSWORD = 'sys_user_initPassword'; +export const SYS_API_TOKEN = 'sys_api_token'; /** 超级管理员用户 id */ -export const ROOT_USER_ID = 1 +export const ROOT_USER_ID = 1; /** 超级管理员角色 id */ -export const ROOT_ROLE_ID = 1 +export const ROOT_ROLE_ID = 1; diff --git a/src/global/env.ts b/src/global/env.ts index 8f7b82b..4e283c0 100644 --- a/src/global/env.ts +++ b/src/global/env.ts @@ -1,18 +1,18 @@ -import cluster from 'node:cluster' +import cluster from 'node:cluster'; -export const isMainCluster - = process.env.NODE_APP_INSTANCE && Number.parseInt(process.env.NODE_APP_INSTANCE) === 0 -export const isMainProcess = cluster.isPrimary || isMainCluster +export const isMainCluster = + process.env.NODE_APP_INSTANCE && Number.parseInt(process.env.NODE_APP_INSTANCE) === 0; +export const isMainProcess = cluster.isPrimary || isMainCluster; -export const isDev = process.env.NODE_ENV === 'development' +export const isDev = process.env.NODE_ENV === 'development'; -export const isTest = !!process.env.TEST -export const cwd = process.cwd() +export const isTest = !!process.env.TEST; +export const cwd = process.cwd(); /** * 基础类型接口 */ -export type BaseType = boolean | number | string | undefined | null +export type BaseType = boolean | number | string | undefined | null; /** * 格式化环境变量 @@ -20,43 +20,43 @@ export type BaseType = boolean | number | string | undefined | null * @param defaultValue 默认值 * @param callback 格式化函数 */ -function fromatValue(key: string, defaultValue: T, callback?: (value: string) => T): T { - const value: string | undefined = process.env[key] - if (typeof value === 'undefined') - return defaultValue +function fromatValue( + key: string, + defaultValue: T, + callback?: (value: string) => T +): T { + const value: string | undefined = process.env[key]; + if (typeof value === 'undefined') return defaultValue; - if (!callback) - return value as unknown as T + if (!callback) return value as unknown as T; - return callback(value) + return callback(value); } export function env(key: string, defaultValue: string = '') { - return fromatValue(key, defaultValue) + return fromatValue(key, defaultValue); } export function envString(key: string, defaultValue: string = '') { - return fromatValue(key, defaultValue) + return fromatValue(key, defaultValue); } export function envNumber(key: string, defaultValue: number = 0) { - return fromatValue(key, defaultValue, (value) => { + return fromatValue(key, defaultValue, value => { try { - return Number(value) + return Number(value); + } catch { + throw new Error(`${key} environment variable is not a number`); } - catch { - throw new Error(`${key} environment variable is not a number`) - } - }) + }); } export function envBoolean(key: string, defaultValue: boolean = false) { - return fromatValue(key, defaultValue, (value) => { + return fromatValue(key, defaultValue, value => { try { - return Boolean(JSON.parse(value)) + return Boolean(JSON.parse(value)); + } catch { + throw new Error(`${key} environment variable is not a boolean`); } - catch { - throw new Error(`${key} environment variable is not a boolean`) - } - }) + }); } diff --git a/src/helper/catchError.ts b/src/helper/catchError.ts index d21789c..78b8502 100644 --- a/src/helper/catchError.ts +++ b/src/helper/catchError.ts @@ -1,5 +1,5 @@ export function catchError() { process.on('unhandledRejection', (reason, p) => { - console.log('Promise: ', p, 'Reason: ', reason) - }) + console.log('Promise: ', p, 'Reason: ', reason); + }); } diff --git a/src/helper/crud/base.service.ts b/src/helper/crud/base.service.ts index daa67a4..41c526b 100644 --- a/src/helper/crud/base.service.ts +++ b/src/helper/crud/base.service.ts @@ -1,40 +1,35 @@ -import { NotFoundException } from '@nestjs/common' -import { ObjectLiteral, Repository } from 'typeorm' +import { NotFoundException } from '@nestjs/common'; +import { ObjectLiteral, Repository } from 'typeorm'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; -import { paginate } from '../paginate' -import { Pagination } from '../paginate/pagination' +import { paginate } from '../paginate'; +import { Pagination } from '../paginate/pagination'; export class BaseService = Repository> { - constructor(private repository: R) { - } + constructor(private repository: R) {} - async list({ - page, - pageSize, - }: PagerDto): Promise> { - return paginate(this.repository, { page, pageSize }) + async list({ page, pageSize }: PagerDto): Promise> { + return paginate(this.repository, { page, pageSize }); } async findOne(id: number): Promise { - const item = await this.repository.createQueryBuilder().where({ id }).getOne() - if (!item) - throw new NotFoundException('未找到该记录') + const item = await this.repository.createQueryBuilder().where({ id }).getOne(); + if (!item) throw new NotFoundException('未找到该记录'); - return item + return item; } async create(dto: any): Promise { - return await this.repository.save(dto) + return await this.repository.save(dto); } async update(id: number, dto: any): Promise { - await this.repository.update(id, dto) + await this.repository.update(id, dto); } async delete(id: number): Promise { - const item = await this.findOne(id) - await this.repository.remove(item) + const item = await this.findOne(id); + await this.repository.remove(item); } } diff --git a/src/helper/crud/crud.factory.ts b/src/helper/crud/crud.factory.ts index 7ae1fc8..67b0e6d 100644 --- a/src/helper/crud/crud.factory.ts +++ b/src/helper/crud/crud.factory.ts @@ -1,81 +1,80 @@ -import type { Type } from '@nestjs/common' +import type { Type } from '@nestjs/common'; -import { - Body, - Controller, - Delete, - Get, - Patch, - Post, - Put, - Query, -} from '@nestjs/common' -import { ApiBody, IntersectionType, PartialType } from '@nestjs/swagger' -import pluralize from 'pluralize' +import { Body, Controller, Delete, Get, Patch, Post, Put, Query } from '@nestjs/common'; +import { ApiBody, IntersectionType, PartialType } from '@nestjs/swagger'; +import pluralize from 'pluralize'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { PagerDto } from '~/common/dto/pager.dto' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { PagerDto } from '~/common/dto/pager.dto'; -import { BaseService } from './base.service' +import { BaseService } from './base.service'; -export function BaseCrudFactory< - E extends new (...args: any[]) => any, ->({ entity, dto, permissions }: { entity: E, dto?: Type, permissions?: Record }): Type { - const prefix = entity.name.toLowerCase().replace(/entity$/, '') - const pluralizeName = pluralize(prefix) as string +export function BaseCrudFactory any>({ + entity, + dto, + permissions, +}: { + entity: E; + dto?: Type; + permissions?: Record; +}): Type { + const prefix = entity.name.toLowerCase().replace(/entity$/, ''); + const pluralizeName = pluralize(prefix) as string; - dto = dto ?? class extends entity {} + dto = dto ?? class extends entity {}; class Dto extends dto {} class UpdateDto extends PartialType(Dto) {} class QueryDto extends IntersectionType(PagerDto, PartialType(Dto)) {} - permissions = permissions ?? { - LIST: `${prefix}:list`, - CREATE: `${prefix}:create`, - READ: `${prefix}:read`, - UPDATE: `${prefix}:update`, - DELETE: `${prefix}:delete`, - } as const + permissions = + permissions ?? + ({ + LIST: `${prefix}:list`, + CREATE: `${prefix}:create`, + READ: `${prefix}:read`, + UPDATE: `${prefix}:update`, + DELETE: `${prefix}:delete`, + } as const); @Controller(pluralizeName) class BaseController> { - constructor(private service: S) { } + constructor(private service: S) {} @Get() @ApiResult({ type: [entity], isPage: true }) async list(@Query() pager: QueryDto) { - return await this.service.list(pager) + return await this.service.list(pager); } @Get(':id') @ApiResult({ type: entity }) async get(@IdParam() id: number) { - return await this.service.findOne(id) + return await this.service.findOne(id); } @Post() @ApiBody({ type: dto }) async create(@Body() dto: Dto) { - return await this.service.create(dto) + return await this.service.create(dto); } @Put(':id') async update(@IdParam() id: number, @Body() dto: UpdateDto) { - return await this.service.update(id, dto) + return await this.service.update(id, dto); } @Patch(':id') async patch(@IdParam() id: number, @Body() dto: UpdateDto) { - await this.service.update(id, dto) + await this.service.update(id, dto); } @Delete(':id') async delete(@IdParam() id: number) { - await this.service.delete(id) + await this.service.delete(id); } } - return BaseController + return BaseController; } diff --git a/src/helper/genRedisKey.ts b/src/helper/genRedisKey.ts index 1dab9a3..4cbe485 100644 --- a/src/helper/genRedisKey.ts +++ b/src/helper/genRedisKey.ts @@ -1,19 +1,19 @@ -import { RedisKeys } from '~/constants/cache.constant' +import { RedisKeys } from '~/constants/cache.constant'; /** 生成验证码 redis key */ export function genCaptchaImgKey(val: string | number) { - return `${RedisKeys.CAPTCHA_IMG_PREFIX}${String(val)}` as const + return `${RedisKeys.CAPTCHA_IMG_PREFIX}${String(val)}` as const; } /** 生成 auth token redis key */ export function genAuthTokenKey(val: string | number) { - return `${RedisKeys.AUTH_TOKEN_PREFIX}${String(val)}` as const + return `${RedisKeys.AUTH_TOKEN_PREFIX}${String(val)}` as const; } /** 生成 auth permission redis key */ export function genAuthPermKey(val: string | number) { - return `${RedisKeys.AUTH_PERM_PREFIX}${String(val)}` as const + return `${RedisKeys.AUTH_PERM_PREFIX}${String(val)}` as const; } /** 生成 auth passwordVersion redis key */ export function genAuthPVKey(val: string | number) { - return `${RedisKeys.AUTH_PASSWORD_V_PREFIX}${String(val)}` as const + return `${RedisKeys.AUTH_PASSWORD_V_PREFIX}${String(val)}` as const; } diff --git a/src/helper/paginate/create-pagination.ts b/src/helper/paginate/create-pagination.ts index 5c1182f..589e416 100644 --- a/src/helper/paginate/create-pagination.ts +++ b/src/helper/paginate/create-pagination.ts @@ -1,5 +1,5 @@ -import { IPaginationMeta } from './interface' -import { Pagination } from './pagination' +import { IPaginationMeta } from './interface'; +import { Pagination } from './pagination'; export function createPaginationObject({ items, @@ -7,13 +7,12 @@ export function createPaginationObject({ currentPage, limit, }: { - items: T[] - totalItems?: number - currentPage: number - limit: number + items: T[]; + totalItems?: number; + currentPage: number; + limit: number; }): Pagination { - const totalPages - = totalItems !== undefined ? Math.ceil(totalItems / limit) : undefined + const totalPages = totalItems !== undefined ? Math.ceil(totalItems / limit) : undefined; const meta: IPaginationMeta = { totalItems, @@ -21,7 +20,7 @@ export function createPaginationObject({ itemsPerPage: limit, totalPages, currentPage, - } + }; - return new Pagination(items, meta) + return new Pagination(items, meta); } diff --git a/src/helper/paginate/index.ts b/src/helper/paginate/index.ts index fa8b487..b50b8a8 100644 --- a/src/helper/paginate/index.ts +++ b/src/helper/paginate/index.ts @@ -4,33 +4,31 @@ import { ObjectLiteral, Repository, SelectQueryBuilder, -} from 'typeorm' +} from 'typeorm'; -import { createPaginationObject } from './create-pagination' -import { IPaginationOptions, PaginationTypeEnum } from './interface' -import { Pagination } from './pagination' +import { createPaginationObject } from './create-pagination'; +import { IPaginationOptions, PaginationTypeEnum } from './interface'; +import { Pagination } from './pagination'; -const DEFAULT_LIMIT = 10 -const DEFAULT_PAGE = 1 +const DEFAULT_LIMIT = 10; +const DEFAULT_PAGE = 1; -function resolveOptions( - options: IPaginationOptions, -): [number, number, PaginationTypeEnum] { - const { page, pageSize, paginationType } = options +function resolveOptions(options: IPaginationOptions): [number, number, PaginationTypeEnum] { + const { page, pageSize, paginationType } = options; return [ page || DEFAULT_PAGE, pageSize || DEFAULT_LIMIT, paginationType || PaginationTypeEnum.TAKE_AND_SKIP, - ] + ]; } async function paginateRepository( repository: Repository, options: IPaginationOptions, - searchOptions?: FindOptionsWhere | FindManyOptions, + searchOptions?: FindOptionsWhere | FindManyOptions ): Promise> { - const [page, limit] = resolveOptions(options) + const [page, limit] = resolveOptions(options); const promises: [Promise, Promise | undefined] = [ repository.find({ @@ -39,44 +37,43 @@ async function paginateRepository( ...searchOptions, }), undefined, - ] + ]; - const [items, total] = await Promise.all(promises) + const [items, total] = await Promise.all(promises); return createPaginationObject({ items, totalItems: total, currentPage: page, limit, - }) + }); } async function paginateQueryBuilder( queryBuilder: SelectQueryBuilder, - options: IPaginationOptions, + options: IPaginationOptions ): Promise> { - const [page, limit, paginationType] = resolveOptions(options) + const [page, limit, paginationType] = resolveOptions(options); if (paginationType === PaginationTypeEnum.TAKE_AND_SKIP) - queryBuilder.take(limit).skip((page - 1) * limit) - else - queryBuilder.limit(limit).offset((page - 1) * limit) + queryBuilder.take(limit).skip((page - 1) * limit); + else queryBuilder.limit(limit).offset((page - 1) * limit); - const [items, total] = await queryBuilder.getManyAndCount() + const [items, total] = await queryBuilder.getManyAndCount(); return createPaginationObject({ items, totalItems: total, currentPage: page, limit, - }) + }); } export async function paginateRaw( queryBuilder: SelectQueryBuilder, - options: IPaginationOptions, + options: IPaginationOptions ): Promise> { - const [page, limit, paginationType] = resolveOptions(options) + const [page, limit, paginationType] = resolveOptions(options); const promises: [Promise, Promise | undefined] = [ (paginationType === PaginationTypeEnum.LIMIT_AND_OFFSET @@ -84,36 +81,33 @@ export async function paginateRaw( : queryBuilder.take(limit).skip((page - 1) * limit) ).getRawMany(), queryBuilder.getCount(), - ] + ]; - const [items, total] = await Promise.all(promises) + const [items, total] = await Promise.all(promises); return createPaginationObject({ items, totalItems: total, currentPage: page, limit, - }) + }); } export async function paginateRawAndEntities( queryBuilder: SelectQueryBuilder, - options: IPaginationOptions, + options: IPaginationOptions ): Promise<[Pagination, Partial[]]> { - const [page, limit, paginationType] = resolveOptions(options) + const [page, limit, paginationType] = resolveOptions(options); - const promises: [ - Promise<{ entities: T[], raw: T[] }>, - Promise | undefined, - ] = [ + const promises: [Promise<{ entities: T[]; raw: T[] }>, Promise | undefined] = [ (paginationType === PaginationTypeEnum.LIMIT_AND_OFFSET ? queryBuilder.limit(limit).offset((page - 1) * limit) : queryBuilder.take(limit).skip((page - 1) * limit) ).getRawAndEntities(), queryBuilder.getCount(), - ] + ]; - const [itemObject, total] = await Promise.all(promises) + const [itemObject, total] = await Promise.all(promises); return [ createPaginationObject({ @@ -123,25 +117,25 @@ export async function paginateRawAndEntities( limit, }), itemObject.raw, - ] + ]; } export async function paginate( repository: Repository, options: IPaginationOptions, - searchOptions?: FindOptionsWhere | FindManyOptions, -): Promise> + searchOptions?: FindOptionsWhere | FindManyOptions +): Promise>; export async function paginate( queryBuilder: SelectQueryBuilder, - options: IPaginationOptions, -): Promise> + options: IPaginationOptions +): Promise>; export async function paginate( repositoryOrQueryBuilder: Repository | SelectQueryBuilder, options: IPaginationOptions, - searchOptions?: FindOptionsWhere | FindManyOptions, + searchOptions?: FindOptionsWhere | FindManyOptions ) { return repositoryOrQueryBuilder instanceof Repository ? paginateRepository(repositoryOrQueryBuilder, options, searchOptions) - : paginateQueryBuilder(repositoryOrQueryBuilder, options) + : paginateQueryBuilder(repositoryOrQueryBuilder, options); } diff --git a/src/helper/paginate/interface.ts b/src/helper/paginate/interface.ts index e7c6ee2..d2cd7d9 100644 --- a/src/helper/paginate/interface.ts +++ b/src/helper/paginate/interface.ts @@ -1,4 +1,4 @@ -import { ObjectLiteral } from 'typeorm' +import { ObjectLiteral } from 'typeorm'; export enum PaginationTypeEnum { LIMIT_AND_OFFSET = 'limit', @@ -6,22 +6,22 @@ export enum PaginationTypeEnum { } export interface IPaginationOptions { - page: number - pageSize: number - paginationType?: PaginationTypeEnum + page: number; + pageSize: number; + paginationType?: PaginationTypeEnum; } export interface IPaginationMeta extends ObjectLiteral { - itemCount: number - totalItems?: number - itemsPerPage: number - totalPages?: number - currentPage: number + itemCount: number; + totalItems?: number; + itemsPerPage: number; + totalPages?: number; + currentPage: number; } export interface IPaginationLinks { - first?: string - previous?: string - next?: string - last?: string + first?: string; + previous?: string; + next?: string; + last?: string; } diff --git a/src/helper/paginate/pagination.ts b/src/helper/paginate/pagination.ts index 5bcf90f..1d52aba 100644 --- a/src/helper/paginate/pagination.ts +++ b/src/helper/paginate/pagination.ts @@ -1,14 +1,11 @@ -import { ObjectLiteral } from 'typeorm' +import { ObjectLiteral } from 'typeorm'; -import { IPaginationMeta } from './interface' +import { IPaginationMeta } from './interface'; -export class Pagination< - PaginationObject, - T extends ObjectLiteral = IPaginationMeta, -> { +export class Pagination { constructor( public readonly items: PaginationObject[], - public readonly meta: T, + public readonly meta: T ) {} } diff --git a/src/main.ts b/src/main.ts index 4fd1732..2a174a4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,56 +1,46 @@ -import cluster from 'node:cluster' -import path from 'node:path' +import cluster from 'node:cluster'; +import path from 'node:path'; -import { - HttpStatus, - Logger, - UnprocessableEntityException, - ValidationPipe, -} from '@nestjs/common' -import { ConfigService } from '@nestjs/config' -import { NestFactory } from '@nestjs/core' -import { NestFastifyApplication } from '@nestjs/platform-fastify' +import { HttpStatus, Logger, UnprocessableEntityException, ValidationPipe } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { NestFactory } from '@nestjs/core'; +import { NestFastifyApplication } from '@nestjs/platform-fastify'; -import { useContainer } from 'class-validator' +import { useContainer } from 'class-validator'; -import { AppModule } from './app.module' +import { AppModule } from './app.module'; -import { fastifyApp } from './common/adapters/fastify.adapter' -import { RedisIoAdapter } from './common/adapters/socket.adapter' -import { LoggingInterceptor } from './common/interceptors/logging.interceptor' -import type { ConfigKeyPaths } from './config' -import { isDev, isMainProcess } from './global/env' -import { setupSwagger } from './setup-swagger' -import { LoggerService } from './shared/logger/logger.service' +import { fastifyApp } from './common/adapters/fastify.adapter'; +import { RedisIoAdapter } from './common/adapters/socket.adapter'; +import { LoggingInterceptor } from './common/interceptors/logging.interceptor'; +import type { ConfigKeyPaths } from './config'; +import { isDev, isMainProcess } from './global/env'; +import { setupSwagger } from './setup-swagger'; +import { LoggerService } from './shared/logger/logger.service'; -declare const module: any +declare const module: any; async function bootstrap() { - const app = await NestFactory.create( - AppModule, - fastifyApp, - { - bufferLogs: true, - snapshot: true, - // forceCloseConnections: true, - }, - ) + const app = await NestFactory.create(AppModule, fastifyApp, { + bufferLogs: true, + snapshot: true, + // forceCloseConnections: true, + }); - const configService = app.get(ConfigService) + const configService = app.get(ConfigService); - const { port, globalPrefix } = configService.get('app', { infer: true }) + const { port, globalPrefix } = configService.get('app', { infer: true }); // class-validator 的 DTO 类中注入 nest 容器的依赖 (用于自定义验证器) - useContainer(app.select(AppModule), { fallbackOnErrors: true }) + useContainer(app.select(AppModule), { fallbackOnErrors: true }); - app.enableCors({ origin: '*', credentials: true }) - app.setGlobalPrefix(globalPrefix) - app.useStaticAssets({ root: path.join(__dirname, '..', 'public') }) + app.enableCors({ origin: '*', credentials: true }); + app.setGlobalPrefix(globalPrefix); + app.useStaticAssets({ root: path.join(__dirname, '..', 'public') }); // Starts listening for shutdown hooks - !isDev && app.enableShutdownHooks() + !isDev && app.enableShutdownHooks(); - if (isDev) - app.useGlobalInterceptors(new LoggingInterceptor()) + if (isDev) app.useGlobalInterceptors(new LoggingInterceptor()); app.useGlobalPipes( new ValidationPipe({ @@ -62,40 +52,38 @@ async function bootstrap() { stopAtFirstError: true, exceptionFactory: errors => new UnprocessableEntityException( - errors.map((e) => { - const rule = Object.keys(e.constraints!)[0] - const msg = e.constraints![rule] - return msg - })[0], + errors.map(e => { + const rule = Object.keys(e.constraints!)[0]; + const msg = e.constraints![rule]; + return msg; + })[0] ), - }), - ) + }) + ); - app.useWebSocketAdapter(new RedisIoAdapter(app)) + app.useWebSocketAdapter(new RedisIoAdapter(app)); - setupSwagger(app, configService) + setupSwagger(app, configService); await app.listen(port, '0.0.0.0', async () => { - app.useLogger(app.get(LoggerService)) - const url = await app.getUrl() - const { pid } = process - const env = cluster.isPrimary - const prefix = env ? 'P' : 'W' + app.useLogger(app.get(LoggerService)); + const url = await app.getUrl(); + const { pid } = process; + const env = cluster.isPrimary; + const prefix = env ? 'P' : 'W'; - if (!isMainProcess) - return + if (!isMainProcess) return; - const logger = new Logger('NestApplication') - logger.log(`[${prefix + pid}] Server running on ${url}`) + const logger = new Logger('NestApplication'); + logger.log(`[${prefix + pid}] Server running on ${url}`); - if (isDev) - logger.log(`[${prefix + pid}] OpenAPI: ${url}/api-docs`) - }) + if (isDev) logger.log(`[${prefix + pid}] OpenAPI: ${url}/api-docs`); + }); if (module.hot) { - module.hot.accept() - module.hot.dispose(() => app.close()) + module.hot.accept(); + module.hot.dispose(() => app.close()); } } -bootstrap() +bootstrap(); diff --git a/src/migrations/1707996695540-initData.ts b/src/migrations/1707996695540-initData.ts index 75f758c..88a7bf6 100644 --- a/src/migrations/1707996695540-initData.ts +++ b/src/migrations/1707996695540-initData.ts @@ -1,15 +1,14 @@ -import fs from 'node:fs' -import path from 'node:path' +import fs from 'node:fs'; +import path from 'node:path'; -import { MigrationInterface, QueryRunner } from 'typeorm' +import { MigrationInterface, QueryRunner } from 'typeorm'; -const sql = fs.readFileSync(path.join(__dirname, '../../deploy/sql/hxoa.sql'), 'utf8') +const sql = fs.readFileSync(path.join(__dirname, '../../deploy/sql/hxoa.sql'), 'utf8'); export class InitData1707996695540 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(sql) + await queryRunner.query(sql); } - public async down(queryRunner: QueryRunner): Promise { - } + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/src/modules/auth/auth.constant.ts b/src/modules/auth/auth.constant.ts index 08d48e6..0a9f744 100644 --- a/src/modules/auth/auth.constant.ts +++ b/src/modules/auth/auth.constant.ts @@ -1,10 +1,10 @@ -export const PUBLIC_KEY = '__public_key__' +export const PUBLIC_KEY = '__public_key__'; -export const PERMISSION_KEY = '__permission_key__' +export const PERMISSION_KEY = '__permission_key__'; -export const RESOURCE_KEY = '__resource_key__' +export const RESOURCE_KEY = '__resource_key__'; -export const ALLOW_ANON_KEY = '__allow_anon_permission_key__' +export const ALLOW_ANON_KEY = '__allow_anon_permission_key__'; export const AuthStrategy = { LOCAL: 'local', @@ -15,12 +15,12 @@ export const AuthStrategy = { GITHUB: 'github', GOOGLE: 'google', -} as const +} as const; export const Roles = { ADMIN: 'admin', USER: 'user', // GUEST: 'guest', -} as const +} as const; -export type Role = (typeof Roles)[keyof typeof Roles] +export type Role = (typeof Roles)[keyof typeof Roles]; diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index cb1bed7..b3270a4 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -1,17 +1,17 @@ -import { Body, Controller, Headers, Post, UseGuards } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Headers, Post, UseGuards } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { Ip } from '~/common/decorators/http.decorator' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { Ip } from '~/common/decorators/http.decorator'; -import { UserService } from '../user/user.service' +import { UserService } from '../user/user.service'; -import { AuthService } from './auth.service' -import { Public } from './decorators/public.decorator' -import { LoginDto, RegisterDto } from './dto/auth.dto' -import { LocalGuard } from './guards/local.guard' -import { LoginToken } from './models/auth.model' -import { CaptchaService } from './services/captcha.service' +import { AuthService } from './auth.service'; +import { Public } from './decorators/public.decorator'; +import { LoginDto, RegisterDto } from './dto/auth.dto'; +import { LocalGuard } from './guards/local.guard'; +import { LoginToken } from './models/auth.model'; +import { CaptchaService } from './services/captcha.service'; @ApiTags('Auth - 认证模块') @UseGuards(LocalGuard) @@ -21,27 +21,25 @@ export class AuthController { constructor( private authService: AuthService, private userService: UserService, - private captchaService: CaptchaService, + private captchaService: CaptchaService ) {} @Post('login') @ApiOperation({ summary: '登录' }) @ApiResult({ type: LoginToken }) async login( - @Body() dto: LoginDto, @Ip() ip: string, @Headers('user-agent') ua: string): Promise { - await this.captchaService.checkImgCaptcha(dto.captchaId, dto.verifyCode) - const token = await this.authService.login( - dto.username, - dto.password, - ip, - ua, - ) - return { token } + @Body() dto: LoginDto, + @Ip() ip: string, + @Headers('user-agent') ua: string + ): Promise { + await this.captchaService.checkImgCaptcha(dto.captchaId, dto.verifyCode); + const token = await this.authService.login(dto.username, dto.password, ip, ua); + return { token }; } @Post('register') @ApiOperation({ summary: '注册' }) async register(@Body() dto: RegisterDto): Promise { - await this.userService.register(dto) + await this.userService.register(dto); } } diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index edaf919..8d67c3a 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -1,38 +1,33 @@ -import { Module } from '@nestjs/common' +import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config' -import { JwtModule } from '@nestjs/jwt' -import { PassportModule } from '@nestjs/passport' -import { TypeOrmModule } from '@nestjs/typeorm' +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { JwtModule } from '@nestjs/jwt'; +import { PassportModule } from '@nestjs/passport'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigKeyPaths, ISecurityConfig } from '~/config' -import { isDev } from '~/global/env' +import { ConfigKeyPaths, ISecurityConfig } from '~/config'; +import { isDev } from '~/global/env'; -import { LogModule } from '../system/log/log.module' -import { MenuModule } from '../system/menu/menu.module' -import { RoleModule } from '../system/role/role.module' -import { UserModule } from '../user/user.module' +import { LogModule } from '../system/log/log.module'; +import { MenuModule } from '../system/menu/menu.module'; +import { RoleModule } from '../system/role/role.module'; +import { UserModule } from '../user/user.module'; -import { AuthController } from './auth.controller' -import { AuthService } from './auth.service' -import { AccountController } from './controllers/account.controller' -import { CaptchaController } from './controllers/captcha.controller' -import { EmailController } from './controllers/email.controller' -import { AccessTokenEntity } from './entities/access-token.entity' -import { RefreshTokenEntity } from './entities/refresh-token.entity' -import { CaptchaService } from './services/captcha.service' -import { TokenService } from './services/token.service' -import { JwtStrategy } from './strategies/jwt.strategy' -import { LocalStrategy } from './strategies/local.strategy' +import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { AccountController } from './controllers/account.controller'; +import { CaptchaController } from './controllers/captcha.controller'; +import { EmailController } from './controllers/email.controller'; +import { AccessTokenEntity } from './entities/access-token.entity'; +import { RefreshTokenEntity } from './entities/refresh-token.entity'; +import { CaptchaService } from './services/captcha.service'; +import { TokenService } from './services/token.service'; +import { JwtStrategy } from './strategies/jwt.strategy'; +import { LocalStrategy } from './strategies/local.strategy'; -const controllers = [ - AuthController, - AccountController, - CaptchaController, - EmailController, -] -const providers = [AuthService, TokenService, CaptchaService] -const strategies = [LocalStrategy, JwtStrategy] +const controllers = [AuthController, AccountController, CaptchaController, EmailController]; +const providers = [AuthService, TokenService, CaptchaService]; +const strategies = [LocalStrategy, JwtStrategy]; @Module({ imports: [ @@ -41,14 +36,13 @@ const strategies = [LocalStrategy, JwtStrategy] JwtModule.registerAsync({ imports: [ConfigModule], useFactory: (configService: ConfigService) => { - const { jwtSecret, jwtExprire } - = configService.get('security') + const { jwtSecret, jwtExprire } = configService.get('security'); return { secret: jwtSecret, expires: jwtExprire, ignoreExpiration: isDev, - } + }; }, inject: [ConfigService], }), diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index b734e27..8f97c97 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -1,23 +1,23 @@ -import { InjectRedis } from '@liaoliaots/nestjs-redis' -import { Injectable } from '@nestjs/common' +import { InjectRedis } from '@liaoliaots/nestjs-redis'; +import { Injectable } from '@nestjs/common'; -import Redis from 'ioredis' -import { isEmpty } from 'lodash' +import Redis from 'ioredis'; +import { isEmpty } from 'lodash'; -import { BusinessException } from '~/common/exceptions/biz.exception' +import { BusinessException } from '~/common/exceptions/biz.exception'; -import { ErrorEnum } from '~/constants/error-code.constant' -import { genAuthPVKey, genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey' +import { ErrorEnum } from '~/constants/error-code.constant'; +import { genAuthPVKey, genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey'; -import { UserService } from '~/modules/user/user.service' +import { UserService } from '~/modules/user/user.service'; -import { md5 } from '~/utils' +import { md5 } from '~/utils'; -import { LoginLogService } from '../system/log/services/login-log.service' -import { MenuService } from '../system/menu/menu.service' -import { RoleService } from '../system/role/role.service' +import { LoginLogService } from '../system/log/services/login-log.service'; +import { MenuService } from '../system/menu/menu.service'; +import { RoleService } from '../system/role/role.service'; -import { TokenService } from './services/token.service' +import { TokenService } from './services/token.service'; @Injectable() export class AuthService { @@ -27,130 +27,123 @@ export class AuthService { private roleService: RoleService, private userService: UserService, private loginLogService: LoginLogService, - private tokenService: TokenService, + private tokenService: TokenService ) {} async validateUser(credential: string, password: string): Promise { - const user = await this.userService.findUserByUserName(credential) + const user = await this.userService.findUserByUserName(credential); - if (isEmpty(user)) - throw new BusinessException(ErrorEnum.USER_NOT_FOUND) + if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND); - const comparePassword = md5(`${password}${user.psalt}`) + const comparePassword = md5(`${password}${user.psalt}`); if (user.password !== comparePassword) - throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD) + throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); if (user) { - const { password, ...result } = user - return result + const { password, ...result } = user; + return result; } - return null + return null; } /** * 获取登录JWT * 返回null则账号密码有误,不存在该用户 */ - async login( - username: string, - password: string, - ip: string, - ua: string, - ): Promise { - const user = await this.userService.findUserByUserName(username) - if (isEmpty(user)) - throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD) + async login(username: string, password: string, ip: string, ua: string): Promise { + const user = await this.userService.findUserByUserName(username); + if (isEmpty(user)) throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); - const comparePassword = md5(`${password}${user.psalt}`) + const comparePassword = md5(`${password}${user.psalt}`); if (user.password !== comparePassword) - throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD) + throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); - const roleIds = await this.roleService.getRoleIdsByUser(user.id) + const roleIds = await this.roleService.getRoleIdsByUser(user.id); - const roles = await this.roleService.getRoleValues(roleIds) + const roles = await this.roleService.getRoleValues(roleIds); // 包含access_token和refresh_token - const token = await this.tokenService.generateAccessToken(user.id, roles) + const token = await this.tokenService.generateAccessToken(user.id, roles); - await this.redis.set(genAuthTokenKey(user.id), token.accessToken) + await this.redis.set(genAuthTokenKey(user.id), token.accessToken); // 设置密码版本号 当密码修改时,版本号+1 - await this.redis.set(genAuthPVKey(user.id), 1) + await this.redis.set(genAuthPVKey(user.id), 1); // 设置菜单权限 - const permissions = await this.menuService.getPermissions(user.id) - await this.setPermissionsCache(user.id, permissions) + const permissions = await this.menuService.getPermissions(user.id); + await this.setPermissionsCache(user.id, permissions); - await this.loginLogService.create(user.id, ip, ua) + await this.loginLogService.create(user.id, ip, ua); - return token.accessToken + return token.accessToken; } /** * 效验账号密码 */ async checkPassword(username: string, password: string) { - const user = await this.userService.findUserByUserName(username) + const user = await this.userService.findUserByUserName(username); - const comparePassword = md5(`${password}${user.psalt}`) + const comparePassword = md5(`${password}${user.psalt}`); if (user.password !== comparePassword) - throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD) + throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); } async loginLog(uid: number, ip: string, ua: string) { - await this.loginLogService.create(uid, ip, ua) + await this.loginLogService.create(uid, ip, ua); } async logout(uid: number) { // 删除token - await this.userService.forbidden(uid) + await this.userService.forbidden(uid); } /** * 重置密码 */ async resetPassword(username: string, password: string) { - const user = await this.userService.findUserByUserName(username) + const user = await this.userService.findUserByUserName(username); - await this.userService.forceUpdatePassword(user.id, password) + await this.userService.forceUpdatePassword(user.id, password); } /** * 清除登录状态信息 */ async clearLoginStatus(uid: number): Promise { - await this.userService.forbidden(uid) + await this.userService.forbidden(uid); } /** * 获取菜单列表 */ async getMenus(uid: number): Promise { - return this.menuService.getMenus(uid) + return this.menuService.getMenus(uid); } /** * 获取权限列表 */ async getPermissions(uid: number): Promise { - return this.menuService.getPermissions(uid) + return this.menuService.getPermissions(uid); } async getPermissionsCache(uid: number): Promise { - const permissionString = await this.redis.get(genAuthPermKey(uid)) - return permissionString ? JSON.parse(permissionString) : [] + const permissionString = await this.redis.get(genAuthPermKey(uid)); + return permissionString ? JSON.parse(permissionString) : []; } async setPermissionsCache(uid: number, permissions: string[]): Promise { - await this.redis.set(genAuthPermKey(uid), JSON.stringify(permissions)) + await this.redis.set(genAuthPermKey(uid), JSON.stringify(permissions)); } async getPasswordVersionByUid(uid: number): Promise { - return this.redis.get(genAuthPVKey(uid)) + return this.redis.get(genAuthPVKey(uid)); } async getTokenByUid(uid: number): Promise { - return this.redis.get(genAuthTokenKey(uid)) + return this.redis.get(genAuthTokenKey(uid)); } } diff --git a/src/modules/auth/controllers/account.controller.ts b/src/modules/auth/controllers/account.controller.ts index d6072a1..ec3a4f9 100644 --- a/src/modules/auth/controllers/account.controller.ts +++ b/src/modules/auth/controllers/account.controller.ts @@ -1,19 +1,19 @@ -import { Body, Controller, Get, Post, Put, UseGuards } from '@nestjs/common' -import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Get, Post, Put, UseGuards } from '@nestjs/common'; +import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' +import { ApiResult } from '~/common/decorators/api-result.decorator'; -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { AllowAnon } from '~/modules/auth/decorators/allow-anon.decorator' -import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { AllowAnon } from '~/modules/auth/decorators/allow-anon.decorator'; +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator'; -import { PasswordUpdateDto } from '~/modules/user/dto/password.dto' +import { PasswordUpdateDto } from '~/modules/user/dto/password.dto'; -import { AccountInfo } from '../../user/user.model' -import { UserService } from '../../user/user.service' -import { AuthService } from '../auth.service' -import { AccountMenus, AccountUpdateDto } from '../dto/account.dto' -import { JwtAuthGuard } from '../guards/jwt-auth.guard' +import { AccountInfo } from '../../user/user.model'; +import { UserService } from '../../user/user.service'; +import { AuthService } from '../auth.service'; +import { AccountMenus, AccountUpdateDto } from '../dto/account.dto'; +import { JwtAuthGuard } from '../guards/jwt-auth.guard'; @ApiTags('Account - 账户模块') @ApiSecurityAuth() @@ -23,7 +23,7 @@ import { JwtAuthGuard } from '../guards/jwt-auth.guard' export class AccountController { constructor( private userService: UserService, - private authService: AuthService, + private authService: AuthService ) {} @Get('profile') @@ -31,14 +31,14 @@ export class AccountController { @ApiResult({ type: AccountInfo }) @AllowAnon() async profile(@AuthUser() user: IAuthUser): Promise { - return this.userService.getAccountInfo(user.uid) + return this.userService.getAccountInfo(user.uid); } @Get('logout') @ApiOperation({ summary: '账户登出' }) @AllowAnon() async logout(@AuthUser() user: IAuthUser): Promise { - await this.authService.clearLoginStatus(user.uid) + await this.authService.clearLoginStatus(user.uid); } @Get('menus') @@ -46,7 +46,7 @@ export class AccountController { @ApiResult({ type: [AccountMenus] }) @AllowAnon() async menu(@AuthUser() user: IAuthUser): Promise { - return this.authService.getMenus(user.uid) + return this.authService.getMenus(user.uid); } @Get('permissions') @@ -54,22 +54,20 @@ export class AccountController { @ApiResult({ type: [String] }) @AllowAnon() async permissions(@AuthUser() user: IAuthUser): Promise { - return this.authService.getPermissions(user.uid) + return this.authService.getPermissions(user.uid); } @Put('update') @ApiOperation({ summary: '更改账户资料' }) @AllowAnon() - async update( - @AuthUser() user: IAuthUser, @Body() dto: AccountUpdateDto): Promise { - await this.userService.updateAccountInfo(user.uid, dto) + async update(@AuthUser() user: IAuthUser, @Body() dto: AccountUpdateDto): Promise { + await this.userService.updateAccountInfo(user.uid, dto); } @Post('password') @ApiOperation({ summary: '更改账户密码' }) @AllowAnon() - async password( - @AuthUser() user: IAuthUser, @Body() dto: PasswordUpdateDto): Promise { - await this.userService.updatePassword(user.uid, dto) + async password(@AuthUser() user: IAuthUser, @Body() dto: PasswordUpdateDto): Promise { + await this.userService.updatePassword(user.uid, dto); } } diff --git a/src/modules/auth/controllers/captcha.controller.ts b/src/modules/auth/controllers/captcha.controller.ts index 154236c..4200db0 100644 --- a/src/modules/auth/controllers/captcha.controller.ts +++ b/src/modules/auth/controllers/captcha.controller.ts @@ -1,19 +1,19 @@ -import { InjectRedis } from '@liaoliaots/nestjs-redis' -import { Controller, Get, Query } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { InjectRedis } from '@liaoliaots/nestjs-redis'; +import { Controller, Get, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import Redis from 'ioredis' -import { isEmpty } from 'lodash' -import * as svgCaptcha from 'svg-captcha' +import Redis from 'ioredis'; +import { isEmpty } from 'lodash'; +import * as svgCaptcha from 'svg-captcha'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { genCaptchaImgKey } from '~/helper/genRedisKey' -import { generateUUID } from '~/utils' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { genCaptchaImgKey } from '~/helper/genRedisKey'; +import { generateUUID } from '~/utils'; -import { Public } from '../decorators/public.decorator' +import { Public } from '../decorators/public.decorator'; -import { ImageCaptchaDto } from '../dto/captcha.dto' -import { ImageCaptcha } from '../models/auth.model' +import { ImageCaptchaDto } from '../dto/captcha.dto'; +import { ImageCaptcha } from '../models/auth.model'; @ApiTags('Captcha - 验证码模块') // @UseGuards(ThrottlerGuard) @@ -27,7 +27,7 @@ export class CaptchaController { @Public() // @Throttle({ default: { limit: 2, ttl: 600000 } }) async captchaByImg(@Query() dto: ImageCaptchaDto): Promise { - const { width, height } = dto + const { width, height } = dto; const svg = svgCaptcha.create({ size: 4, @@ -36,15 +36,13 @@ export class CaptchaController { width: isEmpty(width) ? 100 : width, height: isEmpty(height) ? 50 : height, charPreset: '1234567890', - }) + }); const result = { - img: `data:image/svg+xml;base64,${Buffer.from(svg.data).toString( - 'base64', - )}`, + img: `data:image/svg+xml;base64,${Buffer.from(svg.data).toString('base64')}`, id: generateUUID(), - } + }; // 5分钟过期时间 - await this.redis.set(genCaptchaImgKey(result.id), svg.text, 'EX', 60 * 5) - return result + await this.redis.set(genCaptchaImgKey(result.id), svg.text, 'EX', 60 * 5); + return result; } } diff --git a/src/modules/auth/controllers/email.controller.ts b/src/modules/auth/controllers/email.controller.ts index 99ce81a..f0fb6c4 100644 --- a/src/modules/auth/controllers/email.controller.ts +++ b/src/modules/auth/controllers/email.controller.ts @@ -1,15 +1,15 @@ -import { Body, Controller, Post, UseGuards } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Post, UseGuards } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { Throttle, ThrottlerGuard } from '@nestjs/throttler' +import { Throttle, ThrottlerGuard } from '@nestjs/throttler'; -import { Ip } from '~/common/decorators/http.decorator' +import { Ip } from '~/common/decorators/http.decorator'; -import { MailerService } from '~/shared/mailer/mailer.service' +import { MailerService } from '~/shared/mailer/mailer.service'; -import { Public } from '../decorators/public.decorator' +import { Public } from '../decorators/public.decorator'; -import { SendEmailCodeDto } from '../dto/captcha.dto' +import { SendEmailCodeDto } from '../dto/captcha.dto'; @ApiTags('Auth - 认证模块') @UseGuards(ThrottlerGuard) @@ -21,17 +21,14 @@ export class EmailController { @ApiOperation({ summary: '发送邮箱验证码' }) @Public() @Throttle({ default: { limit: 2, ttl: 600000 } }) - async sendEmailCode( - @Body() dto: SendEmailCodeDto, - @Ip() ip: string, - ): Promise { + async sendEmailCode(@Body() dto: SendEmailCodeDto, @Ip() ip: string): Promise { // await this.authService.checkImgCaptcha(dto.captchaId, dto.verifyCode); - const { email } = dto + const { email } = dto; - await this.mailerService.checkLimit(email, ip) - const { code } = await this.mailerService.sendVerificationCode(email) + await this.mailerService.checkLimit(email, ip); + const { code } = await this.mailerService.sendVerificationCode(email); - await this.mailerService.log(email, code, ip) + await this.mailerService.log(email, code, ip); } // @Post() diff --git a/src/modules/auth/decorators/allow-anon.decorator.ts b/src/modules/auth/decorators/allow-anon.decorator.ts index 21954f6..b70dfac 100644 --- a/src/modules/auth/decorators/allow-anon.decorator.ts +++ b/src/modules/auth/decorators/allow-anon.decorator.ts @@ -1,8 +1,8 @@ -import { SetMetadata } from '@nestjs/common' +import { SetMetadata } from '@nestjs/common'; -import { ALLOW_ANON_KEY } from '../auth.constant' +import { ALLOW_ANON_KEY } from '../auth.constant'; /** * 当接口不需要检测用户是否具有操作权限时添加该装饰器 */ -export const AllowAnon = () => SetMetadata(ALLOW_ANON_KEY, true) +export const AllowAnon = () => SetMetadata(ALLOW_ANON_KEY, true); diff --git a/src/modules/auth/decorators/auth-user.decorator.ts b/src/modules/auth/decorators/auth-user.decorator.ts index ef0733d..bac65da 100644 --- a/src/modules/auth/decorators/auth-user.decorator.ts +++ b/src/modules/auth/decorators/auth-user.decorator.ts @@ -1,17 +1,15 @@ -import { ExecutionContext, createParamDecorator } from '@nestjs/common' -import { FastifyRequest } from 'fastify' +import { ExecutionContext, createParamDecorator } from '@nestjs/common'; +import { FastifyRequest } from 'fastify'; -type Payload = keyof IAuthUser +type Payload = keyof IAuthUser; /** * @description 获取当前登录用户信息, 并挂载到request上 */ -export const AuthUser = createParamDecorator( - (data: Payload, ctx: ExecutionContext) => { - const request = ctx.switchToHttp().getRequest() - // auth guard will mount this - const user = request.user as IAuthUser +export const AuthUser = createParamDecorator((data: Payload, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + // auth guard will mount this + const user = request.user as IAuthUser; - return data ? user?.[data] : user - }, -) + return data ? user?.[data] : user; +}); diff --git a/src/modules/auth/decorators/permission.decorator.ts b/src/modules/auth/decorators/permission.decorator.ts index 943679f..e88096c 100644 --- a/src/modules/auth/decorators/permission.decorator.ts +++ b/src/modules/auth/decorators/permission.decorator.ts @@ -1,23 +1,23 @@ -import { SetMetadata, applyDecorators } from '@nestjs/common' +import { SetMetadata, applyDecorators } from '@nestjs/common'; -import { isPlainObject } from 'lodash' +import { isPlainObject } from 'lodash'; -import { PERMISSION_KEY } from '../auth.constant' +import { PERMISSION_KEY } from '../auth.constant'; - type TupleToObject> = { - [K in Uppercase]: `${T}:${Lowercase}` - } - type AddPrefixToObjectValue> = { - [K in keyof P]: K extends string ? `${T}:${P[K]}` : never - } +type TupleToObject> = { + [K in Uppercase]: `${T}:${Lowercase}`; +}; +type AddPrefixToObjectValue> = { + [K in keyof P]: K extends string ? `${T}:${P[K]}` : never; +}; /** 资源操作需要特定的权限 */ export function Perm(permission: string | string[]) { - return applyDecorators(SetMetadata(PERMISSION_KEY, permission)) + return applyDecorators(SetMetadata(PERMISSION_KEY, permission)); } /** (此举非必需)保存通过 definePermission 定义的所有权限,可用于前端开发人员开发阶段的 ts 类型提示,避免前端权限定义与后端定义不匹配 */ -let permissions: string[] = [] +let permissions: string[] = []; /** * 定义权限,同时收集所有被定义的权限 * @@ -33,26 +33,31 @@ let permissions: string[] = [] * definePermission('app:health', ['network']); * ``` */ -export function definePermission>(modulePrefix: T, actionMap: U): AddPrefixToObjectValue -export function definePermission>(modulePrefix: T, actions: U): TupleToObject +export function definePermission>( + modulePrefix: T, + actionMap: U +): AddPrefixToObjectValue; +export function definePermission>( + modulePrefix: T, + actions: U +): TupleToObject; export function definePermission(modulePrefix: string, actions) { if (isPlainObject(actions)) { Object.entries(actions).forEach(([key, action]) => { - actions[key] = `${modulePrefix}:${action}` - }) - permissions = [...new Set([...permissions, ...Object.values(actions)])] - return actions - } - else if (Array.isArray(actions)) { - const permissionFormats = actions.map(action => `${modulePrefix}:${action}`) - permissions = [...new Set([...permissions, ...permissionFormats])] + actions[key] = `${modulePrefix}:${action}`; + }); + permissions = [...new Set([...permissions, ...Object.values(actions)])]; + return actions; + } else if (Array.isArray(actions)) { + const permissionFormats = actions.map(action => `${modulePrefix}:${action}`); + permissions = [...new Set([...permissions, ...permissionFormats])]; return actions.reduce((prev, action) => { - prev[action.toUpperCase()] = `${modulePrefix}:${action}` - return prev - }, {}) + prev[action.toUpperCase()] = `${modulePrefix}:${action}`; + return prev; + }, {}); } } /** 获取所有通过 definePermission 定义的权限 */ -export const getDefinePermissions = () => permissions +export const getDefinePermissions = () => permissions; diff --git a/src/modules/auth/decorators/public.decorator.ts b/src/modules/auth/decorators/public.decorator.ts index c3409ca..b9592f2 100644 --- a/src/modules/auth/decorators/public.decorator.ts +++ b/src/modules/auth/decorators/public.decorator.ts @@ -1,8 +1,8 @@ -import { SetMetadata } from '@nestjs/common' +import { SetMetadata } from '@nestjs/common'; -import { PUBLIC_KEY } from '../auth.constant' +import { PUBLIC_KEY } from '../auth.constant'; /** * 当接口不需要检测用户登录时添加该装饰器 */ -export const Public = () => SetMetadata(PUBLIC_KEY, true) +export const Public = () => SetMetadata(PUBLIC_KEY, true); diff --git a/src/modules/auth/decorators/resource.decorator.ts b/src/modules/auth/decorators/resource.decorator.ts index 73143b0..6734598 100644 --- a/src/modules/auth/decorators/resource.decorator.ts +++ b/src/modules/auth/decorators/resource.decorator.ts @@ -1,12 +1,22 @@ -import { SetMetadata, applyDecorators } from '@nestjs/common' +import { SetMetadata, applyDecorators } from '@nestjs/common'; -import { ObjectLiteral, ObjectType, Repository } from 'typeorm' +import { ObjectLiteral, ObjectType, Repository } from 'typeorm'; -import { RESOURCE_KEY } from '../auth.constant' +import { RESOURCE_KEY } from '../auth.constant'; -export type Condition = (Repository: Repository, items: number[], user: IAuthUser) => Promise +export type Condition = ( + Repository: Repository, + items: number[], + user: IAuthUser +) => Promise; -export interface ResourceObject { entity: ObjectType, condition: Condition } -export function Resource(entity: ObjectType, condition?: Condition) { - return applyDecorators(SetMetadata(RESOURCE_KEY, { entity, condition })) +export interface ResourceObject { + entity: ObjectType; + condition: Condition; +} +export function Resource( + entity: ObjectType, + condition?: Condition +) { + return applyDecorators(SetMetadata(RESOURCE_KEY, { entity, condition })); } diff --git a/src/modules/auth/dto/account.dto.ts b/src/modules/auth/dto/account.dto.ts index 8b3a5ee..e5b73e5 100644 --- a/src/modules/auth/dto/account.dto.ts +++ b/src/modules/auth/dto/account.dto.ts @@ -1,24 +1,17 @@ -import { ApiProperty, OmitType, PartialType, PickType } from '@nestjs/swagger' -import { - IsEmail, - IsOptional, - IsString, - Matches, - MaxLength, - MinLength, -} from 'class-validator' +import { ApiProperty, OmitType, PartialType, PickType } from '@nestjs/swagger'; +import { IsEmail, IsOptional, IsString, Matches, MaxLength, MinLength } from 'class-validator'; -import { MenuEntity } from '~/modules/system/menu/menu.entity' +import { MenuEntity } from '~/modules/system/menu/menu.entity'; export class AccountUpdateDto { @ApiProperty({ description: '用户呢称' }) @IsString() @IsOptional() - nickname: string + nickname: string; @ApiProperty({ description: '用户邮箱' }) @IsEmail() - email: string + email: string; @ApiProperty({ description: '用户QQ' }) @IsOptional() @@ -26,39 +19,54 @@ export class AccountUpdateDto { @Matches(/^[0-9]+$/) @MinLength(5) @MaxLength(11) - qq: string + qq: string; @ApiProperty({ description: '用户手机号' }) @IsOptional() @IsString() - phone: string + phone: string; @ApiProperty({ description: '用户头像' }) @IsOptional() @IsString() - avatar: string + avatar: string; @ApiProperty({ description: '用户备注' }) @IsOptional() @IsString() - remark: string + remark: string; } export class ResetPasswordDto { @ApiProperty({ description: '临时token', example: 'uuid' }) @IsString() - accessToken: string + accessToken: string; @ApiProperty({ description: '密码', example: 'a123456' }) @IsString() @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/) @MinLength(6) - password: string + password: string; } -export class MenuMeta extends PartialType(OmitType(MenuEntity, ['parentId', 'createdAt', 'updatedAt', 'id', 'roles', 'path', 'name'] as const)) { - title: string +export class MenuMeta extends PartialType( + OmitType(MenuEntity, [ + 'parentId', + 'createdAt', + 'updatedAt', + 'id', + 'roles', + 'path', + 'name', + ] as const) +) { + title: string; } -export class AccountMenus extends PickType(MenuEntity, ['id', 'path', 'name', 'component'] as const) { - meta: MenuMeta +export class AccountMenus extends PickType(MenuEntity, [ + 'id', + 'path', + 'name', + 'component', +] as const) { + meta: MenuMeta; } diff --git a/src/modules/auth/dto/auth.dto.ts b/src/modules/auth/dto/auth.dto.ts index abaf379..8cce9d0 100644 --- a/src/modules/auth/dto/auth.dto.ts +++ b/src/modules/auth/dto/auth.dto.ts @@ -1,43 +1,43 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; -import { IsString, Matches, MaxLength, MinLength } from 'class-validator' +import { IsString, Matches, MaxLength, MinLength } from 'class-validator'; export class LoginDto { @ApiProperty({ description: '手机号/邮箱' }) @IsString() @MinLength(4) - username: string + username: string; @ApiProperty({ description: '密码', example: 'a123456' }) @IsString() @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/) @MinLength(6) - password: string + password: string; @ApiProperty({ description: '验证码标识' }) @IsString() - captchaId: string + captchaId: string; @ApiProperty({ description: '用户输入的验证码' }) @IsString() @MinLength(4) @MaxLength(4) - verifyCode: string + verifyCode: string; } export class RegisterDto { @ApiProperty({ description: '账号' }) @IsString() - username: string + username: string; @ApiProperty({ description: '密码' }) @IsString() @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/) @MinLength(6) @MaxLength(16) - password: string + password: string; @ApiProperty({ description: '语言', examples: ['EN', 'ZH'] }) @IsString() - lang: string + lang: string; } diff --git a/src/modules/auth/dto/captcha.dto.ts b/src/modules/auth/dto/captcha.dto.ts index c24d3d8..8f8c0bf 100644 --- a/src/modules/auth/dto/captcha.dto.ts +++ b/src/modules/auth/dto/captcha.dto.ts @@ -1,12 +1,6 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Type } from 'class-transformer' -import { - IsEmail, - IsInt, - IsMobilePhone, - IsOptional, - IsString, -} from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsEmail, IsInt, IsMobilePhone, IsOptional, IsString } from 'class-validator'; export class ImageCaptchaDto { @ApiProperty({ @@ -17,7 +11,7 @@ export class ImageCaptchaDto { @Type(() => Number) @IsInt() @IsOptional() - readonly width: number = 100 + readonly width: number = 100; @ApiProperty({ required: false, @@ -27,27 +21,27 @@ export class ImageCaptchaDto { @Type(() => Number) @IsInt() @IsOptional() - readonly height: number = 50 + readonly height: number = 50; } export class SendEmailCodeDto { @ApiProperty({ description: '邮箱' }) @IsEmail({}, { message: '邮箱格式不正确' }) - email: string + email: string; } export class SendSmsCodeDto { @ApiProperty({ description: '手机号' }) @IsMobilePhone('zh-CN', {}, { message: '手机号格式不正确' }) - phone: string + phone: string; } export class CheckCodeDto { @ApiProperty({ description: '手机号/邮箱' }) @IsString() - account: string + account: string; @ApiProperty({ description: '验证码' }) @IsString() - code: string + code: string; } diff --git a/src/modules/auth/entities/access-token.entity.ts b/src/modules/auth/entities/access-token.entity.ts index d56afc8..e57a142 100644 --- a/src/modules/auth/entities/access-token.entity.ts +++ b/src/modules/auth/entities/access-token.entity.ts @@ -7,34 +7,34 @@ import { ManyToOne, OneToOne, PrimaryGeneratedColumn, -} from 'typeorm' +} from 'typeorm'; -import { UserEntity } from '~/modules/user/user.entity' +import { UserEntity } from '~/modules/user/user.entity'; -import { RefreshTokenEntity } from './refresh-token.entity' +import { RefreshTokenEntity } from './refresh-token.entity'; @Entity('user_access_tokens') export class AccessTokenEntity extends BaseEntity { @PrimaryGeneratedColumn('uuid') - id!: string + id!: string; @Column({ length: 500 }) - value!: string + value!: string; @Column({ comment: '令牌过期时间' }) - expired_at!: Date + expired_at!: Date; @CreateDateColumn({ comment: '令牌创建时间' }) - created_at!: Date + created_at!: Date; @OneToOne(() => RefreshTokenEntity, refreshToken => refreshToken.accessToken, { cascade: true, }) - refreshToken!: RefreshTokenEntity + refreshToken!: RefreshTokenEntity; @ManyToOne(() => UserEntity, user => user.accessTokens, { onDelete: 'CASCADE', }) @JoinColumn({ name: 'user_id' }) - user!: UserEntity + user!: UserEntity; } diff --git a/src/modules/auth/entities/refresh-token.entity.ts b/src/modules/auth/entities/refresh-token.entity.ts index cec190b..b511922 100644 --- a/src/modules/auth/entities/refresh-token.entity.ts +++ b/src/modules/auth/entities/refresh-token.entity.ts @@ -6,27 +6,27 @@ import { JoinColumn, OneToOne, PrimaryGeneratedColumn, -} from 'typeorm' +} from 'typeorm'; -import { AccessTokenEntity } from './access-token.entity' +import { AccessTokenEntity } from './access-token.entity'; @Entity('user_refresh_tokens') export class RefreshTokenEntity extends BaseEntity { @PrimaryGeneratedColumn('uuid') - id!: string + id!: string; @Column({ length: 500 }) - value!: string + value!: string; @Column({ comment: '令牌过期时间' }) - expired_at!: Date + expired_at!: Date; @CreateDateColumn({ comment: '令牌创建时间' }) - created_at!: Date + created_at!: Date; @OneToOne(() => AccessTokenEntity, accessToken => accessToken.refreshToken, { onDelete: 'CASCADE', }) @JoinColumn() - accessToken!: AccessTokenEntity + accessToken!: AccessTokenEntity; } diff --git a/src/modules/auth/guards/jwt-auth.guard.ts b/src/modules/auth/guards/jwt-auth.guard.ts index 0cbe88c..052c87b 100644 --- a/src/modules/auth/guards/jwt-auth.guard.ts +++ b/src/modules/auth/guards/jwt-auth.guard.ts @@ -1,21 +1,17 @@ -import { - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common' -import { Reflector } from '@nestjs/core' -import { AuthGuard } from '@nestjs/passport' -import { FastifyRequest } from 'fastify' -import { isEmpty, isNil } from 'lodash' +import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { FastifyRequest } from 'fastify'; +import { isEmpty, isNil } from 'lodash'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { AuthService } from '~/modules/auth/auth.service' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { AuthService } from '~/modules/auth/auth.service'; -import { checkIsDemoMode } from '~/utils' +import { checkIsDemoMode } from '~/utils'; -import { AuthStrategy, PUBLIC_KEY } from '../auth.constant' -import { TokenService } from '../services/token.service' +import { AuthStrategy, PUBLIC_KEY } from '../auth.constant'; +import { TokenService } from '../services/token.service'; // https://docs.nestjs.com/recipes/passport#implement-protected-route-and-jwt-strategy-guards @Injectable() @@ -23,66 +19,60 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) { constructor( private reflector: Reflector, private authService: AuthService, - private tokenService: TokenService, + private tokenService: TokenService ) { - super() + super(); } async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ context.getHandler(), context.getClass(), - ]) - const request = context.switchToHttp().getRequest() + ]); + const request = context.switchToHttp().getRequest(); // const response = context.switchToHttp().getResponse() // TODO 此处代码的作用是判断如果在演示环境下,则拒绝用户的增删改操作,去掉此代码不影响正常的业务逻辑 - if (request.method !== 'GET' && !request.url.includes('/auth/login')) - checkIsDemoMode() + if (request.method !== 'GET' && !request.url.includes('/auth/login')) checkIsDemoMode(); - const isSse = request.headers.accept === 'text/event-stream' + const isSse = request.headers.accept === 'text/event-stream'; if (isSse && !request.headers.authorization?.startsWith('Bearer')) { - const { token } = request.query as Record - if (token) - request.headers.authorization = `Bearer ${token}` + const { token } = request.query as Record; + if (token) request.headers.authorization = `Bearer ${token}`; } - const Authorization = request.headers.authorization + const Authorization = request.headers.authorization; - let result: any = false + let result: any = false; try { - result = await super.canActivate(context) - } - catch (e) { + result = await super.canActivate(context); + } catch (e) { // 需要后置判断 这样携带了 token 的用户就能够解析到 request.user - if (isPublic) - return true + if (isPublic) return true; - if (isEmpty(Authorization)) - throw new UnauthorizedException('未登录') + if (isEmpty(Authorization)) throw new UnauthorizedException('未登录'); // 判断 token 是否存在, 如果不存在则认证失败 const accessToken = isNil(Authorization) ? undefined - : await this.tokenService.checkAccessToken(Authorization!) + : await this.tokenService.checkAccessToken(Authorization!); - if (!accessToken) - throw new UnauthorizedException('令牌无效') + if (!accessToken) throw new UnauthorizedException('令牌无效'); } // SSE 请求 if (isSse) { - const { uid } = request.params as Record + const { uid } = request.params as Record; if (Number(uid) !== request.user.uid) - throw new UnauthorizedException('路径参数 uid 与当前 token 登录的用户 uid 不一致') + throw new UnauthorizedException('路径参数 uid 与当前 token 登录的用户 uid 不一致'); } - const pv = await this.authService.getPasswordVersionByUid(request.user.uid) + const pv = await this.authService.getPasswordVersionByUid(request.user.uid); if (pv !== `${request.user.pv}`) { // 密码版本不一致,登录期间已更改过密码 - throw new BusinessException(ErrorEnum.INVALID_LOGIN) + throw new BusinessException(ErrorEnum.INVALID_LOGIN); } // 不允许多端登录 @@ -92,14 +82,13 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) { // throw new ApiException(ErrorEnum.CODE_1106); // } - return result + return result; } handleRequest(err, user, info) { // You can throw an exception based on either "info" or "err" arguments - if (err || !user) - throw err || new UnauthorizedException() + if (err || !user) throw err || new UnauthorizedException(); - return user + return user; } } diff --git a/src/modules/auth/guards/local.guard.ts b/src/modules/auth/guards/local.guard.ts index c2de171..2bcaca9 100644 --- a/src/modules/auth/guards/local.guard.ts +++ b/src/modules/auth/guards/local.guard.ts @@ -1,11 +1,11 @@ -import { ExecutionContext, Injectable } from '@nestjs/common' -import { AuthGuard } from '@nestjs/passport' +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; -import { AuthStrategy } from '../auth.constant' +import { AuthStrategy } from '../auth.constant'; @Injectable() export class LocalGuard extends AuthGuard(AuthStrategy.LOCAL) { async canActivate(context: ExecutionContext) { - return true + return true; } } diff --git a/src/modules/auth/guards/rbac.guard.ts b/src/modules/auth/guards/rbac.guard.ts index 8545adb..408a747 100644 --- a/src/modules/auth/guards/rbac.guard.ts +++ b/src/modules/auth/guards/rbac.guard.ts @@ -1,76 +1,64 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common' -import { Reflector } from '@nestjs/core' -import { FastifyRequest } from 'fastify' +import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { FastifyRequest } from 'fastify'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { AuthService } from '~/modules/auth/auth.service' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { AuthService } from '~/modules/auth/auth.service'; -import { ALLOW_ANON_KEY, PERMISSION_KEY, PUBLIC_KEY, Roles } from '../auth.constant' +import { ALLOW_ANON_KEY, PERMISSION_KEY, PUBLIC_KEY, Roles } from '../auth.constant'; @Injectable() export class RbacGuard implements CanActivate { constructor( private reflector: Reflector, - private authService: AuthService, + private authService: AuthService ) {} async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ context.getHandler(), context.getClass(), - ]) + ]); - if (isPublic) - return true + if (isPublic) return true; - const request = context.switchToHttp().getRequest() + const request = context.switchToHttp().getRequest(); - const { user } = request - if (!user) - throw new UnauthorizedException('登录无效') + const { user } = request; + if (!user) throw new UnauthorizedException('登录无效'); // allowAnon 是需要登录后可访问(无需权限), Public 则是无需登录也可访问. - const allowAnon = this.reflector.get( - ALLOW_ANON_KEY, - context.getHandler(), - ) - if (allowAnon) - return true + const allowAnon = this.reflector.get(ALLOW_ANON_KEY, context.getHandler()); + if (allowAnon) return true; - const payloadPermission = this.reflector.getAllAndOverride< - string | string[] - >(PERMISSION_KEY, [context.getHandler(), context.getClass()]) + const payloadPermission = this.reflector.getAllAndOverride(PERMISSION_KEY, [ + context.getHandler(), + context.getClass(), + ]); // 控制器没有设置接口权限,则默认通过 - if (!payloadPermission) - return true + if (!payloadPermission) return true; // 管理员放开所有权限 - if (user.roles.includes(Roles.ADMIN)) - return true + if (user.roles.includes(Roles.ADMIN)) return true; - const allPermissions = await this.authService.getPermissionsCache(user.uid) ?? await this.authService.getPermissions(user.uid) + const allPermissions = + (await this.authService.getPermissionsCache(user.uid)) ?? + (await this.authService.getPermissions(user.uid)); // console.log(allPermissions) - let canNext = false + let canNext = false; // handle permission strings if (Array.isArray(payloadPermission)) { // 只要有一个权限满足即可 - canNext = payloadPermission.every(i => allPermissions.includes(i)) + canNext = payloadPermission.every(i => allPermissions.includes(i)); } - if (typeof payloadPermission === 'string') - canNext = allPermissions.includes(payloadPermission) + if (typeof payloadPermission === 'string') canNext = allPermissions.includes(payloadPermission); - if (!canNext) - throw new BusinessException(ErrorEnum.NO_PERMISSION) + if (!canNext) throw new BusinessException(ErrorEnum.NO_PERMISSION); - return true + return true; } } diff --git a/src/modules/auth/guards/resource.guard.ts b/src/modules/auth/guards/resource.guard.ts index b78da0c..db45299 100644 --- a/src/modules/auth/guards/resource.guard.ts +++ b/src/modules/auth/guards/resource.guard.ts @@ -1,72 +1,67 @@ -import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common' -import { Reflector } from '@nestjs/core' -import { FastifyRequest } from 'fastify' +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { FastifyRequest } from 'fastify'; -import { isArray, isEmpty, isNil } from 'lodash' +import { isArray, isEmpty, isNil } from 'lodash'; -import { DataSource, In, Repository } from 'typeorm' +import { DataSource, In, Repository } from 'typeorm'; -import { BusinessException } from '~/common/exceptions/biz.exception' +import { BusinessException } from '~/common/exceptions/biz.exception'; -import { ErrorEnum } from '~/constants/error-code.constant' +import { ErrorEnum } from '~/constants/error-code.constant'; -import { PUBLIC_KEY, RESOURCE_KEY, Roles } from '../auth.constant' -import { ResourceObject } from '../decorators/resource.decorator' +import { PUBLIC_KEY, RESOURCE_KEY, Roles } from '../auth.constant'; +import { ResourceObject } from '../decorators/resource.decorator'; @Injectable() export class ResourceGuard implements CanActivate { constructor( private reflector: Reflector, - private dataSource: DataSource, + private dataSource: DataSource ) {} async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ context.getHandler(), context.getClass(), - ]) + ]); - const request = context.switchToHttp().getRequest() - const isSse = request.headers.accept === 'text/event-stream' + const request = context.switchToHttp().getRequest(); + const isSse = request.headers.accept === 'text/event-stream'; // 忽略 sse 请求 - if (isPublic || isSse) - return true + if (isPublic || isSse) return true; - const { user } = request + const { user } = request; - if (!user) - return false + if (!user) return false; // 如果是检查资源所属,且不是超级管理员,还需要进一步判断是否是自己的数据 const { entity, condition } = this.reflector.get( RESOURCE_KEY, - context.getHandler(), - ) ?? { entity: null, condition: null } + context.getHandler() + ) ?? { entity: null, condition: null }; if (entity && !user.roles.includes(Roles.ADMIN)) { - const repo: Repository = this.dataSource.getRepository(entity) + const repo: Repository = this.dataSource.getRepository(entity); /** * 获取请求中的 items (ids) 验证数据拥有者 * @param request */ const getRequestItems = (request?: FastifyRequest): number[] => { - const { params = {}, body = {}, query = {} } = (request ?? {}) as any - const id = params.id ?? body.id ?? query.id + const { params = {}, body = {}, query = {} } = (request ?? {}) as any; + const id = params.id ?? body.id ?? query.id; - if (id) - return [id] + if (id) return [id]; - const { items } = body - return !isNil(items) && isArray(items) ? items : [] - } + const { items } = body; + return !isNil(items) && isArray(items) ? items : []; + }; - const items = getRequestItems(request) - if (isEmpty(items)) - throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND) + const items = getRequestItems(request); + if (isEmpty(items)) throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND); - if (condition) - return condition(repo, items, user) + if (condition) return condition(repo, items, user); const recordQuery = { where: { @@ -74,14 +69,13 @@ export class ResourceGuard implements CanActivate { user: { id: user.uid }, }, relations: ['user'], - } + }; - const records = await repo.find(recordQuery) + const records = await repo.find(recordQuery); - if (isEmpty(records)) - throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND) + if (isEmpty(records)) throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND); } - return true + return true; } } diff --git a/src/modules/auth/models/auth.model.ts b/src/modules/auth/models/auth.model.ts index faeada1..01a5e08 100644 --- a/src/modules/auth/models/auth.model.ts +++ b/src/modules/auth/models/auth.model.ts @@ -1,14 +1,14 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; export class ImageCaptcha { @ApiProperty({ description: 'base64格式的svg图片' }) - img: string + img: string; @ApiProperty({ description: '验证码对应的唯一ID' }) - id: string + id: string; } export class LoginToken { @ApiProperty({ description: 'JWT身份Token' }) - token: string + token: string; } diff --git a/src/modules/auth/services/captcha.service.ts b/src/modules/auth/services/captcha.service.ts index 612a6b7..f7ddfc0 100644 --- a/src/modules/auth/services/captcha.service.ts +++ b/src/modules/auth/services/captcha.service.ts @@ -1,40 +1,35 @@ -import { InjectRedis } from '@liaoliaots/nestjs-redis' -import { Injectable } from '@nestjs/common' +import { InjectRedis } from '@liaoliaots/nestjs-redis'; +import { Injectable } from '@nestjs/common'; -import Redis from 'ioredis' -import { isEmpty } from 'lodash' +import Redis from 'ioredis'; +import { isEmpty } from 'lodash'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { genCaptchaImgKey } from '~/helper/genRedisKey' -import { CaptchaLogService } from '~/modules/system/log/services/captcha-log.service' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { genCaptchaImgKey } from '~/helper/genRedisKey'; +import { CaptchaLogService } from '~/modules/system/log/services/captcha-log.service'; @Injectable() export class CaptchaService { constructor( @InjectRedis() private redis: Redis, - private captchaLogService: CaptchaLogService, + private captchaLogService: CaptchaLogService ) {} /** * 校验图片验证码 */ async checkImgCaptcha(id: string, code: string): Promise { - const result = await this.redis.get(genCaptchaImgKey(id)) + const result = await this.redis.get(genCaptchaImgKey(id)); if (isEmpty(result) || code.toLowerCase() !== result.toLowerCase()) - throw new BusinessException(ErrorEnum.INVALID_VERIFICATION_CODE) + throw new BusinessException(ErrorEnum.INVALID_VERIFICATION_CODE); // 校验成功后移除验证码 - await this.redis.del(genCaptchaImgKey(id)) + await this.redis.del(genCaptchaImgKey(id)); } - async log( - account: string, - code: string, - provider: 'sms' | 'email', - uid?: number, - ): Promise { - await this.captchaLogService.create(account, code, provider, uid) + async log(account: string, code: string, provider: 'sms' | 'email', uid?: number): Promise { + await this.captchaLogService.create(account, code, provider, uid); } } diff --git a/src/modules/auth/services/token.service.ts b/src/modules/auth/services/token.service.ts index e680ecc..786090f 100644 --- a/src/modules/auth/services/token.service.ts +++ b/src/modules/auth/services/token.service.ts @@ -1,14 +1,14 @@ -import { Inject, Injectable } from '@nestjs/common' -import { JwtService } from '@nestjs/jwt' -import dayjs from 'dayjs' +import { Inject, Injectable } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import dayjs from 'dayjs'; -import { ISecurityConfig, SecurityConfig } from '~/config' -import { RoleService } from '~/modules/system/role/role.service' -import { UserEntity } from '~/modules/user/user.entity' -import { generateUUID } from '~/utils' +import { ISecurityConfig, SecurityConfig } from '~/config'; +import { RoleService } from '~/modules/system/role/role.service'; +import { UserEntity } from '~/modules/user/user.entity'; +import { generateUUID } from '~/utils'; -import { AccessTokenEntity } from '../entities/access-token.entity' -import { RefreshTokenEntity } from '../entities/refresh-token.entity' +import { AccessTokenEntity } from '../entities/access-token.entity'; +import { RefreshTokenEntity } from '../entities/refresh-token.entity'; /** * 令牌服务 @@ -18,7 +18,7 @@ export class TokenService { constructor( private jwtService: JwtService, private roleService: RoleService, - @Inject(SecurityConfig.KEY) private securityConfig: ISecurityConfig, + @Inject(SecurityConfig.KEY) private securityConfig: ISecurityConfig ) {} /** @@ -27,30 +27,29 @@ export class TokenService { * @param response */ async refreshToken(accessToken: AccessTokenEntity) { - const { user, refreshToken } = accessToken + const { user, refreshToken } = accessToken; if (refreshToken) { - const now = dayjs() + const now = dayjs(); // 判断refreshToken是否过期 - if (now.isAfter(refreshToken.expired_at)) - return null + if (now.isAfter(refreshToken.expired_at)) return null; - const roleIds = await this.roleService.getRoleIdsByUser(user.id) - const roleValues = await this.roleService.getRoleValues(roleIds) + const roleIds = await this.roleService.getRoleIdsByUser(user.id); + const roleValues = await this.roleService.getRoleValues(roleIds); // 如果没过期则生成新的access_token和refresh_token - const token = await this.generateAccessToken(user.id, roleValues) + const token = await this.generateAccessToken(user.id, roleValues); - await accessToken.remove() - return token + await accessToken.remove(); + return token; } - return null + return null; } generateJwtSign(payload: any) { - const jwtSign = this.jwtService.sign(payload) + const jwtSign = this.jwtService.sign(payload); - return jwtSign + return jwtSign; } async generateAccessToken(uid: number, roles: string[] = []) { @@ -58,27 +57,25 @@ export class TokenService { uid, pv: 1, roles, - } + }; - const jwtSign = this.jwtService.sign(payload) + const jwtSign = this.jwtService.sign(payload); // 生成accessToken - const accessToken = new AccessTokenEntity() - accessToken.value = jwtSign - accessToken.user = { id: uid } as UserEntity - accessToken.expired_at = dayjs() - .add(this.securityConfig.jwtExprire, 'second') - .toDate() + const accessToken = new AccessTokenEntity(); + accessToken.value = jwtSign; + accessToken.user = { id: uid } as UserEntity; + accessToken.expired_at = dayjs().add(this.securityConfig.jwtExprire, 'second').toDate(); - await accessToken.save() + await accessToken.save(); // 生成refreshToken - const refreshToken = await this.generateRefreshToken(accessToken, dayjs()) + const refreshToken = await this.generateRefreshToken(accessToken, dayjs()); return { accessToken: jwtSign, refreshToken, - } + }; } /** @@ -86,28 +83,23 @@ export class TokenService { * @param accessToken * @param now */ - async generateRefreshToken( - accessToken: AccessTokenEntity, - now: dayjs.Dayjs, - ): Promise { + async generateRefreshToken(accessToken: AccessTokenEntity, now: dayjs.Dayjs): Promise { const refreshTokenPayload = { uuid: generateUUID(), - } + }; const refreshTokenSign = this.jwtService.sign(refreshTokenPayload, { secret: this.securityConfig.refreshSecret, - }) + }); - const refreshToken = new RefreshTokenEntity() - refreshToken.value = refreshTokenSign - refreshToken.expired_at = now - .add(this.securityConfig.refreshExpire, 'second') - .toDate() - refreshToken.accessToken = accessToken + const refreshToken = new RefreshTokenEntity(); + refreshToken.value = refreshTokenSign; + refreshToken.expired_at = now.add(this.securityConfig.refreshExpire, 'second').toDate(); + refreshToken.accessToken = accessToken; - await refreshToken.save() + await refreshToken.save(); - return refreshTokenSign + return refreshTokenSign; } /** @@ -119,7 +111,7 @@ export class TokenService { where: { value }, relations: ['user', 'refreshToken'], cache: true, - }) + }); } /** @@ -129,9 +121,8 @@ export class TokenService { async removeAccessToken(value: string) { const accessToken = await AccessTokenEntity.findOne({ where: { value }, - }) - if (accessToken) - await accessToken.remove() + }); + if (accessToken) await accessToken.remove(); } /** @@ -142,11 +133,10 @@ export class TokenService { const refreshToken = await RefreshTokenEntity.findOne({ where: { value }, relations: ['accessToken'], - }) + }); if (refreshToken) { - if (refreshToken.accessToken) - await refreshToken.accessToken.remove() - await refreshToken.remove() + if (refreshToken.accessToken) await refreshToken.accessToken.remove(); + await refreshToken.remove(); } } @@ -155,6 +145,6 @@ export class TokenService { * @param token */ async verifyAccessToken(token: string): Promise { - return this.jwtService.verify(token) + return this.jwtService.verify(token); } } diff --git a/src/modules/auth/strategies/jwt.strategy.ts b/src/modules/auth/strategies/jwt.strategy.ts index 1fc00fd..caf763b 100644 --- a/src/modules/auth/strategies/jwt.strategy.ts +++ b/src/modules/auth/strategies/jwt.strategy.ts @@ -1,24 +1,22 @@ -import { Inject, Injectable } from '@nestjs/common' -import { PassportStrategy } from '@nestjs/passport' -import { ExtractJwt, Strategy } from 'passport-jwt' +import { Inject, Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; -import { ISecurityConfig, SecurityConfig } from '~/config' +import { ISecurityConfig, SecurityConfig } from '~/config'; -import { AuthStrategy } from '../auth.constant' +import { AuthStrategy } from '../auth.constant'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy, AuthStrategy.JWT) { - constructor( - @Inject(SecurityConfig.KEY) private securityConfig: ISecurityConfig, - ) { + constructor(@Inject(SecurityConfig.KEY) private securityConfig: ISecurityConfig) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: securityConfig.jwtSecret, - }) + }); } async validate(payload: IAuthUser) { - return payload + return payload; } } diff --git a/src/modules/auth/strategies/local.strategy.ts b/src/modules/auth/strategies/local.strategy.ts index 69c59a9..f0c10da 100644 --- a/src/modules/auth/strategies/local.strategy.ts +++ b/src/modules/auth/strategies/local.strategy.ts @@ -1,24 +1,21 @@ -import { Injectable } from '@nestjs/common' -import { PassportStrategy } from '@nestjs/passport' -import { Strategy } from 'passport-local' +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { Strategy } from 'passport-local'; -import { AuthStrategy } from '../auth.constant' -import { AuthService } from '../auth.service' +import { AuthStrategy } from '../auth.constant'; +import { AuthService } from '../auth.service'; @Injectable() -export class LocalStrategy extends PassportStrategy( - Strategy, - AuthStrategy.LOCAL, -) { +export class LocalStrategy extends PassportStrategy(Strategy, AuthStrategy.LOCAL) { constructor(private authService: AuthService) { super({ usernameField: 'credential', passwordField: 'password', - }) + }); } async validate(username: string, password: string): Promise { - const user = await this.authService.validateUser(username, password) - return user + const user = await this.authService.validateUser(username, password); + return user; } } diff --git a/src/modules/contract/contract.controller.ts b/src/modules/contract/contract.controller.ts new file mode 100644 index 0000000..7a20986 --- /dev/null +++ b/src/modules/contract/contract.controller.ts @@ -0,0 +1,63 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { definePermission } from '../auth/decorators/permission.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { ContractService } from './contract.service'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +export const permissions = definePermission('app:contract', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', +} as const); + +@ApiTags('Contract - 合同') +@ApiSecurityAuth() +@Controller('contract') +export class ContractController { + constructor(private menuService: ContractService) {} + + // @Get() + // @ApiOperation({ summary: '获取合同列表' }) + // @ApiResult({ type: [RoleEntity], isPage: true }) + // @Perm(permissions.LIST) + // async list(@Query() dto: RoleQueryDto) { + // return this.roleService.findAll(dto) + // } + + // @Get(':id') + // @ApiOperation({ summary: '获取角色信息' }) + // @ApiResult({ type: RoleInfo }) + // @Perm(permissions.READ) + // async info(@IdParam() id: number) { + // return this.roleService.info(id) + // } + + // @Post() + // @ApiOperation({ summary: '新增角色' }) + // @Perm(permissions.CREATE) + // async create(@Body() dto: RoleDto): Promise { + // await this.roleService.create(dto) + // } + + // @Put(':id') + // @ApiOperation({ summary: '更新角色' }) + // @Perm(permissions.UPDATE) + // async update( + // @IdParam() id: number, @Body() dto: RoleUpdateDto): Promise { + // await this.roleService.update(id, dto) + // await this.menuService.refreshOnlineUserPerms() + // } + + // @Delete(':id') + // @ApiOperation({ summary: '删除角色' }) + // @Perm(permissions.DELETE) + // async delete(@IdParam() id: number): Promise { + // if (await this.roleService.checkUserByRoleId(id)) + // throw new BadRequestException('该角色存在关联用户,无法删除') + + // await this.roleService.delete(id) + // await this.menuService.refreshOnlineUserPerms() + // } +} diff --git a/src/modules/contract/contract.entity.ts b/src/modules/contract/contract.entity.ts new file mode 100644 index 0000000..108c153 --- /dev/null +++ b/src/modules/contract/contract.entity.ts @@ -0,0 +1,39 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; + +@Entity({ name: 'contract' }) +export class RoleEntity extends CommonEntity { + @Column({ length: 100, unique: true, name: 'contract_number' }) + @ApiProperty({ description: '合同编号' }) + contractNumber: string; + + @Column({ length: 255, name: 'title', type: 'varchar' }) + @ApiProperty({ description: '合同标题' }) + title: string; + + @Column() + @ApiProperty({ description: '合同类型', type: 'varchar' }) + type: number; + + @Column({ name: 'partyA', length: 255, type: 'varchar' }) + @ApiProperty({ description: '甲方' }) + partyA: string; + + // @Column({ type: 'tinyint', nullable: true, default: 1 }) + // @ApiProperty({ description: '状态:1启用,0禁用' }) + // status: number + + // @Column({ nullable: true }) + // @ApiProperty({ description: '是否默认用户' }) + // default: boolean + + // @ApiHideProperty() + // @ManyToMany(() => MenuEntity, menu => menu.roles, {}) + // @JoinTable({ + // name: 'sys_role_menus', + // joinColumn: { name: 'role_id', referencedColumnName: 'id' }, + // inverseJoinColumn: { name: 'menu_id', referencedColumnName: 'id' }, + // }) + // menus: Relation +} diff --git a/src/modules/contract/contract.module.ts b/src/modules/contract/contract.module.ts new file mode 100644 index 0000000..2dab2b4 --- /dev/null +++ b/src/modules/contract/contract.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { ContractController } from './contract.controller'; +import { ContractService } from './contract.service'; + +@Module({ + controllers: [ContractController], + providers: [ContractService], +}) +export class ContractModule {} diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts new file mode 100644 index 0000000..87c091d --- /dev/null +++ b/src/modules/contract/contract.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ContractService {} diff --git a/src/modules/health/health.controller.ts b/src/modules/health/health.controller.ts index ad81caa..372e19b 100644 --- a/src/modules/health/health.controller.ts +++ b/src/modules/health/health.controller.ts @@ -1,14 +1,14 @@ -import { Controller, Get } from '@nestjs/common' -import { ApiTags } from '@nestjs/swagger' +import { Controller, Get } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; import { DiskHealthIndicator, HealthCheck, HttpHealthIndicator, MemoryHealthIndicator, TypeOrmHealthIndicator, -} from '@nestjs/terminus' +} from '@nestjs/terminus'; -import { Perm, definePermission } from '../auth/decorators/permission.decorator' +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; export const PermissionHealth = definePermission('app:health', { NETWORK: 'network', @@ -16,7 +16,7 @@ export const PermissionHealth = definePermission('app:health', { MH: 'memory-heap', MR: 'memory-rss', DISK: 'disk', -} as const) +} as const); @ApiTags('Health - 健康检查') @Controller('health') @@ -25,21 +25,21 @@ export class HealthController { private http: HttpHealthIndicator, private db: TypeOrmHealthIndicator, private memory: MemoryHealthIndicator, - private disk: DiskHealthIndicator, + private disk: DiskHealthIndicator ) {} @Get('network') @HealthCheck() @Perm(PermissionHealth.NETWORK) async checkNetwork() { - return this.http.pingCheck('buqiyuan', 'https://buqiyuan.gitee.io/') + return this.http.pingCheck('louis', 'https://gitee.com/lu-zixun'); } @Get('database') @HealthCheck() @Perm(PermissionHealth.DB) async checkDatabase() { - return this.db.pingCheck('database') + return this.db.pingCheck('database'); } @Get('memory-heap') @@ -47,7 +47,7 @@ export class HealthController { @Perm(PermissionHealth.MH) async checkMemoryHeap() { // the process should not use more than 200MB memory - return this.memory.checkHeap('memory-heap', 200 * 1024 * 1024) + return this.memory.checkHeap('memory-heap', 200 * 1024 * 1024); } @Get('memory-rss') @@ -55,7 +55,7 @@ export class HealthController { @Perm(PermissionHealth.MR) async checkMemoryRSS() { // the process should not have more than 200MB RSS memory allocated - return this.memory.checkRSS('memory-rss', 200 * 1024 * 1024) + return this.memory.checkRSS('memory-rss', 200 * 1024 * 1024); } @Get('disk') @@ -66,6 +66,6 @@ export class HealthController { // The used disk storage should not exceed 75% of the full disk size thresholdPercent: 0.75, path: '/', - }) + }); } } diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts index 141fadc..e488987 100644 --- a/src/modules/health/health.module.ts +++ b/src/modules/health/health.module.ts @@ -1,8 +1,8 @@ -import { HttpModule } from '@nestjs/axios' -import { Module } from '@nestjs/common' -import { TerminusModule } from '@nestjs/terminus' +import { HttpModule } from '@nestjs/axios'; +import { Module } from '@nestjs/common'; +import { TerminusModule } from '@nestjs/terminus'; -import { HealthController } from './health.controller' +import { HealthController } from './health.controller'; @Module({ imports: [TerminusModule, HttpModule], diff --git a/src/modules/netdisk/manager/manage.class.ts b/src/modules/netdisk/manager/manage.class.ts index ef774c1..d2dfa0b 100644 --- a/src/modules/netdisk/manager/manage.class.ts +++ b/src/modules/netdisk/manager/manage.class.ts @@ -49,8 +49,7 @@ export class SFileInfoDetail { mimeType: string; @ApiProperty({ - description: - '文件存储类型,2 表示归档存储,1 表示低频存储,0表示普通存储。', + description: '文件存储类型,2 表示归档存储,1 表示低频存储,0表示普通存储。', }) type: number; diff --git a/src/modules/netdisk/manager/manage.controller.ts b/src/modules/netdisk/manager/manage.controller.ts index 652d6aa..58e12e6 100644 --- a/src/modules/netdisk/manager/manage.controller.ts +++ b/src/modules/netdisk/manager/manage.controller.ts @@ -1,19 +1,15 @@ -import { Body, Controller, Get, Post, Query } from '@nestjs/common' -import { - ApiOkResponse, - ApiOperation, - ApiTags, -} from '@nestjs/swagger' +import { Body, Controller, Get, Post, Query } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator'; -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; -import { checkIsDemoMode } from '~/utils' +import { checkIsDemoMode } from '~/utils'; -import { SFileInfoDetail, SFileList, UploadToken } from './manage.class' +import { SFileInfoDetail, SFileList, UploadToken } from './manage.class'; import { DeleteDto, FileInfoDto, @@ -22,8 +18,8 @@ import { MKDirDto, MarkFileDto, RenameDto, -} from './manage.dto' -import { NetDiskManageService } from './manage.service' +} from './manage.dto'; +import { NetDiskManageService } from './manage.service'; export const permissions = definePermission('netdisk:manage', { LIST: 'list', @@ -38,7 +34,7 @@ export const permissions = definePermission('netdisk:manage', { RENAME: 'rename', CUT: 'cut', COPY: 'copy', -} as const) +} as const); @ApiTags('NetDiskManage - 网盘管理模块') @Controller('manage') @@ -50,20 +46,17 @@ export class NetDiskManageController { @ApiOkResponse({ type: SFileList }) @Perm(permissions.LIST) async list(@Query() dto: GetFileListDto): Promise { - return await this.manageService.getFileList(dto.path, dto.marker, dto.key) + return await this.manageService.getFileList(dto.path, dto.marker, dto.key); } @Post('mkdir') @ApiOperation({ summary: '创建文件夹,支持多级' }) @Perm(permissions.MKDIR) async mkdir(@Body() dto: MKDirDto): Promise { - const result = await this.manageService.checkFileExist( - `${dto.path}${dto.dirName}/`, - ) - if (result) - throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST) + const result = await this.manageService.checkFileExist(`${dto.path}${dto.dirName}/`); + if (result) throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST); - await this.manageService.createDir(`${dto.path}${dto.dirName}`) + await this.manageService.createDir(`${dto.path}${dto.dirName}`); } @Get('token') @@ -71,11 +64,11 @@ export class NetDiskManageController { @ApiOkResponse({ type: UploadToken }) @Perm(permissions.TOKEN) async token(@AuthUser() user: IAuthUser): Promise { - checkIsDemoMode() + checkIsDemoMode(); return { token: this.manageService.createUploadToken(`${user.uid}`), - } + }; } @Get('info') @@ -83,7 +76,7 @@ export class NetDiskManageController { @ApiOkResponse({ type: SFileInfoDetail }) @Perm(permissions.INFO) async info(@Query() dto: FileInfoDto): Promise { - return await this.manageService.getFileInfo(dto.name, dto.path) + return await this.manageService.getFileInfo(dto.name, dto.path); } @Post('mark') @@ -92,7 +85,7 @@ export class NetDiskManageController { async mark(@Body() dto: MarkFileDto): Promise { await this.manageService.changeFileHeaders(dto.name, dto.path, { mark: dto.mark, - }) + }); } @Get('download') @@ -100,7 +93,7 @@ export class NetDiskManageController { @ApiOkResponse({ type: String }) @Perm(permissions.DOWNLOAD) async download(@Query() dto: FileInfoDto): Promise { - return this.manageService.getDownloadLink(`${dto.path}${dto.name}`) + return this.manageService.getDownloadLink(`${dto.path}${dto.name}`); } @Post('rename') @@ -108,22 +101,19 @@ export class NetDiskManageController { @Perm(permissions.RENAME) async rename(@Body() dto: RenameDto): Promise { const result = await this.manageService.checkFileExist( - `${dto.path}${dto.toName}${dto.type === 'dir' ? '/' : ''}`, - ) - if (result) - throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST) + `${dto.path}${dto.toName}${dto.type === 'dir' ? '/' : ''}` + ); + if (result) throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST); - if (dto.type === 'file') - await this.manageService.renameFile(dto.path, dto.name, dto.toName) - else - await this.manageService.renameDir(dto.path, dto.name, dto.toName) + if (dto.type === 'file') await this.manageService.renameFile(dto.path, dto.name, dto.toName); + else await this.manageService.renameDir(dto.path, dto.name, dto.toName); } @Post('delete') @ApiOperation({ summary: '删除文件或文件夹' }) @Perm(permissions.DELETE) async delete(@Body() dto: DeleteDto): Promise { - await this.manageService.deleteMultiFileOrDir(dto.files, dto.path) + await this.manageService.deleteMultiFileOrDir(dto.files, dto.path); } @Post('cut') @@ -131,23 +121,15 @@ export class NetDiskManageController { @Perm(permissions.CUT) async cut(@Body() dto: FileOpDto): Promise { if (dto.originPath === dto.toPath) - throw new BusinessException(ErrorEnum.OSS_NO_OPERATION_REQUIRED) + throw new BusinessException(ErrorEnum.OSS_NO_OPERATION_REQUIRED); - await this.manageService.moveMultiFileOrDir( - dto.files, - dto.originPath, - dto.toPath, - ) + await this.manageService.moveMultiFileOrDir(dto.files, dto.originPath, dto.toPath); } @Post('copy') @ApiOperation({ summary: '复制文件或文件夹,支持批量' }) @Perm(permissions.COPY) async copy(@Body() dto: FileOpDto): Promise { - await this.manageService.copyMultiFileOrDir( - dto.files, - dto.originPath, - dto.toPath, - ) + await this.manageService.copyMultiFileOrDir(dto.files, dto.originPath, dto.toPath); } } diff --git a/src/modules/netdisk/manager/manage.dto.ts b/src/modules/netdisk/manager/manage.dto.ts index 984ef9b..1b54c84 100644 --- a/src/modules/netdisk/manager/manage.dto.ts +++ b/src/modules/netdisk/manager/manage.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' -import { Type } from 'class-transformer' +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; import { ArrayMaxSize, IsNotEmpty, @@ -12,31 +12,28 @@ import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface, -} from 'class-validator' -import { isEmpty } from 'lodash' +} from 'class-validator'; +import { isEmpty } from 'lodash'; -import { NETDISK_HANDLE_MAX_ITEM } from '~/constants/oss.constant' +import { NETDISK_HANDLE_MAX_ITEM } from '~/constants/oss.constant'; @ValidatorConstraint({ name: 'IsLegalNameExpression', async: false }) export class IsLegalNameExpression implements ValidatorConstraintInterface { validate(value: string, args: ValidationArguments) { try { - if (isEmpty(value)) - throw new Error('dir name is empty') + if (isEmpty(value)) throw new Error('dir name is empty'); - if (value.includes('/')) - throw new Error('dir name not allow /') + if (value.includes('/')) throw new Error('dir name not allow /'); - return true - } - catch (e) { - return false + return true; + } catch (e) { + return false; } } defaultMessage(_args: ValidationArguments) { // here you can provide default error message if validation failed - return 'file or dir name invalid' + return 'file or dir name invalid'; } } @@ -44,30 +41,30 @@ export class FileOpItem { @ApiProperty({ description: '文件类型', enum: ['file', 'dir'] }) @IsString() @Matches(/(^file$)|(^dir$)/) - type: string + type: string; @ApiProperty({ description: '文件名称' }) @IsString() @IsNotEmpty() @Validate(IsLegalNameExpression) - name: string + name: string; } export class GetFileListDto { @ApiProperty({ description: '分页标识' }) @IsOptional() @IsString() - marker: string + marker: string; @ApiProperty({ description: '当前路径' }) @IsString() - path: string + path: string; @ApiPropertyOptional({ description: '搜索关键字' }) @Validate(IsLegalNameExpression) @ValidateIf(o => !isEmpty(o.key)) @IsString() - key: string + key: string; } export class MKDirDto { @@ -75,34 +72,34 @@ export class MKDirDto { @IsNotEmpty() @IsString() @Validate(IsLegalNameExpression) - dirName: string + dirName: string; @ApiProperty({ description: '所属路径' }) @IsString() - path: string + path: string; } export class RenameDto { @ApiProperty({ description: '文件类型' }) @IsString() @Matches(/(^file$)|(^dir$)/) - type: string + type: string; @ApiProperty({ description: '更改的名称' }) @IsString() @IsNotEmpty() @Validate(IsLegalNameExpression) - toName: string + toName: string; @ApiProperty({ description: '原来的名称' }) @IsString() @IsNotEmpty() @Validate(IsLegalNameExpression) - name: string + name: string; @ApiProperty({ description: '路径' }) @IsString() - path: string + path: string; } export class FileInfoDto { @@ -110,11 +107,11 @@ export class FileInfoDto { @IsString() @IsNotEmpty() @Validate(IsLegalNameExpression) - name: string + name: string; @ApiProperty({ description: '文件所在路径' }) @IsString() - path: string + path: string; } export class DeleteDto { @@ -122,11 +119,11 @@ export class DeleteDto { @Type(() => FileOpItem) @ArrayMaxSize(NETDISK_HANDLE_MAX_ITEM) @ValidateNested({ each: true }) - files: FileOpItem[] + files: FileOpItem[]; @ApiProperty({ description: '所在目录' }) @IsString() - path: string + path: string; } export class MarkFileDto { @@ -134,15 +131,15 @@ export class MarkFileDto { @IsString() @IsNotEmpty() @Validate(IsLegalNameExpression) - name: string + name: string; @ApiProperty({ description: '文件所在路径' }) @IsString() - path: string + path: string; @ApiProperty({ description: '备注信息' }) @IsString() - mark: string + mark: string; } export class FileOpDto { @@ -150,13 +147,13 @@ export class FileOpDto { @Type(() => FileOpItem) @ArrayMaxSize(NETDISK_HANDLE_MAX_ITEM) @ValidateNested({ each: true }) - files: FileOpItem[] + files: FileOpItem[]; @ApiProperty({ description: '操作前的目录' }) @IsString() - originPath: string + originPath: string; @ApiProperty({ description: '操作后的目录' }) @IsString() - toPath: string + toPath: string; } diff --git a/src/modules/netdisk/manager/manage.service.ts b/src/modules/netdisk/manager/manage.service.ts index 3574a65..9f639c7 100644 --- a/src/modules/netdisk/manager/manage.service.ts +++ b/src/modules/netdisk/manager/manage.service.ts @@ -1,45 +1,47 @@ -import { basename, extname } from 'node:path' +import { basename, extname } from 'node:path'; -import { Injectable } from '@nestjs/common' -import { ConfigService } from '@nestjs/config' -import { isEmpty } from 'lodash' -import * as qiniu from 'qiniu' -import { auth, conf, rs } from 'qiniu' +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { isEmpty } from 'lodash'; +import * as qiniu from 'qiniu'; +import { auth, conf, rs } from 'qiniu'; -import { ConfigKeyPaths } from '~/config' -import { NETDISK_COPY_SUFFIX, NETDISK_DELIMITER, NETDISK_HANDLE_MAX_ITEM, NETDISK_LIMIT } from '~/constants/oss.constant' +import { ConfigKeyPaths } from '~/config'; +import { + NETDISK_COPY_SUFFIX, + NETDISK_DELIMITER, + NETDISK_HANDLE_MAX_ITEM, + NETDISK_LIMIT, +} from '~/constants/oss.constant'; -import { AccountInfo } from '~/modules/user/user.model' -import { UserService } from '~/modules/user/user.service' +import { AccountInfo } from '~/modules/user/user.model'; +import { UserService } from '~/modules/user/user.service'; -import { generateRandomValue } from '~/utils' +import { generateRandomValue } from '~/utils'; -import { SFileInfo, SFileInfoDetail, SFileList } from './manage.class' -import { FileOpItem } from './manage.dto' +import { SFileInfo, SFileInfoDetail, SFileList } from './manage.class'; +import { FileOpItem } from './manage.dto'; @Injectable() export class NetDiskManageService { - private config: conf.ConfigOptions - private mac: auth.digest.Mac - private bucketManager: rs.BucketManager + private config: conf.ConfigOptions; + private mac: auth.digest.Mac; + private bucketManager: rs.BucketManager; private get qiniuConfig() { - return this.configService.get('oss', { infer: true }) + return this.configService.get('oss', { infer: true }); } constructor( private configService: ConfigService, - private userService: UserService, + private userService: UserService ) { - this.mac = new qiniu.auth.digest.Mac( - this.qiniuConfig.accessKey, - this.qiniuConfig.secretKey, - ) + this.mac = new qiniu.auth.digest.Mac(this.qiniuConfig.accessKey, this.qiniuConfig.secretKey); this.config = new qiniu.conf.Config({ zone: this.qiniuConfig.zone, - }) + }); // bucket manager - this.bucketManager = new qiniu.rs.BucketManager(this.mac, this.config) + this.bucketManager = new qiniu.rs.BucketManager(this.mac, this.config); } /** @@ -50,7 +52,7 @@ export class NetDiskManageService { */ async getFileList(prefix = '', marker = '', skey = ''): Promise { // 是否需要搜索 - const searching = !isEmpty(skey) + const searching = !isEmpty(skey); return new Promise((resolve, reject) => { this.bucketManager.listPrefix( this.qiniuConfig.bucket, @@ -62,28 +64,24 @@ export class NetDiskManageService { }, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { // 如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候, // 指定options里面的marker为这个值 - const fileList: SFileInfo[] = [] + const fileList: SFileInfo[] = []; // 处理目录,但只有非搜索模式下可用 if (!searching && !isEmpty(respBody.commonPrefixes)) { // dir for (const dirPath of respBody.commonPrefixes) { - const name = (dirPath as string) - .substr(0, dirPath.length - 1) - .replace(prefix, '') + const name = (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''); if (isEmpty(skey) || name.includes(skey)) { fileList.push({ - name: (dirPath as string) - .substr(0, dirPath.length - 1) - .replace(prefix, ''), + name: (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''), type: 'dir', id: generateRandomValue(10), - }) + }); } } } @@ -93,23 +91,22 @@ export class NetDiskManageService { for (const item of respBody.items) { // 搜索模式下处理 if (searching) { - const pathList: string[] = item.key.split(NETDISK_DELIMITER) + const pathList: string[] = item.key.split(NETDISK_DELIMITER); // dir is empty stirng, file is key string - const name = pathList.pop() + const name = pathList.pop(); if ( - item.key.endsWith(NETDISK_DELIMITER) - && pathList[pathList.length - 1].includes(skey) + item.key.endsWith(NETDISK_DELIMITER) && + pathList[pathList.length - 1].includes(skey) ) { // 结果是目录 - const ditName = pathList.pop() + const ditName = pathList.pop(); fileList.push({ id: generateRandomValue(10), name: ditName, type: 'dir', belongTo: pathList.join(NETDISK_DELIMITER), - }) - } - else if (name.includes(skey)) { + }); + } else if (name.includes(skey)) { // 文件 fileList.push({ id: generateRandomValue(10), @@ -119,12 +116,11 @@ export class NetDiskManageService { mimeType: item.mimeType, putTime: new Date(Number.parseInt(item.putTime) / 10000), belongTo: pathList.join(NETDISK_DELIMITER), - }) + }); } - } - else { + } else { // 正常获取列表 - const fileKey = item.key.replace(prefix, '') as string + const fileKey = item.key.replace(prefix, '') as string; if (!isEmpty(fileKey)) { fileList.push({ id: generateRandomValue(10), @@ -133,7 +129,7 @@ export class NetDiskManageService { fsize: item.fsize, mimeType: item.mimeType, putTime: new Date(Number.parseInt(item.putTime) / 10000), - }) + }); } } } @@ -141,18 +137,15 @@ export class NetDiskManageService { resolve({ list: fileList, marker: respBody.marker || null, - }) - } - else { + }); + } else { reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); } - }, - ) - }) + } + ); + }); } /** @@ -165,8 +158,8 @@ export class NetDiskManageService { `${path}${name}`, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { const detailInfo: SFileInfoDetail = { @@ -178,34 +171,29 @@ export class NetDiskManageService { type: respBody.type, uploader: '', mark: respBody?.['x-qn-meta']?.['!mark'] ?? '', - } + }; if (!respBody.endUser) { - resolve(detailInfo) - } - else { + resolve(detailInfo); + } else { this.userService .getAccountInfo(Number.parseInt(respBody.endUser)) .then((user: AccountInfo) => { if (isEmpty(user)) { - resolve(detailInfo) + resolve(detailInfo); + } else { + detailInfo.uploader = user.username; + resolve(detailInfo); } - else { - detailInfo.uploader = user.username - resolve(detailInfo) - } - }) + }); } - } - else { + } else { reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); } - }, - ) - }) + } + ); + }); } /** @@ -214,7 +202,7 @@ export class NetDiskManageService { async changeFileHeaders( name: string, path: string, - headers: { [k: string]: string }, + headers: { [k: string]: string } ): Promise { return new Promise((resolve, reject) => { this.bucketManager.changeHeaders( @@ -223,22 +211,19 @@ export class NetDiskManageService { headers, (err, _, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - resolve() - } - else { + resolve(); + } else { reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); } - }, - ) - }) + } + ); + }); } /** @@ -246,11 +231,11 @@ export class NetDiskManageService { * @returns true创建成功 */ async createDir(dirName: string): Promise { - const safeDirName = dirName.endsWith('/') ? dirName : `${dirName}/` + const safeDirName = dirName.endsWith('/') ? dirName : `${dirName}/`; return new Promise((resolve, reject) => { // 上传一个空文件以用于显示文件夹效果 - const formUploader = new qiniu.form_up.FormUploader(this.config) - const putExtra = new qiniu.form_up.PutExtra() + const formUploader = new qiniu.form_up.FormUploader(this.config); + const putExtra = new qiniu.form_up.PutExtra(); formUploader.put( this.createUploadToken(''), safeDirName, @@ -258,22 +243,19 @@ export class NetDiskManageService { putExtra, (respErr, respBody, respInfo) => { if (respErr) { - reject(respErr) - return + reject(respErr); + return; } if (respInfo.statusCode === 200) { - resolve() - } - else { + resolve(); + } else { reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); } - }, - ) - }) + } + ); + }); } /** @@ -284,32 +266,24 @@ export class NetDiskManageService { // fix path end must a / // 检测文件夹是否存在 - this.bucketManager.stat( - this.qiniuConfig.bucket, - filePath, - (respErr, respBody, respInfo) => { - if (respErr) { - reject(respErr) - return - } - if (respInfo.statusCode === 200) { - // 文件夹存在 - resolve(true) - } - else if (respInfo.statusCode === 612) { - // 文件夹不存在 - resolve(false) - } - else { - reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) - } - }, - ) - }) + this.bucketManager.stat(this.qiniuConfig.bucket, filePath, (respErr, respBody, respInfo) => { + if (respErr) { + reject(respErr); + return; + } + if (respInfo.statusCode === 200) { + // 文件夹存在 + resolve(true); + } else if (respInfo.statusCode === 612) { + // 文件夹不存在 + resolve(false); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + }); + }); } /** @@ -322,9 +296,9 @@ export class NetDiskManageService { insertOnly: 1, fsizeLimit: 1024 ** 2 * 10, endUser, - }) - const uploadToken = policy.uploadToken(this.mac) - return uploadToken + }); + const uploadToken = policy.uploadToken(this.mac); + return uploadToken; } /** @@ -333,11 +307,11 @@ export class NetDiskManageService { * @param name 文件名称 */ async renameFile(dir: string, name: string, toName: string): Promise { - const fileName = `${dir}${name}` - const toFileName = `${dir}${toName}` + const fileName = `${dir}${name}`; + const toFileName = `${dir}${toName}`; const op = { force: true, - } + }; return new Promise((resolve, reject) => { this.bucketManager.move( this.qiniuConfig.bucket, @@ -347,34 +321,32 @@ export class NetDiskManageService { op, (err, respBody, respInfo) => { if (err) { - reject(err) - } - else { + reject(err); + } else { if (respInfo.statusCode === 200) { - resolve() - } - else { + resolve(); + } else { reject( new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); } } - }, - ) - }) + } + ); + }); } /** * 移动文件 */ async moveFile(dir: string, toDir: string, name: string): Promise { - const fileName = `${dir}${name}` - const toFileName = `${toDir}${name}` + const fileName = `${dir}${name}`; + const toFileName = `${toDir}${name}`; const op = { force: true, - } + }; return new Promise((resolve, reject) => { this.bucketManager.move( this.qiniuConfig.bucket, @@ -384,37 +356,35 @@ export class NetDiskManageService { op, (err, respBody, respInfo) => { if (err) { - reject(err) - } - else { + reject(err); + } else { if (respInfo.statusCode === 200) { - resolve() - } - else { + resolve(); + } else { reject( new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); } } - }, - ) - }) + } + ); + }); } /** * 复制文件 */ async copyFile(dir: string, toDir: string, name: string): Promise { - const fileName = `${dir}${name}` + const fileName = `${dir}${name}`; // 拼接文件名 - const ext = extname(name) - const bn = basename(name, ext) - const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}` + const ext = extname(name); + const bn = basename(name, ext); + const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; const op = { force: true, - } + }; return new Promise((resolve, reject) => { this.bucketManager.copy( this.qiniuConfig.bucket, @@ -424,37 +394,35 @@ export class NetDiskManageService { op, (err, respBody, respInfo) => { if (err) { - reject(err) - } - else { + reject(err); + } else { if (respInfo.statusCode === 200) { - resolve() - } - else { + resolve(); + } else { reject( new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); } } - }, - ) - }) + } + ); + }); } /** * 重命名文件夹 */ async renameDir(path: string, name: string, toName: string): Promise { - const dirName = `${path}${name}` - const toDirName = `${path}${toName}` - let hasFile = true - let marker = '' + const dirName = `${path}${name}`; + const toDirName = `${path}${toName}`; + let hasFile = true; + let marker = ''; const op = { force: true, - } - const bucketName = this.qiniuConfig.bucket + }; + const bucketName = this.qiniuConfig.bucket; while (hasFile) { await new Promise((resolve, reject) => { // 列举当前目录下的所有文件 @@ -467,56 +435,43 @@ export class NetDiskManageService { }, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - const moveOperations = respBody.items.map((item) => { - const { key } = item - const destKey = key.replace(dirName, toDirName) - return qiniu.rs.moveOp( - bucketName, - key, - bucketName, - destKey, - op, - ) - }) - this.bucketManager.batch( - moveOperations, - (err2, respBody2, respInfo2) => { - if (err2) { - reject(err2) - return - } - if (respInfo2.statusCode === 200) { - if (isEmpty(respBody.marker)) - hasFile = false - else - marker = respBody.marker + const moveOperations = respBody.items.map(item => { + const { key } = item; + const destKey = key.replace(dirName, toDirName); + return qiniu.rs.moveOp(bucketName, key, bucketName, destKey, op); + }); + this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2); + return; + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) hasFile = false; + else marker = respBody.marker; - resolve() - } - else { - reject( - new Error( - `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}`, - ), + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` ) - } - }, - ) - } - else { + ); + } + }); + } else { reject( new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); } - }, - ) - }) + } + ); + }); } } @@ -527,16 +482,15 @@ export class NetDiskManageService { */ getDownloadLink(key: string): string { if (this.qiniuConfig.access === 'public') { - return this.bucketManager.publicDownloadUrl(this.qiniuConfig.domain, key) - } - else if (this.qiniuConfig.access === 'private') { + return this.bucketManager.publicDownloadUrl(this.qiniuConfig.domain, key); + } else if (this.qiniuConfig.access === 'private') { return this.bucketManager.privateDownloadUrl( this.qiniuConfig.domain, key, - Date.now() / 1000 + 36000, - ) + Date.now() / 1000 + 36000 + ); } - throw new Error('qiniu config access type not support') + throw new Error('qiniu config access type not support'); } /** @@ -551,22 +505,19 @@ export class NetDiskManageService { `${dir}${name}`, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - resolve() - } - else { + resolve(); + } else { reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); } - }, - ) - }) + } + ); + }); } /** @@ -574,47 +525,40 @@ export class NetDiskManageService { * @param dir 文件夹所在的上级目录 * @param name 文件目录名称 */ - async deleteMultiFileOrDir( - fileList: FileOpItem[], - dir: string, - ): Promise { - const files = fileList.filter(item => item.type === 'file') + async deleteMultiFileOrDir(fileList: FileOpItem[], dir: string): Promise { + const files = fileList.filter(item => item.type === 'file'); if (files.length > 0) { // 批处理文件 - const copyOperations = files.map((item) => { - const fileName = `${dir}${item.name}` - return qiniu.rs.deleteOp(this.qiniuConfig.bucket, fileName) - }) + const copyOperations = files.map(item => { + const fileName = `${dir}${item.name}`; + return qiniu.rs.deleteOp(this.qiniuConfig.bucket, fileName); + }); await new Promise((resolve, reject) => { this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - resolve() - } - else if (respInfo.statusCode === 298) { - reject(new Error('操作异常,但部分文件夹删除成功')) - } - else { + resolve(); + } else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')); + } else { reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); } - }) - }) + }); + }); } // 处理文件夹 - const dirs = fileList.filter(item => item.type === 'dir') + const dirs = fileList.filter(item => item.type === 'dir'); if (dirs.length > 0) { // 处理文件夹的复制 for (let i = 0; i < dirs.length; i++) { - const dirName = `${dir}${dirs[i].name}/` - let hasFile = true - let marker = '' + const dirName = `${dir}${dirs[i].name}/`; + let hasFile = true; + let marker = ''; while (hasFile) { await new Promise((resolve, reject) => { // 列举当前目录下的所有文件 @@ -627,49 +571,42 @@ export class NetDiskManageService { }, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - const moveOperations = respBody.items.map((item) => { - const { key } = item - return qiniu.rs.deleteOp(this.qiniuConfig.bucket, key) - }) - this.bucketManager.batch( - moveOperations, - (err2, respBody2, respInfo2) => { - if (err2) { - reject(err2) - return - } - if (respInfo2.statusCode === 200) { - if (isEmpty(respBody.marker)) - hasFile = false - else - marker = respBody.marker + const moveOperations = respBody.items.map(item => { + const { key } = item; + return qiniu.rs.deleteOp(this.qiniuConfig.bucket, key); + }); + this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2); + return; + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) hasFile = false; + else marker = respBody.marker; - resolve() - } - else { - reject( - new Error( - `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}`, - ), + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` ) - } - }, - ) - } - else { + ); + } + }); + } else { reject( new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); } - }, - ) - }) + } + ); + }); } } } @@ -678,62 +615,54 @@ export class NetDiskManageService { /** * 复制文件,含文件夹 */ - async copyMultiFileOrDir( - fileList: FileOpItem[], - dir: string, - toDir: string, - ): Promise { - const files = fileList.filter(item => item.type === 'file') + async copyMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { + const files = fileList.filter(item => item.type === 'file'); const op = { force: true, - } + }; if (files.length > 0) { // 批处理文件 - const copyOperations = files.map((item) => { - const fileName = `${dir}${item.name}` + const copyOperations = files.map(item => { + const fileName = `${dir}${item.name}`; // 拼接文件名 - const ext = extname(item.name) - const bn = basename(item.name, ext) - const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}` + const ext = extname(item.name); + const bn = basename(item.name, ext); + const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; return qiniu.rs.copyOp( this.qiniuConfig.bucket, fileName, this.qiniuConfig.bucket, toFileName, - op, - ) - }) + op + ); + }); await new Promise((resolve, reject) => { this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - resolve() - } - else if (respInfo.statusCode === 298) { - reject(new Error('操作异常,但部分文件夹删除成功')) - } - else { + resolve(); + } else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')); + } else { reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); } - }) - }) + }); + }); } // 处理文件夹 - const dirs = fileList.filter(item => item.type === 'dir') + const dirs = fileList.filter(item => item.type === 'dir'); if (dirs.length > 0) { // 处理文件夹的复制 for (let i = 0; i < dirs.length; i++) { - const dirName = `${dir}${dirs[i].name}/` - const copyDirName = `${toDir}${dirs[i].name}${NETDISK_COPY_SUFFIX}/` - let hasFile = true - let marker = '' + const dirName = `${dir}${dirs[i].name}/`; + const copyDirName = `${toDir}${dirs[i].name}${NETDISK_COPY_SUFFIX}/`; + let hasFile = true; + let marker = ''; while (hasFile) { await new Promise((resolve, reject) => { // 列举当前目录下的所有文件 @@ -746,56 +675,49 @@ export class NetDiskManageService { }, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - const moveOperations = respBody.items.map((item) => { - const { key } = item - const destKey = key.replace(dirName, copyDirName) + const moveOperations = respBody.items.map(item => { + const { key } = item; + const destKey = key.replace(dirName, copyDirName); return qiniu.rs.copyOp( this.qiniuConfig.bucket, key, this.qiniuConfig.bucket, destKey, - op, - ) - }) - this.bucketManager.batch( - moveOperations, - (err2, respBody2, respInfo2) => { - if (err2) { - reject(err2) - return - } - if (respInfo2.statusCode === 200) { - if (isEmpty(respBody.marker)) - hasFile = false - else - marker = respBody.marker + op + ); + }); + this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2); + return; + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) hasFile = false; + else marker = respBody.marker; - resolve() - } - else { - reject( - new Error( - `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}`, - ), + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` ) - } - }, - ) - } - else { + ); + } + }); + } else { reject( new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); } - }, - ) - }) + } + ); + }); } } } @@ -804,63 +726,54 @@ export class NetDiskManageService { /** * 移动文件,含文件夹 */ - async moveMultiFileOrDir( - fileList: FileOpItem[], - dir: string, - toDir: string, - ): Promise { - const files = fileList.filter(item => item.type === 'file') + async moveMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { + const files = fileList.filter(item => item.type === 'file'); const op = { force: true, - } + }; if (files.length > 0) { // 批处理文件 - const copyOperations = files.map((item) => { - const fileName = `${dir}${item.name}` - const toFileName = `${toDir}${item.name}` + const copyOperations = files.map(item => { + const fileName = `${dir}${item.name}`; + const toFileName = `${toDir}${item.name}`; return qiniu.rs.moveOp( this.qiniuConfig.bucket, fileName, this.qiniuConfig.bucket, toFileName, - op, - ) - }) + op + ); + }); await new Promise((resolve, reject) => { this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - resolve() - } - else if (respInfo.statusCode === 298) { - reject(new Error('操作异常,但部分文件夹删除成功')) - } - else { + resolve(); + } else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')); + } else { reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); } - }) - }) + }); + }); } // 处理文件夹 - const dirs = fileList.filter(item => item.type === 'dir') + const dirs = fileList.filter(item => item.type === 'dir'); if (dirs.length > 0) { // 处理文件夹的复制 for (let i = 0; i < dirs.length; i++) { - const dirName = `${dir}${dirs[i].name}/` - const toDirName = `${toDir}${dirs[i].name}/` + const dirName = `${dir}${dirs[i].name}/`; + const toDirName = `${toDir}${dirs[i].name}/`; // 移动的目录不是是自己 - if (toDirName.startsWith(dirName)) - continue + if (toDirName.startsWith(dirName)) continue; - let hasFile = true - let marker = '' + let hasFile = true; + let marker = ''; while (hasFile) { await new Promise((resolve, reject) => { // 列举当前目录下的所有文件 @@ -873,56 +786,49 @@ export class NetDiskManageService { }, (err, respBody, respInfo) => { if (err) { - reject(err) - return + reject(err); + return; } if (respInfo.statusCode === 200) { - const moveOperations = respBody.items.map((item) => { - const { key } = item - const destKey = key.replace(dirName, toDirName) + const moveOperations = respBody.items.map(item => { + const { key } = item; + const destKey = key.replace(dirName, toDirName); return qiniu.rs.moveOp( this.qiniuConfig.bucket, key, this.qiniuConfig.bucket, destKey, - op, - ) - }) - this.bucketManager.batch( - moveOperations, - (err2, respBody2, respInfo2) => { - if (err2) { - reject(err2) - return - } - if (respInfo2.statusCode === 200) { - if (isEmpty(respBody.marker)) - hasFile = false - else - marker = respBody.marker + op + ); + }); + this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2); + return; + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) hasFile = false; + else marker = respBody.marker; - resolve() - } - else { - reject( - new Error( - `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}`, - ), + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` ) - } - }, - ) - } - else { + ); + } + }); + } else { reject( new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`, - ), - ) + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); } - }, - ) - }) + } + ); + }); } } } diff --git a/src/modules/netdisk/netdisk.module.ts b/src/modules/netdisk/netdisk.module.ts index aa5a3f7..4883791 100644 --- a/src/modules/netdisk/netdisk.module.ts +++ b/src/modules/netdisk/netdisk.module.ts @@ -1,21 +1,24 @@ -import { Module } from '@nestjs/common' +import { Module } from '@nestjs/common'; -import { RouterModule } from '@nestjs/core' +import { RouterModule } from '@nestjs/core'; -import { UserModule } from '../user/user.module' +import { UserModule } from '../user/user.module'; -import { NetDiskManageController } from './manager/manage.controller' -import { NetDiskManageService } from './manager/manage.service' -import { NetDiskOverviewController } from './overview/overview.controller' -import { NetDiskOverviewService } from './overview/overview.service' +import { NetDiskManageController } from './manager/manage.controller'; +import { NetDiskManageService } from './manager/manage.service'; +import { NetDiskOverviewController } from './overview/overview.controller'; +import { NetDiskOverviewService } from './overview/overview.service'; @Module({ - imports: [UserModule, RouterModule.register([ - { - path: 'netdisk', - module: NetdiskModule, - }, - ])], + imports: [ + UserModule, + RouterModule.register([ + { + path: 'netdisk', + module: NetdiskModule, + }, + ]), + ], controllers: [NetDiskManageController, NetDiskOverviewController], providers: [NetDiskManageService, NetDiskOverviewService], }) diff --git a/src/modules/netdisk/overview/overview.controller.ts b/src/modules/netdisk/overview/overview.controller.ts index a7b7df9..acdfc9f 100644 --- a/src/modules/netdisk/overview/overview.controller.ts +++ b/src/modules/netdisk/overview/overview.controller.ts @@ -1,23 +1,15 @@ -import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager' -import { - Controller, - Get, - UseInterceptors, -} from '@nestjs/common' -import { - ApiOkResponse, - ApiOperation, - ApiTags, -} from '@nestjs/swagger' +import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager'; +import { Controller, Get, UseInterceptors } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; -import { OverviewSpaceInfo } from './overview.dto' -import { NetDiskOverviewService } from './overview.service' +import { OverviewSpaceInfo } from './overview.dto'; +import { NetDiskOverviewService } from './overview.service'; export const permissions = definePermission('netdisk:overview', { DESC: 'desc', -} as const) +} as const); @ApiTags('NetDiskOverview - 网盘概览模块') @Controller('overview') @@ -32,11 +24,11 @@ export class NetDiskOverviewController { @ApiOkResponse({ type: OverviewSpaceInfo }) @Perm(permissions.DESC) async space(): Promise { - const date = this.overviewService.getZeroHourAnd1Day(new Date()) - const hit = await this.overviewService.getHit(date) - const flow = await this.overviewService.getFlow(date) - const space = await this.overviewService.getSpace(date) - const count = await this.overviewService.getCount(date) + const date = this.overviewService.getZeroHourAnd1Day(new Date()); + const hit = await this.overviewService.getHit(date); + const flow = await this.overviewService.getFlow(date); + const space = await this.overviewService.getSpace(date); + const count = await this.overviewService.getCount(date); return { fileSize: count.datas[count.datas.length - 1], flowSize: flow.datas[flow.datas.length - 1], @@ -44,6 +36,6 @@ export class NetDiskOverviewController { spaceSize: space.datas[space.datas.length - 1], flowTrend: flow, sizeTrend: space, - } + }; } } diff --git a/src/modules/netdisk/overview/overview.service.ts b/src/modules/netdisk/overview/overview.service.ts index 40ff410..72e0387 100644 --- a/src/modules/netdisk/overview/overview.service.ts +++ b/src/modules/netdisk/overview/overview.service.ts @@ -1,35 +1,32 @@ -import { HttpService } from '@nestjs/axios' -import { Injectable } from '@nestjs/common' -import { ConfigService } from '@nestjs/config' -import dayjs from 'dayjs' -import * as qiniu from 'qiniu' +import { HttpService } from '@nestjs/axios'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import dayjs from 'dayjs'; +import * as qiniu from 'qiniu'; -import { ConfigKeyPaths } from '~/config' -import { OSS_API } from '~/constants/oss.constant' +import { ConfigKeyPaths } from '~/config'; +import { OSS_API } from '~/constants/oss.constant'; -import { CountInfo, FlowInfo, HitInfo, SpaceInfo } from './overview.dto' +import { CountInfo, FlowInfo, HitInfo, SpaceInfo } from './overview.dto'; @Injectable() export class NetDiskOverviewService { - private mac: qiniu.auth.digest.Mac - private readonly FORMAT = 'YYYYMMDDHHmmss' + private mac: qiniu.auth.digest.Mac; + private readonly FORMAT = 'YYYYMMDDHHmmss'; private get qiniuConfig() { - return this.configService.get('oss', { infer: true }) + return this.configService.get('oss', { infer: true }); } constructor( private configService: ConfigService, - private readonly httpService: HttpService, + private readonly httpService: HttpService ) { - this.mac = new qiniu.auth.digest.Mac( - this.qiniuConfig.accessKey, - this.qiniuConfig.secretKey, - ) + this.mac = new qiniu.auth.digest.Mac(this.qiniuConfig.accessKey, this.qiniuConfig.secretKey); } /** 获取格式化后的起始和结束时间 */ getStartAndEndDate(start: Date, end = new Date()) { - return [dayjs(start).format(this.FORMAT), dayjs(end).format(this.FORMAT)] + return [dayjs(start).format(this.FORMAT), dayjs(end).format(this.FORMAT)]; } /** @@ -40,9 +37,9 @@ export class NetDiskOverviewService { const defaultParams = { $bucket: this.qiniuConfig.bucket, g: 'day', - } - const searchParams = new URLSearchParams({ ...defaultParams, ...queryParams }) - return decodeURIComponent(`${OSS_API}/v6/${type}?${searchParams}`) + }; + const searchParams = new URLSearchParams({ ...defaultParams, ...queryParams }); + return decodeURIComponent(`${OSS_API}/v6/${type}?${searchParams}`); } /** 获取统计数据 */ @@ -51,33 +48,33 @@ export class NetDiskOverviewService { this.mac, url, 'GET', - 'application/x-www-form-urlencoded', - ) + 'application/x-www-form-urlencoded' + ); return this.httpService.axiosRef.get(url, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', - 'Authorization': `${accessToken}`, + Authorization: `${accessToken}`, }, - }) + }); } /** * 获取当天零时 */ getZeroHourToDay(current: Date): Date { - const year = dayjs(current).year() - const month = dayjs(current).month() - const date = dayjs(current).date() - return new Date(year, month, date, 0) + const year = dayjs(current).year(); + const month = dayjs(current).month(); + const date = dayjs(current).date(); + return new Date(year, month, date, 0); } /** * 获取当月1号零时 */ getZeroHourAnd1Day(current: Date): Date { - const year = dayjs(current).year() - const month = dayjs(current).month() - return new Date(year, month, 1, 0) + const year = dayjs(current).year(); + const month = dayjs(current).month(); + return new Date(year, month, 1, 0); } /** @@ -85,15 +82,15 @@ export class NetDiskOverviewService { * https://developer.qiniu.com/kodo/3908/statistic-space */ async getSpace(beginDate: Date, endDate = new Date()): Promise { - const [begin, end] = this.getStartAndEndDate(beginDate, endDate) - const url = this.getStatisticUrl('space', { begin, end }) - const { data } = await this.getStatisticData(url) + const [begin, end] = this.getStartAndEndDate(beginDate, endDate); + const url = this.getStatisticUrl('space', { begin, end }); + const { data } = await this.getStatisticData(url); return { datas: data.datas, - times: data.times.map((e) => { - return dayjs.unix(e).date() + times: data.times.map(e => { + return dayjs.unix(e).date(); }), - } + }; } /** @@ -101,15 +98,15 @@ export class NetDiskOverviewService { * https://developer.qiniu.com/kodo/3914/count */ async getCount(beginDate: Date, endDate = new Date()): Promise { - const [begin, end] = this.getStartAndEndDate(beginDate, endDate) - const url = this.getStatisticUrl('count', { begin, end }) - const { data } = await this.getStatisticData(url) + const [begin, end] = this.getStartAndEndDate(beginDate, endDate); + const url = this.getStatisticUrl('count', { begin, end }); + const { data } = await this.getStatisticData(url); return { - times: data.times.map((e) => { - return dayjs.unix(e).date() + times: data.times.map(e => { + return dayjs.unix(e).date(); }), datas: data.datas, - } + }; } /** @@ -118,19 +115,25 @@ export class NetDiskOverviewService { * https://developer.qiniu.com/kodo/3820/blob-io */ async getFlow(beginDate: Date, endDate = new Date()): Promise { - const [begin, end] = this.getStartAndEndDate(beginDate, endDate) - const url = this.getStatisticUrl('blob_io', { begin, end, $ftype: 0, $src: 'origin', select: 'flow' }) - const { data } = await this.getStatisticData(url) - const times = [] - const datas = [] - data.forEach((e) => { - times.push(dayjs(e.time).date()) - datas.push(e.values.flow) - }) + const [begin, end] = this.getStartAndEndDate(beginDate, endDate); + const url = this.getStatisticUrl('blob_io', { + begin, + end, + $ftype: 0, + $src: 'origin', + select: 'flow', + }); + const { data } = await this.getStatisticData(url); + const times = []; + const datas = []; + data.forEach(e => { + times.push(dayjs(e.time).date()); + datas.push(e.values.flow); + }); return { times, datas, - } + }; } /** @@ -139,18 +142,24 @@ export class NetDiskOverviewService { * https://developer.qiniu.com/kodo/3820/blob-io */ async getHit(beginDate: Date, endDate = new Date()): Promise { - const [begin, end] = this.getStartAndEndDate(beginDate, endDate) - const url = this.getStatisticUrl('blob_io', { begin, end, $ftype: 0, $src: 'inner', select: 'hit' }) - const { data } = await this.getStatisticData(url) - const times = [] - const datas = [] - data.forEach((e) => { - times.push(dayjs(e.time).date()) - datas.push(e.values.hit) - }) + const [begin, end] = this.getStartAndEndDate(beginDate, endDate); + const url = this.getStatisticUrl('blob_io', { + begin, + end, + $ftype: 0, + $src: 'inner', + select: 'hit', + }); + const { data } = await this.getStatisticData(url); + const times = []; + const datas = []; + data.forEach(e => { + times.push(dayjs(e.time).date()); + datas.push(e.values.hit); + }); return { times, datas, - } + }; } } diff --git a/src/modules/sse/sse.controller.ts b/src/modules/sse/sse.controller.ts index 1e475b7..e9f0ecc 100644 --- a/src/modules/sse/sse.controller.ts +++ b/src/modules/sse/sse.controller.ts @@ -1,17 +1,25 @@ -import { BeforeApplicationShutdown, Controller, Param, ParseIntPipe, Req, Res, Sse } from '@nestjs/common' -import { ApiTags } from '@nestjs/swagger' -import { FastifyReply, FastifyRequest } from 'fastify' -import { Observable, interval } from 'rxjs' +import { + BeforeApplicationShutdown, + Controller, + Param, + ParseIntPipe, + Req, + Res, + Sse, +} from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { FastifyReply, FastifyRequest } from 'fastify'; +import { Observable, interval } from 'rxjs'; -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; -import { MessageEvent, SseService } from './sse.service' +import { MessageEvent, SseService } from './sse.service'; @ApiTags('System - sse模块') @ApiSecurityAuth() @Controller('sse') export class SseController implements BeforeApplicationShutdown { - private replyMap: Map = new Map() + private replyMap: Map = new Map(); constructor(private readonly sseService: SseService) {} @@ -19,36 +27,40 @@ export class SseController implements BeforeApplicationShutdown { this.sseService.sendToAll({ type: 'close', data: 'bye~', - }) - this.replyMap.forEach((reply) => { - reply.raw.end().destroy() - }) + }); + this.replyMap.forEach(reply => { + reply.raw.end().destroy(); + }); } // 通过控制台关闭程序时触发 beforeApplicationShutdown() { // console.log('beforeApplicationShutdown') - this.closeAllConnect() + this.closeAllConnect(); } @Sse(':uid') - sse(@Param('uid', ParseIntPipe) uid: number, @Req() req: FastifyRequest, @Res() res: FastifyReply): Observable { - this.replyMap.set(uid, res) + sse( + @Param('uid', ParseIntPipe) uid: number, + @Req() req: FastifyRequest, + @Res() res: FastifyReply + ): Observable { + this.replyMap.set(uid, res); const subscription = interval(10000).subscribe(() => { - this.sseService.sendToClient(uid, { type: 'ping' }) - }) + this.sseService.sendToClient(uid, { type: 'ping' }); + }); // 当客户端断开连接时 req.raw.on('close', () => { - subscription.unsubscribe() - this.sseService.removeClient(uid) - this.replyMap.delete(uid) + subscription.unsubscribe(); + this.sseService.removeClient(uid); + this.replyMap.delete(uid); // console.log(`user-${uid}已关闭`) - }) + }); - return new Observable((subscriber) => { - this.sseService.addClient(uid, subscriber) - }) + return new Observable(subscriber => { + this.sseService.addClient(uid, subscriber); + }); } } diff --git a/src/modules/sse/sse.module.ts b/src/modules/sse/sse.module.ts index e6af2b1..62bcc39 100644 --- a/src/modules/sse/sse.module.ts +++ b/src/modules/sse/sse.module.ts @@ -1,7 +1,7 @@ -import { Module } from '@nestjs/common' +import { Module } from '@nestjs/common'; -import { SseController } from './sse.controller' -import { SseService } from './sse.service' +import { SseController } from './sse.controller'; +import { SseService } from './sse.service'; @Module({ imports: [], diff --git a/src/modules/sse/sse.service.ts b/src/modules/sse/sse.service.ts index 076e415..a514e36 100644 --- a/src/modules/sse/sse.service.ts +++ b/src/modules/sse/sse.service.ts @@ -1,42 +1,42 @@ -import { Injectable } from '@nestjs/common' -import { Subscriber } from 'rxjs' -import { In } from 'typeorm' +import { Injectable } from '@nestjs/common'; +import { Subscriber } from 'rxjs'; +import { In } from 'typeorm'; -import { ROOT_ROLE_ID } from '~/constants/system.constant' +import { ROOT_ROLE_ID } from '~/constants/system.constant'; -import { RoleEntity } from '~/modules/system/role/role.entity' -import { UserEntity } from '~/modules/user/user.entity' +import { RoleEntity } from '~/modules/system/role/role.entity'; +import { UserEntity } from '~/modules/user/user.entity'; export interface MessageEvent { - data?: string | object - id?: string - type?: 'ping' | 'close' | 'updatePermsAndMenus' - retry?: number + data?: string | object; + id?: string; + type?: 'ping' | 'close' | 'updatePermsAndMenus'; + retry?: number; } -const clientMap: Map> = new Map() +const clientMap: Map> = new Map(); @Injectable() export class SseService { addClient(uid: number, subscriber: Subscriber) { - clientMap.set(uid, subscriber) + clientMap.set(uid, subscriber); } removeClient(uid: number): void { - const client = clientMap.get(uid) - client?.complete() - clientMap.delete(uid) + const client = clientMap.get(uid); + client?.complete(); + clientMap.delete(uid); } sendToClient(uid: number, data: MessageEvent): void { - const client = clientMap.get(uid) - client?.next?.(data) + const client = clientMap.get(uid); + client?.next?.(data); } sendToAll(data: MessageEvent): void { - clientMap.forEach((client) => { - client.next(data) - }) + clientMap.forEach(client => { + client.next(data); + }); } /** @@ -45,10 +45,10 @@ export class SseService { * @constructor */ async noticeClientToUpdateMenusByUserIds(uid: number | number[]) { - const userIds = [].concat(uid) as number[] - userIds.forEach((uid) => { - this.sendToClient(uid, { type: 'updatePermsAndMenus' }) - }) + const userIds = [].concat(uid) as number[]; + userIds.forEach(uid => { + this.sendToClient(uid, { type: 'updatePermsAndMenus' }); + }); } /** @@ -61,9 +61,9 @@ export class SseService { id: In(menuIds), }, }, - }) - const roleIds = roleMenus.map(n => n.id).concat(ROOT_ROLE_ID) - await this.noticeClientToUpdateMenusByRoleIds(roleIds) + }); + const roleIds = roleMenus.map(n => n.id).concat(ROOT_ROLE_ID); + await this.noticeClientToUpdateMenusByRoleIds(roleIds); } /** @@ -76,10 +76,10 @@ export class SseService { id: In(roleIds), }, }, - }) + }); if (users) { - const userIds = users.map(n => n.id) - await this.noticeClientToUpdateMenusByUserIds(userIds) + const userIds = users.map(n => n.id); + await this.noticeClientToUpdateMenusByUserIds(userIds); } } } diff --git a/src/modules/system/dept/dept.controller.ts b/src/modules/system/dept/dept.controller.ts index 81b6ef9..06d28a8 100644 --- a/src/modules/system/dept/dept.controller.ts +++ b/src/modules/system/dept/dept.controller.ts @@ -1,17 +1,17 @@ -import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' -import { DeptEntity } from '~/modules/system/dept/dept.entity' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { DeptEntity } from '~/modules/system/dept/dept.entity'; -import { DeptDto, DeptQueryDto } from './dept.dto' -import { DeptService } from './dept.service' +import { DeptDto, DeptQueryDto } from './dept.dto'; +import { DeptService } from './dept.service'; export const permissions = definePermission('system:dept', { LIST: 'list', @@ -19,7 +19,7 @@ export const permissions = definePermission('system:dept', { READ: 'read', UPDATE: 'update', DELETE: 'delete', -} as const) +} as const); @ApiSecurityAuth() @ApiTags('System - 部门模块') @@ -31,31 +31,29 @@ export class DeptController { @ApiOperation({ summary: '获取部门列表' }) @ApiResult({ type: [DeptEntity] }) @Perm(permissions.LIST) - async list( - @Query() dto: DeptQueryDto, @AuthUser('uid') uid: number): Promise { - return this.deptService.getDeptTree(uid, dto) + async list(@Query() dto: DeptQueryDto, @AuthUser('uid') uid: number): Promise { + return this.deptService.getDeptTree(uid, dto); } @Post() @ApiOperation({ summary: '创建部门' }) @Perm(permissions.CREATE) async create(@Body() dto: DeptDto): Promise { - await this.deptService.create(dto) + await this.deptService.create(dto); } @Get(':id') @ApiOperation({ summary: '查询部门信息' }) @Perm(permissions.READ) async info(@IdParam() id: number) { - return this.deptService.info(id) + return this.deptService.info(id); } @Put(':id') @ApiOperation({ summary: '更新部门' }) @Perm(permissions.UPDATE) - async update( - @IdParam() id: number, @Body() updateDeptDto: DeptDto): Promise { - await this.deptService.update(id, updateDeptDto) + async update(@IdParam() id: number, @Body() updateDeptDto: DeptDto): Promise { + await this.deptService.update(id, updateDeptDto); } @Delete(':id') @@ -63,16 +61,14 @@ export class DeptController { @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { // 查询是否有关联用户或者部门,如果含有则无法删除 - const count = await this.deptService.countUserByDeptId(id) - if (count > 0) - throw new BusinessException(ErrorEnum.DEPARTMENT_HAS_ASSOCIATED_USERS) + const count = await this.deptService.countUserByDeptId(id); + if (count > 0) throw new BusinessException(ErrorEnum.DEPARTMENT_HAS_ASSOCIATED_USERS); - const count2 = await this.deptService.countChildDept(id) - console.log('count2', count2) - if (count2 > 0) - throw new BusinessException(ErrorEnum.DEPARTMENT_HAS_CHILD_DEPARTMENTS) + const count2 = await this.deptService.countChildDept(id); + console.log('count2', count2); + if (count2 > 0) throw new BusinessException(ErrorEnum.DEPARTMENT_HAS_CHILD_DEPARTMENTS); - await this.deptService.delete(id) + await this.deptService.delete(id); } // @Post('move') diff --git a/src/modules/system/dept/dept.dto.ts b/src/modules/system/dept/dept.dto.ts index 5d79782..14b15cf 100644 --- a/src/modules/system/dept/dept.dto.ts +++ b/src/modules/system/dept/dept.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Type } from 'class-transformer' +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, @@ -9,62 +9,62 @@ import { Min, MinLength, ValidateNested, -} from 'class-validator' +} from 'class-validator'; export class DeptDto { @ApiProperty({ description: '部门名称' }) @IsString() @MinLength(1) - name: string + name: string; @ApiProperty({ description: '父级部门id' }) @Type(() => Number) @IsInt() @IsOptional() - parentId: number + parentId: number; @ApiProperty({ description: '排序编号', required: false }) @IsInt() @Min(0) @IsOptional() - orderNo: number + orderNo: number; } export class TransferDeptDto { @ApiProperty({ description: '需要转移的管理员列表编号', type: [Number] }) @IsArray() @ArrayNotEmpty() - userIds: number[] + userIds: number[]; @ApiProperty({ description: '需要转移过去的系统部门ID' }) @IsInt() @Min(0) - deptId: number + deptId: number; } export class MoveDept { @ApiProperty({ description: '当前部门ID' }) @IsInt() @Min(0) - id: number + id: number; @ApiProperty({ description: '移动到指定父级部门的ID' }) @IsInt() @Min(0) @IsOptional() - parentId: number + parentId: number; } export class MoveDeptDto { @ApiProperty({ description: '部门列表', type: [MoveDept] }) @ValidateNested({ each: true }) @Type(() => MoveDept) - depts: MoveDept[] + depts: MoveDept[]; } export class DeptQueryDto { @ApiProperty({ description: '部门名称' }) @IsString() @IsOptional() - name?: string + name?: string; } diff --git a/src/modules/system/dept/dept.entity.ts b/src/modules/system/dept/dept.entity.ts index 3a69396..7092dbe 100644 --- a/src/modules/system/dept/dept.entity.ts +++ b/src/modules/system/dept/dept.entity.ts @@ -1,36 +1,28 @@ -import { ApiHideProperty, ApiProperty } from '@nestjs/swagger' -import { - Column, - Entity, - OneToMany, - Relation, - Tree, - TreeChildren, - TreeParent, -} from 'typeorm' +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, OneToMany, Relation, Tree, TreeChildren, TreeParent } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; -import { UserEntity } from '../../user/user.entity' +import { UserEntity } from '../../user/user.entity'; @Entity({ name: 'sys_dept' }) @Tree('materialized-path') export class DeptEntity extends CommonEntity { @Column() @ApiProperty({ description: '部门名称' }) - name: string + name: string; @Column({ nullable: true, default: 0 }) @ApiProperty({ description: '排序' }) - orderNo: number + orderNo: number; @TreeChildren({ cascade: true }) - children: DeptEntity[] + children: DeptEntity[]; @TreeParent({ onDelete: 'SET NULL' }) - parent?: DeptEntity + parent?: DeptEntity; @ApiHideProperty() @OneToMany(() => UserEntity, user => user.dept) - users: Relation + users: Relation; } diff --git a/src/modules/system/dept/dept.module.ts b/src/modules/system/dept/dept.module.ts index d936ed9..aeade9c 100644 --- a/src/modules/system/dept/dept.module.ts +++ b/src/modules/system/dept/dept.module.ts @@ -1,14 +1,14 @@ -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { UserModule } from '../../user/user.module' -import { RoleModule } from '../role/role.module' +import { UserModule } from '../../user/user.module'; +import { RoleModule } from '../role/role.module'; -import { DeptController } from './dept.controller' -import { DeptEntity } from './dept.entity' -import { DeptService } from './dept.service' +import { DeptController } from './dept.controller'; +import { DeptEntity } from './dept.entity'; +import { DeptService } from './dept.service'; -const services = [DeptService] +const services = [DeptService]; @Module({ imports: [TypeOrmModule.forFeature([DeptEntity]), UserModule, RoleModule], diff --git a/src/modules/system/dept/dept.service.ts b/src/modules/system/dept/dept.service.ts index 1eae39b..418eaf3 100644 --- a/src/modules/system/dept/dept.service.ts +++ b/src/modules/system/dept/dept.service.ts @@ -1,16 +1,16 @@ -import { Injectable } from '@nestjs/common' -import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm' -import { isEmpty } from 'lodash' -import { EntityManager, Repository, TreeRepository } from 'typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { isEmpty } from 'lodash'; +import { EntityManager, Repository, TreeRepository } from 'typeorm'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { DeptEntity } from '~/modules/system/dept/dept.entity' -import { UserEntity } from '~/modules/user/user.entity' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { DeptEntity } from '~/modules/system/dept/dept.entity'; +import { UserEntity } from '~/modules/user/user.entity'; -import { deleteEmptyChildren } from '~/utils/list2tree.util' +import { deleteEmptyChildren } from '~/utils/list2tree.util'; -import { DeptDto, DeptQueryDto, MoveDept } from './dept.dto' +import { DeptDto, DeptQueryDto, MoveDept } from './dept.dto'; @Injectable() export class DeptService { @@ -19,11 +19,11 @@ export class DeptService { private userRepository: Repository, @InjectRepository(DeptEntity) private deptRepository: TreeRepository, - @InjectEntityManager() private entityManager: EntityManager, + @InjectEntityManager() private entityManager: EntityManager ) {} async list(): Promise { - return this.deptRepository.find({ order: { orderNo: 'DESC' } }) + return this.deptRepository.find({ order: { orderNo: 'DESC' } }); } async info(id: number): Promise { @@ -31,104 +31,97 @@ export class DeptService { .createQueryBuilder('dept') .leftJoinAndSelect('dept.parent', 'parent') .where({ id }) - .getOne() + .getOne(); - if (isEmpty(dept)) - throw new BusinessException(ErrorEnum.DEPARTMENT_NOT_FOUND) + if (isEmpty(dept)) throw new BusinessException(ErrorEnum.DEPARTMENT_NOT_FOUND); - return dept + return dept; } async create({ parentId, ...data }: DeptDto): Promise { const parent = await this.deptRepository .createQueryBuilder('dept') .where({ id: parentId }) - .getOne() + .getOne(); await this.deptRepository.save({ ...data, parent, - }) + }); } async update(id: number, { parentId, ...data }: DeptDto): Promise { - const item = await this.deptRepository - .createQueryBuilder('dept') - .where({ id }) - .getOne() + const item = await this.deptRepository.createQueryBuilder('dept').where({ id }).getOne(); const parent = await this.deptRepository .createQueryBuilder('dept') .where({ id: parentId }) - .getOne() + .getOne(); await this.deptRepository.save({ ...item, ...data, parent, - }) + }); } async delete(id: number): Promise { - await this.deptRepository.delete(id) + await this.deptRepository.delete(id); } /** * 移动排序 */ async move(depts: MoveDept[]): Promise { - await this.entityManager.transaction(async (manager) => { - await manager.save(depts) - }) + await this.entityManager.transaction(async manager => { + await manager.save(depts); + }); } /** * 根据部门查询关联的用户数量 */ async countUserByDeptId(id: number): Promise { - return this.userRepository.countBy({ dept: { id } }) + return this.userRepository.countBy({ dept: { id } }); } /** * 查找当前部门下的子部门数量 */ async countChildDept(id: number): Promise { - const item = await this.deptRepository.findOneBy({ id }) - return (await this.deptRepository.countDescendants(item)) - 1 + const item = await this.deptRepository.findOneBy({ id }); + return (await this.deptRepository.countDescendants(item)) - 1; } /** * 获取部门列表树结构 */ - async getDeptTree( - uid: number, - { name }: DeptQueryDto, - ): Promise { - const tree: DeptEntity[] = [] + async getDeptTree(uid: number, { name }: DeptQueryDto): Promise { + const tree: DeptEntity[] = []; if (name) { const deptList = await this.deptRepository .createQueryBuilder('dept') .where('dept.name like :name', { name: `%${name}%` }) - .getMany() + .getMany(); for (const dept of deptList) { - const deptTree = await this.deptRepository.findDescendantsTree(dept) - tree.push(deptTree) + const deptTree = await this.deptRepository.findDescendantsTree(dept); + tree.push(deptTree); } - deleteEmptyChildren(tree) + deleteEmptyChildren(tree); - return tree + return tree; } const deptTree = await this.deptRepository.findTrees({ depth: 2, relations: ['parent'], - }) + }); - deleteEmptyChildren(deptTree) + deleteEmptyChildren(deptTree); - return deptTree + return deptTree; } } diff --git a/src/modules/system/dict-item/dict-item.controller.ts b/src/modules/system/dict-item/dict-item.controller.ts index 6906aa6..a0ac474 100644 --- a/src/modules/system/dict-item/dict-item.controller.ts +++ b/src/modules/system/dict-item/dict-item.controller.ts @@ -1,16 +1,16 @@ -import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { Pagination } from '~/helper/paginate/pagination' -import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' -import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { Pagination } from '~/helper/paginate/pagination'; +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity'; -import { DictItemDto, DictItemQueryDto } from './dict-item.dto' -import { DictItemService } from './dict-item.service' +import { DictItemDto, DictItemQueryDto } from './dict-item.dto'; +import { DictItemService } from './dict-item.service'; export const permissions = definePermission('system:dict-item', { LIST: 'list', @@ -18,7 +18,7 @@ export const permissions = definePermission('system:dict-item', { READ: 'read', UPDATE: 'update', DELETE: 'delete', -} as const) +} as const); @ApiTags('System - 字典项模块') @ApiSecurityAuth() @@ -31,16 +31,16 @@ export class DictItemController { @ApiResult({ type: [DictItemEntity], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: DictItemQueryDto): Promise> { - return this.dictItemService.page(dto) + return this.dictItemService.page(dto); } @Post() @ApiOperation({ summary: '新增字典项' }) @Perm(permissions.CREATE) async create(@Body() dto: DictItemDto, @AuthUser() user: IAuthUser): Promise { - await this.dictItemService.isExistKey(dto) - dto.createBy = dto.updateBy = user.uid - await this.dictItemService.create(dto) + await this.dictItemService.isExistKey(dto); + dto.createBy = dto.updateBy = user.uid; + await this.dictItemService.create(dto); } @Get(':id') @@ -48,21 +48,25 @@ export class DictItemController { @ApiResult({ type: DictItemEntity }) @Perm(permissions.READ) async info(@IdParam() id: number): Promise { - return this.dictItemService.findOne(id) + return this.dictItemService.findOne(id); } @Post(':id') @ApiOperation({ summary: '更新字典项' }) @Perm(permissions.UPDATE) - async update(@IdParam() id: number, @Body() dto: DictItemDto, @AuthUser() user: IAuthUser): Promise { - dto.updateBy = user.uid - await this.dictItemService.update(id, dto) + async update( + @IdParam() id: number, + @Body() dto: DictItemDto, + @AuthUser() user: IAuthUser + ): Promise { + dto.updateBy = user.uid; + await this.dictItemService.update(id, dto); } @Delete(':id') @ApiOperation({ summary: '删除指定的字典项' }) @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { - await this.dictItemService.delete(id) + await this.dictItemService.delete(id); } } diff --git a/src/modules/system/dict-item/dict-item.dto.ts b/src/modules/system/dict-item/dict-item.dto.ts index fa5cf65..5bcd7d5 100644 --- a/src/modules/system/dict-item/dict-item.dto.ts +++ b/src/modules/system/dict-item/dict-item.dto.ts @@ -1,48 +1,48 @@ -import { ApiProperty, PartialType } from '@nestjs/swagger' -import { IsInt, IsOptional, IsString, MinLength } from 'class-validator' +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { IsInt, IsOptional, IsString, MinLength } from 'class-validator'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; -import { DictItemEntity } from './dict-item.entity' +import { DictItemEntity } from './dict-item.entity'; export class DictItemDto extends PartialType(DictItemEntity) { @ApiProperty({ description: '字典类型 ID' }) @IsInt() - typeId: number + typeId: number; @ApiProperty({ description: '字典项键名' }) @IsString() @MinLength(1) - label: string + label: string; @ApiProperty({ description: '字典项值' }) @IsString() @MinLength(1) - value: string + value: string; @ApiProperty({ description: '状态' }) @IsOptional() @IsInt() - status?: number + status?: number; @ApiProperty({ description: '备注' }) @IsOptional() @IsString() - remark?: string + remark?: string; } export class DictItemQueryDto extends PagerDto { @ApiProperty({ description: '字典类型 ID', required: true }) @IsInt() - typeId: number + typeId: number; @ApiProperty({ description: '字典项键名' }) @IsString() @IsOptional() - label?: string + label?: string; @ApiProperty({ description: '字典项值' }) @IsString() @IsOptional() - value?: string + value?: string; } diff --git a/src/modules/system/dict-item/dict-item.entity.ts b/src/modules/system/dict-item/dict-item.entity.ts index d4885a8..523f4d0 100644 --- a/src/modules/system/dict-item/dict-item.entity.ts +++ b/src/modules/system/dict-item/dict-item.entity.ts @@ -1,32 +1,32 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; -import { CompleteEntity } from '~/common/entity/common.entity' +import { CompleteEntity } from '~/common/entity/common.entity'; -import { DictTypeEntity } from '../dict-type/dict-type.entity' +import { DictTypeEntity } from '../dict-type/dict-type.entity'; @Entity({ name: 'sys_dict_item' }) export class DictItemEntity extends CompleteEntity { @ManyToOne(() => DictTypeEntity, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'type_id' }) - type: DictTypeEntity + type: DictTypeEntity; @Column({ type: 'varchar', length: 50 }) @ApiProperty({ description: '字典项键名' }) - label: string + label: string; @Column({ type: 'varchar', length: 50 }) @ApiProperty({ description: '字典项值' }) - value: string + value: string; @Column({ nullable: true, comment: '字典项排序' }) - orderNo: number + orderNo: number; @Column({ type: 'tinyint', default: 1 }) @ApiProperty({ description: ' 状态' }) - status: number + status: number; @Column({ type: 'varchar', nullable: true }) @ApiProperty({ description: '备注' }) - remark: string + remark: string; } diff --git a/src/modules/system/dict-item/dict-item.module.ts b/src/modules/system/dict-item/dict-item.module.ts index e5ee832..824e0f9 100644 --- a/src/modules/system/dict-item/dict-item.module.ts +++ b/src/modules/system/dict-item/dict-item.module.ts @@ -1,11 +1,11 @@ -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { DictItemController } from './dict-item.controller' -import { DictItemEntity } from './dict-item.entity' -import { DictItemService } from './dict-item.service' +import { DictItemController } from './dict-item.controller'; +import { DictItemEntity } from './dict-item.entity'; +import { DictItemService } from './dict-item.service'; -const services = [DictItemService] +const services = [DictItemService]; @Module({ imports: [TypeOrmModule.forFeature([DictItemEntity])], diff --git a/src/modules/system/dict-item/dict-item.service.ts b/src/modules/system/dict-item/dict-item.service.ts index ae5f0a0..295cb60 100644 --- a/src/modules/system/dict-item/dict-item.service.ts +++ b/src/modules/system/dict-item/dict-item.service.ts @@ -1,21 +1,21 @@ -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; -import { Like, Repository } from 'typeorm' +import { Like, Repository } from 'typeorm'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { paginate } from '~/helper/paginate' -import { Pagination } from '~/helper/paginate/pagination' -import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { paginate } from '~/helper/paginate'; +import { Pagination } from '~/helper/paginate/pagination'; +import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity'; -import { DictItemDto, DictItemQueryDto } from './dict-item.dto' +import { DictItemDto, DictItemQueryDto } from './dict-item.dto'; @Injectable() export class DictItemService { constructor( @InjectRepository(DictItemEntity) - private dictItemRepository: Repository, + private dictItemRepository: Repository ) {} /** @@ -28,7 +28,8 @@ export class DictItemService { value, typeId, }: DictItemQueryDto): Promise> { - const queryBuilder = this.dictItemRepository.createQueryBuilder('dict_item') + const queryBuilder = this.dictItemRepository + .createQueryBuilder('dict_item') .orderBy({ orderNo: 'ASC' }) .where({ ...(label && { label: Like(`%${label}%`) }), @@ -36,62 +37,61 @@ export class DictItemService { type: { id: typeId, }, - }) + }); - return paginate(queryBuilder, { page, pageSize }) + return paginate(queryBuilder, { page, pageSize }); } /** * 获取参数总数 */ async countConfigList(): Promise { - return this.dictItemRepository.count() + return this.dictItemRepository.count(); } /** * 新增 */ async create(dto: DictItemDto): Promise { - const { typeId, ...rest } = dto + const { typeId, ...rest } = dto; await this.dictItemRepository.insert({ ...rest, type: { id: typeId, }, - }) + }); } /** * 更新 */ async update(id: number, dto: Partial): Promise { - const { typeId, ...rest } = dto + const { typeId, ...rest } = dto; await this.dictItemRepository.update(id, { ...rest, type: { id: typeId, }, - }) + }); } /** * 删除 */ async delete(id: number): Promise { - await this.dictItemRepository.delete(id) + await this.dictItemRepository.delete(id); } /** * 查询单个 */ async findOne(id: number): Promise { - return this.dictItemRepository.findOneBy({ id }) + return this.dictItemRepository.findOneBy({ id }); } async isExistKey(dto: DictItemDto): Promise { - const { value, typeId } = dto - const result = await this.dictItemRepository.findOneBy({ value, type: { id: typeId } }) - if (result) - throw new BusinessException(ErrorEnum.DICT_NAME_EXISTS) + const { value, typeId } = dto; + const result = await this.dictItemRepository.findOneBy({ value, type: { id: typeId } }); + if (result) throw new BusinessException(ErrorEnum.DICT_NAME_EXISTS); } } diff --git a/src/modules/system/dict-type/dict-type.controller.ts b/src/modules/system/dict-type/dict-type.controller.ts index 652a94b..4d98575 100644 --- a/src/modules/system/dict-type/dict-type.controller.ts +++ b/src/modules/system/dict-type/dict-type.controller.ts @@ -1,16 +1,16 @@ -import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { Pagination } from '~/helper/paginate/pagination' -import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' -import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { Pagination } from '~/helper/paginate/pagination'; +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity'; -import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto' -import { DictTypeService } from './dict-type.service' +import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto'; +import { DictTypeService } from './dict-type.service'; export const permissions = definePermission('system:dict-type', { LIST: 'list', @@ -18,7 +18,7 @@ export const permissions = definePermission('system:dict-type', { READ: 'read', UPDATE: 'update', DELETE: 'delete', -} as const) +} as const); @ApiTags('System - 字典类型模块') @ApiSecurityAuth() @@ -31,7 +31,7 @@ export class DictTypeController { @ApiResult({ type: [DictTypeEntity], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: DictTypeQueryDto): Promise> { - return this.dictTypeService.page(dto) + return this.dictTypeService.page(dto); } @Get('select-options') @@ -39,16 +39,16 @@ export class DictTypeController { @ApiResult({ type: [DictTypeEntity] }) @Perm(permissions.LIST) async getAll(): Promise { - return this.dictTypeService.getAll() + return this.dictTypeService.getAll(); } @Post() @ApiOperation({ summary: '新增字典类型' }) @Perm(permissions.CREATE) async create(@Body() dto: DictTypeDto, @AuthUser() user: IAuthUser): Promise { - await this.dictTypeService.isExistKey(dto.name) - dto.createBy = dto.updateBy = user.uid - await this.dictTypeService.create(dto) + await this.dictTypeService.isExistKey(dto.name); + dto.createBy = dto.updateBy = user.uid; + await this.dictTypeService.create(dto); } @Get(':id') @@ -56,21 +56,25 @@ export class DictTypeController { @ApiResult({ type: DictTypeEntity }) @Perm(permissions.READ) async info(@IdParam() id: number): Promise { - return this.dictTypeService.findOne(id) + return this.dictTypeService.findOne(id); } @Post(':id') @ApiOperation({ summary: '更新字典类型' }) @Perm(permissions.UPDATE) - async update(@IdParam() id: number, @Body() dto: DictTypeDto, @AuthUser() user: IAuthUser): Promise { - dto.updateBy = user.uid - await this.dictTypeService.update(id, dto) + async update( + @IdParam() id: number, + @Body() dto: DictTypeDto, + @AuthUser() user: IAuthUser + ): Promise { + dto.updateBy = user.uid; + await this.dictTypeService.update(id, dto); } @Delete(':id') @ApiOperation({ summary: '删除指定的字典类型' }) @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { - await this.dictTypeService.delete(id) + await this.dictTypeService.delete(id); } } diff --git a/src/modules/system/dict-type/dict-type.dto.ts b/src/modules/system/dict-type/dict-type.dto.ts index c3809b4..010479f 100644 --- a/src/modules/system/dict-type/dict-type.dto.ts +++ b/src/modules/system/dict-type/dict-type.dto.ts @@ -1,40 +1,40 @@ -import { ApiProperty, PartialType } from '@nestjs/swagger' -import { IsInt, IsOptional, IsString, MinLength } from 'class-validator' +import { ApiProperty, PartialType } from '@nestjs/swagger'; +import { IsInt, IsOptional, IsString, MinLength } from 'class-validator'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; -import { DictTypeEntity } from './dict-type.entity' +import { DictTypeEntity } from './dict-type.entity'; export class DictTypeDto extends PartialType(DictTypeEntity) { @ApiProperty({ description: '字典类型名称' }) @IsString() @MinLength(1) - name: string + name: string; @ApiProperty({ description: '字典类型code' }) @IsString() @MinLength(3) - code: string + code: string; @ApiProperty({ description: '状态' }) @IsOptional() @IsInt() - status?: number + status?: number; @ApiProperty({ description: '备注' }) @IsOptional() @IsString() - remark?: string + remark?: string; } export class DictTypeQueryDto extends PagerDto { @ApiProperty({ description: '字典类型名称' }) @IsString() @IsOptional() - name: string + name: string; @ApiProperty({ description: '字典类型code' }) @IsString() @IsOptional() - code: string + code: string; } diff --git a/src/modules/system/dict-type/dict-type.entity.ts b/src/modules/system/dict-type/dict-type.entity.ts index 8d2608f..e4f0e8c 100644 --- a/src/modules/system/dict-type/dict-type.entity.ts +++ b/src/modules/system/dict-type/dict-type.entity.ts @@ -1,23 +1,23 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity } from 'typeorm'; -import { CompleteEntity } from '~/common/entity/common.entity' +import { CompleteEntity } from '~/common/entity/common.entity'; @Entity({ name: 'sys_dict_type' }) export class DictTypeEntity extends CompleteEntity { @Column({ type: 'varchar', length: 50 }) @ApiProperty({ description: '字典名称' }) - name: string + name: string; @Column({ type: 'varchar', length: 50, unique: true }) @ApiProperty({ description: '字典类型' }) - code: string + code: string; @Column({ type: 'tinyint', default: 1 }) @ApiProperty({ description: ' 状态' }) - status: number + status: number; @Column({ type: 'varchar', nullable: true }) @ApiProperty({ description: '备注' }) - remark: string + remark: string; } diff --git a/src/modules/system/dict-type/dict-type.module.ts b/src/modules/system/dict-type/dict-type.module.ts index 5f8f238..b5ad211 100644 --- a/src/modules/system/dict-type/dict-type.module.ts +++ b/src/modules/system/dict-type/dict-type.module.ts @@ -1,11 +1,11 @@ -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { DictTypeController } from './dict-type.controller' -import { DictTypeEntity } from './dict-type.entity' -import { DictTypeService } from './dict-type.service' +import { DictTypeController } from './dict-type.controller'; +import { DictTypeEntity } from './dict-type.entity'; +import { DictTypeService } from './dict-type.service'; -const services = [DictTypeService] +const services = [DictTypeService]; @Module({ imports: [TypeOrmModule.forFeature([DictTypeEntity])], diff --git a/src/modules/system/dict-type/dict-type.service.ts b/src/modules/system/dict-type/dict-type.service.ts index 04e6c47..2b730fc 100644 --- a/src/modules/system/dict-type/dict-type.service.ts +++ b/src/modules/system/dict-type/dict-type.service.ts @@ -1,21 +1,21 @@ -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; -import { Like, Repository } from 'typeorm' +import { Like, Repository } from 'typeorm'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { paginate } from '~/helper/paginate' -import { Pagination } from '~/helper/paginate/pagination' -import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { paginate } from '~/helper/paginate'; +import { Pagination } from '~/helper/paginate/pagination'; +import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity'; -import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto' +import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto'; @Injectable() export class DictTypeService { constructor( @InjectRepository(DictTypeEntity) - private dictTypeRepository: Repository, + private dictTypeRepository: Repository ) {} /** @@ -27,58 +27,56 @@ export class DictTypeService { name, code, }: DictTypeQueryDto): Promise> { - const queryBuilder = this.dictTypeRepository.createQueryBuilder('dict_type') - .where({ - ...(name && { name: Like(`%${name}%`) }), - ...(code && { code: Like(`%${code}%`) }), - }) + const queryBuilder = this.dictTypeRepository.createQueryBuilder('dict_type').where({ + ...(name && { name: Like(`%${name}%`) }), + ...(code && { code: Like(`%${code}%`) }), + }); - return paginate(queryBuilder, { page, pageSize }) + return paginate(queryBuilder, { page, pageSize }); } /** 一次性获取所有的字典类型 */ async getAll() { - return this.dictTypeRepository.find() + return this.dictTypeRepository.find(); } /** * 获取参数总数 */ async countConfigList(): Promise { - return this.dictTypeRepository.count() + return this.dictTypeRepository.count(); } /** * 新增 */ async create(dto: DictTypeDto): Promise { - await this.dictTypeRepository.insert(dto) + await this.dictTypeRepository.insert(dto); } /** * 更新 */ async update(id: number, dto: Partial): Promise { - await this.dictTypeRepository.update(id, dto) + await this.dictTypeRepository.update(id, dto); } /** * 删除 */ async delete(id: number): Promise { - await this.dictTypeRepository.delete(id) + await this.dictTypeRepository.delete(id); } /** * 查询单个 */ async findOne(id: number): Promise { - return this.dictTypeRepository.findOneBy({ id }) + return this.dictTypeRepository.findOneBy({ id }); } async isExistKey(name: string): Promise { - const result = await this.dictTypeRepository.findOneBy({ name }) - if (result) - throw new BusinessException(ErrorEnum.DICT_NAME_EXISTS) + const result = await this.dictTypeRepository.findOneBy({ name }); + if (result) throw new BusinessException(ErrorEnum.DICT_NAME_EXISTS); } } diff --git a/src/modules/system/log/dto/log.dto.ts b/src/modules/system/log/dto/log.dto.ts index 5cf0ff3..fa2f27e 100644 --- a/src/modules/system/log/dto/log.dto.ts +++ b/src/modules/system/log/dto/log.dto.ts @@ -1,57 +1,57 @@ -import { ApiProperty } from '@nestjs/swagger' -import { IsOptional, IsString } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; export class LoginLogQueryDto extends PagerDto { @ApiProperty({ description: '用户名' }) @IsString() @IsOptional() - username: string + username: string; @ApiProperty({ description: '登录IP' }) @IsOptional() @IsString() - ip?: string + ip?: string; @ApiProperty({ description: '登录地点' }) @IsOptional() @IsString() - address?: string + address?: string; @ApiProperty({ description: '登录时间' }) @IsOptional() - time?: string[] + time?: string[]; } export class TaskLogQueryDto extends PagerDto { @ApiProperty({ description: '用户名' }) @IsOptional() @IsString() - username: string + username: string; @ApiProperty({ description: '登录IP' }) @IsString() @IsOptional() - ip?: string + ip?: string; @ApiProperty({ description: '登录时间' }) @IsOptional() - time?: string[] + time?: string[]; } export class CaptchaLogQueryDto extends PagerDto { @ApiProperty({ description: '用户名' }) @IsOptional() @IsString() - username: string + username: string; @ApiProperty({ description: '验证码' }) @IsString() @IsOptional() - code?: string + code?: string; @ApiProperty({ description: '发送时间' }) @IsOptional() - time?: string[] + time?: string[]; } diff --git a/src/modules/system/log/entities/captcha-log.entity.ts b/src/modules/system/log/entities/captcha-log.entity.ts index 048bec8..a00bb5c 100644 --- a/src/modules/system/log/entities/captcha-log.entity.ts +++ b/src/modules/system/log/entities/captcha-log.entity.ts @@ -1,23 +1,23 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; @Entity({ name: 'sys_captcha_log' }) export class CaptchaLogEntity extends CommonEntity { @Column({ name: 'user_id', nullable: true }) @ApiProperty({ description: '用户ID' }) - userId: number + userId: number; @Column({ nullable: true }) @ApiProperty({ description: '账号' }) - account: string + account: string; @Column({ nullable: true }) @ApiProperty({ description: '验证码' }) - code: string + code: string; @Column({ nullable: true }) @ApiProperty({ description: '验证码提供方' }) - provider: 'sms' | 'email' + provider: 'sms' | 'email'; } diff --git a/src/modules/system/log/entities/login-log.entity.ts b/src/modules/system/log/entities/login-log.entity.ts index 7a56568..25d5826 100644 --- a/src/modules/system/log/entities/login-log.entity.ts +++ b/src/modules/system/log/entities/login-log.entity.ts @@ -1,29 +1,29 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; -import { UserEntity } from '../../../user/user.entity' +import { UserEntity } from '../../../user/user.entity'; @Entity({ name: 'sys_login_log' }) export class LoginLogEntity extends CommonEntity { @Column({ nullable: true }) @ApiProperty({ description: 'IP' }) - ip: string + ip: string; @Column({ nullable: true }) @ApiProperty({ description: '地址' }) - address: string + address: string; @Column({ nullable: true }) @ApiProperty({ description: '登录方式' }) - provider: string + provider: string; @Column({ length: 500, nullable: true }) @ApiProperty({ description: '浏览器ua' }) - ua: string + ua: string; @ManyToOne(() => UserEntity, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'user_id' }) - user: Relation + user: Relation; } diff --git a/src/modules/system/log/entities/task-log.entity.ts b/src/modules/system/log/entities/task-log.entity.ts index 1721d68..9d7325f 100644 --- a/src/modules/system/log/entities/task-log.entity.ts +++ b/src/modules/system/log/entities/task-log.entity.ts @@ -1,25 +1,25 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; -import { TaskEntity } from '../../task/task.entity' +import { TaskEntity } from '../../task/task.entity'; @Entity({ name: 'sys_task_log' }) export class TaskLogEntity extends CommonEntity { @Column({ type: 'tinyint', default: 0 }) @ApiProperty({ description: '任务状态:0失败,1成功' }) - status: number + status: number; @Column({ type: 'text', nullable: true }) @ApiProperty({ description: '任务日志信息' }) - detail: string + detail: string; @Column({ type: 'int', nullable: true, name: 'consume_time', default: 0 }) @ApiProperty({ description: '任务耗时' }) - consumeTime: number + consumeTime: number; @ManyToOne(() => TaskEntity) @JoinColumn({ name: 'task_id' }) - task: Relation + task: Relation; } diff --git a/src/modules/system/log/log.controller.ts b/src/modules/system/log/log.controller.ts index 489f3cb..144de21 100644 --- a/src/modules/system/log/log.controller.ts +++ b/src/modules/system/log/log.controller.ts @@ -1,28 +1,24 @@ -import { Controller, Get, Query } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Controller, Get, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { Pagination } from '~/helper/paginate/pagination' -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { Pagination } from '~/helper/paginate/pagination'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; -import { - CaptchaLogQueryDto, - LoginLogQueryDto, - TaskLogQueryDto, -} from './dto/log.dto' -import { CaptchaLogEntity } from './entities/captcha-log.entity' -import { TaskLogEntity } from './entities/task-log.entity' -import { LoginLogInfo } from './models/log.model' -import { CaptchaLogService } from './services/captcha-log.service' -import { LoginLogService } from './services/login-log.service' -import { TaskLogService } from './services/task-log.service' +import { CaptchaLogQueryDto, LoginLogQueryDto, TaskLogQueryDto } from './dto/log.dto'; +import { CaptchaLogEntity } from './entities/captcha-log.entity'; +import { TaskLogEntity } from './entities/task-log.entity'; +import { LoginLogInfo } from './models/log.model'; +import { CaptchaLogService } from './services/captcha-log.service'; +import { LoginLogService } from './services/login-log.service'; +import { TaskLogService } from './services/task-log.service'; export const permissions = definePermission('system:log', { TaskList: 'task:list', LogList: 'login:list', CaptchaList: 'captcha:list', -} as const) +} as const); @ApiSecurityAuth() @ApiTags('System - 日志模块') @@ -31,17 +27,15 @@ export class LogController { constructor( private loginLogService: LoginLogService, private taskService: TaskLogService, - private captchaLogService: CaptchaLogService, + private captchaLogService: CaptchaLogService ) {} @Get('login/list') @ApiOperation({ summary: '查询登录日志列表' }) @ApiResult({ type: [LoginLogInfo], isPage: true }) @Perm(permissions.TaskList) - async loginLogPage( - @Query() dto: LoginLogQueryDto, - ): Promise> { - return this.loginLogService.list(dto) + async loginLogPage(@Query() dto: LoginLogQueryDto): Promise> { + return this.loginLogService.list(dto); } @Get('task/list') @@ -49,16 +43,14 @@ export class LogController { @ApiResult({ type: [TaskLogEntity], isPage: true }) @Perm(permissions.LogList) async taskList(@Query() dto: TaskLogQueryDto) { - return this.taskService.list(dto) + return this.taskService.list(dto); } @Get('captcha/list') @ApiOperation({ summary: '查询验证码日志列表' }) @ApiResult({ type: [CaptchaLogEntity], isPage: true }) @Perm(permissions.CaptchaList) - async captchaList( - @Query() dto: CaptchaLogQueryDto, - ): Promise> { - return this.captchaLogService.paginate(dto) + async captchaList(@Query() dto: CaptchaLogQueryDto): Promise> { + return this.captchaLogService.paginate(dto); } } diff --git a/src/modules/system/log/log.module.ts b/src/modules/system/log/log.module.ts index 27f9f04..7f1b5b8 100644 --- a/src/modules/system/log/log.module.ts +++ b/src/modules/system/log/log.module.ts @@ -1,17 +1,17 @@ -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { UserModule } from '../../user/user.module' +import { UserModule } from '../../user/user.module'; -import { CaptchaLogEntity } from './entities/captcha-log.entity' -import { LoginLogEntity } from './entities/login-log.entity' -import { TaskLogEntity } from './entities/task-log.entity' -import { LogController } from './log.controller' -import { CaptchaLogService } from './services/captcha-log.service' -import { LoginLogService } from './services/login-log.service' -import { TaskLogService } from './services/task-log.service' +import { CaptchaLogEntity } from './entities/captcha-log.entity'; +import { LoginLogEntity } from './entities/login-log.entity'; +import { TaskLogEntity } from './entities/task-log.entity'; +import { LogController } from './log.controller'; +import { CaptchaLogService } from './services/captcha-log.service'; +import { LoginLogService } from './services/login-log.service'; +import { TaskLogService } from './services/task-log.service'; -const providers = [LoginLogService, TaskLogService, CaptchaLogService] +const providers = [LoginLogService, TaskLogService, CaptchaLogService]; @Module({ imports: [ diff --git a/src/modules/system/log/models/log.model.ts b/src/modules/system/log/models/log.model.ts index f128be0..580f227 100644 --- a/src/modules/system/log/models/log.model.ts +++ b/src/modules/system/log/models/log.model.ts @@ -1,47 +1,47 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; export class LoginLogInfo { @ApiProperty({ description: '日志编号' }) - id: number + id: number; @ApiProperty({ description: '登录ip', example: '1.1.1.1' }) - ip: string + ip: string; @ApiProperty({ description: '登录地址' }) - address: string + address: string; @ApiProperty({ description: '系统', example: 'Windows 10' }) - os: string + os: string; @ApiProperty({ description: '浏览器', example: 'Chrome' }) - browser: string + browser: string; @ApiProperty({ description: '登录用户名', example: 'admin' }) - username: string + username: string; @ApiProperty({ description: '登录时间', example: '2023-12-22 16:46:20.333843' }) - time: string + time: string; } export class TaskLogInfo { @ApiProperty({ description: '日志编号' }) - id: number + id: number; @ApiProperty({ description: '任务编号' }) - taskId: number + taskId: number; @ApiProperty({ description: '任务名称' }) - name: string + name: string; @ApiProperty({ description: '创建时间' }) - createdAt: string + createdAt: string; @ApiProperty({ description: '耗时' }) - consumeTime: number + consumeTime: number; @ApiProperty({ description: '执行信息' }) - detail: string + detail: string; @ApiProperty({ description: '任务执行状态' }) - status: number + status: number; } diff --git a/src/modules/system/log/services/captcha-log.service.ts b/src/modules/system/log/services/captcha-log.service.ts index 5bc01e1..b45c04c 100644 --- a/src/modules/system/log/services/captcha-log.service.ts +++ b/src/modules/system/log/services/captcha-log.service.ts @@ -1,50 +1,50 @@ -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; -import { LessThan, Repository } from 'typeorm' +import { LessThan, Repository } from 'typeorm'; -import { paginate } from '~/helper/paginate' +import { paginate } from '~/helper/paginate'; -import { CaptchaLogQueryDto } from '../dto/log.dto' -import { CaptchaLogEntity } from '../entities/captcha-log.entity' +import { CaptchaLogQueryDto } from '../dto/log.dto'; +import { CaptchaLogEntity } from '../entities/captcha-log.entity'; @Injectable() export class CaptchaLogService { constructor( @InjectRepository(CaptchaLogEntity) - private captchaLogRepository: Repository, + private captchaLogRepository: Repository ) {} async create( account: string, code: string, provider: 'sms' | 'email', - uid?: number, + uid?: number ): Promise { await this.captchaLogRepository.save({ account, code, provider, userId: uid, - }) + }); } async paginate({ page, pageSize }: CaptchaLogQueryDto) { const queryBuilder = await this.captchaLogRepository .createQueryBuilder('captcha_log') - .orderBy('captcha_log.id', 'DESC') + .orderBy('captcha_log.id', 'DESC'); return paginate(queryBuilder, { page, pageSize, - }) + }); } async clearLog(): Promise { - await this.captchaLogRepository.clear() + await this.captchaLogRepository.clear(); } async clearLogBeforeTime(time: Date): Promise { - await this.captchaLogRepository.delete({ createdAt: LessThan(time) }) + await this.captchaLogRepository.delete({ createdAt: LessThan(time) }); } } diff --git a/src/modules/system/log/services/login-log.service.ts b/src/modules/system/log/services/login-log.service.ts index b3420de..256c78b 100644 --- a/src/modules/system/log/services/login-log.service.ts +++ b/src/modules/system/log/services/login-log.service.ts @@ -1,20 +1,20 @@ -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; -import { Between, LessThan, Like, Repository } from 'typeorm' +import { Between, LessThan, Like, Repository } from 'typeorm'; -import UAParser from 'ua-parser-js' +import UAParser from 'ua-parser-js'; -import { paginateRaw } from '~/helper/paginate' +import { paginateRaw } from '~/helper/paginate'; -import { getIpAddress } from '~/utils/ip.util' +import { getIpAddress } from '~/utils/ip.util'; -import { LoginLogQueryDto } from '../dto/log.dto' -import { LoginLogEntity } from '../entities/login-log.entity' -import { LoginLogInfo } from '../models/log.model' +import { LoginLogQueryDto } from '../dto/log.dto'; +import { LoginLogEntity } from '../entities/login-log.entity'; +import { LoginLogInfo } from '../models/log.model'; async function parseLoginLog(e: any, parser: UAParser): Promise { - const uaResult = parser.setUA(e.login_log_ua).getResult() + const uaResult = parser.setUA(e.login_log_ua).getResult(); return { id: e.login_log_id, @@ -24,41 +24,32 @@ async function parseLoginLog(e: any, parser: UAParser): Promise { browser: `${`${uaResult.browser.name ?? ''} `}${uaResult.browser.version}`, username: e.user_username, time: e.login_log_created_at, - } + }; } @Injectable() export class LoginLogService { constructor( @InjectRepository(LoginLogEntity) - private loginLogRepository: Repository, - + private loginLogRepository: Repository ) {} async create(uid: number, ip: string, ua: string): Promise { try { - const address = await getIpAddress(ip) + const address = await getIpAddress(ip); await this.loginLogRepository.save({ ip, ua, address, user: { id: uid }, - }) - } - catch (e) { - console.error(e) + }); + } catch (e) { + console.error(e); } } - async list({ - page, - pageSize, - username, - ip, - address, - time, - }: LoginLogQueryDto) { + async list({ page, pageSize, username, ip, address, time }: LoginLogQueryDto) { const queryBuilder = await this.loginLogRepository .createQueryBuilder('login_log') .innerJoinAndSelect('login_log.user', 'user') @@ -72,29 +63,27 @@ export class LoginLogService { }, }), }) - .orderBy('login_log.created_at', 'DESC') + .orderBy('login_log.created_at', 'DESC'); const { items, ...rest } = await paginateRaw(queryBuilder, { page, pageSize, - }) + }); - const parser = new UAParser() - const loginLogInfos = await Promise.all( - items.map(item => parseLoginLog(item, parser)), - ) + const parser = new UAParser(); + const loginLogInfos = await Promise.all(items.map(item => parseLoginLog(item, parser))); return { items: loginLogInfos, ...rest, - } + }; } async clearLog(): Promise { - await this.loginLogRepository.clear() + await this.loginLogRepository.clear(); } async clearLogBeforeTime(time: Date): Promise { - await this.loginLogRepository.delete({ createdAt: LessThan(time) }) + await this.loginLogRepository.delete({ createdAt: LessThan(time) }); } } diff --git a/src/modules/system/log/services/task-log.service.ts b/src/modules/system/log/services/task-log.service.ts index b57410f..e0da14a 100644 --- a/src/modules/system/log/services/task-log.service.ts +++ b/src/modules/system/log/services/task-log.service.ts @@ -1,52 +1,47 @@ -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; -import { LessThan, Repository } from 'typeorm' +import { LessThan, Repository } from 'typeorm'; -import { paginate } from '~/helper/paginate' +import { paginate } from '~/helper/paginate'; -import { TaskLogQueryDto } from '../dto/log.dto' -import { TaskLogEntity } from '../entities/task-log.entity' +import { TaskLogQueryDto } from '../dto/log.dto'; +import { TaskLogEntity } from '../entities/task-log.entity'; @Injectable() export class TaskLogService { constructor( @InjectRepository(TaskLogEntity) - private taskLogRepository: Repository, + private taskLogRepository: Repository ) {} - async create( - tid: number, - status: number, - time?: number, - err?: string, - ): Promise { + async create(tid: number, status: number, time?: number, err?: string): Promise { const result = await this.taskLogRepository.save({ status, detail: err, time, task: { id: tid }, - }) - return result.id + }); + return result.id; } async list({ page, pageSize }: TaskLogQueryDto) { const queryBuilder = await this.taskLogRepository .createQueryBuilder('task_log') .leftJoinAndSelect('task_log.task', 'task') - .orderBy('task_log.id', 'DESC') + .orderBy('task_log.id', 'DESC'); return paginate(queryBuilder, { page, pageSize, - }) + }); } async clearLog(): Promise { - await this.taskLogRepository.clear() + await this.taskLogRepository.clear(); } async clearLogBeforeTime(time: Date): Promise { - await this.taskLogRepository.delete({ createdAt: LessThan(time) }) + await this.taskLogRepository.delete({ createdAt: LessThan(time) }); } } diff --git a/src/modules/system/menu/menu.controller.ts b/src/modules/system/menu/menu.controller.ts index 2de0370..af30512 100644 --- a/src/modules/system/menu/menu.controller.ts +++ b/src/modules/system/menu/menu.controller.ts @@ -7,18 +7,22 @@ import { Post, Put, Query, -} from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' -import { flattenDeep } from 'lodash' +} from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { flattenDeep } from 'lodash'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { Perm, definePermission, getDefinePermissions } from '~/modules/auth/decorators/permission.decorator' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { + Perm, + definePermission, + getDefinePermissions, +} from '~/modules/auth/decorators/permission.decorator'; -import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto' -import { MenuItemInfo } from './menu.model' -import { MenuService } from './menu.service' +import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto'; +import { MenuItemInfo } from './menu.model'; +import { MenuService } from './menu.service'; export const permissions = definePermission('system:menu', { LIST: 'list', @@ -26,7 +30,7 @@ export const permissions = definePermission('system:menu', { READ: 'read', UPDATE: 'update', DELETE: 'delete', -} as const) +} as const); @ApiTags('System - 菜单权限模块') @ApiSecurityAuth() @@ -39,14 +43,14 @@ export class MenuController { @ApiResult({ type: [MenuItemInfo] }) @Perm(permissions.LIST) async list(@Query() dto: MenuQueryDto) { - return this.menuService.list(dto) + return this.menuService.list(dto); } @Get(':id') @ApiOperation({ summary: '获取菜单或权限信息' }) @Perm(permissions.READ) async info(@IdParam() id: number) { - return this.menuService.getMenuItemAndParentInfo(id) + return this.menuService.getMenuItemAndParentInfo(id); } @Post() @@ -54,31 +58,28 @@ export class MenuController { @Perm(permissions.CREATE) async create(@Body() dto: MenuDto): Promise { // check - await this.menuService.check(dto) - if (!dto.parentId) - dto.parentId = null + await this.menuService.check(dto); + if (!dto.parentId) dto.parentId = null; - await this.menuService.create(dto) + await this.menuService.create(dto); if (dto.type === 2) { // 如果是权限发生更改,则刷新所有在线用户的权限 - await this.menuService.refreshOnlineUserPerms() + await this.menuService.refreshOnlineUserPerms(); } } @Put(':id') @ApiOperation({ summary: '更新菜单或权限' }) @Perm(permissions.UPDATE) - async update( - @IdParam() id: number, @Body() dto: MenuUpdateDto): Promise { + async update(@IdParam() id: number, @Body() dto: MenuUpdateDto): Promise { // check - await this.menuService.check(dto) - if (dto.parentId === -1 || !dto.parentId) - dto.parentId = null + await this.menuService.check(dto); + if (dto.parentId === -1 || !dto.parentId) dto.parentId = null; - await this.menuService.update(id, dto) + await this.menuService.update(id, dto); if (dto.type === 2) { // 如果是权限发生更改,则刷新所有在线用户的权限 - await this.menuService.refreshOnlineUserPerms() + await this.menuService.refreshOnlineUserPerms(); } } @@ -87,18 +88,18 @@ export class MenuController { @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { if (await this.menuService.checkRoleByMenuId(id)) - throw new BadRequestException('该菜单存在关联角色,无法删除') + throw new BadRequestException('该菜单存在关联角色,无法删除'); // 如果有子目录,一并删除 - const childMenus = await this.menuService.findChildMenus(id) - await this.menuService.deleteMenuItem(flattenDeep([id, childMenus])) + const childMenus = await this.menuService.findChildMenus(id); + await this.menuService.deleteMenuItem(flattenDeep([id, childMenus])); // 刷新在线用户权限 - await this.menuService.refreshOnlineUserPerms() + await this.menuService.refreshOnlineUserPerms(); } @Get('permissions') @ApiOperation({ summary: '获取后端定义的所有权限集' }) async getPermissions(): Promise { - return getDefinePermissions() + return getDefinePermissions(); } } diff --git a/src/modules/system/menu/menu.dto.ts b/src/modules/system/menu/menu.dto.ts index 5a24aab..4d734e5 100644 --- a/src/modules/system/menu/menu.dto.ts +++ b/src/modules/system/menu/menu.dto.ts @@ -1,4 +1,4 @@ -import { ApiProperty, PartialType } from '@nestjs/swagger' +import { ApiProperty, PartialType } from '@nestjs/swagger'; import { IsBoolean, IsIn, @@ -8,79 +8,79 @@ import { Min, MinLength, ValidateIf, -} from 'class-validator' +} from 'class-validator'; export class MenuDto { @ApiProperty({ description: '菜单类型' }) @IsIn([0, 1, 2]) - type: number + type: number; @ApiProperty({ description: '父级菜单' }) @IsOptional() - parentId: number + parentId: number; @ApiProperty({ description: '菜单或权限名称' }) @IsString() @MinLength(2) - name: string + name: string; @ApiProperty({ description: '排序' }) @IsInt() @Min(0) - orderNo: number + orderNo: number; @ApiProperty({ description: '前端路由地址' }) // @Matches(/^[/]$/) @ValidateIf(o => o.type !== 2) - path: string + path: string; @ApiProperty({ description: '是否外链', default: false }) @ValidateIf(o => o.type !== 2) @IsBoolean() - isExt: boolean + isExt: boolean; @ApiProperty({ description: '外链打开方式', default: 1 }) @ValidateIf((o: MenuDto) => o.isExt) @IsIn([1, 2]) - extOpenMode: number + extOpenMode: number; @ApiProperty({ description: '菜单是否显示', default: 1 }) @ValidateIf((o: MenuDto) => o.type !== 2) @IsIn([0, 1]) - show: number + show: number; @ApiProperty({ description: '设置当前路由高亮的菜单项,一般用于详情页' }) @ValidateIf((o: MenuDto) => o.type !== 2 && o.show === 0) @IsString() @IsOptional() - activeMenu?: string + activeMenu?: string; @ApiProperty({ description: '是否开启页面缓存', default: 1 }) @ValidateIf((o: MenuDto) => o.type === 1) @IsIn([0, 1]) - keepAlive: number + keepAlive: number; @ApiProperty({ description: '状态', default: 1 }) @IsIn([0, 1]) - status: number + status: number; @ApiProperty({ description: '菜单图标' }) @IsOptional() @ValidateIf((o: MenuDto) => o.type !== 2) @IsString() - icon?: string + icon?: string; @ApiProperty({ description: '对应权限' }) @ValidateIf((o: MenuDto) => o.type === 2) @IsString() @IsOptional() - permission: string + permission: string; @ApiProperty({ description: '菜单路由路径或外链' }) @ValidateIf((o: MenuDto) => o.type !== 2) @IsString() @IsOptional() - component?: string + component?: string; } export class MenuUpdateDto extends PartialType(MenuDto) {} diff --git a/src/modules/system/menu/menu.entity.ts b/src/modules/system/menu/menu.entity.ts index 950d057..e31a638 100644 --- a/src/modules/system/menu/menu.entity.ts +++ b/src/modules/system/menu/menu.entity.ts @@ -1,55 +1,55 @@ -import { Column, Entity, ManyToMany, Relation } from 'typeorm' +import { Column, Entity, ManyToMany, Relation } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; -import { RoleEntity } from '../role/role.entity' +import { RoleEntity } from '../role/role.entity'; @Entity({ name: 'sys_menu' }) export class MenuEntity extends CommonEntity { @Column({ name: 'parent_id', nullable: true }) - parentId: number + parentId: number; @Column() - name: string + name: string; @Column({ nullable: true }) - path: string + path: string; @Column({ nullable: true }) - permission: string + permission: string; @Column({ type: 'tinyint', default: 0 }) - type: number + type: number; @Column({ nullable: true, default: '' }) - icon: string + icon: string; @Column({ name: 'order_no', type: 'int', nullable: true, default: 0 }) - orderNo: number + orderNo: number; @Column({ name: 'component', nullable: true }) - component: string + component: string; @Column({ name: 'is_ext', type: 'boolean', default: false }) - isExt: boolean + isExt: boolean; @Column({ name: 'ext_open_mode', type: 'tinyint', default: 1 }) - extOpenMode: number + extOpenMode: number; @Column({ name: 'keep_alive', type: 'tinyint', default: 1 }) - keepAlive: number + keepAlive: number; @Column({ type: 'tinyint', default: 1 }) - show: number + show: number; @Column({ name: 'active_menu', nullable: true }) - activeMenu: string + activeMenu: string; @Column({ type: 'tinyint', default: 1 }) - status: number + status: number; @ManyToMany(() => RoleEntity, role => role.menus, { onDelete: 'CASCADE', }) - roles: Relation + roles: Relation; } diff --git a/src/modules/system/menu/menu.model.ts b/src/modules/system/menu/menu.model.ts index 36ca084..27a3b4c 100644 --- a/src/modules/system/menu/menu.model.ts +++ b/src/modules/system/menu/menu.model.ts @@ -1,8 +1,8 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; -import { MenuEntity } from './menu.entity' +import { MenuEntity } from './menu.entity'; export class MenuItemInfo extends MenuEntity { @ApiProperty({ type: [MenuItemInfo] }) - children: MenuItemInfo[] + children: MenuItemInfo[]; } diff --git a/src/modules/system/menu/menu.module.ts b/src/modules/system/menu/menu.module.ts index cdb6ead..bfe18d5 100644 --- a/src/modules/system/menu/menu.module.ts +++ b/src/modules/system/menu/menu.module.ts @@ -1,22 +1,18 @@ -import { Module, forwardRef } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { SseService } from '~/modules/sse/sse.service' +import { SseService } from '~/modules/sse/sse.service'; -import { RoleModule } from '../role/role.module' +import { RoleModule } from '../role/role.module'; -import { MenuController } from './menu.controller' -import { MenuEntity } from './menu.entity' -import { MenuService } from './menu.service' +import { MenuController } from './menu.controller'; +import { MenuEntity } from './menu.entity'; +import { MenuService } from './menu.service'; -const providers = [MenuService, SseService] +const providers = [MenuService, SseService]; @Module({ - imports: [ - TypeOrmModule.forFeature([MenuEntity]), - - forwardRef(() => RoleModule), - ], + imports: [TypeOrmModule.forFeature([MenuEntity]), forwardRef(() => RoleModule)], controllers: [MenuController], providers: [...providers], exports: [TypeOrmModule, ...providers], diff --git a/src/modules/system/menu/menu.service.ts b/src/modules/system/menu/menu.service.ts index 85ce773..9adaf75 100644 --- a/src/modules/system/menu/menu.service.ts +++ b/src/modules/system/menu/menu.service.ts @@ -1,23 +1,23 @@ -import { InjectRedis } from '@liaoliaots/nestjs-redis' -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import Redis from 'ioredis' -import { concat, isEmpty, isNumber, uniq } from 'lodash' +import { InjectRedis } from '@liaoliaots/nestjs-redis'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import Redis from 'ioredis'; +import { concat, isEmpty, isNumber, uniq } from 'lodash'; -import { In, IsNull, Like, Not, Repository } from 'typeorm' +import { In, IsNull, Like, Not, Repository } from 'typeorm'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { RedisKeys } from '~/constants/cache.constant' -import { ErrorEnum } from '~/constants/error-code.constant' -import { genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey' -import { SseService } from '~/modules/sse/sse.service' -import { MenuEntity } from '~/modules/system/menu/menu.entity' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { RedisKeys } from '~/constants/cache.constant'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey'; +import { SseService } from '~/modules/sse/sse.service'; +import { MenuEntity } from '~/modules/system/menu/menu.entity'; -import { deleteEmptyChildren, generatorMenu, generatorRouters } from '~/utils' +import { deleteEmptyChildren, generatorMenu, generatorRouters } from '~/utils'; -import { RoleService } from '../role/role.service' +import { RoleService } from '../role/role.service'; -import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto' +import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto'; @Injectable() export class MenuService { @@ -26,19 +26,13 @@ export class MenuService { @InjectRepository(MenuEntity) private menuRepository: Repository, private roleService: RoleService, - private sseService: SseService, + private sseService: SseService ) {} /** * 获取所有菜单以及权限 */ - async list({ - name, - path, - permission, - component, - status, - }: MenuQueryDto): Promise { + async list({ name, path, permission, component, status }: MenuQueryDto): Promise { const menus = await this.menuRepository.find({ where: { ...(name && { name: Like(`%${name}%`) }), @@ -48,51 +42,49 @@ export class MenuService { ...(isNumber(status) ? { status } : null), }, order: { orderNo: 'ASC' }, - }) - const menuList = generatorMenu(menus) + }); + const menuList = generatorMenu(menus); if (!isEmpty(menuList)) { - deleteEmptyChildren(menuList) - return menuList + deleteEmptyChildren(menuList); + return menuList; } // 如果生产树形结构为空,则返回原始菜单列表 - return menus + return menus; } async create(menu: MenuDto): Promise { - const result = await this.menuRepository.save(menu) - this.sseService.noticeClientToUpdateMenusByMenuIds([result.id]) + const result = await this.menuRepository.save(menu); + this.sseService.noticeClientToUpdateMenusByMenuIds([result.id]); } async update(id: number, menu: MenuUpdateDto): Promise { - await this.menuRepository.update(id, menu) - this.sseService.noticeClientToUpdateMenusByMenuIds([id]) + await this.menuRepository.update(id, menu); + this.sseService.noticeClientToUpdateMenusByMenuIds([id]); } /** * 根据角色获取所有菜单 */ async getMenus(uid: number): Promise { - const roleIds = await this.roleService.getRoleIdsByUser(uid) - let menus: MenuEntity[] = [] + const roleIds = await this.roleService.getRoleIdsByUser(uid); + let menus: MenuEntity[] = []; - if (isEmpty(roleIds)) - return generatorRouters([]) + if (isEmpty(roleIds)) return generatorRouters([]); if (this.roleService.hasAdminRole(roleIds)) { - menus = await this.menuRepository.find({ order: { orderNo: 'ASC' } }) - } - else { + menus = await this.menuRepository.find({ order: { orderNo: 'ASC' } }); + } else { menus = await this.menuRepository .createQueryBuilder('menu') .innerJoinAndSelect('menu.roles', 'role') .andWhere('role.id IN (:...roleIds)', { roleIds }) .orderBy('menu.order_no', 'ASC') - .getMany() + .getMany(); } - const menuList = generatorRouters(menus) - return menuList + const menuList = generatorRouters(menus); + return menuList; } /** @@ -101,18 +93,15 @@ export class MenuService { async check(dto: Partial): Promise { if (dto.type === 2 && !dto.parentId) { // 无法直接创建权限,必须有parent - throw new BusinessException(ErrorEnum.PERMISSION_REQUIRES_PARENT) + throw new BusinessException(ErrorEnum.PERMISSION_REQUIRES_PARENT); } if (dto.type === 1 && dto.parentId) { - const parent = await this.getMenuItemInfo(dto.parentId) - if (isEmpty(parent)) - throw new BusinessException(ErrorEnum.PARENT_MENU_NOT_FOUND) + const parent = await this.getMenuItemInfo(dto.parentId); + if (isEmpty(parent)) throw new BusinessException(ErrorEnum.PARENT_MENU_NOT_FOUND); if (parent && parent.type === 1) { // 当前新增为菜单但父节点也为菜单时为非法操作 - throw new BusinessException( - ErrorEnum.ILLEGAL_OPERATION_DIRECTORY_PARENT, - ) + throw new BusinessException(ErrorEnum.ILLEGAL_OPERATION_DIRECTORY_PARENT); } } } @@ -121,8 +110,8 @@ export class MenuService { * 查找当前菜单下的子菜单,目录以及菜单 */ async findChildMenus(mid: number): Promise { - const allMenus: any = [] - const menus = await this.menuRepository.findBy({ parentId: mid }) + const allMenus: any = []; + const menus = await this.menuRepository.findBy({ parentId: mid }); // if (_.isEmpty(menus)) { // return allMenus; // } @@ -130,12 +119,12 @@ export class MenuService { for (const menu of menus) { if (menu.type !== 2) { // 子目录下是菜单或目录,继续往下级查找 - const c = await this.findChildMenus(menu.id) - allMenus.push(c) + const c = await this.findChildMenus(menu.id); + allMenus.push(c); } - allMenus.push(menu.id) + allMenus.push(menu.id); } - return allMenus + return allMenus; } /** @@ -143,46 +132,44 @@ export class MenuService { * @param mid menu id */ async getMenuItemInfo(mid: number): Promise { - const menu = await this.menuRepository.findOneBy({ id: mid }) - return menu + const menu = await this.menuRepository.findOneBy({ id: mid }); + return menu; } /** * 获取某个菜单以及关联的父菜单的信息 */ async getMenuItemAndParentInfo(mid: number) { - const menu = await this.menuRepository.findOneBy({ id: mid }) - let parentMenu: MenuEntity | undefined + const menu = await this.menuRepository.findOneBy({ id: mid }); + let parentMenu: MenuEntity | undefined; if (menu && menu.parentId) - parentMenu = await this.menuRepository.findOneBy({ id: menu.parentId }) + parentMenu = await this.menuRepository.findOneBy({ id: menu.parentId }); - return { menu, parentMenu } + return { menu, parentMenu }; } /** * 查找节点路由是否存在 */ async findRouterExist(path: string): Promise { - const menus = await this.menuRepository.findOneBy({ path }) - return !isEmpty(menus) + const menus = await this.menuRepository.findOneBy({ path }); + return !isEmpty(menus); } /** * 获取当前用户的所有权限 */ async getPermissions(uid: number): Promise { - const roleIds = await this.roleService.getRoleIdsByUser(uid) - let permission: any[] = [] - let result: any = null + const roleIds = await this.roleService.getRoleIdsByUser(uid); + let permission: any[] = []; + let result: any = null; if (this.roleService.hasAdminRole(roleIds)) { result = await this.menuRepository.findBy({ permission: Not(IsNull()), type: In([1, 2]), - }) - } - else { - if (isEmpty(roleIds)) - return permission + }); + } else { + if (isEmpty(roleIds)) return permission; result = await this.menuRepository .createQueryBuilder('menu') @@ -190,37 +177,36 @@ export class MenuService { .andWhere('role.id IN (:...roleIds)', { roleIds }) .andWhere('menu.type IN (1,2)') .andWhere('menu.permission IS NOT NULL') - .getMany() + .getMany(); } if (!isEmpty(result)) { - result.forEach((e) => { - if (e.permission) - permission = concat(permission, e.permission.split(',')) - }) - permission = uniq(permission) + result.forEach(e => { + if (e.permission) permission = concat(permission, e.permission.split(',')); + }); + permission = uniq(permission); } - return permission + return permission; } /** * 删除多项菜单 */ async deleteMenuItem(mids: number[]): Promise { - await this.menuRepository.delete(mids) + await this.menuRepository.delete(mids); } /** * 刷新指定用户ID的权限 */ async refreshPerms(uid: number): Promise { - const perms = await this.getPermissions(uid) - const online = await this.redis.get(genAuthTokenKey(uid)) + const perms = await this.getPermissions(uid); + const online = await this.redis.get(genAuthTokenKey(uid)); if (online) { // 判断是否在线 - await this.redis.set(genAuthPermKey(uid), JSON.stringify(perms)) - console.log('refreshPerms') + await this.redis.set(genAuthPermKey(uid), JSON.stringify(perms)); + console.log('refreshPerms'); - this.sseService.noticeClientToUpdateMenusByUserIds([uid]) + this.sseService.noticeClientToUpdateMenusByUserIds([uid]); } } @@ -228,19 +214,19 @@ export class MenuService { * 刷新所有在线用户的权限 */ async refreshOnlineUserPerms(): Promise { - const onlineUserIds: string[] = await this.redis.keys(genAuthTokenKey('*')) + const onlineUserIds: string[] = await this.redis.keys(genAuthTokenKey('*')); if (onlineUserIds && onlineUserIds.length > 0) { const promiseArr = onlineUserIds .map(i => Number.parseInt(i.split(RedisKeys.AUTH_TOKEN_PREFIX)[1])) .filter(i => i) - .map(async (uid) => { - const perms = await this.getPermissions(uid) - await this.redis.set(genAuthPermKey(uid), JSON.stringify(perms)) - return uid - }) - const uids = await Promise.all(promiseArr) - console.log('refreshOnlineUserPerms') - this.sseService.noticeClientToUpdateMenusByUserIds(uids) + .map(async uid => { + const perms = await this.getPermissions(uid); + await this.redis.set(genAuthPermKey(uid), JSON.stringify(perms)); + return uid; + }); + const uids = await Promise.all(promiseArr); + console.log('refreshOnlineUserPerms'); + this.sseService.noticeClientToUpdateMenusByUserIds(uids); } } @@ -254,6 +240,6 @@ export class MenuService { id, }, }, - })) + })); } } diff --git a/src/modules/system/online/online.controller.ts b/src/modules/system/online/online.controller.ts index c484037..e1069cd 100644 --- a/src/modules/system/online/online.controller.ts +++ b/src/modules/system/online/online.controller.ts @@ -1,23 +1,23 @@ -import { Body, Controller, Get, Post } from '@nestjs/common' -import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Get, Post } from '@nestjs/common'; +import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; -import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator'; -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; -import { KickDto } from './online.dto' -import { OnlineUserInfo } from './online.model' -import { OnlineService } from './online.service' +import { KickDto } from './online.dto'; +import { OnlineUserInfo } from './online.model'; +import { OnlineService } from './online.service'; export const permissions = definePermission('system:online', { LIST: 'list', KICK: 'kick', -} as const) +} as const); @ApiTags('System - 在线用户模块') @ApiSecurityAuth() @@ -31,16 +31,15 @@ export class OnlineController { @ApiResult({ type: [OnlineUserInfo] }) @Perm(permissions.LIST) async list(@AuthUser() user: IAuthUser): Promise { - return this.onlineService.listOnlineUser(user.uid) + return this.onlineService.listOnlineUser(user.uid); } @Post('kick') @ApiOperation({ summary: '下线指定在线用户' }) @Perm(permissions.KICK) async kick(@Body() dto: KickDto, @AuthUser() user: IAuthUser): Promise { - if (dto.id === user.uid) - throw new BusinessException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER) + if (dto.id === user.uid) throw new BusinessException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER); - await this.onlineService.kickUser(dto.id, user.uid) + await this.onlineService.kickUser(dto.id, user.uid); } } diff --git a/src/modules/system/online/online.dto.ts b/src/modules/system/online/online.dto.ts index fbe8f8b..736a2c8 100644 --- a/src/modules/system/online/online.dto.ts +++ b/src/modules/system/online/online.dto.ts @@ -1,8 +1,8 @@ -import { ApiProperty } from '@nestjs/swagger' -import { IsInt } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { IsInt } from 'class-validator'; export class KickDto { @ApiProperty({ description: '需要下线的角色ID' }) @IsInt() - id: number + id: number; } diff --git a/src/modules/system/online/online.model.ts b/src/modules/system/online/online.model.ts index a08f812..9c140d3 100644 --- a/src/modules/system/online/online.model.ts +++ b/src/modules/system/online/online.model.ts @@ -1,27 +1,27 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; export class OnlineUserInfo { @ApiProperty({ description: '最近的一条登录日志ID' }) - id: number + id: number; @ApiProperty({ description: '登录IP' }) - ip: string + ip: string; @ApiProperty({ description: '登录地点' }) - address: string + address: string; @ApiProperty({ description: '用户名' }) - username: string + username: string; @ApiProperty({ description: '是否当前' }) - isCurrent: boolean + isCurrent: boolean; @ApiProperty({ description: '系统' }) - os: string + os: string; @ApiProperty({ description: '浏览器' }) - browser: string + browser: string; @ApiProperty({ description: '是否禁用' }) - disable: boolean + disable: boolean; } diff --git a/src/modules/system/online/online.module.ts b/src/modules/system/online/online.module.ts index 4de292a..793e71b 100644 --- a/src/modules/system/online/online.module.ts +++ b/src/modules/system/online/online.module.ts @@ -1,16 +1,16 @@ -import { Module, forwardRef } from '@nestjs/common' +import { Module, forwardRef } from '@nestjs/common'; -import { AuthModule } from '~/modules/auth/auth.module' -import { SocketModule } from '~/socket/socket.module' +import { AuthModule } from '~/modules/auth/auth.module'; +import { SocketModule } from '~/socket/socket.module'; -import { UserModule } from '../../user/user.module' -import { RoleModule } from '../role/role.module' -import { SystemModule } from '../system.module' +import { UserModule } from '../../user/user.module'; +import { RoleModule } from '../role/role.module'; +import { SystemModule } from '../system.module'; -import { OnlineController } from './online.controller' -import { OnlineService } from './online.service' +import { OnlineController } from './online.controller'; +import { OnlineService } from './online.service'; -const providers = [OnlineService] +const providers = [OnlineService]; @Module({ imports: [ diff --git a/src/modules/system/online/online.service.ts b/src/modules/system/online/online.service.ts index 31ad4c0..8bf11fa 100644 --- a/src/modules/system/online/online.service.ts +++ b/src/modules/system/online/online.service.ts @@ -1,21 +1,21 @@ -import { Injectable } from '@nestjs/common' -import { JwtService } from '@nestjs/jwt' -import { InjectEntityManager } from '@nestjs/typeorm' +import { Injectable } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { InjectEntityManager } from '@nestjs/typeorm'; -import { RemoteSocket } from 'socket.io' -import { EntityManager } from 'typeorm' +import { RemoteSocket } from 'socket.io'; +import { EntityManager } from 'typeorm'; -import { UAParser } from 'ua-parser-js' +import { UAParser } from 'ua-parser-js'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; -import { BusinessEvents } from '~/socket/business-event.constant' -import { AdminEventsGateway } from '~/socket/events/admin.gateway' +import { BusinessEvents } from '~/socket/business-event.constant'; +import { AdminEventsGateway } from '~/socket/events/admin.gateway'; -import { UserService } from '../../user/user.service' +import { UserService } from '../../user/user.service'; -import { OnlineUserInfo } from './online.model' +import { OnlineUserInfo } from './online.model'; @Injectable() export class OnlineService { @@ -23,55 +23,50 @@ export class OnlineService { @InjectEntityManager() private readonly entityManager: EntityManager, private readonly userService: UserService, private readonly adminEventsGateWay: AdminEventsGateway, - private readonly jwtService: JwtService, + private readonly jwtService: JwtService ) {} /** * 罗列在线用户列表 */ async listOnlineUser(currentUid: number): Promise { - const onlineSockets = await this.getOnlineSockets() - if (!onlineSockets || onlineSockets.length <= 0) - return [] + const onlineSockets = await this.getOnlineSockets(); + if (!onlineSockets || onlineSockets.length <= 0) return []; - const onlineIds = onlineSockets.map((socket) => { - const token = socket.handshake.query?.token as string - return this.jwtService.verify(token).uid - }) - return this.findLastLoginInfoList(onlineIds, currentUid) + const onlineIds = onlineSockets.map(socket => { + const token = socket.handshake.query?.token as string; + return this.jwtService.verify(token).uid; + }); + return this.findLastLoginInfoList(onlineIds, currentUid); } /** * 下线当前用户 */ async kickUser(uid: number, currentUid: number): Promise { - const rootUserId = await this.userService.findRootUserId() - const currentUserInfo = await this.userService.getAccountInfo(currentUid) - if (uid === rootUserId) - throw new BusinessException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER) + const rootUserId = await this.userService.findRootUserId(); + const currentUserInfo = await this.userService.getAccountInfo(currentUid); + if (uid === rootUserId) throw new BusinessException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER); // reset redis keys - await this.userService.forbidden(uid) + await this.userService.forbidden(uid); // socket emit - const socket = await this.findSocketIdByUid(uid) + const socket = await this.findSocketIdByUid(uid); if (socket) { // socket emit event this.adminEventsGateWay.server .to(socket.id) - .emit(BusinessEvents.USER_KICK, { operater: currentUserInfo.username }) + .emit(BusinessEvents.USER_KICK, { operater: currentUserInfo.username }); // close socket - socket.disconnect() + socket.disconnect(); } } /** * 根据用户id列表查找最近登录信息和用户信息 */ - async findLastLoginInfoList( - ids: number[], - currentUid: number, - ): Promise { - const rootUserId = await this.userService.findRootUserId() + async findLastLoginInfoList(ids: number[], currentUid: number): Promise { + const rootUserId = await this.userService.findRootUserId(); const result = await this.entityManager.query( ` SELECT sys_login_log.created_at, sys_login_log.ip, sys_login_log.address, sys_login_log.ua, sys_user.id, sys_user.username, sys_user.nick_name @@ -80,12 +75,12 @@ export class OnlineService { WHERE sys_login_log.created_at IN (SELECT MAX(created_at) as createdAt FROM sys_login_log GROUP BY user_id) AND sys_user.id IN (?) `, - [ids], - ) + [ids] + ); if (result) { - const parser = new UAParser() - return result.map((e) => { - const u = parser.setUA(e.ua).getResult() + const parser = new UAParser(); + return result.map(e => { + const u = parser.setUA(e.ua).getResult(); return { id: e.id, ip: e.ip, @@ -96,39 +91,37 @@ export class OnlineService { os: `${u.os.name} ${u.os.version}`, browser: `${u.browser.name} ${u.browser.version}`, disable: currentUid === e.id || e.id === rootUserId, - } - }) + }; + }); } - return [] + return []; } /** * 根据uid查找socketid */ async findSocketIdByUid(uid: number): Promise> { - const onlineSockets = await this.getOnlineSockets() - const socket = onlineSockets.find((socket) => { - const token = socket.handshake.query?.token as string - const tokenUid = this.jwtService.verify(token).uid - return tokenUid === uid - }) - return socket + const onlineSockets = await this.getOnlineSockets(); + const socket = onlineSockets.find(socket => { + const token = socket.handshake.query?.token as string; + const tokenUid = this.jwtService.verify(token).uid; + return tokenUid === uid; + }); + return socket; } - async filterSocketIdByUidArr( - uids: number[], - ): Promise[]> { - const onlineSockets = await this.getOnlineSockets() - const sockets = onlineSockets.filter((socket) => { - const token = socket.handshake.query?.token as string - const tokenUid = this.jwtService.verify(token).uid - return uids.includes(tokenUid) - }) - return sockets + async filterSocketIdByUidArr(uids: number[]): Promise[]> { + const onlineSockets = await this.getOnlineSockets(); + const sockets = onlineSockets.filter(socket => { + const token = socket.handshake.query?.token as string; + const tokenUid = this.jwtService.verify(token).uid; + return uids.includes(tokenUid); + }); + return sockets; } async getOnlineSockets() { - const onlineSockets = await this.adminEventsGateWay.server.fetchSockets() - return onlineSockets + const onlineSockets = await this.adminEventsGateWay.server.fetchSockets(); + return onlineSockets; } } diff --git a/src/modules/system/param-config/param-config.controller.ts b/src/modules/system/param-config/param-config.controller.ts index b96ab9d..b792eb3 100644 --- a/src/modules/system/param-config/param-config.controller.ts +++ b/src/modules/system/param-config/param-config.controller.ts @@ -1,15 +1,15 @@ -import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { Pagination } from '~/helper/paginate/pagination' -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' -import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { Pagination } from '~/helper/paginate/pagination'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity'; -import { ParamConfigDto, ParamConfigQueryDto } from './param-config.dto' -import { ParamConfigService } from './param-config.service' +import { ParamConfigDto, ParamConfigQueryDto } from './param-config.dto'; +import { ParamConfigService } from './param-config.service'; export const permissions = definePermission('system:param-config', { LIST: 'list', @@ -17,7 +17,7 @@ export const permissions = definePermission('system:param-config', { READ: 'read', UPDATE: 'update', DELETE: 'delete', -} as const) +} as const); @ApiTags('System - 参数配置模块') @ApiSecurityAuth() @@ -30,15 +30,15 @@ export class ParamConfigController { @ApiResult({ type: [ParamConfigEntity], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: ParamConfigQueryDto): Promise> { - return this.paramConfigService.page(dto) + return this.paramConfigService.page(dto); } @Post() @ApiOperation({ summary: '新增参数配置' }) @Perm(permissions.CREATE) async create(@Body() dto: ParamConfigDto): Promise { - await this.paramConfigService.isExistKey(dto.key) - await this.paramConfigService.create(dto) + await this.paramConfigService.isExistKey(dto.key); + await this.paramConfigService.create(dto); } @Get(':id') @@ -46,20 +46,20 @@ export class ParamConfigController { @ApiResult({ type: ParamConfigEntity }) @Perm(permissions.READ) async info(@IdParam() id: number): Promise { - return this.paramConfigService.findOne(id) + return this.paramConfigService.findOne(id); } @Post(':id') @ApiOperation({ summary: '更新参数配置' }) @Perm(permissions.UPDATE) async update(@IdParam() id: number, @Body() dto: ParamConfigDto): Promise { - await this.paramConfigService.update(id, dto) + await this.paramConfigService.update(id, dto); } @Delete(':id') @ApiOperation({ summary: '删除指定的参数配置' }) @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { - await this.paramConfigService.delete(id) + await this.paramConfigService.delete(id); } } diff --git a/src/modules/system/param-config/param-config.dto.ts b/src/modules/system/param-config/param-config.dto.ts index 5b720ba..921d5ed 100644 --- a/src/modules/system/param-config/param-config.dto.ts +++ b/src/modules/system/param-config/param-config.dto.ts @@ -1,31 +1,31 @@ -import { ApiProperty } from '@nestjs/swagger' -import { IsOptional, IsString, MinLength } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString, MinLength } from 'class-validator'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; export class ParamConfigDto { @ApiProperty({ description: '参数名称' }) @IsString() - name: string + name: string; @ApiProperty({ description: '参数键名' }) @IsString() @MinLength(3) - key: string + key: string; @ApiProperty({ description: '参数值' }) @IsString() - value: string + value: string; @ApiProperty({ description: '备注' }) @IsOptional() @IsString() - remark?: string + remark?: string; } export class ParamConfigQueryDto extends PagerDto { @ApiProperty({ description: '参数名称' }) @IsString() @IsOptional() - name: string + name: string; } diff --git a/src/modules/system/param-config/param-config.entity.ts b/src/modules/system/param-config/param-config.entity.ts index 2b5c4e0..47153e7 100644 --- a/src/modules/system/param-config/param-config.entity.ts +++ b/src/modules/system/param-config/param-config.entity.ts @@ -1,23 +1,23 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; @Entity({ name: 'sys_config' }) export class ParamConfigEntity extends CommonEntity { @Column({ type: 'varchar', length: 50 }) @ApiProperty({ description: '配置名' }) - name: string + name: string; @Column({ type: 'varchar', length: 50, unique: true }) @ApiProperty({ description: '配置键名' }) - key: string + key: string; @Column({ type: 'varchar', nullable: true }) @ApiProperty({ description: '配置值' }) - value: string + value: string; @Column({ type: 'varchar', nullable: true }) @ApiProperty({ description: '配置描述' }) - remark: string + remark: string; } diff --git a/src/modules/system/param-config/param-config.module.ts b/src/modules/system/param-config/param-config.module.ts index e1c7a9e..0da5acf 100644 --- a/src/modules/system/param-config/param-config.module.ts +++ b/src/modules/system/param-config/param-config.module.ts @@ -1,11 +1,11 @@ -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { ParamConfigController } from './param-config.controller' -import { ParamConfigEntity } from './param-config.entity' -import { ParamConfigService } from './param-config.service' +import { ParamConfigController } from './param-config.controller'; +import { ParamConfigEntity } from './param-config.entity'; +import { ParamConfigService } from './param-config.service'; -const services = [ParamConfigService] +const services = [ParamConfigService]; @Module({ imports: [TypeOrmModule.forFeature([ParamConfigEntity])], diff --git a/src/modules/system/param-config/param-config.service.ts b/src/modules/system/param-config/param-config.service.ts index 36cbee3..926b5ae 100644 --- a/src/modules/system/param-config/param-config.service.ts +++ b/src/modules/system/param-config/param-config.service.ts @@ -1,21 +1,21 @@ -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm' +import { Repository } from 'typeorm'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { paginate } from '~/helper/paginate' -import { Pagination } from '~/helper/paginate/pagination' -import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { paginate } from '~/helper/paginate'; +import { Pagination } from '~/helper/paginate/pagination'; +import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity'; -import { ParamConfigDto, ParamConfigQueryDto } from './param-config.dto' +import { ParamConfigDto, ParamConfigQueryDto } from './param-config.dto'; @Injectable() export class ParamConfigService { constructor( @InjectRepository(ParamConfigEntity) - private paramConfigRepository: Repository, + private paramConfigRepository: Repository ) {} /** @@ -26,66 +26,64 @@ export class ParamConfigService { pageSize, name, }: ParamConfigQueryDto): Promise> { - const queryBuilder = this.paramConfigRepository.createQueryBuilder('config') + const queryBuilder = this.paramConfigRepository.createQueryBuilder('config'); if (name) { queryBuilder.where('config.name LIKE :name', { name: `%${name}%`, - }) + }); } - return paginate(queryBuilder, { page, pageSize }) + return paginate(queryBuilder, { page, pageSize }); } /** * 获取参数总数 */ async countConfigList(): Promise { - return this.paramConfigRepository.count() + return this.paramConfigRepository.count(); } /** * 新增 */ async create(dto: ParamConfigDto): Promise { - await this.paramConfigRepository.insert(dto) + await this.paramConfigRepository.insert(dto); } /** * 更新 */ async update(id: number, dto: Partial): Promise { - await this.paramConfigRepository.update(id, dto) + await this.paramConfigRepository.update(id, dto); } /** * 删除 */ async delete(id: number): Promise { - await this.paramConfigRepository.delete(id) + await this.paramConfigRepository.delete(id); } /** * 查询单个 */ async findOne(id: number): Promise { - return this.paramConfigRepository.findOneBy({ id }) + return this.paramConfigRepository.findOneBy({ id }); } async isExistKey(key: string): Promise { - const result = await this.paramConfigRepository.findOneBy({ key }) - if (result) - throw new BusinessException(ErrorEnum.PARAMETER_CONFIG_KEY_EXISTS) + const result = await this.paramConfigRepository.findOneBy({ key }); + if (result) throw new BusinessException(ErrorEnum.PARAMETER_CONFIG_KEY_EXISTS); } async findValueByKey(key: string): Promise { const result = await this.paramConfigRepository.findOne({ where: { key }, select: ['value'], - }) - if (result) - return result.value + }); + if (result) return result.value; - return null + return null; } } diff --git a/src/modules/system/role/role.controller.ts b/src/modules/system/role/role.controller.ts index 1f5b7a0..05ba9c6 100644 --- a/src/modules/system/role/role.controller.ts +++ b/src/modules/system/role/role.controller.ts @@ -7,21 +7,21 @@ import { Post, Put, Query, -} from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +} from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { PagerDto } from '~/common/dto/pager.dto' -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' -import { RoleEntity } from '~/modules/system/role/role.entity' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { RoleEntity } from '~/modules/system/role/role.entity'; -import { MenuService } from '../menu/menu.service' +import { MenuService } from '../menu/menu.service'; -import { RoleDto, RoleQueryDto, RoleUpdateDto } from './role.dto' -import { RoleInfo } from './role.model' -import { RoleService } from './role.service' +import { RoleDto, RoleQueryDto, RoleUpdateDto } from './role.dto'; +import { RoleInfo } from './role.model'; +import { RoleService } from './role.service'; export const permissions = definePermission('system:role', { LIST: 'list', @@ -29,7 +29,7 @@ export const permissions = definePermission('system:role', { READ: 'read', UPDATE: 'update', DELETE: 'delete', -} as const) +} as const); @ApiTags('System - 角色模块') @ApiSecurityAuth() @@ -37,7 +37,7 @@ export const permissions = definePermission('system:role', { export class RoleController { constructor( private roleService: RoleService, - private menuService: MenuService, + private menuService: MenuService ) {} @Get() @@ -45,7 +45,7 @@ export class RoleController { @ApiResult({ type: [RoleEntity], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: RoleQueryDto) { - return this.roleService.findAll(dto) + return this.roleService.findAll(dto); } @Get(':id') @@ -53,23 +53,22 @@ export class RoleController { @ApiResult({ type: RoleInfo }) @Perm(permissions.READ) async info(@IdParam() id: number) { - return this.roleService.info(id) + return this.roleService.info(id); } @Post() @ApiOperation({ summary: '新增角色' }) @Perm(permissions.CREATE) async create(@Body() dto: RoleDto): Promise { - await this.roleService.create(dto) + await this.roleService.create(dto); } @Put(':id') @ApiOperation({ summary: '更新角色' }) @Perm(permissions.UPDATE) - async update( - @IdParam() id: number, @Body() dto: RoleUpdateDto): Promise { - await this.roleService.update(id, dto) - await this.menuService.refreshOnlineUserPerms() + async update(@IdParam() id: number, @Body() dto: RoleUpdateDto): Promise { + await this.roleService.update(id, dto); + await this.menuService.refreshOnlineUserPerms(); } @Delete(':id') @@ -77,9 +76,9 @@ export class RoleController { @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { if (await this.roleService.checkUserByRoleId(id)) - throw new BadRequestException('该角色存在关联用户,无法删除') + throw new BadRequestException('该角色存在关联用户,无法删除'); - await this.roleService.delete(id) - await this.menuService.refreshOnlineUserPerms() + await this.roleService.delete(id); + await this.menuService.refreshOnlineUserPerms(); } } diff --git a/src/modules/system/role/role.dto.ts b/src/modules/system/role/role.dto.ts index 2e9d0e2..7c48dcd 100644 --- a/src/modules/system/role/role.dto.ts +++ b/src/modules/system/role/role.dto.ts @@ -1,48 +1,38 @@ -import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger' -import { - IsArray, - IsIn, - IsInt, - IsOptional, - IsString, - Matches, - MinLength, -} from 'class-validator' -import { PagerDto } from '~/common/dto/pager.dto' +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { IsArray, IsIn, IsInt, IsOptional, IsString, Matches, MinLength } from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; export class RoleDto { @ApiProperty({ description: '角色名称' }) @IsString() @MinLength(2, { message: '角色名称长度不能小于2' }) - name: string + name: string; @ApiProperty({ description: '角色值' }) @IsString() @Matches(/^[a-z0-9A-Z]+$/, { message: '角色值只能包含字母和数字' }) @MinLength(2, { message: '角色值长度不能小于2' }) - value: string + value: string; @ApiProperty({ description: '角色备注' }) @IsString() @IsOptional() - remark?: string + remark?: string; @ApiProperty({ description: '状态' }) @IsIn([0, 1]) - status: number + status: number; @ApiProperty({ description: '关联菜单、权限编号' }) @IsOptional() @IsArray() - menuIds?: number[] + menuIds?: number[]; } export class RoleUpdateDto extends PartialType(RoleDto) {} export class RoleQueryDto extends IntersectionType(PagerDto, PartialType(RoleDto)) { - @ApiProperty({ description: '状态', example: 0, required: false }) @IsInt() @IsOptional() - status?: number - + status?: number; } diff --git a/src/modules/system/role/role.entity.ts b/src/modules/system/role/role.entity.ts index 1317697..7a4854b 100644 --- a/src/modules/system/role/role.entity.ts +++ b/src/modules/system/role/role.entity.ts @@ -1,36 +1,36 @@ -import { ApiHideProperty, ApiProperty } from '@nestjs/swagger' -import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm' +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; -import { UserEntity } from '../../user/user.entity' -import { MenuEntity } from '../menu/menu.entity' +import { UserEntity } from '../../user/user.entity'; +import { MenuEntity } from '../menu/menu.entity'; @Entity({ name: 'sys_role' }) export class RoleEntity extends CommonEntity { @Column({ length: 50, unique: true }) @ApiProperty({ description: '角色名' }) - name: string + name: string; @Column({ unique: true }) @ApiProperty({ description: '角色标识' }) - value: string + value: string; @Column({ nullable: true }) @ApiProperty({ description: '角色描述' }) - remark: string + remark: string; @Column({ type: 'tinyint', nullable: true, default: 1 }) @ApiProperty({ description: '状态:1启用,0禁用' }) - status: number + status: number; @Column({ nullable: true }) @ApiProperty({ description: '是否默认用户' }) - default: boolean + default: boolean; @ApiHideProperty() @ManyToMany(() => UserEntity, user => user.roles) - users: Relation + users: Relation; @ApiHideProperty() @ManyToMany(() => MenuEntity, menu => menu.roles, {}) @@ -39,5 +39,5 @@ export class RoleEntity extends CommonEntity { joinColumn: { name: 'role_id', referencedColumnName: 'id' }, inverseJoinColumn: { name: 'menu_id', referencedColumnName: 'id' }, }) - menus: Relation + menus: Relation; } diff --git a/src/modules/system/role/role.model.ts b/src/modules/system/role/role.model.ts index 2d11ab4..285df41 100644 --- a/src/modules/system/role/role.model.ts +++ b/src/modules/system/role/role.model.ts @@ -1,8 +1,8 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; -import { RoleEntity } from './role.entity' +import { RoleEntity } from './role.entity'; export class RoleInfo extends RoleEntity { @ApiProperty({ type: [Number] }) - menuIds: number[] + menuIds: number[]; } diff --git a/src/modules/system/role/role.module.ts b/src/modules/system/role/role.module.ts index 98c24e6..b43e4a2 100644 --- a/src/modules/system/role/role.module.ts +++ b/src/modules/system/role/role.module.ts @@ -1,21 +1,18 @@ -import { Module, forwardRef } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module, forwardRef } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { SseService } from '~/modules/sse/sse.service' +import { SseService } from '~/modules/sse/sse.service'; -import { MenuModule } from '../menu/menu.module' +import { MenuModule } from '../menu/menu.module'; -import { RoleController } from './role.controller' -import { RoleEntity } from './role.entity' -import { RoleService } from './role.service' +import { RoleController } from './role.controller'; +import { RoleEntity } from './role.entity'; +import { RoleService } from './role.service'; -const providers = [RoleService, SseService] +const providers = [RoleService, SseService]; @Module({ - imports: [ - TypeOrmModule.forFeature([RoleEntity]), - forwardRef(() => MenuModule), - ], + imports: [TypeOrmModule.forFeature([RoleEntity]), forwardRef(() => MenuModule)], controllers: [RoleController], providers: [...providers], exports: [TypeOrmModule, ...providers], diff --git a/src/modules/system/role/role.service.ts b/src/modules/system/role/role.service.ts index 2f0c807..0908d4f 100644 --- a/src/modules/system/role/role.service.ts +++ b/src/modules/system/role/role.service.ts @@ -1,17 +1,17 @@ -import { Injectable } from '@nestjs/common' -import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm' -import { isEmpty, isNumber } from 'lodash' -import { EntityManager, In, Like, Repository } from 'typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { isEmpty, isNumber } from 'lodash'; +import { EntityManager, In, Like, Repository } from 'typeorm'; -import { PagerDto } from '~/common/dto/pager.dto' -import { ROOT_ROLE_ID } from '~/constants/system.constant' -import { paginate } from '~/helper/paginate' -import { Pagination } from '~/helper/paginate/pagination' -import { SseService } from '~/modules/sse/sse.service' -import { MenuEntity } from '~/modules/system/menu/menu.entity' -import { RoleEntity } from '~/modules/system/role/role.entity' +import { PagerDto } from '~/common/dto/pager.dto'; +import { ROOT_ROLE_ID } from '~/constants/system.constant'; +import { paginate } from '~/helper/paginate'; +import { Pagination } from '~/helper/paginate/pagination'; +import { SseService } from '~/modules/sse/sse.service'; +import { MenuEntity } from '~/modules/system/menu/menu.entity'; +import { RoleEntity } from '~/modules/system/role/role.entity'; -import { RoleDto, RoleQueryDto, RoleUpdateDto } from './role.dto' +import { RoleDto, RoleQueryDto, RoleUpdateDto } from './role.dto'; @Injectable() export class RoleService { @@ -21,8 +21,7 @@ export class RoleService { @InjectRepository(MenuEntity) private menuRepository: Repository, @InjectEntityManager() private entityManager: EntityManager, - private sseService: SseService, - + private sseService: SseService ) {} /** @@ -35,18 +34,16 @@ export class RoleService { value, status, }: RoleQueryDto): Promise> { - const queryBuilder = this.roleRepository - .createQueryBuilder('role') - .where({ + const queryBuilder = this.roleRepository.createQueryBuilder('role').where({ ...(name ? { name: Like(`%${name}%`) } : null), ...(value ? { value: Like(`%${value}%`) } : null), ...(isNumber(status) ? { status } : null), - }) + }); - return paginate(queryBuilder, { - page, - pageSize, - }) + return paginate(queryBuilder, { + page, + pageSize, + }); } /** @@ -58,20 +55,19 @@ export class RoleService { .where({ id, }) - .getOne() + .getOne(); const menus = await this.menuRepository.find({ where: { roles: { id } }, select: ['id'], - }) + }); - return { ...info, menuIds: menus.map(m => m.id) } + return { ...info, menuIds: menus.map(m => m.id) }; } async delete(id: number): Promise { - if (id === ROOT_ROLE_ID) - throw new Error('不能删除超级管理员') - await this.roleRepository.delete(id) + if (id === ROOT_ROLE_ID) throw new Error('不能删除超级管理员'); + await this.roleRepository.delete(id); } /** @@ -80,31 +76,29 @@ export class RoleService { async create({ menuIds, ...data }: RoleDto): Promise<{ roleId: number }> { const role = await this.roleRepository.save({ ...data, - menus: menuIds - ? await this.menuRepository.findBy({ id: In(menuIds) }) - : [], - }) + menus: menuIds ? await this.menuRepository.findBy({ id: In(menuIds) }) : [], + }); - return { roleId: role.id } + return { roleId: role.id }; } /** * 更新角色信息 */ async update(id, { menuIds, ...data }: RoleUpdateDto): Promise { - await this.roleRepository.update(id, data) + await this.roleRepository.update(id, data); if (!isEmpty(menuIds)) { // using transaction - await this.entityManager.transaction(async (manager) => { + await this.entityManager.transaction(async manager => { const menus = await this.menuRepository.find({ where: { id: In(menuIds) }, - }) + }); - const role = await this.roleRepository.findOne({ where: { id } }) - role.menus = menus - await manager.save(role) - }) + const role = await this.roleRepository.findOne({ where: { id } }); + role.menus = menus; + await manager.save(role); + }); } } @@ -116,12 +110,11 @@ export class RoleService { where: { users: { id }, }, - }) + }); - if (!isEmpty(roles)) - return roles.map(r => r.id) + if (!isEmpty(roles)) return roles.map(r => r.id); - return [] + return []; } async getRoleValues(ids: number[]): Promise { @@ -129,7 +122,7 @@ export class RoleService { await this.roleRepository.findBy({ id: In(ids), }) - ).map(r => r.value) + ).map(r => r.value); } async isAdminRoleByUser(uid: number): Promise { @@ -137,18 +130,16 @@ export class RoleService { where: { users: { id: uid }, }, - }) + }); if (!isEmpty(roles)) { - return roles.some( - r => r.id === ROOT_ROLE_ID, - ) + return roles.some(r => r.id === ROOT_ROLE_ID); } - return false + return false; } hasAdminRole(rids: number[]): boolean { - return rids.includes(ROOT_ROLE_ID) + return rids.includes(ROOT_ROLE_ID); } /** @@ -161,6 +152,6 @@ export class RoleService { roles: { id }, }, }, - }) + }); } } diff --git a/src/modules/system/serve/serve.controller.ts b/src/modules/system/serve/serve.controller.ts index eb27465..d931f4d 100644 --- a/src/modules/system/serve/serve.controller.ts +++ b/src/modules/system/serve/serve.controller.ts @@ -1,15 +1,15 @@ -import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager' -import { Controller, Get, UseInterceptors } from '@nestjs/common' -import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger' +import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager'; +import { Controller, Get, UseInterceptors } from '@nestjs/common'; +import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' +import { ApiResult } from '~/common/decorators/api-result.decorator'; -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; -import { AllowAnon } from '~/modules/auth/decorators/allow-anon.decorator' +import { AllowAnon } from '~/modules/auth/decorators/allow-anon.decorator'; -import { ServeStatInfo } from './serve.model' -import { ServeService } from './serve.service' +import { ServeStatInfo } from './serve.model'; +import { ServeService } from './serve.service'; @ApiTags('System - 服务监控') @ApiSecurityAuth() @@ -26,6 +26,6 @@ export class ServeController { @ApiResult({ type: ServeStatInfo }) @AllowAnon() async stat(): Promise { - return this.serveService.getServeStat() + return this.serveService.getServeStat(); } } diff --git a/src/modules/system/serve/serve.model.ts b/src/modules/system/serve/serve.model.ts index 4c602cb..1fba434 100644 --- a/src/modules/system/serve/serve.model.ts +++ b/src/modules/system/serve/serve.model.ts @@ -1,71 +1,71 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; export class Runtime { @ApiProperty({ description: '系统' }) - os?: string + os?: string; @ApiProperty({ description: '服务器架构' }) - arch?: string + arch?: string; @ApiProperty({ description: 'Node版本' }) - nodeVersion?: string + nodeVersion?: string; @ApiProperty({ description: 'Npm版本' }) - npmVersion?: string + npmVersion?: string; } export class CoreLoad { @ApiProperty({ description: '当前CPU资源消耗' }) - rawLoad?: number + rawLoad?: number; @ApiProperty({ description: '当前空闲CPU资源' }) - rawLoadIdle?: number + rawLoadIdle?: number; } // Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz export class Cpu { @ApiProperty({ description: '制造商' }) - manufacturer?: string + manufacturer?: string; @ApiProperty({ description: '品牌' }) - brand?: string + brand?: string; @ApiProperty({ description: '物理核心数' }) - physicalCores?: number + physicalCores?: number; @ApiProperty({ description: '型号' }) - model?: string + model?: string; @ApiProperty({ description: '速度 in GHz' }) - speed?: number + speed?: number; @ApiProperty({ description: 'CPU资源消耗 原始滴答' }) - rawCurrentLoad?: number + rawCurrentLoad?: number; @ApiProperty({ description: '空闲CPU资源 原始滴答' }) - rawCurrentLoadIdle?: number + rawCurrentLoadIdle?: number; @ApiProperty({ description: 'cpu资源消耗', type: [CoreLoad] }) - coresLoad?: CoreLoad[] + coresLoad?: CoreLoad[]; } export class Disk { @ApiProperty({ description: '磁盘空间大小 (bytes)' }) - size?: number + size?: number; @ApiProperty({ description: '已使用磁盘空间 (bytes)' }) - used?: number + used?: number; @ApiProperty({ description: '可用磁盘空间 (bytes)' }) - available?: number + available?: number; } export class Memory { @ApiProperty({ description: 'total memory in bytes' }) - total?: number + total?: number; @ApiProperty({ description: '可用内存' }) - available?: number + available?: number; } /** @@ -73,14 +73,14 @@ export class Memory { */ export class ServeStatInfo { @ApiProperty({ description: '运行环境', type: Runtime }) - runtime?: Runtime + runtime?: Runtime; @ApiProperty({ description: 'CPU信息', type: Cpu }) - cpu?: Cpu + cpu?: Cpu; @ApiProperty({ description: '磁盘信息', type: Disk }) - disk?: Disk + disk?: Disk; @ApiProperty({ description: '内存信息', type: Memory }) - memory?: Memory + memory?: Memory; } diff --git a/src/modules/system/serve/serve.module.ts b/src/modules/system/serve/serve.module.ts index db44177..a2963cc 100644 --- a/src/modules/system/serve/serve.module.ts +++ b/src/modules/system/serve/serve.module.ts @@ -1,11 +1,11 @@ -import { Module, forwardRef } from '@nestjs/common' +import { Module, forwardRef } from '@nestjs/common'; -import { SystemModule } from '../system.module' +import { SystemModule } from '../system.module'; -import { ServeController } from './serve.controller' -import { ServeService } from './serve.service' +import { ServeController } from './serve.controller'; +import { ServeService } from './serve.service'; -const providers = [ServeService] +const providers = [ServeService]; @Module({ imports: [forwardRef(() => SystemModule)], diff --git a/src/modules/system/serve/serve.service.ts b/src/modules/system/serve/serve.service.ts index 2b92f3a..b20c2f5 100644 --- a/src/modules/system/serve/serve.service.ts +++ b/src/modules/system/serve/serve.service.ts @@ -1,7 +1,7 @@ -import { Injectable } from '@nestjs/common' -import * as si from 'systeminformation' +import { Injectable } from '@nestjs/common'; +import * as si from 'systeminformation'; -import { Disk, ServeStatInfo } from './serve.model' +import { Disk, ServeStatInfo } from './serve.model'; @Injectable() export class ServeService { @@ -17,19 +17,19 @@ export class ServeService { si.currentLoad(), si.mem(), ]) - ).map((p: any) => p.value) + ).map((p: any) => p.value); // 计算总空间 - const diskListInfo = await si.fsSize() - const diskinfo = new Disk() - diskinfo.size = 0 - diskinfo.available = 0 - diskinfo.used = 0 - diskListInfo.forEach((d) => { - diskinfo.size += d.size - diskinfo.available += d.available - diskinfo.used += d.used - }) + const diskListInfo = await si.fsSize(); + const diskinfo = new Disk(); + diskinfo.size = 0; + diskinfo.available = 0; + diskinfo.used = 0; + diskListInfo.forEach(d => { + diskinfo.size += d.size; + diskinfo.available += d.available; + diskinfo.used += d.used; + }); return { runtime: { @@ -46,11 +46,11 @@ export class ServeService { speed: cpuinfo.speed, rawCurrentLoad: currentLoadinfo.rawCurrentLoad, rawCurrentLoadIdle: currentLoadinfo.rawCurrentLoadIdle, - coresLoad: currentLoadinfo.cpus.map((e) => { + coresLoad: currentLoadinfo.cpus.map(e => { return { rawLoad: e.rawLoad, rawLoadIdle: e.rawLoadIdle, - } + }; }), }, disk: diskinfo, @@ -58,6 +58,6 @@ export class ServeService { total: meminfo.total, available: meminfo.available, }, - } + }; } } diff --git a/src/modules/system/system.module.ts b/src/modules/system/system.module.ts index ae4b12f..c919c1c 100644 --- a/src/modules/system/system.module.ts +++ b/src/modules/system/system.module.ts @@ -1,19 +1,19 @@ -import { Module } from '@nestjs/common' +import { Module } from '@nestjs/common'; -import { RouterModule } from '@nestjs/core' +import { RouterModule } from '@nestjs/core'; -import { UserModule } from '../user/user.module' +import { UserModule } from '../user/user.module'; -import { DeptModule } from './dept/dept.module' -import { DictItemModule } from './dict-item/dict-item.module' -import { DictTypeModule } from './dict-type/dict-type.module' -import { LogModule } from './log/log.module' -import { MenuModule } from './menu/menu.module' -import { OnlineModule } from './online/online.module' -import { ParamConfigModule } from './param-config/param-config.module' -import { RoleModule } from './role/role.module' -import { ServeModule } from './serve/serve.module' -import { TaskModule } from './task/task.module' +import { DeptModule } from './dept/dept.module'; +import { DictItemModule } from './dict-item/dict-item.module'; +import { DictTypeModule } from './dict-type/dict-type.module'; +import { LogModule } from './log/log.module'; +import { MenuModule } from './menu/menu.module'; +import { OnlineModule } from './online/online.module'; +import { ParamConfigModule } from './param-config/param-config.module'; +import { RoleModule } from './role/role.module'; +import { ServeModule } from './serve/serve.module'; +import { TaskModule } from './task/task.module'; const modules = [ UserModule, @@ -27,7 +27,7 @@ const modules = [ TaskModule, OnlineModule, ServeModule, -] +]; @Module({ imports: [ diff --git a/src/modules/system/task/constant.ts b/src/modules/system/task/constant.ts index 53f5d9b..e32bf7a 100644 --- a/src/modules/system/task/constant.ts +++ b/src/modules/system/task/constant.ts @@ -8,5 +8,5 @@ export enum TaskType { Interval = 1, } -export const SYS_TASK_QUEUE_NAME = 'system:sys-task' -export const SYS_TASK_QUEUE_PREFIX = 'system:sys:task' +export const SYS_TASK_QUEUE_NAME = 'system:sys-task'; +export const SYS_TASK_QUEUE_PREFIX = 'system:sys:task'; diff --git a/src/modules/system/task/task.controller.ts b/src/modules/system/task/task.controller.ts index 6888664..975c0b5 100644 --- a/src/modules/system/task/task.controller.ts +++ b/src/modules/system/task/task.controller.ts @@ -1,15 +1,15 @@ -import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { Pagination } from '~/helper/paginate/pagination' -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' -import { TaskEntity } from '~/modules/system/task/task.entity' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { Pagination } from '~/helper/paginate/pagination'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { TaskEntity } from '~/modules/system/task/task.entity'; -import { TaskDto, TaskQueryDto, TaskUpdateDto } from './task.dto' -import { TaskService } from './task.service' +import { TaskDto, TaskQueryDto, TaskUpdateDto } from './task.dto'; +import { TaskService } from './task.service'; export const permissions = definePermission('system:task', { LIST: 'list', @@ -21,7 +21,7 @@ export const permissions = definePermission('system:task', { ONCE: 'once', START: 'start', STOP: 'stop', -} as const) +} as const); @ApiTags('System - 任务调度模块') @ApiSecurityAuth() @@ -34,25 +34,25 @@ export class TaskController { @ApiResult({ type: [TaskEntity], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: TaskQueryDto): Promise> { - return this.taskService.list(dto) + return this.taskService.list(dto); } @Post() @ApiOperation({ summary: '添加任务' }) @Perm(permissions.CREATE) async create(@Body() dto: TaskDto): Promise { - const serviceCall = dto.service.split('.') - await this.taskService.checkHasMissionMeta(serviceCall[0], serviceCall[1]) - await this.taskService.create(dto) + const serviceCall = dto.service.split('.'); + await this.taskService.checkHasMissionMeta(serviceCall[0], serviceCall[1]); + await this.taskService.create(dto); } @Put(':id') @ApiOperation({ summary: '更新任务' }) @Perm(permissions.UPDATE) async update(@IdParam() id: number, @Body() dto: TaskUpdateDto): Promise { - const serviceCall = dto.service.split('.') - await this.taskService.checkHasMissionMeta(serviceCall[0], serviceCall[1]) - await this.taskService.update(id, dto) + const serviceCall = dto.service.split('.'); + await this.taskService.checkHasMissionMeta(serviceCall[0], serviceCall[1]); + await this.taskService.update(id, dto); } @Get(':id') @@ -60,39 +60,39 @@ export class TaskController { @ApiResult({ type: TaskEntity }) @Perm(permissions.READ) async info(@IdParam() id: number): Promise { - return this.taskService.info(id) + return this.taskService.info(id); } @Delete(':id') @ApiOperation({ summary: '删除任务' }) @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { - const task = await this.taskService.info(id) - await this.taskService.delete(task) + const task = await this.taskService.info(id); + await this.taskService.delete(task); } @Put(':id/once') @ApiOperation({ summary: '手动执行一次任务' }) @Perm(permissions.ONCE) async once(@IdParam() id: number): Promise { - const task = await this.taskService.info(id) - await this.taskService.once(task) + const task = await this.taskService.info(id); + await this.taskService.once(task); } @Put(':id/stop') @ApiOperation({ summary: '停止任务' }) @Perm(permissions.STOP) async stop(@IdParam() id: number): Promise { - const task = await this.taskService.info(id) - await this.taskService.stop(task) + const task = await this.taskService.info(id); + await this.taskService.stop(task); } @Put(':id/start') @ApiOperation({ summary: '启动任务' }) @Perm(permissions.START) async start(@IdParam() id: number): Promise { - const task = await this.taskService.info(id) + const task = await this.taskService.info(id); - await this.taskService.start(task) + await this.taskService.start(task); } } diff --git a/src/modules/system/task/task.dto.ts b/src/modules/system/task/task.dto.ts index c02d24f..0d9af8b 100644 --- a/src/modules/system/task/task.dto.ts +++ b/src/modules/system/task/task.dto.ts @@ -1,5 +1,5 @@ -import { BadRequestException } from '@nestjs/common' -import { ApiProperty, ApiPropertyOptional, IntersectionType, PartialType } from '@nestjs/swagger' +import { BadRequestException } from '@nestjs/common'; +import { ApiProperty, ApiPropertyOptional, IntersectionType, PartialType } from '@nestjs/swagger'; import { IsDateString, IsIn, @@ -14,30 +14,28 @@ import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface, -} from 'class-validator' -import * as parser from 'cron-parser' -import { isEmpty } from 'lodash' +} from 'class-validator'; +import * as parser from 'cron-parser'; +import { isEmpty } from 'lodash'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; // cron 表达式验证,bull lib下引用了cron-parser @ValidatorConstraint({ name: 'isCronExpression', async: false }) export class IsCronExpression implements ValidatorConstraintInterface { validate(value: string, _args: ValidationArguments) { try { - if (isEmpty(value)) - throw new BadRequestException('cron expression is empty') + if (isEmpty(value)) throw new BadRequestException('cron expression is empty'); - parser.parseExpression(value) - return true - } - catch (e) { - return false + parser.parseExpression(value); + return true; + } catch (e) { + return false; } } defaultMessage(_args: ValidationArguments) { - return 'this cron expression ($value) invalid' + return 'this cron expression ($value) invalid'; } } @@ -46,58 +44,58 @@ export class TaskDto { @IsString() @MinLength(2) @MaxLength(50) - name: string + name: string; @ApiProperty({ description: '调用的服务' }) @IsString() @MinLength(1) - service: string + service: string; @ApiProperty({ description: '任务类别:cron | interval' }) @IsIn([0, 1]) - type: number + type: number; @ApiProperty({ description: '任务状态' }) @IsIn([0, 1]) - status: number + status: number; @ApiPropertyOptional({ description: '开始时间', type: Date }) @IsDateString() @ValidateIf(o => !isEmpty(o.startTime)) - startTime: string + startTime: string; @ApiPropertyOptional({ description: '结束时间', type: Date }) @IsDateString() @ValidateIf(o => !isEmpty(o.endTime)) - endTime: string + endTime: string; @ApiPropertyOptional({ description: '限制执行次数,负数则无限制', }) @IsOptional() @IsInt() - limit?: number = -1 + limit?: number = -1; @ApiProperty({ description: 'cron表达式' }) @Validate(IsCronExpression) @ValidateIf(o => o.type === 0) - cron: string + cron: string; @ApiProperty({ description: '执行间隔,毫秒单位' }) @IsInt() @Min(100) @ValidateIf(o => o.type === 1) - every?: number + every?: number; @ApiPropertyOptional({ description: '执行参数' }) @IsOptional() @IsString() - data?: string + data?: string; @ApiPropertyOptional({ description: '任务备注' }) @IsOptional() @IsString() - remark?: string + remark?: string; } export class TaskUpdateDto extends PartialType(TaskDto) {} diff --git a/src/modules/system/task/task.entity.ts b/src/modules/system/task/task.entity.ts index 9ceb548..29738f6 100644 --- a/src/modules/system/task/task.entity.ts +++ b/src/modules/system/task/task.entity.ts @@ -1,55 +1,55 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; @Entity({ name: 'sys_task' }) export class TaskEntity extends CommonEntity { @Column({ type: 'varchar', length: 50, unique: true }) @ApiProperty({ description: '任务名' }) - name: string + name: string; @Column() @ApiProperty({ description: '任务标识' }) - service: string + service: string; @Column({ type: 'tinyint', default: 0 }) @ApiProperty({ description: '任务类型 0cron 1间隔' }) - type: number + type: number; @Column({ type: 'tinyint', default: 1 }) @ApiProperty({ description: '任务状态 0禁用 1启用' }) - status: number + status: number; @Column({ name: 'start_time', type: 'datetime', nullable: true }) @ApiProperty({ description: '开始时间' }) - startTime: Date + startTime: Date; @Column({ name: 'end_time', type: 'datetime', nullable: true }) @ApiProperty({ description: '结束时间' }) - endTime: Date + endTime: Date; @Column({ type: 'int', nullable: true, default: 0 }) @ApiProperty({ description: '间隔时间' }) - limit: number + limit: number; @Column({ nullable: true }) @ApiProperty({ description: 'cron表达式' }) - cron: string + cron: string; @Column({ type: 'int', nullable: true }) @ApiProperty({ description: '执行次数' }) - every: number + every: number; @Column({ type: 'text', nullable: true }) @ApiProperty({ description: '任务参数' }) - data: string + data: string; @Column({ name: 'job_opts', type: 'text', nullable: true }) @ApiProperty({ description: '任务配置' }) - jobOpts: string + jobOpts: string; @Column({ nullable: true }) @ApiProperty({ description: '任务描述' }) - remark: string + remark: string; } diff --git a/src/modules/system/task/task.module.ts b/src/modules/system/task/task.module.ts index 1fed543..99ce40b 100644 --- a/src/modules/system/task/task.module.ts +++ b/src/modules/system/task/task.module.ts @@ -1,21 +1,21 @@ -import { BullModule } from '@nestjs/bull' -import { Module } from '@nestjs/common' +import { BullModule } from '@nestjs/bull'; +import { Module } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config' -import { TypeOrmModule } from '@nestjs/typeorm' +import { ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigKeyPaths, IRedisConfig } from '~/config' +import { ConfigKeyPaths, IRedisConfig } from '~/config'; -import { LogModule } from '../log/log.module' +import { LogModule } from '../log/log.module'; -import { SYS_TASK_QUEUE_NAME, SYS_TASK_QUEUE_PREFIX } from './constant' +import { SYS_TASK_QUEUE_NAME, SYS_TASK_QUEUE_PREFIX } from './constant'; -import { TaskController } from './task.controller' -import { TaskEntity } from './task.entity' -import { TaskConsumer } from './task.processor' -import { TaskService } from './task.service' +import { TaskController } from './task.controller'; +import { TaskEntity } from './task.entity'; +import { TaskConsumer } from './task.processor'; +import { TaskService } from './task.service'; -const providers = [TaskService, TaskConsumer] +const providers = [TaskService, TaskConsumer]; @Module({ imports: [ diff --git a/src/modules/system/task/task.processor.ts b/src/modules/system/task/task.processor.ts index d45e24c..57147ab 100644 --- a/src/modules/system/task/task.processor.ts +++ b/src/modules/system/task/task.processor.ts @@ -1,44 +1,43 @@ -import { OnQueueCompleted, Process, Processor } from '@nestjs/bull' -import { Job } from 'bull' +import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; +import { Job } from 'bull'; -import { TaskLogService } from '../log/services/task-log.service' +import { TaskLogService } from '../log/services/task-log.service'; -import { SYS_TASK_QUEUE_NAME } from './constant' +import { SYS_TASK_QUEUE_NAME } from './constant'; -import { TaskService } from './task.service' +import { TaskService } from './task.service'; export interface ExecuteData { - id: number - args?: string | null - service: string + id: number; + args?: string | null; + service: string; } @Processor(SYS_TASK_QUEUE_NAME) export class TaskConsumer { constructor( private taskService: TaskService, - private taskLogService: TaskLogService, + private taskLogService: TaskLogService ) {} @Process() async handle(job: Job): Promise { - const startTime = Date.now() - const { data } = job + const startTime = Date.now(); + const { data } = job; try { - await this.taskService.callService(data.service, data.args) - const timing = Date.now() - startTime + await this.taskService.callService(data.service, data.args); + const timing = Date.now() - startTime; // 任务执行成功 - await this.taskLogService.create(data.id, 1, timing) - } - catch (e) { - const timing = Date.now() - startTime + await this.taskLogService.create(data.id, 1, timing); + } catch (e) { + const timing = Date.now() - startTime; // 执行失败 - await this.taskLogService.create(data.id, 0, timing, `${e}`) + await this.taskLogService.create(data.id, 0, timing, `${e}`); } } @OnQueueCompleted() onCompleted(job: Job) { - this.taskService.updateTaskCompleteStatus(job.data.id) + this.taskService.updateTaskCompleteStatus(job.data.id); } } diff --git a/src/modules/system/task/task.service.ts b/src/modules/system/task/task.service.ts index eaba96d..dddf2ed 100644 --- a/src/modules/system/task/task.service.ts +++ b/src/modules/system/task/task.service.ts @@ -1,39 +1,35 @@ -import { InjectRedis } from '@liaoliaots/nestjs-redis' -import { InjectQueue } from '@nestjs/bull' +import { InjectRedis } from '@liaoliaots/nestjs-redis'; +import { InjectQueue } from '@nestjs/bull'; import { BadRequestException, Injectable, Logger, NotFoundException, OnModuleInit, -} from '@nestjs/common' -import { ModuleRef, Reflector } from '@nestjs/core' -import { UnknownElementException } from '@nestjs/core/errors/exceptions/unknown-element.exception' -import { InjectRepository } from '@nestjs/typeorm' -import { Queue } from 'bull' -import Redis from 'ioredis' -import { isEmpty, isNumber } from 'lodash' -import { Like, Repository } from 'typeorm' +} from '@nestjs/common'; +import { ModuleRef, Reflector } from '@nestjs/core'; +import { UnknownElementException } from '@nestjs/core/errors/exceptions/unknown-element.exception'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Queue } from 'bull'; +import Redis from 'ioredis'; +import { isEmpty, isNumber } from 'lodash'; +import { Like, Repository } from 'typeorm'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; -import { paginate } from '~/helper/paginate' -import { Pagination } from '~/helper/paginate/pagination' +import { paginate } from '~/helper/paginate'; +import { Pagination } from '~/helper/paginate/pagination'; -import { TaskEntity } from '~/modules/system/task/task.entity' -import { MISSION_DECORATOR_KEY } from '~/modules/tasks/mission.decorator' +import { TaskEntity } from '~/modules/system/task/task.entity'; +import { MISSION_DECORATOR_KEY } from '~/modules/tasks/mission.decorator'; -import { - SYS_TASK_QUEUE_NAME, - SYS_TASK_QUEUE_PREFIX, - TaskStatus, -} from './constant' -import { TaskDto, TaskQueryDto, TaskUpdateDto } from './task.dto' +import { SYS_TASK_QUEUE_NAME, SYS_TASK_QUEUE_PREFIX, TaskStatus } from './constant'; +import { TaskDto, TaskQueryDto, TaskUpdateDto } from './task.dto'; @Injectable() export class TaskService implements OnModuleInit { - private logger = new Logger(TaskService.name) + private logger = new Logger(TaskService.name); constructor( @InjectRepository(TaskEntity) @@ -41,31 +37,31 @@ export class TaskService implements OnModuleInit { @InjectQueue(SYS_TASK_QUEUE_NAME) private taskQueue: Queue, private moduleRef: ModuleRef, private reflector: Reflector, - @InjectRedis() private redis: Redis, + @InjectRedis() private redis: Redis ) {} /** * module init */ async onModuleInit() { - await this.initTask() + await this.initTask(); } /** * 初始化任务,系统启动前调用 */ async initTask(): Promise { - const initKey = `${SYS_TASK_QUEUE_PREFIX}:init` + const initKey = `${SYS_TASK_QUEUE_PREFIX}:init`; // 防止重复初始化 const result = await this.redis .multi() .setnx(initKey, new Date().getTime()) .expire(initKey, 60 * 30) - .exec() + .exec(); if (result[0][1] === 0) { // 存在锁则直接跳过防止重复初始化 - this.logger.log('Init task is lock', TaskService.name) - return + this.logger.log('Init task is lock', TaskService.name); + return; } const jobs = await this.taskQueue.getJobs([ 'active', @@ -74,19 +70,18 @@ export class TaskService implements OnModuleInit { 'paused', 'waiting', 'completed', - ]) - jobs.forEach((j) => { - j.remove() - }) + ]); + jobs.forEach(j => { + j.remove(); + }); // 查找所有需要运行的任务 - const tasks = await this.taskRepository.findBy({ status: 1 }) + const tasks = await this.taskRepository.findBy({ status: 1 }); if (tasks && tasks.length > 0) { - for (const t of tasks) - await this.start(t) + for (const t of tasks) await this.start(t); } // 启动后释放锁 - await this.redis.del(initKey) + await this.redis.del(initKey); } async list({ @@ -105,35 +100,30 @@ export class TaskService implements OnModuleInit { ...(type ? { type } : null), ...(isNumber(status) ? { status } : null), }) - .orderBy('task.id', 'ASC') + .orderBy('task.id', 'ASC'); - return paginate(queryBuilder, { page, pageSize }) + return paginate(queryBuilder, { page, pageSize }); } /** * task info */ async info(id: number): Promise { - const task = this.taskRepository - .createQueryBuilder('task') - .where({ id }) - .getOne() + const task = this.taskRepository.createQueryBuilder('task').where({ id }).getOne(); - if (!task) - throw new NotFoundException('Task Not Found') + if (!task) throw new NotFoundException('Task Not Found'); - return task + return task; } /** * delete task */ async delete(task: TaskEntity): Promise { - if (!task) - throw new BadRequestException('Task is Empty') + if (!task) throw new BadRequestException('Task is Empty'); - await this.stop(task) - await this.taskRepository.delete(task.id) + await this.stop(task); + await this.taskRepository.delete(task.id); } /** @@ -143,80 +133,69 @@ export class TaskService implements OnModuleInit { if (task) { await this.taskQueue.add( { id: task.id, service: task.service, args: task.data }, - { jobId: task.id, removeOnComplete: true, removeOnFail: true }, - ) - } - else { - throw new BadRequestException('Task is Empty') + { jobId: task.id, removeOnComplete: true, removeOnFail: true } + ); + } else { + throw new BadRequestException('Task is Empty'); } } async create(dto: TaskDto): Promise { - const result = await this.taskRepository.save(dto) - const task = await this.info(result.id) - if (result.status === 0) - await this.stop(task) - else if (result.status === TaskStatus.Activited) - await this.start(task) + const result = await this.taskRepository.save(dto); + const task = await this.info(result.id); + if (result.status === 0) await this.stop(task); + else if (result.status === TaskStatus.Activited) await this.start(task); } async update(id: number, dto: TaskUpdateDto): Promise { - await this.taskRepository.update(id, dto) - const task = await this.info(id) - if (task.status === 0) - await this.stop(task) - else if (task.status === TaskStatus.Activited) - await this.start(task) + await this.taskRepository.update(id, dto); + const task = await this.info(id); + if (task.status === 0) await this.stop(task); + else if (task.status === TaskStatus.Activited) await this.start(task); } /** * 启动任务 */ async start(task: TaskEntity): Promise { - if (!task) - throw new BadRequestException('Task is Empty') + if (!task) throw new BadRequestException('Task is Empty'); // 先停掉之前存在的任务 - await this.stop(task) - let repeat: any + await this.stop(task); + let repeat: any; if (task.type === 1) { // 间隔 Repeat every millis (cron setting cannot be used together with this setting.) repeat = { every: task.every, - } - } - else { + }; + } else { // cron repeat = { cron: task.cron, - } + }; // Start date when the repeat job should start repeating (only with cron). - if (task.startTime) - repeat.startDate = task.startTime + if (task.startTime) repeat.startDate = task.startTime; - if (task.endTime) - repeat.endDate = task.endTime + if (task.endTime) repeat.endDate = task.endTime; } - if (task.limit > 0) - repeat.limit = task.limit + if (task.limit > 0) repeat.limit = task.limit; const job = await this.taskQueue.add( { id: task.id, service: task.service, args: task.data }, - { jobId: task.id, removeOnComplete: true, removeOnFail: true, repeat }, - ) + { jobId: task.id, removeOnComplete: true, removeOnFail: true, repeat } + ); if (job && job.opts) { await this.taskRepository.update(task.id, { jobOpts: JSON.stringify(job.opts.repeat), status: 1, - }) - } - else { + }); + } else { // update status to 0,标识暂停任务,因为启动失败 - await job?.remove() + await job?.remove(); await this.taskRepository.update(task.id, { status: TaskStatus.Disabled, - }) - throw new BadRequestException('Task Start failed') + }); + throw new BadRequestException('Task Start failed'); } } @@ -224,15 +203,14 @@ export class TaskService implements OnModuleInit { * 停止任务 */ async stop(task: TaskEntity): Promise { - if (!task) - throw new BadRequestException('Task is Empty') + if (!task) throw new BadRequestException('Task is Empty'); - const exist = await this.existJob(task.id.toString()) + const exist = await this.existJob(task.id.toString()); if (!exist) { await this.taskRepository.update(task.id, { status: TaskStatus.Disabled, - }) - return + }); + return; } const jobs = await this.taskQueue.getJobs([ 'active', @@ -241,14 +219,14 @@ export class TaskService implements OnModuleInit { 'paused', 'waiting', 'completed', - ]) + ]); jobs .filter(j => j.data.id === task.id) - .forEach(async (j) => { - await j.remove() - }) + .forEach(async j => { + await j.remove(); + }); - await this.taskRepository.update(task.id, { status: TaskStatus.Disabled }) + await this.taskRepository.update(task.id, { status: TaskStatus.Disabled }); // if (task.jobOpts) { // await this.app.queue.sys.removeRepeatable(JSON.parse(task.jobOpts)); // // update status @@ -261,26 +239,26 @@ export class TaskService implements OnModuleInit { */ async existJob(jobId: string): Promise { // https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#queueremoverepeatablebykey - const jobs = await this.taskQueue.getRepeatableJobs() - const ids = jobs.map((e) => { - return e.id - }) - return ids.includes(jobId) + const jobs = await this.taskQueue.getRepeatableJobs(); + const ids = jobs.map(e => { + return e.id; + }); + return ids.includes(jobId); } /** * 更新是否已经完成,完成则移除该任务并修改状态 */ async updateTaskCompleteStatus(tid: number): Promise { - const jobs = await this.taskQueue.getRepeatableJobs() - const task = await this.taskRepository.findOneBy({ id: tid }) + const jobs = await this.taskQueue.getRepeatableJobs(); + const task = await this.taskRepository.findOneBy({ id: tid }); // 如果下次执行时间小于当前时间,则表示已经执行完成。 for (const job of jobs) { - const currentTime = new Date().getTime() + const currentTime = new Date().getTime(); if (job.id === tid.toString() && job.next < currentTime) { // 如果下次执行时间小于当前时间,则表示已经执行完成。 - await this.stop(task) - break + await this.stop(task); + break; } } } @@ -289,38 +267,27 @@ export class TaskService implements OnModuleInit { * 检测service是否有注解定义 * @param serviceName service */ - async checkHasMissionMeta( - nameOrInstance: string | unknown, - exec: string, - ): Promise { + async checkHasMissionMeta(nameOrInstance: string | unknown, exec: string): Promise { try { - let service: any + let service: any; if (typeof nameOrInstance === 'string') - service = await this.moduleRef.get(nameOrInstance, { strict: false }) - else - service = nameOrInstance + service = await this.moduleRef.get(nameOrInstance, { strict: false }); + else service = nameOrInstance; // 所执行的任务不存在 - if (!service || !(exec in service)) - throw new NotFoundException('任务不存在') + if (!service || !(exec in service)) throw new NotFoundException('任务不存在'); // 检测是否有Mission注解 - const hasMission = this.reflector.get( - MISSION_DECORATOR_KEY, - service.constructor, - ) + const hasMission = this.reflector.get(MISSION_DECORATOR_KEY, service.constructor); // 如果没有,则抛出错误 - if (!hasMission) - throw new BusinessException(ErrorEnum.INSECURE_MISSION) - } - catch (e) { + if (!hasMission) throw new BusinessException(ErrorEnum.INSECURE_MISSION); + } catch (e) { if (e instanceof UnknownElementException) { // 任务不存在 - throw new NotFoundException('任务不存在') - } - else { + throw new NotFoundException('任务不存在'); + } else { // 其余错误则不处理,继续抛出 - throw e + throw e; } } } @@ -330,29 +297,26 @@ export class TaskService implements OnModuleInit { */ async callService(name: string, args: string): Promise { if (name) { - const [serviceName, methodName] = name.split('.') - if (!methodName) - throw new BadRequestException('serviceName define BadRequestException') + const [serviceName, methodName] = name.split('.'); + if (!methodName) throw new BadRequestException('serviceName define BadRequestException'); const service = await this.moduleRef.get(serviceName, { strict: false, - }) + }); // 安全注解检查 - await this.checkHasMissionMeta(service, methodName) + await this.checkHasMissionMeta(service, methodName); if (isEmpty(args)) { - await service[methodName]() - } - else { + await service[methodName](); + } else { // 参数安全判断 - const parseArgs = this.safeParse(args) + const parseArgs = this.safeParse(args); if (Array.isArray(parseArgs)) { // 数组形式则自动扩展成方法参数回掉 - await service[methodName](...parseArgs) - } - else { - await service[methodName](parseArgs) + await service[methodName](...parseArgs); + } else { + await service[methodName](parseArgs); } } } @@ -360,10 +324,9 @@ export class TaskService implements OnModuleInit { safeParse(args: string): unknown | string { try { - return JSON.parse(args) - } - catch (e) { - return args + return JSON.parse(args); + } catch (e) { + return args; } } } diff --git a/src/modules/system/task/task.ts b/src/modules/system/task/task.ts index 26d1e2c..10c09b3 100644 --- a/src/modules/system/task/task.ts +++ b/src/modules/system/task/task.ts @@ -1,2 +1,2 @@ -export const SYS_TASK_QUEUE_NAME = 'system:sys-task' -export const SYS_TASK_QUEUE_PREFIX = 'system:sys:task' +export const SYS_TASK_QUEUE_NAME = 'system:sys-task'; +export const SYS_TASK_QUEUE_PREFIX = 'system:sys:task'; diff --git a/src/modules/tasks/jobs/email.job.ts b/src/modules/tasks/jobs/email.job.ts index 1f923e4..be1bc61 100644 --- a/src/modules/tasks/jobs/email.job.ts +++ b/src/modules/tasks/jobs/email.job.ts @@ -1,9 +1,9 @@ -import { BadRequestException, Injectable } from '@nestjs/common' +import { BadRequestException, Injectable } from '@nestjs/common'; -import { LoggerService } from '~/shared/logger/logger.service' -import { MailerService } from '~/shared/mailer/mailer.service' +import { LoggerService } from '~/shared/logger/logger.service'; +import { MailerService } from '~/shared/mailer/mailer.service'; -import { Mission } from '../mission.decorator' +import { Mission } from '../mission.decorator'; /** * Api接口请求类型任务 @@ -13,17 +13,16 @@ import { Mission } from '../mission.decorator' export class EmailJob { constructor( private readonly emailService: MailerService, - private readonly logger: LoggerService, + private readonly logger: LoggerService ) {} async send(config: any): Promise { if (config) { - const { to, subject, content } = config - const result = await this.emailService.send(to, subject, content) - this.logger.log(result, EmailJob.name) - } - else { - throw new BadRequestException('Email send job param is empty') + const { to, subject, content } = config; + const result = await this.emailService.send(to, subject, content); + this.logger.log(result, EmailJob.name); + } else { + throw new BadRequestException('Email send job param is empty'); } } } diff --git a/src/modules/tasks/jobs/http-request.job.ts b/src/modules/tasks/jobs/http-request.job.ts index 359cb06..7922913 100644 --- a/src/modules/tasks/jobs/http-request.job.ts +++ b/src/modules/tasks/jobs/http-request.job.ts @@ -1,9 +1,9 @@ -import { HttpService } from '@nestjs/axios' -import { BadRequestException, Injectable } from '@nestjs/common' +import { HttpService } from '@nestjs/axios'; +import { BadRequestException, Injectable } from '@nestjs/common'; -import { LoggerService } from '~/shared/logger/logger.service' +import { LoggerService } from '~/shared/logger/logger.service'; -import { Mission } from '../mission.decorator' +import { Mission } from '../mission.decorator'; /** * Api接口请求类型任务 @@ -13,7 +13,7 @@ import { Mission } from '../mission.decorator' export class HttpRequestJob { constructor( private readonly httpService: HttpService, - private readonly logger: LoggerService, + private readonly logger: LoggerService ) {} /** @@ -22,11 +22,10 @@ export class HttpRequestJob { */ async handle(config: unknown): Promise { if (config) { - const result = await this.httpService.request(config) - this.logger.log(result, HttpRequestJob.name) - } - else { - throw new BadRequestException('Http request job param is empty') + const result = await this.httpService.request(config); + this.logger.log(result, HttpRequestJob.name); + } else { + throw new BadRequestException('Http request job param is empty'); } } } diff --git a/src/modules/tasks/jobs/log-clear.job.ts b/src/modules/tasks/jobs/log-clear.job.ts index fe6fa52..dc24f5d 100644 --- a/src/modules/tasks/jobs/log-clear.job.ts +++ b/src/modules/tasks/jobs/log-clear.job.ts @@ -1,9 +1,9 @@ -import { Injectable } from '@nestjs/common' +import { Injectable } from '@nestjs/common'; -import { LoginLogService } from '~/modules/system/log/services/login-log.service' -import { TaskLogService } from '~/modules/system/log/services/task-log.service' +import { LoginLogService } from '~/modules/system/log/services/login-log.service'; +import { TaskLogService } from '~/modules/system/log/services/task-log.service'; -import { Mission } from '../mission.decorator' +import { Mission } from '../mission.decorator'; /** * 管理后台日志清理任务 @@ -13,14 +13,14 @@ import { Mission } from '../mission.decorator' export class LogClearJob { constructor( private loginLogService: LoginLogService, - private taskLogService: TaskLogService, + private taskLogService: TaskLogService ) {} async clearLoginLog(): Promise { - await this.loginLogService.clearLog() + await this.loginLogService.clearLog(); } async clearTaskLog(): Promise { - await this.taskLogService.clearLog() + await this.taskLogService.clearLog(); } } diff --git a/src/modules/tasks/mission.decorator.ts b/src/modules/tasks/mission.decorator.ts index c14c7c9..beef1b1 100644 --- a/src/modules/tasks/mission.decorator.ts +++ b/src/modules/tasks/mission.decorator.ts @@ -1,8 +1,8 @@ -import { SetMetadata } from '@nestjs/common' +import { SetMetadata } from '@nestjs/common'; -export const MISSION_DECORATOR_KEY = 'decorator:mission' +export const MISSION_DECORATOR_KEY = 'decorator:mission'; /** * 定时任务标记,没有该任务标记的任务不会被执行,保证全局获取下的模块被安全执行 */ -export const Mission = () => SetMetadata(MISSION_DECORATOR_KEY, true) +export const Mission = () => SetMetadata(MISSION_DECORATOR_KEY, true); diff --git a/src/modules/tasks/tasks.module.ts b/src/modules/tasks/tasks.module.ts index 9c6b1de..dd7561d 100644 --- a/src/modules/tasks/tasks.module.ts +++ b/src/modules/tasks/tasks.module.ts @@ -1,13 +1,13 @@ -import { DynamicModule, ExistingProvider, Module } from '@nestjs/common' +import { DynamicModule, ExistingProvider, Module } from '@nestjs/common'; -import { LogModule } from '~/modules/system/log/log.module' -import { SystemModule } from '~/modules/system/system.module' +import { LogModule } from '~/modules/system/log/log.module'; +import { SystemModule } from '~/modules/system/system.module'; -import { EmailJob } from './jobs/email.job' -import { HttpRequestJob } from './jobs/http-request.job' -import { LogClearJob } from './jobs/log-clear.job' +import { EmailJob } from './jobs/email.job'; +import { HttpRequestJob } from './jobs/http-request.job'; +import { LogClearJob } from './jobs/log-clear.job'; -const providers = [LogClearJob, HttpRequestJob, EmailJob] +const providers = [LogClearJob, HttpRequestJob, EmailJob]; /** * auto create alias @@ -17,14 +17,14 @@ const providers = [LogClearJob, HttpRequestJob, EmailJob] * } */ function createAliasProviders(): ExistingProvider[] { - const aliasProviders: ExistingProvider[] = [] + const aliasProviders: ExistingProvider[] = []; for (const p of providers) { aliasProviders.push({ provide: p.name, useExisting: p, - }) + }); } - return aliasProviders + return aliasProviders; } /** @@ -34,13 +34,13 @@ function createAliasProviders(): ExistingProvider[] { export class TasksModule { static forRoot(): DynamicModule { // 使用Alias定义别名,使得可以通过字符串类型获取定义的Service,否则无法获取 - const aliasProviders = createAliasProviders() + const aliasProviders = createAliasProviders(); return { global: true, module: TasksModule, imports: [SystemModule, LogModule], providers: [...providers, ...aliasProviders], exports: aliasProviders, - } + }; } } diff --git a/src/modules/todo/todo.controller.ts b/src/modules/todo/todo.controller.ts index 0cb95d6..ca415d0 100644 --- a/src/modules/todo/todo.controller.ts +++ b/src/modules/todo/todo.controller.ts @@ -1,27 +1,18 @@ -import { - Body, - Controller, - Delete, - Get, - Post, - Put, - Query, - UseGuards, -} from '@nestjs/common' -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { Body, Controller, Delete, Get, Post, Put, Query, UseGuards } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; -import { Pagination } from '~/helper/paginate/pagination' -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' -import { Resource } from '~/modules/auth/decorators/resource.decorator' +import { Pagination } from '~/helper/paginate/pagination'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { Resource } from '~/modules/auth/decorators/resource.decorator'; -import { ResourceGuard } from '~/modules/auth/guards/resource.guard' -import { TodoEntity } from '~/modules/todo/todo.entity' +import { ResourceGuard } from '~/modules/auth/guards/resource.guard'; +import { TodoEntity } from '~/modules/todo/todo.entity'; -import { TodoDto, TodoQueryDto, TodoUpdateDto } from './todo.dto' -import { TodoService } from './todo.service' +import { TodoDto, TodoQueryDto, TodoUpdateDto } from './todo.dto'; +import { TodoService } from './todo.service'; export const permissions = definePermission('todo', { LIST: 'list', @@ -29,7 +20,7 @@ export const permissions = definePermission('todo', { READ: 'read', UPDATE: 'update', DELETE: 'delete', -} as const) +} as const); @ApiTags('Business - Todo模块') @UseGuards(ResourceGuard) @@ -42,7 +33,7 @@ export class TodoController { @ApiResult({ type: [TodoEntity] }) @Perm(permissions.LIST) async list(@Query() dto: TodoQueryDto): Promise> { - return this.todoService.list(dto) + return this.todoService.list(dto); } @Get(':id') @@ -50,23 +41,22 @@ export class TodoController { @ApiResult({ type: TodoEntity }) @Perm(permissions.READ) async info(@IdParam() id: number): Promise { - return this.todoService.detail(id) + return this.todoService.detail(id); } @Post() @ApiOperation({ summary: '创建Todo' }) @Perm(permissions.CREATE) async create(@Body() dto: TodoDto): Promise { - await this.todoService.create(dto) + await this.todoService.create(dto); } @Put(':id') @ApiOperation({ summary: '更新Todo' }) @Perm(permissions.UPDATE) @Resource(TodoEntity) - async update( - @IdParam() id: number, @Body() dto: TodoUpdateDto): Promise { - await this.todoService.update(id, dto) + async update(@IdParam() id: number, @Body() dto: TodoUpdateDto): Promise { + await this.todoService.update(id, dto); } @Delete(':id') @@ -74,6 +64,6 @@ export class TodoController { @Perm(permissions.DELETE) @Resource(TodoEntity) async delete(@IdParam() id: number): Promise { - await this.todoService.delete(id) + await this.todoService.delete(id); } } diff --git a/src/modules/todo/todo.dto.ts b/src/modules/todo/todo.dto.ts index 8b222b3..9554651 100644 --- a/src/modules/todo/todo.dto.ts +++ b/src/modules/todo/todo.dto.ts @@ -1,12 +1,12 @@ -import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger' -import { IsString } from 'class-validator' +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; export class TodoDto { @ApiProperty({ description: '名称' }) @IsString() - value: string + value: string; } export class TodoUpdateDto extends PartialType(TodoDto) {} diff --git a/src/modules/todo/todo.entity.ts b/src/modules/todo/todo.entity.ts index acc32ad..41b8ffb 100644 --- a/src/modules/todo/todo.entity.ts +++ b/src/modules/todo/todo.entity.ts @@ -1,20 +1,20 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinColumn, ManyToOne, Relation } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' -import { UserEntity } from '~/modules/user/user.entity' +import { CommonEntity } from '~/common/entity/common.entity'; +import { UserEntity } from '~/modules/user/user.entity'; @Entity('todo') export class TodoEntity extends CommonEntity { @Column() @ApiProperty({ description: 'todo' }) - value: string + value: string; @ApiProperty({ description: 'todo' }) @Column({ default: false }) - status: boolean + status: boolean; @ManyToOne(() => UserEntity) @JoinColumn({ name: 'user_id' }) - user: Relation + user: Relation; } diff --git a/src/modules/todo/todo.module.ts b/src/modules/todo/todo.module.ts index 5a3d417..78ee8ff 100644 --- a/src/modules/todo/todo.module.ts +++ b/src/modules/todo/todo.module.ts @@ -1,11 +1,11 @@ -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { TodoController } from './todo.controller' -import { TodoEntity } from './todo.entity' -import { TodoService } from './todo.service' +import { TodoController } from './todo.controller'; +import { TodoEntity } from './todo.entity'; +import { TodoService } from './todo.service'; -const services = [TodoService] +const services = [TodoService]; @Module({ imports: [TypeOrmModule.forFeature([TodoEntity])], diff --git a/src/modules/todo/todo.service.ts b/src/modules/todo/todo.service.ts index 4fbf35e..26b4e23 100644 --- a/src/modules/todo/todo.service.ts +++ b/src/modules/todo/todo.service.ts @@ -1,46 +1,42 @@ -import { Injectable, NotFoundException } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Repository } from 'typeorm' +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; -import { paginate } from '~/helper/paginate' -import { Pagination } from '~/helper/paginate/pagination' -import { TodoEntity } from '~/modules/todo/todo.entity' +import { paginate } from '~/helper/paginate'; +import { Pagination } from '~/helper/paginate/pagination'; +import { TodoEntity } from '~/modules/todo/todo.entity'; -import { TodoDto, TodoQueryDto, TodoUpdateDto } from './todo.dto' +import { TodoDto, TodoQueryDto, TodoUpdateDto } from './todo.dto'; @Injectable() export class TodoService { constructor( @InjectRepository(TodoEntity) - private todoRepository: Repository, + private todoRepository: Repository ) {} - async list({ - page, - pageSize, - }: TodoQueryDto): Promise> { - return paginate(this.todoRepository, { page, pageSize }) + async list({ page, pageSize }: TodoQueryDto): Promise> { + return paginate(this.todoRepository, { page, pageSize }); } async detail(id: number): Promise { - const item = await this.todoRepository.findOneBy({ id }) - if (!item) - throw new NotFoundException('未找到该记录') + const item = await this.todoRepository.findOneBy({ id }); + if (!item) throw new NotFoundException('未找到该记录'); - return item + return item; } async create(dto: TodoDto) { - await this.todoRepository.save(dto) + await this.todoRepository.save(dto); } async update(id: number, dto: TodoUpdateDto) { - await this.todoRepository.update(id, dto) + await this.todoRepository.update(id, dto); } async delete(id: number) { - const item = await this.detail(id) + const item = await this.detail(id); - await this.todoRepository.remove(item) + await this.todoRepository.remove(item); } } diff --git a/src/modules/tools/email/email.controller.ts b/src/modules/tools/email/email.controller.ts index d3a1816..6afde9d 100644 --- a/src/modules/tools/email/email.controller.ts +++ b/src/modules/tools/email/email.controller.ts @@ -1,11 +1,11 @@ -import { Body, Controller, Post } from '@nestjs/common' +import { Body, Controller, Post } from '@nestjs/common'; -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { MailerService } from '~/shared/mailer/mailer.service' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { MailerService } from '~/shared/mailer/mailer.service'; -import { EmailSendDto } from './email.dto' +import { EmailSendDto } from './email.dto'; @ApiTags('System - 邮箱模块') @ApiSecurityAuth() @@ -16,7 +16,7 @@ export class EmailController { @ApiOperation({ summary: '发送邮件' }) @Post('send') async send(@Body() dto: EmailSendDto): Promise { - const { to, subject, content } = dto - await this.emailService.send(to, subject, content, 'html') + const { to, subject, content } = dto; + await this.emailService.send(to, subject, content, 'html'); } } diff --git a/src/modules/tools/email/email.dto.ts b/src/modules/tools/email/email.dto.ts index 8dcca35..b6cd598 100644 --- a/src/modules/tools/email/email.dto.ts +++ b/src/modules/tools/email/email.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger' -import { IsEmail, IsString } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail, IsString } from 'class-validator'; /** * 发送邮件 @@ -7,13 +7,13 @@ import { IsEmail, IsString } from 'class-validator' export class EmailSendDto { @ApiProperty({ description: '收件人邮箱' }) @IsEmail() - to: string + to: string; @ApiProperty({ description: '标题' }) @IsString() - subject: string + subject: string; @ApiProperty({ description: '正文' }) @IsString() - content: string + content: string; } diff --git a/src/modules/tools/email/email.module.ts b/src/modules/tools/email/email.module.ts index e9ba5b1..76e689a 100644 --- a/src/modules/tools/email/email.module.ts +++ b/src/modules/tools/email/email.module.ts @@ -1,6 +1,6 @@ -import { Module } from '@nestjs/common' +import { Module } from '@nestjs/common'; -import { EmailController } from './email.controller' +import { EmailController } from './email.controller'; @Module({ imports: [], diff --git a/src/modules/tools/storage/storage.controller.ts b/src/modules/tools/storage/storage.controller.ts index bdd92ba..c7a13a3 100644 --- a/src/modules/tools/storage/storage.controller.ts +++ b/src/modules/tools/storage/storage.controller.ts @@ -1,22 +1,22 @@ -import { Body, Controller, Get, Post, Query } from '@nestjs/common' +import { Body, Controller, Get, Post, Query } from '@nestjs/common'; -import { ApiOperation, ApiTags } from '@nestjs/swagger' +import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; -import { Pagination } from '~/helper/paginate/pagination' +import { Pagination } from '~/helper/paginate/pagination'; -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; -import { StorageDeleteDto, StoragePageDto } from './storage.dto' -import { StorageInfo } from './storage.modal' -import { StorageService } from './storage.service' +import { StorageDeleteDto, StoragePageDto } from './storage.dto'; +import { StorageInfo } from './storage.modal'; +import { StorageService } from './storage.service'; export const permissions = definePermission('tool:storage', { LIST: 'list', DELETE: 'delete', -} as const) +} as const); @ApiTags('Tools - 存储模块') @ApiSecurityAuth() @@ -29,13 +29,13 @@ export class StorageController { @ApiResult({ type: [StorageInfo], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: StoragePageDto): Promise> { - return this.storageService.list(dto) + return this.storageService.list(dto); } @ApiOperation({ summary: '删除文件' }) @Post('delete') @Perm(permissions.DELETE) async delete(@Body() dto: StorageDeleteDto): Promise { - await this.storageService.delete(dto.ids) + await this.storageService.delete(dto.ids); } } diff --git a/src/modules/tools/storage/storage.dto.ts b/src/modules/tools/storage/storage.dto.ts index 5e50398..ed25a7e 100644 --- a/src/modules/tools/storage/storage.dto.ts +++ b/src/modules/tools/storage/storage.dto.ts @@ -1,68 +1,68 @@ -import { ApiProperty } from '@nestjs/swagger' -import { ArrayNotEmpty, IsArray, IsOptional, IsString } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { ArrayNotEmpty, IsArray, IsOptional, IsString } from 'class-validator'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; export class StoragePageDto extends PagerDto { @ApiProperty({ description: '文件名' }) @IsOptional() @IsString() - name: string + name: string; @ApiProperty({ description: '文件后缀' }) @IsString() @IsOptional() - extName: string + extName: string; @ApiProperty({ description: '文件类型' }) @IsString() @IsOptional() - type: string + type: string; @ApiProperty({ description: '大小' }) @IsString() @IsOptional() - size: string + size: string; @ApiProperty({ description: '上传时间' }) @IsOptional() - time: string[] + time: string[]; @ApiProperty({ description: '上传者' }) @IsString() @IsOptional() - username: string + username: string; } export class StorageCreateDto { @ApiProperty({ description: '文件名' }) @IsString() - name: string + name: string; @ApiProperty({ description: '真实文件名' }) @IsString() - fileName: string + fileName: string; @ApiProperty({ description: '文件扩展名' }) @IsString() - extName: string + extName: string; @ApiProperty({ description: '文件路径' }) @IsString() - path: string + path: string; @ApiProperty({ description: '文件路径' }) @IsString() - type: string + type: string; @ApiProperty({ description: '文件大小' }) @IsString() - size: string + size: string; } export class StorageDeleteDto { @ApiProperty({ description: '需要删除的文件ID列表', type: [Number] }) @IsArray() @ArrayNotEmpty() - ids: number[] + ids: number[]; } diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index c68dcf5..584ec59 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -1,13 +1,13 @@ -import { ApiProperty } from '@nestjs/swagger' -import { Column, Entity } from 'typeorm' +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity } from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; @Entity({ name: 'tool_storage' }) export class Storage extends CommonEntity { @Column({ type: 'varchar', length: 200, comment: '文件名' }) @ApiProperty({ description: '文件名' }) - name: string + name: string; @Column({ type: 'varchar', @@ -16,25 +16,25 @@ export class Storage extends CommonEntity { comment: '真实文件名', }) @ApiProperty({ description: '真实文件名' }) - fileName: string + fileName: string; @Column({ name: 'ext_name', type: 'varchar', nullable: true }) @ApiProperty({ description: '扩展名' }) - extName: string + extName: string; @Column({ type: 'varchar' }) @ApiProperty({ description: '文件类型' }) - path: string + path: string; @Column({ type: 'varchar', nullable: true }) @ApiProperty({ description: '文件类型' }) - type: string + type: string; @Column({ type: 'varchar', nullable: true }) @ApiProperty({ description: '文件大小' }) - size: string + size: string; @Column({ nullable: true, name: 'user_id' }) @ApiProperty({ description: '用户ID' }) - userId: number + userId: number; } diff --git a/src/modules/tools/storage/storage.modal.ts b/src/modules/tools/storage/storage.modal.ts index 203e974..43ede4e 100644 --- a/src/modules/tools/storage/storage.modal.ts +++ b/src/modules/tools/storage/storage.modal.ts @@ -1,27 +1,27 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; export class StorageInfo { @ApiProperty({ description: '文件ID' }) - id: number + id: number; @ApiProperty({ description: '文件名' }) - name: string + name: string; @ApiProperty({ description: '文件扩展名' }) - extName: string + extName: string; @ApiProperty({ description: '文件路径' }) - path: string + path: string; @ApiProperty({ description: '文件类型' }) - type: string + type: string; @ApiProperty({ description: '大小' }) - size: string + size: string; @ApiProperty({ description: '上传时间' }) - createdAt: string + createdAt: string; @ApiProperty({ description: '上传者' }) - username: string + username: string; } diff --git a/src/modules/tools/storage/storage.module.ts b/src/modules/tools/storage/storage.module.ts index aed7f33..e74ca3f 100644 --- a/src/modules/tools/storage/storage.module.ts +++ b/src/modules/tools/storage/storage.module.ts @@ -1,13 +1,13 @@ -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { UserEntity } from '~/modules/user/user.entity' +import { UserEntity } from '~/modules/user/user.entity'; -import { StorageController } from './storage.controller' -import { Storage } from './storage.entity' -import { StorageService } from './storage.service' +import { StorageController } from './storage.controller'; +import { Storage } from './storage.entity'; +import { StorageService } from './storage.service'; -const services = [StorageService] +const services = [StorageService]; @Module({ imports: [TypeOrmModule.forFeature([Storage, UserEntity])], diff --git a/src/modules/tools/storage/storage.service.ts b/src/modules/tools/storage/storage.service.ts index e7ddefa..99c5fb9 100644 --- a/src/modules/tools/storage/storage.service.ts +++ b/src/modules/tools/storage/storage.service.ts @@ -1,16 +1,16 @@ -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Between, Like, Repository } from 'typeorm' +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Between, Like, Repository } from 'typeorm'; -import { paginateRaw } from '~/helper/paginate' -import { PaginationTypeEnum } from '~/helper/paginate/interface' -import { Pagination } from '~/helper/paginate/pagination' -import { Storage } from '~/modules/tools/storage/storage.entity' -import { UserEntity } from '~/modules/user/user.entity' -import { deleteFile } from '~/utils' +import { paginateRaw } from '~/helper/paginate'; +import { PaginationTypeEnum } from '~/helper/paginate/interface'; +import { Pagination } from '~/helper/paginate/pagination'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { UserEntity } from '~/modules/user/user.entity'; +import { deleteFile } from '~/utils'; -import { StorageCreateDto, StoragePageDto } from './storage.dto' -import { StorageInfo } from './storage.modal' +import { StorageCreateDto, StoragePageDto } from './storage.dto'; +import { StorageInfo } from './storage.modal'; @Injectable() export class StorageService { @@ -18,26 +18,26 @@ export class StorageService { @InjectRepository(Storage) private storageRepository: Repository, @InjectRepository(UserEntity) - private userRepository: Repository, + private userRepository: Repository ) {} async create(dto: StorageCreateDto, userId: number): Promise { await this.storageRepository.save({ ...dto, userId, - }) + }); } /** * 删除文件 */ async delete(fileIds: number[]): Promise { - const items = await this.storageRepository.findByIds(fileIds) - await this.storageRepository.delete(fileIds) + const items = await this.storageRepository.findByIds(fileIds); + await this.storageRepository.delete(fileIds); - items.forEach((el) => { - deleteFile(el.path) - }) + items.forEach(el => { + deleteFile(el.path); + }); } async list({ @@ -63,13 +63,13 @@ export class StorageService { userId: await (await this.userRepository.findOneBy({ username })).id, }), }) - .orderBy('storage.created_at', 'DESC') + .orderBy('storage.created_at', 'DESC'); const { items, ...rest } = await paginateRaw(queryBuilder, { page, pageSize, paginationType: PaginationTypeEnum.LIMIT_AND_OFFSET, - }) + }); function formatResult(result: Storage[]) { return result.map((e: any) => { @@ -82,17 +82,17 @@ export class StorageService { size: e.storage_size, createdAt: e.storage_created_at, username: e.user_username, - } - }) + }; + }); } return { items: formatResult(items), ...rest, - } + }; } async count(): Promise { - return this.storageRepository.count() + return this.storageRepository.count(); } } diff --git a/src/modules/tools/tools.module.ts b/src/modules/tools/tools.module.ts index d22d131..9e8b5e0 100644 --- a/src/modules/tools/tools.module.ts +++ b/src/modules/tools/tools.module.ts @@ -1,21 +1,24 @@ -import { Module } from '@nestjs/common' +import { Module } from '@nestjs/common'; -import { RouterModule } from '@nestjs/core' +import { RouterModule } from '@nestjs/core'; -import { EmailModule } from './email/email.module' -import { StorageModule } from './storage/storage.module' -import { UploadModule } from './upload/upload.module' +import { EmailModule } from './email/email.module'; +import { StorageModule } from './storage/storage.module'; +import { UploadModule } from './upload/upload.module'; -const modules = [StorageModule, EmailModule, UploadModule] +const modules = [StorageModule, EmailModule, UploadModule]; @Module({ - imports: [...modules, RouterModule.register([ - { - path: 'tools', - module: ToolsModule, - children: [...modules], - }, - ])], + imports: [ + ...modules, + RouterModule.register([ + { + path: 'tools', + module: ToolsModule, + children: [...modules], + }, + ]), + ], exports: [...modules], }) export class ToolsModule {} diff --git a/src/modules/tools/upload/file.constraint.ts b/src/modules/tools/upload/file.constraint.ts index dfaa04b..3adcf49 100644 --- a/src/modules/tools/upload/file.constraint.ts +++ b/src/modules/tools/upload/file.constraint.ts @@ -1,45 +1,35 @@ -import { FastifyMultipartBaseOptions, MultipartFile } from '@fastify/multipart' +import { FastifyMultipartBaseOptions, MultipartFile } from '@fastify/multipart'; import { ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, registerDecorator, -} from 'class-validator' -import { has, isArray } from 'lodash' +} from 'class-validator'; +import { has, isArray } from 'lodash'; -type FileLimit = Pick< - FastifyMultipartBaseOptions['limits'], - 'fileSize' | 'files' -> & { - mimetypes?: string[] -} +type FileLimit = Pick & { + mimetypes?: string[]; +}; function checkFileAndLimit(file: MultipartFile, limits: FileLimit = {}) { - if (!('mimetype' in file)) - return false - if (limits.mimetypes && !limits.mimetypes.includes(file.mimetype)) - return false - if ( - has(file, '_buf') - && Buffer.byteLength((file as any)._buf) > limits.fileSize - ) - return false - return true + if (!('mimetype' in file)) return false; + if (limits.mimetypes && !limits.mimetypes.includes(file.mimetype)) return false; + if (has(file, '_buf') && Buffer.byteLength((file as any)._buf) > limits.fileSize) return false; + return true; } @ValidatorConstraint({ name: 'isFile' }) export class FileConstraint implements ValidatorConstraintInterface { validate(value: MultipartFile, args: ValidationArguments) { - const [limits = {}] = args.constraints - const values = (args.object as any)[args.property] - const filesLimit = (limits as FileLimit).files ?? 0 - if (filesLimit > 0 && isArray(values) && values.length > filesLimit) - return false - return checkFileAndLimit(value, limits) + const [limits = {}] = args.constraints; + const values = (args.object as any)[args.property]; + const filesLimit = (limits as FileLimit).files ?? 0; + if (filesLimit > 0 && isArray(values) && values.length > filesLimit) return false; + return checkFileAndLimit(value, limits); } defaultMessage(_args: ValidationArguments) { - return `The file which to upload's conditions are not met` + return `The file which to upload's conditions are not met`; } } @@ -48,10 +38,7 @@ export class FileConstraint implements ValidatorConstraintInterface { * @param limits 限制选项 * @param validationOptions class-validator选项 */ -export function IsFile( - limits?: FileLimit, - validationOptions?: ValidationOptions, -) { +export function IsFile(limits?: FileLimit, validationOptions?: ValidationOptions) { return (object: Record, propertyName: string) => { registerDecorator({ target: object.constructor, @@ -59,6 +46,6 @@ export function IsFile( options: validationOptions, constraints: [limits], validator: FileConstraint, - }) - } + }); + }; } diff --git a/src/modules/tools/upload/upload.controller.ts b/src/modules/tools/upload/upload.controller.ts index 8a73704..e501d68 100644 --- a/src/modules/tools/upload/upload.controller.ts +++ b/src/modules/tools/upload/upload.controller.ts @@ -1,18 +1,18 @@ -import { BadRequestException, Controller, Post, Req } from '@nestjs/common' -import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger' -import { FastifyRequest } from 'fastify' +import { BadRequestException, Controller, Post, Req } from '@nestjs/common'; +import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { FastifyRequest } from 'fastify'; -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator' +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { AuthUser } from '~/modules/auth/decorators/auth-user.decorator'; -import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator' +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; -import { FileUploadDto } from './upload.dto' -import { UploadService } from './upload.service' +import { FileUploadDto } from './upload.dto'; +import { UploadService } from './upload.service'; export const permissions = definePermission('upload', { UPLOAD: 'upload', -} as const) +} as const); @ApiSecurityAuth() @ApiTags('Tools - 上传模块') @@ -28,10 +28,9 @@ export class UploadController { type: FileUploadDto, }) async upload(@Req() req: FastifyRequest, @AuthUser() user: IAuthUser) { - if (!req.isMultipart()) - throw new BadRequestException('Request is not multipart') + if (!req.isMultipart()) throw new BadRequestException('Request is not multipart'); - const file = await req.file() + const file = await req.file(); // https://github.com/fastify/fastify-multipart // const parts = req.files() @@ -39,15 +38,14 @@ export class UploadController { // console.log(part.file) try { - const path = await this.uploadService.saveFile(file, user.uid) + const path = await this.uploadService.saveFile(file, user.uid); return { filename: path, - } - } - catch (error) { - console.log(error) - throw new BadRequestException('上传失败') + }; + } catch (error) { + console.log(error); + throw new BadRequestException('上传失败'); } } } diff --git a/src/modules/tools/upload/upload.dto.ts b/src/modules/tools/upload/upload.dto.ts index 3ab9a01..1bf0400 100644 --- a/src/modules/tools/upload/upload.dto.ts +++ b/src/modules/tools/upload/upload.dto.ts @@ -1,27 +1,21 @@ -import { MultipartFile } from '@fastify/multipart' -import { ApiProperty } from '@nestjs/swagger' +import { MultipartFile } from '@fastify/multipart'; +import { ApiProperty } from '@nestjs/swagger'; -import { IsDefined } from 'class-validator' +import { IsDefined } from 'class-validator'; -import { IsFile } from './file.constraint' +import { IsFile } from './file.constraint'; export class FileUploadDto { @ApiProperty({ type: Buffer, format: 'binary', description: '文件' }) @IsDefined() @IsFile( { - mimetypes: [ - 'image/png', - 'image/gif', - 'image/jpeg', - 'image/webp', - 'image/svg+xml', - ], + mimetypes: ['image/png', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'], fileSize: 1024 * 1024 * 10, }, { message: '文件类型不正确', - }, + } ) - file: MultipartFile + file: MultipartFile; } diff --git a/src/modules/tools/upload/upload.module.ts b/src/modules/tools/upload/upload.module.ts index 6674739..b5a83df 100644 --- a/src/modules/tools/upload/upload.module.ts +++ b/src/modules/tools/upload/upload.module.ts @@ -1,11 +1,11 @@ -import { Module, forwardRef } from '@nestjs/common' +import { Module, forwardRef } from '@nestjs/common'; -import { StorageModule } from '../storage/storage.module' +import { StorageModule } from '../storage/storage.module'; -import { UploadController } from './upload.controller' -import { UploadService } from './upload.service' +import { UploadController } from './upload.controller'; +import { UploadService } from './upload.service'; -const services = [UploadService] +const services = [UploadService]; @Module({ imports: [forwardRef(() => StorageModule)], diff --git a/src/modules/tools/upload/upload.service.ts b/src/modules/tools/upload/upload.service.ts index 792448f..7e42663 100644 --- a/src/modules/tools/upload/upload.service.ts +++ b/src/modules/tools/upload/upload.service.ts @@ -1,10 +1,10 @@ -import { MultipartFile } from '@fastify/multipart' -import { Injectable, NotFoundException } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { isNil } from 'lodash' -import { Repository } from 'typeorm' +import { MultipartFile } from '@fastify/multipart'; +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { isNil } from 'lodash'; +import { Repository } from 'typeorm'; -import { Storage } from '~/modules/tools/storage/storage.entity' +import { Storage } from '~/modules/tools/storage/storage.entity'; import { fileRename, @@ -13,30 +13,29 @@ import { getFileType, getSize, saveLocalFile, -} from '~/utils/file.util' +} from '~/utils/file.util'; @Injectable() export class UploadService { constructor( @InjectRepository(Storage) - private storageRepository: Repository, + private storageRepository: Repository ) {} /** * 保存文件上传记录 */ async saveFile(file: MultipartFile, userId: number): Promise { - if (isNil(file)) - throw new NotFoundException('Have not any file to upload!') + if (isNil(file)) throw new NotFoundException('Have not any file to upload!'); - const fileName = file.filename - const size = getSize(file.file.bytesRead) - const extName = getExtname(fileName) - const type = getFileType(extName) - const name = fileRename(fileName) - const path = getFilePath(name) + const fileName = file.filename; + const size = getSize(file.file.bytesRead); + const extName = getExtname(fileName); + const type = getFileType(extName); + const name = fileRename(fileName); + const path = getFilePath(name); - saveLocalFile(await file.toBuffer(), name) + saveLocalFile(await file.toBuffer(), name); await this.storageRepository.save({ name, @@ -46,8 +45,8 @@ export class UploadService { type, size, userId, - }) + }); - return path + return path; } } diff --git a/src/modules/user/dto/password.dto.ts b/src/modules/user/dto/password.dto.ts index 3a6d97b..6b5e30c 100644 --- a/src/modules/user/dto/password.dto.ts +++ b/src/modules/user/dto/password.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger' -import { IsString, Matches, MaxLength, MinLength } from 'class-validator' +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, Matches, MaxLength, MinLength } from 'class-validator'; export class PasswordUpdateDto { @ApiProperty({ description: '旧密码' }) @@ -7,13 +7,13 @@ export class PasswordUpdateDto { @Matches(/^[a-z0-9A-Z\W_]+$/) @MinLength(6) @MaxLength(20) - oldPassword: string + oldPassword: string; @ApiProperty({ description: '新密码' }) @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { message: '密码必须包含数字、字母,长度为6-16', }) - newPassword: string + newPassword: string; } export class UserPasswordDto { @@ -26,7 +26,7 @@ export class UserPasswordDto { @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { message: '密码格式不正确', }) - password: string + password: string; } export class UserExistDto { @@ -35,5 +35,5 @@ export class UserExistDto { @Matches(/^[a-zA-Z0-9_-]{4,16}$/) @MinLength(6) @MaxLength(20) - username: string + username: string; } diff --git a/src/modules/user/dto/user.dto.ts b/src/modules/user/dto/user.dto.ts index 60a44fa..17bb5b6 100644 --- a/src/modules/user/dto/user.dto.ts +++ b/src/modules/user/dto/user.dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger' -import { Type } from 'class-transformer' +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; import { ArrayMaxSize, ArrayMinSize, @@ -13,57 +13,57 @@ import { MaxLength, MinLength, ValidateIf, -} from 'class-validator' -import { isEmpty } from 'lodash' +} from 'class-validator'; +import { isEmpty } from 'lodash'; -import { PagerDto } from '~/common/dto/pager.dto' +import { PagerDto } from '~/common/dto/pager.dto'; export class UserDto { @ApiProperty({ description: '头像' }) @IsOptional() @IsString() - avatar?: string + avatar?: string; @ApiProperty({ description: '登录账号', example: 'admin' }) @IsString() @Matches(/^[a-z0-9A-Z\W_]+$/) @MinLength(1) @MaxLength(20) - username: string + username: string; @ApiProperty({ description: '登录密码', example: 'a123456' }) @IsOptional() @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { message: '密码必须包含数字、字母,长度为6-16', }) - password: string + password: string; @ApiProperty({ description: '归属角色', type: [Number] }) @ArrayNotEmpty() @ArrayMinSize(1) @ArrayMaxSize(3) - roleIds: number[] + roleIds: number[]; @ApiProperty({ description: '归属大区', type: Number }) @Type(() => Number) @IsInt() @IsOptional() - deptId?: number + deptId?: number; @ApiProperty({ description: '呢称', example: 'admin' }) @IsOptional() @IsString() - nickname: string + nickname: string; @ApiProperty({ description: '邮箱', example: 'bqy.dev@qq.com' }) @IsEmail() @ValidateIf(o => !isEmpty(o.email)) - email: string + email: string; @ApiProperty({ description: '手机号' }) @IsOptional() @IsString() - phone?: string + phone?: string; @ApiProperty({ description: 'QQ' }) @IsOptional() @@ -71,16 +71,16 @@ export class UserDto { @Matches(/^[1-9][0-9]{4,10}$/) @MinLength(5) @MaxLength(11) - qq?: string + qq?: string; @ApiProperty({ description: '备注' }) @IsOptional() @IsString() - remark?: string + remark?: string; @ApiProperty({ description: '状态' }) @IsIn([0, 1]) - status: number + status: number; } export class UserUpdateDto extends PartialType(UserDto) {} @@ -89,11 +89,10 @@ export class UserQueryDto extends IntersectionType(PagerDto, PartialTyp @ApiProperty({ description: '归属大区', example: 1, required: false }) @IsInt() @IsOptional() - deptId?: number + deptId?: number; @ApiProperty({ description: '状态', example: 0, required: false }) @IsInt() @IsOptional() - status?: number - + status?: number; } diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index 7b4fd1f..d462edb 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -1,17 +1,27 @@ -import { Body, Controller, Delete, Get, Param, ParseArrayPipe, Post, Put, Query } from '@nestjs/common' -import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger' +import { + Body, + Controller, + Delete, + Get, + Param, + ParseArrayPipe, + Post, + Put, + Query, +} from '@nestjs/common'; +import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; -import { ApiResult } from '~/common/decorators/api-result.decorator' -import { IdParam } from '~/common/decorators/id-param.decorator' -import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator' -import { MenuService } from '~/modules/system/menu/menu.service' +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { MenuService } from '~/modules/system/menu/menu.service'; -import { Perm, definePermission } from '../auth/decorators/permission.decorator' +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; -import { UserPasswordDto } from './dto/password.dto' -import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto' -import { UserEntity } from './user.entity' -import { UserService } from './user.service' +import { UserPasswordDto } from './dto/password.dto'; +import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto'; +import { UserEntity } from './user.entity'; +import { UserService } from './user.service'; export const permissions = definePermission('system:user', { LIST: 'list', @@ -22,7 +32,7 @@ export const permissions = definePermission('system:user', { PASSWORD_UPDATE: 'password:update', PASSWORD_RESET: 'pass:reset', -} as const) +} as const); @ApiTags('System - 用户模块') @ApiSecurityAuth() @@ -30,7 +40,7 @@ export const permissions = definePermission('system:user', { export class UserController { constructor( private userService: UserService, - private menuService: MenuService, + private menuService: MenuService ) {} @Get() @@ -38,45 +48,50 @@ export class UserController { @ApiResult({ type: [UserEntity], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: UserQueryDto) { - return this.userService.list(dto) + return this.userService.list(dto); } @Get(':id') @ApiOperation({ summary: '查询用户' }) @Perm(permissions.READ) async read(@IdParam() id: number) { - return this.userService.info(id) + return this.userService.info(id); } @Post() @ApiOperation({ summary: '新增用户' }) @Perm(permissions.CREATE) async create(@Body() dto: UserDto): Promise { - await this.userService.create(dto) + await this.userService.create(dto); } @Put(':id') @ApiOperation({ summary: '更新用户' }) @Perm(permissions.UPDATE) - async update( - @IdParam() id: number, @Body() dto: UserUpdateDto): Promise { - await this.userService.update(id, dto) - await this.menuService.refreshPerms(id) + async update(@IdParam() id: number, @Body() dto: UserUpdateDto): Promise { + await this.userService.update(id, dto); + await this.menuService.refreshPerms(id); } @Delete(':id') @ApiOperation({ summary: '删除用户' }) - @ApiParam({ name: 'id', type: String, schema: { oneOf: [{ type: 'string' }, { type: 'number' }] } }) + @ApiParam({ + name: 'id', + type: String, + schema: { oneOf: [{ type: 'string' }, { type: 'number' }] }, + }) @Perm(permissions.DELETE) - async delete(@Param('id', new ParseArrayPipe({ items: Number, separator: ',' })) ids: number[]): Promise { - await this.userService.delete(ids) - await this.userService.multiForbidden(ids) + async delete( + @Param('id', new ParseArrayPipe({ items: Number, separator: ',' })) ids: number[] + ): Promise { + await this.userService.delete(ids); + await this.userService.multiForbidden(ids); } @Post(':id/password') @ApiOperation({ summary: '更改用户密码' }) @Perm(permissions.PASSWORD_UPDATE) async password(@IdParam() id: number, @Body() dto: UserPasswordDto): Promise { - await this.userService.forceUpdatePassword(id, dto.password) + await this.userService.forceUpdatePassword(id, dto.password); } } diff --git a/src/modules/user/user.entity.ts b/src/modules/user/user.entity.ts index b1f66b8..7cd2031 100644 --- a/src/modules/user/user.entity.ts +++ b/src/modules/user/user.entity.ts @@ -1,4 +1,4 @@ -import { Exclude } from 'class-transformer' +import { Exclude } from 'class-transformer'; import { Column, Entity, @@ -8,47 +8,47 @@ import { ManyToOne, OneToMany, Relation, -} from 'typeorm' +} from 'typeorm'; -import { CommonEntity } from '~/common/entity/common.entity' +import { CommonEntity } from '~/common/entity/common.entity'; -import { AccessTokenEntity } from '~/modules/auth/entities/access-token.entity' +import { AccessTokenEntity } from '~/modules/auth/entities/access-token.entity'; -import { DeptEntity } from '~/modules/system/dept/dept.entity' -import { RoleEntity } from '~/modules/system/role/role.entity' +import { DeptEntity } from '~/modules/system/dept/dept.entity'; +import { RoleEntity } from '~/modules/system/role/role.entity'; @Entity({ name: 'sys_user' }) export class UserEntity extends CommonEntity { @Column({ unique: true }) - username: string + username: string; @Exclude() @Column() - password: string + password: string; @Column({ length: 32 }) - psalt: string + psalt: string; @Column({ nullable: true }) - nickname: string + nickname: string; @Column({ name: 'avatar', nullable: true }) - avatar: string + avatar: string; @Column({ nullable: true }) - qq: string + qq: string; @Column({ nullable: true }) - email: string + email: string; @Column({ nullable: true }) - phone: string + phone: string; @Column({ nullable: true }) - remark: string + remark: string; @Column({ type: 'tinyint', nullable: true, default: 1 }) - status: number + status: number; @ManyToMany(() => RoleEntity, role => role.users) @JoinTable({ @@ -56,14 +56,14 @@ export class UserEntity extends CommonEntity { joinColumn: { name: 'user_id', referencedColumnName: 'id' }, inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' }, }) - roles: Relation + roles: Relation; @ManyToOne(() => DeptEntity, dept => dept.users) @JoinColumn({ name: 'dept_id' }) - dept: Relation + dept: Relation; @OneToMany(() => AccessTokenEntity, accessToken => accessToken.user, { cascade: true, }) - accessTokens: Relation + accessTokens: Relation; } diff --git a/src/modules/user/user.model.ts b/src/modules/user/user.model.ts index 1493187..beb082b 100644 --- a/src/modules/user/user.model.ts +++ b/src/modules/user/user.model.ts @@ -1,21 +1,21 @@ -import { ApiProperty } from '@nestjs/swagger' +import { ApiProperty } from '@nestjs/swagger'; export class AccountInfo { @ApiProperty({ description: '用户名' }) - username: string + username: string; @ApiProperty({ description: '昵称' }) - nickname: string + nickname: string; @ApiProperty({ description: '邮箱' }) - email: string + email: string; @ApiProperty({ description: '手机号' }) - phone: string + phone: string; @ApiProperty({ description: '备注' }) - remark: string + remark: string; @ApiProperty({ description: '头像' }) - avatar: string + avatar: string; } diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts index bd26eae..928c52b 100644 --- a/src/modules/user/user.module.ts +++ b/src/modules/user/user.module.ts @@ -1,24 +1,19 @@ -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { MenuModule } from '../system/menu/menu.module' -import { ParamConfigModule } from '../system/param-config/param-config.module' +import { MenuModule } from '../system/menu/menu.module'; +import { ParamConfigModule } from '../system/param-config/param-config.module'; -import { RoleModule } from '../system/role/role.module' +import { RoleModule } from '../system/role/role.module'; -import { UserController } from './user.controller' -import { UserEntity } from './user.entity' -import { UserService } from './user.service' +import { UserController } from './user.controller'; +import { UserEntity } from './user.entity'; +import { UserService } from './user.service'; -const providers = [UserService] +const providers = [UserService]; @Module({ - imports: [ - TypeOrmModule.forFeature([UserEntity]), - RoleModule, - MenuModule, - ParamConfigModule, - ], + imports: [TypeOrmModule.forFeature([UserEntity]), RoleModule, MenuModule, ParamConfigModule], controllers: [UserController], providers: [...providers], exports: [TypeOrmModule, ...providers], diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 294c5ef..36fa5d8 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -1,33 +1,33 @@ -import { InjectRedis } from '@liaoliaots/nestjs-redis' -import { BadRequestException, Injectable } from '@nestjs/common' -import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm' -import Redis from 'ioredis' -import { isEmpty, isNil, isNumber } from 'lodash' +import { InjectRedis } from '@liaoliaots/nestjs-redis'; +import { BadRequestException, Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import Redis from 'ioredis'; +import { isEmpty, isNil, isNumber } from 'lodash'; -import { EntityManager, In, Like, Repository } from 'typeorm' +import { EntityManager, In, Like, Repository } from 'typeorm'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { ErrorEnum } from '~/constants/error-code.constant' -import { ROOT_ROLE_ID, SYS_USER_INITPASSWORD } from '~/constants/system.constant' -import { genAuthPVKey, genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { ROOT_ROLE_ID, SYS_USER_INITPASSWORD } from '~/constants/system.constant'; +import { genAuthPVKey, genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey'; -import { paginate } from '~/helper/paginate' -import { Pagination } from '~/helper/paginate/pagination' -import { AccountUpdateDto } from '~/modules/auth/dto/account.dto' -import { RegisterDto } from '~/modules/auth/dto/auth.dto' -import { QQService } from '~/shared/helper/qq.service' +import { paginate } from '~/helper/paginate'; +import { Pagination } from '~/helper/paginate/pagination'; +import { AccountUpdateDto } from '~/modules/auth/dto/account.dto'; +import { RegisterDto } from '~/modules/auth/dto/auth.dto'; +import { QQService } from '~/shared/helper/qq.service'; -import { md5, randomValue } from '~/utils' +import { md5, randomValue } from '~/utils'; -import { DeptEntity } from '../system/dept/dept.entity' -import { ParamConfigService } from '../system/param-config/param-config.service' -import { RoleEntity } from '../system/role/role.entity' +import { DeptEntity } from '../system/dept/dept.entity'; +import { ParamConfigService } from '../system/param-config/param-config.service'; +import { RoleEntity } from '../system/role/role.entity'; -import { UserStatus } from './constant' -import { PasswordUpdateDto } from './dto/password.dto' -import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto' -import { UserEntity } from './user.entity' -import { AccountInfo } from './user.model' +import { UserStatus } from './constant'; +import { PasswordUpdateDto } from './dto/password.dto'; +import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto'; +import { UserEntity } from './user.entity'; +import { AccountInfo } from './user.model'; @Injectable() export class UserService { @@ -40,7 +40,7 @@ export class UserService { private readonly roleRepository: Repository, @InjectEntityManager() private entityManager: EntityManager, private readonly paramConfigService: ParamConfigService, - private readonly qqService: QQService, + private readonly qqService: QQService ) {} async findUserById(id: number): Promise { @@ -50,7 +50,7 @@ export class UserService { id, status: UserStatus.Enabled, }) - .getOne() + .getOne(); } async findUserByUserName(username: string): Promise { @@ -60,7 +60,7 @@ export class UserService { username, status: UserStatus.Enabled, }) - .getOne() + .getOne(); } /** @@ -72,23 +72,21 @@ export class UserService { .createQueryBuilder('user') .leftJoinAndSelect('user.roles', 'role') .where(`user.id = :uid`, { uid }) - .getOne() + .getOne(); - if (isEmpty(user)) - throw new BusinessException(ErrorEnum.USER_NOT_FOUND) + if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND); - delete user?.psalt + delete user?.psalt; - return user + return user; } /** * 更新个人信息 */ async updateAccountInfo(uid: number, info: AccountUpdateDto): Promise { - const user = await this.userRepository.findOneBy({ id: uid }) - if (isEmpty(user)) - throw new BusinessException(ErrorEnum.USER_NOT_FOUND) + const user = await this.userRepository.findOneBy({ id: uid }); + if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND); const data = { ...(info.nickname ? { nickname: info.nickname } : null), @@ -97,73 +95,60 @@ export class UserService { ...(info.phone ? { phone: info.phone } : null), ...(info.qq ? { qq: info.qq } : null), ...(info.remark ? { remark: info.remark } : null), - } + }; if (!info.avatar && info.qq) { // 如果qq不等于原qq,则更新qq头像 - if (info.qq !== user.qq) - data.avatar = await this.qqService.getAvater(info.qq) + if (info.qq !== user.qq) data.avatar = await this.qqService.getAvater(info.qq); } - await this.userRepository.update(uid, data) + await this.userRepository.update(uid, data); } /** * 更改密码 */ async updatePassword(uid: number, dto: PasswordUpdateDto): Promise { - const user = await this.userRepository.findOneBy({ id: uid }) - if (isEmpty(user)) - throw new BusinessException(ErrorEnum.USER_NOT_FOUND) + const user = await this.userRepository.findOneBy({ id: uid }); + if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND); - const comparePassword = md5(`${dto.oldPassword}${user.psalt}`) + const comparePassword = md5(`${dto.oldPassword}${user.psalt}`); // 原密码不一致,不允许更改 - if (user.password !== comparePassword) - throw new BusinessException(ErrorEnum.PASSWORD_MISMATCH) + if (user.password !== comparePassword) throw new BusinessException(ErrorEnum.PASSWORD_MISMATCH); - const password = md5(`${dto.newPassword}${user.psalt}`) - await this.userRepository.update({ id: uid }, { password }) - await this.upgradePasswordV(user.id) + const password = md5(`${dto.newPassword}${user.psalt}`); + await this.userRepository.update({ id: uid }, { password }); + await this.upgradePasswordV(user.id); } /** * 直接更改密码 */ async forceUpdatePassword(uid: number, password: string): Promise { - const user = await this.userRepository.findOneBy({ id: uid }) + const user = await this.userRepository.findOneBy({ id: uid }); - const newPassword = md5(`${password}${user.psalt}`) - await this.userRepository.update({ id: uid }, { password: newPassword }) - await this.upgradePasswordV(user.id) + const newPassword = md5(`${password}${user.psalt}`); + await this.userRepository.update({ id: uid }, { password: newPassword }); + await this.upgradePasswordV(user.id); } /** * 增加系统用户,如果返回false则表示已存在该用户 */ - async create({ - username, - password, - roleIds, - deptId, - ...data - }: UserDto): Promise { + async create({ username, password, roleIds, deptId, ...data }: UserDto): Promise { const exists = await this.userRepository.findOneBy({ username, - }) - if (!isEmpty(exists)) - throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS) + }); + if (!isEmpty(exists)) throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS); - await this.entityManager.transaction(async (manager) => { - const salt = randomValue(32) + await this.entityManager.transaction(async manager => { + const salt = randomValue(32); if (!password) { - const initPassword = await this.paramConfigService.findValueByKey( - SYS_USER_INITPASSWORD, - ) - password = md5(`${initPassword ?? '123456'}${salt}`) - } - else { - password = md5(`${password ?? '123456'}${salt}`) + const initPassword = await this.paramConfigService.findValueByKey(SYS_USER_INITPASSWORD); + password = md5(`${initPassword ?? '123456'}${salt}`); + } else { + password = md5(`${password ?? '123456'}${salt}`); } const u = manager.create(UserEntity, { username, @@ -172,11 +157,11 @@ export class UserService { psalt: salt, roles: await this.roleRepository.findBy({ id: In(roleIds) }), dept: await DeptEntity.findOneBy({ id: deptId }), - }) + }); - const result = await manager.save(u) - return result - }) + const result = await manager.save(u); + return result; + }); } /** @@ -184,41 +169,36 @@ export class UserService { */ async update( id: number, - { password, deptId, roleIds, status, ...data }: UserUpdateDto, + { password, deptId, roleIds, status, ...data }: UserUpdateDto ): Promise { - await this.entityManager.transaction(async (manager) => { - if (password) - await this.forceUpdatePassword(id, password) + await this.entityManager.transaction(async manager => { + if (password) await this.forceUpdatePassword(id, password); await manager.update(UserEntity, id, { ...data, status, - }) + }); const user = await this.userRepository .createQueryBuilder('user') .leftJoinAndSelect('user.roles', 'roles') .leftJoinAndSelect('user.dept', 'dept') .where('user.id = :id', { id }) - .getOne() + .getOne(); await manager .createQueryBuilder() .relation(UserEntity, 'roles') .of(id) - .addAndRemove(roleIds, user.roles) + .addAndRemove(roleIds, user.roles); - await manager - .createQueryBuilder() - .relation(UserEntity, 'dept') - .of(id) - .set(deptId) + await manager.createQueryBuilder().relation(UserEntity, 'dept').of(id).set(deptId); if (status === 0) { // 禁用状态 - await this.forbidden(id) + await this.forbidden(id); } - }) + }); } /** @@ -231,23 +211,22 @@ export class UserService { .leftJoinAndSelect('user.roles', 'roles') .leftJoinAndSelect('user.dept', 'dept') .where('user.id = :id', { id }) - .getOne() + .getOne(); - delete user.password - delete user.psalt + delete user.password; + delete user.psalt; - return user + return user; } /** * 根据ID列表删除用户 */ async delete(userIds: number[]): Promise { - const rootUserId = await this.findRootUserId() - if (userIds.includes(rootUserId)) - throw new BadRequestException('不能删除root用户!') + const rootUserId = await this.findRootUserId(); + if (userIds.includes(rootUserId)) throw new BadRequestException('不能删除root用户!'); - await this.userRepository.delete(userIds) + await this.userRepository.delete(userIds); } /** @@ -256,8 +235,8 @@ export class UserService { async findRootUserId(): Promise { const user = await this.userRepository.findOneBy({ roles: { id: ROOT_ROLE_ID }, - }) - return user.id + }); + return user.id; } /** @@ -282,24 +261,23 @@ export class UserService { ...(nickname ? { nickname: Like(`%${nickname}%`) } : null), ...(email ? { email: Like(`%${email}%`) } : null), ...(isNumber(status) ? { status } : null), - }) + }); - if (deptId) - queryBuilder.andWhere('dept.id = :deptId', { deptId }) + if (deptId) queryBuilder.andWhere('dept.id = :deptId', { deptId }); return paginate(queryBuilder, { page, pageSize, - }) + }); } /** * 禁用用户 */ async forbidden(uid: number): Promise { - await this.redis.del(genAuthPVKey(uid)) - await this.redis.del(genAuthTokenKey(uid)) - await this.redis.del(genAuthPermKey(uid)) + await this.redis.del(genAuthPVKey(uid)); + await this.redis.del(genAuthTokenKey(uid)); + await this.redis.del(genAuthPermKey(uid)); } /** @@ -307,17 +285,17 @@ export class UserService { */ async multiForbidden(uids: number[]): Promise { if (uids) { - const pvs: string[] = [] - const ts: string[] = [] - const ps: string[] = [] - uids.forEach((uid) => { - pvs.push(genAuthPVKey(uid)) - ts.push(genAuthTokenKey(uid)) - ps.push(genAuthPermKey(uid)) - }) - await this.redis.del(pvs) - await this.redis.del(ts) - await this.redis.del(ps) + const pvs: string[] = []; + const ts: string[] = []; + const ps: string[] = []; + uids.forEach(uid => { + pvs.push(genAuthPVKey(uid)); + ts.push(genAuthTokenKey(uid)); + ps.push(genAuthPermKey(uid)); + }); + await this.redis.del(pvs); + await this.redis.del(ts); + await this.redis.del(ps); } } @@ -326,20 +304,18 @@ export class UserService { */ async upgradePasswordV(id: number): Promise { // admin:passwordVersion:${param.id} - const v = await this.redis.get(genAuthPVKey(id)) - if (!isEmpty(v)) - await this.redis.set(genAuthPVKey(id), Number.parseInt(v) + 1) + const v = await this.redis.get(genAuthPVKey(id)); + if (!isEmpty(v)) await this.redis.set(genAuthPVKey(id), Number.parseInt(v) + 1); } /** * 判断用户名是否存在 */ async exist(username: string) { - const user = await this.userRepository.findOneBy({ username }) - if (isNil(user)) - throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS) + const user = await this.userRepository.findOneBy({ username }); + if (isNil(user)) throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS); - return true + return true; } /** @@ -348,25 +324,24 @@ export class UserService { async register({ username, ...data }: RegisterDto): Promise { const exists = await this.userRepository.findOneBy({ username, - }) - if (!isEmpty(exists)) - throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS) + }); + if (!isEmpty(exists)) throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS); - await this.entityManager.transaction(async (manager) => { - const salt = randomValue(32) + await this.entityManager.transaction(async manager => { + const salt = randomValue(32); - const password = md5(`${data.password ?? 'a123456'}${salt}`) + const password = md5(`${data.password ?? 'a123456'}${salt}`); const u = manager.create(UserEntity, { username, password, status: 1, psalt: salt, - }) + }); - const user = await manager.save(u) + const user = await manager.save(u); - return user - }) + return user; + }); } } diff --git a/src/repl.ts b/src/repl.ts index 649d7ba..2a2a177 100644 --- a/src/repl.ts +++ b/src/repl.ts @@ -1,12 +1,11 @@ -import { repl } from '@nestjs/core' +import { repl } from '@nestjs/core'; -import { AppModule } from './app.module' +import { AppModule } from './app.module'; async function bootstrap() { - const replServer = await repl(AppModule) - replServer.setupHistory('.nestjs_repl_history', (err) => { - if (err) - console.error(err) - }) + const replServer = await repl(AppModule); + replServer.setupHistory('.nestjs_repl_history', err => { + if (err) console.error(err); + }); } -bootstrap() +bootstrap(); diff --git a/src/setup-swagger.ts b/src/setup-swagger.ts index 4858584..d80d52e 100644 --- a/src/setup-swagger.ts +++ b/src/setup-swagger.ts @@ -1,27 +1,26 @@ -import { INestApplication, Logger } from '@nestjs/common' -import { ConfigService } from '@nestjs/config' -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' +import { INestApplication, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { API_SECURITY_AUTH } from './common/decorators/swagger.decorator' -import { CommonEntity } from './common/entity/common.entity' -import { ResOp, TreeResult } from './common/model/response.model' -import { ConfigKeyPaths, IAppConfig, ISwaggerConfig } from './config' -import { Pagination } from './helper/paginate/pagination' +import { API_SECURITY_AUTH } from './common/decorators/swagger.decorator'; +import { CommonEntity } from './common/entity/common.entity'; +import { ResOp, TreeResult } from './common/model/response.model'; +import { ConfigKeyPaths, IAppConfig, ISwaggerConfig } from './config'; +import { Pagination } from './helper/paginate/pagination'; export function setupSwagger( app: INestApplication, - configService: ConfigService, + configService: ConfigService ): void { - const { name, port } = configService.get('app')! - const { enable, path } = configService.get('swagger')! + const { name, port } = configService.get('app')!; + const { enable, path } = configService.get('swagger')!; - if (!enable) - return + if (!enable) return; const documentBuilder = new DocumentBuilder() .setTitle(name) .setDescription(`${name} API document`) - .setVersion('1.0') + .setVersion('1.0'); // auth security documentBuilder.addSecurity(API_SECURITY_AUTH, { @@ -29,16 +28,16 @@ export function setupSwagger( type: 'apiKey', in: 'header', name: 'Authorization', - }) + }); const document = SwaggerModule.createDocument(app, documentBuilder.build(), { ignoreGlobalPrefix: false, extraModels: [CommonEntity, ResOp, Pagination, TreeResult], - }) + }); - SwaggerModule.setup(path, app, document) + SwaggerModule.setup(path, app, document); // started log - const logger = new Logger('SwaggerModule') - logger.log(`Document running on http://127.0.0.1:${port}/${path}`) + const logger = new Logger('SwaggerModule'); + logger.log(`Document running on http://127.0.0.1:${port}/${path}`); } diff --git a/src/shared/database/constraints/entity-exist.constraint.ts b/src/shared/database/constraints/entity-exist.constraint.ts index 2ce89cd..3f482b7 100644 --- a/src/shared/database/constraints/entity-exist.constraint.ts +++ b/src/shared/database/constraints/entity-exist.constraint.ts @@ -1,17 +1,17 @@ -import { Injectable } from '@nestjs/common' +import { Injectable } from '@nestjs/common'; import { ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, registerDecorator, -} from 'class-validator' -import { DataSource, ObjectType, Repository } from 'typeorm' +} from 'class-validator'; +import { DataSource, ObjectType, Repository } from 'typeorm'; interface Condition { - entity: ObjectType + entity: ObjectType; // 如果没有指定字段则使用当前验证的属性作为查询依据 - field?: string + field?: string; } /** @@ -23,32 +23,29 @@ export class EntityExistConstraint implements ValidatorConstraintInterface { constructor(private dataSource: DataSource) {} async validate(value: string, args: ValidationArguments) { - let repo: Repository + let repo: Repository; - if (!value) - return true + if (!value) return true; // 默认对比字段是id - let field = 'id' + let field = 'id'; // 通过传入的 entity 获取其 repository if ('entity' in args.constraints[0]) { // 传入的是对象 可以指定对比字段 - field = args.constraints[0].field ?? 'id' - repo = this.dataSource.getRepository(args.constraints[0].entity) - } - else { + field = args.constraints[0].field ?? 'id'; + repo = this.dataSource.getRepository(args.constraints[0].entity); + } else { // 传入的是实体类 - repo = this.dataSource.getRepository(args.constraints[0]) + repo = this.dataSource.getRepository(args.constraints[0]); } // 通过查询记录是否存在进行验证 - const item = await repo.findOne({ where: { [field]: value } }) - return !!item + const item = await repo.findOne({ where: { [field]: value } }); + return !!item; } defaultMessage(args: ValidationArguments) { - if (!args.constraints[0]) - return 'Model not been specified!' + if (!args.constraints[0]) return 'Model not been specified!'; - return `All instance of ${args.constraints[0].name} must been exists in databse!` + return `All instance of ${args.constraints[0].name} must been exists in databse!`; } } @@ -59,17 +56,17 @@ export class EntityExistConstraint implements ValidatorConstraintInterface { */ function IsEntityExist( entity: ObjectType, - validationOptions?: ValidationOptions, -): (object: Record, propertyName: string) => void + validationOptions?: ValidationOptions +): (object: Record, propertyName: string) => void; function IsEntityExist( - condition: { entity: ObjectType, field?: string }, - validationOptions?: ValidationOptions, -): (object: Record, propertyName: string) => void + condition: { entity: ObjectType; field?: string }, + validationOptions?: ValidationOptions +): (object: Record, propertyName: string) => void; function IsEntityExist( - condition: ObjectType | { entity: ObjectType, field?: string }, - validationOptions?: ValidationOptions, + condition: ObjectType | { entity: ObjectType; field?: string }, + validationOptions?: ValidationOptions ): (object: Record, propertyName: string) => void { return (object: Record, propertyName: string) => { registerDecorator({ @@ -78,8 +75,8 @@ function IsEntityExist( options: validationOptions, constraints: [condition], validator: EntityExistConstraint, - }) - } + }); + }; } -export { IsEntityExist } +export { IsEntityExist }; diff --git a/src/shared/database/constraints/unique.constraint.ts b/src/shared/database/constraints/unique.constraint.ts index 65fa562..2f3450b 100644 --- a/src/shared/database/constraints/unique.constraint.ts +++ b/src/shared/database/constraints/unique.constraint.ts @@ -1,18 +1,18 @@ -import { Injectable } from '@nestjs/common' +import { Injectable } from '@nestjs/common'; import { ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, registerDecorator, -} from 'class-validator' -import { isNil, merge } from 'lodash' -import { DataSource, ObjectType } from 'typeorm' +} from 'class-validator'; +import { isNil, merge } from 'lodash'; +import { DataSource, ObjectType } from 'typeorm'; interface Condition { - entity: ObjectType + entity: ObjectType; // 如果没有指定字段则使用当前验证的属性作为查询依据 - field?: string + field?: string; } /** @@ -27,40 +27,36 @@ export class UniqueConstraint implements ValidatorConstraintInterface { // 获取要验证的模型和字段 const config: Omit = { field: args.property, - } + }; const condition = ('entity' in args.constraints[0] ? merge(config, args.constraints[0]) : { ...config, entity: args.constraints[0], - }) as unknown as Required - if (!condition.entity) - return false + }) as unknown as Required; + if (!condition.entity) return false; try { // 查询是否存在数据,如果已经存在则验证失败 - const repo = this.dataSource.getRepository(condition.entity) + const repo = this.dataSource.getRepository(condition.entity); return isNil( await repo.findOne({ where: { [condition.field]: value }, - }), - ) - } - catch (err) { + }) + ); + } catch (err) { // 如果数据库操作异常则验证失败 - return false + return false; } } defaultMessage(args: ValidationArguments) { - const { entity, property } = args.constraints[0] - const queryProperty = property ?? args.property - if (!(args.object as any).getManager) - return 'getManager function not been found!' + const { entity, property } = args.constraints[0]; + const queryProperty = property ?? args.property; + if (!(args.object as any).getManager) return 'getManager function not been found!'; - if (!entity) - return 'Model not been specified!' + if (!entity) return 'Model not been specified!'; - return `${queryProperty} of ${entity.name} must been unique!` + return `${queryProperty} of ${entity.name} must been unique!`; } } @@ -71,18 +67,15 @@ export class UniqueConstraint implements ValidatorConstraintInterface { */ function IsUnique( entity: ObjectType, - validationOptions?: ValidationOptions, -): (object: Record, propertyName: string) => void + validationOptions?: ValidationOptions +): (object: Record, propertyName: string) => void; function IsUnique( condition: Condition, - validationOptions?: ValidationOptions, -): (object: Record, propertyName: string) => void + validationOptions?: ValidationOptions +): (object: Record, propertyName: string) => void; -function IsUnique( - params: ObjectType | Condition, - validationOptions?: ValidationOptions, -) { +function IsUnique(params: ObjectType | Condition, validationOptions?: ValidationOptions) { return (object: Record, propertyName: string) => { registerDecorator({ target: object.constructor, @@ -90,8 +83,8 @@ function IsUnique( options: validationOptions, constraints: [params], validator: UniqueConstraint, - }) - } + }); + }; } -export { IsUnique } +export { IsUnique }; diff --git a/src/shared/database/database.module.ts b/src/shared/database/database.module.ts index acb5526..528df0f 100644 --- a/src/shared/database/database.module.ts +++ b/src/shared/database/database.module.ts @@ -1,32 +1,31 @@ -import { Module } from '@nestjs/common' +import { Module } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config' -import { TypeOrmModule } from '@nestjs/typeorm' +import { ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; -import { DataSource, LoggerOptions } from 'typeorm' +import { DataSource, LoggerOptions } from 'typeorm'; -import { ConfigKeyPaths, IDatabaseConfig } from '~/config' +import { ConfigKeyPaths, IDatabaseConfig } from '~/config'; -import { env } from '~/global/env' +import { env } from '~/global/env'; -import { EntityExistConstraint } from './constraints/entity-exist.constraint' -import { UniqueConstraint } from './constraints/unique.constraint' -import { TypeORMLogger } from './typeorm-logger' +import { EntityExistConstraint } from './constraints/entity-exist.constraint'; +import { UniqueConstraint } from './constraints/unique.constraint'; +import { TypeORMLogger } from './typeorm-logger'; -const providers = [EntityExistConstraint, UniqueConstraint] +const providers = [EntityExistConstraint, UniqueConstraint]; @Module({ imports: [ TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (configService: ConfigService) => { - let loggerOptions: LoggerOptions = env('DB_LOGGING') as 'all' + let loggerOptions: LoggerOptions = env('DB_LOGGING') as 'all'; try { // 解析成 js 数组 ['error'] - loggerOptions = JSON.parse(loggerOptions) - } - catch { + loggerOptions = JSON.parse(loggerOptions); + } catch { // ignore } @@ -35,13 +34,13 @@ const providers = [EntityExistConstraint, UniqueConstraint] autoLoadEntities: true, logging: loggerOptions, logger: new TypeORMLogger(loggerOptions), - } + }; }, // dataSource receives the configured DataSourceOptions // and returns a Promise. - dataSourceFactory: async (options) => { - const dataSource = await new DataSource(options).initialize() - return dataSource + dataSourceFactory: async options => { + const dataSource = await new DataSource(options).initialize(); + return dataSource; }, }), ], diff --git a/src/shared/database/typeorm-logger.ts b/src/shared/database/typeorm-logger.ts index 434932b..8f425f7 100644 --- a/src/shared/database/typeorm-logger.ts +++ b/src/shared/database/typeorm-logger.ts @@ -1,91 +1,77 @@ -import { Logger } from '@nestjs/common' -import { Logger as ITypeORMLogger, LoggerOptions, QueryRunner } from 'typeorm' +import { Logger } from '@nestjs/common'; +import { Logger as ITypeORMLogger, LoggerOptions, QueryRunner } from 'typeorm'; export class TypeORMLogger implements ITypeORMLogger { - private logger = new Logger(TypeORMLogger.name) - + private logger = new Logger(TypeORMLogger.name); + constructor(private options: LoggerOptions) {} logQuery(query: string, parameters?: any[], _queryRunner?: QueryRunner) { - if (!this.isEnable('query')) - return + if (!this.isEnable('query')) return; - const sql - = query - + (parameters && parameters.length + const sql = + query + + (parameters && parameters.length ? ` -- PARAMETERS: ${this.stringifyParams(parameters)}` - : '') + : ''); - this.logger.log(`[QUERY]: ${sql}`) + this.logger.log(`[QUERY]: ${sql}`); } logQueryError( error: string | Error, query: string, parameters?: any[], - _queryRunner?: QueryRunner, + _queryRunner?: QueryRunner ) { - if (!this.isEnable('error')) - return + if (!this.isEnable('error')) return; - const sql - = query - + (parameters && parameters.length + const sql = + query + + (parameters && parameters.length ? ` -- PARAMETERS: ${this.stringifyParams(parameters)}` - : '') + : ''); - this.logger.error([`[FAILED QUERY]: ${sql}`, `[QUERY ERROR]: ${error}`]) + this.logger.error([`[FAILED QUERY]: ${sql}`, `[QUERY ERROR]: ${error}`]); } - logQuerySlow( - time: number, - query: string, - parameters?: any[], - _queryRunner?: QueryRunner, - ) { - const sql - = query - + (parameters && parameters.length + logQuerySlow(time: number, query: string, parameters?: any[], _queryRunner?: QueryRunner) { + const sql = + query + + (parameters && parameters.length ? ` -- PARAMETERS: ${this.stringifyParams(parameters)}` - : '') + : ''); - this.logger.warn(`[SLOW QUERY: ${time} ms]: ${sql}`) + this.logger.warn(`[SLOW QUERY: ${time} ms]: ${sql}`); } logSchemaBuild(message: string, _queryRunner?: QueryRunner) { - if (!this.isEnable('schema')) - return + if (!this.isEnable('schema')) return; - this.logger.log(message) + this.logger.log(message); } logMigration(message: string, _queryRunner?: QueryRunner) { - if (!this.isEnable('migration')) - return + if (!this.isEnable('migration')) return; - this.logger.log(message) + this.logger.log(message); } - log( - level: 'warn' | 'info' | 'log', - message: any, - _queryRunner?: QueryRunner, - ) { - if (!this.isEnable(level)) - return + log(level: 'warn' | 'info' | 'log', message: any, _queryRunner?: QueryRunner) { + if (!this.isEnable(level)) return; switch (level) { case 'log': - this.logger.debug(message) - break + this.logger.debug(message); + break; case 'info': - this.logger.log(message) - break + this.logger.log(message); + break; case 'warn': - this.logger.warn(message) - break + this.logger.warn(message); + break; default: - break + break; } } @@ -95,11 +81,10 @@ export class TypeORMLogger implements ITypeORMLogger { */ private stringifyParams(parameters: any[]) { try { - return JSON.stringify(parameters) - } - catch (error) { + return JSON.stringify(parameters); + } catch (error) { // most probably circular objects in parameters - return parameters + return parameters; } } @@ -107,12 +92,12 @@ export class TypeORMLogger implements ITypeORMLogger { * check enbale log */ private isEnable( - level: 'query' | 'schema' | 'error' | 'warn' | 'info' | 'log' | 'migration', + level: 'query' | 'schema' | 'error' | 'warn' | 'info' | 'log' | 'migration' ): boolean { return ( - this.options === 'all' - || this.options === true - || (Array.isArray(this.options) && this.options.includes(level)) - ) + this.options === 'all' || + this.options === true || + (Array.isArray(this.options) && this.options.includes(level)) + ); } } diff --git a/src/shared/helper/cron.service.ts b/src/shared/helper/cron.service.ts index 94d031b..09fb895 100644 --- a/src/shared/helper/cron.service.ts +++ b/src/shared/helper/cron.service.ts @@ -1,48 +1,44 @@ -import { Injectable, Logger } from '@nestjs/common' -import { ConfigService } from '@nestjs/config' -import { CronExpression } from '@nestjs/schedule' -import dayjs from 'dayjs' +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { CronExpression } from '@nestjs/schedule'; +import dayjs from 'dayjs'; -import { LessThan } from 'typeorm' +import { LessThan } from 'typeorm'; -import { CronOnce } from '~/common/decorators/cron-once.decorator' -import { ConfigKeyPaths } from '~/config' -import { AccessTokenEntity } from '~/modules/auth/entities/access-token.entity' +import { CronOnce } from '~/common/decorators/cron-once.decorator'; +import { ConfigKeyPaths } from '~/config'; +import { AccessTokenEntity } from '~/modules/auth/entities/access-token.entity'; @Injectable() export class CronService { - private logger: Logger = new Logger(CronService.name) - constructor( - private readonly configService: ConfigService, - ) {} + private logger: Logger = new Logger(CronService.name); + constructor(private readonly configService: ConfigService) {} @CronOnce(CronExpression.EVERY_DAY_AT_MIDNIGHT) async deleteExpiredJWT() { - this.logger.log('--> 开始扫表,清除过期的 token') + this.logger.log('--> 开始扫表,清除过期的 token'); const expiredTokens = await AccessTokenEntity.find({ where: { expired_at: LessThan(new Date()), }, - }) + }); - let deleteCount = 0 + let deleteCount = 0; await Promise.all( - expiredTokens.map(async (token) => { - const { value, created_at } = token + expiredTokens.map(async token => { + const { value, created_at } = token; - await AccessTokenEntity.remove(token) + await AccessTokenEntity.remove(token); this.logger.debug( - `--> 删除过期的 token:${value}, 签发于 ${dayjs(created_at).format( - 'YYYY-MM-DD H:mm:ss', - )}`, - ) + `--> 删除过期的 token:${value}, 签发于 ${dayjs(created_at).format('YYYY-MM-DD H:mm:ss')}` + ); - deleteCount += 1 - }), - ) + deleteCount += 1; + }) + ); - this.logger.log(`--> 删除了 ${deleteCount} 个过期的 token`) + this.logger.log(`--> 删除了 ${deleteCount} 个过期的 token`); } } diff --git a/src/shared/helper/helper.module.ts b/src/shared/helper/helper.module.ts index 1261c7b..dc8e166 100644 --- a/src/shared/helper/helper.module.ts +++ b/src/shared/helper/helper.module.ts @@ -1,12 +1,9 @@ -import { Global, Module, type Provider } from '@nestjs/common' +import { Global, Module, type Provider } from '@nestjs/common'; -import { CronService } from './cron.service' -import { QQService } from './qq.service' +import { CronService } from './cron.service'; +import { QQService } from './qq.service'; -const providers: Provider[] = [ - CronService, - QQService, -] +const providers: Provider[] = [CronService, QQService]; @Global() @Module({ diff --git a/src/shared/helper/qq.service.ts b/src/shared/helper/qq.service.ts index 9c5f788..568cef4 100644 --- a/src/shared/helper/qq.service.ts +++ b/src/shared/helper/qq.service.ts @@ -1,5 +1,5 @@ -import { HttpService } from '@nestjs/axios' -import { Injectable } from '@nestjs/common' +import { HttpService } from '@nestjs/axios'; +import { Injectable } from '@nestjs/common'; @Injectable() export class QQService { @@ -7,13 +7,13 @@ export class QQService { async getNickname(qq: string | number) { const { data } = await this.http.axiosRef.get( - `https://users.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins=${qq}`, - ) - return data + `https://users.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins=${qq}` + ); + return data; } async getAvater(qq: string | number) { // https://thirdqq.qlogo.cn/headimg_dl?dst_uin=1743369777&spec=640&img_type=jpg - return `https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=${qq}` + return `https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=${qq}`; } } diff --git a/src/shared/logger/logger.module.ts b/src/shared/logger/logger.module.ts index 3757496..0cd7a70 100644 --- a/src/shared/logger/logger.module.ts +++ b/src/shared/logger/logger.module.ts @@ -1,6 +1,6 @@ -import { Module } from '@nestjs/common' +import { Module } from '@nestjs/common'; -import { LoggerService } from './logger.service' +import { LoggerService } from './logger.service'; @Module({}) export class LoggerModule { @@ -10,6 +10,6 @@ export class LoggerModule { module: LoggerModule, providers: [LoggerService], exports: [LoggerService], - } + }; } } diff --git a/src/shared/logger/logger.service.ts b/src/shared/logger/logger.service.ts index f2856e1..ce477cd 100644 --- a/src/shared/logger/logger.service.ts +++ b/src/shared/logger/logger.service.ts @@ -1,13 +1,13 @@ -import { ConsoleLogger, ConsoleLoggerOptions, Injectable } from '@nestjs/common' +import { ConsoleLogger, ConsoleLoggerOptions, Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config' -import type { Logger as WinstonLogger } from 'winston' +import { ConfigService } from '@nestjs/config'; +import type { Logger as WinstonLogger } from 'winston'; -import { config, createLogger, format, transports } from 'winston' +import { config, createLogger, format, transports } from 'winston'; -import 'winston-daily-rotate-file' +import 'winston-daily-rotate-file'; -import { ConfigKeyPaths } from '~/config' +import { ConfigKeyPaths } from '~/config'; export enum LogLevel { ERROR = 'error', @@ -19,33 +19,29 @@ export enum LogLevel { @Injectable() export class LoggerService extends ConsoleLogger { - private winstonLogger: WinstonLogger + private winstonLogger: WinstonLogger; constructor( context: string, options: ConsoleLoggerOptions, - private configService: ConfigService, + private configService: ConfigService ) { - super(context, options) - this.initWinston() + super(context, options); + this.initWinston(); } protected get level(): LogLevel { - return this.configService.get('app.logger.level', { infer: true }) as LogLevel + return this.configService.get('app.logger.level', { infer: true }) as LogLevel; } protected get maxFiles(): number { - return this.configService.get('app.logger.maxFiles', { infer: true }) + return this.configService.get('app.logger.maxFiles', { infer: true }); } protected initWinston(): void { this.winstonLogger = createLogger({ levels: config.npm.levels, - format: format.combine( - format.errors({ stack: true }), - format.timestamp(), - format.json(), - ), + format: format.combine(format.errors({ stack: true }), format.timestamp(), format.json()), transports: [ new transports.DailyRotateFile({ level: this.level, @@ -64,7 +60,7 @@ export class LoggerService extends ConsoleLogger { auditFile: 'logs/.audit/app-error.json', }), ], - }) + }); // if (isDev) { // this.winstonLogger.add( @@ -80,36 +76,36 @@ export class LoggerService extends ConsoleLogger { } verbose(message: any, context?: string): void { - super.verbose.apply(this, [message, context]) + super.verbose.apply(this, [message, context]); - this.winstonLogger.log(LogLevel.VERBOSE, message, { context }) + this.winstonLogger.log(LogLevel.VERBOSE, message, { context }); } debug(message: any, context?: string): void { - super.debug.apply(this, [message, context]) + super.debug.apply(this, [message, context]); - this.winstonLogger.log(LogLevel.DEBUG, message, { context }) + this.winstonLogger.log(LogLevel.DEBUG, message, { context }); } log(message: any, context?: string): void { - super.log.apply(this, [message, context]) + super.log.apply(this, [message, context]); - this.winstonLogger.log(LogLevel.INFO, message, { context }) + this.winstonLogger.log(LogLevel.INFO, message, { context }); } warn(message: any, context?: string): void { - super.warn.apply(this, [message, context]) + super.warn.apply(this, [message, context]); - this.winstonLogger.log(LogLevel.WARN, message) + this.winstonLogger.log(LogLevel.WARN, message); } error(message: any, stack?: string, context?: string): void { - super.error.apply(this, [message, stack, context]) + super.error.apply(this, [message, stack, context]); - const hasStack = !!context + const hasStack = !!context; this.winstonLogger.log(LogLevel.ERROR, { context: hasStack ? context : stack, message: hasStack ? new Error(message) : message, - }) + }); } } diff --git a/src/shared/mailer/mailer.module.ts b/src/shared/mailer/mailer.module.ts index 9ee4128..24d1e1f 100644 --- a/src/shared/mailer/mailer.module.ts +++ b/src/shared/mailer/mailer.module.ts @@ -1,17 +1,15 @@ -import { join } from 'node:path' +import { join } from 'node:path'; -import { Module, Provider } from '@nestjs/common' -import { ConfigModule, ConfigService } from '@nestjs/config' -import { MailerModule as NestMailerModule } from '@nestjs-modules/mailer' -import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter' +import { Module, Provider } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { MailerModule as NestMailerModule } from '@nestjs-modules/mailer'; +import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; -import { ConfigKeyPaths, IAppConfig, IMailerConfig } from '~/config' +import { ConfigKeyPaths, IAppConfig, IMailerConfig } from '~/config'; -import { MailerService } from './mailer.service' +import { MailerService } from './mailer.service'; -const providers: Provider[] = [ - MailerService, -] +const providers: Provider[] = [MailerService]; @Module({ imports: [ diff --git a/src/shared/mailer/mailer.service.ts b/src/shared/mailer/mailer.service.ts index 16aba4a..2b5fcf3 100644 --- a/src/shared/mailer/mailer.service.ts +++ b/src/shared/mailer/mailer.service.ts @@ -1,119 +1,97 @@ -import { InjectRedis } from '@liaoliaots/nestjs-redis' -import { Inject, Injectable } from '@nestjs/common' +import { InjectRedis } from '@liaoliaots/nestjs-redis'; +import { Inject, Injectable } from '@nestjs/common'; -import { MailerService as NestMailerService } from '@nestjs-modules/mailer' -import dayjs from 'dayjs' +import { MailerService as NestMailerService } from '@nestjs-modules/mailer'; +import dayjs from 'dayjs'; -import Redis from 'ioredis' +import Redis from 'ioredis'; -import { BusinessException } from '~/common/exceptions/biz.exception' -import { AppConfig, IAppConfig } from '~/config' -import { ErrorEnum } from '~/constants/error-code.constant' -import { randomValue } from '~/utils' +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { AppConfig, IAppConfig } from '~/config'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { randomValue } from '~/utils'; @Injectable() export class MailerService { constructor( @Inject(AppConfig.KEY) private appConfig: IAppConfig, @InjectRedis() private redis: Redis, - private mailerService: NestMailerService, + private mailerService: NestMailerService ) {} async log(to: string, code: string, ip: string) { const getRemainTime = () => { - const now = dayjs() - return now.endOf('day').diff(now, 'second') - } + const now = dayjs(); + return now.endOf('day').diff(now, 'second'); + }; - await this.redis.set(`captcha:${to}`, code, 'EX', 60 * 5) + await this.redis.set(`captcha:${to}`, code, 'EX', 60 * 5); - const limitCountOfDay = await this.redis.get(`captcha:${to}:limit-day`) - const ipLimitCountOfDay = await this.redis.get(`ip:${ip}:send:limit-day`) + const limitCountOfDay = await this.redis.get(`captcha:${to}:limit-day`); + const ipLimitCountOfDay = await this.redis.get(`ip:${ip}:send:limit-day`); - await this.redis.set(`ip:${ip}:send:limit`, 1, 'EX', 60) - await this.redis.set(`captcha:${to}:limit`, 1, 'EX', 60) + await this.redis.set(`ip:${ip}:send:limit`, 1, 'EX', 60); + await this.redis.set(`captcha:${to}:limit`, 1, 'EX', 60); await this.redis.set( `captcha:${to}:send:limit-count-day`, limitCountOfDay, 'EX', - getRemainTime(), - ) - await this.redis.set( - `ip:${ip}:send:limit-count-day`, - ipLimitCountOfDay, - 'EX', - getRemainTime(), - ) + getRemainTime() + ); + await this.redis.set(`ip:${ip}:send:limit-count-day`, ipLimitCountOfDay, 'EX', getRemainTime()); } async checkCode(to, code) { - const ret = await this.redis.get(`captcha:${to}`) - if (ret !== code) - throw new BusinessException(ErrorEnum.INVALID_VERIFICATION_CODE) + const ret = await this.redis.get(`captcha:${to}`); + if (ret !== code) throw new BusinessException(ErrorEnum.INVALID_VERIFICATION_CODE); - await this.redis.del(`captcha:${to}`) + await this.redis.del(`captcha:${to}`); } async checkLimit(to, ip) { - const LIMIT_TIME = 5 + const LIMIT_TIME = 5; // ip限制 - const ipLimit = await this.redis.get(`ip:${ip}:send:limit`) - if (ipLimit) - throw new BusinessException(ErrorEnum.TOO_MANY_REQUESTS) + const ipLimit = await this.redis.get(`ip:${ip}:send:limit`); + if (ipLimit) throw new BusinessException(ErrorEnum.TOO_MANY_REQUESTS); // 1分钟最多接收1条 - const limit = await this.redis.get(`captcha:${to}:limit`) - if (limit) - throw new BusinessException(ErrorEnum.TOO_MANY_REQUESTS) + const limit = await this.redis.get(`captcha:${to}:limit`); + if (limit) throw new BusinessException(ErrorEnum.TOO_MANY_REQUESTS); // 1天一个邮箱最多接收5条 - let limitCountOfDay: string | number = await this.redis.get( - `captcha:${to}:limit-day`, - ) - limitCountOfDay = limitCountOfDay ? Number(limitCountOfDay) : 0 + let limitCountOfDay: string | number = await this.redis.get(`captcha:${to}:limit-day`); + limitCountOfDay = limitCountOfDay ? Number(limitCountOfDay) : 0; if (limitCountOfDay > LIMIT_TIME) { - throw new BusinessException( - ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY, - ) + throw new BusinessException(ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY); } // 1天一个ip最多发送5条 - let ipLimitCountOfDay: string | number = await this.redis.get( - `ip:${ip}:send:limit-day`, - ) - ipLimitCountOfDay = ipLimitCountOfDay ? Number(ipLimitCountOfDay) : 0 + let ipLimitCountOfDay: string | number = await this.redis.get(`ip:${ip}:send:limit-day`); + ipLimitCountOfDay = ipLimitCountOfDay ? Number(ipLimitCountOfDay) : 0; if (ipLimitCountOfDay > LIMIT_TIME) { - throw new BusinessException( - ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY, - ) + throw new BusinessException(ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY); } } - async send( - to, - subject, - content: string, - type: 'text' | 'html' = 'text', - ): Promise { + async send(to, subject, content: string, type: 'text' | 'html' = 'text'): Promise { if (type === 'text') { return this.mailerService.sendMail({ to, subject, text: content, - }) - } - else { + }); + } else { return this.mailerService.sendMail({ to, subject, html: content, - }) + }); } } async sendVerificationCode(to, code = randomValue(4, '1234567890')) { - const subject = `[${this.appConfig.name}] 验证码` + const subject = `[${this.appConfig.name}] 验证码`; try { await this.mailerService.sendMail({ @@ -123,17 +101,16 @@ export class MailerService { context: { code, }, - }) - } - catch (error) { - console.log(error) - throw new BusinessException(ErrorEnum.VERIFICATION_CODE_SEND_FAILED) + }); + } catch (error) { + console.log(error); + throw new BusinessException(ErrorEnum.VERIFICATION_CODE_SEND_FAILED); } return { to, code, - } + }; } // async sendUserConfirmation(user: UserEntity, token: string) { diff --git a/src/shared/redis/cache.service.ts b/src/shared/redis/cache.service.ts index 90c8707..6edd80e 100644 --- a/src/shared/redis/cache.service.ts +++ b/src/shared/redis/cache.service.ts @@ -1,68 +1,67 @@ -import { CACHE_MANAGER } from '@nestjs/cache-manager' -import { Inject, Injectable } from '@nestjs/common' -import { Emitter } from '@socket.io/redis-emitter' -import { Cache } from 'cache-manager' -import type { Redis } from 'ioredis' +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Inject, Injectable } from '@nestjs/common'; +import { Emitter } from '@socket.io/redis-emitter'; +import { Cache } from 'cache-manager'; +import type { Redis } from 'ioredis'; -import { RedisIoAdapterKey } from '~/common/adapters/socket.adapter' +import { RedisIoAdapterKey } from '~/common/adapters/socket.adapter'; -import { API_CACHE_PREFIX } from '~/constants/cache.constant' -import { getRedisKey } from '~/utils/redis.util' +import { API_CACHE_PREFIX } from '~/constants/cache.constant'; +import { getRedisKey } from '~/utils/redis.util'; // 获取器 -export type TCacheKey = string -export type TCacheResult = Promise +export type TCacheKey = string; +export type TCacheResult = Promise; @Injectable() export class CacheService { - private cache!: Cache + private cache!: Cache; - private ioRedis!: Redis + private ioRedis!: Redis; constructor(@Inject(CACHE_MANAGER) cache: Cache) { - this.cache = cache + this.cache = cache; } private get redisClient(): Redis { // eslint-disable-next-line ts/ban-ts-comment // @ts-expect-error - return this.cache.store.client + return this.cache.store.client; } public get(key: TCacheKey): TCacheResult { - return this.cache.get(key) + return this.cache.get(key); } public set(key: TCacheKey, value: any, milliseconds: number) { - return this.cache.set(key, value, milliseconds) + return this.cache.set(key, value, milliseconds); } public getClient() { - return this.redisClient + return this.redisClient; } - private _emitter: Emitter + private _emitter: Emitter; public get emitter(): Emitter { - if (this._emitter) - return this._emitter + if (this._emitter) return this._emitter; this._emitter = new Emitter(this.redisClient, { key: RedisIoAdapterKey, - }) + }); - return this._emitter + return this._emitter; } public async cleanCatch() { - const redis = this.getClient() - const keys: string[] = await redis.keys(`${API_CACHE_PREFIX}*`) - await Promise.all(keys.map(key => redis.del(key))) + const redis = this.getClient(); + const keys: string[] = await redis.keys(`${API_CACHE_PREFIX}*`); + await Promise.all(keys.map(key => redis.del(key))); } public async cleanAllRedisKey() { - const redis = this.getClient() - const keys: string[] = await redis.keys(getRedisKey('*')) + const redis = this.getClient(); + const keys: string[] = await redis.keys(getRedisKey('*')); - await Promise.all(keys.map(key => redis.del(key))) + await Promise.all(keys.map(key => redis.del(key))); } } diff --git a/src/shared/redis/redis-subpub.ts b/src/shared/redis/redis-subpub.ts index 7634753..4f4934c 100644 --- a/src/shared/redis/redis-subpub.ts +++ b/src/shared/redis/redis-subpub.ts @@ -1,68 +1,65 @@ -import { Logger } from '@nestjs/common' -import IORedis from 'ioredis' -import type { Redis, RedisOptions } from 'ioredis' +import { Logger } from '@nestjs/common'; +import IORedis from 'ioredis'; +import type { Redis, RedisOptions } from 'ioredis'; export class RedisSubPub { - public pubClient: Redis - public subClient: Redis + public pubClient: Redis; + public subClient: Redis; constructor( private redisConfig: RedisOptions, - private channelPrefix: string = 'm-shop-channel#', + private channelPrefix: string = 'm-shop-channel#' ) { - this.init() + this.init(); } public init() { const redisOptions: RedisOptions = { host: this.redisConfig.host, port: this.redisConfig.port, - } + }; - if (this.redisConfig.password) - redisOptions.password = this.redisConfig.password + if (this.redisConfig.password) redisOptions.password = this.redisConfig.password; - const pubClient = new IORedis(redisOptions) - const subClient = pubClient.duplicate() - this.pubClient = pubClient - this.subClient = subClient + const pubClient = new IORedis(redisOptions); + const subClient = pubClient.duplicate(); + this.pubClient = pubClient; + this.subClient = subClient; } public async publish(event: string, data: any) { - const channel = this.channelPrefix + event - const _data = JSON.stringify(data) - if (event !== 'log') - Logger.debug(`发布事件:${channel} <- ${_data}`, RedisSubPub.name) + const channel = this.channelPrefix + event; + const _data = JSON.stringify(data); + if (event !== 'log') Logger.debug(`发布事件:${channel} <- ${_data}`, RedisSubPub.name); - await this.pubClient.publish(channel, _data) + await this.pubClient.publish(channel, _data); } - private ctc = new WeakMap void>() + private ctc = new WeakMap void>(); public async subscribe(event: string, callback: (data: any) => void) { - const myChannel = this.channelPrefix + event - this.subClient.subscribe(myChannel) + const myChannel = this.channelPrefix + event; + this.subClient.subscribe(myChannel); const cb = (channel, message) => { if (channel === myChannel) { - if (event !== 'log') - Logger.debug(`接收事件:${channel} -> ${message}`, RedisSubPub.name) + if (event !== 'log') Logger.debug(`接收事件:${channel} -> ${message}`, RedisSubPub.name); - callback(JSON.parse(message)) + callback(JSON.parse(message)); } - } + }; - this.ctc.set(callback, cb) - this.subClient.on('message', cb) + this.ctc.set(callback, cb); + this.subClient.on('message', cb); } public async unsubscribe(event: string, callback: (data: any) => void) { - const channel = this.channelPrefix + event - this.subClient.unsubscribe(channel) - const cb = this.ctc.get(callback) + const channel = this.channelPrefix + event; + this.subClient.unsubscribe(channel); + const cb = this.ctc.get(callback); if (cb) { - this.subClient.off('message', cb) + this.subClient.off('message', cb); - this.ctc.delete(callback) + this.ctc.delete(callback); } } } diff --git a/src/shared/redis/redis.constant.ts b/src/shared/redis/redis.constant.ts index 9b82a3b..1111312 100644 --- a/src/shared/redis/redis.constant.ts +++ b/src/shared/redis/redis.constant.ts @@ -1 +1 @@ -export const REDIS_PUBSUB = Symbol('REDIS_PUBSUB') +export const REDIS_PUBSUB = Symbol('REDIS_PUBSUB'); diff --git a/src/shared/redis/redis.module.ts b/src/shared/redis/redis.module.ts index 5c19851..b227914 100644 --- a/src/shared/redis/redis.module.ts +++ b/src/shared/redis/redis.module.ts @@ -1,30 +1,30 @@ -import { RedisModule as NestRedisModule } from '@liaoliaots/nestjs-redis' -import { CacheModule } from '@nestjs/cache-manager' -import { Global, Module, Provider } from '@nestjs/common' -import { ConfigModule, ConfigService } from '@nestjs/config' +import { RedisModule as NestRedisModule } from '@liaoliaots/nestjs-redis'; +import { CacheModule } from '@nestjs/cache-manager'; +import { Global, Module, Provider } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; -import { redisStore } from 'cache-manager-ioredis-yet' -import { RedisOptions } from 'ioredis' +import { redisStore } from 'cache-manager-ioredis-yet'; +import { RedisOptions } from 'ioredis'; -import { ConfigKeyPaths, IRedisConfig } from '~/config' +import { ConfigKeyPaths, IRedisConfig } from '~/config'; -import { CacheService } from './cache.service' -import { RedisSubPub } from './redis-subpub' -import { REDIS_PUBSUB } from './redis.constant' -import { RedisPubSubService } from './subpub.service' +import { CacheService } from './cache.service'; +import { RedisSubPub } from './redis-subpub'; +import { REDIS_PUBSUB } from './redis.constant'; +import { RedisPubSubService } from './subpub.service'; const providers: Provider[] = [ CacheService, { provide: REDIS_PUBSUB, useFactory: (configService: ConfigService) => { - const redisOptions: RedisOptions = configService.get('redis') - return new RedisSubPub(redisOptions) + const redisOptions: RedisOptions = configService.get('redis'); + return new RedisSubPub(redisOptions); }, inject: [ConfigService], }, RedisPubSubService, -] +]; @Global() @Module({ @@ -33,14 +33,14 @@ const providers: Provider[] = [ CacheModule.registerAsync({ imports: [ConfigModule], useFactory: (configService: ConfigService) => { - const redisOptions: RedisOptions = configService.get('redis') + const redisOptions: RedisOptions = configService.get('redis'); return { isGlobal: true, store: redisStore, isCacheableValue: () => true, ...redisOptions, - } + }; }, inject: [ConfigService], }), diff --git a/src/shared/redis/subpub.service.ts b/src/shared/redis/subpub.service.ts index 2ba347f..fc9c409 100644 --- a/src/shared/redis/subpub.service.ts +++ b/src/shared/redis/subpub.service.ts @@ -1,21 +1,21 @@ -import { Inject, Injectable } from '@nestjs/common' +import { Inject, Injectable } from '@nestjs/common'; -import { RedisSubPub } from './redis-subpub' -import { REDIS_PUBSUB } from './redis.constant' +import { RedisSubPub } from './redis-subpub'; +import { REDIS_PUBSUB } from './redis.constant'; @Injectable() export class RedisPubSubService { constructor(@Inject(REDIS_PUBSUB) private readonly redisSubPub: RedisSubPub) {} public async publish(event: string, data: any) { - return this.redisSubPub.publish(event, data) + return this.redisSubPub.publish(event, data); } public async subscribe(event: string, callback: (data: any) => void) { - return this.redisSubPub.subscribe(event, callback) + return this.redisSubPub.subscribe(event, callback); } public async unsubscribe(event: string, callback: (data: any) => void) { - return this.redisSubPub.unsubscribe(event, callback) + return this.redisSubPub.unsubscribe(event, callback); } } diff --git a/src/shared/shared.module.ts b/src/shared/shared.module.ts index 59cf92e..f0f222f 100644 --- a/src/shared/shared.module.ts +++ b/src/shared/shared.module.ts @@ -1,16 +1,16 @@ -import { HttpModule } from '@nestjs/axios' -import { Global, Module } from '@nestjs/common' -import { EventEmitterModule } from '@nestjs/event-emitter' -import { ScheduleModule } from '@nestjs/schedule' -import { ThrottlerModule } from '@nestjs/throttler' +import { HttpModule } from '@nestjs/axios'; +import { Global, Module } from '@nestjs/common'; +import { EventEmitterModule } from '@nestjs/event-emitter'; +import { ScheduleModule } from '@nestjs/schedule'; +import { ThrottlerModule } from '@nestjs/throttler'; -import { isDev } from '~/global/env' +import { isDev } from '~/global/env'; -import { HelperModule } from './helper/helper.module' -import { LoggerModule } from './logger/logger.module' -import { MailerModule } from './mailer/mailer.module' +import { HelperModule } from './helper/helper.module'; +import { LoggerModule } from './logger/logger.module'; +import { MailerModule } from './mailer/mailer.module'; -import { RedisModule } from './redis/redis.module' +import { RedisModule } from './redis/redis.module'; @Global() @Module({ diff --git a/src/socket/base.gateway.ts b/src/socket/base.gateway.ts index a664892..8ce4e34 100644 --- a/src/socket/base.gateway.ts +++ b/src/socket/base.gateway.ts @@ -1,30 +1,22 @@ -import type { Socket } from 'socket.io' +import type { Socket } from 'socket.io'; -import { BusinessEvents } from './business-event.constant' +import { BusinessEvents } from './business-event.constant'; export abstract class BaseGateway { - public gatewayMessageFormat( - type: BusinessEvents, - message: any, - code?: number, - ) { + public gatewayMessageFormat(type: BusinessEvents, message: any, code?: number) { return { type, data: message, code, - } + }; } handleDisconnect(client: Socket) { - client.send( - this.gatewayMessageFormat(BusinessEvents.GATEWAY_CONNECT, 'WebSocket 断开'), - ) + client.send(this.gatewayMessageFormat(BusinessEvents.GATEWAY_CONNECT, 'WebSocket 断开')); } handleConnect(client: Socket) { - client.send( - this.gatewayMessageFormat(BusinessEvents.GATEWAY_CONNECT, 'WebSocket 已连接'), - ) + client.send(this.gatewayMessageFormat(BusinessEvents.GATEWAY_CONNECT, 'WebSocket 已连接')); } } diff --git a/src/socket/events/admin.gateway.ts b/src/socket/events/admin.gateway.ts index 60f930a..2b8c810 100644 --- a/src/socket/events/admin.gateway.ts +++ b/src/socket/events/admin.gateway.ts @@ -1,37 +1,38 @@ -import { JwtService } from '@nestjs/jwt' +import { JwtService } from '@nestjs/jwt'; import { GatewayMetadata, OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway, WebSocketServer, -} from '@nestjs/websockets' +} from '@nestjs/websockets'; -import { Server } from 'socket.io' +import { Server } from 'socket.io'; -import { AuthService } from '~/modules/auth/auth.service' -import { CacheService } from '~/shared/redis/cache.service' +import { AuthService } from '~/modules/auth/auth.service'; +import { CacheService } from '~/shared/redis/cache.service'; -import { createAuthGateway } from '../shared/auth.gateway' +import { createAuthGateway } from '../shared/auth.gateway'; -const AuthGateway = createAuthGateway({ namespace: 'admin' }) +const AuthGateway = createAuthGateway({ namespace: 'admin' }); @WebSocketGateway({ namespace: 'admin' }) export class AdminEventsGateway extends AuthGateway - implements OnGatewayConnection, OnGatewayDisconnect { + implements OnGatewayConnection, OnGatewayDisconnect +{ constructor( protected readonly jwtService: JwtService, protected readonly authService: AuthService, - private readonly cacheService: CacheService, + private readonly cacheService: CacheService ) { - super(jwtService, authService, cacheService) + super(jwtService, authService, cacheService); } @WebSocketServer() - protected _server: Server + protected _server: Server; get server() { - return this._server + return this._server; } } diff --git a/src/socket/events/web.gateway.ts b/src/socket/events/web.gateway.ts index 94b83a3..7d46d32 100644 --- a/src/socket/events/web.gateway.ts +++ b/src/socket/events/web.gateway.ts @@ -1,36 +1,37 @@ -import { JwtService } from '@nestjs/jwt' +import { JwtService } from '@nestjs/jwt'; import { GatewayMetadata, OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway, WebSocketServer, -} from '@nestjs/websockets' +} from '@nestjs/websockets'; -import { Server } from 'socket.io' +import { Server } from 'socket.io'; -import { TokenService } from '~/modules/auth/services/token.service' -import { CacheService } from '~/shared/redis/cache.service' +import { TokenService } from '~/modules/auth/services/token.service'; +import { CacheService } from '~/shared/redis/cache.service'; -import { createAuthGateway } from '../shared/auth.gateway' +import { createAuthGateway } from '../shared/auth.gateway'; -const AuthGateway = createAuthGateway({ namespace: 'web' }) +const AuthGateway = createAuthGateway({ namespace: 'web' }); @WebSocketGateway({ namespace: 'web' }) export class WebEventsGateway extends AuthGateway - implements OnGatewayConnection, OnGatewayDisconnect { + implements OnGatewayConnection, OnGatewayDisconnect +{ constructor( protected readonly jwtService: JwtService, protected readonly tokenService: TokenService, - private readonly cacheService: CacheService, + private readonly cacheService: CacheService ) { - super(jwtService, tokenService, cacheService) + super(jwtService, tokenService, cacheService); } @WebSocketServer() - protected _server: Server + protected _server: Server; get server() { - return this._server + return this._server; } } diff --git a/src/socket/shared/auth.gateway.ts b/src/socket/shared/auth.gateway.ts index 8285338..c18e59d 100644 --- a/src/socket/shared/auth.gateway.ts +++ b/src/socket/shared/auth.gateway.ts @@ -1,120 +1,114 @@ -import { } from '@nestjs/common' -import { OnEvent } from '@nestjs/event-emitter' -import { JwtService } from '@nestjs/jwt' -import type { - OnGatewayConnection, - OnGatewayDisconnect, -} from '@nestjs/websockets' -import { WebSocketServer } from '@nestjs/websockets' -import { Namespace } from 'socket.io' -import type { Socket } from 'socket.io' +import {} from '@nestjs/common'; +import { OnEvent } from '@nestjs/event-emitter'; +import { JwtService } from '@nestjs/jwt'; +import type { OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets'; +import { WebSocketServer } from '@nestjs/websockets'; +import { Namespace } from 'socket.io'; +import type { Socket } from 'socket.io'; -import { EventBusEvents } from '~/constants/event-bus.constant' +import { EventBusEvents } from '~/constants/event-bus.constant'; -import { TokenService } from '~/modules/auth/services/token.service' -import { CacheService } from '~/shared/redis/cache.service' +import { TokenService } from '~/modules/auth/services/token.service'; +import { CacheService } from '~/shared/redis/cache.service'; -import { BroadcastBaseGateway } from '../base.gateway' -import { BusinessEvents } from '../business-event.constant' +import { BroadcastBaseGateway } from '../base.gateway'; +import { BusinessEvents } from '../business-event.constant'; export interface AuthGatewayOptions { - namespace: string + namespace: string; } // eslint-disable-next-line ts/ban-ts-comment // @ts-expect-error -export interface IAuthGateway extends OnGatewayConnection, OnGatewayDisconnect, BroadcastBaseGateway {} +export interface IAuthGateway + extends OnGatewayConnection, + OnGatewayDisconnect, + BroadcastBaseGateway {} -export function createAuthGateway(options: AuthGatewayOptions): new (...args: any[]) => IAuthGateway { - const { namespace } = options +export function createAuthGateway( + options: AuthGatewayOptions +): new (...args: any[]) => IAuthGateway { + const { namespace } = options; class AuthGateway extends BroadcastBaseGateway implements IAuthGateway { constructor( protected readonly jwtService: JwtService, protected readonly tokenService: TokenService, - private readonly cacheService: CacheService, + private readonly cacheService: CacheService ) { - super() + super(); } @WebSocketServer() - protected namespace: Namespace + protected namespace: Namespace; async authFailed(client: Socket) { - client.send( - this.gatewayMessageFormat(BusinessEvents.AUTH_FAILED, '认证失败'), - ) - client.disconnect() + client.send(this.gatewayMessageFormat(BusinessEvents.AUTH_FAILED, '认证失败')); + client.disconnect(); } async authToken(token: string): Promise { - if (typeof token !== 'string') - return false + if (typeof token !== 'string') return false; const validJwt = async () => { try { - const ok = await this.jwtService.verify(token) + const ok = await this.jwtService.verify(token); - if (!ok) - return false - } - catch { - return false + if (!ok) return false; + } catch { + return false; } // is not crash, is verify - return true - } + return true; + }; - return await validJwt() + return await validJwt(); } async handleConnection(client: Socket) { - const token - = client.handshake.query.token - || client.handshake.headers.authorization - || client.handshake.headers.Authorization - if (!token) - return this.authFailed(client) + const token = + client.handshake.query.token || + client.handshake.headers.authorization || + client.handshake.headers.Authorization; + if (!token) return this.authFailed(client); - if (!(await this.authToken(token as string))) - return this.authFailed(client) + if (!(await this.authToken(token as string))) return this.authFailed(client); - super.handleConnect(client) + super.handleConnect(client); - const sid = client.id - this.tokenSocketIdMap.set(token.toString(), sid) + const sid = client.id; + this.tokenSocketIdMap.set(token.toString(), sid); } handleDisconnect(client: Socket) { - super.handleDisconnect(client) + super.handleDisconnect(client); } - tokenSocketIdMap = new Map() + tokenSocketIdMap = new Map(); @OnEvent(EventBusEvents.TokenExpired) handleTokenExpired(token: string) { // consola.debug(`token expired: ${token}`) - const server = this.namespace.server - const sid = this.tokenSocketIdMap.get(token) - if (!sid) - return false + const server = this.namespace.server; + const sid = this.tokenSocketIdMap.get(token); + if (!sid) return false; - const socket = server.of(`/${namespace}`).sockets.get(sid) + const socket = server.of(`/${namespace}`).sockets.get(sid); if (socket) { - socket.disconnect() - super.handleDisconnect(socket) - return true + socket.disconnect(); + super.handleDisconnect(socket); + return true; } - return false + return false; } override broadcast(event: BusinessEvents, data: any) { this.cacheService.emitter .of(`/${namespace}`) - .emit('message', this.gatewayMessageFormat(event, data)) + .emit('message', this.gatewayMessageFormat(event, data)); } } - return AuthGateway + return AuthGateway; } diff --git a/src/socket/socket.module.ts b/src/socket/socket.module.ts index a1a1de8..0affa35 100644 --- a/src/socket/socket.module.ts +++ b/src/socket/socket.module.ts @@ -1,12 +1,12 @@ -import { Module, Provider, forwardRef } from '@nestjs/common' +import { Module, Provider, forwardRef } from '@nestjs/common'; -import { AuthModule } from '../modules/auth/auth.module' -import { SystemModule } from '../modules/system/system.module' +import { AuthModule } from '../modules/auth/auth.module'; +import { SystemModule } from '../modules/system/system.module'; -import { AdminEventsGateway } from './events/admin.gateway' -import { WebEventsGateway } from './events/web.gateway' +import { AdminEventsGateway } from './events/admin.gateway'; +import { WebEventsGateway } from './events/web.gateway'; -const providers: Provider[] = [AdminEventsGateway, WebEventsGateway] +const providers: Provider[] = [AdminEventsGateway, WebEventsGateway]; @Module({ imports: [forwardRef(() => SystemModule), AuthModule], diff --git a/src/utils/captcha.util.ts b/src/utils/captcha.util.ts index 4f416bc..65ccf3d 100644 --- a/src/utils/captcha.util.ts +++ b/src/utils/captcha.util.ts @@ -1,4 +1,4 @@ -import svgCaptcha from 'svg-captcha' +import svgCaptcha from 'svg-captcha'; export function createCaptcha() { return svgCaptcha.createMathExpr({ @@ -10,10 +10,10 @@ export function createCaptcha() { fontSize: 50, width: 110, height: 38, - }) + }); } export function createMathExpr() { - const options = {} - return svgCaptcha.createMathExpr(options) + const options = {}; + return svgCaptcha.createMathExpr(options); } diff --git a/src/utils/crypto.util.ts b/src/utils/crypto.util.ts index 83fd841..58dd902 100644 --- a/src/utils/crypto.util.ts +++ b/src/utils/crypto.util.ts @@ -1,30 +1,28 @@ -import CryptoJS from 'crypto-js' +import CryptoJS from 'crypto-js'; -const key = CryptoJS.enc.Utf8.parse('louisabcdefe9bc') -const iv = CryptoJS.enc.Utf8.parse('0123456789louis') +const key = CryptoJS.enc.Utf8.parse('louisabcdefe9bc'); +const iv = CryptoJS.enc.Utf8.parse('0123456789louis'); export function aesEncrypt(data) { - if (!data) - return data + if (!data) return data; const enc = CryptoJS.AES.encrypt(data, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, - }) - return enc.toString() + }); + return enc.toString(); } export function aesDecrypt(data) { - if (!data) - return data + if (!data) return data; const dec = CryptoJS.AES.decrypt(data, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, - }) - return dec.toString(CryptoJS.enc.Utf8) + }); + return dec.toString(CryptoJS.enc.Utf8); } export function md5(str: string) { - return CryptoJS.MD5(str).toString() + return CryptoJS.MD5(str).toString(); } diff --git a/src/utils/date.util.ts b/src/utils/date.util.ts index 54a15d1..d275d1a 100644 --- a/src/utils/date.util.ts +++ b/src/utils/date.util.ts @@ -1,23 +1,23 @@ -import dayjs from 'dayjs' -import { isDate } from 'lodash' +import dayjs from 'dayjs'; +import { isDate } from 'lodash'; -const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss' -const DATE_FORMAT = 'YYYY-MM-DD' +const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; +const DATE_FORMAT = 'YYYY-MM-DD'; export function formatToDateTime( date: string | number | Date | dayjs.Dayjs | null | undefined = undefined, - format = DATE_TIME_FORMAT, + format = DATE_TIME_FORMAT ): string { - return dayjs(date).format(format) + return dayjs(date).format(format); } export function formatToDate( date: string | number | Date | dayjs.Dayjs | null | undefined = undefined, - format = DATE_FORMAT, + format = DATE_FORMAT ): string { - return dayjs(date).format(format) + return dayjs(date).format(format); } export function isDateObject(obj: unknown): boolean { - return isDate(obj) || dayjs.isDayjs(obj) + return isDate(obj) || dayjs.isDayjs(obj); } diff --git a/src/utils/file.util.ts b/src/utils/file.util.ts index 6d6ba48..35a63b6 100644 --- a/src/utils/file.util.ts +++ b/src/utils/file.util.ts @@ -1,9 +1,9 @@ -import fs from 'node:fs' -import path from 'node:path' +import fs from 'node:fs'; +import path from 'node:path'; -import { MultipartFile } from '@fastify/multipart' +import { MultipartFile } from '@fastify/multipart'; -import dayjs from 'dayjs' +import dayjs from 'dayjs'; enum Type { IMAGE = '图片', @@ -14,76 +14,69 @@ enum Type { } export function getFileType(extName: string) { - const documents = 'txt doc pdf ppt pps xlsx xls docx' - const music = 'mp3 wav wma mpa ram ra aac aif m4a' - const video = 'avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg' - const image - = 'bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg' - if (image.includes(extName)) - return Type.IMAGE + const documents = 'txt doc pdf ppt pps xlsx xls docx'; + const music = 'mp3 wav wma mpa ram ra aac aif m4a'; + const video = 'avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg'; + const image = 'bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg'; + if (image.includes(extName)) return Type.IMAGE; - if (documents.includes(extName)) - return Type.TXT + if (documents.includes(extName)) return Type.TXT; - if (music.includes(extName)) - return Type.MUSIC + if (music.includes(extName)) return Type.MUSIC; - if (video.includes(extName)) - return Type.VIDEO + if (video.includes(extName)) return Type.VIDEO; - return Type.OTHER + return Type.OTHER; } export function getName(fileName: string) { - if (fileName.includes('.')) - return fileName.split('.')[0] + if (fileName.includes('.')) return fileName.split('.')[0]; - return fileName + return fileName; } export function getExtname(fileName: string) { - return path.extname(fileName).replace('.', '') + return path.extname(fileName).replace('.', ''); } export function getSize(bytes: number, decimals = 2) { - if (bytes === 0) - return '0 Bytes' + if (bytes === 0) return '0 Bytes'; - const k = 1024 - const dm = decimals < 0 ? 0 : decimals - const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - const i = Math.floor(Math.log(bytes) / Math.log(k)) + const i = Math.floor(Math.log(bytes) / Math.log(k)); - return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}` + return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`; } export function fileRename(fileName: string) { - const name = fileName.split('.')[0] - const extName = path.extname(fileName) - const time = dayjs().format('YYYYMMDDHHmmSSS') - return `${name}-${time}${extName}` + const name = fileName.split('.')[0]; + const extName = path.extname(fileName); + const time = dayjs().format('YYYYMMDDHHmmSSS'); + return `${name}-${time}${extName}`; } export function getFilePath(name: string) { - return `/upload/${name}` + return `/upload/${name}`; } export function saveLocalFile(buffer: Buffer, name: string) { - const filePath = path.join(__dirname, '../../', 'public/upload', name) - const writeStream = fs.createWriteStream(filePath) - writeStream.write(buffer) + const filePath = path.join(__dirname, '../../', 'public/upload', name); + const writeStream = fs.createWriteStream(filePath); + writeStream.write(buffer); } export async function saveFile(file: MultipartFile, name: string) { - const filePath = path.join(__dirname, '../../', 'public/upload', name) - const writeStream = fs.createWriteStream(filePath) - const buffer = await file.toBuffer() - writeStream.write(buffer) + const filePath = path.join(__dirname, '../../', 'public/upload', name); + const writeStream = fs.createWriteStream(filePath); + const buffer = await file.toBuffer(); + writeStream.write(buffer); } export async function deleteFile(name: string) { fs.unlink(path.join(__dirname, '../../', 'public', name), () => { // console.log(error); - }) + }); } diff --git a/src/utils/index.ts b/src/utils/index.ts index b889238..598276a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,11 +1,11 @@ -export * from './captcha.util' -export * from './crypto.util' -export * from './date.util' -export * from './file.util' -export * from './ip.util' -export * from './is.util' -export * from './list2tree.util' -export * from './permission.util' -export * from './redis.util' -export * from './schedule.util' -export * from './tool.util' +export * from './captcha.util'; +export * from './crypto.util'; +export * from './date.util'; +export * from './file.util'; +export * from './ip.util'; +export * from './is.util'; +export * from './list2tree.util'; +export * from './permission.util'; +export * from './redis.util'; +export * from './schedule.util'; +export * from './tool.util'; diff --git a/src/utils/ip.util.ts b/src/utils/ip.util.ts index 8c6ce06..60ce375 100644 --- a/src/utils/ip.util.ts +++ b/src/utils/ip.util.ts @@ -2,65 +2,56 @@ * @module utils/ip * @description IP utility functions */ -import type { IncomingMessage } from 'node:http' +import type { IncomingMessage } from 'node:http'; -import axios from 'axios' -import type { FastifyRequest } from 'fastify' +import axios from 'axios'; +import type { FastifyRequest } from 'fastify'; /* 判断IP是不是内网 */ function isLAN(ip: string) { - ip.toLowerCase() - if (ip === 'localhost') - return true - let a_ip = 0 - if (ip === '') - return false - const aNum = ip.split('.') - if (aNum.length !== 4) - return false - a_ip += Number.parseInt(aNum[0]) << 24 - a_ip += Number.parseInt(aNum[1]) << 16 - a_ip += Number.parseInt(aNum[2]) << 8 - a_ip += Number.parseInt(aNum[3]) << 0 - a_ip = (a_ip >> 16) & 0xFFFF + ip.toLowerCase(); + if (ip === 'localhost') return true; + let a_ip = 0; + if (ip === '') return false; + const aNum = ip.split('.'); + if (aNum.length !== 4) return false; + a_ip += Number.parseInt(aNum[0]) << 24; + a_ip += Number.parseInt(aNum[1]) << 16; + a_ip += Number.parseInt(aNum[2]) << 8; + a_ip += Number.parseInt(aNum[3]) << 0; + a_ip = (a_ip >> 16) & 0xffff; return ( - a_ip >> 8 === 0x7F - || a_ip >> 8 === 0xA - || a_ip === 0xC0A8 - || (a_ip >= 0xAC10 && a_ip <= 0xAC1F) - ) + a_ip >> 8 === 0x7f || a_ip >> 8 === 0xa || a_ip === 0xc0a8 || (a_ip >= 0xac10 && a_ip <= 0xac1f) + ); } export function getIp(request: FastifyRequest | IncomingMessage) { - const req = request as any + const req = request as any; - let ip: string - = request.headers['x-forwarded-for'] - || request.headers['X-Forwarded-For'] - || request.headers['X-Real-IP'] - || request.headers['x-real-ip'] - || req?.ip - || req?.raw?.connection?.remoteAddress - || req?.raw?.socket?.remoteAddress - || undefined - if (ip && ip.split(',').length > 0) - ip = ip.split(',')[0] + let ip: string = + request.headers['x-forwarded-for'] || + request.headers['X-Forwarded-For'] || + request.headers['X-Real-IP'] || + request.headers['x-real-ip'] || + req?.ip || + req?.raw?.connection?.remoteAddress || + req?.raw?.socket?.remoteAddress || + undefined; + if (ip && ip.split(',').length > 0) ip = ip.split(',')[0]; - return ip + return ip; } export async function getIpAddress(ip: string) { - if (isLAN(ip)) - return '内网IP' + if (isLAN(ip)) return '内网IP'; try { - let { data } = await axios.get( - `https://whois.pconline.com.cn/ipJson.jsp?ip=${ip}&json=true`, - { responseType: 'arraybuffer' }, - ) - data = new TextDecoder('gbk').decode(data) - data = JSON.parse(data) - return data.addr.trim().split(' ').at(0) + let { data } = await axios.get(`https://whois.pconline.com.cn/ipJson.jsp?ip=${ip}&json=true`, { + responseType: 'arraybuffer', + }); + data = new TextDecoder('gbk').decode(data); + data = JSON.parse(data); + return data.addr.trim().split(' ').at(0); } catch (error) { - return '第三方接口请求失败' + return '第三方接口请求失败'; } } diff --git a/src/utils/is.util.ts b/src/utils/is.util.ts index ce9e048..f3802cf 100644 --- a/src/utils/is.util.ts +++ b/src/utils/is.util.ts @@ -1,4 +1,5 @@ export function isExternal(path: string): boolean { - const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/ - return reg.test(path) + const reg = + /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/; + return reg.test(path); } diff --git a/src/utils/list2tree.util.ts b/src/utils/list2tree.util.ts index 663a9f4..d1999a3 100644 --- a/src/utils/list2tree.util.ts +++ b/src/utils/list2tree.util.ts @@ -1,27 +1,27 @@ export type TreeNode = T & { - id: number - parentId: number - children?: TreeNode[] -} + id: number; + parentId: number; + children?: TreeNode[]; +}; export type ListNode = T & { - id: number - parentId: number -} + id: number; + parentId: number; +}; export function list2Tree( items: T, - parentId: number | null = null, + parentId: number | null = null ): TreeNode[] { return items .filter(item => item.parentId === parentId) - .map((item) => { - const children = list2Tree(items, item.id) + .map(item => { + const children = list2Tree(items, item.id); return { ...item, ...(children.length ? { children } : null), - } - }) + }; + }); } /** @@ -34,20 +34,19 @@ export function list2Tree( export function filterTree2List(treeData, key, value) { const filterChildrenTree = (resTree, treeItem) => { if (treeItem[key].includes(value)) { - resTree.push(treeItem) - return resTree + resTree.push(treeItem); + return resTree; } if (Array.isArray(treeItem.children)) { - const children = treeItem.children.reduce(filterChildrenTree, []) + const children = treeItem.children.reduce(filterChildrenTree, []); - const data = { ...treeItem, children } + const data = { ...treeItem, children }; - if (children.length) - resTree.push({ ...data }) + if (children.length) resTree.push({ ...data }); } - return resTree - } - return treeData.reduce(filterChildrenTree, []) + return resTree; + }; + return treeData.reduce(filterChildrenTree, []); } /** @@ -58,29 +57,25 @@ export function filterTree2List(treeData, key, value) { */ export function filterTree( treeData: TreeNode[], - predicate: (data: T) => boolean, + predicate: (data: T) => boolean ): TreeNode[] { function filter(treeData: TreeNode[]): TreeNode[] { - if (!treeData?.length) - return treeData + if (!treeData?.length) return treeData; - return treeData.filter((data) => { - if (!predicate(data)) - return false + return treeData.filter(data => { + if (!predicate(data)) return false; - data.children = filter(data.children) - return true - }) + data.children = filter(data.children); + return true; + }); } - return filter(treeData) || [] + return filter(treeData) || []; } export function deleteEmptyChildren(arr: any) { - arr?.forEach((node) => { - if (node.children?.length === 0) - delete node.children - else - deleteEmptyChildren(node.children) - }) + arr?.forEach(node => { + if (node.children?.length === 0) delete node.children; + else deleteEmptyChildren(node.children); + }); } diff --git a/src/utils/permission.util.ts b/src/utils/permission.util.ts index 6aa34ce..6769122 100644 --- a/src/utils/permission.util.ts +++ b/src/utils/permission.util.ts @@ -1,8 +1,8 @@ -import { ForbiddenException } from '@nestjs/common' +import { ForbiddenException } from '@nestjs/common'; -import { envBoolean } from '~/global/env' -import { MenuEntity } from '~/modules/system/menu/menu.entity' -import { isExternal } from '~/utils/is.util' +import { envBoolean } from '~/global/env'; +import { MenuEntity } from '~/modules/system/menu/menu.entity'; +import { isExternal } from '~/utils/is.util'; function createRoute(menu: MenuEntity, _isRoot) { const commonMeta = { @@ -16,7 +16,7 @@ function createRoute(menu: MenuEntity, _isRoot) { activeMenu: menu.activeMenu, status: menu.status, keepAlive: menu.keepAlive, - } + }; if (isExternal(menu.path)) { return { @@ -25,7 +25,7 @@ function createRoute(menu: MenuEntity, _isRoot) { // component: 'IFrame', name: menu.name, meta: { ...commonMeta }, - } + }; } // 目录 @@ -36,7 +36,7 @@ function createRoute(menu: MenuEntity, _isRoot) { component: menu.component, name: menu.name, meta: { ...commonMeta }, - } + }; } return { @@ -47,112 +47,95 @@ function createRoute(menu: MenuEntity, _isRoot) { meta: { ...commonMeta, }, - } + }; } function filterAsyncRoutes(menus: MenuEntity[], parentRoute) { - const res = [] + const res = []; - menus.forEach((menu) => { + menus.forEach(menu => { if (menu.type === 2 || !menu.status) { // 如果是权限或禁用直接跳过 - return + return; } // 根级别菜单渲染 - let realRoute + let realRoute; if (!parentRoute && !menu.parentId && menu.type === 1) { // 根菜单 - realRoute = createRoute(menu, true) - } - else if (!parentRoute && !menu.parentId && menu.type === 0) { + realRoute = createRoute(menu, true); + } else if (!parentRoute && !menu.parentId && menu.type === 0) { // 目录 - const childRoutes = filterAsyncRoutes(menus, menu) - realRoute = createRoute(menu, true) + const childRoutes = filterAsyncRoutes(menus, menu); + realRoute = createRoute(menu, true); if (childRoutes && childRoutes.length > 0) { - realRoute.redirect = childRoutes[0].path - realRoute.children = childRoutes + realRoute.redirect = childRoutes[0].path; + realRoute.children = childRoutes; } - } - else if ( - parentRoute - && parentRoute.id === menu.parentId - && menu.type === 1 - ) { + } else if (parentRoute && parentRoute.id === menu.parentId && menu.type === 1) { // 子菜单 - realRoute = createRoute(menu, false) - } - else if ( - parentRoute - && parentRoute.id === menu.parentId - && menu.type === 0 - ) { + realRoute = createRoute(menu, false); + } else if (parentRoute && parentRoute.id === menu.parentId && menu.type === 0) { // 如果还是目录,继续递归 - const childRoute = filterAsyncRoutes(menus, menu) - realRoute = createRoute(menu, false) + const childRoute = filterAsyncRoutes(menus, menu); + realRoute = createRoute(menu, false); if (childRoute && childRoute.length > 0) { - realRoute.redirect = childRoute[0].path - realRoute.children = childRoute + realRoute.redirect = childRoute[0].path; + realRoute.children = childRoute; } } // add curent route - if (realRoute) - res.push(realRoute) - }) - return res + if (realRoute) res.push(realRoute); + }); + return res; } export function generatorRouters(menus: MenuEntity[]) { - return filterAsyncRoutes(menus, null) + return filterAsyncRoutes(menus, null); } // 获取所有菜单以及权限 function filterMenuToTable(menus: MenuEntity[], parentMenu) { - const res = [] - menus.forEach((menu) => { + const res = []; + menus.forEach(menu => { // 根级别菜单渲染 - let realMenu + let realMenu; if (!parentMenu && !menu.parentId && menu.type === 1) { // 根菜单,查找该跟菜单下子菜单,因为可能会包含权限 - const childMenu = filterMenuToTable(menus, menu) - realMenu = { ...menu } - realMenu.children = childMenu - } - else if (!parentMenu && !menu.parentId && menu.type === 0) { + const childMenu = filterMenuToTable(menus, menu); + realMenu = { ...menu }; + realMenu.children = childMenu; + } else if (!parentMenu && !menu.parentId && menu.type === 0) { // 根目录 - const childMenu = filterMenuToTable(menus, menu) - realMenu = { ...menu } - realMenu.children = childMenu - } - else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 1) { + const childMenu = filterMenuToTable(menus, menu); + realMenu = { ...menu }; + realMenu.children = childMenu; + } else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 1) { // 子菜单下继续找是否有子菜单 - const childMenu = filterMenuToTable(menus, menu) - realMenu = { ...menu } - realMenu.children = childMenu - } - else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 0) { + const childMenu = filterMenuToTable(menus, menu); + realMenu = { ...menu }; + realMenu.children = childMenu; + } else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 0) { // 如果还是目录,继续递归 - const childMenu = filterMenuToTable(menus, menu) - realMenu = { ...menu } - realMenu.children = childMenu - } - else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 2) { - realMenu = { ...menu } + const childMenu = filterMenuToTable(menus, menu); + realMenu = { ...menu }; + realMenu.children = childMenu; + } else if (parentMenu && parentMenu.id === menu.parentId && menu.type === 2) { + realMenu = { ...menu }; } // add curent route if (realMenu) { - realMenu.pid = menu.id - res.push(realMenu) + realMenu.pid = menu.id; + res.push(realMenu); } - }) - return res + }); + return res; } export function generatorMenu(menu: MenuEntity[]) { - return filterMenuToTable(menu, null) + return filterMenuToTable(menu, null); } /** 检测是否为演示环境, 如果为演示环境,则拒绝该操作 */ export function checkIsDemoMode() { - if (envBoolean('IS_DEMO')) - throw new ForbiddenException('演示模式下不允许操作') + if (envBoolean('IS_DEMO')) throw new ForbiddenException('演示模式下不允许操作'); } diff --git a/src/utils/redis.util.ts b/src/utils/redis.util.ts index c09380c..f18dad5 100644 --- a/src/utils/redis.util.ts +++ b/src/utils/redis.util.ts @@ -1,10 +1,11 @@ -import type { RedisKeys } from '~/constants/cache.constant' +import type { RedisKeys } from '~/constants/cache.constant'; -type Prefix = 'm-shop' -const prefix = 'm-shop' +type Prefix = 'm-shop'; +const prefix = 'm-shop'; -export function getRedisKey(key: T, ...concatKeys: string[]): `${Prefix}:${T}${string | ''}` { - return `${prefix}:${key}${ - concatKeys && concatKeys.length ? `:${concatKeys.join('_')}` : '' - }` +export function getRedisKey( + key: T, + ...concatKeys: string[] +): `${Prefix}:${T}${string | ''}` { + return `${prefix}:${key}${concatKeys && concatKeys.length ? `:${concatKeys.join('_')}` : ''}`; } diff --git a/src/utils/schedule.util.ts b/src/utils/schedule.util.ts index 184fede..052fc80 100644 --- a/src/utils/schedule.util.ts +++ b/src/utils/schedule.util.ts @@ -1,63 +1,60 @@ -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) +const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); export function scheduleMicrotask(callback: () => void) { - sleep(0).then(callback) + sleep(0).then(callback); } -type NotifyCallback = () => void +type NotifyCallback = () => void; -type NotifyFunction = (callback: () => void) => void +type NotifyFunction = (callback: () => void) => void; -type BatchNotifyFunction = (callback: () => void) => void +type BatchNotifyFunction = (callback: () => void) => void; export function createNotifyManager() { - let queue: NotifyCallback[] = [] - let transactions = 0 - let notifyFn: NotifyFunction = (callback) => { - callback() - } + let queue: NotifyCallback[] = []; + let transactions = 0; + let notifyFn: NotifyFunction = callback => { + callback(); + }; let batchNotifyFn: BatchNotifyFunction = (callback: () => void) => { - callback() - } + callback(); + }; const flush = (): void => { - const originalQueue = queue - queue = [] + const originalQueue = queue; + queue = []; if (originalQueue.length) { scheduleMicrotask(() => { batchNotifyFn(() => { - originalQueue.forEach((callback) => { - notifyFn(callback) - }) - }) - }) + originalQueue.forEach(callback => { + notifyFn(callback); + }); + }); + }); } - } + }; const batch = (callback: () => T): T => { - let result - transactions++ + let result; + transactions++; try { - result = callback() + result = callback(); + } finally { + transactions--; + if (!transactions) flush(); } - finally { - transactions-- - if (!transactions) - flush() - } - return result - } + return result; + }; const schedule = (callback: NotifyCallback): void => { if (transactions) { - queue.push(callback) - } - else { + queue.push(callback); + } else { scheduleMicrotask(() => { - notifyFn(callback) - }) + notifyFn(callback); + }); } - } + }; /** * All calls to the wrapped function will be batched. @@ -65,26 +62,26 @@ export function createNotifyManager() { const batchCalls = (callback: T): T => { return ((...args: any[]) => { schedule(() => { - callback(...args) - }) - }) as any - } + callback(...args); + }); + }) as any; + }; /** * Use this method to set a custom notify function. * This can be used to for example wrap notifications with `React.act` while running tests. */ const setNotifyFunction = (fn: NotifyFunction) => { - notifyFn = fn - } + notifyFn = fn; + }; /** * Use this method to set a custom function to batch notifications together into a single tick. * By default React Query will use the batch function provided by ReactDOM or React Native. */ const setBatchNotifyFunction = (fn: BatchNotifyFunction) => { - batchNotifyFn = fn - } + batchNotifyFn = fn; + }; return { batch, @@ -92,8 +89,8 @@ export function createNotifyManager() { schedule, setNotifyFunction, setBatchNotifyFunction, - } as const + } as const; } // SINGLETON -export const scheduleManager = createNotifyManager() +export const scheduleManager = createNotifyManager(); diff --git a/src/utils/tool.util.ts b/src/utils/tool.util.ts index 9982cf5..4bd86d8 100644 --- a/src/utils/tool.util.ts +++ b/src/utils/tool.util.ts @@ -1,20 +1,19 @@ -import { customAlphabet, nanoid } from 'nanoid' +import { customAlphabet, nanoid } from 'nanoid'; -import { md5 } from './crypto.util' +import { md5 } from './crypto.util'; export function getAvatar(mail: string | undefined) { - if (!mail) - return '' + if (!mail) return ''; - return `https://cravatar.cn/avatar/${md5(mail)}?d=retro` + return `https://cravatar.cn/avatar/${md5(mail)}?d=retro`; } export function generateUUID(size: number = 21): string { - return nanoid(size) + return nanoid(size); } export function generateShortUUID(): string { - return nanoid(10) + return nanoid(10); } /** @@ -22,10 +21,10 @@ export function generateShortUUID(): string { */ export function generateRandomValue( length: number, - placeholder = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM', + placeholder = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' ): string { - const customNanoid = customAlphabet(placeholder, length) - return customNanoid() + const customNanoid = customAlphabet(placeholder, length); + return customNanoid(); } /** @@ -33,28 +32,24 @@ export function generateRandomValue( */ export function randomValue( size = 16, - dict = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict', + dict = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict' ): string { - let id = '' - let i = size - const len = dict.length - while (i--) id += dict[(Math.random() * len) | 0] - return id + let id = ''; + let i = size; + const len = dict.length; + while (i--) id += dict[(Math.random() * len) | 0]; + return id; } export const hashString = function (str, seed = 0) { - let h1 = 0xDEADBEEF ^ seed - let h2 = 0x41C6CE57 ^ seed + let h1 = 0xdeadbeef ^ seed; + let h2 = 0x41c6ce57 ^ seed; for (let i = 0, ch; i < str.length; i++) { - ch = str.charCodeAt(i) - h1 = Math.imul(h1 ^ ch, 2654435761) - h2 = Math.imul(h2 ^ ch, 1597334677) + ch = str.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 2654435761); + h2 = Math.imul(h2 ^ ch, 1597334677); } - h1 - = Math.imul(h1 ^ (h1 >>> 16), 2246822507) - ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909) - h2 - = Math.imul(h2 ^ (h2 >>> 16), 2246822507) - ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909) - return 4294967296 * (2097151 & h2) + (h1 >>> 0) -} + h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909); + h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909); + return 4294967296 * (2097151 & h2) + (h1 >>> 0); +}; From 6b9b4cdd17cef504d983940cb27c7d8f56e2eb8e Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 29 Feb 2024 09:28:34 +0800 Subject: [PATCH 08/64] build: prettier issue --- .eslintignore | 1 + .prettierignore | 3 +- .prettierrc.cjs | 2 +- .vscode/settings.json | 41 - package.json | 2 +- pnpm-lock.yaml | 1922 +++-------------------------------------- 6 files changed, 121 insertions(+), 1850 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.eslintignore b/.eslintignore index cc3ee7a..890e6f9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ build/ .eslintrc.js package.json tsconfig**.json +.vscode/ \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index c4d6feb..a68494c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,4 +7,5 @@ **/*.sh /public/* -test/**/* \ No newline at end of file +test/**/* +/.vscode/* \ No newline at end of file diff --git a/.prettierrc.cjs b/.prettierrc.cjs index bf858ce..552b0a7 100644 --- a/.prettierrc.cjs +++ b/.prettierrc.cjs @@ -4,7 +4,7 @@ module.exports = { useTabs: false, // 是否使用tab进行缩进(默认false) singleQuote: true, // 使用单引号(默认false) semi: true, // 声明结尾使用分号(默认true) - trailingComma: 'es5', // 多行使用拖尾逗号(默认none) + trailingComma: 'none', // 多行使用拖尾逗号(默认none) bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true) arrowParens: 'avoid', // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid) endOfLine: 'auto', // 文件换行格式 LF/CRLF diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 56ee0fc..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - // Enable the ESlint flat config support - "eslint.experimental.useFlatConfig": true, - - // Disable the default formatter, use eslint instead - "prettier.enable": false, - "editor.formatOnSave": false, - - // Auto fix - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit", - "source.organizeImports": "never" - }, - - // Silent the stylistic rules in you IDE, but still auto fix them - "eslint.rules.customizations": [ - { "rule": "style/*", "severity": "off" }, - { "rule": "*-indent", "severity": "off" }, - { "rule": "*-spacing", "severity": "off" }, - { "rule": "*-spaces", "severity": "off" }, - { "rule": "*-order", "severity": "off" }, - { "rule": "*-dangle", "severity": "off" }, - { "rule": "*-newline", "severity": "off" }, - { "rule": "*quotes", "severity": "off" }, - { "rule": "*semi", "severity": "off" } - ], - - // Enable eslint for all supported languages - "eslint.validate": [ - "javascript", - "javascriptreact", - "typescript", - "typescriptreact", - "vue", - "html", - "markdown", - "json", - "jsonc", - "yaml" - ] -} diff --git a/package.json b/package.json index 711305a..65b95af 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "c": "git add . && git cz && git push", "release": "standard-version", "commitlint": "commitlint --config commitlint.config.cjs -e -V", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"" + "format": "prettier --write \"src/**/*.ts\"" }, "dependencies": { "@fastify/cookie": "^9.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e03c7c8..3c4d0a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - '@liaoliaots/nestjs-redis': npm:@songkeys/nestjs-redis - dependencies: '@fastify/cookie': specifier: ^9.3.1 @@ -18,8 +15,8 @@ dependencies: specifier: ^7.0.1 version: 7.0.1 '@liaoliaots/nestjs-redis': - specifier: npm:@songkeys/nestjs-redis - version: /@songkeys/nestjs-redis@10.0.0(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(ioredis@5.3.2) + specifier: ^9.0.5 + version: 9.0.5(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(ioredis@5.3.2) '@nestjs-modules/mailer': specifier: ^1.10.3 version: 1.10.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(nodemailer@6.9.9) @@ -190,9 +187,6 @@ dependencies: version: 5.0.0(winston@3.11.0) devDependencies: - '@antfu/eslint-config': - specifier: ^2.6.4 - version: 2.6.4(@vue/compiler-sfc@3.4.19)(eslint@8.56.0)(typescript@5.3.3) '@compodoc/compodoc': specifier: ^1.1.23 version: 1.1.23(typescript@5.3.3) @@ -223,6 +217,12 @@ devDependencies: '@types/ua-parser-js': specifier: ^0.7.39 version: 0.7.39 + '@typescript-eslint/eslint-plugin': + specifier: ^5.0.0 + version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: ^5.0.0 + version: 5.62.0(eslint@8.57.0)(typescript@5.3.3) cliui: specifier: ^8.0.1 version: 8.0.1 @@ -236,29 +236,20 @@ devDependencies: specifier: ^7.0.0 version: 7.0.0 eslint: - specifier: ^8.56.0 - version: 8.56.0 + specifier: ^8.0.1 + version: 8.57.0 eslint-config-prettier: - specifier: ~9.1.0 - version: 9.1.0(eslint@8.56.0) - eslint-define-config: - specifier: ~2.1.0 - version: 2.1.0 - eslint-plugin-import: - specifier: ~2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint@8.56.0) + specifier: ^8.3.0 + version: 8.10.0(eslint@8.57.0) eslint-plugin-prettier: - specifier: ~5.1.3 - version: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.2.5) + specifier: ^4.0.0 + version: 4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.0)(prettier@3.2.5) husky: specifier: ^8.0.0 version: 8.0.3 jest: specifier: ^29.7.0 version: 29.7.0(@types/node@20.11.18)(ts-node@10.9.2) - lint-staged: - specifier: ^15.2.2 - version: 15.2.2 prettier: specifier: ~3.2.5 version: 3.2.5 @@ -382,93 +373,6 @@ packages: - chokidar dev: true - /@antfu/eslint-config@2.6.4(@vue/compiler-sfc@3.4.19)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-dMD/QC5KWS1OltdpKLhfZM7W7y7zils85opk8d4lyNr7yn0OFjZs7eMYtcC6DrrN2kQ1JrFvBM7uB0QdWn5PUQ==} - hasBin: true - peerDependencies: - '@unocss/eslint-plugin': '>=0.50.0' - eslint: '>=8.40.0' - eslint-plugin-format: '>=0.1.0' - eslint-plugin-react: ^7.33.2 - eslint-plugin-react-hooks: ^4.6.0 - eslint-plugin-react-refresh: ^0.4.4 - eslint-plugin-svelte: ^2.34.1 - svelte-eslint-parser: ^0.33.1 - peerDependenciesMeta: - '@unocss/eslint-plugin': - optional: true - eslint-plugin-format: - optional: true - eslint-plugin-react: - optional: true - eslint-plugin-react-hooks: - optional: true - eslint-plugin-react-refresh: - optional: true - eslint-plugin-svelte: - optional: true - svelte-eslint-parser: - optional: true - dependencies: - '@antfu/eslint-define-config': 1.23.0-2 - '@antfu/install-pkg': 0.3.1 - '@eslint-types/jsdoc': 46.8.2-1 - '@eslint-types/typescript-eslint': 6.21.0 - '@eslint-types/unicorn': 50.0.1 - '@stylistic/eslint-plugin': 1.6.2(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 - eslint-config-flat-gitignore: 0.1.3 - eslint-merge-processors: 0.1.0(eslint@8.56.0) - eslint-plugin-antfu: 2.1.2(eslint@8.56.0) - eslint-plugin-eslint-comments: 3.2.0(eslint@8.56.0) - eslint-plugin-i: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint@8.56.0) - eslint-plugin-jsdoc: 48.1.0(eslint@8.56.0) - eslint-plugin-jsonc: 2.13.0(eslint@8.56.0) - eslint-plugin-markdown: 3.0.1(eslint@8.56.0) - eslint-plugin-n: 16.6.2(eslint@8.56.0) - eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-perfectionist: 2.5.0(eslint@8.56.0)(typescript@5.3.3)(vue-eslint-parser@9.4.2) - eslint-plugin-toml: 0.9.2(eslint@8.56.0) - eslint-plugin-unicorn: 50.0.1(eslint@8.56.0) - eslint-plugin-unused-imports: 3.1.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0) - eslint-plugin-vitest: 0.3.22(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0)(typescript@5.3.3) - eslint-plugin-vue: 9.21.1(eslint@8.56.0) - eslint-plugin-yml: 1.12.2(eslint@8.56.0) - eslint-processor-vue-blocks: 0.1.1(@vue/compiler-sfc@3.4.19)(eslint@8.56.0) - globals: 13.24.0 - jsonc-eslint-parser: 2.4.0 - local-pkg: 0.5.0 - parse-gitignore: 2.0.0 - picocolors: 1.0.0 - prompts: 2.4.2 - toml-eslint-parser: 0.9.3 - vue-eslint-parser: 9.4.2(eslint@8.56.0) - yaml-eslint-parser: 1.2.2 - yargs: 17.7.2 - transitivePeerDependencies: - - '@vue/compiler-sfc' - - astro-eslint-parser - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - svelte - - typescript - - vitest - dev: true - - /@antfu/eslint-define-config@1.23.0-2: - resolution: {integrity: sha512-LvxY21+ZhpuBf/aHeBUtGQhSEfad4PkNKXKvDOSvukaM3XVTfBhwmHX2EKwAsdq5DlfjbT3qqYyMiueBIO5iDQ==} - engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>= 8.6.0'} - dev: true - - /@antfu/install-pkg@0.3.1: - resolution: {integrity: sha512-A3zWY9VeTPnxlMiZtsGHw2lSd3ghwvL8s9RiGOtqvDxhhFfZ781ynsGBa/iUnDJ5zBrmTFQrJDud3TGgRISaxw==} - dependencies: - execa: 8.0.1 - dev: true - /@babel/code-frame@7.23.5: resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} engines: {node: '>=6.9.0'} @@ -1858,22 +1762,13 @@ packages: kuler: 2.0.0 dev: false - /@es-joy/jsdoccomment@0.42.0: - resolution: {integrity: sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==} - engines: {node: '>=16'} - dependencies: - comment-parser: 1.4.1 - esquery: 1.5.0 - jsdoc-type-pratt-parser: 4.0.0 - dev: true - - /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.56.0 + eslint: 8.57.0 eslint-visitor-keys: 3.4.3 dev: true @@ -1882,18 +1777,6 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint-types/jsdoc@46.8.2-1: - resolution: {integrity: sha512-FwD7V0xX0jyaqj8Ul5ZY+TAAPohDfVqtbuXJNHb+OIv1aTIqZi5+Zn3F2UwQ5O3BnQd2mTduyK0+HjGx3/AMFg==} - dev: true - - /@eslint-types/typescript-eslint@6.21.0: - resolution: {integrity: sha512-ao4TdMLw+zFdAJ9q6iBBxC5GSrJ14Hpv0VKaergr++jRTDaGgoYiAq84tx1FYqUJzQgzJC7dm6s52IAQP7EiHA==} - dev: true - - /@eslint-types/unicorn@50.0.1: - resolution: {integrity: sha512-nuJuipTNcg9f+oxZ+3QZw4tuDLmir4RJOPfM/oujgToiy1s+tePDZhwg5jUGc3q8OzTtPbVpsFSYX7QApjO3EA==} - dev: true - /@eslint/eslintrc@2.1.4: resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1911,8 +1794,8 @@ packages: - supports-color dev: true - /@eslint/js@8.56.0: - resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -2365,6 +2248,20 @@ packages: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + /@liaoliaots/nestjs-redis@9.0.5(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(ioredis@5.3.2): + resolution: {integrity: sha512-nPcGLj0zW4mEsYtQYfWx3o7PmrMjuzFk6+t/g2IRopAeWWUZZ/5nIJ4KTKiz/3DJEUkbX8PZqB+dOhklGF0SVA==} + engines: {node: '>=12.22.0'} + peerDependencies: + '@nestjs/common': ^9.0.0 + '@nestjs/core': ^9.0.0 + ioredis: ^5.0.0 + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + ioredis: 5.3.2 + tslib: 2.4.1 + dev: false + /@ljharb/through@2.3.12: resolution: {integrity: sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==} engines: {node: '>= 0.4'} @@ -2936,11 +2833,6 @@ packages: requiresBuild: true optional: true - /@pkgr/core@0.1.1: - resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - dev: true - /@selderee/plugin-htmlparser2@0.11.0: resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} dependencies: @@ -2991,96 +2883,10 @@ packages: - supports-color dev: false - /@songkeys/nestjs-redis@10.0.0(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(ioredis@5.3.2): - resolution: {integrity: sha512-s56+NECuJXzcaPLYzpvA2xjL0e/1Zy55UE0q6b1UqpbQSKI06TFPFCWCMUadJigiuB26O1hxi+lmDbzahKvcLg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@nestjs/common': ^10.0.0 - '@nestjs/core': ^10.0.0 - ioredis: ^5.0.0 - dependencies: - '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) - '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) - ioredis: 5.3.2 - tslib: 2.6.0 - dev: false - /@sqltools/formatter@1.2.5: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false - /@stylistic/eslint-plugin-js@1.6.2(eslint@8.56.0): - resolution: {integrity: sha512-ndT6X2KgWGxv8101pdMOxL8pihlYIHcOv3ICd70cgaJ9exwkPn8hJj4YQwslxoAlre1TFHnXd/G1/hYXgDrjIA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: '>=8.40.0' - dependencies: - '@types/eslint': 8.56.2 - acorn: 8.11.3 - escape-string-regexp: 4.0.0 - eslint: 8.56.0 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - dev: true - - /@stylistic/eslint-plugin-jsx@1.6.2(eslint@8.56.0): - resolution: {integrity: sha512-hbbouazSJbHD/fshBIOLh9JgtSphKNoTCfHLSNBjAkXLK+GR4i2jhEZZF9P0mtXrNuy2WWInmpq/g0pfWBmSBA==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: '>=8.40.0' - dependencies: - '@stylistic/eslint-plugin-js': 1.6.2(eslint@8.56.0) - '@types/eslint': 8.56.2 - eslint: 8.56.0 - estraverse: 5.3.0 - picomatch: 4.0.1 - dev: true - - /@stylistic/eslint-plugin-plus@1.6.2(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-EDMwa6gzKw4bXRqdIAUvZDfIgwotbjJs8o+vYE22chAYtVAnA0Pcq+cPx0Uk35t2gvJWb5OaLDjqA6oy1tD0jg==} - peerDependencies: - eslint: '*' - dependencies: - '@types/eslint': 8.56.2 - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@stylistic/eslint-plugin-ts@1.6.2(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-FizV58em0OjO/xFHRIy/LJJVqzxCNmYC/xVtKDf8aGDRgZpLo+lkaBKfBrbMkAGzhBKbYj+iLEFI4WEl6aVZGQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: '>=8.40.0' - dependencies: - '@stylistic/eslint-plugin-js': 1.6.2(eslint@8.56.0) - '@types/eslint': 8.56.2 - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@stylistic/eslint-plugin@1.6.2(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-EFnVcKOE5HTiMlVwisL9hHjz8a69yBbJRscWF/z+/vl6M4ew8NVrBlY8ea7KdV8QtyCY4Yapmsbg5ZDfhWlEgg==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: '>=8.40.0' - dependencies: - '@stylistic/eslint-plugin-js': 1.6.2(eslint@8.56.0) - '@stylistic/eslint-plugin-jsx': 1.6.2(eslint@8.56.0) - '@stylistic/eslint-plugin-plus': 1.6.2(eslint@8.56.0)(typescript@5.3.3) - '@stylistic/eslint-plugin-ts': 1.6.2(eslint@8.56.0)(typescript@5.3.3) - '@types/eslint': 8.56.2 - eslint: 8.56.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - /@thednp/event-listener@2.0.4: resolution: {integrity: sha512-sc4B7AzYAIvnGnivirq0XyR7LfzEDhGiiB70Q0qdNn8wSJ2pL1buVAsEZxrlc47qRJiBV4YIP+BFkyMm2r3NLg==} engines: {node: '>=16', pnpm: '>=8.6.0'} @@ -3248,10 +3054,6 @@ packages: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: true - /@types/jsonwebtoken@9.0.5: resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} dependencies: @@ -3266,12 +3068,6 @@ packages: resolution: {integrity: sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ==} dev: false - /@types/mdast@3.0.15: - resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} - dependencies: - '@types/unist': 2.0.10 - dev: true - /@types/methods@1.1.4: resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} dev: true @@ -3362,10 +3158,6 @@ packages: resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} dev: true - /@types/unist@2.0.10: - resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} - dev: true - /@types/validator@13.11.9: resolution: {integrity: sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==} @@ -3379,135 +3171,133 @@ packages: '@types/yargs-parser': 21.0.3 dev: true - /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.3.3) debug: 4.3.4 - eslint: 8.56.0 + eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 - natural-compare: 1.4.0 + natural-compare-lite: 1.4.0 semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) + tsutils: 3.21.0(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) debug: 4.3.4 - eslint: 8.56.0 + eslint: 8.57.0 typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@6.21.0: - resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/type-utils@6.21.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: '*' typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.3.3) debug: 4.3.4 - eslint: 8.56.0 - ts-api-utils: 1.2.1(typescript@5.3.3) + eslint: 8.57.0 + tsutils: 3.21.0(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.21.0: - resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3): - resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.3.3): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.3 semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) + tsutils: 3.21.0(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.21.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.7 - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - eslint: 8.56.0 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) + eslint: 8.57.0 + eslint-scope: 5.1.1 semver: 7.6.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@6.21.0: - resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} - engines: {node: ^16.0.0 || >=18.0.0} + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 dev: true @@ -3515,48 +3305,6 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vue/compiler-core@3.4.19: - resolution: {integrity: sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==} - dependencies: - '@babel/parser': 7.23.9 - '@vue/shared': 3.4.19 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.0.2 - dev: true - - /@vue/compiler-dom@3.4.19: - resolution: {integrity: sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==} - dependencies: - '@vue/compiler-core': 3.4.19 - '@vue/shared': 3.4.19 - dev: true - - /@vue/compiler-sfc@3.4.19: - resolution: {integrity: sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==} - dependencies: - '@babel/parser': 7.23.9 - '@vue/compiler-core': 3.4.19 - '@vue/compiler-dom': 3.4.19 - '@vue/compiler-ssr': 3.4.19 - '@vue/shared': 3.4.19 - estree-walker: 2.0.2 - magic-string: 0.30.7 - postcss: 8.4.35 - source-map-js: 1.0.2 - dev: true - - /@vue/compiler-ssr@3.4.19: - resolution: {integrity: sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==} - dependencies: - '@vue/compiler-dom': 3.4.19 - '@vue/shared': 3.4.19 - dev: true - - /@vue/shared@3.4.19: - resolution: {integrity: sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==} - dev: true - /@webassemblyjs/ast@1.11.6: resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} dependencies: @@ -3848,13 +3596,6 @@ packages: type-fest: 0.21.3 dev: true - /ansi-escapes@6.2.0: - resolution: {integrity: sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==} - engines: {node: '>=14.16'} - dependencies: - type-fest: 3.13.1 - dev: true - /ansi-regex@3.0.1: resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} engines: {node: '>=4'} @@ -3927,11 +3668,6 @@ packages: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} dev: false - /are-docs-informative@0.0.2: - resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} - engines: {node: '>=14'} - dev: true - /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -3944,14 +3680,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - /array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - dev: true - /array-from@2.1.1: resolution: {integrity: sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==} dev: true @@ -3960,17 +3688,6 @@ packages: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} dev: true - /array-includes@3.1.7: - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - get-intrinsic: 1.2.4 - is-string: 1.0.7 - dev: true - /array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} dev: true @@ -3980,62 +3697,6 @@ packages: engines: {node: '>=8'} dev: true - /array.prototype.filter@1.0.3: - resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - es-array-method-boxes-properly: 1.0.0 - is-string: 1.0.7 - dev: true - - /array.prototype.findlastindex@1.2.4: - resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - es-errors: 1.3.0 - es-shim-unscopables: 1.0.2 - dev: true - - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - es-shim-unscopables: 1.0.2 - dev: true - - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - es-shim-unscopables: 1.0.2 - dev: true - - /arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 - dev: true - /arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -4079,13 +3740,6 @@ packages: engines: {node: '>=8.0.0'} dev: false - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - dependencies: - possible-typed-array-names: 1.0.0 - dev: true - /avvio@8.3.0: resolution: {integrity: sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==} dependencies: @@ -4402,17 +4056,6 @@ packages: ieee754: 1.2.1 dev: false - /builtin-modules@3.3.0: - resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} - engines: {node: '>=6'} - dev: true - - /builtins@5.0.1: - resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} - dependencies: - semver: 7.6.0 - dev: true - /bull@4.12.2: resolution: {integrity: sha512-WPuc0VCYx+cIVMiZtPwRpWyyJFBrj4/OgKJ6n9Jf4tIw7rQNV+HAKQv15UDkcTvfpGFehvod7Fd1YztbYSJIDQ==} engines: {node: '>=12'} @@ -4532,14 +4175,6 @@ packages: engines: {node: '>=10'} dev: true - /character-entities-legacy@1.1.4: - resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} - dev: true - - /character-entities@1.2.4: - resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} - dev: true - /character-parser@2.2.0: resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==} requiresBuild: true @@ -4547,10 +4182,6 @@ packages: is-regex: 1.1.4 dev: false - /character-reference-invalid@1.1.4: - resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} - dev: true - /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true @@ -4605,11 +4236,6 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - /ci-info@4.0.0: - resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} - engines: {node: '>=8'} - dev: true - /cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} dev: true @@ -4631,13 +4257,6 @@ packages: source-map: 0.6.1 dev: false - /clean-regexp@1.0.0: - resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} - engines: {node: '>=4'} - dependencies: - escape-string-regexp: 1.0.5 - dev: true - /cli-boxes@2.2.1: resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} engines: {node: '>=6'} @@ -4657,13 +4276,6 @@ packages: restore-cursor: 3.1.0 dev: true - /cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - restore-cursor: 4.0.0 - dev: true - /cli-highlight@2.1.11: resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} engines: {node: '>=8.0.0', npm: '>=5.0.0'} @@ -4691,14 +4303,6 @@ packages: '@colors/colors': 1.5.0 dev: true - /cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} - engines: {node: '>=18'} - dependencies: - slice-ansi: 5.0.0 - string-width: 7.1.0 - dev: true - /cli-width@2.2.1: resolution: {integrity: sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==} dev: true @@ -4787,10 +4391,6 @@ packages: color-string: 1.9.1 dev: false - /colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - dev: true - /colors@1.4.0: resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} engines: {node: '>=0.1.90'} @@ -4843,11 +4443,6 @@ packages: repeat-string: 1.6.1 dev: true - /comment-parser@1.4.1: - resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} - engines: {node: '>= 12.0.0'} - dev: true - /commitizen@4.3.0(@types/node@20.11.18)(typescript@5.3.3): resolution: {integrity: sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw==} engines: {node: '>= 12'} @@ -5285,12 +4880,6 @@ packages: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} - /cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - dev: true - /cz-conventional-changelog@3.3.0(@types/node@20.11.18)(typescript@5.3.3): resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==} engines: {node: '>= 10'} @@ -5361,17 +4950,6 @@ packages: dependencies: ms: 2.0.0 - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - dev: true - /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -5558,13 +5136,6 @@ packages: run-applescript: 3.2.0 dev: false - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - dev: true - /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -5723,10 +5294,6 @@ packages: engines: {node: '>=12'} dev: true - /emoji-regex@10.3.0: - resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} - dev: true - /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -5802,57 +5369,6 @@ packages: stackframe: 1.3.4 dev: false - /es-abstract@1.22.4: - resolution: {integrity: sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.3 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.1 - has-symbols: 1.0.3 - hasown: 2.0.1 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.1 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.0 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 - string.prototype.trimstart: 1.0.7 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.5 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.14 - dev: true - - /es-array-method-boxes-properly@1.0.0: - resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} - dev: true - /es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} @@ -5867,30 +5383,6 @@ packages: resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} dev: true - /es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.4 - has-tostringtag: 1.0.2 - hasown: 2.0.1 - dev: true - - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - dependencies: - hasown: 2.0.1 - dev: true - - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - dev: true - /es5-ext@0.10.62: resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} engines: {node: '>=0.10'} @@ -6017,428 +5509,30 @@ packages: source-map: 0.6.1 dev: true - /eslint-compat-utils@0.1.2(eslint@8.56.0): - resolution: {integrity: sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - eslint: 8.56.0 - dev: true - - /eslint-compat-utils@0.4.1(eslint@8.56.0): - resolution: {integrity: sha512-5N7ZaJG5pZxUeNNJfUchurLVrunD1xJvyg5kYOIVF8kg1f3ajTikmAu/5fZ9w100omNPOoMjngRszh/Q/uFGMg==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - eslint: 8.56.0 - semver: 7.6.0 - dev: true - - /eslint-config-flat-gitignore@0.1.3: - resolution: {integrity: sha512-oQD+dEZv3RThN60tFqGFt+NJcO1DmssUcP+T/nlX+ZzEoEvVUYH0GU9X/VlmDXsbMsS9mONI1HrlxLgtKojw7w==} - dependencies: - find-up: 7.0.0 - parse-gitignore: 2.0.0 - dev: true - - /eslint-config-prettier@9.1.0(eslint@8.56.0): - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + /eslint-config-prettier@8.10.0(eslint@8.57.0): + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.56.0 + eslint: 8.57.0 dev: true - /eslint-define-config@2.1.0: - resolution: {integrity: sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==} - engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>=8.6.0'} - dev: true - - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - dependencies: - debug: 3.2.7 - is-core-module: 2.13.1 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-merge-processors@0.1.0(eslint@8.56.0): - resolution: {integrity: sha512-IvRXXtEajLeyssvW4wJcZ2etxkR9mUf4zpNwgI+m/Uac9RfXHskuJefkHUcawVzePnd6xp24enp5jfgdHzjRdQ==} + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.0)(prettier@3.2.5): + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} peerDependencies: - eslint: '*' - dependencies: - eslint: 8.56.0 - dev: true - - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0): - resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - dependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - debug: 3.2.7 - eslint: 8.56.0 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-antfu@2.1.2(eslint@8.56.0): - resolution: {integrity: sha512-s7ZTOM3uq0iqpp6gF0UEotnvup7f2PHBUftCytLZX0+6C9j9KadKZQh6bVVngAyFgsmeD9+gcBopOYLClb2oDg==} - peerDependencies: - eslint: '*' - dependencies: - eslint: 8.56.0 - dev: true - - /eslint-plugin-es-x@7.5.0(eslint@8.56.0): - resolution: {integrity: sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '>=8' - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - '@eslint-community/regexpp': 4.10.0 - eslint: 8.56.0 - eslint-compat-utils: 0.1.2(eslint@8.56.0) - dev: true - - /eslint-plugin-eslint-comments@3.2.0(eslint@8.56.0): - resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} - engines: {node: '>=6.5.0'} - peerDependencies: - eslint: '>=4.19.1' - dependencies: - escape-string-regexp: 1.0.5 - eslint: 8.56.0 - ignore: 5.3.1 - dev: true - - /eslint-plugin-i@2.29.1(@typescript-eslint/parser@6.21.0)(eslint@8.56.0): - resolution: {integrity: sha512-ORizX37MelIWLbMyqI7hi8VJMf7A0CskMmYkB+lkCX3aF4pkGV7kwx5bSEb4qx7Yce2rAf9s34HqDRPjGRZPNQ==} - engines: {node: '>=12'} - peerDependencies: - eslint: ^7.2.0 || ^8 - dependencies: - debug: 4.3.4 - doctrine: 3.0.0 - eslint: 8.56.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0) - get-tsconfig: 4.7.2 - is-glob: 4.0.3 - minimatch: 3.1.2 - semver: 7.6.0 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - dev: true - - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0)(eslint@8.56.0): - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - dependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - array-includes: 3.1.7 - array.prototype.findlastindex: 1.2.4 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.56.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0) - hasown: 2.0.1 - is-core-module: 2.13.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.7 - object.groupby: 1.0.2 - object.values: 1.1.7 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - dev: true - - /eslint-plugin-jsdoc@48.1.0(eslint@8.56.0): - resolution: {integrity: sha512-g9S8ukmTd1DVcV/xeBYPPXOZ6rc8WJ4yi0+MVxJ1jBOrz5kmxV9gJJQ64ltCqIWFnBChLIhLVx3tbTSarqVyFA==} - engines: {node: '>=18'} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - dependencies: - '@es-joy/jsdoccomment': 0.42.0 - are-docs-informative: 0.0.2 - comment-parser: 1.4.1 - debug: 4.3.4 - escape-string-regexp: 4.0.0 - eslint: 8.56.0 - esquery: 1.5.0 - is-builtin-module: 3.2.1 - semver: 7.6.0 - spdx-expression-parse: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-jsonc@2.13.0(eslint@8.56.0): - resolution: {integrity: sha512-2wWdJfpO/UbZzPDABuUVvlUQjfMJa2p2iQfYt/oWxOMpXCcjuiMUSaA02gtY/Dbu82vpaSqc+O7Xq6ECHwtIxA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - eslint: 8.56.0 - eslint-compat-utils: 0.4.1(eslint@8.56.0) - espree: 9.6.1 - graphemer: 1.4.0 - jsonc-eslint-parser: 2.4.0 - natural-compare: 1.4.0 - synckit: 0.6.2 - dev: true - - /eslint-plugin-markdown@3.0.1(eslint@8.56.0): - resolution: {integrity: sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - eslint: 8.56.0 - mdast-util-from-markdown: 0.8.5 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-n@16.6.2(eslint@8.56.0): - resolution: {integrity: sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - eslint: '>=7.0.0' - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - builtins: 5.0.1 - eslint: 8.56.0 - eslint-plugin-es-x: 7.5.0(eslint@8.56.0) - get-tsconfig: 4.7.2 - globals: 13.24.0 - ignore: 5.3.1 - is-builtin-module: 3.2.1 - is-core-module: 2.13.1 - minimatch: 3.1.2 - resolve: 1.22.8 - semver: 7.6.0 - dev: true - - /eslint-plugin-no-only-tests@3.1.0: - resolution: {integrity: sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==} - engines: {node: '>=5.0.0'} - dev: true - - /eslint-plugin-perfectionist@2.5.0(eslint@8.56.0)(typescript@5.3.3)(vue-eslint-parser@9.4.2): - resolution: {integrity: sha512-F6XXcq4mKKUe/SREoMGQqzgw6cgCgf3pFzkFfQVIGtqD1yXVpQjnhTepzhBeZfxZwgMzR9HO4yH4CUhIQ2WBcQ==} - peerDependencies: - astro-eslint-parser: ^0.16.0 - eslint: '>=8.0.0' - svelte: '>=3.0.0' - svelte-eslint-parser: ^0.33.0 - vue-eslint-parser: '>=9.0.0' - peerDependenciesMeta: - astro-eslint-parser: - optional: true - svelte: - optional: true - svelte-eslint-parser: - optional: true - vue-eslint-parser: - optional: true - dependencies: - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 - minimatch: 9.0.3 - natural-compare-lite: 1.4.0 - vue-eslint-parser: 9.4.2(eslint@8.56.0) - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.2.5): - resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' + eslint: '>=7.28.0' eslint-config-prettier: '*' - prettier: '>=3.0.0' + prettier: '>=2.0.0' peerDependenciesMeta: - '@types/eslint': - optional: true eslint-config-prettier: optional: true dependencies: - eslint: 8.56.0 - eslint-config-prettier: 9.1.0(eslint@8.56.0) + eslint: 8.57.0 + eslint-config-prettier: 8.10.0(eslint@8.57.0) prettier: 3.2.5 prettier-linter-helpers: 1.0.0 - synckit: 0.8.8 - dev: true - - /eslint-plugin-toml@0.9.2(eslint@8.56.0): - resolution: {integrity: sha512-ri0xf63PYf3pIq/WY9BIwrqxZmGTIwSkAO0bHddI0ajUwN4KGz6W8vOvdXFHOpRdRfzxlmXze/vfsY/aTEXESg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - debug: 4.3.4 - eslint: 8.56.0 - eslint-compat-utils: 0.4.1(eslint@8.56.0) - lodash: 4.17.21 - toml-eslint-parser: 0.9.3 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-unicorn@50.0.1(eslint@8.56.0): - resolution: {integrity: sha512-KxenCZxqSYW0GWHH18okDlOQcpezcitm5aOSz6EnobyJ6BIByiPDviQRjJIUAjG/tMN11958MxaQ+qCoU6lfDA==} - engines: {node: '>=16'} - peerDependencies: - eslint: '>=8.56.0' - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - '@eslint/eslintrc': 2.1.4 - ci-info: 4.0.0 - clean-regexp: 1.0.0 - core-js-compat: 3.36.0 - eslint: 8.56.0 - esquery: 1.5.0 - indent-string: 4.0.0 - is-builtin-module: 3.2.1 - jsesc: 3.0.2 - pluralize: 8.0.0 - read-pkg-up: 7.0.1 - regexp-tree: 0.1.27 - regjsparser: 0.10.0 - semver: 7.6.0 - strip-indent: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-unused-imports@3.1.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0): - resolution: {integrity: sha512-9l1YFCzXKkw1qtAru1RWUtG2EVDZY0a0eChKXcL+EZ5jitG7qxdctu4RnvhOJHv4xfmUf7h+JJPINlVpGhZMrw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/eslint-plugin': 6 - 7 - eslint: '8' - peerDependenciesMeta: - '@typescript-eslint/eslint-plugin': - optional: true - dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 - eslint-rule-composer: 0.3.0 - dev: true - - /eslint-plugin-vitest@0.3.22(@typescript-eslint/eslint-plugin@6.21.0)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-atkFGQ7aVgcuSeSMDqnyevIyUpfBPMnosksgEPrKE7Y8xQlqG/5z2IQ6UDau05zXaaFv7Iz8uzqvIuKshjZ0Zw==} - engines: {node: ^18.0.0 || >= 20.0.0} - peerDependencies: - '@typescript-eslint/eslint-plugin': '*' - eslint: '>=8.0.0' - vitest: '*' - peerDependenciesMeta: - '@typescript-eslint/eslint-plugin': - optional: true - vitest: - optional: true - dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /eslint-plugin-vue@9.21.1(eslint@8.56.0): - resolution: {integrity: sha512-XVtI7z39yOVBFJyi8Ljbn7kY9yHzznKXL02qQYn+ta63Iy4A9JFBw6o4OSB9hyD2++tVT+su9kQqetUyCCwhjw==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - eslint: 8.56.0 - natural-compare: 1.4.0 - nth-check: 2.1.1 - postcss-selector-parser: 6.0.15 - semver: 7.6.0 - vue-eslint-parser: 9.4.2(eslint@8.56.0) - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-yml@1.12.2(eslint@8.56.0): - resolution: {integrity: sha512-hvS9p08FhPT7i/ynwl7/Wt7ke7Rf4P2D6fT8lZlL43peZDTsHtH2A0SIFQ7Kt7+mJ6if6P+FX3iJhMkdnxQwpg==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - debug: 4.3.4 - eslint: 8.56.0 - eslint-compat-utils: 0.4.1(eslint@8.56.0) - lodash: 4.17.21 - natural-compare: 1.4.0 - yaml-eslint-parser: 1.2.2 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-processor-vue-blocks@0.1.1(@vue/compiler-sfc@3.4.19)(eslint@8.56.0): - resolution: {integrity: sha512-9+dU5lU881log570oBwpelaJmOfOzSniben7IWEDRYQPPWwlvaV7NhOtsTuUWDqpYT+dtKKWPsgz4OkOi+aZnA==} - peerDependencies: - '@vue/compiler-sfc': ^3.3.0 - eslint: ^8.50.0 - dependencies: - '@vue/compiler-sfc': 3.4.19 - eslint: 8.56.0 - dev: true - - /eslint-rule-composer@0.3.0: - resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} - engines: {node: '>=4.0.0'} dev: true /eslint-scope@5.1.1: @@ -6462,15 +5556,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.56.0: - resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.10.0 '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.56.0 + '@eslint/js': 8.57.0 '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -6574,10 +5668,6 @@ packages: resolution: {integrity: sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA==} dev: true - /estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: true - /esutils@1.0.0: resolution: {integrity: sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==} engines: {node: '>=0.10.0'} @@ -6621,10 +5711,6 @@ packages: resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} dev: false - /eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - dev: true - /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -6672,21 +5758,6 @@ packages: strip-final-newline: 2.0.0 dev: true - /execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.2.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - dev: true - /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} @@ -6977,15 +6048,6 @@ packages: path-exists: 4.0.0 dev: true - /find-up@7.0.0: - resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} - engines: {node: '>=18'} - dependencies: - locate-path: 7.2.0 - path-exists: 5.0.0 - unicorn-magic: 0.1.0 - dev: true - /findup-sync@4.0.0: resolution: {integrity: sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==} engines: {node: '>= 8'} @@ -7035,12 +6097,6 @@ packages: optional: true dev: false - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - dependencies: - is-callable: 1.2.7 - dev: true - /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -7155,16 +6211,6 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - functions-have-names: 1.2.3 - dev: true - /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true @@ -7188,11 +6234,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - /get-east-asian-width@1.2.0: - resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} - engines: {node: '>=18'} - dev: true - /get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -7241,26 +6282,6 @@ packages: engines: {node: '>=10'} dev: true - /get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - dev: true - - /get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - dev: true - - /get-tsconfig@4.7.2: - resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} - dependencies: - resolve-pkg-maps: 1.0.0 - dev: true - /git-raw-commits@2.0.11: resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} engines: {node: '>=10'} @@ -7396,13 +6417,6 @@ packages: type-fest: 0.20.2 dev: true - /globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} - dependencies: - define-properties: 1.2.1 - dev: true - /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -7450,10 +6464,6 @@ packages: engines: {node: '>=6'} dev: true - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true - /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -7477,11 +6487,6 @@ packages: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} engines: {node: '>= 0.4'} - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - dev: true - /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} @@ -7639,11 +6644,6 @@ packages: engines: {node: '>=10.17.0'} dev: true - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: true - /humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} dependencies: @@ -7807,15 +6807,6 @@ packages: wrap-ansi: 6.2.0 dev: true - /internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} - dependencies: - es-errors: 1.3.0 - hasown: 2.0.1 - side-channel: 1.0.5 - dev: true - /interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} @@ -7847,17 +6838,6 @@ packages: engines: {node: '>= 0.10'} dev: false - /is-alphabetical@1.0.4: - resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} - dev: true - - /is-alphanumerical@1.0.4: - resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} - dependencies: - is-alphabetical: 1.0.4 - is-decimal: 1.0.4 - dev: true - /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -7866,14 +6846,6 @@ packages: has-tostringtag: 1.0.2 dev: true - /is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - dev: true - /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true @@ -7882,38 +6854,12 @@ packages: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} dev: false - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - dependencies: - has-bigints: 1.0.2 - dev: true - /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - dev: true - - /is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} - dependencies: - builtin-modules: 3.3.0 - dev: true - - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: true - /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: @@ -7926,10 +6872,6 @@ packages: has-tostringtag: 1.0.2 dev: true - /is-decimal@1.0.4: - resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} - dev: true - /is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -7961,18 +6903,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - /is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - dev: true - - /is-fullwidth-code-point@5.0.0: - resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} - engines: {node: '>=18'} - dependencies: - get-east-asian-width: 1.2.0 - dev: true - /is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} @@ -7984,27 +6914,11 @@ packages: dependencies: is-extglob: 2.1.1 - /is-hexadecimal@1.0.4: - resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} - dev: true - /is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} dev: true - /is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - dev: true - - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.2 - dev: true - /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -8040,13 +6954,6 @@ packages: call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - dev: true - /is-stream@1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} @@ -8056,25 +6963,6 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true - - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.2 - dev: true - - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - dev: true - /is-text-path@1.0.1: resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} engines: {node: '>=0.10.0'} @@ -8082,13 +6970,6 @@ packages: text-extensions: 1.9.0 dev: true - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - dependencies: - which-typed-array: 1.1.14 - dev: true - /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -8103,12 +6984,6 @@ packages: resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} dev: true - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - dependencies: - call-bind: 1.0.7 - dev: true - /is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -8124,10 +6999,6 @@ packages: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} dev: true - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true - /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -8676,11 +7547,6 @@ packages: dependencies: argparse: 2.0.1 - /jsdoc-type-pratt-parser@4.0.0: - resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} - engines: {node: '>=12.0.0'} - dev: true - /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -8692,12 +7558,6 @@ packages: hasBin: true dev: true - /jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} - engines: {node: '>=6'} - hasBin: true - dev: true - /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: true @@ -8731,29 +7591,12 @@ packages: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: true - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - dependencies: - minimist: 1.2.8 - dev: true - /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true dev: true - /jsonc-eslint-parser@2.4.0: - resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.11.3 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - semver: 7.6.0 - dev: true - /jsonc-parser@3.1.0: resolution: {integrity: sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==} dev: true @@ -8918,11 +7761,6 @@ packages: set-cookie-parser: 2.6.0 dev: false - /lilconfig@3.0.0: - resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} - engines: {node: '>=14'} - dev: true - /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -8933,37 +7771,6 @@ packages: uc.micro: 2.0.0 dev: false - /lint-staged@15.2.2: - resolution: {integrity: sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==} - engines: {node: '>=18.12.0'} - hasBin: true - dependencies: - chalk: 5.3.0 - commander: 11.1.0 - debug: 4.3.4 - execa: 8.0.1 - lilconfig: 3.0.0 - listr2: 8.0.1 - micromatch: 4.0.5 - pidtree: 0.6.0 - string-argv: 0.3.2 - yaml: 2.3.4 - transitivePeerDependencies: - - supports-color - dev: true - - /listr2@8.0.1: - resolution: {integrity: sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==} - engines: {node: '>=18.0.0'} - dependencies: - cli-truncate: 4.0.0 - colorette: 2.0.20 - eventemitter3: 5.0.1 - log-update: 6.0.0 - rfdc: 1.3.1 - wrap-ansi: 9.0.0 - dev: true - /load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} @@ -8979,14 +7786,6 @@ packages: engines: {node: '>=6.11.5'} dev: true - /local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} - engines: {node: '>=14'} - dependencies: - mlly: 1.5.0 - pkg-types: 1.0.3 - dev: true - /locate-path@2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} engines: {node: '>=4'} @@ -9017,13 +7816,6 @@ packages: p-locate: 5.0.0 dev: true - /locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - p-locate: 6.0.0 - dev: true - /lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} dev: false @@ -9106,17 +7898,6 @@ packages: is-unicode-supported: 0.1.0 dev: true - /log-update@6.0.0: - resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} - engines: {node: '>=18'} - dependencies: - ansi-escapes: 6.2.0 - cli-cursor: 4.0.0 - slice-ansi: 7.1.0 - strip-ansi: 7.1.0 - wrap-ansi: 9.0.0 - dev: true - /logform@2.6.0: resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} engines: {node: '>= 12.0.0'} @@ -9211,13 +7992,6 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /magic-string@0.30.7: - resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - /mailparser@3.6.7: resolution: {integrity: sha512-/3x8HW70DNehw+3vdOPKdlLuxOHoWcGB5jfx5vJ5XUbY9/2jUJbrrhda5Si8Dj/3w08U0y5uGAkqs5+SPTPKoA==} dependencies: @@ -9276,22 +8050,6 @@ packages: hasBin: true dev: true - /mdast-util-from-markdown@0.8.5: - resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} - dependencies: - '@types/mdast': 3.0.15 - mdast-util-to-string: 2.0.0 - micromark: 2.11.4 - parse-entities: 2.0.0 - unist-util-stringify-position: 2.0.3 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-to-string@2.0.0: - resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} - dev: true - /memfs@3.5.3: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} @@ -9344,15 +8102,6 @@ packages: engines: {node: '>= 0.6'} dev: true - /micromark@2.11.4: - resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} - dependencies: - debug: 4.3.4 - parse-entities: 2.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -9398,11 +8147,6 @@ packages: engines: {node: '>=6'} dev: true - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true - /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -9847,15 +8591,6 @@ packages: engines: {node: '>=10'} hasBin: true - /mlly@1.5.0: - resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==} - dependencies: - acorn: 8.11.3 - pathe: 1.1.2 - pkg-types: 1.0.3 - ufo: 1.4.0 - dev: true - /mnemonist@0.39.6: resolution: {integrity: sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==} dependencies: @@ -9964,6 +8699,7 @@ packages: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + dev: false /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} @@ -10084,13 +8820,6 @@ packages: path-key: 3.1.1 dev: true - /npm-run-path@5.2.0: - resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - path-key: 4.0.0 - dev: true - /nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: @@ -10124,44 +8853,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - dev: true - - /object.fromentries@2.0.7: - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true - - /object.groupby@1.0.2: - resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==} - dependencies: - array.prototype.filter: 1.0.3 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - es-errors: 1.3.0 - dev: true - - /object.values@1.1.7: - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true - /obliterator@2.0.4: resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} dev: false @@ -10215,13 +8906,6 @@ packages: mimic-fn: 2.1.0 dev: true - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - dependencies: - mimic-fn: 4.0.0 - dev: true - /open@7.4.2: resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} engines: {node: '>=8'} @@ -10358,13 +9042,6 @@ packages: yocto-queue: 0.1.0 dev: true - /p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - yocto-queue: 1.0.0 - dev: true - /p-locate@2.0.0: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} engines: {node: '>=4'} @@ -10393,13 +9070,6 @@ packages: p-limit: 3.1.0 dev: true - /p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - p-limit: 4.0.0 - dev: true - /p-timeout@3.2.0: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} engines: {node: '>=8'} @@ -10441,22 +9111,6 @@ packages: callsites: 3.1.0 dev: true - /parse-entities@2.0.0: - resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} - dependencies: - character-entities: 1.2.4 - character-entities-legacy: 1.1.4 - character-reference-invalid: 1.1.4 - is-alphanumerical: 1.0.4 - is-decimal: 1.0.4 - is-hexadecimal: 1.0.4 - dev: true - - /parse-gitignore@2.0.0: - resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} - engines: {node: '>=14'} - dev: true - /parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} @@ -10577,11 +9231,6 @@ packages: engines: {node: '>=8'} dev: true - /path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true - /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -10595,11 +9244,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true - /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -10629,10 +9273,6 @@ packages: engines: {node: '>=8'} dev: true - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true - /pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} dependencies: @@ -10678,17 +9318,6 @@ packages: engines: {node: '>=10'} dev: true - /picomatch@4.0.1: - resolution: {integrity: sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==} - engines: {node: '>=12'} - dev: true - - /pidtree@0.6.0: - resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} - engines: {node: '>=0.10'} - hasBin: true - dev: true - /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -10739,14 +9368,6 @@ packages: find-up: 4.1.0 dev: true - /pkg-types@1.0.3: - resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} - dependencies: - jsonc-parser: 3.2.1 - mlly: 1.5.0 - pathe: 1.1.2 - dev: true - /pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -10756,28 +9377,6 @@ packages: resolution: {integrity: sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==} dev: true - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - dev: true - - /postcss-selector-parser@6.0.15: - resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} - engines: {node: '>=4'} - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - dev: true - - /postcss@8.4.35: - resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: true - /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -11222,11 +9821,6 @@ packages: '@babel/runtime': 7.23.9 dev: true - /regexp-tree@0.1.27: - resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} - hasBin: true - dev: true - /regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -11249,13 +9843,6 @@ packages: unicode-match-property-value-ecmascript: 2.1.0 dev: true - /regjsparser@0.10.0: - resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} - hasBin: true - dependencies: - jsesc: 0.5.0 - dev: true - /regjsparser@0.9.1: resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} hasBin: true @@ -11315,10 +9902,6 @@ packages: dev: true optional: true - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: true - /resolve.exports@2.0.2: resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} engines: {node: '>=10'} @@ -11352,14 +9935,6 @@ packages: signal-exit: 3.0.7 dev: true - /restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - dev: true - /ret@0.2.2: resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} engines: {node: '>=4'} @@ -11371,6 +9946,7 @@ packages: /rfdc@1.3.1: resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} + dev: false /rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} @@ -11437,16 +10013,6 @@ packages: dependencies: tslib: 2.6.2 - /safe-array-concat@1.1.0: - resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==} - engines: {node: '>=0.4'} - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - isarray: 2.0.5 - dev: true - /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} dev: true @@ -11454,15 +10020,6 @@ packages: /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - /safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 - dev: true - /safe-regex2@2.0.0: resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} dependencies: @@ -11680,22 +10237,6 @@ packages: engines: {node: '>=8'} dev: true - /slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} - dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 4.0.0 - dev: true - - /slice-ansi@7.1.0: - resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} - engines: {node: '>=18'} - dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 5.0.0 - dev: true - /slick@1.12.2: resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} dev: false @@ -11739,11 +10280,6 @@ packages: atomic-sleep: 1.0.0 dev: false - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - dev: true - /source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: @@ -11809,13 +10345,6 @@ packages: spdx-license-ids: 3.0.17 dev: true - /spdx-expression-parse@4.0.0: - resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.17 - dev: true - /spdx-license-ids@3.0.17: resolution: {integrity: sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==} dev: true @@ -11952,11 +10481,6 @@ packages: engines: {node: '>=4.0.0'} dev: false - /string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - dev: true - /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -11989,40 +10513,6 @@ packages: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - /string-width@7.1.0: - resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} - engines: {node: '>=18'} - dependencies: - emoji-regex: 10.3.0 - get-east-asian-width: 1.2.0 - strip-ansi: 7.1.0 - dev: true - - /string.prototype.trim@1.2.8: - resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true - - /string.prototype.trimend@1.0.7: - resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true - - /string.prototype.trimstart@1.0.7: - resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - dev: true - /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -12085,11 +10575,6 @@ packages: engines: {node: '>=6'} dev: true - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true - /strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -12179,21 +10664,6 @@ packages: engines: {node: '>=0.10'} dev: true - /synckit@0.6.2: - resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} - engines: {node: '>=12.20'} - dependencies: - tslib: 2.6.2 - dev: true - - /synckit@0.8.8: - resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} - engines: {node: ^14.18.0 || >=16.0.0} - dependencies: - '@pkgr/core': 0.1.1 - tslib: 2.6.2 - dev: true - /systeminformation@5.22.0: resolution: {integrity: sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==} engines: {node: '>=8.0.0'} @@ -12357,13 +10827,6 @@ packages: requiresBuild: true dev: false - /toml-eslint-parser@0.9.3: - resolution: {integrity: sha512-moYoCvkNUAPCxSW9jmHmRElhm4tVJpHL8ItC/+uYD0EpPSFXbck7yREz9tNdJVTSpHVod8+HoipcpbQ0oE6gsw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - eslint-visitor-keys: 3.4.3 - dev: true - /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -12387,15 +10850,6 @@ packages: engines: {node: '>= 14.0.0'} dev: false - /ts-api-utils@1.2.1(typescript@5.3.3): - resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - dependencies: - typescript: 5.3.3 - dev: true - /ts-jest@29.1.2(@babel/core@7.23.9)(jest@29.7.0)(typescript@5.3.3): resolution: {integrity: sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==} engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -12492,15 +10946,6 @@ packages: tsconfig-paths: 4.2.0 dev: true - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - dev: true - /tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -12514,13 +10959,23 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib@2.6.0: - resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==} + /tslib@2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} dev: false /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + /tsutils@3.21.0(typescript@5.3.3): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.3.3 + dev: true + /tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: @@ -12570,11 +11025,6 @@ packages: engines: {node: '>=8'} dev: true - /type-fest@3.13.1: - resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} - engines: {node: '>=14.16'} - dev: true - /type@1.2.0: resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} dev: true @@ -12583,50 +11033,6 @@ packages: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} dev: true - /typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 - dev: true - - /typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - dev: true - - /typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - dev: true - - /typed-array-length@1.0.5: - resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 - dev: true - /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true @@ -12724,10 +11130,6 @@ packages: resolution: {integrity: sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==} dev: false - /ufo@1.4.0: - resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==} - dev: true - /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} @@ -12748,15 +11150,6 @@ packages: dependencies: '@lukeed/csprng': 1.1.0 - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - dev: true - /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -12804,17 +11197,6 @@ packages: tiny-inflate: 1.0.3 dev: true - /unicorn-magic@0.1.0: - resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} - engines: {node: '>=18'} - dev: true - - /unist-util-stringify-position@2.0.3: - resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} - dependencies: - '@types/unist': 2.0.10 - dev: true - /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -12958,24 +11340,6 @@ packages: requiresBuild: true dev: false - /vue-eslint-parser@9.4.2(eslint@8.56.0): - resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - dependencies: - debug: 4.3.4 - eslint: 8.56.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 - lodash: 4.17.21 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - dev: true - /walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: @@ -13083,27 +11447,6 @@ packages: tr46: 0.0.3 webidl-conversions: 3.0.1 - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - dev: true - - /which-typed-array@1.1.14: - resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - dev: true - /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -13221,15 +11564,6 @@ packages: string-width: 5.1.2 strip-ansi: 7.1.0 - /wrap-ansi@9.0.0: - resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} - engines: {node: '>=18'} - dependencies: - ansi-styles: 6.2.1 - string-width: 7.1.0 - strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -13253,11 +11587,6 @@ packages: utf-8-validate: optional: true - /xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - dev: true - /xmldoc@1.3.0: resolution: {integrity: sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==} dependencies: @@ -13280,20 +11609,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - /yaml-eslint-parser@1.2.2: - resolution: {integrity: sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==} - engines: {node: ^14.17.0 || >=16.0.0} - dependencies: - eslint-visitor-keys: 3.4.3 - lodash: 4.17.21 - yaml: 2.3.4 - dev: true - - /yaml@2.3.4: - resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} - engines: {node: '>= 14'} - dev: true - /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -13335,11 +11650,6 @@ packages: engines: {node: '>=10'} dev: true - /yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} - engines: {node: '>=12.20'} - dev: true - /zepto@1.2.0: resolution: {integrity: sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==} dev: true From 88a97ee1621acc6c2469c1aa4f275522b2b5ecb4 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 29 Feb 2024 09:29:03 +0800 Subject: [PATCH 09/64] refactor: --- src/app.module.ts | 8 ++-- src/common/adapters/fastify.adapter.ts | 8 ++-- src/common/adapters/socket.adapter.ts | 2 +- src/common/decorators/api-result.decorator.ts | 24 +++++------ src/common/decorators/field.decorator.ts | 4 +- src/common/decorators/id-param.decorator.ts | 2 +- src/common/dto/cursor.dto.ts | 4 +- src/common/dto/pager.dto.ts | 6 +-- src/common/entity/common.entity.ts | 2 +- src/common/exceptions/biz.exception.ts | 4 +- src/common/exceptions/socket.exception.ts | 4 +- src/common/filters/any-exception.filter.ts | 4 +- .../interceptors/idempotence.interceptor.ts | 6 +-- .../interceptors/timeout.interceptor.ts | 2 +- .../interceptors/transform.interceptor.ts | 2 +- src/config/app.config.ts | 4 +- src/config/database.config.ts | 2 +- src/config/index.ts | 2 +- src/config/mailer.config.ts | 4 +- src/config/oss.config.ts | 2 +- src/config/redis.config.ts | 2 +- src/config/security.config.ts | 2 +- src/config/swagger.config.ts | 2 +- src/constants/cache.constant.ts | 2 +- src/constants/error-code.constant.ts | 2 +- src/constants/event-bus.constant.ts | 2 +- src/constants/response.constant.ts | 2 +- src/helper/crud/crud.factory.ts | 4 +- src/helper/paginate/create-pagination.ts | 4 +- src/helper/paginate/index.ts | 22 +++++----- src/helper/paginate/interface.ts | 2 +- src/main.ts | 4 +- src/modules/auth/auth.constant.ts | 4 +- src/modules/auth/auth.module.ts | 8 ++-- .../auth/controllers/captcha.controller.ts | 4 +- src/modules/auth/dto/account.dto.ts | 4 +- src/modules/auth/dto/captcha.dto.ts | 4 +- .../auth/entities/access-token.entity.ts | 6 +-- .../auth/entities/refresh-token.entity.ts | 4 +- src/modules/auth/guards/jwt-auth.guard.ts | 2 +- src/modules/auth/guards/rbac.guard.ts | 4 +- src/modules/auth/guards/resource.guard.ts | 6 +-- src/modules/auth/services/token.service.ts | 14 +++---- src/modules/auth/strategies/jwt.strategy.ts | 2 +- src/modules/auth/strategies/local.strategy.ts | 2 +- src/modules/contract/contract.controller.ts | 2 +- src/modules/contract/contract.module.ts | 2 +- src/modules/health/health.controller.ts | 6 +-- src/modules/health/health.module.ts | 2 +- src/modules/netdisk/manager/manage.class.ts | 2 +- .../netdisk/manager/manage.controller.ts | 8 ++-- src/modules/netdisk/manager/manage.dto.ts | 2 +- src/modules/netdisk/manager/manage.service.ts | 40 +++++++++---------- src/modules/netdisk/netdisk.module.ts | 8 ++-- .../netdisk/overview/overview.controller.ts | 4 +- .../netdisk/overview/overview.service.ts | 18 ++++----- src/modules/sse/sse.controller.ts | 4 +- src/modules/sse/sse.module.ts | 2 +- src/modules/sse/sse.service.ts | 12 +++--- src/modules/system/dept/dept.controller.ts | 2 +- src/modules/system/dept/dept.dto.ts | 2 +- src/modules/system/dept/dept.module.ts | 2 +- src/modules/system/dept/dept.service.ts | 6 +-- .../system/dict-item/dict-item.controller.ts | 2 +- .../system/dict-item/dict-item.module.ts | 2 +- .../system/dict-item/dict-item.service.ts | 14 +++---- .../system/dict-type/dict-type.controller.ts | 2 +- .../system/dict-type/dict-type.module.ts | 2 +- .../system/dict-type/dict-type.service.ts | 4 +- src/modules/system/log/log.controller.ts | 2 +- src/modules/system/log/log.module.ts | 4 +- .../log/services/captcha-log.service.ts | 4 +- .../system/log/services/login-log.service.ts | 14 +++---- .../system/log/services/task-log.service.ts | 4 +- src/modules/system/menu/menu.controller.ts | 6 +-- src/modules/system/menu/menu.dto.ts | 2 +- src/modules/system/menu/menu.entity.ts | 2 +- src/modules/system/menu/menu.module.ts | 2 +- src/modules/system/menu/menu.service.ts | 12 +++--- .../system/online/online.controller.ts | 2 +- src/modules/system/online/online.module.ts | 4 +- src/modules/system/online/online.service.ts | 2 +- .../param-config/param-config.controller.ts | 2 +- .../param-config/param-config.module.ts | 2 +- .../param-config/param-config.service.ts | 6 +-- src/modules/system/role/role.controller.ts | 4 +- src/modules/system/role/role.entity.ts | 2 +- src/modules/system/role/role.module.ts | 2 +- src/modules/system/role/role.service.ts | 30 +++++++------- src/modules/system/serve/serve.module.ts | 2 +- src/modules/system/serve/serve.service.ts | 12 +++--- src/modules/system/system.module.ts | 10 ++--- src/modules/system/task/constant.ts | 4 +- src/modules/system/task/task.controller.ts | 2 +- src/modules/system/task/task.dto.ts | 4 +- src/modules/system/task/task.module.ts | 8 ++-- src/modules/system/task/task.service.ts | 22 +++++----- src/modules/tasks/tasks.module.ts | 4 +- src/modules/todo/todo.controller.ts | 2 +- src/modules/todo/todo.module.ts | 2 +- src/modules/tools/email/email.module.ts | 2 +- .../tools/storage/storage.controller.ts | 2 +- src/modules/tools/storage/storage.entity.ts | 2 +- src/modules/tools/storage/storage.module.ts | 2 +- src/modules/tools/storage/storage.service.ts | 14 +++---- src/modules/tools/tools.module.ts | 8 ++-- src/modules/tools/upload/file.constraint.ts | 4 +- src/modules/tools/upload/upload.controller.ts | 6 +-- src/modules/tools/upload/upload.dto.ts | 4 +- src/modules/tools/upload/upload.module.ts | 2 +- src/modules/tools/upload/upload.service.ts | 4 +- src/modules/user/constant.ts | 2 +- src/modules/user/dto/password.dto.ts | 4 +- src/modules/user/dto/user.dto.ts | 4 +- src/modules/user/user.controller.ts | 6 +-- src/modules/user/user.entity.ts | 6 +-- src/modules/user/user.module.ts | 2 +- src/modules/user/user.service.ts | 24 +++++------ src/setup-swagger.ts | 4 +- .../constraints/entity-exist.constraint.ts | 4 +- .../database/constraints/unique.constraint.ts | 10 ++--- src/shared/database/database.module.ts | 8 ++-- src/shared/helper/cron.service.ts | 4 +- src/shared/helper/helper.module.ts | 2 +- src/shared/logger/logger.module.ts | 2 +- src/shared/logger/logger.service.ts | 12 +++--- src/shared/mailer/mailer.module.ts | 16 ++++---- src/shared/mailer/mailer.service.ts | 10 ++--- src/shared/redis/cache.service.ts | 2 +- src/shared/redis/redis-subpub.ts | 2 +- src/shared/redis/redis.module.ts | 16 ++++---- src/shared/shared.module.ts | 10 ++--- src/socket/base.gateway.ts | 2 +- src/socket/business-event.constant.ts | 2 +- src/socket/events/admin.gateway.ts | 2 +- src/socket/events/web.gateway.ts | 2 +- src/socket/socket.module.ts | 2 +- src/utils/captcha.util.ts | 2 +- src/utils/crypto.util.ts | 4 +- src/utils/file.util.ts | 2 +- src/utils/ip.util.ts | 2 +- src/utils/list2tree.util.ts | 2 +- src/utils/permission.util.ts | 10 ++--- src/utils/schedule.util.ts | 2 +- 144 files changed, 387 insertions(+), 387 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index 7b5542d..d5484cd 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -33,7 +33,7 @@ import { ContractModule } from './modules/contract/contract.module'; expandVariables: true, // 指定多个 env 文件时,第一个优先级最高 envFilePath: ['.env.local', `.env.${process.env.NODE_ENV}`, '.env'], - load: [...Object.values(config)], + load: [...Object.values(config)] }), SharedModule, DatabaseModule, @@ -53,7 +53,7 @@ import { ContractModule } from './modules/contract/contract.module'; TodoModule, - ContractModule, + ContractModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, @@ -64,7 +64,7 @@ import { ContractModule } from './modules/contract/contract.module'; { provide: APP_INTERCEPTOR, useClass: IdempotenceInterceptor }, { provide: APP_GUARD, useClass: JwtAuthGuard }, - { provide: APP_GUARD, useClass: RbacGuard }, - ], + { provide: APP_GUARD, useClass: RbacGuard } + ] }) export class AppModule {} diff --git a/src/common/adapters/fastify.adapter.ts b/src/common/adapters/fastify.adapter.ts index 67ecb8c..c36f024 100644 --- a/src/common/adapters/fastify.adapter.ts +++ b/src/common/adapters/fastify.adapter.ts @@ -4,7 +4,7 @@ import { FastifyAdapter } from '@nestjs/platform-fastify'; const app: FastifyAdapter = new FastifyAdapter({ trustProxy: true, - logger: false, + logger: false // forceCloseConnections: true, }); export { app as fastifyApp }; @@ -13,12 +13,12 @@ app.register(FastifyMultipart, { limits: { fields: 10, // Max number of non-file fields fileSize: 1024 * 1024 * 6, // limit size 6M - files: 5, // Max number of file fields - }, + files: 5 // Max number of file fields + } }); app.register(FastifyCookie, { - secret: 'cookie-secret', // 这个 secret 不太重要,不存鉴权相关,无关紧要 + secret: 'cookie-secret' // 这个 secret 不太重要,不存鉴权相关,无关紧要 }); app.getInstance().addHook('onRequest', (request, reply, done) => { diff --git a/src/common/adapters/socket.adapter.ts b/src/common/adapters/socket.adapter.ts index ce50f40..ae18518 100644 --- a/src/common/adapters/socket.adapter.ts +++ b/src/common/adapters/socket.adapter.ts @@ -18,7 +18,7 @@ export class RedisIoAdapter extends IoAdapter { const redisAdapter = createAdapter(pubClient, subClient, { key: RedisIoAdapterKey, - requestsTimeout: 10000, + requestsTimeout: 10000 }); server.adapter(redisAdapter); return server; diff --git a/src/common/decorators/api-result.decorator.ts b/src/common/decorators/api-result.decorator.ts index 65887fa..dc54efe 100644 --- a/src/common/decorators/api-result.decorator.ts +++ b/src/common/decorators/api-result.decorator.ts @@ -16,7 +16,7 @@ function genBaseProp(type: Type) { export function ApiResult>({ type, isPage, - status, + status }: { type?: TModel | TModel[]; isPage?: boolean; @@ -31,7 +31,7 @@ export function ApiResult>({ properties: { items: { type: 'array', - items: { $ref: getSchemaPath(type[0]) }, + items: { $ref: getSchemaPath(type[0]) } }, meta: { type: 'object', @@ -40,15 +40,15 @@ export function ApiResult>({ totalItems: { type: 'number', default: 0 }, itemsPerPage: { type: 'number', default: 0 }, totalPages: { type: 'number', default: 0 }, - currentPage: { type: 'number', default: 0 }, - }, - }, - }, + currentPage: { type: 'number', default: 0 } + } + } + } }; } else { prop = { type: 'array', - items: genBaseProp(type[0]), + items: genBaseProp(type[0]) }; } } else if (type) { @@ -68,11 +68,11 @@ export function ApiResult>({ { $ref: getSchemaPath(ResOp) }, { properties: { - data: prop, - }, - }, - ], - }, + data: prop + } + } + ] + } }) ); } diff --git a/src/common/decorators/field.decorator.ts b/src/common/decorators/field.decorator.ts index f3863f2..3423135 100644 --- a/src/common/decorators/field.decorator.ts +++ b/src/common/decorators/field.decorator.ts @@ -11,7 +11,7 @@ import { Max, MaxLength, Min, - MinLength, + MinLength } from 'class-validator'; import { isNumber } from 'lodash'; @@ -22,7 +22,7 @@ import { ToLowerCase, ToNumber, ToTrim, - ToUpperCase, + ToUpperCase } from './transform.decorator'; interface IOptionalOptions { diff --git a/src/common/decorators/id-param.decorator.ts b/src/common/decorators/id-param.decorator.ts index 4967ce2..eb41a79 100644 --- a/src/common/decorators/id-param.decorator.ts +++ b/src/common/decorators/id-param.decorator.ts @@ -7,7 +7,7 @@ export function IdParam() { errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE, exceptionFactory: _error => { throw new NotAcceptableException('id 格式不正确'); - }, + } }) ); } diff --git a/src/common/dto/cursor.dto.ts b/src/common/dto/cursor.dto.ts index 59e7912..b3b0305 100644 --- a/src/common/dto/cursor.dto.ts +++ b/src/common/dto/cursor.dto.ts @@ -9,7 +9,7 @@ export class CursorDto { @Expose() @IsOptional({ always: true }) @Transform(({ value: val }) => (val ? Number.parseInt(val) : 0), { - toClassOnly: true, + toClassOnly: true }) cursor?: number; @@ -20,7 +20,7 @@ export class CursorDto { @IsOptional({ always: true }) @Expose() @Transform(({ value: val }) => (val ? Number.parseInt(val) : 10), { - toClassOnly: true, + toClassOnly: true }) limit?: number; } diff --git a/src/common/dto/pager.dto.ts b/src/common/dto/pager.dto.ts index 2467702..11f9348 100644 --- a/src/common/dto/pager.dto.ts +++ b/src/common/dto/pager.dto.ts @@ -4,7 +4,7 @@ import { Allow, IsEnum, IsInt, IsOptional, IsString, Max, Min } from 'class-vali export enum Order { ASC = 'ASC', - DESC = 'DESC', + DESC = 'DESC' } export class PagerDto { @@ -14,7 +14,7 @@ export class PagerDto { @Expose() @IsOptional({ always: true }) @Transform(({ value: val }) => (val ? Number.parseInt(val) : 1), { - toClassOnly: true, + toClassOnly: true }) page?: number; @@ -25,7 +25,7 @@ export class PagerDto { @IsOptional({ always: true }) @Expose() @Transform(({ value: val }) => (val ? Number.parseInt(val) : 10), { - toClassOnly: true, + toClassOnly: true }) pageSize?: number; diff --git a/src/common/entity/common.entity.ts b/src/common/entity/common.entity.ts index b726edf..f8c190e 100644 --- a/src/common/entity/common.entity.ts +++ b/src/common/entity/common.entity.ts @@ -6,7 +6,7 @@ import { CreateDateColumn, PrimaryGeneratedColumn, UpdateDateColumn, - VirtualColumn, + VirtualColumn } from 'typeorm'; // 如果觉得前端转换时间太麻烦,并且不考虑通用性的话,可以在服务端进行转换,eg: @UpdateDateColumn({ name: 'updated_at', transformer }) diff --git a/src/common/exceptions/biz.exception.ts b/src/common/exceptions/biz.exception.ts index 9b51262..11275b9 100644 --- a/src/common/exceptions/biz.exception.ts +++ b/src/common/exceptions/biz.exception.ts @@ -12,7 +12,7 @@ export class BusinessException extends HttpException { super( HttpException.createBody({ code: RESPONSE_SUCCESS_CODE, - message: error, + message: error }), HttpStatus.OK ); @@ -24,7 +24,7 @@ export class BusinessException extends HttpException { super( HttpException.createBody({ code, - message, + message }), HttpStatus.OK ); diff --git a/src/common/exceptions/socket.exception.ts b/src/common/exceptions/socket.exception.ts index c25de02..ca0cde3 100644 --- a/src/common/exceptions/socket.exception.ts +++ b/src/common/exceptions/socket.exception.ts @@ -14,7 +14,7 @@ export class SocketException extends WsException { super( HttpException.createBody({ code: 0, - message: error, + message: error }) ); this.errorCode = 0; @@ -25,7 +25,7 @@ export class SocketException extends WsException { super( HttpException.createBody({ code, - message, + message }) ); diff --git a/src/common/filters/any-exception.filter.ts b/src/common/filters/any-exception.filter.ts index f31ab40..aee7fd5 100644 --- a/src/common/filters/any-exception.filter.ts +++ b/src/common/filters/any-exception.filter.ts @@ -4,7 +4,7 @@ import { ExceptionFilter, HttpException, HttpStatus, - Logger, + Logger } from '@nestjs/common'; import { FastifyReply, FastifyRequest } from 'fastify'; @@ -62,7 +62,7 @@ export class AllExceptionsFilter implements ExceptionFilter { const resBody: IBaseResponse = { code: apiErrorCode, message, - data: null, + data: null }; response.status(status).send(resBody); diff --git a/src/common/interceptors/idempotence.interceptor.ts b/src/common/interceptors/idempotence.interceptor.ts index 6fab029..0569aa6 100644 --- a/src/common/interceptors/idempotence.interceptor.ts +++ b/src/common/interceptors/idempotence.interceptor.ts @@ -12,7 +12,7 @@ import { getRedisKey } from '~/utils/redis.util'; import { HTTP_IDEMPOTENCE_KEY, - HTTP_IDEMPOTENCE_OPTIONS, + HTTP_IDEMPOTENCE_OPTIONS } from '../decorators/idempotence.decorator'; const IdempotenceHeaderKey = 'x-idempotence'; @@ -70,7 +70,7 @@ export class IdempotenceInterceptor implements NestInterceptor { pendingMessage = '相同请求正在处理中...', handler: errorHandler, expired = 60, - disableGenerateKey = false, + disableGenerateKey = false } = options; const redis = this.cacheService.getClient(); @@ -93,7 +93,7 @@ export class IdempotenceInterceptor implements NestInterceptor { const message = { 1: errorMessage, - 0: pendingMessage, + 0: pendingMessage }[resultValue]; throw new ConflictException(message); } else { diff --git a/src/common/interceptors/timeout.interceptor.ts b/src/common/interceptors/timeout.interceptor.ts index 464d53c..7a1382b 100644 --- a/src/common/interceptors/timeout.interceptor.ts +++ b/src/common/interceptors/timeout.interceptor.ts @@ -3,7 +3,7 @@ import { ExecutionContext, Injectable, NestInterceptor, - RequestTimeoutException, + RequestTimeoutException } from '@nestjs/common'; import { Observable, TimeoutError, throwError } from 'rxjs'; import { catchError, timeout } from 'rxjs/operators'; diff --git a/src/common/interceptors/transform.interceptor.ts b/src/common/interceptors/transform.interceptor.ts index 8ba5c95..f52f8b9 100644 --- a/src/common/interceptors/transform.interceptor.ts +++ b/src/common/interceptors/transform.interceptor.ts @@ -3,7 +3,7 @@ import { ExecutionContext, HttpStatus, Injectable, - NestInterceptor, + NestInterceptor } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { Observable } from 'rxjs'; diff --git a/src/config/app.config.ts b/src/config/app.config.ts index 072be51..079e0ce 100644 --- a/src/config/app.config.ts +++ b/src/config/app.config.ts @@ -13,8 +13,8 @@ export const AppConfig = registerAs(appRegToken, () => ({ logger: { level: env('LOGGER_LEVEL'), - maxFiles: envNumber('LOGGER_MAX_FILES'), - }, + maxFiles: envNumber('LOGGER_MAX_FILES') + } })); export type IAppConfig = ConfigType; diff --git a/src/config/database.config.ts b/src/config/database.config.ts index 2f9f706..02df861 100644 --- a/src/config/database.config.ts +++ b/src/config/database.config.ts @@ -24,7 +24,7 @@ const dataSourceOptions: DataSourceOptions = { multipleStatements: currentScript === 'typeorm', entities: ['dist/modules/**/*.entity{.ts,.js}'], migrations: ['dist/migrations/*{.ts,.js}'], - subscribers: ['dist/modules/**/*.subscriber{.ts,.js}'], + subscribers: ['dist/modules/**/*.subscriber{.ts,.js}'] }; export const dbRegToken = 'database'; diff --git a/src/config/index.ts b/src/config/index.ts index 4154c0a..57bae35 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -33,5 +33,5 @@ export default { OssConfig, RedisConfig, SecurityConfig, - SwaggerConfig, + SwaggerConfig }; diff --git a/src/config/mailer.config.ts b/src/config/mailer.config.ts index 2f60052..6e17271 100644 --- a/src/config/mailer.config.ts +++ b/src/config/mailer.config.ts @@ -11,8 +11,8 @@ export const MailerConfig = registerAs(mailerRegToken, () => ({ secure: true, auth: { user: env('SMTP_USER'), - pass: env('SMTP_PASS'), - }, + pass: env('SMTP_PASS') + } })); export type IMailerConfig = ConfigType; diff --git a/src/config/oss.config.ts b/src/config/oss.config.ts index 0f00e42..10ffda8 100644 --- a/src/config/oss.config.ts +++ b/src/config/oss.config.ts @@ -26,7 +26,7 @@ export const OssConfig = registerAs(ossRegToken, () => ({ domain: env('OSS_DOMAIN'), bucket: env('OSS_BUCKET'), zone: parseZone(env('OSS_ZONE') || 'Zone_z2'), - access: (env('OSS_ACCESS_TYPE') as any) || 'public', + access: (env('OSS_ACCESS_TYPE') as any) || 'public' })); export type IOssConfig = ConfigType; diff --git a/src/config/redis.config.ts b/src/config/redis.config.ts index 5cbdbff..3260bc4 100644 --- a/src/config/redis.config.ts +++ b/src/config/redis.config.ts @@ -8,7 +8,7 @@ export const RedisConfig = registerAs(redisRegToken, () => ({ host: env('REDIS_HOST', '127.0.0.1'), port: envNumber('REDIS_PORT', 6379), password: env('REDIS_PASSWORD'), - db: envNumber('REDIS_DB'), + db: envNumber('REDIS_DB') })); export type IRedisConfig = ConfigType; diff --git a/src/config/security.config.ts b/src/config/security.config.ts index f69ac11..fb2f1b1 100644 --- a/src/config/security.config.ts +++ b/src/config/security.config.ts @@ -8,7 +8,7 @@ export const SecurityConfig = registerAs(securityRegToken, () => ({ jwtSecret: env('JWT_SECRET'), jwtExprire: envNumber('JWT_EXPIRE'), refreshSecret: env('REFRESH_TOKEN_SECRET'), - refreshExpire: envNumber('REFRESH_TOKEN_EXPIRE'), + refreshExpire: envNumber('REFRESH_TOKEN_EXPIRE') })); export type ISecurityConfig = ConfigType; diff --git a/src/config/swagger.config.ts b/src/config/swagger.config.ts index c29a9c3..efbbb1a 100644 --- a/src/config/swagger.config.ts +++ b/src/config/swagger.config.ts @@ -6,7 +6,7 @@ export const swaggerRegToken = 'swagger'; export const SwaggerConfig = registerAs(swaggerRegToken, () => ({ enable: envBoolean('SWAGGER_ENABLE'), - path: env('SWAGGER_PATH'), + path: env('SWAGGER_PATH') })); export type ISwaggerConfig = ConfigType; diff --git a/src/constants/cache.constant.ts b/src/constants/cache.constant.ts index 34d9b26..858bf7f 100644 --- a/src/constants/cache.constant.ts +++ b/src/constants/cache.constant.ts @@ -3,6 +3,6 @@ export enum RedisKeys { CAPTCHA_IMG_PREFIX = 'captcha:img:', AUTH_TOKEN_PREFIX = 'auth:token:', AUTH_PERM_PREFIX = 'auth:permission:', - AUTH_PASSWORD_V_PREFIX = 'auth:passwordVersion:', + AUTH_PASSWORD_V_PREFIX = 'auth:passwordVersion:' } export const API_CACHE_PREFIX = 'api-cache:'; diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 4046758..95af623 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -45,5 +45,5 @@ export enum ErrorEnum { // OSS相关 OSS_FILE_OR_DIR_EXIST = '1401:当前创建的文件或目录已存在', OSS_NO_OPERATION_REQUIRED = '1402:无需操作', - OSS_EXCEE_MAXIMUM_QUANTITY = '1403:已超出支持的最大处理数量', + OSS_EXCEE_MAXIMUM_QUANTITY = '1403:已超出支持的最大处理数量' } diff --git a/src/constants/event-bus.constant.ts b/src/constants/event-bus.constant.ts index 7d35e8a..ad5e7e7 100644 --- a/src/constants/event-bus.constant.ts +++ b/src/constants/event-bus.constant.ts @@ -1,4 +1,4 @@ export enum EventBusEvents { TokenExpired = 'token.expired', - SystemException = 'system.exception', + SystemException = 'system.exception' } diff --git a/src/constants/response.constant.ts b/src/constants/response.constant.ts index 5b5395b..a34fb9b 100644 --- a/src/constants/response.constant.ts +++ b/src/constants/response.constant.ts @@ -11,5 +11,5 @@ export enum ContentTypeEnum { // form-data qs FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', // form-data upload - FORM_DATA = 'multipart/form-data;charset=UTF-8', + FORM_DATA = 'multipart/form-data;charset=UTF-8' } diff --git a/src/helper/crud/crud.factory.ts b/src/helper/crud/crud.factory.ts index 67b0e6d..af72c80 100644 --- a/src/helper/crud/crud.factory.ts +++ b/src/helper/crud/crud.factory.ts @@ -13,7 +13,7 @@ import { BaseService } from './base.service'; export function BaseCrudFactory any>({ entity, dto, - permissions, + permissions }: { entity: E; dto?: Type; @@ -35,7 +35,7 @@ export function BaseCrudFactory any>({ CREATE: `${prefix}:create`, READ: `${prefix}:read`, UPDATE: `${prefix}:update`, - DELETE: `${prefix}:delete`, + DELETE: `${prefix}:delete` } as const); @Controller(pluralizeName) diff --git a/src/helper/paginate/create-pagination.ts b/src/helper/paginate/create-pagination.ts index 589e416..2adfa11 100644 --- a/src/helper/paginate/create-pagination.ts +++ b/src/helper/paginate/create-pagination.ts @@ -5,7 +5,7 @@ export function createPaginationObject({ items, totalItems, currentPage, - limit, + limit }: { items: T[]; totalItems?: number; @@ -19,7 +19,7 @@ export function createPaginationObject({ itemCount: items.length, itemsPerPage: limit, totalPages, - currentPage, + currentPage }; return new Pagination(items, meta); diff --git a/src/helper/paginate/index.ts b/src/helper/paginate/index.ts index b50b8a8..181243d 100644 --- a/src/helper/paginate/index.ts +++ b/src/helper/paginate/index.ts @@ -3,7 +3,7 @@ import { FindOptionsWhere, ObjectLiteral, Repository, - SelectQueryBuilder, + SelectQueryBuilder } from 'typeorm'; import { createPaginationObject } from './create-pagination'; @@ -19,7 +19,7 @@ function resolveOptions(options: IPaginationOptions): [number, number, Paginatio return [ page || DEFAULT_PAGE, pageSize || DEFAULT_LIMIT, - paginationType || PaginationTypeEnum.TAKE_AND_SKIP, + paginationType || PaginationTypeEnum.TAKE_AND_SKIP ]; } @@ -34,9 +34,9 @@ async function paginateRepository( repository.find({ skip: limit * (page - 1), take: limit, - ...searchOptions, + ...searchOptions }), - undefined, + undefined ]; const [items, total] = await Promise.all(promises); @@ -45,7 +45,7 @@ async function paginateRepository( items, totalItems: total, currentPage: page, - limit, + limit }); } @@ -65,7 +65,7 @@ async function paginateQueryBuilder( items, totalItems: total, currentPage: page, - limit, + limit }); } @@ -80,7 +80,7 @@ export async function paginateRaw( ? queryBuilder.limit(limit).offset((page - 1) * limit) : queryBuilder.take(limit).skip((page - 1) * limit) ).getRawMany(), - queryBuilder.getCount(), + queryBuilder.getCount() ]; const [items, total] = await Promise.all(promises); @@ -89,7 +89,7 @@ export async function paginateRaw( items, totalItems: total, currentPage: page, - limit, + limit }); } @@ -104,7 +104,7 @@ export async function paginateRawAndEntities( ? queryBuilder.limit(limit).offset((page - 1) * limit) : queryBuilder.take(limit).skip((page - 1) * limit) ).getRawAndEntities(), - queryBuilder.getCount(), + queryBuilder.getCount() ]; const [itemObject, total] = await Promise.all(promises); @@ -114,9 +114,9 @@ export async function paginateRawAndEntities( items: itemObject.entities, totalItems: total, currentPage: page, - limit, + limit }), - itemObject.raw, + itemObject.raw ]; } diff --git a/src/helper/paginate/interface.ts b/src/helper/paginate/interface.ts index d2cd7d9..c7d3062 100644 --- a/src/helper/paginate/interface.ts +++ b/src/helper/paginate/interface.ts @@ -2,7 +2,7 @@ import { ObjectLiteral } from 'typeorm'; export enum PaginationTypeEnum { LIMIT_AND_OFFSET = 'limit', - TAKE_AND_SKIP = 'take', + TAKE_AND_SKIP = 'take' } export interface IPaginationOptions { diff --git a/src/main.ts b/src/main.ts index 2a174a4..3c6498e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -23,7 +23,7 @@ declare const module: any; async function bootstrap() { const app = await NestFactory.create(AppModule, fastifyApp, { bufferLogs: true, - snapshot: true, + snapshot: true // forceCloseConnections: true, }); @@ -57,7 +57,7 @@ async function bootstrap() { const msg = e.constraints![rule]; return msg; })[0] - ), + ) }) ); diff --git a/src/modules/auth/auth.constant.ts b/src/modules/auth/auth.constant.ts index 0a9f744..0815718 100644 --- a/src/modules/auth/auth.constant.ts +++ b/src/modules/auth/auth.constant.ts @@ -14,12 +14,12 @@ export const AuthStrategy = { JWT: 'jwt', GITHUB: 'github', - GOOGLE: 'google', + GOOGLE: 'google' } as const; export const Roles = { ADMIN: 'admin', - USER: 'user', + USER: 'user' // GUEST: 'guest', } as const; diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 8d67c3a..e0c54d6 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -41,18 +41,18 @@ const strategies = [LocalStrategy, JwtStrategy]; return { secret: jwtSecret, expires: jwtExprire, - ignoreExpiration: isDev, + ignoreExpiration: isDev }; }, - inject: [ConfigService], + inject: [ConfigService] }), UserModule, RoleModule, MenuModule, - LogModule, + LogModule ], controllers: [...controllers], providers: [...providers, ...strategies], - exports: [TypeOrmModule, JwtModule, ...providers], + exports: [TypeOrmModule, JwtModule, ...providers] }) export class AuthModule {} diff --git a/src/modules/auth/controllers/captcha.controller.ts b/src/modules/auth/controllers/captcha.controller.ts index 4200db0..41b0591 100644 --- a/src/modules/auth/controllers/captcha.controller.ts +++ b/src/modules/auth/controllers/captcha.controller.ts @@ -35,11 +35,11 @@ export class CaptchaController { noise: 4, width: isEmpty(width) ? 100 : width, height: isEmpty(height) ? 50 : height, - charPreset: '1234567890', + charPreset: '1234567890' }); const result = { img: `data:image/svg+xml;base64,${Buffer.from(svg.data).toString('base64')}`, - id: generateUUID(), + id: generateUUID() }; // 5分钟过期时间 await this.redis.set(genCaptchaImgKey(result.id), svg.text, 'EX', 60 * 5); diff --git a/src/modules/auth/dto/account.dto.ts b/src/modules/auth/dto/account.dto.ts index e5b73e5..cf87459 100644 --- a/src/modules/auth/dto/account.dto.ts +++ b/src/modules/auth/dto/account.dto.ts @@ -57,7 +57,7 @@ export class MenuMeta extends PartialType( 'id', 'roles', 'path', - 'name', + 'name' ] as const) ) { title: string; @@ -66,7 +66,7 @@ export class AccountMenus extends PickType(MenuEntity, [ 'id', 'path', 'name', - 'component', + 'component' ] as const) { meta: MenuMeta; } diff --git a/src/modules/auth/dto/captcha.dto.ts b/src/modules/auth/dto/captcha.dto.ts index 8f8c0bf..fdff06c 100644 --- a/src/modules/auth/dto/captcha.dto.ts +++ b/src/modules/auth/dto/captcha.dto.ts @@ -6,7 +6,7 @@ export class ImageCaptchaDto { @ApiProperty({ required: false, default: 100, - description: '验证码宽度', + description: '验证码宽度' }) @Type(() => Number) @IsInt() @@ -16,7 +16,7 @@ export class ImageCaptchaDto { @ApiProperty({ required: false, default: 50, - description: '验证码宽度', + description: '验证码宽度' }) @Type(() => Number) @IsInt() diff --git a/src/modules/auth/entities/access-token.entity.ts b/src/modules/auth/entities/access-token.entity.ts index e57a142..fbbdc0c 100644 --- a/src/modules/auth/entities/access-token.entity.ts +++ b/src/modules/auth/entities/access-token.entity.ts @@ -6,7 +6,7 @@ import { JoinColumn, ManyToOne, OneToOne, - PrimaryGeneratedColumn, + PrimaryGeneratedColumn } from 'typeorm'; import { UserEntity } from '~/modules/user/user.entity'; @@ -28,12 +28,12 @@ export class AccessTokenEntity extends BaseEntity { created_at!: Date; @OneToOne(() => RefreshTokenEntity, refreshToken => refreshToken.accessToken, { - cascade: true, + cascade: true }) refreshToken!: RefreshTokenEntity; @ManyToOne(() => UserEntity, user => user.accessTokens, { - onDelete: 'CASCADE', + onDelete: 'CASCADE' }) @JoinColumn({ name: 'user_id' }) user!: UserEntity; diff --git a/src/modules/auth/entities/refresh-token.entity.ts b/src/modules/auth/entities/refresh-token.entity.ts index b511922..4df36ce 100644 --- a/src/modules/auth/entities/refresh-token.entity.ts +++ b/src/modules/auth/entities/refresh-token.entity.ts @@ -5,7 +5,7 @@ import { Entity, JoinColumn, OneToOne, - PrimaryGeneratedColumn, + PrimaryGeneratedColumn } from 'typeorm'; import { AccessTokenEntity } from './access-token.entity'; @@ -25,7 +25,7 @@ export class RefreshTokenEntity extends BaseEntity { created_at!: Date; @OneToOne(() => AccessTokenEntity, accessToken => accessToken.refreshToken, { - onDelete: 'CASCADE', + onDelete: 'CASCADE' }) @JoinColumn() accessToken!: AccessTokenEntity; diff --git a/src/modules/auth/guards/jwt-auth.guard.ts b/src/modules/auth/guards/jwt-auth.guard.ts index 052c87b..c42ff73 100644 --- a/src/modules/auth/guards/jwt-auth.guard.ts +++ b/src/modules/auth/guards/jwt-auth.guard.ts @@ -27,7 +27,7 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) { async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ context.getHandler(), - context.getClass(), + context.getClass() ]); const request = context.switchToHttp().getRequest(); // const response = context.switchToHttp().getResponse() diff --git a/src/modules/auth/guards/rbac.guard.ts b/src/modules/auth/guards/rbac.guard.ts index 408a747..3cf7239 100644 --- a/src/modules/auth/guards/rbac.guard.ts +++ b/src/modules/auth/guards/rbac.guard.ts @@ -18,7 +18,7 @@ export class RbacGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ context.getHandler(), - context.getClass(), + context.getClass() ]); if (isPublic) return true; @@ -34,7 +34,7 @@ export class RbacGuard implements CanActivate { const payloadPermission = this.reflector.getAllAndOverride(PERMISSION_KEY, [ context.getHandler(), - context.getClass(), + context.getClass() ]); // 控制器没有设置接口权限,则默认通过 diff --git a/src/modules/auth/guards/resource.guard.ts b/src/modules/auth/guards/resource.guard.ts index db45299..301181f 100644 --- a/src/modules/auth/guards/resource.guard.ts +++ b/src/modules/auth/guards/resource.guard.ts @@ -23,7 +23,7 @@ export class ResourceGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [ context.getHandler(), - context.getClass(), + context.getClass() ]); const request = context.switchToHttp().getRequest(); @@ -66,9 +66,9 @@ export class ResourceGuard implements CanActivate { const recordQuery = { where: { id: In(items), - user: { id: user.uid }, + user: { id: user.uid } }, - relations: ['user'], + relations: ['user'] }; const records = await repo.find(recordQuery); diff --git a/src/modules/auth/services/token.service.ts b/src/modules/auth/services/token.service.ts index 786090f..2e552cd 100644 --- a/src/modules/auth/services/token.service.ts +++ b/src/modules/auth/services/token.service.ts @@ -56,7 +56,7 @@ export class TokenService { const payload: IAuthUser = { uid, pv: 1, - roles, + roles }; const jwtSign = this.jwtService.sign(payload); @@ -74,7 +74,7 @@ export class TokenService { return { accessToken: jwtSign, - refreshToken, + refreshToken }; } @@ -85,11 +85,11 @@ export class TokenService { */ async generateRefreshToken(accessToken: AccessTokenEntity, now: dayjs.Dayjs): Promise { const refreshTokenPayload = { - uuid: generateUUID(), + uuid: generateUUID() }; const refreshTokenSign = this.jwtService.sign(refreshTokenPayload, { - secret: this.securityConfig.refreshSecret, + secret: this.securityConfig.refreshSecret }); const refreshToken = new RefreshTokenEntity(); @@ -110,7 +110,7 @@ export class TokenService { return AccessTokenEntity.findOne({ where: { value }, relations: ['user', 'refreshToken'], - cache: true, + cache: true }); } @@ -120,7 +120,7 @@ export class TokenService { */ async removeAccessToken(value: string) { const accessToken = await AccessTokenEntity.findOne({ - where: { value }, + where: { value } }); if (accessToken) await accessToken.remove(); } @@ -132,7 +132,7 @@ export class TokenService { async removeRefreshToken(value: string) { const refreshToken = await RefreshTokenEntity.findOne({ where: { value }, - relations: ['accessToken'], + relations: ['accessToken'] }); if (refreshToken) { if (refreshToken.accessToken) await refreshToken.accessToken.remove(); diff --git a/src/modules/auth/strategies/jwt.strategy.ts b/src/modules/auth/strategies/jwt.strategy.ts index caf763b..a5175e7 100644 --- a/src/modules/auth/strategies/jwt.strategy.ts +++ b/src/modules/auth/strategies/jwt.strategy.ts @@ -12,7 +12,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, AuthStrategy.JWT) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, - secretOrKey: securityConfig.jwtSecret, + secretOrKey: securityConfig.jwtSecret }); } diff --git a/src/modules/auth/strategies/local.strategy.ts b/src/modules/auth/strategies/local.strategy.ts index f0c10da..da2d9d2 100644 --- a/src/modules/auth/strategies/local.strategy.ts +++ b/src/modules/auth/strategies/local.strategy.ts @@ -10,7 +10,7 @@ export class LocalStrategy extends PassportStrategy(Strategy, AuthStrategy.LOCAL constructor(private authService: AuthService) { super({ usernameField: 'credential', - passwordField: 'password', + passwordField: 'password' }); } diff --git a/src/modules/contract/contract.controller.ts b/src/modules/contract/contract.controller.ts index 7a20986..5755395 100644 --- a/src/modules/contract/contract.controller.ts +++ b/src/modules/contract/contract.controller.ts @@ -9,7 +9,7 @@ export const permissions = definePermission('app:contract', { CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiTags('Contract - 合同') diff --git a/src/modules/contract/contract.module.ts b/src/modules/contract/contract.module.ts index 2dab2b4..ad30ded 100644 --- a/src/modules/contract/contract.module.ts +++ b/src/modules/contract/contract.module.ts @@ -4,6 +4,6 @@ import { ContractService } from './contract.service'; @Module({ controllers: [ContractController], - providers: [ContractService], + providers: [ContractService] }) export class ContractModule {} diff --git a/src/modules/health/health.controller.ts b/src/modules/health/health.controller.ts index 372e19b..6927b4c 100644 --- a/src/modules/health/health.controller.ts +++ b/src/modules/health/health.controller.ts @@ -5,7 +5,7 @@ import { HealthCheck, HttpHealthIndicator, MemoryHealthIndicator, - TypeOrmHealthIndicator, + TypeOrmHealthIndicator } from '@nestjs/terminus'; import { Perm, definePermission } from '../auth/decorators/permission.decorator'; @@ -15,7 +15,7 @@ export const PermissionHealth = definePermission('app:health', { DB: 'database', MH: 'memory-heap', MR: 'memory-rss', - DISK: 'disk', + DISK: 'disk' } as const); @ApiTags('Health - 健康检查') @@ -65,7 +65,7 @@ export class HealthController { return this.disk.checkStorage('disk', { // The used disk storage should not exceed 75% of the full disk size thresholdPercent: 0.75, - path: '/', + path: '/' }); } } diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts index e488987..674c072 100644 --- a/src/modules/health/health.module.ts +++ b/src/modules/health/health.module.ts @@ -6,6 +6,6 @@ import { HealthController } from './health.controller'; @Module({ imports: [TerminusModule, HttpModule], - controllers: [HealthController], + controllers: [HealthController] }) export class HealthModule {} diff --git a/src/modules/netdisk/manager/manage.class.ts b/src/modules/netdisk/manager/manage.class.ts index d2dfa0b..1c099d8 100644 --- a/src/modules/netdisk/manager/manage.class.ts +++ b/src/modules/netdisk/manager/manage.class.ts @@ -49,7 +49,7 @@ export class SFileInfoDetail { mimeType: string; @ApiProperty({ - description: '文件存储类型,2 表示归档存储,1 表示低频存储,0表示普通存储。', + description: '文件存储类型,2 表示归档存储,1 表示低频存储,0表示普通存储。' }) type: number; diff --git a/src/modules/netdisk/manager/manage.controller.ts b/src/modules/netdisk/manager/manage.controller.ts index 58e12e6..8a6d2eb 100644 --- a/src/modules/netdisk/manager/manage.controller.ts +++ b/src/modules/netdisk/manager/manage.controller.ts @@ -17,7 +17,7 @@ import { GetFileListDto, MKDirDto, MarkFileDto, - RenameDto, + RenameDto } from './manage.dto'; import { NetDiskManageService } from './manage.service'; @@ -33,7 +33,7 @@ export const permissions = definePermission('netdisk:manage', { DOWNLOAD: 'download', RENAME: 'rename', CUT: 'cut', - COPY: 'copy', + COPY: 'copy' } as const); @ApiTags('NetDiskManage - 网盘管理模块') @@ -67,7 +67,7 @@ export class NetDiskManageController { checkIsDemoMode(); return { - token: this.manageService.createUploadToken(`${user.uid}`), + token: this.manageService.createUploadToken(`${user.uid}`) }; } @@ -84,7 +84,7 @@ export class NetDiskManageController { @Perm(permissions.MARK) async mark(@Body() dto: MarkFileDto): Promise { await this.manageService.changeFileHeaders(dto.name, dto.path, { - mark: dto.mark, + mark: dto.mark }); } diff --git a/src/modules/netdisk/manager/manage.dto.ts b/src/modules/netdisk/manager/manage.dto.ts index 1b54c84..11897e4 100644 --- a/src/modules/netdisk/manager/manage.dto.ts +++ b/src/modules/netdisk/manager/manage.dto.ts @@ -11,7 +11,7 @@ import { ValidateNested, ValidationArguments, ValidatorConstraint, - ValidatorConstraintInterface, + ValidatorConstraintInterface } from 'class-validator'; import { isEmpty } from 'lodash'; diff --git a/src/modules/netdisk/manager/manage.service.ts b/src/modules/netdisk/manager/manage.service.ts index 9f639c7..50f4d8e 100644 --- a/src/modules/netdisk/manager/manage.service.ts +++ b/src/modules/netdisk/manager/manage.service.ts @@ -11,7 +11,7 @@ import { NETDISK_COPY_SUFFIX, NETDISK_DELIMITER, NETDISK_HANDLE_MAX_ITEM, - NETDISK_LIMIT, + NETDISK_LIMIT } from '~/constants/oss.constant'; import { AccountInfo } from '~/modules/user/user.model'; @@ -38,7 +38,7 @@ export class NetDiskManageService { ) { this.mac = new qiniu.auth.digest.Mac(this.qiniuConfig.accessKey, this.qiniuConfig.secretKey); this.config = new qiniu.conf.Config({ - zone: this.qiniuConfig.zone, + zone: this.qiniuConfig.zone }); // bucket manager this.bucketManager = new qiniu.rs.BucketManager(this.mac, this.config); @@ -60,7 +60,7 @@ export class NetDiskManageService { prefix: searching ? '' : prefix, limit: NETDISK_LIMIT, delimiter: searching ? '' : NETDISK_DELIMITER, - marker, + marker }, (err, respBody, respInfo) => { if (err) { @@ -80,7 +80,7 @@ export class NetDiskManageService { fileList.push({ name: (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''), type: 'dir', - id: generateRandomValue(10), + id: generateRandomValue(10) }); } } @@ -104,7 +104,7 @@ export class NetDiskManageService { id: generateRandomValue(10), name: ditName, type: 'dir', - belongTo: pathList.join(NETDISK_DELIMITER), + belongTo: pathList.join(NETDISK_DELIMITER) }); } else if (name.includes(skey)) { // 文件 @@ -115,7 +115,7 @@ export class NetDiskManageService { fsize: item.fsize, mimeType: item.mimeType, putTime: new Date(Number.parseInt(item.putTime) / 10000), - belongTo: pathList.join(NETDISK_DELIMITER), + belongTo: pathList.join(NETDISK_DELIMITER) }); } } else { @@ -128,7 +128,7 @@ export class NetDiskManageService { type: 'file', fsize: item.fsize, mimeType: item.mimeType, - putTime: new Date(Number.parseInt(item.putTime) / 10000), + putTime: new Date(Number.parseInt(item.putTime) / 10000) }); } } @@ -136,7 +136,7 @@ export class NetDiskManageService { } resolve({ list: fileList, - marker: respBody.marker || null, + marker: respBody.marker || null }); } else { reject( @@ -170,7 +170,7 @@ export class NetDiskManageService { putTime: new Date(Number.parseInt(respBody.putTime) / 10000), type: respBody.type, uploader: '', - mark: respBody?.['x-qn-meta']?.['!mark'] ?? '', + mark: respBody?.['x-qn-meta']?.['!mark'] ?? '' }; if (!respBody.endUser) { resolve(detailInfo); @@ -295,7 +295,7 @@ export class NetDiskManageService { scope: this.qiniuConfig.bucket, insertOnly: 1, fsizeLimit: 1024 ** 2 * 10, - endUser, + endUser }); const uploadToken = policy.uploadToken(this.mac); return uploadToken; @@ -310,7 +310,7 @@ export class NetDiskManageService { const fileName = `${dir}${name}`; const toFileName = `${dir}${toName}`; const op = { - force: true, + force: true }; return new Promise((resolve, reject) => { this.bucketManager.move( @@ -345,7 +345,7 @@ export class NetDiskManageService { const fileName = `${dir}${name}`; const toFileName = `${toDir}${name}`; const op = { - force: true, + force: true }; return new Promise((resolve, reject) => { this.bucketManager.move( @@ -383,7 +383,7 @@ export class NetDiskManageService { const bn = basename(name, ext); const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; const op = { - force: true, + force: true }; return new Promise((resolve, reject) => { this.bucketManager.copy( @@ -420,7 +420,7 @@ export class NetDiskManageService { let hasFile = true; let marker = ''; const op = { - force: true, + force: true }; const bucketName = this.qiniuConfig.bucket; while (hasFile) { @@ -431,7 +431,7 @@ export class NetDiskManageService { { prefix: dirName, limit: NETDISK_HANDLE_MAX_ITEM, - marker, + marker }, (err, respBody, respInfo) => { if (err) { @@ -567,7 +567,7 @@ export class NetDiskManageService { { prefix: dirName, limit: NETDISK_HANDLE_MAX_ITEM, - marker, + marker }, (err, respBody, respInfo) => { if (err) { @@ -618,7 +618,7 @@ export class NetDiskManageService { async copyMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { const files = fileList.filter(item => item.type === 'file'); const op = { - force: true, + force: true }; if (files.length > 0) { // 批处理文件 @@ -671,7 +671,7 @@ export class NetDiskManageService { { prefix: dirName, limit: NETDISK_HANDLE_MAX_ITEM, - marker, + marker }, (err, respBody, respInfo) => { if (err) { @@ -729,7 +729,7 @@ export class NetDiskManageService { async moveMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { const files = fileList.filter(item => item.type === 'file'); const op = { - force: true, + force: true }; if (files.length > 0) { // 批处理文件 @@ -782,7 +782,7 @@ export class NetDiskManageService { { prefix: dirName, limit: NETDISK_HANDLE_MAX_ITEM, - marker, + marker }, (err, respBody, respInfo) => { if (err) { diff --git a/src/modules/netdisk/netdisk.module.ts b/src/modules/netdisk/netdisk.module.ts index 4883791..dc14031 100644 --- a/src/modules/netdisk/netdisk.module.ts +++ b/src/modules/netdisk/netdisk.module.ts @@ -15,11 +15,11 @@ import { NetDiskOverviewService } from './overview/overview.service'; RouterModule.register([ { path: 'netdisk', - module: NetdiskModule, - }, - ]), + module: NetdiskModule + } + ]) ], controllers: [NetDiskManageController, NetDiskOverviewController], - providers: [NetDiskManageService, NetDiskOverviewService], + providers: [NetDiskManageService, NetDiskOverviewService] }) export class NetdiskModule {} diff --git a/src/modules/netdisk/overview/overview.controller.ts b/src/modules/netdisk/overview/overview.controller.ts index acdfc9f..20a8ae7 100644 --- a/src/modules/netdisk/overview/overview.controller.ts +++ b/src/modules/netdisk/overview/overview.controller.ts @@ -8,7 +8,7 @@ import { OverviewSpaceInfo } from './overview.dto'; import { NetDiskOverviewService } from './overview.service'; export const permissions = definePermission('netdisk:overview', { - DESC: 'desc', + DESC: 'desc' } as const); @ApiTags('NetDiskOverview - 网盘概览模块') @@ -35,7 +35,7 @@ export class NetDiskOverviewController { hitSize: hit.datas[hit.datas.length - 1], spaceSize: space.datas[space.datas.length - 1], flowTrend: flow, - sizeTrend: space, + sizeTrend: space }; } } diff --git a/src/modules/netdisk/overview/overview.service.ts b/src/modules/netdisk/overview/overview.service.ts index 72e0387..1f8a352 100644 --- a/src/modules/netdisk/overview/overview.service.ts +++ b/src/modules/netdisk/overview/overview.service.ts @@ -36,7 +36,7 @@ export class NetDiskOverviewService { getStatisticUrl(type: string, queryParams = {}) { const defaultParams = { $bucket: this.qiniuConfig.bucket, - g: 'day', + g: 'day' }; const searchParams = new URLSearchParams({ ...defaultParams, ...queryParams }); return decodeURIComponent(`${OSS_API}/v6/${type}?${searchParams}`); @@ -53,8 +53,8 @@ export class NetDiskOverviewService { return this.httpService.axiosRef.get(url, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: `${accessToken}`, - }, + Authorization: `${accessToken}` + } }); } @@ -89,7 +89,7 @@ export class NetDiskOverviewService { datas: data.datas, times: data.times.map(e => { return dayjs.unix(e).date(); - }), + }) }; } @@ -105,7 +105,7 @@ export class NetDiskOverviewService { times: data.times.map(e => { return dayjs.unix(e).date(); }), - datas: data.datas, + datas: data.datas }; } @@ -121,7 +121,7 @@ export class NetDiskOverviewService { end, $ftype: 0, $src: 'origin', - select: 'flow', + select: 'flow' }); const { data } = await this.getStatisticData(url); const times = []; @@ -132,7 +132,7 @@ export class NetDiskOverviewService { }); return { times, - datas, + datas }; } @@ -148,7 +148,7 @@ export class NetDiskOverviewService { end, $ftype: 0, $src: 'inner', - select: 'hit', + select: 'hit' }); const { data } = await this.getStatisticData(url); const times = []; @@ -159,7 +159,7 @@ export class NetDiskOverviewService { }); return { times, - datas, + datas }; } } diff --git a/src/modules/sse/sse.controller.ts b/src/modules/sse/sse.controller.ts index e9f0ecc..e6c581f 100644 --- a/src/modules/sse/sse.controller.ts +++ b/src/modules/sse/sse.controller.ts @@ -5,7 +5,7 @@ import { ParseIntPipe, Req, Res, - Sse, + Sse } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { FastifyReply, FastifyRequest } from 'fastify'; @@ -26,7 +26,7 @@ export class SseController implements BeforeApplicationShutdown { private closeAllConnect() { this.sseService.sendToAll({ type: 'close', - data: 'bye~', + data: 'bye~' }); this.replyMap.forEach(reply => { reply.raw.end().destroy(); diff --git a/src/modules/sse/sse.module.ts b/src/modules/sse/sse.module.ts index 62bcc39..4185d1b 100644 --- a/src/modules/sse/sse.module.ts +++ b/src/modules/sse/sse.module.ts @@ -7,6 +7,6 @@ import { SseService } from './sse.service'; imports: [], controllers: [SseController], providers: [SseService], - exports: [SseService], + exports: [SseService] }) export class SseModule {} diff --git a/src/modules/sse/sse.service.ts b/src/modules/sse/sse.service.ts index a514e36..cec6e37 100644 --- a/src/modules/sse/sse.service.ts +++ b/src/modules/sse/sse.service.ts @@ -58,9 +58,9 @@ export class SseService { const roleMenus = await RoleEntity.find({ where: { menus: { - id: In(menuIds), - }, - }, + id: In(menuIds) + } + } }); const roleIds = roleMenus.map(n => n.id).concat(ROOT_ROLE_ID); await this.noticeClientToUpdateMenusByRoleIds(roleIds); @@ -73,9 +73,9 @@ export class SseService { const users = await UserEntity.find({ where: { roles: { - id: In(roleIds), - }, - }, + id: In(roleIds) + } + } }); if (users) { const userIds = users.map(n => n.id); diff --git a/src/modules/system/dept/dept.controller.ts b/src/modules/system/dept/dept.controller.ts index 06d28a8..044c045 100644 --- a/src/modules/system/dept/dept.controller.ts +++ b/src/modules/system/dept/dept.controller.ts @@ -18,7 +18,7 @@ export const permissions = definePermission('system:dept', { CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiSecurityAuth() diff --git a/src/modules/system/dept/dept.dto.ts b/src/modules/system/dept/dept.dto.ts index 14b15cf..42d2507 100644 --- a/src/modules/system/dept/dept.dto.ts +++ b/src/modules/system/dept/dept.dto.ts @@ -8,7 +8,7 @@ import { IsString, Min, MinLength, - ValidateNested, + ValidateNested } from 'class-validator'; export class DeptDto { diff --git a/src/modules/system/dept/dept.module.ts b/src/modules/system/dept/dept.module.ts index aeade9c..6de2058 100644 --- a/src/modules/system/dept/dept.module.ts +++ b/src/modules/system/dept/dept.module.ts @@ -14,6 +14,6 @@ const services = [DeptService]; imports: [TypeOrmModule.forFeature([DeptEntity]), UserModule, RoleModule], controllers: [DeptController], providers: [...services], - exports: [TypeOrmModule, ...services], + exports: [TypeOrmModule, ...services] }) export class DeptModule {} diff --git a/src/modules/system/dept/dept.service.ts b/src/modules/system/dept/dept.service.ts index 418eaf3..8d846c3 100644 --- a/src/modules/system/dept/dept.service.ts +++ b/src/modules/system/dept/dept.service.ts @@ -46,7 +46,7 @@ export class DeptService { await this.deptRepository.save({ ...data, - parent, + parent }); } @@ -61,7 +61,7 @@ export class DeptService { await this.deptRepository.save({ ...item, ...data, - parent, + parent }); } @@ -117,7 +117,7 @@ export class DeptService { const deptTree = await this.deptRepository.findTrees({ depth: 2, - relations: ['parent'], + relations: ['parent'] }); deleteEmptyChildren(deptTree); diff --git a/src/modules/system/dict-item/dict-item.controller.ts b/src/modules/system/dict-item/dict-item.controller.ts index a0ac474..4687594 100644 --- a/src/modules/system/dict-item/dict-item.controller.ts +++ b/src/modules/system/dict-item/dict-item.controller.ts @@ -17,7 +17,7 @@ export const permissions = definePermission('system:dict-item', { CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiTags('System - 字典项模块') diff --git a/src/modules/system/dict-item/dict-item.module.ts b/src/modules/system/dict-item/dict-item.module.ts index 824e0f9..4929562 100644 --- a/src/modules/system/dict-item/dict-item.module.ts +++ b/src/modules/system/dict-item/dict-item.module.ts @@ -11,6 +11,6 @@ const services = [DictItemService]; imports: [TypeOrmModule.forFeature([DictItemEntity])], controllers: [DictItemController], providers: [...services], - exports: [TypeOrmModule, ...services], + exports: [TypeOrmModule, ...services] }) export class DictItemModule {} diff --git a/src/modules/system/dict-item/dict-item.service.ts b/src/modules/system/dict-item/dict-item.service.ts index 295cb60..a1f5cac 100644 --- a/src/modules/system/dict-item/dict-item.service.ts +++ b/src/modules/system/dict-item/dict-item.service.ts @@ -26,7 +26,7 @@ export class DictItemService { pageSize, label, value, - typeId, + typeId }: DictItemQueryDto): Promise> { const queryBuilder = this.dictItemRepository .createQueryBuilder('dict_item') @@ -35,8 +35,8 @@ export class DictItemService { ...(label && { label: Like(`%${label}%`) }), ...(value && { value: Like(`%${value}%`) }), type: { - id: typeId, - }, + id: typeId + } }); return paginate(queryBuilder, { page, pageSize }); @@ -57,8 +57,8 @@ export class DictItemService { await this.dictItemRepository.insert({ ...rest, type: { - id: typeId, - }, + id: typeId + } }); } @@ -70,8 +70,8 @@ export class DictItemService { await this.dictItemRepository.update(id, { ...rest, type: { - id: typeId, - }, + id: typeId + } }); } diff --git a/src/modules/system/dict-type/dict-type.controller.ts b/src/modules/system/dict-type/dict-type.controller.ts index 4d98575..8c3c347 100644 --- a/src/modules/system/dict-type/dict-type.controller.ts +++ b/src/modules/system/dict-type/dict-type.controller.ts @@ -17,7 +17,7 @@ export const permissions = definePermission('system:dict-type', { CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiTags('System - 字典类型模块') diff --git a/src/modules/system/dict-type/dict-type.module.ts b/src/modules/system/dict-type/dict-type.module.ts index b5ad211..626bb5e 100644 --- a/src/modules/system/dict-type/dict-type.module.ts +++ b/src/modules/system/dict-type/dict-type.module.ts @@ -11,6 +11,6 @@ const services = [DictTypeService]; imports: [TypeOrmModule.forFeature([DictTypeEntity])], controllers: [DictTypeController], providers: [...services], - exports: [TypeOrmModule, ...services], + exports: [TypeOrmModule, ...services] }) export class DictTypeModule {} diff --git a/src/modules/system/dict-type/dict-type.service.ts b/src/modules/system/dict-type/dict-type.service.ts index 2b730fc..cda4d92 100644 --- a/src/modules/system/dict-type/dict-type.service.ts +++ b/src/modules/system/dict-type/dict-type.service.ts @@ -25,11 +25,11 @@ export class DictTypeService { page, pageSize, name, - code, + code }: DictTypeQueryDto): Promise> { const queryBuilder = this.dictTypeRepository.createQueryBuilder('dict_type').where({ ...(name && { name: Like(`%${name}%`) }), - ...(code && { code: Like(`%${code}%`) }), + ...(code && { code: Like(`%${code}%`) }) }); return paginate(queryBuilder, { page, pageSize }); diff --git a/src/modules/system/log/log.controller.ts b/src/modules/system/log/log.controller.ts index 144de21..b98c08d 100644 --- a/src/modules/system/log/log.controller.ts +++ b/src/modules/system/log/log.controller.ts @@ -17,7 +17,7 @@ import { TaskLogService } from './services/task-log.service'; export const permissions = definePermission('system:log', { TaskList: 'task:list', LogList: 'login:list', - CaptchaList: 'captcha:list', + CaptchaList: 'captcha:list' } as const); @ApiSecurityAuth() diff --git a/src/modules/system/log/log.module.ts b/src/modules/system/log/log.module.ts index 7f1b5b8..b39d2ea 100644 --- a/src/modules/system/log/log.module.ts +++ b/src/modules/system/log/log.module.ts @@ -16,10 +16,10 @@ const providers = [LoginLogService, TaskLogService, CaptchaLogService]; @Module({ imports: [ TypeOrmModule.forFeature([LoginLogEntity, CaptchaLogEntity, TaskLogEntity]), - UserModule, + UserModule ], controllers: [LogController], providers: [...providers], - exports: [TypeOrmModule, ...providers], + exports: [TypeOrmModule, ...providers] }) export class LogModule {} diff --git a/src/modules/system/log/services/captcha-log.service.ts b/src/modules/system/log/services/captcha-log.service.ts index b45c04c..970a7d8 100644 --- a/src/modules/system/log/services/captcha-log.service.ts +++ b/src/modules/system/log/services/captcha-log.service.ts @@ -25,7 +25,7 @@ export class CaptchaLogService { account, code, provider, - userId: uid, + userId: uid }); } @@ -36,7 +36,7 @@ export class CaptchaLogService { return paginate(queryBuilder, { page, - pageSize, + pageSize }); } diff --git a/src/modules/system/log/services/login-log.service.ts b/src/modules/system/log/services/login-log.service.ts index 256c78b..6f03578 100644 --- a/src/modules/system/log/services/login-log.service.ts +++ b/src/modules/system/log/services/login-log.service.ts @@ -23,7 +23,7 @@ async function parseLoginLog(e: any, parser: UAParser): Promise { os: `${`${uaResult.os.name ?? ''} `}${uaResult.os.version}`, browser: `${`${uaResult.browser.name ?? ''} `}${uaResult.browser.version}`, username: e.user_username, - time: e.login_log_created_at, + time: e.login_log_created_at }; } @@ -42,7 +42,7 @@ export class LoginLogService { ip, ua, address, - user: { id: uid }, + user: { id: uid } }); } catch (e) { console.error(e); @@ -59,15 +59,15 @@ export class LoginLogService { ...(time && { createdAt: Between(time[0], time[1]) }), ...(username && { user: { - username: Like(`%${username}%`), - }, - }), + username: Like(`%${username}%`) + } + }) }) .orderBy('login_log.created_at', 'DESC'); const { items, ...rest } = await paginateRaw(queryBuilder, { page, - pageSize, + pageSize }); const parser = new UAParser(); @@ -75,7 +75,7 @@ export class LoginLogService { return { items: loginLogInfos, - ...rest, + ...rest }; } diff --git a/src/modules/system/log/services/task-log.service.ts b/src/modules/system/log/services/task-log.service.ts index e0da14a..c5fd706 100644 --- a/src/modules/system/log/services/task-log.service.ts +++ b/src/modules/system/log/services/task-log.service.ts @@ -20,7 +20,7 @@ export class TaskLogService { status, detail: err, time, - task: { id: tid }, + task: { id: tid } }); return result.id; } @@ -33,7 +33,7 @@ export class TaskLogService { return paginate(queryBuilder, { page, - pageSize, + pageSize }); } diff --git a/src/modules/system/menu/menu.controller.ts b/src/modules/system/menu/menu.controller.ts index af30512..d14b8de 100644 --- a/src/modules/system/menu/menu.controller.ts +++ b/src/modules/system/menu/menu.controller.ts @@ -6,7 +6,7 @@ import { Get, Post, Put, - Query, + Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { flattenDeep } from 'lodash'; @@ -17,7 +17,7 @@ import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; import { Perm, definePermission, - getDefinePermissions, + getDefinePermissions } from '~/modules/auth/decorators/permission.decorator'; import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto'; @@ -29,7 +29,7 @@ export const permissions = definePermission('system:menu', { CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiTags('System - 菜单权限模块') diff --git a/src/modules/system/menu/menu.dto.ts b/src/modules/system/menu/menu.dto.ts index 4d734e5..da2b024 100644 --- a/src/modules/system/menu/menu.dto.ts +++ b/src/modules/system/menu/menu.dto.ts @@ -7,7 +7,7 @@ import { IsString, Min, MinLength, - ValidateIf, + ValidateIf } from 'class-validator'; export class MenuDto { diff --git a/src/modules/system/menu/menu.entity.ts b/src/modules/system/menu/menu.entity.ts index e31a638..938c497 100644 --- a/src/modules/system/menu/menu.entity.ts +++ b/src/modules/system/menu/menu.entity.ts @@ -49,7 +49,7 @@ export class MenuEntity extends CommonEntity { status: number; @ManyToMany(() => RoleEntity, role => role.menus, { - onDelete: 'CASCADE', + onDelete: 'CASCADE' }) roles: Relation; } diff --git a/src/modules/system/menu/menu.module.ts b/src/modules/system/menu/menu.module.ts index bfe18d5..03aa7c2 100644 --- a/src/modules/system/menu/menu.module.ts +++ b/src/modules/system/menu/menu.module.ts @@ -15,6 +15,6 @@ const providers = [MenuService, SseService]; imports: [TypeOrmModule.forFeature([MenuEntity]), forwardRef(() => RoleModule)], controllers: [MenuController], providers: [...providers], - exports: [TypeOrmModule, ...providers], + exports: [TypeOrmModule, ...providers] }) export class MenuModule {} diff --git a/src/modules/system/menu/menu.service.ts b/src/modules/system/menu/menu.service.ts index 9adaf75..b156714 100644 --- a/src/modules/system/menu/menu.service.ts +++ b/src/modules/system/menu/menu.service.ts @@ -39,9 +39,9 @@ export class MenuService { ...(path && { path: Like(`%${path}%`) }), ...(permission && { permission: Like(`%${permission}%`) }), ...(component && { component: Like(`%${component}%`) }), - ...(isNumber(status) ? { status } : null), + ...(isNumber(status) ? { status } : null) }, - order: { orderNo: 'ASC' }, + order: { orderNo: 'ASC' } }); const menuList = generatorMenu(menus); @@ -166,7 +166,7 @@ export class MenuService { if (this.roleService.hasAdminRole(roleIds)) { result = await this.menuRepository.findBy({ permission: Not(IsNull()), - type: In([1, 2]), + type: In([1, 2]) }); } else { if (isEmpty(roleIds)) return permission; @@ -237,9 +237,9 @@ export class MenuService { return !!(await this.menuRepository.findOne({ where: { roles: { - id, - }, - }, + id + } + } })); } } diff --git a/src/modules/system/online/online.controller.ts b/src/modules/system/online/online.controller.ts index e1069cd..22c96f8 100644 --- a/src/modules/system/online/online.controller.ts +++ b/src/modules/system/online/online.controller.ts @@ -16,7 +16,7 @@ import { OnlineService } from './online.service'; export const permissions = definePermission('system:online', { LIST: 'list', - KICK: 'kick', + KICK: 'kick' } as const); @ApiTags('System - 在线用户模块') diff --git a/src/modules/system/online/online.module.ts b/src/modules/system/online/online.module.ts index 793e71b..85ec082 100644 --- a/src/modules/system/online/online.module.ts +++ b/src/modules/system/online/online.module.ts @@ -18,10 +18,10 @@ const providers = [OnlineService]; forwardRef(() => SocketModule), AuthModule, UserModule, - RoleModule, + RoleModule ], controllers: [OnlineController], providers, - exports: [...providers], + exports: [...providers] }) export class OnlineModule {} diff --git a/src/modules/system/online/online.service.ts b/src/modules/system/online/online.service.ts index 8bf11fa..39ba7c2 100644 --- a/src/modules/system/online/online.service.ts +++ b/src/modules/system/online/online.service.ts @@ -90,7 +90,7 @@ export class OnlineService { time: e.created_at, os: `${u.os.name} ${u.os.version}`, browser: `${u.browser.name} ${u.browser.version}`, - disable: currentUid === e.id || e.id === rootUserId, + disable: currentUid === e.id || e.id === rootUserId }; }); } diff --git a/src/modules/system/param-config/param-config.controller.ts b/src/modules/system/param-config/param-config.controller.ts index b792eb3..baaddd7 100644 --- a/src/modules/system/param-config/param-config.controller.ts +++ b/src/modules/system/param-config/param-config.controller.ts @@ -16,7 +16,7 @@ export const permissions = definePermission('system:param-config', { CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiTags('System - 参数配置模块') diff --git a/src/modules/system/param-config/param-config.module.ts b/src/modules/system/param-config/param-config.module.ts index 0da5acf..e7797b4 100644 --- a/src/modules/system/param-config/param-config.module.ts +++ b/src/modules/system/param-config/param-config.module.ts @@ -11,6 +11,6 @@ const services = [ParamConfigService]; imports: [TypeOrmModule.forFeature([ParamConfigEntity])], controllers: [ParamConfigController], providers: [...services], - exports: [TypeOrmModule, ...services], + exports: [TypeOrmModule, ...services] }) export class ParamConfigModule {} diff --git a/src/modules/system/param-config/param-config.service.ts b/src/modules/system/param-config/param-config.service.ts index 926b5ae..7f185ff 100644 --- a/src/modules/system/param-config/param-config.service.ts +++ b/src/modules/system/param-config/param-config.service.ts @@ -24,13 +24,13 @@ export class ParamConfigService { async page({ page, pageSize, - name, + name }: ParamConfigQueryDto): Promise> { const queryBuilder = this.paramConfigRepository.createQueryBuilder('config'); if (name) { queryBuilder.where('config.name LIKE :name', { - name: `%${name}%`, + name: `%${name}%` }); } @@ -80,7 +80,7 @@ export class ParamConfigService { async findValueByKey(key: string): Promise { const result = await this.paramConfigRepository.findOne({ where: { key }, - select: ['value'], + select: ['value'] }); if (result) return result.value; diff --git a/src/modules/system/role/role.controller.ts b/src/modules/system/role/role.controller.ts index 05ba9c6..192ca13 100644 --- a/src/modules/system/role/role.controller.ts +++ b/src/modules/system/role/role.controller.ts @@ -6,7 +6,7 @@ import { Get, Post, Put, - Query, + Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; @@ -28,7 +28,7 @@ export const permissions = definePermission('system:role', { CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiTags('System - 角色模块') diff --git a/src/modules/system/role/role.entity.ts b/src/modules/system/role/role.entity.ts index 7a4854b..58d2159 100644 --- a/src/modules/system/role/role.entity.ts +++ b/src/modules/system/role/role.entity.ts @@ -37,7 +37,7 @@ export class RoleEntity extends CommonEntity { @JoinTable({ name: 'sys_role_menus', joinColumn: { name: 'role_id', referencedColumnName: 'id' }, - inverseJoinColumn: { name: 'menu_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'menu_id', referencedColumnName: 'id' } }) menus: Relation; } diff --git a/src/modules/system/role/role.module.ts b/src/modules/system/role/role.module.ts index b43e4a2..087fef6 100644 --- a/src/modules/system/role/role.module.ts +++ b/src/modules/system/role/role.module.ts @@ -15,6 +15,6 @@ const providers = [RoleService, SseService]; imports: [TypeOrmModule.forFeature([RoleEntity]), forwardRef(() => MenuModule)], controllers: [RoleController], providers: [...providers], - exports: [TypeOrmModule, ...providers], + exports: [TypeOrmModule, ...providers] }) export class RoleModule {} diff --git a/src/modules/system/role/role.service.ts b/src/modules/system/role/role.service.ts index 0908d4f..d8694cc 100644 --- a/src/modules/system/role/role.service.ts +++ b/src/modules/system/role/role.service.ts @@ -32,17 +32,17 @@ export class RoleService { pageSize, name, value, - status, + status }: RoleQueryDto): Promise> { const queryBuilder = this.roleRepository.createQueryBuilder('role').where({ ...(name ? { name: Like(`%${name}%`) } : null), ...(value ? { value: Like(`%${value}%`) } : null), - ...(isNumber(status) ? { status } : null), + ...(isNumber(status) ? { status } : null) }); return paginate(queryBuilder, { page, - pageSize, + pageSize }); } @@ -53,13 +53,13 @@ export class RoleService { const info = await this.roleRepository .createQueryBuilder('role') .where({ - id, + id }) .getOne(); const menus = await this.menuRepository.find({ where: { roles: { id } }, - select: ['id'], + select: ['id'] }); return { ...info, menuIds: menus.map(m => m.id) }; @@ -76,7 +76,7 @@ export class RoleService { async create({ menuIds, ...data }: RoleDto): Promise<{ roleId: number }> { const role = await this.roleRepository.save({ ...data, - menus: menuIds ? await this.menuRepository.findBy({ id: In(menuIds) }) : [], + menus: menuIds ? await this.menuRepository.findBy({ id: In(menuIds) }) : [] }); return { roleId: role.id }; @@ -92,7 +92,7 @@ export class RoleService { // using transaction await this.entityManager.transaction(async manager => { const menus = await this.menuRepository.find({ - where: { id: In(menuIds) }, + where: { id: In(menuIds) } }); const role = await this.roleRepository.findOne({ where: { id } }); @@ -108,8 +108,8 @@ export class RoleService { async getRoleIdsByUser(id: number): Promise { const roles = await this.roleRepository.find({ where: { - users: { id }, - }, + users: { id } + } }); if (!isEmpty(roles)) return roles.map(r => r.id); @@ -120,7 +120,7 @@ export class RoleService { async getRoleValues(ids: number[]): Promise { return ( await this.roleRepository.findBy({ - id: In(ids), + id: In(ids) }) ).map(r => r.value); } @@ -128,8 +128,8 @@ export class RoleService { async isAdminRoleByUser(uid: number): Promise { const roles = await this.roleRepository.find({ where: { - users: { id: uid }, - }, + users: { id: uid } + } }); if (!isEmpty(roles)) { @@ -149,9 +149,9 @@ export class RoleService { return this.roleRepository.exist({ where: { users: { - roles: { id }, - }, - }, + roles: { id } + } + } }); } } diff --git a/src/modules/system/serve/serve.module.ts b/src/modules/system/serve/serve.module.ts index a2963cc..4a2f421 100644 --- a/src/modules/system/serve/serve.module.ts +++ b/src/modules/system/serve/serve.module.ts @@ -11,6 +11,6 @@ const providers = [ServeService]; imports: [forwardRef(() => SystemModule)], controllers: [ServeController], providers: [...providers], - exports: [...providers], + exports: [...providers] }) export class ServeModule {} diff --git a/src/modules/system/serve/serve.service.ts b/src/modules/system/serve/serve.service.ts index b20c2f5..59b7ba1 100644 --- a/src/modules/system/serve/serve.service.ts +++ b/src/modules/system/serve/serve.service.ts @@ -15,7 +15,7 @@ export class ServeService { si.osInfo(), si.cpu(), si.currentLoad(), - si.mem(), + si.mem() ]) ).map((p: any) => p.value); @@ -36,7 +36,7 @@ export class ServeService { npmVersion: versions.npm, nodeVersion: versions.node, os: osinfo.platform, - arch: osinfo.arch, + arch: osinfo.arch }, cpu: { manufacturer: cpuinfo.manufacturer, @@ -49,15 +49,15 @@ export class ServeService { coresLoad: currentLoadinfo.cpus.map(e => { return { rawLoad: e.rawLoad, - rawLoadIdle: e.rawLoadIdle, + rawLoadIdle: e.rawLoadIdle }; - }), + }) }, disk: diskinfo, memory: { total: meminfo.total, - available: meminfo.available, - }, + available: meminfo.available + } }; } } diff --git a/src/modules/system/system.module.ts b/src/modules/system/system.module.ts index c919c1c..83d9189 100644 --- a/src/modules/system/system.module.ts +++ b/src/modules/system/system.module.ts @@ -26,7 +26,7 @@ const modules = [ LogModule, TaskModule, OnlineModule, - ServeModule, + ServeModule ]; @Module({ @@ -36,10 +36,10 @@ const modules = [ { path: 'system', module: SystemModule, - children: [...modules], - }, - ]), + children: [...modules] + } + ]) ], - exports: [...modules], + exports: [...modules] }) export class SystemModule {} diff --git a/src/modules/system/task/constant.ts b/src/modules/system/task/constant.ts index e32bf7a..0dac2d1 100644 --- a/src/modules/system/task/constant.ts +++ b/src/modules/system/task/constant.ts @@ -1,11 +1,11 @@ export enum TaskStatus { Disabled = 0, - Activited = 1, + Activited = 1 } export enum TaskType { Cron = 0, - Interval = 1, + Interval = 1 } export const SYS_TASK_QUEUE_NAME = 'system:sys-task'; diff --git a/src/modules/system/task/task.controller.ts b/src/modules/system/task/task.controller.ts index 975c0b5..e0c7a92 100644 --- a/src/modules/system/task/task.controller.ts +++ b/src/modules/system/task/task.controller.ts @@ -20,7 +20,7 @@ export const permissions = definePermission('system:task', { ONCE: 'once', START: 'start', - STOP: 'stop', + STOP: 'stop' } as const); @ApiTags('System - 任务调度模块') diff --git a/src/modules/system/task/task.dto.ts b/src/modules/system/task/task.dto.ts index 0d9af8b..62ab7e6 100644 --- a/src/modules/system/task/task.dto.ts +++ b/src/modules/system/task/task.dto.ts @@ -13,7 +13,7 @@ import { ValidateIf, ValidationArguments, ValidatorConstraint, - ValidatorConstraintInterface, + ValidatorConstraintInterface } from 'class-validator'; import * as parser from 'cron-parser'; import { isEmpty } from 'lodash'; @@ -70,7 +70,7 @@ export class TaskDto { endTime: string; @ApiPropertyOptional({ - description: '限制执行次数,负数则无限制', + description: '限制执行次数,负数则无限制' }) @IsOptional() @IsInt() diff --git a/src/modules/system/task/task.module.ts b/src/modules/system/task/task.module.ts index 99ce40b..5aba9a3 100644 --- a/src/modules/system/task/task.module.ts +++ b/src/modules/system/task/task.module.ts @@ -24,14 +24,14 @@ const providers = [TaskService, TaskConsumer]; name: SYS_TASK_QUEUE_NAME, useFactory: (configService: ConfigService) => ({ redis: configService.get('redis'), - prefix: SYS_TASK_QUEUE_PREFIX, + prefix: SYS_TASK_QUEUE_PREFIX }), - inject: [ConfigService], + inject: [ConfigService] }), - LogModule, + LogModule ], controllers: [TaskController], providers: [...providers], - exports: [TypeOrmModule, ...providers], + exports: [TypeOrmModule, ...providers] }) export class TaskModule {} diff --git a/src/modules/system/task/task.service.ts b/src/modules/system/task/task.service.ts index dddf2ed..aa59bf5 100644 --- a/src/modules/system/task/task.service.ts +++ b/src/modules/system/task/task.service.ts @@ -5,7 +5,7 @@ import { Injectable, Logger, NotFoundException, - OnModuleInit, + OnModuleInit } from '@nestjs/common'; import { ModuleRef, Reflector } from '@nestjs/core'; import { UnknownElementException } from '@nestjs/core/errors/exceptions/unknown-element.exception'; @@ -69,7 +69,7 @@ export class TaskService implements OnModuleInit { 'failed', 'paused', 'waiting', - 'completed', + 'completed' ]); jobs.forEach(j => { j.remove(); @@ -90,7 +90,7 @@ export class TaskService implements OnModuleInit { name, service, type, - status, + status }: TaskQueryDto): Promise> { const queryBuilder = this.taskRepository .createQueryBuilder('task') @@ -98,7 +98,7 @@ export class TaskService implements OnModuleInit { ...(name ? { name: Like(`%${name}%`) } : null), ...(service ? { service: Like(`%${service}%`) } : null), ...(type ? { type } : null), - ...(isNumber(status) ? { status } : null), + ...(isNumber(status) ? { status } : null) }) .orderBy('task.id', 'ASC'); @@ -166,12 +166,12 @@ export class TaskService implements OnModuleInit { if (task.type === 1) { // 间隔 Repeat every millis (cron setting cannot be used together with this setting.) repeat = { - every: task.every, + every: task.every }; } else { // cron repeat = { - cron: task.cron, + cron: task.cron }; // Start date when the repeat job should start repeating (only with cron). if (task.startTime) repeat.startDate = task.startTime; @@ -187,13 +187,13 @@ export class TaskService implements OnModuleInit { if (job && job.opts) { await this.taskRepository.update(task.id, { jobOpts: JSON.stringify(job.opts.repeat), - status: 1, + status: 1 }); } else { // update status to 0,标识暂停任务,因为启动失败 await job?.remove(); await this.taskRepository.update(task.id, { - status: TaskStatus.Disabled, + status: TaskStatus.Disabled }); throw new BadRequestException('Task Start failed'); } @@ -208,7 +208,7 @@ export class TaskService implements OnModuleInit { const exist = await this.existJob(task.id.toString()); if (!exist) { await this.taskRepository.update(task.id, { - status: TaskStatus.Disabled, + status: TaskStatus.Disabled }); return; } @@ -218,7 +218,7 @@ export class TaskService implements OnModuleInit { 'failed', 'paused', 'waiting', - 'completed', + 'completed' ]); jobs .filter(j => j.data.id === task.id) @@ -301,7 +301,7 @@ export class TaskService implements OnModuleInit { if (!methodName) throw new BadRequestException('serviceName define BadRequestException'); const service = await this.moduleRef.get(serviceName, { - strict: false, + strict: false }); // 安全注解检查 diff --git a/src/modules/tasks/tasks.module.ts b/src/modules/tasks/tasks.module.ts index dd7561d..c422124 100644 --- a/src/modules/tasks/tasks.module.ts +++ b/src/modules/tasks/tasks.module.ts @@ -21,7 +21,7 @@ function createAliasProviders(): ExistingProvider[] { for (const p of providers) { aliasProviders.push({ provide: p.name, - useExisting: p, + useExisting: p }); } return aliasProviders; @@ -40,7 +40,7 @@ export class TasksModule { module: TasksModule, imports: [SystemModule, LogModule], providers: [...providers, ...aliasProviders], - exports: aliasProviders, + exports: aliasProviders }; } } diff --git a/src/modules/todo/todo.controller.ts b/src/modules/todo/todo.controller.ts index ca415d0..d1802a0 100644 --- a/src/modules/todo/todo.controller.ts +++ b/src/modules/todo/todo.controller.ts @@ -19,7 +19,7 @@ export const permissions = definePermission('todo', { CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiTags('Business - Todo模块') diff --git a/src/modules/todo/todo.module.ts b/src/modules/todo/todo.module.ts index 78ee8ff..28a7b3e 100644 --- a/src/modules/todo/todo.module.ts +++ b/src/modules/todo/todo.module.ts @@ -11,6 +11,6 @@ const services = [TodoService]; imports: [TypeOrmModule.forFeature([TodoEntity])], controllers: [TodoController], providers: [...services], - exports: [TypeOrmModule, ...services], + exports: [TypeOrmModule, ...services] }) export class TodoModule {} diff --git a/src/modules/tools/email/email.module.ts b/src/modules/tools/email/email.module.ts index 76e689a..504c1de 100644 --- a/src/modules/tools/email/email.module.ts +++ b/src/modules/tools/email/email.module.ts @@ -4,6 +4,6 @@ import { EmailController } from './email.controller'; @Module({ imports: [], - controllers: [EmailController], + controllers: [EmailController] }) export class EmailModule {} diff --git a/src/modules/tools/storage/storage.controller.ts b/src/modules/tools/storage/storage.controller.ts index c7a13a3..0be18f2 100644 --- a/src/modules/tools/storage/storage.controller.ts +++ b/src/modules/tools/storage/storage.controller.ts @@ -15,7 +15,7 @@ import { StorageService } from './storage.service'; export const permissions = definePermission('tool:storage', { LIST: 'list', - DELETE: 'delete', + DELETE: 'delete' } as const); @ApiTags('Tools - 存储模块') diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index 584ec59..7680f12 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -13,7 +13,7 @@ export class Storage extends CommonEntity { type: 'varchar', length: 200, nullable: true, - comment: '真实文件名', + comment: '真实文件名' }) @ApiProperty({ description: '真实文件名' }) fileName: string; diff --git a/src/modules/tools/storage/storage.module.ts b/src/modules/tools/storage/storage.module.ts index e74ca3f..3324cf3 100644 --- a/src/modules/tools/storage/storage.module.ts +++ b/src/modules/tools/storage/storage.module.ts @@ -13,6 +13,6 @@ const services = [StorageService]; imports: [TypeOrmModule.forFeature([Storage, UserEntity])], controllers: [StorageController], providers: [...services], - exports: [TypeOrmModule, ...services], + exports: [TypeOrmModule, ...services] }) export class StorageModule {} diff --git a/src/modules/tools/storage/storage.service.ts b/src/modules/tools/storage/storage.service.ts index 99c5fb9..5a8cbf3 100644 --- a/src/modules/tools/storage/storage.service.ts +++ b/src/modules/tools/storage/storage.service.ts @@ -24,7 +24,7 @@ export class StorageService { async create(dto: StorageCreateDto, userId: number): Promise { await this.storageRepository.save({ ...dto, - userId, + userId }); } @@ -48,7 +48,7 @@ export class StorageService { size, extName, time, - username, + username }: StoragePageDto): Promise> { const queryBuilder = this.storageRepository .createQueryBuilder('storage') @@ -60,15 +60,15 @@ export class StorageService { ...(size && { size: Between(size[0], size[1]) }), ...(time && { createdAt: Between(time[0], time[1]) }), ...(username && { - userId: await (await this.userRepository.findOneBy({ username })).id, - }), + userId: await (await this.userRepository.findOneBy({ username })).id + }) }) .orderBy('storage.created_at', 'DESC'); const { items, ...rest } = await paginateRaw(queryBuilder, { page, pageSize, - paginationType: PaginationTypeEnum.LIMIT_AND_OFFSET, + paginationType: PaginationTypeEnum.LIMIT_AND_OFFSET }); function formatResult(result: Storage[]) { @@ -81,14 +81,14 @@ export class StorageService { type: e.storage_type, size: e.storage_size, createdAt: e.storage_created_at, - username: e.user_username, + username: e.user_username }; }); } return { items: formatResult(items), - ...rest, + ...rest }; } diff --git a/src/modules/tools/tools.module.ts b/src/modules/tools/tools.module.ts index 9e8b5e0..efbd337 100644 --- a/src/modules/tools/tools.module.ts +++ b/src/modules/tools/tools.module.ts @@ -15,10 +15,10 @@ const modules = [StorageModule, EmailModule, UploadModule]; { path: 'tools', module: ToolsModule, - children: [...modules], - }, - ]), + children: [...modules] + } + ]) ], - exports: [...modules], + exports: [...modules] }) export class ToolsModule {} diff --git a/src/modules/tools/upload/file.constraint.ts b/src/modules/tools/upload/file.constraint.ts index 3adcf49..ab3caf2 100644 --- a/src/modules/tools/upload/file.constraint.ts +++ b/src/modules/tools/upload/file.constraint.ts @@ -4,7 +4,7 @@ import { ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, - registerDecorator, + registerDecorator } from 'class-validator'; import { has, isArray } from 'lodash'; @@ -45,7 +45,7 @@ export function IsFile(limits?: FileLimit, validationOptions?: ValidationOptions propertyName, options: validationOptions, constraints: [limits], - validator: FileConstraint, + validator: FileConstraint }); }; } diff --git a/src/modules/tools/upload/upload.controller.ts b/src/modules/tools/upload/upload.controller.ts index e501d68..7c44644 100644 --- a/src/modules/tools/upload/upload.controller.ts +++ b/src/modules/tools/upload/upload.controller.ts @@ -11,7 +11,7 @@ import { FileUploadDto } from './upload.dto'; import { UploadService } from './upload.service'; export const permissions = definePermission('upload', { - UPLOAD: 'upload', + UPLOAD: 'upload' } as const); @ApiSecurityAuth() @@ -25,7 +25,7 @@ export class UploadController { @ApiOperation({ summary: '上传' }) @ApiConsumes('multipart/form-data') @ApiBody({ - type: FileUploadDto, + type: FileUploadDto }) async upload(@Req() req: FastifyRequest, @AuthUser() user: IAuthUser) { if (!req.isMultipart()) throw new BadRequestException('Request is not multipart'); @@ -41,7 +41,7 @@ export class UploadController { const path = await this.uploadService.saveFile(file, user.uid); return { - filename: path, + filename: path }; } catch (error) { console.log(error); diff --git a/src/modules/tools/upload/upload.dto.ts b/src/modules/tools/upload/upload.dto.ts index 1bf0400..dc28e7c 100644 --- a/src/modules/tools/upload/upload.dto.ts +++ b/src/modules/tools/upload/upload.dto.ts @@ -11,10 +11,10 @@ export class FileUploadDto { @IsFile( { mimetypes: ['image/png', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'], - fileSize: 1024 * 1024 * 10, + fileSize: 1024 * 1024 * 10 }, { - message: '文件类型不正确', + message: '文件类型不正确' } ) file: MultipartFile; diff --git a/src/modules/tools/upload/upload.module.ts b/src/modules/tools/upload/upload.module.ts index b5a83df..4f14cc1 100644 --- a/src/modules/tools/upload/upload.module.ts +++ b/src/modules/tools/upload/upload.module.ts @@ -11,6 +11,6 @@ const services = [UploadService]; imports: [forwardRef(() => StorageModule)], controllers: [UploadController], providers: [...services], - exports: [...services], + exports: [...services] }) export class UploadModule {} diff --git a/src/modules/tools/upload/upload.service.ts b/src/modules/tools/upload/upload.service.ts index 7e42663..605f977 100644 --- a/src/modules/tools/upload/upload.service.ts +++ b/src/modules/tools/upload/upload.service.ts @@ -12,7 +12,7 @@ import { getFilePath, getFileType, getSize, - saveLocalFile, + saveLocalFile } from '~/utils/file.util'; @Injectable() @@ -44,7 +44,7 @@ export class UploadService { path, type, size, - userId, + userId }); return path; diff --git a/src/modules/user/constant.ts b/src/modules/user/constant.ts index 2510ff6..82e2c34 100644 --- a/src/modules/user/constant.ts +++ b/src/modules/user/constant.ts @@ -1,4 +1,4 @@ export enum UserStatus { Disable = 0, - Enabled = 1, + Enabled = 1 } diff --git a/src/modules/user/dto/password.dto.ts b/src/modules/user/dto/password.dto.ts index 6b5e30c..ea5fedb 100644 --- a/src/modules/user/dto/password.dto.ts +++ b/src/modules/user/dto/password.dto.ts @@ -11,7 +11,7 @@ export class PasswordUpdateDto { @ApiProperty({ description: '新密码' }) @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { - message: '密码必须包含数字、字母,长度为6-16', + message: '密码必须包含数字、字母,长度为6-16' }) newPassword: string; } @@ -24,7 +24,7 @@ export class UserPasswordDto { @ApiProperty({ description: '更改后的密码' }) @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { - message: '密码格式不正确', + message: '密码格式不正确' }) password: string; } diff --git a/src/modules/user/dto/user.dto.ts b/src/modules/user/dto/user.dto.ts index 17bb5b6..3b2c3c7 100644 --- a/src/modules/user/dto/user.dto.ts +++ b/src/modules/user/dto/user.dto.ts @@ -12,7 +12,7 @@ import { Matches, MaxLength, MinLength, - ValidateIf, + ValidateIf } from 'class-validator'; import { isEmpty } from 'lodash'; @@ -34,7 +34,7 @@ export class UserDto { @ApiProperty({ description: '登录密码', example: 'a123456' }) @IsOptional() @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { - message: '密码必须包含数字、字母,长度为6-16', + message: '密码必须包含数字、字母,长度为6-16' }) password: string; diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index d462edb..fca5e25 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -7,7 +7,7 @@ import { ParseArrayPipe, Post, Put, - Query, + Query } from '@nestjs/common'; import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; @@ -31,7 +31,7 @@ export const permissions = definePermission('system:user', { DELETE: 'delete', PASSWORD_UPDATE: 'password:update', - PASSWORD_RESET: 'pass:reset', + PASSWORD_RESET: 'pass:reset' } as const); @ApiTags('System - 用户模块') @@ -78,7 +78,7 @@ export class UserController { @ApiParam({ name: 'id', type: String, - schema: { oneOf: [{ type: 'string' }, { type: 'number' }] }, + schema: { oneOf: [{ type: 'string' }, { type: 'number' }] } }) @Perm(permissions.DELETE) async delete( diff --git a/src/modules/user/user.entity.ts b/src/modules/user/user.entity.ts index 7cd2031..3df5839 100644 --- a/src/modules/user/user.entity.ts +++ b/src/modules/user/user.entity.ts @@ -7,7 +7,7 @@ import { ManyToMany, ManyToOne, OneToMany, - Relation, + Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; @@ -54,7 +54,7 @@ export class UserEntity extends CommonEntity { @JoinTable({ name: 'sys_user_roles', joinColumn: { name: 'user_id', referencedColumnName: 'id' }, - inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' } }) roles: Relation; @@ -63,7 +63,7 @@ export class UserEntity extends CommonEntity { dept: Relation; @OneToMany(() => AccessTokenEntity, accessToken => accessToken.user, { - cascade: true, + cascade: true }) accessTokens: Relation; } diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts index 928c52b..621db3a 100644 --- a/src/modules/user/user.module.ts +++ b/src/modules/user/user.module.ts @@ -16,6 +16,6 @@ const providers = [UserService]; imports: [TypeOrmModule.forFeature([UserEntity]), RoleModule, MenuModule, ParamConfigModule], controllers: [UserController], providers: [...providers], - exports: [TypeOrmModule, ...providers], + exports: [TypeOrmModule, ...providers] }) export class UserModule {} diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 36fa5d8..a3a2867 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -48,7 +48,7 @@ export class UserService { .createQueryBuilder('user') .where({ id, - status: UserStatus.Enabled, + status: UserStatus.Enabled }) .getOne(); } @@ -58,7 +58,7 @@ export class UserService { .createQueryBuilder('user') .where({ username, - status: UserStatus.Enabled, + status: UserStatus.Enabled }) .getOne(); } @@ -94,7 +94,7 @@ export class UserService { ...(info.email ? { email: info.email } : null), ...(info.phone ? { phone: info.phone } : null), ...(info.qq ? { qq: info.qq } : null), - ...(info.remark ? { remark: info.remark } : null), + ...(info.remark ? { remark: info.remark } : null) }; if (!info.avatar && info.qq) { @@ -137,7 +137,7 @@ export class UserService { */ async create({ username, password, roleIds, deptId, ...data }: UserDto): Promise { const exists = await this.userRepository.findOneBy({ - username, + username }); if (!isEmpty(exists)) throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS); @@ -156,7 +156,7 @@ export class UserService { ...data, psalt: salt, roles: await this.roleRepository.findBy({ id: In(roleIds) }), - dept: await DeptEntity.findOneBy({ id: deptId }), + dept: await DeptEntity.findOneBy({ id: deptId }) }); const result = await manager.save(u); @@ -176,7 +176,7 @@ export class UserService { await manager.update(UserEntity, id, { ...data, - status, + status }); const user = await this.userRepository @@ -234,7 +234,7 @@ export class UserService { */ async findRootUserId(): Promise { const user = await this.userRepository.findOneBy({ - roles: { id: ROOT_ROLE_ID }, + roles: { id: ROOT_ROLE_ID } }); return user.id; } @@ -249,7 +249,7 @@ export class UserService { nickname, deptId, email, - status, + status }: UserQueryDto): Promise> { const queryBuilder = this.userRepository .createQueryBuilder('user') @@ -260,14 +260,14 @@ export class UserService { ...(username ? { username: Like(`%${username}%`) } : null), ...(nickname ? { nickname: Like(`%${nickname}%`) } : null), ...(email ? { email: Like(`%${email}%`) } : null), - ...(isNumber(status) ? { status } : null), + ...(isNumber(status) ? { status } : null) }); if (deptId) queryBuilder.andWhere('dept.id = :deptId', { deptId }); return paginate(queryBuilder, { page, - pageSize, + pageSize }); } @@ -323,7 +323,7 @@ export class UserService { */ async register({ username, ...data }: RegisterDto): Promise { const exists = await this.userRepository.findOneBy({ - username, + username }); if (!isEmpty(exists)) throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS); @@ -336,7 +336,7 @@ export class UserService { username, password, status: 1, - psalt: salt, + psalt: salt }); const user = await manager.save(u); diff --git a/src/setup-swagger.ts b/src/setup-swagger.ts index d80d52e..8b49b32 100644 --- a/src/setup-swagger.ts +++ b/src/setup-swagger.ts @@ -27,12 +27,12 @@ export function setupSwagger( description: 'Auth', type: 'apiKey', in: 'header', - name: 'Authorization', + name: 'Authorization' }); const document = SwaggerModule.createDocument(app, documentBuilder.build(), { ignoreGlobalPrefix: false, - extraModels: [CommonEntity, ResOp, Pagination, TreeResult], + extraModels: [CommonEntity, ResOp, Pagination, TreeResult] }); SwaggerModule.setup(path, app, document); diff --git a/src/shared/database/constraints/entity-exist.constraint.ts b/src/shared/database/constraints/entity-exist.constraint.ts index 3f482b7..37702b1 100644 --- a/src/shared/database/constraints/entity-exist.constraint.ts +++ b/src/shared/database/constraints/entity-exist.constraint.ts @@ -4,7 +4,7 @@ import { ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, - registerDecorator, + registerDecorator } from 'class-validator'; import { DataSource, ObjectType, Repository } from 'typeorm'; @@ -74,7 +74,7 @@ function IsEntityExist( propertyName, options: validationOptions, constraints: [condition], - validator: EntityExistConstraint, + validator: EntityExistConstraint }); }; } diff --git a/src/shared/database/constraints/unique.constraint.ts b/src/shared/database/constraints/unique.constraint.ts index 2f3450b..39d4fec 100644 --- a/src/shared/database/constraints/unique.constraint.ts +++ b/src/shared/database/constraints/unique.constraint.ts @@ -4,7 +4,7 @@ import { ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, - registerDecorator, + registerDecorator } from 'class-validator'; import { isNil, merge } from 'lodash'; import { DataSource, ObjectType } from 'typeorm'; @@ -26,13 +26,13 @@ export class UniqueConstraint implements ValidatorConstraintInterface { async validate(value: any, args: ValidationArguments) { // 获取要验证的模型和字段 const config: Omit = { - field: args.property, + field: args.property }; const condition = ('entity' in args.constraints[0] ? merge(config, args.constraints[0]) : { ...config, - entity: args.constraints[0], + entity: args.constraints[0] }) as unknown as Required; if (!condition.entity) return false; try { @@ -40,7 +40,7 @@ export class UniqueConstraint implements ValidatorConstraintInterface { const repo = this.dataSource.getRepository(condition.entity); return isNil( await repo.findOne({ - where: { [condition.field]: value }, + where: { [condition.field]: value } }) ); } catch (err) { @@ -82,7 +82,7 @@ function IsUnique(params: ObjectType | Condition, validationOptions?: Valid propertyName, options: validationOptions, constraints: [params], - validator: UniqueConstraint, + validator: UniqueConstraint }); }; } diff --git a/src/shared/database/database.module.ts b/src/shared/database/database.module.ts index 528df0f..eeee523 100644 --- a/src/shared/database/database.module.ts +++ b/src/shared/database/database.module.ts @@ -33,7 +33,7 @@ const providers = [EntityExistConstraint, UniqueConstraint]; ...configService.get('database'), autoLoadEntities: true, logging: loggerOptions, - logger: new TypeORMLogger(loggerOptions), + logger: new TypeORMLogger(loggerOptions) }; }, // dataSource receives the configured DataSourceOptions @@ -41,10 +41,10 @@ const providers = [EntityExistConstraint, UniqueConstraint]; dataSourceFactory: async options => { const dataSource = await new DataSource(options).initialize(); return dataSource; - }, - }), + } + }) ], providers, - exports: providers, + exports: providers }) export class DatabaseModule {} diff --git a/src/shared/helper/cron.service.ts b/src/shared/helper/cron.service.ts index 09fb895..c53b4ea 100644 --- a/src/shared/helper/cron.service.ts +++ b/src/shared/helper/cron.service.ts @@ -20,8 +20,8 @@ export class CronService { const expiredTokens = await AccessTokenEntity.find({ where: { - expired_at: LessThan(new Date()), - }, + expired_at: LessThan(new Date()) + } }); let deleteCount = 0; diff --git a/src/shared/helper/helper.module.ts b/src/shared/helper/helper.module.ts index dc8e166..7ce4fc5 100644 --- a/src/shared/helper/helper.module.ts +++ b/src/shared/helper/helper.module.ts @@ -9,6 +9,6 @@ const providers: Provider[] = [CronService, QQService]; @Module({ imports: [], providers, - exports: providers, + exports: providers }) export class HelperModule {} diff --git a/src/shared/logger/logger.module.ts b/src/shared/logger/logger.module.ts index 0cd7a70..385b778 100644 --- a/src/shared/logger/logger.module.ts +++ b/src/shared/logger/logger.module.ts @@ -9,7 +9,7 @@ export class LoggerModule { global: true, module: LoggerModule, providers: [LoggerService], - exports: [LoggerService], + exports: [LoggerService] }; } } diff --git a/src/shared/logger/logger.service.ts b/src/shared/logger/logger.service.ts index ce477cd..bb9fd37 100644 --- a/src/shared/logger/logger.service.ts +++ b/src/shared/logger/logger.service.ts @@ -14,7 +14,7 @@ export enum LogLevel { WARN = 'warn', INFO = 'info', DEBUG = 'debug', - VERBOSE = 'verbose', + VERBOSE = 'verbose' } @Injectable() @@ -49,7 +49,7 @@ export class LoggerService extends ConsoleLogger { datePattern: 'YYYY-MM-DD', maxFiles: this.maxFiles, format: format.combine(format.timestamp(), format.json()), - auditFile: 'logs/.audit/app.json', + auditFile: 'logs/.audit/app.json' }), new transports.DailyRotateFile({ level: LogLevel.ERROR, @@ -57,9 +57,9 @@ export class LoggerService extends ConsoleLogger { datePattern: 'YYYY-MM-DD', maxFiles: this.maxFiles, format: format.combine(format.timestamp(), format.json()), - auditFile: 'logs/.audit/app-error.json', - }), - ], + auditFile: 'logs/.audit/app-error.json' + }) + ] }); // if (isDev) { @@ -105,7 +105,7 @@ export class LoggerService extends ConsoleLogger { const hasStack = !!context; this.winstonLogger.log(LogLevel.ERROR, { context: hasStack ? context : stack, - message: hasStack ? new Error(message) : message, + message: hasStack ? new Error(message) : message }); } } diff --git a/src/shared/mailer/mailer.module.ts b/src/shared/mailer/mailer.module.ts index 24d1e1f..25ba1d0 100644 --- a/src/shared/mailer/mailer.module.ts +++ b/src/shared/mailer/mailer.module.ts @@ -20,21 +20,21 @@ const providers: Provider[] = [MailerService]; defaults: { from: { name: configService.get('app').name, - address: configService.get('mailer').auth.user, - }, + address: configService.get('mailer').auth.user + } }, template: { dir: join(__dirname, '..', '..', '/assets/templates'), adapter: new HandlebarsAdapter(), options: { - strict: true, - }, - }, + strict: true + } + } }), - inject: [ConfigService], - }), + inject: [ConfigService] + }) ], providers, - exports: providers, + exports: providers }) export class MailerModule {} diff --git a/src/shared/mailer/mailer.service.ts b/src/shared/mailer/mailer.service.ts index 2b5fcf3..cd6b90c 100644 --- a/src/shared/mailer/mailer.service.ts +++ b/src/shared/mailer/mailer.service.ts @@ -79,13 +79,13 @@ export class MailerService { return this.mailerService.sendMail({ to, subject, - text: content, + text: content }); } else { return this.mailerService.sendMail({ to, subject, - html: content, + html: content }); } } @@ -99,8 +99,8 @@ export class MailerService { subject, template: './verification-code-zh', context: { - code, - }, + code + } }); } catch (error) { console.log(error); @@ -109,7 +109,7 @@ export class MailerService { return { to, - code, + code }; } diff --git a/src/shared/redis/cache.service.ts b/src/shared/redis/cache.service.ts index 6edd80e..d8ccb54 100644 --- a/src/shared/redis/cache.service.ts +++ b/src/shared/redis/cache.service.ts @@ -46,7 +46,7 @@ export class CacheService { if (this._emitter) return this._emitter; this._emitter = new Emitter(this.redisClient, { - key: RedisIoAdapterKey, + key: RedisIoAdapterKey }); return this._emitter; diff --git a/src/shared/redis/redis-subpub.ts b/src/shared/redis/redis-subpub.ts index 4f4934c..3a8d07f 100644 --- a/src/shared/redis/redis-subpub.ts +++ b/src/shared/redis/redis-subpub.ts @@ -15,7 +15,7 @@ export class RedisSubPub { public init() { const redisOptions: RedisOptions = { host: this.redisConfig.host, - port: this.redisConfig.port, + port: this.redisConfig.port }; if (this.redisConfig.password) redisOptions.password = this.redisConfig.password; diff --git a/src/shared/redis/redis.module.ts b/src/shared/redis/redis.module.ts index b227914..8fa4c90 100644 --- a/src/shared/redis/redis.module.ts +++ b/src/shared/redis/redis.module.ts @@ -21,9 +21,9 @@ const providers: Provider[] = [ const redisOptions: RedisOptions = configService.get('redis'); return new RedisSubPub(redisOptions); }, - inject: [ConfigService], + inject: [ConfigService] }, - RedisPubSubService, + RedisPubSubService ]; @Global() @@ -39,22 +39,22 @@ const providers: Provider[] = [ isGlobal: true, store: redisStore, isCacheableValue: () => true, - ...redisOptions, + ...redisOptions }; }, - inject: [ConfigService], + inject: [ConfigService] }), // redis NestRedisModule.forRootAsync({ imports: [ConfigModule], useFactory: (configService: ConfigService) => ({ readyLog: true, - config: configService.get('redis'), + config: configService.get('redis') }), - inject: [ConfigService], - }), + inject: [ConfigService] + }) ], providers, - exports: [...providers, CacheModule], + exports: [...providers, CacheModule] }) export class RedisModule {} diff --git a/src/shared/shared.module.ts b/src/shared/shared.module.ts index f0f222f..5eaf868 100644 --- a/src/shared/shared.module.ts +++ b/src/shared/shared.module.ts @@ -25,8 +25,8 @@ import { RedisModule } from './redis/redis.module'; ThrottlerModule.forRoot([ { limit: 3, - ttl: 60000, - }, + ttl: 60000 + } ]), EventEmitterModule.forRoot({ wildcard: true, @@ -35,15 +35,15 @@ import { RedisModule } from './redis/redis.module'; removeListener: false, maxListeners: 20, verboseMemoryLeak: isDev, - ignoreErrors: false, + ignoreErrors: false }), // redis RedisModule, // mailer MailerModule, // helper - HelperModule, + HelperModule ], - exports: [HttpModule, MailerModule, RedisModule, HelperModule], + exports: [HttpModule, MailerModule, RedisModule, HelperModule] }) export class SharedModule {} diff --git a/src/socket/base.gateway.ts b/src/socket/base.gateway.ts index 8ce4e34..920e573 100644 --- a/src/socket/base.gateway.ts +++ b/src/socket/base.gateway.ts @@ -7,7 +7,7 @@ export abstract class BaseGateway { return { type, data: message, - code, + code }; } diff --git a/src/socket/business-event.constant.ts b/src/socket/business-event.constant.ts index 1864d6c..5d0f133 100644 --- a/src/socket/business-event.constant.ts +++ b/src/socket/business-event.constant.ts @@ -7,5 +7,5 @@ export enum BusinessEvents { // 用户上线事件 USER_ONLINE = 'USER_ONLINE', USER_OFFLINE = 'USER_OFFLINE', - USER_KICK = 'USER_KICK', + USER_KICK = 'USER_KICK' } diff --git a/src/socket/events/admin.gateway.ts b/src/socket/events/admin.gateway.ts index 2b8c810..26007b9 100644 --- a/src/socket/events/admin.gateway.ts +++ b/src/socket/events/admin.gateway.ts @@ -4,7 +4,7 @@ import { OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway, - WebSocketServer, + WebSocketServer } from '@nestjs/websockets'; import { Server } from 'socket.io'; diff --git a/src/socket/events/web.gateway.ts b/src/socket/events/web.gateway.ts index 7d46d32..c1250ae 100644 --- a/src/socket/events/web.gateway.ts +++ b/src/socket/events/web.gateway.ts @@ -4,7 +4,7 @@ import { OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway, - WebSocketServer, + WebSocketServer } from '@nestjs/websockets'; import { Server } from 'socket.io'; diff --git a/src/socket/socket.module.ts b/src/socket/socket.module.ts index 0affa35..adadb4b 100644 --- a/src/socket/socket.module.ts +++ b/src/socket/socket.module.ts @@ -11,6 +11,6 @@ const providers: Provider[] = [AdminEventsGateway, WebEventsGateway]; @Module({ imports: [forwardRef(() => SystemModule), AuthModule], providers, - exports: [...providers], + exports: [...providers] }) export class SocketModule {} diff --git a/src/utils/captcha.util.ts b/src/utils/captcha.util.ts index 65ccf3d..443b540 100644 --- a/src/utils/captcha.util.ts +++ b/src/utils/captcha.util.ts @@ -9,7 +9,7 @@ export function createCaptcha() { background: '#eee', fontSize: 50, width: 110, - height: 38, + height: 38 }); } diff --git a/src/utils/crypto.util.ts b/src/utils/crypto.util.ts index 58dd902..1c072dd 100644 --- a/src/utils/crypto.util.ts +++ b/src/utils/crypto.util.ts @@ -8,7 +8,7 @@ export function aesEncrypt(data) { const enc = CryptoJS.AES.encrypt(data, key, { iv, mode: CryptoJS.mode.CBC, - padding: CryptoJS.pad.Pkcs7, + padding: CryptoJS.pad.Pkcs7 }); return enc.toString(); } @@ -18,7 +18,7 @@ export function aesDecrypt(data) { const dec = CryptoJS.AES.decrypt(data, key, { iv, mode: CryptoJS.mode.CBC, - padding: CryptoJS.pad.Pkcs7, + padding: CryptoJS.pad.Pkcs7 }); return dec.toString(CryptoJS.enc.Utf8); } diff --git a/src/utils/file.util.ts b/src/utils/file.util.ts index 35a63b6..b7a41c5 100644 --- a/src/utils/file.util.ts +++ b/src/utils/file.util.ts @@ -10,7 +10,7 @@ enum Type { TXT = '文档', MUSIC = '音乐', VIDEO = '视频', - OTHER = '其他', + OTHER = '其他' } export function getFileType(extName: string) { diff --git a/src/utils/ip.util.ts b/src/utils/ip.util.ts index 60ce375..b9079ca 100644 --- a/src/utils/ip.util.ts +++ b/src/utils/ip.util.ts @@ -46,7 +46,7 @@ export async function getIpAddress(ip: string) { if (isLAN(ip)) return '内网IP'; try { let { data } = await axios.get(`https://whois.pconline.com.cn/ipJson.jsp?ip=${ip}&json=true`, { - responseType: 'arraybuffer', + responseType: 'arraybuffer' }); data = new TextDecoder('gbk').decode(data); data = JSON.parse(data); diff --git a/src/utils/list2tree.util.ts b/src/utils/list2tree.util.ts index d1999a3..10541cb 100644 --- a/src/utils/list2tree.util.ts +++ b/src/utils/list2tree.util.ts @@ -19,7 +19,7 @@ export function list2Tree( const children = list2Tree(items, item.id); return { ...item, - ...(children.length ? { children } : null), + ...(children.length ? { children } : null) }; }); } diff --git a/src/utils/permission.util.ts b/src/utils/permission.util.ts index 6769122..a35d086 100644 --- a/src/utils/permission.util.ts +++ b/src/utils/permission.util.ts @@ -15,7 +15,7 @@ function createRoute(menu: MenuEntity, _isRoot) { show: menu.show, activeMenu: menu.activeMenu, status: menu.status, - keepAlive: menu.keepAlive, + keepAlive: menu.keepAlive }; if (isExternal(menu.path)) { @@ -24,7 +24,7 @@ function createRoute(menu: MenuEntity, _isRoot) { path: menu.path, // component: 'IFrame', name: menu.name, - meta: { ...commonMeta }, + meta: { ...commonMeta } }; } @@ -35,7 +35,7 @@ function createRoute(menu: MenuEntity, _isRoot) { path: menu.path, component: menu.component, name: menu.name, - meta: { ...commonMeta }, + meta: { ...commonMeta } }; } @@ -45,8 +45,8 @@ function createRoute(menu: MenuEntity, _isRoot) { name: menu.name, component: menu.component, meta: { - ...commonMeta, - }, + ...commonMeta + } }; } diff --git a/src/utils/schedule.util.ts b/src/utils/schedule.util.ts index 052fc80..66c468a 100644 --- a/src/utils/schedule.util.ts +++ b/src/utils/schedule.util.ts @@ -88,7 +88,7 @@ export function createNotifyManager() { batchCalls, schedule, setNotifyFunction, - setBatchNotifyFunction, + setBatchNotifyFunction } as const; } From f402438a5aee354420ef2b280253c4241b52c137 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 29 Feb 2024 09:56:03 +0800 Subject: [PATCH 10/64] =?UTF-8?q?feat:=20=E5=90=88=E5=90=8C=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=8F=8A=E8=BD=A6=E8=BE=86=E4=BD=BF=E7=94=A8=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/sql/contracts.sql | 12 --- deploy/sql/vehicle_usage.sql | 4 +- src/app.module.ts | 13 +++- src/modules/contract/contract.entity.ts | 45 ++++++----- src/modules/contract/contract.module.ts | 3 + .../vehicle-usage/vehicle-usage.controller.ts | 4 + .../vehicle-usage/vehicle-usage.entity.ts | 78 +++++++++++++++++++ .../vehicle-usage/vehicle-usage.module.ts | 12 +++ .../vehicle-usage/vehicle-usage.service.ts | 4 + 9 files changed, 138 insertions(+), 37 deletions(-) delete mode 100644 deploy/sql/contracts.sql create mode 100644 src/modules/vehicle-usage/vehicle-usage.controller.ts create mode 100644 src/modules/vehicle-usage/vehicle-usage.entity.ts create mode 100644 src/modules/vehicle-usage/vehicle-usage.module.ts create mode 100644 src/modules/vehicle-usage/vehicle-usage.service.ts diff --git a/deploy/sql/contracts.sql b/deploy/sql/contracts.sql deleted file mode 100644 index b010e9f..0000000 --- a/deploy/sql/contracts.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE contracts ( - id INT AUTO_INCREMENT PRIMARY KEY , - contract_number VARCHAR(255) COMMENT '合同编号', - title VARCHAR(255) COMMENT '合同标题', - type VARCHAR(255) COMMENT '合同类型', - party_a VARCHAR(255) COMMENT '甲方', - party_b VARCHAR(255) COMMENT '乙方', - signing_date DATE COMMENT '签订日期', - delivery_deadline DATE COMMENT '交付期限', - review_result VARCHAR(255) COMMENT '审核结果', - attachment_url VARCHAR(255) COMMENT '下载附件' -); \ No newline at end of file diff --git a/deploy/sql/vehicle_usage.sql b/deploy/sql/vehicle_usage.sql index 081289b..c5973b2 100644 --- a/deploy/sql/vehicle_usage.sql +++ b/deploy/sql/vehicle_usage.sql @@ -1,8 +1,8 @@ CREATE TABLE vehicle_usage ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键,用于唯一标识每条记录', `year` INT COMMENT '年度', - vehicle_name VARCHAR(255) COMMENT '外出使用的车辆名称', - license_plate VARCHAR(255) COMMENT '车牌号', + vehicle_license VARCHAR(255) COMMENT '车牌号', + VARCHAR(255) COMMENT '外出使用的车辆名称', applicant VARCHAR(255) COMMENT '申请人', driver VARCHAR(255) COMMENT '出行司机', current_mileage FLOAT COMMENT '当前车辆里程数(KM)', diff --git a/src/app.module.ts b/src/app.module.ts index d5484cd..5ee2544 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,6 +25,9 @@ import { DatabaseModule } from './shared/database/database.module'; import { SocketModule } from './socket/socket.module'; import { ContractModule } from './modules/contract/contract.module'; +import { VehicleUsageModule } from './modules/vehicle-usage/vehicle-usage.module'; +import { VehicleUsageController } from './modules/vehicle-usage/vehicle-usage.controller'; +import { VehicleUsageService } from './modules/vehicle-usage/vehicle-usage.service'; @Module({ imports: [ @@ -53,7 +56,9 @@ import { ContractModule } from './modules/contract/contract.module'; TodoModule, - ContractModule + ContractModule, + + VehicleUsageModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, @@ -64,7 +69,9 @@ import { ContractModule } from './modules/contract/contract.module'; { provide: APP_INTERCEPTOR, useClass: IdempotenceInterceptor }, { provide: APP_GUARD, useClass: JwtAuthGuard }, - { provide: APP_GUARD, useClass: RbacGuard } - ] + { provide: APP_GUARD, useClass: RbacGuard }, + VehicleUsageService + ], + controllers: [VehicleUsageController] }) export class AppModule {} diff --git a/src/modules/contract/contract.entity.ts b/src/modules/contract/contract.entity.ts index 108c153..e7cfddf 100644 --- a/src/modules/contract/contract.entity.ts +++ b/src/modules/contract/contract.entity.ts @@ -3,37 +3,42 @@ import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; @Entity({ name: 'contract' }) -export class RoleEntity extends CommonEntity { - @Column({ length: 100, unique: true, name: 'contract_number' }) +export class ContractEntity extends CommonEntity { + @Column({ + name: 'contract_number', + type: 'varchar', + length: 255, + unique: true, + comment: '合同编号' + }) @ApiProperty({ description: '合同编号' }) contractNumber: string; - @Column({ length: 255, name: 'title', type: 'varchar' }) + @Column({ name: 'title', type: 'varchar', length: 255, comment: '合同标题' }) @ApiProperty({ description: '合同标题' }) title: string; - @Column() - @ApiProperty({ description: '合同类型', type: 'varchar' }) + @Column({ type: 'int', comment: '合同类型' }) + @ApiProperty({ description: '合同类型' }) type: number; - @Column({ name: 'partyA', length: 255, type: 'varchar' }) + @Column({ name: 'party_a', length: 255, type: 'varchar', comment: '甲方' }) @ApiProperty({ description: '甲方' }) partyA: string; - // @Column({ type: 'tinyint', nullable: true, default: 1 }) - // @ApiProperty({ description: '状态:1启用,0禁用' }) - // status: number + @Column({ name: 'party_b', length: 255, type: 'varchar', comment: '乙方' }) + @ApiProperty({ description: '乙方' }) + partyB: string; - // @Column({ nullable: true }) - // @ApiProperty({ description: '是否默认用户' }) - // default: boolean + @Column({ name: 'signing_date', type: 'date', nullable: true }) + @ApiProperty({ description: '签订日期' }) + signingDate: Date; - // @ApiHideProperty() - // @ManyToMany(() => MenuEntity, menu => menu.roles, {}) - // @JoinTable({ - // name: 'sys_role_menus', - // joinColumn: { name: 'role_id', referencedColumnName: 'id' }, - // inverseJoinColumn: { name: 'menu_id', referencedColumnName: 'id' }, - // }) - // menus: Relation + @Column({ name: 'delivery_deadline', type: 'date', nullable: true }) + @ApiProperty({ description: '交付期限' }) + deliveryDeadline: Date; + + @Column({ name: 'status', type: 'tinyint', default: 0, comment: '审核状态' }) + @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意' }) + status: number; } diff --git a/src/modules/contract/contract.module.ts b/src/modules/contract/contract.module.ts index ad30ded..46dfda5 100644 --- a/src/modules/contract/contract.module.ts +++ b/src/modules/contract/contract.module.ts @@ -1,8 +1,11 @@ import { Module } from '@nestjs/common'; import { ContractController } from './contract.controller'; import { ContractService } from './contract.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { ContractEntity } from './contract.entity'; @Module({ + imports: [TypeOrmModule.forFeature([ContractEntity])], controllers: [ContractController], providers: [ContractService] }) diff --git a/src/modules/vehicle-usage/vehicle-usage.controller.ts b/src/modules/vehicle-usage/vehicle-usage.controller.ts new file mode 100644 index 0000000..9c63da7 --- /dev/null +++ b/src/modules/vehicle-usage/vehicle-usage.controller.ts @@ -0,0 +1,4 @@ +import { Controller } from '@nestjs/common'; + +@Controller('vehicle-usage') +export class VehicleUsageController {} diff --git a/src/modules/vehicle-usage/vehicle-usage.entity.ts b/src/modules/vehicle-usage/vehicle-usage.entity.ts new file mode 100644 index 0000000..abd1972 --- /dev/null +++ b/src/modules/vehicle-usage/vehicle-usage.entity.ts @@ -0,0 +1,78 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; + +@Entity({ name: 'vehicle_usage' }) +export class VehicleUsageEntity extends CommonEntity { + @Column({ type: 'int', comment: '年度' }) + @ApiProperty({ description: '年度' }) + year: number; + + @Column({ name: 'vehicle_license', type: 'int', comment: '外出使用的车辆名称' }) + @ApiProperty({ description: '外出使用的车辆名称' }) + vehicleLicense: number; + + @Column({ + name: 'applicant', + type: 'varchar', + length: 50, + comment: '申请人' + }) + @ApiProperty({ description: '申请人' }) + applicant: string; + + @Column({ + name: 'driver', + type: 'varchar', + length: 50, + comment: '出行司机' + }) + @ApiProperty({ description: '出行司机', nullable: true }) + driver: string; + + @Column({ name: 'current_mileage', type: 'int', comment: '当前车辆里程数(KM)', nullable: true }) + @ApiProperty({ description: '当前车辆里程数(KM)' }) + currentMileage: number; + + @Column({ + name: 'expected_start_date', + type: 'date', + nullable: true, + comment: '预计出行开始时间' + }) + @ApiProperty({ description: '预计出行开始时间' }) + expectedStartDate: Date; + + @Column({ name: 'expected_end_date', type: 'date', nullable: true, comment: '预计出行结束时间' }) + @ApiProperty({ description: '预计出行结束时间' }) + expectedEndDate: Date; + + @Column({ name: 'purpose', type: 'varchar', length: 255, comment: '使用事由', nullable: true }) + @ApiProperty({ description: '使用事由' }) + purpose: string; + + @Column({ + name: 'actual_return_time', + type: 'date', + nullable: true, + comment: '实际回司时间' + }) + @ApiProperty({ description: '实际回司时间' }) + actualReturnTime: Date; + + @Column({ name: 'return_mileage', type: 'int', comment: '回城车辆里程数(KM)', nullable: true }) + @ApiProperty({ description: '回城车辆里程数(KM)' }) + returnMileage: number; + + @Column({ name: 'reviewer', type: 'varchar', length: 50, comment: '审核人', nullable: true }) + @ApiProperty({ description: '审核人' }) + reviewer: string; + + @Column({ name: 'status', type: 'tinyint', default: 0, comment: '审核状态' }) + @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意' }) + status: number; + + @Column({ name: 'remarks', type: 'varchar', length: 255, comment: '备注', nullable: true }) + @ApiProperty({ description: '备注' }) + remarks: string; +} diff --git a/src/modules/vehicle-usage/vehicle-usage.module.ts b/src/modules/vehicle-usage/vehicle-usage.module.ts new file mode 100644 index 0000000..d50dfc1 --- /dev/null +++ b/src/modules/vehicle-usage/vehicle-usage.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { VehicleUsageService } from './vehicle-usage.service'; +import { VehicleUsageController } from './vehicle-usage.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { VehicleUsageEntity } from './vehicle-usage.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([VehicleUsageEntity])], + providers: [VehicleUsageService], + controllers: [VehicleUsageController] +}) +export class VehicleUsageModule {} diff --git a/src/modules/vehicle-usage/vehicle-usage.service.ts b/src/modules/vehicle-usage/vehicle-usage.service.ts new file mode 100644 index 0000000..0ec15a1 --- /dev/null +++ b/src/modules/vehicle-usage/vehicle-usage.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class VehicleUsageService {} From 6650f9f06aad467f855d9cdb6d98eccc3dcea9a5 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 29 Feb 2024 10:32:37 +0800 Subject: [PATCH 11/64] =?UTF-8?q?feat:=20=E5=8E=9F=E6=9D=90=E6=96=99?= =?UTF-8?q?=E7=9B=98=E7=82=B9=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/sql/raw_materials_inventory.sql | 23 +++ deploy/sql/vehicle_usage.sql | 15 -- src/app.module.ts | 5 +- src/modules/contract/contract.entity.ts | 8 +- .../materials_inventory.controller.ts | 4 + .../materials_inventory.entity.ts | 185 ++++++++++++++++++ .../materials_inventory.module.ts | 12 ++ .../materials_inventory.service.ts | 4 + .../vehicle-usage/vehicle-usage.entity.ts | 12 +- 9 files changed, 242 insertions(+), 26 deletions(-) create mode 100644 deploy/sql/raw_materials_inventory.sql delete mode 100644 deploy/sql/vehicle_usage.sql create mode 100644 src/modules/materials_inventory/materials_inventory.controller.ts create mode 100644 src/modules/materials_inventory/materials_inventory.entity.ts create mode 100644 src/modules/materials_inventory/materials_inventory.module.ts create mode 100644 src/modules/materials_inventory/materials_inventory.service.ts diff --git a/deploy/sql/raw_materials_inventory.sql b/deploy/sql/raw_materials_inventory.sql new file mode 100644 index 0000000..9b313cf --- /dev/null +++ b/deploy/sql/raw_materials_inventory.sql @@ -0,0 +1,23 @@ +CREATE TABLE `materials_inventory`( + id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ID', + company_name VARCHAR(255) COMMENT '公司名称', + product_name VARCHAR(255) COMMENT '产品名称', + unit VARCHAR(50) COMMENT '单位', + previous_inventory_quantity INT COMMENT '之前的库存数量', + previous_unit_price DECIMAL(10, 2) COMMENT '之前的单价', + previous_amount DECIMAL(10, 2) COMMENT '之前的金额', + inventory_time DATETIME COMMENT '入库时间', + inventory_quantity INT COMMENT '入库后数量', + inventory_unit_price DECIMAL(10, 2) COMMENT '入库后单价', + inventory_amount DECIMAL(10, 2) COMMENT '入库金额', + out_time DATETIME COMMENT '出库时间', + out_quantity INT COMMENT '出库数量', + out_unit_price DECIMAL(10, 2) COMMENT '出库单价', + out_amount DECIMAL(10, 2) COMMENT '出库金额', + current_inventory_quantity INT COMMENT '现在的结存数量', + current_unit_price DECIMAL(10, 2) COMMENT '现在的单价', + current_amount DECIMAL(10, 2) COMMENT '现在的金额', + agent VARCHAR(100) COMMENT '经办人', + issuance_number VARCHAR(100) COMMENT '领料单号', + remark VARCHAR(255) COMMENT '备注' +); \ No newline at end of file diff --git a/deploy/sql/vehicle_usage.sql b/deploy/sql/vehicle_usage.sql deleted file mode 100644 index c5973b2..0000000 --- a/deploy/sql/vehicle_usage.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TABLE vehicle_usage ( - id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键,用于唯一标识每条记录', - `year` INT COMMENT '年度', - vehicle_license VARCHAR(255) COMMENT '车牌号', - VARCHAR(255) COMMENT '外出使用的车辆名称', - applicant VARCHAR(255) COMMENT '申请人', - driver VARCHAR(255) COMMENT '出行司机', - current_mileage FLOAT COMMENT '当前车辆里程数(KM)', - expected_travel_time DATETIME COMMENT '预计出行时间', - purpose VARCHAR(255) COMMENT '使用事由', - actual_return_time DATETIME COMMENT '实际回司时间', - return_mileage FLOAT COMMENT '回城车辆里程数(KM)', - approval_by_leader VARCHAR(255) COMMENT '公司领导审批', - remarks VARCHAR(255) COMMENT '备注' -); \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index 5ee2544..de220bc 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -28,6 +28,7 @@ import { ContractModule } from './modules/contract/contract.module'; import { VehicleUsageModule } from './modules/vehicle-usage/vehicle-usage.module'; import { VehicleUsageController } from './modules/vehicle-usage/vehicle-usage.controller'; import { VehicleUsageService } from './modules/vehicle-usage/vehicle-usage.service'; +import { MaterialsInventoryModule } from './modules/materials_inventory/materials_inventory.module'; @Module({ imports: [ @@ -58,7 +59,9 @@ import { VehicleUsageService } from './modules/vehicle-usage/vehicle-usage.servi ContractModule, - VehicleUsageModule + VehicleUsageModule, + + MaterialsInventoryModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, diff --git a/src/modules/contract/contract.entity.ts b/src/modules/contract/contract.entity.ts index e7cfddf..979533d 100644 --- a/src/modules/contract/contract.entity.ts +++ b/src/modules/contract/contract.entity.ts @@ -18,8 +18,8 @@ export class ContractEntity extends CommonEntity { @ApiProperty({ description: '合同标题' }) title: string; - @Column({ type: 'int', comment: '合同类型' }) - @ApiProperty({ description: '合同类型' }) + @Column({ type: 'int', comment: '合同类型(字典)' }) + @ApiProperty({ description: '合同类型(字典)' }) type: number; @Column({ name: 'party_a', length: 255, type: 'varchar', comment: '甲方' }) @@ -38,7 +38,7 @@ export class ContractEntity extends CommonEntity { @ApiProperty({ description: '交付期限' }) deliveryDeadline: Date; - @Column({ name: 'status', type: 'tinyint', default: 0, comment: '审核状态' }) - @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意' }) + @Column({ name: 'status', type: 'tinyint', default: 0, comment: '审核状态(字典)' }) + @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意(字典)' }) status: number; } diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts new file mode 100644 index 0000000..c5d1b06 --- /dev/null +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -0,0 +1,4 @@ +import { Controller } from '@nestjs/common'; + +@Controller('materials-inventory') +export class MaterialsInventoryController {} diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts new file mode 100644 index 0000000..c0d429e --- /dev/null +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -0,0 +1,185 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; + +@Entity({ name: 'meterials_inventory' }) +export class MeterialsInventoryEntity extends CommonEntity { + @Column({ name: 'company_name', type: 'varchar', length: 255, comment: '公司名称' }) + @ApiProperty({ description: '公司名称' }) + companyName: number; + + @Column({ + name: 'product', + type: 'int', + comment: '产品名称(字典)' + }) + @ApiProperty({ description: '产品名称(字典)' }) + product: number; + + @Column({ + name: 'unit', + type: 'int', + comment: '单位(字典)' + }) + @ApiProperty({ description: '单位(字典)' }) + unit: number; + + @Column({ + name: 'previous_inventory_quantity', + type: 'int', + default: 0, + comment: '之前的库存数量' + }) + @ApiProperty({ description: '之前的库存数量' }) + previousInventoryQuantity: number; + + @Column({ + name: 'previous_unit_price', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '之前的单价' + }) + @ApiProperty({ description: '之前的单价' }) + previousUnitPrice: number; + + @Column({ + name: 'previous_amount', + type: 'decimal', + precision: 10, + scale: 2, + default: 0, + comment: '之前的金额' + }) + @ApiProperty({ description: '之前的金额' }) + previousAmount: number; + + @Column({ + name: 'inventory_time', + type: 'date', + nullable: true, + comment: '入库时间' + }) + @ApiProperty({ description: '入库时间' }) + inventoryTime: Date; + + @Column({ + name: 'inventory_quantity', + type: 'int', + default: 0, + comment: '入库数量' + }) + @ApiProperty({ description: '入库数量' }) + inventoryQuantity: number; + + @Column({ + name: 'inventory_unit_price', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '入库单价' + }) + @ApiProperty({ description: '入库单价' }) + inventoryUnitPrice: number; + + @Column({ + name: 'inventory_amount', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '入库金额' + }) + @ApiProperty({ description: '入库金额' }) + inventoryAmount: number; + + @Column({ + name: 'out_time', + type: 'date', + nullable: true, + comment: '出库时间' + }) + @ApiProperty({ description: '出库时间' }) + outime: Date; + + @Column({ + name: 'out_quantity', + type: 'int', + default: 0, + comment: '出库数量' + }) + @ApiProperty({ description: '出库数量' }) + outQuantity: number; + + @Column({ + name: 'out_unit_price', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '出库单价' + }) + @ApiProperty({ description: '出库单价' }) + outUnitPrice: number; + + @Column({ + name: 'out_amount', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '出库金额' + }) + @ApiProperty({ description: '出库金额' }) + outAmount: number; + + @Column({ + name: 'current_inventory_quantity', + type: 'int', + default: 0, + comment: '现在的结存数量' + }) + @ApiProperty({ description: '现在的结存数量' }) + currentInventoryQuantity: number; + + @Column({ + name: 'current_unit_price', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '现在的单价' + }) + @ApiProperty({ description: '现在的单价' }) + currentUnitPrice: number; + + @Column({ + name: 'current_amount', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '现在的金额' + }) + @ApiProperty({ description: '现在的金额' }) + currentAmount: number; + + @Column({ name: 'agent', type: 'varchar', length: 50, comment: '经办人', nullable: true }) + @ApiProperty({ description: '经办人' }) + agent: string; + + @Column({ + name: 'issuance_number', + type: 'varchar', + length: 100, + comment: '领料单号' + }) + @ApiProperty({ description: '领料单号' }) + issuanceNumber: string; + + @Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true }) + @ApiProperty({ description: '备注' }) + remark: string; +} diff --git a/src/modules/materials_inventory/materials_inventory.module.ts b/src/modules/materials_inventory/materials_inventory.module.ts new file mode 100644 index 0000000..0572e3d --- /dev/null +++ b/src/modules/materials_inventory/materials_inventory.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { MaterialsInventoryController } from './materials_inventory.controller'; +import { MaterialsInventoryService } from './materials_inventory.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { MeterialsInventoryEntity } from './materials_inventory.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([MeterialsInventoryEntity])], + controllers: [MaterialsInventoryController], + providers: [MaterialsInventoryService] +}) +export class MaterialsInventoryModule {} diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts new file mode 100644 index 0000000..44c7be2 --- /dev/null +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class MaterialsInventoryService {} diff --git a/src/modules/vehicle-usage/vehicle-usage.entity.ts b/src/modules/vehicle-usage/vehicle-usage.entity.ts index abd1972..994fd97 100644 --- a/src/modules/vehicle-usage/vehicle-usage.entity.ts +++ b/src/modules/vehicle-usage/vehicle-usage.entity.ts @@ -8,8 +8,8 @@ export class VehicleUsageEntity extends CommonEntity { @ApiProperty({ description: '年度' }) year: number; - @Column({ name: 'vehicle_license', type: 'int', comment: '外出使用的车辆名称' }) - @ApiProperty({ description: '外出使用的车辆名称' }) + @Column({ name: 'vehicle_license', type: 'int', comment: '外出使用的车辆名称(字典)' }) + @ApiProperty({ description: '外出使用的车辆名称(字典)' }) vehicleLicense: number; @Column({ @@ -68,11 +68,11 @@ export class VehicleUsageEntity extends CommonEntity { @ApiProperty({ description: '审核人' }) reviewer: string; - @Column({ name: 'status', type: 'tinyint', default: 0, comment: '审核状态' }) - @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意' }) + @Column({ name: 'status', type: 'tinyint', default: 0, comment: '审核状态(字典)' }) + @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意(字典)' }) status: number; - @Column({ name: 'remarks', type: 'varchar', length: 255, comment: '备注', nullable: true }) + @Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true }) @ApiProperty({ description: '备注' }) - remarks: string; + remark: string; } From 9c8d2e6ca31861808b2546bf4a2af362b872390d Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 29 Feb 2024 16:51:22 +0800 Subject: [PATCH 12/64] =?UTF-8?q?feat:=20contract=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/contract/contract.controller.ts | 88 ++++++++++--------- src/modules/contract/contract.dto.ts | 56 ++++++++++++ src/modules/contract/contract.service.ts | 71 ++++++++++++++- .../system/dict-item/dict-item.controller.ts | 18 ++-- .../system/dict-item/dict-item.service.ts | 5 ++ 5 files changed, 187 insertions(+), 51 deletions(-) create mode 100644 src/modules/contract/contract.dto.ts diff --git a/src/modules/contract/contract.controller.ts b/src/modules/contract/contract.controller.ts index 5755395..5bdc005 100644 --- a/src/modules/contract/contract.controller.ts +++ b/src/modules/contract/contract.controller.ts @@ -1,9 +1,21 @@ -import { Controller, Get } from '@nestjs/common'; +import { + Body, + Controller, + Get, + Query, + Put, + Delete, + Post, + BadRequestException +} from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { definePermission } from '../auth/decorators/permission.decorator'; +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; import { ContractService } from './contract.service'; import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { ContractEntity } from './contract.entity'; +import { ContractDto, ContractQueryDto, ContractUpdateDto } from './contract.dto'; +import { IdParam } from '~/common/decorators/id-param.decorator'; export const permissions = definePermission('app:contract', { LIST: 'list', CREATE: 'create', @@ -16,48 +28,42 @@ export const permissions = definePermission('app:contract', { @ApiSecurityAuth() @Controller('contract') export class ContractController { - constructor(private menuService: ContractService) {} + constructor(private contractService: ContractService) {} - // @Get() - // @ApiOperation({ summary: '获取合同列表' }) - // @ApiResult({ type: [RoleEntity], isPage: true }) - // @Perm(permissions.LIST) - // async list(@Query() dto: RoleQueryDto) { - // return this.roleService.findAll(dto) - // } + @Get() + @ApiOperation({ summary: '获取合同列表' }) + @ApiResult({ type: [ContractEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: ContractQueryDto) { + return this.contractService.findAll(dto); + } - // @Get(':id') - // @ApiOperation({ summary: '获取角色信息' }) - // @ApiResult({ type: RoleInfo }) - // @Perm(permissions.READ) - // async info(@IdParam() id: number) { - // return this.roleService.info(id) - // } + @Get(':id') + @ApiOperation({ summary: '获取合同信息' }) + @ApiResult({ type: ContractDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.contractService.info(id); + } - // @Post() - // @ApiOperation({ summary: '新增角色' }) - // @Perm(permissions.CREATE) - // async create(@Body() dto: RoleDto): Promise { - // await this.roleService.create(dto) - // } + @Post() + @ApiOperation({ summary: '新增合同' }) + @Perm(permissions.CREATE) + async create(@Body() dto: ContractDto): Promise { + await this.contractService.create(dto); + } - // @Put(':id') - // @ApiOperation({ summary: '更新角色' }) - // @Perm(permissions.UPDATE) - // async update( - // @IdParam() id: number, @Body() dto: RoleUpdateDto): Promise { - // await this.roleService.update(id, dto) - // await this.menuService.refreshOnlineUserPerms() - // } + @Put(':id') + @ApiOperation({ summary: '更新合同' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: ContractUpdateDto): Promise { + await this.contractService.update(id, dto); + } - // @Delete(':id') - // @ApiOperation({ summary: '删除角色' }) - // @Perm(permissions.DELETE) - // async delete(@IdParam() id: number): Promise { - // if (await this.roleService.checkUserByRoleId(id)) - // throw new BadRequestException('该角色存在关联用户,无法删除') - - // await this.roleService.delete(id) - // await this.menuService.refreshOnlineUserPerms() - // } + @Delete(':id') + @ApiOperation({ summary: '删除合同' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.contractService.delete(id); + } } diff --git a/src/modules/contract/contract.dto.ts b/src/modules/contract/contract.dto.ts new file mode 100644 index 0000000..057ea88 --- /dev/null +++ b/src/modules/contract/contract.dto.ts @@ -0,0 +1,56 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { + IsArray, + IsDate, + IsDateString, + IsIn, + IsInt, + IsNumber, + IsOptional, + IsString, + Matches, + MinLength, + +} from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; + +export class ContractDto { + @ApiProperty({ description: '合同编号' }) + @Matches(/^[a-z0-9A-Z]+$/, { message: '合同编号只能包含字母和数字' }) + @IsString() + contractNumber: string; + + @ApiProperty({ description: '合同标题' }) + @IsString() + title: string; + + @ApiProperty({ description: '合同类型' }) + @IsNumber() + type: number; + + @ApiProperty({ description: '甲方' }) + @IsString() + partyA: string; + + @ApiProperty({ description: '乙方' }) + @IsString() + partyB: string; + + @ApiProperty({ description: '签订日期' }) + @IsDateString() + signingDate: string; + + @ApiProperty({ description: '交付期限' }) + @IsDateString() + deliveryDeadline: string; + + @ApiProperty({ description: '审核状态(字典)' }) + @IsIn([0, 1, 2]) + status: number; +} + +export class ContractUpdateDto extends PartialType(ContractDto) {} +export class ContractQueryDto extends IntersectionType( + PagerDto, + PartialType(ContractDto) +) {} diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index 87c091d..6eafc10 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -1,4 +1,73 @@ import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { ContractEntity } from './contract.entity'; +import { Like, Repository } from 'typeorm'; +import { ContractDto, ContractQueryDto } from './contract.dto'; +import { Pagination } from '~/helper/paginate/pagination'; +import { isNumber } from 'lodash'; +import { paginate } from '~/helper/paginate'; @Injectable() -export class ContractService {} +export class ContractService { + constructor( + @InjectRepository(ContractEntity) + private contractRepository: Repository + ) {} + /** + * 列举所有角色:除去超级管理员 + */ + async findAll({ + page, + pageSize, + contractNumber, + title, + type, + status + }: ContractQueryDto): Promise> { + const queryBuilder = this.contractRepository.createQueryBuilder('contract').where({ + ...(contractNumber ? { contractNumber: Like(`%${contractNumber}%`) } : null), + ...(title ? { title: Like(`%${title}%`) } : null), + ...(isNumber(type) ? { type } : null), + ...(isNumber(status) ? { status } : null) + }); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: ContractDto): Promise { + await this.contractRepository.insert(dto); + } + + /** + * 更新 + */ + async update(id: number, dto: Partial): Promise { + await this.contractRepository.update(id, dto); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + await this.contractRepository.softDelete(id); + } + + /** + * 获取单个合同信息 + */ + async info(id: number) { + const info = await this.contractRepository + .createQueryBuilder('contract') + .where({ + id + }) + .getOne(); + return info; + } +} diff --git a/src/modules/system/dict-item/dict-item.controller.ts b/src/modules/system/dict-item/dict-item.controller.ts index 4687594..ece718c 100644 --- a/src/modules/system/dict-item/dict-item.controller.ts +++ b/src/modules/system/dict-item/dict-item.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Post, Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiResult } from '~/common/decorators/api-result.decorator'; @@ -34,6 +34,14 @@ export class DictItemController { return this.dictItemService.page(dto); } + @Get('all/:typeId') + @ApiOperation({ summary: '一次性通过字典类型获取所有所属的字典项(不分页)' }) + @ApiResult({ type: [DictItemEntity] }) + @Perm(permissions.LIST) + async getAll(@Param('typeId') typeId: number): Promise { + return this.dictItemService.getAllByType(typeId); + } + @Post() @ApiOperation({ summary: '新增字典项' }) @Perm(permissions.CREATE) @@ -43,14 +51,6 @@ export class DictItemController { await this.dictItemService.create(dto); } - @Get(':id') - @ApiOperation({ summary: '查询字典项信息' }) - @ApiResult({ type: DictItemEntity }) - @Perm(permissions.READ) - async info(@IdParam() id: number): Promise { - return this.dictItemService.findOne(id); - } - @Post(':id') @ApiOperation({ summary: '更新字典项' }) @Perm(permissions.UPDATE) diff --git a/src/modules/system/dict-item/dict-item.service.ts b/src/modules/system/dict-item/dict-item.service.ts index a1f5cac..2ad8221 100644 --- a/src/modules/system/dict-item/dict-item.service.ts +++ b/src/modules/system/dict-item/dict-item.service.ts @@ -42,6 +42,11 @@ export class DictItemService { return paginate(queryBuilder, { page, pageSize }); } + /** 一次性获取所有的字典项 */ + async getAllByType(typeId: number) { + return this.dictItemRepository.find({ where: { type: { id: typeId } } }); + } + /** * 获取参数总数 */ From 481dd8456eae8500a6679c2995d3efa0a4f81ca9 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 1 Mar 2024 15:23:28 +0800 Subject: [PATCH 13/64] =?UTF-8?q?feat:=20=E5=90=88=E5=90=8C=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E9=99=84=E4=BB=B6=20=E9=99=84=E4=BB=B6=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/error-code.constant.ts | 6 +- src/modules/contract/contract.controller.ts | 8 ++ src/modules/contract/contract.dto.ts | 13 ++- src/modules/contract/contract.entity.ts | 9 +++ src/modules/contract/contract.module.ts | 3 +- src/modules/contract/contract.service.ts | 80 ++++++++++++++++--- src/modules/tools/storage/storage.dto.ts | 13 +++ src/modules/tools/storage/storage.entity.ts | 9 ++- src/modules/tools/storage/storage.service.ts | 30 ++++--- src/modules/tools/upload/upload.controller.ts | 4 +- src/modules/tools/upload/upload.service.ts | 6 +- 11 files changed, 148 insertions(+), 33 deletions(-) diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 95af623..3b381c2 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -45,5 +45,9 @@ export enum ErrorEnum { // OSS相关 OSS_FILE_OR_DIR_EXIST = '1401:当前创建的文件或目录已存在', OSS_NO_OPERATION_REQUIRED = '1402:无需操作', - OSS_EXCEE_MAXIMUM_QUANTITY = '1403:已超出支持的最大处理数量' + OSS_EXCEE_MAXIMUM_QUANTITY = '1403:已超出支持的最大处理数量', + + // Storage相关 + STORAGE_NOT_FOUND = '1404:文件不存在,请重试', + STORAGE_REFRENCE_EXISTS = '1405:文件存在关联,无法删除,请先找到该文件关联的业务解除关联。' } diff --git a/src/modules/contract/contract.controller.ts b/src/modules/contract/contract.controller.ts index 5bdc005..a3e716f 100644 --- a/src/modules/contract/contract.controller.ts +++ b/src/modules/contract/contract.controller.ts @@ -66,4 +66,12 @@ export class ContractController { async delete(@IdParam() id: number): Promise { await this.contractService.delete(id); } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments(@IdParam() id: number, @Body() {fileIds}: ContractUpdateDto): Promise { + await this.contractService.unlinkAttachments(id, fileIds); + } + } diff --git a/src/modules/contract/contract.dto.ts b/src/modules/contract/contract.dto.ts index 057ea88..9b4ca8b 100644 --- a/src/modules/contract/contract.dto.ts +++ b/src/modules/contract/contract.dto.ts @@ -9,10 +9,10 @@ import { IsOptional, IsString, Matches, - MinLength, - + MinLength } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '../tools/storage/storage.entity'; export class ContractDto { @ApiProperty({ description: '合同编号' }) @@ -47,9 +47,16 @@ export class ContractDto { @ApiProperty({ description: '审核状态(字典)' }) @IsIn([0, 1, 2]) status: number; + + @ApiProperty({ description: '附件' }) + files: Storage[]; } -export class ContractUpdateDto extends PartialType(ContractDto) {} +export class ContractUpdateDto extends PartialType(ContractDto) { + @ApiProperty({ description: '附件' }) + @IsArray({}) + fileIds: number[]; +} export class ContractQueryDto extends IntersectionType( PagerDto, PartialType(ContractDto) diff --git a/src/modules/contract/contract.entity.ts b/src/modules/contract/contract.entity.ts index 979533d..afdb5cf 100644 --- a/src/modules/contract/contract.entity.ts +++ b/src/modules/contract/contract.entity.ts @@ -1,6 +1,7 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '../tools/storage/storage.entity'; @Entity({ name: 'contract' }) export class ContractEntity extends CommonEntity { @@ -41,4 +42,12 @@ export class ContractEntity extends CommonEntity { @Column({ name: 'status', type: 'tinyint', default: 0, comment: '审核状态(字典)' }) @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意(字典)' }) status: number; + + @ManyToMany(() => Storage, storage => storage.contracts) + @JoinTable({ + name: 'contract_storage', + joinColumn: { name: 'contract_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; } diff --git a/src/modules/contract/contract.module.ts b/src/modules/contract/contract.module.ts index 46dfda5..d5eabe8 100644 --- a/src/modules/contract/contract.module.ts +++ b/src/modules/contract/contract.module.ts @@ -3,9 +3,10 @@ import { ContractController } from './contract.controller'; import { ContractService } from './contract.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ContractEntity } from './contract.entity'; +import { StorageModule } from '../tools/storage/storage.module'; @Module({ - imports: [TypeOrmModule.forFeature([ContractEntity])], + imports: [TypeOrmModule.forFeature([ContractEntity]), StorageModule], controllers: [ContractController], providers: [ContractService] }) diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index 6eafc10..b28eae9 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -1,17 +1,23 @@ import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; import { ContractEntity } from './contract.entity'; -import { Like, Repository } from 'typeorm'; -import { ContractDto, ContractQueryDto } from './contract.dto'; +import { EntityManager, Like, Repository } from 'typeorm'; +import { ContractDto, ContractQueryDto, ContractUpdateDto } from './contract.dto'; import { Pagination } from '~/helper/paginate/pagination'; import { isNumber } from 'lodash'; import { paginate } from '~/helper/paginate'; +import { Storage } from '../tools/storage/storage.entity'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; @Injectable() export class ContractService { constructor( + @InjectEntityManager() private entityManager: EntityManager, @InjectRepository(ContractEntity) - private contractRepository: Repository + private contractRepository: Repository, + @InjectRepository(Storage) + private storageRepository: Repository ) {} /** * 列举所有角色:除去超级管理员 @@ -24,12 +30,16 @@ export class ContractService { type, status }: ContractQueryDto): Promise> { - const queryBuilder = this.contractRepository.createQueryBuilder('contract').where({ - ...(contractNumber ? { contractNumber: Like(`%${contractNumber}%`) } : null), - ...(title ? { title: Like(`%${title}%`) } : null), - ...(isNumber(type) ? { type } : null), - ...(isNumber(status) ? { status } : null) - }); + const queryBuilder = this.contractRepository + .createQueryBuilder('contract') + .leftJoin('contract.files', 'files') + .addSelect(['files.id', 'files.path']) + .where({ + ...(contractNumber ? { contractNumber: Like(`%${contractNumber}%`) } : null), + ...(title ? { title: Like(`%${title}%`) } : null), + ...(isNumber(type) ? { type } : null), + ...(isNumber(status) ? { status } : null) + }); return paginate(queryBuilder, { page, @@ -47,8 +57,30 @@ export class ContractService { /** * 更新 */ - async update(id: number, dto: Partial): Promise { - await this.contractRepository.update(id, dto); + async update(id: number, { fileIds, ...data }: Partial): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(ContractEntity, id, { + ...data + }); + const contract = await this.contractRepository + .createQueryBuilder('contract') + .leftJoinAndSelect('contract.files', 'files') + .where('contract.id = :id', { id }) + .getOne(); + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(ContractEntity, 'files') + .of(id) + .addAndRemove(fileIds, contract.files); + }); } /** @@ -70,4 +102,28 @@ export class ContractService { .getOne(); return info; } + + /** + * 解除附件关联 + * @param id 合同ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const contract = await this.contractRepository + .createQueryBuilder('contract') + .leftJoinAndSelect('contract.files', 'files') + .where('contract.id = :id', { id }) + .getOne(); + const linkedFiles = contract.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(ContractEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, contract.files); + }); + } } diff --git a/src/modules/tools/storage/storage.dto.ts b/src/modules/tools/storage/storage.dto.ts index ed25a7e..1dfd010 100644 --- a/src/modules/tools/storage/storage.dto.ts +++ b/src/modules/tools/storage/storage.dto.ts @@ -1,4 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsOptional, IsString } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; @@ -32,6 +33,18 @@ export class StoragePageDto extends PagerDto { @IsString() @IsOptional() username: string; + + @ApiProperty({ description: '附件' }) + @IsOptional() + @Transform( + ({ value: val }) => { + return val ? val.split(',').map(item => Number(item)) : []; + }, + { + toClassOnly: true + } + ) + ids: number[]; } export class StorageCreateDto { diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index 7680f12..69253a5 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -1,7 +1,8 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Column, Entity } from 'typeorm'; +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; +import { ContractEntity } from '~/modules/contract/contract.entity'; @Entity({ name: 'tool_storage' }) export class Storage extends CommonEntity { @@ -37,4 +38,8 @@ export class Storage extends CommonEntity { @Column({ nullable: true, name: 'user_id' }) @ApiProperty({ description: '用户ID' }) userId: number; + + @ApiHideProperty() + @ManyToMany(() => ContractEntity, contract => contract.files) + contracts: Relation; } diff --git a/src/modules/tools/storage/storage.service.ts b/src/modules/tools/storage/storage.service.ts index 5a8cbf3..414af8f 100644 --- a/src/modules/tools/storage/storage.service.ts +++ b/src/modules/tools/storage/storage.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Between, Like, Repository } from 'typeorm'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { Between, EntityManager, In, Like, Repository } from 'typeorm'; import { paginateRaw } from '~/helper/paginate'; import { PaginationTypeEnum } from '~/helper/paginate/interface'; @@ -11,10 +11,13 @@ import { deleteFile } from '~/utils'; import { StorageCreateDto, StoragePageDto } from './storage.dto'; import { StorageInfo } from './storage.modal'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; @Injectable() export class StorageService { constructor( + @InjectEntityManager() private entityManager: EntityManager, @InjectRepository(Storage) private storageRepository: Repository, @InjectRepository(UserEntity) @@ -32,11 +35,18 @@ export class StorageService { * 删除文件 */ async delete(fileIds: number[]): Promise { - const items = await this.storageRepository.findByIds(fileIds); - await this.storageRepository.delete(fileIds); - - items.forEach(el => { - deleteFile(el.path); + await this.entityManager.transaction(async manager => { + const items = await this.storageRepository.findBy({ id: In(fileIds) }); + try { + await manager.delete(Storage, fileIds); + items.forEach(el => { + deleteFile(el.path); + }); + } catch (e) { + if (e.code === 'ER_ROW_IS_REFERENCED_2') { + throw new BusinessException(ErrorEnum.STORAGE_REFRENCE_EXISTS); + } + } }); } @@ -48,7 +58,8 @@ export class StorageService { size, extName, time, - username + username, + ids }: StoragePageDto): Promise> { const queryBuilder = this.storageRepository .createQueryBuilder('storage') @@ -61,7 +72,8 @@ export class StorageService { ...(time && { createdAt: Between(time[0], time[1]) }), ...(username && { userId: await (await this.userRepository.findOneBy({ username })).id - }) + }), + ...(ids && { id: In(ids) }) }) .orderBy('storage.created_at', 'DESC'); diff --git a/src/modules/tools/upload/upload.controller.ts b/src/modules/tools/upload/upload.controller.ts index 7c44644..31756ab 100644 --- a/src/modules/tools/upload/upload.controller.ts +++ b/src/modules/tools/upload/upload.controller.ts @@ -38,10 +38,10 @@ export class UploadController { // console.log(part.file) try { - const path = await this.uploadService.saveFile(file, user.uid); + const savedFile = await this.uploadService.saveFile(file, user.uid); return { - filename: path + filename: savedFile }; } catch (error) { console.log(error); diff --git a/src/modules/tools/upload/upload.service.ts b/src/modules/tools/upload/upload.service.ts index 605f977..05b2a31 100644 --- a/src/modules/tools/upload/upload.service.ts +++ b/src/modules/tools/upload/upload.service.ts @@ -25,7 +25,7 @@ export class UploadService { /** * 保存文件上传记录 */ - async saveFile(file: MultipartFile, userId: number): Promise { + async saveFile(file: MultipartFile, userId: number): Promise<{ id: number; path: string }> { if (isNil(file)) throw new NotFoundException('Have not any file to upload!'); const fileName = file.filename; @@ -37,7 +37,7 @@ export class UploadService { saveLocalFile(await file.toBuffer(), name); - await this.storageRepository.save({ + const storage = await this.storageRepository.save({ name, fileName, extName, @@ -47,6 +47,6 @@ export class UploadService { userId }); - return path; + return { path, id: storage.id }; } } From fab8618bb2942f683a6d1bd1005fc40eb3ebee7d Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 1 Mar 2024 15:31:33 +0800 Subject: [PATCH 14/64] =?UTF-8?q?feat:=20=E5=90=88=E5=90=8C=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/entity/common.entity.ts | 1 + src/modules/contract/contract.entity.ts | 4 ++++ src/modules/contract/contract.service.ts | 7 +++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/common/entity/common.entity.ts b/src/common/entity/common.entity.ts index f8c190e..1c9ad3f 100644 --- a/src/common/entity/common.entity.ts +++ b/src/common/entity/common.entity.ts @@ -28,6 +28,7 @@ export abstract class CommonEntity extends BaseEntity { @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date; + } export abstract class CompleteEntity extends CommonEntity { diff --git a/src/modules/contract/contract.entity.ts b/src/modules/contract/contract.entity.ts index afdb5cf..d75b05a 100644 --- a/src/modules/contract/contract.entity.ts +++ b/src/modules/contract/contract.entity.ts @@ -43,6 +43,10 @@ export class ContractEntity extends CommonEntity { @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意(字典)' }) status: number; + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + @ManyToMany(() => Storage, storage => storage.contracts) @JoinTable({ name: 'contract_storage', diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index b28eae9..59bcb22 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -39,7 +39,8 @@ export class ContractService { ...(title ? { title: Like(`%${title}%`) } : null), ...(isNumber(type) ? { type } : null), ...(isNumber(status) ? { status } : null) - }); + }) + .andWhere('contract.isDelete = 0'); return paginate(queryBuilder, { page, @@ -87,7 +88,8 @@ export class ContractService { * 删除 */ async delete(id: number): Promise { - await this.contractRepository.softDelete(id); + // 合同比较重要,做逻辑删除 + await this.contractRepository.update(id, { isDelete: 1 }); } /** @@ -99,6 +101,7 @@ export class ContractService { .where({ id }) + .andWhere('contract.isDelete = 0') .getOne(); return info; } From 24309f47d2c6deba69b2c5dba1fbe1a06b786ce6 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 1 Mar 2024 16:00:42 +0800 Subject: [PATCH 15/64] =?UTF-8?q?feat:=20=E8=A7=A3=E9=99=A4=E9=94=81?= =?UTF-8?q?=E5=B1=8Fapi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/auth/auth.controller.ts | 11 +++++++++++ src/modules/auth/auth.service.ts | 13 +++++++++++++ src/modules/auth/dto/auth.dto.ts | 9 ++++----- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index b3270a4..2922f8e 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -12,6 +12,8 @@ import { LoginDto, RegisterDto } from './dto/auth.dto'; import { LocalGuard } from './guards/local.guard'; import { LoginToken } from './models/auth.model'; import { CaptchaService } from './services/captcha.service'; +import { AuthUser } from './decorators/auth-user.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; @ApiTags('Auth - 认证模块') @UseGuards(LocalGuard) @@ -37,6 +39,15 @@ export class AuthController { return { token }; } + @Post('unlock') + @ApiSecurityAuth() + @ApiOperation({ summary: '屏幕解锁,使用密码和token' }) + @ApiResult({ type: LoginToken }) + async unlock(@Body() dto: LoginDto, @AuthUser() user: IAuthUser): Promise { + await this.authService.unlock(user.uid, dto.password); + return true; + } + @Post('register') @ApiOperation({ summary: '注册' }) async register(@Body() dto: RegisterDto): Promise { diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 8f97c97..9526203 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -80,6 +80,19 @@ export class AuthService { return token.accessToken; } + /** + * 解锁屏幕 + * 返回null则账号密码有误,不存在该用户 + */ + async unlock(uid: number, password: string): Promise { + const user = await this.userService.findUserById(uid); + if (isEmpty(user)) throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); + + const comparePassword = md5(`${password}${user.psalt}`); + if (user.password !== comparePassword) + throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); + } + /** * 效验账号密码 */ diff --git a/src/modules/auth/dto/auth.dto.ts b/src/modules/auth/dto/auth.dto.ts index 8cce9d0..436ed04 100644 --- a/src/modules/auth/dto/auth.dto.ts +++ b/src/modules/auth/dto/auth.dto.ts @@ -1,11 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString, Matches, MaxLength, MinLength } from 'class-validator'; +import { IsOptional, IsString, Matches, MaxLength, MinLength } from 'class-validator'; export class LoginDto { @ApiProperty({ description: '手机号/邮箱' }) - @IsString() - @MinLength(4) + @IsOptional() username: string; @ApiProperty({ description: '密码', example: 'a123456' }) @@ -15,11 +14,11 @@ export class LoginDto { password: string; @ApiProperty({ description: '验证码标识' }) - @IsString() + @IsOptional() captchaId: string; @ApiProperty({ description: '用户输入的验证码' }) - @IsString() + @IsOptional() @MinLength(4) @MaxLength(4) verifyCode: string; From fde00d6ad11b441b0ad3f469688a68f51ecfcf8b Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 1 Mar 2024 16:54:21 +0800 Subject: [PATCH 16/64] =?UTF-8?q?fix:=20=E5=90=88=E5=90=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=97=B6=E9=99=84=E4=BB=B6=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++++ src/modules/contract/contract.dto.ts | 3 ++- src/modules/contract/contract.service.ts | 26 +++++++++++++----------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a2a3f7b..a394936 100644 --- a/README.md +++ b/README.md @@ -94,9 +94,17 @@ pnpm migration:generate ```bash pnpm migration:revert ``` +4.执行sql覆盖docker中的数据库 + +```bash +docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa20240301.sql +``` 更多细节,请移步至[官方文档](https://typeorm.io/migrations) > [!TIP] > 如果你的`实体类`或`数据库配置`有更新,请执行`npm run build`后再进行数据库迁移相关操作。 +### 部署 +chmod +x deploy.sh +./deploy.sh \ No newline at end of file diff --git a/src/modules/contract/contract.dto.ts b/src/modules/contract/contract.dto.ts index 9b4ca8b..a1b7be7 100644 --- a/src/modules/contract/contract.dto.ts +++ b/src/modules/contract/contract.dto.ts @@ -54,7 +54,8 @@ export class ContractDto { export class ContractUpdateDto extends PartialType(ContractDto) { @ApiProperty({ description: '附件' }) - @IsArray({}) + @IsOptional() + @IsArray() fileIds: number[]; } export class ContractQueryDto extends IntersectionType( diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index 59bcb22..6ff5b66 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -68,19 +68,21 @@ export class ContractService { .leftJoinAndSelect('contract.files', 'files') .where('contract.id = :id', { id }) .getOne(); - const count = await this.storageRepository - .createQueryBuilder('storage') - .where('storage.id in(:fileIds)', { fileIds }) - .getCount(); - if (count !== fileIds?.length) { - throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(ContractEntity, 'files') + .of(id) + .addAndRemove(fileIds, contract.files); } - // 附件要批量更新 - await manager - .createQueryBuilder() - .relation(ContractEntity, 'files') - .of(id) - .addAndRemove(fileIds, contract.files); }); } From ba5e47b7662d8f36b03f2760a886893948b60363 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Mon, 4 Mar 2024 14:17:53 +0800 Subject: [PATCH 17/64] =?UTF-8?q?feat:=20=E5=8E=9F=E6=9D=90=E6=96=99?= =?UTF-8?q?=E7=9B=98=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/enum/index.ts | 6 + .../materials_inventory.controller.ts | 70 ++++++++- .../materials_inventory.dto.ts | 31 ++++ .../materials_inventory.entity.ts | 23 ++- .../materials_inventory.module.ts | 5 +- .../materials_inventory.service.ts | 138 +++++++++++++++++- .../system/dict-type/dict-type.controller.ts | 6 +- src/modules/system/dict-type/dict-type.dto.ts | 13 +- .../system/dict-type/dict-type.entity.ts | 8 +- .../system/dict-type/dict-type.service.ts | 18 ++- 10 files changed, 302 insertions(+), 16 deletions(-) create mode 100644 src/constants/enum/index.ts create mode 100644 src/modules/materials_inventory/materials_inventory.dto.ts diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts new file mode 100644 index 0000000..5444016 --- /dev/null +++ b/src/constants/enum/index.ts @@ -0,0 +1,6 @@ +export enum DictTypeStatusEnum { + /** 启用 */ + ENABLE = 1, + /** 禁用 */ + DISABLE = 0 +} diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts index c5d1b06..f9c0cdc 100644 --- a/src/modules/materials_inventory/materials_inventory.controller.ts +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -1,4 +1,70 @@ -import { Controller } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; +import { ContractQueryDto, ContractDto, ContractUpdateDto } from '../contract/contract.dto'; +import { MaterialsInventoryService } from './materials_inventory.service'; +import { MaterialsInventoryEntity } from './materials_inventory.entity'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +export const permissions = definePermission('app:materials_inventory', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('MaterialsI Inventory - 原材料盘点') +@ApiSecurityAuth() @Controller('materials-inventory') -export class MaterialsInventoryController {} +export class MaterialsInventoryController { + constructor(private miService: MaterialsInventoryService) {} + @Get() + @ApiOperation({ summary: '获取原材料盘点列表' }) + @ApiResult({ type: [MaterialsInventoryEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: ContractQueryDto) { + return this.miService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取原材料盘点信息' }) + @ApiResult({ type: ContractDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.miService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增原材料盘点' }) + @Perm(permissions.CREATE) + async create(@Body() dto: ContractDto): Promise { + await this.miService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新原材料盘点' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: ContractUpdateDto): Promise { + await this.miService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除原材料盘点' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.miService.delete(id); + } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: ContractUpdateDto + ): Promise { + await this.miService.unlinkAttachments(id, fileIds); + } +} diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts new file mode 100644 index 0000000..85c839f --- /dev/null +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { + IsArray, + IsDate, + IsDateString, + IsIn, + IsInt, + IsNumber, + IsOptional, + IsString, + Matches, + MinLength +} from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '../tools/storage/storage.entity'; + +export class MaterialsInventoryDto { + @ApiProperty({ description: '附件' }) + files: Storage[]; +} + +export class MaterialsInventoryUpdateDto extends PartialType(MaterialsInventoryDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} +export class MaterialsInventoryQueryDto extends IntersectionType( + PagerDto, + PartialType(MaterialsInventoryDto) +) {} diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index c0d429e..039574a 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -1,9 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Column, Entity } from 'typeorm'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '../tools/storage/storage.entity'; -@Entity({ name: 'meterials_inventory' }) -export class MeterialsInventoryEntity extends CommonEntity { +@Entity({ name: 'materials_inventory' }) +export class MaterialsInventoryEntity extends CommonEntity { @Column({ name: 'company_name', type: 'varchar', length: 255, comment: '公司名称' }) @ApiProperty({ description: '公司名称' }) companyName: number; @@ -182,4 +183,20 @@ export class MeterialsInventoryEntity extends CommonEntity { @Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true }) @ApiProperty({ description: '备注' }) remark: string; + + @Column({ name: 'project', type: 'varchar', length: 255, comment: '项目', nullable: false }) + @ApiProperty({ description: '项目' }) + project: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + + @ManyToMany(() => Storage, storage => storage.contracts) + @JoinTable({ + name: 'materials_inventory_storage', + joinColumn: { name: 'materials_inventory_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; } diff --git a/src/modules/materials_inventory/materials_inventory.module.ts b/src/modules/materials_inventory/materials_inventory.module.ts index 0572e3d..b376835 100644 --- a/src/modules/materials_inventory/materials_inventory.module.ts +++ b/src/modules/materials_inventory/materials_inventory.module.ts @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common'; import { MaterialsInventoryController } from './materials_inventory.controller'; import { MaterialsInventoryService } from './materials_inventory.service'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { MeterialsInventoryEntity } from './materials_inventory.entity'; +import { MaterialsInventoryEntity } from './materials_inventory.entity'; +import { StorageModule } from '../tools/storage/storage.module'; @Module({ - imports: [TypeOrmModule.forFeature([MeterialsInventoryEntity])], + imports: [TypeOrmModule.forFeature([MaterialsInventoryEntity]), StorageModule], controllers: [MaterialsInventoryController], providers: [MaterialsInventoryService] }) diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 44c7be2..00a7b7e 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -1,4 +1,140 @@ import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { MaterialsInventoryEntity } from './materials_inventory.entity'; +import { Storage } from '../tools/storage/storage.entity'; +import { EntityManager, Repository } from 'typeorm'; +import { + MaterialsInventoryDto, + MaterialsInventoryQueryDto, + MaterialsInventoryUpdateDto +} from './materials_inventory.dto'; +import { Pagination } from '~/helper/paginate/pagination'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { paginate } from '~/helper/paginate'; @Injectable() -export class MaterialsInventoryService {} +export class MaterialsInventoryService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(MaterialsInventoryEntity) + private materialsInventoryRepository: Repository, + @InjectRepository(Storage) + private storageRepository: Repository + ) {} + /** + * 列举所有角色:除去超级管理员 + */ + async findAll({ + page, + pageSize + // materialsInventoryNumber, + // title, + // type, + // status + }: MaterialsInventoryQueryDto): Promise> { + const queryBuilder = this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoin('materialsInventory.files', 'files') + .addSelect(['files.id', 'files.path']) + // .where({ + // ...(materialsInventoryNumber ? { materialsInventoryNumber: Like(`%${materialsInventoryNumber}%`) } : null), + // ...(title ? { title: Like(`%${title}%`) } : null), + // ...(isNumber(type) ? { type } : null), + // ...(isNumber(status) ? { status } : null) + // }) + .andWhere('materialsInventory.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: MaterialsInventoryDto): Promise { + await this.materialsInventoryRepository.insert(dto); + } + + /** + * 更新 + */ + async update( + id: number, + { fileIds, ...data }: Partial + ): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(MaterialsInventoryEntity, id, { + ...data + }); + const materialsInventory = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoinAndSelect('materialsInventory.files', 'files') + .where('materialsInventory.id = :id', { id }) + .getOne(); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(MaterialsInventoryEntity, 'files') + .of(id) + .addAndRemove(fileIds, materialsInventory.files); + } + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 合同比较重要,做逻辑删除 + await this.materialsInventoryRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个合同信息 + */ + async info(id: number) { + const info = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .where({ + id + }) + .andWhere('materialsInventory.isDelete = 0') + .getOne(); + return info; + } + + /** + * 解除附件关联 + * @param id 合同ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const materialsInventory = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoinAndSelect('materialsInventory.files', 'files') + .where('materialsInventory.id = :id', { id }) + .getOne(); + const linkedFiles = materialsInventory.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(MaterialsInventoryEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, materialsInventory.files); + }); + } +} diff --git a/src/modules/system/dict-type/dict-type.controller.ts b/src/modules/system/dict-type/dict-type.controller.ts index 8c3c347..d434dc8 100644 --- a/src/modules/system/dict-type/dict-type.controller.ts +++ b/src/modules/system/dict-type/dict-type.controller.ts @@ -34,12 +34,12 @@ export class DictTypeController { return this.dictTypeService.page(dto); } - @Get('select-options') + @Post('all') @ApiOperation({ summary: '一次性获取所有的字典类型(不分页)' }) @ApiResult({ type: [DictTypeEntity] }) @Perm(permissions.LIST) - async getAll(): Promise { - return this.dictTypeService.getAll(); + async getAll(@Body() dto: DictTypeQueryDto): Promise { + return this.dictTypeService.getAll(dto); } @Post() diff --git a/src/modules/system/dict-type/dict-type.dto.ts b/src/modules/system/dict-type/dict-type.dto.ts index 010479f..9ee5771 100644 --- a/src/modules/system/dict-type/dict-type.dto.ts +++ b/src/modules/system/dict-type/dict-type.dto.ts @@ -1,9 +1,10 @@ import { ApiProperty, PartialType } from '@nestjs/swagger'; -import { IsInt, IsOptional, IsString, MinLength } from 'class-validator'; +import { IsArray, IsBoolean, IsInt, IsOptional, IsString, MinLength } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; import { DictTypeEntity } from './dict-type.entity'; +import { isBoolean } from 'lodash'; export class DictTypeDto extends PartialType(DictTypeEntity) { @ApiProperty({ description: '字典类型名称' }) @@ -37,4 +38,14 @@ export class DictTypeQueryDto extends PagerDto { @IsString() @IsOptional() code: string; + + @ApiProperty({ description: '是否用于前端store缓存' }) + @IsOptional() + @IsBoolean() + withItems: boolean; + + @ApiProperty({ description: '需要前端store缓存的code' }) + @IsArray() + @IsOptional() + storeCodes: string[]; } diff --git a/src/modules/system/dict-type/dict-type.entity.ts b/src/modules/system/dict-type/dict-type.entity.ts index e4f0e8c..d9e46ad 100644 --- a/src/modules/system/dict-type/dict-type.entity.ts +++ b/src/modules/system/dict-type/dict-type.entity.ts @@ -1,7 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Column, Entity } from 'typeorm'; +import { Column, Entity, OneToMany, Relation } from 'typeorm'; import { CompleteEntity } from '~/common/entity/common.entity'; +import { DictItemEntity } from '../dict-item/dict-item.entity'; @Entity({ name: 'sys_dict_type' }) export class DictTypeEntity extends CompleteEntity { @@ -20,4 +21,9 @@ export class DictTypeEntity extends CompleteEntity { @Column({ type: 'varchar', nullable: true }) @ApiProperty({ description: '备注' }) remark: string; + + @OneToMany(() => DictItemEntity, dictItem => dictItem.type, { + cascade: true + }) + dictItems: Relation; } diff --git a/src/modules/system/dict-type/dict-type.service.ts b/src/modules/system/dict-type/dict-type.service.ts index cda4d92..dd422dd 100644 --- a/src/modules/system/dict-type/dict-type.service.ts +++ b/src/modules/system/dict-type/dict-type.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Like, Repository } from 'typeorm'; +import { In, Like, Repository } from 'typeorm'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; @@ -10,6 +10,7 @@ import { Pagination } from '~/helper/paginate/pagination'; import { DictTypeEntity } from '~/modules/system/dict-type/dict-type.entity'; import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto'; +import { DictTypeStatusEnum } from '~/constants/enum'; @Injectable() export class DictTypeService { @@ -36,8 +37,19 @@ export class DictTypeService { } /** 一次性获取所有的字典类型 */ - async getAll() { - return this.dictTypeRepository.find(); + async getAll({ withItems, storeCodes }: DictTypeQueryDto) { + const sqb = this.dictTypeRepository + .createQueryBuilder('dict_type') + .addSelect(['dict_type.name', 'dict_type.code', 'dict_type.status']); + if (withItems) { + sqb + .leftJoin('dict_type.dictItems', 'dictItems') + .addSelect(['dictItems.id', 'dictItems.value', 'dictItems.label', 'dictItems.status']); + } + sqb.where('dict_type.status =:status', { status: DictTypeStatusEnum.ENABLE }); + withItems && sqb.andWhere('dictItems.status =:status', { status: DictTypeStatusEnum.ENABLE }); + storeCodes && sqb.andWhere({ code: In(storeCodes) }); + return sqb.getMany(); } /** From 944b5adbff13896569edb4a87193400465af765e Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Mon, 4 Mar 2024 16:34:54 +0800 Subject: [PATCH 18/64] =?UTF-8?q?feat:=20=E4=B9=99=E6=96=B9=E5=85=AC?= =?UTF-8?q?=E5=8F=B8=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 5 +- src/constants/enum/index.ts | 8 ++ src/modules/company/company.controller.ts | 79 +++++++++++ src/modules/company/company.dto.ts | 39 ++++++ src/modules/company/company.entity.ts | 29 ++++ src/modules/company/company.module.ts | 14 ++ src/modules/company/company.service.ts | 127 ++++++++++++++++++ src/modules/contract/contract.service.ts | 16 +-- .../materials_inventory.entity.ts | 2 +- src/modules/tools/storage/storage.entity.ts | 10 ++ src/shared/database/field-search/index.ts | 24 ++++ 11 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 src/modules/company/company.controller.ts create mode 100644 src/modules/company/company.dto.ts create mode 100644 src/modules/company/company.entity.ts create mode 100644 src/modules/company/company.module.ts create mode 100644 src/modules/company/company.service.ts create mode 100644 src/shared/database/field-search/index.ts diff --git a/src/app.module.ts b/src/app.module.ts index de220bc..27710c1 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -29,6 +29,7 @@ import { VehicleUsageModule } from './modules/vehicle-usage/vehicle-usage.module import { VehicleUsageController } from './modules/vehicle-usage/vehicle-usage.controller'; import { VehicleUsageService } from './modules/vehicle-usage/vehicle-usage.service'; import { MaterialsInventoryModule } from './modules/materials_inventory/materials_inventory.module'; +import { CompanyModule } from './modules/company/company.module'; @Module({ imports: [ @@ -61,7 +62,9 @@ import { MaterialsInventoryModule } from './modules/materials_inventory/material VehicleUsageModule, - MaterialsInventoryModule + MaterialsInventoryModule, + + CompanyModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts index 5444016..75f926d 100644 --- a/src/constants/enum/index.ts +++ b/src/constants/enum/index.ts @@ -1,6 +1,14 @@ +// 字典项status export enum DictTypeStatusEnum { /** 启用 */ ENABLE = 1, /** 禁用 */ DISABLE = 0 } + +// 业务模块枚举 +export enum BusinessModuleEnum { + CONTRACT = 1, + MATERIALS_INVENTORY = 2, + COMPANY = 3 +} diff --git a/src/modules/company/company.controller.ts b/src/modules/company/company.controller.ts new file mode 100644 index 0000000..0333d14 --- /dev/null +++ b/src/modules/company/company.controller.ts @@ -0,0 +1,79 @@ +import { + Body, + Controller, + Get, + Query, + Put, + Delete, + Post, + BadRequestException +} from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { CompanyService } from './company.service'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { CompanyEntity } from './company.entity'; +import { CompanyDto, CompanyQueryDto, CompanyUpdateDto } from './company.dto'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +export const permissions = definePermission('app:company', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('Company - 公司') +@ApiSecurityAuth() +@Controller('company') +export class CompanyController { + constructor(private companyService: CompanyService) {} + + @Get() + @ApiOperation({ summary: '获取公司列表' }) + @ApiResult({ type: [CompanyEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: CompanyQueryDto) { + return this.companyService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取公司信息' }) + @ApiResult({ type: CompanyDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.companyService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增公司' }) + @Perm(permissions.CREATE) + async create(@Body() dto: CompanyDto): Promise { + await this.companyService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新公司' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: CompanyUpdateDto): Promise { + await this.companyService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除公司' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.companyService.delete(id); + } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: CompanyUpdateDto + ): Promise { + await this.companyService.unlinkAttachments(id, fileIds); + } +} diff --git a/src/modules/company/company.dto.ts b/src/modules/company/company.dto.ts new file mode 100644 index 0000000..d388b4d --- /dev/null +++ b/src/modules/company/company.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { + IsArray, + IsDate, + IsDateString, + IsIn, + IsInt, + IsNumber, + IsOptional, + IsString, + Matches, + MinLength +} from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '../tools/storage/storage.entity'; +import { IsUnique } from '~/shared/database/constraints/unique.constraint'; +import { CompanyEntity } from './company.entity'; + +export class CompanyDto { + @ApiProperty({ description: '公司名称' }) + @IsUnique(CompanyEntity, { message: '已存在同名公司' }) + @IsString() + name: string; + + @ApiProperty({ description: '附件' }) + files: Storage[]; +} + +export class CompanyUpdateDto extends PartialType(CompanyDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} + +export class CompanyQueryDto extends IntersectionType( + PagerDto, + PartialType(CompanyDto) +) {} diff --git a/src/modules/company/company.entity.ts b/src/modules/company/company.entity.ts new file mode 100644 index 0000000..38d7428 --- /dev/null +++ b/src/modules/company/company.entity.ts @@ -0,0 +1,29 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '../tools/storage/storage.entity'; + +@Entity({ name: 'company' }) +export class CompanyEntity extends CommonEntity { + @Column({ + name: 'name', + type: 'varchar', + unique: true, + length: 255, + comment: '公司名称' + }) + @ApiProperty({ description: '公司名称' }) + name: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + + @ManyToMany(() => Storage, storage => storage.companys) + @JoinTable({ + name: 'company_storage', + joinColumn: { name: 'company_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; +} diff --git a/src/modules/company/company.module.ts b/src/modules/company/company.module.ts new file mode 100644 index 0000000..5fba5b1 --- /dev/null +++ b/src/modules/company/company.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { CompanyController } from './company.controller'; +import { CompanyService } from './company.service'; +import { CompanyEntity } from './company.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { StorageModule } from '../tools/storage/storage.module'; +import { DatabaseModule } from '~/shared/database/database.module'; + +@Module({ + imports: [TypeOrmModule.forFeature([CompanyEntity]), StorageModule, DatabaseModule], + controllers: [CompanyController], + providers: [CompanyService] +}) +export class CompanyModule {} diff --git a/src/modules/company/company.service.ts b/src/modules/company/company.service.ts new file mode 100644 index 0000000..c0f2465 --- /dev/null +++ b/src/modules/company/company.service.ts @@ -0,0 +1,127 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { CompanyEntity } from './company.entity'; +import { EntityManager, Like, Repository } from 'typeorm'; +import { CompanyDto, CompanyQueryDto, CompanyUpdateDto } from './company.dto'; +import { Pagination } from '~/helper/paginate/pagination'; +import { paginate } from '~/helper/paginate'; +import { Storage } from '../tools/storage/storage.entity'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { fieldSearch } from '~/shared/database/field-search'; + +@Injectable() +export class CompanyService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(CompanyEntity) + private companyRepository: Repository, + @InjectRepository(Storage) + private storageRepository: Repository + ) {} + + /** + * 查询所有公司 + */ + async findAll({ + page, + pageSize, + ...fields + }: CompanyQueryDto): Promise> { + const queryBuilder = this.companyRepository + .createQueryBuilder('company') + .leftJoin('company.files', 'files') + .addSelect(['files.id', 'files.path']) + .where(fieldSearch(fields)) + .andWhere('company.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: CompanyDto): Promise { + await this.companyRepository.insert(dto); + } + + /** + * 更新 + */ + async update(id: number, { fileIds, ...data }: Partial): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(CompanyEntity, id, { + ...data + }); + const company = await this.companyRepository + .createQueryBuilder('company') + .leftJoinAndSelect('company.files', 'files') + .where('company.id = :id', { id }) + .getOne(); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(CompanyEntity, 'files') + .of(id) + .addAndRemove(fileIds, company.files); + } + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 合同比较重要,做逻辑删除 + await this.companyRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个合同信息 + */ + async info(id: number) { + const info = await this.companyRepository + .createQueryBuilder('company') + .where({ + id + }) + .andWhere('company.isDelete = 0') + .getOne(); + return info; + } + + /** + * 解除附件关联 + * @param id 合同ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const company = await this.companyRepository + .createQueryBuilder('company') + .leftJoinAndSelect('company.files', 'files') + .where('company.id = :id', { id }) + .getOne(); + const linkedFiles = company.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(CompanyEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, company.files); + }); + } +} diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index 6ff5b66..030ea1a 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -9,6 +9,7 @@ import { paginate } from '~/helper/paginate'; import { Storage } from '../tools/storage/storage.entity'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; +import { fieldSearch } from '~/shared/database/field-search'; @Injectable() export class ContractService { @@ -19,27 +20,20 @@ export class ContractService { @InjectRepository(Storage) private storageRepository: Repository ) {} + /** - * 列举所有角色:除去超级管理员 + * 查找所有合同 */ async findAll({ page, pageSize, - contractNumber, - title, - type, - status + ...fields }: ContractQueryDto): Promise> { const queryBuilder = this.contractRepository .createQueryBuilder('contract') .leftJoin('contract.files', 'files') .addSelect(['files.id', 'files.path']) - .where({ - ...(contractNumber ? { contractNumber: Like(`%${contractNumber}%`) } : null), - ...(title ? { title: Like(`%${title}%`) } : null), - ...(isNumber(type) ? { type } : null), - ...(isNumber(status) ? { status } : null) - }) + .where(fieldSearch(fields)) .andWhere('contract.isDelete = 0'); return paginate(queryBuilder, { diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index 039574a..c0432f4 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -192,7 +192,7 @@ export class MaterialsInventoryEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; - @ManyToMany(() => Storage, storage => storage.contracts) + @ManyToMany(() => Storage, storage => storage.materialsInventories) @JoinTable({ name: 'materials_inventory_storage', joinColumn: { name: 'materials_inventory_id', referencedColumnName: 'id' }, diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index 69253a5..d84c97e 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -2,7 +2,9 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; import { Column, Entity, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; +import { CompanyEntity } from '~/modules/company/company.entity'; import { ContractEntity } from '~/modules/contract/contract.entity'; +import { MaterialsInventoryEntity } from '~/modules/materials_inventory/materials_inventory.entity'; @Entity({ name: 'tool_storage' }) export class Storage extends CommonEntity { @@ -42,4 +44,12 @@ export class Storage extends CommonEntity { @ApiHideProperty() @ManyToMany(() => ContractEntity, contract => contract.files) contracts: Relation; + + @ApiHideProperty() + @ManyToMany(() => CompanyEntity, company => company.files) + companys: Relation; + + @ApiHideProperty() + @ManyToMany(() => MaterialsInventoryEntity, materialsInventories => materialsInventories.files) + materialsInventories: Relation; } diff --git a/src/shared/database/field-search/index.ts b/src/shared/database/field-search/index.ts new file mode 100644 index 0000000..f44e3d6 --- /dev/null +++ b/src/shared/database/field-search/index.ts @@ -0,0 +1,24 @@ +import { isNumber } from 'lodash'; +import { Like, ObjectLiteral, ObjectType } from 'typeorm'; +export const fieldSearch = (entity: Partial): ObjectLiteral => { + let result = {}; + for (let key in entity) { + if (entity.hasOwnProperty(key)) { + switch (typeof entity[key]) { + case 'number': + result = { ...result, ...(isNumber(entity[key]) ? { [key]: entity[key] } : null) }; + break; + case 'string': + result = { ...result, ...(entity[key] ? { [key]: Like(`%${entity[key]}%`) } : null) }; + break; + case 'boolean': + result = { ...result, ...(entity[key] === true ? { [key]: 1 } : { [key]: 0 }) }; + break; + default: + result = { ...result, ...(entity[key] ? { [key]: entity[key] } : null) }; + break; + } + } + } + return result; +}; From 47b7015c16a4c5a836bfaae0b8961f88277a6b5b Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Mon, 4 Mar 2024 17:31:28 +0800 Subject: [PATCH 19/64] =?UTF-8?q?feat:=20=E5=8E=9F=E6=9D=90=E6=96=99?= =?UTF-8?q?=E5=87=BA=E5=85=A5=E5=BA=93=E8=AE=B0=E5=BD=95=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 5 +- src/constants/enum/index.ts | 6 + .../in_out/materials_in_out.controller.ts | 70 +++++++++ .../in_out/materials_in_out.dto.ts | 31 ++++ .../in_out/materials_in_out.entity.ts | 105 ++++++++++++++ .../in_out/materials_in_out.service.ts | 134 ++++++++++++++++++ .../materials_inventory.controller.ts | 10 -- .../materials_inventory.dto.ts | 12 +- .../materials_inventory.entity.ts | 8 -- .../materials_inventory.service.ts | 71 +--------- src/modules/product/product.controller.ts | 79 +++++++++++ src/modules/product/product.dto.ts | 39 +++++ src/modules/product/product.entity.ts | 29 ++++ src/modules/product/product.module.ts | 14 ++ src/modules/product/product.service.ts | 127 +++++++++++++++++ src/modules/tools/storage/storage.entity.ts | 10 +- 16 files changed, 652 insertions(+), 98 deletions(-) create mode 100644 src/modules/materials_inventory/in_out/materials_in_out.controller.ts create mode 100644 src/modules/materials_inventory/in_out/materials_in_out.dto.ts create mode 100644 src/modules/materials_inventory/in_out/materials_in_out.entity.ts create mode 100644 src/modules/materials_inventory/in_out/materials_in_out.service.ts create mode 100644 src/modules/product/product.controller.ts create mode 100644 src/modules/product/product.dto.ts create mode 100644 src/modules/product/product.entity.ts create mode 100644 src/modules/product/product.module.ts create mode 100644 src/modules/product/product.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 27710c1..93b7423 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -30,6 +30,7 @@ import { VehicleUsageController } from './modules/vehicle-usage/vehicle-usage.co import { VehicleUsageService } from './modules/vehicle-usage/vehicle-usage.service'; import { MaterialsInventoryModule } from './modules/materials_inventory/materials_inventory.module'; import { CompanyModule } from './modules/company/company.module'; +import { ProductModule } from './modules/product/product.module'; @Module({ imports: [ @@ -64,7 +65,9 @@ import { CompanyModule } from './modules/company/company.module'; MaterialsInventoryModule, - CompanyModule + CompanyModule, + + ProductModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts index 75f926d..0fd9687 100644 --- a/src/constants/enum/index.ts +++ b/src/constants/enum/index.ts @@ -12,3 +12,9 @@ export enum BusinessModuleEnum { MATERIALS_INVENTORY = 2, COMPANY = 3 } + +// 原材料出库或者入库 +export enum MaterialsInOrOutEnum { + In, + Out +} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts new file mode 100644 index 0000000..b7144e0 --- /dev/null +++ b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts @@ -0,0 +1,70 @@ +import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { MaterialsInOutService } from './materials_in_out.service'; +import { MaterialsInOutEntity } from './materials_in_out.entity'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { definePermission, Perm } from '~/modules/auth/decorators/permission.decorator'; +import { MaterialsInOutQueryDto, MaterialsInOutDto, MaterialsInOutUpdateDto } from './materials_in_out.dto'; + +export const permissions = definePermission('materials_inventory:history_in_out', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('Materials In Out History - 原材料出入库管理') +@ApiSecurityAuth() +@Controller('materials-in-out') +export class MaterialsInOutController { + constructor(private miService: MaterialsInOutService) {} + @Get() + @ApiOperation({ summary: '获取原材料盘点列表' }) + @ApiResult({ type: [MaterialsInOutEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: MaterialsInOutQueryDto) { + return this.miService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取原材料盘点信息' }) + @ApiResult({ type: MaterialsInOutDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.miService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增原材料盘点' }) + @Perm(permissions.CREATE) + async create(@Body() dto: MaterialsInOutDto): Promise { + await this.miService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新原材料盘点' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: MaterialsInOutUpdateDto): Promise { + await this.miService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除原材料盘点' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.miService.delete(id); + } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: MaterialsInOutUpdateDto + ): Promise { + await this.miService.unlinkAttachments(id, fileIds); + } +} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts new file mode 100644 index 0000000..9971663 --- /dev/null +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -0,0 +1,31 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { + IsArray, + IsDate, + IsDateString, + IsIn, + IsInt, + IsNumber, + IsOptional, + IsString, + Matches, + MinLength +} from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '~/modules/tools/storage/storage.entity'; + +export class MaterialsInOutDto { + @ApiProperty({ description: '附件' }) + files: Storage[]; +} + +export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} +export class MaterialsInOutQueryDto extends IntersectionType( + PagerDto, + PartialType(MaterialsInOutDto) +) {} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts new file mode 100644 index 0000000..2cd9370 --- /dev/null +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -0,0 +1,105 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; +import { MaterialsInOrOutEnum } from '~/constants/enum'; +import { Storage } from '~/modules/tools/storage/storage.entity'; + +@Entity({ name: 'materials_in_out' }) +export class MaterialsInOutEntity extends CommonEntity { + @Column({ + name: 'product', + type: 'int', + comment: '产品名称' + }) + @ApiProperty({ description: '产品名称' }) + product: number; + + @Column({ + name: 'unit', + type: 'int', + comment: '单位(字典)' + }) + @ApiProperty({ description: '单位(字典)' }) + unit: number; + + @Column({ + name: 'inOrOut', + type: 'tinyint', + comment: '入库或出库' + }) + @ApiProperty({ description: '入库或出库 0:入库 1:出库' }) + inOrOut: MaterialsInOrOutEnum; + + @Column({ + name: 'inventory_time', + type: 'date', + nullable: true, + comment: '时间' + }) + @ApiProperty({ description: '时间' }) + time: Date; + + @Column({ + name: 'quantity', + type: 'int', + default: 0, + comment: '数量' + }) + @ApiProperty({ description: '数量' }) + quantity: number; + + @Column({ + name: 'unit_price', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '单价' + }) + @ApiProperty({ description: '入库单价' }) + unitPrice: number; + + @Column({ + name: 'amount', + type: 'decimal', + precision: 10, + default: 0, + scale: 2, + comment: '金额' + }) + @ApiProperty({ description: '金额' }) + amount: number; + + @Column({ name: 'agent', type: 'varchar', length: 50, comment: '经办人', nullable: true }) + @ApiProperty({ description: '经办人' }) + agent: string; + + @Column({ + name: 'issuance_number', + type: 'varchar', + length: 100, + comment: '领料单号' + }) + @ApiProperty({ description: '领料单号' }) + issuanceNumber: string; + + @Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true }) + @ApiProperty({ description: '备注' }) + remark: string; + + @Column({ name: 'project', type: 'varchar', length: 255, comment: '项目', nullable: false }) + @ApiProperty({ description: '项目' }) + project: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + + @ManyToMany(() => Storage, storage => storage.materialsInOut) + @JoinTable({ + name: 'materials_in_out_storage', + joinColumn: { name: 'materials_in_out_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; +} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts new file mode 100644 index 0000000..dc72e58 --- /dev/null +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -0,0 +1,134 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; + +import { EntityManager, Repository } from 'typeorm'; +import { Pagination } from '~/helper/paginate/pagination'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { paginate } from '~/helper/paginate'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { MaterialsInOutQueryDto, MaterialsInOutDto, MaterialsInOutUpdateDto } from './materials_in_out.dto'; +import { MaterialsInOutEntity } from './materials_in_out.entity'; + +@Injectable() +export class MaterialsInOutService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(MaterialsInOutEntity) + private materialsInventoryRepository: Repository, + @InjectRepository(Storage) + private storageRepository: Repository + ) {} + /** + * 查询所有出入库记录 + */ + async findAll({ + page, + pageSize + // materialsInventoryNumber, + // title, + // type, + // status + }: MaterialsInOutQueryDto): Promise> { + const queryBuilder = this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoin('materialsInventory.files', 'files') + .addSelect(['files.id', 'files.path']) + // .where({ + // ...(materialsInventoryNumber ? { materialsInventoryNumber: Like(`%${materialsInventoryNumber}%`) } : null), + // ...(title ? { title: Like(`%${title}%`) } : null), + // ...(isNumber(type) ? { type } : null), + // ...(isNumber(status) ? { status } : null) + // }) + .andWhere('materialsInventory.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: MaterialsInOutDto): Promise { + await this.materialsInventoryRepository.insert(dto); + } + + /** + * 更新 + */ + async update(id: number, { fileIds, ...data }: Partial): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(MaterialsInOutEntity, id, { + ...data + }); + const materialsInventory = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoinAndSelect('materialsInventory.files', 'files') + .where('materialsInventory.id = :id', { id }) + .getOne(); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(MaterialsInOutEntity, 'files') + .of(id) + .addAndRemove(fileIds, materialsInventory.files); + } + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 合同比较重要,做逻辑删除 + await this.materialsInventoryRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个合同信息 + */ + async info(id: number) { + const info = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .where({ + id + }) + .andWhere('materialsInventory.isDelete = 0') + .getOne(); + return info; + } + + /** + * 解除附件关联 + * @param id 合同ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const materialsInventory = await this.materialsInventoryRepository + .createQueryBuilder('materialsInventory') + .leftJoinAndSelect('materialsInventory.files', 'files') + .where('materialsInventory.id = :id', { id }) + .getOne(); + const linkedFiles = materialsInventory.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(MaterialsInOutEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, materialsInventory.files); + }); + } +} diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts index f9c0cdc..18a1f27 100644 --- a/src/modules/materials_inventory/materials_inventory.controller.ts +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -57,14 +57,4 @@ export class MaterialsInventoryController { async delete(@IdParam() id: number): Promise { await this.miService.delete(id); } - - @Put('unlink-attachments/:id') - @ApiOperation({ summary: '附件解除关联' }) - @Perm(permissions.UPDATE) - async unlinkAttachments( - @IdParam() id: number, - @Body() { fileIds }: ContractUpdateDto - ): Promise { - await this.miService.unlinkAttachments(id, fileIds); - } } diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts index 85c839f..bee1740 100644 --- a/src/modules/materials_inventory/materials_inventory.dto.ts +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -14,17 +14,9 @@ import { import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; -export class MaterialsInventoryDto { - @ApiProperty({ description: '附件' }) - files: Storage[]; -} +export class MaterialsInventoryDto {} -export class MaterialsInventoryUpdateDto extends PartialType(MaterialsInventoryDto) { - @ApiProperty({ description: '附件' }) - @IsOptional() - @IsArray() - fileIds: number[]; -} +export class MaterialsInventoryUpdateDto extends PartialType(MaterialsInventoryDto) {} export class MaterialsInventoryQueryDto extends IntersectionType( PagerDto, PartialType(MaterialsInventoryDto) diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index c0432f4..e80b830 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -191,12 +191,4 @@ export class MaterialsInventoryEntity extends CommonEntity { @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; - - @ManyToMany(() => Storage, storage => storage.materialsInventories) - @JoinTable({ - name: 'materials_inventory_storage', - joinColumn: { name: 'materials_inventory_id', referencedColumnName: 'id' }, - inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } - }) - files: Relation; } diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 00a7b7e..927337b 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; import { MaterialsInventoryEntity } from './materials_inventory.entity'; -import { Storage } from '../tools/storage/storage.entity'; import { EntityManager, Repository } from 'typeorm'; import { MaterialsInventoryDto, @@ -9,8 +8,7 @@ import { MaterialsInventoryUpdateDto } from './materials_inventory.dto'; import { Pagination } from '~/helper/paginate/pagination'; -import { BusinessException } from '~/common/exceptions/biz.exception'; -import { ErrorEnum } from '~/constants/error-code.constant'; + import { paginate } from '~/helper/paginate'; @Injectable() @@ -18,31 +16,17 @@ export class MaterialsInventoryService { constructor( @InjectEntityManager() private entityManager: EntityManager, @InjectRepository(MaterialsInventoryEntity) - private materialsInventoryRepository: Repository, - @InjectRepository(Storage) - private storageRepository: Repository + private materialsInventoryRepository: Repository ) {} /** - * 列举所有角色:除去超级管理员 + * 查询所有盘点信息 */ async findAll({ page, pageSize - // materialsInventoryNumber, - // title, - // type, - // status }: MaterialsInventoryQueryDto): Promise> { const queryBuilder = this.materialsInventoryRepository .createQueryBuilder('materialsInventory') - .leftJoin('materialsInventory.files', 'files') - .addSelect(['files.id', 'files.path']) - // .where({ - // ...(materialsInventoryNumber ? { materialsInventoryNumber: Like(`%${materialsInventoryNumber}%`) } : null), - // ...(title ? { title: Like(`%${title}%`) } : null), - // ...(isNumber(type) ? { type } : null), - // ...(isNumber(status) ? { status } : null) - // }) .andWhere('materialsInventory.isDelete = 0'); return paginate(queryBuilder, { @@ -61,34 +45,11 @@ export class MaterialsInventoryService { /** * 更新 */ - async update( - id: number, - { fileIds, ...data }: Partial - ): Promise { + async update(id: number, data: Partial): Promise { await this.entityManager.transaction(async manager => { await manager.update(MaterialsInventoryEntity, id, { ...data }); - const materialsInventory = await this.materialsInventoryRepository - .createQueryBuilder('materialsInventory') - .leftJoinAndSelect('materialsInventory.files', 'files') - .where('materialsInventory.id = :id', { id }) - .getOne(); - if (fileIds?.length) { - const count = await this.storageRepository - .createQueryBuilder('storage') - .where('storage.id in(:fileIds)', { fileIds }) - .getCount(); - if (count !== fileIds?.length) { - throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); - } - // 附件要批量更新 - await manager - .createQueryBuilder() - .relation(MaterialsInventoryEntity, 'files') - .of(id) - .addAndRemove(fileIds, materialsInventory.files); - } }); } @@ -113,28 +74,4 @@ export class MaterialsInventoryService { .getOne(); return info; } - - /** - * 解除附件关联 - * @param id 合同ID - * @param fileIds 附件ID - */ - async unlinkAttachments(id: number, fileIds: number[]) { - await this.entityManager.transaction(async manager => { - const materialsInventory = await this.materialsInventoryRepository - .createQueryBuilder('materialsInventory') - .leftJoinAndSelect('materialsInventory.files', 'files') - .where('materialsInventory.id = :id', { id }) - .getOne(); - const linkedFiles = materialsInventory.files - .map(item => item.id) - .filter(item => !fileIds.includes(item)); - // 附件要批量更新 - await manager - .createQueryBuilder() - .relation(MaterialsInventoryEntity, 'files') - .of(id) - .addAndRemove(linkedFiles, materialsInventory.files); - }); - } } diff --git a/src/modules/product/product.controller.ts b/src/modules/product/product.controller.ts new file mode 100644 index 0000000..f1c28c2 --- /dev/null +++ b/src/modules/product/product.controller.ts @@ -0,0 +1,79 @@ +import { + Body, + Controller, + Get, + Query, + Put, + Delete, + Post, + BadRequestException +} from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { ProductService } from './product.service'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { ProductEntity } from './product.entity'; +import { ProductDto, ProductQueryDto, ProductUpdateDto } from './product.dto'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +export const permissions = definePermission('app:product', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('Product - 产品') +@ApiSecurityAuth() +@Controller('product') +export class ProductController { + constructor(private productService: ProductService) {} + + @Get() + @ApiOperation({ summary: '获取产品列表' }) + @ApiResult({ type: [ProductEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: ProductQueryDto) { + return this.productService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取产品信息' }) + @ApiResult({ type: ProductDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.productService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增产品' }) + @Perm(permissions.CREATE) + async create(@Body() dto: ProductDto): Promise { + await this.productService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新产品' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: ProductUpdateDto): Promise { + await this.productService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除产品' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.productService.delete(id); + } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: ProductUpdateDto + ): Promise { + await this.productService.unlinkAttachments(id, fileIds); + } +} diff --git a/src/modules/product/product.dto.ts b/src/modules/product/product.dto.ts new file mode 100644 index 0000000..60414bd --- /dev/null +++ b/src/modules/product/product.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { + IsArray, + IsDate, + IsDateString, + IsIn, + IsInt, + IsNumber, + IsOptional, + IsString, + Matches, + MinLength +} from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '../tools/storage/storage.entity'; +import { IsUnique } from '~/shared/database/constraints/unique.constraint'; +import { ProductEntity } from './product.entity'; + +export class ProductDto { + @ApiProperty({ description: '产品名称' }) + @IsUnique(ProductEntity, { message: '已存在同名产品' }) + @IsString() + name: string; + + @ApiProperty({ description: '附件' }) + files: Storage[]; +} + +export class ProductUpdateDto extends PartialType(ProductDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} + +export class ProductQueryDto extends IntersectionType( + PagerDto, + PartialType(ProductDto) +) {} diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts new file mode 100644 index 0000000..c68bace --- /dev/null +++ b/src/modules/product/product.entity.ts @@ -0,0 +1,29 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '../tools/storage/storage.entity'; + +@Entity({ name: 'product' }) +export class ProductEntity extends CommonEntity { + @Column({ + name: 'name', + type: 'varchar', + unique: true, + length: 255, + comment: '产品名称' + }) + @ApiProperty({ description: '产品名称' }) + name: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + + @ManyToMany(() => Storage, storage => storage.products) + @JoinTable({ + name: 'product_storage', + joinColumn: { name: 'product_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; +} diff --git a/src/modules/product/product.module.ts b/src/modules/product/product.module.ts new file mode 100644 index 0000000..75ea48f --- /dev/null +++ b/src/modules/product/product.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { ProductController } from './product.controller'; +import { ProductService } from './product.service'; +import { ProductEntity } from './product.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { StorageModule } from '../tools/storage/storage.module'; +import { DatabaseModule } from '~/shared/database/database.module'; + +@Module({ + imports: [TypeOrmModule.forFeature([ProductEntity]), StorageModule, DatabaseModule], + controllers: [ProductController], + providers: [ProductService] +}) +export class ProductModule {} diff --git a/src/modules/product/product.service.ts b/src/modules/product/product.service.ts new file mode 100644 index 0000000..df0589f --- /dev/null +++ b/src/modules/product/product.service.ts @@ -0,0 +1,127 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { ProductEntity } from './product.entity'; +import { EntityManager, Like, Repository } from 'typeorm'; +import { ProductDto, ProductQueryDto, ProductUpdateDto } from './product.dto'; +import { Pagination } from '~/helper/paginate/pagination'; +import { paginate } from '~/helper/paginate'; +import { Storage } from '../tools/storage/storage.entity'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { fieldSearch } from '~/shared/database/field-search'; + +@Injectable() +export class ProductService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(ProductEntity) + private productRepository: Repository, + @InjectRepository(Storage) + private storageRepository: Repository + ) {} + + /** + * 查询所有产品 + */ + async findAll({ + page, + pageSize, + ...fields + }: ProductQueryDto): Promise> { + const queryBuilder = this.productRepository + .createQueryBuilder('product') + .leftJoin('product.files', 'files') + .addSelect(['files.id', 'files.path']) + .where(fieldSearch(fields)) + .andWhere('product.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: ProductDto): Promise { + await this.productRepository.insert(dto); + } + + /** + * 更新 + */ + async update(id: number, { fileIds, ...data }: Partial): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(ProductEntity, id, { + ...data + }); + const product = await this.productRepository + .createQueryBuilder('product') + .leftJoinAndSelect('product.files', 'files') + .where('product.id = :id', { id }) + .getOne(); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(ProductEntity, 'files') + .of(id) + .addAndRemove(fileIds, product.files); + } + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 合同比较重要,做逻辑删除 + await this.productRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个合同信息 + */ + async info(id: number) { + const info = await this.productRepository + .createQueryBuilder('product') + .where({ + id + }) + .andWhere('product.isDelete = 0') + .getOne(); + return info; + } + + /** + * 解除附件关联 + * @param id 合同ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const product = await this.productRepository + .createQueryBuilder('product') + .leftJoinAndSelect('product.files', 'files') + .where('product.id = :id', { id }) + .getOne(); + const linkedFiles = product.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(ProductEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, product.files); + }); + } +} diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index d84c97e..c1a59b7 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -4,7 +4,9 @@ import { Column, Entity, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; import { CompanyEntity } from '~/modules/company/company.entity'; import { ContractEntity } from '~/modules/contract/contract.entity'; +import { MaterialsInOutEntity } from '~/modules/materials_inventory/in_out/materials_in_out.entity'; import { MaterialsInventoryEntity } from '~/modules/materials_inventory/materials_inventory.entity'; +import { ProductEntity } from '~/modules/product/product.entity'; @Entity({ name: 'tool_storage' }) export class Storage extends CommonEntity { @@ -50,6 +52,10 @@ export class Storage extends CommonEntity { companys: Relation; @ApiHideProperty() - @ManyToMany(() => MaterialsInventoryEntity, materialsInventories => materialsInventories.files) - materialsInventories: Relation; + @ManyToMany(() => MaterialsInOutEntity, materialsInOut => materialsInOut.files) + materialsInOut: Relation; + + @ApiHideProperty() + @ManyToMany(() => ProductEntity, product => product.files) + products: Relation; } From df8806a5fb7ab0afa9dd11d76ecb7edfa6eb5926 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Tue, 5 Mar 2024 13:57:03 +0800 Subject: [PATCH 20/64] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/error-code.constant.ts | 5 ++- src/modules/company/company.dto.ts | 14 +++++++- src/modules/company/company.entity.ts | 7 +++- .../in_out/materials_in_out.controller.ts | 26 +++++++------- .../materials_inventory.entity.ts | 2 +- .../materials_inventory.module.ts | 12 +++++-- src/modules/product/product.dto.ts | 13 +++++-- src/modules/product/product.entity.ts | 12 +++++-- src/modules/product/product.service.ts | 34 ++++++++++++++----- 9 files changed, 92 insertions(+), 33 deletions(-) diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 3b381c2..8c187a6 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -49,5 +49,8 @@ export enum ErrorEnum { // Storage相关 STORAGE_NOT_FOUND = '1404:文件不存在,请重试', - STORAGE_REFRENCE_EXISTS = '1405:文件存在关联,无法删除,请先找到该文件关联的业务解除关联。' + STORAGE_REFRENCE_EXISTS = '1405:文件存在关联,无法删除,请先找到该文件关联的业务解除关联。', + + // Product + PRODUCT_EXIST = '1406:产品已存在' } diff --git a/src/modules/company/company.dto.ts b/src/modules/company/company.dto.ts index d388b4d..6b39ea5 100644 --- a/src/modules/company/company.dto.ts +++ b/src/modules/company/company.dto.ts @@ -33,7 +33,19 @@ export class CompanyUpdateDto extends PartialType(CompanyDto) { fileIds: number[]; } +export class ComapnyCreateDto extends PartialType(CompanyDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} + export class CompanyQueryDto extends IntersectionType( PagerDto, PartialType(CompanyDto) -) {} +) { + @ApiProperty({ description: '公司名称' }) + @IsOptional() + @IsString() + name: string; +} diff --git a/src/modules/company/company.entity.ts b/src/modules/company/company.entity.ts index 38d7428..6608cbc 100644 --- a/src/modules/company/company.entity.ts +++ b/src/modules/company/company.entity.ts @@ -1,7 +1,8 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; -import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { Column, Entity, JoinTable, ManyToMany, OneToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; import { Storage } from '../tools/storage/storage.entity'; +import { ProductEntity } from '../product/product.entity'; @Entity({ name: 'company' }) export class CompanyEntity extends CommonEntity { @@ -19,6 +20,10 @@ export class CompanyEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + @ApiHideProperty() + @OneToMany(() => ProductEntity, product => product.company) + products: Relation; + @ManyToMany(() => Storage, storage => storage.companys) @JoinTable({ name: 'company_storage', diff --git a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts index b7144e0..406a3e5 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts @@ -16,46 +16,46 @@ export const permissions = definePermission('materials_inventory:history_in_out' DELETE: 'delete' } as const); -@ApiTags('Materials In Out History - 原材料出入库管理') +@ApiTags('Materials In Out History - 原材料出入库记录') @ApiSecurityAuth() @Controller('materials-in-out') export class MaterialsInOutController { - constructor(private miService: MaterialsInOutService) {} + constructor(private materialsInOutService: MaterialsInOutService) {} @Get() - @ApiOperation({ summary: '获取原材料盘点列表' }) + @ApiOperation({ summary: '获取原材料出入库记录列表' }) @ApiResult({ type: [MaterialsInOutEntity], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: MaterialsInOutQueryDto) { - return this.miService.findAll(dto); + return this.materialsInOutService.findAll(dto); } @Get(':id') - @ApiOperation({ summary: '获取原材料盘点信息' }) + @ApiOperation({ summary: '获取原材料出入库记录信息' }) @ApiResult({ type: MaterialsInOutDto }) @Perm(permissions.READ) async info(@IdParam() id: number) { - return this.miService.info(id); + return this.materialsInOutService.info(id); } @Post() - @ApiOperation({ summary: '新增原材料盘点' }) + @ApiOperation({ summary: '新增原材料出入库记录' }) @Perm(permissions.CREATE) async create(@Body() dto: MaterialsInOutDto): Promise { - await this.miService.create(dto); + await this.materialsInOutService.create(dto); } @Put(':id') - @ApiOperation({ summary: '更新原材料盘点' }) + @ApiOperation({ summary: '更新原材料出入库记录' }) @Perm(permissions.UPDATE) async update(@IdParam() id: number, @Body() dto: MaterialsInOutUpdateDto): Promise { - await this.miService.update(id, dto); + await this.materialsInOutService.update(id, dto); } @Delete(':id') - @ApiOperation({ summary: '删除原材料盘点' }) + @ApiOperation({ summary: '删除原材料出入库记录' }) @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { - await this.miService.delete(id); + await this.materialsInOutService.delete(id); } @Put('unlink-attachments/:id') @@ -65,6 +65,6 @@ export class MaterialsInOutController { @IdParam() id: number, @Body() { fileIds }: MaterialsInOutUpdateDto ): Promise { - await this.miService.unlinkAttachments(id, fileIds); + await this.materialsInOutService.unlinkAttachments(id, fileIds); } } diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index e80b830..3023003 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -83,7 +83,7 @@ export class MaterialsInventoryEntity extends CommonEntity { comment: '入库单价' }) @ApiProperty({ description: '入库单价' }) - inventoryUnitPrice: number; + inventoryUnitPrice: number; @Column({ name: 'inventory_amount', diff --git a/src/modules/materials_inventory/materials_inventory.module.ts b/src/modules/materials_inventory/materials_inventory.module.ts index b376835..a834aa5 100644 --- a/src/modules/materials_inventory/materials_inventory.module.ts +++ b/src/modules/materials_inventory/materials_inventory.module.ts @@ -4,10 +4,16 @@ import { MaterialsInventoryService } from './materials_inventory.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { MaterialsInventoryEntity } from './materials_inventory.entity'; import { StorageModule } from '../tools/storage/storage.module'; +import { MaterialsInOutController } from './in_out/materials_in_out.controller'; +import { MaterialsInOutService } from './in_out/materials_in_out.service'; +import { MaterialsInOutEntity } from './in_out/materials_in_out.entity'; @Module({ - imports: [TypeOrmModule.forFeature([MaterialsInventoryEntity]), StorageModule], - controllers: [MaterialsInventoryController], - providers: [MaterialsInventoryService] + imports: [ + TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity]), + StorageModule + ], + controllers: [MaterialsInventoryController, MaterialsInOutController], + providers: [MaterialsInventoryService, MaterialsInOutService] }) export class MaterialsInventoryModule {} diff --git a/src/modules/product/product.dto.ts b/src/modules/product/product.dto.ts index 60414bd..70ab764 100644 --- a/src/modules/product/product.dto.ts +++ b/src/modules/product/product.dto.ts @@ -18,10 +18,14 @@ import { ProductEntity } from './product.entity'; export class ProductDto { @ApiProperty({ description: '产品名称' }) - @IsUnique(ProductEntity, { message: '已存在同名产品' }) @IsString() name: string; + @ApiProperty({ description: '所属公司' }) + @IsOptional() + @IsNumber() + companyId: number; + @ApiProperty({ description: '附件' }) files: Storage[]; } @@ -36,4 +40,9 @@ export class ProductUpdateDto extends PartialType(ProductDto) { export class ProductQueryDto extends IntersectionType( PagerDto, PartialType(ProductDto) -) {} +) { + @ApiProperty({ description: '所属公司名称' }) + @IsOptional() + @IsString() + company: string; +} diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts index c68bace..9049e60 100644 --- a/src/modules/product/product.entity.ts +++ b/src/modules/product/product.entity.ts @@ -1,14 +1,14 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; -import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; import { Storage } from '../tools/storage/storage.entity'; +import { CompanyEntity } from '../company/company.entity'; @Entity({ name: 'product' }) export class ProductEntity extends CommonEntity { @Column({ name: 'name', type: 'varchar', - unique: true, length: 255, comment: '产品名称' }) @@ -19,6 +19,14 @@ export class ProductEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + @Column({ name: 'company_id', type: 'int', comment: '所属公司', nullable: true }) + @ApiProperty({ description: '所属公司' }) + companyId: number; + + @ManyToOne(() => CompanyEntity /* , { onDelete: 'CASCADE' } */) + @JoinColumn({ name: 'company_id' }) + company: CompanyEntity; + @ManyToMany(() => Storage, storage => storage.products) @JoinTable({ name: 'product_storage', diff --git a/src/modules/product/product.service.ts b/src/modules/product/product.service.ts index df0589f..257a91d 100644 --- a/src/modules/product/product.service.ts +++ b/src/modules/product/product.service.ts @@ -28,14 +28,22 @@ export class ProductService { pageSize, ...fields }: ProductQueryDto): Promise> { - const queryBuilder = this.productRepository + const { company: companyName,...ext } = fields; + const sqb = this.productRepository .createQueryBuilder('product') .leftJoin('product.files', 'files') - .addSelect(['files.id', 'files.path']) - .where(fieldSearch(fields)) - .andWhere('product.isDelete = 0'); - - return paginate(queryBuilder, { + .leftJoin('product.company', 'company') + .addSelect(['files.id', 'files.path', 'company.name', 'company.id']) + .where(fieldSearch(ext)) + .where('product.isDelete = 0'); + if (companyName) { + sqb.andWhere({ + company: { + name: Like(`%${companyName}%`) + } + }); + } + return paginate(sqb, { page, pageSize }); @@ -45,6 +53,13 @@ export class ProductService { * 新增 */ async create(dto: ProductDto): Promise { + const { name, companyId } = dto; + const isExsit = await this.productRepository.findOne({ + where: { name, company: { id: companyId } } + }); + if (isExsit) { + throw new BusinessException(ErrorEnum.PRODUCT_EXIST); + } await this.productRepository.insert(dto); } @@ -83,16 +98,17 @@ export class ProductService { * 删除 */ async delete(id: number): Promise { - // 合同比较重要,做逻辑删除 await this.productRepository.update(id, { isDelete: 1 }); } /** - * 获取单个合同信息 + * 获取单个产品信息 */ async info(id: number) { const info = await this.productRepository .createQueryBuilder('product') + .leftJoin('product.company', 'company') + .addSelect(['company.name', 'company.id']) .where({ id }) @@ -103,7 +119,7 @@ export class ProductService { /** * 解除附件关联 - * @param id 合同ID + * @param id 产品ID * @param fileIds 附件ID */ async unlinkAttachments(id: number, fileIds: number[]) { From 655adf787f1311be1e556a0cd4af6c6970dc2eec Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Tue, 5 Mar 2024 22:27:01 +0800 Subject: [PATCH 21/64] =?UTF-8?q?feat:=20=E5=87=BA=E5=85=A5=E5=BA=93?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=A8=A1=E5=9D=97=E5=92=8C=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- package.json | 15 +- pnpm-lock.yaml | 534 +++++++++++++++++- .../in_out/materials_in_out.dto.ts | 77 ++- .../in_out/materials_in_out.entity.ts | 4 +- .../in_out/materials_in_out.service.ts | 59 +- .../materials_inventory.service.ts | 2 +- src/modules/product/product.dto.ts | 5 + src/modules/product/product.entity.ts | 34 +- src/modules/product/product.service.ts | 17 +- src/modules/system/log/dto/log.dto.ts | 10 +- src/shared/database/field-search/index.ts | 13 +- tsconfig.json | 2 +- 13 files changed, 695 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index a394936..774412d 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ pnpm migration:revert 4.执行sql覆盖docker中的数据库 ```bash -docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa20240301.sql +docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa_2024-03-05_151242.sql ``` 更多细节,请移步至[官方文档](https://typeorm.io/migrations) diff --git a/package.json b/package.json index 65b95af..732e0dc 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "passport-google-oauth20": "^2.0.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", + "pinyin": "3", "qiniu": "^7.11.0", "reflect-metadata": "^0.2.1", "rimraf": "^5.0.5", @@ -120,24 +121,24 @@ "@types/ua-parser-js": "^0.7.39", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", + "cliui": "^8.0.1", + "commitizen": "^4.3.0", "cross-env": "^7.0.3", + "cz-customizable": "^7.0.0", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", - "prettier": "~3.2.5", + "husky": "^8.0.0", "jest": "^29.7.0", + "prettier": "~3.2.5", "source-map-support": "^0.5.21", + "standard-version": "^9.5.0", "supertest": "^6.3.4", "ts-jest": "^29.1.2", "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", - "typescript": "^5.3.3", - "cliui": "^8.0.1", - "commitizen": "^4.3.0", - "cz-customizable": "^7.0.0", - "standard-version": "^9.5.0", - "husky": "^8.0.0" + "typescript": "^5.3.3" }, "config": { "commitizen": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c4d0a2..a21d55b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,6 +149,9 @@ dependencies: passport-local: specifier: ^1.0.0 version: 1.0.0 + pinyin: + specifier: '3' + version: 3.1.0 qiniu: specifier: ^7.11.0 version: 7.11.0 @@ -379,7 +382,6 @@ packages: dependencies: '@babel/highlight': 7.23.4 chalk: 2.4.2 - dev: true /@babel/compat-data@7.23.5: resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} @@ -634,7 +636,6 @@ packages: '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 - dev: true /@babel/parser@7.23.9: resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==} @@ -1762,6 +1763,22 @@ packages: kuler: 2.0.0 dev: false + /@emnapi/core@0.45.0: + resolution: {integrity: sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + + /@emnapi/runtime@0.45.0: + resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2278,6 +2295,26 @@ packages: engines: {node: '>=8'} dev: false + /@mapbox/node-pre-gyp@1.0.11: + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + requiresBuild: true + dependencies: + detect-libc: 2.0.2 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.0 + tar: 6.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + optional: true + /@microsoft/tsdoc@0.14.2: resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} dev: false @@ -2330,6 +2367,16 @@ packages: dev: false optional: true + /@napi-rs/wasm-runtime@0.1.1: + resolution: {integrity: sha512-ATj9ua659JgrkICjJscaeZdmPr44cb/KFjNWuD0N6pux0SpzaM7+iOuuK11mAnQM2N9q0DT4REu6NkL8ZEhopw==} + requiresBuild: true + dependencies: + '@emnapi/core': 0.45.0 + '@emnapi/runtime': 0.45.0 + '@tybys/wasm-util': 0.8.1 + dev: false + optional: true + /@nestjs-modules/mailer@1.10.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(nodemailer@6.9.9): resolution: {integrity: sha512-k2gs2NH8Ygq4JnETX+EDBXixLAS8DDZEI/Wbr9LGL3HwO3Qz8zVh8dBJ4ESpySuWniW+a8rARzGXtTUHC4KFlw==} peerDependencies: @@ -2791,6 +2838,159 @@ packages: rxjs: 7.8.1 tslib: 2.6.2 + /@node-rs/jieba-android-arm-eabi@1.10.0: + resolution: {integrity: sha512-bzusJSLHm7I0qL8aQXGLt7IQ51Px35yGGEcQ/Ps4SEt0AxRSJ2/rxNET/8mlwBpOCZ5xiKE3BOBRfQajiPiI3g==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-android-arm64@1.10.0: + resolution: {integrity: sha512-g89Oq5U2RPmtlvuQhjNj8YZc5Gq033ODb7Ot4Z/OdIHvg2WMxi2M1GQhcdKu60dO79/tazc53W6I8/y691DUfQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-darwin-arm64@1.10.0: + resolution: {integrity: sha512-IhR5r+XxFcfhVsF93zQ3uCJy8ndotRntXzoW/JCyKqOahUo/ITQRT6vTKHKMyD9xNmjl222OZonBSo2+mlI2fQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-darwin-x64@1.10.0: + resolution: {integrity: sha512-MBIs8ixKY4FPnifdZ7eTx6ht85TXE4kFBK4c8A/VDAbnmzBzpEyuV7tHUA2wAdfR0muC9j7/5FB4kQGZgYfc8g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-freebsd-x64@1.10.0: + resolution: {integrity: sha512-MuY+1QEXONxo3I/uFLFju0/pSN5bzQORhJkIdP8CYv+jZaVB4Uz6rC7A5HrgjiAXOna6QsKlRgx2bYyHfaBUrA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-linux-arm-gnueabihf@1.10.0: + resolution: {integrity: sha512-QfSBnwISdVuTqsi4iThAO1LSbKRSqSsIWiIJgCduhYsTDDiG9+pHyfiZtcTwSf73SDXHZ400QuBNONWLQ/dSag==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-linux-arm64-gnu@1.10.0: + resolution: {integrity: sha512-vzA2tX/6dReEd/7tZ9927glWQmKDausM6R9S5CqZx4BA4NSaWAK0xFdWsz0K7np459FXqNavLdNB5FVFJb4zzA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-linux-arm64-musl@1.10.0: + resolution: {integrity: sha512-gxqoAVOQsn9sgYK6mFO9dsMZ/yOMvVecLZW5rGvLErjiugVvYUlESXIvCqxp2GSws8RtTqJj6p9u/lBmCCuvaw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-linux-x64-gnu@1.10.0: + resolution: {integrity: sha512-rS5Shs8JITxJjFIjoIZ5a9O+GO21TJgKu03g2qwFE3QaN5ZOvXtz+/AqqyfT4GmmMhCujD83AGqfOGXDmItF9w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-linux-x64-musl@1.10.0: + resolution: {integrity: sha512-BvSiF2rR8Birh2oEVHcYwq0WGC1cegkEdddWsPrrSmpKmukJE2zyjcxaOOggq2apb8fIRsjyeeUh6X3R5AgjvA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-wasm32-wasi@1.10.0: + resolution: {integrity: sha512-EzeAAbRrFTdYw61rd8Mfwdp/fA21d58z9vLY06CDbI+dqANfMFn1IUdwzKWi8S5J/MRhvbzonbbh3yHlz6F43Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + requiresBuild: true + dependencies: + '@napi-rs/wasm-runtime': 0.1.1 + dev: false + optional: true + + /@node-rs/jieba-win32-arm64-msvc@1.10.0: + resolution: {integrity: sha512-eZjRLFUAvq1/E5+xXfJRqIB99Gu6BA+6+EXf/rCLuvEjXrDQuUunhmrSoOL5MjmUXTtazS+bXq9PXV5EFYyOPw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-win32-ia32-msvc@1.10.0: + resolution: {integrity: sha512-DrfbeCN7UcLN+MiocZabWo74XZIjfpQsJ/WMOItZzVbU2gDcJSkSyAhML9+OqId66DhGCMFFlGinocElM8iIAw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba-win32-x64-msvc@1.10.0: + resolution: {integrity: sha512-RjBkBmjjHmj+bofiq5/han8wzbCkDk24OAPJ+YX8PX20GFSHmdjCiWapv3AooN8/RiKqlBfgodjS1JUngNWo5g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@node-rs/jieba@1.10.0: + resolution: {integrity: sha512-9oZMCvZVnrAMeWTSnEjJ0OSw7YcV4dJJKSioqq80oUNf3eYLGdEXsgYwCe1AYEMcfUfNVgvjznItJKrsoud0IA==} + engines: {node: '>= 10'} + requiresBuild: true + optionalDependencies: + '@node-rs/jieba-android-arm-eabi': 1.10.0 + '@node-rs/jieba-android-arm64': 1.10.0 + '@node-rs/jieba-darwin-arm64': 1.10.0 + '@node-rs/jieba-darwin-x64': 1.10.0 + '@node-rs/jieba-freebsd-x64': 1.10.0 + '@node-rs/jieba-linux-arm-gnueabihf': 1.10.0 + '@node-rs/jieba-linux-arm64-gnu': 1.10.0 + '@node-rs/jieba-linux-arm64-musl': 1.10.0 + '@node-rs/jieba-linux-x64-gnu': 1.10.0 + '@node-rs/jieba-linux-x64-musl': 1.10.0 + '@node-rs/jieba-wasm32-wasi': 1.10.0 + '@node-rs/jieba-win32-arm64-msvc': 1.10.0 + '@node-rs/jieba-win32-ia32-msvc': 1.10.0 + '@node-rs/jieba-win32-x64-msvc': 1.10.0 + dev: false + optional: true + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2918,6 +3118,14 @@ packages: /@tsconfig/node16@1.0.4: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + /@tybys/wasm-util@0.8.1: + resolution: {integrity: sha512-GSsTwyBl4pIzsxAY5wroZdyQKyhXk0d8PCRZtrSZ2WEB1cBdrp2EgGBwHOGCZtIIPun/DL3+AykCv+J6fyRH4Q==} + requiresBuild: true + dependencies: + tslib: 2.6.2 + dev: false + optional: true + /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: @@ -3099,6 +3307,12 @@ packages: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: true + /@types/parse-json@4.0.2: + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + requiresBuild: true + dev: false + optional: true + /@types/pug@2.0.10: resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} dev: false @@ -3427,6 +3641,12 @@ packages: through: 2.3.8 dev: true + /abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + requiresBuild: true + dev: false + optional: true + /abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3497,6 +3717,17 @@ packages: resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} dev: true + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + requiresBuild: true + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + optional: true + /agentkeepalive@4.5.0: resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} engines: {node: '>= 8.0.0'} @@ -3619,7 +3850,6 @@ packages: engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - dev: true /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -3664,10 +3894,26 @@ packages: engines: {node: '>= 6.0.0'} dev: false + /aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + requiresBuild: true + dev: false + optional: true + /archy@1.0.0: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} dev: false + /are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + dev: false + optional: true + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -3802,6 +4048,16 @@ packages: '@types/babel__traverse': 7.20.5 dev: true + /babel-plugin-macros@2.8.0: + resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.23.9 + cosmiconfig: 6.0.0 + resolve: 1.22.8 + dev: false + optional: true + /babel-plugin-polyfill-corejs2@0.4.8(@babel/core@7.23.9): resolution: {integrity: sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==} peerDependencies: @@ -3838,6 +4094,17 @@ packages: - supports-color dev: true + /babel-plugin-preval@4.0.0: + resolution: {integrity: sha512-fZI/4cYneinlj2k/FsXw0/lTWSC5KKoepUueS1g25Gb5vx3GrRyaVwxWCshYqx11GEU4mZnbbFhee8vpquFS2w==} + engines: {node: '>=8', npm: '>=6'} + requiresBuild: true + dependencies: + '@babel/runtime': 7.23.9 + babel-plugin-macros: 2.8.0 + require-from-string: 2.0.2 + dev: false + optional: true + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.9): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: @@ -4111,7 +4378,6 @@ packages: /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - dev: true /camel-case@3.0.0: resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} @@ -4149,7 +4415,6 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: true /chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} @@ -4227,6 +4492,13 @@ packages: optionalDependencies: fsevents: 2.3.3 + /chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + requiresBuild: true + dev: false + optional: true + /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} @@ -4382,7 +4654,6 @@ packages: /color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true - dev: true /color@3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} @@ -4409,6 +4680,13 @@ packages: dependencies: delayed-stream: 1.0.0 + /commander@1.1.1: + resolution: {integrity: sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==} + engines: {node: '>= 0.6.x'} + dependencies: + keypress: 0.1.0 + dev: false + /commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} @@ -4523,6 +4801,12 @@ packages: /consola@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + /console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + requiresBuild: true + dev: false + optional: true + /constantinople@4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} requiresBuild: true @@ -4775,6 +5059,19 @@ packages: dev: true optional: true + /cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: false + optional: true + /cosmiconfig@8.3.6(typescript@5.3.3): resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -5057,6 +5354,12 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + /delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + requiresBuild: true + dev: false + optional: true + /denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} @@ -5084,6 +5387,13 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + /detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + requiresBuild: true + dev: false + optional: true + /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -5361,7 +5671,6 @@ packages: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: is-arrayish: 0.2.1 - dev: true /error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} @@ -5455,7 +5764,6 @@ packages: /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - dev: true /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} @@ -6194,6 +6502,15 @@ packages: universalify: 2.0.1 dev: true + /fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + minipass: 3.3.6 + dev: false + optional: true + /fs-monkey@1.0.5: resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} dev: true @@ -6215,6 +6532,23 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true + /gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: false + optional: true + /generate-function@2.3.1: resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} dependencies: @@ -6467,7 +6801,6 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -6497,6 +6830,12 @@ packages: dependencies: has-symbols: 1.0.3 + /has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + requiresBuild: true + dev: false + optional: true + /has@1.0.4: resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} engines: {node: '>= 0.4.0'} @@ -6634,6 +6973,18 @@ packages: resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} dev: true + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + requiresBuild: true + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + optional: true + /human-signals@1.1.1: resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} engines: {node: '>=8.12.0'} @@ -6688,7 +7039,6 @@ packages: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - dev: true /import-local@3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} @@ -6848,7 +7198,6 @@ packages: /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: true /is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} @@ -7531,7 +7880,6 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} @@ -7568,7 +7916,6 @@ packages: /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: true /json-schema-ref-resolver@1.0.1: resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} @@ -7679,6 +8026,10 @@ packages: resolution: {integrity: sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==} dev: true + /keypress@0.1.0: + resolution: {integrity: sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA==} + dev: false + /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: @@ -7763,7 +8114,6 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true /linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} @@ -8014,6 +8364,15 @@ packages: libqp: 2.0.1 dev: false + /make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + semver: 6.3.1 + dev: false + optional: true + /make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -8208,15 +8567,41 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + /minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + yallist: 4.0.0 + dev: false + optional: true + /minipass@4.2.8: resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} engines: {node: '>=8'} dev: true + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + requiresBuild: true + dev: false + optional: true + /minipass@7.0.4: resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} engines: {node: '>=16 || 14 >=14.17'} + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + dev: false + optional: true + /mjml-accordion@4.14.1: resolution: {integrity: sha512-dpNXyjnhYwhM75JSjD4wFUa9JgHm86M2pa0CoTzdv1zOQz67ilc4BoK5mc2S0gOjJpjBShM5eOJuCyVIuAPC6w==} dependencies: @@ -8586,6 +8971,14 @@ packages: dependencies: minimist: 1.2.8 + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + requiresBuild: true + dev: false + optional: true + /mkdirp@2.1.6: resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} engines: {node: '>=10'} @@ -8734,6 +9127,12 @@ packages: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} dev: true + /node-addon-api@3.2.1: + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + requiresBuild: true + dev: false + optional: true + /node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} dependencies: @@ -8766,11 +9165,34 @@ packages: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} dev: true + /nodejieba@2.5.2: + resolution: {integrity: sha512-ByskJvaBrQ2eV+5M0OeD80S5NKoGaHc9zi3Z/PTKl/95eac2YF8RmWduq9AknLpkQLrLAIcqurrtC6BzjpKwwg==} + engines: {node: '>= 10.20.0'} + requiresBuild: true + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 3.2.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + optional: true + /nodemailer@6.9.9: resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==} engines: {node: '>=6.0.0'} dev: false + /nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + requiresBuild: true + dependencies: + abbrev: 1.1.1 + dev: false + optional: true + /nopt@7.2.0: resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -8820,6 +9242,17 @@ packages: path-key: 3.1.1 dev: true + /npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + requiresBuild: true + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + dev: false + optional: true + /nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: @@ -9109,7 +9542,6 @@ packages: engines: {node: '>=6'} dependencies: callsites: 3.1.0 - dev: true /parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} @@ -9127,7 +9559,6 @@ packages: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - dev: true /parse-passwd@1.0.0: resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} @@ -9271,7 +9702,6 @@ packages: /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - dev: true /pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} @@ -9356,6 +9786,21 @@ packages: thread-stream: 2.4.1 dev: false + /pinyin@3.1.0: + resolution: {integrity: sha512-U+COtcFr2eRztdE9is+2EQCrrkTiSncizW/d58lhzINvjhCAWUOoIsaEL1DDX8GZrT5FoW69fi2dtWHjQlk/fw==} + engines: {install-node: ^18.0.0} + hasBin: true + dependencies: + commander: 1.1.1 + optionalDependencies: + '@node-rs/jieba': 1.10.0 + nodejieba: 2.5.2 + segmentit: 2.0.3 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} @@ -9409,6 +9854,14 @@ packages: react-is: 18.2.0 dev: true + /preval.macro@4.0.0: + resolution: {integrity: sha512-sJJnE71X+MPr64CVD2AurmUj4JEDqbudYbStav3L9Xjcqm4AR0ymMm6sugw1mUmfI/7gw4JWA4JXo/k6w34crw==} + requiresBuild: true + dependencies: + babel-plugin-preval: 4.0.0 + dev: false + optional: true + /preview-email@3.0.19: resolution: {integrity: sha512-DBS3Nir18YtKc8loYCCOGitmiaQ0vTdahPoiXxwNweJDpmVZo+w3tppufOhoK0m8skpRxT56llYLs3VrORnmNQ==} engines: {node: '>=14'} @@ -9886,7 +10339,6 @@ packages: /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - dev: true /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} @@ -9960,7 +10412,6 @@ packages: hasBin: true dependencies: glob: 7.2.3 - dev: true /rimraf@4.4.1: resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} @@ -10063,6 +10514,14 @@ packages: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} dev: false + /segmentit@2.0.3: + resolution: {integrity: sha512-7mn2XL3OdTUQ+AhHz7SbgyxLTaQRzTWQNVwiK+UlTO8aePGbSwvKUzTwE4238+OUY9MoR6ksAg35zl8sfTunQQ==} + requiresBuild: true + dependencies: + preval.macro: 4.0.0 + dev: false + optional: true + /selderee@0.11.0: resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} dependencies: @@ -10076,7 +10535,6 @@ packages: /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - dev: true /semver@7.6.0: resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} @@ -10131,6 +10589,12 @@ packages: - supports-color dev: true + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + requiresBuild: true + dev: false + optional: true + /set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} dev: false @@ -10625,7 +11089,6 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -10680,6 +11143,20 @@ packages: engines: {node: '>=6'} dev: true + /tar@6.2.0: + resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + dev: false + optional: true + /temp@0.9.4: resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==} engines: {node: '>=6.0.0'} @@ -11460,6 +11937,14 @@ packages: dependencies: isexe: 2.0.0 + /wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + requiresBuild: true + dependencies: + string-width: 4.2.3 + dev: false + optional: true + /widest-line@3.1.0: resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} engines: {node: '>=8'} @@ -11609,6 +12094,13 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + requiresBuild: true + dev: false + optional: true + /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 9971663..4c04489 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -1,8 +1,10 @@ import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; import { IsArray, IsDate, IsDateString, + IsEnum, IsIn, IsInt, IsNumber, @@ -12,11 +14,63 @@ import { MinLength } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; +import { MaterialsInOrOutEnum } from '~/constants/enum'; import { Storage } from '~/modules/tools/storage/storage.entity'; +import { formatToDate } from '~/utils'; export class MaterialsInOutDto { - @ApiProperty({ description: '附件' }) - files: Storage[]; + @ApiProperty({ description: '产品' }) + @IsNumber() + product: number; + + @ApiProperty({ description: '单位(字典)' }) + @IsNumber() + @IsOptional() + unit: number; + + @ApiProperty({ description: '入库或出库 0:入库 1:出库' }) + @IsEnum(MaterialsInOrOutEnum) + inOrOut: MaterialsInOrOutEnum; + + @ApiProperty({ description: '时间' }) + @IsOptional() + time: Date; + + @ApiProperty({ description: '数量' }) + @IsNumber() + quantity: number; + + @ApiProperty({ description: '单价' }) + @IsNumber() + unitPrice: number; + + @ApiProperty({ description: '金额' }) + @IsNumber() + amount: number; + + @ApiProperty({ description: '经办人' }) + @IsOptional() + @IsString() + agent: string; + + @ApiProperty({ description: '领料单号' }) + @IsOptional() + @IsString() + issuanceNumber: string; + + @IsOptional() + @IsString() + @ApiProperty({ description: '备注' }) + remark: string; + + @IsOptional() + @IsString() + @ApiProperty({ description: '项目' }) + project: string; + + // @ApiProperty({ description: '附件' }) + // @IsOptional() + // files: Storage[]; } export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { @@ -25,7 +79,18 @@ export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { @IsArray() fileIds: number[]; } -export class MaterialsInOutQueryDto extends IntersectionType( - PagerDto, - PartialType(MaterialsInOutDto) -) {} +export class MaterialsInOutQueryDto extends PagerDto { + @ApiProperty({ description: '出入库时间YYYY-MM-DD' }) + @IsOptional() + @IsArray() + @Transform(params => { + // 开始和结束时间用的是一天的开始和一天的结束的时分秒 + const [start, end] = params.value; + return [ + start ? `${formatToDate(start)} 00:00:00` : null, + end ? `${formatToDate(end)} 23:59:59` : null + ]; + }) + time?: string[]; + +} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index 2cd9370..d2e715a 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -31,7 +31,7 @@ export class MaterialsInOutEntity extends CommonEntity { inOrOut: MaterialsInOrOutEnum; @Column({ - name: 'inventory_time', + name: 'time', type: 'date', nullable: true, comment: '时间' @@ -56,7 +56,7 @@ export class MaterialsInOutEntity extends CommonEntity { scale: 2, comment: '单价' }) - @ApiProperty({ description: '入库单价' }) + @ApiProperty({ description: '单价' }) unitPrice: number; @Column({ diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index dc72e58..65578c0 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -9,13 +9,14 @@ import { paginate } from '~/helper/paginate'; import { Storage } from '~/modules/tools/storage/storage.entity'; import { MaterialsInOutQueryDto, MaterialsInOutDto, MaterialsInOutUpdateDto } from './materials_in_out.dto'; import { MaterialsInOutEntity } from './materials_in_out.entity'; +import { fieldSearch } from '~/shared/database/field-search'; @Injectable() export class MaterialsInOutService { constructor( @InjectEntityManager() private entityManager: EntityManager, @InjectRepository(MaterialsInOutEntity) - private materialsInventoryRepository: Repository, + private materialsInOutRepository: Repository, @InjectRepository(Storage) private storageRepository: Repository ) {} @@ -24,23 +25,21 @@ export class MaterialsInOutService { */ async findAll({ page, - pageSize - // materialsInventoryNumber, - // title, - // type, - // status + pageSize, + ...ext }: MaterialsInOutQueryDto): Promise> { - const queryBuilder = this.materialsInventoryRepository - .createQueryBuilder('materialsInventory') - .leftJoin('materialsInventory.files', 'files') + const queryBuilder = this.materialsInOutRepository + .createQueryBuilder('materialsInOut') + .leftJoin('materialsInOut.files', 'files') .addSelect(['files.id', 'files.path']) // .where({ - // ...(materialsInventoryNumber ? { materialsInventoryNumber: Like(`%${materialsInventoryNumber}%`) } : null), + // ...(materialsInOutNumber ? { materialsInOutNumber: Like(`%${materialsInOutNumber}%`) } : null), // ...(title ? { title: Like(`%${title}%`) } : null), // ...(isNumber(type) ? { type } : null), // ...(isNumber(status) ? { status } : null) // }) - .andWhere('materialsInventory.isDelete = 0'); + .where(fieldSearch(ext)) + .andWhere('materialsInOut.isDelete = 0'); return paginate(queryBuilder, { page, @@ -52,7 +51,7 @@ export class MaterialsInOutService { * 新增 */ async create(dto: MaterialsInOutDto): Promise { - await this.materialsInventoryRepository.insert(dto); + await this.materialsInOutRepository.insert(dto); } /** @@ -63,10 +62,10 @@ export class MaterialsInOutService { await manager.update(MaterialsInOutEntity, id, { ...data }); - const materialsInventory = await this.materialsInventoryRepository - .createQueryBuilder('materialsInventory') - .leftJoinAndSelect('materialsInventory.files', 'files') - .where('materialsInventory.id = :id', { id }) + const materialsInOut = await this.materialsInOutRepository + .createQueryBuilder('materialsInOut') + .leftJoinAndSelect('materialsInOut.files', 'files') + .where('materialsInOut.id = :id', { id }) .getOne(); if (fileIds?.length) { const count = await this.storageRepository @@ -81,7 +80,7 @@ export class MaterialsInOutService { .createQueryBuilder() .relation(MaterialsInOutEntity, 'files') .of(id) - .addAndRemove(fileIds, materialsInventory.files); + .addAndRemove(fileIds, materialsInOut.files); } }); } @@ -90,37 +89,37 @@ export class MaterialsInOutService { * 删除 */ async delete(id: number): Promise { - // 合同比较重要,做逻辑删除 - await this.materialsInventoryRepository.update(id, { isDelete: 1 }); + // 出入库比较重要,做逻辑删除 + await this.materialsInOutRepository.update(id, { isDelete: 1 }); } /** - * 获取单个合同信息 + * 获取单个出入库信息 */ async info(id: number) { - const info = await this.materialsInventoryRepository - .createQueryBuilder('materialsInventory') + const info = await this.materialsInOutRepository + .createQueryBuilder('materialsInOut') .where({ id }) - .andWhere('materialsInventory.isDelete = 0') + .andWhere('materialsInOut.isDelete = 0') .getOne(); return info; } /** * 解除附件关联 - * @param id 合同ID + * @param id 出入库ID * @param fileIds 附件ID */ async unlinkAttachments(id: number, fileIds: number[]) { await this.entityManager.transaction(async manager => { - const materialsInventory = await this.materialsInventoryRepository - .createQueryBuilder('materialsInventory') - .leftJoinAndSelect('materialsInventory.files', 'files') - .where('materialsInventory.id = :id', { id }) + const materialsInOut = await this.materialsInOutRepository + .createQueryBuilder('materialsInOut') + .leftJoinAndSelect('materialsInOut.files', 'files') + .where('materialsInOut.id = :id', { id }) .getOne(); - const linkedFiles = materialsInventory.files + const linkedFiles = materialsInOut.files .map(item => item.id) .filter(item => !fileIds.includes(item)); // 附件要批量更新 @@ -128,7 +127,7 @@ export class MaterialsInOutService { .createQueryBuilder() .relation(MaterialsInOutEntity, 'files') .of(id) - .addAndRemove(linkedFiles, materialsInventory.files); + .addAndRemove(linkedFiles, materialsInOut.files); }); } } diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 927337b..c45f1e7 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -27,7 +27,7 @@ export class MaterialsInventoryService { }: MaterialsInventoryQueryDto): Promise> { const queryBuilder = this.materialsInventoryRepository .createQueryBuilder('materialsInventory') - .andWhere('materialsInventory.isDelete = 0'); + .where('materialsInventory.isDelete = 0'); return paginate(queryBuilder, { page, diff --git a/src/modules/product/product.dto.ts b/src/modules/product/product.dto.ts index 70ab764..4e17d7a 100644 --- a/src/modules/product/product.dto.ts +++ b/src/modules/product/product.dto.ts @@ -45,4 +45,9 @@ export class ProductQueryDto extends IntersectionType( @IsOptional() @IsString() company: string; + + @ApiProperty({ description: '产品名字' }) + @IsOptional() + @IsString() + name?: string; } diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts index 9049e60..a5781ed 100644 --- a/src/modules/product/product.entity.ts +++ b/src/modules/product/product.entity.ts @@ -1,9 +1,19 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; -import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, Relation } from 'typeorm'; +import { + BeforeInsert, + BeforeUpdate, + Column, + Entity, + JoinColumn, + JoinTable, + ManyToMany, + ManyToOne, + Relation +} from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; import { Storage } from '../tools/storage/storage.entity'; import { CompanyEntity } from '../company/company.entity'; - +import pinyin from 'pinyin'; @Entity({ name: 'product' }) export class ProductEntity extends CommonEntity { @Column({ @@ -23,6 +33,26 @@ export class ProductEntity extends CommonEntity { @ApiProperty({ description: '所属公司' }) companyId: number; + @ApiHideProperty() + @Column({ + name: 'name_pinyin', + type: 'varchar', + length: 255, + nullable: true, + comment: '产品名称的拼音' + }) + namePinyin: string; + + @BeforeInsert() + @BeforeUpdate() + updateNamePinyin() { + console.log('sssssssssssssss') + this.namePinyin = pinyin(this.name, { + style: pinyin.STYLE_NORMAL, + heteronym: false + }).join(''); + } + @ManyToOne(() => CompanyEntity /* , { onDelete: 'CASCADE' } */) @JoinColumn({ name: 'company_id' }) company: CompanyEntity; diff --git a/src/modules/product/product.service.ts b/src/modules/product/product.service.ts index 257a91d..585a3f4 100644 --- a/src/modules/product/product.service.ts +++ b/src/modules/product/product.service.ts @@ -28,14 +28,15 @@ export class ProductService { pageSize, ...fields }: ProductQueryDto): Promise> { - const { company: companyName,...ext } = fields; + const { company: companyName, ...ext } = fields; const sqb = this.productRepository .createQueryBuilder('product') .leftJoin('product.files', 'files') .leftJoin('product.company', 'company') .addSelect(['files.id', 'files.path', 'company.name', 'company.id']) .where(fieldSearch(ext)) - .where('product.isDelete = 0'); + .andWhere('product.isDelete = 0') + .addOrderBy('product.namePinyin', 'ASC'); if (companyName) { sqb.andWhere({ company: { @@ -60,7 +61,7 @@ export class ProductService { if (isExsit) { throw new BusinessException(ErrorEnum.PRODUCT_EXIST); } - await this.productRepository.insert(dto); + await this.productRepository.insert(this.productRepository.create(dto)); } /** @@ -68,9 +69,13 @@ export class ProductService { */ async update(id: number, { fileIds, ...data }: Partial): Promise { await this.entityManager.transaction(async manager => { - await manager.update(ProductEntity, id, { - ...data - }); + await manager.update( + ProductEntity, + id, + this.productRepository.create({ + ...data + }) + ); const product = await this.productRepository .createQueryBuilder('product') .leftJoinAndSelect('product.files', 'files') diff --git a/src/modules/system/log/dto/log.dto.ts b/src/modules/system/log/dto/log.dto.ts index fa2f27e..3b34900 100644 --- a/src/modules/system/log/dto/log.dto.ts +++ b/src/modules/system/log/dto/log.dto.ts @@ -1,7 +1,9 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsOptional, IsString } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { IsArray, IsOptional, IsString } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; +import { formatToDate } from '~/utils'; export class LoginLogQueryDto extends PagerDto { @ApiProperty({ description: '用户名' }) @@ -21,6 +23,12 @@ export class LoginLogQueryDto extends PagerDto { @ApiProperty({ description: '登录时间' }) @IsOptional() + @IsArray() + @Transform(params => { + // 开始和结束时间用的是一天的开始和一天的结束的时分秒 + const [start, end] = params.value; + return [start ? `${formatToDate(start)} 00:00:00` : null, end ? `${formatToDate(end)} 23:59:59` : null]; + }) time?: string[]; } diff --git a/src/shared/database/field-search/index.ts b/src/shared/database/field-search/index.ts index f44e3d6..dbee970 100644 --- a/src/shared/database/field-search/index.ts +++ b/src/shared/database/field-search/index.ts @@ -1,5 +1,5 @@ import { isNumber } from 'lodash'; -import { Like, ObjectLiteral, ObjectType } from 'typeorm'; +import { Between, Like, ObjectLiteral, ObjectType } from 'typeorm'; export const fieldSearch = (entity: Partial): ObjectLiteral => { let result = {}; for (let key in entity) { @@ -14,6 +14,17 @@ export const fieldSearch = (entity: Partial): ObjectLiteral => { case 'boolean': result = { ...result, ...(entity[key] === true ? { [key]: 1 } : { [key]: 0 }) }; break; + case 'object': + if (Array.isArray(entity[key])) { + result = { + ...result, + ...(entity[key] && { createdAt: Between(entity[key][0], entity[key][1]) }) + }; + } else { + // Handle other object types + } + break; + default: result = { ...result, ...(entity[key] ? { [key]: entity[key] } : null) }; break; diff --git a/tsconfig.json b/tsconfig.json index c146e4a..48e2daa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "incremental": true, - "target": "ES2022", + "target": "ES2020", "lib": ["ESNext"], "emitDecoratorMetadata": true, "experimentalDecorators": true, From cf7c91eab282b084cbeeca51b5d1036f7f35da02 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 6 Mar 2024 11:39:56 +0800 Subject: [PATCH 22/64] =?UTF-8?q?feat:=20=E5=87=BA=E5=85=A5=E5=BA=93?= =?UTF-8?q?=E7=9B=B8=E5=85=B3api=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/helper/paginate/pagination.ts | 2 +- .../in_out/materials_in_out.dto.ts | 22 +++++++--- .../in_out/materials_in_out.entity.ts | 40 ++++++++++++------- .../in_out/materials_in_out.service.ts | 35 ++++++++++------ src/modules/product/product.dto.ts | 20 +++------- src/modules/product/product.entity.ts | 10 ++++- src/modules/product/product.service.ts | 3 +- src/shared/database/field-search/index.ts | 2 +- 9 files changed, 85 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 774412d..af27223 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ pnpm migration:revert 4.执行sql覆盖docker中的数据库 ```bash -docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa_2024-03-05_151242.sql +docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa_2024-03-05_222748.sql ``` 更多细节,请移步至[官方文档](https://typeorm.io/migrations) diff --git a/src/helper/paginate/pagination.ts b/src/helper/paginate/pagination.ts index 1d52aba..02d97c2 100644 --- a/src/helper/paginate/pagination.ts +++ b/src/helper/paginate/pagination.ts @@ -4,7 +4,7 @@ import { IPaginationMeta } from './interface'; export class Pagination { constructor( - public readonly items: PaginationObject[], + public items: PaginationObject[], public readonly meta: T ) {} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 4c04489..8f999b1 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -13,6 +13,7 @@ import { Matches, MinLength } from 'class-validator'; +import dayjs from 'dayjs'; import { PagerDto } from '~/common/dto/pager.dto'; import { MaterialsInOrOutEnum } from '~/constants/enum'; import { Storage } from '~/modules/tools/storage/storage.entity'; @@ -21,12 +22,12 @@ import { formatToDate } from '~/utils'; export class MaterialsInOutDto { @ApiProperty({ description: '产品' }) @IsNumber() - product: number; + productId: number; @ApiProperty({ description: '单位(字典)' }) @IsNumber() @IsOptional() - unit: number; + unitId: number; @ApiProperty({ description: '入库或出库 0:入库 1:出库' }) @IsEnum(MaterialsInOrOutEnum) @@ -82,15 +83,24 @@ export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { export class MaterialsInOutQueryDto extends PagerDto { @ApiProperty({ description: '出入库时间YYYY-MM-DD' }) @IsOptional() - @IsArray() + // @IsString() @Transform(params => { // 开始和结束时间用的是一天的开始和一天的结束的时分秒 - const [start, end] = params.value; + const date = params.value; return [ - start ? `${formatToDate(start)} 00:00:00` : null, - end ? `${formatToDate(end)} 23:59:59` : null + date ? `${formatToDate(dayjs(date).startOf('month'))} 00:00:00` : null, + date ? `${formatToDate(dayjs(date).endOf('month'))} 23:59:59` : null ]; }) time?: string[]; + @ApiProperty({ description: '入库或出库 0:入库 1:出库' }) + @IsOptional() + @IsEnum(MaterialsInOrOutEnum) + inOrOut: MaterialsInOrOutEnum; + + @ApiProperty({ description: '产品名称' }) + @IsOptional() + @IsString() + product: string; } diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index d2e715a..d3b2cae 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -1,26 +1,30 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { + Column, + Entity, + JoinColumn, + JoinTable, + ManyToMany, + ManyToOne, + OneToMany, + Relation, + VirtualColumn +} from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; import { MaterialsInOrOutEnum } from '~/constants/enum'; +import { ProductEntity } from '~/modules/product/product.entity'; +import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity'; import { Storage } from '~/modules/tools/storage/storage.entity'; @Entity({ name: 'materials_in_out' }) export class MaterialsInOutEntity extends CommonEntity { @Column({ - name: 'product', + name: 'product_id', type: 'int', - comment: '产品名称' + comment: '产品' }) - @ApiProperty({ description: '产品名称' }) - product: number; - - @Column({ - name: 'unit', - type: 'int', - comment: '单位(字典)' - }) - @ApiProperty({ description: '单位(字典)' }) - unit: number; + @ApiProperty({ description: '产品' }) + productId: number; @Column({ name: 'inOrOut', @@ -78,6 +82,7 @@ export class MaterialsInOutEntity extends CommonEntity { name: 'issuance_number', type: 'varchar', length: 100, + nullable: true, comment: '领料单号' }) @ApiProperty({ description: '领料单号' }) @@ -95,6 +100,13 @@ export class MaterialsInOutEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + @ManyToOne(() => ProductEntity) + @JoinColumn({ name: 'product_id' }) + product: ProductEntity; + + @ApiHideProperty() + unit: DictItemEntity; + @ManyToMany(() => Storage, storage => storage.materialsInOut) @JoinTable({ name: 'materials_in_out_storage', diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 65578c0..5e17ee4 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -7,7 +7,11 @@ import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; import { paginate } from '~/helper/paginate'; import { Storage } from '~/modules/tools/storage/storage.entity'; -import { MaterialsInOutQueryDto, MaterialsInOutDto, MaterialsInOutUpdateDto } from './materials_in_out.dto'; +import { + MaterialsInOutQueryDto, + MaterialsInOutDto, + MaterialsInOutUpdateDto +} from './materials_in_out.dto'; import { MaterialsInOutEntity } from './materials_in_out.entity'; import { fieldSearch } from '~/shared/database/field-search'; @@ -26,25 +30,32 @@ export class MaterialsInOutService { async findAll({ page, pageSize, + product: productName, ...ext }: MaterialsInOutQueryDto): Promise> { - const queryBuilder = this.materialsInOutRepository + const sqb = this.materialsInOutRepository .createQueryBuilder('materialsInOut') .leftJoin('materialsInOut.files', 'files') - .addSelect(['files.id', 'files.path']) - // .where({ - // ...(materialsInOutNumber ? { materialsInOutNumber: Like(`%${materialsInOutNumber}%`) } : null), - // ...(title ? { title: Like(`%${title}%`) } : null), - // ...(isNumber(type) ? { type } : null), - // ...(isNumber(status) ? { status } : null) - // }) + .leftJoin('materialsInOut.product', 'product') + .leftJoin('product.unit', 'unit') + .addSelect(['files.id', 'files.path', 'product.id', 'product.name', 'unit.id', 'unit.label']) .where(fieldSearch(ext)) - .andWhere('materialsInOut.isDelete = 0'); - - return paginate(queryBuilder, { + .andWhere('materialsInOut.isDelete = 0') + .addOrderBy('materialsInOut.time', 'DESC'); + if (productName) { + sqb.andWhere('product.name like :productName', { productName: `%${productName}%` }); + } + let pageData = await paginate(sqb, { page, pageSize }); + // 产品表中的单位字段需要单独处理 + pageData.items = pageData.items.map(materialsInOut => { + materialsInOut.unit = materialsInOut.product.unit; + delete materialsInOut.product.unit; + return materialsInOut; + }); + return pageData; } /** diff --git a/src/modules/product/product.dto.ts b/src/modules/product/product.dto.ts index 4e17d7a..4824c24 100644 --- a/src/modules/product/product.dto.ts +++ b/src/modules/product/product.dto.ts @@ -1,26 +1,18 @@ import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; -import { - IsArray, - IsDate, - IsDateString, - IsIn, - IsInt, - IsNumber, - IsOptional, - IsString, - Matches, - MinLength -} from 'class-validator'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; -import { IsUnique } from '~/shared/database/constraints/unique.constraint'; -import { ProductEntity } from './product.entity'; export class ProductDto { @ApiProperty({ description: '产品名称' }) @IsString() name: string; + @ApiProperty({ description: '单位(字典)' }) + @IsOptional() + @IsNumber() + unitId: number; + @ApiProperty({ description: '所属公司' }) @IsOptional() @IsNumber() diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts index a5781ed..0c6aa58 100644 --- a/src/modules/product/product.entity.ts +++ b/src/modules/product/product.entity.ts @@ -14,6 +14,7 @@ import { CommonEntity } from '~/common/entity/common.entity'; import { Storage } from '../tools/storage/storage.entity'; import { CompanyEntity } from '../company/company.entity'; import pinyin from 'pinyin'; +import { DictItemEntity } from '../system/dict-item/dict-item.entity'; @Entity({ name: 'product' }) export class ProductEntity extends CommonEntity { @Column({ @@ -33,6 +34,14 @@ export class ProductEntity extends CommonEntity { @ApiProperty({ description: '所属公司' }) companyId: number; + @Column({ name: 'unit_id', type: 'int', comment: '单位(字典)', nullable: true }) + @ApiProperty({ description: '单位(字典)' }) + unitId: number; + + @ManyToOne(() => DictItemEntity) + @JoinColumn({ name: 'unit_id' }) + unit: DictItemEntity; + @ApiHideProperty() @Column({ name: 'name_pinyin', @@ -46,7 +55,6 @@ export class ProductEntity extends CommonEntity { @BeforeInsert() @BeforeUpdate() updateNamePinyin() { - console.log('sssssssssssssss') this.namePinyin = pinyin(this.name, { style: pinyin.STYLE_NORMAL, heteronym: false diff --git a/src/modules/product/product.service.ts b/src/modules/product/product.service.ts index 585a3f4..5428a3e 100644 --- a/src/modules/product/product.service.ts +++ b/src/modules/product/product.service.ts @@ -33,7 +33,8 @@ export class ProductService { .createQueryBuilder('product') .leftJoin('product.files', 'files') .leftJoin('product.company', 'company') - .addSelect(['files.id', 'files.path', 'company.name', 'company.id']) + .leftJoin('product.unit', 'unit') + .addSelect(['files.id', 'files.path', 'company.name', 'company.id','unit.id','unit.label']) .where(fieldSearch(ext)) .andWhere('product.isDelete = 0') .addOrderBy('product.namePinyin', 'ASC'); diff --git a/src/shared/database/field-search/index.ts b/src/shared/database/field-search/index.ts index dbee970..1067765 100644 --- a/src/shared/database/field-search/index.ts +++ b/src/shared/database/field-search/index.ts @@ -18,7 +18,7 @@ export const fieldSearch = (entity: Partial): ObjectLiteral => { if (Array.isArray(entity[key])) { result = { ...result, - ...(entity[key] && { createdAt: Between(entity[key][0], entity[key][1]) }) + ...(entity[key] && { [key]: Between(entity[key][0], entity[key][1]) }) }; } else { // Handle other object types From 466c3ca384ef7bf1efc9e1c53d1f1a7d20851a42 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 6 Mar 2024 17:13:14 +0800 Subject: [PATCH 23/64] =?UTF-8?q?feat:=20=E5=87=BA=E5=85=A5=E5=BA=93?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=A8=A1=E5=9D=97=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/enum/index.ts | 5 ++ .../in_out/materials_in_out.dto.ts | 51 +++++++++++--- .../in_out/materials_in_out.entity.ts | 32 +++++---- .../in_out/materials_in_out.service.ts | 67 ++++++++++++++++--- .../materials_inventory.module.ts | 2 + 5 files changed, 126 insertions(+), 31 deletions(-) diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts index 0fd9687..1d18590 100644 --- a/src/constants/enum/index.ts +++ b/src/constants/enum/index.ts @@ -18,3 +18,8 @@ export enum MaterialsInOrOutEnum { In, Out } + +// 系统参数key +export enum ParamConfigEnum { + MaterialsInOutPrefix = 'materials_in_out_prefix' +} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 8f999b1..8ca15c1 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -2,6 +2,7 @@ import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsArray, + IsBoolean, IsDate, IsDateString, IsEnum, @@ -11,7 +12,8 @@ import { IsOptional, IsString, Matches, - MinLength + MinLength, + ValidateIf } from 'class-validator'; import dayjs from 'dayjs'; import { PagerDto } from '~/common/dto/pager.dto'; @@ -21,9 +23,15 @@ import { formatToDate } from '~/utils'; export class MaterialsInOutDto { @ApiProperty({ description: '产品' }) + @ValidateIf(o => !o.inventoryNumber) @IsNumber() productId: number; + @ApiProperty({ description: '原材料库存编号' }) + @IsOptional() + @IsString() + inventoryNumber: string; + @ApiProperty({ description: '单位(字典)' }) @IsNumber() @IsOptional() @@ -68,10 +76,6 @@ export class MaterialsInOutDto { @IsString() @ApiProperty({ description: '项目' }) project: string; - - // @ApiProperty({ description: '附件' }) - // @IsOptional() - // files: Storage[]; } export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { @@ -80,7 +84,7 @@ export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { @IsArray() fileIds: number[]; } -export class MaterialsInOutQueryDto extends PagerDto { +export class MaterialsInOutQueryDto extends PagerDto { @ApiProperty({ description: '出入库时间YYYY-MM-DD' }) @IsOptional() // @IsString() @@ -97,10 +101,41 @@ export class MaterialsInOutQueryDto extends PagerDto { @ApiProperty({ description: '入库或出库 0:入库 1:出库' }) @IsOptional() @IsEnum(MaterialsInOrOutEnum) - inOrOut: MaterialsInOrOutEnum; + inOrOut?: MaterialsInOrOutEnum; @ApiProperty({ description: '产品名称' }) @IsOptional() @IsString() - product: string; + product?: string; + + @ApiProperty({ description: '经办人' }) + @IsOptional() + @IsString() + agent?: string; + + @ApiProperty({ description: '领料单号' }) + @IsOptional() + @IsString() + issuanceNumber?: string; + + @ApiProperty({ description: '原材料库存编号' }) + @IsOptional() + @IsString() + inventoryNumber?: string; + + + @IsOptional() + @IsString() + @ApiProperty({ description: '备注' }) + remark?: string; + + @IsOptional() + @IsString() + @ApiProperty({ description: '项目' }) + project?: string; + + @IsOptional() + @IsBoolean() + @ApiProperty({ description: '是否是用于创建出库记录' }) + isCreateOut?: boolean; } diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index d3b2cae..5197eb1 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -1,23 +1,34 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Expose } from 'class-transformer'; +import pinyin from 'pinyin'; import { + BeforeInsert, Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, - OneToMany, Relation, - VirtualColumn + Repository } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; -import { MaterialsInOrOutEnum } from '~/constants/enum'; +import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum'; import { ProductEntity } from '~/modules/product/product.entity'; -import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity'; +import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity'; import { Storage } from '~/modules/tools/storage/storage.entity'; - @Entity({ name: 'materials_in_out' }) export class MaterialsInOutEntity extends CommonEntity { + @Column({ + name: 'inventory_number', + type: 'varchar', + length: 50, + comment: '原材料库存编号' + }) + @ApiProperty({ description: '原材料库存编号' }) + inventoryNumber: string; + @Column({ name: 'product_id', type: 'int', @@ -55,9 +66,9 @@ export class MaterialsInOutEntity extends CommonEntity { @Column({ name: 'unit_price', type: 'decimal', - precision: 10, + precision: 15, default: 0, - scale: 2, + scale: 10, comment: '单价' }) @ApiProperty({ description: '单价' }) @@ -66,9 +77,9 @@ export class MaterialsInOutEntity extends CommonEntity { @Column({ name: 'amount', type: 'decimal', - precision: 10, + precision: 15, default: 0, - scale: 2, + scale: 10, comment: '金额' }) @ApiProperty({ description: '金额' }) @@ -104,9 +115,6 @@ export class MaterialsInOutEntity extends CommonEntity { @JoinColumn({ name: 'product_id' }) product: ProductEntity; - @ApiHideProperty() - unit: DictItemEntity; - @ManyToMany(() => Storage, storage => storage.materialsInOut) @JoinTable({ name: 'materials_in_out_storage', diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 5e17ee4..3c8b0c1 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -14,6 +14,8 @@ import { } from './materials_in_out.dto'; import { MaterialsInOutEntity } from './materials_in_out.entity'; import { fieldSearch } from '~/shared/database/field-search'; +import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity'; +import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum'; @Injectable() export class MaterialsInOutService { @@ -22,7 +24,9 @@ export class MaterialsInOutService { @InjectRepository(MaterialsInOutEntity) private materialsInOutRepository: Repository, @InjectRepository(Storage) - private storageRepository: Repository + private storageRepository: Repository, + @InjectRepository(ParamConfigEntity) + private paramConfigRepository: Repository ) {} /** * 查询所有出入库记录 @@ -31,6 +35,7 @@ export class MaterialsInOutService { page, pageSize, product: productName, + isCreateOut, ...ext }: MaterialsInOutQueryDto): Promise> { const sqb = this.materialsInOutRepository @@ -38,23 +43,21 @@ export class MaterialsInOutService { .leftJoin('materialsInOut.files', 'files') .leftJoin('materialsInOut.product', 'product') .leftJoin('product.unit', 'unit') - .addSelect(['files.id', 'files.path', 'product.id', 'product.name', 'unit.id', 'unit.label']) + .leftJoin('product.company', 'company') + .addSelect(['files.path', 'product.name', 'unit.label', 'company.name']) .where(fieldSearch(ext)) .andWhere('materialsInOut.isDelete = 0') - .addOrderBy('materialsInOut.time', 'DESC'); + .addOrderBy('materialsInOut.createdAt', 'DESC'); if (productName) { sqb.andWhere('product.name like :productName', { productName: `%${productName}%` }); } - let pageData = await paginate(sqb, { + if (isCreateOut) { + sqb.andWhere('materialsInOut.inOrOut = 0'); + } + const pageData = await paginate(sqb, { page, pageSize }); - // 产品表中的单位字段需要单独处理 - pageData.items = pageData.items.map(materialsInOut => { - materialsInOut.unit = materialsInOut.product.unit; - delete materialsInOut.product.unit; - return materialsInOut; - }); return pageData; } @@ -62,7 +65,22 @@ export class MaterialsInOutService { * 新增 */ async create(dto: MaterialsInOutDto): Promise { - await this.materialsInOutRepository.insert(dto); + let { inOrOut, inventoryNumber } = dto; + if (inOrOut === MaterialsInOrOutEnum.In) { + inventoryNumber = await this.generateInventoryNumber(); + } else { + const inRecord = await this.materialsInOutRepository.findOne({ + where: { + inventoryNumber + } + }); + const { productId } = inRecord; + dto.productId = productId; + } + await this.materialsInOutRepository.insert({ + ...this.materialsInOutRepository.create(dto), + inventoryNumber + }); } /** @@ -141,4 +159,31 @@ export class MaterialsInOutService { .addAndRemove(linkedFiles, materialsInOut.files); }); } + + /** + * 生成库存单号 + * @returns 库存单号 + */ + async generateInventoryNumber() { + const prefix = + ( + await this.paramConfigRepository.findOne({ + where: { + key: ParamConfigEnum.MaterialsInOutPrefix + } + }) + )?.value || ''; + const lastMaterial = await this.materialsInOutRepository + .createQueryBuilder('materialsInOut') + .select( + `MAX(CAST(REPLACE(materialsInOut.inventoryNumber, '${prefix}', '') AS UNSIGNED))`, + 'maxInventoryNumber' + ) + .getRawOne(); + const lastNumber = lastMaterial.maxInventoryNumber + ? parseInt(lastMaterial.maxInventoryNumber.replace(prefix, '')) + : 0; + const newNumber = lastNumber + 1 < 1000 ? 1000 : lastNumber + 1; + return `${prefix}${newNumber}`; + } } diff --git a/src/modules/materials_inventory/materials_inventory.module.ts b/src/modules/materials_inventory/materials_inventory.module.ts index a834aa5..f3b42ea 100644 --- a/src/modules/materials_inventory/materials_inventory.module.ts +++ b/src/modules/materials_inventory/materials_inventory.module.ts @@ -7,10 +7,12 @@ import { StorageModule } from '../tools/storage/storage.module'; import { MaterialsInOutController } from './in_out/materials_in_out.controller'; import { MaterialsInOutService } from './in_out/materials_in_out.service'; import { MaterialsInOutEntity } from './in_out/materials_in_out.entity'; +import { ParamConfigModule } from '../system/param-config/param-config.module'; @Module({ imports: [ TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity]), + ParamConfigModule, StorageModule ], controllers: [MaterialsInventoryController, MaterialsInOutController], From 933d9f1e1352b28c9840ec46d7e26a0e7f24495e Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 7 Mar 2024 11:11:13 +0800 Subject: [PATCH 24/64] =?UTF-8?q?feat:=20=E9=A1=B9=E7=9B=AE=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 12 +- .../in_out/materials_in_out.dto.ts | 13 +- .../in_out/materials_in_out.entity.ts | 11 +- .../in_out/materials_in_out.service.ts | 8 +- src/modules/project/project.controller.ts | 79 +++++++++++ src/modules/project/project.dto.ts | 40 ++++++ src/modules/project/project.entity.ts | 38 ++++++ src/modules/project/project.module.ts | 14 ++ src/modules/project/project.service.ts | 127 ++++++++++++++++++ src/modules/tools/storage/storage.entity.ts | 5 + 10 files changed, 334 insertions(+), 13 deletions(-) create mode 100644 src/modules/project/project.controller.ts create mode 100644 src/modules/project/project.dto.ts create mode 100644 src/modules/project/project.entity.ts create mode 100644 src/modules/project/project.module.ts create mode 100644 src/modules/project/project.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 93b7423..5e6ab5d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -31,6 +31,7 @@ import { VehicleUsageService } from './modules/vehicle-usage/vehicle-usage.servi import { MaterialsInventoryModule } from './modules/materials_inventory/materials_inventory.module'; import { CompanyModule } from './modules/company/company.module'; import { ProductModule } from './modules/product/product.module'; +import { ProjectModule } from './modules/project/project.module'; @Module({ imports: [ @@ -58,16 +59,23 @@ import { ProductModule } from './modules/product/product.module'; // end biz TodoModule, - + // 合同模块 ContractModule, + // 车辆管理 VehicleUsageModule, + // 原材料库存 MaterialsInventoryModule, + // 公司管理 CompanyModule, - ProductModule + // 产品管理 + ProductModule, + + // 项目管理 + ProjectModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 8ca15c1..ec41f22 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -22,7 +22,12 @@ import { Storage } from '~/modules/tools/storage/storage.entity'; import { formatToDate } from '~/utils'; export class MaterialsInOutDto { - @ApiProperty({ description: '产品' }) + @IsOptional() + @IsNumber() + @ApiProperty({ description: '项目Id' }) + projectId?: number; + + @ApiProperty({ description: '产品Id' }) @ValidateIf(o => !o.inventoryNumber) @IsNumber() productId: number; @@ -71,11 +76,6 @@ export class MaterialsInOutDto { @IsString() @ApiProperty({ description: '备注' }) remark: string; - - @IsOptional() - @IsString() - @ApiProperty({ description: '项目' }) - project: string; } export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { @@ -122,7 +122,6 @@ export class MaterialsInOutQueryDto extends PagerDto { @IsOptional() @IsString() inventoryNumber?: string; - @IsOptional() @IsString() diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index 5197eb1..833684f 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -16,6 +16,7 @@ import { import { CommonEntity } from '~/common/entity/common.entity'; import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum'; import { ProductEntity } from '~/modules/product/product.entity'; +import { ProjectEntity } from '~/modules/project/project.entity'; import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity'; import { Storage } from '~/modules/tools/storage/storage.entity'; @Entity({ name: 'materials_in_out' }) @@ -103,14 +104,18 @@ export class MaterialsInOutEntity extends CommonEntity { @ApiProperty({ description: '备注' }) remark: string; - @Column({ name: 'project', type: 'varchar', length: 255, comment: '项目', nullable: false }) - @ApiProperty({ description: '项目' }) - project: string; + @Column({ name: 'project_id', type: 'int', comment: '项目', nullable: true }) + @ApiProperty({ description: '项目Id' }) + projectId: number; @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + @ManyToOne(() => ProjectEntity) + @JoinColumn({ name: 'project_id' }) + project: ProjectEntity; + @ManyToOne(() => ProductEntity) @JoinColumn({ name: 'product_id' }) product: ProductEntity; diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 3c8b0c1..7c685aa 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -35,22 +35,28 @@ export class MaterialsInOutService { page, pageSize, product: productName, + project: projectName, isCreateOut, ...ext }: MaterialsInOutQueryDto): Promise> { const sqb = this.materialsInOutRepository .createQueryBuilder('materialsInOut') .leftJoin('materialsInOut.files', 'files') + .leftJoin('materialsInOut.project', 'project') .leftJoin('materialsInOut.product', 'product') .leftJoin('product.unit', 'unit') .leftJoin('product.company', 'company') - .addSelect(['files.path', 'product.name', 'unit.label', 'company.name']) + .addSelect(['files.path', 'project.name', 'product.name', 'unit.label', 'company.name']) .where(fieldSearch(ext)) .andWhere('materialsInOut.isDelete = 0') .addOrderBy('materialsInOut.createdAt', 'DESC'); if (productName) { sqb.andWhere('product.name like :productName', { productName: `%${productName}%` }); } + if (projectName) { + sqb.andWhere('project.name like :projectName', { projectName: `%${projectName}%` }); + } + if (isCreateOut) { sqb.andWhere('materialsInOut.inOrOut = 0'); } diff --git a/src/modules/project/project.controller.ts b/src/modules/project/project.controller.ts new file mode 100644 index 0000000..605090b --- /dev/null +++ b/src/modules/project/project.controller.ts @@ -0,0 +1,79 @@ +import { + Body, + Controller, + Get, + Query, + Put, + Delete, + Post, + BadRequestException +} from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { ProjectService } from './project.service'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { ProjectEntity } from './project.entity'; +import { ProjectDto, ProjectQueryDto, ProjectUpdateDto } from './project.dto'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +export const permissions = definePermission('app:project', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('Project - 项目') +@ApiSecurityAuth() +@Controller('project') +export class ProjectController { + constructor(private projectService: ProjectService) {} + + @Get() + @ApiOperation({ summary: '分页获取项目列表' }) + @ApiResult({ type: [ProjectEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: ProjectQueryDto) { + return this.projectService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取项目信息' }) + @ApiResult({ type: ProjectDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.projectService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增项目' }) + @Perm(permissions.CREATE) + async create(@Body() dto: ProjectDto): Promise { + await this.projectService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新项目' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: ProjectUpdateDto): Promise { + await this.projectService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除项目' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.projectService.delete(id); + } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: ProjectUpdateDto + ): Promise { + await this.projectService.unlinkAttachments(id, fileIds); + } +} diff --git a/src/modules/project/project.dto.ts b/src/modules/project/project.dto.ts new file mode 100644 index 0000000..6c96b6e --- /dev/null +++ b/src/modules/project/project.dto.ts @@ -0,0 +1,40 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { IsArray, IsOptional, IsString } from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '../tools/storage/storage.entity'; +import { IsUnique } from '~/shared/database/constraints/unique.constraint'; +import { ProjectEntity } from './project.entity'; + +export class ProjectDto { + @ApiProperty({ description: '项目名称' }) + @IsUnique(ProjectEntity, { message: '已存在同名项目' }) + @IsString() + name: string; + + @ApiProperty({ description: '附件' }) + files: Storage[]; +} + +export class ProjectUpdateDto extends PartialType(ProjectDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} + +export class ComapnyCreateDto extends PartialType(ProjectDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} + +export class ProjectQueryDto extends IntersectionType( + PagerDto, + PartialType(ProjectDto) +) { + @ApiProperty({ description: '项目名称' }) + @IsOptional() + @IsString() + name: string; +} diff --git a/src/modules/project/project.entity.ts b/src/modules/project/project.entity.ts new file mode 100644 index 0000000..a80b296 --- /dev/null +++ b/src/modules/project/project.entity.ts @@ -0,0 +1,38 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinTable, ManyToMany, OneToMany, Relation } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '../tools/storage/storage.entity'; +import { ProductEntity } from '../product/product.entity'; +import { MaterialsInOutEntity } from '../materials_inventory/in_out/materials_in_out.entity'; + +/** + * 项目实体类 + */ +@Entity({ name: 'project' }) +export class ProjectEntity extends CommonEntity { + @Column({ + name: 'name', + type: 'varchar', + unique: true, + length: 255, + comment: '项目名称' + }) + @ApiProperty({ description: '项目名称' }) + name: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + + @ApiHideProperty() + @OneToMany(() => MaterialsInOutEntity, product => product.project) + materialsInOuts: Relation; + + @ManyToMany(() => Storage, storage => storage.projects) + @JoinTable({ + name: 'project_storage', + joinColumn: { name: 'project_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; +} diff --git a/src/modules/project/project.module.ts b/src/modules/project/project.module.ts new file mode 100644 index 0000000..170c6eb --- /dev/null +++ b/src/modules/project/project.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { ProjectController } from './project.controller'; +import { ProjectService } from './project.service'; +import { ProjectEntity } from './project.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { StorageModule } from '../tools/storage/storage.module'; +import { DatabaseModule } from '~/shared/database/database.module'; + +@Module({ + imports: [TypeOrmModule.forFeature([ProjectEntity]), StorageModule, DatabaseModule], + controllers: [ProjectController], + providers: [ProjectService] +}) +export class ProjectModule {} diff --git a/src/modules/project/project.service.ts b/src/modules/project/project.service.ts new file mode 100644 index 0000000..627299c --- /dev/null +++ b/src/modules/project/project.service.ts @@ -0,0 +1,127 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { ProjectEntity } from './project.entity'; +import { EntityManager, Like, Repository } from 'typeorm'; +import { ProjectDto, ProjectQueryDto, ProjectUpdateDto } from './project.dto'; +import { Pagination } from '~/helper/paginate/pagination'; +import { paginate } from '~/helper/paginate'; +import { Storage } from '../tools/storage/storage.entity'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { fieldSearch } from '~/shared/database/field-search'; + +@Injectable() +export class ProjectService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(ProjectEntity) + private projectRepository: Repository, + @InjectRepository(Storage) + private storageRepository: Repository + ) {} + + /** + * 分页查询所有 + */ + async findAll({ + page, + pageSize, + ...fields + }: ProjectQueryDto): Promise> { + const queryBuilder = this.projectRepository + .createQueryBuilder('project') + .leftJoin('project.files', 'files') + .addSelect(['files.id', 'files.path']) + .where(fieldSearch(fields)) + .andWhere('project.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: ProjectDto): Promise { + await this.projectRepository.insert(dto); + } + + /** + * 更新 + */ + async update(id: number, { fileIds, ...data }: Partial): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(ProjectEntity, id, { + ...data + }); + const project = await this.projectRepository + .createQueryBuilder('project') + .leftJoinAndSelect('project.files', 'files') + .where('project.id = :id', { id }) + .getOne(); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(ProjectEntity, 'files') + .of(id) + .addAndRemove(fileIds, project.files); + } + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 比较重要,做逻辑删除 + await this.projectRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个信息 + */ + async info(id: number) { + const info = await this.projectRepository + .createQueryBuilder('project') + .where({ + id + }) + .andWhere('project.isDelete = 0') + .getOne(); + return info; + } + + /** + * 解除附件关联 + * @param id 实体ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const project = await this.projectRepository + .createQueryBuilder('project') + .leftJoinAndSelect('project.files', 'files') + .where('project.id = :id', { id }) + .getOne(); + const linkedFiles = project.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(ProjectEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, project.files); + }); + } +} diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index c1a59b7..ae0034a 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -7,6 +7,7 @@ import { ContractEntity } from '~/modules/contract/contract.entity'; import { MaterialsInOutEntity } from '~/modules/materials_inventory/in_out/materials_in_out.entity'; import { MaterialsInventoryEntity } from '~/modules/materials_inventory/materials_inventory.entity'; import { ProductEntity } from '~/modules/product/product.entity'; +import { ProjectEntity } from '~/modules/project/project.entity'; @Entity({ name: 'tool_storage' }) export class Storage extends CommonEntity { @@ -58,4 +59,8 @@ export class Storage extends CommonEntity { @ApiHideProperty() @ManyToMany(() => ProductEntity, product => product.files) products: Relation; + + @ApiHideProperty() + @ManyToMany(() => ProjectEntity, project => project.files) + projects: Relation; } From 384b11bd4c5ab0707b2824d5ef1b9062cdf283d5 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 7 Mar 2024 14:12:01 +0800 Subject: [PATCH 25/64] =?UTF-8?q?feat:=20exclejs=20=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=88=9D=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/sql/hxoa.sql | 1340 ++++++++++------- deploy/sql/raw_materials_inventory.sql | 23 - package.json | 1 + pnpm-lock.yaml | 320 +++- .../materials_inventory.controller.ts | 32 +- .../materials_inventory.dto.ts | 21 + .../materials_inventory.service.ts | 44 +- 7 files changed, 1177 insertions(+), 604 deletions(-) delete mode 100644 deploy/sql/raw_materials_inventory.sql diff --git a/deploy/sql/hxoa.sql b/deploy/sql/hxoa.sql index 872e522..8839b95 100644 --- a/deploy/sql/hxoa.sql +++ b/deploy/sql/hxoa.sql @@ -1,119 +1,467 @@ -/* - Navicat Premium Data Transfer +-- MySQL dump 10.13 Distrib 8.0.28, for Win64 (x86_64) +-- +-- Host: 127.0.0.1 Database: hxoa +-- ------------------------------------------------------ +-- Server version 8.3.0 - Source Server : 13307 - Source Server Type : MySQL - Source Server Version : 80300 - Source Host : localhost:13307 - Source Schema : hxoa +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - Target Server Type : MySQL - Target Server Version : 80300 - File Encoding : 65001 +-- +-- Table structure for table `company` +-- - Date: 28/02/2024 11:52:46 -*/ - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - --- ---------------------------- --- Table structure for sys_captcha_log --- ---------------------------- -DROP TABLE IF EXISTS `sys_captcha_log`; -CREATE TABLE `sys_captcha_log` ( +DROP TABLE IF EXISTS `company`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `company` ( `id` int NOT NULL AUTO_INCREMENT, - `user_id` int NULL DEFAULT NULL, - `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, - `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, - `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公司名称', + `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `IDX_a76c5cd486f7779bd9c319afd2` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `company` +-- + +/*!40000 ALTER TABLE `company` DISABLE KEYS */; +INSERT INTO `company` VALUES (1,'2024-03-04 15:44:43.005593','2024-03-04 16:09:44.000000','深圳市立创电子商务有限公司',0),(4,'2024-03-04 16:05:34.701780','2024-03-04 16:05:34.701780','深圳市诚亨泰科技有限公司',0),(5,'2024-03-04 16:05:38.867786','2024-03-04 16:05:38.867786','东莞市顶源电子有限公司',0),(6,'2024-03-04 16:05:42.479027','2024-03-04 16:05:42.479027','深圳市福田区赛格电子市场金佳电子经营部',0),(7,'2024-03-04 16:05:46.775364','2024-03-04 16:05:46.775364','深圳市思界电子科技有限公司',0),(8,'2024-03-04 16:05:55.806537','2024-03-04 16:05:55.806537','广州市星翼电信科技有限公司',0),(9,'2024-03-04 16:06:03.003860','2024-03-04 16:09:49.000000','快递费',1),(10,'2024-03-04 16:06:09.788572','2024-03-04 16:06:09.788572','青岛丰喆精密模具有限公司',0),(11,'2024-03-04 16:06:12.872983','2024-03-04 16:06:12.872983','深圳嘉立创科技集团股份有限公司',0),(12,'2024-03-04 16:06:19.823410','2024-03-04 16:06:19.823410','北京特倍福电子技术有限公司',0),(13,'2024-03-04 16:06:25.937749','2024-03-04 16:06:25.937749','上海脉芯网络科技有限公司',0); +/*!40000 ALTER TABLE `company` ENABLE KEYS */; + +-- +-- Table structure for table `company_storage` +-- + +DROP TABLE IF EXISTS `company_storage`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `company_storage` ( + `company_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`company_id`,`file_id`), + KEY `IDX_0958ee6ca6f52985840624bb91` (`company_id`), + KEY `IDX_bdd3a301229b9dec4b95549dfe` (`file_id`), + CONSTRAINT `FK_0958ee6ca6f52985840624bb916` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_bdd3a301229b9dec4b95549dfe7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `company_storage` +-- + +/*!40000 ALTER TABLE `company_storage` DISABLE KEYS */; +/*!40000 ALTER TABLE `company_storage` ENABLE KEYS */; + +-- +-- Table structure for table `contract` +-- + +DROP TABLE IF EXISTS `contract`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `contract` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `contract_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '合同编号', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '合同标题', + `type` int NOT NULL COMMENT '合同类型(字典)', + `party_a` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '甲方', + `party_b` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '乙方', + `signing_date` date DEFAULT NULL, + `delivery_deadline` date DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT '0' COMMENT '审核状态(字典)', + `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `IDX_a2f8461960ce0fcbd0d6551009` (`contract_number`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `contract` +-- + +/*!40000 ALTER TABLE `contract` DISABLE KEYS */; +INSERT INTO `contract` VALUES (1,'2024-02-29 11:38:20.959071','2024-03-06 10:40:25.000000','2022092301','山东矿机华信智能科技有限公司',11,'山东矿机华信','青岛比特维尔','2024-01-01','2024-02-01',1,0),(2,'2024-02-29 16:11:54.286196','2024-02-29 16:50:30.000000','www','weqw',10,'rqw','rwq','2024-02-01','2024-02-28',2,0),(3,'2024-03-01 15:26:07.794697','2024-03-01 15:30:49.000000','test1211','test',13,'山东搞笑信息','齐鲁医院','2024-03-01','2024-03-20',0,1),(4,'2024-03-01 16:47:22.125670','2024-03-01 16:47:22.125670','33024242','21412412',11,'2141','41241','2024-03-01','2024-03-01',0,0); +/*!40000 ALTER TABLE `contract` ENABLE KEYS */; + +-- +-- Table structure for table `contract_storage` +-- + +DROP TABLE IF EXISTS `contract_storage`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `contract_storage` ( + `contract_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`contract_id`,`file_id`), + KEY `IDX_b0a3f22af56decbc128c674447` (`contract_id`), + KEY `IDX_2fe7cda0f292b099b7e13f8f61` (`file_id`), + CONSTRAINT `FK_2fe7cda0f292b099b7e13f8f612` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`), + CONSTRAINT `FK_b0a3f22af56decbc128c674447e` FOREIGN KEY (`contract_id`) REFERENCES `contract` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `contract_storage` +-- + +/*!40000 ALTER TABLE `contract_storage` DISABLE KEYS */; +/*!40000 ALTER TABLE `contract_storage` ENABLE KEYS */; + +-- +-- Table structure for table `materials_in_out` +-- + +DROP TABLE IF EXISTS `materials_in_out`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `materials_in_out` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `product_id` int NOT NULL COMMENT '产品', + `inOrOut` tinyint NOT NULL COMMENT '入库或出库', + `time` date DEFAULT NULL COMMENT '时间', + `quantity` int NOT NULL DEFAULT '0' COMMENT '数量', + `unit_price` decimal(15,10) NOT NULL DEFAULT '0.0000000000' COMMENT '单价', + `amount` decimal(15,10) NOT NULL DEFAULT '0.0000000000' COMMENT '金额', + `agent` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '经办人', + `issuance_number` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '领料单号', + `remark` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', + `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', + `inventory_number` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '原材料库存编号', + `project_id` int DEFAULT NULL COMMENT '项目', + PRIMARY KEY (`id`), + KEY `FK_770f1c4afd9631499ccc08bd58b` (`product_id`), + KEY `FK_7a5bd19f8fd458f6336efedf765` (`project_id`), + CONSTRAINT `FK_770f1c4afd9631499ccc08bd58b` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`), + CONSTRAINT `FK_7a5bd19f8fd458f6336efedf765` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `materials_in_out` +-- + +/*!40000 ALTER TABLE `materials_in_out` DISABLE KEYS */; +INSERT INTO `materials_in_out` VALUES (7,'2024-03-06 15:39:38.305753','2024-03-07 11:10:10.000000',19,0,'2024-02-16',100,557.5200000000,55752.2100000000,'蒋博',NULL,'沙湾项目',0,'KC1000',3),(16,'2024-03-06 15:50:12.315922','2024-03-07 11:10:07.000000',19,1,'2024-02-16',100,100.0000000000,55752.2100000000,'蒋博','3153019','沙湾项目',0,'KC1000',3),(17,'2024-03-06 16:06:57.397785','2024-03-06 17:09:14.325720',19,1,'2024-03-06',33,33.0000000000,33.0000000000,'3','33','33',1,'KC1000',NULL),(18,'2024-03-06 16:08:37.713482','2024-03-07 11:10:03.000000',20,0,'2024-02-16',100,557.5200000000,55752.2100000000,'蒋博',NULL,'沙湾项目',0,'KC1001',3),(19,'2024-03-06 16:30:52.126055','2024-03-07 11:09:57.000000',20,1,'2024-02-16',100,35.4000000000,3539.8200000000,'蒋博','3153019','沙湾项目',0,'KC1001',3),(20,'2024-03-06 16:34:04.845239','2024-03-07 11:09:52.000000',21,0,'2024-02-16',202,18.5800000000,3753.9800000000,'戚兆伟',NULL,'沙湾项目',0,'KC1002',3),(21,'2024-03-06 16:34:40.829659','2024-03-07 11:09:47.000000',21,1,'2024-02-16',202,18.5800000000,3753.9800000000,'戚兆伟','3153011','沙湾项目',0,'KC1002',3),(22,'2024-03-06 16:35:23.756416','2024-03-07 11:09:43.000000',21,0,'2024-02-16',200,26.5500000000,5309.7300000000,'戚兆伟',NULL,'沙湾项目',0,'KC1003',3),(23,'2024-03-06 16:35:55.157718','2024-03-07 11:09:38.000000',21,1,'2024-02-16',200,26.5500000000,5309.7300000000,'戚兆伟','3153011','沙湾项目',0,'KC1003',3),(24,'2024-03-06 16:38:52.490493','2024-03-07 11:09:34.000000',22,0,'2024-02-20',200,300.8800000000,60176.9900000000,'朱明仁',NULL,'东大项目',0,'KC1004',2),(25,'2024-03-06 16:39:55.423422','2024-03-07 11:09:30.000000',22,1,'2024-02-20',200,300.8849500000,60176.9900000000,'朱明仁','3153022','东大项目',0,'KC1004',2),(26,'2024-03-06 16:41:20.939530','2024-03-07 11:09:27.000000',23,0,'2024-02-19',1,22005.7500000000,22005.7500000000,'朱明仁',NULL,'东大项目',0,'KC1005',2),(27,'2024-03-06 16:41:50.876715','2024-03-07 11:08:31.000000',23,1,'2024-02-19',1,22005.7500000000,22005.7500000000,'朱明仁','3153023','东大项目',0,'KC1005',2),(28,'2024-03-07 09:49:54.511550','2024-03-07 09:49:54.511550',24,0,'2024-02-16',800,0.9609250000,768.7400000000,'朱明仁',NULL,'星火项目',0,'KC1006',1); +/*!40000 ALTER TABLE `materials_in_out` ENABLE KEYS */; + +-- +-- Table structure for table `materials_in_out_storage` +-- + +DROP TABLE IF EXISTS `materials_in_out_storage`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `materials_in_out_storage` ( + `materials_in_out_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`materials_in_out_id`,`file_id`), + KEY `IDX_9df13ab4d4747575c310668581` (`materials_in_out_id`), + KEY `IDX_96c00bfbcd71e93a6cc070e8e6` (`file_id`), + CONSTRAINT `FK_96c00bfbcd71e93a6cc070e8e6c` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`), + CONSTRAINT `FK_9df13ab4d4747575c3106685810` FOREIGN KEY (`materials_in_out_id`) REFERENCES `materials_in_out` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `materials_in_out_storage` +-- + +/*!40000 ALTER TABLE `materials_in_out_storage` DISABLE KEYS */; +/*!40000 ALTER TABLE `materials_in_out_storage` ENABLE KEYS */; + +-- +-- Table structure for table `materials_inventory` +-- + +DROP TABLE IF EXISTS `materials_inventory`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `materials_inventory` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `company_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公司名称', + `product` int NOT NULL COMMENT '产品名称(字典)', + `unit` int NOT NULL COMMENT '单位(字典)', + `previous_inventory_quantity` int NOT NULL DEFAULT '0' COMMENT '之前的库存数量', + `previous_unit_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '之前的单价', + `previous_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '之前的金额', + `inventory_time` date DEFAULT NULL COMMENT '入库时间', + `inventory_quantity` int NOT NULL DEFAULT '0' COMMENT '入库数量', + `inventory_unit_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '入库单价', + `inventory_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '入库金额', + `out_time` date DEFAULT NULL COMMENT '出库时间', + `out_quantity` int NOT NULL DEFAULT '0' COMMENT '出库数量', + `out_unit_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '出库单价', + `out_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '出库金额', + `current_inventory_quantity` int NOT NULL DEFAULT '0' COMMENT '现在的结存数量', + `current_unit_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '现在的单价', + `current_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '现在的金额', + `agent` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '经办人', + `issuance_number` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '领料单号', + `project` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', + `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `materials_inventory` +-- + +/*!40000 ALTER TABLE `materials_inventory` DISABLE KEYS */; +INSERT INTO `materials_inventory` VALUES (1,'2024-03-04 13:47:10.258131','2024-03-04 14:15:15.373283','北京特倍福电子技术有限公司',1,14,0,0.00,0.00,'2024-02-16',100,557.52,55752.21,'2024-02-16',100,557.52,55752.21,0,0.00,0.00,'蒋博','3153019','沙湾项目','沙湾项目',0); +/*!40000 ALTER TABLE `materials_inventory` ENABLE KEYS */; + +-- +-- Table structure for table `product` +-- + +DROP TABLE IF EXISTS `product`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `product` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '产品名称', + `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', + `company_id` int DEFAULT NULL COMMENT '所属公司', + `name_pinyin` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '产品名称的拼音', + `unit_id` int DEFAULT NULL COMMENT '单位(字典)', + PRIMARY KEY (`id`), + KEY `FK_a0503db1630a5b8a4d7deabd556` (`company_id`), + KEY `FK_b15422982adca3bf53adfb535de` (`unit_id`), + CONSTRAINT `FK_a0503db1630a5b8a4d7deabd556` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`), + CONSTRAINT `FK_b15422982adca3bf53adfb535de` FOREIGN KEY (`unit_id`) REFERENCES `sys_dict_item` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `product` +-- + +/*!40000 ALTER TABLE `product` DISABLE KEYS */; +INSERT INTO `product` VALUES (1,'2024-03-04 16:47:37.604035','2024-03-05 21:52:59.000000','位移传感器',0,10,'weiyichuanganqi',NULL),(2,'2024-03-04 16:47:41.483273','2024-03-06 09:13:03.000000','磁环',1,6,'cihuan',NULL),(3,'2024-03-04 16:47:44.911165','2024-03-05 21:52:52.000000','天线',0,5,'tianxian',NULL),(4,'2024-03-04 16:47:48.398543','2024-03-05 21:53:17.000000','集成电路',0,8,'jichengdianlu',NULL),(5,'2024-03-05 09:09:05.015757','2024-03-05 09:27:28.000000','更新',1,NULL,NULL,NULL),(12,'2024-03-05 09:25:29.584423','2024-03-06 09:12:02.000000','巴伦',0,13,'balun',15),(13,'2024-03-05 13:53:15.998630','2024-03-06 09:14:13.000000','极薄煤层控制器',0,10,'jibaomeicengkongzhiqi',14),(14,'2024-03-05 16:05:30.485017','2024-03-06 09:13:59.000000','天线',0,13,'tianxian',15),(15,'2024-03-05 17:21:20.378006','2024-03-05 21:52:57.000000','USB智能充电线',0,7,'USBzhinengchongdianxian',NULL),(16,'2024-03-05 17:24:03.148627','2024-03-05 17:30:48.000000','33',1,5,NULL,NULL),(17,'2024-03-05 17:30:32.450320','2024-03-05 17:30:50.000000','test',1,5,'test',NULL),(18,'2024-03-05 21:52:11.477508','2024-03-05 21:53:34.000000','新增',1,NULL,'xinzeng',NULL),(19,'2024-03-06 08:53:25.600367','2024-03-06 09:13:39.000000','位移传感器',0,12,'weiyichuanganqi',14),(20,'2024-03-06 09:12:47.327409','2024-03-06 09:12:47.327409','磁环',0,12,'cihuan',14),(21,'2024-03-06 09:13:21.382776','2024-03-06 09:13:27.000000','集成电路',0,13,'jichengdianlu',15),(22,'2024-03-06 16:38:06.999498','2024-03-06 16:38:14.000000','电磁阀驱动器',0,10,'diancifaqudongqi',14),(23,'2024-03-06 16:40:32.859846','2024-03-06 16:40:32.859846','电子元器件',0,11,'dianziyuanqijian',21),(24,'2024-03-07 09:48:35.854273','2024-03-07 09:48:35.854273','排针',0,1,'paizhen',15); +/*!40000 ALTER TABLE `product` ENABLE KEYS */; + +-- +-- Table structure for table `product_storage` +-- + +DROP TABLE IF EXISTS `product_storage`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `product_storage` ( + `product_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`product_id`,`file_id`), + KEY `IDX_6dd288598f0a0ea3f72f31cb42` (`product_id`), + KEY `IDX_eecbd68d7d4d565baecee2d76c` (`file_id`), + CONSTRAINT `FK_6dd288598f0a0ea3f72f31cb422` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_eecbd68d7d4d565baecee2d76c7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `product_storage` +-- + +/*!40000 ALTER TABLE `product_storage` DISABLE KEYS */; +INSERT INTO `product_storage` VALUES (1,124); +/*!40000 ALTER TABLE `product_storage` ENABLE KEYS */; + +-- +-- Table structure for table `project` +-- + +DROP TABLE IF EXISTS `project`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `project` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目名称', + `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', + PRIMARY KEY (`id`), + UNIQUE KEY `IDX_dedfea394088ed136ddadeee89` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `project` +-- + +/*!40000 ALTER TABLE `project` DISABLE KEYS */; +INSERT INTO `project` VALUES (1,'2024-03-07 09:35:15.276345','2024-03-07 09:36:01.000000','星火项目',0),(2,'2024-03-07 09:35:20.004729','2024-03-07 09:35:20.004729','东大项目',0),(3,'2024-03-07 09:35:29.213057','2024-03-07 09:35:29.213057','沙湾煤业项目',0); +/*!40000 ALTER TABLE `project` ENABLE KEYS */; + +-- +-- Table structure for table `project_storage` +-- + +DROP TABLE IF EXISTS `project_storage`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `project_storage` ( + `project_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`project_id`,`file_id`), + KEY `IDX_9058e954f8f09e2cfa2261c1f2` (`project_id`), + KEY `IDX_ac08ac8e4f973873f03dafaca2` (`file_id`), + CONSTRAINT `FK_9058e954f8f09e2cfa2261c1f26` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_ac08ac8e4f973873f03dafaca2b` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `project_storage` +-- + +/*!40000 ALTER TABLE `project_storage` DISABLE KEYS */; +INSERT INTO `project_storage` VALUES (1,128); +/*!40000 ALTER TABLE `project_storage` ENABLE KEYS */; + +-- +-- Table structure for table `sys_captcha_log` +-- + +DROP TABLE IF EXISTS `sys_captcha_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_captcha_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, + `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_captcha_log --- ---------------------------- +-- +-- Dumping data for table `sys_captcha_log` +-- + +/*!40000 ALTER TABLE `sys_captcha_log` DISABLE KEYS */; +/*!40000 ALTER TABLE `sys_captcha_log` ENABLE KEYS */; + +-- +-- Table structure for table `sys_config` +-- --- ---------------------------- --- Table structure for sys_config --- ---------------------------- DROP TABLE IF EXISTS `sys_config`; -CREATE TABLE `sys_config` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_config` ( `id` int NOT NULL AUTO_INCREMENT, `key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_2c363c25cf99bcaab3a7f389ba`(`key`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + UNIQUE KEY `IDX_2c363c25cf99bcaab3a7f389ba` (`key`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_config --- ---------------------------- -INSERT INTO `sys_config` VALUES (1, 'sys_user_initPassword', '初始密码', '123456', '创建管理员账号的初始密码', '2023-11-10 00:31:44.154921', '2023-11-10 00:31:44.161263'); -INSERT INTO `sys_config` VALUES (2, 'sys_api_token', 'API Token', 'huaxin-admin', '用于请求 @ApiToken 的控制器', '2023-11-10 00:31:44.154921', '2024-01-29 09:52:27.000000'); +-- +-- Dumping data for table `sys_config` +-- + +/*!40000 ALTER TABLE `sys_config` DISABLE KEYS */; +INSERT INTO `sys_config` VALUES (1,'sys_user_initPassword','初始密码','123456','创建管理员账号的初始密码','2023-11-10 00:31:44.154921','2023-11-10 00:31:44.161263'),(2,'sys_api_token','API Token','huaxin-admin','用于请求 @ApiToken 的控制器','2023-11-10 00:31:44.154921','2024-01-29 09:52:27.000000'),(3,'companyName','公司名称','华信智能','菜单侧栏公司的名称','2024-03-06 13:06:47.347660','2024-03-06 13:07:18.000000'),(4,'materials_in_out_prefix','出入库记录开头编号','KC','出入库记录开头编号','2024-03-06 14:50:04.844992','2024-03-06 14:50:04.844992'); +/*!40000 ALTER TABLE `sys_config` ENABLE KEYS */; + +-- +-- Table structure for table `sys_dept` +-- --- ---------------------------- --- Table structure for sys_dept --- ---------------------------- DROP TABLE IF EXISTS `sys_dept`; -CREATE TABLE `sys_dept` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_dept` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, - `orderNo` int NULL DEFAULT 0, - `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '', - `parentId` int NULL DEFAULT NULL, + `orderNo` int DEFAULT '0', + `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT '', + `parentId` int DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_c75280b01c49779f2323536db67`(`parentId`) USING BTREE, - CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; + KEY `FK_c75280b01c49779f2323536db67` (`parentId`) USING BTREE, + CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_dept --- ---------------------------- -INSERT INTO `sys_dept` VALUES (1, '华东分部', 1, '1.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` VALUES (2, '研发部', 1, '1.2.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` VALUES (3, '市场部', 2, '1.3.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` VALUES (4, '商务部', 3, '1.4.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` VALUES (5, '财务部', 4, '1.5.', 1, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` VALUES (6, '华南分部', 2, '6.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` VALUES (7, '西北分部', 3, '7.', NULL, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` VALUES (8, '研发部', 1, '6.8.', 6, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); -INSERT INTO `sys_dept` VALUES (9, '市场部', 1, '6.9.', 6, '2023-11-10 00:31:43.996025', '2023-11-10 00:31:44.008709'); +-- +-- Dumping data for table `sys_dept` +-- + +/*!40000 ALTER TABLE `sys_dept` DISABLE KEYS */; +INSERT INTO `sys_dept` VALUES (1,'山东矿机华信智能科技',1,'1.',NULL,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(2,'计算机开发部',1,'1.2.',1,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(3,'行政部',2,'1.3.',1,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(4,'商务部',3,'1.4.',1,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(5,'财务部',4,'1.5.',1,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(6,'山东矿机华能装备制造',2,'6.',NULL,'2023-11-10 00:31:43.996025','2024-03-06 17:17:30.000000'),(8,'研发部',1,'6.8.',6,'2023-11-10 00:31:43.996025','2024-03-06 17:17:30.000000'),(9,'市场部',1,'6.9.',6,'2023-11-10 00:31:43.996025','2024-03-06 17:17:30.000000'); +/*!40000 ALTER TABLE `sys_dept` ENABLE KEYS */; + +-- +-- Table structure for table `sys_dict` +-- --- ---------------------------- --- Table structure for sys_dict --- ---------------------------- DROP TABLE IF EXISTS `sys_dict`; -CREATE TABLE `sys_dict` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_dict` ( `id` int NOT NULL AUTO_INCREMENT, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `create_by` int NOT NULL COMMENT '创建者', `update_by` int NOT NULL COMMENT '更新者', `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint NOT NULL DEFAULT 1, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT '1', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_d112365748f740ee260b65ce91`(`name`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + UNIQUE KEY `IDX_d112365748f740ee260b65ce91` (`name`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_dict --- ---------------------------- +-- +-- Dumping data for table `sys_dict` +-- + +/*!40000 ALTER TABLE `sys_dict` DISABLE KEYS */; +/*!40000 ALTER TABLE `sys_dict` ENABLE KEYS */; + +-- +-- Table structure for table `sys_dict_item` +-- --- ---------------------------- --- Table structure for sys_dict_item --- ---------------------------- DROP TABLE IF EXISTS `sys_dict_item`; -CREATE TABLE `sys_dict_item` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_dict_item` ( `id` int NOT NULL AUTO_INCREMENT, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), @@ -121,592 +469,466 @@ CREATE TABLE `sys_dict_item` ( `update_by` int NOT NULL COMMENT '更新者', `label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `order` int NULL DEFAULT NULL COMMENT '字典项排序', - `status` tinyint NOT NULL DEFAULT 1, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `type_id` int NULL DEFAULT NULL, - `orderNo` int NULL DEFAULT NULL COMMENT '字典项排序', + `order` int DEFAULT NULL COMMENT '字典项排序', + `status` tinyint NOT NULL DEFAULT '1', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `type_id` int DEFAULT NULL, + `orderNo` int DEFAULT NULL COMMENT '字典项排序', + `deleted_at` datetime(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_d68ea74fcb041c8cfd1fd659844`(`type_id`) USING BTREE, - CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + KEY `FK_d68ea74fcb041c8cfd1fd659844` (`type_id`) USING BTREE, + CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_dict_item --- ---------------------------- -INSERT INTO `sys_dict_item` VALUES (1, '2024-01-29 01:24:51.846135', '2024-01-29 02:23:19.000000', 1, 1, '男', '1', 0, 1, '性别男', 1, 3); -INSERT INTO `sys_dict_item` VALUES (2, '2024-01-29 01:32:58.458741', '2024-01-29 01:58:20.000000', 1, 1, '女', '0', 1, 1, '性别女', 1, 2); -INSERT INTO `sys_dict_item` VALUES (3, '2024-01-29 01:59:17.805394', '2024-01-29 14:37:18.000000', 1, 1, '人妖王', '3', NULL, 1, '安布里奥·伊万科夫', 1, 0); -INSERT INTO `sys_dict_item` VALUES (5, '2024-01-29 02:13:01.782466', '2024-01-29 02:13:01.782466', 1, 1, '显示', '1', NULL, 1, '显示菜单', 2, 0); -INSERT INTO `sys_dict_item` VALUES (6, '2024-01-29 02:13:31.134721', '2024-01-29 02:13:31.134721', 1, 1, '隐藏', '0', NULL, 1, '隐藏菜单', 2, 0); +-- +-- Dumping data for table `sys_dict_item` +-- + +/*!40000 ALTER TABLE `sys_dict_item` DISABLE KEYS */; +INSERT INTO `sys_dict_item` VALUES (1,'2024-01-29 01:24:51.846135','2024-01-29 02:23:19.000000',1,1,'男','1',0,1,'性别男',1,3,'2024-03-01 15:28:21.702930'),(2,'2024-01-29 01:32:58.458741','2024-01-29 01:58:20.000000',1,1,'女','0',1,1,'性别女',1,2,'2024-03-01 15:28:21.702930'),(3,'2024-01-29 01:59:17.805394','2024-01-29 14:37:18.000000',1,1,'人妖王','3',NULL,1,'安布里奥·伊万科夫',1,0,'2024-03-01 15:28:21.702930'),(5,'2024-01-29 02:13:01.782466','2024-01-29 02:13:01.782466',1,1,'显示','1',NULL,1,'显示菜单',2,0,'2024-03-01 15:28:21.702930'),(6,'2024-01-29 02:13:31.134721','2024-01-29 02:13:31.134721',1,1,'隐藏','0',NULL,1,'隐藏菜单',2,0,'2024-03-01 15:28:21.702930'),(10,'2024-02-28 16:39:44.977246','2024-02-29 15:56:02.670095',1,1,'商务合同','business',NULL,1,'商务合同',3,0,'2024-03-01 15:28:21.702930'),(11,'2024-02-28 16:42:43.539979','2024-02-29 15:56:07.676659',1,1,'销售合同','sales',NULL,1,'',3,1,'2024-03-01 15:28:21.702930'),(12,'2024-02-28 16:42:58.224299','2024-02-29 15:56:05.815675',1,1,'租赁合同','Lease',NULL,1,NULL,3,2,'2024-03-01 15:28:21.702930'),(13,'2024-02-28 16:43:26.311650','2024-02-29 15:56:10.462447',1,1,'服务合同','service',NULL,1,NULL,3,3,'2024-03-01 15:28:21.702930'),(14,'2024-03-04 13:42:26.688441','2024-03-04 13:42:26.688441',1,1,'件','unit_jian',NULL,1,NULL,5,0,'2024-03-04 13:42:26.688441'),(15,'2024-03-04 13:42:38.298733','2024-03-04 13:42:38.298733',1,1,'个','unit_ge',NULL,1,NULL,5,1,'2024-03-04 13:42:38.298733'),(16,'2024-03-04 13:43:30.965353','2024-03-04 13:43:30.965353',1,1,'千克','unit_qianke',NULL,1,NULL,5,2,'2024-03-04 13:43:30.965353'),(17,'2024-03-04 13:43:44.353125','2024-03-04 13:43:44.353125',1,1,'克','unit_ke',NULL,1,NULL,5,3,'2024-03-04 13:43:44.353125'),(18,'2024-03-04 13:43:56.643339','2024-03-04 13:43:56.643339',1,1,'升','unit_sheng',NULL,1,NULL,5,4,'2024-03-04 13:43:56.643339'),(19,'2024-03-04 13:44:09.242901','2024-03-04 13:44:09.242901',1,1,'毫升','unit_haosheng',NULL,1,NULL,5,5,'2024-03-04 13:44:09.242901'),(20,'2024-03-04 13:44:26.620837','2024-03-04 13:44:29.000000',1,1,'卷','unit_juan',NULL,1,NULL,5,6,'2024-03-04 13:44:29.654314'),(21,'2024-03-04 14:10:38.216659','2024-03-04 14:10:54.000000',1,1,'批','unit_pi',NULL,1,NULL,5,7,'2024-03-04 14:10:54.729114'),(22,'2024-03-04 14:10:48.864655','2024-03-04 14:10:48.864655',1,1,'片','unit_pian',NULL,1,NULL,5,8,'2024-03-04 14:10:48.864655'),(23,'2024-03-04 14:11:06.319281','2024-03-04 14:11:06.319281',1,1,'套','unit_tao',NULL,1,NULL,5,9,'2024-03-04 14:11:06.319281'); +/*!40000 ALTER TABLE `sys_dict_item` ENABLE KEYS */; + +-- +-- Table structure for table `sys_dict_type` +-- --- ---------------------------- --- Table structure for sys_dict_type --- ---------------------------- DROP TABLE IF EXISTS `sys_dict_type`; -CREATE TABLE `sys_dict_type` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_dict_type` ( `id` int NOT NULL AUTO_INCREMENT, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `create_by` int NOT NULL COMMENT '创建者', `update_by` int NOT NULL COMMENT '更新者', `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint NOT NULL DEFAULT 1, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT '1', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `deleted_at` datetime(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_74d0045ff7fab9f67adc0b1bda`(`code`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + UNIQUE KEY `IDX_74d0045ff7fab9f67adc0b1bda` (`code`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_dict_type --- ---------------------------- -INSERT INTO `sys_dict_type` VALUES (1, '2024-01-28 08:19:12.777447', '2024-02-08 13:05:10.000000', 1, 1, '性别', 1, '性别单选', 'sys_user_gender'); -INSERT INTO `sys_dict_type` VALUES (2, '2024-01-28 08:38:41.235185', '2024-01-29 02:11:33.000000', 1, 1, '菜单显示状态', 1, '菜单显示状态', 'sys_show_hide'); +-- +-- Dumping data for table `sys_dict_type` +-- + +/*!40000 ALTER TABLE `sys_dict_type` DISABLE KEYS */; +INSERT INTO `sys_dict_type` VALUES (1,'2024-01-28 08:19:12.777447','2024-02-08 13:05:10.000000',1,1,'性别',1,'性别单选','sys_user_gender','2024-03-01 15:28:21.689753'),(2,'2024-01-28 08:38:41.235185','2024-01-29 02:11:33.000000',1,1,'菜单显示状态',1,'菜单显示状态','sys_show_hide','2024-03-01 15:28:21.689753'),(3,'2024-02-28 16:38:27.311577','2024-03-04 13:26:29.000000',1,1,'合同类型',1,'合同类型','contract_type','2024-03-04 13:26:29.911469'),(5,'2024-03-04 13:41:05.156027','2024-03-04 13:41:05.156027',1,1,'单位',1,'材料盘点表等单位。件。个','unit','2024-03-04 13:41:05.156027'); +/*!40000 ALTER TABLE `sys_dict_type` ENABLE KEYS */; + +-- +-- Table structure for table `sys_login_log` +-- --- ---------------------------- --- Table structure for sys_login_log --- ---------------------------- DROP TABLE IF EXISTS `sys_login_log`; -CREATE TABLE `sys_login_log` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_login_log` ( `id` int NOT NULL AUTO_INCREMENT, - `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `ua` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `provider` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `ua` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `user_id` int NULL DEFAULT NULL, + `user_id` int DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_3029712e0df6a28edaee46fd470`(`user_id`) USING BTREE, - CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + KEY `FK_3029712e0df6a28edaee46fd470` (`user_id`) USING BTREE, + CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_login_log --- ---------------------------- -INSERT INTO `sys_login_log` VALUES (1, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 11:49:38.330842', '2024-02-28 11:49:38.330842', 1); +-- +-- Dumping data for table `sys_login_log` +-- + +/*!40000 ALTER TABLE `sys_login_log` DISABLE KEYS */; +INSERT INTO `sys_login_log` VALUES (1,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 11:49:38.330842','2024-02-28 11:49:38.330842',1),(2,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:06:01.450911','2024-02-28 13:06:01.450911',1),(3,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:08:18.525617','2024-02-28 13:08:18.525617',1),(4,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:10:51.368580','2024-02-28 13:10:51.368580',1),(5,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:53:17.506614','2024-02-28 13:53:17.506614',1),(6,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:53:53.201053','2024-02-28 13:53:53.201053',1),(7,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-29 16:25:14.762388','2024-02-29 16:25:14.762388',1),(8,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36','内网IP',NULL,'2024-02-29 16:26:09.106911','2024-02-29 16:26:09.106911',1),(9,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 10:52:33.093110','2024-03-01 10:52:33.093110',1),(10,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 10:53:09.633370','2024-03-01 10:53:09.633370',1),(11,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:45:36.933407','2024-03-01 15:45:36.933407',1),(12,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:04.780541','2024-03-01 15:57:04.780541',1),(13,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:11.280355','2024-03-01 15:57:11.280355',1),(14,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:18.351492','2024-03-01 15:57:18.351492',1),(15,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:29.111440','2024-03-01 15:57:29.111440',1),(16,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:33.903797','2024-03-01 15:57:33.903797',1),(17,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 16:53:24.824327','2024-03-01 16:53:24.824327',1),(18,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 11:36:22.860536','2024-03-04 11:36:22.860536',1),(19,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 13:14:12.256792','2024-03-04 13:14:12.256792',1),(20,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 13:15:42.383909','2024-03-04 13:15:42.383909',1),(21,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 13:16:04.702698','2024-03-04 13:16:04.702698',1),(22,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 15:42:54.327613','2024-03-04 15:42:54.327613',1),(23,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 17:25:42.137701','2024-03-04 17:25:42.137701',1),(24,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-05 14:34:08.444527','2024-03-05 14:34:08.444527',1),(25,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-06 08:45:04.588314','2024-03-06 08:45:04.588314',1),(26,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-06 17:17:55.139092','2024-03-06 17:17:55.139092',1),(27,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-07 09:14:33.829960','2024-03-07 09:14:33.829960',1),(28,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-07 11:11:38.239629','2024-03-07 11:11:38.239629',1),(29,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-07 11:46:14.622820','2024-03-07 11:46:14.622820',1); +/*!40000 ALTER TABLE `sys_login_log` ENABLE KEYS */; + +-- +-- Table structure for table `sys_menu` +-- --- ---------------------------- --- Table structure for sys_menu --- ---------------------------- DROP TABLE IF EXISTS `sys_menu`; -CREATE TABLE `sys_menu` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_menu` ( `id` int NOT NULL AUTO_INCREMENT, - `parent_id` int NULL DEFAULT NULL, - `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `parent_id` int DEFAULT NULL, + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `type` tinyint NOT NULL DEFAULT 0, - `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '', - `order_no` int NULL DEFAULT 0, - `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `keep_alive` tinyint NOT NULL DEFAULT 1, - `show` tinyint NOT NULL DEFAULT 1, - `status` tinyint NOT NULL DEFAULT 1, + `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `type` tinyint NOT NULL DEFAULT '0', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `order_no` int DEFAULT '0', + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `keep_alive` tinyint NOT NULL DEFAULT '1', + `show` tinyint NOT NULL DEFAULT '1', + `status` tinyint NOT NULL DEFAULT '1', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `is_ext` tinyint NOT NULL DEFAULT 0, - `ext_open_mode` tinyint NOT NULL DEFAULT 1, - `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `is_ext` tinyint NOT NULL DEFAULT '0', + `ext_open_mode` tinyint NOT NULL DEFAULT '1', + `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 128 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_menu --- ---------------------------- -INSERT INTO `sys_menu` VALUES (1, NULL, '/system', '系统管理', '', 0, 'ant-design:setting-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:46.668745', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (2, 1, '/system/user', '用户管理', 'system:user:list', 1, 'ant-design:user-outlined', 0, 'system/user/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:10:30.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (3, 1, '/system/role', '角色管理', 'system:role:list', 1, 'ep:user', 1, 'system/role/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:02.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (4, 1, '/system/menu', '菜单管理', 'system:menu:list', 1, 'ep:menu', 2, 'system/menu/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:18.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:44.567023', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (6, 5, '/system/monitor/online', '在线用户', 'system:online:list', 1, '', 0, 'system/monitor/online/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:13:59.519267', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (7, 5, '/sys/monitor/login-log', '登录日志', 'system:log:login:list', 1, '', 0, 'system/monitor/log/login/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:02.610719', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (8, 5, '/system/monitor/serve', '服务监控', 'system:serve:stat', 1, '', 4, 'system/monitor/serve/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:05.606355', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:52.967983', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (10, 9, '/system/task', '任务管理', '', 1, '', 0, 'system/schedule/task/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:14:39.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (11, 9, '/system/task/log', '任务日志', 'system:task:list', 1, '', 0, 'system/schedule/log/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:15:01.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (12, NULL, '/document', '文档', '', 0, 'ion:tv-outline', 2, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:51.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (14, 12, 'https://www.typeorm.org/', 'Typeorm中文文档(外链)', NULL, 1, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:39:53.000000', 1, 1, NULL); -INSERT INTO `sys_menu` VALUES (15, 12, 'https://docs.nestjs.cn/', 'Nest.js中文文档(内嵌)', '', 1, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:40:43.000000', 1, 2, NULL); -INSERT INTO `sys_menu` VALUES (20, 2, NULL, '新增', 'system:user:create', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (21, 2, '', '删除', 'system:user:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (22, 2, '', '更新', 'system:user:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (23, 2, '', '查询', 'system:user:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (24, 3, '', '新增', 'system:role:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (25, 3, '', '删除', 'system:role:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (26, 3, '', '修改', 'system:role:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (27, 3, '', '查询', 'system:role:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (28, 4, NULL, '新增', 'system:menu:create', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (29, 4, NULL, '删除', 'system:menu:delete', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (30, 4, '', '修改', 'system:menu:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (31, 4, NULL, '查询', 'system:menu:read', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (32, 6, '', '下线', 'system:online:kick', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (34, 10, '', '新增', 'system:task:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (35, 10, '', '删除', 'system:task:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (36, 10, '', '执行一次', 'system:task:once', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (37, 10, '', '查询', 'system:task:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (38, 10, '', '运行', 'system:task:start', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (39, 10, '', '暂停', 'system:task:stop', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (40, 10, '', '更新', 'system:task:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (41, 7, '', '查询登录日志', 'system:log:login:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (42, 7, '', '查询任务日志', 'system:log:task:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (43, NULL, '/about', '关于', '', 1, 'ant-design:info-circle-outlined', 260, 'account/about', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-10 09:35:41.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (48, NULL, '/tool', '系统工具', NULL, 0, 'ant-design:tool-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:28.327223', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (49, 48, '/tool/email', '邮件工具', 'system:tools:email', 1, 'ant-design:send-outlined', 1, 'tool/email/index', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:38.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (50, 49, NULL, '发送邮件', 'tools:email:send', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (51, 48, '/tool/storage', '存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:59:17.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (52, 51, NULL, '文件上传', 'upload:upload', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 01:04:08.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (53, 51, NULL, '文件删除', 'tool:storage:delete', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:56:01.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (54, 2, NULL, '修改密码', 'system:user:password', 2, '', 5, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (56, 1, '/system/dict-type', '字典管理', 'system:dict-type:list', 1, 'ant-design:book-outlined', 4, 'system/dict-type/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:12.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (57, 56, NULL, '新增', 'system:dict-type:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:20.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (58, 56, NULL, '更新', 'system:dict-type:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:26.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (59, 56, NULL, '删除', 'system:dict-type:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:42.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (60, 56, NULL, '查询', 'system:dict-type:info', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (61, 1, '/system/dept', '部门管理', 'system:dept:list', 1, 'ant-design:deployment-unit-outlined', 3, 'system/dept/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:55.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (62, 61, NULL, '新增', 'system:dept:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (63, 61, NULL, '更新', 'system:dept:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (64, 61, NULL, '删除', 'system:dept:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (65, 61, NULL, '查询', 'system:dept:read', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (68, 5, '/health', '健康检查', '', 1, '', 4, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:33.352155', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (69, 68, NULL, '网络', 'app:health:network', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (70, 68, NULL, '数据库', 'app:health: database', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (86, 1, '/param-config', '参数配置', 'system:param-config:list', 1, 'ep:edit', 255, 'system/param-config/index', 0, 1, 1, '2024-01-10 17:34:52.569663', '2024-01-19 02:11:27.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (87, 86, NULL, '查询', 'system:param-config:read', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:20.983241', '2024-01-10 17:39:20.983241', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (88, 86, NULL, '新增', 'system:param-config:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:57.543510', '2024-01-10 17:39:57.543510', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (89, 86, NULL, '更新', 'system:param-config:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:40:27.355944', '2024-01-10 17:40:27.355944', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (92, 86, NULL, '删除', 'system:param-config:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:57:32.059887', '2024-01-10 17:57:32.059887', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (107, 1, 'system/dict-item/:id', '字典项管理', 'system:dict-item:list', 1, 'ant-design:facebook-outlined', 255, 'system/dict-item/index', 0, 0, 1, '2024-01-28 09:21:17.409532', '2024-01-30 13:09:47.000000', 0, 1, '字典管理'); -INSERT INTO `sys_menu` VALUES (108, 107, NULL, '新增', 'system:dict-item:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:22:39.401758', '2024-01-28 22:38:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (109, 107, NULL, '更新', 'system:dict-item:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:26:43.911886', '2024-01-28 09:26:43.911886', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (110, 107, NULL, '删除', 'system:dict-item:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:28.535225', '2024-01-28 09:27:28.535225', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL); -INSERT INTO `sys_menu` VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 0, 1, '2024-02-10 08:00:02.394616', '2024-02-28 11:51:21.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (116, 115, 'manage', '文件管理', 'netdisk:manage:list', 1, '', 252, 'netdisk/manage', 0, 1, 1, '2024-02-10 08:03:49.837348', '2024-02-10 09:34:41.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (120, 116, NULL, '删除', 'netdisk:manage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:09.480601', '2024-02-10 08:42:09.480601', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (121, 116, NULL, '获取文件上传token', 'netdisk:manage:token', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:57.688104', '2024-02-10 08:42:57.688104', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (122, 116, NULL, '添加文件备注', 'netdisk:manage:mark', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:43:40.117321', '2024-02-10 08:43:40.117321', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (123, 116, NULL, '下载文件', 'netdisk:manage:download', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:01.338984', '2024-02-10 08:44:01.338984', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (124, 116, NULL, '重命名文件或文件夹', 'netdisk:manage:rename', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:27.233379', '2024-02-10 08:45:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (125, 116, NULL, '复制文件或文件夹', 'netdisk:manage:copy', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:44.725391', '2024-02-10 08:45:48.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (126, 116, NULL, '剪切文件或文件夹', 'netdisk:manage:cut', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:45:21.660511', '2024-02-10 08:45:21.660511', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (127, 115, 'overview', '网盘概览', 'netdisk:overview:desc', 1, '', 254, 'netdisk/overview', 0, 1, 1, '2024-02-10 09:32:56.981190', '2024-02-10 09:34:18.000000', 0, 1, NULL); +-- +-- Dumping data for table `sys_menu` +-- + +/*!40000 ALTER TABLE `sys_menu` DISABLE KEYS */; +INSERT INTO `sys_menu` VALUES (1,NULL,'/system','系统管理','',0,'ant-design:setting-outlined',254,'',1,1,1,'2023-11-10 00:31:44.023393','2024-02-29 10:41:29.000000',0,1,NULL),(2,1,'/system/user','用户管理','system:user:list',1,'ant-design:user-outlined',0,'system/user/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:10:30.000000',0,1,NULL),(3,1,'/system/role','角色管理','system:role:list',1,'ep:user',1,'system/role/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:11:02.000000',0,1,NULL),(4,1,'/system/menu','菜单管理','system:menu:list',1,'ep:menu',2,'system/menu/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:11:18.000000',0,1,NULL),(5,1,'/system/monitor','系统监控','',0,'ep:monitor',5,'',1,1,1,'2023-11-10 00:31:44.023393','2024-01-27 18:53:44.567023',0,1,NULL),(6,5,'/system/monitor/online','在线用户','system:online:list',1,'',0,'system/monitor/online/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-15 22:13:59.519267',0,1,NULL),(7,5,'/sys/monitor/login-log','登录日志','system:log:login:list',1,'',0,'system/monitor/log/login/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-15 22:14:02.610719',0,1,NULL),(8,5,'/system/monitor/serve','服务监控','system:serve:stat',1,'',4,'system/monitor/serve/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-15 22:14:05.606355',0,1,NULL),(9,1,'/system/schedule','任务调度','',0,'ant-design:schedule-filled',6,'',1,1,1,'2023-11-10 00:31:44.023393','2024-01-27 18:53:52.967983',0,1,NULL),(10,9,'/system/task','任务管理','',1,'',0,'system/schedule/task/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:14:39.000000',0,1,NULL),(11,9,'/system/task/log','任务日志','system:task:list',1,'',0,'system/schedule/log/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:15:01.000000',0,1,NULL),(12,NULL,'/document','文档','',0,'ion:tv-outline',2,'',1,0,1,'2023-11-10 00:31:44.023393','2024-02-28 11:51:51.000000',0,1,NULL),(14,12,'https://www.typeorm.org/','Typeorm中文文档(外链)',NULL,1,'',3,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-30 18:39:53.000000',1,1,NULL),(15,12,'https://docs.nestjs.cn/','Nest.js中文文档(内嵌)','',1,'',4,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-30 18:40:43.000000',1,2,NULL),(20,2,NULL,'新增','system:user:create',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(21,2,'','删除','system:user:delete',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(22,2,'','更新','system:user:update',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(23,2,'','查询','system:user:read',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(24,3,'','新增','system:role:create',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(25,3,'','删除','system:role:delete',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(26,3,'','修改','system:role:update',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(27,3,'','查询','system:role:read',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(28,4,NULL,'新增','system:menu:create',2,NULL,0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(29,4,NULL,'删除','system:menu:delete',2,NULL,0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(30,4,'','修改','system:menu:update',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(31,4,NULL,'查询','system:menu:read',2,NULL,0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(32,6,'','下线','system:online:kick',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(34,10,'','新增','system:task:create',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(35,10,'','删除','system:task:delete',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(36,10,'','执行一次','system:task:once',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(37,10,'','查询','system:task:read',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(38,10,'','运行','system:task:start',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(39,10,'','暂停','system:task:stop',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(40,10,'','更新','system:task:update',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(41,7,'','查询登录日志','system:log:login:list',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(42,7,'','查询任务日志','system:log:task:list',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(43,NULL,'/about','关于','',1,'ant-design:info-circle-outlined',260,'account/about',1,1,1,'2023-11-10 00:31:44.023393','2024-02-10 09:35:41.000000',0,1,NULL),(48,NULL,'/tool','系统工具',NULL,0,'ant-design:tool-outlined',255,'',1,1,1,'2023-11-10 00:31:44.023393','2024-02-29 10:41:25.000000',0,1,NULL),(49,48,'/tool/email','邮件工具','system:tools:email',1,'ant-design:send-outlined',1,'tool/email/index',1,0,1,'2023-11-10 00:31:44.023393','2024-02-28 11:51:38.000000',0,1,NULL),(50,49,NULL,'发送邮件','tools:email:send',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(51,48,'/tool/storage','存储管理','tool:storage:list',1,'ant-design:appstore-outlined',2,'tool/storage/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-25 00:59:17.000000',0,1,NULL),(52,51,NULL,'文件上传','upload:upload',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-25 01:04:08.000000',0,1,NULL),(53,51,NULL,'文件删除','tool:storage:delete',2,'',2,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-25 00:56:01.000000',0,1,NULL),(54,2,NULL,'修改密码','system:user:password',2,'',5,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(56,1,'/system/dict-type','字典管理','system:dict-type:list',1,'ant-design:book-outlined',4,'system/dict-type/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:12.000000',0,1,NULL),(57,56,NULL,'新增','system:dict-type:create',2,'',1,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:20.000000',0,1,NULL),(58,56,NULL,'更新','system:dict-type:update',2,'',2,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:26.000000',0,1,NULL),(59,56,NULL,'删除','system:dict-type:delete',2,'',3,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:42.000000',0,1,NULL),(60,56,NULL,'查询','system:dict-type:info',2,'',4,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:36.000000',0,1,NULL),(61,1,'/system/dept','部门管理','system:dept:list',1,'ant-design:deployment-unit-outlined',3,'system/dept/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:11:55.000000',0,1,NULL),(62,61,NULL,'新增','system:dept:create',2,'',1,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(63,61,NULL,'更新','system:dept:update',2,'',2,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(64,61,NULL,'删除','system:dept:delete',2,'',3,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(65,61,NULL,'查询','system:dept:read',2,'',4,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(68,5,'/health','健康检查','',1,'',4,'',1,0,1,'2023-11-10 00:31:44.023393','2024-01-27 18:53:33.352155',0,1,NULL),(69,68,NULL,'网络','app:health:network',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(70,68,NULL,'数据库','app:health: database',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(86,1,'/param-config','参数配置','system:param-config:list',1,'ep:edit',255,'system/param-config/index',0,1,1,'2024-01-10 17:34:52.569663','2024-01-19 02:11:27.000000',0,1,NULL),(87,86,NULL,'查询','system:param-config:read',2,'',255,NULL,1,1,1,'2024-01-10 17:39:20.983241','2024-01-10 17:39:20.983241',0,1,NULL),(88,86,NULL,'新增','system:param-config:create',2,'',255,NULL,1,1,1,'2024-01-10 17:39:57.543510','2024-01-10 17:39:57.543510',0,1,NULL),(89,86,NULL,'更新','system:param-config:update',2,'',255,NULL,1,1,1,'2024-01-10 17:40:27.355944','2024-01-10 17:40:27.355944',0,1,NULL),(92,86,NULL,'删除','system:param-config:delete',2,'',255,NULL,1,1,1,'2024-01-10 17:57:32.059887','2024-01-10 17:57:32.059887',0,1,NULL),(107,1,'system/dict-item/:id','字典项管理','system:dict-item:list',1,'ant-design:facebook-outlined',255,'system/dict-item/index',0,0,1,'2024-01-28 09:21:17.409532','2024-01-30 13:09:47.000000',0,1,'字典管理'),(108,107,NULL,'新增','system:dict-item:create',2,'',255,NULL,1,1,1,'2024-01-28 09:22:39.401758','2024-01-28 22:38:36.000000',0,1,NULL),(109,107,NULL,'更新','system:dict-item:update',2,'',255,NULL,1,1,1,'2024-01-28 09:26:43.911886','2024-01-28 09:26:43.911886',0,1,NULL),(110,107,NULL,'删除','system:dict-item:delete',2,'',255,NULL,1,1,1,'2024-01-28 09:27:28.535225','2024-01-28 09:27:28.535225',0,1,NULL),(111,107,NULL,'查询','system:dict-item:info',2,'',255,NULL,1,1,1,'2024-01-28 09:27:43.894820','2024-01-28 09:27:43.894820',0,1,NULL),(112,12,'https://antdv.com/components/overview-cn','antdv文档(内嵌)',NULL,1,'',255,NULL,1,1,1,'2024-01-29 09:23:08.407723','2024-01-30 18:41:19.000000',1,2,NULL),(115,NULL,'netdisk','网盘管理',NULL,0,'ant-design:cloud-server-outlined',255,NULL,1,0,1,'2024-02-10 08:00:02.394616','2024-02-28 11:51:21.000000',0,1,NULL),(116,115,'manage','文件管理','netdisk:manage:list',1,'',252,'netdisk/manage',0,1,1,'2024-02-10 08:03:49.837348','2024-02-10 09:34:41.000000',0,1,NULL),(117,116,NULL,'创建文件或文件夹','netdisk:manage:create',2,'',255,NULL,1,1,1,'2024-02-10 08:40:22.317257','2024-02-10 08:40:22.317257',0,1,NULL),(118,116,NULL,'查看文件','netdisk:manage:read',2,'',255,NULL,1,1,1,'2024-02-10 08:41:22.008015','2024-02-10 08:41:22.008015',0,1,NULL),(119,116,NULL,'更新','netdisk:manage:update',2,'',255,NULL,1,1,1,'2024-02-10 08:41:50.691643','2024-02-10 08:41:50.691643',0,1,NULL),(120,116,NULL,'删除','netdisk:manage:delete',2,'',255,NULL,1,1,1,'2024-02-10 08:42:09.480601','2024-02-10 08:42:09.480601',0,1,NULL),(121,116,NULL,'获取文件上传token','netdisk:manage:token',2,'',255,NULL,1,1,1,'2024-02-10 08:42:57.688104','2024-02-10 08:42:57.688104',0,1,NULL),(122,116,NULL,'添加文件备注','netdisk:manage:mark',2,'',255,NULL,1,1,1,'2024-02-10 08:43:40.117321','2024-02-10 08:43:40.117321',0,1,NULL),(123,116,NULL,'下载文件','netdisk:manage:download',2,'',255,NULL,1,1,1,'2024-02-10 08:44:01.338984','2024-02-10 08:44:01.338984',0,1,NULL),(124,116,NULL,'重命名文件或文件夹','netdisk:manage:rename',2,'',255,NULL,1,1,1,'2024-02-10 08:44:27.233379','2024-02-10 08:45:36.000000',0,1,NULL),(125,116,NULL,'复制文件或文件夹','netdisk:manage:copy',2,'',255,NULL,1,1,1,'2024-02-10 08:44:44.725391','2024-02-10 08:45:48.000000',0,1,NULL),(126,116,NULL,'剪切文件或文件夹','netdisk:manage:cut',2,'',255,NULL,1,1,1,'2024-02-10 08:45:21.660511','2024-02-10 08:45:21.660511',0,1,NULL),(127,115,'overview','网盘概览','netdisk:overview:desc',1,'',254,'netdisk/overview',0,1,1,'2024-02-10 09:32:56.981190','2024-02-10 09:34:18.000000',0,1,NULL),(128,NULL,'/contract','合同管理',NULL,0,'ep:document',1,NULL,1,1,1,'2024-02-29 10:40:39.080419','2024-02-29 10:42:37.000000',0,1,NULL),(129,128,'/contract/index','合同审核','app:contract:list',1,'ep:document',1,'contract/index',0,1,1,'2024-02-29 10:46:09.245521','2024-02-29 14:59:56.000000',0,1,NULL),(130,NULL,'/vehicle-usage/index','车辆使用',NULL,1,'ant-design:car-outlined',4,'vehicle-usage/index',0,1,1,'2024-02-29 10:48:35.035363','2024-03-04 14:18:36.000000',0,1,NULL),(131,150,'/materials-inventory/record-in-out','出入库记录','materials_inventory:history_in_out:list',1,'ep:coin',3,'materials-inventory/in-out/index',0,1,1,'2024-02-29 11:03:49.710130','2024-03-05 15:22:06.000000',0,1,NULL),(132,129,NULL,'更新','app:contract:update',2,'',255,NULL,1,1,1,'2024-02-29 15:00:39.641043','2024-02-29 15:00:39.641043',0,1,NULL),(133,129,NULL,'删除','app:contract:delete',2,'',255,NULL,1,1,1,'2024-02-29 15:00:59.376071','2024-02-29 15:00:59.376071',0,1,NULL),(134,129,NULL,'查询','app:contract:read',2,'',255,NULL,1,1,1,'2024-02-29 15:01:14.209396','2024-02-29 15:45:29.000000',0,1,NULL),(135,129,NULL,'新增','app:contract:create',2,'',255,NULL,1,1,1,'2024-02-29 15:44:46.950582','2024-02-29 15:44:46.950582',0,1,NULL),(136,131,NULL,'新增','materials_inventory:history_in_out:create',2,'',255,NULL,1,1,1,'2024-03-01 17:17:02.597782','2024-03-06 10:54:28.000000',0,1,NULL),(137,131,NULL,'更新','materials_inventory:history_in_out:update',2,'',255,NULL,1,1,1,'2024-03-01 17:17:15.192910','2024-03-06 10:54:57.000000',0,1,NULL),(138,131,NULL,'查询单个','app:contract:read',2,'',255,NULL,1,1,1,'2024-03-01 17:17:32.488892','2024-03-01 17:17:32.488892',0,1,NULL),(139,131,NULL,'删除','materials_inventory:history_in_out:delete',2,'',255,NULL,1,1,1,'2024-03-01 17:17:43.455773','2024-03-06 10:55:06.000000',0,1,NULL),(140,150,'/materials-inventory/company','乙方公司管理','app:company:list',1,'ep:office-building',6,'materials-inventory/company/index',0,1,1,'2024-03-04 15:44:30.769048','2024-03-05 15:22:22.000000',0,1,NULL),(141,140,NULL,'单个查询','app:company:read',2,'',1,NULL,1,1,1,'2024-03-04 15:45:55.979802','2024-03-04 15:45:55.979802',0,1,NULL),(142,140,NULL,'新增','app:company:create',2,'',2,NULL,1,1,1,'2024-03-04 15:46:11.260636','2024-03-04 15:46:11.260636',0,1,NULL),(143,140,NULL,'更新','app:company:update',2,'',3,NULL,1,1,1,'2024-03-04 15:46:25.098204','2024-03-04 15:46:25.098204',0,1,NULL),(144,140,NULL,'删除','app:company:delete',2,'',4,NULL,1,1,1,'2024-03-04 15:46:50.812446','2024-03-04 15:46:50.812446',0,1,NULL),(145,150,'/materials-inventory/product','产品目录','app:product:list',1,'ant-design:product-outlined',6,'materials-inventory/product/index',0,1,1,'2024-03-04 16:43:22.749281','2024-03-06 13:40:03.000000',0,1,NULL),(146,145,NULL,'单个查询','app:product:read',2,'',1,NULL,1,1,1,'2024-03-04 16:44:56.482508','2024-03-04 16:44:56.482508',0,1,NULL),(147,145,NULL,'新增','app:product:create',2,'',255,NULL,1,1,1,'2024-03-04 16:45:08.211188','2024-03-04 16:45:08.211188',0,1,NULL),(148,145,NULL,'更新','app:product:update',2,'',255,NULL,1,1,1,'2024-03-04 16:45:25.457903','2024-03-04 16:45:25.457903',0,1,NULL),(149,145,NULL,'删除','app:product:delete',2,'',255,NULL,1,1,1,'2024-03-04 16:45:39.352621','2024-03-04 16:45:39.352621',0,1,NULL),(150,NULL,'/materials-inventory','原材料盘点',NULL,0,'ant-design:dashboard-outlined',3,NULL,1,1,1,'2024-03-04 16:53:32.172674','2024-03-04 16:53:32.172674',0,1,NULL),(151,131,NULL,'导出','materials_inventory:history_in_out:export',2,'',5,NULL,1,1,1,'2024-03-06 13:09:39.201093','2024-03-06 13:09:39.201093',0,1,NULL),(152,150,'/materials-inventory/inventory-check','原材料盘点','app:materials_inventory:list',1,'ant-design:dashboard-outlined',2,'materials-inventory/inventory-check/index',1,0,1,'2024-03-06 13:33:24.795599','2024-03-06 17:14:14.000000',0,1,NULL),(153,150,'/materials-inventory/project','项目管理','app:project:list',1,'ep:memo',4,'materials-inventory/project/index',0,1,1,'2024-03-07 09:28:19.234454','2024-03-07 11:12:00.000000',0,1,NULL),(154,153,NULL,'新增','app:project:create',2,'',1,NULL,1,1,1,'2024-03-07 09:28:47.855064','2024-03-07 09:28:47.855064',0,1,NULL),(155,153,NULL,'更新','app:project:update',2,'',2,NULL,1,1,1,'2024-03-07 09:29:03.183084','2024-03-07 09:29:03.183084',0,1,NULL),(156,153,NULL,'删除','app:project:delete',2,'',3,NULL,1,1,1,'2024-03-07 09:29:16.684943','2024-03-07 09:29:16.684943',0,1,NULL),(157,153,NULL,'单个信息','app:project:read',2,'',4,NULL,1,1,1,'2024-03-07 09:29:33.424578','2024-03-07 09:29:33.424578',0,1,NULL),(158,131,NULL,'导出原材料盘点表','app:materials_inventory:export',2,'',255,NULL,1,1,1,'2024-03-07 11:46:54.468400','2024-03-07 11:46:54.468400',0,1,NULL); +/*!40000 ALTER TABLE `sys_menu` ENABLE KEYS */; + +-- +-- Table structure for table `sys_role` +-- --- ---------------------------- --- Table structure for sys_role --- ---------------------------- DROP TABLE IF EXISTS `sys_role`; -CREATE TABLE `sys_role` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_role` ( `id` int NOT NULL AUTO_INCREMENT, `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `status` tinyint NULL DEFAULT 1, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `status` tinyint DEFAULT '1', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `default` tinyint NULL DEFAULT NULL, + `default` tinyint DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_223de54d6badbe43a5490450c3`(`name`) USING BTREE, - UNIQUE INDEX `IDX_05edc0a51f41bb16b7d8137da9`(`value`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + UNIQUE KEY `IDX_223de54d6badbe43a5490450c3` (`name`) USING BTREE, + UNIQUE KEY `IDX_05edc0a51f41bb16b7d8137da9` (`value`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_role --- ---------------------------- -INSERT INTO `sys_role` VALUES (1, 'admin', '管理员', '超级管理员', 1, '2023-11-10 00:31:44.058463', '2024-01-28 21:08:39.000000', NULL); -INSERT INTO `sys_role` VALUES (2, 'user', '用户', '', 1, '2023-11-10 00:31:44.058463', '2024-01-30 18:44:45.000000', 1); -INSERT INTO `sys_role` VALUES (9, 'test', '测试', NULL, 1, '2024-01-23 22:46:52.408827', '2024-01-30 01:04:52.000000', NULL); +-- +-- Dumping data for table `sys_role` +-- + +/*!40000 ALTER TABLE `sys_role` DISABLE KEYS */; +INSERT INTO `sys_role` VALUES (1,'admin','管理员','超级管理员',1,'2023-11-10 00:31:44.058463','2024-01-28 21:08:39.000000',NULL),(2,'user','用户','',1,'2023-11-10 00:31:44.058463','2024-01-30 18:44:45.000000',1),(9,'test','测试',NULL,1,'2024-01-23 22:46:52.408827','2024-01-30 01:04:52.000000',NULL); +/*!40000 ALTER TABLE `sys_role` ENABLE KEYS */; + +-- +-- Table structure for table `sys_role_menus` +-- --- ---------------------------- --- Table structure for sys_role_menus --- ---------------------------- DROP TABLE IF EXISTS `sys_role_menus`; -CREATE TABLE `sys_role_menus` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_role_menus` ( `role_id` int NOT NULL, `menu_id` int NOT NULL, - PRIMARY KEY (`role_id`, `menu_id`) USING BTREE, - INDEX `IDX_35ce749b04d57e226d059e0f63`(`role_id`) USING BTREE, - INDEX `IDX_2b95fdc95b329d66c18f5baed6`(`menu_id`) USING BTREE, - CONSTRAINT `FK_2b95fdc95b329d66c18f5baed6d` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + PRIMARY KEY (`role_id`,`menu_id`) USING BTREE, + KEY `IDX_35ce749b04d57e226d059e0f63` (`role_id`) USING BTREE, + KEY `IDX_2b95fdc95b329d66c18f5baed6` (`menu_id`) USING BTREE, + CONSTRAINT `FK_2b95fdc95b329d66c18f5baed6d` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE, CONSTRAINT `FK_35ce749b04d57e226d059e0f633` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_role_menus --- ---------------------------- -INSERT INTO `sys_role_menus` VALUES (1, 1); -INSERT INTO `sys_role_menus` VALUES (1, 2); -INSERT INTO `sys_role_menus` VALUES (1, 3); -INSERT INTO `sys_role_menus` VALUES (1, 4); -INSERT INTO `sys_role_menus` VALUES (1, 5); -INSERT INTO `sys_role_menus` VALUES (1, 6); -INSERT INTO `sys_role_menus` VALUES (1, 7); -INSERT INTO `sys_role_menus` VALUES (1, 8); -INSERT INTO `sys_role_menus` VALUES (1, 9); -INSERT INTO `sys_role_menus` VALUES (1, 10); -INSERT INTO `sys_role_menus` VALUES (1, 11); -INSERT INTO `sys_role_menus` VALUES (1, 12); -INSERT INTO `sys_role_menus` VALUES (1, 14); -INSERT INTO `sys_role_menus` VALUES (1, 15); -INSERT INTO `sys_role_menus` VALUES (1, 20); -INSERT INTO `sys_role_menus` VALUES (1, 21); -INSERT INTO `sys_role_menus` VALUES (1, 22); -INSERT INTO `sys_role_menus` VALUES (1, 23); -INSERT INTO `sys_role_menus` VALUES (1, 24); -INSERT INTO `sys_role_menus` VALUES (1, 25); -INSERT INTO `sys_role_menus` VALUES (1, 26); -INSERT INTO `sys_role_menus` VALUES (1, 27); -INSERT INTO `sys_role_menus` VALUES (1, 28); -INSERT INTO `sys_role_menus` VALUES (1, 29); -INSERT INTO `sys_role_menus` VALUES (1, 30); -INSERT INTO `sys_role_menus` VALUES (1, 31); -INSERT INTO `sys_role_menus` VALUES (1, 32); -INSERT INTO `sys_role_menus` VALUES (1, 34); -INSERT INTO `sys_role_menus` VALUES (1, 35); -INSERT INTO `sys_role_menus` VALUES (1, 36); -INSERT INTO `sys_role_menus` VALUES (1, 37); -INSERT INTO `sys_role_menus` VALUES (1, 38); -INSERT INTO `sys_role_menus` VALUES (1, 39); -INSERT INTO `sys_role_menus` VALUES (1, 40); -INSERT INTO `sys_role_menus` VALUES (1, 41); -INSERT INTO `sys_role_menus` VALUES (1, 42); -INSERT INTO `sys_role_menus` VALUES (1, 43); -INSERT INTO `sys_role_menus` VALUES (1, 48); -INSERT INTO `sys_role_menus` VALUES (1, 49); -INSERT INTO `sys_role_menus` VALUES (1, 50); -INSERT INTO `sys_role_menus` VALUES (1, 51); -INSERT INTO `sys_role_menus` VALUES (1, 52); -INSERT INTO `sys_role_menus` VALUES (1, 53); -INSERT INTO `sys_role_menus` VALUES (1, 54); -INSERT INTO `sys_role_menus` VALUES (1, 56); -INSERT INTO `sys_role_menus` VALUES (1, 57); -INSERT INTO `sys_role_menus` VALUES (1, 58); -INSERT INTO `sys_role_menus` VALUES (1, 59); -INSERT INTO `sys_role_menus` VALUES (1, 60); -INSERT INTO `sys_role_menus` VALUES (1, 61); -INSERT INTO `sys_role_menus` VALUES (1, 62); -INSERT INTO `sys_role_menus` VALUES (1, 63); -INSERT INTO `sys_role_menus` VALUES (1, 64); -INSERT INTO `sys_role_menus` VALUES (1, 65); -INSERT INTO `sys_role_menus` VALUES (1, 68); -INSERT INTO `sys_role_menus` VALUES (1, 69); -INSERT INTO `sys_role_menus` VALUES (1, 70); -INSERT INTO `sys_role_menus` VALUES (1, 86); -INSERT INTO `sys_role_menus` VALUES (1, 87); -INSERT INTO `sys_role_menus` VALUES (1, 88); -INSERT INTO `sys_role_menus` VALUES (1, 89); -INSERT INTO `sys_role_menus` VALUES (1, 92); -INSERT INTO `sys_role_menus` VALUES (1, 107); -INSERT INTO `sys_role_menus` VALUES (1, 108); -INSERT INTO `sys_role_menus` VALUES (1, 109); -INSERT INTO `sys_role_menus` VALUES (1, 110); -INSERT INTO `sys_role_menus` VALUES (1, 111); -INSERT INTO `sys_role_menus` VALUES (2, 1); -INSERT INTO `sys_role_menus` VALUES (2, 5); -INSERT INTO `sys_role_menus` VALUES (2, 6); -INSERT INTO `sys_role_menus` VALUES (2, 7); -INSERT INTO `sys_role_menus` VALUES (2, 8); -INSERT INTO `sys_role_menus` VALUES (2, 9); -INSERT INTO `sys_role_menus` VALUES (2, 10); -INSERT INTO `sys_role_menus` VALUES (2, 11); -INSERT INTO `sys_role_menus` VALUES (2, 12); -INSERT INTO `sys_role_menus` VALUES (2, 14); -INSERT INTO `sys_role_menus` VALUES (2, 15); -INSERT INTO `sys_role_menus` VALUES (2, 32); -INSERT INTO `sys_role_menus` VALUES (2, 34); -INSERT INTO `sys_role_menus` VALUES (2, 35); -INSERT INTO `sys_role_menus` VALUES (2, 36); -INSERT INTO `sys_role_menus` VALUES (2, 37); -INSERT INTO `sys_role_menus` VALUES (2, 38); -INSERT INTO `sys_role_menus` VALUES (2, 39); -INSERT INTO `sys_role_menus` VALUES (2, 40); -INSERT INTO `sys_role_menus` VALUES (2, 41); -INSERT INTO `sys_role_menus` VALUES (2, 42); -INSERT INTO `sys_role_menus` VALUES (2, 43); -INSERT INTO `sys_role_menus` VALUES (2, 48); -INSERT INTO `sys_role_menus` VALUES (2, 49); -INSERT INTO `sys_role_menus` VALUES (2, 50); -INSERT INTO `sys_role_menus` VALUES (2, 51); -INSERT INTO `sys_role_menus` VALUES (2, 52); -INSERT INTO `sys_role_menus` VALUES (2, 53); -INSERT INTO `sys_role_menus` VALUES (2, 56); -INSERT INTO `sys_role_menus` VALUES (2, 57); -INSERT INTO `sys_role_menus` VALUES (2, 58); -INSERT INTO `sys_role_menus` VALUES (2, 59); -INSERT INTO `sys_role_menus` VALUES (2, 60); -INSERT INTO `sys_role_menus` VALUES (2, 68); -INSERT INTO `sys_role_menus` VALUES (2, 69); -INSERT INTO `sys_role_menus` VALUES (2, 70); -INSERT INTO `sys_role_menus` VALUES (2, 86); -INSERT INTO `sys_role_menus` VALUES (2, 87); -INSERT INTO `sys_role_menus` VALUES (2, 88); -INSERT INTO `sys_role_menus` VALUES (2, 89); -INSERT INTO `sys_role_menus` VALUES (2, 92); -INSERT INTO `sys_role_menus` VALUES (2, 107); -INSERT INTO `sys_role_menus` VALUES (2, 108); -INSERT INTO `sys_role_menus` VALUES (2, 109); -INSERT INTO `sys_role_menus` VALUES (2, 110); -INSERT INTO `sys_role_menus` VALUES (2, 111); -INSERT INTO `sys_role_menus` VALUES (2, 112); -INSERT INTO `sys_role_menus` VALUES (9, 1); -INSERT INTO `sys_role_menus` VALUES (9, 2); -INSERT INTO `sys_role_menus` VALUES (9, 3); -INSERT INTO `sys_role_menus` VALUES (9, 4); -INSERT INTO `sys_role_menus` VALUES (9, 5); -INSERT INTO `sys_role_menus` VALUES (9, 6); -INSERT INTO `sys_role_menus` VALUES (9, 7); -INSERT INTO `sys_role_menus` VALUES (9, 8); -INSERT INTO `sys_role_menus` VALUES (9, 9); -INSERT INTO `sys_role_menus` VALUES (9, 10); -INSERT INTO `sys_role_menus` VALUES (9, 11); -INSERT INTO `sys_role_menus` VALUES (9, 20); -INSERT INTO `sys_role_menus` VALUES (9, 21); -INSERT INTO `sys_role_menus` VALUES (9, 22); -INSERT INTO `sys_role_menus` VALUES (9, 23); -INSERT INTO `sys_role_menus` VALUES (9, 24); -INSERT INTO `sys_role_menus` VALUES (9, 25); -INSERT INTO `sys_role_menus` VALUES (9, 26); -INSERT INTO `sys_role_menus` VALUES (9, 27); -INSERT INTO `sys_role_menus` VALUES (9, 28); -INSERT INTO `sys_role_menus` VALUES (9, 29); -INSERT INTO `sys_role_menus` VALUES (9, 30); -INSERT INTO `sys_role_menus` VALUES (9, 31); -INSERT INTO `sys_role_menus` VALUES (9, 32); -INSERT INTO `sys_role_menus` VALUES (9, 34); -INSERT INTO `sys_role_menus` VALUES (9, 35); -INSERT INTO `sys_role_menus` VALUES (9, 36); -INSERT INTO `sys_role_menus` VALUES (9, 37); -INSERT INTO `sys_role_menus` VALUES (9, 38); -INSERT INTO `sys_role_menus` VALUES (9, 39); -INSERT INTO `sys_role_menus` VALUES (9, 40); -INSERT INTO `sys_role_menus` VALUES (9, 41); -INSERT INTO `sys_role_menus` VALUES (9, 42); -INSERT INTO `sys_role_menus` VALUES (9, 54); -INSERT INTO `sys_role_menus` VALUES (9, 56); -INSERT INTO `sys_role_menus` VALUES (9, 57); -INSERT INTO `sys_role_menus` VALUES (9, 58); -INSERT INTO `sys_role_menus` VALUES (9, 59); -INSERT INTO `sys_role_menus` VALUES (9, 60); -INSERT INTO `sys_role_menus` VALUES (9, 61); -INSERT INTO `sys_role_menus` VALUES (9, 62); -INSERT INTO `sys_role_menus` VALUES (9, 63); -INSERT INTO `sys_role_menus` VALUES (9, 64); -INSERT INTO `sys_role_menus` VALUES (9, 65); -INSERT INTO `sys_role_menus` VALUES (9, 68); -INSERT INTO `sys_role_menus` VALUES (9, 69); -INSERT INTO `sys_role_menus` VALUES (9, 70); -INSERT INTO `sys_role_menus` VALUES (9, 86); -INSERT INTO `sys_role_menus` VALUES (9, 87); -INSERT INTO `sys_role_menus` VALUES (9, 88); -INSERT INTO `sys_role_menus` VALUES (9, 89); -INSERT INTO `sys_role_menus` VALUES (9, 92); +-- +-- Dumping data for table `sys_role_menus` +-- + +/*!40000 ALTER TABLE `sys_role_menus` DISABLE KEYS */; +INSERT INTO `sys_role_menus` VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9),(1,10),(1,11),(1,12),(1,14),(1,15),(1,20),(1,21),(1,22),(1,23),(1,24),(1,25),(1,26),(1,27),(1,28),(1,29),(1,30),(1,31),(1,32),(1,34),(1,35),(1,36),(1,37),(1,38),(1,39),(1,40),(1,41),(1,42),(1,43),(1,48),(1,49),(1,50),(1,51),(1,52),(1,53),(1,54),(1,56),(1,57),(1,58),(1,59),(1,60),(1,61),(1,62),(1,63),(1,64),(1,65),(1,68),(1,69),(1,70),(1,86),(1,87),(1,88),(1,89),(1,92),(1,107),(1,108),(1,109),(1,110),(1,111),(2,1),(2,5),(2,6),(2,7),(2,8),(2,9),(2,10),(2,11),(2,12),(2,14),(2,15),(2,32),(2,34),(2,35),(2,36),(2,37),(2,38),(2,39),(2,40),(2,41),(2,42),(2,43),(2,48),(2,49),(2,50),(2,51),(2,52),(2,53),(2,56),(2,57),(2,58),(2,59),(2,60),(2,68),(2,69),(2,70),(2,86),(2,87),(2,88),(2,89),(2,92),(2,107),(2,108),(2,109),(2,110),(2,111),(2,112),(9,1),(9,2),(9,3),(9,4),(9,5),(9,6),(9,7),(9,8),(9,9),(9,10),(9,11),(9,20),(9,21),(9,22),(9,23),(9,24),(9,25),(9,26),(9,27),(9,28),(9,29),(9,30),(9,31),(9,32),(9,34),(9,35),(9,36),(9,37),(9,38),(9,39),(9,40),(9,41),(9,42),(9,54),(9,56),(9,57),(9,58),(9,59),(9,60),(9,61),(9,62),(9,63),(9,64),(9,65),(9,68),(9,69),(9,70),(9,86),(9,87),(9,88),(9,89),(9,92); +/*!40000 ALTER TABLE `sys_role_menus` ENABLE KEYS */; + +-- +-- Table structure for table `sys_task` +-- --- ---------------------------- --- Table structure for sys_task --- ---------------------------- DROP TABLE IF EXISTS `sys_task`; -CREATE TABLE `sys_task` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_task` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `service` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `type` tinyint NOT NULL DEFAULT 0, - `status` tinyint NOT NULL DEFAULT 1, - `start_time` datetime NULL DEFAULT NULL, - `end_time` datetime NULL DEFAULT NULL, - `limit` int NULL DEFAULT 0, - `cron` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `every` int NULL DEFAULT NULL, - `data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, - `job_opts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `type` tinyint NOT NULL DEFAULT '0', + `status` tinyint NOT NULL DEFAULT '1', + `start_time` datetime DEFAULT NULL, + `end_time` datetime DEFAULT NULL, + `limit` int DEFAULT '0', + `cron` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `every` int DEFAULT NULL, + `data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `job_opts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_ef8e5ab5ef2fe0ddb1428439ef`(`name`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + UNIQUE KEY `IDX_ef8e5ab5ef2fe0ddb1428439ef` (`name`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_task --- ---------------------------- -INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-02-28 11:44:00.000000'); -INSERT INTO `sys_task` VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-02-28 11:44:00.000000'); -INSERT INTO `sys_task` VALUES (4, '访问百度首页', 'HttpRequestJob.handle', 0, 0, NULL, NULL, 1, '* * * * * ?', NULL, '{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}', NULL, '访问百度首页', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); -INSERT INTO `sys_task` VALUES (5, '发送邮箱', 'EmailJob.send', 0, 0, NULL, NULL, -1, '0 0 0 1 * ?', NULL, '{\"subject\":\"这是标题\",\"to\":\"zeyu57@163.com\",\"content\":\"这是正文\"}', NULL, '每月发送邮箱', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); +-- +-- Dumping data for table `sys_task` +-- + +/*!40000 ALTER TABLE `sys_task` DISABLE KEYS */; +INSERT INTO `sys_task` VALUES (2,'定时清空登录日志','LogClearJob.clearLoginLog',0,1,NULL,NULL,0,'0 0 3 ? * 1',0,'','{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}','定时清空登录日志','2023-11-10 00:31:44.197779','2024-03-07 11:46:09.000000'),(3,'定时清空任务日志','LogClearJob.clearTaskLog',0,1,NULL,NULL,0,'0 0 3 ? * 1',0,'','{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}','定时清空任务日志','2023-11-10 00:31:44.197779','2024-03-07 11:46:09.000000'),(4,'访问百度首页','HttpRequestJob.handle',0,0,NULL,NULL,1,'* * * * * ?',NULL,'{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}',NULL,'访问百度首页','2023-11-10 00:31:44.197779','2023-11-10 00:31:44.206935'),(5,'发送邮箱','EmailJob.send',0,0,NULL,NULL,-1,'0 0 0 1 * ?',NULL,'{\"subject\":\"这是标题\",\"to\":\"18661983080@163.com\",\"content\":\"这是正文\"}',NULL,'每月发送邮箱','2023-11-10 00:31:44.197779','2024-03-07 11:14:53.000000'); +/*!40000 ALTER TABLE `sys_task` ENABLE KEYS */; + +-- +-- Table structure for table `sys_task_log` +-- --- ---------------------------- --- Table structure for sys_task_log --- ---------------------------- DROP TABLE IF EXISTS `sys_task_log`; -CREATE TABLE `sys_task_log` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_task_log` ( `id` int NOT NULL AUTO_INCREMENT, - `task_id` int NULL DEFAULT NULL, - `status` tinyint NOT NULL DEFAULT 0, - `detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, - `consume_time` int NULL DEFAULT 0, + `task_id` int DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT '0', + `detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `consume_time` int DEFAULT '0', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_f4d9c36052fdb188ff5c089454b`(`task_id`) USING BTREE, - CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + KEY `FK_f4d9c36052fdb188ff5c089454b` (`task_id`) USING BTREE, + CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_task_log --- ---------------------------- -INSERT INTO `sys_task_log` VALUES (1, 3, 1, NULL, 0, '2024-02-05 03:06:22.037448', '2024-02-05 03:06:22.037448'); -INSERT INTO `sys_task_log` VALUES (2, 2, 1, NULL, 0, '2024-02-10 09:42:21.738712', '2024-02-10 09:42:21.738712'); +-- +-- Dumping data for table `sys_task_log` +-- + +/*!40000 ALTER TABLE `sys_task_log` DISABLE KEYS */; +INSERT INTO `sys_task_log` VALUES (1,3,1,NULL,0,'2024-02-05 03:06:22.037448','2024-02-05 03:06:22.037448'),(2,2,1,NULL,0,'2024-02-10 09:42:21.738712','2024-02-10 09:42:21.738712'); +/*!40000 ALTER TABLE `sys_task_log` ENABLE KEYS */; + +-- +-- Table structure for table `sys_user` +-- --- ---------------------------- --- Table structure for sys_user --- ---------------------------- DROP TABLE IF EXISTS `sys_user`; -CREATE TABLE `sys_user` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_user` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `psalt` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint NULL DEFAULT 1, - `qq` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `status` tinyint DEFAULT '1', + `qq` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `dept_id` int NULL DEFAULT NULL, + `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `dept_id` int DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_9e7164b2f1ea1348bc0eb0a7da`(`username`) USING BTREE, - INDEX `FK_96bde34263e2ae3b46f011124ac`(`dept_id`) USING BTREE, - CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 27 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + UNIQUE KEY `IDX_9e7164b2f1ea1348bc0eb0a7da` (`username`) USING BTREE, + KEY `FK_96bde34263e2ae3b46f011124ac` (`dept_id`) USING BTREE, + CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_user --- ---------------------------- -INSERT INTO `sys_user` VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-01-29 09:49:43.000000', 'bqy', 1); -INSERT INTO `sys_user` VALUES (2, 'user', 'dbd89546dec743f82bb9073d6ac39361', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', 'luffy@qq.com', '10010', '王路飞', 'qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-01-29 09:49:57.000000', 'luffy', 8); -INSERT INTO `sys_user` VALUES (8, 'developer', 'f03fa2a99595127b9a39587421d471f6', '/upload/报名照片-202402281149824.jpg', 'nami@qq.com', '10000', '小贼猫', 'NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-02-28 11:49:54.000000', '娜美', 7); +-- +-- Dumping data for table `sys_user` +-- + +/*!40000 ALTER TABLE `sys_user` DISABLE KEYS */; +INSERT INTO `sys_user` VALUES (1,'admin','a11571e778ee85e82caae2d980952546','https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777','1743369777@qq.com','10086','管理员','xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d',1,'1743369777','2023-11-10 00:31:44.104382','2024-03-06 17:18:04.000000','bqy',2),(2,'user','dbd89546dec743f82bb9073d6ac39361','https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777','luffy@qq.com','10010','王路飞','qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe',1,'1743369777','2023-11-10 00:31:44.104382','2024-03-01 11:31:11.000000','luffy',8),(8,'developer','f03fa2a99595127b9a39587421d471f6','/upload/报名照片-202402281149824.jpg','nami@qq.com','10000','小贼猫','NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE',1,'1743369777','2023-11-10 00:31:44.104382','2024-03-06 17:17:21.000000','娜美',2); +/*!40000 ALTER TABLE `sys_user` ENABLE KEYS */; + +-- +-- Table structure for table `sys_user_roles` +-- --- ---------------------------- --- Table structure for sys_user_roles --- ---------------------------- DROP TABLE IF EXISTS `sys_user_roles`; -CREATE TABLE `sys_user_roles` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_user_roles` ( `user_id` int NOT NULL, `role_id` int NOT NULL, - PRIMARY KEY (`user_id`, `role_id`) USING BTREE, - INDEX `IDX_96311d970191a044ec048011f4`(`user_id`) USING BTREE, - INDEX `IDX_6d61c5b3f76a3419d93a421669`(`role_id`) USING BTREE, - CONSTRAINT `FK_6d61c5b3f76a3419d93a4216695` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + PRIMARY KEY (`user_id`,`role_id`) USING BTREE, + KEY `IDX_96311d970191a044ec048011f4` (`user_id`) USING BTREE, + KEY `IDX_6d61c5b3f76a3419d93a421669` (`role_id`) USING BTREE, + CONSTRAINT `FK_6d61c5b3f76a3419d93a4216695` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`), CONSTRAINT `FK_96311d970191a044ec048011f44` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of sys_user_roles --- ---------------------------- -INSERT INTO `sys_user_roles` VALUES (1, 1); -INSERT INTO `sys_user_roles` VALUES (2, 2); -INSERT INTO `sys_user_roles` VALUES (8, 2); +-- +-- Dumping data for table `sys_user_roles` +-- + +/*!40000 ALTER TABLE `sys_user_roles` DISABLE KEYS */; +INSERT INTO `sys_user_roles` VALUES (1,1),(2,2),(8,2); +/*!40000 ALTER TABLE `sys_user_roles` ENABLE KEYS */; + +-- +-- Table structure for table `todo` +-- --- ---------------------------- --- Table structure for todo --- ---------------------------- DROP TABLE IF EXISTS `todo`; -CREATE TABLE `todo` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `todo` ( `id` int NOT NULL AUTO_INCREMENT, `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, - `user_id` int NULL DEFAULT NULL, - `status` tinyint NOT NULL DEFAULT 0, + `user_id` int DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT '0', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_9cb7989853c4cb7fe427db4b260`(`user_id`) USING BTREE, - CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + KEY `FK_9cb7989853c4cb7fe427db4b260` (`user_id`) USING BTREE, + CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of todo --- ---------------------------- -INSERT INTO `todo` VALUES (1, 'nest.js', NULL, 0, '2023-11-10 00:31:44.139730', '2023-11-10 00:31:44.147629'); +-- +-- Dumping data for table `todo` +-- + +/*!40000 ALTER TABLE `todo` DISABLE KEYS */; +INSERT INTO `todo` VALUES (1,'nest.js',NULL,0,'2023-11-10 00:31:44.139730','2023-11-10 00:31:44.147629'); +/*!40000 ALTER TABLE `todo` ENABLE KEYS */; + +-- +-- Table structure for table `tool_storage` +-- --- ---------------------------- --- Table structure for tool_storage --- ---------------------------- DROP TABLE IF EXISTS `tool_storage`; -CREATE TABLE `tool_storage` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `tool_storage` ( `id` int NOT NULL AUTO_INCREMENT, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名', - `fileName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '真实文件名', - `ext_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `fileName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '真实文件名', + `ext_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, - `size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, - `user_id` int NULL DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `user_id` int DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 80 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; +) ENGINE=InnoDB AUTO_INCREMENT=129 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of tool_storage --- ---------------------------- -INSERT INTO `tool_storage` VALUES (78, '2024-02-03 21:41:16.851178', '2024-02-03 21:41:16.851178', 'cfd0d14459bc1a47-202402032141838.jpeg', 'cfd0d14459bc1a47.jpeg', 'jpeg', '/upload/cfd0d14459bc1a47-202402032141838.jpeg', '图片', '33.92 KB', 1); -INSERT INTO `tool_storage` VALUES (79, '2024-02-28 11:49:53.834125', '2024-02-28 11:49:53.834125', '报名照片-202402281149824.jpg', '报名照片.jpg', 'jpg', '/upload/报名照片-202402281149824.jpg', '图片', '8.7 KB', 1); +-- +-- Dumping data for table `tool_storage` +-- + +/*!40000 ALTER TABLE `tool_storage` DISABLE KEYS */; +INSERT INTO `tool_storage` VALUES (121,'2024-03-01 16:46:58.441084','2024-03-01 16:46:58.441084','1709172270328-202403011646430.jpg','1709172270328.jpg','jpg','/upload/1709172270328-202403011646430.jpg','图片','62.79 KB',1),(122,'2024-03-01 17:21:35.454311','2024-03-01 17:21:35.454311','盘点表-202403011721448.xlsx','盘点表.xlsx','xlsx','/upload/盘点表-202403011721448.xlsx','文档','10.83 KB',1),(123,'2024-03-04 15:51:50.664699','2024-03-04 15:51:50.664699','20240304-202403041551657.sql','20240304.sql','sql','/upload/20240304-202403041551657.sql','其他','62.86 KB',1),(124,'2024-03-05 10:39:45.040659','2024-03-05 10:39:45.040659','盘点表-202403051039028.xlsx','盘点表.xlsx','xlsx','/upload/盘点表-202403051039028.xlsx','文档','10.83 KB',1),(126,'2024-03-06 13:04:17.636919','2024-03-06 13:04:17.636919','盘点表-202403061304624.xlsx','盘点表.xlsx','xlsx','/upload/盘点表-202403061304624.xlsx','文档','10.83 KB',1),(127,'2024-03-07 09:35:51.916017','2024-03-07 09:35:51.916017','盘点表-202403070935910.xlsx','盘点表.xlsx','xlsx','/upload/盘点表-202403070935910.xlsx','文档','10.83 KB',1),(128,'2024-03-07 09:36:01.570104','2024-03-07 09:36:01.570104','1709172270328-202403070936565.jpg','1709172270328.jpg','jpg','/upload/1709172270328-202403070936565.jpg','图片','62.86 KB',1); +/*!40000 ALTER TABLE `tool_storage` ENABLE KEYS */; + +-- +-- Table structure for table `user_access_tokens` +-- --- ---------------------------- --- Table structure for user_access_tokens --- ---------------------------- DROP TABLE IF EXISTS `user_access_tokens`; -CREATE TABLE `user_access_tokens` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_access_tokens` ( `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, `expired_at` datetime NOT NULL COMMENT '令牌过期时间', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', - `user_id` int NULL DEFAULT NULL, + `user_id` int DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_e9d9d0c303432e4e5e48c1c3e90`(`user_id`) USING BTREE, - CONSTRAINT `FK_e9d9d0c303432e4e5e48c1c3e90` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + KEY `FK_e9d9d0c303432e4e5e48c1c3e90` (`user_id`) USING BTREE, + CONSTRAINT `FK_e9d9d0c303432e4e5e48c1c3e90` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of user_access_tokens --- ---------------------------- -INSERT INTO `user_access_tokens` VALUES ('09cf7b0a-62e0-45ee-96b0-e31de32361e0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1MDkxNTd9.0gtKlcxrxQ-TarEai2lsBnfMc852ZDYHeSjjhpo5Fn8', '2024-02-11 04:05:58', '2024-02-10 04:05:57.696509', 1); -INSERT INTO `user_access_tokens` VALUES ('31399cec-e506-4194-a9cc-47f7a1a953a9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTIxNzh9.Da3j0Ht_zAsPldEYJ18RFYZqTYNhgPxSefCEuXd-4T8', '2024-02-29 11:49:38', '2024-02-28 11:49:38.289991', 1); -INSERT INTO `user_access_tokens` VALUES ('3f7dffae-db1f-47dc-9677-5c956c3de39e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczMTEzMDJ9.D5Qpht1RquKor8WtgfGAcCp8LwG7z3FZhIwbyQzhDmE', '2024-02-08 21:08:22', '2024-02-07 21:08:22.130066', 1); -INSERT INTO `user_access_tokens` VALUES ('40342c3e-194c-42eb-adee-189389839195', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxNjF9.tRQOxhB-01Pcut5MXm4L5D1OrbMJfS4LfUys0XB4kWs', '2024-02-09 14:02:41', '2024-02-08 14:02:41.081164', 1); -INSERT INTO `user_access_tokens` VALUES ('9d1ba8e9-dffc-4b15-b21f-4a90f196e39c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1Mjc5MDV9.7LeiS3LBBdiAc7YrULWpmnI1oNSvR79K-qjEOlBYOnI', '2024-02-11 09:18:26', '2024-02-10 09:18:25.656695', 1); -INSERT INTO `user_access_tokens` VALUES ('edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxMjd9.VRuJHGca2IPrdfTyW09wfhht4x8JX207pKG-0aZyF60', '2024-02-09 14:02:07', '2024-02-08 14:02:07.390658', 1); +-- +-- Dumping data for table `user_access_tokens` +-- + +/*!40000 ALTER TABLE `user_access_tokens` DISABLE KEYS */; +INSERT INTO `user_access_tokens` VALUES ('07276357-6286-478c-8058-d249f4ca4dde','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI4MzIwNH0.CiG2E-GnJlDR_6YJMDkxTJOa-dS29HV5rWNEszn-jeA','2024-03-02 16:53:25','2024-03-01 16:53:24.786130',1),('09cf7b0a-62e0-45ee-96b0-e31de32361e0','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1MDkxNTd9.0gtKlcxrxQ-TarEai2lsBnfMc852ZDYHeSjjhpo5Fn8','2024-02-11 04:05:58','2024-02-10 04:05:57.696509',1),('0b95a340-d9eb-4c3a-bd70-2432d3108984','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyMzM4Mn0.IA8TwkjfDwmv9470mgVswU775HCRdGo7zCoPblRNgRU','2024-03-05 11:36:23','2024-03-04 11:36:22.819835',1),('17593c96-3779-4f03-8dbc-cb21ac9f7981','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwiaWF0IjoxNzA5MjYxNTUzfQ.hDAHgYrDfsckG6bSyfGa_QLRmo1tvbJEkYgQUfwqZbs','2024-03-02 10:52:33','2024-03-01 10:52:33.059989',1),('19e593f4-4a03-46e0-9b4e-a009af15790a','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTcxNjY3NX0.a0xNnKWIueWIxu5SXAawpLq6Bl4cTD5O6bqLknDU05U','2024-03-07 17:17:55','2024-03-06 17:17:55.105520',1),('1bb9d14e-823c-4f9f-8562-cbc2fd2be318','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTI1Mn0.-OgIPgXJtFC5ioAu_xpwCY6LRcAyTykXR_1pVrrSCN4','2024-03-05 13:14:12','2024-03-04 13:14:12.192684',1),('31399cec-e506-4194-a9cc-47f7a1a953a9','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTIxNzh9.Da3j0Ht_zAsPldEYJ18RFYZqTYNhgPxSefCEuXd-4T8','2024-02-29 11:49:38','2024-02-28 11:49:38.289991',1),('31875da0-8b93-4ca2-bdb8-ab40c1c059bd','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTg0OX0.g8Rr9eTlEZaghqdd_W5lSyxVV-ZMMnMMkyF2iNrUXpY','2024-03-02 15:57:29','2024-03-01 15:57:29.074445',1),('35fd4bb6-2608-4203-ac2c-9550034993ea','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3ODEwOTh9.WmBVjKJkJs1lve9PrYfF8pYWF-qeg8KQu8IeSDSF3Cw','2024-03-08 11:11:38','2024-03-07 11:11:38.199744',1),('367aaa79-1130-4511-87c4-25aacdf27d3d','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgzOH0.5_K7t6bAuqIO9A8NvgrWACEJUVKduu--1q-rgmAB4Vw','2024-03-02 15:57:18','2024-03-01 15:57:18.314066',1),('3f7dffae-db1f-47dc-9677-5c956c3de39e','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczMTEzMDJ9.D5Qpht1RquKor8WtgfGAcCp8LwG7z3FZhIwbyQzhDmE','2024-02-08 21:08:22','2024-02-07 21:08:22.130066',1),('40342c3e-194c-42eb-adee-189389839195','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxNjF9.tRQOxhB-01Pcut5MXm4L5D1OrbMJfS4LfUys0XB4kWs','2024-02-09 14:02:41','2024-02-08 14:02:41.081164',1),('4541a9e8-a508-4b92-8ae3-c3744ddcad92','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUzODE3NH0.Gb_IUruHmd25Wk1ZFHUtB8G2w1bg7i-ivCa11fT8JTw','2024-03-05 15:42:54','2024-03-04 15:42:54.286838',1),('53a08eaa-74fb-45cb-9e6c-514b67346e06','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTU0NDM0Mn0.rcVSgy1s7zNWtB_Q93zdZzL633UFbeH_RWEY_kelVHQ','2024-03-05 17:25:42','2024-03-04 17:25:42.096331',1),('6ea87ca4-89e4-419b-9eb3-7bef4ada53ca','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTM0Mn0.kLsAfIgUDqNEogqBZWaDt7iUYb4gE4dznjTKoUzSKj0','2024-03-05 13:15:42','2024-03-04 13:15:42.347589',1),('6f3990bd-d093-4cc4-9f9e-cba45260ec42','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkxOTUxMTR9.XvbjbvGNz7pYm-Yol8X3uTDwYkn35i-Fu_LsmMU4PdM','2024-03-01 16:25:15','2024-02-29 16:25:14.725434',1),('78519125-77c1-4768-9644-d8fa639f3ff0','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3NzQwNzN9._ZJd1B4EybHlO3W9p98uZ0NJDm6fJur9ifLwEIwywI0','2024-03-08 09:14:34','2024-03-07 09:14:33.765186',1),('7ebc6a49-421c-4437-a7a8-bc8aaeb96b26','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkxOTUxNjl9.oiSa_amY2bX-MaAxL8vs_EmSEyhyBLVJLn9uPf_HgY4','2024-03-01 16:26:09','2024-02-29 16:26:09.075302',1),('8e26aa27-fa67-4b67-8705-019be3e8f1f8','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTY4OTh9.LemwyefdnGCm2ZM2KE3RN2-d6n-Xj2TAujs7iBWaYs8','2024-02-29 13:08:18','2024-02-28 13:08:18.482193',1),('8f7fcaed-c4ed-4cf0-840f-37bcd3a39a59','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTg1M30.woUKWiIQ96woTIFDSdPWkQHwypfTc9T6EqFDbwN56mE','2024-03-02 15:57:34','2024-03-01 15:57:33.866391',1),('9d1ba8e9-dffc-4b15-b21f-4a90f196e39c','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1Mjc5MDV9.7LeiS3LBBdiAc7YrULWpmnI1oNSvR79K-qjEOlBYOnI','2024-02-11 09:18:26','2024-02-10 09:18:25.656695',1),('ab62dfc5-46d8-4dad-9055-bb2803d1451c','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTY3NjF9.9C_cPmmiW3Sh5OrPNSnw3h1IoqqLa3GlEYvgNFvv5Rg','2024-02-29 13:06:01','2024-02-28 13:06:01.416655',1),('af43fb31-3759-43d5-b13e-e558bccdca58','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTY4NTkwNH0.eKKbI4Wi7WKSqE-gDt5isFihQze-LOysFntMzyaP5DY','2024-03-07 08:45:05','2024-03-06 08:45:04.541645',1),('b6053fd5-7c7c-453e-a8ea-6de17b30304a','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTcwNTF9.LyR5igCxJhcSnB-pXCc7wJNJ2H1ERdu8LO6d94cMz2c','2024-02-29 13:10:51','2024-02-28 13:10:51.331328',1),('c24acee3-7bbd-4e7a-b8f6-a80be1db0b33','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgyNH0.6YmdwppJX1DV9-jdFiHLWRH9X_WbeGYbZtaRIizLRgM','2024-03-02 15:57:05','2024-03-01 15:57:04.739116',1),('cbfcc75c-d357-47fd-afbf-db0629885eb6','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTYyMDQ0OH0.LQXYkRJVyDN7YCVRSsS7osGMzC31ik1XD36-SWDbghI','2024-03-06 14:34:08','2024-03-05 14:34:08.374481',1),('cdc3e837-0953-42e7-8296-d27cfbc41353','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTEzNn0.SUycnmUW-ZDjLoBP4DMB_si11dUVHfXmCsG0tLMVBg0','2024-03-02 15:45:37','2024-03-01 15:45:36.880024',1),('d32e25cc-b00c-43cf-94b0-0f61c3314f4c','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI2MTU4OX0.-IM-L0Dt7Jc5xaEU5Q93z5vhAQTpbD9ngS_uhzZ0haI','2024-03-02 10:53:10','2024-03-01 10:53:09.606838',1),('d9044d67-9a95-4bd5-8103-f6ddc9701f83','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTM2NH0.gEQmOm9E7Adpti0pux6KjtopqZpNy62fZzgWf0Ps1wM','2024-03-05 13:16:05','2024-03-04 13:16:04.665874',1),('e042f0c3-9a35-40cf-83a3-918f617f2ac7','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3ODMxNzR9.6jKaSK15fdN8XkToaimiWbqJ-vhiaQr2QkpAc5XvFuA','2024-03-08 11:46:15','2024-03-07 11:46:14.586995',1),('e754d168-106a-43f5-bbf8-80f1b187cb1e','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTk2MzN9.IQ_1NQHKvQ3Cso7cXYQ32xK3qhaOUUF6jtoQ87cN6b0','2024-02-29 13:53:53','2024-02-28 13:53:53.168581',1),('ebc5fbbb-b29e-4a17-acd6-002ca7936099','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgzMX0.d29Hn1Ub-YJBv9Xw9Olo7kBNB9ugxcs1P1hrtJEQpJo','2024-03-02 15:57:11','2024-03-01 15:57:11.247365',1),('edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxMjd9.VRuJHGca2IPrdfTyW09wfhht4x8JX207pKG-0aZyF60','2024-02-09 14:02:07','2024-02-08 14:02:07.390658',1),('eff3fd41-7307-4027-9f8c-2e3b0fd35051','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTk1OTd9.aLKmV2GGF6pkkolLnJzuq0PXAllSbGIZzJrUR4wlNag','2024-02-29 13:53:17','2024-02-28 13:53:17.473705',1); +/*!40000 ALTER TABLE `user_access_tokens` ENABLE KEYS */; + +-- +-- Table structure for table `user_refresh_tokens` +-- --- ---------------------------- --- Table structure for user_refresh_tokens --- ---------------------------- DROP TABLE IF EXISTS `user_refresh_tokens`; -CREATE TABLE `user_refresh_tokens` ( +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_refresh_tokens` ( `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, `expired_at` datetime NOT NULL COMMENT '令牌过期时间', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', - `accessTokenId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `accessTokenId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `REL_1dfd080c2abf42198691b60ae3`(`accessTokenId`) USING BTREE, - CONSTRAINT `FK_1dfd080c2abf42198691b60ae39` FOREIGN KEY (`accessTokenId`) REFERENCES `user_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + UNIQUE KEY `REL_1dfd080c2abf42198691b60ae3` (`accessTokenId`) USING BTREE, + CONSTRAINT `FK_1dfd080c2abf42198691b60ae39` FOREIGN KEY (`accessTokenId`) REFERENCES `user_access_tokens` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; --- ---------------------------- --- Records of user_refresh_tokens --- ---------------------------- -INSERT INTO `user_refresh_tokens` VALUES ('045ad38e-ab82-4ea1-8b61-c2da89060999', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRWMxcEQwZ1VrYnFtZEhyWVdKTDVPIiwiaWF0IjoxNzA5MDkyMTc4fQ.K2nBZ_B8jneihHfs51LIp0fvkkgV7lFawe2cu4sOjN4', '2024-03-29 11:49:38', '2024-02-28 11:49:38.302821', '31399cec-e506-4194-a9cc-47f7a1a953a9'); -INSERT INTO `user_refresh_tokens` VALUES ('202d0969-6721-4f6f-bf34-f0d1931d4d01', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRpOXVYei1TdldjdWRnclFXVmFXIiwiaWF0IjoxNzA3MzcyMTYxfQ.NOQufR5EAPE2uZoyenmAj9H7S7qo4d6W1aW2ojDxZQc', '2024-03-09 14:02:41', '2024-02-08 14:02:41.091492', '40342c3e-194c-42eb-adee-189389839195'); -INSERT INTO `user_refresh_tokens` VALUES ('461f9b7c-e500-4762-a6d9-f9ea47163064', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicXJvTWNYMnhNRW5uRmZGWkQtaUx0IiwiaWF0IjoxNzA3MzExMzAyfQ.dFIWCePZnn2z2Qv1D5PKBKXUwVDI0Gp091MIOi9jiIo', '2024-03-08 21:08:22', '2024-02-07 21:08:22.145464', '3f7dffae-db1f-47dc-9677-5c956c3de39e'); -INSERT INTO `user_refresh_tokens` VALUES ('b375e623-2d82-48f0-9b7a-9058e3850cc6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicDhUMzdGNFFaUDJHLU5yNGVha21wIiwiaWF0IjoxNzA3MzcyMTI3fQ.fn3It6RKIxXlKmqixg0BMmY_YsQmAxtetueqW-0y1IM', '2024-03-09 14:02:07', '2024-02-08 14:02:07.410008', 'edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb'); -INSERT INTO `user_refresh_tokens` VALUES ('e620ccc1-9e40-4387-9f21-f0722e535a63', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNE5WdmFIc2hWaU05ZFh0QnVBaHNsIiwiaWF0IjoxNzA3NTI3OTA1fQ.zzyGX0mOJe6KWpTzIi7We9d9c0MRuDeGC86DMB0Vubs', '2024-03-11 09:18:26', '2024-02-10 09:18:25.664251', '9d1ba8e9-dffc-4b15-b21f-4a90f196e39c'); -INSERT INTO `user_refresh_tokens` VALUES ('f9a003e8-91b7-41ee-979e-e39cca3534ec', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGJQdl9SVjFtUl80N0o0TGF0QlV5IiwiaWF0IjoxNzA3NTA5MTU3fQ.oEVdWSigTpAQY7F8MlwBnedldH0sJT1YF1Mt0ZUbIw4', '2024-03-11 04:05:58', '2024-02-10 04:05:57.706763', '09cf7b0a-62e0-45ee-96b0-e31de32361e0'); +-- +-- Dumping data for table `user_refresh_tokens` +-- -SET FOREIGN_KEY_CHECKS = 1; +/*!40000 ALTER TABLE `user_refresh_tokens` DISABLE KEYS */; +INSERT INTO `user_refresh_tokens` VALUES ('045ad38e-ab82-4ea1-8b61-c2da89060999','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRWMxcEQwZ1VrYnFtZEhyWVdKTDVPIiwiaWF0IjoxNzA5MDkyMTc4fQ.K2nBZ_B8jneihHfs51LIp0fvkkgV7lFawe2cu4sOjN4','2024-03-29 11:49:38','2024-02-28 11:49:38.302821','31399cec-e506-4194-a9cc-47f7a1a953a9'),('046cd195-a105-493e-9d6c-0d5d87081834','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRlU3a3poeGxNUkhWVUdleDR6VmJTIiwiaWF0IjoxNzA5NjIwNDQ4fQ.UKal8R3pPSiIGPAZFUf78-ne81csm2TwQbRtWIB_B1k','2024-04-04 14:34:08','2024-03-05 14:34:08.406121','cbfcc75c-d357-47fd-afbf-db0629885eb6'),('08f57b8a-6a86-434a-89cf-280255a0d1b6','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaWxvdmdUWmJueU5RTGR4bmxpc3Y0IiwiaWF0IjoxNzA5MDk2ODk4fQ.v29AorJL9FJVAS89GxtFKeJYc92nPaIpE8O-1GY_nhw','2024-03-29 13:08:18','2024-02-28 13:08:18.499682','8e26aa27-fa67-4b67-8705-019be3e8f1f8'),('157299e9-45e0-4fe3-9caf-ff944644605d','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNVVsRHhFSE1EMnpMVGd4bUNmRnVSIiwiaWF0IjoxNzA5Mjc5MTM2fQ.mbmS1urJdB6YxBdrj1n09-IgARY_LC5lRuTAntVUZlg','2024-03-31 15:45:37','2024-03-01 15:45:36.901193','cdc3e837-0953-42e7-8296-d27cfbc41353'),('1f613862-2567-4c10-8026-c4743c62d01a','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1ZZMzl3dnpVLUxmcTFxSjBYVGktIiwiaWF0IjoxNzA5MDk5NTk3fQ.Qjhsglo7PbTsT1_KE3vTMNweHx08dwcz_q1e1f2W9R0','2024-03-29 13:53:17','2024-02-28 13:53:17.487831','eff3fd41-7307-4027-9f8c-2e3b0fd35051'),('202d0969-6721-4f6f-bf34-f0d1931d4d01','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRpOXVYei1TdldjdWRnclFXVmFXIiwiaWF0IjoxNzA3MzcyMTYxfQ.NOQufR5EAPE2uZoyenmAj9H7S7qo4d6W1aW2ojDxZQc','2024-03-09 14:02:41','2024-02-08 14:02:41.091492','40342c3e-194c-42eb-adee-189389839195'),('21e28f7b-c156-41bf-902e-583d48109ebf','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoieVU2NU5NSGVuak5lUmozY3BWVDhwIiwiaWF0IjoxNzA5MTk1MTY5fQ.4Vibz4kCF5UulsR9OmAhmQRhrA9idKCyKXCITVLOAkk','2024-03-30 16:26:09','2024-02-29 16:26:09.086894','7ebc6a49-421c-4437-a7a8-bc8aaeb96b26'),('25e9acfd-eb72-4ab8-9e5e-04f0a683e60f','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibndPOEFIb1hab2d3Y0tpWG41NXlJIiwiaWF0IjoxNzA5Mjc5ODI0fQ._r8VN3caZ5XzEdMpH1FM9e1yVqHuACpknfOeSyixXns','2024-03-31 15:57:05','2024-03-01 15:57:04.755457','c24acee3-7bbd-4e7a-b8f6-a80be1db0b33'),('42cb1246-4a1b-4443-a9b0-926761f13527','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaVBwVW1FQ0xUTXgzUC1KVUdvaXJaIiwiaWF0IjoxNzA5NTI5MzQyfQ.e6TbYee7T2uEt0GJznuLmlEGYvmT8LWQVk0phYMS6Lw','2024-04-03 13:15:42','2024-03-04 13:15:42.358715','6ea87ca4-89e4-419b-9eb3-7bef4ada53ca'),('46195349-cbf4-492f-b193-7aec06cb556b','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN1NGYy1PTGNWc1MxVG5BWEdqLW1NIiwiaWF0IjoxNzA5Mjc5ODQ5fQ.n1eSzEGrz_M_hrYNdxx7UtGWHzt7soyJqDh1UK670fE','2024-03-31 15:57:29','2024-03-01 15:57:29.086263','31875da0-8b93-4ca2-bdb8-ab40c1c059bd'),('461f9b7c-e500-4762-a6d9-f9ea47163064','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicXJvTWNYMnhNRW5uRmZGWkQtaUx0IiwiaWF0IjoxNzA3MzExMzAyfQ.dFIWCePZnn2z2Qv1D5PKBKXUwVDI0Gp091MIOi9jiIo','2024-03-08 21:08:22','2024-02-07 21:08:22.145464','3f7dffae-db1f-47dc-9677-5c956c3de39e'),('4a4a3a33-1308-4fc1-9ffa-f828d875e004','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLWREemYwWUdXMEhVQXI3RHNqbG9qIiwiaWF0IjoxNzA5NzE2Njc1fQ.ro8DesLH5duC9IBySpMLwtuzC5pMPr1NY6U21Ff4ZHg','2024-04-05 17:17:55','2024-03-06 17:17:55.118361','19e593f4-4a03-46e0-9b4e-a009af15790a'),('4b82a1b7-b571-42de-8809-487f7abc2e65','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiR1BkbUVfTmhjZG5xWnpWX2lGN0JuIiwiaWF0IjoxNzA5NTQ0MzQyfQ.hH2bzYj_8Qim997-C7HNUSJqkk43Z1YAiexpRhxR8cQ','2024-04-03 17:25:42','2024-03-04 17:25:42.114236','53a08eaa-74fb-45cb-9e6c-514b67346e06'),('50b2f3ad-232a-41f6-bf97-2c52127a3e28','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVnlZNjYwYWNPRHlTd2NZWGhscWdFIiwiaWF0IjoxNzA5Mjc5ODMxfQ.uG0qmKgWzYNvBJFfCvvOmndCgrwpkZ3NVL4aynD6Uxc','2024-03-31 15:57:11','2024-03-01 15:57:11.259022','ebc5fbbb-b29e-4a17-acd6-002ca7936099'),('53c79bc8-f092-4d07-b480-f8637a3887fa','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOEoyQ0w5RXYtSkhmWW0xSDNMMUxEIiwiaWF0IjoxNzA5MjYxNTg5fQ.3Ez1mfJkLZ9LFo2nTshTtOjs-VN56k1LWNoijWzSYaA','2024-03-31 10:53:10','2024-03-01 10:53:09.615615','d32e25cc-b00c-43cf-94b0-0f61c3314f4c'),('718ccd7b-bb4f-4400-8f40-ccd6141ef4c5','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiamNTcWlzUkFIaW5SOTlUX2lvVzV0IiwiaWF0IjoxNzA5MDk2NzYxfQ.X-iqKmDeV6juqCQC8XjU7o-8CXNxEyLyn5uck4mkJoY','2024-03-29 13:06:01','2024-02-28 13:06:01.430737','ab62dfc5-46d8-4dad-9055-bb2803d1451c'),('9557d0a9-5004-4392-95b6-637b3343dbc3','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTnNqRGZsMUpKY3RWTzFYaEtEMV9FIiwiaWF0IjoxNzA5NzgzMTc0fQ.osKC1PeUm6LAMj6ZyIuNhUxFcKN1t1ZvrjkE2Rn1pxw','2024-04-06 11:46:15','2024-03-07 11:46:14.600560','e042f0c3-9a35-40cf-83a3-918f617f2ac7'),('9d54c429-5dab-4e72-b70e-d1e6918d364f','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ1UxYnkxTkVNT05ZQVJsWjBkYzIwIiwiaWF0IjoxNzA5Nzc0MDczfQ.rMG5F7Cec_gcP7C7gr_WL6XiquvuWmvV8Wh2SQOEwuY','2024-04-06 09:14:34','2024-03-07 09:14:33.788438','78519125-77c1-4768-9644-d8fa639f3ff0'),('a2e12448-35b0-487e-a18e-a60461a54370','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZHRvUU9fUjhCZGFOQ1EzMVRhYmxtIiwiaWF0IjoxNzA5NzgxMDk4fQ.3ORghjx3Yzh9_f62tYHLeY_x602pBF-wMrKEVX7Dd2c','2024-04-06 11:11:38','2024-03-07 11:11:38.213630','35fd4bb6-2608-4203-ac2c-9550034993ea'),('a30969f3-b766-4190-b3c6-544f81a05a5a','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRlRUN0VqRWlfc1RkSXd6cjhGZmtZIiwiaWF0IjoxNzA5NTI5MzY0fQ.5qz5M4MC4nU614Nn8hyW0l1TuGcFNuKRHC_ZYzEZwO8','2024-04-03 13:16:05','2024-03-04 13:16:04.680424','d9044d67-9a95-4bd5-8103-f6ddc9701f83'),('a500bb13-4b9a-4cf2-b691-b046586d9ee7','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoic3R1TDFpUmljTnBlSTlhR0FEUFQwIiwiaWF0IjoxNzA5Mjc5ODUzfQ.oiNGDkAWVr4jko01TY7rqWoOYvCcwZbWpfLCzgtqMHc','2024-03-31 15:57:34','2024-03-01 15:57:33.879108','8f7fcaed-c4ed-4cf0-840f-37bcd3a39a59'),('a8451340-008c-4fa7-bea0-3bdeee5d5cfa','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVXdDQ2dJeUU1eGpUT1lLRDVtcnU3IiwiaWF0IjoxNzA5MjYxNTUzfQ.dA6eFaURUP0wYU7Pr0Xjv2oFkkfrwQb-cB_gziAK4G4','2024-03-31 10:52:33','2024-03-01 10:52:33.072305','17593c96-3779-4f03-8dbc-cb21ac9f7981'),('b375e623-2d82-48f0-9b7a-9058e3850cc6','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicDhUMzdGNFFaUDJHLU5yNGVha21wIiwiaWF0IjoxNzA3MzcyMTI3fQ.fn3It6RKIxXlKmqixg0BMmY_YsQmAxtetueqW-0y1IM','2024-03-09 14:02:07','2024-02-08 14:02:07.410008','edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb'),('c92355b8-da11-4e8d-b5a6-0826bb44aac0','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUlVnM3RtQTBROWZjZ0JZWnJxMXNxIiwiaWF0IjoxNzA5Njg1OTA0fQ.CraA5kdSrHeZhyRchvmZfnfdiPXfjd_4A6FZWPh-r7Y','2024-04-05 08:45:05','2024-03-06 08:45:04.557816','af43fb31-3759-43d5-b13e-e558bccdca58'),('e620ccc1-9e40-4387-9f21-f0722e535a63','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNE5WdmFIc2hWaU05ZFh0QnVBaHNsIiwiaWF0IjoxNzA3NTI3OTA1fQ.zzyGX0mOJe6KWpTzIi7We9d9c0MRuDeGC86DMB0Vubs','2024-03-11 09:18:26','2024-02-10 09:18:25.664251','9d1ba8e9-dffc-4b15-b21f-4a90f196e39c'),('ea877712-28d7-4ee1-97ad-27f4455d59a5','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZUVsNUM2RXZFMjBacENpZnZZalI4IiwiaWF0IjoxNzA5MTk1MTE0fQ.SnF7us-m0ktL6JJIXl8NgNPrsVlHMSN_l5-Nr9L2FeA','2024-03-30 16:25:15','2024-02-29 16:25:14.741842','6f3990bd-d093-4cc4-9f9e-cba45260ec42'),('eb8e0905-8f49-42db-b9f8-771f8d706ab0','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiSEZWTG9xY25UeV84ZGpOWWxZTy02IiwiaWF0IjoxNzA5MDk3MDUxfQ.cC5X2an9Vn4q3NThsqKdA72locR6J10yOCPPqGT_hnc','2024-03-29 13:10:51','2024-02-28 13:10:51.345270','b6053fd5-7c7c-453e-a8ea-6de17b30304a'),('ece48e5b-4a1e-4683-acbc-4da02db0d511','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiai1DZjIyUllQX0taaWpPaFkyV01iIiwiaWF0IjoxNzA5NTM4MTc0fQ.oAUJSvbA2EQbdqnpia8nHiiH7VH6igEHOka9INSnTVE','2024-04-03 15:42:54','2024-03-04 15:42:54.302651','4541a9e8-a508-4b92-8ae3-c3744ddcad92'),('ef3560f5-8f24-4a76-a5e8-c17de1f1fcfe','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiY2VkQ2xZdl9TcTJ2SUEybzZaZS1NIiwiaWF0IjoxNzA5MDk5NjMzfQ.mxekqz0IB_fzwvoBcZyN7BPKezISmd4wPHt7fR7-b3g','2024-03-29 13:53:53','2024-02-28 13:53:53.180619','e754d168-106a-43f5-bbf8-80f1b187cb1e'),('f48bf795-9163-4899-8268-2345a0d2ba4e','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVjc1SUFMMkhVLUFuTkZINzVFbmhfIiwiaWF0IjoxNzA5Mjc5ODM4fQ.epc14TsoOkTpJOoo1zW9N3qKxiiUTqQ42Sbb455ppAE','2024-03-31 15:57:18','2024-03-01 15:57:18.327929','367aaa79-1130-4511-87c4-25aacdf27d3d'),('f9a003e8-91b7-41ee-979e-e39cca3534ec','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGJQdl9SVjFtUl80N0o0TGF0QlV5IiwiaWF0IjoxNzA3NTA5MTU3fQ.oEVdWSigTpAQY7F8MlwBnedldH0sJT1YF1Mt0ZUbIw4','2024-03-11 04:05:58','2024-02-10 04:05:57.706763','09cf7b0a-62e0-45ee-96b0-e31de32361e0'),('faef8e19-55d3-4005-8022-ca735c5c9390','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1ZZeXlpQWNJREVTdktFNUhjeER1IiwiaWF0IjoxNzA5NTI5MjUyfQ.IcNLUGhcsxTWIYWxT-0TeII4NbM--Bgq7BJ0VbRfUMo','2024-04-03 13:14:12','2024-03-04 13:14:12.215057','1bb9d14e-823c-4f9f-8562-cbc2fd2be318'),('fb10a7dd-5f84-4151-ab25-b24ec9fca46e','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMUFzVUdRc3hjZEdnaVhjYkVUcm1WIiwiaWF0IjoxNzA5NTIzMzgyfQ.t2zzBijhlt4cV-p8KElBjJk4TURABI-tKRk-mIkmb4w','2024-04-03 11:36:23','2024-03-04 11:36:22.837030','0b95a340-d9eb-4c3a-bd70-2432d3108984'),('fda03f46-8638-43c1-8417-12ecb1ec7cf7','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNUx1NkJuc1pQeVdtVm1DbHg4MGxwIiwiaWF0IjoxNzA5MjgzMjA0fQ.yAmdtYl3KqMa_R32R2Ht2xxMtQIMpncQ-hFzhzTJTkE','2024-03-31 16:53:25','2024-03-01 16:53:24.799017','07276357-6286-478c-8058-d249f4ca4dde'); +/*!40000 ALTER TABLE `user_refresh_tokens` ENABLE KEYS */; + +-- +-- Table structure for table `vehicle_usage` +-- + +DROP TABLE IF EXISTS `vehicle_usage`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `vehicle_usage` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `year` int NOT NULL COMMENT '年度', + `vehicle_license` int NOT NULL COMMENT '外出使用的车辆名称(字典)', + `applicant` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '申请人', + `driver` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '出行司机', + `current_mileage` int DEFAULT NULL COMMENT '当前车辆里程数(KM)', + `expected_start_date` date DEFAULT NULL COMMENT '预计出行开始时间', + `expected_end_date` date DEFAULT NULL COMMENT '预计出行结束时间', + `purpose` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '使用事由', + `actual_return_time` date DEFAULT NULL COMMENT '实际回司时间', + `return_mileage` int DEFAULT NULL COMMENT '回城车辆里程数(KM)', + `reviewer` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '审核人', + `status` tinyint NOT NULL DEFAULT '0' COMMENT '审核状态(字典)', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `vehicle_usage` +-- + +/*!40000 ALTER TABLE `vehicle_usage` DISABLE KEYS */; +/*!40000 ALTER TABLE `vehicle_usage` ENABLE KEYS */; + +-- +-- Dumping routines for database 'hxoa' +-- +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2024-03-07 12:04:02 diff --git a/deploy/sql/raw_materials_inventory.sql b/deploy/sql/raw_materials_inventory.sql deleted file mode 100644 index 9b313cf..0000000 --- a/deploy/sql/raw_materials_inventory.sql +++ /dev/null @@ -1,23 +0,0 @@ -CREATE TABLE `materials_inventory`( - id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ID', - company_name VARCHAR(255) COMMENT '公司名称', - product_name VARCHAR(255) COMMENT '产品名称', - unit VARCHAR(50) COMMENT '单位', - previous_inventory_quantity INT COMMENT '之前的库存数量', - previous_unit_price DECIMAL(10, 2) COMMENT '之前的单价', - previous_amount DECIMAL(10, 2) COMMENT '之前的金额', - inventory_time DATETIME COMMENT '入库时间', - inventory_quantity INT COMMENT '入库后数量', - inventory_unit_price DECIMAL(10, 2) COMMENT '入库后单价', - inventory_amount DECIMAL(10, 2) COMMENT '入库金额', - out_time DATETIME COMMENT '出库时间', - out_quantity INT COMMENT '出库数量', - out_unit_price DECIMAL(10, 2) COMMENT '出库单价', - out_amount DECIMAL(10, 2) COMMENT '出库金额', - current_inventory_quantity INT COMMENT '现在的结存数量', - current_unit_price DECIMAL(10, 2) COMMENT '现在的单价', - current_amount DECIMAL(10, 2) COMMENT '现在的金额', - agent VARCHAR(100) COMMENT '经办人', - issuance_number VARCHAR(100) COMMENT '领料单号', - remark VARCHAR(255) COMMENT '备注' -); \ No newline at end of file diff --git a/package.json b/package.json index 732e0dc..9cb7ada 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "crypto-js": "^4.2.0", "dayjs": "^1.11.10", "dotenv": "16.4.4", + "exceljs": "^4.4.0", "handlebars": "^4.7.8", "helmet": "^7.1.0", "ioredis": "^5.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a21d55b..9528f32 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,6 +116,9 @@ dependencies: dotenv: specifier: 16.4.4 version: 16.4.4 + exceljs: + specifier: ^4.4.0 + version: 4.4.0 handlebars: specifier: ^4.7.8 version: 4.7.8 @@ -1816,6 +1819,29 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@fast-csv/format@4.3.5: + resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==} + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.isboolean: 3.0.3 + lodash.isequal: 4.5.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + dev: false + + /@fast-csv/parse@4.3.6: + resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.groupby: 4.6.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + lodash.isundefined: 3.0.1 + lodash.uniq: 4.5.0 + dev: false + /@fastify/accept-negotiator@1.1.0: resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==} engines: {node: '>=14'} @@ -3298,6 +3324,10 @@ packages: '@types/express': 4.17.21 dev: true + /@types/node@14.18.63: + resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} + dev: false + /@types/node@20.11.18: resolution: {integrity: sha512-ABT5VWnnYneSBcNWYSCuR05M826RoMyMSGiFivXGx6ZUIsXb9vn4643IEwkg2zbEOSgAiSogtapN2fgc4mAPlw==} dependencies: @@ -3900,6 +3930,51 @@ packages: dev: false optional: true + /archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + dev: false + + /archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: false + + /archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + dependencies: + archiver-utils: 2.1.0 + async: 3.2.5 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + dev: false + /archy@1.0.0: resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} dev: false @@ -4182,17 +4257,28 @@ packages: resolution: {integrity: sha512-1J5SWbkoVJH9DTALN8igB4p+nPKZzPrJ/HomqBDLpfUvDXCdjdBmBUcH5McZfur0lftVssVU6BZug5NYh87zTw==} dev: false + /big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + dev: false + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + /binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + dev: false + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true /block-stream2@2.1.0: resolution: {integrity: sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==} @@ -4200,6 +4286,10 @@ packages: readable-stream: 3.6.2 dev: false + /bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + dev: false + /boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -4296,6 +4386,10 @@ packages: node-int64: 0.4.0 dev: true + /buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: false + /buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} dev: false @@ -4309,12 +4403,16 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true + /buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + dev: false + /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -4323,6 +4421,11 @@ packages: ieee754: 1.2.1 dev: false + /buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + dev: false + /bull@4.12.2: resolution: {integrity: sha512-WPuc0VCYx+cIVMiZtPwRpWyyJFBrj4/OgKJ6n9Jf4tIw7rQNV+HAKQv15UDkcTvfpGFehvod7Fd1YztbYSJIDQ==} engines: {node: '>=12'} @@ -4408,6 +4511,12 @@ packages: resolution: {integrity: sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==} dev: true + /chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} + dependencies: + traverse: 0.3.9 + dev: false + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -4756,6 +4865,16 @@ packages: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} dev: true + /compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -5034,7 +5153,6 @@ packages: /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true /cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} @@ -5088,6 +5206,20 @@ packages: typescript: 5.3.3 dev: true + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: false + + /crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + dev: false + /crc32@0.2.2: resolution: {integrity: sha512-PFZEGbDUeoNbL2GHIEpJRQGheXReDody/9axKTxhXtQqIL443wnNigtVZO9iuCIMPApKZRv7k2xr8euXHqNxQQ==} engines: {node: '>= 0.4.0'} @@ -5549,7 +5681,6 @@ packages: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} dependencies: readable-stream: 2.3.8 - dev: true /duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -6023,6 +6154,21 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + /exceljs@4.4.0: + resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} + engines: {node: '>=8.3.0'} + dependencies: + archiver: 5.3.2 + dayjs: 1.11.10 + fast-csv: 4.3.6 + jszip: 3.10.1 + readable-stream: 3.6.2 + saxes: 5.0.1 + tmp: 0.2.3 + unzipper: 0.10.14 + uuid: 8.3.2 + dev: false + /execa@0.10.0: resolution: {integrity: sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==} engines: {node: '>=4'} @@ -6126,6 +6272,14 @@ packages: resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} dev: false + /fast-csv@4.3.6: + resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} + engines: {node: '>=10.0.0'} + dependencies: + '@fast-csv/format': 4.3.5 + '@fast-csv/parse': 4.3.6 + dev: false + /fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} dev: false @@ -6474,6 +6628,10 @@ packages: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} dev: true + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: false + /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -6525,6 +6683,16 @@ packages: requiresBuild: true optional: true + /fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.6.3 + dev: false + /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -6770,7 +6938,6 @@ packages: /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -7033,6 +7200,10 @@ packages: engines: {node: '>= 4'} dev: true + /immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + dev: false + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -7346,7 +7517,6 @@ packages: /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -7993,6 +8163,15 @@ packages: promise: 7.3.1 dev: false + /jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + dev: false + /juice@9.1.0: resolution: {integrity: sha512-odblShmPrUoHUwRuC8EmLji5bPP2MLO1GL+gt4XU3tT2ECmbSrrMjtMQaqg3wgMFP2zvUzdPZGfxc5Trk3Z+fQ==} engines: {node: '>=10.0.0'} @@ -8050,6 +8229,13 @@ packages: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} dev: false + /lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + dependencies: + readable-stream: 2.3.8 + dev: false + /leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} dev: false @@ -8104,6 +8290,12 @@ packages: resolution: {integrity: sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==} dev: false + /lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + dependencies: + immediate: 3.0.6 + dev: false + /light-my-request@5.11.0: resolution: {integrity: sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==} dependencies: @@ -8121,6 +8313,10 @@ packages: uc.micro: 2.0.0 dev: false + /listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + dev: false + /load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} @@ -8178,6 +8374,22 @@ packages: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} dev: false + /lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + dev: false + + /lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + dev: false + + /lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + dev: false + + /lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + dev: false + /lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} dev: false @@ -8190,6 +8402,14 @@ packages: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} dev: false + /lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + dev: false + + /lodash.isfunction@3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + dev: false + /lodash.isinteger@4.0.4: resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} dev: false @@ -8198,6 +8418,10 @@ packages: resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} dev: true + /lodash.isnil@4.0.0: + resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} + dev: false + /lodash.isnumber@3.0.3: resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} dev: false @@ -8209,6 +8433,10 @@ packages: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} dev: false + /lodash.isundefined@3.0.1: + resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==} + dev: false + /lodash.map@4.6.0: resolution: {integrity: sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==} dev: true @@ -8231,11 +8459,13 @@ packages: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} dev: false + /lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + dev: false + /lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} requiresBuild: true - dev: true - optional: true /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -9531,6 +9761,10 @@ packages: resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} dev: true + /pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + dev: false + /param-case@2.1.1: resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} dependencies: @@ -9886,7 +10120,6 @@ packages: /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true /process-warning@2.3.2: resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} @@ -10188,7 +10421,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} @@ -10209,6 +10441,12 @@ packages: string_decoder: 1.3.0 dev: false + /readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + dependencies: + minimatch: 5.1.6 + dev: false + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -10405,7 +10643,6 @@ packages: hasBin: true dependencies: glob: 7.2.3 - dev: true /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} @@ -10466,7 +10703,6 @@ packages: /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -10489,6 +10725,13 @@ packages: resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} dev: true + /saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + dependencies: + xmlchars: 2.2.0 + dev: false + /schema-utils@3.3.0: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} @@ -10619,6 +10862,10 @@ packages: has-property-descriptors: 1.0.2 dev: true + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: false + /setprototypeof@1.1.0: resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} dev: true @@ -10981,7 +11228,6 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: true /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -11143,6 +11389,17 @@ packages: engines: {node: '>=6'} dev: true + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + /tar@6.2.0: resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} engines: {node: '>=10'} @@ -11276,6 +11533,11 @@ packages: os-tmpdir: 1.0.2 dev: true + /tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + dev: false + /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: true @@ -11307,6 +11569,10 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + /traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + dev: false + /traverse@0.6.8: resolution: {integrity: sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==} engines: {node: '>= 0.4'} @@ -11688,6 +11954,21 @@ packages: engines: {node: '>= 0.8'} dev: true + /unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + dependencies: + big-integer: 1.6.52 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + dev: false + /update-browserslist-db@1.0.13(browserslist@4.23.0): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true @@ -12072,6 +12353,10 @@ packages: utf-8-validate: optional: true + /xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: false + /xmldoc@1.3.0: resolution: {integrity: sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==} dependencies: @@ -12145,3 +12430,12 @@ packages: /zepto@1.2.0: resolution: {integrity: sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==} dev: true + + /zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + dependencies: + archiver-utils: 3.0.4 + compress-commons: 4.1.2 + readable-stream: 3.6.2 + dev: false diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts index 18a1f27..c615a32 100644 --- a/src/modules/materials_inventory/materials_inventory.controller.ts +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -1,19 +1,26 @@ -import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Post, Put, Query, Res } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiResult } from '~/common/decorators/api-result.decorator'; import { IdParam } from '~/common/decorators/id-param.decorator'; import { Perm, definePermission } from '../auth/decorators/permission.decorator'; -import { ContractQueryDto, ContractDto, ContractUpdateDto } from '../contract/contract.dto'; +import { + MaterialsInventoryQueryDto, + MaterialsInventoryDto, + MaterialsInventoryUpdateDto, + MaterialsInventoryExportDto +} from '../materials_inventory/materials_inventory.dto'; import { MaterialsInventoryService } from './materials_inventory.service'; import { MaterialsInventoryEntity } from './materials_inventory.entity'; import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { FastifyReply } from 'fastify'; export const permissions = definePermission('app:materials_inventory', { LIST: 'list', CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete' + DELETE: 'delete', + EXPORT: 'export' } as const); @ApiTags('MaterialsI Inventory - 原材料盘点') @@ -21,17 +28,28 @@ export const permissions = definePermission('app:materials_inventory', { @Controller('materials-inventory') export class MaterialsInventoryController { constructor(private miService: MaterialsInventoryService) {} + + @Get('export') + @ApiOperation({ summary: '导出原材料盘点表' }) + @Perm(permissions.EXPORT) + async exportMaterialsInventoryCheck( + @Param() dto: MaterialsInventoryExportDto, + @Res() res: FastifyReply + ): Promise { + await this.miService.exportMaterialsInventoryCheck(dto, res); + } + @Get() @ApiOperation({ summary: '获取原材料盘点列表' }) @ApiResult({ type: [MaterialsInventoryEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: ContractQueryDto) { + async list(@Query() dto: MaterialsInventoryQueryDto) { return this.miService.findAll(dto); } @Get(':id') @ApiOperation({ summary: '获取原材料盘点信息' }) - @ApiResult({ type: ContractDto }) + @ApiResult({ type: MaterialsInventoryDto }) @Perm(permissions.READ) async info(@IdParam() id: number) { return this.miService.info(id); @@ -40,14 +58,14 @@ export class MaterialsInventoryController { @Post() @ApiOperation({ summary: '新增原材料盘点' }) @Perm(permissions.CREATE) - async create(@Body() dto: ContractDto): Promise { + async create(@Body() dto: MaterialsInventoryDto): Promise { await this.miService.create(dto); } @Put(':id') @ApiOperation({ summary: '更新原材料盘点' }) @Perm(permissions.UPDATE) - async update(@IdParam() id: number, @Body() dto: ContractUpdateDto): Promise { + async update(@IdParam() id: number, @Body() dto: MaterialsInventoryUpdateDto): Promise { await this.miService.update(id, dto); } diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts index bee1740..ad49c69 100644 --- a/src/modules/materials_inventory/materials_inventory.dto.ts +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -13,6 +13,9 @@ import { } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; +import { Transform } from 'class-transformer'; +import dayjs from 'dayjs'; +import { formatToDate } from '~/utils'; export class MaterialsInventoryDto {} @@ -21,3 +24,21 @@ export class MaterialsInventoryQueryDto extends IntersectionType( PagerDto, PartialType(MaterialsInventoryDto) ) {} +export class MaterialsInventoryExportDto { + @ApiProperty({ description: '项目' }) + @IsOptional() + @IsNumber() + projectId: number; + + @ApiProperty({ description: '导出时间YYYY-MM-DD' }) + @IsOptional() + @Transform(params => { + // 开始和结束时间用的是一月的开始和一月的结束的时分秒 + const date = params.value; + return [ + date ? `${formatToDate(dayjs(date).startOf('month'))} 00:00:00` : null, + date ? `${formatToDate(dayjs(date).endOf('month'))} 23:59:59` : null + ]; + }) + time?: string[]; +} diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index c45f1e7..d33d5d1 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -4,13 +4,14 @@ import { MaterialsInventoryEntity } from './materials_inventory.entity'; import { EntityManager, Repository } from 'typeorm'; import { MaterialsInventoryDto, + MaterialsInventoryExportDto, MaterialsInventoryQueryDto, MaterialsInventoryUpdateDto } from './materials_inventory.dto'; import { Pagination } from '~/helper/paginate/pagination'; - +import { FastifyReply } from 'fastify'; import { paginate } from '~/helper/paginate'; - +import * as ExcelJS from 'exceljs'; @Injectable() export class MaterialsInventoryService { constructor( @@ -18,6 +19,45 @@ export class MaterialsInventoryService { @InjectRepository(MaterialsInventoryEntity) private materialsInventoryRepository: Repository ) {} + + /** + * 导出原材料盘点表 + */ + async exportMaterialsInventoryCheck(dto: MaterialsInventoryExportDto, res: FastifyReply) { + const workbook = new ExcelJS.Workbook(); + // 创建一个工作表 + const sheet = workbook.addWorksheet('CDKEY'); + // 设置表头 + sheet.columns = [ + { header: '名称', key: 'name', width: 32 }, + { header: '创建人', key: 'create_user', width: 32 }, + { header: '创建时间', key: 'create_time', width: 32 } + ]; + const data = [ + { + name: '名称', + create_user: 'admin', + create_time: '2023-05-18 12:00:00' + } + ]; + data.forEach(item => { + sheet.addRow({ + name: item.name, + create_user: item.create_user, + create_time: item.create_time + }); + }); + + const buffer = await workbook.xlsx.writeBuffer(); + res + .header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + .header( + 'Content-Disposition', + `attachment; filename="${encodeURIComponent('导出_excel' + new Date().getTime() + '.xlsx')}"` + ) + .send(buffer); + } + /** * 查询所有盘点信息 */ From 96135fffd10aaea64fdd99ab5442c927f904ae48 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 7 Mar 2024 16:28:03 +0800 Subject: [PATCH 26/64] =?UTF-8?q?feat:=20=E5=AF=BC=E5=87=BA=E7=9B=98?= =?UTF-8?q?=E7=82=B9=E8=A1=A8=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 1 + ecosystem.config.js | 2 +- .../materials_inventory.controller.ts | 4 +- .../materials_inventory.dto.ts | 3 +- .../materials_inventory.module.ts | 7 +- .../materials_inventory.service.ts | 236 ++++++++++++++++-- 6 files changed, 222 insertions(+), 31 deletions(-) diff --git a/.env.development b/.env.development index b33890f..c330ac4 100644 --- a/.env.development +++ b/.env.development @@ -32,4 +32,5 @@ SMTP_HOST = smtp.163.com SMTP_PORT = 465 SMTP_USER = nest_admin@163.com SMTP_PASS = VIPLLOIPMETTROYU + \ No newline at end of file diff --git a/ecosystem.config.js b/ecosystem.config.js index 4b329d9..a52b8ef 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -10,7 +10,7 @@ module.exports = { autorestart: true, exec_mode: 'cluster', watch: false, - instances: cpuLen, + instances: cpuLen, max_memory_restart: '520M', args: '', env: { diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts index c615a32..5e483a5 100644 --- a/src/modules/materials_inventory/materials_inventory.controller.ts +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -33,9 +33,9 @@ export class MaterialsInventoryController { @ApiOperation({ summary: '导出原材料盘点表' }) @Perm(permissions.EXPORT) async exportMaterialsInventoryCheck( - @Param() dto: MaterialsInventoryExportDto, + @Query() dto: MaterialsInventoryExportDto, @Res() res: FastifyReply - ): Promise { + ): Promise { await this.miService.exportMaterialsInventoryCheck(dto, res); } diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts index ad49c69..20bfd68 100644 --- a/src/modules/materials_inventory/materials_inventory.dto.ts +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -29,9 +29,10 @@ export class MaterialsInventoryExportDto { @IsOptional() @IsNumber() projectId: number; - + @ApiProperty({ description: '导出时间YYYY-MM-DD' }) @IsOptional() + @IsArray() @Transform(params => { // 开始和结束时间用的是一月的开始和一月的结束的时分秒 const date = params.value; diff --git a/src/modules/materials_inventory/materials_inventory.module.ts b/src/modules/materials_inventory/materials_inventory.module.ts index f3b42ea..4e9aee3 100644 --- a/src/modules/materials_inventory/materials_inventory.module.ts +++ b/src/modules/materials_inventory/materials_inventory.module.ts @@ -8,12 +8,15 @@ import { MaterialsInOutController } from './in_out/materials_in_out.controller'; import { MaterialsInOutService } from './in_out/materials_in_out.service'; import { MaterialsInOutEntity } from './in_out/materials_in_out.entity'; import { ParamConfigModule } from '../system/param-config/param-config.module'; +import { ProjectModule } from '../project/project.module'; +import { ProjectEntity } from '../project/project.entity'; @Module({ imports: [ - TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity]), + TypeOrmModule.forFeature([MaterialsInventoryEntity, MaterialsInOutEntity,ProjectEntity]), ParamConfigModule, - StorageModule + StorageModule, + ProjectModule ], controllers: [MaterialsInventoryController, MaterialsInOutController], providers: [MaterialsInventoryService, MaterialsInOutService] diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index d33d5d1..73dd3de 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -12,48 +12,234 @@ import { Pagination } from '~/helper/paginate/pagination'; import { FastifyReply } from 'fastify'; import { paginate } from '~/helper/paginate'; import * as ExcelJS from 'exceljs'; +import dayjs from 'dayjs'; +import { MaterialsInOutEntity } from './in_out/materials_in_out.entity'; +import { fieldSearch } from '~/shared/database/field-search'; +import { groupBy, uniqBy } from 'lodash'; +import { MaterialsInOrOutEnum } from '~/constants/enum'; +import { ProjectEntity } from '../project/project.entity'; @Injectable() export class MaterialsInventoryService { + const; constructor( @InjectEntityManager() private entityManager: EntityManager, @InjectRepository(MaterialsInventoryEntity) - private materialsInventoryRepository: Repository + private materialsInventoryRepository: Repository, + @InjectRepository(MaterialsInOutEntity) + private materialsInOutRepository: Repository, + @InjectRepository(ProjectEntity) + private projectRepository: Repository ) {} /** * 导出原材料盘点表 */ - async exportMaterialsInventoryCheck(dto: MaterialsInventoryExportDto, res: FastifyReply) { + async exportMaterialsInventoryCheck( + { time, projectId }: MaterialsInventoryExportDto, + res: FastifyReply + ): Promise { + const ROW_HEIGHT = 20; + const HEADER_FONT_SIZE = 18; const workbook = new ExcelJS.Workbook(); - // 创建一个工作表 - const sheet = workbook.addWorksheet('CDKEY'); - // 设置表头 - sheet.columns = [ - { header: '名称', key: 'name', width: 32 }, - { header: '创建人', key: 'create_user', width: 32 }, - { header: '创建时间', key: 'create_time', width: 32 } - ]; - const data = [ - { - name: '名称', - create_user: 'admin', - create_time: '2023-05-18 12:00:00' - } - ]; - data.forEach(item => { - sheet.addRow({ - name: item.name, - create_user: item.create_user, - create_time: item.create_time - }); - }); + let projects: ProjectEntity[] = []; + if (projectId) { + projects = [await this.projectRepository.findOneBy({ id: projectId })]; + } + // 生成数据 + const sqb = this.materialsInOutRepository + .createQueryBuilder('mio') + .leftJoin('mio.project', 'project') + .leftJoin('mio.product', 'product') + .leftJoin('product.unit', 'unit') + .leftJoin('product.company', 'company') + .addSelect(['project.id','project.name', 'product.name', 'unit.label', 'company.name']) + .where(fieldSearch({ time })) + .andWhere('mio.isDelete = 0'); + if (projectId) { + sqb.andWhere('project.id = :projectId', { projectId }); + } + const data = await sqb.addOrderBy('mio.time', 'DESC').getMany(); + if (!projectId) { + projects = uniqBy( + data.map(item => item.project), + 'id' + ); + } + + for (const project of projects) { + const currentProjectData = data.filter(item => item.projectId === project.id); + const sheet = workbook.addWorksheet(project.name); + sheet.mergeCells('A1:T1'); + // 设置标题 + sheet.getCell('A1').value = '山东矿机华信智能科技有限公司原材料盘点表'; + + // 设置日期 + sheet.mergeCells('A2:B2'); + sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`; + // 设置表头 + const headers = [ + '库存编号', + '公司名称', + '产品名称', + '单位', + '库存数量', + '单价', + '金额', + '', + '', + '', + '', + '', + '', + '', + '', + '结存数量', + '单价', + '金额', + '经办人', + '领料单号', + '备注' + ]; + sheet.addRow(headers); + sheet.addRow([ + '', + '', + '', + '', + '', + '', + '', + '入库时间', + '数量', + '单价', + '金额', + '出库时间', + '数量', + '单价', + '金额', + '', + '', + '', + '', + '', + '' + ]); + for (let i = 1; i <= 7; i++) { + sheet.mergeCells(`${String.fromCharCode(64 + i)}3:${String.fromCharCode(64 + i)}4`); + } + // 入库 + sheet.mergeCells('H3:K3'); + sheet.getCell('H3').value = '入库'; + + // 出库 + sheet.mergeCells('L3:O3'); + sheet.getCell('L3').value = '出库'; + + for (let i = 8; i <= 15; i++) { + sheet.getCell(`${String.fromCharCode(64 + i)}4`).style.fill = { + type: 'pattern', + pattern: 'solid', + fgColor: { argb: 'FFFFC000' } + }; + } + + for (let i = 16; i <= 21; i++) { + sheet.mergeCells(`${String.fromCharCode(64 + i)}3:${String.fromCharCode(64 + i)}4`); + } + + // 固定信息样式设定 + sheet.eachRow((row, index) => { + row.alignment = { vertical: 'middle', horizontal: 'center' }; + row.font = { bold: true }; + row.height = ROW_HEIGHT; + if (index >= 3) { + row.eachCell(cell => { + cell.border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + }); + } + }); + + const groupedData = groupBy(currentProjectData, 'inventoryNumber'); + let number = 0; + for (const key in groupedData) { + // 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。 + const inRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.In); + const outRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.Out); + number++; + sheet.addRow([ + `${inRecord.inventoryNumber}`, + inRecord.product.company.name, + inRecord.product.name, + inRecord.product.unit.label, + '0', + '0', + '0', + inRecord.time, + inRecord.quantity, + parseFloat(`${inRecord.unitPrice || 0}`), + parseFloat(`${inRecord.amount || 0}`), + outRecord?.time || '', + outRecord?.quantity || '', + parseFloat(`${outRecord.unitPrice || 0}`), + parseFloat(`${outRecord.amount || 0}`), + '0', + '0', + '0', + outRecord?.agent, + outRecord?.issuanceNumber, + outRecord?.remark + ]); + } + sheet.getCell('A1').font = { size: HEADER_FONT_SIZE }; + + // 固定信息样式设定 + sheet.eachRow((row, index) => { + if (index >= 5) { + row.alignment = { vertical: 'middle', horizontal: 'center' }; + row.height = ROW_HEIGHT; + row.eachCell(cell => { + cell.border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + }); + } + }); + + sheet.columns.forEach((column, index: number) => { + let maxColumnLength = 0; + const autoWidth = ['B', 'C', 'U']; + if (String.fromCharCode(65 + index) === 'B') maxColumnLength = 20; + if (autoWidth.includes(String.fromCharCode(65 + index))) { + column.eachCell({ includeEmpty: true }, (cell, rowIndex) => { + if (rowIndex >= 5) { + const columnLength = `${cell.value || ''}`.length; + if (columnLength > maxColumnLength) { + maxColumnLength = columnLength; + } + } + }); + column.width = maxColumnLength < 12 ? 12 : maxColumnLength; // Minimum width of 10 + } else { + column.width = 12; + } + }); + } + //读取buffer进行传输 const buffer = await workbook.xlsx.writeBuffer(); res .header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') .header( 'Content-Disposition', - `attachment; filename="${encodeURIComponent('导出_excel' + new Date().getTime() + '.xlsx')}"` + `attachment; filename="${encodeURIComponent('导出_excel' + new Date().getTime() + '.xls')}"` ) .send(buffer); } From 10f6f1ff370ade56beb3da8d38e47276ef82894e Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 7 Mar 2024 17:19:33 +0800 Subject: [PATCH 27/64] =?UTF-8?q?feat:=20=E8=BD=A6=E8=BE=86=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 17 +-- src/modules/product/product.module.ts | 3 +- src/modules/tools/storage/storage.entity.ts | 5 + .../vehicle-usage/vehicle-usage.controller.ts | 4 - .../vehicle-usage/vehicle-usage.module.ts | 12 -- .../vehicle-usage/vehicle-usage.service.ts | 4 - .../vehicle_usage/vehicle_usage.controller.ts | 80 ++++++++++ .../vehicle_usage/vehicle_usage.dto.ts | 72 +++++++++ .../vehicle_usage.entity.ts} | 20 ++- .../vehicle_usage/vehicle_usage.module.ts | 13 ++ .../vehicle_usage/vehicle_usage.service.ts | 140 ++++++++++++++++++ 11 files changed, 335 insertions(+), 35 deletions(-) delete mode 100644 src/modules/vehicle-usage/vehicle-usage.controller.ts delete mode 100644 src/modules/vehicle-usage/vehicle-usage.module.ts delete mode 100644 src/modules/vehicle-usage/vehicle-usage.service.ts create mode 100644 src/modules/vehicle_usage/vehicle_usage.controller.ts create mode 100644 src/modules/vehicle_usage/vehicle_usage.dto.ts rename src/modules/{vehicle-usage/vehicle-usage.entity.ts => vehicle_usage/vehicle_usage.entity.ts} (76%) create mode 100644 src/modules/vehicle_usage/vehicle_usage.module.ts create mode 100644 src/modules/vehicle_usage/vehicle_usage.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 5e6ab5d..28b5c6d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,13 +25,11 @@ import { DatabaseModule } from './shared/database/database.module'; import { SocketModule } from './socket/socket.module'; import { ContractModule } from './modules/contract/contract.module'; -import { VehicleUsageModule } from './modules/vehicle-usage/vehicle-usage.module'; -import { VehicleUsageController } from './modules/vehicle-usage/vehicle-usage.controller'; -import { VehicleUsageService } from './modules/vehicle-usage/vehicle-usage.service'; import { MaterialsInventoryModule } from './modules/materials_inventory/materials_inventory.module'; import { CompanyModule } from './modules/company/company.module'; import { ProductModule } from './modules/product/product.module'; import { ProjectModule } from './modules/project/project.module'; +import { VehicleUsageModule } from './modules/vehicle_usage/vehicle_usage.module'; @Module({ imports: [ @@ -62,9 +60,6 @@ import { ProjectModule } from './modules/project/project.module'; // 合同模块 ContractModule, - // 车辆管理 - VehicleUsageModule, - // 原材料库存 MaterialsInventoryModule, @@ -75,7 +70,10 @@ import { ProjectModule } from './modules/project/project.module'; ProductModule, // 项目管理 - ProjectModule + ProjectModule, + + // 车辆管理 + VehicleUsageModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, @@ -86,9 +84,8 @@ import { ProjectModule } from './modules/project/project.module'; { provide: APP_INTERCEPTOR, useClass: IdempotenceInterceptor }, { provide: APP_GUARD, useClass: JwtAuthGuard }, - { provide: APP_GUARD, useClass: RbacGuard }, - VehicleUsageService + { provide: APP_GUARD, useClass: RbacGuard } ], - controllers: [VehicleUsageController] + controllers: [] }) export class AppModule {} diff --git a/src/modules/product/product.module.ts b/src/modules/product/product.module.ts index 75ea48f..fdc44e9 100644 --- a/src/modules/product/product.module.ts +++ b/src/modules/product/product.module.ts @@ -4,10 +4,9 @@ import { ProductService } from './product.service'; import { ProductEntity } from './product.entity'; import { TypeOrmModule } from '@nestjs/typeorm'; import { StorageModule } from '../tools/storage/storage.module'; -import { DatabaseModule } from '~/shared/database/database.module'; @Module({ - imports: [TypeOrmModule.forFeature([ProductEntity]), StorageModule, DatabaseModule], + imports: [TypeOrmModule.forFeature([ProductEntity]), StorageModule], controllers: [ProductController], providers: [ProductService] }) diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index ae0034a..cc58f42 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -8,6 +8,7 @@ import { MaterialsInOutEntity } from '~/modules/materials_inventory/in_out/mater import { MaterialsInventoryEntity } from '~/modules/materials_inventory/materials_inventory.entity'; import { ProductEntity } from '~/modules/product/product.entity'; import { ProjectEntity } from '~/modules/project/project.entity'; +import { VehicleUsageEntity } from '~/modules/vehicle_usage/vehicle_usage.entity'; @Entity({ name: 'tool_storage' }) export class Storage extends CommonEntity { @@ -63,4 +64,8 @@ export class Storage extends CommonEntity { @ApiHideProperty() @ManyToMany(() => ProjectEntity, project => project.files) projects: Relation; + + @ApiHideProperty() + @ManyToMany(() => VehicleUsageEntity, vu => vu.files) + vehicleUsage: Relation; } diff --git a/src/modules/vehicle-usage/vehicle-usage.controller.ts b/src/modules/vehicle-usage/vehicle-usage.controller.ts deleted file mode 100644 index 9c63da7..0000000 --- a/src/modules/vehicle-usage/vehicle-usage.controller.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Controller } from '@nestjs/common'; - -@Controller('vehicle-usage') -export class VehicleUsageController {} diff --git a/src/modules/vehicle-usage/vehicle-usage.module.ts b/src/modules/vehicle-usage/vehicle-usage.module.ts deleted file mode 100644 index d50dfc1..0000000 --- a/src/modules/vehicle-usage/vehicle-usage.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { VehicleUsageService } from './vehicle-usage.service'; -import { VehicleUsageController } from './vehicle-usage.controller'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { VehicleUsageEntity } from './vehicle-usage.entity'; - -@Module({ - imports: [TypeOrmModule.forFeature([VehicleUsageEntity])], - providers: [VehicleUsageService], - controllers: [VehicleUsageController] -}) -export class VehicleUsageModule {} diff --git a/src/modules/vehicle-usage/vehicle-usage.service.ts b/src/modules/vehicle-usage/vehicle-usage.service.ts deleted file mode 100644 index 0ec15a1..0000000 --- a/src/modules/vehicle-usage/vehicle-usage.service.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class VehicleUsageService {} diff --git a/src/modules/vehicle_usage/vehicle_usage.controller.ts b/src/modules/vehicle_usage/vehicle_usage.controller.ts new file mode 100644 index 0000000..59d5f92 --- /dev/null +++ b/src/modules/vehicle_usage/vehicle_usage.controller.ts @@ -0,0 +1,80 @@ +import { + Body, + Controller, + Get, + Query, + Put, + Delete, + Post, + BadRequestException +} from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; + +import { VehicleUsageService } from './vehicle_usage.service'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { VehicleUsageQueryDto, VehicleUsageDto, VehicleUsageUpdateDto } from './vehicle_usage.dto'; +import { VehicleUsageEntity } from '../vehicle_usage/vehicle_usage.entity'; +export const permissions = definePermission('app:vehicle_usage', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('VehicleUsage - 车辆使用') +@ApiSecurityAuth() +@Controller('vehicle-usage') +export class VehicleUsageController { + constructor(private vehicleUsageService: VehicleUsageService) {} + + @Get() + @ApiOperation({ summary: '获取车辆使用列表' }) + @ApiResult({ type: [VehicleUsageEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: VehicleUsageQueryDto) { + return this.vehicleUsageService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取车辆使用信息' }) + @ApiResult({ type: VehicleUsageDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.vehicleUsageService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增车辆使用' }) + @Perm(permissions.CREATE) + async create(@Body() dto: VehicleUsageDto): Promise { + await this.vehicleUsageService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新车辆使用' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: VehicleUsageUpdateDto): Promise { + await this.vehicleUsageService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除车辆使用' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.vehicleUsageService.delete(id); + } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: VehicleUsageUpdateDto + ): Promise { + await this.vehicleUsageService.unlinkAttachments(id, fileIds); + } +} diff --git a/src/modules/vehicle_usage/vehicle_usage.dto.ts b/src/modules/vehicle_usage/vehicle_usage.dto.ts new file mode 100644 index 0000000..15100a1 --- /dev/null +++ b/src/modules/vehicle_usage/vehicle_usage.dto.ts @@ -0,0 +1,72 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '../tools/storage/storage.entity'; + +export class VehicleUsageDto { + @ApiProperty({ description: '年度' }) + @IsNumber() + year: number; + + @ApiProperty({ description: '外出使用的车辆名称(字典)' }) + @IsNumber() + vechicleId: number; + + @ApiProperty({ description: '申请人' }) + @IsString() + applicant: string; + + @ApiProperty({ description: '出行司机', nullable: true }) + @IsOptional() + @IsString() + driver: string; + + @ApiProperty({ description: '当前车辆里程数(KM)' }) + @IsOptional() + @IsNumber() + currentMileage: number; + + @ApiProperty({ description: '预计出行开始时间' }) + @IsOptional() + expectedStartDate: Date; + + @ApiProperty({ description: '预计出行结束时间' }) + @IsOptional() + expectedEndDate: Date; + + @ApiProperty({ description: '使用事由' }) + @IsOptional() + purpose: string; + + @ApiProperty({ description: '实际回司时间' }) + @IsOptional() + actualReturnTime: Date; + + @ApiProperty({ description: '回城车辆里程数(KM)' }) + @IsOptional() + returnMileage: number; + + @ApiProperty({ description: '审核人' }) + @IsOptional() + reviewer: string; + + @ApiProperty({ description: '审核状态:0待审核,1同意,2.不同意(字典)' }) + @IsOptional() + status: number; + + @ApiProperty({ description: '备注' }) + @IsOptional() + remark: string; +} + +export class VehicleUsageUpdateDto extends PartialType(VehicleUsageDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} + +export class VehicleUsageQueryDto extends IntersectionType( + PagerDto, + PartialType(VehicleUsageDto) +) {} diff --git a/src/modules/vehicle-usage/vehicle-usage.entity.ts b/src/modules/vehicle_usage/vehicle_usage.entity.ts similarity index 76% rename from src/modules/vehicle-usage/vehicle-usage.entity.ts rename to src/modules/vehicle_usage/vehicle_usage.entity.ts index 994fd97..73c5dc9 100644 --- a/src/modules/vehicle-usage/vehicle-usage.entity.ts +++ b/src/modules/vehicle_usage/vehicle_usage.entity.ts @@ -1,6 +1,8 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; -import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; +import { DictItemEntity } from '../system/dict-item/dict-item.entity'; +import { Storage } from '../tools/storage/storage.entity'; @Entity({ name: 'vehicle_usage' }) export class VehicleUsageEntity extends CommonEntity { @@ -8,9 +10,9 @@ export class VehicleUsageEntity extends CommonEntity { @ApiProperty({ description: '年度' }) year: number; - @Column({ name: 'vehicle_license', type: 'int', comment: '外出使用的车辆名称(字典)' }) + @Column({ name: 'vehicle_id', type: 'int', comment: '外出使用的车辆名称(字典)' }) @ApiProperty({ description: '外出使用的车辆名称(字典)' }) - vehicleLicense: number; + vechicleId: number; @Column({ name: 'applicant', @@ -75,4 +77,16 @@ export class VehicleUsageEntity extends CommonEntity { @Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true }) @ApiProperty({ description: '备注' }) remark: string; + + @ManyToOne(() => DictItemEntity) + @JoinColumn({ name: 'vehicle_id' }) + vehicle: DictItemEntity; + + @ManyToMany(() => Storage, storage => storage.vehicleUsage) + @JoinTable({ + name: 'vehicle_usage_storage', + joinColumn: { name: 'vehicle_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; } diff --git a/src/modules/vehicle_usage/vehicle_usage.module.ts b/src/modules/vehicle_usage/vehicle_usage.module.ts new file mode 100644 index 0000000..e446f1e --- /dev/null +++ b/src/modules/vehicle_usage/vehicle_usage.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { VehicleUsageService } from './vehicle_usage.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { VehicleUsageEntity } from './vehicle_usage.entity'; +import { VehicleUsageController } from './vehicle_usage.controller'; +import { StorageModule } from '../tools/storage/storage.module'; + +@Module({ + imports: [TypeOrmModule.forFeature([VehicleUsageEntity]), StorageModule], + providers: [VehicleUsageService], + controllers: [VehicleUsageController] +}) +export class VehicleUsageModule {} diff --git a/src/modules/vehicle_usage/vehicle_usage.service.ts b/src/modules/vehicle_usage/vehicle_usage.service.ts new file mode 100644 index 0000000..2c064b7 --- /dev/null +++ b/src/modules/vehicle_usage/vehicle_usage.service.ts @@ -0,0 +1,140 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { EntityManager, Repository } from 'typeorm'; +import { VehicleUsageEntity } from './vehicle_usage.entity'; +import { Storage } from '../tools/storage/storage.entity'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { paginate } from '~/helper/paginate'; +import { Pagination } from '~/helper/paginate/pagination'; +import { fieldSearch } from '~/shared/database/field-search'; +import { VehicleUsageQueryDto, VehicleUsageDto } from './vehicle_usage.dto'; +import { VehicleUsageUpdateDto } from './vehicle_usage.dto'; + +@Injectable() +export class VehicleUsageService { + constructor( + @InjectRepository(VehicleUsageEntity) + private vehicleUsageRepository: Repository, + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(Storage) + private storageRepository: Repository + ) {} + + /** + * 分页查询所有 + */ + async findAll({ + page, + pageSize, + ...fields + }: VehicleUsageQueryDto): Promise> { + const { ...ext } = fields; + const sqb = this.vehicleUsageRepository + .createQueryBuilder('vehicle_usage') + .leftJoin('vehicle_usage.files', 'files') + .leftJoin('vehicle_usage.vehicle', 'vehicle') + .addSelect(['files.id', 'files.path', 'vehicle.id', 'vehicle.label']) + .where(fieldSearch(ext)) + return paginate(sqb, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: VehicleUsageDto): Promise { + // const { name, companyId } = dto; + // const isExsit = await this.vehicleUsageRepository.findOne({ + // where: { name, company: { id: companyId } } + // }); + // if (isExsit) { + // throw new BusinessException(ErrorEnum.PRODUCT_EXIST); + // } + await this.vehicleUsageRepository.insert(this.vehicleUsageRepository.create(dto)); + } + + /** + * 更新 + */ + async update(id: number, { fileIds, ...data }: Partial): Promise { + await this.entityManager.transaction(async manager => { + await manager.update( + VehicleUsageEntity, + id, + this.vehicleUsageRepository.create({ + ...data + }) + ); + const vehicleUsage = await this.vehicleUsageRepository + .createQueryBuilder('vehicle_usage') + .leftJoinAndSelect('vehicle_usage.files', 'files') + .where('vehicle_usage.id = :id', { id }) + .getOne(); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(VehicleUsageEntity, 'files') + .of(id) + .addAndRemove(fileIds, vehicleUsage.files); + } + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + await this.vehicleUsageRepository.delete(id); + } + + /** + * 获取单个信息 + */ + async info(id: number) { + const info = await this.vehicleUsageRepository + .createQueryBuilder('vehicle_usage') + .leftJoin('vehicle_usage.company', 'company') + .addSelect(['company.name', 'company.id']) + .where({ + id + }) + .andWhere('vehicle_usage.isDelete = 0') + .getOne(); + return info; + } + + /** + * 解除附件关联 + * @param id 记录ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const vehicle_usage = await this.vehicleUsageRepository + .createQueryBuilder('vehicle_usage') + .leftJoinAndSelect('vehicle_usage.files', 'files') + .where('vehicle_usage.id = :id', { id }) + .getOne(); + const linkedFiles = vehicle_usage.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(VehicleUsageEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, vehicle_usage.files); + }); + } +} From f65a4c0b99f4fe0454cf9dc12b3c634db089fe61 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 8 Mar 2024 10:38:28 +0800 Subject: [PATCH 28/64] =?UTF-8?q?fix:=20=E6=89=80=E6=9C=89=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=96=87=E4=BB=B6bug=EF=BC=8C=E8=BD=A6=E8=BE=86?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=A8=A1=E5=9D=97=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/constants/enum/index.ts | 7 +++ src/constants/error-code.constant.ts | 5 +- src/modules/company/company.service.ts | 4 +- src/modules/contract/contract.dto.ts | 11 +++-- src/modules/contract/contract.service.ts | 47 ++++++++++++------- .../in_out/materials_in_out.entity.ts | 2 +- .../in_out/materials_in_out.service.ts | 11 ++--- src/modules/product/product.service.ts | 4 +- src/modules/project/project.service.ts | 4 +- src/modules/tools/storage/storage.entity.ts | 2 +- .../vehicle_usage/vehicle_usage.dto.ts | 15 ++++-- .../vehicle_usage/vehicle_usage.entity.ts | 17 +++++-- .../vehicle_usage/vehicle_usage.service.ts | 36 ++++++++------ 14 files changed, 109 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index af27223..eff84ea 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ pnpm migration:revert 4.执行sql覆盖docker中的数据库 ```bash -docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa_2024-03-05_222748.sql +docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa_2024-03-07_171919.sql ``` 更多细节,请移步至[官方文档](https://typeorm.io/migrations) diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts index 1d18590..7cc6079 100644 --- a/src/constants/enum/index.ts +++ b/src/constants/enum/index.ts @@ -23,3 +23,10 @@ export enum MaterialsInOrOutEnum { export enum ParamConfigEnum { MaterialsInOutPrefix = 'materials_in_out_prefix' } + +// 合同审核状态 +export enum ContractStatusEnum { + Pending = 0, // 待审核 + Approved = 1, // 已通过 + Rejected = 2 // 已拒绝 +} diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 8c187a6..2dd82b4 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -52,5 +52,8 @@ export enum ErrorEnum { STORAGE_REFRENCE_EXISTS = '1405:文件存在关联,无法删除,请先找到该文件关联的业务解除关联。', // Product - PRODUCT_EXIST = '1406:产品已存在' + PRODUCT_EXIST = '1406:产品已存在', + + // Contract + CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号' } diff --git a/src/modules/company/company.service.ts b/src/modules/company/company.service.ts index c0f2465..aca6da4 100644 --- a/src/modules/company/company.service.ts +++ b/src/modules/company/company.service.ts @@ -69,12 +69,12 @@ export class CompanyService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(CompanyEntity, 'files') .of(id) - .addAndRemove(fileIds, company.files); + .add(fileIds); } }); } diff --git a/src/modules/contract/contract.dto.ts b/src/modules/contract/contract.dto.ts index a1b7be7..3ada817 100644 --- a/src/modules/contract/contract.dto.ts +++ b/src/modules/contract/contract.dto.ts @@ -3,6 +3,7 @@ import { IsArray, IsDate, IsDateString, + IsEnum, IsIn, IsInt, IsNumber, @@ -13,6 +14,7 @@ import { } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; +import { ContractStatusEnum } from '~/constants/enum'; export class ContractDto { @ApiProperty({ description: '合同编号' }) @@ -37,15 +39,18 @@ export class ContractDto { partyB: string; @ApiProperty({ description: '签订日期' }) + @IsOptional() @IsDateString() - signingDate: string; + signingDate?: string; @ApiProperty({ description: '交付期限' }) + @IsOptional() @IsDateString() - deliveryDeadline: string; + deliveryDeadline?: string; @ApiProperty({ description: '审核状态(字典)' }) - @IsIn([0, 1, 2]) + @IsOptional() + @IsEnum(ContractStatusEnum) status: number; @ApiProperty({ description: '附件' }) diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index 030ea1a..587ff1b 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; import { ContractEntity } from './contract.entity'; -import { EntityManager, Like, Repository } from 'typeorm'; +import { EntityManager, Like, Not, Repository } from 'typeorm'; import { ContractDto, ContractQueryDto, ContractUpdateDto } from './contract.dto'; import { Pagination } from '~/helper/paginate/pagination'; import { isNumber } from 'lodash'; @@ -45,23 +45,30 @@ export class ContractService { /** * 新增 */ - async create(dto: ContractDto): Promise { - await this.contractRepository.insert(dto); + async create({ contractNumber, ...ext }: ContractDto): Promise { + if (await this.checkIsContractNumberExsit(contractNumber)) { + throw new BusinessException(ErrorEnum.CONTRACT_NUMBER_EXIST); + } + await this.contractRepository.insert( + this.contractRepository.create({ contractNumber, ...ext }) + ); } /** * 更新 */ - async update(id: number, { fileIds, ...data }: Partial): Promise { + async update( + id: number, + { fileIds, contractNumber, ...ext }: Partial + ): Promise { await this.entityManager.transaction(async manager => { + if (await this.checkIsContractNumberExsit(contractNumber, id)) { + throw new BusinessException(ErrorEnum.CONTRACT_NUMBER_EXIST); + } await manager.update(ContractEntity, id, { - ...data + ...ext }); - const contract = await this.contractRepository - .createQueryBuilder('contract') - .leftJoinAndSelect('contract.files', 'files') - .where('contract.id = :id', { id }) - .getOne(); + if (fileIds?.length) { const count = await this.storageRepository .createQueryBuilder('storage') @@ -70,16 +77,24 @@ export class ContractService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 - await manager - .createQueryBuilder() - .relation(ContractEntity, 'files') - .of(id) - .addAndRemove(fileIds, contract.files); + // 附件要批量插入 + await manager.createQueryBuilder().relation(ContractEntity, 'files').of(id).add(fileIds); } }); } + /** + * 是否存在相同编号的合同 + * @param contractNumber 合同编号 + */ + async checkIsContractNumberExsit(contractNumber: string, id?: number): Promise { + return !!(await this.contractRepository.findOne({ + where: { + contractNumber: contractNumber, + id: Not(id) + } + })); + } /** * 删除 */ diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index 833684f..5351eb2 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -120,7 +120,7 @@ export class MaterialsInOutEntity extends CommonEntity { @JoinColumn({ name: 'product_id' }) product: ProductEntity; - @ManyToMany(() => Storage, storage => storage.materialsInOut) + @ManyToMany(() => Storage, storage => storage.materialsInOuts) @JoinTable({ name: 'materials_in_out_storage', joinColumn: { name: 'materials_in_out_id', referencedColumnName: 'id' }, diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 7c685aa..3c82855 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -46,7 +46,7 @@ export class MaterialsInOutService { .leftJoin('materialsInOut.product', 'product') .leftJoin('product.unit', 'unit') .leftJoin('product.company', 'company') - .addSelect(['files.path', 'project.name', 'product.name', 'unit.label', 'company.name']) + .addSelect(['files.id','files.path', 'project.name', 'product.name', 'unit.label', 'company.name']) .where(fieldSearch(ext)) .andWhere('materialsInOut.isDelete = 0') .addOrderBy('materialsInOut.createdAt', 'DESC'); @@ -97,11 +97,6 @@ export class MaterialsInOutService { await manager.update(MaterialsInOutEntity, id, { ...data }); - const materialsInOut = await this.materialsInOutRepository - .createQueryBuilder('materialsInOut') - .leftJoinAndSelect('materialsInOut.files', 'files') - .where('materialsInOut.id = :id', { id }) - .getOne(); if (fileIds?.length) { const count = await this.storageRepository .createQueryBuilder('storage') @@ -110,12 +105,12 @@ export class MaterialsInOutService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(MaterialsInOutEntity, 'files') .of(id) - .addAndRemove(fileIds, materialsInOut.files); + .add(fileIds); } }); } diff --git a/src/modules/product/product.service.ts b/src/modules/product/product.service.ts index 5428a3e..88cce60 100644 --- a/src/modules/product/product.service.ts +++ b/src/modules/product/product.service.ts @@ -90,12 +90,12 @@ export class ProductService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(ProductEntity, 'files') .of(id) - .addAndRemove(fileIds, product.files); + .add(fileIds); } }); } diff --git a/src/modules/project/project.service.ts b/src/modules/project/project.service.ts index 627299c..fcd4dd0 100644 --- a/src/modules/project/project.service.ts +++ b/src/modules/project/project.service.ts @@ -69,12 +69,12 @@ export class ProjectService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(ProjectEntity, 'files') .of(id) - .addAndRemove(fileIds, project.files); + .add(fileIds); } }); } diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index cc58f42..588b033 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -55,7 +55,7 @@ export class Storage extends CommonEntity { @ApiHideProperty() @ManyToMany(() => MaterialsInOutEntity, materialsInOut => materialsInOut.files) - materialsInOut: Relation; + materialsInOuts: Relation; @ApiHideProperty() @ManyToMany(() => ProductEntity, product => product.files) diff --git a/src/modules/vehicle_usage/vehicle_usage.dto.ts b/src/modules/vehicle_usage/vehicle_usage.dto.ts index 15100a1..e210ffb 100644 --- a/src/modules/vehicle_usage/vehicle_usage.dto.ts +++ b/src/modules/vehicle_usage/vehicle_usage.dto.ts @@ -10,17 +10,22 @@ export class VehicleUsageDto { @ApiProperty({ description: '外出使用的车辆名称(字典)' }) @IsNumber() - vechicleId: number; + vehicleId: number; @ApiProperty({ description: '申请人' }) @IsString() applicant: string; - @ApiProperty({ description: '出行司机', nullable: true }) + @ApiProperty({ description: '出行司机' }) @IsOptional() @IsString() driver: string; + @ApiProperty({ description: '随行人员' }) + @IsOptional() + @IsString() + partner?: string; + @ApiProperty({ description: '当前车辆里程数(KM)' }) @IsOptional() @IsNumber() @@ -69,4 +74,8 @@ export class VehicleUsageUpdateDto extends PartialType(VehicleUsageDto) { export class VehicleUsageQueryDto extends IntersectionType( PagerDto, PartialType(VehicleUsageDto) -) {} +) { + @ApiProperty({ description: '车辆名称或者车牌号' }) + @IsOptional() + vehicle?: string; +} diff --git a/src/modules/vehicle_usage/vehicle_usage.entity.ts b/src/modules/vehicle_usage/vehicle_usage.entity.ts index 73c5dc9..6821433 100644 --- a/src/modules/vehicle_usage/vehicle_usage.entity.ts +++ b/src/modules/vehicle_usage/vehicle_usage.entity.ts @@ -12,7 +12,7 @@ export class VehicleUsageEntity extends CommonEntity { @Column({ name: 'vehicle_id', type: 'int', comment: '外出使用的车辆名称(字典)' }) @ApiProperty({ description: '外出使用的车辆名称(字典)' }) - vechicleId: number; + vehicleId: number; @Column({ name: 'applicant', @@ -27,11 +27,22 @@ export class VehicleUsageEntity extends CommonEntity { name: 'driver', type: 'varchar', length: 50, - comment: '出行司机' + comment: '出行司机', + nullable: true }) - @ApiProperty({ description: '出行司机', nullable: true }) + @ApiProperty({ description: '出行司机' }) driver: string; + @Column({ + name: 'partner', + type: 'varchar', + length: 50, + comment: '随行人员', + nullable: true + }) + @ApiProperty({ description: '随行人员' }) + partner: string; + @Column({ name: 'current_mileage', type: 'int', comment: '当前车辆里程数(KM)', nullable: true }) @ApiProperty({ description: '当前车辆里程数(KM)' }) currentMileage: number; diff --git a/src/modules/vehicle_usage/vehicle_usage.service.ts b/src/modules/vehicle_usage/vehicle_usage.service.ts index 2c064b7..b1516d2 100644 --- a/src/modules/vehicle_usage/vehicle_usage.service.ts +++ b/src/modules/vehicle_usage/vehicle_usage.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; -import { EntityManager, Repository } from 'typeorm'; +import { EntityManager, Repository, SelectQueryBuilder } from 'typeorm'; import { VehicleUsageEntity } from './vehicle_usage.entity'; import { Storage } from '../tools/storage/storage.entity'; import { ErrorEnum } from '~/constants/error-code.constant'; @@ -27,15 +27,14 @@ export class VehicleUsageService { async findAll({ page, pageSize, + ...fields }: VehicleUsageQueryDto): Promise> { - const { ...ext } = fields; - const sqb = this.vehicleUsageRepository - .createQueryBuilder('vehicle_usage') - .leftJoin('vehicle_usage.files', 'files') - .leftJoin('vehicle_usage.vehicle', 'vehicle') - .addSelect(['files.id', 'files.path', 'vehicle.id', 'vehicle.label']) - .where(fieldSearch(ext)) + const { vehicle, ...ext } = fields; + const sqb = this.buildLeftJoinRelations().where(fieldSearch(ext)); + if (vehicle) { + sqb.andWhere('vehicle.label like :vehicleName', { vehicleName: `%${vehicle}%` }); + } return paginate(sqb, { page, pageSize @@ -81,12 +80,12 @@ export class VehicleUsageService { if (count !== fileIds?.length) { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } - // 附件要批量更新 + // 附件要批量插入 await manager .createQueryBuilder() .relation(VehicleUsageEntity, 'files') .of(id) - .addAndRemove(fileIds, vehicleUsage.files); + .add(fileIds); } }); } @@ -102,14 +101,10 @@ export class VehicleUsageService { * 获取单个信息 */ async info(id: number) { - const info = await this.vehicleUsageRepository - .createQueryBuilder('vehicle_usage') - .leftJoin('vehicle_usage.company', 'company') - .addSelect(['company.name', 'company.id']) + const info = this.buildLeftJoinRelations() .where({ id }) - .andWhere('vehicle_usage.isDelete = 0') .getOne(); return info; } @@ -137,4 +132,15 @@ export class VehicleUsageService { .addAndRemove(linkedFiles, vehicle_usage.files); }); } + + /** + * 封装和查询关联关系 + */ + buildLeftJoinRelations() { + return this.vehicleUsageRepository + .createQueryBuilder('vehicle_usage') + .leftJoin('vehicle_usage.files', 'files') + .leftJoin('vehicle_usage.vehicle', 'vehicle') + .addSelect(['files.id', 'files.path', 'vehicle.id', 'vehicle.label']); + } } From 46a41328771d3b1b693d911bc22cef3588a54ef0 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 8 Mar 2024 13:19:33 +0800 Subject: [PATCH 29/64] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E6=B7=BB=E5=8A=A0=E4=B8=9A=E5=8A=A1=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=92=8C=E4=B8=9A=E5=8A=A1=E8=AE=B0=E5=BD=95id=E5=86=97?= =?UTF-8?q?=E4=BD=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/adapters/fastify.adapter.ts | 1 + src/main.ts | 1 - src/modules/contract/contract.service.ts | 5 +++-- .../in_out/materials_in_out.dto.ts | 6 ++--- .../in_out/materials_in_out.service.ts | 17 ++++++++++---- src/modules/tools/storage/storage.dto.ts | 12 +++++++++- src/modules/tools/storage/storage.entity.ts | 9 ++++++++ src/modules/tools/storage/storage.service.ts | 4 +++- src/modules/tools/upload/upload.controller.ts | 22 +++++++++---------- src/modules/tools/upload/upload.dto.ts | 2 +- src/modules/tools/upload/upload.service.ts | 11 ++++++++-- 11 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/common/adapters/fastify.adapter.ts b/src/common/adapters/fastify.adapter.ts index c36f024..65db5c9 100644 --- a/src/common/adapters/fastify.adapter.ts +++ b/src/common/adapters/fastify.adapter.ts @@ -10,6 +10,7 @@ const app: FastifyAdapter = new FastifyAdapter({ export { app as fastifyApp }; app.register(FastifyMultipart, { + attachFieldsToBody:true, limits: { fields: 10, // Max number of non-file fields fileSize: 1024 * 1024 * 6, // limit size 6M diff --git a/src/main.ts b/src/main.ts index 3c6498e..51a2048 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,7 +33,6 @@ async function bootstrap() { // class-validator 的 DTO 类中注入 nest 容器的依赖 (用于自定义验证器) useContainer(app.select(AppModule), { fallbackOnErrors: true }); - app.enableCors({ origin: '*', credentials: true }); app.setGlobalPrefix(globalPrefix); app.useStaticAssets({ root: path.join(__dirname, '..', 'public') }); diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index 587ff1b..ae961dd 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -62,11 +62,12 @@ export class ContractService { { fileIds, contractNumber, ...ext }: Partial ): Promise { await this.entityManager.transaction(async manager => { - if (await this.checkIsContractNumberExsit(contractNumber, id)) { + if (contractNumber && (await this.checkIsContractNumberExsit(contractNumber, id))) { throw new BusinessException(ErrorEnum.CONTRACT_NUMBER_EXIST); } await manager.update(ContractEntity, id, { - ...ext + ...ext, + contractNumber }); if (fileIds?.length) { diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index ec41f22..0d0fec2 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -129,9 +129,9 @@ export class MaterialsInOutQueryDto extends PagerDto { remark?: string; @IsOptional() - @IsString() - @ApiProperty({ description: '项目' }) - project?: string; + @IsNumber() + @ApiProperty({ description: '项目Id' }) + projectId?: number; @IsOptional() @IsBoolean() diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 3c82855..3d2dd77 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -35,7 +35,7 @@ export class MaterialsInOutService { page, pageSize, product: productName, - project: projectName, + projectId, isCreateOut, ...ext }: MaterialsInOutQueryDto): Promise> { @@ -46,15 +46,24 @@ export class MaterialsInOutService { .leftJoin('materialsInOut.product', 'product') .leftJoin('product.unit', 'unit') .leftJoin('product.company', 'company') - .addSelect(['files.id','files.path', 'project.name', 'product.name', 'unit.label', 'company.name']) + .addSelect([ + 'files.id', + 'files.path', + 'project.name', + 'product.name', + 'unit.label', + 'company.name' + ]) .where(fieldSearch(ext)) .andWhere('materialsInOut.isDelete = 0') .addOrderBy('materialsInOut.createdAt', 'DESC'); + if (productName) { sqb.andWhere('product.name like :productName', { productName: `%${productName}%` }); } - if (projectName) { - sqb.andWhere('project.name like :projectName', { projectName: `%${projectName}%` }); + + if (projectId) { + sqb.andWhere('project.id = :projectId', { projectId }); } if (isCreateOut) { diff --git a/src/modules/tools/storage/storage.dto.ts b/src/modules/tools/storage/storage.dto.ts index 1dfd010..2ef2924 100644 --- a/src/modules/tools/storage/storage.dto.ts +++ b/src/modules/tools/storage/storage.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { ArrayNotEmpty, IsArray, IsOptional, IsString } from 'class-validator'; +import { ArrayNotEmpty, IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; @@ -34,6 +34,16 @@ export class StoragePageDto extends PagerDto { @IsOptional() username: string; + @ApiProperty({ description: '文件所属模块' }) + @IsString() + @IsOptional() + businessModule: string; + + @ApiProperty({ description: '文件上传的业务记录ID' }) + @IsNumber() + @IsOptional() + bussinessRecordId: string; + @ApiProperty({ description: '附件' }) @IsOptional() @Transform( diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index 588b033..6929c0e 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -45,6 +45,15 @@ export class Storage extends CommonEntity { @ApiProperty({ description: '用户ID' }) userId: number; + @Column({ nullable: true, name: 'bussiness_module' }) + @ApiProperty({ description: '文件上传的业务模块' }) + bussinessModule: string; + + @Column({ nullable: true, name: 'bussiness_record_id' }) + @ApiProperty({ description: '文件上传的业务记录ID' }) + bussinessRecordId: number; + + @ApiHideProperty() @ManyToMany(() => ContractEntity, contract => contract.files) contracts: Relation; diff --git a/src/modules/tools/storage/storage.service.ts b/src/modules/tools/storage/storage.service.ts index 414af8f..a24c122 100644 --- a/src/modules/tools/storage/storage.service.ts +++ b/src/modules/tools/storage/storage.service.ts @@ -93,7 +93,9 @@ export class StorageService { type: e.storage_type, size: e.storage_size, createdAt: e.storage_created_at, - username: e.user_username + username: e.user_username, + bussinessRecordId:e.storage_bussiness_record_id, + bussinessModule:e.storage_bussiness_module }; }); } diff --git a/src/modules/tools/upload/upload.controller.ts b/src/modules/tools/upload/upload.controller.ts index 31756ab..c01c713 100644 --- a/src/modules/tools/upload/upload.controller.ts +++ b/src/modules/tools/upload/upload.controller.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Controller, Post, Req } from '@nestjs/common'; +import { BadRequestException, Body, Controller, Post, Req, UseInterceptors } from '@nestjs/common'; import { ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger'; import { FastifyRequest } from 'fastify'; @@ -27,18 +27,18 @@ export class UploadController { @ApiBody({ type: FileUploadDto }) - async upload(@Req() req: FastifyRequest, @AuthUser() user: IAuthUser) { + async upload(@Req() req: FastifyRequest, @AuthUser() user: IAuthUser, @Body() body: any) { if (!req.isMultipart()) throw new BadRequestException('Request is not multipart'); - - const file = await req.file(); - - // https://github.com/fastify/fastify-multipart - // const parts = req.files() - // for await (const part of parts) - // console.log(part.file) - + const { file } = body; + const bussinessModule = body.bussinessModule?.value; + const bussinessRecordId = Number(body.bussinessRecordId?.value) || null; try { - const savedFile = await this.uploadService.saveFile(file, user.uid); + const savedFile = await this.uploadService.saveFile( + file, + user.uid, + bussinessModule, + bussinessRecordId + ); return { filename: savedFile diff --git a/src/modules/tools/upload/upload.dto.ts b/src/modules/tools/upload/upload.dto.ts index dc28e7c..e25dcdd 100644 --- a/src/modules/tools/upload/upload.dto.ts +++ b/src/modules/tools/upload/upload.dto.ts @@ -1,7 +1,7 @@ import { MultipartFile } from '@fastify/multipart'; import { ApiProperty } from '@nestjs/swagger'; -import { IsDefined } from 'class-validator'; +import { IsDefined, IsNumber, IsOptional, IsString } from 'class-validator'; import { IsFile } from './file.constraint'; diff --git a/src/modules/tools/upload/upload.service.ts b/src/modules/tools/upload/upload.service.ts index 05b2a31..8186817 100644 --- a/src/modules/tools/upload/upload.service.ts +++ b/src/modules/tools/upload/upload.service.ts @@ -25,7 +25,12 @@ export class UploadService { /** * 保存文件上传记录 */ - async saveFile(file: MultipartFile, userId: number): Promise<{ id: number; path: string }> { + async saveFile( + file: MultipartFile, + userId: number, + bussinessModule?: string, + bussinessRecordId?: number + ): Promise<{ id: number; path: string }> { if (isNil(file)) throw new NotFoundException('Have not any file to upload!'); const fileName = file.filename; @@ -44,7 +49,9 @@ export class UploadService { path, type, size, - userId + userId, + bussinessModule, + bussinessRecordId }); return { path, id: storage.id }; From 3610d8fdc6f7913f59f17ae66d1a863aa733294e Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 8 Mar 2024 16:24:29 +0800 Subject: [PATCH 30/64] =?UTF-8?q?feat:=20minio=E5=8A=9F=E8=83=BD=E5=BC=80?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 15 +- .env.development | 1 - .env.production | 3 +- minio.js | 14 + package.json | 1 + pnpm-lock.yaml | 169 +- src/config/oss.config.ts | 4 +- .../netdisk/manager/manage-qiniu.service.ts | 836 +++++++++ src/modules/netdisk/manager/manage.class.ts | 4 +- .../netdisk/manager/manage.controller.ts | 114 +- src/modules/netdisk/manager/manage.service.ts | 1524 ++++++++--------- src/modules/netdisk/minio/minio.service.ts | 8 + src/modules/netdisk/netdisk.module.ts | 5 +- 13 files changed, 1853 insertions(+), 845 deletions(-) create mode 100644 minio.js create mode 100644 src/modules/netdisk/manager/manage-qiniu.service.ts create mode 100644 src/modules/netdisk/minio/minio.service.ts diff --git a/.env b/.env index 6a2b67a..43a592f 100644 --- a/.env +++ b/.env @@ -11,13 +11,17 @@ LOGGER_MAX_FILES = 31 TZ = Asia/Shanghai # OSS(minio) -OSS_ACCESSKEY=xxx -OSS_SECRETKEY=xxx -OSS_DOMAIN=https://cdn.louis.site -OSS_BUCKET=hxoa +OSS_ACCESSKEY=8Zttvx4ZbF2ikFRb +OSS_SECRETKEY=SCgOJEJXM5vMNQL4fF8opXA1wmpACRfw +OSS_PORT=8021 +OSS_DOMAIN=144.123.43.138 +OSS_DOMAIN_USE_SSL=false +OSS_BUCKET=tes1 OSS_ZONE=Zone_z2 # Zone_as0 | Zone_na0 | Zone_z0 | Zone_z1 | Zone_z2 OSS_ACCESS_TYPE=public # or private + + DB_HOST = host.docker.internal DB_PORT = 13307 DB_DATABASE = hxoa @@ -29,4 +33,5 @@ DB_LOGGING = ["error"] REDIS_PORT = 6379 REDIS_HOST = host.docker.internal REDIS_PASSWORD = 123456 -REDIS_DB = 0 \ No newline at end of file +REDIS_DB = 0 + diff --git a/.env.development b/.env.development index c330ac4..89c8c66 100644 --- a/.env.development +++ b/.env.development @@ -33,4 +33,3 @@ SMTP_PORT = 465 SMTP_USER = nest_admin@163.com SMTP_PASS = VIPLLOIPMETTROYU - \ No newline at end of file diff --git a/.env.production b/.env.production index 0a4e6bd..8dc6113 100644 --- a/.env.production +++ b/.env.production @@ -34,4 +34,5 @@ SMTP_USER = nest_admin@163.com SMTP_PASS = VIPLLOIPMETTROYU # 是否为演示模式(在演示模式下,会拦截除 GET 方法以外的所有请求) -IS_DEMO = false \ No newline at end of file +IS_DEMO = false + diff --git a/minio.js b/minio.js new file mode 100644 index 0000000..8d0410f --- /dev/null +++ b/minio.js @@ -0,0 +1,14 @@ +const Minio = require('minio'); + +const minioClient = new Minio.Client({ + endPoint: '144.123.43.138', + port: 8021, + useSSL: false, + accessKey: '8Zttvx4ZbF2ikFRb', + secretKey: 'SCgOJEJXM5vMNQL4fF8opXA1wmpACRfw' +}); + +minioClient.listBuckets((err, buckets) => { + if (err) return console.log(err); + console.log('Buckets:', buckets); +}); diff --git a/package.json b/package.json index 9cb7ada..7c66bd9 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "helmet": "^7.1.0", "ioredis": "^5.3.2", "lodash": "^4.17.21", + "minio": "^7.1.3", "mysql2": "^3.9.1", "nanoid": "^3.3.7", "nodemailer": "^6.9.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9528f32..71071f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,6 +131,9 @@ dependencies: lodash: specifier: ^4.17.21 version: 4.17.21 + minio: + specifier: ^7.1.3 + version: 7.1.3 mysql2: specifier: ^3.9.1 version: 3.9.1 @@ -3663,6 +3666,12 @@ packages: resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} dev: true + /@zxing/text-encoding@0.9.0: + resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} + requiresBuild: true + dev: false + optional: true + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -4061,6 +4070,13 @@ packages: engines: {node: '>=8.0.0'} dev: false + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 + dev: false + /avvio@8.3.0: resolution: {integrity: sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==} dependencies: @@ -4348,6 +4364,10 @@ packages: base64-js: 1.5.1 dev: true + /browser-or-node@2.1.1: + resolution: {integrity: sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==} + dev: false + /browser-resolve@1.11.3: resolution: {integrity: sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==} dependencies: @@ -5409,6 +5429,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + dev: false + /dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true @@ -6340,6 +6365,13 @@ packages: resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} dev: false + /fast-xml-parser@4.3.5: + resolution: {integrity: sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==} + hasBin: true + dependencies: + strnum: 1.0.5 + dev: false + /fastify-plugin@4.5.1: resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} dev: false @@ -6438,6 +6470,11 @@ packages: dependencies: to-regex-range: 5.0.1 + /filter-obj@1.1.0: + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} + dev: false + /finalhandler@1.1.2: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} engines: {node: '>= 0.8'} @@ -6559,6 +6596,12 @@ packages: optional: true dev: false + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: false + /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -7359,13 +7402,17 @@ packages: engines: {node: '>= 0.10'} dev: false + /ipaddr.js@2.1.0: + resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==} + engines: {node: '>= 10'} + dev: false + /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -7380,6 +7427,11 @@ packages: dependencies: binary-extensions: 2.2.0 + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: false + /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: @@ -7428,6 +7480,13 @@ packages: engines: {node: '>=6'} dev: true + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: false + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -7490,6 +7549,13 @@ packages: text-extensions: 1.9.0 dev: true + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.14 + dev: false + /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -8104,6 +8170,10 @@ packages: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true + /json-stream@1.0.0: + resolution: {integrity: sha512-H/ZGY0nIAg3QcOwE1QN/rK/Fa7gJn7Ii5obwp6zyPO4xiPNwpIMjqy2gwjBEGqzkF/vSWEIBQCBuN19hYiL6Qg==} + dev: false + /json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: true @@ -8797,6 +8867,26 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + /minio@7.1.3: + resolution: {integrity: sha512-xPrLjWkTT5E7H7VnzOjF//xBp9I40jYB4aWhb2xTFopXXfw+Wo82DDWngdUju7Doy3Wk7R8C4LAgwhLHHnf0wA==} + engines: {node: ^16 || ^18 || >=20} + dependencies: + async: 3.2.5 + block-stream2: 2.1.0 + browser-or-node: 2.1.1 + buffer-crc32: 0.2.13 + fast-xml-parser: 4.3.5 + ipaddr.js: 2.1.0 + json-stream: 1.0.0 + lodash: 4.17.21 + mime-types: 2.1.35 + query-string: 7.1.3 + through2: 4.0.2 + web-encoding: 1.1.5 + xml: 1.0.1 + xml2js: 0.5.0 + dev: false + /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -10056,6 +10146,11 @@ packages: resolution: {integrity: sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==} dev: true + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + dev: false + /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -10328,6 +10423,16 @@ packages: dependencies: side-channel: 1.0.5 + /query-string@7.1.3: + resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} + engines: {node: '>=6'} + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + dev: false + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -10723,7 +10828,6 @@ packages: /sax@1.3.0: resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} - dev: true /saxes@5.0.1: resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} @@ -11060,6 +11164,11 @@ packages: resolution: {integrity: sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==} dev: true + /split-on-first@1.1.0: + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} + dev: false + /split2@3.2.2: resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} dependencies: @@ -11192,6 +11301,11 @@ packages: engines: {node: '>=4.0.0'} dev: false + /strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + dev: false + /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -11302,6 +11416,10 @@ packages: engines: {node: '>=8'} dev: true + /strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + dev: false + /superagent@8.1.2: resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} engines: {node: '>=6.4.0 <13 || >=14'} @@ -11513,7 +11631,6 @@ packages: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} dependencies: readable-stream: 3.6.2 - dev: true /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -12026,6 +12143,16 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.13 + which-typed-array: 1.1.14 + dev: false + /utility@1.18.0: resolution: {integrity: sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==} engines: {node: '>= 0.12.0'} @@ -12118,6 +12245,14 @@ packages: defaults: 1.0.4 dev: true + /web-encoding@1.1.5: + resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} + dependencies: + util: 0.12.5 + optionalDependencies: + '@zxing/text-encoding': 0.9.0 + dev: false + /web-resource-inliner@6.0.1: resolution: {integrity: sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==} engines: {node: '>=10.0.0'} @@ -12205,6 +12340,17 @@ packages: tr46: 0.0.3 webidl-conversions: 3.0.1 + /which-typed-array@1.1.14: + resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + dev: false + /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -12353,6 +12499,23 @@ packages: utf-8-validate: optional: true + /xml2js@0.5.0: + resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + engines: {node: '>=4.0.0'} + dependencies: + sax: 1.3.0 + xmlbuilder: 11.0.1 + dev: false + + /xml@1.0.1: + resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + dev: false + + /xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + dev: false + /xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} dev: false diff --git a/src/config/oss.config.ts b/src/config/oss.config.ts index 10ffda8..42f46c0 100644 --- a/src/config/oss.config.ts +++ b/src/config/oss.config.ts @@ -1,7 +1,7 @@ import { ConfigType, registerAs } from '@nestjs/config'; import * as qiniu from 'qiniu'; -import { env } from '~/global/env'; +import { env, envBoolean, envNumber } from '~/global/env'; function parseZone(zone: string) { switch (zone) { @@ -24,6 +24,8 @@ export const OssConfig = registerAs(ossRegToken, () => ({ accessKey: env('OSS_ACCESSKEY'), secretKey: env('OSS_SECRETKEY'), domain: env('OSS_DOMAIN'), + port: envNumber('OSS_PORT'), + useSSL: envBoolean('OSS_USE_SSL'), bucket: env('OSS_BUCKET'), zone: parseZone(env('OSS_ZONE') || 'Zone_z2'), access: (env('OSS_ACCESS_TYPE') as any) || 'public' diff --git a/src/modules/netdisk/manager/manage-qiniu.service.ts b/src/modules/netdisk/manager/manage-qiniu.service.ts new file mode 100644 index 0000000..3554b7b --- /dev/null +++ b/src/modules/netdisk/manager/manage-qiniu.service.ts @@ -0,0 +1,836 @@ +import { basename, extname } from 'node:path'; + +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { isEmpty } from 'lodash'; +import * as qiniu from 'qiniu'; +import { auth, conf, rs } from 'qiniu'; + +import { ConfigKeyPaths } from '~/config'; +import { + NETDISK_COPY_SUFFIX, + NETDISK_DELIMITER, + NETDISK_HANDLE_MAX_ITEM, + NETDISK_LIMIT +} from '~/constants/oss.constant'; + +import { AccountInfo } from '~/modules/user/user.model'; +import { UserService } from '~/modules/user/user.service'; + +import { generateRandomValue } from '~/utils'; + +import { SFileInfo, SFileInfoDetail, SFileList } from './manage.class'; +import { FileOpItem } from './manage.dto'; + +@Injectable() +export class QiNiuNetDiskManageService { + private config: conf.ConfigOptions; + private mac: auth.digest.Mac; + private bucketManager: rs.BucketManager; + + private get qiniuConfig() { + return this.configService.get('oss', { infer: true }); + } + + constructor( + private configService: ConfigService, + private userService: UserService + ) { + this.mac = new qiniu.auth.digest.Mac(this.qiniuConfig.accessKey, this.qiniuConfig.secretKey); + this.config = new qiniu.conf.Config({ + zone: this.qiniuConfig.zone + }); + // bucket manager + this.bucketManager = new qiniu.rs.BucketManager(this.mac, this.config); + } + + /** + * 获取文件列表 + * @param prefix 当前文件夹路径,搜索模式下会被忽略 + * @param marker 下一页标识 + * @returns iFileListResult + */ + async getFileList(prefix = '', marker = '', skey = ''): Promise { + // 是否需要搜索 + const searching = !isEmpty(skey); + return new Promise((resolve, reject) => { + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: searching ? '' : prefix, + limit: NETDISK_LIMIT, + delimiter: searching ? '' : NETDISK_DELIMITER, + marker + }, + (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + // 如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候, + // 指定options里面的marker为这个值 + const fileList: SFileInfo[] = []; + // 处理目录,但只有非搜索模式下可用 + if (!searching && !isEmpty(respBody.commonPrefixes)) { + // dir + for (const dirPath of respBody.commonPrefixes) { + const name = (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''); + if (isEmpty(skey) || name.includes(skey)) { + fileList.push({ + name: (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''), + type: 'dir', + id: generateRandomValue(10) + }); + } + } + } + // handle items + if (!isEmpty(respBody.items)) { + // file + for (const item of respBody.items) { + // 搜索模式下处理 + if (searching) { + const pathList: string[] = item.key.split(NETDISK_DELIMITER); + // dir is empty stirng, file is key string + const name = pathList.pop(); + if ( + item.key.endsWith(NETDISK_DELIMITER) && + pathList[pathList.length - 1].includes(skey) + ) { + // 结果是目录 + const ditName = pathList.pop(); + fileList.push({ + id: generateRandomValue(10), + name: ditName, + type: 'dir', + belongTo: pathList.join(NETDISK_DELIMITER) + }); + } else if (name.includes(skey)) { + // 文件 + fileList.push({ + id: generateRandomValue(10), + name, + type: 'file', + fsize: item.fsize, + mimeType: item.mimeType, + putTime: new Date(Number.parseInt(item.putTime) / 10000), + belongTo: pathList.join(NETDISK_DELIMITER) + }); + } + } else { + // 正常获取列表 + const fileKey = item.key.replace(prefix, '') as string; + if (!isEmpty(fileKey)) { + fileList.push({ + id: generateRandomValue(10), + name: fileKey, + type: 'file', + fsize: item.fsize, + mimeType: item.mimeType, + putTime: new Date(Number.parseInt(item.putTime) / 10000) + }); + } + } + } + } + resolve({ + list: fileList, + marker: respBody.marker || null + }); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + } + ); + }); + } + + /** + * 获取文件信息 + */ + async getFileInfo(name: string, path: string): Promise { + return new Promise((resolve, reject) => { + this.bucketManager.stat( + this.qiniuConfig.bucket, + `${path}${name}`, + (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + const detailInfo: SFileInfoDetail = { + fsize: respBody.fsize, + hash: respBody.hash, + md5: respBody.md5, + mimeType: respBody.mimeType.split('/x-qn-meta')[0], + putTime: new Date(Number.parseInt(respBody.putTime) / 10000), + type: respBody.type, + uploader: '', + mark: respBody?.['x-qn-meta']?.['!mark'] ?? '' + }; + if (!respBody.endUser) { + resolve(detailInfo); + } else { + this.userService + .getAccountInfo(Number.parseInt(respBody.endUser)) + .then((user: AccountInfo) => { + if (isEmpty(user)) { + resolve(detailInfo); + } else { + detailInfo.uploader = user.username; + resolve(detailInfo); + } + }); + } + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + } + ); + }); + } + + /** + * 修改文件MimeType + */ + async changeFileHeaders( + name: string, + path: string, + headers: { [k: string]: string } + ): Promise { + return new Promise((resolve, reject) => { + this.bucketManager.changeHeaders( + this.qiniuConfig.bucket, + `${path}${name}`, + headers, + (err, _, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + resolve(); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + } + ); + }); + } + + /** + * 创建文件夹 + * @returns true创建成功 + */ + async createDir(dirName: string): Promise { + const safeDirName = dirName.endsWith('/') ? dirName : `${dirName}/`; + return new Promise((resolve, reject) => { + // 上传一个空文件以用于显示文件夹效果 + const formUploader = new qiniu.form_up.FormUploader(this.config); + const putExtra = new qiniu.form_up.PutExtra(); + formUploader.put( + this.createUploadToken(''), + safeDirName, + ' ', + putExtra, + (respErr, respBody, respInfo) => { + if (respErr) { + reject(respErr); + return; + } + if (respInfo.statusCode === 200) { + resolve(); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + } + ); + }); + } + + /** + * 检查文件是否存在,同可检查目录 + */ + async checkFileExist(filePath: string): Promise { + return new Promise((resolve, reject) => { + // fix path end must a / + + // 检测文件夹是否存在 + this.bucketManager.stat(this.qiniuConfig.bucket, filePath, (respErr, respBody, respInfo) => { + if (respErr) { + reject(respErr); + return; + } + if (respInfo.statusCode === 200) { + // 文件夹存在 + resolve(true); + } else if (respInfo.statusCode === 612) { + // 文件夹不存在 + resolve(false); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + }); + }); + } + + /** + * 创建Upload Token, 默认过期时间一小时 + * @returns upload token + */ + createUploadToken(endUser: string): string { + const policy = new qiniu.rs.PutPolicy({ + scope: this.qiniuConfig.bucket, + insertOnly: 1, + fsizeLimit: 1024 ** 2 * 10, + endUser + }); + const uploadToken = policy.uploadToken(this.mac); + return uploadToken; + } + + /** + * 重命名文件 + * @param dir 文件路径 + * @param name 文件名称 + */ + async renameFile(dir: string, name: string, toName: string): Promise { + const fileName = `${dir}${name}`; + const toFileName = `${dir}${toName}`; + const op = { + force: true + }; + return new Promise((resolve, reject) => { + this.bucketManager.move( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op, + (err, respBody, respInfo) => { + if (err) { + reject(err); + } else { + if (respInfo.statusCode === 200) { + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); + } + } + } + ); + }); + } + + /** + * 移动文件 + */ + async moveFile(dir: string, toDir: string, name: string): Promise { + const fileName = `${dir}${name}`; + const toFileName = `${toDir}${name}`; + const op = { + force: true + }; + return new Promise((resolve, reject) => { + this.bucketManager.move( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op, + (err, respBody, respInfo) => { + if (err) { + reject(err); + } else { + if (respInfo.statusCode === 200) { + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); + } + } + } + ); + }); + } + + /** + * 复制文件 + */ + async copyFile(dir: string, toDir: string, name: string): Promise { + const fileName = `${dir}${name}`; + // 拼接文件名 + const ext = extname(name); + const bn = basename(name, ext); + const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; + const op = { + force: true + }; + return new Promise((resolve, reject) => { + this.bucketManager.copy( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op, + (err, respBody, respInfo) => { + if (err) { + reject(err); + } else { + if (respInfo.statusCode === 200) { + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); + } + } + } + ); + }); + } + + /** + * 重命名文件夹 + */ + async renameDir(path: string, name: string, toName: string): Promise { + const dirName = `${path}${name}`; + const toDirName = `${path}${toName}`; + let hasFile = true; + let marker = ''; + const op = { + force: true + }; + const bucketName = this.qiniuConfig.bucket; + while (hasFile) { + await new Promise((resolve, reject) => { + // 列举当前目录下的所有文件 + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: dirName, + limit: NETDISK_HANDLE_MAX_ITEM, + marker + }, + (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + const moveOperations = respBody.items.map(item => { + const { key } = item; + const destKey = key.replace(dirName, toDirName); + return qiniu.rs.moveOp(bucketName, key, bucketName, destKey, op); + }); + this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2); + return; + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) hasFile = false; + else marker = respBody.marker; + + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` + ) + ); + } + }); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); + } + } + ); + }); + } + } + + /** + * 获取七牛下载的文件url链接 + * @param key 文件路径 + * @returns 连接 + */ + getDownloadLink(key: string): string { + if (this.qiniuConfig.access === 'public') { + return this.bucketManager.publicDownloadUrl(this.qiniuConfig.domain, key); + } else if (this.qiniuConfig.access === 'private') { + return this.bucketManager.privateDownloadUrl( + this.qiniuConfig.domain, + key, + Date.now() / 1000 + 36000 + ); + } + throw new Error('qiniu config access type not support'); + } + + /** + * 删除文件 + * @param dir 删除的文件夹目录 + * @param name 文件名 + */ + async deleteFile(dir: string, name: string): Promise { + return new Promise((resolve, reject) => { + this.bucketManager.delete( + this.qiniuConfig.bucket, + `${dir}${name}`, + (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + resolve(); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + } + ); + }); + } + + /** + * 删除文件夹 + * @param dir 文件夹所在的上级目录 + * @param name 文件目录名称 + */ + async deleteMultiFileOrDir(fileList: FileOpItem[], dir: string): Promise { + const files = fileList.filter(item => item.type === 'file'); + if (files.length > 0) { + // 批处理文件 + const copyOperations = files.map(item => { + const fileName = `${dir}${item.name}`; + return qiniu.rs.deleteOp(this.qiniuConfig.bucket, fileName); + }); + await new Promise((resolve, reject) => { + this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + resolve(); + } else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + }); + }); + } + // 处理文件夹 + const dirs = fileList.filter(item => item.type === 'dir'); + if (dirs.length > 0) { + // 处理文件夹的复制 + for (let i = 0; i < dirs.length; i++) { + const dirName = `${dir}${dirs[i].name}/`; + let hasFile = true; + let marker = ''; + while (hasFile) { + await new Promise((resolve, reject) => { + // 列举当前目录下的所有文件 + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: dirName, + limit: NETDISK_HANDLE_MAX_ITEM, + marker + }, + (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + const moveOperations = respBody.items.map(item => { + const { key } = item; + return qiniu.rs.deleteOp(this.qiniuConfig.bucket, key); + }); + this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2); + return; + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) hasFile = false; + else marker = respBody.marker; + + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` + ) + ); + } + }); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); + } + } + ); + }); + } + } + } + } + + /** + * 复制文件,含文件夹 + */ + async copyMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { + const files = fileList.filter(item => item.type === 'file'); + const op = { + force: true + }; + if (files.length > 0) { + // 批处理文件 + const copyOperations = files.map(item => { + const fileName = `${dir}${item.name}`; + // 拼接文件名 + const ext = extname(item.name); + const bn = basename(item.name, ext); + const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; + return qiniu.rs.copyOp( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op + ); + }); + await new Promise((resolve, reject) => { + this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + resolve(); + } else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + }); + }); + } + // 处理文件夹 + const dirs = fileList.filter(item => item.type === 'dir'); + if (dirs.length > 0) { + // 处理文件夹的复制 + for (let i = 0; i < dirs.length; i++) { + const dirName = `${dir}${dirs[i].name}/`; + const copyDirName = `${toDir}${dirs[i].name}${NETDISK_COPY_SUFFIX}/`; + let hasFile = true; + let marker = ''; + while (hasFile) { + await new Promise((resolve, reject) => { + // 列举当前目录下的所有文件 + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: dirName, + limit: NETDISK_HANDLE_MAX_ITEM, + marker + }, + (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + const moveOperations = respBody.items.map(item => { + const { key } = item; + const destKey = key.replace(dirName, copyDirName); + return qiniu.rs.copyOp( + this.qiniuConfig.bucket, + key, + this.qiniuConfig.bucket, + destKey, + op + ); + }); + this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2); + return; + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) hasFile = false; + else marker = respBody.marker; + + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` + ) + ); + } + }); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); + } + } + ); + }); + } + } + } + } + + /** + * 移动文件,含文件夹 + */ + async moveMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { + const files = fileList.filter(item => item.type === 'file'); + const op = { + force: true + }; + if (files.length > 0) { + // 批处理文件 + const copyOperations = files.map(item => { + const fileName = `${dir}${item.name}`; + const toFileName = `${toDir}${item.name}`; + return qiniu.rs.moveOp( + this.qiniuConfig.bucket, + fileName, + this.qiniuConfig.bucket, + toFileName, + op + ); + }); + await new Promise((resolve, reject) => { + this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + resolve(); + } else if (respInfo.statusCode === 298) { + reject(new Error('操作异常,但部分文件夹删除成功')); + } else { + reject( + new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + ); + } + }); + }); + } + // 处理文件夹 + const dirs = fileList.filter(item => item.type === 'dir'); + if (dirs.length > 0) { + // 处理文件夹的复制 + for (let i = 0; i < dirs.length; i++) { + const dirName = `${dir}${dirs[i].name}/`; + const toDirName = `${toDir}${dirs[i].name}/`; + // 移动的目录不是是自己 + if (toDirName.startsWith(dirName)) continue; + + let hasFile = true; + let marker = ''; + while (hasFile) { + await new Promise((resolve, reject) => { + // 列举当前目录下的所有文件 + this.bucketManager.listPrefix( + this.qiniuConfig.bucket, + { + prefix: dirName, + limit: NETDISK_HANDLE_MAX_ITEM, + marker + }, + (err, respBody, respInfo) => { + if (err) { + reject(err); + return; + } + if (respInfo.statusCode === 200) { + const moveOperations = respBody.items.map(item => { + const { key } = item; + const destKey = key.replace(dirName, toDirName); + return qiniu.rs.moveOp( + this.qiniuConfig.bucket, + key, + this.qiniuConfig.bucket, + destKey, + op + ); + }); + this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + if (err2) { + reject(err2); + return; + } + if (respInfo2.statusCode === 200) { + if (isEmpty(respBody.marker)) hasFile = false; + else marker = respBody.marker; + + resolve(); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` + ) + ); + } + }); + } else { + reject( + new Error( + `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + ) + ); + } + } + ); + }); + } + } + } + } +} diff --git a/src/modules/netdisk/manager/manage.class.ts b/src/modules/netdisk/manager/manage.class.ts index 1c099d8..3f236f6 100644 --- a/src/modules/netdisk/manager/manage.class.ts +++ b/src/modules/netdisk/manager/manage.class.ts @@ -51,7 +51,7 @@ export class SFileInfoDetail { @ApiProperty({ description: '文件存储类型,2 表示归档存储,1 表示低频存储,0表示普通存储。' }) - type: number; + type?: number; @ApiProperty({ description: '文件上传时间', type: Date }) putTime: Date; @@ -60,7 +60,7 @@ export class SFileInfoDetail { md5: string; @ApiProperty({ description: '上传人' }) - uploader: string; + uploader?: string; @ApiProperty({ description: '文件备注' }) mark?: string; diff --git a/src/modules/netdisk/manager/manage.controller.ts b/src/modules/netdisk/manager/manage.controller.ts index 8a6d2eb..f3e092c 100644 --- a/src/modules/netdisk/manager/manage.controller.ts +++ b/src/modules/netdisk/manager/manage.controller.ts @@ -49,27 +49,27 @@ export class NetDiskManageController { return await this.manageService.getFileList(dto.path, dto.marker, dto.key); } - @Post('mkdir') - @ApiOperation({ summary: '创建文件夹,支持多级' }) - @Perm(permissions.MKDIR) - async mkdir(@Body() dto: MKDirDto): Promise { - const result = await this.manageService.checkFileExist(`${dto.path}${dto.dirName}/`); - if (result) throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST); + // @Post('mkdir') + // @ApiOperation({ summary: '创建文件夹,支持多级' }) + // @Perm(permissions.MKDIR) + // async mkdir(@Body() dto: MKDirDto): Promise { + // const result = await this.manageService.checkFileExist(`${dto.path}${dto.dirName}/`); + // if (result) throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST); - await this.manageService.createDir(`${dto.path}${dto.dirName}`); - } + // await this.manageService.createDir(`${dto.path}${dto.dirName}`); + // } - @Get('token') - @ApiOperation({ summary: '获取上传Token,无Token前端无法上传' }) - @ApiOkResponse({ type: UploadToken }) - @Perm(permissions.TOKEN) - async token(@AuthUser() user: IAuthUser): Promise { - checkIsDemoMode(); + // @Get('token') + // @ApiOperation({ summary: '获取上传Token,无Token前端无法上传' }) + // @ApiOkResponse({ type: UploadToken }) + // @Perm(permissions.TOKEN) + // async token(@AuthUser() user: IAuthUser): Promise { + // checkIsDemoMode(); - return { - token: this.manageService.createUploadToken(`${user.uid}`) - }; - } + // return { + // token: this.manageService.createUploadToken(`${user.uid}`) + // }; + // } @Get('info') @ApiOperation({ summary: '获取文件详细信息' }) @@ -79,14 +79,14 @@ export class NetDiskManageController { return await this.manageService.getFileInfo(dto.name, dto.path); } - @Post('mark') - @ApiOperation({ summary: '添加文件备注' }) - @Perm(permissions.MARK) - async mark(@Body() dto: MarkFileDto): Promise { - await this.manageService.changeFileHeaders(dto.name, dto.path, { - mark: dto.mark - }); - } + // @Post('mark') + // @ApiOperation({ summary: '添加文件备注' }) + // @Perm(permissions.MARK) + // async mark(@Body() dto: MarkFileDto): Promise { + // await this.manageService.changeFileHeaders(dto.name, dto.path, { + // mark: dto.mark + // }); + // } @Get('download') @ApiOperation({ summary: '获取下载链接,不支持下载文件夹' }) @@ -96,40 +96,40 @@ export class NetDiskManageController { return this.manageService.getDownloadLink(`${dto.path}${dto.name}`); } - @Post('rename') - @ApiOperation({ summary: '重命名文件或文件夹' }) - @Perm(permissions.RENAME) - async rename(@Body() dto: RenameDto): Promise { - const result = await this.manageService.checkFileExist( - `${dto.path}${dto.toName}${dto.type === 'dir' ? '/' : ''}` - ); - if (result) throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST); + // @Post('rename') + // @ApiOperation({ summary: '重命名文件或文件夹' }) + // @Perm(permissions.RENAME) + // async rename(@Body() dto: RenameDto): Promise { + // const result = await this.manageService.checkFileExist( + // `${dto.path}${dto.toName}${dto.type === 'dir' ? '/' : ''}` + // ); + // if (result) throw new BusinessException(ErrorEnum.OSS_FILE_OR_DIR_EXIST); - if (dto.type === 'file') await this.manageService.renameFile(dto.path, dto.name, dto.toName); - else await this.manageService.renameDir(dto.path, dto.name, dto.toName); - } + // if (dto.type === 'file') await this.manageService.renameFile(dto.path, dto.name, dto.toName); + // else await this.manageService.renameDir(dto.path, dto.name, dto.toName); + // } - @Post('delete') - @ApiOperation({ summary: '删除文件或文件夹' }) - @Perm(permissions.DELETE) - async delete(@Body() dto: DeleteDto): Promise { - await this.manageService.deleteMultiFileOrDir(dto.files, dto.path); - } + // @Post('delete') + // @ApiOperation({ summary: '删除文件或文件夹' }) + // @Perm(permissions.DELETE) + // async delete(@Body() dto: DeleteDto): Promise { + // await this.manageService.deleteMultiFileOrDir(dto.files, dto.path); + // } - @Post('cut') - @ApiOperation({ summary: '剪切文件或文件夹,支持批量' }) - @Perm(permissions.CUT) - async cut(@Body() dto: FileOpDto): Promise { - if (dto.originPath === dto.toPath) - throw new BusinessException(ErrorEnum.OSS_NO_OPERATION_REQUIRED); + // @Post('cut') + // @ApiOperation({ summary: '剪切文件或文件夹,支持批量' }) + // @Perm(permissions.CUT) + // async cut(@Body() dto: FileOpDto): Promise { + // if (dto.originPath === dto.toPath) + // throw new BusinessException(ErrorEnum.OSS_NO_OPERATION_REQUIRED); - await this.manageService.moveMultiFileOrDir(dto.files, dto.originPath, dto.toPath); - } + // await this.manageService.moveMultiFileOrDir(dto.files, dto.originPath, dto.toPath); + // } - @Post('copy') - @ApiOperation({ summary: '复制文件或文件夹,支持批量' }) - @Perm(permissions.COPY) - async copy(@Body() dto: FileOpDto): Promise { - await this.manageService.copyMultiFileOrDir(dto.files, dto.originPath, dto.toPath); - } + // @Post('copy') + // @ApiOperation({ summary: '复制文件或文件夹,支持批量' }) + // @Perm(permissions.COPY) + // async copy(@Body() dto: FileOpDto): Promise { + // await this.manageService.copyMultiFileOrDir(dto.files, dto.originPath, dto.toPath); + // } } diff --git a/src/modules/netdisk/manager/manage.service.ts b/src/modules/netdisk/manager/manage.service.ts index 50f4d8e..b6d0c74 100644 --- a/src/modules/netdisk/manager/manage.service.ts +++ b/src/modules/netdisk/manager/manage.service.ts @@ -3,8 +3,6 @@ import { basename, extname } from 'node:path'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { isEmpty } from 'lodash'; -import * as qiniu from 'qiniu'; -import { auth, conf, rs } from 'qiniu'; import { ConfigKeyPaths } from '~/config'; import { @@ -17,31 +15,25 @@ import { import { AccountInfo } from '~/modules/user/user.model'; import { UserService } from '~/modules/user/user.service'; -import { generateRandomValue } from '~/utils'; +import { fileRename, generateRandomValue } from '~/utils'; import { SFileInfo, SFileInfoDetail, SFileList } from './manage.class'; import { FileOpItem } from './manage.dto'; - +import * as Minio from 'Minio'; @Injectable() export class NetDiskManageService { - private config: conf.ConfigOptions; - private mac: auth.digest.Mac; - private bucketManager: rs.BucketManager; - - private get qiniuConfig() { + private get ossConfig() { return this.configService.get('oss', { infer: true }); } - - constructor( - private configService: ConfigService, - private userService: UserService - ) { - this.mac = new qiniu.auth.digest.Mac(this.qiniuConfig.accessKey, this.qiniuConfig.secretKey); - this.config = new qiniu.conf.Config({ - zone: this.qiniuConfig.zone + private minioClient: Minio.Client; + constructor(private configService: ConfigService) { + this.minioClient = new Minio.Client({ + endPoint: this.ossConfig.domain, + port: this.ossConfig.port, + useSSL: this.ossConfig.useSSL, + accessKey: this.ossConfig.accessKey, + secretKey: this.ossConfig.secretKey }); - // bucket manager - this.bucketManager = new qiniu.rs.BucketManager(this.mac, this.config); } /** @@ -54,97 +46,86 @@ export class NetDiskManageService { // 是否需要搜索 const searching = !isEmpty(skey); return new Promise((resolve, reject) => { - this.bucketManager.listPrefix( - this.qiniuConfig.bucket, - { - prefix: searching ? '' : prefix, - limit: NETDISK_LIMIT, - delimiter: searching ? '' : NETDISK_DELIMITER, - marker - }, - (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - // 如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候, - // 指定options里面的marker为这个值 - const fileList: SFileInfo[] = []; - // 处理目录,但只有非搜索模式下可用 - if (!searching && !isEmpty(respBody.commonPrefixes)) { - // dir - for (const dirPath of respBody.commonPrefixes) { - const name = (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''); - if (isEmpty(skey) || name.includes(skey)) { - fileList.push({ - name: (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''), - type: 'dir', - id: generateRandomValue(10) - }); - } + try { + const fileStream = this.minioClient.listObjects(this.ossConfig.bucket, prefix, false); + this.readStreamData(fileStream).then(respBody => { + console.log(respBody); + const dirs = respBody.filter(item => 'prefix' in item).map(item => item.prefix); + const files = respBody.filter(item => !('prefix' in item)); + // 如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候, + // 指定options里面的marker为这个值 + const fileList: SFileInfo[] = []; + // 处理目录,但只有非搜索模式下可用 + if (!searching && !isEmpty(dirs)) { + // dir + for (const dirPath of dirs) { + const name = (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''); + if (isEmpty(skey) || name.includes(skey)) { + fileList.push({ + name: (dirPath as string).substr(0, dirPath.length - 1).replace(prefix, ''), + type: 'dir', + id: generateRandomValue(10) + }); } } - // handle items - if (!isEmpty(respBody.items)) { - // file - for (const item of respBody.items) { - // 搜索模式下处理 - if (searching) { - const pathList: string[] = item.key.split(NETDISK_DELIMITER); - // dir is empty stirng, file is key string - const name = pathList.pop(); - if ( - item.key.endsWith(NETDISK_DELIMITER) && - pathList[pathList.length - 1].includes(skey) - ) { - // 结果是目录 - const ditName = pathList.pop(); - fileList.push({ - id: generateRandomValue(10), - name: ditName, - type: 'dir', - belongTo: pathList.join(NETDISK_DELIMITER) - }); - } else if (name.includes(skey)) { - // 文件 - fileList.push({ - id: generateRandomValue(10), - name, - type: 'file', - fsize: item.fsize, - mimeType: item.mimeType, - putTime: new Date(Number.parseInt(item.putTime) / 10000), - belongTo: pathList.join(NETDISK_DELIMITER) - }); - } - } else { - // 正常获取列表 - const fileKey = item.key.replace(prefix, '') as string; - if (!isEmpty(fileKey)) { - fileList.push({ - id: generateRandomValue(10), - name: fileKey, - type: 'file', - fsize: item.fsize, - mimeType: item.mimeType, - putTime: new Date(Number.parseInt(item.putTime) / 10000) - }); - } - } + } + // handle items + if (!isEmpty(files)) { + // file + for (const item of files) { + // 搜索模式下处理 + // if (searching) { + // const pathList: string[] = item.key.split(NETDISK_DELIMITER); + // // dir is empty stirng, file is key string + // const name = pathList.pop(); + // if ( + // item.key.endsWith(NETDISK_DELIMITER) && + // pathList[pathList.length - 1].includes(skey) + // ) { + // // 结果是目录 + // const ditName = pathList.pop(); + // fileList.push({ + // id: generateRandomValue(10), + // name: ditName, + // type: 'dir', + // belongTo: pathList.join(NETDISK_DELIMITER) + // }); + // } else if (name.includes(skey)) { + // // 文件 + // fileList.push({ + // id: generateRandomValue(10), + // name, + // type: 'file', + // fsize: item.fsize, + // mimeType: item.mimeType, + // putTime: new Date(Number.parseInt(item.putTime) / 10000), + // belongTo: pathList.join(NETDISK_DELIMITER) + // }); + // } + // } else { + // 正常获取列表 + const fileKey = item.name.replace(prefix, '') as string; + if (!isEmpty(fileKey)) { + fileList.push({ + id: generateRandomValue(10), + name: fileKey, + type: 'file', + fsize: `${item.size || 0}`, + mimeType: item.name.split('.').pop(), + putTime: new Date(item.lastModified) + }); + // } } } - resolve({ - list: fileList, - marker: respBody.marker || null - }); - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); } - } - ); + resolve({ + list: fileList + // marker: respBody.marker || null + }); + }); + } catch (e) { + reject(e); + } }); } @@ -152,685 +133,682 @@ export class NetDiskManageService { * 获取文件信息 */ async getFileInfo(name: string, path: string): Promise { - return new Promise((resolve, reject) => { - this.bucketManager.stat( - this.qiniuConfig.bucket, - `${path}${name}`, - (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - const detailInfo: SFileInfoDetail = { - fsize: respBody.fsize, - hash: respBody.hash, - md5: respBody.md5, - mimeType: respBody.mimeType.split('/x-qn-meta')[0], - putTime: new Date(Number.parseInt(respBody.putTime) / 10000), - type: respBody.type, - uploader: '', - mark: respBody?.['x-qn-meta']?.['!mark'] ?? '' - }; - if (!respBody.endUser) { - resolve(detailInfo); - } else { - this.userService - .getAccountInfo(Number.parseInt(respBody.endUser)) - .then((user: AccountInfo) => { - if (isEmpty(user)) { - resolve(detailInfo); - } else { - detailInfo.uploader = user.username; - resolve(detailInfo); - } - }); - } - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); - } - } - ); - }); - } - - /** - * 修改文件MimeType - */ - async changeFileHeaders( - name: string, - path: string, - headers: { [k: string]: string } - ): Promise { - return new Promise((resolve, reject) => { - this.bucketManager.changeHeaders( - this.qiniuConfig.bucket, - `${path}${name}`, - headers, - (err, _, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - resolve(); - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); - } - } - ); - }); - } - - /** - * 创建文件夹 - * @returns true创建成功 - */ - async createDir(dirName: string): Promise { - const safeDirName = dirName.endsWith('/') ? dirName : `${dirName}/`; - return new Promise((resolve, reject) => { - // 上传一个空文件以用于显示文件夹效果 - const formUploader = new qiniu.form_up.FormUploader(this.config); - const putExtra = new qiniu.form_up.PutExtra(); - formUploader.put( - this.createUploadToken(''), - safeDirName, - ' ', - putExtra, - (respErr, respBody, respInfo) => { - if (respErr) { - reject(respErr); - return; - } - if (respInfo.statusCode === 200) { - resolve(); - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); - } - } - ); - }); - } - - /** - * 检查文件是否存在,同可检查目录 - */ - async checkFileExist(filePath: string): Promise { - return new Promise((resolve, reject) => { - // fix path end must a / - - // 检测文件夹是否存在 - this.bucketManager.stat(this.qiniuConfig.bucket, filePath, (respErr, respBody, respInfo) => { - if (respErr) { - reject(respErr); - return; - } - if (respInfo.statusCode === 200) { - // 文件夹存在 - resolve(true); - } else if (respInfo.statusCode === 612) { - // 文件夹不存在 - resolve(false); - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); - } - }); - }); - } - - /** - * 创建Upload Token, 默认过期时间一小时 - * @returns upload token - */ - createUploadToken(endUser: string): string { - const policy = new qiniu.rs.PutPolicy({ - scope: this.qiniuConfig.bucket, - insertOnly: 1, - fsizeLimit: 1024 ** 2 * 10, - endUser - }); - const uploadToken = policy.uploadToken(this.mac); - return uploadToken; - } - - /** - * 重命名文件 - * @param dir 文件路径 - * @param name 文件名称 - */ - async renameFile(dir: string, name: string, toName: string): Promise { - const fileName = `${dir}${name}`; - const toFileName = `${dir}${toName}`; - const op = { - force: true + const respBody = await this.minioClient.statObject(this.ossConfig.bucket, `${path}${name}`); + const detailInfo: SFileInfoDetail = { + fsize: respBody.size, + hash: respBody.metaData.hash || '', + md5: respBody.metaData.md5 || '', + mimeType: respBody.metaData['content-type'], + putTime: new Date(respBody.lastModified) }; - return new Promise((resolve, reject) => { - this.bucketManager.move( - this.qiniuConfig.bucket, - fileName, - this.qiniuConfig.bucket, - toFileName, - op, - (err, respBody, respInfo) => { - if (err) { - reject(err); - } else { - if (respInfo.statusCode === 200) { - resolve(); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` - ) - ); - } - } - } - ); - }); + return detailInfo; } - /** - * 移动文件 - */ - async moveFile(dir: string, toDir: string, name: string): Promise { - const fileName = `${dir}${name}`; - const toFileName = `${toDir}${name}`; - const op = { - force: true - }; + // /** + // * 修改文件MimeType + // */ + // async changeFileHeaders( + // name: string, + // path: string, + // headers: { [k: string]: string } + // ): Promise { + // return new Promise((resolve, reject) => { + // this.bucketManager.changeHeaders( + // this.qiniuConfig.bucket, + // `${path}${name}`, + // headers, + // (err, _, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // resolve(); + // } else { + // reject( + // new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + // ); + // } + // } + // ); + // }); + // } + + // /** + // * 创建文件夹 + // * @returns true创建成功 + // */ + // async createDir(dirName: string): Promise { + // const safeDirName = dirName.endsWith('/') ? dirName : `${dirName}/`; + // return new Promise((resolve, reject) => { + // // 上传一个空文件以用于显示文件夹效果 + // const formUploader = new qiniu.form_up.FormUploader(this.config); + // const putExtra = new qiniu.form_up.PutExtra(); + // formUploader.put( + // this.createUploadToken(''), + // safeDirName, + // ' ', + // putExtra, + // (respErr, respBody, respInfo) => { + // if (respErr) { + // reject(respErr); + // return; + // } + // if (respInfo.statusCode === 200) { + // resolve(); + // } else { + // reject( + // new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + // ); + // } + // } + // ); + // }); + // } + + // /** + // * 检查文件是否存在,同可检查目录 + // */ + // async checkFileExist(filePath: string): Promise { + // return new Promise((resolve, reject) => { + // // fix path end must a / + + // // 检测文件夹是否存在 + // this.bucketManager.stat(this.qiniuConfig.bucket, filePath, (respErr, respBody, respInfo) => { + // if (respErr) { + // reject(respErr); + // return; + // } + // if (respInfo.statusCode === 200) { + // // 文件夹存在 + // resolve(true); + // } else if (respInfo.statusCode === 612) { + // // 文件夹不存在 + // resolve(false); + // } else { + // reject( + // new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + // ); + // } + // }); + // }); + // } + + // /** + // * 创建Upload Token, 默认过期时间一小时 + // * @returns upload token + // */ + // createUploadToken(endUser: string): string { + // const policy = new qiniu.rs.PutPolicy({ + // scope: this.qiniuConfig.bucket, + // insertOnly: 1, + // fsizeLimit: 1024 ** 2 * 10, + // endUser + // }); + // const uploadToken = policy.uploadToken(this.mac); + // return uploadToken; + // } + + // /** + // * 重命名文件 + // * @param dir 文件路径 + // * @param name 文件名称 + // */ + // async renameFile(dir: string, name: string, toName: string): Promise { + // const fileName = `${dir}${name}`; + // const toFileName = `${dir}${toName}`; + // const op = { + // force: true + // }; + // return new Promise((resolve, reject) => { + // this.bucketManager.move( + // this.qiniuConfig.bucket, + // fileName, + // this.qiniuConfig.bucket, + // toFileName, + // op, + // (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // } else { + // if (respInfo.statusCode === 200) { + // resolve(); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + // ) + // ); + // } + // } + // } + // ); + // }); + // } + + // /** + // * 移动文件 + // */ + // async moveFile(dir: string, toDir: string, name: string): Promise { + // const fileName = `${dir}${name}`; + // const toFileName = `${toDir}${name}`; + // const op = { + // force: true + // }; + // return new Promise((resolve, reject) => { + // this.bucketManager.move( + // this.qiniuConfig.bucket, + // fileName, + // this.qiniuConfig.bucket, + // toFileName, + // op, + // (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // } else { + // if (respInfo.statusCode === 200) { + // resolve(); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + // ) + // ); + // } + // } + // } + // ); + // }); + // } + + // /** + // * 复制文件 + // */ + // async copyFile(dir: string, toDir: string, name: string): Promise { + // const fileName = `${dir}${name}`; + // // 拼接文件名 + // const ext = extname(name); + // const bn = basename(name, ext); + // const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; + // const op = { + // force: true + // }; + // return new Promise((resolve, reject) => { + // this.bucketManager.copy( + // this.qiniuConfig.bucket, + // fileName, + // this.qiniuConfig.bucket, + // toFileName, + // op, + // (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // } else { + // if (respInfo.statusCode === 200) { + // resolve(); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + // ) + // ); + // } + // } + // } + // ); + // }); + // } + + // /** + // * 重命名文件夹 + // */ + // async renameDir(path: string, name: string, toName: string): Promise { + // const dirName = `${path}${name}`; + // const toDirName = `${path}${toName}`; + // let hasFile = true; + // let marker = ''; + // const op = { + // force: true + // }; + // const bucketName = this.qiniuConfig.bucket; + // while (hasFile) { + // await new Promise((resolve, reject) => { + // // 列举当前目录下的所有文件 + // this.bucketManager.listPrefix( + // this.qiniuConfig.bucket, + // { + // prefix: dirName, + // limit: NETDISK_HANDLE_MAX_ITEM, + // marker + // }, + // (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // const moveOperations = respBody.items.map(item => { + // const { key } = item; + // const destKey = key.replace(dirName, toDirName); + // return qiniu.rs.moveOp(bucketName, key, bucketName, destKey, op); + // }); + // this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + // if (err2) { + // reject(err2); + // return; + // } + // if (respInfo2.statusCode === 200) { + // if (isEmpty(respBody.marker)) hasFile = false; + // else marker = respBody.marker; + + // resolve(); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` + // ) + // ); + // } + // }); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + // ) + // ); + // } + // } + // ); + // }); + // } + // } + + // /** + // * 获取七牛下载的文件url链接 + // * @param key 文件路径 + // * @returns 连接 + // */ + getDownloadLink(key: string): Promise { return new Promise((resolve, reject) => { - this.bucketManager.move( - this.qiniuConfig.bucket, - fileName, - this.qiniuConfig.bucket, - toFileName, - op, - (err, respBody, respInfo) => { - if (err) { - reject(err); - } else { - if (respInfo.statusCode === 200) { - resolve(); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` - ) - ); - } - } - } - ); - }); - } - - /** - * 复制文件 - */ - async copyFile(dir: string, toDir: string, name: string): Promise { - const fileName = `${dir}${name}`; - // 拼接文件名 - const ext = extname(name); - const bn = basename(name, ext); - const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; - const op = { - force: true - }; - return new Promise((resolve, reject) => { - this.bucketManager.copy( - this.qiniuConfig.bucket, - fileName, - this.qiniuConfig.bucket, - toFileName, - op, - (err, respBody, respInfo) => { - if (err) { - reject(err); - } else { - if (respInfo.statusCode === 200) { - resolve(); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` - ) - ); - } - } - } - ); - }); - } - - /** - * 重命名文件夹 - */ - async renameDir(path: string, name: string, toName: string): Promise { - const dirName = `${path}${name}`; - const toDirName = `${path}${toName}`; - let hasFile = true; - let marker = ''; - const op = { - force: true - }; - const bucketName = this.qiniuConfig.bucket; - while (hasFile) { - await new Promise((resolve, reject) => { - // 列举当前目录下的所有文件 - this.bucketManager.listPrefix( - this.qiniuConfig.bucket, - { - prefix: dirName, - limit: NETDISK_HANDLE_MAX_ITEM, - marker - }, - (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - const moveOperations = respBody.items.map(item => { - const { key } = item; - const destKey = key.replace(dirName, toDirName); - return qiniu.rs.moveOp(bucketName, key, bucketName, destKey, op); - }); - this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { - if (err2) { - reject(err2); - return; - } - if (respInfo2.statusCode === 200) { - if (isEmpty(respBody.marker)) hasFile = false; - else marker = respBody.marker; - - resolve(); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` - ) - ); - } - }); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` - ) - ); - } + if (this.ossConfig.access === 'public') { + this.minioClient.presignedUrl( + 'GET', + this.ossConfig.bucket, + key, + 24 * 60 * 60, + (err, presignedUrl) => { + if (err) reject(); + resolve(presignedUrl); } ); - }); - } - } - - /** - * 获取七牛下载的文件url链接 - * @param key 文件路径 - * @returns 连接 - */ - getDownloadLink(key: string): string { - if (this.qiniuConfig.access === 'public') { - return this.bucketManager.publicDownloadUrl(this.qiniuConfig.domain, key); - } else if (this.qiniuConfig.access === 'private') { - return this.bucketManager.privateDownloadUrl( - this.qiniuConfig.domain, - key, - Date.now() / 1000 + 36000 - ); - } - throw new Error('qiniu config access type not support'); - } - - /** - * 删除文件 - * @param dir 删除的文件夹目录 - * @param name 文件名 - */ - async deleteFile(dir: string, name: string): Promise { - return new Promise((resolve, reject) => { - this.bucketManager.delete( - this.qiniuConfig.bucket, - `${dir}${name}`, - (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - resolve(); - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); - } - } - ); + } else if (this.ossConfig.access === 'private') { + // return this.bucketManager.privateDownloadUrl( + // this.qiniuConfig.domain, + // key, + // Date.now() / 1000 + 36000 + // ); + } }); } + // /** + // * 删除文件 + // * @param dir 删除的文件夹目录 + // * @param name 文件名 + // */ + // async deleteFile(dir: string, name: string): Promise { + // return new Promise((resolve, reject) => { + // this.bucketManager.delete( + // this.qiniuConfig.bucket, + // `${dir}${name}`, + // (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // resolve(); + // } else { + // reject( + // new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + // ); + // } + // } + // ); + // }); + // } + + // /** + // * 删除文件夹 + // * @param dir 文件夹所在的上级目录 + // * @param name 文件目录名称 + // */ + // async deleteMultiFileOrDir(fileList: FileOpItem[], dir: string): Promise { + // const files = fileList.filter(item => item.type === 'file'); + // if (files.length > 0) { + // // 批处理文件 + // const copyOperations = files.map(item => { + // const fileName = `${dir}${item.name}`; + // return qiniu.rs.deleteOp(this.qiniuConfig.bucket, fileName); + // }); + // await new Promise((resolve, reject) => { + // this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // resolve(); + // } else if (respInfo.statusCode === 298) { + // reject(new Error('操作异常,但部分文件夹删除成功')); + // } else { + // reject( + // new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + // ); + // } + // }); + // }); + // } + // // 处理文件夹 + // const dirs = fileList.filter(item => item.type === 'dir'); + // if (dirs.length > 0) { + // // 处理文件夹的复制 + // for (let i = 0; i < dirs.length; i++) { + // const dirName = `${dir}${dirs[i].name}/`; + // let hasFile = true; + // let marker = ''; + // while (hasFile) { + // await new Promise((resolve, reject) => { + // // 列举当前目录下的所有文件 + // this.bucketManager.listPrefix( + // this.qiniuConfig.bucket, + // { + // prefix: dirName, + // limit: NETDISK_HANDLE_MAX_ITEM, + // marker + // }, + // (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // const moveOperations = respBody.items.map(item => { + // const { key } = item; + // return qiniu.rs.deleteOp(this.qiniuConfig.bucket, key); + // }); + // this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + // if (err2) { + // reject(err2); + // return; + // } + // if (respInfo2.statusCode === 200) { + // if (isEmpty(respBody.marker)) hasFile = false; + // else marker = respBody.marker; + + // resolve(); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` + // ) + // ); + // } + // }); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + // ) + // ); + // } + // } + // ); + // }); + // } + // } + // } + // } + + // /** + // * 复制文件,含文件夹 + // */ + // async copyMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { + // const files = fileList.filter(item => item.type === 'file'); + // const op = { + // force: true + // }; + // if (files.length > 0) { + // // 批处理文件 + // const copyOperations = files.map(item => { + // const fileName = `${dir}${item.name}`; + // // 拼接文件名 + // const ext = extname(item.name); + // const bn = basename(item.name, ext); + // const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; + // return qiniu.rs.copyOp( + // this.qiniuConfig.bucket, + // fileName, + // this.qiniuConfig.bucket, + // toFileName, + // op + // ); + // }); + // await new Promise((resolve, reject) => { + // this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // resolve(); + // } else if (respInfo.statusCode === 298) { + // reject(new Error('操作异常,但部分文件夹删除成功')); + // } else { + // reject( + // new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + // ); + // } + // }); + // }); + // } + // // 处理文件夹 + // const dirs = fileList.filter(item => item.type === 'dir'); + // if (dirs.length > 0) { + // // 处理文件夹的复制 + // for (let i = 0; i < dirs.length; i++) { + // const dirName = `${dir}${dirs[i].name}/`; + // const copyDirName = `${toDir}${dirs[i].name}${NETDISK_COPY_SUFFIX}/`; + // let hasFile = true; + // let marker = ''; + // while (hasFile) { + // await new Promise((resolve, reject) => { + // // 列举当前目录下的所有文件 + // this.bucketManager.listPrefix( + // this.qiniuConfig.bucket, + // { + // prefix: dirName, + // limit: NETDISK_HANDLE_MAX_ITEM, + // marker + // }, + // (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // const moveOperations = respBody.items.map(item => { + // const { key } = item; + // const destKey = key.replace(dirName, copyDirName); + // return qiniu.rs.copyOp( + // this.qiniuConfig.bucket, + // key, + // this.qiniuConfig.bucket, + // destKey, + // op + // ); + // }); + // this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + // if (err2) { + // reject(err2); + // return; + // } + // if (respInfo2.statusCode === 200) { + // if (isEmpty(respBody.marker)) hasFile = false; + // else marker = respBody.marker; + + // resolve(); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` + // ) + // ); + // } + // }); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + // ) + // ); + // } + // } + // ); + // }); + // } + // } + // } + // } + + // /** + // * 移动文件,含文件夹 + // */ + // async moveMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { + // const files = fileList.filter(item => item.type === 'file'); + // const op = { + // force: true + // }; + // if (files.length > 0) { + // // 批处理文件 + // const copyOperations = files.map(item => { + // const fileName = `${dir}${item.name}`; + // const toFileName = `${toDir}${item.name}`; + // return qiniu.rs.moveOp( + // this.qiniuConfig.bucket, + // fileName, + // this.qiniuConfig.bucket, + // toFileName, + // op + // ); + // }); + // await new Promise((resolve, reject) => { + // this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // resolve(); + // } else if (respInfo.statusCode === 298) { + // reject(new Error('操作异常,但部分文件夹删除成功')); + // } else { + // reject( + // new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) + // ); + // } + // }); + // }); + // } + // // 处理文件夹 + // const dirs = fileList.filter(item => item.type === 'dir'); + // if (dirs.length > 0) { + // // 处理文件夹的复制 + // for (let i = 0; i < dirs.length; i++) { + // const dirName = `${dir}${dirs[i].name}/`; + // const toDirName = `${toDir}${dirs[i].name}/`; + // // 移动的目录不是是自己 + // if (toDirName.startsWith(dirName)) continue; + + // let hasFile = true; + // let marker = ''; + // while (hasFile) { + // await new Promise((resolve, reject) => { + // // 列举当前目录下的所有文件 + // this.bucketManager.listPrefix( + // this.qiniuConfig.bucket, + // { + // prefix: dirName, + // limit: NETDISK_HANDLE_MAX_ITEM, + // marker + // }, + // (err, respBody, respInfo) => { + // if (err) { + // reject(err); + // return; + // } + // if (respInfo.statusCode === 200) { + // const moveOperations = respBody.items.map(item => { + // const { key } = item; + // const destKey = key.replace(dirName, toDirName); + // return qiniu.rs.moveOp( + // this.qiniuConfig.bucket, + // key, + // this.qiniuConfig.bucket, + // destKey, + // op + // ); + // }); + // this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { + // if (err2) { + // reject(err2); + // return; + // } + // if (respInfo2.statusCode === 200) { + // if (isEmpty(respBody.marker)) hasFile = false; + // else marker = respBody.marker; + + // resolve(); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` + // ) + // ); + // } + // }); + // } else { + // reject( + // new Error( + // `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` + // ) + // ); + // } + // } + // ); + // }); + // } + // } + // } + // } /** - * 删除文件夹 - * @param dir 文件夹所在的上级目录 - * @param name 文件目录名称 + * + * @param stream 文件流 + * @returns [] */ - async deleteMultiFileOrDir(fileList: FileOpItem[], dir: string): Promise { - const files = fileList.filter(item => item.type === 'file'); - if (files.length > 0) { - // 批处理文件 - const copyOperations = files.map(item => { - const fileName = `${dir}${item.name}`; - return qiniu.rs.deleteOp(this.qiniuConfig.bucket, fileName); - }); - await new Promise((resolve, reject) => { - this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - resolve(); - } else if (respInfo.statusCode === 298) { - reject(new Error('操作异常,但部分文件夹删除成功')); - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); - } + readStreamData(stream): Promise { + return new Promise((resolve, reject) => { + const result: T[] = []; + stream + .on('data', function (row) { + result.push(row); + }) + .on('end', function () { + resolve(result); + }) + .on('error', function (error) { + reject(error); }); - }); - } - // 处理文件夹 - const dirs = fileList.filter(item => item.type === 'dir'); - if (dirs.length > 0) { - // 处理文件夹的复制 - for (let i = 0; i < dirs.length; i++) { - const dirName = `${dir}${dirs[i].name}/`; - let hasFile = true; - let marker = ''; - while (hasFile) { - await new Promise((resolve, reject) => { - // 列举当前目录下的所有文件 - this.bucketManager.listPrefix( - this.qiniuConfig.bucket, - { - prefix: dirName, - limit: NETDISK_HANDLE_MAX_ITEM, - marker - }, - (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - const moveOperations = respBody.items.map(item => { - const { key } = item; - return qiniu.rs.deleteOp(this.qiniuConfig.bucket, key); - }); - this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { - if (err2) { - reject(err2); - return; - } - if (respInfo2.statusCode === 200) { - if (isEmpty(respBody.marker)) hasFile = false; - else marker = respBody.marker; - - resolve(); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` - ) - ); - } - }); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` - ) - ); - } - } - ); - }); - } - } - } - } - - /** - * 复制文件,含文件夹 - */ - async copyMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { - const files = fileList.filter(item => item.type === 'file'); - const op = { - force: true - }; - if (files.length > 0) { - // 批处理文件 - const copyOperations = files.map(item => { - const fileName = `${dir}${item.name}`; - // 拼接文件名 - const ext = extname(item.name); - const bn = basename(item.name, ext); - const toFileName = `${toDir}${bn}${NETDISK_COPY_SUFFIX}${ext}`; - return qiniu.rs.copyOp( - this.qiniuConfig.bucket, - fileName, - this.qiniuConfig.bucket, - toFileName, - op - ); - }); - await new Promise((resolve, reject) => { - this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - resolve(); - } else if (respInfo.statusCode === 298) { - reject(new Error('操作异常,但部分文件夹删除成功')); - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); - } - }); - }); - } - // 处理文件夹 - const dirs = fileList.filter(item => item.type === 'dir'); - if (dirs.length > 0) { - // 处理文件夹的复制 - for (let i = 0; i < dirs.length; i++) { - const dirName = `${dir}${dirs[i].name}/`; - const copyDirName = `${toDir}${dirs[i].name}${NETDISK_COPY_SUFFIX}/`; - let hasFile = true; - let marker = ''; - while (hasFile) { - await new Promise((resolve, reject) => { - // 列举当前目录下的所有文件 - this.bucketManager.listPrefix( - this.qiniuConfig.bucket, - { - prefix: dirName, - limit: NETDISK_HANDLE_MAX_ITEM, - marker - }, - (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - const moveOperations = respBody.items.map(item => { - const { key } = item; - const destKey = key.replace(dirName, copyDirName); - return qiniu.rs.copyOp( - this.qiniuConfig.bucket, - key, - this.qiniuConfig.bucket, - destKey, - op - ); - }); - this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { - if (err2) { - reject(err2); - return; - } - if (respInfo2.statusCode === 200) { - if (isEmpty(respBody.marker)) hasFile = false; - else marker = respBody.marker; - - resolve(); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` - ) - ); - } - }); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` - ) - ); - } - } - ); - }); - } - } - } - } - - /** - * 移动文件,含文件夹 - */ - async moveMultiFileOrDir(fileList: FileOpItem[], dir: string, toDir: string): Promise { - const files = fileList.filter(item => item.type === 'file'); - const op = { - force: true - }; - if (files.length > 0) { - // 批处理文件 - const copyOperations = files.map(item => { - const fileName = `${dir}${item.name}`; - const toFileName = `${toDir}${item.name}`; - return qiniu.rs.moveOp( - this.qiniuConfig.bucket, - fileName, - this.qiniuConfig.bucket, - toFileName, - op - ); - }); - await new Promise((resolve, reject) => { - this.bucketManager.batch(copyOperations, (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - resolve(); - } else if (respInfo.statusCode === 298) { - reject(new Error('操作异常,但部分文件夹删除成功')); - } else { - reject( - new Error(`Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}`) - ); - } - }); - }); - } - // 处理文件夹 - const dirs = fileList.filter(item => item.type === 'dir'); - if (dirs.length > 0) { - // 处理文件夹的复制 - for (let i = 0; i < dirs.length; i++) { - const dirName = `${dir}${dirs[i].name}/`; - const toDirName = `${toDir}${dirs[i].name}/`; - // 移动的目录不是是自己 - if (toDirName.startsWith(dirName)) continue; - - let hasFile = true; - let marker = ''; - while (hasFile) { - await new Promise((resolve, reject) => { - // 列举当前目录下的所有文件 - this.bucketManager.listPrefix( - this.qiniuConfig.bucket, - { - prefix: dirName, - limit: NETDISK_HANDLE_MAX_ITEM, - marker - }, - (err, respBody, respInfo) => { - if (err) { - reject(err); - return; - } - if (respInfo.statusCode === 200) { - const moveOperations = respBody.items.map(item => { - const { key } = item; - const destKey = key.replace(dirName, toDirName); - return qiniu.rs.moveOp( - this.qiniuConfig.bucket, - key, - this.qiniuConfig.bucket, - destKey, - op - ); - }); - this.bucketManager.batch(moveOperations, (err2, respBody2, respInfo2) => { - if (err2) { - reject(err2); - return; - } - if (respInfo2.statusCode === 200) { - if (isEmpty(respBody.marker)) hasFile = false; - else marker = respBody.marker; - - resolve(); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo2.statusCode}, Info: ${respInfo2.statusMessage}` - ) - ); - } - }); - } else { - reject( - new Error( - `Qiniu Error Code: ${respInfo.statusCode}, Info: ${respInfo.statusMessage}` - ) - ); - } - } - ); - }); - } - } - } + }); } } diff --git a/src/modules/netdisk/minio/minio.service.ts b/src/modules/netdisk/minio/minio.service.ts new file mode 100644 index 0000000..294f16f --- /dev/null +++ b/src/modules/netdisk/minio/minio.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import * as Minio from 'minio'; +import { ConfigKeyPaths } from '~/config'; +@Injectable() +export class MinioService { + +} diff --git a/src/modules/netdisk/netdisk.module.ts b/src/modules/netdisk/netdisk.module.ts index dc14031..0ea272f 100644 --- a/src/modules/netdisk/netdisk.module.ts +++ b/src/modules/netdisk/netdisk.module.ts @@ -5,9 +5,10 @@ import { RouterModule } from '@nestjs/core'; import { UserModule } from '../user/user.module'; import { NetDiskManageController } from './manager/manage.controller'; -import { NetDiskManageService } from './manager/manage.service'; import { NetDiskOverviewController } from './overview/overview.controller'; import { NetDiskOverviewService } from './overview/overview.service'; +import { MinioService } from './minio/minio.service'; +import { NetDiskManageService } from './manager/manage.service'; @Module({ imports: [ @@ -20,6 +21,6 @@ import { NetDiskOverviewService } from './overview/overview.service'; ]) ], controllers: [NetDiskManageController, NetDiskOverviewController], - providers: [NetDiskManageService, NetDiskOverviewService] + providers: [NetDiskManageService, NetDiskOverviewService, MinioService] }) export class NetdiskModule {} From 5d9ce4cef2fef3dd8922ea46782f7e8875fc52b3 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 8 Mar 2024 17:23:33 +0800 Subject: [PATCH 31/64] =?UTF-8?q?feat:=20=E7=9B=98=E7=82=B9=E8=A1=A8?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 52 ++++++ src/constants/error-code.constant.ts | 5 +- src/modules/common/base.service.ts | 11 ++ .../in_out/materials_in_out.service.ts | 21 ++- .../materials_inventory.controller.ts | 12 +- .../materials_inventory.entity.ts | 176 ++---------------- .../materials_inventory.service.ts | 82 +++++++- src/utils/tool.util.ts | 23 +++ 9 files changed, 204 insertions(+), 179 deletions(-) create mode 100644 src/modules/common/base.service.ts diff --git a/package.json b/package.json index 7c66bd9..5b84432 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "helmet": "^7.1.0", "ioredis": "^5.3.2", "lodash": "^4.17.21", + "mathjs": "^12.4.0", "minio": "^7.1.3", "mysql2": "^3.9.1", "nanoid": "^3.3.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 71071f8..94fdde3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,6 +131,9 @@ dependencies: lodash: specifier: ^4.17.21 version: 4.17.21 + mathjs: + specifier: ^12.4.0 + version: 12.4.0 minio: specifier: ^7.1.3 version: 7.1.3 @@ -4881,6 +4884,10 @@ packages: dot-prop: 5.3.0 dev: true + /complex.js@2.1.1: + resolution: {integrity: sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==} + dev: false + /component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} dev: true @@ -5429,6 +5436,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: false + /decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} @@ -5912,6 +5923,10 @@ packages: /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + /escape-latex@1.2.0: + resolution: {integrity: sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==} + dev: false + /escape-string-applescript@1.0.0: resolution: {integrity: sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==} engines: {node: '>=0.10.0'} @@ -6662,6 +6677,10 @@ packages: engines: {node: '>= 0.6'} dev: false + /fraction.js@4.3.4: + resolution: {integrity: sha512-pwiTgt0Q7t+GHZA4yaLjObx4vXmmdcS0iSJ19o8d/goUGgItX9UZWKWNnLHehxviD8wU2IWRsnR8cD5+yOJP2Q==} + dev: false + /fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} @@ -7671,6 +7690,10 @@ packages: dev: false optional: true + /javascript-natural-sort@0.7.1: + resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} + dev: false + /jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -8709,6 +8732,22 @@ packages: hasBin: true dev: true + /mathjs@12.4.0: + resolution: {integrity: sha512-4Moy0RNjwMSajEkGGxNUyMMC/CZAcl87WBopvNsJWB4E4EFebpTedr+0/rhqmnOSTH3Wu/3WfiWiw6mqiaHxVw==} + engines: {node: '>= 18'} + hasBin: true + dependencies: + '@babel/runtime': 7.23.9 + complex.js: 2.1.1 + decimal.js: 10.4.3 + escape-latex: 1.2.0 + fraction.js: 4.3.4 + javascript-natural-sort: 0.7.1 + seedrandom: 3.0.5 + tiny-emitter: 2.1.0 + typed-function: 4.1.1 + dev: false + /memfs@3.5.3: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} @@ -10861,6 +10900,10 @@ packages: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} dev: false + /seedrandom@3.0.5: + resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==} + dev: false + /segmentit@2.0.3: resolution: {integrity: sha512-7mn2XL3OdTUQ+AhHz7SbgyxLTaQRzTWQNVwiK+UlTO8aePGbSwvKUzTwE4238+OUY9MoR6ksAg35zl8sfTunQQ==} requiresBuild: true @@ -11635,6 +11678,10 @@ packages: /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + /tiny-emitter@2.1.0: + resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + dev: false + /tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -11893,6 +11940,11 @@ packages: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} dev: true + /typed-function@4.1.1: + resolution: {integrity: sha512-Pq1DVubcvibmm8bYcMowjVnnMwPVMeh0DIdA8ad8NZY2sJgapANJmiigSUwlt+EgXxpfIv8MWrQXTIzkfYZLYQ==} + engines: {node: '>= 14'} + dev: false + /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 2dd82b4..688571a 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -55,5 +55,8 @@ export enum ErrorEnum { PRODUCT_EXIST = '1406:产品已存在', // Contract - CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号' + CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号', + + // Inventory 库存不足 + INVENTORY_INSUFFICIENT = '1408:库存不足' } diff --git a/src/modules/common/base.service.ts b/src/modules/common/base.service.ts new file mode 100644 index 0000000..7b47117 --- /dev/null +++ b/src/modules/common/base.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BaseService { + generateInventoryNumber(): string { + // Generate a random inventory number + return Math.floor(Math.random() * 1000000).toString(); + } + + // Add more common methods here +} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 3d2dd77..7bff520 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -16,6 +16,8 @@ import { MaterialsInOutEntity } from './materials_in_out.entity'; import { fieldSearch } from '~/shared/database/field-search'; import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity'; import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum'; +import { MaterialsInventoryEntity } from '../materials_inventory.entity'; +import { MaterialsInventoryService } from '../materials_inventory.service'; @Injectable() export class MaterialsInOutService { @@ -26,7 +28,8 @@ export class MaterialsInOutService { @InjectRepository(Storage) private storageRepository: Repository, @InjectRepository(ParamConfigEntity) - private paramConfigRepository: Repository + private paramConfigRepository: Repository, + private materialsInventoryService: MaterialsInventoryService ) {} /** * 查询所有出入库记录 @@ -57,7 +60,7 @@ export class MaterialsInOutService { .where(fieldSearch(ext)) .andWhere('materialsInOut.isDelete = 0') .addOrderBy('materialsInOut.createdAt', 'DESC'); - + if (productName) { sqb.andWhere('product.name like :productName', { productName: `%${productName}%` }); } @@ -82,8 +85,10 @@ export class MaterialsInOutService { async create(dto: MaterialsInOutDto): Promise { let { inOrOut, inventoryNumber } = dto; if (inOrOut === MaterialsInOrOutEnum.In) { + // 入库 inventoryNumber = await this.generateInventoryNumber(); } else { + // 出库 const inRecord = await this.materialsInOutRepository.findOne({ where: { inventoryNumber @@ -92,9 +97,15 @@ export class MaterialsInOutService { const { productId } = inRecord; dto.productId = productId; } - await this.materialsInOutRepository.insert({ - ...this.materialsInOutRepository.create(dto), - inventoryNumber + + await this.entityManager.transaction(async manager => { + // 1.生成出入库记录 + const { productId, quantity } = await manager.create(MaterialsInOutEntity, { + ...this.materialsInOutRepository.create(dto), + inventoryNumber + }); + // 2.更新库存 + await this.materialsInventoryService.inInventory({ productId, inQuantity: quantity }); }); } diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts index 5e483a5..da479d1 100644 --- a/src/modules/materials_inventory/materials_inventory.controller.ts +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -23,7 +23,7 @@ export const permissions = definePermission('app:materials_inventory', { EXPORT: 'export' } as const); -@ApiTags('MaterialsI Inventory - 原材料盘点') +@ApiTags('MaterialsI Inventory - 原材料库存') @ApiSecurityAuth() @Controller('materials-inventory') export class MaterialsInventoryController { @@ -40,7 +40,7 @@ export class MaterialsInventoryController { } @Get() - @ApiOperation({ summary: '获取原材料盘点列表' }) + @ApiOperation({ summary: '获取原材料库存列表' }) @ApiResult({ type: [MaterialsInventoryEntity], isPage: true }) @Perm(permissions.LIST) async list(@Query() dto: MaterialsInventoryQueryDto) { @@ -48,7 +48,7 @@ export class MaterialsInventoryController { } @Get(':id') - @ApiOperation({ summary: '获取原材料盘点信息' }) + @ApiOperation({ summary: '获取原材料库存信息' }) @ApiResult({ type: MaterialsInventoryDto }) @Perm(permissions.READ) async info(@IdParam() id: number) { @@ -56,21 +56,21 @@ export class MaterialsInventoryController { } @Post() - @ApiOperation({ summary: '新增原材料盘点' }) + @ApiOperation({ summary: '新增原材料库存' }) @Perm(permissions.CREATE) async create(@Body() dto: MaterialsInventoryDto): Promise { await this.miService.create(dto); } @Put(':id') - @ApiOperation({ summary: '更新原材料盘点' }) + @ApiOperation({ summary: '更新原材料库存' }) @Perm(permissions.UPDATE) async update(@IdParam() id: number, @Body() dto: MaterialsInventoryUpdateDto): Promise { await this.miService.update(id, dto); } @Delete(':id') - @ApiOperation({ summary: '删除原材料盘点' }) + @ApiOperation({ summary: '删除原材料库存' }) @Perm(permissions.DELETE) async delete(@IdParam() id: number): Promise { await this.miService.delete(id); diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index 3023003..0d7fc29 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -1,193 +1,41 @@ import { ApiProperty } from '@nestjs/swagger'; import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; -import { Storage } from '../tools/storage/storage.entity'; @Entity({ name: 'materials_inventory' }) export class MaterialsInventoryEntity extends CommonEntity { - @Column({ name: 'company_name', type: 'varchar', length: 255, comment: '公司名称' }) - @ApiProperty({ description: '公司名称' }) - companyName: number; - @Column({ - name: 'product', + name: 'product_id', type: 'int', - comment: '产品名称(字典)' + comment: '产品' }) - @ApiProperty({ description: '产品名称(字典)' }) - product: number; + @ApiProperty({ description: '产品' }) + productId: number; @Column({ - name: 'unit', - type: 'int', - comment: '单位(字典)' - }) - @ApiProperty({ description: '单位(字典)' }) - unit: number; - - @Column({ - name: 'previous_inventory_quantity', + name: 'quantity', type: 'int', default: 0, - comment: '之前的库存数量' + comment: '库存产品数量' }) - @ApiProperty({ description: '之前的库存数量' }) - previousInventoryQuantity: number; + @ApiProperty({ description: '库存产品数量' }) + quantity: number; @Column({ - name: 'previous_unit_price', + name: 'unit_price', type: 'decimal', precision: 10, default: 0, scale: 2, - comment: '之前的单价' + comment: '库存产品单价' }) - @ApiProperty({ description: '之前的单价' }) - previousUnitPrice: number; - - @Column({ - name: 'previous_amount', - type: 'decimal', - precision: 10, - scale: 2, - default: 0, - comment: '之前的金额' - }) - @ApiProperty({ description: '之前的金额' }) - previousAmount: number; - - @Column({ - name: 'inventory_time', - type: 'date', - nullable: true, - comment: '入库时间' - }) - @ApiProperty({ description: '入库时间' }) - inventoryTime: Date; - - @Column({ - name: 'inventory_quantity', - type: 'int', - default: 0, - comment: '入库数量' - }) - @ApiProperty({ description: '入库数量' }) - inventoryQuantity: number; - - @Column({ - name: 'inventory_unit_price', - type: 'decimal', - precision: 10, - default: 0, - scale: 2, - comment: '入库单价' - }) - @ApiProperty({ description: '入库单价' }) - inventoryUnitPrice: number; - - @Column({ - name: 'inventory_amount', - type: 'decimal', - precision: 10, - default: 0, - scale: 2, - comment: '入库金额' - }) - @ApiProperty({ description: '入库金额' }) - inventoryAmount: number; - - @Column({ - name: 'out_time', - type: 'date', - nullable: true, - comment: '出库时间' - }) - @ApiProperty({ description: '出库时间' }) - outime: Date; - - @Column({ - name: 'out_quantity', - type: 'int', - default: 0, - comment: '出库数量' - }) - @ApiProperty({ description: '出库数量' }) - outQuantity: number; - - @Column({ - name: 'out_unit_price', - type: 'decimal', - precision: 10, - default: 0, - scale: 2, - comment: '出库单价' - }) - @ApiProperty({ description: '出库单价' }) - outUnitPrice: number; - - @Column({ - name: 'out_amount', - type: 'decimal', - precision: 10, - default: 0, - scale: 2, - comment: '出库金额' - }) - @ApiProperty({ description: '出库金额' }) - outAmount: number; - - @Column({ - name: 'current_inventory_quantity', - type: 'int', - default: 0, - comment: '现在的结存数量' - }) - @ApiProperty({ description: '现在的结存数量' }) - currentInventoryQuantity: number; - - @Column({ - name: 'current_unit_price', - type: 'decimal', - precision: 10, - default: 0, - scale: 2, - comment: '现在的单价' - }) - @ApiProperty({ description: '现在的单价' }) - currentUnitPrice: number; - - @Column({ - name: 'current_amount', - type: 'decimal', - precision: 10, - default: 0, - scale: 2, - comment: '现在的金额' - }) - @ApiProperty({ description: '现在的金额' }) - currentAmount: number; - - @Column({ name: 'agent', type: 'varchar', length: 50, comment: '经办人', nullable: true }) - @ApiProperty({ description: '经办人' }) - agent: string; - - @Column({ - name: 'issuance_number', - type: 'varchar', - length: 100, - comment: '领料单号' - }) - @ApiProperty({ description: '领料单号' }) - issuanceNumber: string; + @ApiProperty({ description: '库存产品单价' }) + unitPrice: number; @Column({ name: 'remark', type: 'varchar', length: 255, comment: '备注', nullable: true }) @ApiProperty({ description: '备注' }) remark: string; - @Column({ name: 'project', type: 'varchar', length: 255, comment: '项目', nullable: false }) - @ApiProperty({ description: '项目' }) - project: string; - @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 73dd3de..4033086 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -18,6 +18,9 @@ import { fieldSearch } from '~/shared/database/field-search'; import { groupBy, uniqBy } from 'lodash'; import { MaterialsInOrOutEnum } from '~/constants/enum'; import { ProjectEntity } from '../project/project.entity'; +import { calcNumber } from '~/utils'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; @Injectable() export class MaterialsInventoryService { const; @@ -52,7 +55,7 @@ export class MaterialsInventoryService { .leftJoin('mio.product', 'product') .leftJoin('product.unit', 'unit') .leftJoin('product.company', 'company') - .addSelect(['project.id','project.name', 'product.name', 'unit.label', 'company.name']) + .addSelect(['project.id', 'project.name', 'product.name', 'unit.label', 'company.name']) .where(fieldSearch({ time })) .andWhere('mio.isDelete = 0'); @@ -262,14 +265,14 @@ export class MaterialsInventoryService { } /** - * 新增 + * 新增库存 */ async create(dto: MaterialsInventoryDto): Promise { await this.materialsInventoryRepository.insert(dto); } /** - * 更新 + * 更新库存 */ async update(id: number, data: Partial): Promise { await this.entityManager.transaction(async manager => { @@ -279,6 +282,79 @@ export class MaterialsInventoryService { }); } + /** + * 产品入库 + */ + async inInventory(data: { + productId: number; + inQuantity: number; + unitPrice?: number; + }): Promise { + const { productId, inQuantity, unitPrice } = data; + + await this.entityManager.transaction(async manager => { + const exsitedInventory = await this.materialsInventoryRepository.findOne({ + where: { productId }, + lock: { mode: 'pessimistic_write' } // 开启悲观行锁,防止脏读和修改 + }); + + // 若不存在库存,直接新增库存 + if (!exsitedInventory) { + await this.entityManager.transaction(async manager => { + if (exsitedInventory) { + await manager.insert(MaterialsInventoryEntity, { + productId, + unitPrice, + quantity: inQuantity + }); + } + }); + return; + } + // 若存在库存,则库存增加 + let { quantity, id } = exsitedInventory; + const newQuantity = calcNumber(quantity || 0, inQuantity || 0, 'add'); + if (isNaN(newQuantity)) { + throw new Error('库存数量不合法'); + } + await manager.update(MaterialsInventoryEntity, id, { + quantity: newQuantity + }); + }); + } + + /** + * 产品入库 + */ + async outInventory(data: { productId: number; outQuantity: number }): Promise { + const { productId, outQuantity } = data; + + await this.entityManager.transaction(async manager => { + // 开启悲观行锁,防止脏读和修改 + const inventory = await this.materialsInventoryRepository.findOne({ + where: { productId }, + lock: { mode: 'pessimistic_write' } + }); + // 检查库存剩余 + if (inventory.quantity < outQuantity) { + throw new BusinessException(ErrorEnum.INVENTORY_INSUFFICIENT); + } + // 库存充足,可以出库 + let { quantity, id } = inventory; + const newQuantity = calcNumber(quantity || 0, outQuantity || 0, 'subtract'); + if (isNaN(newQuantity)) { + throw new Error('库存数量不合法'); + } + await manager.update(MaterialsInventoryEntity, id, { + quantity: newQuantity + }); + }); + } + + /** + * 产品出库 + */ + /** * 删除 */ diff --git a/src/utils/tool.util.ts b/src/utils/tool.util.ts index 4bd86d8..9320ca9 100644 --- a/src/utils/tool.util.ts +++ b/src/utils/tool.util.ts @@ -1,6 +1,7 @@ import { customAlphabet, nanoid } from 'nanoid'; import { md5 } from './crypto.util'; +import { add, subtract, multiply, divide, bignumber, BigNumber } from 'mathjs'; export function getAvatar(mail: string | undefined) { if (!mail) return ''; @@ -53,3 +54,25 @@ export const hashString = function (str, seed = 0) { h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909); return 4294967296 * (2097151 & h2) + (h1 >>> 0); }; +/** + * 使用mathjs进行四则运算,不丢失精度 + */ +export function calcNumber( + firstNumber: number, + secondNumber: number, + option: CalclateOption +): number { + switch (option) { + case 'add': + return add(bignumber(firstNumber), bignumber(secondNumber)).toNumber(); + case 'subtract': + return subtract(bignumber(firstNumber), bignumber(secondNumber)).toNumber(); + // case 'multiply': + // return multiply(bignumber(firstNumber), bignumber(secondNumber)); + // case 'divide': + // return divide(bignumber(firstNumber), bignumber(secondNumber)); + default: + return 0; + } +} +type CalclateOption = 'add' | 'subtract' | 'multiply' | 'divide'; From b07434e536760f051fdd37156ddf4b231643c497 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Mon, 11 Mar 2024 13:41:41 +0800 Subject: [PATCH 32/64] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E9=A1=B9=E7=9B=AE=E8=BF=9B=E8=A1=8C=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E7=BB=B4=E6=8A=A4=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/error-code.constant.ts | 5 +- .../in_out/materials_in_out.service.ts | 86 ++++++- .../materials_inventory.entity.ts | 24 +- .../materials_inventory.service.ts | 215 +++++++++++------- src/modules/product/product.dto.ts | 5 + src/modules/product/product.entity.ts | 9 + src/utils/tool.util.ts | 8 +- 7 files changed, 259 insertions(+), 93 deletions(-) diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 688571a..beb178b 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -57,6 +57,7 @@ export enum ErrorEnum { // Contract CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号', - // Inventory 库存不足 - INVENTORY_INSUFFICIENT = '1408:库存不足' + // Inventory + INVENTORY_INSUFFICIENT = '1408:库存不足', + MATERIALS_IN_OUT_NOT_FOUND = '1409:出入库信息不存在', } diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 7bff520..aea3e81 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -18,6 +18,7 @@ import { ParamConfigEntity } from '~/modules/system/param-config/param-config.en import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum'; import { MaterialsInventoryEntity } from '../materials_inventory.entity'; import { MaterialsInventoryService } from '../materials_inventory.service'; +import { isDefined } from 'class-validator'; @Injectable() export class MaterialsInOutService { @@ -83,8 +84,8 @@ export class MaterialsInOutService { * 新增 */ async create(dto: MaterialsInOutDto): Promise { - let { inOrOut, inventoryNumber } = dto; - if (inOrOut === MaterialsInOrOutEnum.In) { + let { inOrOut, inventoryNumber, projectId } = dto; + if (Object.is(inOrOut, MaterialsInOrOutEnum.In)) { // 入库 inventoryNumber = await this.generateInventoryNumber(); } else { @@ -100,12 +101,16 @@ export class MaterialsInOutService { await this.entityManager.transaction(async manager => { // 1.生成出入库记录 - const { productId, quantity } = await manager.create(MaterialsInOutEntity, { + const { productId, quantity, unitPrice } = await manager.save(MaterialsInOutEntity, { ...this.materialsInOutRepository.create(dto), inventoryNumber }); - // 2.更新库存 - await this.materialsInventoryService.inInventory({ productId, inQuantity: quantity }); + // 2.更新增减库存 + await ( + Object.is(inOrOut, MaterialsInOrOutEnum.In) + ? this.materialsInventoryService.inInventory + : this.materialsInventoryService.outInventory + )({ productId, quantity, unitPrice, projectId }, manager); }); } @@ -114,9 +119,52 @@ export class MaterialsInOutService { */ async update(id: number, { fileIds, ...data }: Partial): Promise { await this.entityManager.transaction(async manager => { + const entity = await manager.findOne(MaterialsInOutEntity, { + where: { + id + }, + lock: { mode: 'pessimistic_write' } + }); await manager.update(MaterialsInOutEntity, id, { ...data }); + let changedQuantity = 0; + if (isDefined(data.quantity) && entity.quantity !== data.quantity) { + if (entity.inOrOut === MaterialsInOrOutEnum.In) { + // 入库减少等于出库 + if (data.quantity - entity.quantity < 0) { + data.inOrOut = MaterialsInOrOutEnum.Out; + } else { + // 入库增多等于入库 + data.inOrOut = MaterialsInOrOutEnum.In; + } + } else { + // 出库减少等于入库 + if (data.quantity - entity.quantity < 0) { + data.inOrOut = MaterialsInOrOutEnum.In; + } else { + // 出库增多等于出库 + data.inOrOut = MaterialsInOrOutEnum.Out; + } + } + changedQuantity = Math.abs(data.quantity - entity.quantity); + } + // 2.更新增减库存 + if (changedQuantity !== 0) { + await ( + Object.is(data.inOrOut, MaterialsInOrOutEnum.In) + ? this.materialsInventoryService.inInventory + : this.materialsInventoryService.outInventory + )( + { + productId: entity.productId, + quantity: Math.abs(changedQuantity), + unitPrice: undefined, + projectId: entity.projectId + }, + manager + ); + } if (fileIds?.length) { const count = await this.storageRepository .createQueryBuilder('storage') @@ -139,6 +187,34 @@ export class MaterialsInOutService { * 删除 */ async delete(id: number): Promise { + await this.entityManager.transaction(async manager => { + const entity = await manager.findOne(MaterialsInOutEntity, { + where: { + id, + isDelete: 0 + }, + lock: { mode: 'pessimistic_write' } + }); + if (!entity) { + throw new BusinessException(ErrorEnum.MATERIALS_IN_OUT_NOT_FOUND); + } + + // 更新库存 + await ( + Object.is(entity.inOrOut, MaterialsInOrOutEnum.In) + ? this.materialsInventoryService.outInventory + : this.materialsInventoryService.inInventory + )( + { + productId: entity.productId, + quantity: entity.quantity, + unitPrice: undefined, + projectId: entity.projectId + }, + manager + ); + }); + // 出入库比较重要,做逻辑删除 await this.materialsInOutRepository.update(id, { isDelete: 1 }); } diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index 0d7fc29..b8c509a 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -1,9 +1,19 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; +import { ProductEntity } from '../product/product.entity'; +import { ProjectEntity } from '../project/project.entity'; @Entity({ name: 'materials_inventory' }) export class MaterialsInventoryEntity extends CommonEntity { + @Column({ + name: 'project_id', + type: 'int', + comment: '项目' + }) + @ApiProperty({ description: '项目' }) + projectId: number; + @Column({ name: 'product_id', type: 'int', @@ -24,9 +34,9 @@ export class MaterialsInventoryEntity extends CommonEntity { @Column({ name: 'unit_price', type: 'decimal', - precision: 10, + precision: 15, default: 0, - scale: 2, + scale: 10, comment: '库存产品单价' }) @ApiProperty({ description: '库存产品单价' }) @@ -39,4 +49,12 @@ export class MaterialsInventoryEntity extends CommonEntity { @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + + @ManyToOne(() => ProjectEntity) + @JoinColumn({ name: 'project_id' }) + project: ProjectEntity; + + @ManyToOne(() => ProductEntity) + @JoinColumn({ name: 'product_id' }) + product: ProductEntity; } diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 4033086..c305d57 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; import { MaterialsInventoryEntity } from './materials_inventory.entity'; -import { EntityManager, Repository } from 'typeorm'; +import { EntityManager, In, MoreThan, Repository } from 'typeorm'; import { MaterialsInventoryDto, MaterialsInventoryExportDto, @@ -15,7 +15,7 @@ import * as ExcelJS from 'exceljs'; import dayjs from 'dayjs'; import { MaterialsInOutEntity } from './in_out/materials_in_out.entity'; import { fieldSearch } from '~/shared/database/field-search'; -import { groupBy, uniqBy } from 'lodash'; +import { groupBy, sum, uniqBy } from 'lodash'; import { MaterialsInOrOutEnum } from '~/constants/enum'; import { ProjectEntity } from '../project/project.entity'; import { calcNumber } from '~/utils'; @@ -48,6 +48,13 @@ export class MaterialsInventoryService { if (projectId) { projects = [await this.projectRepository.findOneBy({ id: projectId })]; } + // 查询出项目产品所属的当前库存 + const inventoriesInProjects = await this.materialsInventoryRepository.find({ + where: { + ...(projects?.length ? { projectId: In(projects.map(item => item.id)) } : null) + } + }); + // 生成数据 const sqb = this.materialsInOutRepository .createQueryBuilder('mio') @@ -56,12 +63,15 @@ export class MaterialsInventoryService { .leftJoin('product.unit', 'unit') .leftJoin('product.company', 'company') .addSelect(['project.id', 'project.name', 'product.name', 'unit.label', 'company.name']) - .where(fieldSearch({ time })) + .where({ + time: MoreThan(time[0]) + }) .andWhere('mio.isDelete = 0'); if (projectId) { sqb.andWhere('project.id = :projectId', { projectId }); } + const data = await sqb.addOrderBy('mio.time', 'DESC').getMany(); if (!projectId) { projects = uniqBy( @@ -71,12 +81,19 @@ export class MaterialsInventoryService { } for (const project of projects) { + const currentProjectInventories = inventoriesInProjects.filter(({ projectId }) => + Object.is(projectId, project.id) + ); const currentProjectData = data.filter(item => item.projectId === project.id); + const currentMonthProjectData = currentProjectData.filter(item => { + return ( + dayjs(item.time).isAfter(dayjs(time[0])) && dayjs(item.time).isBefore(dayjs(time[1])) + ); + }); const sheet = workbook.addWorksheet(project.name); sheet.mergeCells('A1:T1'); // 设置标题 sheet.getCell('A1').value = '山东矿机华信智能科技有限公司原材料盘点表'; - // 设置日期 sheet.mergeCells('A2:B2'); sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`; @@ -168,21 +185,53 @@ export class MaterialsInventoryService { } }); - const groupedData = groupBy(currentProjectData, 'inventoryNumber'); + const groupedData = groupBy(currentMonthProjectData, 'inventoryNumber'); let number = 0; + const groupedInventories = groupBy( + currentProjectInventories, + item => `${item.projectId}_${item.productId}` + ); for (const key in groupedData) { // 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。 const inRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.In); const outRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.Out); + const currInventories = + groupedInventories[`${inRecord.projectId}_${inRecord.productId}`]?.shift(); + const allDataFromMonth = data.filter( + res => res.projectId === inRecord.projectId && res.productId === inRecord.productId + ); + let currentQuantity = 0; + let balanceQuantity = 0; + // 月初库存数量 + if (currInventories) { + const sumIn = sum( + allDataFromMonth + .filter(res => Object.is(res.inOrOut, MaterialsInOrOutEnum.In)) + .map(item => item.quantity) + ); + const sumOut = sum( + allDataFromMonth + .filter(res => Object.is(res.inOrOut, MaterialsInOrOutEnum.Out)) + .map(item => item.quantity) + ); + const sumDistance = calcNumber(sumIn, sumOut, 'subtract'); + currentQuantity = calcNumber(currInventories.quantity, sumDistance, 'subtract'); + } + // 结存库存数量 + balanceQuantity = calcNumber( + currentQuantity, + calcNumber(inRecord.quantity, outRecord.quantity, 'subtract'), + 'add' + ); number++; sheet.addRow([ - `${inRecord.inventoryNumber}`, - inRecord.product.company.name, - inRecord.product.name, - inRecord.product.unit.label, - '0', - '0', - '0', + `${inRecord.inventoryNumber || ''}`, + inRecord.product.company.name || '', + inRecord.product.name || '', + inRecord.product.unit.label || '', + currentQuantity, + parseFloat(`${inRecord.unitPrice || 0}`), + calcNumber(currentQuantity, inRecord.unitPrice || 0, 'multiply'), inRecord.time, inRecord.quantity, parseFloat(`${inRecord.unitPrice || 0}`), @@ -191,12 +240,12 @@ export class MaterialsInventoryService { outRecord?.quantity || '', parseFloat(`${outRecord.unitPrice || 0}`), parseFloat(`${outRecord.amount || 0}`), - '0', - '0', - '0', - outRecord?.agent, - outRecord?.issuanceNumber, - outRecord?.remark + balanceQuantity, + parseFloat(`${outRecord.unitPrice || 0}`), + calcNumber(balanceQuantity, outRecord.unitPrice || 0, 'multiply'), + outRecord?.agent || '', + outRecord?.issuanceNumber || '', + outRecord?.remark || '' ]); } sheet.getCell('A1').font = { size: HEADER_FONT_SIZE }; @@ -256,8 +305,12 @@ export class MaterialsInventoryService { }: MaterialsInventoryQueryDto): Promise> { const queryBuilder = this.materialsInventoryRepository .createQueryBuilder('materialsInventory') + .leftJoin('materialsInventory.project', 'project') + .leftJoin('materialsInventory.product', 'product') + .leftJoin('product.unit', 'unit') + .leftJoin('product.company', 'company') + .addSelect(['project.name', 'product.name', 'unit.label', 'company.name']) .where('materialsInventory.isDelete = 0'); - return paginate(queryBuilder, { page, pageSize @@ -283,78 +336,82 @@ export class MaterialsInventoryService { } /** - * 产品入库 + * 产品入库后计算最新库存 + * @param data 传入项目ID,产品ID和入库数量和单价 + * @param manager 传入事务对象防止开启多重事务 */ - async inInventory(data: { - productId: number; - inQuantity: number; - unitPrice?: number; - }): Promise { - const { productId, inQuantity, unitPrice } = data; + async inInventory( + data: { + projectId: number; + productId: number; + quantity: number; + unitPrice?: number; + }, + manager: EntityManager + ): Promise { + const { projectId, productId, quantity: inQuantity, unitPrice } = data; - await this.entityManager.transaction(async manager => { - const exsitedInventory = await this.materialsInventoryRepository.findOne({ - where: { productId }, - lock: { mode: 'pessimistic_write' } // 开启悲观行锁,防止脏读和修改 - }); - - // 若不存在库存,直接新增库存 - if (!exsitedInventory) { - await this.entityManager.transaction(async manager => { - if (exsitedInventory) { - await manager.insert(MaterialsInventoryEntity, { - productId, - unitPrice, - quantity: inQuantity - }); - } - }); - return; - } - // 若存在库存,则库存增加 - let { quantity, id } = exsitedInventory; - const newQuantity = calcNumber(quantity || 0, inQuantity || 0, 'add'); - if (isNaN(newQuantity)) { - throw new Error('库存数量不合法'); - } - await manager.update(MaterialsInventoryEntity, id, { - quantity: newQuantity - }); + const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, { + where: { projectId, productId }, // 查出某个项目的某个产品的库存情况 + lock: { mode: 'pessimistic_write' } // 开启悲观行锁,防止脏读和修改 }); - } - /** - * 产品入库 - */ - async outInventory(data: { productId: number; outQuantity: number }): Promise { - const { productId, outQuantity } = data; - - await this.entityManager.transaction(async manager => { - // 开启悲观行锁,防止脏读和修改 - const inventory = await this.materialsInventoryRepository.findOne({ - where: { productId }, - lock: { mode: 'pessimistic_write' } - }); - // 检查库存剩余 - if (inventory.quantity < outQuantity) { - throw new BusinessException(ErrorEnum.INVENTORY_INSUFFICIENT); - } - // 库存充足,可以出库 - let { quantity, id } = inventory; - const newQuantity = calcNumber(quantity || 0, outQuantity || 0, 'subtract'); - if (isNaN(newQuantity)) { - throw new Error('库存数量不合法'); - } - await manager.update(MaterialsInventoryEntity, id, { - quantity: newQuantity + // 若不存在库存,直接新增库存 + if (!exsitedInventory) { + await manager.insert(MaterialsInventoryEntity, { + projectId, + productId, + unitPrice, + quantity: inQuantity }); + return; + } + // 若该项目存在库存,则该项目该产品的库存增加 + let { quantity, id } = exsitedInventory; + const newQuantity = calcNumber(quantity || 0, inQuantity || 0, 'add'); + if (isNaN(newQuantity)) { + throw new Error('库存数量不合法'); + } + await manager.update(MaterialsInventoryEntity, id, { + quantity: newQuantity }); } /** * 产品出库 + * @param data 传入产品id和入库数量和单价 + * @param manager 传入事务对象防止开启多重事务 */ + async outInventory( + data: { + projectId: number; + productId: number; + quantity: number; + unitPrice?: number; + }, + manager: EntityManager + ): Promise { + const { projectId, productId, quantity: outQuantity } = data; + // 开启悲观行锁,防止脏读和修改 + const inventory = await manager.findOne(MaterialsInventoryEntity, { + where: { projectId, productId }, + lock: { mode: 'pessimistic_write' } + }); + // 检查库存剩余 + if (inventory.quantity < outQuantity) { + throw new BusinessException(ErrorEnum.INVENTORY_INSUFFICIENT); + } + // 若该项目的该产品库存充足,则该项目该产品的库存减少 + let { quantity, id } = inventory; + const newQuantity = calcNumber(quantity || 0, outQuantity || 0, 'subtract'); + if (isNaN(newQuantity)) { + throw new Error('库存数量不合法'); + } + await manager.update(MaterialsInventoryEntity, id, { + quantity: newQuantity + }); + } /** * 删除 */ diff --git a/src/modules/product/product.dto.ts b/src/modules/product/product.dto.ts index 4824c24..e8b4b08 100644 --- a/src/modules/product/product.dto.ts +++ b/src/modules/product/product.dto.ts @@ -8,6 +8,11 @@ export class ProductDto { @IsString() name: string; + @ApiProperty({ description: '产品备注' }) + @IsOptional() + @IsString() + remark: string; + @ApiProperty({ description: '单位(字典)' }) @IsOptional() @IsNumber() diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts index 0c6aa58..6b0ea0b 100644 --- a/src/modules/product/product.entity.ts +++ b/src/modules/product/product.entity.ts @@ -26,6 +26,15 @@ export class ProductEntity extends CommonEntity { @ApiProperty({ description: '产品名称' }) name: string; + @Column({ + name: 'remark', + type: 'varchar', + length: 255, + comment: '备注' + }) + @ApiProperty({ description: '产品备注' }) + remark: string; + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; diff --git a/src/utils/tool.util.ts b/src/utils/tool.util.ts index 9320ca9..5f8afbd 100644 --- a/src/utils/tool.util.ts +++ b/src/utils/tool.util.ts @@ -67,10 +67,10 @@ export function calcNumber( return add(bignumber(firstNumber), bignumber(secondNumber)).toNumber(); case 'subtract': return subtract(bignumber(firstNumber), bignumber(secondNumber)).toNumber(); - // case 'multiply': - // return multiply(bignumber(firstNumber), bignumber(secondNumber)); - // case 'divide': - // return divide(bignumber(firstNumber), bignumber(secondNumber)); + case 'multiply': + return (multiply(bignumber(firstNumber), bignumber(secondNumber)) as BigNumber).toNumber(); + case 'divide': + return (divide(bignumber(firstNumber), bignumber(secondNumber)) as BigNumber).toNumber(); default: return 0; } From 414f0d54d6c955fb74f2a1ab7ea424a52275fe02 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Mon, 11 Mar 2024 14:12:57 +0800 Subject: [PATCH 33/64] =?UTF-8?q?feat:=20=E5=AF=BC=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../materials_inventory.service.ts | 20 +++++++++---------- src/modules/product/product.entity.ts | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index c305d57..09ea420 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -220,7 +220,7 @@ export class MaterialsInventoryService { // 结存库存数量 balanceQuantity = calcNumber( currentQuantity, - calcNumber(inRecord.quantity, outRecord.quantity, 'subtract'), + calcNumber(inRecord.quantity, outRecord?.quantity || 0, 'subtract'), 'add' ); number++; @@ -233,19 +233,19 @@ export class MaterialsInventoryService { parseFloat(`${inRecord.unitPrice || 0}`), calcNumber(currentQuantity, inRecord.unitPrice || 0, 'multiply'), inRecord.time, - inRecord.quantity, + inRecord.quantity || 0, parseFloat(`${inRecord.unitPrice || 0}`), parseFloat(`${inRecord.amount || 0}`), outRecord?.time || '', - outRecord?.quantity || '', - parseFloat(`${outRecord.unitPrice || 0}`), - parseFloat(`${outRecord.amount || 0}`), + outRecord?.quantity || 0, + parseFloat(`${outRecord?.unitPrice || 0}`), + parseFloat(`${outRecord?.amount || 0}`), balanceQuantity, - parseFloat(`${outRecord.unitPrice || 0}`), - calcNumber(balanceQuantity, outRecord.unitPrice || 0, 'multiply'), - outRecord?.agent || '', + parseFloat(`${inRecord?.unitPrice || 0}`), + calcNumber(balanceQuantity, inRecord?.unitPrice || 0, 'multiply'), + `${inRecord?.agent || ''}/${outRecord?.agent || ''}`, outRecord?.issuanceNumber || '', - outRecord?.remark || '' + `${inRecord?.remark || ''}/${outRecord?.remark || ''}` ]); } sheet.getCell('A1').font = { size: HEADER_FONT_SIZE }; @@ -268,7 +268,7 @@ export class MaterialsInventoryService { sheet.columns.forEach((column, index: number) => { let maxColumnLength = 0; - const autoWidth = ['B', 'C', 'U']; + const autoWidth = ['B', 'C', 'S', 'U']; if (String.fromCharCode(65 + index) === 'B') maxColumnLength = 20; if (autoWidth.includes(String.fromCharCode(65 + index))) { column.eachCell({ includeEmpty: true }, (cell, rowIndex) => { diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts index 6b0ea0b..cc90a05 100644 --- a/src/modules/product/product.entity.ts +++ b/src/modules/product/product.entity.ts @@ -30,6 +30,7 @@ export class ProductEntity extends CommonEntity { name: 'remark', type: 'varchar', length: 255, + nullable: true, comment: '备注' }) @ApiProperty({ description: '产品备注' }) From 77247c140c7890d11cf96992fb0feff921e8ab82 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 21 Mar 2024 15:03:07 +0800 Subject: [PATCH 34/64] feat: create isMobile dereactor --- package.json | 2 +- pnpm-lock.yaml | 61 ++++++++++------- src/common/decorators/http.decorator.ts | 10 ++- src/modules/auth/auth.controller.ts | 12 +++- src/modules/auth/dto/auth.dto.ts | 2 +- .../in_out/materials_in_out.dto.ts | 2 + src/modules/netdisk/manager/manage.service.ts | 35 +++------- src/modules/netdisk/netdisk.module.ts | 67 ++++++++++++++++++- src/utils/ip.util.ts | 5 ++ 9 files changed, 139 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 5b84432..874abc1 100644 --- a/package.json +++ b/package.json @@ -89,9 +89,9 @@ "ioredis": "^5.3.2", "lodash": "^4.17.21", "mathjs": "^12.4.0", - "minio": "^7.1.3", "mysql2": "^3.9.1", "nanoid": "^3.3.7", + "nestjs-minio": "^2.5.4", "nodemailer": "^6.9.9", "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94fdde3..ee995eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,15 +134,15 @@ dependencies: mathjs: specifier: ^12.4.0 version: 12.4.0 - minio: - specifier: ^7.1.3 - version: 7.1.3 mysql2: specifier: ^3.9.1 version: 3.9.1 nanoid: specifier: ^3.3.7 version: 3.3.7 + nestjs-minio: + specifier: ^2.5.4 + version: 2.5.4(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) nodemailer: specifier: ^6.9.9 version: 6.9.9 @@ -1733,7 +1733,7 @@ packages: object-assign: 4.1.1 open: 8.4.0 proxy-middleware: 0.15.0 - send: 0.18.0 + send: 1.0.0-beta.2 serve-index: 1.9.1 transitivePeerDependencies: - supports-color @@ -5406,6 +5406,17 @@ packages: dependencies: ms: 2.0.0 + /debug@3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: true + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -6380,8 +6391,8 @@ packages: resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} dev: false - /fast-xml-parser@4.3.5: - resolution: {integrity: sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==} + /fast-xml-parser@4.3.6: + resolution: {integrity: sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==} hasBin: true dependencies: strnum: 1.0.5 @@ -7572,7 +7583,7 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} dependencies: - which-typed-array: 1.1.14 + which-typed-array: 1.1.15 dev: false /is-unicode-supported@0.1.0: @@ -8818,12 +8829,6 @@ packages: dependencies: mime-db: 1.52.0 - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - dev: true - /mime@2.6.0: resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} engines: {node: '>=4.0.0'} @@ -8914,7 +8919,7 @@ packages: block-stream2: 2.1.0 browser-or-node: 2.1.1 buffer-crc32: 0.2.13 - fast-xml-parser: 4.3.5 + fast-xml-parser: 4.3.6 ipaddr.js: 2.1.0 json-stream: 1.0.0 lodash: 4.17.21 @@ -9468,6 +9473,17 @@ packages: /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + /nestjs-minio@2.5.4(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-b99fCEjK1Kt7cNDfANrfhckQiYC10mKoVqfdwUJQaVCWMKTm2E8Kb7oiPIyWyjcVP6RERn5oUMPmf/rZLJEr3A==} + peerDependencies: + '@nestjs/common': '>7.0.0' + '@nestjs/core': '>7.0.0' + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + minio: 7.1.3 + dev: false + /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: true @@ -10933,19 +10949,18 @@ packages: dependencies: lru-cache: 6.0.0 - /send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} + /send@1.0.0-beta.2: + resolution: {integrity: sha512-k1yHu/FNK745PULKdsGpQ+bVSXYNwSk+bWnYzbxGZbt5obZc0JKDVANsCRuJD1X/EG15JtP9eZpwxkhUxIYEcg==} + engines: {node: '>= 0.10'} dependencies: - debug: 2.6.9 - depd: 2.0.0 + debug: 3.1.0 destroy: 1.2.0 encodeurl: 1.0.2 escape-html: 1.0.3 etag: 1.8.1 fresh: 0.5.2 http-errors: 2.0.0 - mime: 1.6.0 + mime-types: 2.1.35 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 @@ -12202,7 +12217,7 @@ packages: is-arguments: 1.1.1 is-generator-function: 1.0.10 is-typed-array: 1.1.13 - which-typed-array: 1.1.14 + which-typed-array: 1.1.15 dev: false /utility@1.18.0: @@ -12392,8 +12407,8 @@ packages: tr46: 0.0.3 webidl-conversions: 3.0.1 - /which-typed-array@1.1.14: - resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.7 diff --git a/src/common/decorators/http.decorator.ts b/src/common/decorators/http.decorator.ts index 22c13f7..f90d311 100644 --- a/src/common/decorators/http.decorator.ts +++ b/src/common/decorators/http.decorator.ts @@ -3,7 +3,15 @@ import type { ExecutionContext } from '@nestjs/common'; import { createParamDecorator } from '@nestjs/common'; import type { FastifyRequest } from 'fastify'; -import { getIp } from '~/utils/ip.util'; +import { getIp, getIsMobile } from '~/utils/ip.util'; + +/** + * 快速获取IP + */ +export const IsMobile = createParamDecorator((_, context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + return getIsMobile(request); +}); /** * 快速获取IP diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 2922f8e..25e72a9 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -2,7 +2,7 @@ import { Body, Controller, Headers, Post, UseGuards } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiResult } from '~/common/decorators/api-result.decorator'; -import { Ip } from '~/common/decorators/http.decorator'; +import { Ip, IsMobile } from '~/common/decorators/http.decorator'; import { UserService } from '../user/user.service'; @@ -32,9 +32,12 @@ export class AuthController { async login( @Body() dto: LoginDto, @Ip() ip: string, + @IsMobile() isMobile: boolean, @Headers('user-agent') ua: string ): Promise { - await this.captchaService.checkImgCaptcha(dto.captchaId, dto.verifyCode); + if(!isMobile){ + await this.captchaService.checkImgCaptcha(dto.captchaId, dto.verifyCode); + } const token = await this.authService.login(dto.username, dto.password, ip, ua); return { token }; } @@ -43,6 +46,11 @@ export class AuthController { @ApiSecurityAuth() @ApiOperation({ summary: '屏幕解锁,使用密码和token' }) @ApiResult({ type: LoginToken }) + + + + + async unlock(@Body() dto: LoginDto, @AuthUser() user: IAuthUser): Promise { await this.authService.unlock(user.uid, dto.password); return true; diff --git a/src/modules/auth/dto/auth.dto.ts b/src/modules/auth/dto/auth.dto.ts index 436ed04..0919641 100644 --- a/src/modules/auth/dto/auth.dto.ts +++ b/src/modules/auth/dto/auth.dto.ts @@ -13,7 +13,7 @@ export class LoginDto { @MinLength(6) password: string; - @ApiProperty({ description: '验证码标识' }) + @ApiProperty({ description: '验证码标识,手机端不需要' }) @IsOptional() captchaId: string; diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 0d0fec2..8244b59 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -55,10 +55,12 @@ export class MaterialsInOutDto { quantity: number; @ApiProperty({ description: '单价' }) + @IsOptional() @IsNumber() unitPrice: number; @ApiProperty({ description: '金额' }) + @IsOptional() @IsNumber() amount: number; diff --git a/src/modules/netdisk/manager/manage.service.ts b/src/modules/netdisk/manager/manage.service.ts index b6d0c74..d1a1209 100644 --- a/src/modules/netdisk/manager/manage.service.ts +++ b/src/modules/netdisk/manager/manage.service.ts @@ -1,41 +1,22 @@ -import { basename, extname } from 'node:path'; - import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { isEmpty } from 'lodash'; - import { ConfigKeyPaths } from '~/config'; -import { - NETDISK_COPY_SUFFIX, - NETDISK_DELIMITER, - NETDISK_HANDLE_MAX_ITEM, - NETDISK_LIMIT -} from '~/constants/oss.constant'; - -import { AccountInfo } from '~/modules/user/user.model'; -import { UserService } from '~/modules/user/user.service'; - -import { fileRename, generateRandomValue } from '~/utils'; - +import { generateRandomValue } from '~/utils'; import { SFileInfo, SFileInfoDetail, SFileList } from './manage.class'; -import { FileOpItem } from './manage.dto'; import * as Minio from 'Minio'; +import { InjectMinio } from 'nestjs-minio'; +import { Client } from 'minio'; + @Injectable() export class NetDiskManageService { private get ossConfig() { return this.configService.get('oss', { infer: true }); } - private minioClient: Minio.Client; - constructor(private configService: ConfigService) { - this.minioClient = new Minio.Client({ - endPoint: this.ossConfig.domain, - port: this.ossConfig.port, - useSSL: this.ossConfig.useSSL, - accessKey: this.ossConfig.accessKey, - secretKey: this.ossConfig.secretKey - }); - } - + constructor( + private configService: ConfigService, + @InjectMinio() private readonly minioClient: Client + ) {} /** * 获取文件列表 * @param prefix 当前文件夹路径,搜索模式下会被忽略 diff --git a/src/modules/netdisk/netdisk.module.ts b/src/modules/netdisk/netdisk.module.ts index 0ea272f..cc067e2 100644 --- a/src/modules/netdisk/netdisk.module.ts +++ b/src/modules/netdisk/netdisk.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; - +import { ConfigService } from '@nestjs/config'; import { RouterModule } from '@nestjs/core'; import { UserModule } from '../user/user.module'; @@ -9,6 +9,22 @@ import { NetDiskOverviewController } from './overview/overview.controller'; import { NetDiskOverviewService } from './overview/overview.service'; import { MinioService } from './minio/minio.service'; import { NetDiskManageService } from './manager/manage.service'; +import { NestMinioModule } from 'nestjs-minio'; +// const getMinioConfig = () => { +// const configService = new ConfigService(); +// const endPoint = configService.get('MINIO_ENDPOINT', 'localhost'); +// const accessKey = configService.get('MINIO_ACCESSKEY', 'accessKey'); +// const secretKey = configService.get('MINIO_SECRET_KEY', 'secretKey'); + +// return NestMinioModule.register({ +// isGlobal: true, +// endPoint, +// port: 9000, +// accessKey, +// secretKey, +// useSSL: false +// }); +// }; @Module({ imports: [ @@ -18,9 +34,56 @@ import { NetDiskManageService } from './manager/manage.service'; path: 'netdisk', module: NetdiskModule } - ]) + ]), + // getMinioConfig() + NestMinioModule.registerAsync({ + inject: [ConfigService], + isGlobal: true, + useFactory: async (configService: ConfigService) => { + const ossConfig = configService.get('oss'); + return { + endPoint: ossConfig.domain, + port: ossConfig.port, + useSSL: ossConfig.useSSL, + accessKey: ossConfig.accessKey, + secretKey: ossConfig.secretKey + }; + } + // endPoint: 'play.min.io', + // port: 9000, + // useSSL: true, + // accessKey: 'Q3AM3UQ867SPQQA43P2F', + // secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG' + }) ], controllers: [NetDiskManageController, NetDiskOverviewController], providers: [NetDiskManageService, NetDiskOverviewService, MinioService] }) export class NetdiskModule {} +// TypeOrmModule.forRootAsync({ +// inject: [ConfigService], +// useFactory: (configService: ConfigService) => { +// let loggerOptions: LoggerOptions = env('DB_LOGGING') as 'all'; + +// try { +// // 解析成 js 数组 ['error'] +// loggerOptions = JSON.parse(loggerOptions); +// } catch { +// // ignore +// } + +// return { +// ...configService.get('database'), +// autoLoadEntities: true, +// logging: loggerOptions, +// logger: new TypeORMLogger(loggerOptions) +// }; +// }, +// // dataSource receives the configured DataSourceOptions +// // and returns a Promise. +// dataSourceFactory: async options => { +// const dataSource = await new DataSource(options).initialize(); +// return dataSource; +// } +// }) +// ], diff --git a/src/utils/ip.util.ts b/src/utils/ip.util.ts index b9079ca..58682ec 100644 --- a/src/utils/ip.util.ts +++ b/src/utils/ip.util.ts @@ -25,6 +25,11 @@ function isLAN(ip: string) { ); } +// 是否来自手机的请求 +export function getIsMobile(request: FastifyRequest | IncomingMessage): boolean { + return request.headers['user-agent'].includes('Dart'); +} + export function getIp(request: FastifyRequest | IncomingMessage) { const req = request as any; From cab022af0a28f6111e285af36df9481032c8c5f8 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 22 Mar 2024 16:47:26 +0800 Subject: [PATCH 35/64] =?UTF-8?q?feat:=20=E9=87=8D=E5=81=9A=E5=87=BA?= =?UTF-8?q?=E5=85=A5=E5=BA=93=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 4 +- src/constants/enum/index.ts | 4 +- src/constants/error-code.constant.ts | 3 +- src/modules/auth/auth.controller.ts | 5 -- .../in_out/materials_in_out.dto.ts | 8 +- .../in_out/materials_in_out.entity.ts | 2 +- .../in_out/materials_in_out.service.ts | 77 +++++++++++++------ .../materials_inventory.service.ts | 66 ++++++++++++---- src/modules/product/product.dto.ts | 12 +++ src/modules/product/product.entity.ts | 21 ++++- src/modules/product/product.module.ts | 3 +- src/modules/product/product.service.ts | 57 +++++++++++--- 12 files changed, 202 insertions(+), 60 deletions(-) diff --git a/.env.development b/.env.development index 89c8c66..673dab6 100644 --- a/.env.development +++ b/.env.development @@ -13,7 +13,7 @@ SWAGGER_PATH = api-docs SWAGGER_VERSION = 1.0 # db -DB_HOST = 127.0.0.1 +DB_HOST = 192.168.60.39 DB_PORT = 13307 DB_DATABASE = hxoa DB_USERNAME = root @@ -23,7 +23,7 @@ DB_LOGGING = "all" # redis REDIS_PORT = 6379 -REDIS_HOST = 127.0.0.1 +REDIS_HOST = 192.168.60.39 REDIS_PASSWORD = 123456 REDIS_DB = 0 diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts index 7cc6079..257a9dd 100644 --- a/src/constants/enum/index.ts +++ b/src/constants/enum/index.ts @@ -21,7 +21,9 @@ export enum MaterialsInOrOutEnum { // 系统参数key export enum ParamConfigEnum { - MaterialsInOutPrefix = 'materials_in_out_prefix' + InventoryNumberPrefixIn = 'inventory_number_prefix_in', + InventoryNumberPrefixOut = 'inventory_number_prefix_out', + ProductNumberPrefix = 'product_number_prefix' } // 合同审核状态 diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index beb178b..0b6be78 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -57,7 +57,8 @@ export enum ErrorEnum { // Contract CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号', - // Inventory + // Inventory INVENTORY_INSUFFICIENT = '1408:库存不足', MATERIALS_IN_OUT_NOT_FOUND = '1409:出入库信息不存在', + MATERIALS_IN_OUT_UNIT_PRICE_CANNOT_BE_MODIFIED = '1410:该价格的产品已经出库,单价不允许修改。若有疑问,请联系管理员' } diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 25e72a9..4e2a593 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -46,11 +46,6 @@ export class AuthController { @ApiSecurityAuth() @ApiOperation({ summary: '屏幕解锁,使用密码和token' }) @ApiResult({ type: LoginToken }) - - - - - async unlock(@Body() dto: LoginDto, @AuthUser() user: IAuthUser): Promise { await this.authService.unlock(user.uid, dto.password); return true; diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 8244b59..767a29c 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -13,7 +13,8 @@ import { IsString, Matches, MinLength, - ValidateIf + ValidateIf, + isNumber } from 'class-validator'; import dayjs from 'dayjs'; import { PagerDto } from '~/common/dto/pager.dto'; @@ -37,6 +38,11 @@ export class MaterialsInOutDto { @IsString() inventoryNumber: string; + @ApiProperty({ description: '库存id(产品和单价双主键决定一条库存)' }) + @IsOptional() + @IsNumber() + inventoryId: number; + @ApiProperty({ description: '单位(字典)' }) @IsNumber() @IsOptional() diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index 5351eb2..eb6ab98 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -39,7 +39,7 @@ export class MaterialsInOutEntity extends CommonEntity { productId: number; @Column({ - name: 'inOrOut', + name: 'in_or_out', type: 'tinyint', comment: '入库或出库' }) diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index aea3e81..617a80d 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -49,12 +49,17 @@ export class MaterialsInOutService { .leftJoin('materialsInOut.project', 'project') .leftJoin('materialsInOut.product', 'product') .leftJoin('product.unit', 'unit') + .leftJoin('product.files', 'productFiles') .leftJoin('product.company', 'company') .addSelect([ 'files.id', 'files.path', 'project.name', 'product.name', + 'product.productSpecification', + 'product.productNumber', + 'productFiles.id', + 'productFiles.path', 'unit.label', 'company.name' ]) @@ -84,21 +89,8 @@ export class MaterialsInOutService { * 新增 */ async create(dto: MaterialsInOutDto): Promise { - let { inOrOut, inventoryNumber, projectId } = dto; - if (Object.is(inOrOut, MaterialsInOrOutEnum.In)) { - // 入库 - inventoryNumber = await this.generateInventoryNumber(); - } else { - // 出库 - const inRecord = await this.materialsInOutRepository.findOne({ - where: { - inventoryNumber - } - }); - const { productId } = inRecord; - dto.productId = productId; - } - + let { inOrOut, inventoryNumber, projectId, inventoryId } = dto; + inventoryNumber = await this.generateInventoryNumber(inOrOut); await this.entityManager.transaction(async manager => { // 1.生成出入库记录 const { productId, quantity, unitPrice } = await manager.save(MaterialsInOutEntity, { @@ -110,7 +102,7 @@ export class MaterialsInOutService { Object.is(inOrOut, MaterialsInOrOutEnum.In) ? this.materialsInventoryService.inInventory : this.materialsInventoryService.outInventory - )({ productId, quantity, unitPrice, projectId }, manager); + )({ productId, quantity, unitPrice, projectId, inventoryId }, manager); }); } @@ -125,9 +117,41 @@ export class MaterialsInOutService { }, lock: { mode: 'pessimistic_write' } }); - await manager.update(MaterialsInOutEntity, id, { - ...data - }); + + // 修改入库记录的价格,会直接更改库存实际价格. + // 1.如果有了出库记录,不允许修改入库价格。 + if ( + Object.is(data.inOrOut, MaterialsInOrOutEnum.In) && + isDefined(data.unitPrice) && + Math.abs(Number(data.unitPrice) - Number(entity.unitPrice)) !== 0 + ) { + const outEntity = await manager.findOne(MaterialsInOutEntity, { + where: { + unitPrice: entity.unitPrice, + inOrOut: MaterialsInOrOutEnum.Out, + projectId: entity.projectId, + productId: entity.productId + } + }); + if (isDefined(outEntity)) { + throw new BusinessException(ErrorEnum.MATERIALS_IN_OUT_UNIT_PRICE_CANNOT_BE_MODIFIED); + } + await ( + Object.is(data.inOrOut, MaterialsInOrOutEnum.In) + ? this.materialsInventoryService.inInventory + : this.materialsInventoryService.outInventory + )( + { + productId: entity.productId, + quantity: 0, + unitPrice: entity.unitPrice, + projectId: entity.projectId, + changedUnitPrice: data.unitPrice + }, + manager + ); + } + let changedQuantity = 0; if (isDefined(data.quantity) && entity.quantity !== data.quantity) { if (entity.inOrOut === MaterialsInOrOutEnum.In) { @@ -165,6 +189,12 @@ export class MaterialsInOutService { manager ); } + + // 完成所有业务逻辑后,更新出入库记录 + await manager.update(MaterialsInOutEntity, id, { + ...data + }); + if (fileIds?.length) { const count = await this.storageRepository .createQueryBuilder('storage') @@ -208,7 +238,7 @@ export class MaterialsInOutService { { productId: entity.productId, quantity: entity.quantity, - unitPrice: undefined, + unitPrice: entity.unitPrice, projectId: entity.projectId }, manager @@ -261,12 +291,14 @@ export class MaterialsInOutService { * 生成库存单号 * @returns 库存单号 */ - async generateInventoryNumber() { + async generateInventoryNumber(inOrOut: MaterialsInOrOutEnum = MaterialsInOrOutEnum.In) { const prefix = ( await this.paramConfigRepository.findOne({ where: { - key: ParamConfigEnum.MaterialsInOutPrefix + key: inOrOut + ? ParamConfigEnum.InventoryNumberPrefixOut + : ParamConfigEnum.InventoryNumberPrefixIn } }) )?.value || ''; @@ -276,6 +308,7 @@ export class MaterialsInOutService { `MAX(CAST(REPLACE(materialsInOut.inventoryNumber, '${prefix}', '') AS UNSIGNED))`, 'maxInventoryNumber' ) + .where('materialsInOut.inOrOut = :inOrOut', { inOrOut }) .getRawOne(); const lastNumber = lastMaterial.maxInventoryNumber ? parseInt(lastMaterial.maxInventoryNumber.replace(prefix, '')) diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 09ea420..1a144c3 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -62,7 +62,15 @@ export class MaterialsInventoryService { .leftJoin('mio.product', 'product') .leftJoin('product.unit', 'unit') .leftJoin('product.company', 'company') - .addSelect(['project.id', 'project.name', 'product.name', 'unit.label', 'company.name']) + .addSelect([ + 'project.id', + 'project.name', + 'unit.label', + 'company.name', + 'product.name', + 'product.productSpecification', + 'product.productNumber' + ]) .where({ time: MoreThan(time[0]) }) @@ -99,7 +107,7 @@ export class MaterialsInventoryService { sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`; // 设置表头 const headers = [ - '库存编号', + '出入库单号', '公司名称', '产品名称', '单位', @@ -185,7 +193,10 @@ export class MaterialsInventoryService { } }); - const groupedData = groupBy(currentMonthProjectData, 'inventoryNumber'); + const groupedData = groupBy( + currentMonthProjectData, + record => `${record.productId}-${record.unitPrice}` + ); let number = 0; const groupedInventories = groupBy( currentProjectInventories, @@ -309,7 +320,14 @@ export class MaterialsInventoryService { .leftJoin('materialsInventory.product', 'product') .leftJoin('product.unit', 'unit') .leftJoin('product.company', 'company') - .addSelect(['project.name', 'product.name', 'unit.label', 'company.name']) + .addSelect([ + 'project.name', + 'unit.label', + 'company.name', + 'product.name', + 'product.productSpecification', + 'product.productNumber' + ]) .where('materialsInventory.isDelete = 0'); return paginate(queryBuilder, { page, @@ -337,6 +355,7 @@ export class MaterialsInventoryService { /** * 产品入库后计算最新库存 + * 请注意。产品库存需要根据产品id和价格双主键存储。因为产品价格会变化,需要分开统计。 * @param data 传入项目ID,产品ID和入库数量和单价 * @param manager 传入事务对象防止开启多重事务 */ @@ -346,13 +365,14 @@ export class MaterialsInventoryService { productId: number; quantity: number; unitPrice?: number; + changedUnitPrice?: number; }, manager: EntityManager ): Promise { - const { projectId, productId, quantity: inQuantity, unitPrice } = data; + const { projectId, productId, quantity: inQuantity, unitPrice, changedUnitPrice } = data; const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, { - where: { projectId, productId }, // 查出某个项目的某个产品的库存情况 + where: { projectId, productId, unitPrice }, // 根据项目,产品,价格查出之前的实时库存情况 lock: { mode: 'pessimistic_write' } // 开启悲观行锁,防止脏读和修改 }); @@ -373,29 +393,28 @@ export class MaterialsInventoryService { throw new Error('库存数量不合法'); } await manager.update(MaterialsInventoryEntity, id, { - quantity: newQuantity + quantity: newQuantity, + unitPrice: changedUnitPrice || undefined }); } /** * 产品出库 - * @param data 传入产品id和入库数量和单价 + * @param data 传入库存ID(一定存在。) * @param manager 传入事务对象防止开启多重事务 */ async outInventory( data: { - projectId: number; - productId: number; quantity: number; - unitPrice?: number; + inventoryId?: number; }, manager: EntityManager ): Promise { - const { projectId, productId, quantity: outQuantity } = data; + const { quantity: outQuantity, inventoryId } = data; // 开启悲观行锁,防止脏读和修改 const inventory = await manager.findOne(MaterialsInventoryEntity, { - where: { projectId, productId }, + where: { id: inventoryId }, lock: { mode: 'pessimistic_write' } }); // 检查库存剩余 @@ -406,26 +425,41 @@ export class MaterialsInventoryService { let { quantity, id } = inventory; const newQuantity = calcNumber(quantity || 0, outQuantity || 0, 'subtract'); if (isNaN(newQuantity)) { - throw new Error('库存数量不合法'); + throw new Error('库存数量不足。请检查库存或重新操作。'); } await manager.update(MaterialsInventoryEntity, id, { quantity: newQuantity }); } + /** * 删除 */ async delete(id: number): Promise { - // 合同比较重要,做逻辑删除 + // 比较重要,做逻辑删除 await this.materialsInventoryRepository.update(id, { isDelete: 1 }); } /** - * 获取单个合同信息 + * 获取某个价格的某个商品库存信息 */ async info(id: number) { const info = await this.materialsInventoryRepository .createQueryBuilder('materialsInventory') + .leftJoin('materialsInventory.project', 'project') + .leftJoin('materialsInventory.product', 'product') + .leftJoin('product.unit', 'unit') + .leftJoin('product.company', 'company') + .addSelect([ + 'project.name', + 'project.id', + 'product.id', + 'product.name', + 'unit.label', + 'company.name', + 'product.productSpecification', + 'product.productNumber' + ]) .where({ id }) diff --git a/src/modules/product/product.dto.ts b/src/modules/product/product.dto.ts index e8b4b08..85ca3fd 100644 --- a/src/modules/product/product.dto.ts +++ b/src/modules/product/product.dto.ts @@ -8,6 +8,12 @@ export class ProductDto { @IsString() name: string; + + @ApiProperty({ description: '产品规格' }) + @IsOptional() + @IsString() + productSpecification: string; + @ApiProperty({ description: '产品备注' }) @IsOptional() @IsString() @@ -47,4 +53,10 @@ export class ProductQueryDto extends IntersectionType( @IsOptional() @IsString() name?: string; + + @ApiProperty({ description: '关键字(名字/编号/规格)' }) + @IsOptional() + @IsString() + keyword?: string; + } diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts index cc90a05..6fbbce5 100644 --- a/src/modules/product/product.entity.ts +++ b/src/modules/product/product.entity.ts @@ -17,6 +17,16 @@ import pinyin from 'pinyin'; import { DictItemEntity } from '../system/dict-item/dict-item.entity'; @Entity({ name: 'product' }) export class ProductEntity extends CommonEntity { + + @Column({ + name: 'product_number', + type: 'varchar', + length: 255, + comment: '产品编号' + }) + @ApiProperty({ description: '产品编号' }) + productNumber: string; + @Column({ name: 'name', type: 'varchar', @@ -25,7 +35,16 @@ export class ProductEntity extends CommonEntity { }) @ApiProperty({ description: '产品名称' }) name: string; - + + @Column({ + name: 'product_specification', + type: 'varchar', + nullable: true, + length: 255, + comment: '产品规格' + }) + @ApiProperty({ description: '产品规格', nullable: true }) + productSpecification?: string; @Column({ name: 'remark', type: 'varchar', diff --git a/src/modules/product/product.module.ts b/src/modules/product/product.module.ts index fdc44e9..14e2367 100644 --- a/src/modules/product/product.module.ts +++ b/src/modules/product/product.module.ts @@ -4,9 +4,10 @@ import { ProductService } from './product.service'; import { ProductEntity } from './product.entity'; import { TypeOrmModule } from '@nestjs/typeorm'; import { StorageModule } from '../tools/storage/storage.module'; +import { ParamConfigModule } from '../system/param-config/param-config.module'; @Module({ - imports: [TypeOrmModule.forFeature([ProductEntity]), StorageModule], + imports: [TypeOrmModule.forFeature([ProductEntity]), StorageModule, ParamConfigModule], controllers: [ProductController], providers: [ProductService] }) diff --git a/src/modules/product/product.service.ts b/src/modules/product/product.service.ts index 88cce60..eacf1e4 100644 --- a/src/modules/product/product.service.ts +++ b/src/modules/product/product.service.ts @@ -9,6 +9,8 @@ import { Storage } from '../tools/storage/storage.entity'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; import { fieldSearch } from '~/shared/database/field-search'; +import { ParamConfigEnum } from '~/constants/enum'; +import { ParamConfigEntity } from '../system/param-config/param-config.entity'; @Injectable() export class ProductService { @@ -17,7 +19,9 @@ export class ProductService { @InjectRepository(ProductEntity) private productRepository: Repository, @InjectRepository(Storage) - private storageRepository: Repository + private storageRepository: Repository, + @InjectRepository(ParamConfigEntity) + private paramConfigRepository: Repository ) {} /** @@ -28,13 +32,13 @@ export class ProductService { pageSize, ...fields }: ProductQueryDto): Promise> { - const { company: companyName, ...ext } = fields; + const { company: companyName, keyword, ...ext } = fields; const sqb = this.productRepository .createQueryBuilder('product') .leftJoin('product.files', 'files') .leftJoin('product.company', 'company') .leftJoin('product.unit', 'unit') - .addSelect(['files.id', 'files.path', 'company.name', 'company.id','unit.id','unit.label']) + .addSelect(['files.id', 'files.path', 'company.name', 'company.id', 'unit.id', 'unit.label']) .where(fieldSearch(ext)) .andWhere('product.isDelete = 0') .addOrderBy('product.namePinyin', 'ASC'); @@ -45,6 +49,16 @@ export class ProductService { } }); } + if (keyword) { + //关键字模糊查询product的name,productNumber,productSpecification + sqb.andWhere( + '(product.name like :keyword or product.productNumber like :keyword or product.productSpecification like :keyword)', + { + keyword: `%${keyword}%` + } + ); + + } return paginate(sqb, { page, pageSize @@ -62,7 +76,9 @@ export class ProductService { if (isExsit) { throw new BusinessException(ErrorEnum.PRODUCT_EXIST); } - await this.productRepository.insert(this.productRepository.create(dto)); + await this.productRepository.insert( + this.productRepository.create({ ...dto, productNumber: await this.generateProductNumber() }) + ); } /** @@ -91,11 +107,7 @@ export class ProductService { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } // 附件要批量插入 - await manager - .createQueryBuilder() - .relation(ProductEntity, 'files') - .of(id) - .add(fileIds); + await manager.createQueryBuilder().relation(ProductEntity, 'files').of(id).add(fileIds); } }); } @@ -146,4 +158,31 @@ export class ProductService { .addAndRemove(linkedFiles, product.files); }); } + + /** + * 生成产品编号 + * @returns 产品编号 + */ + async generateProductNumber(): Promise { + const prefix = + ( + await this.paramConfigRepository.findOne({ + where: { + key: ParamConfigEnum.ProductNumberPrefix + } + }) + )?.value || ''; + const lastProduct = await this.productRepository + .createQueryBuilder('product') + .select( + `MAX(CAST(REPLACE(COALESCE(product.product_number, ''), '${prefix}', '') AS UNSIGNED))`, + 'productNumber' + ) + .getRawOne(); + const lastNumber = lastProduct.productNumber + ? parseInt(lastProduct.productNumber.replace(prefix, '')) + : 0; + const newNumber = lastNumber + 1 < 1000 ? 1000 : lastNumber + 1; + return `${prefix}${newNumber}`; + } } From ce54da72c93ab12870e8f9a9436dcbba54f4187f Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 27 Mar 2024 14:11:11 +0800 Subject: [PATCH 36/64] feat: inventory --- src/common/exceptions/biz.exception.ts | 2 +- src/constants/enum/index.ts | 12 +- src/modules/auth/guards/jwt-auth.guard.ts | 4 +- src/modules/common/base.service.ts | 2 +- .../in_out/materials_in_out.controller.ts | 10 +- .../in_out/materials_in_out.dto.ts | 22 ++-- .../in_out/materials_in_out.entity.ts | 21 +++- .../in_out/materials_in_out.service.ts | 115 ++++++++++-------- .../materials_inventory.dto.ts | 19 ++- .../materials_inventory.entity.ts | 37 +++++- .../materials_inventory.service.ts | 103 ++++++++++++++-- src/modules/product/product.entity.ts | 4 +- 12 files changed, 263 insertions(+), 88 deletions(-) diff --git a/src/common/exceptions/biz.exception.ts b/src/common/exceptions/biz.exception.ts index 11275b9..7b00f13 100644 --- a/src/common/exceptions/biz.exception.ts +++ b/src/common/exceptions/biz.exception.ts @@ -26,7 +26,7 @@ export class BusinessException extends HttpException { code, message }), - HttpStatus.OK + HttpStatus.BAD_REQUEST ); this.errorCode = Number(code); diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts index 257a9dd..70ec17b 100644 --- a/src/constants/enum/index.ts +++ b/src/constants/enum/index.ts @@ -21,8 +21,9 @@ export enum MaterialsInOrOutEnum { // 系统参数key export enum ParamConfigEnum { - InventoryNumberPrefixIn = 'inventory_number_prefix_in', - InventoryNumberPrefixOut = 'inventory_number_prefix_out', + InventoryNumberPrefix = 'inventory_number_prefix', + InventoryInOutNumberPrefixIn = 'inventory_inout_number_prefix_in', + InventoryInOutNumberPrefixOut = 'inventory_inout_number_prefix_out', ProductNumberPrefix = 'product_number_prefix' } @@ -32,3 +33,10 @@ export enum ContractStatusEnum { Approved = 1, // 已通过 Rejected = 2 // 已拒绝 } + +// 库存查询剩余咋黄台 +export enum HasInventoryStatusEnum { + All = 0, // 全部 + Yes = 1, // 有库存 + No = 2 // 无库存 +} diff --git a/src/modules/auth/guards/jwt-auth.guard.ts b/src/modules/auth/guards/jwt-auth.guard.ts index c42ff73..4349a13 100644 --- a/src/modules/auth/guards/jwt-auth.guard.ts +++ b/src/modules/auth/guards/jwt-auth.guard.ts @@ -1,4 +1,4 @@ -import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; +import { ExecutionContext, HttpException, HttpStatus, Injectable, UnauthorizedException } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { FastifyRequest } from 'fastify'; @@ -72,7 +72,7 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) { const pv = await this.authService.getPasswordVersionByUid(request.user.uid); if (pv !== `${request.user.pv}`) { // 密码版本不一致,登录期间已更改过密码 - throw new BusinessException(ErrorEnum.INVALID_LOGIN); + throw new HttpException(ErrorEnum.INVALID_LOGIN,HttpStatus.UNAUTHORIZED); } // 不允许多端登录 diff --git a/src/modules/common/base.service.ts b/src/modules/common/base.service.ts index 7b47117..7cb472d 100644 --- a/src/modules/common/base.service.ts +++ b/src/modules/common/base.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; @Injectable() export class BaseService { - generateInventoryNumber(): string { + generateInventoryInOutNumber(): string { // Generate a random inventory number return Math.floor(Math.random() * 1000000).toString(); } diff --git a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts index 406a3e5..2bf709b 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts @@ -6,7 +6,11 @@ import { MaterialsInOutService } from './materials_in_out.service'; import { MaterialsInOutEntity } from './materials_in_out.entity'; import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; import { definePermission, Perm } from '~/modules/auth/decorators/permission.decorator'; -import { MaterialsInOutQueryDto, MaterialsInOutDto, MaterialsInOutUpdateDto } from './materials_in_out.dto'; +import { + MaterialsInOutQueryDto, + MaterialsInOutDto, + MaterialsInOutUpdateDto +} from './materials_in_out.dto'; export const permissions = definePermission('materials_inventory:history_in_out', { LIST: 'list', @@ -40,8 +44,8 @@ export class MaterialsInOutController { @Post() @ApiOperation({ summary: '新增原材料出入库记录' }) @Perm(permissions.CREATE) - async create(@Body() dto: MaterialsInOutDto): Promise { - await this.materialsInOutService.create(dto); + async create(@Body() dto: MaterialsInOutDto): Promise { + return this.materialsInOutService.create(dto); } @Put(':id') diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 767a29c..a2f7fb7 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -29,14 +29,14 @@ export class MaterialsInOutDto { projectId?: number; @ApiProperty({ description: '产品Id' }) - @ValidateIf(o => !o.inventoryNumber) + @ValidateIf(o => !o.inventoryInOutNumber) @IsNumber() productId: number; @ApiProperty({ description: '原材料库存编号' }) @IsOptional() @IsString() - inventoryNumber: string; + inventoryInOutNumber: string; @ApiProperty({ description: '库存id(产品和单价双主键决定一条库存)' }) @IsOptional() @@ -80,6 +80,11 @@ export class MaterialsInOutDto { @IsString() issuanceNumber: string; + @ApiProperty({ description: '库存位置' }) + @IsOptional() + @IsString() + position: string; + @IsOptional() @IsString() @ApiProperty({ description: '备注' }) @@ -98,11 +103,12 @@ export class MaterialsInOutQueryDto extends PagerDto { // @IsString() @Transform(params => { // 开始和结束时间用的是一天的开始和一天的结束的时分秒 - const date = params.value; - return [ - date ? `${formatToDate(dayjs(date).startOf('month'))} 00:00:00` : null, - date ? `${formatToDate(dayjs(date).endOf('month'))} 23:59:59` : null - ]; + return params.value + ? [ + params.value[0] ? `${formatToDate(params.value[0], 'YYYY-MM-DD')} 00:00:00` : null, + params.value[1] ? `${formatToDate(params.value[1], 'YYYY-MM-DD')} 23:59:59` : null + ] + : []; }) time?: string[]; @@ -129,7 +135,7 @@ export class MaterialsInOutQueryDto extends PagerDto { @ApiProperty({ description: '原材料库存编号' }) @IsOptional() @IsString() - inventoryNumber?: string; + inventoryInOutNumber?: string; @IsOptional() @IsString() diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index eb6ab98..56c4057 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -19,16 +19,17 @@ import { ProductEntity } from '~/modules/product/product.entity'; import { ProjectEntity } from '~/modules/project/project.entity'; import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity'; import { Storage } from '~/modules/tools/storage/storage.entity'; +import { MaterialsInventoryEntity } from '../materials_inventory.entity'; @Entity({ name: 'materials_in_out' }) export class MaterialsInOutEntity extends CommonEntity { @Column({ - name: 'inventory_number', + name: 'inventory_inout_number', type: 'varchar', length: 50, - comment: '原材料库存编号' + comment: '原材料出入库编号' }) - @ApiProperty({ description: '原材料库存编号' }) - inventoryNumber: string; + @ApiProperty({ description: '原材料出入库编号' }) + inventoryInOutNumber: string; @Column({ name: 'product_id', @@ -38,6 +39,14 @@ export class MaterialsInOutEntity extends CommonEntity { @ApiProperty({ description: '产品' }) productId: number; + @Column({ + name: 'inventory_id', + type: 'int', + comment: '库存' + }) + @ApiProperty({ description: '库存' }) + inventoryId: number; + @Column({ name: 'in_or_out', type: 'tinyint', @@ -127,4 +136,8 @@ export class MaterialsInOutEntity extends CommonEntity { inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } }) files: Relation; + + @ManyToOne(() => MaterialsInventoryEntity) + @JoinColumn({ name: 'inventory_id' }) + inventory: MaterialsInventoryEntity; } diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 617a80d..82e4857 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -43,26 +43,7 @@ export class MaterialsInOutService { isCreateOut, ...ext }: MaterialsInOutQueryDto): Promise> { - const sqb = this.materialsInOutRepository - .createQueryBuilder('materialsInOut') - .leftJoin('materialsInOut.files', 'files') - .leftJoin('materialsInOut.project', 'project') - .leftJoin('materialsInOut.product', 'product') - .leftJoin('product.unit', 'unit') - .leftJoin('product.files', 'productFiles') - .leftJoin('product.company', 'company') - .addSelect([ - 'files.id', - 'files.path', - 'project.name', - 'product.name', - 'product.productSpecification', - 'product.productNumber', - 'productFiles.id', - 'productFiles.path', - 'unit.label', - 'company.name' - ]) + const sqb = this.buildSearchQuery() .where(fieldSearch(ext)) .andWhere('materialsInOut.isDelete = 0') .addOrderBy('materialsInOut.createdAt', 'DESC'); @@ -85,25 +66,63 @@ export class MaterialsInOutService { return pageData; } + buildSearchQuery() { + return this.materialsInOutRepository + .createQueryBuilder('materialsInOut') + .leftJoin('materialsInOut.files', 'files') + .leftJoin('materialsInOut.project', 'project') + .leftJoin('materialsInOut.product', 'product') + .leftJoin('materialsInOut.inventory', 'inventory') + .leftJoin('product.unit', 'unit') + .leftJoin('product.files', 'productFiles') + .leftJoin('product.company', 'company') + .addSelect([ + 'inventory.id', + 'inventory.position', + 'files.id', + 'files.path', + 'project.name', + 'product.name', + 'product.productSpecification', + 'product.productNumber', + 'productFiles.id', + 'productFiles.path', + 'unit.label', + 'company.name' + ]); + } /** * 新增 */ - async create(dto: MaterialsInOutDto): Promise { - let { inOrOut, inventoryNumber, projectId, inventoryId } = dto; - inventoryNumber = await this.generateInventoryNumber(inOrOut); + async create(dto: MaterialsInOutDto): Promise { + let { + inOrOut, + inventoryInOutNumber, + projectId, + inventoryId, + position, + unitPrice, + quantity, + productId + } = dto; + inventoryInOutNumber = await this.generateInventoryInOutNumber(inOrOut); + let newRecordId; await this.entityManager.transaction(async manager => { - // 1.生成出入库记录 - const { productId, quantity, unitPrice } = await manager.save(MaterialsInOutEntity, { - ...this.materialsInOutRepository.create(dto), - inventoryNumber - }); - // 2.更新增减库存 - await ( + delete dto.position; + // 1.更新增减库存 + const inventoryEntity = await ( Object.is(inOrOut, MaterialsInOrOutEnum.In) - ? this.materialsInventoryService.inInventory - : this.materialsInventoryService.outInventory - )({ productId, quantity, unitPrice, projectId, inventoryId }, manager); + ? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService) + : this.materialsInventoryService.outInventory.bind(this.materialsInventoryService) + )({ productId, quantity, unitPrice, projectId, inventoryId, position }, manager); + // 2.生成出入库记录 + const { id } = await manager.save(MaterialsInOutEntity, { + ...this.materialsInOutRepository.create({ ...dto, inventoryId: inventoryEntity?.id }), + inventoryInOutNumber + }); + newRecordId = id; }); + return newRecordId; } /** @@ -138,8 +157,8 @@ export class MaterialsInOutService { } await ( Object.is(data.inOrOut, MaterialsInOrOutEnum.In) - ? this.materialsInventoryService.inInventory - : this.materialsInventoryService.outInventory + ? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService) + : this.materialsInventoryService.outInventory.bind(this.materialsInventoryService) )( { productId: entity.productId, @@ -189,7 +208,6 @@ export class MaterialsInOutService { manager ); } - // 完成所有业务逻辑后,更新出入库记录 await manager.update(MaterialsInOutEntity, id, { ...data @@ -232,8 +250,8 @@ export class MaterialsInOutService { // 更新库存 await ( Object.is(entity.inOrOut, MaterialsInOrOutEnum.In) - ? this.materialsInventoryService.outInventory - : this.materialsInventoryService.inInventory + ? this.materialsInventoryService.outInventory.bind(this.materialsInventoryService) + : this.materialsInventoryService.inInventory.bind(this.materialsInventoryService) )( { productId: entity.productId, @@ -253,8 +271,7 @@ export class MaterialsInOutService { * 获取单个出入库信息 */ async info(id: number) { - const info = await this.materialsInOutRepository - .createQueryBuilder('materialsInOut') + const info = await this.buildSearchQuery() .where({ id }) @@ -288,30 +305,30 @@ export class MaterialsInOutService { } /** - * 生成库存单号 - * @returns 库存单号 + * 生成库存出入库单号 + * @returns 库存出入库单号 */ - async generateInventoryNumber(inOrOut: MaterialsInOrOutEnum = MaterialsInOrOutEnum.In) { + async generateInventoryInOutNumber(inOrOut: MaterialsInOrOutEnum = MaterialsInOrOutEnum.In) { const prefix = ( await this.paramConfigRepository.findOne({ where: { key: inOrOut - ? ParamConfigEnum.InventoryNumberPrefixOut - : ParamConfigEnum.InventoryNumberPrefixIn + ? ParamConfigEnum.InventoryInOutNumberPrefixOut + : ParamConfigEnum.InventoryInOutNumberPrefixIn } }) )?.value || ''; const lastMaterial = await this.materialsInOutRepository .createQueryBuilder('materialsInOut') .select( - `MAX(CAST(REPLACE(materialsInOut.inventoryNumber, '${prefix}', '') AS UNSIGNED))`, - 'maxInventoryNumber' + `MAX(CAST(REPLACE(materialsInOut.inventoryInOutNumber, '${prefix}', '') AS UNSIGNED))`, + 'maxInventoryInOutNumber' ) .where('materialsInOut.inOrOut = :inOrOut', { inOrOut }) .getRawOne(); - const lastNumber = lastMaterial.maxInventoryNumber - ? parseInt(lastMaterial.maxInventoryNumber.replace(prefix, '')) + const lastNumber = lastMaterial.maxInventoryInOutNumber + ? parseInt(lastMaterial.maxInventoryInOutNumber.replace(prefix, '')) : 0; const newNumber = lastNumber + 1 < 1000 ? 1000 : lastNumber + 1; return `${prefix}${newNumber}`; diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts index 20bfd68..f4111ee 100644 --- a/src/modules/materials_inventory/materials_inventory.dto.ts +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -3,6 +3,7 @@ import { IsArray, IsDate, IsDateString, + IsEnum, IsIn, IsInt, IsNumber, @@ -16,6 +17,7 @@ import { Storage } from '../tools/storage/storage.entity'; import { Transform } from 'class-transformer'; import dayjs from 'dayjs'; import { formatToDate } from '~/utils'; +import { HasInventoryStatusEnum } from '~/constants/enum'; export class MaterialsInventoryDto {} @@ -23,7 +25,22 @@ export class MaterialsInventoryUpdateDto extends PartialType(MaterialsInventoryD export class MaterialsInventoryQueryDto extends IntersectionType( PagerDto, PartialType(MaterialsInventoryDto) -) {} +) { + @ApiProperty({ description: '产品名' }) + @IsOptional() + @IsString() + product: string; + + @ApiProperty({ description: '关键字' }) + @IsOptional() + @IsString() + keyword: string; + + @ApiProperty({ description: '产品名' }) + @IsOptional() + @IsEnum(HasInventoryStatusEnum) + isHasInventory: HasInventoryStatusEnum; +} export class MaterialsInventoryExportDto { @ApiProperty({ description: '项目' }) @IsOptional() diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index b8c509a..69dfc7b 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -1,8 +1,18 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, Relation } from 'typeorm'; +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { + Column, + Entity, + JoinColumn, + JoinTable, + ManyToMany, + ManyToOne, + OneToMany, + Relation +} from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; import { ProductEntity } from '../product/product.entity'; import { ProjectEntity } from '../project/project.entity'; +import { MaterialsInOutEntity } from './in_out/materials_in_out.entity'; @Entity({ name: 'materials_inventory' }) export class MaterialsInventoryEntity extends CommonEntity { @@ -22,6 +32,16 @@ export class MaterialsInventoryEntity extends CommonEntity { @ApiProperty({ description: '产品' }) productId: number; + @Column({ + name: 'position', + type: 'varchar', + length: 255, + nullable: true, + comment: '库存位置' + }) + @ApiProperty({ description: '库存位置' }) + position: string; + @Column({ name: 'quantity', type: 'int', @@ -57,4 +77,17 @@ export class MaterialsInventoryEntity extends CommonEntity { @ManyToOne(() => ProductEntity) @JoinColumn({ name: 'product_id' }) product: ProductEntity; + + @Column({ + name: 'inventory_number', + type: 'varchar', + length: 50, + comment: '库存编号' + }) + @ApiProperty({ description: '库存编号' }) + inventoryNumber: string; + + @ApiHideProperty() + @OneToMany(() => MaterialsInOutEntity, inout => inout.inventory) + materialsInOuts: Relation; } diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 1a144c3..435bd9b 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -16,14 +16,15 @@ import dayjs from 'dayjs'; import { MaterialsInOutEntity } from './in_out/materials_in_out.entity'; import { fieldSearch } from '~/shared/database/field-search'; import { groupBy, sum, uniqBy } from 'lodash'; -import { MaterialsInOrOutEnum } from '~/constants/enum'; +import { HasInventoryStatusEnum, MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum'; import { ProjectEntity } from '../project/project.entity'; import { calcNumber } from '~/utils'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; +import { ParamConfigEntity } from '../system/param-config/param-config.entity'; +import { isDefined } from 'class-validator'; @Injectable() export class MaterialsInventoryService { - const; constructor( @InjectEntityManager() private entityManager: EntityManager, @InjectRepository(MaterialsInventoryEntity) @@ -31,7 +32,9 @@ export class MaterialsInventoryService { @InjectRepository(MaterialsInOutEntity) private materialsInOutRepository: Repository, @InjectRepository(ProjectEntity) - private projectRepository: Repository + private projectRepository: Repository, + @InjectRepository(ParamConfigEntity) + private paramConfigRepository: Repository ) {} /** @@ -236,7 +239,7 @@ export class MaterialsInventoryService { ); number++; sheet.addRow([ - `${inRecord.inventoryNumber || ''}`, + `${inRecord.inventoryInOutNumber || ''}`, inRecord.product.company.name || '', inRecord.product.name || '', inRecord.product.unit.label || '', @@ -312,7 +315,10 @@ export class MaterialsInventoryService { */ async findAll({ page, - pageSize + pageSize, + product, + keyword, + isHasInventory }: MaterialsInventoryQueryDto): Promise> { const queryBuilder = this.materialsInventoryRepository .createQueryBuilder('materialsInventory') @@ -322,13 +328,34 @@ export class MaterialsInventoryService { .leftJoin('product.company', 'company') .addSelect([ 'project.name', + 'project.id', + 'unit.id', 'unit.label', + 'company.id', 'company.name', + 'product.id', 'product.name', 'product.productSpecification', 'product.productNumber' ]) .where('materialsInventory.isDelete = 0'); + if (product) { + queryBuilder.andWhere('product.name like :product', { product: `%${product}%` }); + } + if (keyword) { + queryBuilder.andWhere( + '(materialsInventory.inventoryNumber like :keyword or product.name like :keyword or product.productNumber like :keyword or product.productSpecification like :keyword)', + { + keyword: `%${keyword}%` + } + ); + } + if (isHasInventory == HasInventoryStatusEnum.Yes) { + queryBuilder.andWhere('materialsInventory.quantity > 0'); + } + if (isHasInventory == HasInventoryStatusEnum.No) { + queryBuilder.andWhere('materialsInventory.quantity = 0'); + } return paginate(queryBuilder, { page, pageSize @@ -361,6 +388,7 @@ export class MaterialsInventoryService { */ async inInventory( data: { + position?: string; projectId: number; productId: number; quantity: number; @@ -368,8 +396,15 @@ export class MaterialsInventoryService { changedUnitPrice?: number; }, manager: EntityManager - ): Promise { - const { projectId, productId, quantity: inQuantity, unitPrice, changedUnitPrice } = data; + ): Promise { + const { + projectId, + productId, + quantity: inQuantity, + unitPrice, + changedUnitPrice, + position + } = data; const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, { where: { projectId, productId, unitPrice }, // 根据项目,产品,价格查出之前的实时库存情况 @@ -378,13 +413,16 @@ export class MaterialsInventoryService { // 若不存在库存,直接新增库存 if (!exsitedInventory) { - await manager.insert(MaterialsInventoryEntity, { + const inventoryNumber = await this.generateInventoryNumber(); + const { raw } = await manager.insert(MaterialsInventoryEntity, { projectId, productId, unitPrice, + inventoryNumber, + position, quantity: inQuantity }); - return; + return manager.findOne(MaterialsInventoryEntity, { where: { id: raw.insertId } }); } // 若该项目存在库存,则该项目该产品的库存增加 let { quantity, id } = exsitedInventory; @@ -396,6 +434,8 @@ export class MaterialsInventoryService { quantity: newQuantity, unitPrice: changedUnitPrice || undefined }); + + return manager.findOne(MaterialsInventoryEntity, { where: { id } }); } /** @@ -407,14 +447,22 @@ export class MaterialsInventoryService { data: { quantity: number; inventoryId?: number; + productId: number; + unitPrice?: number; }, manager: EntityManager - ): Promise { - const { quantity: outQuantity, inventoryId } = data; - + ): Promise { + const { quantity: outQuantity, inventoryId, productId, unitPrice } = data; + let searchPayload: any = {}; + if (inventoryId) { + searchPayload.id = inventoryId; + } else { + // 删除出入库记录时,需要根据产品ID和价格查找库存 + searchPayload = { productId, unitPrice }; + } // 开启悲观行锁,防止脏读和修改 const inventory = await manager.findOne(MaterialsInventoryEntity, { - where: { id: inventoryId }, + where: searchPayload, lock: { mode: 'pessimistic_write' } }); // 检查库存剩余 @@ -430,6 +478,8 @@ export class MaterialsInventoryService { await manager.update(MaterialsInventoryEntity, id, { quantity: newQuantity }); + + return manager.findOne(MaterialsInventoryEntity, { where: { id } }); } /** @@ -467,4 +517,31 @@ export class MaterialsInventoryService { .getOne(); return info; } + + /** + * 生成库存编号 + * @returns 库存编号 + */ + async generateInventoryNumber() { + const prefix = + ( + await this.paramConfigRepository.findOne({ + where: { + key: ParamConfigEnum.InventoryNumberPrefix + } + }) + )?.value || ''; + const lastInventory = await this.materialsInventoryRepository + .createQueryBuilder('materials_inventory') + .select( + `MAX(CAST(REPLACE(materials_inventory.inventoryNumber, '${prefix}', '') AS UNSIGNED))`, + 'maxInventoryNumber' + ) + .getRawOne(); + const lastNumber = lastInventory.maxInventoryNumber + ? parseInt(lastInventory.maxInventoryNumber.replace(prefix, '')) + : 0; + const newNumber = lastNumber + 1 < 1000 ? 1000 : lastNumber + 1; + return `${prefix}${newNumber}`; + } } diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts index 6fbbce5..f7bfad6 100644 --- a/src/modules/product/product.entity.ts +++ b/src/modules/product/product.entity.ts @@ -17,7 +17,6 @@ import pinyin from 'pinyin'; import { DictItemEntity } from '../system/dict-item/dict-item.entity'; @Entity({ name: 'product' }) export class ProductEntity extends CommonEntity { - @Column({ name: 'product_number', type: 'varchar', @@ -35,7 +34,7 @@ export class ProductEntity extends CommonEntity { }) @ApiProperty({ description: '产品名称' }) name: string; - + @Column({ name: 'product_specification', type: 'varchar', @@ -45,6 +44,7 @@ export class ProductEntity extends CommonEntity { }) @ApiProperty({ description: '产品规格', nullable: true }) productSpecification?: string; + @Column({ name: 'remark', type: 'varchar', From c9437d7c6769b1a5484db73ad1eec3a72bd84146 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 27 Mar 2024 14:18:11 +0800 Subject: [PATCH 37/64] fix: minio --- src/modules/netdisk/manager/manage.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/netdisk/manager/manage.service.ts b/src/modules/netdisk/manager/manage.service.ts index d1a1209..9706867 100644 --- a/src/modules/netdisk/manager/manage.service.ts +++ b/src/modules/netdisk/manager/manage.service.ts @@ -4,7 +4,6 @@ import { isEmpty } from 'lodash'; import { ConfigKeyPaths } from '~/config'; import { generateRandomValue } from '~/utils'; import { SFileInfo, SFileInfoDetail, SFileList } from './manage.class'; -import * as Minio from 'Minio'; import { InjectMinio } from 'nestjs-minio'; import { Client } from 'minio'; @@ -29,7 +28,7 @@ export class NetDiskManageService { return new Promise((resolve, reject) => { try { const fileStream = this.minioClient.listObjects(this.ossConfig.bucket, prefix, false); - this.readStreamData(fileStream).then(respBody => { + this.readStreamData(fileStream).then(respBody => { console.log(respBody); const dirs = respBody.filter(item => 'prefix' in item).map(item => item.prefix); const files = respBody.filter(item => !('prefix' in item)); From 8b445f99250768ac11c2983991412d5e250bfba1 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 28 Mar 2024 12:48:22 +0800 Subject: [PATCH 38/64] refactor: build on production updated --- .env | 5 +- Dockerfile | 2 +- README.md | 2 +- deploy.sh | 5 +- deploy/sql/hxoa.sql | 934 ----------------- docker-compose.yml | 7 +- ecosystem.config.js | 16 +- init_data/sql/hxoa.sql | 1175 ++++++++++++++++++++++ src/migrations/1707996695540-initData.ts | 2 +- 9 files changed, 1198 insertions(+), 950 deletions(-) delete mode 100644 deploy/sql/hxoa.sql create mode 100644 init_data/sql/hxoa.sql diff --git a/.env b/.env index 43a592f..b6ff217 100644 --- a/.env +++ b/.env @@ -1,9 +1,12 @@ # app APP_NAME = Huaxin OA -APP_PORT = 7001 +APP_PORT = 8001 APP_BASE_URL = http://localhost:${APP_PORT} APP_LOCALE = zh-CN +# cluster +CPU_LEN = 1 + # logger LOGGER_LEVEL = verbose LOGGER_MAX_FILES = 31 diff --git a/Dockerfile b/Dockerfile index 58a6897..f4acb8d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ FROM node:20-slim AS base ENV PROJECT_DIR=/huaxin-admin \ DB_HOST=mysql \ - APP_PORT=7001 \ + APP_PORT=8001 \ PNPM_HOME="/pnpm" \ PATH="$PNPM_HOME:$PATH" diff --git a/README.md b/README.md index eff84ea..04406fe 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ pnpm migration:revert 4.执行sql覆盖docker中的数据库 ```bash -docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < hxoa_2024-03-07_171919.sql +docker exec -i huaxin-admin-mysql mysql -h 127.0.0.1 -u root -phuaxin123 hxoa < huaxinoa0327.sql ``` 更多细节,请移步至[官方文档](https://typeorm.io/migrations) diff --git a/deploy.sh b/deploy.sh index b39ff4d..271ef35 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,9 @@ #!/bin/bash # 拉取最新的代码 + +git config core.fileMode false + git pull -docker compose --env-file .env --env-file .env.production up -d --build \ No newline at end of file +docker-compose --env-file .env --env-file .env.production up -d --build \ No newline at end of file diff --git a/deploy/sql/hxoa.sql b/deploy/sql/hxoa.sql deleted file mode 100644 index 8839b95..0000000 --- a/deploy/sql/hxoa.sql +++ /dev/null @@ -1,934 +0,0 @@ --- MySQL dump 10.13 Distrib 8.0.28, for Win64 (x86_64) --- --- Host: 127.0.0.1 Database: hxoa --- ------------------------------------------------------ --- Server version 8.3.0 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8mb4 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `company` --- - -DROP TABLE IF EXISTS `company`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `company` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公司名称', - `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_a76c5cd486f7779bd9c319afd2` (`name`) -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `company` --- - -/*!40000 ALTER TABLE `company` DISABLE KEYS */; -INSERT INTO `company` VALUES (1,'2024-03-04 15:44:43.005593','2024-03-04 16:09:44.000000','深圳市立创电子商务有限公司',0),(4,'2024-03-04 16:05:34.701780','2024-03-04 16:05:34.701780','深圳市诚亨泰科技有限公司',0),(5,'2024-03-04 16:05:38.867786','2024-03-04 16:05:38.867786','东莞市顶源电子有限公司',0),(6,'2024-03-04 16:05:42.479027','2024-03-04 16:05:42.479027','深圳市福田区赛格电子市场金佳电子经营部',0),(7,'2024-03-04 16:05:46.775364','2024-03-04 16:05:46.775364','深圳市思界电子科技有限公司',0),(8,'2024-03-04 16:05:55.806537','2024-03-04 16:05:55.806537','广州市星翼电信科技有限公司',0),(9,'2024-03-04 16:06:03.003860','2024-03-04 16:09:49.000000','快递费',1),(10,'2024-03-04 16:06:09.788572','2024-03-04 16:06:09.788572','青岛丰喆精密模具有限公司',0),(11,'2024-03-04 16:06:12.872983','2024-03-04 16:06:12.872983','深圳嘉立创科技集团股份有限公司',0),(12,'2024-03-04 16:06:19.823410','2024-03-04 16:06:19.823410','北京特倍福电子技术有限公司',0),(13,'2024-03-04 16:06:25.937749','2024-03-04 16:06:25.937749','上海脉芯网络科技有限公司',0); -/*!40000 ALTER TABLE `company` ENABLE KEYS */; - --- --- Table structure for table `company_storage` --- - -DROP TABLE IF EXISTS `company_storage`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `company_storage` ( - `company_id` int NOT NULL, - `file_id` int NOT NULL, - PRIMARY KEY (`company_id`,`file_id`), - KEY `IDX_0958ee6ca6f52985840624bb91` (`company_id`), - KEY `IDX_bdd3a301229b9dec4b95549dfe` (`file_id`), - CONSTRAINT `FK_0958ee6ca6f52985840624bb916` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `FK_bdd3a301229b9dec4b95549dfe7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `company_storage` --- - -/*!40000 ALTER TABLE `company_storage` DISABLE KEYS */; -/*!40000 ALTER TABLE `company_storage` ENABLE KEYS */; - --- --- Table structure for table `contract` --- - -DROP TABLE IF EXISTS `contract`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `contract` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `contract_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '合同编号', - `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '合同标题', - `type` int NOT NULL COMMENT '合同类型(字典)', - `party_a` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '甲方', - `party_b` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '乙方', - `signing_date` date DEFAULT NULL, - `delivery_deadline` date DEFAULT NULL, - `status` tinyint NOT NULL DEFAULT '0' COMMENT '审核状态(字典)', - `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_a2f8461960ce0fcbd0d6551009` (`contract_number`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `contract` --- - -/*!40000 ALTER TABLE `contract` DISABLE KEYS */; -INSERT INTO `contract` VALUES (1,'2024-02-29 11:38:20.959071','2024-03-06 10:40:25.000000','2022092301','山东矿机华信智能科技有限公司',11,'山东矿机华信','青岛比特维尔','2024-01-01','2024-02-01',1,0),(2,'2024-02-29 16:11:54.286196','2024-02-29 16:50:30.000000','www','weqw',10,'rqw','rwq','2024-02-01','2024-02-28',2,0),(3,'2024-03-01 15:26:07.794697','2024-03-01 15:30:49.000000','test1211','test',13,'山东搞笑信息','齐鲁医院','2024-03-01','2024-03-20',0,1),(4,'2024-03-01 16:47:22.125670','2024-03-01 16:47:22.125670','33024242','21412412',11,'2141','41241','2024-03-01','2024-03-01',0,0); -/*!40000 ALTER TABLE `contract` ENABLE KEYS */; - --- --- Table structure for table `contract_storage` --- - -DROP TABLE IF EXISTS `contract_storage`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `contract_storage` ( - `contract_id` int NOT NULL, - `file_id` int NOT NULL, - PRIMARY KEY (`contract_id`,`file_id`), - KEY `IDX_b0a3f22af56decbc128c674447` (`contract_id`), - KEY `IDX_2fe7cda0f292b099b7e13f8f61` (`file_id`), - CONSTRAINT `FK_2fe7cda0f292b099b7e13f8f612` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`), - CONSTRAINT `FK_b0a3f22af56decbc128c674447e` FOREIGN KEY (`contract_id`) REFERENCES `contract` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `contract_storage` --- - -/*!40000 ALTER TABLE `contract_storage` DISABLE KEYS */; -/*!40000 ALTER TABLE `contract_storage` ENABLE KEYS */; - --- --- Table structure for table `materials_in_out` --- - -DROP TABLE IF EXISTS `materials_in_out`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `materials_in_out` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `product_id` int NOT NULL COMMENT '产品', - `inOrOut` tinyint NOT NULL COMMENT '入库或出库', - `time` date DEFAULT NULL COMMENT '时间', - `quantity` int NOT NULL DEFAULT '0' COMMENT '数量', - `unit_price` decimal(15,10) NOT NULL DEFAULT '0.0000000000' COMMENT '单价', - `amount` decimal(15,10) NOT NULL DEFAULT '0.0000000000' COMMENT '金额', - `agent` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '经办人', - `issuance_number` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '领料单号', - `remark` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', - `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', - `inventory_number` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '原材料库存编号', - `project_id` int DEFAULT NULL COMMENT '项目', - PRIMARY KEY (`id`), - KEY `FK_770f1c4afd9631499ccc08bd58b` (`product_id`), - KEY `FK_7a5bd19f8fd458f6336efedf765` (`project_id`), - CONSTRAINT `FK_770f1c4afd9631499ccc08bd58b` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`), - CONSTRAINT `FK_7a5bd19f8fd458f6336efedf765` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `materials_in_out` --- - -/*!40000 ALTER TABLE `materials_in_out` DISABLE KEYS */; -INSERT INTO `materials_in_out` VALUES (7,'2024-03-06 15:39:38.305753','2024-03-07 11:10:10.000000',19,0,'2024-02-16',100,557.5200000000,55752.2100000000,'蒋博',NULL,'沙湾项目',0,'KC1000',3),(16,'2024-03-06 15:50:12.315922','2024-03-07 11:10:07.000000',19,1,'2024-02-16',100,100.0000000000,55752.2100000000,'蒋博','3153019','沙湾项目',0,'KC1000',3),(17,'2024-03-06 16:06:57.397785','2024-03-06 17:09:14.325720',19,1,'2024-03-06',33,33.0000000000,33.0000000000,'3','33','33',1,'KC1000',NULL),(18,'2024-03-06 16:08:37.713482','2024-03-07 11:10:03.000000',20,0,'2024-02-16',100,557.5200000000,55752.2100000000,'蒋博',NULL,'沙湾项目',0,'KC1001',3),(19,'2024-03-06 16:30:52.126055','2024-03-07 11:09:57.000000',20,1,'2024-02-16',100,35.4000000000,3539.8200000000,'蒋博','3153019','沙湾项目',0,'KC1001',3),(20,'2024-03-06 16:34:04.845239','2024-03-07 11:09:52.000000',21,0,'2024-02-16',202,18.5800000000,3753.9800000000,'戚兆伟',NULL,'沙湾项目',0,'KC1002',3),(21,'2024-03-06 16:34:40.829659','2024-03-07 11:09:47.000000',21,1,'2024-02-16',202,18.5800000000,3753.9800000000,'戚兆伟','3153011','沙湾项目',0,'KC1002',3),(22,'2024-03-06 16:35:23.756416','2024-03-07 11:09:43.000000',21,0,'2024-02-16',200,26.5500000000,5309.7300000000,'戚兆伟',NULL,'沙湾项目',0,'KC1003',3),(23,'2024-03-06 16:35:55.157718','2024-03-07 11:09:38.000000',21,1,'2024-02-16',200,26.5500000000,5309.7300000000,'戚兆伟','3153011','沙湾项目',0,'KC1003',3),(24,'2024-03-06 16:38:52.490493','2024-03-07 11:09:34.000000',22,0,'2024-02-20',200,300.8800000000,60176.9900000000,'朱明仁',NULL,'东大项目',0,'KC1004',2),(25,'2024-03-06 16:39:55.423422','2024-03-07 11:09:30.000000',22,1,'2024-02-20',200,300.8849500000,60176.9900000000,'朱明仁','3153022','东大项目',0,'KC1004',2),(26,'2024-03-06 16:41:20.939530','2024-03-07 11:09:27.000000',23,0,'2024-02-19',1,22005.7500000000,22005.7500000000,'朱明仁',NULL,'东大项目',0,'KC1005',2),(27,'2024-03-06 16:41:50.876715','2024-03-07 11:08:31.000000',23,1,'2024-02-19',1,22005.7500000000,22005.7500000000,'朱明仁','3153023','东大项目',0,'KC1005',2),(28,'2024-03-07 09:49:54.511550','2024-03-07 09:49:54.511550',24,0,'2024-02-16',800,0.9609250000,768.7400000000,'朱明仁',NULL,'星火项目',0,'KC1006',1); -/*!40000 ALTER TABLE `materials_in_out` ENABLE KEYS */; - --- --- Table structure for table `materials_in_out_storage` --- - -DROP TABLE IF EXISTS `materials_in_out_storage`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `materials_in_out_storage` ( - `materials_in_out_id` int NOT NULL, - `file_id` int NOT NULL, - PRIMARY KEY (`materials_in_out_id`,`file_id`), - KEY `IDX_9df13ab4d4747575c310668581` (`materials_in_out_id`), - KEY `IDX_96c00bfbcd71e93a6cc070e8e6` (`file_id`), - CONSTRAINT `FK_96c00bfbcd71e93a6cc070e8e6c` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`), - CONSTRAINT `FK_9df13ab4d4747575c3106685810` FOREIGN KEY (`materials_in_out_id`) REFERENCES `materials_in_out` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `materials_in_out_storage` --- - -/*!40000 ALTER TABLE `materials_in_out_storage` DISABLE KEYS */; -/*!40000 ALTER TABLE `materials_in_out_storage` ENABLE KEYS */; - --- --- Table structure for table `materials_inventory` --- - -DROP TABLE IF EXISTS `materials_inventory`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `materials_inventory` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `company_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公司名称', - `product` int NOT NULL COMMENT '产品名称(字典)', - `unit` int NOT NULL COMMENT '单位(字典)', - `previous_inventory_quantity` int NOT NULL DEFAULT '0' COMMENT '之前的库存数量', - `previous_unit_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '之前的单价', - `previous_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '之前的金额', - `inventory_time` date DEFAULT NULL COMMENT '入库时间', - `inventory_quantity` int NOT NULL DEFAULT '0' COMMENT '入库数量', - `inventory_unit_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '入库单价', - `inventory_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '入库金额', - `out_time` date DEFAULT NULL COMMENT '出库时间', - `out_quantity` int NOT NULL DEFAULT '0' COMMENT '出库数量', - `out_unit_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '出库单价', - `out_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '出库金额', - `current_inventory_quantity` int NOT NULL DEFAULT '0' COMMENT '现在的结存数量', - `current_unit_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '现在的单价', - `current_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '现在的金额', - `agent` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '经办人', - `issuance_number` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '领料单号', - `project` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', - `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `materials_inventory` --- - -/*!40000 ALTER TABLE `materials_inventory` DISABLE KEYS */; -INSERT INTO `materials_inventory` VALUES (1,'2024-03-04 13:47:10.258131','2024-03-04 14:15:15.373283','北京特倍福电子技术有限公司',1,14,0,0.00,0.00,'2024-02-16',100,557.52,55752.21,'2024-02-16',100,557.52,55752.21,0,0.00,0.00,'蒋博','3153019','沙湾项目','沙湾项目',0); -/*!40000 ALTER TABLE `materials_inventory` ENABLE KEYS */; - --- --- Table structure for table `product` --- - -DROP TABLE IF EXISTS `product`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `product` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '产品名称', - `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', - `company_id` int DEFAULT NULL COMMENT '所属公司', - `name_pinyin` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '产品名称的拼音', - `unit_id` int DEFAULT NULL COMMENT '单位(字典)', - PRIMARY KEY (`id`), - KEY `FK_a0503db1630a5b8a4d7deabd556` (`company_id`), - KEY `FK_b15422982adca3bf53adfb535de` (`unit_id`), - CONSTRAINT `FK_a0503db1630a5b8a4d7deabd556` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`), - CONSTRAINT `FK_b15422982adca3bf53adfb535de` FOREIGN KEY (`unit_id`) REFERENCES `sys_dict_item` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `product` --- - -/*!40000 ALTER TABLE `product` DISABLE KEYS */; -INSERT INTO `product` VALUES (1,'2024-03-04 16:47:37.604035','2024-03-05 21:52:59.000000','位移传感器',0,10,'weiyichuanganqi',NULL),(2,'2024-03-04 16:47:41.483273','2024-03-06 09:13:03.000000','磁环',1,6,'cihuan',NULL),(3,'2024-03-04 16:47:44.911165','2024-03-05 21:52:52.000000','天线',0,5,'tianxian',NULL),(4,'2024-03-04 16:47:48.398543','2024-03-05 21:53:17.000000','集成电路',0,8,'jichengdianlu',NULL),(5,'2024-03-05 09:09:05.015757','2024-03-05 09:27:28.000000','更新',1,NULL,NULL,NULL),(12,'2024-03-05 09:25:29.584423','2024-03-06 09:12:02.000000','巴伦',0,13,'balun',15),(13,'2024-03-05 13:53:15.998630','2024-03-06 09:14:13.000000','极薄煤层控制器',0,10,'jibaomeicengkongzhiqi',14),(14,'2024-03-05 16:05:30.485017','2024-03-06 09:13:59.000000','天线',0,13,'tianxian',15),(15,'2024-03-05 17:21:20.378006','2024-03-05 21:52:57.000000','USB智能充电线',0,7,'USBzhinengchongdianxian',NULL),(16,'2024-03-05 17:24:03.148627','2024-03-05 17:30:48.000000','33',1,5,NULL,NULL),(17,'2024-03-05 17:30:32.450320','2024-03-05 17:30:50.000000','test',1,5,'test',NULL),(18,'2024-03-05 21:52:11.477508','2024-03-05 21:53:34.000000','新增',1,NULL,'xinzeng',NULL),(19,'2024-03-06 08:53:25.600367','2024-03-06 09:13:39.000000','位移传感器',0,12,'weiyichuanganqi',14),(20,'2024-03-06 09:12:47.327409','2024-03-06 09:12:47.327409','磁环',0,12,'cihuan',14),(21,'2024-03-06 09:13:21.382776','2024-03-06 09:13:27.000000','集成电路',0,13,'jichengdianlu',15),(22,'2024-03-06 16:38:06.999498','2024-03-06 16:38:14.000000','电磁阀驱动器',0,10,'diancifaqudongqi',14),(23,'2024-03-06 16:40:32.859846','2024-03-06 16:40:32.859846','电子元器件',0,11,'dianziyuanqijian',21),(24,'2024-03-07 09:48:35.854273','2024-03-07 09:48:35.854273','排针',0,1,'paizhen',15); -/*!40000 ALTER TABLE `product` ENABLE KEYS */; - --- --- Table structure for table `product_storage` --- - -DROP TABLE IF EXISTS `product_storage`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `product_storage` ( - `product_id` int NOT NULL, - `file_id` int NOT NULL, - PRIMARY KEY (`product_id`,`file_id`), - KEY `IDX_6dd288598f0a0ea3f72f31cb42` (`product_id`), - KEY `IDX_eecbd68d7d4d565baecee2d76c` (`file_id`), - CONSTRAINT `FK_6dd288598f0a0ea3f72f31cb422` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `FK_eecbd68d7d4d565baecee2d76c7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `product_storage` --- - -/*!40000 ALTER TABLE `product_storage` DISABLE KEYS */; -INSERT INTO `product_storage` VALUES (1,124); -/*!40000 ALTER TABLE `product_storage` ENABLE KEYS */; - --- --- Table structure for table `project` --- - -DROP TABLE IF EXISTS `project`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `project` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目名称', - `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', - PRIMARY KEY (`id`), - UNIQUE KEY `IDX_dedfea394088ed136ddadeee89` (`name`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `project` --- - -/*!40000 ALTER TABLE `project` DISABLE KEYS */; -INSERT INTO `project` VALUES (1,'2024-03-07 09:35:15.276345','2024-03-07 09:36:01.000000','星火项目',0),(2,'2024-03-07 09:35:20.004729','2024-03-07 09:35:20.004729','东大项目',0),(3,'2024-03-07 09:35:29.213057','2024-03-07 09:35:29.213057','沙湾煤业项目',0); -/*!40000 ALTER TABLE `project` ENABLE KEYS */; - --- --- Table structure for table `project_storage` --- - -DROP TABLE IF EXISTS `project_storage`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `project_storage` ( - `project_id` int NOT NULL, - `file_id` int NOT NULL, - PRIMARY KEY (`project_id`,`file_id`), - KEY `IDX_9058e954f8f09e2cfa2261c1f2` (`project_id`), - KEY `IDX_ac08ac8e4f973873f03dafaca2` (`file_id`), - CONSTRAINT `FK_9058e954f8f09e2cfa2261c1f26` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `FK_ac08ac8e4f973873f03dafaca2b` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `project_storage` --- - -/*!40000 ALTER TABLE `project_storage` DISABLE KEYS */; -INSERT INTO `project_storage` VALUES (1,128); -/*!40000 ALTER TABLE `project_storage` ENABLE KEYS */; - --- --- Table structure for table `sys_captcha_log` --- - -DROP TABLE IF EXISTS `sys_captcha_log`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_captcha_log` ( - `id` int NOT NULL AUTO_INCREMENT, - `user_id` int DEFAULT NULL, - `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, - `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, - `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_captcha_log` --- - -/*!40000 ALTER TABLE `sys_captcha_log` DISABLE KEYS */; -/*!40000 ALTER TABLE `sys_captcha_log` ENABLE KEYS */; - --- --- Table structure for table `sys_config` --- - -DROP TABLE IF EXISTS `sys_config`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_config` ( - `id` int NOT NULL AUTO_INCREMENT, - `key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_2c363c25cf99bcaab3a7f389ba` (`key`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_config` --- - -/*!40000 ALTER TABLE `sys_config` DISABLE KEYS */; -INSERT INTO `sys_config` VALUES (1,'sys_user_initPassword','初始密码','123456','创建管理员账号的初始密码','2023-11-10 00:31:44.154921','2023-11-10 00:31:44.161263'),(2,'sys_api_token','API Token','huaxin-admin','用于请求 @ApiToken 的控制器','2023-11-10 00:31:44.154921','2024-01-29 09:52:27.000000'),(3,'companyName','公司名称','华信智能','菜单侧栏公司的名称','2024-03-06 13:06:47.347660','2024-03-06 13:07:18.000000'),(4,'materials_in_out_prefix','出入库记录开头编号','KC','出入库记录开头编号','2024-03-06 14:50:04.844992','2024-03-06 14:50:04.844992'); -/*!40000 ALTER TABLE `sys_config` ENABLE KEYS */; - --- --- Table structure for table `sys_dept` --- - -DROP TABLE IF EXISTS `sys_dept`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_dept` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, - `orderNo` int DEFAULT '0', - `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT '', - `parentId` int DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_c75280b01c49779f2323536db67` (`parentId`) USING BTREE, - CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_dept` --- - -/*!40000 ALTER TABLE `sys_dept` DISABLE KEYS */; -INSERT INTO `sys_dept` VALUES (1,'山东矿机华信智能科技',1,'1.',NULL,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(2,'计算机开发部',1,'1.2.',1,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(3,'行政部',2,'1.3.',1,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(4,'商务部',3,'1.4.',1,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(5,'财务部',4,'1.5.',1,'2023-11-10 00:31:43.996025','2024-03-06 17:16:48.000000'),(6,'山东矿机华能装备制造',2,'6.',NULL,'2023-11-10 00:31:43.996025','2024-03-06 17:17:30.000000'),(8,'研发部',1,'6.8.',6,'2023-11-10 00:31:43.996025','2024-03-06 17:17:30.000000'),(9,'市场部',1,'6.9.',6,'2023-11-10 00:31:43.996025','2024-03-06 17:17:30.000000'); -/*!40000 ALTER TABLE `sys_dept` ENABLE KEYS */; - --- --- Table structure for table `sys_dict` --- - -DROP TABLE IF EXISTS `sys_dict`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_dict` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `create_by` int NOT NULL COMMENT '创建者', - `update_by` int NOT NULL COMMENT '更新者', - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint NOT NULL DEFAULT '1', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_d112365748f740ee260b65ce91` (`name`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_dict` --- - -/*!40000 ALTER TABLE `sys_dict` DISABLE KEYS */; -/*!40000 ALTER TABLE `sys_dict` ENABLE KEYS */; - --- --- Table structure for table `sys_dict_item` --- - -DROP TABLE IF EXISTS `sys_dict_item`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_dict_item` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `create_by` int NOT NULL COMMENT '创建者', - `update_by` int NOT NULL COMMENT '更新者', - `label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `order` int DEFAULT NULL COMMENT '字典项排序', - `status` tinyint NOT NULL DEFAULT '1', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `type_id` int DEFAULT NULL, - `orderNo` int DEFAULT NULL COMMENT '字典项排序', - `deleted_at` datetime(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_d68ea74fcb041c8cfd1fd659844` (`type_id`) USING BTREE, - CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_dict_item` --- - -/*!40000 ALTER TABLE `sys_dict_item` DISABLE KEYS */; -INSERT INTO `sys_dict_item` VALUES (1,'2024-01-29 01:24:51.846135','2024-01-29 02:23:19.000000',1,1,'男','1',0,1,'性别男',1,3,'2024-03-01 15:28:21.702930'),(2,'2024-01-29 01:32:58.458741','2024-01-29 01:58:20.000000',1,1,'女','0',1,1,'性别女',1,2,'2024-03-01 15:28:21.702930'),(3,'2024-01-29 01:59:17.805394','2024-01-29 14:37:18.000000',1,1,'人妖王','3',NULL,1,'安布里奥·伊万科夫',1,0,'2024-03-01 15:28:21.702930'),(5,'2024-01-29 02:13:01.782466','2024-01-29 02:13:01.782466',1,1,'显示','1',NULL,1,'显示菜单',2,0,'2024-03-01 15:28:21.702930'),(6,'2024-01-29 02:13:31.134721','2024-01-29 02:13:31.134721',1,1,'隐藏','0',NULL,1,'隐藏菜单',2,0,'2024-03-01 15:28:21.702930'),(10,'2024-02-28 16:39:44.977246','2024-02-29 15:56:02.670095',1,1,'商务合同','business',NULL,1,'商务合同',3,0,'2024-03-01 15:28:21.702930'),(11,'2024-02-28 16:42:43.539979','2024-02-29 15:56:07.676659',1,1,'销售合同','sales',NULL,1,'',3,1,'2024-03-01 15:28:21.702930'),(12,'2024-02-28 16:42:58.224299','2024-02-29 15:56:05.815675',1,1,'租赁合同','Lease',NULL,1,NULL,3,2,'2024-03-01 15:28:21.702930'),(13,'2024-02-28 16:43:26.311650','2024-02-29 15:56:10.462447',1,1,'服务合同','service',NULL,1,NULL,3,3,'2024-03-01 15:28:21.702930'),(14,'2024-03-04 13:42:26.688441','2024-03-04 13:42:26.688441',1,1,'件','unit_jian',NULL,1,NULL,5,0,'2024-03-04 13:42:26.688441'),(15,'2024-03-04 13:42:38.298733','2024-03-04 13:42:38.298733',1,1,'个','unit_ge',NULL,1,NULL,5,1,'2024-03-04 13:42:38.298733'),(16,'2024-03-04 13:43:30.965353','2024-03-04 13:43:30.965353',1,1,'千克','unit_qianke',NULL,1,NULL,5,2,'2024-03-04 13:43:30.965353'),(17,'2024-03-04 13:43:44.353125','2024-03-04 13:43:44.353125',1,1,'克','unit_ke',NULL,1,NULL,5,3,'2024-03-04 13:43:44.353125'),(18,'2024-03-04 13:43:56.643339','2024-03-04 13:43:56.643339',1,1,'升','unit_sheng',NULL,1,NULL,5,4,'2024-03-04 13:43:56.643339'),(19,'2024-03-04 13:44:09.242901','2024-03-04 13:44:09.242901',1,1,'毫升','unit_haosheng',NULL,1,NULL,5,5,'2024-03-04 13:44:09.242901'),(20,'2024-03-04 13:44:26.620837','2024-03-04 13:44:29.000000',1,1,'卷','unit_juan',NULL,1,NULL,5,6,'2024-03-04 13:44:29.654314'),(21,'2024-03-04 14:10:38.216659','2024-03-04 14:10:54.000000',1,1,'批','unit_pi',NULL,1,NULL,5,7,'2024-03-04 14:10:54.729114'),(22,'2024-03-04 14:10:48.864655','2024-03-04 14:10:48.864655',1,1,'片','unit_pian',NULL,1,NULL,5,8,'2024-03-04 14:10:48.864655'),(23,'2024-03-04 14:11:06.319281','2024-03-04 14:11:06.319281',1,1,'套','unit_tao',NULL,1,NULL,5,9,'2024-03-04 14:11:06.319281'); -/*!40000 ALTER TABLE `sys_dict_item` ENABLE KEYS */; - --- --- Table structure for table `sys_dict_type` --- - -DROP TABLE IF EXISTS `sys_dict_type`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_dict_type` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `create_by` int NOT NULL COMMENT '创建者', - `update_by` int NOT NULL COMMENT '更新者', - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint NOT NULL DEFAULT '1', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `deleted_at` datetime(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_74d0045ff7fab9f67adc0b1bda` (`code`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_dict_type` --- - -/*!40000 ALTER TABLE `sys_dict_type` DISABLE KEYS */; -INSERT INTO `sys_dict_type` VALUES (1,'2024-01-28 08:19:12.777447','2024-02-08 13:05:10.000000',1,1,'性别',1,'性别单选','sys_user_gender','2024-03-01 15:28:21.689753'),(2,'2024-01-28 08:38:41.235185','2024-01-29 02:11:33.000000',1,1,'菜单显示状态',1,'菜单显示状态','sys_show_hide','2024-03-01 15:28:21.689753'),(3,'2024-02-28 16:38:27.311577','2024-03-04 13:26:29.000000',1,1,'合同类型',1,'合同类型','contract_type','2024-03-04 13:26:29.911469'),(5,'2024-03-04 13:41:05.156027','2024-03-04 13:41:05.156027',1,1,'单位',1,'材料盘点表等单位。件。个','unit','2024-03-04 13:41:05.156027'); -/*!40000 ALTER TABLE `sys_dict_type` ENABLE KEYS */; - --- --- Table structure for table `sys_login_log` --- - -DROP TABLE IF EXISTS `sys_login_log`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_login_log` ( - `id` int NOT NULL AUTO_INCREMENT, - `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `ua` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `provider` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `user_id` int DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_3029712e0df6a28edaee46fd470` (`user_id`) USING BTREE, - CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_login_log` --- - -/*!40000 ALTER TABLE `sys_login_log` DISABLE KEYS */; -INSERT INTO `sys_login_log` VALUES (1,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 11:49:38.330842','2024-02-28 11:49:38.330842',1),(2,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:06:01.450911','2024-02-28 13:06:01.450911',1),(3,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:08:18.525617','2024-02-28 13:08:18.525617',1),(4,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:10:51.368580','2024-02-28 13:10:51.368580',1),(5,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:53:17.506614','2024-02-28 13:53:17.506614',1),(6,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-28 13:53:53.201053','2024-02-28 13:53:53.201053',1),(7,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-02-29 16:25:14.762388','2024-02-29 16:25:14.762388',1),(8,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36','内网IP',NULL,'2024-02-29 16:26:09.106911','2024-02-29 16:26:09.106911',1),(9,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 10:52:33.093110','2024-03-01 10:52:33.093110',1),(10,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 10:53:09.633370','2024-03-01 10:53:09.633370',1),(11,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:45:36.933407','2024-03-01 15:45:36.933407',1),(12,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:04.780541','2024-03-01 15:57:04.780541',1),(13,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:11.280355','2024-03-01 15:57:11.280355',1),(14,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:18.351492','2024-03-01 15:57:18.351492',1),(15,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:29.111440','2024-03-01 15:57:29.111440',1),(16,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 15:57:33.903797','2024-03-01 15:57:33.903797',1),(17,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-01 16:53:24.824327','2024-03-01 16:53:24.824327',1),(18,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 11:36:22.860536','2024-03-04 11:36:22.860536',1),(19,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 13:14:12.256792','2024-03-04 13:14:12.256792',1),(20,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 13:15:42.383909','2024-03-04 13:15:42.383909',1),(21,'192.168.48.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 13:16:04.702698','2024-03-04 13:16:04.702698',1),(22,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 15:42:54.327613','2024-03-04 15:42:54.327613',1),(23,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-04 17:25:42.137701','2024-03-04 17:25:42.137701',1),(24,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-05 14:34:08.444527','2024-03-05 14:34:08.444527',1),(25,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-06 08:45:04.588314','2024-03-06 08:45:04.588314',1),(26,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-06 17:17:55.139092','2024-03-06 17:17:55.139092',1),(27,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-07 09:14:33.829960','2024-03-07 09:14:33.829960',1),(28,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-07 11:11:38.239629','2024-03-07 11:11:38.239629',1),(29,'127.0.0.1','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0','内网IP',NULL,'2024-03-07 11:46:14.622820','2024-03-07 11:46:14.622820',1); -/*!40000 ALTER TABLE `sys_login_log` ENABLE KEYS */; - --- --- Table structure for table `sys_menu` --- - -DROP TABLE IF EXISTS `sys_menu`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_menu` ( - `id` int NOT NULL AUTO_INCREMENT, - `parent_id` int DEFAULT NULL, - `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `type` tinyint NOT NULL DEFAULT '0', - `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', - `order_no` int DEFAULT '0', - `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `keep_alive` tinyint NOT NULL DEFAULT '1', - `show` tinyint NOT NULL DEFAULT '1', - `status` tinyint NOT NULL DEFAULT '1', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `is_ext` tinyint NOT NULL DEFAULT '0', - `ext_open_mode` tinyint NOT NULL DEFAULT '1', - `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_menu` --- - -/*!40000 ALTER TABLE `sys_menu` DISABLE KEYS */; -INSERT INTO `sys_menu` VALUES (1,NULL,'/system','系统管理','',0,'ant-design:setting-outlined',254,'',1,1,1,'2023-11-10 00:31:44.023393','2024-02-29 10:41:29.000000',0,1,NULL),(2,1,'/system/user','用户管理','system:user:list',1,'ant-design:user-outlined',0,'system/user/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:10:30.000000',0,1,NULL),(3,1,'/system/role','角色管理','system:role:list',1,'ep:user',1,'system/role/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:11:02.000000',0,1,NULL),(4,1,'/system/menu','菜单管理','system:menu:list',1,'ep:menu',2,'system/menu/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:11:18.000000',0,1,NULL),(5,1,'/system/monitor','系统监控','',0,'ep:monitor',5,'',1,1,1,'2023-11-10 00:31:44.023393','2024-01-27 18:53:44.567023',0,1,NULL),(6,5,'/system/monitor/online','在线用户','system:online:list',1,'',0,'system/monitor/online/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-15 22:13:59.519267',0,1,NULL),(7,5,'/sys/monitor/login-log','登录日志','system:log:login:list',1,'',0,'system/monitor/log/login/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-15 22:14:02.610719',0,1,NULL),(8,5,'/system/monitor/serve','服务监控','system:serve:stat',1,'',4,'system/monitor/serve/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-15 22:14:05.606355',0,1,NULL),(9,1,'/system/schedule','任务调度','',0,'ant-design:schedule-filled',6,'',1,1,1,'2023-11-10 00:31:44.023393','2024-01-27 18:53:52.967983',0,1,NULL),(10,9,'/system/task','任务管理','',1,'',0,'system/schedule/task/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:14:39.000000',0,1,NULL),(11,9,'/system/task/log','任务日志','system:task:list',1,'',0,'system/schedule/log/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:15:01.000000',0,1,NULL),(12,NULL,'/document','文档','',0,'ion:tv-outline',2,'',1,0,1,'2023-11-10 00:31:44.023393','2024-02-28 11:51:51.000000',0,1,NULL),(14,12,'https://www.typeorm.org/','Typeorm中文文档(外链)',NULL,1,'',3,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-30 18:39:53.000000',1,1,NULL),(15,12,'https://docs.nestjs.cn/','Nest.js中文文档(内嵌)','',1,'',4,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-30 18:40:43.000000',1,2,NULL),(20,2,NULL,'新增','system:user:create',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(21,2,'','删除','system:user:delete',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(22,2,'','更新','system:user:update',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(23,2,'','查询','system:user:read',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(24,3,'','新增','system:role:create',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(25,3,'','删除','system:role:delete',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(26,3,'','修改','system:role:update',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(27,3,'','查询','system:role:read',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(28,4,NULL,'新增','system:menu:create',2,NULL,0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(29,4,NULL,'删除','system:menu:delete',2,NULL,0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(30,4,'','修改','system:menu:update',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(31,4,NULL,'查询','system:menu:read',2,NULL,0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(32,6,'','下线','system:online:kick',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(34,10,'','新增','system:task:create',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(35,10,'','删除','system:task:delete',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(36,10,'','执行一次','system:task:once',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(37,10,'','查询','system:task:read',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(38,10,'','运行','system:task:start',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(39,10,'','暂停','system:task:stop',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(40,10,'','更新','system:task:update',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(41,7,'','查询登录日志','system:log:login:list',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(42,7,'','查询任务日志','system:log:task:list',2,'',0,'',1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(43,NULL,'/about','关于','',1,'ant-design:info-circle-outlined',260,'account/about',1,1,1,'2023-11-10 00:31:44.023393','2024-02-10 09:35:41.000000',0,1,NULL),(48,NULL,'/tool','系统工具',NULL,0,'ant-design:tool-outlined',255,'',1,1,1,'2023-11-10 00:31:44.023393','2024-02-29 10:41:25.000000',0,1,NULL),(49,48,'/tool/email','邮件工具','system:tools:email',1,'ant-design:send-outlined',1,'tool/email/index',1,0,1,'2023-11-10 00:31:44.023393','2024-02-28 11:51:38.000000',0,1,NULL),(50,49,NULL,'发送邮件','tools:email:send',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(51,48,'/tool/storage','存储管理','tool:storage:list',1,'ant-design:appstore-outlined',2,'tool/storage/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-25 00:59:17.000000',0,1,NULL),(52,51,NULL,'文件上传','upload:upload',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-25 01:04:08.000000',0,1,NULL),(53,51,NULL,'文件删除','tool:storage:delete',2,'',2,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-25 00:56:01.000000',0,1,NULL),(54,2,NULL,'修改密码','system:user:password',2,'',5,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(56,1,'/system/dict-type','字典管理','system:dict-type:list',1,'ant-design:book-outlined',4,'system/dict-type/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:12.000000',0,1,NULL),(57,56,NULL,'新增','system:dict-type:create',2,'',1,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:20.000000',0,1,NULL),(58,56,NULL,'更新','system:dict-type:update',2,'',2,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:26.000000',0,1,NULL),(59,56,NULL,'删除','system:dict-type:delete',2,'',3,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:42.000000',0,1,NULL),(60,56,NULL,'查询','system:dict-type:info',2,'',4,NULL,1,1,1,'2023-11-10 00:31:44.023393','2024-01-28 09:07:36.000000',0,1,NULL),(61,1,'/system/dept','部门管理','system:dept:list',1,'ant-design:deployment-unit-outlined',3,'system/dept/index',1,1,1,'2023-11-10 00:31:44.023393','2024-01-17 03:11:55.000000',0,1,NULL),(62,61,NULL,'新增','system:dept:create',2,'',1,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(63,61,NULL,'更新','system:dept:update',2,'',2,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(64,61,NULL,'删除','system:dept:delete',2,'',3,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(65,61,NULL,'查询','system:dept:read',2,'',4,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(68,5,'/health','健康检查','',1,'',4,'',1,0,1,'2023-11-10 00:31:44.023393','2024-01-27 18:53:33.352155',0,1,NULL),(69,68,NULL,'网络','app:health:network',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(70,68,NULL,'数据库','app:health: database',2,'',0,NULL,1,1,1,'2023-11-10 00:31:44.023393','2023-11-10 00:31:44.034474',0,1,NULL),(86,1,'/param-config','参数配置','system:param-config:list',1,'ep:edit',255,'system/param-config/index',0,1,1,'2024-01-10 17:34:52.569663','2024-01-19 02:11:27.000000',0,1,NULL),(87,86,NULL,'查询','system:param-config:read',2,'',255,NULL,1,1,1,'2024-01-10 17:39:20.983241','2024-01-10 17:39:20.983241',0,1,NULL),(88,86,NULL,'新增','system:param-config:create',2,'',255,NULL,1,1,1,'2024-01-10 17:39:57.543510','2024-01-10 17:39:57.543510',0,1,NULL),(89,86,NULL,'更新','system:param-config:update',2,'',255,NULL,1,1,1,'2024-01-10 17:40:27.355944','2024-01-10 17:40:27.355944',0,1,NULL),(92,86,NULL,'删除','system:param-config:delete',2,'',255,NULL,1,1,1,'2024-01-10 17:57:32.059887','2024-01-10 17:57:32.059887',0,1,NULL),(107,1,'system/dict-item/:id','字典项管理','system:dict-item:list',1,'ant-design:facebook-outlined',255,'system/dict-item/index',0,0,1,'2024-01-28 09:21:17.409532','2024-01-30 13:09:47.000000',0,1,'字典管理'),(108,107,NULL,'新增','system:dict-item:create',2,'',255,NULL,1,1,1,'2024-01-28 09:22:39.401758','2024-01-28 22:38:36.000000',0,1,NULL),(109,107,NULL,'更新','system:dict-item:update',2,'',255,NULL,1,1,1,'2024-01-28 09:26:43.911886','2024-01-28 09:26:43.911886',0,1,NULL),(110,107,NULL,'删除','system:dict-item:delete',2,'',255,NULL,1,1,1,'2024-01-28 09:27:28.535225','2024-01-28 09:27:28.535225',0,1,NULL),(111,107,NULL,'查询','system:dict-item:info',2,'',255,NULL,1,1,1,'2024-01-28 09:27:43.894820','2024-01-28 09:27:43.894820',0,1,NULL),(112,12,'https://antdv.com/components/overview-cn','antdv文档(内嵌)',NULL,1,'',255,NULL,1,1,1,'2024-01-29 09:23:08.407723','2024-01-30 18:41:19.000000',1,2,NULL),(115,NULL,'netdisk','网盘管理',NULL,0,'ant-design:cloud-server-outlined',255,NULL,1,0,1,'2024-02-10 08:00:02.394616','2024-02-28 11:51:21.000000',0,1,NULL),(116,115,'manage','文件管理','netdisk:manage:list',1,'',252,'netdisk/manage',0,1,1,'2024-02-10 08:03:49.837348','2024-02-10 09:34:41.000000',0,1,NULL),(117,116,NULL,'创建文件或文件夹','netdisk:manage:create',2,'',255,NULL,1,1,1,'2024-02-10 08:40:22.317257','2024-02-10 08:40:22.317257',0,1,NULL),(118,116,NULL,'查看文件','netdisk:manage:read',2,'',255,NULL,1,1,1,'2024-02-10 08:41:22.008015','2024-02-10 08:41:22.008015',0,1,NULL),(119,116,NULL,'更新','netdisk:manage:update',2,'',255,NULL,1,1,1,'2024-02-10 08:41:50.691643','2024-02-10 08:41:50.691643',0,1,NULL),(120,116,NULL,'删除','netdisk:manage:delete',2,'',255,NULL,1,1,1,'2024-02-10 08:42:09.480601','2024-02-10 08:42:09.480601',0,1,NULL),(121,116,NULL,'获取文件上传token','netdisk:manage:token',2,'',255,NULL,1,1,1,'2024-02-10 08:42:57.688104','2024-02-10 08:42:57.688104',0,1,NULL),(122,116,NULL,'添加文件备注','netdisk:manage:mark',2,'',255,NULL,1,1,1,'2024-02-10 08:43:40.117321','2024-02-10 08:43:40.117321',0,1,NULL),(123,116,NULL,'下载文件','netdisk:manage:download',2,'',255,NULL,1,1,1,'2024-02-10 08:44:01.338984','2024-02-10 08:44:01.338984',0,1,NULL),(124,116,NULL,'重命名文件或文件夹','netdisk:manage:rename',2,'',255,NULL,1,1,1,'2024-02-10 08:44:27.233379','2024-02-10 08:45:36.000000',0,1,NULL),(125,116,NULL,'复制文件或文件夹','netdisk:manage:copy',2,'',255,NULL,1,1,1,'2024-02-10 08:44:44.725391','2024-02-10 08:45:48.000000',0,1,NULL),(126,116,NULL,'剪切文件或文件夹','netdisk:manage:cut',2,'',255,NULL,1,1,1,'2024-02-10 08:45:21.660511','2024-02-10 08:45:21.660511',0,1,NULL),(127,115,'overview','网盘概览','netdisk:overview:desc',1,'',254,'netdisk/overview',0,1,1,'2024-02-10 09:32:56.981190','2024-02-10 09:34:18.000000',0,1,NULL),(128,NULL,'/contract','合同管理',NULL,0,'ep:document',1,NULL,1,1,1,'2024-02-29 10:40:39.080419','2024-02-29 10:42:37.000000',0,1,NULL),(129,128,'/contract/index','合同审核','app:contract:list',1,'ep:document',1,'contract/index',0,1,1,'2024-02-29 10:46:09.245521','2024-02-29 14:59:56.000000',0,1,NULL),(130,NULL,'/vehicle-usage/index','车辆使用',NULL,1,'ant-design:car-outlined',4,'vehicle-usage/index',0,1,1,'2024-02-29 10:48:35.035363','2024-03-04 14:18:36.000000',0,1,NULL),(131,150,'/materials-inventory/record-in-out','出入库记录','materials_inventory:history_in_out:list',1,'ep:coin',3,'materials-inventory/in-out/index',0,1,1,'2024-02-29 11:03:49.710130','2024-03-05 15:22:06.000000',0,1,NULL),(132,129,NULL,'更新','app:contract:update',2,'',255,NULL,1,1,1,'2024-02-29 15:00:39.641043','2024-02-29 15:00:39.641043',0,1,NULL),(133,129,NULL,'删除','app:contract:delete',2,'',255,NULL,1,1,1,'2024-02-29 15:00:59.376071','2024-02-29 15:00:59.376071',0,1,NULL),(134,129,NULL,'查询','app:contract:read',2,'',255,NULL,1,1,1,'2024-02-29 15:01:14.209396','2024-02-29 15:45:29.000000',0,1,NULL),(135,129,NULL,'新增','app:contract:create',2,'',255,NULL,1,1,1,'2024-02-29 15:44:46.950582','2024-02-29 15:44:46.950582',0,1,NULL),(136,131,NULL,'新增','materials_inventory:history_in_out:create',2,'',255,NULL,1,1,1,'2024-03-01 17:17:02.597782','2024-03-06 10:54:28.000000',0,1,NULL),(137,131,NULL,'更新','materials_inventory:history_in_out:update',2,'',255,NULL,1,1,1,'2024-03-01 17:17:15.192910','2024-03-06 10:54:57.000000',0,1,NULL),(138,131,NULL,'查询单个','app:contract:read',2,'',255,NULL,1,1,1,'2024-03-01 17:17:32.488892','2024-03-01 17:17:32.488892',0,1,NULL),(139,131,NULL,'删除','materials_inventory:history_in_out:delete',2,'',255,NULL,1,1,1,'2024-03-01 17:17:43.455773','2024-03-06 10:55:06.000000',0,1,NULL),(140,150,'/materials-inventory/company','乙方公司管理','app:company:list',1,'ep:office-building',6,'materials-inventory/company/index',0,1,1,'2024-03-04 15:44:30.769048','2024-03-05 15:22:22.000000',0,1,NULL),(141,140,NULL,'单个查询','app:company:read',2,'',1,NULL,1,1,1,'2024-03-04 15:45:55.979802','2024-03-04 15:45:55.979802',0,1,NULL),(142,140,NULL,'新增','app:company:create',2,'',2,NULL,1,1,1,'2024-03-04 15:46:11.260636','2024-03-04 15:46:11.260636',0,1,NULL),(143,140,NULL,'更新','app:company:update',2,'',3,NULL,1,1,1,'2024-03-04 15:46:25.098204','2024-03-04 15:46:25.098204',0,1,NULL),(144,140,NULL,'删除','app:company:delete',2,'',4,NULL,1,1,1,'2024-03-04 15:46:50.812446','2024-03-04 15:46:50.812446',0,1,NULL),(145,150,'/materials-inventory/product','产品目录','app:product:list',1,'ant-design:product-outlined',6,'materials-inventory/product/index',0,1,1,'2024-03-04 16:43:22.749281','2024-03-06 13:40:03.000000',0,1,NULL),(146,145,NULL,'单个查询','app:product:read',2,'',1,NULL,1,1,1,'2024-03-04 16:44:56.482508','2024-03-04 16:44:56.482508',0,1,NULL),(147,145,NULL,'新增','app:product:create',2,'',255,NULL,1,1,1,'2024-03-04 16:45:08.211188','2024-03-04 16:45:08.211188',0,1,NULL),(148,145,NULL,'更新','app:product:update',2,'',255,NULL,1,1,1,'2024-03-04 16:45:25.457903','2024-03-04 16:45:25.457903',0,1,NULL),(149,145,NULL,'删除','app:product:delete',2,'',255,NULL,1,1,1,'2024-03-04 16:45:39.352621','2024-03-04 16:45:39.352621',0,1,NULL),(150,NULL,'/materials-inventory','原材料盘点',NULL,0,'ant-design:dashboard-outlined',3,NULL,1,1,1,'2024-03-04 16:53:32.172674','2024-03-04 16:53:32.172674',0,1,NULL),(151,131,NULL,'导出','materials_inventory:history_in_out:export',2,'',5,NULL,1,1,1,'2024-03-06 13:09:39.201093','2024-03-06 13:09:39.201093',0,1,NULL),(152,150,'/materials-inventory/inventory-check','原材料盘点','app:materials_inventory:list',1,'ant-design:dashboard-outlined',2,'materials-inventory/inventory-check/index',1,0,1,'2024-03-06 13:33:24.795599','2024-03-06 17:14:14.000000',0,1,NULL),(153,150,'/materials-inventory/project','项目管理','app:project:list',1,'ep:memo',4,'materials-inventory/project/index',0,1,1,'2024-03-07 09:28:19.234454','2024-03-07 11:12:00.000000',0,1,NULL),(154,153,NULL,'新增','app:project:create',2,'',1,NULL,1,1,1,'2024-03-07 09:28:47.855064','2024-03-07 09:28:47.855064',0,1,NULL),(155,153,NULL,'更新','app:project:update',2,'',2,NULL,1,1,1,'2024-03-07 09:29:03.183084','2024-03-07 09:29:03.183084',0,1,NULL),(156,153,NULL,'删除','app:project:delete',2,'',3,NULL,1,1,1,'2024-03-07 09:29:16.684943','2024-03-07 09:29:16.684943',0,1,NULL),(157,153,NULL,'单个信息','app:project:read',2,'',4,NULL,1,1,1,'2024-03-07 09:29:33.424578','2024-03-07 09:29:33.424578',0,1,NULL),(158,131,NULL,'导出原材料盘点表','app:materials_inventory:export',2,'',255,NULL,1,1,1,'2024-03-07 11:46:54.468400','2024-03-07 11:46:54.468400',0,1,NULL); -/*!40000 ALTER TABLE `sys_menu` ENABLE KEYS */; - --- --- Table structure for table `sys_role` --- - -DROP TABLE IF EXISTS `sys_role`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_role` ( - `id` int NOT NULL AUTO_INCREMENT, - `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `status` tinyint DEFAULT '1', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `default` tinyint DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_223de54d6badbe43a5490450c3` (`name`) USING BTREE, - UNIQUE KEY `IDX_05edc0a51f41bb16b7d8137da9` (`value`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_role` --- - -/*!40000 ALTER TABLE `sys_role` DISABLE KEYS */; -INSERT INTO `sys_role` VALUES (1,'admin','管理员','超级管理员',1,'2023-11-10 00:31:44.058463','2024-01-28 21:08:39.000000',NULL),(2,'user','用户','',1,'2023-11-10 00:31:44.058463','2024-01-30 18:44:45.000000',1),(9,'test','测试',NULL,1,'2024-01-23 22:46:52.408827','2024-01-30 01:04:52.000000',NULL); -/*!40000 ALTER TABLE `sys_role` ENABLE KEYS */; - --- --- Table structure for table `sys_role_menus` --- - -DROP TABLE IF EXISTS `sys_role_menus`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_role_menus` ( - `role_id` int NOT NULL, - `menu_id` int NOT NULL, - PRIMARY KEY (`role_id`,`menu_id`) USING BTREE, - KEY `IDX_35ce749b04d57e226d059e0f63` (`role_id`) USING BTREE, - KEY `IDX_2b95fdc95b329d66c18f5baed6` (`menu_id`) USING BTREE, - CONSTRAINT `FK_2b95fdc95b329d66c18f5baed6d` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE, - CONSTRAINT `FK_35ce749b04d57e226d059e0f633` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_role_menus` --- - -/*!40000 ALTER TABLE `sys_role_menus` DISABLE KEYS */; -INSERT INTO `sys_role_menus` VALUES (1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9),(1,10),(1,11),(1,12),(1,14),(1,15),(1,20),(1,21),(1,22),(1,23),(1,24),(1,25),(1,26),(1,27),(1,28),(1,29),(1,30),(1,31),(1,32),(1,34),(1,35),(1,36),(1,37),(1,38),(1,39),(1,40),(1,41),(1,42),(1,43),(1,48),(1,49),(1,50),(1,51),(1,52),(1,53),(1,54),(1,56),(1,57),(1,58),(1,59),(1,60),(1,61),(1,62),(1,63),(1,64),(1,65),(1,68),(1,69),(1,70),(1,86),(1,87),(1,88),(1,89),(1,92),(1,107),(1,108),(1,109),(1,110),(1,111),(2,1),(2,5),(2,6),(2,7),(2,8),(2,9),(2,10),(2,11),(2,12),(2,14),(2,15),(2,32),(2,34),(2,35),(2,36),(2,37),(2,38),(2,39),(2,40),(2,41),(2,42),(2,43),(2,48),(2,49),(2,50),(2,51),(2,52),(2,53),(2,56),(2,57),(2,58),(2,59),(2,60),(2,68),(2,69),(2,70),(2,86),(2,87),(2,88),(2,89),(2,92),(2,107),(2,108),(2,109),(2,110),(2,111),(2,112),(9,1),(9,2),(9,3),(9,4),(9,5),(9,6),(9,7),(9,8),(9,9),(9,10),(9,11),(9,20),(9,21),(9,22),(9,23),(9,24),(9,25),(9,26),(9,27),(9,28),(9,29),(9,30),(9,31),(9,32),(9,34),(9,35),(9,36),(9,37),(9,38),(9,39),(9,40),(9,41),(9,42),(9,54),(9,56),(9,57),(9,58),(9,59),(9,60),(9,61),(9,62),(9,63),(9,64),(9,65),(9,68),(9,69),(9,70),(9,86),(9,87),(9,88),(9,89),(9,92); -/*!40000 ALTER TABLE `sys_role_menus` ENABLE KEYS */; - --- --- Table structure for table `sys_task` --- - -DROP TABLE IF EXISTS `sys_task`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_task` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `service` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `type` tinyint NOT NULL DEFAULT '0', - `status` tinyint NOT NULL DEFAULT '1', - `start_time` datetime DEFAULT NULL, - `end_time` datetime DEFAULT NULL, - `limit` int DEFAULT '0', - `cron` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `every` int DEFAULT NULL, - `data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, - `job_opts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_ef8e5ab5ef2fe0ddb1428439ef` (`name`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_task` --- - -/*!40000 ALTER TABLE `sys_task` DISABLE KEYS */; -INSERT INTO `sys_task` VALUES (2,'定时清空登录日志','LogClearJob.clearLoginLog',0,1,NULL,NULL,0,'0 0 3 ? * 1',0,'','{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}','定时清空登录日志','2023-11-10 00:31:44.197779','2024-03-07 11:46:09.000000'),(3,'定时清空任务日志','LogClearJob.clearTaskLog',0,1,NULL,NULL,0,'0 0 3 ? * 1',0,'','{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}','定时清空任务日志','2023-11-10 00:31:44.197779','2024-03-07 11:46:09.000000'),(4,'访问百度首页','HttpRequestJob.handle',0,0,NULL,NULL,1,'* * * * * ?',NULL,'{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}',NULL,'访问百度首页','2023-11-10 00:31:44.197779','2023-11-10 00:31:44.206935'),(5,'发送邮箱','EmailJob.send',0,0,NULL,NULL,-1,'0 0 0 1 * ?',NULL,'{\"subject\":\"这是标题\",\"to\":\"18661983080@163.com\",\"content\":\"这是正文\"}',NULL,'每月发送邮箱','2023-11-10 00:31:44.197779','2024-03-07 11:14:53.000000'); -/*!40000 ALTER TABLE `sys_task` ENABLE KEYS */; - --- --- Table structure for table `sys_task_log` --- - -DROP TABLE IF EXISTS `sys_task_log`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_task_log` ( - `id` int NOT NULL AUTO_INCREMENT, - `task_id` int DEFAULT NULL, - `status` tinyint NOT NULL DEFAULT '0', - `detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, - `consume_time` int DEFAULT '0', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_f4d9c36052fdb188ff5c089454b` (`task_id`) USING BTREE, - CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_task_log` --- - -/*!40000 ALTER TABLE `sys_task_log` DISABLE KEYS */; -INSERT INTO `sys_task_log` VALUES (1,3,1,NULL,0,'2024-02-05 03:06:22.037448','2024-02-05 03:06:22.037448'),(2,2,1,NULL,0,'2024-02-10 09:42:21.738712','2024-02-10 09:42:21.738712'); -/*!40000 ALTER TABLE `sys_task_log` ENABLE KEYS */; - --- --- Table structure for table `sys_user` --- - -DROP TABLE IF EXISTS `sys_user`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_user` ( - `id` int NOT NULL AUTO_INCREMENT, - `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `psalt` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `status` tinyint DEFAULT '1', - `qq` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, - `dept_id` int DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `IDX_9e7164b2f1ea1348bc0eb0a7da` (`username`) USING BTREE, - KEY `FK_96bde34263e2ae3b46f011124ac` (`dept_id`) USING BTREE, - CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_user` --- - -/*!40000 ALTER TABLE `sys_user` DISABLE KEYS */; -INSERT INTO `sys_user` VALUES (1,'admin','a11571e778ee85e82caae2d980952546','https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777','1743369777@qq.com','10086','管理员','xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d',1,'1743369777','2023-11-10 00:31:44.104382','2024-03-06 17:18:04.000000','bqy',2),(2,'user','dbd89546dec743f82bb9073d6ac39361','https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777','luffy@qq.com','10010','王路飞','qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe',1,'1743369777','2023-11-10 00:31:44.104382','2024-03-01 11:31:11.000000','luffy',8),(8,'developer','f03fa2a99595127b9a39587421d471f6','/upload/报名照片-202402281149824.jpg','nami@qq.com','10000','小贼猫','NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE',1,'1743369777','2023-11-10 00:31:44.104382','2024-03-06 17:17:21.000000','娜美',2); -/*!40000 ALTER TABLE `sys_user` ENABLE KEYS */; - --- --- Table structure for table `sys_user_roles` --- - -DROP TABLE IF EXISTS `sys_user_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `sys_user_roles` ( - `user_id` int NOT NULL, - `role_id` int NOT NULL, - PRIMARY KEY (`user_id`,`role_id`) USING BTREE, - KEY `IDX_96311d970191a044ec048011f4` (`user_id`) USING BTREE, - KEY `IDX_6d61c5b3f76a3419d93a421669` (`role_id`) USING BTREE, - CONSTRAINT `FK_6d61c5b3f76a3419d93a4216695` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`), - CONSTRAINT `FK_96311d970191a044ec048011f44` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `sys_user_roles` --- - -/*!40000 ALTER TABLE `sys_user_roles` DISABLE KEYS */; -INSERT INTO `sys_user_roles` VALUES (1,1),(2,2),(8,2); -/*!40000 ALTER TABLE `sys_user_roles` ENABLE KEYS */; - --- --- Table structure for table `todo` --- - -DROP TABLE IF EXISTS `todo`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `todo` ( - `id` int NOT NULL AUTO_INCREMENT, - `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, - `user_id` int DEFAULT NULL, - `status` tinyint NOT NULL DEFAULT '0', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_9cb7989853c4cb7fe427db4b260` (`user_id`) USING BTREE, - CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `todo` --- - -/*!40000 ALTER TABLE `todo` DISABLE KEYS */; -INSERT INTO `todo` VALUES (1,'nest.js',NULL,0,'2023-11-10 00:31:44.139730','2023-11-10 00:31:44.147629'); -/*!40000 ALTER TABLE `todo` ENABLE KEYS */; - --- --- Table structure for table `tool_storage` --- - -DROP TABLE IF EXISTS `tool_storage`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `tool_storage` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名', - `fileName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '真实文件名', - `ext_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `user_id` int DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=129 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `tool_storage` --- - -/*!40000 ALTER TABLE `tool_storage` DISABLE KEYS */; -INSERT INTO `tool_storage` VALUES (121,'2024-03-01 16:46:58.441084','2024-03-01 16:46:58.441084','1709172270328-202403011646430.jpg','1709172270328.jpg','jpg','/upload/1709172270328-202403011646430.jpg','图片','62.79 KB',1),(122,'2024-03-01 17:21:35.454311','2024-03-01 17:21:35.454311','盘点表-202403011721448.xlsx','盘点表.xlsx','xlsx','/upload/盘点表-202403011721448.xlsx','文档','10.83 KB',1),(123,'2024-03-04 15:51:50.664699','2024-03-04 15:51:50.664699','20240304-202403041551657.sql','20240304.sql','sql','/upload/20240304-202403041551657.sql','其他','62.86 KB',1),(124,'2024-03-05 10:39:45.040659','2024-03-05 10:39:45.040659','盘点表-202403051039028.xlsx','盘点表.xlsx','xlsx','/upload/盘点表-202403051039028.xlsx','文档','10.83 KB',1),(126,'2024-03-06 13:04:17.636919','2024-03-06 13:04:17.636919','盘点表-202403061304624.xlsx','盘点表.xlsx','xlsx','/upload/盘点表-202403061304624.xlsx','文档','10.83 KB',1),(127,'2024-03-07 09:35:51.916017','2024-03-07 09:35:51.916017','盘点表-202403070935910.xlsx','盘点表.xlsx','xlsx','/upload/盘点表-202403070935910.xlsx','文档','10.83 KB',1),(128,'2024-03-07 09:36:01.570104','2024-03-07 09:36:01.570104','1709172270328-202403070936565.jpg','1709172270328.jpg','jpg','/upload/1709172270328-202403070936565.jpg','图片','62.86 KB',1); -/*!40000 ALTER TABLE `tool_storage` ENABLE KEYS */; - --- --- Table structure for table `user_access_tokens` --- - -DROP TABLE IF EXISTS `user_access_tokens`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `user_access_tokens` ( - `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `expired_at` datetime NOT NULL COMMENT '令牌过期时间', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', - `user_id` int DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - KEY `FK_e9d9d0c303432e4e5e48c1c3e90` (`user_id`) USING BTREE, - CONSTRAINT `FK_e9d9d0c303432e4e5e48c1c3e90` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_access_tokens` --- - -/*!40000 ALTER TABLE `user_access_tokens` DISABLE KEYS */; -INSERT INTO `user_access_tokens` VALUES ('07276357-6286-478c-8058-d249f4ca4dde','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI4MzIwNH0.CiG2E-GnJlDR_6YJMDkxTJOa-dS29HV5rWNEszn-jeA','2024-03-02 16:53:25','2024-03-01 16:53:24.786130',1),('09cf7b0a-62e0-45ee-96b0-e31de32361e0','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1MDkxNTd9.0gtKlcxrxQ-TarEai2lsBnfMc852ZDYHeSjjhpo5Fn8','2024-02-11 04:05:58','2024-02-10 04:05:57.696509',1),('0b95a340-d9eb-4c3a-bd70-2432d3108984','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyMzM4Mn0.IA8TwkjfDwmv9470mgVswU775HCRdGo7zCoPblRNgRU','2024-03-05 11:36:23','2024-03-04 11:36:22.819835',1),('17593c96-3779-4f03-8dbc-cb21ac9f7981','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwiaWF0IjoxNzA5MjYxNTUzfQ.hDAHgYrDfsckG6bSyfGa_QLRmo1tvbJEkYgQUfwqZbs','2024-03-02 10:52:33','2024-03-01 10:52:33.059989',1),('19e593f4-4a03-46e0-9b4e-a009af15790a','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTcxNjY3NX0.a0xNnKWIueWIxu5SXAawpLq6Bl4cTD5O6bqLknDU05U','2024-03-07 17:17:55','2024-03-06 17:17:55.105520',1),('1bb9d14e-823c-4f9f-8562-cbc2fd2be318','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTI1Mn0.-OgIPgXJtFC5ioAu_xpwCY6LRcAyTykXR_1pVrrSCN4','2024-03-05 13:14:12','2024-03-04 13:14:12.192684',1),('31399cec-e506-4194-a9cc-47f7a1a953a9','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTIxNzh9.Da3j0Ht_zAsPldEYJ18RFYZqTYNhgPxSefCEuXd-4T8','2024-02-29 11:49:38','2024-02-28 11:49:38.289991',1),('31875da0-8b93-4ca2-bdb8-ab40c1c059bd','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTg0OX0.g8Rr9eTlEZaghqdd_W5lSyxVV-ZMMnMMkyF2iNrUXpY','2024-03-02 15:57:29','2024-03-01 15:57:29.074445',1),('35fd4bb6-2608-4203-ac2c-9550034993ea','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3ODEwOTh9.WmBVjKJkJs1lve9PrYfF8pYWF-qeg8KQu8IeSDSF3Cw','2024-03-08 11:11:38','2024-03-07 11:11:38.199744',1),('367aaa79-1130-4511-87c4-25aacdf27d3d','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgzOH0.5_K7t6bAuqIO9A8NvgrWACEJUVKduu--1q-rgmAB4Vw','2024-03-02 15:57:18','2024-03-01 15:57:18.314066',1),('3f7dffae-db1f-47dc-9677-5c956c3de39e','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczMTEzMDJ9.D5Qpht1RquKor8WtgfGAcCp8LwG7z3FZhIwbyQzhDmE','2024-02-08 21:08:22','2024-02-07 21:08:22.130066',1),('40342c3e-194c-42eb-adee-189389839195','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxNjF9.tRQOxhB-01Pcut5MXm4L5D1OrbMJfS4LfUys0XB4kWs','2024-02-09 14:02:41','2024-02-08 14:02:41.081164',1),('4541a9e8-a508-4b92-8ae3-c3744ddcad92','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUzODE3NH0.Gb_IUruHmd25Wk1ZFHUtB8G2w1bg7i-ivCa11fT8JTw','2024-03-05 15:42:54','2024-03-04 15:42:54.286838',1),('53a08eaa-74fb-45cb-9e6c-514b67346e06','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTU0NDM0Mn0.rcVSgy1s7zNWtB_Q93zdZzL633UFbeH_RWEY_kelVHQ','2024-03-05 17:25:42','2024-03-04 17:25:42.096331',1),('6ea87ca4-89e4-419b-9eb3-7bef4ada53ca','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTM0Mn0.kLsAfIgUDqNEogqBZWaDt7iUYb4gE4dznjTKoUzSKj0','2024-03-05 13:15:42','2024-03-04 13:15:42.347589',1),('6f3990bd-d093-4cc4-9f9e-cba45260ec42','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkxOTUxMTR9.XvbjbvGNz7pYm-Yol8X3uTDwYkn35i-Fu_LsmMU4PdM','2024-03-01 16:25:15','2024-02-29 16:25:14.725434',1),('78519125-77c1-4768-9644-d8fa639f3ff0','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3NzQwNzN9._ZJd1B4EybHlO3W9p98uZ0NJDm6fJur9ifLwEIwywI0','2024-03-08 09:14:34','2024-03-07 09:14:33.765186',1),('7ebc6a49-421c-4437-a7a8-bc8aaeb96b26','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkxOTUxNjl9.oiSa_amY2bX-MaAxL8vs_EmSEyhyBLVJLn9uPf_HgY4','2024-03-01 16:26:09','2024-02-29 16:26:09.075302',1),('8e26aa27-fa67-4b67-8705-019be3e8f1f8','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTY4OTh9.LemwyefdnGCm2ZM2KE3RN2-d6n-Xj2TAujs7iBWaYs8','2024-02-29 13:08:18','2024-02-28 13:08:18.482193',1),('8f7fcaed-c4ed-4cf0-840f-37bcd3a39a59','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTg1M30.woUKWiIQ96woTIFDSdPWkQHwypfTc9T6EqFDbwN56mE','2024-03-02 15:57:34','2024-03-01 15:57:33.866391',1),('9d1ba8e9-dffc-4b15-b21f-4a90f196e39c','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1Mjc5MDV9.7LeiS3LBBdiAc7YrULWpmnI1oNSvR79K-qjEOlBYOnI','2024-02-11 09:18:26','2024-02-10 09:18:25.656695',1),('ab62dfc5-46d8-4dad-9055-bb2803d1451c','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTY3NjF9.9C_cPmmiW3Sh5OrPNSnw3h1IoqqLa3GlEYvgNFvv5Rg','2024-02-29 13:06:01','2024-02-28 13:06:01.416655',1),('af43fb31-3759-43d5-b13e-e558bccdca58','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTY4NTkwNH0.eKKbI4Wi7WKSqE-gDt5isFihQze-LOysFntMzyaP5DY','2024-03-07 08:45:05','2024-03-06 08:45:04.541645',1),('b6053fd5-7c7c-453e-a8ea-6de17b30304a','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTcwNTF9.LyR5igCxJhcSnB-pXCc7wJNJ2H1ERdu8LO6d94cMz2c','2024-02-29 13:10:51','2024-02-28 13:10:51.331328',1),('c24acee3-7bbd-4e7a-b8f6-a80be1db0b33','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgyNH0.6YmdwppJX1DV9-jdFiHLWRH9X_WbeGYbZtaRIizLRgM','2024-03-02 15:57:05','2024-03-01 15:57:04.739116',1),('cbfcc75c-d357-47fd-afbf-db0629885eb6','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTYyMDQ0OH0.LQXYkRJVyDN7YCVRSsS7osGMzC31ik1XD36-SWDbghI','2024-03-06 14:34:08','2024-03-05 14:34:08.374481',1),('cdc3e837-0953-42e7-8296-d27cfbc41353','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTEzNn0.SUycnmUW-ZDjLoBP4DMB_si11dUVHfXmCsG0tLMVBg0','2024-03-02 15:45:37','2024-03-01 15:45:36.880024',1),('d32e25cc-b00c-43cf-94b0-0f61c3314f4c','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI2MTU4OX0.-IM-L0Dt7Jc5xaEU5Q93z5vhAQTpbD9ngS_uhzZ0haI','2024-03-02 10:53:10','2024-03-01 10:53:09.606838',1),('d9044d67-9a95-4bd5-8103-f6ddc9701f83','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTM2NH0.gEQmOm9E7Adpti0pux6KjtopqZpNy62fZzgWf0Ps1wM','2024-03-05 13:16:05','2024-03-04 13:16:04.665874',1),('e042f0c3-9a35-40cf-83a3-918f617f2ac7','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3ODMxNzR9.6jKaSK15fdN8XkToaimiWbqJ-vhiaQr2QkpAc5XvFuA','2024-03-08 11:46:15','2024-03-07 11:46:14.586995',1),('e754d168-106a-43f5-bbf8-80f1b187cb1e','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTk2MzN9.IQ_1NQHKvQ3Cso7cXYQ32xK3qhaOUUF6jtoQ87cN6b0','2024-02-29 13:53:53','2024-02-28 13:53:53.168581',1),('ebc5fbbb-b29e-4a17-acd6-002ca7936099','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgzMX0.d29Hn1Ub-YJBv9Xw9Olo7kBNB9ugxcs1P1hrtJEQpJo','2024-03-02 15:57:11','2024-03-01 15:57:11.247365',1),('edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxMjd9.VRuJHGca2IPrdfTyW09wfhht4x8JX207pKG-0aZyF60','2024-02-09 14:02:07','2024-02-08 14:02:07.390658',1),('eff3fd41-7307-4027-9f8c-2e3b0fd35051','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTk1OTd9.aLKmV2GGF6pkkolLnJzuq0PXAllSbGIZzJrUR4wlNag','2024-02-29 13:53:17','2024-02-28 13:53:17.473705',1); -/*!40000 ALTER TABLE `user_access_tokens` ENABLE KEYS */; - --- --- Table structure for table `user_refresh_tokens` --- - -DROP TABLE IF EXISTS `user_refresh_tokens`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `user_refresh_tokens` ( - `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `expired_at` datetime NOT NULL COMMENT '令牌过期时间', - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', - `accessTokenId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `REL_1dfd080c2abf42198691b60ae3` (`accessTokenId`) USING BTREE, - CONSTRAINT `FK_1dfd080c2abf42198691b60ae39` FOREIGN KEY (`accessTokenId`) REFERENCES `user_access_tokens` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_refresh_tokens` --- - -/*!40000 ALTER TABLE `user_refresh_tokens` DISABLE KEYS */; -INSERT INTO `user_refresh_tokens` VALUES ('045ad38e-ab82-4ea1-8b61-c2da89060999','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRWMxcEQwZ1VrYnFtZEhyWVdKTDVPIiwiaWF0IjoxNzA5MDkyMTc4fQ.K2nBZ_B8jneihHfs51LIp0fvkkgV7lFawe2cu4sOjN4','2024-03-29 11:49:38','2024-02-28 11:49:38.302821','31399cec-e506-4194-a9cc-47f7a1a953a9'),('046cd195-a105-493e-9d6c-0d5d87081834','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRlU3a3poeGxNUkhWVUdleDR6VmJTIiwiaWF0IjoxNzA5NjIwNDQ4fQ.UKal8R3pPSiIGPAZFUf78-ne81csm2TwQbRtWIB_B1k','2024-04-04 14:34:08','2024-03-05 14:34:08.406121','cbfcc75c-d357-47fd-afbf-db0629885eb6'),('08f57b8a-6a86-434a-89cf-280255a0d1b6','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaWxvdmdUWmJueU5RTGR4bmxpc3Y0IiwiaWF0IjoxNzA5MDk2ODk4fQ.v29AorJL9FJVAS89GxtFKeJYc92nPaIpE8O-1GY_nhw','2024-03-29 13:08:18','2024-02-28 13:08:18.499682','8e26aa27-fa67-4b67-8705-019be3e8f1f8'),('157299e9-45e0-4fe3-9caf-ff944644605d','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNVVsRHhFSE1EMnpMVGd4bUNmRnVSIiwiaWF0IjoxNzA5Mjc5MTM2fQ.mbmS1urJdB6YxBdrj1n09-IgARY_LC5lRuTAntVUZlg','2024-03-31 15:45:37','2024-03-01 15:45:36.901193','cdc3e837-0953-42e7-8296-d27cfbc41353'),('1f613862-2567-4c10-8026-c4743c62d01a','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1ZZMzl3dnpVLUxmcTFxSjBYVGktIiwiaWF0IjoxNzA5MDk5NTk3fQ.Qjhsglo7PbTsT1_KE3vTMNweHx08dwcz_q1e1f2W9R0','2024-03-29 13:53:17','2024-02-28 13:53:17.487831','eff3fd41-7307-4027-9f8c-2e3b0fd35051'),('202d0969-6721-4f6f-bf34-f0d1931d4d01','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRpOXVYei1TdldjdWRnclFXVmFXIiwiaWF0IjoxNzA3MzcyMTYxfQ.NOQufR5EAPE2uZoyenmAj9H7S7qo4d6W1aW2ojDxZQc','2024-03-09 14:02:41','2024-02-08 14:02:41.091492','40342c3e-194c-42eb-adee-189389839195'),('21e28f7b-c156-41bf-902e-583d48109ebf','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoieVU2NU5NSGVuak5lUmozY3BWVDhwIiwiaWF0IjoxNzA5MTk1MTY5fQ.4Vibz4kCF5UulsR9OmAhmQRhrA9idKCyKXCITVLOAkk','2024-03-30 16:26:09','2024-02-29 16:26:09.086894','7ebc6a49-421c-4437-a7a8-bc8aaeb96b26'),('25e9acfd-eb72-4ab8-9e5e-04f0a683e60f','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibndPOEFIb1hab2d3Y0tpWG41NXlJIiwiaWF0IjoxNzA5Mjc5ODI0fQ._r8VN3caZ5XzEdMpH1FM9e1yVqHuACpknfOeSyixXns','2024-03-31 15:57:05','2024-03-01 15:57:04.755457','c24acee3-7bbd-4e7a-b8f6-a80be1db0b33'),('42cb1246-4a1b-4443-a9b0-926761f13527','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaVBwVW1FQ0xUTXgzUC1KVUdvaXJaIiwiaWF0IjoxNzA5NTI5MzQyfQ.e6TbYee7T2uEt0GJznuLmlEGYvmT8LWQVk0phYMS6Lw','2024-04-03 13:15:42','2024-03-04 13:15:42.358715','6ea87ca4-89e4-419b-9eb3-7bef4ada53ca'),('46195349-cbf4-492f-b193-7aec06cb556b','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN1NGYy1PTGNWc1MxVG5BWEdqLW1NIiwiaWF0IjoxNzA5Mjc5ODQ5fQ.n1eSzEGrz_M_hrYNdxx7UtGWHzt7soyJqDh1UK670fE','2024-03-31 15:57:29','2024-03-01 15:57:29.086263','31875da0-8b93-4ca2-bdb8-ab40c1c059bd'),('461f9b7c-e500-4762-a6d9-f9ea47163064','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicXJvTWNYMnhNRW5uRmZGWkQtaUx0IiwiaWF0IjoxNzA3MzExMzAyfQ.dFIWCePZnn2z2Qv1D5PKBKXUwVDI0Gp091MIOi9jiIo','2024-03-08 21:08:22','2024-02-07 21:08:22.145464','3f7dffae-db1f-47dc-9677-5c956c3de39e'),('4a4a3a33-1308-4fc1-9ffa-f828d875e004','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLWREemYwWUdXMEhVQXI3RHNqbG9qIiwiaWF0IjoxNzA5NzE2Njc1fQ.ro8DesLH5duC9IBySpMLwtuzC5pMPr1NY6U21Ff4ZHg','2024-04-05 17:17:55','2024-03-06 17:17:55.118361','19e593f4-4a03-46e0-9b4e-a009af15790a'),('4b82a1b7-b571-42de-8809-487f7abc2e65','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiR1BkbUVfTmhjZG5xWnpWX2lGN0JuIiwiaWF0IjoxNzA5NTQ0MzQyfQ.hH2bzYj_8Qim997-C7HNUSJqkk43Z1YAiexpRhxR8cQ','2024-04-03 17:25:42','2024-03-04 17:25:42.114236','53a08eaa-74fb-45cb-9e6c-514b67346e06'),('50b2f3ad-232a-41f6-bf97-2c52127a3e28','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVnlZNjYwYWNPRHlTd2NZWGhscWdFIiwiaWF0IjoxNzA5Mjc5ODMxfQ.uG0qmKgWzYNvBJFfCvvOmndCgrwpkZ3NVL4aynD6Uxc','2024-03-31 15:57:11','2024-03-01 15:57:11.259022','ebc5fbbb-b29e-4a17-acd6-002ca7936099'),('53c79bc8-f092-4d07-b480-f8637a3887fa','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOEoyQ0w5RXYtSkhmWW0xSDNMMUxEIiwiaWF0IjoxNzA5MjYxNTg5fQ.3Ez1mfJkLZ9LFo2nTshTtOjs-VN56k1LWNoijWzSYaA','2024-03-31 10:53:10','2024-03-01 10:53:09.615615','d32e25cc-b00c-43cf-94b0-0f61c3314f4c'),('718ccd7b-bb4f-4400-8f40-ccd6141ef4c5','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiamNTcWlzUkFIaW5SOTlUX2lvVzV0IiwiaWF0IjoxNzA5MDk2NzYxfQ.X-iqKmDeV6juqCQC8XjU7o-8CXNxEyLyn5uck4mkJoY','2024-03-29 13:06:01','2024-02-28 13:06:01.430737','ab62dfc5-46d8-4dad-9055-bb2803d1451c'),('9557d0a9-5004-4392-95b6-637b3343dbc3','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTnNqRGZsMUpKY3RWTzFYaEtEMV9FIiwiaWF0IjoxNzA5NzgzMTc0fQ.osKC1PeUm6LAMj6ZyIuNhUxFcKN1t1ZvrjkE2Rn1pxw','2024-04-06 11:46:15','2024-03-07 11:46:14.600560','e042f0c3-9a35-40cf-83a3-918f617f2ac7'),('9d54c429-5dab-4e72-b70e-d1e6918d364f','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ1UxYnkxTkVNT05ZQVJsWjBkYzIwIiwiaWF0IjoxNzA5Nzc0MDczfQ.rMG5F7Cec_gcP7C7gr_WL6XiquvuWmvV8Wh2SQOEwuY','2024-04-06 09:14:34','2024-03-07 09:14:33.788438','78519125-77c1-4768-9644-d8fa639f3ff0'),('a2e12448-35b0-487e-a18e-a60461a54370','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZHRvUU9fUjhCZGFOQ1EzMVRhYmxtIiwiaWF0IjoxNzA5NzgxMDk4fQ.3ORghjx3Yzh9_f62tYHLeY_x602pBF-wMrKEVX7Dd2c','2024-04-06 11:11:38','2024-03-07 11:11:38.213630','35fd4bb6-2608-4203-ac2c-9550034993ea'),('a30969f3-b766-4190-b3c6-544f81a05a5a','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRlRUN0VqRWlfc1RkSXd6cjhGZmtZIiwiaWF0IjoxNzA5NTI5MzY0fQ.5qz5M4MC4nU614Nn8hyW0l1TuGcFNuKRHC_ZYzEZwO8','2024-04-03 13:16:05','2024-03-04 13:16:04.680424','d9044d67-9a95-4bd5-8103-f6ddc9701f83'),('a500bb13-4b9a-4cf2-b691-b046586d9ee7','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoic3R1TDFpUmljTnBlSTlhR0FEUFQwIiwiaWF0IjoxNzA5Mjc5ODUzfQ.oiNGDkAWVr4jko01TY7rqWoOYvCcwZbWpfLCzgtqMHc','2024-03-31 15:57:34','2024-03-01 15:57:33.879108','8f7fcaed-c4ed-4cf0-840f-37bcd3a39a59'),('a8451340-008c-4fa7-bea0-3bdeee5d5cfa','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVXdDQ2dJeUU1eGpUT1lLRDVtcnU3IiwiaWF0IjoxNzA5MjYxNTUzfQ.dA6eFaURUP0wYU7Pr0Xjv2oFkkfrwQb-cB_gziAK4G4','2024-03-31 10:52:33','2024-03-01 10:52:33.072305','17593c96-3779-4f03-8dbc-cb21ac9f7981'),('b375e623-2d82-48f0-9b7a-9058e3850cc6','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicDhUMzdGNFFaUDJHLU5yNGVha21wIiwiaWF0IjoxNzA3MzcyMTI3fQ.fn3It6RKIxXlKmqixg0BMmY_YsQmAxtetueqW-0y1IM','2024-03-09 14:02:07','2024-02-08 14:02:07.410008','edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb'),('c92355b8-da11-4e8d-b5a6-0826bb44aac0','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUlVnM3RtQTBROWZjZ0JZWnJxMXNxIiwiaWF0IjoxNzA5Njg1OTA0fQ.CraA5kdSrHeZhyRchvmZfnfdiPXfjd_4A6FZWPh-r7Y','2024-04-05 08:45:05','2024-03-06 08:45:04.557816','af43fb31-3759-43d5-b13e-e558bccdca58'),('e620ccc1-9e40-4387-9f21-f0722e535a63','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNE5WdmFIc2hWaU05ZFh0QnVBaHNsIiwiaWF0IjoxNzA3NTI3OTA1fQ.zzyGX0mOJe6KWpTzIi7We9d9c0MRuDeGC86DMB0Vubs','2024-03-11 09:18:26','2024-02-10 09:18:25.664251','9d1ba8e9-dffc-4b15-b21f-4a90f196e39c'),('ea877712-28d7-4ee1-97ad-27f4455d59a5','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZUVsNUM2RXZFMjBacENpZnZZalI4IiwiaWF0IjoxNzA5MTk1MTE0fQ.SnF7us-m0ktL6JJIXl8NgNPrsVlHMSN_l5-Nr9L2FeA','2024-03-30 16:25:15','2024-02-29 16:25:14.741842','6f3990bd-d093-4cc4-9f9e-cba45260ec42'),('eb8e0905-8f49-42db-b9f8-771f8d706ab0','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiSEZWTG9xY25UeV84ZGpOWWxZTy02IiwiaWF0IjoxNzA5MDk3MDUxfQ.cC5X2an9Vn4q3NThsqKdA72locR6J10yOCPPqGT_hnc','2024-03-29 13:10:51','2024-02-28 13:10:51.345270','b6053fd5-7c7c-453e-a8ea-6de17b30304a'),('ece48e5b-4a1e-4683-acbc-4da02db0d511','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiai1DZjIyUllQX0taaWpPaFkyV01iIiwiaWF0IjoxNzA5NTM4MTc0fQ.oAUJSvbA2EQbdqnpia8nHiiH7VH6igEHOka9INSnTVE','2024-04-03 15:42:54','2024-03-04 15:42:54.302651','4541a9e8-a508-4b92-8ae3-c3744ddcad92'),('ef3560f5-8f24-4a76-a5e8-c17de1f1fcfe','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiY2VkQ2xZdl9TcTJ2SUEybzZaZS1NIiwiaWF0IjoxNzA5MDk5NjMzfQ.mxekqz0IB_fzwvoBcZyN7BPKezISmd4wPHt7fR7-b3g','2024-03-29 13:53:53','2024-02-28 13:53:53.180619','e754d168-106a-43f5-bbf8-80f1b187cb1e'),('f48bf795-9163-4899-8268-2345a0d2ba4e','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVjc1SUFMMkhVLUFuTkZINzVFbmhfIiwiaWF0IjoxNzA5Mjc5ODM4fQ.epc14TsoOkTpJOoo1zW9N3qKxiiUTqQ42Sbb455ppAE','2024-03-31 15:57:18','2024-03-01 15:57:18.327929','367aaa79-1130-4511-87c4-25aacdf27d3d'),('f9a003e8-91b7-41ee-979e-e39cca3534ec','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGJQdl9SVjFtUl80N0o0TGF0QlV5IiwiaWF0IjoxNzA3NTA5MTU3fQ.oEVdWSigTpAQY7F8MlwBnedldH0sJT1YF1Mt0ZUbIw4','2024-03-11 04:05:58','2024-02-10 04:05:57.706763','09cf7b0a-62e0-45ee-96b0-e31de32361e0'),('faef8e19-55d3-4005-8022-ca735c5c9390','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1ZZeXlpQWNJREVTdktFNUhjeER1IiwiaWF0IjoxNzA5NTI5MjUyfQ.IcNLUGhcsxTWIYWxT-0TeII4NbM--Bgq7BJ0VbRfUMo','2024-04-03 13:14:12','2024-03-04 13:14:12.215057','1bb9d14e-823c-4f9f-8562-cbc2fd2be318'),('fb10a7dd-5f84-4151-ab25-b24ec9fca46e','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMUFzVUdRc3hjZEdnaVhjYkVUcm1WIiwiaWF0IjoxNzA5NTIzMzgyfQ.t2zzBijhlt4cV-p8KElBjJk4TURABI-tKRk-mIkmb4w','2024-04-03 11:36:23','2024-03-04 11:36:22.837030','0b95a340-d9eb-4c3a-bd70-2432d3108984'),('fda03f46-8638-43c1-8417-12ecb1ec7cf7','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNUx1NkJuc1pQeVdtVm1DbHg4MGxwIiwiaWF0IjoxNzA5MjgzMjA0fQ.yAmdtYl3KqMa_R32R2Ht2xxMtQIMpncQ-hFzhzTJTkE','2024-03-31 16:53:25','2024-03-01 16:53:24.799017','07276357-6286-478c-8058-d249f4ca4dde'); -/*!40000 ALTER TABLE `user_refresh_tokens` ENABLE KEYS */; - --- --- Table structure for table `vehicle_usage` --- - -DROP TABLE IF EXISTS `vehicle_usage`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `vehicle_usage` ( - `id` int NOT NULL AUTO_INCREMENT, - `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `year` int NOT NULL COMMENT '年度', - `vehicle_license` int NOT NULL COMMENT '外出使用的车辆名称(字典)', - `applicant` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '申请人', - `driver` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '出行司机', - `current_mileage` int DEFAULT NULL COMMENT '当前车辆里程数(KM)', - `expected_start_date` date DEFAULT NULL COMMENT '预计出行开始时间', - `expected_end_date` date DEFAULT NULL COMMENT '预计出行结束时间', - `purpose` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '使用事由', - `actual_return_time` date DEFAULT NULL COMMENT '实际回司时间', - `return_mileage` int DEFAULT NULL COMMENT '回城车辆里程数(KM)', - `reviewer` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '审核人', - `status` tinyint NOT NULL DEFAULT '0' COMMENT '审核状态(字典)', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', - PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `vehicle_usage` --- - -/*!40000 ALTER TABLE `vehicle_usage` DISABLE KEYS */; -/*!40000 ALTER TABLE `vehicle_usage` ENABLE KEYS */; - --- --- Dumping routines for database 'hxoa' --- -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2024-03-07 12:04:02 diff --git a/docker-compose.yml b/docker-compose.yml index 8d84b03..35e629b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: mysql: - image: mysql:latest + image: mysql:8.0 container_name: huaxin-admin-mysql restart: always env_file: @@ -17,10 +17,11 @@ services: - MYSQL_ROOT_PASSWORD=${DB_PASSWORD} ports: - '${DB_PORT}:3306' - command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci #设置utf8字符集 + command: + mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci volumes: - ./__data/mysql/:/var/lib/mysql/ # ./__data/mysql/ 路径可以替换成自己的路径 - - ./deploy/sql/:/docker-entrypoint-initdb.d/ # 初始化的脚本,若 ./__data/mysql/ 文件夹存在数据,则不会执行初始化脚本 + - ./init_data/sql/:/docker-entrypoint-initdb.d/ # 初始化的脚本,若 ./__data/mysql/ 文件夹存在数据,则不会执行初始化脚本 networks: - huaxin_admin_net diff --git a/ecosystem.config.js b/ecosystem.config.js index a52b8ef..b08faf1 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -1,6 +1,6 @@ -const { cpus } = require('os') +const { cpus } = require('os'); -const cpuLen = cpus().length +const cpuLen = cpus().length; module.exports = { apps: [ @@ -10,13 +10,13 @@ module.exports = { autorestart: true, exec_mode: 'cluster', watch: false, - instances: cpuLen, + instances: process.env.CPU_LEN ?? cpuLen, max_memory_restart: '520M', args: '', env: { NODE_ENV: 'production', - PORT: process.env.APP_PORT, - }, - }, - ], -} + PORT: process.env.APP_PORT + } + } + ] +}; diff --git a/init_data/sql/hxoa.sql b/init_data/sql/hxoa.sql new file mode 100644 index 0000000..2e34ab7 --- /dev/null +++ b/init_data/sql/hxoa.sql @@ -0,0 +1,1175 @@ +/* + Navicat Premium Data Transfer + + Source Server : hxoa_local + Source Server Type : MySQL + Source Server Version : 80300 + Source Host : localhost:13307 + Source Schema : hxoa + + Target Server Type : MySQL + Target Server Version : 80300 + File Encoding : 65001 + + Date: 28/03/2024 12:40:37 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for company +-- ---------------------------- +DROP TABLE IF EXISTS `company`; +CREATE TABLE `company` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公司名称', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_a76c5cd486f7779bd9c319afd2`(`name`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of company +-- ---------------------------- +INSERT INTO `company` VALUES (1, '2024-03-04 15:44:43.005593', '2024-03-04 16:09:44.000000', '深圳市立创电子商务有限公司', 0); +INSERT INTO `company` VALUES (4, '2024-03-04 16:05:34.701780', '2024-03-04 16:05:34.701780', '深圳市诚亨泰科技有限公司', 0); +INSERT INTO `company` VALUES (5, '2024-03-04 16:05:38.867786', '2024-03-04 16:05:38.867786', '东莞市顶源电子有限公司', 0); +INSERT INTO `company` VALUES (6, '2024-03-04 16:05:42.479027', '2024-03-04 16:05:42.479027', '深圳市福田区赛格电子市场金佳电子经营部', 0); +INSERT INTO `company` VALUES (7, '2024-03-04 16:05:46.775364', '2024-03-04 16:05:46.775364', '深圳市思界电子科技有限公司', 0); +INSERT INTO `company` VALUES (8, '2024-03-04 16:05:55.806537', '2024-03-04 16:05:55.806537', '广州市星翼电信科技有限公司', 0); +INSERT INTO `company` VALUES (9, '2024-03-04 16:06:03.003860', '2024-03-04 16:09:49.000000', '快递费', 1); +INSERT INTO `company` VALUES (10, '2024-03-04 16:06:09.788572', '2024-03-04 16:06:09.788572', '青岛丰喆精密模具有限公司', 0); +INSERT INTO `company` VALUES (11, '2024-03-04 16:06:12.872983', '2024-03-04 16:06:12.872983', '深圳嘉立创科技集团股份有限公司', 0); +INSERT INTO `company` VALUES (12, '2024-03-04 16:06:19.823410', '2024-03-04 16:06:19.823410', '北京特倍福电子技术有限公司', 0); +INSERT INTO `company` VALUES (13, '2024-03-04 16:06:25.937749', '2024-03-04 16:06:25.937749', '上海脉芯网络科技有限公司', 0); + +-- ---------------------------- +-- Table structure for company_storage +-- ---------------------------- +DROP TABLE IF EXISTS `company_storage`; +CREATE TABLE `company_storage` ( + `company_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`company_id`, `file_id`) USING BTREE, + INDEX `IDX_0958ee6ca6f52985840624bb91`(`company_id`) USING BTREE, + INDEX `IDX_bdd3a301229b9dec4b95549dfe`(`file_id`) USING BTREE, + CONSTRAINT `FK_0958ee6ca6f52985840624bb916` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_bdd3a301229b9dec4b95549dfe7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of company_storage +-- ---------------------------- + +-- ---------------------------- +-- Table structure for contract +-- ---------------------------- +DROP TABLE IF EXISTS `contract`; +CREATE TABLE `contract` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `contract_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '合同编号', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '合同标题', + `type` int NOT NULL COMMENT '合同类型(字典)', + `party_a` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '甲方', + `party_b` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '乙方', + `signing_date` date NULL DEFAULT NULL, + `delivery_deadline` date NULL DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT 0 COMMENT '审核状态(字典)', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_a2f8461960ce0fcbd0d6551009`(`contract_number`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of contract +-- ---------------------------- +INSERT INTO `contract` VALUES (1, '2024-02-29 11:38:20.959071', '2024-03-06 10:40:25.000000', '2022092301', '山东矿机华信智能科技有限公司', 11, '山东矿机华信', '青岛比特维尔', '2024-01-01', '2024-02-01', 1, 0); +INSERT INTO `contract` VALUES (2, '2024-02-29 16:11:54.286196', '2024-02-29 16:50:30.000000', 'www', 'weqw', 10, 'rqw', 'rwq', '2024-02-01', '2024-02-28', 2, 0); +INSERT INTO `contract` VALUES (3, '2024-03-01 15:26:07.794697', '2024-03-01 15:30:49.000000', 'test1211', 'test', 13, '山东搞笑信息', '齐鲁医院', '2024-03-01', '2024-03-20', 0, 1); +INSERT INTO `contract` VALUES (4, '2024-03-01 16:47:22.125670', '2024-03-01 16:47:22.125670', '33024242', '21412412', 11, '2141', '41241', '2024-03-01', '2024-03-01', 0, 0); + +-- ---------------------------- +-- Table structure for contract_storage +-- ---------------------------- +DROP TABLE IF EXISTS `contract_storage`; +CREATE TABLE `contract_storage` ( + `contract_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`contract_id`, `file_id`) USING BTREE, + INDEX `IDX_b0a3f22af56decbc128c674447`(`contract_id`) USING BTREE, + INDEX `IDX_2fe7cda0f292b099b7e13f8f61`(`file_id`) USING BTREE, + CONSTRAINT `FK_2fe7cda0f292b099b7e13f8f612` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_b0a3f22af56decbc128c674447e` FOREIGN KEY (`contract_id`) REFERENCES `contract` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of contract_storage +-- ---------------------------- + +-- ---------------------------- +-- Table structure for materials_in_out +-- ---------------------------- +DROP TABLE IF EXISTS `materials_in_out`; +CREATE TABLE `materials_in_out` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `product_id` int NOT NULL COMMENT '产品', + `inOrOut` tinyint NOT NULL COMMENT '入库或出库', + `time` date NULL DEFAULT NULL COMMENT '时间', + `quantity` int NOT NULL DEFAULT 0 COMMENT '数量', + `unit_price` decimal(15, 10) NOT NULL DEFAULT 0.0000000000 COMMENT '单价', + `amount` decimal(15, 10) NOT NULL DEFAULT 0.0000000000 COMMENT '金额', + `agent` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '经办人', + `issuance_number` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '领料单号', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + `inventory_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '原材料库存编号', + `project_id` int NULL DEFAULT NULL COMMENT '项目', + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_770f1c4afd9631499ccc08bd58b`(`product_id`) USING BTREE, + INDEX `FK_7a5bd19f8fd458f6336efedf765`(`project_id`) USING BTREE, + CONSTRAINT `FK_770f1c4afd9631499ccc08bd58b` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_7a5bd19f8fd458f6336efedf765` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 28 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of materials_in_out +-- ---------------------------- +INSERT INTO `materials_in_out` VALUES (7, '2024-03-06 15:39:38.305753', '2024-03-07 11:10:10.000000', 19, 0, '2024-02-16', 100, 557.5200000000, 55752.2100000000, '蒋博', NULL, '沙湾项目', 0, 'KC1000', 3); +INSERT INTO `materials_in_out` VALUES (16, '2024-03-06 15:50:12.315922', '2024-03-07 11:10:07.000000', 19, 1, '2024-02-16', 100, 100.0000000000, 55752.2100000000, '蒋博', '3153019', '沙湾项目', 0, 'KC1000', 3); +INSERT INTO `materials_in_out` VALUES (17, '2024-03-06 16:06:57.397785', '2024-03-06 17:09:14.325720', 19, 1, '2024-03-06', 33, 33.0000000000, 33.0000000000, '3', '33', '33', 1, 'KC1000', NULL); +INSERT INTO `materials_in_out` VALUES (18, '2024-03-06 16:08:37.713482', '2024-03-07 11:10:03.000000', 20, 0, '2024-02-16', 100, 557.5200000000, 55752.2100000000, '蒋博', NULL, '沙湾项目', 0, 'KC1001', 3); +INSERT INTO `materials_in_out` VALUES (19, '2024-03-06 16:30:52.126055', '2024-03-07 11:09:57.000000', 20, 1, '2024-02-16', 100, 35.4000000000, 3539.8200000000, '蒋博', '3153019', '沙湾项目', 0, 'KC1001', 3); +INSERT INTO `materials_in_out` VALUES (20, '2024-03-06 16:34:04.845239', '2024-03-07 11:09:52.000000', 21, 0, '2024-02-16', 202, 18.5800000000, 3753.9800000000, '戚兆伟', NULL, '沙湾项目', 0, 'KC1002', 3); +INSERT INTO `materials_in_out` VALUES (21, '2024-03-06 16:34:40.829659', '2024-03-07 11:09:47.000000', 21, 1, '2024-02-16', 202, 18.5800000000, 3753.9800000000, '戚兆伟', '3153011', '沙湾项目', 0, 'KC1002', 3); +INSERT INTO `materials_in_out` VALUES (22, '2024-03-06 16:35:23.756416', '2024-03-07 11:09:43.000000', 21, 0, '2024-02-16', 200, 26.5500000000, 5309.7300000000, '戚兆伟', NULL, '沙湾项目', 0, 'KC1003', 3); +INSERT INTO `materials_in_out` VALUES (23, '2024-03-06 16:35:55.157718', '2024-03-07 11:09:38.000000', 21, 1, '2024-02-16', 200, 26.5500000000, 5309.7300000000, '戚兆伟', '3153011', '沙湾项目', 0, 'KC1003', 3); +INSERT INTO `materials_in_out` VALUES (24, '2024-03-06 16:38:52.490493', '2024-03-07 11:09:34.000000', 22, 0, '2024-02-20', 200, 300.8800000000, 60176.9900000000, '朱明仁', NULL, '东大项目', 0, 'KC1004', 2); +INSERT INTO `materials_in_out` VALUES (25, '2024-03-06 16:39:55.423422', '2024-03-07 11:09:30.000000', 22, 1, '2024-02-20', 200, 300.8849500000, 60176.9900000000, '朱明仁', '3153022', '东大项目', 0, 'KC1004', 2); +INSERT INTO `materials_in_out` VALUES (26, '2024-03-06 16:41:20.939530', '2024-03-07 11:09:27.000000', 23, 0, '2024-02-19', 1, 22005.7500000000, 22005.7500000000, '朱明仁', NULL, '东大项目', 0, 'KC1005', 2); +INSERT INTO `materials_in_out` VALUES (27, '2024-03-06 16:41:50.876715', '2024-03-07 11:08:31.000000', 23, 1, '2024-02-19', 1, 22005.7500000000, 22005.7500000000, '朱明仁', '3153023', '东大项目', 0, 'KC1005', 2); +INSERT INTO `materials_in_out` VALUES (28, '2024-03-07 09:49:54.511550', '2024-03-07 09:49:54.511550', 24, 0, '2024-02-16', 800, 0.9609250000, 768.7400000000, '朱明仁', NULL, '星火项目', 0, 'KC1006', 1); + +-- ---------------------------- +-- Table structure for materials_in_out_storage +-- ---------------------------- +DROP TABLE IF EXISTS `materials_in_out_storage`; +CREATE TABLE `materials_in_out_storage` ( + `materials_in_out_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`materials_in_out_id`, `file_id`) USING BTREE, + INDEX `IDX_9df13ab4d4747575c310668581`(`materials_in_out_id`) USING BTREE, + INDEX `IDX_96c00bfbcd71e93a6cc070e8e6`(`file_id`) USING BTREE, + CONSTRAINT `FK_96c00bfbcd71e93a6cc070e8e6c` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_9df13ab4d4747575c3106685810` FOREIGN KEY (`materials_in_out_id`) REFERENCES `materials_in_out` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of materials_in_out_storage +-- ---------------------------- + +-- ---------------------------- +-- Table structure for materials_inventory +-- ---------------------------- +DROP TABLE IF EXISTS `materials_inventory`; +CREATE TABLE `materials_inventory` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `company_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公司名称', + `product` int NOT NULL COMMENT '产品名称(字典)', + `unit` int NOT NULL COMMENT '单位(字典)', + `previous_inventory_quantity` int NOT NULL DEFAULT 0 COMMENT '之前的库存数量', + `previous_unit_price` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '之前的单价', + `previous_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '之前的金额', + `inventory_time` date NULL DEFAULT NULL COMMENT '入库时间', + `inventory_quantity` int NOT NULL DEFAULT 0 COMMENT '入库数量', + `inventory_unit_price` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '入库单价', + `inventory_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '入库金额', + `out_time` date NULL DEFAULT NULL COMMENT '出库时间', + `out_quantity` int NOT NULL DEFAULT 0 COMMENT '出库数量', + `out_unit_price` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '出库单价', + `out_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '出库金额', + `current_inventory_quantity` int NOT NULL DEFAULT 0 COMMENT '现在的结存数量', + `current_unit_price` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '现在的单价', + `current_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '现在的金额', + `agent` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '经办人', + `issuance_number` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '领料单号', + `project` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of materials_inventory +-- ---------------------------- +INSERT INTO `materials_inventory` VALUES (1, '2024-03-04 13:47:10.258131', '2024-03-04 14:15:15.373283', '北京特倍福电子技术有限公司', 1, 14, 0, 0.00, 0.00, '2024-02-16', 100, 557.52, 55752.21, '2024-02-16', 100, 557.52, 55752.21, 0, 0.00, 0.00, '蒋博', '3153019', '沙湾项目', '沙湾项目', 0); + +-- ---------------------------- +-- Table structure for product +-- ---------------------------- +DROP TABLE IF EXISTS `product`; +CREATE TABLE `product` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '产品名称', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + `company_id` int NULL DEFAULT NULL COMMENT '所属公司', + `name_pinyin` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '产品名称的拼音', + `unit_id` int NULL DEFAULT NULL COMMENT '单位(字典)', + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_a0503db1630a5b8a4d7deabd556`(`company_id`) USING BTREE, + INDEX `FK_b15422982adca3bf53adfb535de`(`unit_id`) USING BTREE, + CONSTRAINT `FK_a0503db1630a5b8a4d7deabd556` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_b15422982adca3bf53adfb535de` FOREIGN KEY (`unit_id`) REFERENCES `sys_dict_item` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of product +-- ---------------------------- +INSERT INTO `product` VALUES (1, '2024-03-04 16:47:37.604035', '2024-03-05 21:52:59.000000', '位移传感器', 0, 10, 'weiyichuanganqi', NULL); +INSERT INTO `product` VALUES (2, '2024-03-04 16:47:41.483273', '2024-03-06 09:13:03.000000', '磁环', 1, 6, 'cihuan', NULL); +INSERT INTO `product` VALUES (3, '2024-03-04 16:47:44.911165', '2024-03-05 21:52:52.000000', '天线', 0, 5, 'tianxian', NULL); +INSERT INTO `product` VALUES (4, '2024-03-04 16:47:48.398543', '2024-03-05 21:53:17.000000', '集成电路', 0, 8, 'jichengdianlu', NULL); +INSERT INTO `product` VALUES (5, '2024-03-05 09:09:05.015757', '2024-03-05 09:27:28.000000', '更新', 1, NULL, NULL, NULL); +INSERT INTO `product` VALUES (12, '2024-03-05 09:25:29.584423', '2024-03-06 09:12:02.000000', '巴伦', 0, 13, 'balun', 15); +INSERT INTO `product` VALUES (13, '2024-03-05 13:53:15.998630', '2024-03-06 09:14:13.000000', '极薄煤层控制器', 0, 10, 'jibaomeicengkongzhiqi', 14); +INSERT INTO `product` VALUES (14, '2024-03-05 16:05:30.485017', '2024-03-06 09:13:59.000000', '天线', 0, 13, 'tianxian', 15); +INSERT INTO `product` VALUES (15, '2024-03-05 17:21:20.378006', '2024-03-05 21:52:57.000000', 'USB智能充电线', 0, 7, 'USBzhinengchongdianxian', NULL); +INSERT INTO `product` VALUES (16, '2024-03-05 17:24:03.148627', '2024-03-05 17:30:48.000000', '33', 1, 5, NULL, NULL); +INSERT INTO `product` VALUES (17, '2024-03-05 17:30:32.450320', '2024-03-05 17:30:50.000000', 'test', 1, 5, 'test', NULL); +INSERT INTO `product` VALUES (18, '2024-03-05 21:52:11.477508', '2024-03-05 21:53:34.000000', '新增', 1, NULL, 'xinzeng', NULL); +INSERT INTO `product` VALUES (19, '2024-03-06 08:53:25.600367', '2024-03-06 09:13:39.000000', '位移传感器', 0, 12, 'weiyichuanganqi', 14); +INSERT INTO `product` VALUES (20, '2024-03-06 09:12:47.327409', '2024-03-06 09:12:47.327409', '磁环', 0, 12, 'cihuan', 14); +INSERT INTO `product` VALUES (21, '2024-03-06 09:13:21.382776', '2024-03-06 09:13:27.000000', '集成电路', 0, 13, 'jichengdianlu', 15); +INSERT INTO `product` VALUES (22, '2024-03-06 16:38:06.999498', '2024-03-06 16:38:14.000000', '电磁阀驱动器', 0, 10, 'diancifaqudongqi', 14); +INSERT INTO `product` VALUES (23, '2024-03-06 16:40:32.859846', '2024-03-06 16:40:32.859846', '电子元器件', 0, 11, 'dianziyuanqijian', 21); +INSERT INTO `product` VALUES (24, '2024-03-07 09:48:35.854273', '2024-03-07 09:48:35.854273', '排针', 0, 1, 'paizhen', 15); + +-- ---------------------------- +-- Table structure for product_storage +-- ---------------------------- +DROP TABLE IF EXISTS `product_storage`; +CREATE TABLE `product_storage` ( + `product_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`product_id`, `file_id`) USING BTREE, + INDEX `IDX_6dd288598f0a0ea3f72f31cb42`(`product_id`) USING BTREE, + INDEX `IDX_eecbd68d7d4d565baecee2d76c`(`file_id`) USING BTREE, + CONSTRAINT `FK_6dd288598f0a0ea3f72f31cb422` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_eecbd68d7d4d565baecee2d76c7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of product_storage +-- ---------------------------- +INSERT INTO `product_storage` VALUES (1, 124); + +-- ---------------------------- +-- Table structure for project +-- ---------------------------- +DROP TABLE IF EXISTS `project`; +CREATE TABLE `project` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目名称', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_dedfea394088ed136ddadeee89`(`name`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of project +-- ---------------------------- +INSERT INTO `project` VALUES (1, '2024-03-07 09:35:15.276345', '2024-03-07 09:36:01.000000', '星火项目', 0); +INSERT INTO `project` VALUES (2, '2024-03-07 09:35:20.004729', '2024-03-07 09:35:20.004729', '东大项目', 0); +INSERT INTO `project` VALUES (3, '2024-03-07 09:35:29.213057', '2024-03-07 09:35:29.213057', '沙湾煤业项目', 0); + +-- ---------------------------- +-- Table structure for project_storage +-- ---------------------------- +DROP TABLE IF EXISTS `project_storage`; +CREATE TABLE `project_storage` ( + `project_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`project_id`, `file_id`) USING BTREE, + INDEX `IDX_9058e954f8f09e2cfa2261c1f2`(`project_id`) USING BTREE, + INDEX `IDX_ac08ac8e4f973873f03dafaca2`(`file_id`) USING BTREE, + CONSTRAINT `FK_9058e954f8f09e2cfa2261c1f26` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_ac08ac8e4f973873f03dafaca2b` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of project_storage +-- ---------------------------- +INSERT INTO `project_storage` VALUES (1, 128); + +-- ---------------------------- +-- Table structure for sys_captcha_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_captcha_log`; +CREATE TABLE `sys_captcha_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NULL DEFAULT NULL, + `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_captcha_log +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sys_config +-- ---------------------------- +DROP TABLE IF EXISTS `sys_config`; +CREATE TABLE `sys_config` ( + `id` int NOT NULL AUTO_INCREMENT, + `key` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_2c363c25cf99bcaab3a7f389ba`(`key`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_config +-- ---------------------------- +INSERT INTO `sys_config` VALUES (1, 'sys_user_initPassword', '初始密码', '123456', '创建管理员账号的初始密码', '2023-11-10 00:31:44.154921', '2023-11-10 00:31:44.161263'); +INSERT INTO `sys_config` VALUES (2, 'sys_api_token', 'API Token', 'huaxin-admin', '用于请求 @ApiToken 的控制器', '2023-11-10 00:31:44.154921', '2024-01-29 09:52:27.000000'); +INSERT INTO `sys_config` VALUES (3, 'companyName', '公司名称', '华信智能', '菜单侧栏公司的名称', '2024-03-06 13:06:47.347660', '2024-03-06 13:07:18.000000'); +INSERT INTO `sys_config` VALUES (4, 'materials_in_out_prefix', '出入库记录开头编号', 'KC', '出入库记录开头编号', '2024-03-06 14:50:04.844992', '2024-03-06 14:50:04.844992'); + +-- ---------------------------- +-- Table structure for sys_dept +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dept`; +CREATE TABLE `sys_dept` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `orderNo` int NULL DEFAULT 0, + `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '', + `parentId` int NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_c75280b01c49779f2323536db67`(`parentId`) USING BTREE, + CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_dept +-- ---------------------------- +INSERT INTO `sys_dept` VALUES (1, '山东矿机华信智能科技', 1, '1.', NULL, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); +INSERT INTO `sys_dept` VALUES (2, '计算机开发部', 1, '1.2.', 1, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); +INSERT INTO `sys_dept` VALUES (3, '行政部', 2, '1.3.', 1, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); +INSERT INTO `sys_dept` VALUES (4, '商务部', 3, '1.4.', 1, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); +INSERT INTO `sys_dept` VALUES (5, '财务部', 4, '1.5.', 1, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); +INSERT INTO `sys_dept` VALUES (6, '山东矿机华能装备制造', 2, '6.', NULL, '2023-11-10 00:31:43.996025', '2024-03-06 17:17:30.000000'); +INSERT INTO `sys_dept` VALUES (8, '研发部', 1, '6.8.', 6, '2023-11-10 00:31:43.996025', '2024-03-06 17:17:30.000000'); +INSERT INTO `sys_dept` VALUES (9, '市场部', 1, '6.9.', 6, '2023-11-10 00:31:43.996025', '2024-03-06 17:17:30.000000'); + +-- ---------------------------- +-- Table structure for sys_dict +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict`; +CREATE TABLE `sys_dict` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint NOT NULL DEFAULT 1, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_d112365748f740ee260b65ce91`(`name`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_dict +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sys_dict_item +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_item`; +CREATE TABLE `sys_dict_item` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `order` int NULL DEFAULT NULL COMMENT '字典项排序', + `status` tinyint NOT NULL DEFAULT 1, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `type_id` int NULL DEFAULT NULL, + `orderNo` int NULL DEFAULT NULL COMMENT '字典项排序', + `deleted_at` datetime(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_d68ea74fcb041c8cfd1fd659844`(`type_id`) USING BTREE, + CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 23 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_dict_item +-- ---------------------------- +INSERT INTO `sys_dict_item` VALUES (1, '2024-01-29 01:24:51.846135', '2024-01-29 02:23:19.000000', 1, 1, '男', '1', 0, 1, '性别男', 1, 3, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (2, '2024-01-29 01:32:58.458741', '2024-01-29 01:58:20.000000', 1, 1, '女', '0', 1, 1, '性别女', 1, 2, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (3, '2024-01-29 01:59:17.805394', '2024-01-29 14:37:18.000000', 1, 1, '人妖王', '3', NULL, 1, '安布里奥·伊万科夫', 1, 0, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (5, '2024-01-29 02:13:01.782466', '2024-01-29 02:13:01.782466', 1, 1, '显示', '1', NULL, 1, '显示菜单', 2, 0, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (6, '2024-01-29 02:13:31.134721', '2024-01-29 02:13:31.134721', 1, 1, '隐藏', '0', NULL, 1, '隐藏菜单', 2, 0, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (10, '2024-02-28 16:39:44.977246', '2024-02-29 15:56:02.670095', 1, 1, '商务合同', 'business', NULL, 1, '商务合同', 3, 0, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (11, '2024-02-28 16:42:43.539979', '2024-02-29 15:56:07.676659', 1, 1, '销售合同', 'sales', NULL, 1, '', 3, 1, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (12, '2024-02-28 16:42:58.224299', '2024-02-29 15:56:05.815675', 1, 1, '租赁合同', 'Lease', NULL, 1, NULL, 3, 2, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (13, '2024-02-28 16:43:26.311650', '2024-02-29 15:56:10.462447', 1, 1, '服务合同', 'service', NULL, 1, NULL, 3, 3, '2024-03-01 15:28:21.702930'); +INSERT INTO `sys_dict_item` VALUES (14, '2024-03-04 13:42:26.688441', '2024-03-04 13:42:26.688441', 1, 1, '件', 'unit_jian', NULL, 1, NULL, 5, 0, '2024-03-04 13:42:26.688441'); +INSERT INTO `sys_dict_item` VALUES (15, '2024-03-04 13:42:38.298733', '2024-03-04 13:42:38.298733', 1, 1, '个', 'unit_ge', NULL, 1, NULL, 5, 1, '2024-03-04 13:42:38.298733'); +INSERT INTO `sys_dict_item` VALUES (16, '2024-03-04 13:43:30.965353', '2024-03-04 13:43:30.965353', 1, 1, '千克', 'unit_qianke', NULL, 1, NULL, 5, 2, '2024-03-04 13:43:30.965353'); +INSERT INTO `sys_dict_item` VALUES (17, '2024-03-04 13:43:44.353125', '2024-03-04 13:43:44.353125', 1, 1, '克', 'unit_ke', NULL, 1, NULL, 5, 3, '2024-03-04 13:43:44.353125'); +INSERT INTO `sys_dict_item` VALUES (18, '2024-03-04 13:43:56.643339', '2024-03-04 13:43:56.643339', 1, 1, '升', 'unit_sheng', NULL, 1, NULL, 5, 4, '2024-03-04 13:43:56.643339'); +INSERT INTO `sys_dict_item` VALUES (19, '2024-03-04 13:44:09.242901', '2024-03-04 13:44:09.242901', 1, 1, '毫升', 'unit_haosheng', NULL, 1, NULL, 5, 5, '2024-03-04 13:44:09.242901'); +INSERT INTO `sys_dict_item` VALUES (20, '2024-03-04 13:44:26.620837', '2024-03-04 13:44:29.000000', 1, 1, '卷', 'unit_juan', NULL, 1, NULL, 5, 6, '2024-03-04 13:44:29.654314'); +INSERT INTO `sys_dict_item` VALUES (21, '2024-03-04 14:10:38.216659', '2024-03-04 14:10:54.000000', 1, 1, '批', 'unit_pi', NULL, 1, NULL, 5, 7, '2024-03-04 14:10:54.729114'); +INSERT INTO `sys_dict_item` VALUES (22, '2024-03-04 14:10:48.864655', '2024-03-04 14:10:48.864655', 1, 1, '片', 'unit_pian', NULL, 1, NULL, 5, 8, '2024-03-04 14:10:48.864655'); +INSERT INTO `sys_dict_item` VALUES (23, '2024-03-04 14:11:06.319281', '2024-03-04 14:11:06.319281', 1, 1, '套', 'unit_tao', NULL, 1, NULL, 5, 9, '2024-03-04 14:11:06.319281'); + +-- ---------------------------- +-- Table structure for sys_dict_type +-- ---------------------------- +DROP TABLE IF EXISTS `sys_dict_type`; +CREATE TABLE `sys_dict_type` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `create_by` int NOT NULL COMMENT '创建者', + `update_by` int NOT NULL COMMENT '更新者', + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint NOT NULL DEFAULT 1, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `deleted_at` datetime(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_74d0045ff7fab9f67adc0b1bda`(`code`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_dict_type +-- ---------------------------- +INSERT INTO `sys_dict_type` VALUES (1, '2024-01-28 08:19:12.777447', '2024-02-08 13:05:10.000000', 1, 1, '性别', 1, '性别单选', 'sys_user_gender', '2024-03-01 15:28:21.689753'); +INSERT INTO `sys_dict_type` VALUES (2, '2024-01-28 08:38:41.235185', '2024-01-29 02:11:33.000000', 1, 1, '菜单显示状态', 1, '菜单显示状态', 'sys_show_hide', '2024-03-01 15:28:21.689753'); +INSERT INTO `sys_dict_type` VALUES (3, '2024-02-28 16:38:27.311577', '2024-03-04 13:26:29.000000', 1, 1, '合同类型', 1, '合同类型', 'contract_type', '2024-03-04 13:26:29.911469'); +INSERT INTO `sys_dict_type` VALUES (5, '2024-03-04 13:41:05.156027', '2024-03-04 13:41:05.156027', 1, 1, '单位', 1, '材料盘点表等单位。件。个', 'unit', '2024-03-04 13:41:05.156027'); + +-- ---------------------------- +-- Table structure for sys_login_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_login_log`; +CREATE TABLE `sys_login_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `ua` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `user_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_3029712e0df6a28edaee46fd470`(`user_id`) USING BTREE, + CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_login_log +-- ---------------------------- +INSERT INTO `sys_login_log` VALUES (1, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 11:49:38.330842', '2024-02-28 11:49:38.330842', 1); +INSERT INTO `sys_login_log` VALUES (2, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:06:01.450911', '2024-02-28 13:06:01.450911', 1); +INSERT INTO `sys_login_log` VALUES (3, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:08:18.525617', '2024-02-28 13:08:18.525617', 1); +INSERT INTO `sys_login_log` VALUES (4, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:10:51.368580', '2024-02-28 13:10:51.368580', 1); +INSERT INTO `sys_login_log` VALUES (5, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:53:17.506614', '2024-02-28 13:53:17.506614', 1); +INSERT INTO `sys_login_log` VALUES (6, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:53:53.201053', '2024-02-28 13:53:53.201053', 1); +INSERT INTO `sys_login_log` VALUES (7, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-29 16:25:14.762388', '2024-02-29 16:25:14.762388', 1); +INSERT INTO `sys_login_log` VALUES (8, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', '内网IP', NULL, '2024-02-29 16:26:09.106911', '2024-02-29 16:26:09.106911', 1); +INSERT INTO `sys_login_log` VALUES (9, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 10:52:33.093110', '2024-03-01 10:52:33.093110', 1); +INSERT INTO `sys_login_log` VALUES (10, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 10:53:09.633370', '2024-03-01 10:53:09.633370', 1); +INSERT INTO `sys_login_log` VALUES (11, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:45:36.933407', '2024-03-01 15:45:36.933407', 1); +INSERT INTO `sys_login_log` VALUES (12, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:04.780541', '2024-03-01 15:57:04.780541', 1); +INSERT INTO `sys_login_log` VALUES (13, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:11.280355', '2024-03-01 15:57:11.280355', 1); +INSERT INTO `sys_login_log` VALUES (14, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:18.351492', '2024-03-01 15:57:18.351492', 1); +INSERT INTO `sys_login_log` VALUES (15, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:29.111440', '2024-03-01 15:57:29.111440', 1); +INSERT INTO `sys_login_log` VALUES (16, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:33.903797', '2024-03-01 15:57:33.903797', 1); +INSERT INTO `sys_login_log` VALUES (17, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 16:53:24.824327', '2024-03-01 16:53:24.824327', 1); +INSERT INTO `sys_login_log` VALUES (18, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 11:36:22.860536', '2024-03-04 11:36:22.860536', 1); +INSERT INTO `sys_login_log` VALUES (19, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 13:14:12.256792', '2024-03-04 13:14:12.256792', 1); +INSERT INTO `sys_login_log` VALUES (20, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 13:15:42.383909', '2024-03-04 13:15:42.383909', 1); +INSERT INTO `sys_login_log` VALUES (21, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 13:16:04.702698', '2024-03-04 13:16:04.702698', 1); +INSERT INTO `sys_login_log` VALUES (22, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 15:42:54.327613', '2024-03-04 15:42:54.327613', 1); +INSERT INTO `sys_login_log` VALUES (23, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 17:25:42.137701', '2024-03-04 17:25:42.137701', 1); +INSERT INTO `sys_login_log` VALUES (24, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-05 14:34:08.444527', '2024-03-05 14:34:08.444527', 1); +INSERT INTO `sys_login_log` VALUES (25, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-06 08:45:04.588314', '2024-03-06 08:45:04.588314', 1); +INSERT INTO `sys_login_log` VALUES (26, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-06 17:17:55.139092', '2024-03-06 17:17:55.139092', 1); +INSERT INTO `sys_login_log` VALUES (27, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-07 09:14:33.829960', '2024-03-07 09:14:33.829960', 1); +INSERT INTO `sys_login_log` VALUES (28, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-07 11:11:38.239629', '2024-03-07 11:11:38.239629', 1); +INSERT INTO `sys_login_log` VALUES (29, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-07 11:46:14.622820', '2024-03-07 11:46:14.622820', 1); + +-- ---------------------------- +-- Table structure for sys_menu +-- ---------------------------- +DROP TABLE IF EXISTS `sys_menu`; +CREATE TABLE `sys_menu` ( + `id` int NOT NULL AUTO_INCREMENT, + `parent_id` int NULL DEFAULT NULL, + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `type` tinyint NOT NULL DEFAULT 0, + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '', + `order_no` int NULL DEFAULT 0, + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `keep_alive` tinyint NOT NULL DEFAULT 1, + `show` tinyint NOT NULL DEFAULT 1, + `status` tinyint NOT NULL DEFAULT 1, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `is_ext` tinyint NOT NULL DEFAULT 0, + `ext_open_mode` tinyint NOT NULL DEFAULT 1, + `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 158 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_menu +-- ---------------------------- +INSERT INTO `sys_menu` VALUES (1, NULL, '/system', '系统管理', '', 0, 'ant-design:setting-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-29 10:41:29.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (2, 1, '/system/user', '用户管理', 'system:user:list', 1, 'ant-design:user-outlined', 0, 'system/user/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:10:30.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (3, 1, '/system/role', '角色管理', 'system:role:list', 1, 'ep:user', 1, 'system/role/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:02.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (4, 1, '/system/menu', '菜单管理', 'system:menu:list', 1, 'ep:menu', 2, 'system/menu/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:18.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:44.567023', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (6, 5, '/system/monitor/online', '在线用户', 'system:online:list', 1, '', 0, 'system/monitor/online/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:13:59.519267', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (7, 5, '/sys/monitor/login-log', '登录日志', 'system:log:login:list', 1, '', 0, 'system/monitor/log/login/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:02.610719', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (8, 5, '/system/monitor/serve', '服务监控', 'system:serve:stat', 1, '', 4, 'system/monitor/serve/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:05.606355', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:52.967983', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (10, 9, '/system/task', '任务管理', '', 1, '', 0, 'system/schedule/task/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:14:39.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (11, 9, '/system/task/log', '任务日志', 'system:task:list', 1, '', 0, 'system/schedule/log/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:15:01.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (12, NULL, '/document', '文档', '', 0, 'ion:tv-outline', 2, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:51.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (14, 12, 'https://www.typeorm.org/', 'Typeorm中文文档(外链)', NULL, 1, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:39:53.000000', 1, 1, NULL); +INSERT INTO `sys_menu` VALUES (15, 12, 'https://docs.nestjs.cn/', 'Nest.js中文文档(内嵌)', '', 1, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:40:43.000000', 1, 2, NULL); +INSERT INTO `sys_menu` VALUES (20, 2, NULL, '新增', 'system:user:create', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (21, 2, '', '删除', 'system:user:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (22, 2, '', '更新', 'system:user:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (23, 2, '', '查询', 'system:user:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (24, 3, '', '新增', 'system:role:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (25, 3, '', '删除', 'system:role:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (26, 3, '', '修改', 'system:role:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (27, 3, '', '查询', 'system:role:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (28, 4, NULL, '新增', 'system:menu:create', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (29, 4, NULL, '删除', 'system:menu:delete', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (30, 4, '', '修改', 'system:menu:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (31, 4, NULL, '查询', 'system:menu:read', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (32, 6, '', '下线', 'system:online:kick', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (34, 10, '', '新增', 'system:task:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (35, 10, '', '删除', 'system:task:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (36, 10, '', '执行一次', 'system:task:once', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (37, 10, '', '查询', 'system:task:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (38, 10, '', '运行', 'system:task:start', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (39, 10, '', '暂停', 'system:task:stop', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (40, 10, '', '更新', 'system:task:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (41, 7, '', '查询登录日志', 'system:log:login:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (42, 7, '', '查询任务日志', 'system:log:task:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (43, NULL, '/about', '关于', '', 1, 'ant-design:info-circle-outlined', 260, 'account/about', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-10 09:35:41.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (48, NULL, '/tool', '系统工具', NULL, 0, 'ant-design:tool-outlined', 255, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-29 10:41:25.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (49, 48, '/tool/email', '邮件工具', 'system:tools:email', 1, 'ant-design:send-outlined', 1, 'tool/email/index', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:38.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (50, 49, NULL, '发送邮件', 'tools:email:send', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (51, 48, '/tool/storage', '存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:59:17.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (52, 51, NULL, '文件上传', 'upload:upload', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 01:04:08.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (53, 51, NULL, '文件删除', 'tool:storage:delete', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:56:01.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (54, 2, NULL, '修改密码', 'system:user:password', 2, '', 5, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (56, 1, '/system/dict-type', '字典管理', 'system:dict-type:list', 1, 'ant-design:book-outlined', 4, 'system/dict-type/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:12.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (57, 56, NULL, '新增', 'system:dict-type:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:20.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (58, 56, NULL, '更新', 'system:dict-type:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:26.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (59, 56, NULL, '删除', 'system:dict-type:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:42.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (60, 56, NULL, '查询', 'system:dict-type:info', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (61, 1, '/system/dept', '部门管理', 'system:dept:list', 1, 'ant-design:deployment-unit-outlined', 3, 'system/dept/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:55.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (62, 61, NULL, '新增', 'system:dept:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (63, 61, NULL, '更新', 'system:dept:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (64, 61, NULL, '删除', 'system:dept:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (65, 61, NULL, '查询', 'system:dept:read', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (68, 5, '/health', '健康检查', '', 1, '', 4, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:33.352155', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (69, 68, NULL, '网络', 'app:health:network', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (70, 68, NULL, '数据库', 'app:health: database', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (86, 1, '/param-config', '参数配置', 'system:param-config:list', 1, 'ep:edit', 255, 'system/param-config/index', 0, 1, 1, '2024-01-10 17:34:52.569663', '2024-01-19 02:11:27.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (87, 86, NULL, '查询', 'system:param-config:read', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:20.983241', '2024-01-10 17:39:20.983241', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (88, 86, NULL, '新增', 'system:param-config:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:57.543510', '2024-01-10 17:39:57.543510', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (89, 86, NULL, '更新', 'system:param-config:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:40:27.355944', '2024-01-10 17:40:27.355944', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (92, 86, NULL, '删除', 'system:param-config:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:57:32.059887', '2024-01-10 17:57:32.059887', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (107, 1, 'system/dict-item/:id', '字典项管理', 'system:dict-item:list', 1, 'ant-design:facebook-outlined', 255, 'system/dict-item/index', 0, 0, 1, '2024-01-28 09:21:17.409532', '2024-01-30 13:09:47.000000', 0, 1, '字典管理'); +INSERT INTO `sys_menu` VALUES (108, 107, NULL, '新增', 'system:dict-item:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:22:39.401758', '2024-01-28 22:38:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (109, 107, NULL, '更新', 'system:dict-item:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:26:43.911886', '2024-01-28 09:26:43.911886', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (110, 107, NULL, '删除', 'system:dict-item:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:28.535225', '2024-01-28 09:27:28.535225', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL); +INSERT INTO `sys_menu` VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 0, 1, '2024-02-10 08:00:02.394616', '2024-02-28 11:51:21.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (116, 115, 'manage', '文件管理', 'netdisk:manage:list', 1, '', 252, 'netdisk/manage', 0, 1, 1, '2024-02-10 08:03:49.837348', '2024-02-10 09:34:41.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (120, 116, NULL, '删除', 'netdisk:manage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:09.480601', '2024-02-10 08:42:09.480601', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (121, 116, NULL, '获取文件上传token', 'netdisk:manage:token', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:57.688104', '2024-02-10 08:42:57.688104', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (122, 116, NULL, '添加文件备注', 'netdisk:manage:mark', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:43:40.117321', '2024-02-10 08:43:40.117321', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (123, 116, NULL, '下载文件', 'netdisk:manage:download', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:01.338984', '2024-02-10 08:44:01.338984', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (124, 116, NULL, '重命名文件或文件夹', 'netdisk:manage:rename', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:27.233379', '2024-02-10 08:45:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (125, 116, NULL, '复制文件或文件夹', 'netdisk:manage:copy', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:44.725391', '2024-02-10 08:45:48.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (126, 116, NULL, '剪切文件或文件夹', 'netdisk:manage:cut', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:45:21.660511', '2024-02-10 08:45:21.660511', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (127, 115, 'overview', '网盘概览', 'netdisk:overview:desc', 1, '', 254, 'netdisk/overview', 0, 1, 1, '2024-02-10 09:32:56.981190', '2024-02-10 09:34:18.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (128, NULL, '/contract', '合同管理', NULL, 0, 'ep:document', 1, NULL, 1, 1, 1, '2024-02-29 10:40:39.080419', '2024-02-29 10:42:37.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (129, 128, '/contract/index', '合同审核', 'app:contract:list', 1, 'ep:document', 1, 'contract/index', 0, 1, 1, '2024-02-29 10:46:09.245521', '2024-02-29 14:59:56.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (130, NULL, '/vehicle-usage/index', '车辆使用', NULL, 1, 'ant-design:car-outlined', 4, 'vehicle-usage/index', 0, 1, 1, '2024-02-29 10:48:35.035363', '2024-03-04 14:18:36.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (131, 150, '/materials-inventory/record-in-out', '出入库记录', 'materials_inventory:history_in_out:list', 1, 'ep:coin', 3, 'materials-inventory/in-out/index', 0, 1, 1, '2024-02-29 11:03:49.710130', '2024-03-05 15:22:06.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (132, 129, NULL, '更新', 'app:contract:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:00:39.641043', '2024-02-29 15:00:39.641043', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (133, 129, NULL, '删除', 'app:contract:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:00:59.376071', '2024-02-29 15:00:59.376071', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (134, 129, NULL, '查询', 'app:contract:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:01:14.209396', '2024-02-29 15:45:29.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (135, 129, NULL, '新增', 'app:contract:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:44:46.950582', '2024-02-29 15:44:46.950582', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (136, 131, NULL, '新增', 'materials_inventory:history_in_out:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:02.597782', '2024-03-06 10:54:28.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (137, 131, NULL, '更新', 'materials_inventory:history_in_out:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:15.192910', '2024-03-06 10:54:57.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (138, 131, NULL, '查询单个', 'app:contract:read', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:32.488892', '2024-03-01 17:17:32.488892', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (139, 131, NULL, '删除', 'materials_inventory:history_in_out:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:43.455773', '2024-03-06 10:55:06.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (140, 150, '/materials-inventory/company', '乙方公司管理', 'app:company:list', 1, 'ep:office-building', 6, 'materials-inventory/company/index', 0, 1, 1, '2024-03-04 15:44:30.769048', '2024-03-05 15:22:22.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (141, 140, NULL, '单个查询', 'app:company:read', 2, '', 1, NULL, 1, 1, 1, '2024-03-04 15:45:55.979802', '2024-03-04 15:45:55.979802', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (142, 140, NULL, '新增', 'app:company:create', 2, '', 2, NULL, 1, 1, 1, '2024-03-04 15:46:11.260636', '2024-03-04 15:46:11.260636', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (143, 140, NULL, '更新', 'app:company:update', 2, '', 3, NULL, 1, 1, 1, '2024-03-04 15:46:25.098204', '2024-03-04 15:46:25.098204', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (144, 140, NULL, '删除', 'app:company:delete', 2, '', 4, NULL, 1, 1, 1, '2024-03-04 15:46:50.812446', '2024-03-04 15:46:50.812446', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (145, 150, '/materials-inventory/product', '产品目录', 'app:product:list', 1, 'ant-design:product-outlined', 6, 'materials-inventory/product/index', 0, 1, 1, '2024-03-04 16:43:22.749281', '2024-03-06 13:40:03.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (146, 145, NULL, '单个查询', 'app:product:read', 2, '', 1, NULL, 1, 1, 1, '2024-03-04 16:44:56.482508', '2024-03-04 16:44:56.482508', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (147, 145, NULL, '新增', 'app:product:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:08.211188', '2024-03-04 16:45:08.211188', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (148, 145, NULL, '更新', 'app:product:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:25.457903', '2024-03-04 16:45:25.457903', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (149, 145, NULL, '删除', 'app:product:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:39.352621', '2024-03-04 16:45:39.352621', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (150, NULL, '/materials-inventory', '原材料盘点', NULL, 0, 'ant-design:dashboard-outlined', 3, NULL, 1, 1, 1, '2024-03-04 16:53:32.172674', '2024-03-04 16:53:32.172674', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (151, 131, NULL, '导出', 'materials_inventory:history_in_out:export', 2, '', 5, NULL, 1, 1, 1, '2024-03-06 13:09:39.201093', '2024-03-06 13:09:39.201093', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (152, 150, '/materials-inventory/inventory-check', '原材料盘点', 'app:materials_inventory:list', 1, 'ant-design:dashboard-outlined', 2, 'materials-inventory/inventory-check/index', 1, 0, 1, '2024-03-06 13:33:24.795599', '2024-03-06 17:14:14.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (153, 150, '/materials-inventory/project', '项目管理', 'app:project:list', 1, 'ep:memo', 4, 'materials-inventory/project/index', 0, 1, 1, '2024-03-07 09:28:19.234454', '2024-03-07 11:12:00.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (154, 153, NULL, '新增', 'app:project:create', 2, '', 1, NULL, 1, 1, 1, '2024-03-07 09:28:47.855064', '2024-03-07 09:28:47.855064', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (155, 153, NULL, '更新', 'app:project:update', 2, '', 2, NULL, 1, 1, 1, '2024-03-07 09:29:03.183084', '2024-03-07 09:29:03.183084', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (156, 153, NULL, '删除', 'app:project:delete', 2, '', 3, NULL, 1, 1, 1, '2024-03-07 09:29:16.684943', '2024-03-07 09:29:16.684943', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (157, 153, NULL, '单个信息', 'app:project:read', 2, '', 4, NULL, 1, 1, 1, '2024-03-07 09:29:33.424578', '2024-03-07 09:29:33.424578', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (158, 131, NULL, '导出原材料盘点表', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 11:46:54.468400', '2024-03-07 11:46:54.468400', 0, 1, NULL); + +-- ---------------------------- +-- Table structure for sys_role +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role`; +CREATE TABLE `sys_role` ( + `id` int NOT NULL AUTO_INCREMENT, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `status` tinyint NULL DEFAULT 1, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `default` tinyint NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_223de54d6badbe43a5490450c3`(`name`) USING BTREE, + UNIQUE INDEX `IDX_05edc0a51f41bb16b7d8137da9`(`value`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_role +-- ---------------------------- +INSERT INTO `sys_role` VALUES (1, 'admin', '管理员', '超级管理员', 1, '2023-11-10 00:31:44.058463', '2024-01-28 21:08:39.000000', NULL); +INSERT INTO `sys_role` VALUES (2, 'user', '用户', '', 1, '2023-11-10 00:31:44.058463', '2024-01-30 18:44:45.000000', 1); +INSERT INTO `sys_role` VALUES (9, 'test', '测试', NULL, 1, '2024-01-23 22:46:52.408827', '2024-01-30 01:04:52.000000', NULL); + +-- ---------------------------- +-- Table structure for sys_role_menus +-- ---------------------------- +DROP TABLE IF EXISTS `sys_role_menus`; +CREATE TABLE `sys_role_menus` ( + `role_id` int NOT NULL, + `menu_id` int NOT NULL, + PRIMARY KEY (`role_id`, `menu_id`) USING BTREE, + INDEX `IDX_35ce749b04d57e226d059e0f63`(`role_id`) USING BTREE, + INDEX `IDX_2b95fdc95b329d66c18f5baed6`(`menu_id`) USING BTREE, + CONSTRAINT `FK_2b95fdc95b329d66c18f5baed6d` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `FK_35ce749b04d57e226d059e0f633` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_role_menus +-- ---------------------------- +INSERT INTO `sys_role_menus` VALUES (1, 1); +INSERT INTO `sys_role_menus` VALUES (1, 2); +INSERT INTO `sys_role_menus` VALUES (1, 3); +INSERT INTO `sys_role_menus` VALUES (1, 4); +INSERT INTO `sys_role_menus` VALUES (1, 5); +INSERT INTO `sys_role_menus` VALUES (1, 6); +INSERT INTO `sys_role_menus` VALUES (1, 7); +INSERT INTO `sys_role_menus` VALUES (1, 8); +INSERT INTO `sys_role_menus` VALUES (1, 9); +INSERT INTO `sys_role_menus` VALUES (1, 10); +INSERT INTO `sys_role_menus` VALUES (1, 11); +INSERT INTO `sys_role_menus` VALUES (1, 12); +INSERT INTO `sys_role_menus` VALUES (1, 14); +INSERT INTO `sys_role_menus` VALUES (1, 15); +INSERT INTO `sys_role_menus` VALUES (1, 20); +INSERT INTO `sys_role_menus` VALUES (1, 21); +INSERT INTO `sys_role_menus` VALUES (1, 22); +INSERT INTO `sys_role_menus` VALUES (1, 23); +INSERT INTO `sys_role_menus` VALUES (1, 24); +INSERT INTO `sys_role_menus` VALUES (1, 25); +INSERT INTO `sys_role_menus` VALUES (1, 26); +INSERT INTO `sys_role_menus` VALUES (1, 27); +INSERT INTO `sys_role_menus` VALUES (1, 28); +INSERT INTO `sys_role_menus` VALUES (1, 29); +INSERT INTO `sys_role_menus` VALUES (1, 30); +INSERT INTO `sys_role_menus` VALUES (1, 31); +INSERT INTO `sys_role_menus` VALUES (1, 32); +INSERT INTO `sys_role_menus` VALUES (1, 34); +INSERT INTO `sys_role_menus` VALUES (1, 35); +INSERT INTO `sys_role_menus` VALUES (1, 36); +INSERT INTO `sys_role_menus` VALUES (1, 37); +INSERT INTO `sys_role_menus` VALUES (1, 38); +INSERT INTO `sys_role_menus` VALUES (1, 39); +INSERT INTO `sys_role_menus` VALUES (1, 40); +INSERT INTO `sys_role_menus` VALUES (1, 41); +INSERT INTO `sys_role_menus` VALUES (1, 42); +INSERT INTO `sys_role_menus` VALUES (1, 43); +INSERT INTO `sys_role_menus` VALUES (1, 48); +INSERT INTO `sys_role_menus` VALUES (1, 49); +INSERT INTO `sys_role_menus` VALUES (1, 50); +INSERT INTO `sys_role_menus` VALUES (1, 51); +INSERT INTO `sys_role_menus` VALUES (1, 52); +INSERT INTO `sys_role_menus` VALUES (1, 53); +INSERT INTO `sys_role_menus` VALUES (1, 54); +INSERT INTO `sys_role_menus` VALUES (1, 56); +INSERT INTO `sys_role_menus` VALUES (1, 57); +INSERT INTO `sys_role_menus` VALUES (1, 58); +INSERT INTO `sys_role_menus` VALUES (1, 59); +INSERT INTO `sys_role_menus` VALUES (1, 60); +INSERT INTO `sys_role_menus` VALUES (1, 61); +INSERT INTO `sys_role_menus` VALUES (1, 62); +INSERT INTO `sys_role_menus` VALUES (1, 63); +INSERT INTO `sys_role_menus` VALUES (1, 64); +INSERT INTO `sys_role_menus` VALUES (1, 65); +INSERT INTO `sys_role_menus` VALUES (1, 68); +INSERT INTO `sys_role_menus` VALUES (1, 69); +INSERT INTO `sys_role_menus` VALUES (1, 70); +INSERT INTO `sys_role_menus` VALUES (1, 86); +INSERT INTO `sys_role_menus` VALUES (1, 87); +INSERT INTO `sys_role_menus` VALUES (1, 88); +INSERT INTO `sys_role_menus` VALUES (1, 89); +INSERT INTO `sys_role_menus` VALUES (1, 92); +INSERT INTO `sys_role_menus` VALUES (1, 107); +INSERT INTO `sys_role_menus` VALUES (1, 108); +INSERT INTO `sys_role_menus` VALUES (1, 109); +INSERT INTO `sys_role_menus` VALUES (1, 110); +INSERT INTO `sys_role_menus` VALUES (1, 111); +INSERT INTO `sys_role_menus` VALUES (2, 1); +INSERT INTO `sys_role_menus` VALUES (2, 5); +INSERT INTO `sys_role_menus` VALUES (2, 6); +INSERT INTO `sys_role_menus` VALUES (2, 7); +INSERT INTO `sys_role_menus` VALUES (2, 8); +INSERT INTO `sys_role_menus` VALUES (2, 9); +INSERT INTO `sys_role_menus` VALUES (2, 10); +INSERT INTO `sys_role_menus` VALUES (2, 11); +INSERT INTO `sys_role_menus` VALUES (2, 12); +INSERT INTO `sys_role_menus` VALUES (2, 14); +INSERT INTO `sys_role_menus` VALUES (2, 15); +INSERT INTO `sys_role_menus` VALUES (2, 32); +INSERT INTO `sys_role_menus` VALUES (2, 34); +INSERT INTO `sys_role_menus` VALUES (2, 35); +INSERT INTO `sys_role_menus` VALUES (2, 36); +INSERT INTO `sys_role_menus` VALUES (2, 37); +INSERT INTO `sys_role_menus` VALUES (2, 38); +INSERT INTO `sys_role_menus` VALUES (2, 39); +INSERT INTO `sys_role_menus` VALUES (2, 40); +INSERT INTO `sys_role_menus` VALUES (2, 41); +INSERT INTO `sys_role_menus` VALUES (2, 42); +INSERT INTO `sys_role_menus` VALUES (2, 43); +INSERT INTO `sys_role_menus` VALUES (2, 48); +INSERT INTO `sys_role_menus` VALUES (2, 49); +INSERT INTO `sys_role_menus` VALUES (2, 50); +INSERT INTO `sys_role_menus` VALUES (2, 51); +INSERT INTO `sys_role_menus` VALUES (2, 52); +INSERT INTO `sys_role_menus` VALUES (2, 53); +INSERT INTO `sys_role_menus` VALUES (2, 56); +INSERT INTO `sys_role_menus` VALUES (2, 57); +INSERT INTO `sys_role_menus` VALUES (2, 58); +INSERT INTO `sys_role_menus` VALUES (2, 59); +INSERT INTO `sys_role_menus` VALUES (2, 60); +INSERT INTO `sys_role_menus` VALUES (2, 68); +INSERT INTO `sys_role_menus` VALUES (2, 69); +INSERT INTO `sys_role_menus` VALUES (2, 70); +INSERT INTO `sys_role_menus` VALUES (2, 86); +INSERT INTO `sys_role_menus` VALUES (2, 87); +INSERT INTO `sys_role_menus` VALUES (2, 88); +INSERT INTO `sys_role_menus` VALUES (2, 89); +INSERT INTO `sys_role_menus` VALUES (2, 92); +INSERT INTO `sys_role_menus` VALUES (2, 107); +INSERT INTO `sys_role_menus` VALUES (2, 108); +INSERT INTO `sys_role_menus` VALUES (2, 109); +INSERT INTO `sys_role_menus` VALUES (2, 110); +INSERT INTO `sys_role_menus` VALUES (2, 111); +INSERT INTO `sys_role_menus` VALUES (2, 112); +INSERT INTO `sys_role_menus` VALUES (9, 1); +INSERT INTO `sys_role_menus` VALUES (9, 2); +INSERT INTO `sys_role_menus` VALUES (9, 3); +INSERT INTO `sys_role_menus` VALUES (9, 4); +INSERT INTO `sys_role_menus` VALUES (9, 5); +INSERT INTO `sys_role_menus` VALUES (9, 6); +INSERT INTO `sys_role_menus` VALUES (9, 7); +INSERT INTO `sys_role_menus` VALUES (9, 8); +INSERT INTO `sys_role_menus` VALUES (9, 9); +INSERT INTO `sys_role_menus` VALUES (9, 10); +INSERT INTO `sys_role_menus` VALUES (9, 11); +INSERT INTO `sys_role_menus` VALUES (9, 20); +INSERT INTO `sys_role_menus` VALUES (9, 21); +INSERT INTO `sys_role_menus` VALUES (9, 22); +INSERT INTO `sys_role_menus` VALUES (9, 23); +INSERT INTO `sys_role_menus` VALUES (9, 24); +INSERT INTO `sys_role_menus` VALUES (9, 25); +INSERT INTO `sys_role_menus` VALUES (9, 26); +INSERT INTO `sys_role_menus` VALUES (9, 27); +INSERT INTO `sys_role_menus` VALUES (9, 28); +INSERT INTO `sys_role_menus` VALUES (9, 29); +INSERT INTO `sys_role_menus` VALUES (9, 30); +INSERT INTO `sys_role_menus` VALUES (9, 31); +INSERT INTO `sys_role_menus` VALUES (9, 32); +INSERT INTO `sys_role_menus` VALUES (9, 34); +INSERT INTO `sys_role_menus` VALUES (9, 35); +INSERT INTO `sys_role_menus` VALUES (9, 36); +INSERT INTO `sys_role_menus` VALUES (9, 37); +INSERT INTO `sys_role_menus` VALUES (9, 38); +INSERT INTO `sys_role_menus` VALUES (9, 39); +INSERT INTO `sys_role_menus` VALUES (9, 40); +INSERT INTO `sys_role_menus` VALUES (9, 41); +INSERT INTO `sys_role_menus` VALUES (9, 42); +INSERT INTO `sys_role_menus` VALUES (9, 54); +INSERT INTO `sys_role_menus` VALUES (9, 56); +INSERT INTO `sys_role_menus` VALUES (9, 57); +INSERT INTO `sys_role_menus` VALUES (9, 58); +INSERT INTO `sys_role_menus` VALUES (9, 59); +INSERT INTO `sys_role_menus` VALUES (9, 60); +INSERT INTO `sys_role_menus` VALUES (9, 61); +INSERT INTO `sys_role_menus` VALUES (9, 62); +INSERT INTO `sys_role_menus` VALUES (9, 63); +INSERT INTO `sys_role_menus` VALUES (9, 64); +INSERT INTO `sys_role_menus` VALUES (9, 65); +INSERT INTO `sys_role_menus` VALUES (9, 68); +INSERT INTO `sys_role_menus` VALUES (9, 69); +INSERT INTO `sys_role_menus` VALUES (9, 70); +INSERT INTO `sys_role_menus` VALUES (9, 86); +INSERT INTO `sys_role_menus` VALUES (9, 87); +INSERT INTO `sys_role_menus` VALUES (9, 88); +INSERT INTO `sys_role_menus` VALUES (9, 89); +INSERT INTO `sys_role_menus` VALUES (9, 92); + +-- ---------------------------- +-- Table structure for sys_task +-- ---------------------------- +DROP TABLE IF EXISTS `sys_task`; +CREATE TABLE `sys_task` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `service` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `type` tinyint NOT NULL DEFAULT 0, + `status` tinyint NOT NULL DEFAULT 1, + `start_time` datetime NULL DEFAULT NULL, + `end_time` datetime NULL DEFAULT NULL, + `limit` int NULL DEFAULT 0, + `cron` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `every` int NULL DEFAULT NULL, + `data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `job_opts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_ef8e5ab5ef2fe0ddb1428439ef`(`name`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_task +-- ---------------------------- +INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-03-28 11:01:34.000000'); +INSERT INTO `sys_task` VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-03-28 11:01:34.000000'); +INSERT INTO `sys_task` VALUES (4, '访问百度首页', 'HttpRequestJob.handle', 0, 0, NULL, NULL, 1, '* * * * * ?', NULL, '{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}', NULL, '访问百度首页', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); +INSERT INTO `sys_task` VALUES (5, '发送邮箱', 'EmailJob.send', 0, 0, NULL, NULL, -1, '0 0 0 1 * ?', NULL, '{\"subject\":\"这是标题\",\"to\":\"18661983080@163.com\",\"content\":\"这是正文\"}', NULL, '每月发送邮箱', '2023-11-10 00:31:44.197779', '2024-03-07 11:14:53.000000'); + +-- ---------------------------- +-- Table structure for sys_task_log +-- ---------------------------- +DROP TABLE IF EXISTS `sys_task_log`; +CREATE TABLE `sys_task_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `task_id` int NULL DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT 0, + `detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `consume_time` int NULL DEFAULT 0, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_f4d9c36052fdb188ff5c089454b`(`task_id`) USING BTREE, + CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_task_log +-- ---------------------------- +INSERT INTO `sys_task_log` VALUES (1, 3, 1, NULL, 0, '2024-02-05 03:06:22.037448', '2024-02-05 03:06:22.037448'); +INSERT INTO `sys_task_log` VALUES (2, 2, 1, NULL, 0, '2024-02-10 09:42:21.738712', '2024-02-10 09:42:21.738712'); + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `psalt` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint NULL DEFAULT 1, + `qq` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `dept_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_9e7164b2f1ea1348bc0eb0a7da`(`username`) USING BTREE, + INDEX `FK_96bde34263e2ae3b46f011124ac`(`dept_id`) USING BTREE, + CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_user +-- ---------------------------- +INSERT INTO `sys_user` VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-06 17:18:04.000000', 'bqy', 2); +INSERT INTO `sys_user` VALUES (2, 'user', 'dbd89546dec743f82bb9073d6ac39361', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', 'luffy@qq.com', '10010', '王路飞', 'qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-01 11:31:11.000000', 'luffy', 8); +INSERT INTO `sys_user` VALUES (8, 'developer', 'f03fa2a99595127b9a39587421d471f6', '/upload/报名照片-202402281149824.jpg', 'nami@qq.com', '10000', '小贼猫', 'NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-06 17:17:21.000000', '娜美', 2); + +-- ---------------------------- +-- Table structure for sys_user_roles +-- ---------------------------- +DROP TABLE IF EXISTS `sys_user_roles`; +CREATE TABLE `sys_user_roles` ( + `user_id` int NOT NULL, + `role_id` int NOT NULL, + PRIMARY KEY (`user_id`, `role_id`) USING BTREE, + INDEX `IDX_96311d970191a044ec048011f4`(`user_id`) USING BTREE, + INDEX `IDX_6d61c5b3f76a3419d93a421669`(`role_id`) USING BTREE, + CONSTRAINT `FK_6d61c5b3f76a3419d93a4216695` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_96311d970191a044ec048011f44` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sys_user_roles +-- ---------------------------- +INSERT INTO `sys_user_roles` VALUES (1, 1); +INSERT INTO `sys_user_roles` VALUES (2, 2); +INSERT INTO `sys_user_roles` VALUES (8, 2); + +-- ---------------------------- +-- Table structure for todo +-- ---------------------------- +DROP TABLE IF EXISTS `todo`; +CREATE TABLE `todo` ( + `id` int NOT NULL AUTO_INCREMENT, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `user_id` int NULL DEFAULT NULL, + `status` tinyint NOT NULL DEFAULT 0, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_9cb7989853c4cb7fe427db4b260`(`user_id`) USING BTREE, + CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of todo +-- ---------------------------- +INSERT INTO `todo` VALUES (1, 'nest.js', NULL, 0, '2023-11-10 00:31:44.139730', '2023-11-10 00:31:44.147629'); + +-- ---------------------------- +-- Table structure for tool_storage +-- ---------------------------- +DROP TABLE IF EXISTS `tool_storage`; +CREATE TABLE `tool_storage` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件名', + `fileName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '真实文件名', + `ext_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `user_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 128 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of tool_storage +-- ---------------------------- +INSERT INTO `tool_storage` VALUES (121, '2024-03-01 16:46:58.441084', '2024-03-01 16:46:58.441084', '1709172270328-202403011646430.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403011646430.jpg', '图片', '62.79 KB', 1); +INSERT INTO `tool_storage` VALUES (122, '2024-03-01 17:21:35.454311', '2024-03-01 17:21:35.454311', '盘点表-202403011721448.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403011721448.xlsx', '文档', '10.83 KB', 1); +INSERT INTO `tool_storage` VALUES (123, '2024-03-04 15:51:50.664699', '2024-03-04 15:51:50.664699', '20240304-202403041551657.sql', '20240304.sql', 'sql', '/upload/20240304-202403041551657.sql', '其他', '62.86 KB', 1); +INSERT INTO `tool_storage` VALUES (124, '2024-03-05 10:39:45.040659', '2024-03-05 10:39:45.040659', '盘点表-202403051039028.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403051039028.xlsx', '文档', '10.83 KB', 1); +INSERT INTO `tool_storage` VALUES (126, '2024-03-06 13:04:17.636919', '2024-03-06 13:04:17.636919', '盘点表-202403061304624.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403061304624.xlsx', '文档', '10.83 KB', 1); +INSERT INTO `tool_storage` VALUES (127, '2024-03-07 09:35:51.916017', '2024-03-07 09:35:51.916017', '盘点表-202403070935910.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403070935910.xlsx', '文档', '10.83 KB', 1); +INSERT INTO `tool_storage` VALUES (128, '2024-03-07 09:36:01.570104', '2024-03-07 09:36:01.570104', '1709172270328-202403070936565.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403070936565.jpg', '图片', '62.86 KB', 1); + +-- ---------------------------- +-- Table structure for user_access_tokens +-- ---------------------------- +DROP TABLE IF EXISTS `user_access_tokens`; +CREATE TABLE `user_access_tokens` ( + `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `expired_at` datetime NOT NULL COMMENT '令牌过期时间', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', + `user_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_e9d9d0c303432e4e5e48c1c3e90`(`user_id`) USING BTREE, + CONSTRAINT `FK_e9d9d0c303432e4e5e48c1c3e90` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of user_access_tokens +-- ---------------------------- +INSERT INTO `user_access_tokens` VALUES ('07276357-6286-478c-8058-d249f4ca4dde', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI4MzIwNH0.CiG2E-GnJlDR_6YJMDkxTJOa-dS29HV5rWNEszn-jeA', '2024-03-02 16:53:25', '2024-03-01 16:53:24.786130', 1); +INSERT INTO `user_access_tokens` VALUES ('09cf7b0a-62e0-45ee-96b0-e31de32361e0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1MDkxNTd9.0gtKlcxrxQ-TarEai2lsBnfMc852ZDYHeSjjhpo5Fn8', '2024-02-11 04:05:58', '2024-02-10 04:05:57.696509', 1); +INSERT INTO `user_access_tokens` VALUES ('0b95a340-d9eb-4c3a-bd70-2432d3108984', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyMzM4Mn0.IA8TwkjfDwmv9470mgVswU775HCRdGo7zCoPblRNgRU', '2024-03-05 11:36:23', '2024-03-04 11:36:22.819835', 1); +INSERT INTO `user_access_tokens` VALUES ('17593c96-3779-4f03-8dbc-cb21ac9f7981', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwiaWF0IjoxNzA5MjYxNTUzfQ.hDAHgYrDfsckG6bSyfGa_QLRmo1tvbJEkYgQUfwqZbs', '2024-03-02 10:52:33', '2024-03-01 10:52:33.059989', 1); +INSERT INTO `user_access_tokens` VALUES ('19e593f4-4a03-46e0-9b4e-a009af15790a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTcxNjY3NX0.a0xNnKWIueWIxu5SXAawpLq6Bl4cTD5O6bqLknDU05U', '2024-03-07 17:17:55', '2024-03-06 17:17:55.105520', 1); +INSERT INTO `user_access_tokens` VALUES ('1bb9d14e-823c-4f9f-8562-cbc2fd2be318', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTI1Mn0.-OgIPgXJtFC5ioAu_xpwCY6LRcAyTykXR_1pVrrSCN4', '2024-03-05 13:14:12', '2024-03-04 13:14:12.192684', 1); +INSERT INTO `user_access_tokens` VALUES ('31399cec-e506-4194-a9cc-47f7a1a953a9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTIxNzh9.Da3j0Ht_zAsPldEYJ18RFYZqTYNhgPxSefCEuXd-4T8', '2024-02-29 11:49:38', '2024-02-28 11:49:38.289991', 1); +INSERT INTO `user_access_tokens` VALUES ('31875da0-8b93-4ca2-bdb8-ab40c1c059bd', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTg0OX0.g8Rr9eTlEZaghqdd_W5lSyxVV-ZMMnMMkyF2iNrUXpY', '2024-03-02 15:57:29', '2024-03-01 15:57:29.074445', 1); +INSERT INTO `user_access_tokens` VALUES ('35fd4bb6-2608-4203-ac2c-9550034993ea', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3ODEwOTh9.WmBVjKJkJs1lve9PrYfF8pYWF-qeg8KQu8IeSDSF3Cw', '2024-03-08 11:11:38', '2024-03-07 11:11:38.199744', 1); +INSERT INTO `user_access_tokens` VALUES ('367aaa79-1130-4511-87c4-25aacdf27d3d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgzOH0.5_K7t6bAuqIO9A8NvgrWACEJUVKduu--1q-rgmAB4Vw', '2024-03-02 15:57:18', '2024-03-01 15:57:18.314066', 1); +INSERT INTO `user_access_tokens` VALUES ('3f7dffae-db1f-47dc-9677-5c956c3de39e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczMTEzMDJ9.D5Qpht1RquKor8WtgfGAcCp8LwG7z3FZhIwbyQzhDmE', '2024-02-08 21:08:22', '2024-02-07 21:08:22.130066', 1); +INSERT INTO `user_access_tokens` VALUES ('40342c3e-194c-42eb-adee-189389839195', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxNjF9.tRQOxhB-01Pcut5MXm4L5D1OrbMJfS4LfUys0XB4kWs', '2024-02-09 14:02:41', '2024-02-08 14:02:41.081164', 1); +INSERT INTO `user_access_tokens` VALUES ('4541a9e8-a508-4b92-8ae3-c3744ddcad92', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUzODE3NH0.Gb_IUruHmd25Wk1ZFHUtB8G2w1bg7i-ivCa11fT8JTw', '2024-03-05 15:42:54', '2024-03-04 15:42:54.286838', 1); +INSERT INTO `user_access_tokens` VALUES ('53a08eaa-74fb-45cb-9e6c-514b67346e06', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTU0NDM0Mn0.rcVSgy1s7zNWtB_Q93zdZzL633UFbeH_RWEY_kelVHQ', '2024-03-05 17:25:42', '2024-03-04 17:25:42.096331', 1); +INSERT INTO `user_access_tokens` VALUES ('6ea87ca4-89e4-419b-9eb3-7bef4ada53ca', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTM0Mn0.kLsAfIgUDqNEogqBZWaDt7iUYb4gE4dznjTKoUzSKj0', '2024-03-05 13:15:42', '2024-03-04 13:15:42.347589', 1); +INSERT INTO `user_access_tokens` VALUES ('6f3990bd-d093-4cc4-9f9e-cba45260ec42', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkxOTUxMTR9.XvbjbvGNz7pYm-Yol8X3uTDwYkn35i-Fu_LsmMU4PdM', '2024-03-01 16:25:15', '2024-02-29 16:25:14.725434', 1); +INSERT INTO `user_access_tokens` VALUES ('78519125-77c1-4768-9644-d8fa639f3ff0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3NzQwNzN9._ZJd1B4EybHlO3W9p98uZ0NJDm6fJur9ifLwEIwywI0', '2024-03-08 09:14:34', '2024-03-07 09:14:33.765186', 1); +INSERT INTO `user_access_tokens` VALUES ('7ebc6a49-421c-4437-a7a8-bc8aaeb96b26', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkxOTUxNjl9.oiSa_amY2bX-MaAxL8vs_EmSEyhyBLVJLn9uPf_HgY4', '2024-03-01 16:26:09', '2024-02-29 16:26:09.075302', 1); +INSERT INTO `user_access_tokens` VALUES ('8e26aa27-fa67-4b67-8705-019be3e8f1f8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTY4OTh9.LemwyefdnGCm2ZM2KE3RN2-d6n-Xj2TAujs7iBWaYs8', '2024-02-29 13:08:18', '2024-02-28 13:08:18.482193', 1); +INSERT INTO `user_access_tokens` VALUES ('8f7fcaed-c4ed-4cf0-840f-37bcd3a39a59', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTg1M30.woUKWiIQ96woTIFDSdPWkQHwypfTc9T6EqFDbwN56mE', '2024-03-02 15:57:34', '2024-03-01 15:57:33.866391', 1); +INSERT INTO `user_access_tokens` VALUES ('9d1ba8e9-dffc-4b15-b21f-4a90f196e39c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1Mjc5MDV9.7LeiS3LBBdiAc7YrULWpmnI1oNSvR79K-qjEOlBYOnI', '2024-02-11 09:18:26', '2024-02-10 09:18:25.656695', 1); +INSERT INTO `user_access_tokens` VALUES ('ab62dfc5-46d8-4dad-9055-bb2803d1451c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTY3NjF9.9C_cPmmiW3Sh5OrPNSnw3h1IoqqLa3GlEYvgNFvv5Rg', '2024-02-29 13:06:01', '2024-02-28 13:06:01.416655', 1); +INSERT INTO `user_access_tokens` VALUES ('af43fb31-3759-43d5-b13e-e558bccdca58', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTY4NTkwNH0.eKKbI4Wi7WKSqE-gDt5isFihQze-LOysFntMzyaP5DY', '2024-03-07 08:45:05', '2024-03-06 08:45:04.541645', 1); +INSERT INTO `user_access_tokens` VALUES ('b6053fd5-7c7c-453e-a8ea-6de17b30304a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTcwNTF9.LyR5igCxJhcSnB-pXCc7wJNJ2H1ERdu8LO6d94cMz2c', '2024-02-29 13:10:51', '2024-02-28 13:10:51.331328', 1); +INSERT INTO `user_access_tokens` VALUES ('c24acee3-7bbd-4e7a-b8f6-a80be1db0b33', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgyNH0.6YmdwppJX1DV9-jdFiHLWRH9X_WbeGYbZtaRIizLRgM', '2024-03-02 15:57:05', '2024-03-01 15:57:04.739116', 1); +INSERT INTO `user_access_tokens` VALUES ('cbfcc75c-d357-47fd-afbf-db0629885eb6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTYyMDQ0OH0.LQXYkRJVyDN7YCVRSsS7osGMzC31ik1XD36-SWDbghI', '2024-03-06 14:34:08', '2024-03-05 14:34:08.374481', 1); +INSERT INTO `user_access_tokens` VALUES ('cdc3e837-0953-42e7-8296-d27cfbc41353', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTEzNn0.SUycnmUW-ZDjLoBP4DMB_si11dUVHfXmCsG0tLMVBg0', '2024-03-02 15:45:37', '2024-03-01 15:45:36.880024', 1); +INSERT INTO `user_access_tokens` VALUES ('d32e25cc-b00c-43cf-94b0-0f61c3314f4c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI2MTU4OX0.-IM-L0Dt7Jc5xaEU5Q93z5vhAQTpbD9ngS_uhzZ0haI', '2024-03-02 10:53:10', '2024-03-01 10:53:09.606838', 1); +INSERT INTO `user_access_tokens` VALUES ('d9044d67-9a95-4bd5-8103-f6ddc9701f83', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTM2NH0.gEQmOm9E7Adpti0pux6KjtopqZpNy62fZzgWf0Ps1wM', '2024-03-05 13:16:05', '2024-03-04 13:16:04.665874', 1); +INSERT INTO `user_access_tokens` VALUES ('e042f0c3-9a35-40cf-83a3-918f617f2ac7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3ODMxNzR9.6jKaSK15fdN8XkToaimiWbqJ-vhiaQr2QkpAc5XvFuA', '2024-03-08 11:46:15', '2024-03-07 11:46:14.586995', 1); +INSERT INTO `user_access_tokens` VALUES ('e754d168-106a-43f5-bbf8-80f1b187cb1e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTk2MzN9.IQ_1NQHKvQ3Cso7cXYQ32xK3qhaOUUF6jtoQ87cN6b0', '2024-02-29 13:53:53', '2024-02-28 13:53:53.168581', 1); +INSERT INTO `user_access_tokens` VALUES ('ebc5fbbb-b29e-4a17-acd6-002ca7936099', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgzMX0.d29Hn1Ub-YJBv9Xw9Olo7kBNB9ugxcs1P1hrtJEQpJo', '2024-03-02 15:57:11', '2024-03-01 15:57:11.247365', 1); +INSERT INTO `user_access_tokens` VALUES ('edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxMjd9.VRuJHGca2IPrdfTyW09wfhht4x8JX207pKG-0aZyF60', '2024-02-09 14:02:07', '2024-02-08 14:02:07.390658', 1); +INSERT INTO `user_access_tokens` VALUES ('eff3fd41-7307-4027-9f8c-2e3b0fd35051', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTk1OTd9.aLKmV2GGF6pkkolLnJzuq0PXAllSbGIZzJrUR4wlNag', '2024-02-29 13:53:17', '2024-02-28 13:53:17.473705', 1); + +-- ---------------------------- +-- Table structure for user_refresh_tokens +-- ---------------------------- +DROP TABLE IF EXISTS `user_refresh_tokens`; +CREATE TABLE `user_refresh_tokens` ( + `id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `expired_at` datetime NOT NULL COMMENT '令牌过期时间', + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', + `accessTokenId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `REL_1dfd080c2abf42198691b60ae3`(`accessTokenId`) USING BTREE, + CONSTRAINT `FK_1dfd080c2abf42198691b60ae39` FOREIGN KEY (`accessTokenId`) REFERENCES `user_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of user_refresh_tokens +-- ---------------------------- +INSERT INTO `user_refresh_tokens` VALUES ('045ad38e-ab82-4ea1-8b61-c2da89060999', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRWMxcEQwZ1VrYnFtZEhyWVdKTDVPIiwiaWF0IjoxNzA5MDkyMTc4fQ.K2nBZ_B8jneihHfs51LIp0fvkkgV7lFawe2cu4sOjN4', '2024-03-29 11:49:38', '2024-02-28 11:49:38.302821', '31399cec-e506-4194-a9cc-47f7a1a953a9'); +INSERT INTO `user_refresh_tokens` VALUES ('046cd195-a105-493e-9d6c-0d5d87081834', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRlU3a3poeGxNUkhWVUdleDR6VmJTIiwiaWF0IjoxNzA5NjIwNDQ4fQ.UKal8R3pPSiIGPAZFUf78-ne81csm2TwQbRtWIB_B1k', '2024-04-04 14:34:08', '2024-03-05 14:34:08.406121', 'cbfcc75c-d357-47fd-afbf-db0629885eb6'); +INSERT INTO `user_refresh_tokens` VALUES ('08f57b8a-6a86-434a-89cf-280255a0d1b6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaWxvdmdUWmJueU5RTGR4bmxpc3Y0IiwiaWF0IjoxNzA5MDk2ODk4fQ.v29AorJL9FJVAS89GxtFKeJYc92nPaIpE8O-1GY_nhw', '2024-03-29 13:08:18', '2024-02-28 13:08:18.499682', '8e26aa27-fa67-4b67-8705-019be3e8f1f8'); +INSERT INTO `user_refresh_tokens` VALUES ('157299e9-45e0-4fe3-9caf-ff944644605d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNVVsRHhFSE1EMnpMVGd4bUNmRnVSIiwiaWF0IjoxNzA5Mjc5MTM2fQ.mbmS1urJdB6YxBdrj1n09-IgARY_LC5lRuTAntVUZlg', '2024-03-31 15:45:37', '2024-03-01 15:45:36.901193', 'cdc3e837-0953-42e7-8296-d27cfbc41353'); +INSERT INTO `user_refresh_tokens` VALUES ('1f613862-2567-4c10-8026-c4743c62d01a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1ZZMzl3dnpVLUxmcTFxSjBYVGktIiwiaWF0IjoxNzA5MDk5NTk3fQ.Qjhsglo7PbTsT1_KE3vTMNweHx08dwcz_q1e1f2W9R0', '2024-03-29 13:53:17', '2024-02-28 13:53:17.487831', 'eff3fd41-7307-4027-9f8c-2e3b0fd35051'); +INSERT INTO `user_refresh_tokens` VALUES ('202d0969-6721-4f6f-bf34-f0d1931d4d01', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRpOXVYei1TdldjdWRnclFXVmFXIiwiaWF0IjoxNzA3MzcyMTYxfQ.NOQufR5EAPE2uZoyenmAj9H7S7qo4d6W1aW2ojDxZQc', '2024-03-09 14:02:41', '2024-02-08 14:02:41.091492', '40342c3e-194c-42eb-adee-189389839195'); +INSERT INTO `user_refresh_tokens` VALUES ('21e28f7b-c156-41bf-902e-583d48109ebf', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoieVU2NU5NSGVuak5lUmozY3BWVDhwIiwiaWF0IjoxNzA5MTk1MTY5fQ.4Vibz4kCF5UulsR9OmAhmQRhrA9idKCyKXCITVLOAkk', '2024-03-30 16:26:09', '2024-02-29 16:26:09.086894', '7ebc6a49-421c-4437-a7a8-bc8aaeb96b26'); +INSERT INTO `user_refresh_tokens` VALUES ('25e9acfd-eb72-4ab8-9e5e-04f0a683e60f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibndPOEFIb1hab2d3Y0tpWG41NXlJIiwiaWF0IjoxNzA5Mjc5ODI0fQ._r8VN3caZ5XzEdMpH1FM9e1yVqHuACpknfOeSyixXns', '2024-03-31 15:57:05', '2024-03-01 15:57:04.755457', 'c24acee3-7bbd-4e7a-b8f6-a80be1db0b33'); +INSERT INTO `user_refresh_tokens` VALUES ('42cb1246-4a1b-4443-a9b0-926761f13527', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaVBwVW1FQ0xUTXgzUC1KVUdvaXJaIiwiaWF0IjoxNzA5NTI5MzQyfQ.e6TbYee7T2uEt0GJznuLmlEGYvmT8LWQVk0phYMS6Lw', '2024-04-03 13:15:42', '2024-03-04 13:15:42.358715', '6ea87ca4-89e4-419b-9eb3-7bef4ada53ca'); +INSERT INTO `user_refresh_tokens` VALUES ('46195349-cbf4-492f-b193-7aec06cb556b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN1NGYy1PTGNWc1MxVG5BWEdqLW1NIiwiaWF0IjoxNzA5Mjc5ODQ5fQ.n1eSzEGrz_M_hrYNdxx7UtGWHzt7soyJqDh1UK670fE', '2024-03-31 15:57:29', '2024-03-01 15:57:29.086263', '31875da0-8b93-4ca2-bdb8-ab40c1c059bd'); +INSERT INTO `user_refresh_tokens` VALUES ('461f9b7c-e500-4762-a6d9-f9ea47163064', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicXJvTWNYMnhNRW5uRmZGWkQtaUx0IiwiaWF0IjoxNzA3MzExMzAyfQ.dFIWCePZnn2z2Qv1D5PKBKXUwVDI0Gp091MIOi9jiIo', '2024-03-08 21:08:22', '2024-02-07 21:08:22.145464', '3f7dffae-db1f-47dc-9677-5c956c3de39e'); +INSERT INTO `user_refresh_tokens` VALUES ('4a4a3a33-1308-4fc1-9ffa-f828d875e004', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLWREemYwWUdXMEhVQXI3RHNqbG9qIiwiaWF0IjoxNzA5NzE2Njc1fQ.ro8DesLH5duC9IBySpMLwtuzC5pMPr1NY6U21Ff4ZHg', '2024-04-05 17:17:55', '2024-03-06 17:17:55.118361', '19e593f4-4a03-46e0-9b4e-a009af15790a'); +INSERT INTO `user_refresh_tokens` VALUES ('4b82a1b7-b571-42de-8809-487f7abc2e65', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiR1BkbUVfTmhjZG5xWnpWX2lGN0JuIiwiaWF0IjoxNzA5NTQ0MzQyfQ.hH2bzYj_8Qim997-C7HNUSJqkk43Z1YAiexpRhxR8cQ', '2024-04-03 17:25:42', '2024-03-04 17:25:42.114236', '53a08eaa-74fb-45cb-9e6c-514b67346e06'); +INSERT INTO `user_refresh_tokens` VALUES ('50b2f3ad-232a-41f6-bf97-2c52127a3e28', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVnlZNjYwYWNPRHlTd2NZWGhscWdFIiwiaWF0IjoxNzA5Mjc5ODMxfQ.uG0qmKgWzYNvBJFfCvvOmndCgrwpkZ3NVL4aynD6Uxc', '2024-03-31 15:57:11', '2024-03-01 15:57:11.259022', 'ebc5fbbb-b29e-4a17-acd6-002ca7936099'); +INSERT INTO `user_refresh_tokens` VALUES ('53c79bc8-f092-4d07-b480-f8637a3887fa', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOEoyQ0w5RXYtSkhmWW0xSDNMMUxEIiwiaWF0IjoxNzA5MjYxNTg5fQ.3Ez1mfJkLZ9LFo2nTshTtOjs-VN56k1LWNoijWzSYaA', '2024-03-31 10:53:10', '2024-03-01 10:53:09.615615', 'd32e25cc-b00c-43cf-94b0-0f61c3314f4c'); +INSERT INTO `user_refresh_tokens` VALUES ('718ccd7b-bb4f-4400-8f40-ccd6141ef4c5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiamNTcWlzUkFIaW5SOTlUX2lvVzV0IiwiaWF0IjoxNzA5MDk2NzYxfQ.X-iqKmDeV6juqCQC8XjU7o-8CXNxEyLyn5uck4mkJoY', '2024-03-29 13:06:01', '2024-02-28 13:06:01.430737', 'ab62dfc5-46d8-4dad-9055-bb2803d1451c'); +INSERT INTO `user_refresh_tokens` VALUES ('9557d0a9-5004-4392-95b6-637b3343dbc3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTnNqRGZsMUpKY3RWTzFYaEtEMV9FIiwiaWF0IjoxNzA5NzgzMTc0fQ.osKC1PeUm6LAMj6ZyIuNhUxFcKN1t1ZvrjkE2Rn1pxw', '2024-04-06 11:46:15', '2024-03-07 11:46:14.600560', 'e042f0c3-9a35-40cf-83a3-918f617f2ac7'); +INSERT INTO `user_refresh_tokens` VALUES ('9d54c429-5dab-4e72-b70e-d1e6918d364f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ1UxYnkxTkVNT05ZQVJsWjBkYzIwIiwiaWF0IjoxNzA5Nzc0MDczfQ.rMG5F7Cec_gcP7C7gr_WL6XiquvuWmvV8Wh2SQOEwuY', '2024-04-06 09:14:34', '2024-03-07 09:14:33.788438', '78519125-77c1-4768-9644-d8fa639f3ff0'); +INSERT INTO `user_refresh_tokens` VALUES ('a2e12448-35b0-487e-a18e-a60461a54370', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZHRvUU9fUjhCZGFOQ1EzMVRhYmxtIiwiaWF0IjoxNzA5NzgxMDk4fQ.3ORghjx3Yzh9_f62tYHLeY_x602pBF-wMrKEVX7Dd2c', '2024-04-06 11:11:38', '2024-03-07 11:11:38.213630', '35fd4bb6-2608-4203-ac2c-9550034993ea'); +INSERT INTO `user_refresh_tokens` VALUES ('a30969f3-b766-4190-b3c6-544f81a05a5a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRlRUN0VqRWlfc1RkSXd6cjhGZmtZIiwiaWF0IjoxNzA5NTI5MzY0fQ.5qz5M4MC4nU614Nn8hyW0l1TuGcFNuKRHC_ZYzEZwO8', '2024-04-03 13:16:05', '2024-03-04 13:16:04.680424', 'd9044d67-9a95-4bd5-8103-f6ddc9701f83'); +INSERT INTO `user_refresh_tokens` VALUES ('a500bb13-4b9a-4cf2-b691-b046586d9ee7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoic3R1TDFpUmljTnBlSTlhR0FEUFQwIiwiaWF0IjoxNzA5Mjc5ODUzfQ.oiNGDkAWVr4jko01TY7rqWoOYvCcwZbWpfLCzgtqMHc', '2024-03-31 15:57:34', '2024-03-01 15:57:33.879108', '8f7fcaed-c4ed-4cf0-840f-37bcd3a39a59'); +INSERT INTO `user_refresh_tokens` VALUES ('a8451340-008c-4fa7-bea0-3bdeee5d5cfa', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVXdDQ2dJeUU1eGpUT1lLRDVtcnU3IiwiaWF0IjoxNzA5MjYxNTUzfQ.dA6eFaURUP0wYU7Pr0Xjv2oFkkfrwQb-cB_gziAK4G4', '2024-03-31 10:52:33', '2024-03-01 10:52:33.072305', '17593c96-3779-4f03-8dbc-cb21ac9f7981'); +INSERT INTO `user_refresh_tokens` VALUES ('b375e623-2d82-48f0-9b7a-9058e3850cc6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicDhUMzdGNFFaUDJHLU5yNGVha21wIiwiaWF0IjoxNzA3MzcyMTI3fQ.fn3It6RKIxXlKmqixg0BMmY_YsQmAxtetueqW-0y1IM', '2024-03-09 14:02:07', '2024-02-08 14:02:07.410008', 'edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb'); +INSERT INTO `user_refresh_tokens` VALUES ('c92355b8-da11-4e8d-b5a6-0826bb44aac0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUlVnM3RtQTBROWZjZ0JZWnJxMXNxIiwiaWF0IjoxNzA5Njg1OTA0fQ.CraA5kdSrHeZhyRchvmZfnfdiPXfjd_4A6FZWPh-r7Y', '2024-04-05 08:45:05', '2024-03-06 08:45:04.557816', 'af43fb31-3759-43d5-b13e-e558bccdca58'); +INSERT INTO `user_refresh_tokens` VALUES ('e620ccc1-9e40-4387-9f21-f0722e535a63', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNE5WdmFIc2hWaU05ZFh0QnVBaHNsIiwiaWF0IjoxNzA3NTI3OTA1fQ.zzyGX0mOJe6KWpTzIi7We9d9c0MRuDeGC86DMB0Vubs', '2024-03-11 09:18:26', '2024-02-10 09:18:25.664251', '9d1ba8e9-dffc-4b15-b21f-4a90f196e39c'); +INSERT INTO `user_refresh_tokens` VALUES ('ea877712-28d7-4ee1-97ad-27f4455d59a5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZUVsNUM2RXZFMjBacENpZnZZalI4IiwiaWF0IjoxNzA5MTk1MTE0fQ.SnF7us-m0ktL6JJIXl8NgNPrsVlHMSN_l5-Nr9L2FeA', '2024-03-30 16:25:15', '2024-02-29 16:25:14.741842', '6f3990bd-d093-4cc4-9f9e-cba45260ec42'); +INSERT INTO `user_refresh_tokens` VALUES ('eb8e0905-8f49-42db-b9f8-771f8d706ab0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiSEZWTG9xY25UeV84ZGpOWWxZTy02IiwiaWF0IjoxNzA5MDk3MDUxfQ.cC5X2an9Vn4q3NThsqKdA72locR6J10yOCPPqGT_hnc', '2024-03-29 13:10:51', '2024-02-28 13:10:51.345270', 'b6053fd5-7c7c-453e-a8ea-6de17b30304a'); +INSERT INTO `user_refresh_tokens` VALUES ('ece48e5b-4a1e-4683-acbc-4da02db0d511', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiai1DZjIyUllQX0taaWpPaFkyV01iIiwiaWF0IjoxNzA5NTM4MTc0fQ.oAUJSvbA2EQbdqnpia8nHiiH7VH6igEHOka9INSnTVE', '2024-04-03 15:42:54', '2024-03-04 15:42:54.302651', '4541a9e8-a508-4b92-8ae3-c3744ddcad92'); +INSERT INTO `user_refresh_tokens` VALUES ('ef3560f5-8f24-4a76-a5e8-c17de1f1fcfe', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiY2VkQ2xZdl9TcTJ2SUEybzZaZS1NIiwiaWF0IjoxNzA5MDk5NjMzfQ.mxekqz0IB_fzwvoBcZyN7BPKezISmd4wPHt7fR7-b3g', '2024-03-29 13:53:53', '2024-02-28 13:53:53.180619', 'e754d168-106a-43f5-bbf8-80f1b187cb1e'); +INSERT INTO `user_refresh_tokens` VALUES ('f48bf795-9163-4899-8268-2345a0d2ba4e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVjc1SUFMMkhVLUFuTkZINzVFbmhfIiwiaWF0IjoxNzA5Mjc5ODM4fQ.epc14TsoOkTpJOoo1zW9N3qKxiiUTqQ42Sbb455ppAE', '2024-03-31 15:57:18', '2024-03-01 15:57:18.327929', '367aaa79-1130-4511-87c4-25aacdf27d3d'); +INSERT INTO `user_refresh_tokens` VALUES ('f9a003e8-91b7-41ee-979e-e39cca3534ec', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGJQdl9SVjFtUl80N0o0TGF0QlV5IiwiaWF0IjoxNzA3NTA5MTU3fQ.oEVdWSigTpAQY7F8MlwBnedldH0sJT1YF1Mt0ZUbIw4', '2024-03-11 04:05:58', '2024-02-10 04:05:57.706763', '09cf7b0a-62e0-45ee-96b0-e31de32361e0'); +INSERT INTO `user_refresh_tokens` VALUES ('faef8e19-55d3-4005-8022-ca735c5c9390', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1ZZeXlpQWNJREVTdktFNUhjeER1IiwiaWF0IjoxNzA5NTI5MjUyfQ.IcNLUGhcsxTWIYWxT-0TeII4NbM--Bgq7BJ0VbRfUMo', '2024-04-03 13:14:12', '2024-03-04 13:14:12.215057', '1bb9d14e-823c-4f9f-8562-cbc2fd2be318'); +INSERT INTO `user_refresh_tokens` VALUES ('fb10a7dd-5f84-4151-ab25-b24ec9fca46e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMUFzVUdRc3hjZEdnaVhjYkVUcm1WIiwiaWF0IjoxNzA5NTIzMzgyfQ.t2zzBijhlt4cV-p8KElBjJk4TURABI-tKRk-mIkmb4w', '2024-04-03 11:36:23', '2024-03-04 11:36:22.837030', '0b95a340-d9eb-4c3a-bd70-2432d3108984'); +INSERT INTO `user_refresh_tokens` VALUES ('fda03f46-8638-43c1-8417-12ecb1ec7cf7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNUx1NkJuc1pQeVdtVm1DbHg4MGxwIiwiaWF0IjoxNzA5MjgzMjA0fQ.yAmdtYl3KqMa_R32R2Ht2xxMtQIMpncQ-hFzhzTJTkE', '2024-03-31 16:53:25', '2024-03-01 16:53:24.799017', '07276357-6286-478c-8058-d249f4ca4dde'); + +-- ---------------------------- +-- Table structure for vehicle_usage +-- ---------------------------- +DROP TABLE IF EXISTS `vehicle_usage`; +CREATE TABLE `vehicle_usage` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `year` int NOT NULL COMMENT '年度', + `vehicle_license` int NOT NULL COMMENT '外出使用的车辆名称(字典)', + `applicant` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '申请人', + `driver` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '出行司机', + `current_mileage` int NULL DEFAULT NULL COMMENT '当前车辆里程数(KM)', + `expected_start_date` date NULL DEFAULT NULL COMMENT '预计出行开始时间', + `expected_end_date` date NULL DEFAULT NULL COMMENT '预计出行结束时间', + `purpose` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '使用事由', + `actual_return_time` date NULL DEFAULT NULL COMMENT '实际回司时间', + `return_mileage` int NULL DEFAULT NULL COMMENT '回城车辆里程数(KM)', + `reviewer` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '审核人', + `status` tinyint NOT NULL DEFAULT 0 COMMENT '审核状态(字典)', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of vehicle_usage +-- ---------------------------- + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/migrations/1707996695540-initData.ts b/src/migrations/1707996695540-initData.ts index 88a7bf6..8434099 100644 --- a/src/migrations/1707996695540-initData.ts +++ b/src/migrations/1707996695540-initData.ts @@ -3,7 +3,7 @@ import path from 'node:path'; import { MigrationInterface, QueryRunner } from 'typeorm'; -const sql = fs.readFileSync(path.join(__dirname, '../../deploy/sql/hxoa.sql'), 'utf8'); +const sql = fs.readFileSync(path.join(__dirname, '../../init_data/sql/hxoa.sql'), 'utf8'); export class InitData1707996695540 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { From 1714782f368eeb691a70343da555795eccb76848 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 28 Mar 2024 13:08:19 +0800 Subject: [PATCH 39/64] fix: mysql init --- init_data/sql/hxoa.sql | 722 +++++++++++++++++++++++++++++------------ 1 file changed, 508 insertions(+), 214 deletions(-) diff --git a/init_data/sql/hxoa.sql b/init_data/sql/hxoa.sql index 2e34ab7..0848747 100644 --- a/init_data/sql/hxoa.sql +++ b/init_data/sql/hxoa.sql @@ -1,17 +1,17 @@ /* Navicat Premium Data Transfer - Source Server : hxoa_local + Source Server : 公司电脑 Source Server Type : MySQL - Source Server Version : 80300 - Source Host : localhost:13307 + Source Server Version : 80027 + Source Host : 192.168.60.39:13307 Source Schema : hxoa Target Server Type : MySQL - Target Server Version : 80300 + Target Server Version : 80027 File Encoding : 65001 - Date: 28/03/2024 12:40:37 + Date: 28/03/2024 13:07:35 */ SET NAMES utf8mb4; @@ -29,12 +29,12 @@ CREATE TABLE `company` ( `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_a76c5cd486f7779bd9c319afd2`(`name`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of company -- ---------------------------- -INSERT INTO `company` VALUES (1, '2024-03-04 15:44:43.005593', '2024-03-04 16:09:44.000000', '深圳市立创电子商务有限公司', 0); +INSERT INTO `company` VALUES (1, '2024-03-04 15:44:43.005593', '2024-03-08 08:48:37.000000', '深圳市立创电子商务有限公司', 0); INSERT INTO `company` VALUES (4, '2024-03-04 16:05:34.701780', '2024-03-04 16:05:34.701780', '深圳市诚亨泰科技有限公司', 0); INSERT INTO `company` VALUES (5, '2024-03-04 16:05:38.867786', '2024-03-04 16:05:38.867786', '东莞市顶源电子有限公司', 0); INSERT INTO `company` VALUES (6, '2024-03-04 16:05:42.479027', '2024-03-04 16:05:42.479027', '深圳市福田区赛格电子市场金佳电子经营部', 0); @@ -45,6 +45,8 @@ INSERT INTO `company` VALUES (10, '2024-03-04 16:06:09.788572', '2024-03-04 16:0 INSERT INTO `company` VALUES (11, '2024-03-04 16:06:12.872983', '2024-03-04 16:06:12.872983', '深圳嘉立创科技集团股份有限公司', 0); INSERT INTO `company` VALUES (12, '2024-03-04 16:06:19.823410', '2024-03-04 16:06:19.823410', '北京特倍福电子技术有限公司', 0); INSERT INTO `company` VALUES (13, '2024-03-04 16:06:25.937749', '2024-03-04 16:06:25.937749', '上海脉芯网络科技有限公司', 0); +INSERT INTO `company` VALUES (14, '2024-03-22 11:01:20.588144', '2024-03-22 11:01:20.588144', '深圳市声能达科技有限公司', 0); +INSERT INTO `company` VALUES (15, '2024-03-26 10:29:45.595059', '2024-03-26 10:29:45.595059', '深圳市新得润电子有限公司', 0); -- ---------------------------- -- Table structure for company_storage @@ -63,6 +65,9 @@ CREATE TABLE `company_storage` ( -- ---------------------------- -- Records of company_storage -- ---------------------------- +INSERT INTO `company_storage` VALUES (1, 141); +INSERT INTO `company_storage` VALUES (1, 142); +INSERT INTO `company_storage` VALUES (1, 143); -- ---------------------------- -- Table structure for contract @@ -83,15 +88,18 @@ CREATE TABLE `contract` ( `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_a2f8461960ce0fcbd0d6551009`(`contract_number`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of contract -- ---------------------------- -INSERT INTO `contract` VALUES (1, '2024-02-29 11:38:20.959071', '2024-03-06 10:40:25.000000', '2022092301', '山东矿机华信智能科技有限公司', 11, '山东矿机华信', '青岛比特维尔', '2024-01-01', '2024-02-01', 1, 0); -INSERT INTO `contract` VALUES (2, '2024-02-29 16:11:54.286196', '2024-02-29 16:50:30.000000', 'www', 'weqw', 10, 'rqw', 'rwq', '2024-02-01', '2024-02-28', 2, 0); +INSERT INTO `contract` VALUES (1, '2024-02-29 11:38:20.959071', '2024-03-08 08:36:57.000000', '2022092301', '山东矿机华信智能科技有限公司', 11, '山东矿机华信', '青岛比特维尔', '2024-01-01', '2024-02-01', 1, 0); +INSERT INTO `contract` VALUES (2, '2024-02-29 16:11:54.286196', '2024-03-08 13:17:00.000000', 'www', 'weqw', 10, 'rqw', 'rwq', '2024-02-01', '2024-02-28', 2, 0); INSERT INTO `contract` VALUES (3, '2024-03-01 15:26:07.794697', '2024-03-01 15:30:49.000000', 'test1211', 'test', 13, '山东搞笑信息', '齐鲁医院', '2024-03-01', '2024-03-20', 0, 1); INSERT INTO `contract` VALUES (4, '2024-03-01 16:47:22.125670', '2024-03-01 16:47:22.125670', '33024242', '21412412', 11, '2141', '41241', '2024-03-01', '2024-03-01', 0, 0); +INSERT INTO `contract` VALUES (6, '2024-03-08 10:37:33.943916', '2024-03-08 10:37:59.000000', 'www12', 'weqw', 11, 'fw', 'fw', NULL, NULL, 1, 0); +INSERT INTO `contract` VALUES (7, '2024-03-11 15:28:38.834970', '2024-03-11 15:29:12.000000', 'wfwfwqf', 'fwqfwq', 11, 'wqfwq', 'fwqfw', NULL, NULL, 1, 0); +INSERT INTO `contract` VALUES (8, '2024-03-11 17:10:24.104620', '2024-03-11 17:11:04.000000', '24214', '214214', 10, '21', '4214', NULL, NULL, 1, 0); -- ---------------------------- -- Table structure for contract_storage @@ -110,6 +118,13 @@ CREATE TABLE `contract_storage` ( -- ---------------------------- -- Records of contract_storage -- ---------------------------- +INSERT INTO `contract_storage` VALUES (1, 133); +INSERT INTO `contract_storage` VALUES (1, 134); +INSERT INTO `contract_storage` VALUES (2, 168); +INSERT INTO `contract_storage` VALUES (2, 169); +INSERT INTO `contract_storage` VALUES (7, 170); +INSERT INTO `contract_storage` VALUES (7, 171); +INSERT INTO `contract_storage` VALUES (8, 172); -- ---------------------------- -- Table structure for materials_in_out @@ -120,7 +135,7 @@ CREATE TABLE `materials_in_out` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `product_id` int NOT NULL COMMENT '产品', - `inOrOut` tinyint NOT NULL COMMENT '入库或出库', + `in_or_out` tinyint NOT NULL COMMENT '入库或出库', `time` date NULL DEFAULT NULL COMMENT '时间', `quantity` int NOT NULL DEFAULT 0 COMMENT '数量', `unit_price` decimal(15, 10) NOT NULL DEFAULT 0.0000000000 COMMENT '单价', @@ -129,32 +144,21 @@ CREATE TABLE `materials_in_out` ( `issuance_number` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '领料单号', `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', - `inventory_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '原材料库存编号', + `inventory_inout_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '原材料出入库编号', `project_id` int NULL DEFAULT NULL COMMENT '项目', + `inventory_id` int NOT NULL COMMENT '库存', PRIMARY KEY (`id`) USING BTREE, INDEX `FK_770f1c4afd9631499ccc08bd58b`(`product_id`) USING BTREE, INDEX `FK_7a5bd19f8fd458f6336efedf765`(`project_id`) USING BTREE, + INDEX `FK_f5dc1f1e4db2f990ef89f0398fa`(`inventory_id`) USING BTREE, CONSTRAINT `FK_770f1c4afd9631499ccc08bd58b` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, - CONSTRAINT `FK_7a5bd19f8fd458f6336efedf765` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 28 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + CONSTRAINT `FK_7a5bd19f8fd458f6336efedf765` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_f5dc1f1e4db2f990ef89f0398fa` FOREIGN KEY (`inventory_id`) REFERENCES `materials_inventory` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 168 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of materials_in_out -- ---------------------------- -INSERT INTO `materials_in_out` VALUES (7, '2024-03-06 15:39:38.305753', '2024-03-07 11:10:10.000000', 19, 0, '2024-02-16', 100, 557.5200000000, 55752.2100000000, '蒋博', NULL, '沙湾项目', 0, 'KC1000', 3); -INSERT INTO `materials_in_out` VALUES (16, '2024-03-06 15:50:12.315922', '2024-03-07 11:10:07.000000', 19, 1, '2024-02-16', 100, 100.0000000000, 55752.2100000000, '蒋博', '3153019', '沙湾项目', 0, 'KC1000', 3); -INSERT INTO `materials_in_out` VALUES (17, '2024-03-06 16:06:57.397785', '2024-03-06 17:09:14.325720', 19, 1, '2024-03-06', 33, 33.0000000000, 33.0000000000, '3', '33', '33', 1, 'KC1000', NULL); -INSERT INTO `materials_in_out` VALUES (18, '2024-03-06 16:08:37.713482', '2024-03-07 11:10:03.000000', 20, 0, '2024-02-16', 100, 557.5200000000, 55752.2100000000, '蒋博', NULL, '沙湾项目', 0, 'KC1001', 3); -INSERT INTO `materials_in_out` VALUES (19, '2024-03-06 16:30:52.126055', '2024-03-07 11:09:57.000000', 20, 1, '2024-02-16', 100, 35.4000000000, 3539.8200000000, '蒋博', '3153019', '沙湾项目', 0, 'KC1001', 3); -INSERT INTO `materials_in_out` VALUES (20, '2024-03-06 16:34:04.845239', '2024-03-07 11:09:52.000000', 21, 0, '2024-02-16', 202, 18.5800000000, 3753.9800000000, '戚兆伟', NULL, '沙湾项目', 0, 'KC1002', 3); -INSERT INTO `materials_in_out` VALUES (21, '2024-03-06 16:34:40.829659', '2024-03-07 11:09:47.000000', 21, 1, '2024-02-16', 202, 18.5800000000, 3753.9800000000, '戚兆伟', '3153011', '沙湾项目', 0, 'KC1002', 3); -INSERT INTO `materials_in_out` VALUES (22, '2024-03-06 16:35:23.756416', '2024-03-07 11:09:43.000000', 21, 0, '2024-02-16', 200, 26.5500000000, 5309.7300000000, '戚兆伟', NULL, '沙湾项目', 0, 'KC1003', 3); -INSERT INTO `materials_in_out` VALUES (23, '2024-03-06 16:35:55.157718', '2024-03-07 11:09:38.000000', 21, 1, '2024-02-16', 200, 26.5500000000, 5309.7300000000, '戚兆伟', '3153011', '沙湾项目', 0, 'KC1003', 3); -INSERT INTO `materials_in_out` VALUES (24, '2024-03-06 16:38:52.490493', '2024-03-07 11:09:34.000000', 22, 0, '2024-02-20', 200, 300.8800000000, 60176.9900000000, '朱明仁', NULL, '东大项目', 0, 'KC1004', 2); -INSERT INTO `materials_in_out` VALUES (25, '2024-03-06 16:39:55.423422', '2024-03-07 11:09:30.000000', 22, 1, '2024-02-20', 200, 300.8849500000, 60176.9900000000, '朱明仁', '3153022', '东大项目', 0, 'KC1004', 2); -INSERT INTO `materials_in_out` VALUES (26, '2024-03-06 16:41:20.939530', '2024-03-07 11:09:27.000000', 23, 0, '2024-02-19', 1, 22005.7500000000, 22005.7500000000, '朱明仁', NULL, '东大项目', 0, 'KC1005', 2); -INSERT INTO `materials_in_out` VALUES (27, '2024-03-06 16:41:50.876715', '2024-03-07 11:08:31.000000', 23, 1, '2024-02-19', 1, 22005.7500000000, 22005.7500000000, '朱明仁', '3153023', '东大项目', 0, 'KC1005', 2); -INSERT INTO `materials_in_out` VALUES (28, '2024-03-07 09:49:54.511550', '2024-03-07 09:49:54.511550', 24, 0, '2024-02-16', 800, 0.9609250000, 768.7400000000, '朱明仁', NULL, '星火项目', 0, 'KC1006', 1); -- ---------------------------- -- Table structure for materials_in_out_storage @@ -182,35 +186,24 @@ CREATE TABLE `materials_inventory` ( `id` int NOT NULL AUTO_INCREMENT, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), - `company_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公司名称', - `product` int NOT NULL COMMENT '产品名称(字典)', - `unit` int NOT NULL COMMENT '单位(字典)', - `previous_inventory_quantity` int NOT NULL DEFAULT 0 COMMENT '之前的库存数量', - `previous_unit_price` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '之前的单价', - `previous_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '之前的金额', - `inventory_time` date NULL DEFAULT NULL COMMENT '入库时间', - `inventory_quantity` int NOT NULL DEFAULT 0 COMMENT '入库数量', - `inventory_unit_price` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '入库单价', - `inventory_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '入库金额', - `out_time` date NULL DEFAULT NULL COMMENT '出库时间', - `out_quantity` int NOT NULL DEFAULT 0 COMMENT '出库数量', - `out_unit_price` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '出库单价', - `out_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '出库金额', - `current_inventory_quantity` int NOT NULL DEFAULT 0 COMMENT '现在的结存数量', - `current_unit_price` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '现在的单价', - `current_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '现在的金额', - `agent` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '经办人', - `issuance_number` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '领料单号', - `project` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目', `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + `product_id` int NOT NULL COMMENT '产品', + `quantity` int NOT NULL DEFAULT 0 COMMENT '库存产品数量', + `unit_price` decimal(15, 10) NOT NULL DEFAULT 0.0000000000 COMMENT '库存产品单价', + `project_id` int NOT NULL COMMENT '项目', + `position` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '库存位置', + `inventory_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '库存编号', + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_3915e159f03408035a62721d5be`(`project_id`) USING BTREE, + INDEX `FK_413008d6a91b215b66971c9a9e8`(`product_id`) USING BTREE, + CONSTRAINT `FK_3915e159f03408035a62721d5be` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_413008d6a91b215b66971c9a9e8` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 54 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of materials_inventory -- ---------------------------- -INSERT INTO `materials_inventory` VALUES (1, '2024-03-04 13:47:10.258131', '2024-03-04 14:15:15.373283', '北京特倍福电子技术有限公司', 1, 14, 0, 0.00, 0.00, '2024-02-16', 100, 557.52, 55752.21, '2024-02-16', 100, 557.52, 55752.21, 0, 0.00, 0.00, '蒋博', '3153019', '沙湾项目', '沙湾项目', 0); -- ---------------------------- -- Table structure for product @@ -225,34 +218,45 @@ CREATE TABLE `product` ( `company_id` int NULL DEFAULT NULL COMMENT '所属公司', `name_pinyin` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '产品名称的拼音', `unit_id` int NULL DEFAULT NULL COMMENT '单位(字典)', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `product_specification` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '产品规格', + `product_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '产品编号', PRIMARY KEY (`id`) USING BTREE, INDEX `FK_a0503db1630a5b8a4d7deabd556`(`company_id`) USING BTREE, INDEX `FK_b15422982adca3bf53adfb535de`(`unit_id`) USING BTREE, CONSTRAINT `FK_a0503db1630a5b8a4d7deabd556` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_b15422982adca3bf53adfb535de` FOREIGN KEY (`unit_id`) REFERENCES `sys_dict_item` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB AUTO_INCREMENT = 33 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of product -- ---------------------------- -INSERT INTO `product` VALUES (1, '2024-03-04 16:47:37.604035', '2024-03-05 21:52:59.000000', '位移传感器', 0, 10, 'weiyichuanganqi', NULL); -INSERT INTO `product` VALUES (2, '2024-03-04 16:47:41.483273', '2024-03-06 09:13:03.000000', '磁环', 1, 6, 'cihuan', NULL); -INSERT INTO `product` VALUES (3, '2024-03-04 16:47:44.911165', '2024-03-05 21:52:52.000000', '天线', 0, 5, 'tianxian', NULL); -INSERT INTO `product` VALUES (4, '2024-03-04 16:47:48.398543', '2024-03-05 21:53:17.000000', '集成电路', 0, 8, 'jichengdianlu', NULL); -INSERT INTO `product` VALUES (5, '2024-03-05 09:09:05.015757', '2024-03-05 09:27:28.000000', '更新', 1, NULL, NULL, NULL); -INSERT INTO `product` VALUES (12, '2024-03-05 09:25:29.584423', '2024-03-06 09:12:02.000000', '巴伦', 0, 13, 'balun', 15); -INSERT INTO `product` VALUES (13, '2024-03-05 13:53:15.998630', '2024-03-06 09:14:13.000000', '极薄煤层控制器', 0, 10, 'jibaomeicengkongzhiqi', 14); -INSERT INTO `product` VALUES (14, '2024-03-05 16:05:30.485017', '2024-03-06 09:13:59.000000', '天线', 0, 13, 'tianxian', 15); -INSERT INTO `product` VALUES (15, '2024-03-05 17:21:20.378006', '2024-03-05 21:52:57.000000', 'USB智能充电线', 0, 7, 'USBzhinengchongdianxian', NULL); -INSERT INTO `product` VALUES (16, '2024-03-05 17:24:03.148627', '2024-03-05 17:30:48.000000', '33', 1, 5, NULL, NULL); -INSERT INTO `product` VALUES (17, '2024-03-05 17:30:32.450320', '2024-03-05 17:30:50.000000', 'test', 1, 5, 'test', NULL); -INSERT INTO `product` VALUES (18, '2024-03-05 21:52:11.477508', '2024-03-05 21:53:34.000000', '新增', 1, NULL, 'xinzeng', NULL); -INSERT INTO `product` VALUES (19, '2024-03-06 08:53:25.600367', '2024-03-06 09:13:39.000000', '位移传感器', 0, 12, 'weiyichuanganqi', 14); -INSERT INTO `product` VALUES (20, '2024-03-06 09:12:47.327409', '2024-03-06 09:12:47.327409', '磁环', 0, 12, 'cihuan', 14); -INSERT INTO `product` VALUES (21, '2024-03-06 09:13:21.382776', '2024-03-06 09:13:27.000000', '集成电路', 0, 13, 'jichengdianlu', 15); -INSERT INTO `product` VALUES (22, '2024-03-06 16:38:06.999498', '2024-03-06 16:38:14.000000', '电磁阀驱动器', 0, 10, 'diancifaqudongqi', 14); -INSERT INTO `product` VALUES (23, '2024-03-06 16:40:32.859846', '2024-03-06 16:40:32.859846', '电子元器件', 0, 11, 'dianziyuanqijian', 21); -INSERT INTO `product` VALUES (24, '2024-03-07 09:48:35.854273', '2024-03-07 09:48:35.854273', '排针', 0, 1, 'paizhen', 15); +INSERT INTO `product` VALUES (1, '2024-03-04 16:47:37.604035', '2024-03-25 11:33:59.175698', '位移传感器', 0, 10, 'weiyichuanganqi', NULL, '', '', 'P1024'); +INSERT INTO `product` VALUES (2, '2024-03-04 16:47:41.483273', '2024-03-25 11:33:55.073301', '磁环', 1, 6, 'cihuan', NULL, '', '', 'P1023'); +INSERT INTO `product` VALUES (3, '2024-03-04 16:47:44.911165', '2024-03-25 11:33:47.367179', '天线', 0, 5, 'tianxian', NULL, '', '', 'P1022'); +INSERT INTO `product` VALUES (4, '2024-03-04 16:47:48.398543', '2024-03-25 11:33:45.962454', '集成电路', 0, 8, 'jichengdianlu', NULL, '', '', 'P1021'); +INSERT INTO `product` VALUES (5, '2024-03-05 09:09:05.015757', '2024-03-25 11:33:44.769681', '更新', 1, NULL, NULL, NULL, '', '', 'P1020'); +INSERT INTO `product` VALUES (12, '2024-03-05 09:25:29.584423', '2024-03-25 11:33:43.028337', '巴伦', 0, 13, 'balun', 15, 'test', '', 'P1019'); +INSERT INTO `product` VALUES (13, '2024-03-05 13:53:15.998630', '2024-03-25 11:33:41.013316', '极薄煤层控制器', 0, 10, 'jibaomeicengkongzhiqi', 14, '', '', 'P1018'); +INSERT INTO `product` VALUES (14, '2024-03-05 16:05:30.485017', '2024-03-25 11:33:38.276238', '天线', 0, 13, 'tianxian', 15, '', '', 'P1017'); +INSERT INTO `product` VALUES (15, '2024-03-05 17:21:20.378006', '2024-03-25 11:33:36.429920', 'USB智能充电线', 0, 7, 'USBzhinengchongdianxian', NULL, '', '', 'P1016'); +INSERT INTO `product` VALUES (16, '2024-03-05 17:24:03.148627', '2024-03-25 11:33:34.814008', '33', 1, 5, NULL, NULL, '', '', 'P1015'); +INSERT INTO `product` VALUES (17, '2024-03-05 17:30:32.450320', '2024-03-25 11:33:33.437417', 'test', 1, 5, 'test', NULL, '', '', 'P1014'); +INSERT INTO `product` VALUES (18, '2024-03-05 21:52:11.477508', '2024-03-25 11:33:31.247032', '新增', 1, NULL, 'xinzeng', NULL, '', '', 'P1013'); +INSERT INTO `product` VALUES (19, '2024-03-06 08:53:25.600367', '2024-03-27 11:57:24.000000', '位移传感器', 0, 12, 'weiyichuanganqi', 14, '', 'GUC2400-860mm', 'P1012'); +INSERT INTO `product` VALUES (20, '2024-03-06 09:12:47.327409', '2024-03-27 11:57:41.000000', '磁环', 0, 12, 'cihuan', 14, '', '12-1081', 'P1011'); +INSERT INTO `product` VALUES (21, '2024-03-06 09:13:21.382776', '2024-03-25 11:33:26.704971', '集成电路', 0, 13, 'jichengdianlu', 15, '', '', 'P1010'); +INSERT INTO `product` VALUES (22, '2024-03-06 16:38:06.999498', '2024-03-25 11:33:24.663945', '电磁阀驱动器', 0, 10, '', 14, '', '', 'P1009'); +INSERT INTO `product` VALUES (23, '2024-03-06 16:40:32.859846', '2024-03-25 11:32:56.316568', '电子元器件', 0, 11, 'dianziyuanqijian', 21, '', '', 'P1006'); +INSERT INTO `product` VALUES (24, '2024-03-07 09:48:35.854273', '2024-03-25 11:32:58.560805', '排针', 0, 1, 'paizhen', 15, '', '', 'P1007'); +INSERT INTO `product` VALUES (25, '2024-03-11 13:19:40.149703', '2024-03-25 11:33:01.812839', '更新', 1, 5, 'gengxin', 16, 'weeeeeeeee', '', 'P1008'); +INSERT INTO `product` VALUES (26, '2024-03-11 13:52:41.663603', '2024-03-25 11:32:32.798873', 'PCBA-线路板', 0, 11, 'PCBA-xianluban', 22, NULL, '', 'P1005'); +INSERT INTO `product` VALUES (27, '2024-03-22 11:01:53.195802', '2024-03-27 11:57:32.000000', '蜂鸣器', 0, 14, 'fengmingqi', 15, '刘大伟', '', 'P1004'); +INSERT INTO `product` VALUES (28, '2024-03-22 15:46:35.784165', '2024-03-27 11:57:02.000000', '222', 1, 1, '222', 14, '222', '222', 'P1005'); +INSERT INTO `product` VALUES (29, '2024-03-22 16:02:00.927537', '2024-03-22 16:02:00.927537', 'wwr', 0, 4, 'wwr', 14, NULL, 'wrq', 'P1000'); +INSERT INTO `product` VALUES (30, '2024-03-22 16:02:16.834435', '2024-03-27 11:57:04.000000', 'A321421', 1, 5, 'A321421', 14, NULL, '241', 'P1002'); +INSERT INTO `product` VALUES (31, '2024-03-22 16:03:00.226909', '2024-03-22 16:03:39.000000', '集成电路', 0, 4, 'jichengdianlu', 15, NULL, 'SW8110Q', 'P1001'); +INSERT INTO `product` VALUES (32, '2024-03-26 10:30:10.448050', '2024-03-26 10:30:10.448050', '端子线', 0, 15, 'duanzixian', 21, NULL, NULL, 'P1025'); -- ---------------------------- -- Table structure for product_storage @@ -272,6 +276,10 @@ CREATE TABLE `product_storage` ( -- Records of product_storage -- ---------------------------- INSERT INTO `product_storage` VALUES (1, 124); +INSERT INTO `product_storage` VALUES (12, 144); +INSERT INTO `product_storage` VALUES (12, 145); +INSERT INTO `product_storage` VALUES (22, 176); +INSERT INTO `product_storage` VALUES (27, 177); -- ---------------------------- -- Table structure for project @@ -285,14 +293,23 @@ CREATE TABLE `project` ( `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_dedfea394088ed136ddadeee89`(`name`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of project -- ---------------------------- -INSERT INTO `project` VALUES (1, '2024-03-07 09:35:15.276345', '2024-03-07 09:36:01.000000', '星火项目', 0); +INSERT INTO `project` VALUES (1, '2024-03-07 09:35:15.276345', '2024-03-08 08:48:21.000000', '星火项目', 0); INSERT INTO `project` VALUES (2, '2024-03-07 09:35:20.004729', '2024-03-07 09:35:20.004729', '东大项目', 0); INSERT INTO `project` VALUES (3, '2024-03-07 09:35:29.213057', '2024-03-07 09:35:29.213057', '沙湾煤业项目', 0); +INSERT INTO `project` VALUES (4, '2024-03-20 14:01:02.203909', '2024-03-20 14:24:33.000000', '测试项目2', 0); +INSERT INTO `project` VALUES (5, '2024-03-20 14:24:36.245179', '2024-03-20 14:24:36.245179', '测试项目3', 0); +INSERT INTO `project` VALUES (6, '2024-03-20 14:24:39.336620', '2024-03-20 14:24:39.336620', '测试项目4', 0); +INSERT INTO `project` VALUES (7, '2024-03-20 14:24:43.261902', '2024-03-20 14:24:43.261902', '测试项目测试项目测试项目测试项目测试项目', 0); +INSERT INTO `project` VALUES (8, '2024-03-20 14:24:45.976572', '2024-03-20 14:24:45.976572', '测试项目5', 0); +INSERT INTO `project` VALUES (9, '2024-03-20 14:24:53.929293', '2024-03-20 14:24:53.929293', '测试项目6', 0); +INSERT INTO `project` VALUES (10, '2024-03-20 14:24:56.436181', '2024-03-20 14:24:56.436181', '测试项目7', 0); +INSERT INTO `project` VALUES (11, '2024-03-20 14:24:58.916471', '2024-03-20 14:24:58.916471', '测试项目8', 0); +INSERT INTO `project` VALUES (12, '2024-03-20 14:25:13.153119', '2024-03-20 14:25:13.153119', '测试项目9', 0); -- ---------------------------- -- Table structure for project_storage @@ -311,7 +328,8 @@ CREATE TABLE `project_storage` ( -- ---------------------------- -- Records of project_storage -- ---------------------------- -INSERT INTO `project_storage` VALUES (1, 128); +INSERT INTO `project_storage` VALUES (1, 139); +INSERT INTO `project_storage` VALUES (1, 140); -- ---------------------------- -- Table structure for sys_captcha_log @@ -320,13 +338,13 @@ DROP TABLE IF EXISTS `sys_captcha_log`; CREATE TABLE `sys_captcha_log` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NULL DEFAULT NULL, - `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, - `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, - `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `account` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_captcha_log @@ -346,7 +364,7 @@ CREATE TABLE `sys_config` ( `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_2c363c25cf99bcaab3a7f389ba`(`key`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_config @@ -354,7 +372,10 @@ CREATE TABLE `sys_config` ( INSERT INTO `sys_config` VALUES (1, 'sys_user_initPassword', '初始密码', '123456', '创建管理员账号的初始密码', '2023-11-10 00:31:44.154921', '2023-11-10 00:31:44.161263'); INSERT INTO `sys_config` VALUES (2, 'sys_api_token', 'API Token', 'huaxin-admin', '用于请求 @ApiToken 的控制器', '2023-11-10 00:31:44.154921', '2024-01-29 09:52:27.000000'); INSERT INTO `sys_config` VALUES (3, 'companyName', '公司名称', '华信智能', '菜单侧栏公司的名称', '2024-03-06 13:06:47.347660', '2024-03-06 13:07:18.000000'); -INSERT INTO `sys_config` VALUES (4, 'materials_in_out_prefix', '出入库记录开头编号', 'KC', '出入库记录开头编号', '2024-03-06 14:50:04.844992', '2024-03-06 14:50:04.844992'); +INSERT INTO `sys_config` VALUES (4, 'inventory_inout_number_prefix_in', '人库单号前缀', 'SI', '人库单号前缀', '2024-03-06 14:50:04.844992', '2024-03-25 15:52:28.000000'); +INSERT INTO `sys_config` VALUES (5, 'inventory_inout_number_prefix_out', '出库单号前缀', 'SO', '出库单号前缀', '2024-03-22 13:37:21.165879', '2024-03-25 15:52:32.000000'); +INSERT INTO `sys_config` VALUES (6, 'product_number_prefix', '产品编号前缀', 'P', '产品编号前缀', '2024-03-22 15:51:10.960064', '2024-03-22 15:51:10.960064'); +INSERT INTO `sys_config` VALUES (7, 'inventory_number_prefix', '库存编号', 'S', '库存编号', '2024-03-25 15:54:08.836711', '2024-03-25 15:54:08.836711'); -- ---------------------------- -- Table structure for sys_dept @@ -362,16 +383,16 @@ INSERT INTO `sys_config` VALUES (4, 'materials_in_out_prefix', '出入库记录 DROP TABLE IF EXISTS `sys_dept`; CREATE TABLE `sys_dept` ( `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `orderNo` int NULL DEFAULT 0, - `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '', + `mpath` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '', `parentId` int NULL DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, INDEX `FK_c75280b01c49779f2323536db67`(`parentId`) USING BTREE, CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dept @@ -400,7 +421,7 @@ CREATE TABLE `sys_dict` ( `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_d112365748f740ee260b65ce91`(`name`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict @@ -427,7 +448,7 @@ CREATE TABLE `sys_dict_item` ( PRIMARY KEY (`id`) USING BTREE, INDEX `FK_d68ea74fcb041c8cfd1fd659844`(`type_id`) USING BTREE, CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 23 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict_item @@ -451,6 +472,22 @@ INSERT INTO `sys_dict_item` VALUES (20, '2024-03-04 13:44:26.620837', '2024-03-0 INSERT INTO `sys_dict_item` VALUES (21, '2024-03-04 14:10:38.216659', '2024-03-04 14:10:54.000000', 1, 1, '批', 'unit_pi', NULL, 1, NULL, 5, 7, '2024-03-04 14:10:54.729114'); INSERT INTO `sys_dict_item` VALUES (22, '2024-03-04 14:10:48.864655', '2024-03-04 14:10:48.864655', 1, 1, '片', 'unit_pian', NULL, 1, NULL, 5, 8, '2024-03-04 14:10:48.864655'); INSERT INTO `sys_dict_item` VALUES (23, '2024-03-04 14:11:06.319281', '2024-03-04 14:11:06.319281', 1, 1, '套', 'unit_tao', NULL, 1, NULL, 5, 9, '2024-03-04 14:11:06.319281'); +INSERT INTO `sys_dict_item` VALUES (24, '2024-03-07 16:33:17.412474', '2024-03-07 16:33:17.412474', 1, 1, '奥迪A8(鲁UKS052)', 'car_1', NULL, 1, NULL, 6, 1, '2024-03-07 16:33:17.412474'); +INSERT INTO `sys_dict_item` VALUES (25, '2024-03-07 16:33:44.438153', '2024-03-07 16:33:44.438153', 1, 1, '阿尔法(鲁B33A52)', 'car_2', NULL, 1, NULL, 6, 1, '2024-03-07 16:33:44.438153'); +INSERT INTO `sys_dict_item` VALUES (26, '2024-03-07 16:34:08.872618', '2024-03-07 16:34:08.872618', 1, 1, '威尔法(鲁B33G21)', 'car_3', NULL, 1, NULL, 6, 2, '2024-03-07 16:34:08.872618'); +INSERT INTO `sys_dict_item` VALUES (27, '2024-03-25 08:28:13.363025', '2024-03-25 08:30:51.000000', 1, 1, '库房一', 'room_1', NULL, 1, NULL, 7, 0, '2024-03-25 08:30:51.792948'); +INSERT INTO `sys_dict_item` VALUES (28, '2024-03-25 08:28:23.806536', '2024-03-25 08:30:55.000000', 1, 1, '库房二', 'room_2', NULL, 1, NULL, 7, 1, '2024-03-25 08:30:55.408039'); +INSERT INTO `sys_dict_item` VALUES (29, '2024-03-25 08:28:31.643400', '2024-03-25 08:30:59.000000', 1, 1, '库房三', 'room_3', NULL, 1, NULL, 7, 2, '2024-03-25 08:30:59.195490'); +INSERT INTO `sys_dict_item` VALUES (30, '2024-03-25 08:29:49.485531', '2024-03-25 08:30:39.000000', 1, 1, '第一排', 'line_1', NULL, 1, NULL, 8, 0, '2024-03-25 08:30:39.156586'); +INSERT INTO `sys_dict_item` VALUES (31, '2024-03-25 08:29:58.991397', '2024-03-25 08:30:22.000000', 1, 1, '第二排', 'line_2', NULL, 1, NULL, 8, 1, '2024-03-25 08:30:22.398794'); +INSERT INTO `sys_dict_item` VALUES (32, '2024-03-25 08:30:09.155470', '2024-03-25 08:30:09.155470', 1, 1, '第三排', 'line_3', NULL, 1, NULL, 8, 2, '2024-03-25 08:30:09.155470'); +INSERT INTO `sys_dict_item` VALUES (33, '2024-03-25 08:30:18.716726', '2024-03-25 08:30:18.716726', 1, 1, '第四排', 'line_4', NULL, 1, NULL, 8, 3, '2024-03-25 08:30:18.716726'); +INSERT INTO `sys_dict_item` VALUES (34, '2024-03-25 08:30:33.674158', '2024-03-25 08:30:33.674158', 1, 1, '第五排', 'line_5', NULL, 1, NULL, 8, 4, '2024-03-25 08:30:33.674158'); +INSERT INTO `sys_dict_item` VALUES (35, '2024-03-25 08:32:06.027559', '2024-03-25 08:32:06.027559', 1, 1, '第一层', 'level_1', NULL, 1, NULL, 9, 0, '2024-03-25 08:32:06.027559'); +INSERT INTO `sys_dict_item` VALUES (36, '2024-03-25 08:32:14.302500', '2024-03-25 08:32:14.302500', 1, 1, '第二层', 'level_2', NULL, 1, NULL, 9, 1, '2024-03-25 08:32:14.302500'); +INSERT INTO `sys_dict_item` VALUES (37, '2024-03-25 08:32:54.412145', '2024-03-25 08:32:54.412145', 1, 1, '第三层', 'level_3', NULL, 1, NULL, 9, 2, '2024-03-25 08:32:54.412145'); +INSERT INTO `sys_dict_item` VALUES (38, '2024-03-25 08:33:02.567402', '2024-03-25 08:33:02.567402', 1, 1, '第四层', 'level_4', NULL, 1, NULL, 9, 3, '2024-03-25 08:33:02.567402'); +INSERT INTO `sys_dict_item` VALUES (39, '2024-03-25 08:33:12.209556', '2024-03-25 08:33:12.209556', 1, 1, '第五层', 'level_5', NULL, 1, NULL, 9, 4, '2024-03-25 08:33:12.209556'); -- ---------------------------- -- Table structure for sys_dict_type @@ -469,7 +506,7 @@ CREATE TABLE `sys_dict_type` ( `deleted_at` datetime(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_74d0045ff7fab9f67adc0b1bda`(`code`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict_type @@ -478,6 +515,10 @@ INSERT INTO `sys_dict_type` VALUES (1, '2024-01-28 08:19:12.777447', '2024-02-08 INSERT INTO `sys_dict_type` VALUES (2, '2024-01-28 08:38:41.235185', '2024-01-29 02:11:33.000000', 1, 1, '菜单显示状态', 1, '菜单显示状态', 'sys_show_hide', '2024-03-01 15:28:21.689753'); INSERT INTO `sys_dict_type` VALUES (3, '2024-02-28 16:38:27.311577', '2024-03-04 13:26:29.000000', 1, 1, '合同类型', 1, '合同类型', 'contract_type', '2024-03-04 13:26:29.911469'); INSERT INTO `sys_dict_type` VALUES (5, '2024-03-04 13:41:05.156027', '2024-03-04 13:41:05.156027', 1, 1, '单位', 1, '材料盘点表等单位。件。个', 'unit', '2024-03-04 13:41:05.156027'); +INSERT INTO `sys_dict_type` VALUES (6, '2024-03-07 16:32:26.985730', '2024-03-07 16:32:26.985730', 1, 1, '公司车辆', 1, '公司的公车', 'vehicle', '2024-03-07 16:32:26.985730'); +INSERT INTO `sys_dict_type` VALUES (7, '2024-03-25 08:27:37.461575', '2024-03-25 08:27:37.461575', 1, 1, '库存位置-房间', 1, '库存存放的房间', 'inventory_room', '2024-03-25 08:27:37.461575'); +INSERT INTO `sys_dict_type` VALUES (8, '2024-03-25 08:29:29.110447', '2024-03-25 08:29:29.110447', 1, 1, '库存位置-排架号', 1, '库存位置-排架号', 'inventory_line', '2024-03-25 08:29:29.110447'); +INSERT INTO `sys_dict_type` VALUES (9, '2024-03-25 08:31:52.669289', '2024-03-25 08:31:52.669289', 1, 1, '库存位置-层数', 1, NULL, 'inventory_line_level', '2024-03-25 08:31:52.669289'); -- ---------------------------- -- Table structure for sys_login_log @@ -495,40 +536,85 @@ CREATE TABLE `sys_login_log` ( PRIMARY KEY (`id`) USING BTREE, INDEX `FK_3029712e0df6a28edaee46fd470`(`user_id`) USING BTREE, CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 29 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 74 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_login_log -- ---------------------------- -INSERT INTO `sys_login_log` VALUES (1, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 11:49:38.330842', '2024-02-28 11:49:38.330842', 1); -INSERT INTO `sys_login_log` VALUES (2, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:06:01.450911', '2024-02-28 13:06:01.450911', 1); -INSERT INTO `sys_login_log` VALUES (3, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:08:18.525617', '2024-02-28 13:08:18.525617', 1); -INSERT INTO `sys_login_log` VALUES (4, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:10:51.368580', '2024-02-28 13:10:51.368580', 1); -INSERT INTO `sys_login_log` VALUES (5, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:53:17.506614', '2024-02-28 13:53:17.506614', 1); -INSERT INTO `sys_login_log` VALUES (6, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-28 13:53:53.201053', '2024-02-28 13:53:53.201053', 1); -INSERT INTO `sys_login_log` VALUES (7, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-02-29 16:25:14.762388', '2024-02-29 16:25:14.762388', 1); -INSERT INTO `sys_login_log` VALUES (8, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', '内网IP', NULL, '2024-02-29 16:26:09.106911', '2024-02-29 16:26:09.106911', 1); -INSERT INTO `sys_login_log` VALUES (9, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 10:52:33.093110', '2024-03-01 10:52:33.093110', 1); -INSERT INTO `sys_login_log` VALUES (10, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 10:53:09.633370', '2024-03-01 10:53:09.633370', 1); -INSERT INTO `sys_login_log` VALUES (11, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:45:36.933407', '2024-03-01 15:45:36.933407', 1); -INSERT INTO `sys_login_log` VALUES (12, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:04.780541', '2024-03-01 15:57:04.780541', 1); -INSERT INTO `sys_login_log` VALUES (13, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:11.280355', '2024-03-01 15:57:11.280355', 1); -INSERT INTO `sys_login_log` VALUES (14, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:18.351492', '2024-03-01 15:57:18.351492', 1); -INSERT INTO `sys_login_log` VALUES (15, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:29.111440', '2024-03-01 15:57:29.111440', 1); -INSERT INTO `sys_login_log` VALUES (16, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 15:57:33.903797', '2024-03-01 15:57:33.903797', 1); -INSERT INTO `sys_login_log` VALUES (17, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-01 16:53:24.824327', '2024-03-01 16:53:24.824327', 1); -INSERT INTO `sys_login_log` VALUES (18, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 11:36:22.860536', '2024-03-04 11:36:22.860536', 1); -INSERT INTO `sys_login_log` VALUES (19, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 13:14:12.256792', '2024-03-04 13:14:12.256792', 1); -INSERT INTO `sys_login_log` VALUES (20, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 13:15:42.383909', '2024-03-04 13:15:42.383909', 1); -INSERT INTO `sys_login_log` VALUES (21, '192.168.48.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 13:16:04.702698', '2024-03-04 13:16:04.702698', 1); -INSERT INTO `sys_login_log` VALUES (22, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 15:42:54.327613', '2024-03-04 15:42:54.327613', 1); -INSERT INTO `sys_login_log` VALUES (23, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-04 17:25:42.137701', '2024-03-04 17:25:42.137701', 1); -INSERT INTO `sys_login_log` VALUES (24, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-05 14:34:08.444527', '2024-03-05 14:34:08.444527', 1); -INSERT INTO `sys_login_log` VALUES (25, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-06 08:45:04.588314', '2024-03-06 08:45:04.588314', 1); -INSERT INTO `sys_login_log` VALUES (26, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-06 17:17:55.139092', '2024-03-06 17:17:55.139092', 1); -INSERT INTO `sys_login_log` VALUES (27, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-07 09:14:33.829960', '2024-03-07 09:14:33.829960', 1); -INSERT INTO `sys_login_log` VALUES (28, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-07 11:11:38.239629', '2024-03-07 11:11:38.239629', 1); -INSERT INTO `sys_login_log` VALUES (29, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-07 11:46:14.622820', '2024-03-07 11:46:14.622820', 1); +INSERT INTO `sys_login_log` VALUES (1, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-11 16:25:35.193443', '2024-03-11 16:25:35.193443', 1); +INSERT INTO `sys_login_log` VALUES (2, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-11 16:41:47.582236', '2024-03-11 16:41:47.582236', 1); +INSERT INTO `sys_login_log` VALUES (3, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-11 17:08:24.336525', '2024-03-11 17:08:24.336525', 1); +INSERT INTO `sys_login_log` VALUES (4, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-12 08:32:35.378738', '2024-03-12 08:32:35.378738', 1); +INSERT INTO `sys_login_log` VALUES (5, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-12 08:50:34.563830', '2024-03-12 08:50:34.563830', 1); +INSERT INTO `sys_login_log` VALUES (6, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-12 09:02:32.708992', '2024-03-12 09:02:32.708992', 1); +INSERT INTO `sys_login_log` VALUES (7, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-12 14:14:27.170328', '2024-03-12 14:14:27.170328', 1); +INSERT INTO `sys_login_log` VALUES (8, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-18 16:45:53.669633', '2024-03-18 16:45:53.669633', 1); +INSERT INTO `sys_login_log` VALUES (9, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-18 16:47:04.640742', '2024-03-18 16:47:04.640742', 1); +INSERT INTO `sys_login_log` VALUES (10, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:01:14.337676', '2024-03-19 09:01:14.337676', 1); +INSERT INTO `sys_login_log` VALUES (11, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:02:49.212590', '2024-03-19 09:02:49.212590', 1); +INSERT INTO `sys_login_log` VALUES (12, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:10:41.698998', '2024-03-19 09:10:41.698998', 1); +INSERT INTO `sys_login_log` VALUES (13, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:20:20.393109', '2024-03-19 09:20:20.393109', 1); +INSERT INTO `sys_login_log` VALUES (14, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:22:27.320019', '2024-03-19 09:22:27.320019', 1); +INSERT INTO `sys_login_log` VALUES (15, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:26:47.750336', '2024-03-19 09:26:47.750336', 1); +INSERT INTO `sys_login_log` VALUES (16, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:27:47.614307', '2024-03-19 09:27:47.614307', 1); +INSERT INTO `sys_login_log` VALUES (17, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:27:56.144948', '2024-03-19 09:27:56.144948', 1); +INSERT INTO `sys_login_log` VALUES (18, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:28:48.043487', '2024-03-19 09:28:48.043487', 1); +INSERT INTO `sys_login_log` VALUES (19, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:29:03.235340', '2024-03-19 09:29:03.235340', 1); +INSERT INTO `sys_login_log` VALUES (20, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:39:07.529231', '2024-03-19 09:39:07.529231', 1); +INSERT INTO `sys_login_log` VALUES (21, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:39:44.392483', '2024-03-19 09:39:44.392483', 1); +INSERT INTO `sys_login_log` VALUES (22, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:39:59.249030', '2024-03-19 09:39:59.249030', 1); +INSERT INTO `sys_login_log` VALUES (23, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:40:34.075596', '2024-03-19 09:40:34.075596', 1); +INSERT INTO `sys_login_log` VALUES (24, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 11:25:55.110382', '2024-03-19 11:25:55.110382', 1); +INSERT INTO `sys_login_log` VALUES (25, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 13:01:22.927432', '2024-03-19 13:01:22.927432', 1); +INSERT INTO `sys_login_log` VALUES (26, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-19 13:39:49.898149', '2024-03-19 13:39:49.898149', 1); +INSERT INTO `sys_login_log` VALUES (27, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 15:00:10.238618', '2024-03-19 15:00:10.238618', 1); +INSERT INTO `sys_login_log` VALUES (28, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 15:53:32.340056', '2024-03-19 15:53:32.340056', 1); +INSERT INTO `sys_login_log` VALUES (29, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 16:32:41.403566', '2024-03-19 16:32:41.403566', 1); +INSERT INTO `sys_login_log` VALUES (30, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 16:47:04.973345', '2024-03-19 16:47:04.973345', 1); +INSERT INTO `sys_login_log` VALUES (31, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 17:30:27.560965', '2024-03-19 17:30:27.560965', 1); +INSERT INTO `sys_login_log` VALUES (32, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-20 09:17:13.462569', '2024-03-20 09:17:13.462569', 1); +INSERT INTO `sys_login_log` VALUES (33, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-20 13:01:56.230866', '2024-03-20 13:01:56.230866', 1); +INSERT INTO `sys_login_log` VALUES (34, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-20 13:27:27.578581', '2024-03-20 13:27:27.578581', 1); +INSERT INTO `sys_login_log` VALUES (35, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 09:40:24.947391', '2024-03-21 09:40:24.947391', 1); +INSERT INTO `sys_login_log` VALUES (36, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 11:00:56.747380', '2024-03-21 11:00:56.747380', 1); +INSERT INTO `sys_login_log` VALUES (37, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 11:20:46.050676', '2024-03-21 11:20:46.050676', 1); +INSERT INTO `sys_login_log` VALUES (38, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', '内网IP', NULL, '2024-03-21 13:52:09.392800', '2024-03-21 13:52:09.392800', 1); +INSERT INTO `sys_login_log` VALUES (39, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 14:13:54.119595', '2024-03-21 14:13:54.119595', 1); +INSERT INTO `sys_login_log` VALUES (40, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 14:18:43.489028', '2024-03-21 14:18:43.489028', 1); +INSERT INTO `sys_login_log` VALUES (41, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 16:53:02.442100', '2024-03-21 16:53:02.442100', 1); +INSERT INTO `sys_login_log` VALUES (42, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 17:22:14.487551', '2024-03-21 17:22:14.487551', 1); +INSERT INTO `sys_login_log` VALUES (43, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:28:39.945316', '2024-03-22 08:28:39.945316', 1); +INSERT INTO `sys_login_log` VALUES (44, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:47:49.668244', '2024-03-22 08:47:49.668244', 1); +INSERT INTO `sys_login_log` VALUES (45, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:48:40.888213', '2024-03-22 08:48:40.888213', 1); +INSERT INTO `sys_login_log` VALUES (46, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:52:29.539297', '2024-03-22 08:52:29.539297', 1); +INSERT INTO `sys_login_log` VALUES (47, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:54:51.502428', '2024-03-22 08:54:51.502428', 1); +INSERT INTO `sys_login_log` VALUES (48, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', '内网IP', NULL, '2024-03-22 09:49:50.215940', '2024-03-22 09:49:50.215940', 1); +INSERT INTO `sys_login_log` VALUES (49, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 10:18:26.403105', '2024-03-22 10:18:26.403105', 1); +INSERT INTO `sys_login_log` VALUES (50, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-25 10:28:48.017616', '2024-03-25 10:28:48.017616', 1); +INSERT INTO `sys_login_log` VALUES (51, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-25 11:40:21.480492', '2024-03-25 11:40:21.480492', 1); +INSERT INTO `sys_login_log` VALUES (52, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:29:48.608014', '2024-03-26 15:29:48.608014', 1); +INSERT INTO `sys_login_log` VALUES (53, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:30:16.175586', '2024-03-26 15:30:16.175586', 1); +INSERT INTO `sys_login_log` VALUES (54, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:31:31.752879', '2024-03-26 15:31:31.752879', 1); +INSERT INTO `sys_login_log` VALUES (55, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:32:04.628186', '2024-03-26 15:32:04.628186', 1); +INSERT INTO `sys_login_log` VALUES (56, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:53:43.139332', '2024-03-26 15:53:43.139332', 1); +INSERT INTO `sys_login_log` VALUES (57, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:54:15.500462', '2024-03-26 15:54:15.500462', 1); +INSERT INTO `sys_login_log` VALUES (58, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:54:45.176950', '2024-03-26 15:54:45.176950', 1); +INSERT INTO `sys_login_log` VALUES (59, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:57:02.624723', '2024-03-26 15:57:02.624723', 1); +INSERT INTO `sys_login_log` VALUES (60, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 16:02:07.716342', '2024-03-26 16:02:07.716342', 1); +INSERT INTO `sys_login_log` VALUES (61, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 09:45:06.617531', '2024-03-27 09:45:06.617531', 1); +INSERT INTO `sys_login_log` VALUES (62, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-27 10:04:30.119475', '2024-03-27 10:04:30.119475', 1); +INSERT INTO `sys_login_log` VALUES (63, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-27 10:59:23.447855', '2024-03-27 10:59:23.447855', 1); +INSERT INTO `sys_login_log` VALUES (64, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 10:59:38.112830', '2024-03-27 10:59:38.112830', 1); +INSERT INTO `sys_login_log` VALUES (65, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:04:13.903132', '2024-03-27 11:04:13.903132', 1); +INSERT INTO `sys_login_log` VALUES (66, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:05:32.728343', '2024-03-27 11:05:32.728343', 1); +INSERT INTO `sys_login_log` VALUES (67, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:15:54.118365', '2024-03-27 11:15:54.118365', 1); +INSERT INTO `sys_login_log` VALUES (68, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:23:51.030200', '2024-03-27 11:23:51.030200', 1); +INSERT INTO `sys_login_log` VALUES (69, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-27 11:26:55.224029', '2024-03-27 11:26:55.224029', 1); +INSERT INTO `sys_login_log` VALUES (70, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:31:45.389808', '2024-03-27 11:31:45.389808', 1); +INSERT INTO `sys_login_log` VALUES (71, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 13:50:06.483334', '2024-03-27 13:50:06.483334', 1); +INSERT INTO `sys_login_log` VALUES (72, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 13:50:18.729275', '2024-03-27 13:50:18.729275', 1); +INSERT INTO `sys_login_log` VALUES (73, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 13:54:12.303628', '2024-03-27 13:54:12.303628', 1); +INSERT INTO `sys_login_log` VALUES (74, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-28 13:06:16.680834', '2024-03-28 13:06:16.680834', 1); -- ---------------------------- -- Table structure for sys_menu @@ -553,7 +639,7 @@ CREATE TABLE `sys_menu` ( `ext_open_mode` tinyint NOT NULL DEFAULT 1, `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 158 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 167 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_menu @@ -598,7 +684,7 @@ INSERT INTO `sys_menu` VALUES (43, NULL, '/about', '关于', '', 1, 'ant-design: INSERT INTO `sys_menu` VALUES (48, NULL, '/tool', '系统工具', NULL, 0, 'ant-design:tool-outlined', 255, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-29 10:41:25.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (49, 48, '/tool/email', '邮件工具', 'system:tools:email', 1, 'ant-design:send-outlined', 1, 'tool/email/index', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:38.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (50, 49, NULL, '发送邮件', 'tools:email:send', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (51, 48, '/tool/storage', '存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:59:17.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (51, 48, '/tool/storage', '本地存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-03-08 15:13:32.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (52, 51, NULL, '文件上传', 'upload:upload', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 01:04:08.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (53, 51, NULL, '文件删除', 'tool:storage:delete', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:56:01.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (54, 2, NULL, '修改密码', 'system:user:password', 2, '', 5, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); @@ -626,8 +712,8 @@ INSERT INTO `sys_menu` VALUES (109, 107, NULL, '更新', 'system:dict-item:updat INSERT INTO `sys_menu` VALUES (110, 107, NULL, '删除', 'system:dict-item:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:28.535225', '2024-01-28 09:27:28.535225', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL); -INSERT INTO `sys_menu` VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 0, 1, '2024-02-10 08:00:02.394616', '2024-02-28 11:51:21.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (116, 115, 'manage', '文件管理', 'netdisk:manage:list', 1, '', 252, 'netdisk/manage', 0, 1, 1, '2024-02-10 08:03:49.837348', '2024-02-10 09:34:41.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 0, 1, '2024-02-10 08:00:02.394616', '2024-03-08 15:14:06.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (116, 48, 'manage', '云盘管理', 'netdisk:manage:list', 1, 'ant-design:cloud-server-outlined', 252, 'netdisk/manage', 0, 1, 1, '2024-02-10 08:03:49.837348', '2024-03-08 15:14:57.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL); @@ -651,25 +737,33 @@ INSERT INTO `sys_menu` VALUES (136, 131, NULL, '新增', 'materials_inventory:hi INSERT INTO `sys_menu` VALUES (137, 131, NULL, '更新', 'materials_inventory:history_in_out:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:15.192910', '2024-03-06 10:54:57.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (138, 131, NULL, '查询单个', 'app:contract:read', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:32.488892', '2024-03-01 17:17:32.488892', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (139, 131, NULL, '删除', 'materials_inventory:history_in_out:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:43.455773', '2024-03-06 10:55:06.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (140, 150, '/materials-inventory/company', '乙方公司管理', 'app:company:list', 1, 'ep:office-building', 6, 'materials-inventory/company/index', 0, 1, 1, '2024-03-04 15:44:30.769048', '2024-03-05 15:22:22.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (140, NULL, '/materials-inventory/company', '乙方公司管理', 'app:company:list', 1, 'ep:office-building', 6, 'materials-inventory/company/index', 0, 1, 1, '2024-03-04 15:44:30.769048', '2024-03-27 12:57:31.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (141, 140, NULL, '单个查询', 'app:company:read', 2, '', 1, NULL, 1, 1, 1, '2024-03-04 15:45:55.979802', '2024-03-04 15:45:55.979802', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (142, 140, NULL, '新增', 'app:company:create', 2, '', 2, NULL, 1, 1, 1, '2024-03-04 15:46:11.260636', '2024-03-04 15:46:11.260636', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (143, 140, NULL, '更新', 'app:company:update', 2, '', 3, NULL, 1, 1, 1, '2024-03-04 15:46:25.098204', '2024-03-04 15:46:25.098204', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (144, 140, NULL, '删除', 'app:company:delete', 2, '', 4, NULL, 1, 1, 1, '2024-03-04 15:46:50.812446', '2024-03-04 15:46:50.812446', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (145, 150, '/materials-inventory/product', '产品目录', 'app:product:list', 1, 'ant-design:product-outlined', 6, 'materials-inventory/product/index', 0, 1, 1, '2024-03-04 16:43:22.749281', '2024-03-06 13:40:03.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (145, NULL, '/materials-inventory/product', '产品目录', 'app:product:list', 1, 'ant-design:product-outlined', 6, 'materials-inventory/product/index', 0, 1, 1, '2024-03-04 16:43:22.749281', '2024-03-27 12:56:52.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (146, 145, NULL, '单个查询', 'app:product:read', 2, '', 1, NULL, 1, 1, 1, '2024-03-04 16:44:56.482508', '2024-03-04 16:44:56.482508', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (147, 145, NULL, '新增', 'app:product:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:08.211188', '2024-03-04 16:45:08.211188', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (148, 145, NULL, '更新', 'app:product:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:25.457903', '2024-03-04 16:45:25.457903', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (149, 145, NULL, '删除', 'app:product:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:39.352621', '2024-03-04 16:45:39.352621', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (150, NULL, '/materials-inventory', '原材料盘点', NULL, 0, 'ant-design:dashboard-outlined', 3, NULL, 1, 1, 1, '2024-03-04 16:53:32.172674', '2024-03-04 16:53:32.172674', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (151, 131, NULL, '导出', 'materials_inventory:history_in_out:export', 2, '', 5, NULL, 1, 1, 1, '2024-03-06 13:09:39.201093', '2024-03-06 13:09:39.201093', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (152, 150, '/materials-inventory/inventory-check', '原材料盘点', 'app:materials_inventory:list', 1, 'ant-design:dashboard-outlined', 2, 'materials-inventory/inventory-check/index', 1, 0, 1, '2024-03-06 13:33:24.795599', '2024-03-06 17:14:14.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (153, 150, '/materials-inventory/project', '项目管理', 'app:project:list', 1, 'ep:memo', 4, 'materials-inventory/project/index', 0, 1, 1, '2024-03-07 09:28:19.234454', '2024-03-07 11:12:00.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (152, 150, '/materials-inventory/inventory-check', '原材料库存管理', 'app:materials_inventory:list', 1, 'ant-design:dashboard-outlined', 2, 'materials-inventory/inventory-check/index', 1, 1, 1, '2024-03-06 13:33:24.795599', '2024-03-10 18:46:33.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (153, NULL, '/materials-inventory/project', '项目管理', 'app:project:list', 1, 'ep:memo', 4, 'materials-inventory/project/index', 0, 1, 1, '2024-03-07 09:28:19.234454', '2024-03-27 12:57:13.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (154, 153, NULL, '新增', 'app:project:create', 2, '', 1, NULL, 1, 1, 1, '2024-03-07 09:28:47.855064', '2024-03-07 09:28:47.855064', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (155, 153, NULL, '更新', 'app:project:update', 2, '', 2, NULL, 1, 1, 1, '2024-03-07 09:29:03.183084', '2024-03-07 09:29:03.183084', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (156, 153, NULL, '删除', 'app:project:delete', 2, '', 3, NULL, 1, 1, 1, '2024-03-07 09:29:16.684943', '2024-03-07 09:29:16.684943', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (157, 153, NULL, '单个信息', 'app:project:read', 2, '', 4, NULL, 1, 1, 1, '2024-03-07 09:29:33.424578', '2024-03-07 09:29:33.424578', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (158, 131, NULL, '导出原材料盘点表', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 11:46:54.468400', '2024-03-07 11:46:54.468400', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (159, 130, NULL, '更新', 'app:vehicle_usage:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:04.324327', '2024-03-07 17:05:04.324327', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (160, 130, NULL, '删除', 'app:vehicle_usage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:13.776313', '2024-03-07 17:05:13.776313', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (161, 130, NULL, '新增', 'app:vehicle_usage:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:25.081691', '2024-03-07 17:05:25.081691', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (162, 130, NULL, '单个信息', 'app:vehicle_usage:read', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:48.310497', '2024-03-07 17:05:48.310497', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (163, 152, NULL, '导出', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:43:41.135585', '2024-03-11 13:43:41.135585', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (164, 152, NULL, '更新', 'app:materials_inventory:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:44:23.144410', '2024-03-11 13:44:23.144410', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (165, 152, NULL, '删除', 'app:materials_inventory:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:44:47.383396', '2024-03-11 13:44:47.383396', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (166, 128, '/contract/task', '任务管控', NULL, 1, 'ant-design:align-left-outlined', 255, 'task/index', 0, 1, 1, '2024-03-12 10:18:54.645756', '2024-03-12 10:18:54.645756', 0, 1, NULL); -- ---------------------------- -- Table structure for sys_role @@ -687,7 +781,7 @@ CREATE TABLE `sys_role` ( PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_223de54d6badbe43a5490450c3`(`name`) USING BTREE, UNIQUE INDEX `IDX_05edc0a51f41bb16b7d8137da9`(`value`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_role @@ -907,8 +1001,8 @@ CREATE TABLE `sys_task` ( -- ---------------------------- -- Records of sys_task -- ---------------------------- -INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-03-28 11:01:34.000000'); -INSERT INTO `sys_task` VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-03-28 11:01:34.000000'); +INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-03-28 13:02:28.000000'); +INSERT INTO `sys_task` VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 0, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-03-22 14:12:52.000000'); INSERT INTO `sys_task` VALUES (4, '访问百度首页', 'HttpRequestJob.handle', 0, 0, NULL, NULL, 1, '* * * * * ?', NULL, '{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}', NULL, '访问百度首页', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); INSERT INTO `sys_task` VALUES (5, '发送邮箱', 'EmailJob.send', 0, 0, NULL, NULL, -1, '0 0 0 1 * ?', NULL, '{\"subject\":\"这是标题\",\"to\":\"18661983080@163.com\",\"content\":\"这是正文\"}', NULL, '每月发送邮箱', '2023-11-10 00:31:44.197779', '2024-03-07 11:14:53.000000'); @@ -932,8 +1026,8 @@ CREATE TABLE `sys_task_log` ( -- ---------------------------- -- Records of sys_task_log -- ---------------------------- -INSERT INTO `sys_task_log` VALUES (1, 3, 1, NULL, 0, '2024-02-05 03:06:22.037448', '2024-02-05 03:06:22.037448'); -INSERT INTO `sys_task_log` VALUES (2, 2, 1, NULL, 0, '2024-02-10 09:42:21.738712', '2024-02-10 09:42:21.738712'); +INSERT INTO `sys_task_log` VALUES (1, 3, 1, NULL, 0, '2024-03-11 07:37:16.258223', '2024-03-11 07:37:16.258223'); +INSERT INTO `sys_task_log` VALUES (2, 2, 1, NULL, 0, '2024-03-11 08:29:25.175865', '2024-03-11 08:29:25.175865'); -- ---------------------------- -- Table structure for sys_user @@ -958,12 +1052,12 @@ CREATE TABLE `sys_user` ( UNIQUE INDEX `IDX_9e7164b2f1ea1348bc0eb0a7da`(`username`) USING BTREE, INDEX `FK_96bde34263e2ae3b46f011124ac`(`dept_id`) USING BTREE, CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_user -- ---------------------------- -INSERT INTO `sys_user` VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-06 17:18:04.000000', 'bqy', 2); +INSERT INTO `sys_user` VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-27 11:32:42.000000', '朱明仁12', 2); INSERT INTO `sys_user` VALUES (2, 'user', 'dbd89546dec743f82bb9073d6ac39361', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', 'luffy@qq.com', '10010', '王路飞', 'qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-01 11:31:11.000000', 'luffy', 8); INSERT INTO `sys_user` VALUES (8, 'developer', 'f03fa2a99595127b9a39587421d471f6', '/upload/报名照片-202402281149824.jpg', 'nami@qq.com', '10000', '小贼猫', 'NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-06 17:17:21.000000', '娜美', 2); @@ -1002,7 +1096,7 @@ CREATE TABLE `todo` ( PRIMARY KEY (`id`) USING BTREE, INDEX `FK_9cb7989853c4cb7fe427db4b260`(`user_id`) USING BTREE, CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of todo @@ -1024,19 +1118,114 @@ CREATE TABLE `tool_storage` ( `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `user_id` int NULL DEFAULT NULL, + `bussiness_module` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + `bussiness_record_id` int NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 128 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 224 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of tool_storage -- ---------------------------- -INSERT INTO `tool_storage` VALUES (121, '2024-03-01 16:46:58.441084', '2024-03-01 16:46:58.441084', '1709172270328-202403011646430.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403011646430.jpg', '图片', '62.79 KB', 1); -INSERT INTO `tool_storage` VALUES (122, '2024-03-01 17:21:35.454311', '2024-03-01 17:21:35.454311', '盘点表-202403011721448.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403011721448.xlsx', '文档', '10.83 KB', 1); -INSERT INTO `tool_storage` VALUES (123, '2024-03-04 15:51:50.664699', '2024-03-04 15:51:50.664699', '20240304-202403041551657.sql', '20240304.sql', 'sql', '/upload/20240304-202403041551657.sql', '其他', '62.86 KB', 1); -INSERT INTO `tool_storage` VALUES (124, '2024-03-05 10:39:45.040659', '2024-03-05 10:39:45.040659', '盘点表-202403051039028.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403051039028.xlsx', '文档', '10.83 KB', 1); -INSERT INTO `tool_storage` VALUES (126, '2024-03-06 13:04:17.636919', '2024-03-06 13:04:17.636919', '盘点表-202403061304624.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403061304624.xlsx', '文档', '10.83 KB', 1); -INSERT INTO `tool_storage` VALUES (127, '2024-03-07 09:35:51.916017', '2024-03-07 09:35:51.916017', '盘点表-202403070935910.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403070935910.xlsx', '文档', '10.83 KB', 1); -INSERT INTO `tool_storage` VALUES (128, '2024-03-07 09:36:01.570104', '2024-03-07 09:36:01.570104', '1709172270328-202403070936565.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403070936565.jpg', '图片', '62.86 KB', 1); +INSERT INTO `tool_storage` VALUES (121, '2024-03-01 16:46:58.441084', '2024-03-01 16:46:58.441084', '1709172270328-202403011646430.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403011646430.jpg', '图片', '62.79 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (122, '2024-03-01 17:21:35.454311', '2024-03-01 17:21:35.454311', '盘点表-202403011721448.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403011721448.xlsx', '文档', '10.83 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (123, '2024-03-04 15:51:50.664699', '2024-03-04 15:51:50.664699', '20240304-202403041551657.sql', '20240304.sql', 'sql', '/upload/20240304-202403041551657.sql', '其他', '62.86 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (124, '2024-03-05 10:39:45.040659', '2024-03-05 10:39:45.040659', '盘点表-202403051039028.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403051039028.xlsx', '文档', '10.83 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (126, '2024-03-06 13:04:17.636919', '2024-03-06 13:04:17.636919', '盘点表-202403061304624.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403061304624.xlsx', '文档', '10.83 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (127, '2024-03-07 09:35:51.916017', '2024-03-07 09:35:51.916017', '盘点表-202403070935910.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403070935910.xlsx', '文档', '10.83 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (128, '2024-03-07 09:36:01.570104', '2024-03-07 09:36:01.570104', '1709172270328-202403070936565.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403070936565.jpg', '图片', '62.86 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (129, '2024-03-07 16:28:46.729632', '2024-03-07 16:28:46.729632', '盘点表-202403071628719.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403071628719.xlsx', '文档', '10.83 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (130, '2024-03-08 08:29:50.602980', '2024-03-08 08:29:50.602980', '1709172270328-202403080829597.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080829597.jpg', '图片', '62.86 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (133, '2024-03-08 08:36:52.107852', '2024-03-08 08:36:52.107852', '1709172270328-202403080836097.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080836097.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (134, '2024-03-08 08:36:57.397270', '2024-03-08 08:36:57.397270', 'hxoa_2024-03-07_171919-202403080836391.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080836391.sql', '其他', '62.88 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (135, '2024-03-08 08:39:36.243846', '2024-03-08 08:39:36.243846', 'hxoa_2024-03-07_171919-202403080839235.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080839235.sql', '其他', '62.88 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (136, '2024-03-08 08:39:45.015279', '2024-03-08 08:39:45.015279', '1709172270328-202403080839012.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080839012.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (137, '2024-03-08 08:41:01.350779', '2024-03-08 08:41:01.350779', 'hxoa_2024-03-07_171919-202403080841340.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080841340.sql', '其他', '62.88 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (138, '2024-03-08 08:42:09.015239', '2024-03-08 08:42:09.015239', '1709172270328-202403080842003.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080842003.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (139, '2024-03-08 08:46:36.613712', '2024-03-08 08:46:36.613712', '1709172270328-202403080846601.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080846601.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (140, '2024-03-08 08:48:21.472060', '2024-03-08 08:48:21.472060', '1709172270328-202403080848461.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080848461.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (141, '2024-03-08 08:48:32.069968', '2024-03-08 08:48:32.069968', '1709172270328-202403080848060.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080848060.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (142, '2024-03-08 08:48:32.071978', '2024-03-08 08:48:32.071978', 'hxoa_2024-03-07_171919-202403080848065.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080848065.sql', '其他', '62.88 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (143, '2024-03-08 08:48:36.850064', '2024-03-08 08:48:36.850064', '1709172270328-202403080848844.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080848844.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (144, '2024-03-08 08:48:43.982565', '2024-03-08 08:48:43.982565', '1709172270328-202403080848977.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080848977.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (145, '2024-03-08 08:48:49.327172', '2024-03-08 08:48:49.327172', 'hxoa_2024-03-07_171919-202403080848322.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080848322.sql', '其他', '62.88 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (146, '2024-03-08 10:05:23.771674', '2024-03-08 10:05:23.771674', '1709172270328-202403081005760.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081005760.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (147, '2024-03-08 10:05:29.481209', '2024-03-08 10:05:29.481209', 'hxoa_2024-03-07_171919-202403081005473.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403081005473.sql', '其他', '62.88 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (148, '2024-03-08 10:57:38.555901', '2024-03-08 10:57:38.555901', '1709172270328-202403081057545.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081057545.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (149, '2024-03-08 11:06:36.879749', '2024-03-08 11:06:36.879749', '1709172270328-202403081106867.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081106867.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (150, '2024-03-08 11:07:11.217830', '2024-03-08 11:07:11.217830', '1709172270328-202403081107212.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081107212.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (151, '2024-03-08 11:08:24.817797', '2024-03-08 11:08:24.817797', '1709172270328-202403081108811.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081108811.jpg', '图片', '15.86 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (152, '2024-03-08 11:10:12.868485', '2024-03-08 11:10:12.868485', '1709172270328-202403081109532.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081109532.jpg', '图片', '62.86 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (153, '2024-03-08 11:10:32.700427', '2024-03-08 11:10:32.700427', '1709172270328-202403081110695.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081110695.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (154, '2024-03-08 11:11:25.391852', '2024-03-08 11:11:25.391852', '微信图片_20240308085222-202403081111382.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081111382.jpg', '图片', '62.89 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (155, '2024-03-08 11:17:01.314543', '2024-03-08 11:17:01.314543', '微信图片_20240308085222-202403081117276.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081117276.jpg', '图片', '62.89 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (156, '2024-03-08 11:23:33.233526', '2024-03-08 11:23:33.233526', '微信图片_20240308085222-202403081123218.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081123218.jpg', '图片', '62.89 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (157, '2024-03-08 11:28:30.444379', '2024-03-08 11:28:30.444379', '微信图片_20240308085222-202403081128414.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081128414.jpg', '图片', '62.89 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (158, '2024-03-08 11:31:22.373716', '2024-03-08 11:31:22.373716', '1709172270328-202403081131338.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081131338.jpg', '图片', '62.9 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (159, '2024-03-08 11:33:01.737383', '2024-03-08 11:33:01.737383', '微信图片_20240308085222-202403081133723.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081133723.jpg', '图片', '62.89 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (160, '2024-03-08 11:42:52.468709', '2024-03-08 11:42:52.468709', '1709172270328-202403081142459.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081142459.jpg', '图片', '81.56 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (161, '2024-03-08 11:43:32.556103', '2024-03-08 11:43:32.556103', '1709172270328-202403081143533.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081143533.jpg', '图片', '81.56 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (162, '2024-03-08 11:44:02.489693', '2024-03-08 11:44:02.489693', '1709172270328-202403081144477.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081144477.jpg', '图片', '81.56 KB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (163, '2024-03-08 11:44:54.626236', '2024-03-08 11:44:54.626236', '1709172270328-202403081144616.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081144616.jpg', '图片', '81.56 KB', 1, 'materialsInOut', 28); +INSERT INTO `tool_storage` VALUES (164, '2024-03-08 13:11:26.444515', '2024-03-08 13:11:26.444515', '1709172270328-202403081311434.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081311434.jpg', '图片', '81.56 KB', 1, 'contract', 2); +INSERT INTO `tool_storage` VALUES (165, '2024-03-08 13:11:26.750173', '2024-03-08 13:11:26.750173', '微信图片_20240308085222-202403081311745.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081311745.jpg', '图片', '314.97 KB', 1, 'contract', 2); +INSERT INTO `tool_storage` VALUES (166, '2024-03-08 13:16:11.698799', '2024-03-08 13:16:11.698799', '1709172270328-202403081316681.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081316681.jpg', '图片', '81.56 KB', 1, 'contract', 2); +INSERT INTO `tool_storage` VALUES (167, '2024-03-08 13:16:11.709257', '2024-03-08 13:16:11.709257', '微信图片_20240308085222-202403081316688.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081316688.jpg', '图片', '314.97 KB', 1, 'contract', 2); +INSERT INTO `tool_storage` VALUES (168, '2024-03-08 13:17:00.151497', '2024-03-08 13:17:00.151497', '1709172270328-202403081317138.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081317138.jpg', '图片', '81.56 KB', 1, 'contract', 2); +INSERT INTO `tool_storage` VALUES (169, '2024-03-08 13:17:00.160739', '2024-03-08 13:17:00.160739', '微信图片_20240308085222-202403081317143.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081317143.jpg', '图片', '314.97 KB', 1, 'contract', 2); +INSERT INTO `tool_storage` VALUES (170, '2024-03-11 15:29:12.145015', '2024-03-11 15:29:12.145015', '1709172270328-202403111529135.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403111529135.jpg', '图片', '81.56 KB', 1, 'contract', 7); +INSERT INTO `tool_storage` VALUES (171, '2024-03-11 15:29:12.147324', '2024-03-11 15:29:12.147324', '微信图片_20240308085222-202403111529139.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403111529139.jpg', '图片', '314.97 KB', 1, 'contract', 7); +INSERT INTO `tool_storage` VALUES (172, '2024-03-11 17:10:39.695036', '2024-03-11 17:10:39.695036', '1709172270328-202403111710690.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403111710690.jpg', '图片', '81.56 KB', 1, 'contract', 8); +INSERT INTO `tool_storage` VALUES (173, '2024-03-21 16:08:32.854324', '2024-03-21 16:08:32.854324', 'yanshi1-202403211608556.jpg', 'yanshi1.jpg', 'jpg', '/upload/yanshi1-202403211608556.jpg', '图片', '61.08 KB', 1, 'materialsInOut', 72); +INSERT INTO `tool_storage` VALUES (174, '2024-03-21 17:09:15.653321', '2024-03-21 17:09:15.653321', 'pos-x-202403211709234.jpg', 'pos-x.jpg', 'jpg', '/upload/pos-x-202403211709234.jpg', '图片', '63.04 KB', 1, 'materialsInOut', 71); +INSERT INTO `tool_storage` VALUES (175, '2024-03-21 17:09:32.944092', '2024-03-21 17:09:32.944092', '3d66Model-12494952-files-320-202403211709521.jpg', '3d66Model-12494952-files-320.jpg', 'jpg', '/upload/3d66Model-12494952-files-320-202403211709521.jpg', '图片', '63.66 KB', 1, 'materialsInOut', 70); +INSERT INTO `tool_storage` VALUES (176, '2024-03-21 17:14:06.418390', '2024-03-21 17:14:06.418390', '3d66Model-12494952-files-320-202403211714672.jpg', '3d66Model-12494952-files-320.jpg', 'jpg', '/upload/3d66Model-12494952-files-320-202403211714672.jpg', '图片', '63.66 KB', 1, 'product', 22); +INSERT INTO `tool_storage` VALUES (177, '2024-03-22 17:25:30.260926', '2024-03-22 17:25:30.260926', '3d66Model-12494952-files-320-202403221725405.jpg', '3d66Model-12494952-files-320.jpg', 'jpg', '/upload/3d66Model-12494952-files-320-202403221725405.jpg', '图片', '63.66 KB', 1, 'product', 27); +INSERT INTO `tool_storage` VALUES (178, '2024-03-25 16:06:10.167823', '2024-03-25 16:06:10.167823', '3d66Model-12494952-files-3-202403251606976.jpg', '3d66Model-12494952-files-3.jpg', 'jpg', '/upload/3d66Model-12494952-files-3-202403251606976.jpg', '图片', '98.35 KB', 1, 'materialsInOut', 97); +INSERT INTO `tool_storage` VALUES (179, '2024-03-25 16:27:59.456520', '2024-03-25 16:27:59.456520', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251627221.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251627221.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (180, '2024-03-25 16:28:19.171211', '2024-03-25 16:28:19.171211', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628947.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628947.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (181, '2024-03-25 16:28:24.271093', '2024-03-25 16:28:24.271093', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628046.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628046.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (182, '2024-03-25 16:28:51.337125', '2024-03-25 16:28:51.337125', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628103.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628103.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (183, '2024-03-25 16:29:52.097157', '2024-03-25 16:29:52.097157', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251629862.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251629862.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (184, '2024-03-25 16:29:52.100139', '2024-03-25 16:29:52.100139', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251629866.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251629866.jpg', '图片', '57.89 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (185, '2024-03-25 16:30:12.681895', '2024-03-25 16:30:12.681895', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251630444.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251630444.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (186, '2024-03-25 16:30:12.683375', '2024-03-25 16:30:12.683375', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251630444.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251630444.jpg', '图片', '57.89 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (187, '2024-03-25 16:31:29.716111', '2024-03-25 16:31:29.716111', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251631475.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251631475.jpg', '图片', '57.89 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (188, '2024-03-25 16:31:29.716924', '2024-03-25 16:31:29.716924', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251631476.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251631476.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (189, '2024-03-25 16:32:42.493071', '2024-03-25 16:32:42.493071', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251632252.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251632252.jpg', '图片', '57.89 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (190, '2024-03-25 16:32:42.497067', '2024-03-25 16:32:42.497067', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251632256.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251632256.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (191, '2024-03-25 16:35:27.743107', '2024-03-25 16:35:27.743107', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251635499.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251635499.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (192, '2024-03-25 16:35:27.746570', '2024-03-25 16:35:27.746570', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251635504.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251635504.jpg', '图片', '57.89 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (193, '2024-03-25 16:35:57.996009', '2024-03-25 16:35:57.996009', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251635747.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251635747.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (194, '2024-03-25 16:35:57.997445', '2024-03-25 16:35:57.997445', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251635747.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251635747.jpg', '图片', '57.89 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (195, '2024-03-25 16:37:20.048302', '2024-03-25 16:37:20.048302', '3d66Model-12494952-files-3-202403251637797.jpg', '3d66Model-12494952-files-3.jpg', 'jpg', '/upload/3d66Model-12494952-files-3-202403251637797.jpg', '图片', '98.35 KB', 1, 'materialsInOut', 112); +INSERT INTO `tool_storage` VALUES (196, '2024-03-25 16:37:43.625126', '2024-03-25 16:37:43.625126', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251637373.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251637373.jpg', '图片', '53.94 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (197, '2024-03-25 16:37:43.626911', '2024-03-25 16:37:43.626911', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251637376.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251637376.jpg', '图片', '57.89 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (198, '2024-03-25 16:38:32.095682', '2024-03-25 16:38:32.095682', '3d66Model-12494952-files-3-202403251638843.jpg', '3d66Model-12494952-files-3.jpg', 'jpg', '/upload/3d66Model-12494952-files-3-202403251638843.jpg', '图片', '98.35 KB', 1, 'materialsInOut', 112); +INSERT INTO `tool_storage` VALUES (199, '2024-03-25 16:43:55.437346', '2024-03-25 16:43:55.437346', 'scaled_80d5e223-5520-467f-a9f9-810853de652c6486872489674521978-202403251643174.jpg', 'scaled_80d5e223-5520-467f-a9f9-810853de652c6486872489674521978.jpg', 'jpg', '/upload/scaled_80d5e223-5520-467f-a9f9-810853de652c6486872489674521978-202403251643174.jpg', '图片', '55.62 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (200, '2024-03-25 16:43:55.439011', '2024-03-25 16:43:55.439011', 'scaled_a4227c96-596e-490d-a92f-e0f55cef400a8437526180198455207-202403251643177.jpg', 'scaled_a4227c96-596e-490d-a92f-e0f55cef400a8437526180198455207.jpg', 'jpg', '/upload/scaled_a4227c96-596e-490d-a92f-e0f55cef400a8437526180198455207-202403251643177.jpg', '图片', '53.7 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (201, '2024-03-26 09:11:43.924051', '2024-03-26 09:11:43.924051', 'scaled_1a637967-322f-4077-b571-67318831e47a5388336458357771115-202403260911463.jpg', 'scaled_1a637967-322f-4077-b571-67318831e47a5388336458357771115.jpg', 'jpg', '/upload/scaled_1a637967-322f-4077-b571-67318831e47a5388336458357771115-202403260911463.jpg', '图片', '49.49 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (202, '2024-03-26 09:21:05.571986', '2024-03-26 09:21:05.571986', 'scaled_dc17a892-6bbc-4749-8077-1be8ce3d2fa53123598047088265103-202403260921672.jpg', 'scaled_dc17a892-6bbc-4749-8077-1be8ce3d2fa53123598047088265103.jpg', 'jpg', '/upload/scaled_dc17a892-6bbc-4749-8077-1be8ce3d2fa53123598047088265103-202403260921672.jpg', '图片', '51.63 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (203, '2024-03-26 11:34:25.342170', '2024-03-26 11:34:25.342170', 'scaled_d681979b-ca63-453c-8809-e6997c5af7005514768979314679098-202403261134166.jpg', 'scaled_d681979b-ca63-453c-8809-e6997c5af7005514768979314679098.jpg', 'jpg', '/upload/scaled_d681979b-ca63-453c-8809-e6997c5af7005514768979314679098-202403261134166.jpg', '图片', '53.62 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (204, '2024-03-26 13:56:08.343969', '2024-03-26 13:56:08.343969', 'scaled_d2d451ee-173d-462a-952c-352a3e8fd0c37407975546900753888-202403261356811.jpg', 'scaled_d2d451ee-173d-462a-952c-352a3e8fd0c37407975546900753888.jpg', 'jpg', '/upload/scaled_d2d451ee-173d-462a-952c-352a3e8fd0c37407975546900753888-202403261356811.jpg', '图片', '74.12 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (205, '2024-03-26 13:56:08.343974', '2024-03-26 13:56:08.343974', 'scaled_2ea135c3-814d-4036-9937-09c88f4ead9f6687203029443796549-202403261356810.jpg', 'scaled_2ea135c3-814d-4036-9937-09c88f4ead9f6687203029443796549.jpg', 'jpg', '/upload/scaled_2ea135c3-814d-4036-9937-09c88f4ead9f6687203029443796549-202403261356810.jpg', '图片', '89.27 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (206, '2024-03-26 13:56:08.366744', '2024-03-26 13:56:08.366744', 'scaled_599c7a69-d994-460c-b79a-3cefd482e7434061251540207554146-202403261356811.jpg', 'scaled_599c7a69-d994-460c-b79a-3cefd482e7434061251540207554146.jpg', 'jpg', '/upload/scaled_599c7a69-d994-460c-b79a-3cefd482e7434061251540207554146-202403261356811.jpg', '图片', '89.49 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (207, '2024-03-26 13:56:57.545563', '2024-03-26 13:56:57.545563', 'scaled_f7a13c2e-134c-4de4-90ff-8a4680fd652f1553028774544341980-202403261357015.jpg', 'scaled_f7a13c2e-134c-4de4-90ff-8a4680fd652f1553028774544341980.jpg', 'jpg', '/upload/scaled_f7a13c2e-134c-4de4-90ff-8a4680fd652f1553028774544341980-202403261357015.jpg', '图片', '78.58 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (208, '2024-03-26 13:56:57.546780', '2024-03-26 13:56:57.546780', 'scaled_7118a807-43b8-44b8-b86a-2823da96b099726991530082479093-202403261357018.jpg', 'scaled_7118a807-43b8-44b8-b86a-2823da96b099726991530082479093.jpg', 'jpg', '/upload/scaled_7118a807-43b8-44b8-b86a-2823da96b099726991530082479093-202403261357018.jpg', '图片', '59.26 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (209, '2024-03-26 13:56:57.546783', '2024-03-26 13:56:57.546783', 'scaled_92d71763-5574-46ad-9176-330cabf18f268972942500955895967-202403261357018.jpg', 'scaled_92d71763-5574-46ad-9176-330cabf18f268972942500955895967.jpg', 'jpg', '/upload/scaled_92d71763-5574-46ad-9176-330cabf18f268972942500955895967-202403261357018.jpg', '图片', '67.25 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (210, '2024-03-26 13:56:57.567969', '2024-03-26 13:56:57.567969', 'scaled_7a9264a9-fbee-4f25-a0af-4672edf305626973828255576468559-202403261357018.jpg', 'scaled_7a9264a9-fbee-4f25-a0af-4672edf305626973828255576468559.jpg', 'jpg', '/upload/scaled_7a9264a9-fbee-4f25-a0af-4672edf305626973828255576468559-202403261357018.jpg', '图片', '85.57 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (211, '2024-03-26 13:56:57.570940', '2024-03-26 13:56:57.570940', 'scaled_3df02010-2270-4dcc-bb56-371de26897037809041329322071714-202403261357021.jpg', 'scaled_3df02010-2270-4dcc-bb56-371de26897037809041329322071714.jpg', 'jpg', '/upload/scaled_3df02010-2270-4dcc-bb56-371de26897037809041329322071714-202403261357021.jpg', '图片', '99.8 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (212, '2024-03-26 14:27:03.612654', '2024-03-26 14:27:03.612654', 'scaled_643dbbf9-d698-409d-b564-785ada1cf8393161751276012656173-202403261427036.jpg', 'scaled_643dbbf9-d698-409d-b564-785ada1cf8393161751276012656173.jpg', 'jpg', '/upload/scaled_643dbbf9-d698-409d-b564-785ada1cf8393161751276012656173-202403261427036.jpg', '图片', '475.95 KB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (213, '2024-03-26 14:27:51.534329', '2024-03-26 14:27:51.534329', 'scaled_87a5fb79-970e-40b8-8036-06b847ff5ed77569061097438195214-202403261427941.jpg', 'scaled_87a5fb79-970e-40b8-8036-06b847ff5ed77569061097438195214.jpg', 'jpg', '/upload/scaled_87a5fb79-970e-40b8-8036-06b847ff5ed77569061097438195214-202403261427941.jpg', '图片', '1.68 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (214, '2024-03-26 14:48:50.771816', '2024-03-26 14:48:50.771816', 'scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510-202403261448367.jpg', 'scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510.jpg', 'jpg', '/upload/scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510-202403261448367.jpg', '图片', '1.17 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (215, '2024-03-26 14:56:59.991816', '2024-03-26 14:56:59.991816', 'scaled_072d9fe8-fc60-4626-8a3b-06639e1a11102576129031638219701-202403261457570.jpg', 'scaled_072d9fe8-fc60-4626-8a3b-06639e1a11102576129031638219701.jpg', 'jpg', '/upload/scaled_072d9fe8-fc60-4626-8a3b-06639e1a11102576129031638219701-202403261457570.jpg', '图片', '1.79 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (216, '2024-03-26 14:57:00.859728', '2024-03-26 14:57:00.859728', 'scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510-202403261457443.jpg', 'scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510.jpg', 'jpg', '/upload/scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510-202403261457443.jpg', '图片', '1.17 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (217, '2024-03-26 15:00:10.928209', '2024-03-26 15:00:10.928209', 'scaled_cff03a18-0ec1-4fde-b940-01de3acc6d061416191473797538812-202403261500491.jpg', 'scaled_cff03a18-0ec1-4fde-b940-01de3acc6d061416191473797538812.jpg', 'jpg', '/upload/scaled_cff03a18-0ec1-4fde-b940-01de3acc6d061416191473797538812-202403261500491.jpg', '图片', '1.16 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (218, '2024-03-26 15:15:11.475534', '2024-03-26 15:15:11.475534', 'scaled_af448995-1a4c-4196-a11e-6dc95cd91ba05659735836510102013-202403261515014.jpg', 'scaled_af448995-1a4c-4196-a11e-6dc95cd91ba05659735836510102013.jpg', 'jpg', '/upload/scaled_af448995-1a4c-4196-a11e-6dc95cd91ba05659735836510102013-202403261515014.jpg', '图片', '2.26 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (219, '2024-03-26 15:16:10.017398', '2024-03-26 15:16:10.017398', 'scaled_4bc91ea6-1949-49d8-8d51-1b8573bf108e4784062601379129071-202403261516559.jpg', 'scaled_4bc91ea6-1949-49d8-8d51-1b8573bf108e4784062601379129071.jpg', 'jpg', '/upload/scaled_4bc91ea6-1949-49d8-8d51-1b8573bf108e4784062601379129071-202403261516559.jpg', '图片', '1.3 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (220, '2024-03-26 15:16:51.648859', '2024-03-26 15:16:51.648859', 'scaled_314eb685-b25d-4834-841f-e23751cd2a5f5192837258445318655-202403261516191.jpg', 'scaled_314eb685-b25d-4834-841f-e23751cd2a5f5192837258445318655.jpg', 'jpg', '/upload/scaled_314eb685-b25d-4834-841f-e23751cd2a5f5192837258445318655-202403261516191.jpg', '图片', '1.85 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (221, '2024-03-27 10:04:51.360595', '2024-03-27 10:04:51.360595', '3d66Model-12494952-files-3-202403271004438.jpg', '3d66Model-12494952-files-3.jpg', 'jpg', '/upload/3d66Model-12494952-files-3-202403271004438.jpg', '图片', '98.35 KB', 1, 'materialsInOut', 166); +INSERT INTO `tool_storage` VALUES (222, '2024-03-27 10:07:21.843030', '2024-03-27 10:07:21.843030', 'scaled_6926ce62-b333-43f0-b9d5-fa1cff94b0026152296259250934531-202403271007912.jpg', 'scaled_6926ce62-b333-43f0-b9d5-fa1cff94b0026152296259250934531.jpg', 'jpg', '/upload/scaled_6926ce62-b333-43f0-b9d5-fa1cff94b0026152296259250934531-202403271007912.jpg', '图片', '1.18 MB', 1, '', NULL); +INSERT INTO `tool_storage` VALUES (223, '2024-03-27 10:07:21.860260', '2024-03-27 10:07:21.860260', 'scaled_0de1e7f6-cca7-4172-9ad6-5288212ea3de160786817006652480-202403271007930.jpg', 'scaled_0de1e7f6-cca7-4172-9ad6-5288212ea3de160786817006652480.jpg', 'jpg', '/upload/scaled_0de1e7f6-cca7-4172-9ad6-5288212ea3de160786817006652480-202403271007930.jpg', '图片', '1.17 MB', 1, '', NULL); -- ---------------------------- -- Table structure for user_access_tokens @@ -1056,40 +1245,80 @@ CREATE TABLE `user_access_tokens` ( -- ---------------------------- -- Records of user_access_tokens -- ---------------------------- -INSERT INTO `user_access_tokens` VALUES ('07276357-6286-478c-8058-d249f4ca4dde', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI4MzIwNH0.CiG2E-GnJlDR_6YJMDkxTJOa-dS29HV5rWNEszn-jeA', '2024-03-02 16:53:25', '2024-03-01 16:53:24.786130', 1); -INSERT INTO `user_access_tokens` VALUES ('09cf7b0a-62e0-45ee-96b0-e31de32361e0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1MDkxNTd9.0gtKlcxrxQ-TarEai2lsBnfMc852ZDYHeSjjhpo5Fn8', '2024-02-11 04:05:58', '2024-02-10 04:05:57.696509', 1); -INSERT INTO `user_access_tokens` VALUES ('0b95a340-d9eb-4c3a-bd70-2432d3108984', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyMzM4Mn0.IA8TwkjfDwmv9470mgVswU775HCRdGo7zCoPblRNgRU', '2024-03-05 11:36:23', '2024-03-04 11:36:22.819835', 1); -INSERT INTO `user_access_tokens` VALUES ('17593c96-3779-4f03-8dbc-cb21ac9f7981', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwiaWF0IjoxNzA5MjYxNTUzfQ.hDAHgYrDfsckG6bSyfGa_QLRmo1tvbJEkYgQUfwqZbs', '2024-03-02 10:52:33', '2024-03-01 10:52:33.059989', 1); -INSERT INTO `user_access_tokens` VALUES ('19e593f4-4a03-46e0-9b4e-a009af15790a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTcxNjY3NX0.a0xNnKWIueWIxu5SXAawpLq6Bl4cTD5O6bqLknDU05U', '2024-03-07 17:17:55', '2024-03-06 17:17:55.105520', 1); -INSERT INTO `user_access_tokens` VALUES ('1bb9d14e-823c-4f9f-8562-cbc2fd2be318', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTI1Mn0.-OgIPgXJtFC5ioAu_xpwCY6LRcAyTykXR_1pVrrSCN4', '2024-03-05 13:14:12', '2024-03-04 13:14:12.192684', 1); -INSERT INTO `user_access_tokens` VALUES ('31399cec-e506-4194-a9cc-47f7a1a953a9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTIxNzh9.Da3j0Ht_zAsPldEYJ18RFYZqTYNhgPxSefCEuXd-4T8', '2024-02-29 11:49:38', '2024-02-28 11:49:38.289991', 1); -INSERT INTO `user_access_tokens` VALUES ('31875da0-8b93-4ca2-bdb8-ab40c1c059bd', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTg0OX0.g8Rr9eTlEZaghqdd_W5lSyxVV-ZMMnMMkyF2iNrUXpY', '2024-03-02 15:57:29', '2024-03-01 15:57:29.074445', 1); -INSERT INTO `user_access_tokens` VALUES ('35fd4bb6-2608-4203-ac2c-9550034993ea', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3ODEwOTh9.WmBVjKJkJs1lve9PrYfF8pYWF-qeg8KQu8IeSDSF3Cw', '2024-03-08 11:11:38', '2024-03-07 11:11:38.199744', 1); -INSERT INTO `user_access_tokens` VALUES ('367aaa79-1130-4511-87c4-25aacdf27d3d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgzOH0.5_K7t6bAuqIO9A8NvgrWACEJUVKduu--1q-rgmAB4Vw', '2024-03-02 15:57:18', '2024-03-01 15:57:18.314066', 1); -INSERT INTO `user_access_tokens` VALUES ('3f7dffae-db1f-47dc-9677-5c956c3de39e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczMTEzMDJ9.D5Qpht1RquKor8WtgfGAcCp8LwG7z3FZhIwbyQzhDmE', '2024-02-08 21:08:22', '2024-02-07 21:08:22.130066', 1); -INSERT INTO `user_access_tokens` VALUES ('40342c3e-194c-42eb-adee-189389839195', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxNjF9.tRQOxhB-01Pcut5MXm4L5D1OrbMJfS4LfUys0XB4kWs', '2024-02-09 14:02:41', '2024-02-08 14:02:41.081164', 1); -INSERT INTO `user_access_tokens` VALUES ('4541a9e8-a508-4b92-8ae3-c3744ddcad92', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUzODE3NH0.Gb_IUruHmd25Wk1ZFHUtB8G2w1bg7i-ivCa11fT8JTw', '2024-03-05 15:42:54', '2024-03-04 15:42:54.286838', 1); -INSERT INTO `user_access_tokens` VALUES ('53a08eaa-74fb-45cb-9e6c-514b67346e06', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTU0NDM0Mn0.rcVSgy1s7zNWtB_Q93zdZzL633UFbeH_RWEY_kelVHQ', '2024-03-05 17:25:42', '2024-03-04 17:25:42.096331', 1); -INSERT INTO `user_access_tokens` VALUES ('6ea87ca4-89e4-419b-9eb3-7bef4ada53ca', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTM0Mn0.kLsAfIgUDqNEogqBZWaDt7iUYb4gE4dznjTKoUzSKj0', '2024-03-05 13:15:42', '2024-03-04 13:15:42.347589', 1); -INSERT INTO `user_access_tokens` VALUES ('6f3990bd-d093-4cc4-9f9e-cba45260ec42', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkxOTUxMTR9.XvbjbvGNz7pYm-Yol8X3uTDwYkn35i-Fu_LsmMU4PdM', '2024-03-01 16:25:15', '2024-02-29 16:25:14.725434', 1); -INSERT INTO `user_access_tokens` VALUES ('78519125-77c1-4768-9644-d8fa639f3ff0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3NzQwNzN9._ZJd1B4EybHlO3W9p98uZ0NJDm6fJur9ifLwEIwywI0', '2024-03-08 09:14:34', '2024-03-07 09:14:33.765186', 1); -INSERT INTO `user_access_tokens` VALUES ('7ebc6a49-421c-4437-a7a8-bc8aaeb96b26', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkxOTUxNjl9.oiSa_amY2bX-MaAxL8vs_EmSEyhyBLVJLn9uPf_HgY4', '2024-03-01 16:26:09', '2024-02-29 16:26:09.075302', 1); -INSERT INTO `user_access_tokens` VALUES ('8e26aa27-fa67-4b67-8705-019be3e8f1f8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTY4OTh9.LemwyefdnGCm2ZM2KE3RN2-d6n-Xj2TAujs7iBWaYs8', '2024-02-29 13:08:18', '2024-02-28 13:08:18.482193', 1); -INSERT INTO `user_access_tokens` VALUES ('8f7fcaed-c4ed-4cf0-840f-37bcd3a39a59', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTg1M30.woUKWiIQ96woTIFDSdPWkQHwypfTc9T6EqFDbwN56mE', '2024-03-02 15:57:34', '2024-03-01 15:57:33.866391', 1); -INSERT INTO `user_access_tokens` VALUES ('9d1ba8e9-dffc-4b15-b21f-4a90f196e39c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDc1Mjc5MDV9.7LeiS3LBBdiAc7YrULWpmnI1oNSvR79K-qjEOlBYOnI', '2024-02-11 09:18:26', '2024-02-10 09:18:25.656695', 1); -INSERT INTO `user_access_tokens` VALUES ('ab62dfc5-46d8-4dad-9055-bb2803d1451c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTY3NjF9.9C_cPmmiW3Sh5OrPNSnw3h1IoqqLa3GlEYvgNFvv5Rg', '2024-02-29 13:06:01', '2024-02-28 13:06:01.416655', 1); -INSERT INTO `user_access_tokens` VALUES ('af43fb31-3759-43d5-b13e-e558bccdca58', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTY4NTkwNH0.eKKbI4Wi7WKSqE-gDt5isFihQze-LOysFntMzyaP5DY', '2024-03-07 08:45:05', '2024-03-06 08:45:04.541645', 1); -INSERT INTO `user_access_tokens` VALUES ('b6053fd5-7c7c-453e-a8ea-6de17b30304a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTcwNTF9.LyR5igCxJhcSnB-pXCc7wJNJ2H1ERdu8LO6d94cMz2c', '2024-02-29 13:10:51', '2024-02-28 13:10:51.331328', 1); -INSERT INTO `user_access_tokens` VALUES ('c24acee3-7bbd-4e7a-b8f6-a80be1db0b33', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgyNH0.6YmdwppJX1DV9-jdFiHLWRH9X_WbeGYbZtaRIizLRgM', '2024-03-02 15:57:05', '2024-03-01 15:57:04.739116', 1); -INSERT INTO `user_access_tokens` VALUES ('cbfcc75c-d357-47fd-afbf-db0629885eb6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTYyMDQ0OH0.LQXYkRJVyDN7YCVRSsS7osGMzC31ik1XD36-SWDbghI', '2024-03-06 14:34:08', '2024-03-05 14:34:08.374481', 1); -INSERT INTO `user_access_tokens` VALUES ('cdc3e837-0953-42e7-8296-d27cfbc41353', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTEzNn0.SUycnmUW-ZDjLoBP4DMB_si11dUVHfXmCsG0tLMVBg0', '2024-03-02 15:45:37', '2024-03-01 15:45:36.880024', 1); -INSERT INTO `user_access_tokens` VALUES ('d32e25cc-b00c-43cf-94b0-0f61c3314f4c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI2MTU4OX0.-IM-L0Dt7Jc5xaEU5Q93z5vhAQTpbD9ngS_uhzZ0haI', '2024-03-02 10:53:10', '2024-03-01 10:53:09.606838', 1); -INSERT INTO `user_access_tokens` VALUES ('d9044d67-9a95-4bd5-8103-f6ddc9701f83', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTUyOTM2NH0.gEQmOm9E7Adpti0pux6KjtopqZpNy62fZzgWf0Ps1wM', '2024-03-05 13:16:05', '2024-03-04 13:16:04.665874', 1); -INSERT INTO `user_access_tokens` VALUES ('e042f0c3-9a35-40cf-83a3-918f617f2ac7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDk3ODMxNzR9.6jKaSK15fdN8XkToaimiWbqJ-vhiaQr2QkpAc5XvFuA', '2024-03-08 11:46:15', '2024-03-07 11:46:14.586995', 1); -INSERT INTO `user_access_tokens` VALUES ('e754d168-106a-43f5-bbf8-80f1b187cb1e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTk2MzN9.IQ_1NQHKvQ3Cso7cXYQ32xK3qhaOUUF6jtoQ87cN6b0', '2024-02-29 13:53:53', '2024-02-28 13:53:53.168581', 1); -INSERT INTO `user_access_tokens` VALUES ('ebc5fbbb-b29e-4a17-acd6-002ca7936099', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiLCJ0ZXN0Il0sImlhdCI6MTcwOTI3OTgzMX0.d29Hn1Ub-YJBv9Xw9Olo7kBNB9ugxcs1P1hrtJEQpJo', '2024-03-02 15:57:11', '2024-03-01 15:57:11.247365', 1); -INSERT INTO `user_access_tokens` VALUES ('edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDczNzIxMjd9.VRuJHGca2IPrdfTyW09wfhht4x8JX207pKG-0aZyF60', '2024-02-09 14:02:07', '2024-02-08 14:02:07.390658', 1); -INSERT INTO `user_access_tokens` VALUES ('eff3fd41-7307-4027-9f8c-2e3b0fd35051', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MDkwOTk1OTd9.aLKmV2GGF6pkkolLnJzuq0PXAllSbGIZzJrUR4wlNag', '2024-02-29 13:53:17', '2024-02-28 13:53:17.473705', 1); +INSERT INTO `user_access_tokens` VALUES ('00866088-2523-44ab-b972-1edb92b5318a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTg4NTF9.lKuI2g6Zp8Xn_j9TkOpd92OZ3HEAVrXqsGFeBrqjeMg', '2024-03-28 13:54:12', '2024-03-27 13:54:12.187932', 1); +INSERT INTO `user_access_tokens` VALUES ('0233ea11-41af-453a-b8ed-b9731b252d05', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MzcxNTl9.3ZKGerjK3ohCnmZzWIi7DmpvH3DIFX6MGS2VESWnuAQ', '2024-03-20 16:32:40', '2024-03-19 16:32:41.320136', 1); +INSERT INTO `user_access_tokens` VALUES ('0a058377-1c0e-48d6-b143-21d5195ac859', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA3NTE2MjN9.0XL_QKSrMlqkMCMEnon4Yju6Iz44b62CAEWZ9Sd9PvI', '2024-03-19 16:47:04', '2024-03-18 16:47:04.547719', 1); +INSERT INTO `user_access_tokens` VALUES ('0a52047b-397f-4ff2-9bfc-9940e17b2fcb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDgzNzd9.xwBg4Y4QJz2T2t82G2y1EbDTOWHH18AkzvnphAih6vA', '2024-03-28 10:59:38', '2024-03-27 10:59:38.011777', 1); +INSERT INTO `user_access_tokens` VALUES ('0baa35e7-11a8-4280-be13-b12c9416283b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0Mzk2NTd9.u-QKP2oMbKGcItCznOakxzBPg67JJLZRiyWHBDxareM', '2024-03-27 15:54:18', '2024-03-26 15:54:15.402975', 1); +INSERT INTO `user_access_tokens` VALUES ('0bee723d-c635-4396-9656-8165ac9594b0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5OTEyNDR9.tVL8dDnSE5ZqgZpZ1a90cK5JqGN7Bh8pkSZj7OOvdaM', '2024-03-22 11:20:44', '2024-03-21 11:20:45.958604', 1); +INSERT INTO `user_access_tokens` VALUES ('0f9677c0-6a16-4b6b-ac83-2ee669fb7ced', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE2NjN9.vSDaVKDcN5HOGNX5d_rJC4SOo2d3P-oLUttQlkBrm0U', '2024-03-20 09:27:44', '2024-03-19 09:27:47.503333', 1); +INSERT INTO `user_access_tokens` VALUES ('191ada91-e54d-46c5-a20d-448022f5da60', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MzQ4MTB9.5sNHITFJWsfOUMrl2YL6gEnPp_9oo7Ri9-Uumzs_sMo', '2024-03-20 15:53:31', '2024-03-19 15:53:32.249226', 1); +INSERT INTO `user_access_tokens` VALUES ('1added11-2ae5-4e39-bfd1-3972d3d95ad6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTEzNDN9.onuHFlLcuUPtCmNJu30DgZ8KctDtHKpWhp7RWrATXVY', '2024-03-20 09:22:23', '2024-03-19 09:22:27.221110', 1); +INSERT INTO `user_access_tokens` VALUES ('1ca6754c-710d-4a23-8b17-79fe340e1e65', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5MTI0NDR9._218cyUmUipNcFenAz_Hn83D9hwmTQkAaG0ExC6Nar4', '2024-03-21 13:27:24', '2024-03-20 13:27:27.491644', 1); +INSERT INTO `user_access_tokens` VALUES ('202c3f1c-6068-427f-8c75-50b24f93a5c8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAxNDgxMDR9.n3nf3kBqLhSC6hEBcbTVLruzRGGWnab7XVp9irH35yc', '2024-03-12 17:08:24', '2024-03-11 17:08:24.296728', 1); +INSERT INTO `user_access_tokens` VALUES ('29c99bed-2ad0-455d-9085-9e59192e2426', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTEyMTZ9.qDQNz09cAjWStacu6ORM8Kfa2-1uT23GLdqIs1FKjas', '2024-03-20 09:20:16', '2024-03-19 09:20:20.305036', 1); +INSERT INTO `user_access_tokens` VALUES ('2a5358f2-a465-4241-834f-271f72fbf947', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDUwNzB9.-338fcBRpfrto1EubzOAaBWoAYYHAsKd5uMb3qi4P2Y', '2024-03-28 10:04:30', '2024-03-27 10:04:30.009272', 1); +INSERT INTO `user_access_tokens` VALUES ('2a71c8ce-d4d4-47c4-b004-928f5056676b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDM5MDZ9.iYzKUMMmvoYV_ggaTdyzBnq7AJ0zIzKoueEkQVtpM5U', '2024-03-28 09:45:07', '2024-03-27 09:45:06.513597', 1); +INSERT INTO `user_access_tokens` VALUES ('2af24ef8-886d-4510-b8be-816023f2e517', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0MzgyOTR9.-ZI7lT4i1y8DOXRvLIQp33pzRTowOXSmFB4rrh1Y4dc', '2024-03-27 15:31:34', '2024-03-26 15:31:31.651204', 1); +INSERT INTO `user_access_tokens` VALUES ('31cc91fe-e7b0-4d74-9ea2-f5f12063df86', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMTExNzl9.lCc6BsEz_kJAmVYYBKQJkVjimaDBaKns21FPHiGfECs', '2024-03-22 16:53:00', '2024-03-21 16:53:02.299107', 1); +INSERT INTO `user_access_tokens` VALUES ('34463fa6-75ab-4fc2-ae16-b2e537c8b36d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MzgwMjN9.aqnbHvymunC_kVJhftd7t6CiHQ0fEOJ_EcEEIdawEaY', '2024-03-20 16:47:03', '2024-03-19 16:47:04.883026', 1); +INSERT INTO `user_access_tokens` VALUES ('359f13d0-5507-4de6-8577-8c315892fa00', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTg3NTR9.0UaiKzzZ33lI-z_8J88B_xCBS8VaT1P0ixZ4kqKRy8w', '2024-03-20 11:25:54', '2024-03-19 11:25:54.977965', 1); +INSERT INTO `user_access_tokens` VALUES ('366b9989-b264-4d58-bd6a-4f8da8e0250d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjg4OTF9.4TFOAi6JO5b0K5o3as2q_oFz9TlzvQHtaPbzIdtSz2k', '2024-03-23 08:54:52', '2024-03-22 08:54:51.422041', 1); +INSERT INTO `user_access_tokens` VALUES ('3e0918fa-04c2-45df-858e-34d725c21a25', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMDE2MzF9.r1a4CTC4EmLEdQQ1-kzEkWySW5HaPqISQLafTfLMzSc', '2024-03-22 14:13:52', '2024-03-21 14:13:54.021153', 1); +INSERT INTO `user_access_tokens` VALUES ('3e6845c8-9cc3-492e-bcd7-9636b00d5631', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTIzODB9.F7U7AhNWV1gLdqpM_yHIU6qYadNb0AJap0SScckn9vw', '2024-03-20 09:39:40', '2024-03-19 09:39:44.297388', 1); +INSERT INTO `user_access_tokens` VALUES ('494de6e6-9ac1-47f2-b649-afac34468a64', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEzMzM3Mjh9.qjipswowmSD6jkKJO7UZhU7yTLIai4cXaCQIgq6Lj30', '2024-03-26 10:28:48', '2024-03-25 10:28:47.927310', 1); +INSERT INTO `user_access_tokens` VALUES ('5478eda6-191c-4643-9923-a1c1d83a25ee', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE2MDN9.Xh9jupN8Zq7Ubn23ZnjOd_ie-Gt9XLBvVVD2p4Hn-UQ', '2024-03-20 09:26:44', '2024-03-19 09:26:47.658484', 1); +INSERT INTO `user_access_tokens` VALUES ('585cba42-9d53-4003-9194-20ccb52080f1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MzE2MDh9.ee_VZphsUWW48bA0zsZtdgTdEMN8aIDu_3syty3xuZE', '2024-03-20 15:00:09', '2024-03-19 15:00:10.107179', 1); +INSERT INTO `user_access_tokens` VALUES ('58fd6fa7-fd30-4a3e-bc79-53d1575745c2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjg0Njl9.sefSuM1C_FgjcnLUSgrlcFAMEQrtSybPBu_hU3lePlo', '2024-03-23 08:47:50', '2024-03-22 08:47:49.546874', 1); +INSERT INTO `user_access_tokens` VALUES ('591e086d-96c4-4cab-bb9f-fabe6ee27d43', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTIzNDN9.J1gHFnd_56wTjz2SKw-LbeiFngJTNRo06B1ydUc4yHE', '2024-03-20 09:39:03', '2024-03-19 09:39:07.359019', 1); +INSERT INTO `user_access_tokens` VALUES ('59b32e05-ed47-4634-adf2-86076aac9274', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAyMDUzNTJ9.fDrd_FJd9fcbw6APV3BKOu3Lfkhi_5C7N36OnQTcmK8', '2024-03-13 09:02:33', '2024-03-12 09:02:32.681075', 1); +INSERT INTO `user_access_tokens` VALUES ('5bb267d3-8f88-4a1c-90cc-b36c5bf156fa', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNzM5MDZ9.SmlswCpWEJERkfIKSZr-geVEUAvG81X1mG3tki0olqU', '2024-03-23 10:18:26', '2024-03-22 10:18:26.249237', 1); +INSERT INTO `user_access_tokens` VALUES ('63e12288-1b49-4393-a07c-158189f687c7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE3MjN9.hE4dtpr1V2EQ5wGnqPIBsKu6-UROlpDSyBR6o--PLFw', '2024-03-20 09:28:44', '2024-03-19 09:28:47.957727', 1); +INSERT INTO `user_access_tokens` VALUES ('64dd9302-c031-49f6-8eb6-bdb6e079fd2a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDgzNjN9.Patr4gBYADBgtqO418I8VBk8apQIx8yRy3wZC0vqAEA', '2024-03-28 10:59:23', '2024-03-27 10:59:23.326845', 1); +INSERT INTO `user_access_tokens` VALUES ('66a3b775-98e7-4142-be24-415206c6973b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMDE5MjF9.dyIhc08fJqn4wgdEx1r-Tdfl-e6lOwN4Dpw8o4eq6RA', '2024-03-22 14:18:41', '2024-03-21 14:18:43.376336', 1); +INSERT INTO `user_access_tokens` VALUES ('692a1a4d-08fc-490e-97d2-f9997d6e734a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE2MDIzNzR9.QjRleFqx2KVuvhLwBOlRnzsjFS5smKZfw7lqoAv5hwg', '2024-03-29 13:06:15', '2024-03-28 13:06:16.370572', 1); +INSERT INTO `user_access_tokens` VALUES ('6c22c272-e33f-4fa4-a331-17b08293c97c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAxNDY1MDd9.eOQd3_5vhaIWY9GkICA5txh-IQC_hjCj1Hlxoxptfsw', '2024-03-12 16:41:48', '2024-03-11 16:41:47.547701', 1); +INSERT INTO `user_access_tokens` VALUES ('6e81f813-8166-434a-8b01-a3793bc9ba78', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTg2MDV9.Ime_F6N44mbpR0jSUjpV2ABui0vwjTi-ixfshGKaN30', '2024-03-28 13:50:06', '2024-03-27 13:50:06.302482', 1); +INSERT INTO `user_access_tokens` VALUES ('760fa21e-b944-4138-9a28-59ca4c41cc53', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTAwMTV9.H58vmmiqL1a6aqMSzmCOiMaU-SpE03SM4B6Y9fY5dhM', '2024-03-28 11:26:55', '2024-03-27 11:26:55.112351', 1); +INSERT INTO `user_access_tokens` VALUES ('7c7a14dc-5cc7-4fc9-922f-745b916a2e8b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAyMjQwNjd9.cjbEoVNMEGmORms-_qYdxOHlR4mW3HhpjGBQ7xMJ8hA', '2024-03-13 14:14:27', '2024-03-12 14:14:27.137231', 1); +INSERT INTO `user_access_tokens` VALUES ('801e4049-e49a-474c-9622-603e191c9767', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNzIxOTB9.f-mhCiL8RJIWLtfoMrGfsmDlzpMW99cuA1O37ER8aIw', '2024-03-23 09:49:50', '2024-03-22 09:49:50.132778', 1); +INSERT INTO `user_access_tokens` VALUES ('835e7377-9633-444e-9522-ec6df447ff79', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MjY3ODh9.dU8jiXPKYrXLad20GTLykqfX-n5Kf4Be1WEULAuWruk', '2024-03-20 13:39:49', '2024-03-19 13:39:49.803275', 1); +INSERT INTO `user_access_tokens` VALUES ('85ca2577-5bfe-42f2-b1a5-f2342ab0b42a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0Mzk2ODd9.x97vPdg1jJqN_8am7y8-6YpK_kz3-c9BR9bs3LHi-KY', '2024-03-27 15:54:48', '2024-03-26 15:54:45.087331', 1); +INSERT INTO `user_access_tokens` VALUES ('8d765f70-9ea8-4ca9-b072-b20be8fa6a77', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAxNDU1MzV9.NpEHySU1T6i7ATJnYr0ylma3ndEt3rIuDtca9s2e4fo', '2024-03-12 16:25:35', '2024-03-11 16:25:35.139483', 1); +INSERT INTO `user_access_tokens` VALUES ('9096c688-07d7-4709-a431-1bece82f3b93', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTAxNjV9.jtMsYwjq-0cceX-sHWDIb-i7W8xSQTxu_-aneefje94', '2024-03-20 09:02:45', '2024-03-19 09:02:49.121077', 1); +INSERT INTO `user_access_tokens` VALUES ('914b16d0-4508-462e-be5a-44d374c4fd61', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5OTAwNTR9.IpNJRRFAUKZjMuL4FgYHfviAwVdzrElh4nZaflvzY8g', '2024-03-22 11:00:55', '2024-03-21 11:00:56.647606', 1); +INSERT INTO `user_access_tokens` VALUES ('95cc9059-6e9b-4354-84ed-29fe0807fac7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4NDA2MjV9.As1-SCnVDUOZNaohx-dezHJr3x44MZp2RkTB0BXm9YA', '2024-03-20 17:30:26', '2024-03-19 17:30:27.477349', 1); +INSERT INTO `user_access_tokens` VALUES ('9b8a542b-f702-4fa9-87ce-ca87d3063dac', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTg2MTh9.6TU8IYG03jtoA9m5fak3nJfv2FuZQpWFtr1Ghsl8Fdw', '2024-03-28 13:50:18', '2024-03-27 13:50:18.556242', 1); +INSERT INTO `user_access_tokens` VALUES ('a2822d05-b23a-4df0-a048-539fa79593c6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDg3MzJ9.6V07EvTyO9X2eTR-Y_MtuKcp6m0A9Mb4ZiprtagLw28', '2024-03-28 11:05:33', '2024-03-27 11:05:32.631459', 1); +INSERT INTO `user_access_tokens` VALUES ('a4925b1e-ddf5-4382-a643-b45d1bf8199c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE2NzJ9.u1p4PPH6WerSOVw3oVf2UpVLxCGDGjEBxQ8uTHrTZsA', '2024-03-20 09:27:52', '2024-03-19 09:27:56.058970', 1); +INSERT INTO `user_access_tokens` VALUES ('a8caae33-85eb-45f7-afc6-1e4bd187599c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTAzMDV9.Zvbw5lWlok6Hr8R1MAit1J1ZtDU1tKPlaHvQku8lyGA', '2024-03-28 11:31:45', '2024-03-27 11:31:45.293488', 1); +INSERT INTO `user_access_tokens` VALUES ('aa08e3d0-b1ab-410f-8504-0ae8e2504864', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTI0Mjl9.WFKUbUGTJmkdNG3AoKChxp632unDVOvBYknJNVvKRtw', '2024-03-20 09:40:30', '2024-03-19 09:40:33.978807', 1); +INSERT INTO `user_access_tokens` VALUES ('ab1e415f-d811-47ea-9226-86684b71060f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAyMDM1NTV9.-MjD4XNqv-CHWKUJTE82rfFvkfl8vJG8I8SZ1LU5Qo0', '2024-03-13 08:32:35', '2024-03-12 08:32:35.316282', 1); +INSERT INTO `user_access_tokens` VALUES ('b063d499-6fd3-48ea-b75d-95d3bb301c13', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMTI5MzF9.F4i2_962VBcPPlmr2d_qw1-Nu7lOWS-WS8jAantuS_o', '2024-03-22 17:22:12', '2024-03-21 17:22:14.371446', 1); +INSERT INTO `user_access_tokens` VALUES ('b140cb61-64a4-4e5f-b278-5d1764f28524', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjg1MjF9.3tEn-p_MoFohcXRbyOF27WDH6ZiuSSRTtWpH5BEo2Qk', '2024-03-23 08:48:41', '2024-03-22 08:48:40.790543', 1); +INSERT INTO `user_access_tokens` VALUES ('b7f0d7b7-eb71-4eb1-a47d-88c444a44b2f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0MzgyMTh9.KtkYzRJ1NLAQUingFGjJuLCZ9yp5bbqITH_CA2qCLh8', '2024-03-27 15:30:19', '2024-03-26 15:30:16.074185', 1); +INSERT INTO `user_access_tokens` VALUES ('b85f77c8-fd9c-4f97-b680-666be5b2deb6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5ODUyMjN9.oxV57ydkinFtrwBbWI8xK-qrpkDDXi6kEINfCvDqe1c', '2024-03-22 09:40:23', '2024-03-21 09:40:24.862189', 1); +INSERT INTO `user_access_tokens` VALUES ('b92e568c-a942-4155-85be-4ade7c61d774', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAyMDQ2MzR9.kyjByDKeCWQArLkl4x4FsOwOn2jU6zp91WoAfMf5RoY', '2024-03-13 08:50:35', '2024-03-12 08:50:34.530170', 1); +INSERT INTO `user_access_tokens` VALUES ('bb16d202-1f32-4f55-8fc1-7c653d44d874', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MjQ0ODF9.yV7rOObsnoSkTy2yMMZHoNLGcWMsw6P-jYTP_87Uffo', '2024-03-20 13:01:22', '2024-03-19 13:01:22.802716', 1); +INSERT INTO `user_access_tokens` VALUES ('bde4a4d2-ec77-4247-bfe9-2bdcb2b0c647', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDk4MzB9.jr6tcmZysoD5mO51Hvt9d2e4hHS6OZrhp0ynVPt8PNE', '2024-03-28 11:23:51', '2024-03-27 11:23:50.924952', 1); +INSERT INTO `user_access_tokens` VALUES ('bed60fee-ef30-48a1-a8a3-e1394ef69c66', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4OTc0MzB9.KwretnZz0QASH0KXtiJc9UEMqJIG5xhdsXOk1bfOxXQ', '2024-03-21 09:17:11', '2024-03-20 09:17:13.363786', 1); +INSERT INTO `user_access_tokens` VALUES ('c519656a-1312-4074-990d-e9577c000278', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjg3NDl9.-e6nu9CeUL-DFGQSrGA0xCU0bB9NPXOJ4H07E8MmnxQ', '2024-03-23 08:52:30', '2024-03-22 08:52:29.450325', 1); +INSERT INTO `user_access_tokens` VALUES ('c74cf0ed-b704-4e72-a93f-24a79cb7db07', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0NDAxMzB9.EhX-5QSjuuXBHOFXLFyzD6MVBtpo9JoI3zJzLj4oWf4', '2024-03-27 16:02:10', '2024-03-26 16:02:07.610864', 1); +INSERT INTO `user_access_tokens` VALUES ('cc1cc297-6be6-4267-9aec-fa45e59710eb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDg2NTN9.OWsAKScwNrk3-E1FasIJaQ-0mZJcRMMdH0TN35pR_T8', '2024-03-28 11:04:14', '2024-03-27 11:04:13.782127', 1); +INSERT INTO `user_access_tokens` VALUES ('cc472002-f409-4273-aa55-dde97cb1ac29', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0MzgxOTF9.-g-o-zdXpSyjaQEYu8zsfvonHZH4s37jRE09soxhPRs', '2024-03-27 15:29:51', '2024-03-26 15:29:48.495898', 1); +INSERT INTO `user_access_tokens` VALUES ('ce151b19-e87d-4cce-9493-fb0aa203198b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTA2Mzd9.JEvSNANVjeOede04Zjyv65RCZ8MIaAjzUZGK1j-HVlY', '2024-03-20 09:10:38', '2024-03-19 09:10:41.614145', 1); +INSERT INTO `user_access_tokens` VALUES ('d18de5f3-f114-4f19-8981-5ef576706a60', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA3NTE1NTJ9.5HZXP5ZKsPslkEK-kJmeKmYG0MWKaQthAWFUhyQ9TiE', '2024-03-19 16:45:53', '2024-03-18 16:45:53.566424', 1); +INSERT INTO `user_access_tokens` VALUES ('e087f105-324d-4b0d-b44a-4f9c6041a258', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0Mzk4MjV9.ObutDKkzqpGV5HYNckVJmnZG5lpgiPZWiT8h5ej3f_g', '2024-03-27 15:57:05', '2024-03-26 15:57:02.487339', 1); +INSERT INTO `user_access_tokens` VALUES ('e0b66a0d-2522-4423-8a04-81e282520f2f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEzMzgwMjF9.iEEjHupmnw-trA0cFbYj6nmRxVPKXSQ9NLnV5v6nrsA', '2024-03-26 11:40:22', '2024-03-25 11:40:21.378503', 1); +INSERT INTO `user_access_tokens` VALUES ('e0f833b8-4c69-43cc-ae17-51579379c09c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMDAzMjd9.xhiRtcg6yq7q3lLIQdB2_tN7pNAhyj0mI7qMYhPxc64', '2024-03-22 13:52:07', '2024-03-21 13:52:09.245352', 1); +INSERT INTO `user_access_tokens` VALUES ('e23276d2-3f8b-4ce3-aa75-5f15ccc43512', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0Mzk2MjV9.xhByWA1kiCFNT2HaqzIEhU9QYnVyZ2oVjwigf6J1D3A', '2024-03-27 15:53:46', '2024-03-26 15:53:43.048420', 1); +INSERT INTO `user_access_tokens` VALUES ('e7ba638f-c89f-47e9-bd67-8918a7cb8cc3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE3Mzl9.4vXdwhtGgsSzC9vBerAbXk04mDbpeDh5XdpB7_okDPE', '2024-03-20 09:28:59', '2024-03-19 09:29:03.147751', 1); +INSERT INTO `user_access_tokens` VALUES ('ecdd1694-db7c-442c-a998-ef708d6a2a67', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5MTA5MTN9.9opzZI0y3C_-E3euH-XdwaDf7apnftlRtkk3ZIDqh3A', '2024-03-21 13:01:53', '2024-03-20 13:01:56.119760', 1); +INSERT INTO `user_access_tokens` VALUES ('ed53543e-51a0-4163-8954-f96a7e1fadcb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDkzNTN9.E0xGR2iGcyef2BEUDMuaMka97NaLqNq_piRC_w66_x8', '2024-03-28 11:15:54', '2024-03-27 11:15:53.999628', 1); +INSERT INTO `user_access_tokens` VALUES ('eebefeae-565c-44c5-b43c-a2eee2077b5b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTAwNzB9.1CXhZmQZhmmT2x9KusTSclO9a0ZGpgzElvS-lJHhHsM', '2024-03-20 09:01:10', '2024-03-19 09:01:14.203313', 1); +INSERT INTO `user_access_tokens` VALUES ('f63b8aa7-d54e-42db-9b9b-79440133b131', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTIzOTV9.b1QALT2pgRe2tJIckPMtIhPNFTQzI5-UlyVCdSQIC2w', '2024-03-20 09:39:55', '2024-03-19 09:39:59.158099', 1); +INSERT INTO `user_access_tokens` VALUES ('f87de777-667c-4add-938b-d121d69361f6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0MzgzMjd9.uPz09Q5Sh0X78HV43G8GaGYD8brN6WlX1kWneX7M67s', '2024-03-27 15:32:07', '2024-03-26 15:32:04.529181', 1); +INSERT INTO `user_access_tokens` VALUES ('faa26fea-b099-4edf-a9ca-642f06a4e8c1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjczMTl9.QvHe_baU8fJwr9m4PtO-XpBZKFwgcMXM_g3K3h40hKc', '2024-03-23 08:28:40', '2024-03-22 08:28:39.849804', 1); -- ---------------------------- -- Table structure for user_refresh_tokens @@ -1109,40 +1338,80 @@ CREATE TABLE `user_refresh_tokens` ( -- ---------------------------- -- Records of user_refresh_tokens -- ---------------------------- -INSERT INTO `user_refresh_tokens` VALUES ('045ad38e-ab82-4ea1-8b61-c2da89060999', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRWMxcEQwZ1VrYnFtZEhyWVdKTDVPIiwiaWF0IjoxNzA5MDkyMTc4fQ.K2nBZ_B8jneihHfs51LIp0fvkkgV7lFawe2cu4sOjN4', '2024-03-29 11:49:38', '2024-02-28 11:49:38.302821', '31399cec-e506-4194-a9cc-47f7a1a953a9'); -INSERT INTO `user_refresh_tokens` VALUES ('046cd195-a105-493e-9d6c-0d5d87081834', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRlU3a3poeGxNUkhWVUdleDR6VmJTIiwiaWF0IjoxNzA5NjIwNDQ4fQ.UKal8R3pPSiIGPAZFUf78-ne81csm2TwQbRtWIB_B1k', '2024-04-04 14:34:08', '2024-03-05 14:34:08.406121', 'cbfcc75c-d357-47fd-afbf-db0629885eb6'); -INSERT INTO `user_refresh_tokens` VALUES ('08f57b8a-6a86-434a-89cf-280255a0d1b6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaWxvdmdUWmJueU5RTGR4bmxpc3Y0IiwiaWF0IjoxNzA5MDk2ODk4fQ.v29AorJL9FJVAS89GxtFKeJYc92nPaIpE8O-1GY_nhw', '2024-03-29 13:08:18', '2024-02-28 13:08:18.499682', '8e26aa27-fa67-4b67-8705-019be3e8f1f8'); -INSERT INTO `user_refresh_tokens` VALUES ('157299e9-45e0-4fe3-9caf-ff944644605d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNVVsRHhFSE1EMnpMVGd4bUNmRnVSIiwiaWF0IjoxNzA5Mjc5MTM2fQ.mbmS1urJdB6YxBdrj1n09-IgARY_LC5lRuTAntVUZlg', '2024-03-31 15:45:37', '2024-03-01 15:45:36.901193', 'cdc3e837-0953-42e7-8296-d27cfbc41353'); -INSERT INTO `user_refresh_tokens` VALUES ('1f613862-2567-4c10-8026-c4743c62d01a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1ZZMzl3dnpVLUxmcTFxSjBYVGktIiwiaWF0IjoxNzA5MDk5NTk3fQ.Qjhsglo7PbTsT1_KE3vTMNweHx08dwcz_q1e1f2W9R0', '2024-03-29 13:53:17', '2024-02-28 13:53:17.487831', 'eff3fd41-7307-4027-9f8c-2e3b0fd35051'); -INSERT INTO `user_refresh_tokens` VALUES ('202d0969-6721-4f6f-bf34-f0d1931d4d01', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRpOXVYei1TdldjdWRnclFXVmFXIiwiaWF0IjoxNzA3MzcyMTYxfQ.NOQufR5EAPE2uZoyenmAj9H7S7qo4d6W1aW2ojDxZQc', '2024-03-09 14:02:41', '2024-02-08 14:02:41.091492', '40342c3e-194c-42eb-adee-189389839195'); -INSERT INTO `user_refresh_tokens` VALUES ('21e28f7b-c156-41bf-902e-583d48109ebf', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoieVU2NU5NSGVuak5lUmozY3BWVDhwIiwiaWF0IjoxNzA5MTk1MTY5fQ.4Vibz4kCF5UulsR9OmAhmQRhrA9idKCyKXCITVLOAkk', '2024-03-30 16:26:09', '2024-02-29 16:26:09.086894', '7ebc6a49-421c-4437-a7a8-bc8aaeb96b26'); -INSERT INTO `user_refresh_tokens` VALUES ('25e9acfd-eb72-4ab8-9e5e-04f0a683e60f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibndPOEFIb1hab2d3Y0tpWG41NXlJIiwiaWF0IjoxNzA5Mjc5ODI0fQ._r8VN3caZ5XzEdMpH1FM9e1yVqHuACpknfOeSyixXns', '2024-03-31 15:57:05', '2024-03-01 15:57:04.755457', 'c24acee3-7bbd-4e7a-b8f6-a80be1db0b33'); -INSERT INTO `user_refresh_tokens` VALUES ('42cb1246-4a1b-4443-a9b0-926761f13527', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaVBwVW1FQ0xUTXgzUC1KVUdvaXJaIiwiaWF0IjoxNzA5NTI5MzQyfQ.e6TbYee7T2uEt0GJznuLmlEGYvmT8LWQVk0phYMS6Lw', '2024-04-03 13:15:42', '2024-03-04 13:15:42.358715', '6ea87ca4-89e4-419b-9eb3-7bef4ada53ca'); -INSERT INTO `user_refresh_tokens` VALUES ('46195349-cbf4-492f-b193-7aec06cb556b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN1NGYy1PTGNWc1MxVG5BWEdqLW1NIiwiaWF0IjoxNzA5Mjc5ODQ5fQ.n1eSzEGrz_M_hrYNdxx7UtGWHzt7soyJqDh1UK670fE', '2024-03-31 15:57:29', '2024-03-01 15:57:29.086263', '31875da0-8b93-4ca2-bdb8-ab40c1c059bd'); -INSERT INTO `user_refresh_tokens` VALUES ('461f9b7c-e500-4762-a6d9-f9ea47163064', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicXJvTWNYMnhNRW5uRmZGWkQtaUx0IiwiaWF0IjoxNzA3MzExMzAyfQ.dFIWCePZnn2z2Qv1D5PKBKXUwVDI0Gp091MIOi9jiIo', '2024-03-08 21:08:22', '2024-02-07 21:08:22.145464', '3f7dffae-db1f-47dc-9677-5c956c3de39e'); -INSERT INTO `user_refresh_tokens` VALUES ('4a4a3a33-1308-4fc1-9ffa-f828d875e004', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLWREemYwWUdXMEhVQXI3RHNqbG9qIiwiaWF0IjoxNzA5NzE2Njc1fQ.ro8DesLH5duC9IBySpMLwtuzC5pMPr1NY6U21Ff4ZHg', '2024-04-05 17:17:55', '2024-03-06 17:17:55.118361', '19e593f4-4a03-46e0-9b4e-a009af15790a'); -INSERT INTO `user_refresh_tokens` VALUES ('4b82a1b7-b571-42de-8809-487f7abc2e65', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiR1BkbUVfTmhjZG5xWnpWX2lGN0JuIiwiaWF0IjoxNzA5NTQ0MzQyfQ.hH2bzYj_8Qim997-C7HNUSJqkk43Z1YAiexpRhxR8cQ', '2024-04-03 17:25:42', '2024-03-04 17:25:42.114236', '53a08eaa-74fb-45cb-9e6c-514b67346e06'); -INSERT INTO `user_refresh_tokens` VALUES ('50b2f3ad-232a-41f6-bf97-2c52127a3e28', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVnlZNjYwYWNPRHlTd2NZWGhscWdFIiwiaWF0IjoxNzA5Mjc5ODMxfQ.uG0qmKgWzYNvBJFfCvvOmndCgrwpkZ3NVL4aynD6Uxc', '2024-03-31 15:57:11', '2024-03-01 15:57:11.259022', 'ebc5fbbb-b29e-4a17-acd6-002ca7936099'); -INSERT INTO `user_refresh_tokens` VALUES ('53c79bc8-f092-4d07-b480-f8637a3887fa', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOEoyQ0w5RXYtSkhmWW0xSDNMMUxEIiwiaWF0IjoxNzA5MjYxNTg5fQ.3Ez1mfJkLZ9LFo2nTshTtOjs-VN56k1LWNoijWzSYaA', '2024-03-31 10:53:10', '2024-03-01 10:53:09.615615', 'd32e25cc-b00c-43cf-94b0-0f61c3314f4c'); -INSERT INTO `user_refresh_tokens` VALUES ('718ccd7b-bb4f-4400-8f40-ccd6141ef4c5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiamNTcWlzUkFIaW5SOTlUX2lvVzV0IiwiaWF0IjoxNzA5MDk2NzYxfQ.X-iqKmDeV6juqCQC8XjU7o-8CXNxEyLyn5uck4mkJoY', '2024-03-29 13:06:01', '2024-02-28 13:06:01.430737', 'ab62dfc5-46d8-4dad-9055-bb2803d1451c'); -INSERT INTO `user_refresh_tokens` VALUES ('9557d0a9-5004-4392-95b6-637b3343dbc3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTnNqRGZsMUpKY3RWTzFYaEtEMV9FIiwiaWF0IjoxNzA5NzgzMTc0fQ.osKC1PeUm6LAMj6ZyIuNhUxFcKN1t1ZvrjkE2Rn1pxw', '2024-04-06 11:46:15', '2024-03-07 11:46:14.600560', 'e042f0c3-9a35-40cf-83a3-918f617f2ac7'); -INSERT INTO `user_refresh_tokens` VALUES ('9d54c429-5dab-4e72-b70e-d1e6918d364f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ1UxYnkxTkVNT05ZQVJsWjBkYzIwIiwiaWF0IjoxNzA5Nzc0MDczfQ.rMG5F7Cec_gcP7C7gr_WL6XiquvuWmvV8Wh2SQOEwuY', '2024-04-06 09:14:34', '2024-03-07 09:14:33.788438', '78519125-77c1-4768-9644-d8fa639f3ff0'); -INSERT INTO `user_refresh_tokens` VALUES ('a2e12448-35b0-487e-a18e-a60461a54370', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZHRvUU9fUjhCZGFOQ1EzMVRhYmxtIiwiaWF0IjoxNzA5NzgxMDk4fQ.3ORghjx3Yzh9_f62tYHLeY_x602pBF-wMrKEVX7Dd2c', '2024-04-06 11:11:38', '2024-03-07 11:11:38.213630', '35fd4bb6-2608-4203-ac2c-9550034993ea'); -INSERT INTO `user_refresh_tokens` VALUES ('a30969f3-b766-4190-b3c6-544f81a05a5a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRlRUN0VqRWlfc1RkSXd6cjhGZmtZIiwiaWF0IjoxNzA5NTI5MzY0fQ.5qz5M4MC4nU614Nn8hyW0l1TuGcFNuKRHC_ZYzEZwO8', '2024-04-03 13:16:05', '2024-03-04 13:16:04.680424', 'd9044d67-9a95-4bd5-8103-f6ddc9701f83'); -INSERT INTO `user_refresh_tokens` VALUES ('a500bb13-4b9a-4cf2-b691-b046586d9ee7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoic3R1TDFpUmljTnBlSTlhR0FEUFQwIiwiaWF0IjoxNzA5Mjc5ODUzfQ.oiNGDkAWVr4jko01TY7rqWoOYvCcwZbWpfLCzgtqMHc', '2024-03-31 15:57:34', '2024-03-01 15:57:33.879108', '8f7fcaed-c4ed-4cf0-840f-37bcd3a39a59'); -INSERT INTO `user_refresh_tokens` VALUES ('a8451340-008c-4fa7-bea0-3bdeee5d5cfa', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVXdDQ2dJeUU1eGpUT1lLRDVtcnU3IiwiaWF0IjoxNzA5MjYxNTUzfQ.dA6eFaURUP0wYU7Pr0Xjv2oFkkfrwQb-cB_gziAK4G4', '2024-03-31 10:52:33', '2024-03-01 10:52:33.072305', '17593c96-3779-4f03-8dbc-cb21ac9f7981'); -INSERT INTO `user_refresh_tokens` VALUES ('b375e623-2d82-48f0-9b7a-9058e3850cc6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicDhUMzdGNFFaUDJHLU5yNGVha21wIiwiaWF0IjoxNzA3MzcyMTI3fQ.fn3It6RKIxXlKmqixg0BMmY_YsQmAxtetueqW-0y1IM', '2024-03-09 14:02:07', '2024-02-08 14:02:07.410008', 'edbed8fb-bfc7-4fc7-a012-e9fca8ef93fb'); -INSERT INTO `user_refresh_tokens` VALUES ('c92355b8-da11-4e8d-b5a6-0826bb44aac0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUlVnM3RtQTBROWZjZ0JZWnJxMXNxIiwiaWF0IjoxNzA5Njg1OTA0fQ.CraA5kdSrHeZhyRchvmZfnfdiPXfjd_4A6FZWPh-r7Y', '2024-04-05 08:45:05', '2024-03-06 08:45:04.557816', 'af43fb31-3759-43d5-b13e-e558bccdca58'); -INSERT INTO `user_refresh_tokens` VALUES ('e620ccc1-9e40-4387-9f21-f0722e535a63', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNE5WdmFIc2hWaU05ZFh0QnVBaHNsIiwiaWF0IjoxNzA3NTI3OTA1fQ.zzyGX0mOJe6KWpTzIi7We9d9c0MRuDeGC86DMB0Vubs', '2024-03-11 09:18:26', '2024-02-10 09:18:25.664251', '9d1ba8e9-dffc-4b15-b21f-4a90f196e39c'); -INSERT INTO `user_refresh_tokens` VALUES ('ea877712-28d7-4ee1-97ad-27f4455d59a5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZUVsNUM2RXZFMjBacENpZnZZalI4IiwiaWF0IjoxNzA5MTk1MTE0fQ.SnF7us-m0ktL6JJIXl8NgNPrsVlHMSN_l5-Nr9L2FeA', '2024-03-30 16:25:15', '2024-02-29 16:25:14.741842', '6f3990bd-d093-4cc4-9f9e-cba45260ec42'); -INSERT INTO `user_refresh_tokens` VALUES ('eb8e0905-8f49-42db-b9f8-771f8d706ab0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiSEZWTG9xY25UeV84ZGpOWWxZTy02IiwiaWF0IjoxNzA5MDk3MDUxfQ.cC5X2an9Vn4q3NThsqKdA72locR6J10yOCPPqGT_hnc', '2024-03-29 13:10:51', '2024-02-28 13:10:51.345270', 'b6053fd5-7c7c-453e-a8ea-6de17b30304a'); -INSERT INTO `user_refresh_tokens` VALUES ('ece48e5b-4a1e-4683-acbc-4da02db0d511', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiai1DZjIyUllQX0taaWpPaFkyV01iIiwiaWF0IjoxNzA5NTM4MTc0fQ.oAUJSvbA2EQbdqnpia8nHiiH7VH6igEHOka9INSnTVE', '2024-04-03 15:42:54', '2024-03-04 15:42:54.302651', '4541a9e8-a508-4b92-8ae3-c3744ddcad92'); -INSERT INTO `user_refresh_tokens` VALUES ('ef3560f5-8f24-4a76-a5e8-c17de1f1fcfe', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiY2VkQ2xZdl9TcTJ2SUEybzZaZS1NIiwiaWF0IjoxNzA5MDk5NjMzfQ.mxekqz0IB_fzwvoBcZyN7BPKezISmd4wPHt7fR7-b3g', '2024-03-29 13:53:53', '2024-02-28 13:53:53.180619', 'e754d168-106a-43f5-bbf8-80f1b187cb1e'); -INSERT INTO `user_refresh_tokens` VALUES ('f48bf795-9163-4899-8268-2345a0d2ba4e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVjc1SUFMMkhVLUFuTkZINzVFbmhfIiwiaWF0IjoxNzA5Mjc5ODM4fQ.epc14TsoOkTpJOoo1zW9N3qKxiiUTqQ42Sbb455ppAE', '2024-03-31 15:57:18', '2024-03-01 15:57:18.327929', '367aaa79-1130-4511-87c4-25aacdf27d3d'); -INSERT INTO `user_refresh_tokens` VALUES ('f9a003e8-91b7-41ee-979e-e39cca3534ec', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGJQdl9SVjFtUl80N0o0TGF0QlV5IiwiaWF0IjoxNzA3NTA5MTU3fQ.oEVdWSigTpAQY7F8MlwBnedldH0sJT1YF1Mt0ZUbIw4', '2024-03-11 04:05:58', '2024-02-10 04:05:57.706763', '09cf7b0a-62e0-45ee-96b0-e31de32361e0'); -INSERT INTO `user_refresh_tokens` VALUES ('faef8e19-55d3-4005-8022-ca735c5c9390', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1ZZeXlpQWNJREVTdktFNUhjeER1IiwiaWF0IjoxNzA5NTI5MjUyfQ.IcNLUGhcsxTWIYWxT-0TeII4NbM--Bgq7BJ0VbRfUMo', '2024-04-03 13:14:12', '2024-03-04 13:14:12.215057', '1bb9d14e-823c-4f9f-8562-cbc2fd2be318'); -INSERT INTO `user_refresh_tokens` VALUES ('fb10a7dd-5f84-4151-ab25-b24ec9fca46e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMUFzVUdRc3hjZEdnaVhjYkVUcm1WIiwiaWF0IjoxNzA5NTIzMzgyfQ.t2zzBijhlt4cV-p8KElBjJk4TURABI-tKRk-mIkmb4w', '2024-04-03 11:36:23', '2024-03-04 11:36:22.837030', '0b95a340-d9eb-4c3a-bd70-2432d3108984'); -INSERT INTO `user_refresh_tokens` VALUES ('fda03f46-8638-43c1-8417-12ecb1ec7cf7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNUx1NkJuc1pQeVdtVm1DbHg4MGxwIiwiaWF0IjoxNzA5MjgzMjA0fQ.yAmdtYl3KqMa_R32R2Ht2xxMtQIMpncQ-hFzhzTJTkE', '2024-03-31 16:53:25', '2024-03-01 16:53:24.799017', '07276357-6286-478c-8058-d249f4ca4dde'); +INSERT INTO `user_refresh_tokens` VALUES ('010e06ef-1457-437b-ad87-13b027bfbabf', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMVA1WlRkVkNNSVdCeVZzUWNvUXhwIiwiaWF0IjoxNzExMDAxOTIxfQ.ymE1VCWUEP8GOFq-UBkrpmjW4YUIhGQKN4ehkUmPTvk', '2024-04-20 14:18:41', '2024-03-21 14:18:43.417025', '66a3b775-98e7-4142-be24-415206c6973b'); +INSERT INTO `user_refresh_tokens` VALUES ('07e96a3a-b62e-47c5-a7df-9389b7063480', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWVlWdzdPUmNLdEJUSXFBMWNFZms2IiwiaWF0IjoxNzExNTEwMzA1fQ.qVxTN6ysyP7YeWbAdvRpjvsGAxpcm43mcaPbkct95BY', '2024-04-26 11:31:45', '2024-03-27 11:31:45.326110', 'a8caae33-85eb-45f7-afc6-1e4bd187599c'); +INSERT INTO `user_refresh_tokens` VALUES ('08a09272-11ec-4d52-bff0-ce1fa066fae4', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNnZQTEhfM3l1QVViUlE0SHU3UnFLIiwiaWF0IjoxNzEwODExNjcyfQ.zxFU1bygRPc5MdSmD04A1_O8NlkafARR47dKoLhVLoY', '2024-04-18 09:27:52', '2024-03-19 09:27:56.089234', 'a4925b1e-ddf5-4382-a643-b45d1bf8199c'); +INSERT INTO `user_refresh_tokens` VALUES ('0a366a11-90e5-4287-9fbd-8de07fa02d8f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiS2lpbGY0NVQtUm1OcGdBNkVDUEl0IiwiaWF0IjoxNzEwODEyMzk1fQ.oV2IKNaSrWDTsldxsya0jVkTv-rtXer5vwQ7Gb_Hmhg', '2024-04-18 09:39:55', '2024-03-19 09:39:59.185706', 'f63b8aa7-d54e-42db-9b9b-79440133b131'); +INSERT INTO `user_refresh_tokens` VALUES ('0d5a4d35-c3e2-4ee8-b9de-7b3ec878ba30', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZjNCYnRYUlV4aDNoRzc4NWZQMGx4IiwiaWF0IjoxNzEwMjA0NjM0fQ.WzjE8XNFVJxInsboM3ajmce7ajU3wOJwkbrwdHeBHW8', '2024-04-11 08:50:35', '2024-03-12 08:50:34.540128', 'b92e568c-a942-4155-85be-4ade7c61d774'); +INSERT INTO `user_refresh_tokens` VALUES ('0fbbe536-4bc1-4592-a5e2-fe77fbedc938', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZU9vaE90YzZCa203ck5HSzZGQTU0IiwiaWF0IjoxNzExNTA4MzYzfQ.qnxo7i0eckurIxmm1yne62qE7m5xy4wcDcMQQ2ruPvg', '2024-04-26 10:59:23', '2024-03-27 10:59:23.382098', '64dd9302-c031-49f6-8eb6-bdb6e079fd2a'); +INSERT INTO `user_refresh_tokens` VALUES ('11a3293f-ccc4-4ef5-af95-17cb96219743', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNmRJTDR1Mlo4cmlwVW1XUjhmV0VHIiwiaWF0IjoxNzEwODk3NDMwfQ.TkIFl5hIF1gllYasIfOgeCWd36YP1lM2kjtN79POb4A', '2024-04-19 09:17:11', '2024-03-20 09:17:13.399688', 'bed60fee-ef30-48a1-a8a3-e1394ef69c66'); +INSERT INTO `user_refresh_tokens` VALUES ('1ad2cc4b-fed9-412f-a115-b8ef75249bfe', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMnBMc3NHaTJpZkpoTzFqWHZZbXFYIiwiaWF0IjoxNzExMzMzNzI4fQ.P2wpOAdZ5p8_dQB9k2uei5tuR3fCXVZHwPZvnrZaHm0', '2024-04-24 10:28:48', '2024-03-25 10:28:47.958322', '494de6e6-9ac1-47f2-b649-afac34468a64'); +INSERT INTO `user_refresh_tokens` VALUES ('1bdb14fb-f898-4b72-a51e-cf4a5b7baaa6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOEZkaU1YckZPSmd4Q015STdmRUpDIiwiaWF0IjoxNzExNDM5NjI1fQ.Vepis6LXtFw7pprrQVheVG0u7wjrnbA9MQmJixRrhmM', '2024-04-25 15:53:46', '2024-03-26 15:53:43.079038', 'e23276d2-3f8b-4ce3-aa75-5f15ccc43512'); +INSERT INTO `user_refresh_tokens` VALUES ('1c9ec9aa-2e92-4081-babd-3d3eb805b1b6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN1UzeGN0bFdZYThobnZrY2JpemxmIiwiaWF0IjoxNzEwODEyMzQzfQ.BIUCD5cvBfTqmueu-U1wqTo6qUf2udjsUbRphWZNmZw', '2024-04-18 09:39:03', '2024-03-19 09:39:07.405051', '591e086d-96c4-4cab-bb9f-fabe6ee27d43'); +INSERT INTO `user_refresh_tokens` VALUES ('2416d21d-4ad8-4349-85e9-61c73056fa62', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMjZXb1p3LUpWS0ZYN1l2QzhHcFpNIiwiaWF0IjoxNzExNTA5ODMwfQ.H0BhMZ0h5MkwG-_1IVtJMovtcdZnKA8MJ3GzInsBuJM', '2024-04-26 11:23:51', '2024-03-27 11:23:50.959365', 'bde4a4d2-ec77-4247-bfe9-2bdcb2b0c647'); +INSERT INTO `user_refresh_tokens` VALUES ('25e1c246-0b08-4ec7-97c6-76c7180e6ccb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN1A3MFFWajY2NDJZSkMzaGFKS3VWIiwiaWF0IjoxNzEwMTQ2NTA3fQ.VnbDgerC7dqDX3uY7H-wRUbcz0sM73Be4_yB6exyTGY', '2024-04-10 16:41:48', '2024-03-11 16:41:47.561906', '6c22c272-e33f-4fa4-a331-17b08293c97c'); +INSERT INTO `user_refresh_tokens` VALUES ('2840181a-20db-4109-bd69-9ca8c13dd364', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLU9ZQVZhUzNtN1FvdjMwT0dkVVNTIiwiaWF0IjoxNzEwNzUxNjIzfQ.clJe-5xsDNNlll_sByUfsyTMCIPyStl1mofFCnImeI0', '2024-04-17 16:47:04', '2024-03-18 16:47:04.575279', '0a058377-1c0e-48d6-b143-21d5195ac859'); +INSERT INTO `user_refresh_tokens` VALUES ('2843df4e-1fe7-4df1-b6ed-c65b5bb832bd', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidWZjUnNzWFNSTDRLNHNyM3VReEowIiwiaWF0IjoxNzExMDAwMzI3fQ.PGX4sZTbGbqgdlCpSf7yua85jU8Sh5nHsSE0r_xngIw', '2024-04-20 13:52:07', '2024-03-21 13:52:09.304657', 'e0f833b8-4c69-43cc-ae17-51579379c09c'); +INSERT INTO `user_refresh_tokens` VALUES ('29aefbd4-2cd4-46f7-9dc8-fd5480709339', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZTB3THhUTF9HaE5KU0Z5Z05ybkhhIiwiaWF0IjoxNzExNDQwMTMwfQ.ox3LslTVukUV6di-G2mIocFtWIyUGs_9sJQrDRnU4jw', '2024-04-25 16:02:10', '2024-03-26 16:02:07.647313', 'c74cf0ed-b704-4e72-a93f-24a79cb7db07'); +INSERT INTO `user_refresh_tokens` VALUES ('302164ad-4379-4a94-b430-7be5eba3c0c8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZmVaQktfcUtSNktVZVdPRkZBWjRoIiwiaWF0IjoxNzEwODM0ODEwfQ.TMwCSR-QW0zQE7KEJfEkRC4jzBtYpHKOs_cCC_7QIFI', '2024-04-18 15:53:31', '2024-03-19 15:53:32.278476', '191ada91-e54d-46c5-a20d-448022f5da60'); +INSERT INTO `user_refresh_tokens` VALUES ('3265f1de-05b4-4e3f-b4aa-942df1fa6226', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicmtHS0IyNDBHOGhTODlUczlELVdvIiwiaWF0IjoxNzEwOTkxMjQ0fQ.Hl2W3hSqilfjl3w13bB2qNXt-Ob5Z2b0cB99IV-aIH4', '2024-04-20 11:20:44', '2024-03-21 11:20:45.989087', '0bee723d-c635-4396-9656-8165ac9594b0'); +INSERT INTO `user_refresh_tokens` VALUES ('37843616-e5f3-4706-a7e0-a2192eba8214', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGpCal9pZkpDT0laTEpmUHZ3SlU0IiwiaWF0IjoxNzExNDM5NjU3fQ.PtETXpyt60eSVC5OYP9kFvbsmEQAPdHE9tvrGynAuaM', '2024-04-25 15:54:18', '2024-03-26 15:54:15.435171', '0baa35e7-11a8-4280-be13-b12c9416283b'); +INSERT INTO `user_refresh_tokens` VALUES ('3940e4d3-4c4a-4af1-b784-2956817790fe', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidG4teTFOcWE2d3lxQ1Nwd01mSXRRIiwiaWF0IjoxNzEwOTEyNDQ0fQ.Pamh4EJzVBx6snvXnhw5VJTjDLEYWEXRxYe8Tu1UjQA', '2024-04-19 13:27:25', '2024-03-20 13:27:27.521307', '1ca6754c-710d-4a23-8b17-79fe340e1e65'); +INSERT INTO `user_refresh_tokens` VALUES ('3abe7182-349c-4417-b3c6-b62c125f28a4', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoid3ZfRWU3MTk2N0c1TW9jRnV1djNqIiwiaWF0IjoxNzEwOTkwMDU1fQ.jhCbZTW7ljGarY2bGEBV9OMdr6J5If4Y5jukOQrwVbk', '2024-04-20 11:00:55', '2024-03-21 11:00:56.678623', '914b16d0-4508-462e-be5a-44d374c4fd61'); +INSERT INTO `user_refresh_tokens` VALUES ('422896a8-0305-4397-b74e-04441ad311e0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQllhM2FOWFRTSFZjOHNKeW80QTNTIiwiaWF0IjoxNzExMDY4NDY5fQ.KCA5nqJXD3UjgvsVzUBsS7qXwRry0a0mzLd5Jen1Ftg', '2024-04-21 08:47:50', '2024-03-22 08:47:49.588066', '58fd6fa7-fd30-4a3e-bc79-53d1575745c2'); +INSERT INTO `user_refresh_tokens` VALUES ('42b83817-25fd-4a14-85e8-eb74e8f6b2d9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiU3VmUUxEMG5KREdQbVVSdV9FeU5VIiwiaWF0IjoxNzEwODExNzI0fQ._PgPOASqX3eHKfGTy1I_bPZmUCMWaftmBcTx1BCWbQE', '2024-04-18 09:28:44', '2024-03-19 09:28:47.982722', '63e12288-1b49-4393-a07c-158189f687c7'); +INSERT INTO `user_refresh_tokens` VALUES ('46fb00b3-3712-4d57-a233-a3db168ffe6b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZEJRS3hIQ0gxM0xFU0JBVWtFMVhIIiwiaWF0IjoxNzExMDExMTc5fQ.oYXUCnbSfBIQu8Jx2lpAwQV6-lf-vDy0jgGW8d5NMDk', '2024-04-20 16:53:00', '2024-03-21 16:53:02.355897', '31cc91fe-e7b0-4d74-9ea2-f5f12063df86'); +INSERT INTO `user_refresh_tokens` VALUES ('494874e7-8b3a-4e39-84bc-5aad8ff76e94', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ2JzVWhVbFljVGFhZlF6NVYxZVExIiwiaWF0IjoxNzEwODI0NDgxfQ.ICg5zZLHZ1ri5VH-q1XuCvkFVKo11imwYnYgYkt6N0o', '2024-04-18 13:01:22', '2024-03-19 13:01:22.829304', 'bb16d202-1f32-4f55-8fc1-7c653d44d874'); +INSERT INTO `user_refresh_tokens` VALUES ('49a01d59-5696-4101-9e40-fcc8898fb530', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLTdKQk53U29TYTVBakNsOVlqR3R4IiwiaWF0IjoxNzExMDEyOTMxfQ.Ra5Ok3GZPvFre5M12peezgUSxVyHjhU-RECOYdVlRDM', '2024-04-20 17:22:12', '2024-03-21 17:22:14.399610', 'b063d499-6fd3-48ea-b75d-95d3bb301c13'); +INSERT INTO `user_refresh_tokens` VALUES ('4c40606e-a096-4a58-a231-15feab4a50a3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiT1RlQVhMTWpyeW0zMGo1NEtKSDRkIiwiaWF0IjoxNzEwODExNjYzfQ.LPpYzJnkMRFzPGZDgLLgFGHeyV4WpT6_DK_5I0dqrXg', '2024-04-18 09:27:44', '2024-03-19 09:27:47.558544', '0f9677c0-6a16-4b6b-ac83-2ee669fb7ced'); +INSERT INTO `user_refresh_tokens` VALUES ('4c89232a-d38d-4156-84ef-420440c7d259', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNnl5TjhneVNkWUUyUnZiaVdGOEMzIiwiaWF0IjoxNzEwODEwNjM3fQ.5gD2k6T1ZkDWupC95Z7W3iuaFMIibDaYn37E3vQDqdk', '2024-04-18 09:10:38', '2024-03-19 09:10:41.639655', 'ce151b19-e87d-4cce-9493-fb0aa203198b'); +INSERT INTO `user_refresh_tokens` VALUES ('51df5f43-4d4e-4d0d-9ee8-bb9530aae045', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidGY5S1VsRXNzczVmaVhWamNVbW1uIiwiaWF0IjoxNzExNTE4NjA1fQ.YX59QyZ1i6IwBn7xl4_lBqDHanPLXCivM3UV7LpcJ3E', '2024-04-26 13:50:06', '2024-03-27 13:50:06.339976', '6e81f813-8166-434a-8b01-a3793bc9ba78'); +INSERT INTO `user_refresh_tokens` VALUES ('5743467d-c14d-4c38-aa04-2efa605594a5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQktMNk9jazllVjY4MGNiam1xV3JvIiwiaWF0IjoxNzExNjAyMzc0fQ.p6grGAQsZH1LDhV-eqenQ9hixg-wxmJfOMLoFd5t1GY', '2024-04-27 13:06:15', '2024-03-28 13:06:16.580783', '692a1a4d-08fc-490e-97d2-f9997d6e734a'); +INSERT INTO `user_refresh_tokens` VALUES ('5808a8cd-ae81-4bee-a009-f2260272ecfd', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiS1Y5a3ptZG9DS3ZJUlVVQzMwZFFNIiwiaWF0IjoxNzEwODExNjAzfQ.kijNuWLCfEetuAjzCwNhQseI-XQdTprcs92OqiRT_gE', '2024-04-18 09:26:44', '2024-03-19 09:26:47.686629', '5478eda6-191c-4643-9923-a1c1d83a25ee'); +INSERT INTO `user_refresh_tokens` VALUES ('5a888318-988c-4569-b2b6-cc0c904c072b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoienNtY1JVWE04OGhkMzNFUDlhaHktIiwiaWF0IjoxNzExNTA4NzMyfQ.lGSuPjMYJz3K8DdapAxatdaH2MiubuGaMzn0qnj2CAg', '2024-04-26 11:05:33', '2024-03-27 11:05:32.661859', 'a2822d05-b23a-4df0-a048-539fa79593c6'); +INSERT INTO `user_refresh_tokens` VALUES ('5b6b4d4f-6138-428c-b301-ce06fa9e8622', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiY3lTTzJRcGZGQUxvTUI3a3lkWkhYIiwiaWF0IjoxNzExNTA5MzUzfQ.RxyZd98QDBWpnadQHBlrKvG2OfPRj-TNs_rofEeSk08', '2024-04-26 11:15:54', '2024-03-27 11:15:54.036521', 'ed53543e-51a0-4163-8954-f96a7e1fadcb'); +INSERT INTO `user_refresh_tokens` VALUES ('5c368087-4b15-4dd5-9c82-780c26a835c4', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOU1ndU5qMU5BZkJWaG83WDBGeUV4IiwiaWF0IjoxNzEwODMxNjA4fQ._rSOd_AOpCJyXeaHR5EAYXRKMSAA2x-kOEbwfd7iCmc', '2024-04-18 15:00:09', '2024-03-19 15:00:10.133803', '585cba42-9d53-4003-9194-20ccb52080f1'); +INSERT INTO `user_refresh_tokens` VALUES ('6318b4a5-9c58-4a0e-819b-685cfbefe816', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1JUV0R2ZVJiVV93WW8xUTk2ak9ZIiwiaWF0IjoxNzExMDY4ODkxfQ.xvFPtqW5kSfb_eDp-ujCz5lbp3pQyafSfv2oarKVE9E', '2024-04-21 08:54:52', '2024-03-22 08:54:51.445971', '366b9989-b264-4d58-bd6a-4f8da8e0250d'); +INSERT INTO `user_refresh_tokens` VALUES ('66f1ff35-76de-4d8d-a972-8fcd31346701', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNWxzOUNvY3k2cENjSnJ5MzZmdElmIiwiaWF0IjoxNzExNDM5ODI1fQ.oyDCgJ9pCCuZZ5-2g7oTDe-Gs-M9WaIO7WPJcDOCVhk', '2024-04-25 15:57:05', '2024-03-26 15:57:02.544678', 'e087f105-324d-4b0d-b44a-4f9c6041a258'); +INSERT INTO `user_refresh_tokens` VALUES ('67a872e3-4271-4ed6-b86d-c924bff77718', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRmxWUWt0REVmRFNjbHNZTHl4VzVGIiwiaWF0IjoxNzExNTEwMDE1fQ.XrXiErlUhK5n1E1keKwCqbiisBfrQsopn9i_ovYD948', '2024-04-26 11:26:55', '2024-03-27 11:26:55.144420', '760fa21e-b944-4138-9a28-59ca4c41cc53'); +INSERT INTO `user_refresh_tokens` VALUES ('67df20a7-80e6-484d-a759-819220620225', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoic0RJM2hBWTNPbDBQaU1TeHhxVm1nIiwiaWF0IjoxNzExNTA4Mzc4fQ.TIFiY5glD17SYnMdi0GlgMaIyn7xAuqxG5ISWHq6e_0', '2024-04-26 10:59:38', '2024-03-27 10:59:38.044944', '0a52047b-397f-4ff2-9bfc-9940e17b2fcb'); +INSERT INTO `user_refresh_tokens` VALUES ('682f7771-7ef2-49df-bec7-b9bd8fd991c2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVDhWSk5idlByQmU2SmkyeTUwS0xtIiwiaWF0IjoxNzEwODE4NzU0fQ.cAt6yHsB2nfk8E92BvfMpYlibcHf7h6ktEqULOMlzqw', '2024-04-18 11:25:54', '2024-03-19 11:25:55.044706', '359f13d0-5507-4de6-8577-8c315892fa00'); +INSERT INTO `user_refresh_tokens` VALUES ('6dffecbb-34b5-4348-aee1-1fba4de70cbf', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidVEydGV5Nkd0aDVZcDlLa1JaaTRXIiwiaWF0IjoxNzExMDcyMTkwfQ.N1-1tKBDL-i4CKqU7t2OWeKNFLB-uNPJCmf7QsnqXcI', '2024-04-21 09:49:50', '2024-03-22 09:49:50.157484', '801e4049-e49a-474c-9622-603e191c9767'); +INSERT INTO `user_refresh_tokens` VALUES ('7a89b195-6441-40fc-8075-3036d89de7b2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTWdxN0hrTkxHZDlfOTYtZlY1YXR6IiwiaWF0IjoxNzEwMTQ1NTM1fQ.MXcDHgeIqIN43yBxa1eHRWogy71icRm3uG4LqBs2ITM', '2024-04-10 16:25:35', '2024-03-11 16:25:35.165240', '8d765f70-9ea8-4ca9-b072-b20be8fa6a77'); +INSERT INTO `user_refresh_tokens` VALUES ('83eeafc6-f0b2-41b1-b752-ba6e81ea2785', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMGg2cnZ0MVF5bUtCN2dXWUFLU3dtIiwiaWF0IjoxNzEwODEwMTY1fQ.80MvJxXaeLhb1_BsX55hbIzw7emkYTqcJkx1hddk7HA', '2024-04-18 09:02:45', '2024-03-19 09:02:49.147999', '9096c688-07d7-4709-a431-1bece82f3b93'); +INSERT INTO `user_refresh_tokens` VALUES ('8904ac6c-0fe7-46a7-9c2e-c887c02cfc7a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUng1TTlGbXVxSlJPU1NXbFhROGZYIiwiaWF0IjoxNzEwOTEwOTEzfQ.NIVSj-zZAP0m2aN4vxUgD34k7wGtkV_eckqk6egR_5M', '2024-04-19 13:01:53', '2024-03-20 13:01:56.153283', 'ecdd1694-db7c-442c-a998-ef708d6a2a67'); +INSERT INTO `user_refresh_tokens` VALUES ('8c17e219-f258-47db-abef-2a11a7186b2a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNXpBRXVON0ZfYlVJNjduTElpOGdxIiwiaWF0IjoxNzExNTAzOTA2fQ.Z2_HsrSAPvYM4xNFXQnvrCVEg411YBo_8HboDfwcubE', '2024-04-26 09:45:07', '2024-03-27 09:45:06.549262', '2a71c8ce-d4d4-47c4-b004-928f5056676b'); +INSERT INTO `user_refresh_tokens` VALUES ('90dfeaa3-3fcf-4331-8f82-db4fc82f9bff', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiV190N2NKM0E4OEFQU1FjZmR2UDR5IiwiaWF0IjoxNzExNTE4ODUxfQ.d-yOrwMWzGGdPKS5J3trIb_aFhAqOdLLwRnIdNzpbMc', '2024-04-26 13:54:12', '2024-03-27 13:54:12.227440', '00866088-2523-44ab-b972-1edb92b5318a'); +INSERT INTO `user_refresh_tokens` VALUES ('9438a093-f9c5-4109-a01e-e8a41e91cb8b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ0JGOFhSMEwzLTAzVmstbFc5TTFYIiwiaWF0IjoxNzEwODQwNjI1fQ.uLNqf8hiq8YfyCTCrZxBQB1XnuGu9XkSB01wOSfuKA8', '2024-04-18 17:30:26', '2024-03-19 17:30:27.503873', '95cc9059-6e9b-4354-84ed-29fe0807fac7'); +INSERT INTO `user_refresh_tokens` VALUES ('9546e470-dcf8-4c8f-880c-6664aaac22e2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNklNblJ3amIwT2xTZjdhSWJCVTNUIiwiaWF0IjoxNzExNTA1MDcwfQ.5s8e-SxStipQf_7_8hC0kIaxAIDCgJqthJkLK22U0cE', '2024-04-26 10:04:30', '2024-03-27 10:04:30.047305', '2a5358f2-a465-4241-834f-271f72fbf947'); +INSERT INTO `user_refresh_tokens` VALUES ('97e4ba1d-8a09-447c-967c-e9110c7697f6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoieTYzNlY2R3JjS3cxeEZZQUJJU2VEIiwiaWF0IjoxNzEwMjA1MzUyfQ.95FHvqWgjxSPnmhcTXFlSc27f4uzUlFCoAjvdBqDLhk', '2024-04-11 09:02:33', '2024-03-12 09:02:32.691301', '59b32e05-ed47-4634-adf2-86076aac9274'); +INSERT INTO `user_refresh_tokens` VALUES ('9b349285-f4b9-41a6-944b-2c3c4e7dae60', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaGdlM0NJQTVKNTRDR2ZqbWkyMzJIIiwiaWF0IjoxNzExNTA4NjUzfQ.JNYJI2F7NUEaNjVLAf7iVaUVMiJ6vUNdpsCZrfuQHEg', '2024-04-26 11:04:14', '2024-03-27 11:04:13.816062', 'cc1cc297-6be6-4267-9aec-fa45e59710eb'); +INSERT INTO `user_refresh_tokens` VALUES ('a545b874-66c2-496b-80d6-2e2d7c80f54c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaS1ZdEJnOWx1RVMwR2J4SkQxMTVIIiwiaWF0IjoxNzEwODExMzQzfQ.PQtYDGzsAjq-9UGwsT39xrZeqNQrf-7J4YFiby_4Pvo', '2024-04-18 09:22:23', '2024-03-19 09:22:27.254054', '1added11-2ae5-4e39-bfd1-3972d3d95ad6'); +INSERT INTO `user_refresh_tokens` VALUES ('ac390ee3-d586-43ff-aab0-e4820e585fc6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTXMybFdMWndZbUxsbnhrUlRKYmFoIiwiaWF0IjoxNzExNDM4MTkxfQ.HGoplU0yWFYkiSQB2nfkTsys-gydpCLMmJNmJIhHqOU', '2024-04-25 15:29:51', '2024-03-26 15:29:48.535514', 'cc472002-f409-4273-aa55-dde97cb1ac29'); +INSERT INTO `user_refresh_tokens` VALUES ('ae713a5b-7336-4a9b-953c-9f4c010229bb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoial9hb184RGhfX2o0ZF9pdG50U0pCIiwiaWF0IjoxNzEwNzUxNTUyfQ.e6D-WvoL8W-EfuKS7cxo507YMY8agjj6B5VGldBwz24', '2024-04-17 16:45:53', '2024-03-18 16:45:53.602709', 'd18de5f3-f114-4f19-8981-5ef576706a60'); +INSERT INTO `user_refresh_tokens` VALUES ('b04b1fa0-c9a1-42c6-92ad-4c006acd47b5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQ3VzQkt2b01JSEtVVWJrNkVLTUJxIiwiaWF0IjoxNzEwODEwMDcwfQ.BAVsxgpTk1stXBo_7nPiGF7FRPBLfrOauisyQuJbM7g', '2024-04-18 09:01:10', '2024-03-19 09:01:14.248119', 'eebefeae-565c-44c5-b43c-a2eee2077b5b'); +INSERT INTO `user_refresh_tokens` VALUES ('b3e24d0e-75ba-4e52-80c8-efd4c359aa78', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMnV3dGhKVmthMndzNlFVYktVLXRCIiwiaWF0IjoxNzExMDAxNjMxfQ.Njo3WEb8vU_I0-kMDd0HWK0DhwIdUku9nxOP5h8NMHA', '2024-04-20 14:13:52', '2024-03-21 14:13:54.053415', '3e0918fa-04c2-45df-858e-34d725c21a25'); +INSERT INTO `user_refresh_tokens` VALUES ('b4c414c5-fbe1-44a6-99a6-429f29322b67', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibHk0ZmdacWtqcEtLX3FueWNVQ29SIiwiaWF0IjoxNzExNDM4MzI3fQ.uugw-52NPiCfw4vWPhVo8BUoYcAiMiTI7yNwvHuBh2g', '2024-04-25 15:32:07', '2024-03-26 15:32:04.561632', 'f87de777-667c-4add-938b-d121d69361f6'); +INSERT INTO `user_refresh_tokens` VALUES ('b9fbe7eb-638f-4c9a-8c21-d95062bd7bd9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQkRaNWd1ekJ0UVY0Vk43M1pScUJzIiwiaWF0IjoxNzEwMjI0MDY3fQ.IzUJ_rUGyqXyusRgZGW8LvRb4C9ELZQ8LIJ24JEWgx4', '2024-04-11 14:14:27', '2024-03-12 14:14:27.150768', '7c7a14dc-5cc7-4fc9-922f-745b916a2e8b'); +INSERT INTO `user_refresh_tokens` VALUES ('bc30efde-ded0-456e-95e3-8d6c6e0e321b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQUZBdk45ZTB6Rm1HcHNuYWVrdHp1IiwiaWF0IjoxNzEwODEyMzgwfQ.OXLTRX_0LPY_9jx0LJ8l4UE9hExvmTnASAbQgEofOtA', '2024-04-18 09:39:40', '2024-03-19 09:39:44.327657', '3e6845c8-9cc3-492e-bcd7-9636b00d5631'); +INSERT INTO `user_refresh_tokens` VALUES ('cacae50f-eb2f-43a6-82bd-1811a717c111', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMTg1X0xTN2F6SVlOa2FmT1ZsWEVqIiwiaWF0IjoxNzExMzM4MDIxfQ.j-kiCfCcZELGLKWf9FRr_GbOXaMAWz6ysjwo2GPhDns', '2024-04-24 11:40:22', '2024-03-25 11:40:21.412052', 'e0b66a0d-2522-4423-8a04-81e282520f2f'); +INSERT INTO `user_refresh_tokens` VALUES ('d0215443-a830-47ee-a75e-13502f411433', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTEp0bGp3R0RFMGFkY0ZOX3h1dGJoIiwiaWF0IjoxNzEwODI2Nzg4fQ.XNw0G7z9AhKZKdk05InMQIeRJLNYRKEk1CCI-qHaMGU', '2024-04-18 13:39:49', '2024-03-19 13:39:49.834145', '835e7377-9633-444e-9522-ec6df447ff79'); +INSERT INTO `user_refresh_tokens` VALUES ('d0e92ef9-4ee0-41f8-873c-a680b20a43df', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZDRxNVR4ZU1rWWNJMGNYbnJET1JfIiwiaWF0IjoxNzEwODM3MTU5fQ.SmfXw9Imy_z2wV-jOcmQ6g1gtfBVDMf3egw0bMcFf9w', '2024-04-18 16:32:40', '2024-03-19 16:32:41.346471', '0233ea11-41af-453a-b8ed-b9731b252d05'); +INSERT INTO `user_refresh_tokens` VALUES ('d7d1c449-e07c-4700-bab0-f713cbcc3a78', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZkE4OWRwMVIweGFYWE9vV09KTWVfIiwiaWF0IjoxNzEwMTQ4MTA0fQ.ZhoxEyMwPEkJybPS32ERrwiaG0ghfGLwbUdy9otrQp4', '2024-04-10 17:08:24', '2024-03-11 17:08:24.311139', '202c3f1c-6068-427f-8c75-50b24f93a5c8'); +INSERT INTO `user_refresh_tokens` VALUES ('d9767c5f-e5f1-4fdc-a04c-25ff3efcffe1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWXI1bjFsaDExcVN2TVBiOTJVcVJXIiwiaWF0IjoxNzExMDY3MzE5fQ.9iNQ4XzkiWcX2wh1hVtn04ZD11CA72bYnW73KMf3ng8', '2024-04-21 08:28:40', '2024-03-22 08:28:39.885024', 'faa26fea-b099-4edf-a9ca-642f06a4e8c1'); +INSERT INTO `user_refresh_tokens` VALUES ('e198a609-85fd-40da-b5dc-945e11714358', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicWNWRXJfYnpRbExxNjRCOVhMdFdOIiwiaWF0IjoxNzEwMjAzNTU1fQ.cYDa78wUzAus4AKBLJZ0mBCKcGpovG-45-zTUGnhYG0', '2024-04-11 08:32:35', '2024-03-12 08:32:35.344070', 'ab1e415f-d811-47ea-9226-86684b71060f'); +INSERT INTO `user_refresh_tokens` VALUES ('e208e8f8-dab2-499d-b8c1-868e27e455d1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOGlhYWlkcmotU2V6eGk0Vm05Z1g5IiwiaWF0IjoxNzExNTE4NjE4fQ.1JxM1QNvt0k4eq9npx55993acHLSRYJ2XXulKEGEl04', '2024-04-26 13:50:18', '2024-03-27 13:50:18.624158', '9b8a542b-f702-4fa9-87ce-ca87d3063dac'); +INSERT INTO `user_refresh_tokens` VALUES ('e3ae9984-0abc-4d14-a111-287b93dff0f3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ0dtUkxxVXdpOXhTMkNMcFg4amR3IiwiaWF0IjoxNzEwODM4MDIzfQ.Jy_zuiNofB8w7iEKV-S72pm4RGYc6xgorQvSRR5ZP_U', '2024-04-18 16:47:03', '2024-03-19 16:47:04.913410', '34463fa6-75ab-4fc2-ae16-b2e537c8b36d'); +INSERT INTO `user_refresh_tokens` VALUES ('e4adc089-f257-478f-97f0-8b42ce129a8d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiSkJFOTg2a0xFaG94aVllRV9XT1NFIiwiaWF0IjoxNzExNDM4Mjk0fQ.nYOVp0WwjMl_tZaX-su3xtVuZsAnbN5s8YlX-gq1oDs', '2024-04-25 15:31:34', '2024-03-26 15:31:31.685709', '2af24ef8-886d-4510-b8be-816023f2e517'); +INSERT INTO `user_refresh_tokens` VALUES ('e6c586df-a32d-45e2-8f7b-6523d2cee2b2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUjZrQ0VpWTRrYWhPVVdIVzhHN0IzIiwiaWF0IjoxNzExNDM5Njg3fQ.HNxzaw5Ycf0zMImPhbKkV20uCrzn04Vd9Y0BU1g3i2s', '2024-04-25 15:54:48', '2024-03-26 15:54:45.113890', '85ca2577-5bfe-42f2-b1a5-f2342ab0b42a'); +INSERT INTO `user_refresh_tokens` VALUES ('ea7f6fe0-ec8a-43e1-8ebc-ab0c228cbed3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiM1RLN2hHRVRoRS1DXzBsdjBIcEV3IiwiaWF0IjoxNzExMDY4NTIxfQ.o-Hs0yjcCF-WbpIi72VM380Bri-awOx-nCNo9Yp9oRE', '2024-04-21 08:48:41', '2024-03-22 08:48:40.820075', 'b140cb61-64a4-4e5f-b278-5d1764f28524'); +INSERT INTO `user_refresh_tokens` VALUES ('eaaccc99-f345-4df0-8531-10fd5e0da753', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUGNKX3YyQU56ZTVPeDkta2Y3cU1qIiwiaWF0IjoxNzEwODEyNDMwfQ.jhwk0CSQ6rlzy17aqei586kNJkXb2kpSu-9wZ3YuUsY', '2024-04-18 09:40:30', '2024-03-19 09:40:34.009354', 'aa08e3d0-b1ab-410f-8504-0ae8e2504864'); +INSERT INTO `user_refresh_tokens` VALUES ('ec2dbc1b-aa6e-42bf-be3f-9c1fdd230340', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUU9US0l5LU9SZWFRdHI1TVdEM195IiwiaWF0IjoxNzEwODExNzM5fQ.3eXDFyZZPKxnHCuriw0dmeqf6GUOnc_S3uOdC4Oxuy4', '2024-04-18 09:28:59', '2024-03-19 09:29:03.177688', 'e7ba638f-c89f-47e9-bd67-8918a7cb8cc3'); +INSERT INTO `user_refresh_tokens` VALUES ('f1a3e670-9d12-44a6-8b83-b0e0778100ad', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibzJmWWxkQTZGV0w3M1lRR09lRnVHIiwiaWF0IjoxNzExMDY4NzQ5fQ.PD5lAFhnCc69EFqvR2JYBGej7cbn6sFuhG5sXp0gMFU', '2024-04-21 08:52:30', '2024-03-22 08:52:29.476656', 'c519656a-1312-4074-990d-e9577c000278'); +INSERT INTO `user_refresh_tokens` VALUES ('f4fce7f4-fbe3-46b2-a75b-ba11fb647e5c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiamhLa0xfb1NXdUJ5TkdOQ1FYRGd5IiwiaWF0IjoxNzEwODExMjE2fQ.dEeCaXdTog4G9lmJHCCkzWD_QVUQRO1rrlmlBVq_LXQ', '2024-04-18 09:20:16', '2024-03-19 09:20:20.331443', '29c99bed-2ad0-455d-9085-9e59192e2426'); +INSERT INTO `user_refresh_tokens` VALUES ('f7ef3cda-6ff7-4f45-aab8-b9be92fbcece', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiclpYRzhxdTh0d09DUkQ2VXdhSmN4IiwiaWF0IjoxNzExNDM4MjE4fQ.aIWdev__QoIH4swvCYYR3gopPB__Nnx6VohCCM3pWi4', '2024-04-25 15:30:19', '2024-03-26 15:30:16.107682', 'b7f0d7b7-eb71-4eb1-a47d-88c444a44b2f'); +INSERT INTO `user_refresh_tokens` VALUES ('f83618c0-574f-47bc-a98b-2758ff129956', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLUVIWWhpSmNBRDRUaEVkVVZWRGJqIiwiaWF0IjoxNzEwOTg1MjIzfQ.pRRd0XCTS8qVOATSdgcPPNbhoA9t2v8AV8hUkn2n6UY', '2024-04-20 09:40:23', '2024-03-21 09:40:24.889886', 'b85f77c8-fd9c-4f97-b680-666be5b2deb6'); +INSERT INTO `user_refresh_tokens` VALUES ('fdb751bd-22fd-40bd-8fb6-bdfc1ea64a2a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN2E5ZE9KVndsdzlYWVdLbmt5ck9IIiwiaWF0IjoxNzExMDczOTA2fQ.YUoi63l27GMv2A8QVPO73KrkRLkG9xUWV5scac3lhSQ', '2024-04-21 10:18:26', '2024-03-22 10:18:26.309973', '5bb267d3-8f88-4a1c-90cc-b36c5bf156fa'); -- ---------------------------- -- Table structure for vehicle_usage @@ -1152,24 +1421,49 @@ CREATE TABLE `vehicle_usage` ( `id` int NOT NULL AUTO_INCREMENT, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `reviewer` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '审核人', + `status` tinyint NOT NULL DEFAULT 0 COMMENT '审核状态(字典)', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `year` int NOT NULL COMMENT '年度', - `vehicle_license` int NOT NULL COMMENT '外出使用的车辆名称(字典)', + `vehicle_id` int NOT NULL COMMENT '外出使用的车辆名称(字典)', `applicant` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '申请人', - `driver` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '出行司机', + `driver` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '出行司机', `current_mileage` int NULL DEFAULT NULL COMMENT '当前车辆里程数(KM)', `expected_start_date` date NULL DEFAULT NULL COMMENT '预计出行开始时间', `expected_end_date` date NULL DEFAULT NULL COMMENT '预计出行结束时间', `purpose` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '使用事由', `actual_return_time` date NULL DEFAULT NULL COMMENT '实际回司时间', `return_mileage` int NULL DEFAULT NULL COMMENT '回城车辆里程数(KM)', - `reviewer` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '审核人', - `status` tinyint NOT NULL DEFAULT 0 COMMENT '审核状态(字典)', - `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + `partner` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '随行人员', + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_6aff0ec40ff474e6228c1125f5c`(`vehicle_id`) USING BTREE, + CONSTRAINT `FK_6aff0ec40ff474e6228c1125f5c` FOREIGN KEY (`vehicle_id`) REFERENCES `sys_dict_item` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of vehicle_usage -- ---------------------------- +INSERT INTO `vehicle_usage` VALUES (1, '2024-03-08 09:42:45.944580', '2024-03-08 10:23:13.000000', '邹姐', 1, NULL, 2023, 24, '卢大仙', '', 35001, '2024-03-08', '2024-03-15', '接客户', '2024-03-15', 35010, NULL); +INSERT INTO `vehicle_usage` VALUES (2, '2024-03-08 09:51:29.014799', '2024-03-08 09:51:29.014799', NULL, 0, NULL, 2024, 25, '卢大仙', NULL, NULL, '2024-02-27', '2024-02-27', NULL, NULL, NULL, NULL); + +-- ---------------------------- +-- Table structure for vehicle_usage_storage +-- ---------------------------- +DROP TABLE IF EXISTS `vehicle_usage_storage`; +CREATE TABLE `vehicle_usage_storage` ( + `vehicle_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`vehicle_id`, `file_id`) USING BTREE, + INDEX `IDX_1d122393de1ee773c383569e71`(`vehicle_id`) USING BTREE, + INDEX `IDX_a8cbcb6835a9212dd2a49b50ed`(`file_id`) USING BTREE, + CONSTRAINT `FK_1d122393de1ee773c383569e717` FOREIGN KEY (`vehicle_id`) REFERENCES `vehicle_usage` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_a8cbcb6835a9212dd2a49b50ed9` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of vehicle_usage_storage +-- ---------------------------- +INSERT INTO `vehicle_usage_storage` VALUES (1, 146); +INSERT INTO `vehicle_usage_storage` VALUES (1, 147); SET FOREIGN_KEY_CHECKS = 1; From c32267e1165d9846de7318b8b54378bd377ade8b Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 28 Mar 2024 13:17:08 +0800 Subject: [PATCH 40/64] fix: git --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index b3626e6..3c1df4f 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,4 @@ out # temp data __data - -public/upload/* types/env.d.ts \ No newline at end of file From 65ed63312731d8faa5de86f4b76d35aeb8dfec16 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 28 Mar 2024 13:17:26 +0800 Subject: [PATCH 41/64] fix: upload --- public/upload/1.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/upload/1.txt diff --git a/public/upload/1.txt b/public/upload/1.txt new file mode 100644 index 0000000..e69de29 From 8800008d270d17fc913d3db5cbe2fd22b2710023 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 28 Mar 2024 13:18:10 +0800 Subject: [PATCH 42/64] fix: git upload --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3c1df4f..b3626e6 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,6 @@ out # temp data __data + +public/upload/* types/env.d.ts \ No newline at end of file From fdc6af9e20a3c20f5859ea2ce3ab02c808ebf0ea Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 28 Mar 2024 14:35:00 +0800 Subject: [PATCH 43/64] feat: users filter --- README.md | 2 +- src/modules/user/dto/user.dto.ts | 5 +++++ src/modules/user/user.service.ts | 13 +++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 04406fe..f08f061 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ docker rmi huaxin-admin-server:stable ```bash pnpm docker:logs # or -docker compose --env-file .env --env-file .env.production logs -f +docker-compose --env-file .env --env-file .env.production logs -f ``` diff --git a/src/modules/user/dto/user.dto.ts b/src/modules/user/dto/user.dto.ts index 3b2c3c7..f7efc73 100644 --- a/src/modules/user/dto/user.dto.ts +++ b/src/modules/user/dto/user.dto.ts @@ -95,4 +95,9 @@ export class UserQueryDto extends IntersectionType(PagerDto, PartialTyp @IsInt() @IsOptional() status?: number; + + @ApiProperty({ description: '关键字' }) + @IsString() + @IsOptional() + keyword?: string; } diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index a3a2867..afb8345 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -249,7 +249,8 @@ export class UserService { nickname, deptId, email, - status + status, + keyword }: UserQueryDto): Promise> { const queryBuilder = this.userRepository .createQueryBuilder('user') @@ -264,7 +265,15 @@ export class UserService { }); if (deptId) queryBuilder.andWhere('dept.id = :deptId', { deptId }); - + if (keyword) { + //关键字模糊查询product的name,productNumber,productSpecification + queryBuilder.andWhere( + '(user.name like :keyword or dept.name like :keyword)', + { + keyword: `%${keyword}%` + } + ); + } return paginate(queryBuilder, { page, pageSize From 59b77cdbc51b055ce8ff93179dc50732aea812b5 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 28 Mar 2024 15:12:09 +0800 Subject: [PATCH 44/64] fix: user search --- src/modules/user/user.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index afb8345..aa4ee6c 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -268,7 +268,7 @@ export class UserService { if (keyword) { //关键字模糊查询product的name,productNumber,productSpecification queryBuilder.andWhere( - '(user.name like :keyword or dept.name like :keyword)', + '(user.nickname like :keyword or dept.name like :keyword)', { keyword: `%${keyword}%` } From 072777b5e583231bfb952603258fd5349b3e273c Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 29 Mar 2024 14:41:54 +0800 Subject: [PATCH 45/64] feat: project filter --- src/modules/auth/dto/auth.dto.ts | 2 +- .../in_out/materials_in_out.service.ts | 157 +++++++++--------- .../materials_inventory.dto.ts | 5 + .../materials_inventory.service.ts | 32 ++-- 4 files changed, 102 insertions(+), 94 deletions(-) diff --git a/src/modules/auth/dto/auth.dto.ts b/src/modules/auth/dto/auth.dto.ts index 0919641..f6ec215 100644 --- a/src/modules/auth/dto/auth.dto.ts +++ b/src/modules/auth/dto/auth.dto.ts @@ -9,7 +9,7 @@ export class LoginDto { @ApiProperty({ description: '密码', example: 'a123456' }) @IsString() - @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/) + @Matches(/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Za-z])\S*$/, { message: '密码错误' }) @MinLength(6) password: string; diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 82e4857..dfe9264 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -103,7 +103,7 @@ export class MaterialsInOutService { position, unitPrice, quantity, - productId + productId } = dto; inventoryInOutNumber = await this.generateInventoryInOutNumber(inOrOut); let newRecordId; @@ -130,84 +130,85 @@ export class MaterialsInOutService { */ async update(id: number, { fileIds, ...data }: Partial): Promise { await this.entityManager.transaction(async manager => { - const entity = await manager.findOne(MaterialsInOutEntity, { - where: { - id - }, - lock: { mode: 'pessimistic_write' } - }); - + /* 暂时不允许更改金额和数量,以及不能影响库存变化, */ + // const entity = await manager.findOne(MaterialsInOutEntity, { + // where: { + // id + // }, + // lock: { mode: 'pessimistic_write' } + // }); + // // 修改入库记录的价格,会直接更改库存实际价格. // 1.如果有了出库记录,不允许修改入库价格。 - if ( - Object.is(data.inOrOut, MaterialsInOrOutEnum.In) && - isDefined(data.unitPrice) && - Math.abs(Number(data.unitPrice) - Number(entity.unitPrice)) !== 0 - ) { - const outEntity = await manager.findOne(MaterialsInOutEntity, { - where: { - unitPrice: entity.unitPrice, - inOrOut: MaterialsInOrOutEnum.Out, - projectId: entity.projectId, - productId: entity.productId - } - }); - if (isDefined(outEntity)) { - throw new BusinessException(ErrorEnum.MATERIALS_IN_OUT_UNIT_PRICE_CANNOT_BE_MODIFIED); - } - await ( - Object.is(data.inOrOut, MaterialsInOrOutEnum.In) - ? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService) - : this.materialsInventoryService.outInventory.bind(this.materialsInventoryService) - )( - { - productId: entity.productId, - quantity: 0, - unitPrice: entity.unitPrice, - projectId: entity.projectId, - changedUnitPrice: data.unitPrice - }, - manager - ); - } + // if ( + // Object.is(data.inOrOut, MaterialsInOrOutEnum.In) && + // isDefined(data.unitPrice) && + // Math.abs(Number(data.unitPrice) - Number(entity.unitPrice)) !== 0 + // ) { + // const outEntity = await manager.findOne(MaterialsInOutEntity, { + // where: { + // unitPrice: entity.unitPrice, + // inOrOut: MaterialsInOrOutEnum.Out, + // projectId: entity.projectId, + // productId: entity.productId + // } + // }); + // if (isDefined(outEntity)) { + // throw new BusinessException(ErrorEnum.MATERIALS_IN_OUT_UNIT_PRICE_CANNOT_BE_MODIFIED); + // } + // await ( + // Object.is(data.inOrOut, MaterialsInOrOutEnum.In) + // ? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService) + // : this.materialsInventoryService.outInventory.bind(this.materialsInventoryService) + // )( + // { + // productId: entity.productId, + // quantity: 0, + // unitPrice: entity.unitPrice, + // projectId: entity.projectId, + // changedUnitPrice: data.unitPrice + // }, + // manager + // ); + // } - let changedQuantity = 0; - if (isDefined(data.quantity) && entity.quantity !== data.quantity) { - if (entity.inOrOut === MaterialsInOrOutEnum.In) { - // 入库减少等于出库 - if (data.quantity - entity.quantity < 0) { - data.inOrOut = MaterialsInOrOutEnum.Out; - } else { - // 入库增多等于入库 - data.inOrOut = MaterialsInOrOutEnum.In; - } - } else { - // 出库减少等于入库 - if (data.quantity - entity.quantity < 0) { - data.inOrOut = MaterialsInOrOutEnum.In; - } else { - // 出库增多等于出库 - data.inOrOut = MaterialsInOrOutEnum.Out; - } - } - changedQuantity = Math.abs(data.quantity - entity.quantity); - } - // 2.更新增减库存 - if (changedQuantity !== 0) { - await ( - Object.is(data.inOrOut, MaterialsInOrOutEnum.In) - ? this.materialsInventoryService.inInventory - : this.materialsInventoryService.outInventory - )( - { - productId: entity.productId, - quantity: Math.abs(changedQuantity), - unitPrice: undefined, - projectId: entity.projectId - }, - manager - ); - } + // let changedQuantity = 0; + // if (isDefined(data.quantity) && entity.quantity !== data.quantity) { + // if (entity.inOrOut === MaterialsInOrOutEnum.In) { + // // 入库减少等于出库 + // if (data.quantity - entity.quantity < 0) { + // data.inOrOut = MaterialsInOrOutEnum.Out; + // } else { + // // 入库增多等于入库 + // data.inOrOut = MaterialsInOrOutEnum.In; + // } + // } else { + // // 出库减少等于入库 + // if (data.quantity - entity.quantity < 0) { + // data.inOrOut = MaterialsInOrOutEnum.In; + // } else { + // // 出库增多等于出库 + // data.inOrOut = MaterialsInOrOutEnum.Out; + // } + // } + // changedQuantity = Math.abs(data.quantity - entity.quantity); + // } + // // 2.更新增减库存 + // if (changedQuantity !== 0) { + // await ( + // Object.is(data.inOrOut, MaterialsInOrOutEnum.In) + // ? this.materialsInventoryService.inInventory + // : this.materialsInventoryService.outInventory + // )( + // { + // productId: entity.productId, + // quantity: Math.abs(changedQuantity), + // unitPrice: undefined, + // projectId: entity.projectId + // }, + // manager + // ); + // } // 完成所有业务逻辑后,更新出入库记录 await manager.update(MaterialsInOutEntity, id, { ...data @@ -254,10 +255,8 @@ export class MaterialsInOutService { : this.materialsInventoryService.inInventory.bind(this.materialsInventoryService) )( { - productId: entity.productId, quantity: entity.quantity, - unitPrice: entity.unitPrice, - projectId: entity.projectId + inventoryId: entity.inventoryId }, manager ); diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts index f4111ee..256167b 100644 --- a/src/modules/materials_inventory/materials_inventory.dto.ts +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -40,6 +40,11 @@ export class MaterialsInventoryQueryDto extends IntersectionType( @IsOptional() @IsEnum(HasInventoryStatusEnum) isHasInventory: HasInventoryStatusEnum; + + @ApiProperty({ description: '项目Id' }) + @IsOptional() + @IsNumber() + projectId: number; } export class MaterialsInventoryExportDto { @ApiProperty({ description: '项目' }) diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index 435bd9b..fd4a8d8 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -318,6 +318,7 @@ export class MaterialsInventoryService { pageSize, product, keyword, + projectId, isHasInventory }: MaterialsInventoryQueryDto): Promise> { const queryBuilder = this.materialsInventoryRepository @@ -342,6 +343,11 @@ export class MaterialsInventoryService { if (product) { queryBuilder.andWhere('product.name like :product', { product: `%${product}%` }); } + + if (projectId) { + queryBuilder.andWhere('project.id = :projectId', { projectId }); + } + if (keyword) { queryBuilder.andWhere( '(materialsInventory.inventoryNumber like :keyword or product.name like :keyword or product.productNumber like :keyword or product.productSpecification like :keyword)', @@ -392,6 +398,7 @@ export class MaterialsInventoryService { projectId: number; productId: number; quantity: number; + inventoryId?: number; unitPrice?: number; changedUnitPrice?: number; }, @@ -403,11 +410,17 @@ export class MaterialsInventoryService { quantity: inQuantity, unitPrice, changedUnitPrice, - position + position, + inventoryId } = data; - + let searchPayload: any = {}; + if (isDefined(inventoryId)) { + searchPayload = { id: inventoryId }; + } else { + searchPayload = { projectId, productId, unitPrice }; + } const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, { - where: { projectId, productId, unitPrice }, // 根据项目,产品,价格查出之前的实时库存情况 + where: searchPayload, // 根据项目,产品,价格查出之前的实时库存情况 lock: { mode: 'pessimistic_write' } // 开启悲观行锁,防止脏读和修改 }); @@ -447,22 +460,13 @@ export class MaterialsInventoryService { data: { quantity: number; inventoryId?: number; - productId: number; - unitPrice?: number; }, manager: EntityManager ): Promise { - const { quantity: outQuantity, inventoryId, productId, unitPrice } = data; - let searchPayload: any = {}; - if (inventoryId) { - searchPayload.id = inventoryId; - } else { - // 删除出入库记录时,需要根据产品ID和价格查找库存 - searchPayload = { productId, unitPrice }; - } + const { quantity: outQuantity, inventoryId } = data; // 开启悲观行锁,防止脏读和修改 const inventory = await manager.findOne(MaterialsInventoryEntity, { - where: searchPayload, + where: { id: inventoryId }, lock: { mode: 'pessimistic_write' } }); // 检查库存剩余 From 1399835845a32942168dd8c684a6a9820e964788 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 08:57:27 +0800 Subject: [PATCH 46/64] feat: optimize inventory feature --- .env.development | 4 +- src/constants/error-code.constant.ts | 5 +- .../in_out/materials_in_out.entity.ts | 2 +- .../in_out/materials_in_out.service.ts | 98 +++++++++++-------- .../materials_inventory.service.ts | 2 +- .../param-config/param-config.controller.ts | 10 +- 6 files changed, 72 insertions(+), 49 deletions(-) diff --git a/.env.development b/.env.development index 673dab6..78a78df 100644 --- a/.env.development +++ b/.env.development @@ -13,7 +13,7 @@ SWAGGER_PATH = api-docs SWAGGER_VERSION = 1.0 # db -DB_HOST = 192.168.60.39 +DB_HOST = localhost DB_PORT = 13307 DB_DATABASE = hxoa DB_USERNAME = root @@ -23,7 +23,7 @@ DB_LOGGING = "all" # redis REDIS_PORT = 6379 -REDIS_HOST = 192.168.60.39 +REDIS_HOST = localhost REDIS_PASSWORD = 123456 REDIS_DB = 0 diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 0b6be78..40cacd8 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -58,7 +58,8 @@ export enum ErrorEnum { CONTRACT_NUMBER_EXIST = '1407:存在相同的合同编号', // Inventory - INVENTORY_INSUFFICIENT = '1408:库存不足', + INVENTORY_INSUFFICIENT = '1408:库存数量不足。请检查库存或重新操作', MATERIALS_IN_OUT_NOT_FOUND = '1409:出入库信息不存在', - MATERIALS_IN_OUT_UNIT_PRICE_CANNOT_BE_MODIFIED = '1410:该价格的产品已经出库,单价不允许修改。若有疑问,请联系管理员' + MATERIALS_IN_OUT_UNIT_PRICE_CANNOT_BE_MODIFIED = '1410:该价格的产品已经出库,单价不允许修改。若有疑问,请联系管理员', + MATERIALS_IN_OUT_UNIT_PRICE_MUST_ZERO_WHEN_MODIFIED = '1411:只能修改初始单价为0的入库记录。 若有疑问,请联系管理员' } diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index 56c4057..6feef1f 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -57,7 +57,7 @@ export class MaterialsInOutEntity extends CommonEntity { @Column({ name: 'time', - type: 'date', + type: 'datetime', nullable: true, comment: '时间' }) diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index dfe9264..3b3c318 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; -import { EntityManager, Repository } from 'typeorm'; +import { EntityManager, In, Repository } from 'typeorm'; import { Pagination } from '~/helper/paginate/pagination'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; @@ -79,6 +79,7 @@ export class MaterialsInOutService { .addSelect([ 'inventory.id', 'inventory.position', + 'inventory.inventoryNumber', 'files.id', 'files.path', 'project.name', @@ -103,7 +104,7 @@ export class MaterialsInOutService { position, unitPrice, quantity, - productId + productId } = dto; inventoryInOutNumber = await this.generateInventoryInOutNumber(inOrOut); let newRecordId; @@ -131,47 +132,60 @@ export class MaterialsInOutService { async update(id: number, { fileIds, ...data }: Partial): Promise { await this.entityManager.transaction(async manager => { /* 暂时不允许更改金额和数量,以及不能影响库存变化, */ - // const entity = await manager.findOne(MaterialsInOutEntity, { - // where: { - // id - // }, - // lock: { mode: 'pessimistic_write' } - // }); - // - // 修改入库记录的价格,会直接更改库存实际价格. - // 1.如果有了出库记录,不允许修改入库价格。 - // if ( - // Object.is(data.inOrOut, MaterialsInOrOutEnum.In) && - // isDefined(data.unitPrice) && - // Math.abs(Number(data.unitPrice) - Number(entity.unitPrice)) !== 0 - // ) { - // const outEntity = await manager.findOne(MaterialsInOutEntity, { - // where: { - // unitPrice: entity.unitPrice, - // inOrOut: MaterialsInOrOutEnum.Out, - // projectId: entity.projectId, - // productId: entity.productId - // } - // }); - // if (isDefined(outEntity)) { - // throw new BusinessException(ErrorEnum.MATERIALS_IN_OUT_UNIT_PRICE_CANNOT_BE_MODIFIED); - // } - // await ( - // Object.is(data.inOrOut, MaterialsInOrOutEnum.In) - // ? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService) - // : this.materialsInventoryService.outInventory.bind(this.materialsInventoryService) - // )( - // { - // productId: entity.productId, - // quantity: 0, - // unitPrice: entity.unitPrice, - // projectId: entity.projectId, - // changedUnitPrice: data.unitPrice - // }, - // manager - // ); - // } + const entity = await manager.findOne(MaterialsInOutEntity, { + where: { + id + }, + lock: { mode: 'pessimistic_write' } + }); + // 修改入库记录的价格 + // 1.会直接更改库存实际价格.(仅仅只能之前价格为0时可以修改) + // 2.会同步库存所有的出库记录,修改其单价和金额. + if ( + Object.is(data.inOrOut, MaterialsInOrOutEnum.In) && + isDefined(data.unitPrice) && + Math.abs(Number(data.unitPrice) - Number(entity.unitPrice)) !== 0 + ) { + if (entity.unitPrice != 0) { + throw new BusinessException( + ErrorEnum.MATERIALS_IN_OUT_UNIT_PRICE_MUST_ZERO_WHEN_MODIFIED + ); + } + const outEntities = await manager.find(MaterialsInOutEntity, { + where: { + inventoryId: entity.inventoryId, + inOrOut: MaterialsInOrOutEnum.Out + } + }); + if (outEntities?.length > 0) { + await manager.update( + MaterialsInOutEntity, + { + id: In(outEntities.map(item => item.id)) + }, + { + unitPrice: data.unitPrice, + amount: () => `quantity * ${data.unitPrice}` + } + ); + } + await manager.update(MaterialsInventoryEntity, entity.inventoryId, { + unitPrice: data.unitPrice + }); + } + // 修改入库时的项目,必须同步到库存项目中 + if ( + Object.is(data.inOrOut, MaterialsInOrOutEnum.In) && + isDefined(data.projectId) && + data.projectId != entity.projectId + ) { + await manager.update(MaterialsInventoryEntity, entity.inventoryId, { + projectId: data.projectId + }); + } + + // 暂时不允许修改数量 // let changedQuantity = 0; // if (isDefined(data.quantity) && entity.quantity !== data.quantity) { // if (entity.inOrOut === MaterialsInOrOutEnum.In) { diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index fd4a8d8..bcae7e7 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -477,7 +477,7 @@ export class MaterialsInventoryService { let { quantity, id } = inventory; const newQuantity = calcNumber(quantity || 0, outQuantity || 0, 'subtract'); if (isNaN(newQuantity)) { - throw new Error('库存数量不足。请检查库存或重新操作。'); + throw new BusinessException(ErrorEnum.INVENTORY_INSUFFICIENT); } await manager.update(MaterialsInventoryEntity, id, { quantity: newQuantity diff --git a/src/modules/system/param-config/param-config.controller.ts b/src/modules/system/param-config/param-config.controller.ts index baaddd7..b93fd70 100644 --- a/src/modules/system/param-config/param-config.controller.ts +++ b/src/modules/system/param-config/param-config.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Post, Query } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiResult } from '~/common/decorators/api-result.decorator'; @@ -41,6 +41,14 @@ export class ParamConfigController { await this.paramConfigService.create(dto); } + @Get('key/:code') + @ApiOperation({ summary: '查询参数配置信息By key' }) + @ApiResult({ type: String }) + @Perm(permissions.READ) + async code(@Param('code') code: string): Promise { + return this.paramConfigService.findValueByKey(code); + } + @Get(':id') @ApiOperation({ summary: '查询参数配置信息' }) @ApiResult({ type: ParamConfigEntity }) From 9eba27e81438c19224cb0f0934cc145a5999fcf2 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 10:19:46 +0800 Subject: [PATCH 47/64] opti: build docker file --- docker-compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 35e629b..395669a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,6 +53,10 @@ services: - 'host.docker.internal:host-gateway' ports: - '${APP_PORT}:${APP_PORT}' + volumes: + # 将容器中huaxin-admin/dist文件夹映射到本地的dist文件夹 + - ./public:/huaxin-admin/public + - ./node_modules:/huaxin-admin/node_modules # 当前服务启动之前先要把depends_on指定的服务启动起来才行 depends_on: - mysql From 0fa6a13644e87d9cdf59df4b15e0b144faaa5277 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 10:20:34 +0800 Subject: [PATCH 48/64] remove: default file --- public/upload/1.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 public/upload/1.txt diff --git a/public/upload/1.txt b/public/upload/1.txt deleted file mode 100644 index e69de29..0000000 From 2f5d6831abc672ce83f89c269612b2d076dbe64d Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 10:23:34 +0800 Subject: [PATCH 49/64] revert: volumes --- docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 395669a..d2404db 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,6 @@ services: volumes: # 将容器中huaxin-admin/dist文件夹映射到本地的dist文件夹 - ./public:/huaxin-admin/public - - ./node_modules:/huaxin-admin/node_modules # 当前服务启动之前先要把depends_on指定的服务启动起来才行 depends_on: - mysql From 1597fd8019a76fc41fda357d5fb8570c656ce72f Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 11:14:47 +0800 Subject: [PATCH 50/64] feat: remove common role permission limit --- src/modules/system/dict-item/dict-item.controller.ts | 4 ---- src/modules/system/dict-type/dict-type.controller.ts | 5 ----- src/modules/system/param-config/param-config.controller.ts | 3 --- 3 files changed, 12 deletions(-) diff --git a/src/modules/system/dict-item/dict-item.controller.ts b/src/modules/system/dict-item/dict-item.controller.ts index ece718c..d203265 100644 --- a/src/modules/system/dict-item/dict-item.controller.ts +++ b/src/modules/system/dict-item/dict-item.controller.ts @@ -13,9 +13,7 @@ import { DictItemDto, DictItemQueryDto } from './dict-item.dto'; import { DictItemService } from './dict-item.service'; export const permissions = definePermission('system:dict-item', { - LIST: 'list', CREATE: 'create', - READ: 'read', UPDATE: 'update', DELETE: 'delete' } as const); @@ -29,7 +27,6 @@ export class DictItemController { @Get() @ApiOperation({ summary: '获取字典项列表' }) @ApiResult({ type: [DictItemEntity], isPage: true }) - @Perm(permissions.LIST) async list(@Query() dto: DictItemQueryDto): Promise> { return this.dictItemService.page(dto); } @@ -37,7 +34,6 @@ export class DictItemController { @Get('all/:typeId') @ApiOperation({ summary: '一次性通过字典类型获取所有所属的字典项(不分页)' }) @ApiResult({ type: [DictItemEntity] }) - @Perm(permissions.LIST) async getAll(@Param('typeId') typeId: number): Promise { return this.dictItemService.getAllByType(typeId); } diff --git a/src/modules/system/dict-type/dict-type.controller.ts b/src/modules/system/dict-type/dict-type.controller.ts index d434dc8..4f54a55 100644 --- a/src/modules/system/dict-type/dict-type.controller.ts +++ b/src/modules/system/dict-type/dict-type.controller.ts @@ -13,9 +13,7 @@ import { DictTypeDto, DictTypeQueryDto } from './dict-type.dto'; import { DictTypeService } from './dict-type.service'; export const permissions = definePermission('system:dict-type', { - LIST: 'list', CREATE: 'create', - READ: 'read', UPDATE: 'update', DELETE: 'delete' } as const); @@ -29,7 +27,6 @@ export class DictTypeController { @Get() @ApiOperation({ summary: '获取字典类型列表' }) @ApiResult({ type: [DictTypeEntity], isPage: true }) - @Perm(permissions.LIST) async list(@Query() dto: DictTypeQueryDto): Promise> { return this.dictTypeService.page(dto); } @@ -37,7 +34,6 @@ export class DictTypeController { @Post('all') @ApiOperation({ summary: '一次性获取所有的字典类型(不分页)' }) @ApiResult({ type: [DictTypeEntity] }) - @Perm(permissions.LIST) async getAll(@Body() dto: DictTypeQueryDto): Promise { return this.dictTypeService.getAll(dto); } @@ -54,7 +50,6 @@ export class DictTypeController { @Get(':id') @ApiOperation({ summary: '查询字典类型信息' }) @ApiResult({ type: DictTypeEntity }) - @Perm(permissions.READ) async info(@IdParam() id: number): Promise { return this.dictTypeService.findOne(id); } diff --git a/src/modules/system/param-config/param-config.controller.ts b/src/modules/system/param-config/param-config.controller.ts index b93fd70..3214d79 100644 --- a/src/modules/system/param-config/param-config.controller.ts +++ b/src/modules/system/param-config/param-config.controller.ts @@ -28,7 +28,6 @@ export class ParamConfigController { @Get() @ApiOperation({ summary: '获取参数配置列表' }) @ApiResult({ type: [ParamConfigEntity], isPage: true }) - @Perm(permissions.LIST) async list(@Query() dto: ParamConfigQueryDto): Promise> { return this.paramConfigService.page(dto); } @@ -44,7 +43,6 @@ export class ParamConfigController { @Get('key/:code') @ApiOperation({ summary: '查询参数配置信息By key' }) @ApiResult({ type: String }) - @Perm(permissions.READ) async code(@Param('code') code: string): Promise { return this.paramConfigService.findValueByKey(code); } @@ -52,7 +50,6 @@ export class ParamConfigController { @Get(':id') @ApiOperation({ summary: '查询参数配置信息' }) @ApiResult({ type: ParamConfigEntity }) - @Perm(permissions.READ) async info(@IdParam() id: number): Promise { return this.paramConfigService.findOne(id); } From 5ac2b7b0d05d5811d90432485c0adce8f1fa22ce Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 11:24:26 +0800 Subject: [PATCH 51/64] feat: gitignore fix --- .gitignore | 5 +++-- public/upload/.gitkeep | 0 public/upload/test.txt | 0 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 public/upload/.gitkeep create mode 100644 public/upload/test.txt diff --git a/.gitignore b/.gitignore index b3626e6..118cdeb 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,7 @@ out # temp data __data - -public/upload/* +# 我想把upload文件夹传上去 +/upload/* +!/upload/.gitkeep types/env.d.ts \ No newline at end of file diff --git a/public/upload/.gitkeep b/public/upload/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/upload/test.txt b/public/upload/test.txt new file mode 100644 index 0000000..e69de29 From 8940154aee28938142e775f0a215c687e34ec2ff Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 11:25:27 +0800 Subject: [PATCH 52/64] fix: git --- .gitignore | 4 ++-- public/upload/test.txt | 0 src/modules/tools/upload/upload.dto.ts | 21 +++++++++++---------- 3 files changed, 13 insertions(+), 12 deletions(-) delete mode 100644 public/upload/test.txt diff --git a/.gitignore b/.gitignore index 118cdeb..9caffb9 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,6 @@ out # temp data __data # 我想把upload文件夹传上去 -/upload/* -!/upload/.gitkeep +/public/upload/* +!/public/upload/.gitkeep types/env.d.ts \ No newline at end of file diff --git a/public/upload/test.txt b/public/upload/test.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/tools/upload/upload.dto.ts b/src/modules/tools/upload/upload.dto.ts index e25dcdd..05f25b0 100644 --- a/src/modules/tools/upload/upload.dto.ts +++ b/src/modules/tools/upload/upload.dto.ts @@ -7,15 +7,16 @@ import { IsFile } from './file.constraint'; export class FileUploadDto { @ApiProperty({ type: Buffer, format: 'binary', description: '文件' }) - @IsDefined() - @IsFile( - { - mimetypes: ['image/png', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'], - fileSize: 1024 * 1024 * 10 - }, - { - message: '文件类型不正确' - } - ) + @IsDefined() + // @IsFile(// + // { + // mimetypes: ['image/png', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml','apk'], + // // 改成30m + // fileSize: 30 * 1024 * 1024 * 1024 * 1024// 30MB + // }, + // { + // message: '文件类型不正确' + // } + // ) file: MultipartFile; } From cdf010b96a1e2ea97bdd50a273e577c2d7970565 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 11:35:07 +0800 Subject: [PATCH 53/64] feat:update init sql --- init_data/sql/hxoa.sql | 843 ++++++++++++----------------------------- 1 file changed, 232 insertions(+), 611 deletions(-) diff --git a/init_data/sql/hxoa.sql b/init_data/sql/hxoa.sql index 0848747..1edf3cd 100644 --- a/init_data/sql/hxoa.sql +++ b/init_data/sql/hxoa.sql @@ -1,17 +1,17 @@ /* Navicat Premium Data Transfer - Source Server : 公司电脑 + Source Server : localhost Source Server Type : MySQL - Source Server Version : 80027 - Source Host : 192.168.60.39:13307 + Source Server Version : 80036 + Source Host : localhost:13307 Source Schema : hxoa Target Server Type : MySQL - Target Server Version : 80027 + Target Server Version : 80036 File Encoding : 65001 - Date: 28/03/2024 13:07:35 + Date: 07/04/2024 11:11:06 */ SET NAMES utf8mb4; @@ -29,13 +29,13 @@ CREATE TABLE `company` ( `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_a76c5cd486f7779bd9c319afd2`(`name`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of company -- ---------------------------- INSERT INTO `company` VALUES (1, '2024-03-04 15:44:43.005593', '2024-03-08 08:48:37.000000', '深圳市立创电子商务有限公司', 0); -INSERT INTO `company` VALUES (4, '2024-03-04 16:05:34.701780', '2024-03-04 16:05:34.701780', '深圳市诚亨泰科技有限公司', 0); +INSERT INTO `company` VALUES (4, '2024-03-04 16:05:34.701780', '2024-04-07 09:54:56.000000', '深圳市诚亨泰科技有限公司', 0); INSERT INTO `company` VALUES (5, '2024-03-04 16:05:38.867786', '2024-03-04 16:05:38.867786', '东莞市顶源电子有限公司', 0); INSERT INTO `company` VALUES (6, '2024-03-04 16:05:42.479027', '2024-03-04 16:05:42.479027', '深圳市福田区赛格电子市场金佳电子经营部', 0); INSERT INTO `company` VALUES (7, '2024-03-04 16:05:46.775364', '2024-03-04 16:05:46.775364', '深圳市思界电子科技有限公司', 0); @@ -47,6 +47,9 @@ INSERT INTO `company` VALUES (12, '2024-03-04 16:06:19.823410', '2024-03-04 16:0 INSERT INTO `company` VALUES (13, '2024-03-04 16:06:25.937749', '2024-03-04 16:06:25.937749', '上海脉芯网络科技有限公司', 0); INSERT INTO `company` VALUES (14, '2024-03-22 11:01:20.588144', '2024-03-22 11:01:20.588144', '深圳市声能达科技有限公司', 0); INSERT INTO `company` VALUES (15, '2024-03-26 10:29:45.595059', '2024-03-26 10:29:45.595059', '深圳市新得润电子有限公司', 0); +INSERT INTO `company` VALUES (16, '2024-04-05 08:47:49.227114', '2024-04-05 08:47:49.227114', '山东矿机华信智能科技有限公司', 0); +INSERT INTO `company` VALUES (17, '2024-04-05 10:03:44.190698', '2024-04-05 10:03:44.190698', '广东润宇传感器股份有限公司', 0); +INSERT INTO `company` VALUES (18, '2024-04-05 15:21:21.989529', '2024-04-05 15:21:21.989529', '宝鸡兴宇腾测控设备有限公司', 0); -- ---------------------------- -- Table structure for company_storage @@ -60,14 +63,11 @@ CREATE TABLE `company_storage` ( INDEX `IDX_bdd3a301229b9dec4b95549dfe`(`file_id`) USING BTREE, CONSTRAINT `FK_0958ee6ca6f52985840624bb916` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_bdd3a301229b9dec4b95549dfe7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of company_storage -- ---------------------------- -INSERT INTO `company_storage` VALUES (1, 141); -INSERT INTO `company_storage` VALUES (1, 142); -INSERT INTO `company_storage` VALUES (1, 143); -- ---------------------------- -- Table structure for contract @@ -93,13 +93,6 @@ CREATE TABLE `contract` ( -- ---------------------------- -- Records of contract -- ---------------------------- -INSERT INTO `contract` VALUES (1, '2024-02-29 11:38:20.959071', '2024-03-08 08:36:57.000000', '2022092301', '山东矿机华信智能科技有限公司', 11, '山东矿机华信', '青岛比特维尔', '2024-01-01', '2024-02-01', 1, 0); -INSERT INTO `contract` VALUES (2, '2024-02-29 16:11:54.286196', '2024-03-08 13:17:00.000000', 'www', 'weqw', 10, 'rqw', 'rwq', '2024-02-01', '2024-02-28', 2, 0); -INSERT INTO `contract` VALUES (3, '2024-03-01 15:26:07.794697', '2024-03-01 15:30:49.000000', 'test1211', 'test', 13, '山东搞笑信息', '齐鲁医院', '2024-03-01', '2024-03-20', 0, 1); -INSERT INTO `contract` VALUES (4, '2024-03-01 16:47:22.125670', '2024-03-01 16:47:22.125670', '33024242', '21412412', 11, '2141', '41241', '2024-03-01', '2024-03-01', 0, 0); -INSERT INTO `contract` VALUES (6, '2024-03-08 10:37:33.943916', '2024-03-08 10:37:59.000000', 'www12', 'weqw', 11, 'fw', 'fw', NULL, NULL, 1, 0); -INSERT INTO `contract` VALUES (7, '2024-03-11 15:28:38.834970', '2024-03-11 15:29:12.000000', 'wfwfwqf', 'fwqfwq', 11, 'wqfwq', 'fwqfw', NULL, NULL, 1, 0); -INSERT INTO `contract` VALUES (8, '2024-03-11 17:10:24.104620', '2024-03-11 17:11:04.000000', '24214', '214214', 10, '21', '4214', NULL, NULL, 1, 0); -- ---------------------------- -- Table structure for contract_storage @@ -113,18 +106,11 @@ CREATE TABLE `contract_storage` ( INDEX `IDX_2fe7cda0f292b099b7e13f8f61`(`file_id`) USING BTREE, CONSTRAINT `FK_2fe7cda0f292b099b7e13f8f612` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_b0a3f22af56decbc128c674447e` FOREIGN KEY (`contract_id`) REFERENCES `contract` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of contract_storage -- ---------------------------- -INSERT INTO `contract_storage` VALUES (1, 133); -INSERT INTO `contract_storage` VALUES (1, 134); -INSERT INTO `contract_storage` VALUES (2, 168); -INSERT INTO `contract_storage` VALUES (2, 169); -INSERT INTO `contract_storage` VALUES (7, 170); -INSERT INTO `contract_storage` VALUES (7, 171); -INSERT INTO `contract_storage` VALUES (8, 172); -- ---------------------------- -- Table structure for materials_in_out @@ -136,7 +122,6 @@ CREATE TABLE `materials_in_out` ( `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `product_id` int NOT NULL COMMENT '产品', `in_or_out` tinyint NOT NULL COMMENT '入库或出库', - `time` date NULL DEFAULT NULL COMMENT '时间', `quantity` int NOT NULL DEFAULT 0 COMMENT '数量', `unit_price` decimal(15, 10) NOT NULL DEFAULT 0.0000000000 COMMENT '单价', `amount` decimal(15, 10) NOT NULL DEFAULT 0.0000000000 COMMENT '金额', @@ -147,6 +132,7 @@ CREATE TABLE `materials_in_out` ( `inventory_inout_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '原材料出入库编号', `project_id` int NULL DEFAULT NULL COMMENT '项目', `inventory_id` int NOT NULL COMMENT '库存', + `time` datetime NULL DEFAULT NULL COMMENT '时间', PRIMARY KEY (`id`) USING BTREE, INDEX `FK_770f1c4afd9631499ccc08bd58b`(`product_id`) USING BTREE, INDEX `FK_7a5bd19f8fd458f6336efedf765`(`project_id`) USING BTREE, @@ -154,11 +140,17 @@ CREATE TABLE `materials_in_out` ( CONSTRAINT `FK_770f1c4afd9631499ccc08bd58b` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_7a5bd19f8fd458f6336efedf765` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_f5dc1f1e4db2f990ef89f0398fa` FOREIGN KEY (`inventory_id`) REFERENCES `materials_inventory` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 168 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB AUTO_INCREMENT = 196 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of materials_in_out -- ---------------------------- +INSERT INTO `materials_in_out` VALUES (190, '2024-04-05 09:13:45.815541', '2024-04-05 09:13:45.000000', 36, 0, 100, 0.0000000000, 0.0000000000, '孟菲', NULL, '', 0, 'SI1017', 13, 68, NULL); +INSERT INTO `materials_in_out` VALUES (191, '2024-04-05 09:19:39.482730', '2024-04-05 09:19:39.000000', 35, 0, 100, 0.0000000000, 0.0000000000, '孟菲', NULL, '', 0, 'SI1018', 13, 69, NULL); +INSERT INTO `materials_in_out` VALUES (192, '2024-04-05 10:22:32.422926', '2024-04-05 10:22:32.000000', 37, 0, 83, 557.5200000000, 46274.1600000000, '孟菲', NULL, '', 0, 'SI1019', 13, 70, NULL); +INSERT INTO `materials_in_out` VALUES (193, '2024-04-05 14:58:36.121528', '2024-04-05 14:58:48.000000', 36, 0, 300, 0.0000000000, 0.0000000000, '王兴昊', NULL, NULL, 0, 'SI1020', 13, 68, NULL); +INSERT INTO `materials_in_out` VALUES (194, '2024-04-05 15:00:20.218109', '2024-04-05 15:00:20.218109', 35, 0, 300, 0.0000000000, 0.0000000000, '王兴昊', NULL, NULL, 0, 'SI1021', 13, 69, NULL); +INSERT INTO `materials_in_out` VALUES (195, '2024-04-05 15:26:13.804510', '2024-04-05 15:26:13.000000', 38, 0, 130, 0.0000000000, 0.0000000000, '王兴昊', NULL, '', 0, 'SI1022', 13, 71, NULL); -- ---------------------------- -- Table structure for materials_in_out_storage @@ -172,7 +164,7 @@ CREATE TABLE `materials_in_out_storage` ( INDEX `IDX_96c00bfbcd71e93a6cc070e8e6`(`file_id`) USING BTREE, CONSTRAINT `FK_96c00bfbcd71e93a6cc070e8e6c` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_9df13ab4d4747575c3106685810` FOREIGN KEY (`materials_in_out_id`) REFERENCES `materials_in_out` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of materials_in_out_storage @@ -199,11 +191,15 @@ CREATE TABLE `materials_inventory` ( INDEX `FK_413008d6a91b215b66971c9a9e8`(`product_id`) USING BTREE, CONSTRAINT `FK_3915e159f03408035a62721d5be` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_413008d6a91b215b66971c9a9e8` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 54 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 72 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of materials_inventory -- ---------------------------- +INSERT INTO `materials_inventory` VALUES (68, '2024-04-05 09:13:45.802134', '2024-04-05 14:58:36.000000', NULL, 0, 36, 400, 0.0000000000, 13, '库房一, 第三排, 第二层', 'S1014'); +INSERT INTO `materials_inventory` VALUES (69, '2024-04-05 09:19:39.479124', '2024-04-05 15:00:20.000000', NULL, 0, 35, 400, 0.0000000000, 13, '库房一, 第三排, 第二层', 'S1015'); +INSERT INTO `materials_inventory` VALUES (70, '2024-04-05 10:22:32.418802', '2024-04-05 10:22:32.418802', NULL, 0, 37, 83, 557.5200000000, 13, NULL, 'S1016'); +INSERT INTO `materials_inventory` VALUES (71, '2024-04-05 15:26:13.800776', '2024-04-05 15:26:13.800776', NULL, 0, 38, 130, 0.0000000000, 13, NULL, 'S1017'); -- ---------------------------- -- Table structure for product @@ -226,37 +222,16 @@ CREATE TABLE `product` ( INDEX `FK_b15422982adca3bf53adfb535de`(`unit_id`) USING BTREE, CONSTRAINT `FK_a0503db1630a5b8a4d7deabd556` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_b15422982adca3bf53adfb535de` FOREIGN KEY (`unit_id`) REFERENCES `sys_dict_item` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 33 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB AUTO_INCREMENT = 39 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of product -- ---------------------------- -INSERT INTO `product` VALUES (1, '2024-03-04 16:47:37.604035', '2024-03-25 11:33:59.175698', '位移传感器', 0, 10, 'weiyichuanganqi', NULL, '', '', 'P1024'); -INSERT INTO `product` VALUES (2, '2024-03-04 16:47:41.483273', '2024-03-25 11:33:55.073301', '磁环', 1, 6, 'cihuan', NULL, '', '', 'P1023'); -INSERT INTO `product` VALUES (3, '2024-03-04 16:47:44.911165', '2024-03-25 11:33:47.367179', '天线', 0, 5, 'tianxian', NULL, '', '', 'P1022'); -INSERT INTO `product` VALUES (4, '2024-03-04 16:47:48.398543', '2024-03-25 11:33:45.962454', '集成电路', 0, 8, 'jichengdianlu', NULL, '', '', 'P1021'); -INSERT INTO `product` VALUES (5, '2024-03-05 09:09:05.015757', '2024-03-25 11:33:44.769681', '更新', 1, NULL, NULL, NULL, '', '', 'P1020'); -INSERT INTO `product` VALUES (12, '2024-03-05 09:25:29.584423', '2024-03-25 11:33:43.028337', '巴伦', 0, 13, 'balun', 15, 'test', '', 'P1019'); -INSERT INTO `product` VALUES (13, '2024-03-05 13:53:15.998630', '2024-03-25 11:33:41.013316', '极薄煤层控制器', 0, 10, 'jibaomeicengkongzhiqi', 14, '', '', 'P1018'); -INSERT INTO `product` VALUES (14, '2024-03-05 16:05:30.485017', '2024-03-25 11:33:38.276238', '天线', 0, 13, 'tianxian', 15, '', '', 'P1017'); -INSERT INTO `product` VALUES (15, '2024-03-05 17:21:20.378006', '2024-03-25 11:33:36.429920', 'USB智能充电线', 0, 7, 'USBzhinengchongdianxian', NULL, '', '', 'P1016'); -INSERT INTO `product` VALUES (16, '2024-03-05 17:24:03.148627', '2024-03-25 11:33:34.814008', '33', 1, 5, NULL, NULL, '', '', 'P1015'); -INSERT INTO `product` VALUES (17, '2024-03-05 17:30:32.450320', '2024-03-25 11:33:33.437417', 'test', 1, 5, 'test', NULL, '', '', 'P1014'); -INSERT INTO `product` VALUES (18, '2024-03-05 21:52:11.477508', '2024-03-25 11:33:31.247032', '新增', 1, NULL, 'xinzeng', NULL, '', '', 'P1013'); -INSERT INTO `product` VALUES (19, '2024-03-06 08:53:25.600367', '2024-03-27 11:57:24.000000', '位移传感器', 0, 12, 'weiyichuanganqi', 14, '', 'GUC2400-860mm', 'P1012'); -INSERT INTO `product` VALUES (20, '2024-03-06 09:12:47.327409', '2024-03-27 11:57:41.000000', '磁环', 0, 12, 'cihuan', 14, '', '12-1081', 'P1011'); -INSERT INTO `product` VALUES (21, '2024-03-06 09:13:21.382776', '2024-03-25 11:33:26.704971', '集成电路', 0, 13, 'jichengdianlu', 15, '', '', 'P1010'); -INSERT INTO `product` VALUES (22, '2024-03-06 16:38:06.999498', '2024-03-25 11:33:24.663945', '电磁阀驱动器', 0, 10, '', 14, '', '', 'P1009'); -INSERT INTO `product` VALUES (23, '2024-03-06 16:40:32.859846', '2024-03-25 11:32:56.316568', '电子元器件', 0, 11, 'dianziyuanqijian', 21, '', '', 'P1006'); -INSERT INTO `product` VALUES (24, '2024-03-07 09:48:35.854273', '2024-03-25 11:32:58.560805', '排针', 0, 1, 'paizhen', 15, '', '', 'P1007'); -INSERT INTO `product` VALUES (25, '2024-03-11 13:19:40.149703', '2024-03-25 11:33:01.812839', '更新', 1, 5, 'gengxin', 16, 'weeeeeeeee', '', 'P1008'); -INSERT INTO `product` VALUES (26, '2024-03-11 13:52:41.663603', '2024-03-25 11:32:32.798873', 'PCBA-线路板', 0, 11, 'PCBA-xianluban', 22, NULL, '', 'P1005'); -INSERT INTO `product` VALUES (27, '2024-03-22 11:01:53.195802', '2024-03-27 11:57:32.000000', '蜂鸣器', 0, 14, 'fengmingqi', 15, '刘大伟', '', 'P1004'); -INSERT INTO `product` VALUES (28, '2024-03-22 15:46:35.784165', '2024-03-27 11:57:02.000000', '222', 1, 1, '222', 14, '222', '222', 'P1005'); -INSERT INTO `product` VALUES (29, '2024-03-22 16:02:00.927537', '2024-03-22 16:02:00.927537', 'wwr', 0, 4, 'wwr', 14, NULL, 'wrq', 'P1000'); -INSERT INTO `product` VALUES (30, '2024-03-22 16:02:16.834435', '2024-03-27 11:57:04.000000', 'A321421', 1, 5, 'A321421', 14, NULL, '241', 'P1002'); -INSERT INTO `product` VALUES (31, '2024-03-22 16:03:00.226909', '2024-03-22 16:03:39.000000', '集成电路', 0, 4, 'jichengdianlu', 15, NULL, 'SW8110Q', 'P1001'); -INSERT INTO `product` VALUES (32, '2024-03-26 10:30:10.448050', '2024-03-26 10:30:10.448050', '端子线', 0, 15, 'duanzixian', 21, NULL, NULL, 'P1025'); +INSERT INTO `product` VALUES (34, '2024-04-04 14:11:01.924609', '2024-04-04 14:11:01.924609', '网络型控制器按键保护板', 0, 1, 'wangluoxingkongzhiqianjianbaohuban', 15, '网络型控制器保护板', '283*15*4', 'P1027'); +INSERT INTO `product` VALUES (35, '2024-04-05 08:49:29.136458', '2024-04-05 08:49:29.136458', '急停基座', 0, 16, 'jitingjizuo', 15, 'ERP库存名急停端子', 'ZB2BZ102C', 'P1028'); +INSERT INTO `product` VALUES (36, '2024-04-05 08:57:45.214384', '2024-04-05 08:57:45.214384', '急停按钮', 0, 16, 'jitinganniu', 15, NULL, 'ZB2BT4C', 'P1029'); +INSERT INTO `product` VALUES (37, '2024-04-05 10:13:36.340912', '2024-04-05 10:13:36.340912', '传感器', 0, 17, 'chuanganqi', 14, NULL, 'GUC960-700mm', 'P1030'); +INSERT INTO `product` VALUES (38, '2024-04-05 15:21:41.040381', '2024-04-05 15:21:41.040381', '压力传感器', 0, 18, 'yalichuanganqi', 14, NULL, 'GPD60', 'P1031'); -- ---------------------------- -- Table structure for product_storage @@ -270,16 +245,11 @@ CREATE TABLE `product_storage` ( INDEX `IDX_eecbd68d7d4d565baecee2d76c`(`file_id`) USING BTREE, CONSTRAINT `FK_6dd288598f0a0ea3f72f31cb422` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_eecbd68d7d4d565baecee2d76c7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of product_storage -- ---------------------------- -INSERT INTO `product_storage` VALUES (1, 124); -INSERT INTO `product_storage` VALUES (12, 144); -INSERT INTO `product_storage` VALUES (12, 145); -INSERT INTO `product_storage` VALUES (22, 176); -INSERT INTO `product_storage` VALUES (27, 177); -- ---------------------------- -- Table structure for project @@ -293,7 +263,7 @@ CREATE TABLE `project` ( `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_dedfea394088ed136ddadeee89`(`name`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of project @@ -301,15 +271,11 @@ CREATE TABLE `project` ( INSERT INTO `project` VALUES (1, '2024-03-07 09:35:15.276345', '2024-03-08 08:48:21.000000', '星火项目', 0); INSERT INTO `project` VALUES (2, '2024-03-07 09:35:20.004729', '2024-03-07 09:35:20.004729', '东大项目', 0); INSERT INTO `project` VALUES (3, '2024-03-07 09:35:29.213057', '2024-03-07 09:35:29.213057', '沙湾煤业项目', 0); -INSERT INTO `project` VALUES (4, '2024-03-20 14:01:02.203909', '2024-03-20 14:24:33.000000', '测试项目2', 0); -INSERT INTO `project` VALUES (5, '2024-03-20 14:24:36.245179', '2024-03-20 14:24:36.245179', '测试项目3', 0); -INSERT INTO `project` VALUES (6, '2024-03-20 14:24:39.336620', '2024-03-20 14:24:39.336620', '测试项目4', 0); -INSERT INTO `project` VALUES (7, '2024-03-20 14:24:43.261902', '2024-03-20 14:24:43.261902', '测试项目测试项目测试项目测试项目测试项目', 0); -INSERT INTO `project` VALUES (8, '2024-03-20 14:24:45.976572', '2024-03-20 14:24:45.976572', '测试项目5', 0); -INSERT INTO `project` VALUES (9, '2024-03-20 14:24:53.929293', '2024-03-20 14:24:53.929293', '测试项目6', 0); -INSERT INTO `project` VALUES (10, '2024-03-20 14:24:56.436181', '2024-03-20 14:24:56.436181', '测试项目7', 0); -INSERT INTO `project` VALUES (11, '2024-03-20 14:24:58.916471', '2024-03-20 14:24:58.916471', '测试项目8', 0); -INSERT INTO `project` VALUES (12, '2024-03-20 14:25:13.153119', '2024-03-20 14:25:13.153119', '测试项目9', 0); +INSERT INTO `project` VALUES (13, '2024-04-02 13:53:11.973237', '2024-04-04 14:15:15.000000', '未分类项目', 0); +INSERT INTO `project` VALUES (14, '2024-04-04 14:13:50.885496', '2024-04-04 14:13:50.885496', '七台河煤矿', 0); +INSERT INTO `project` VALUES (15, '2024-04-04 14:14:02.655982', '2024-04-04 14:14:02.655982', '红旗煤矿', 0); +INSERT INTO `project` VALUES (16, '2024-04-04 14:14:19.407108', '2024-04-04 14:14:19.407108', '首旺煤矿', 0); +INSERT INTO `project` VALUES (17, '2024-04-04 14:14:43.054552', '2024-04-04 14:14:43.054552', '沫凤项目', 0); -- ---------------------------- -- Table structure for project_storage @@ -323,13 +289,11 @@ CREATE TABLE `project_storage` ( INDEX `IDX_ac08ac8e4f973873f03dafaca2`(`file_id`) USING BTREE, CONSTRAINT `FK_9058e954f8f09e2cfa2261c1f26` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_ac08ac8e4f973873f03dafaca2b` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of project_storage -- ---------------------------- -INSERT INTO `project_storage` VALUES (1, 139); -INSERT INTO `project_storage` VALUES (1, 140); -- ---------------------------- -- Table structure for sys_captcha_log @@ -338,13 +302,13 @@ DROP TABLE IF EXISTS `sys_captcha_log`; CREATE TABLE `sys_captcha_log` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NULL DEFAULT NULL, - `account` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, - `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, - `provider` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `account` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `provider` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_captcha_log @@ -364,7 +328,7 @@ CREATE TABLE `sys_config` ( `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_2c363c25cf99bcaab3a7f389ba`(`key`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_config @@ -376,6 +340,8 @@ INSERT INTO `sys_config` VALUES (4, 'inventory_inout_number_prefix_in', '人库 INSERT INTO `sys_config` VALUES (5, 'inventory_inout_number_prefix_out', '出库单号前缀', 'SO', '出库单号前缀', '2024-03-22 13:37:21.165879', '2024-03-25 15:52:32.000000'); INSERT INTO `sys_config` VALUES (6, 'product_number_prefix', '产品编号前缀', 'P', '产品编号前缀', '2024-03-22 15:51:10.960064', '2024-03-22 15:51:10.960064'); INSERT INTO `sys_config` VALUES (7, 'inventory_number_prefix', '库存编号', 'S', '库存编号', '2024-03-25 15:54:08.836711', '2024-03-25 15:54:08.836711'); +INSERT INTO `sys_config` VALUES (8, 'app_version', 'app版本号', '1.0.1', 'app版本号', '2024-04-07 10:32:41.513615', '2024-04-07 10:32:41.513615'); +INSERT INTO `sys_config` VALUES (9, 'is_app_force_upgrade', 'app是否强制更新', '1', 'app是否强制更新。一般用于必须升级的更新需求。', '2024-04-07 10:33:07.732646', '2024-04-07 10:33:07.732646'); -- ---------------------------- -- Table structure for sys_dept @@ -383,28 +349,24 @@ INSERT INTO `sys_config` VALUES (7, 'inventory_number_prefix', '库存编号', ' DROP TABLE IF EXISTS `sys_dept`; CREATE TABLE `sys_dept` ( `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, `orderNo` int NULL DEFAULT 0, - `mpath` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '', + `mpath` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '', `parentId` int NULL DEFAULT NULL, `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, INDEX `FK_c75280b01c49779f2323536db67`(`parentId`) USING BTREE, CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dept -- ---------------------------- -INSERT INTO `sys_dept` VALUES (1, '山东矿机华信智能科技', 1, '1.', NULL, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); -INSERT INTO `sys_dept` VALUES (2, '计算机开发部', 1, '1.2.', 1, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); -INSERT INTO `sys_dept` VALUES (3, '行政部', 2, '1.3.', 1, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); -INSERT INTO `sys_dept` VALUES (4, '商务部', 3, '1.4.', 1, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); -INSERT INTO `sys_dept` VALUES (5, '财务部', 4, '1.5.', 1, '2023-11-10 00:31:43.996025', '2024-03-06 17:16:48.000000'); -INSERT INTO `sys_dept` VALUES (6, '山东矿机华能装备制造', 2, '6.', NULL, '2023-11-10 00:31:43.996025', '2024-03-06 17:17:30.000000'); -INSERT INTO `sys_dept` VALUES (8, '研发部', 1, '6.8.', 6, '2023-11-10 00:31:43.996025', '2024-03-06 17:17:30.000000'); -INSERT INTO `sys_dept` VALUES (9, '市场部', 1, '6.9.', 6, '2023-11-10 00:31:43.996025', '2024-03-06 17:17:30.000000'); +INSERT INTO `sys_dept` VALUES (1, '山东矿机华能装备制造', 1, '1.', NULL, '2023-11-10 00:31:43.996025', '2024-04-02 14:57:50.000000'); +INSERT INTO `sys_dept` VALUES (2, '信息电控部', 1, '1.2.', 1, '2023-11-10 00:31:43.996025', '2024-04-02 14:58:04.000000'); +INSERT INTO `sys_dept` VALUES (10, '山东矿机华信智能科技', 1, '10.', NULL, '2024-04-07 10:33:31.759382', '2024-04-07 10:33:31.000000'); +INSERT INTO `sys_dept` VALUES (11, '计算机开发部', 0, '10.11.', 10, '2024-04-07 10:33:44.029857', '2024-04-07 10:33:44.000000'); -- ---------------------------- -- Table structure for sys_dict @@ -421,7 +383,7 @@ CREATE TABLE `sys_dict` ( `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_d112365748f740ee260b65ce91`(`name`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict @@ -448,7 +410,7 @@ CREATE TABLE `sys_dict_item` ( PRIMARY KEY (`id`) USING BTREE, INDEX `FK_d68ea74fcb041c8cfd1fd659844`(`type_id`) USING BTREE, CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 49 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict_item @@ -488,6 +450,15 @@ INSERT INTO `sys_dict_item` VALUES (36, '2024-03-25 08:32:14.302500', '2024-03-2 INSERT INTO `sys_dict_item` VALUES (37, '2024-03-25 08:32:54.412145', '2024-03-25 08:32:54.412145', 1, 1, '第三层', 'level_3', NULL, 1, NULL, 9, 2, '2024-03-25 08:32:54.412145'); INSERT INTO `sys_dict_item` VALUES (38, '2024-03-25 08:33:02.567402', '2024-03-25 08:33:02.567402', 1, 1, '第四层', 'level_4', NULL, 1, NULL, 9, 3, '2024-03-25 08:33:02.567402'); INSERT INTO `sys_dict_item` VALUES (39, '2024-03-25 08:33:12.209556', '2024-03-25 08:33:12.209556', 1, 1, '第五层', 'level_5', NULL, 1, NULL, 9, 4, '2024-03-25 08:33:12.209556'); +INSERT INTO `sys_dict_item` VALUES (40, '2024-04-02 12:26:18.720198', '2024-04-02 12:26:18.720198', 1, 1, '中间过渡架电控部分', 'sales_quotation_group_1', NULL, 1, NULL, 10, 0, '2024-04-02 12:26:18.720198'); +INSERT INTO `sys_dict_item` VALUES (41, '2024-04-02 12:26:26.985680', '2024-04-02 12:26:26.985680', 1, 1, '端头架电控部分', 'sales_quotation_group_2', NULL, 1, NULL, 10, 1, '2024-04-02 12:26:26.985680'); +INSERT INTO `sys_dict_item` VALUES (42, '2024-04-02 12:26:39.202760', '2024-04-02 12:26:39.202760', 1, 1, '主阀部分', 'sales_quotation_group_3', NULL, 1, NULL, 10, 2, '2024-04-02 12:26:39.202760'); +INSERT INTO `sys_dict_item` VALUES (43, '2024-04-02 12:26:45.611332', '2024-04-02 12:26:45.611332', 1, 1, '自动反冲洗过滤器部分', 'sales_quotation_group_4', NULL, 1, NULL, 10, 3, '2024-04-02 12:26:45.611332'); +INSERT INTO `sys_dict_item` VALUES (44, '2024-04-02 12:26:52.092188', '2024-04-02 12:26:52.092188', 1, 1, '位移测量部分', 'sales_quotation_group_5', NULL, 1, NULL, 10, 4, '2024-04-02 12:26:52.092188'); +INSERT INTO `sys_dict_item` VALUES (45, '2024-04-02 12:26:59.178581', '2024-04-02 12:26:59.178581', 1, 1, '压力检测部分', 'sales_quotation_group_6', NULL, 1, NULL, 10, 5, '2024-04-02 12:26:59.178581'); +INSERT INTO `sys_dict_item` VALUES (46, '2024-04-02 12:27:05.494866', '2024-04-02 12:27:05.494866', 1, 1, '煤机定位部分', 'sales_quotation_group_7', NULL, 1, NULL, 10, 6, '2024-04-02 12:27:05.494866'); +INSERT INTO `sys_dict_item` VALUES (47, '2024-04-02 12:27:12.313251', '2024-04-02 12:27:12.313251', 1, 1, '姿态检测部分', 'sales_quotation_group_8', NULL, 1, NULL, 10, 7, '2024-04-02 12:27:12.313251'); +INSERT INTO `sys_dict_item` VALUES (48, '2024-04-02 15:01:53.004626', '2024-04-02 15:01:53.004626', 1, 1, 'A区', 'areaA', NULL, 1, NULL, 8, 1, '2024-04-02 15:01:53.004626'); -- ---------------------------- -- Table structure for sys_dict_type @@ -506,7 +477,7 @@ CREATE TABLE `sys_dict_type` ( `deleted_at` datetime(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_74d0045ff7fab9f67adc0b1bda`(`code`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict_type @@ -514,11 +485,12 @@ CREATE TABLE `sys_dict_type` ( INSERT INTO `sys_dict_type` VALUES (1, '2024-01-28 08:19:12.777447', '2024-02-08 13:05:10.000000', 1, 1, '性别', 1, '性别单选', 'sys_user_gender', '2024-03-01 15:28:21.689753'); INSERT INTO `sys_dict_type` VALUES (2, '2024-01-28 08:38:41.235185', '2024-01-29 02:11:33.000000', 1, 1, '菜单显示状态', 1, '菜单显示状态', 'sys_show_hide', '2024-03-01 15:28:21.689753'); INSERT INTO `sys_dict_type` VALUES (3, '2024-02-28 16:38:27.311577', '2024-03-04 13:26:29.000000', 1, 1, '合同类型', 1, '合同类型', 'contract_type', '2024-03-04 13:26:29.911469'); -INSERT INTO `sys_dict_type` VALUES (5, '2024-03-04 13:41:05.156027', '2024-03-04 13:41:05.156027', 1, 1, '单位', 1, '材料盘点表等单位。件。个', 'unit', '2024-03-04 13:41:05.156027'); +INSERT INTO `sys_dict_type` VALUES (5, '2024-03-04 13:41:05.156027', '2024-04-05 10:12:14.000000', 1, 9, '单位', 1, '材料盘点表等单位。件。个。台', 'unit', '2024-04-05 10:12:14.058367'); INSERT INTO `sys_dict_type` VALUES (6, '2024-03-07 16:32:26.985730', '2024-03-07 16:32:26.985730', 1, 1, '公司车辆', 1, '公司的公车', 'vehicle', '2024-03-07 16:32:26.985730'); INSERT INTO `sys_dict_type` VALUES (7, '2024-03-25 08:27:37.461575', '2024-03-25 08:27:37.461575', 1, 1, '库存位置-房间', 1, '库存存放的房间', 'inventory_room', '2024-03-25 08:27:37.461575'); INSERT INTO `sys_dict_type` VALUES (8, '2024-03-25 08:29:29.110447', '2024-03-25 08:29:29.110447', 1, 1, '库存位置-排架号', 1, '库存位置-排架号', 'inventory_line', '2024-03-25 08:29:29.110447'); INSERT INTO `sys_dict_type` VALUES (9, '2024-03-25 08:31:52.669289', '2024-03-25 08:31:52.669289', 1, 1, '库存位置-层数', 1, NULL, 'inventory_line_level', '2024-03-25 08:31:52.669289'); +INSERT INTO `sys_dict_type` VALUES (10, '2024-04-02 12:25:40.233758', '2024-04-02 12:25:40.233758', 1, 1, '电解控报价计算-分组', 1, NULL, 'sale_quotation_group', '2024-04-02 12:25:40.233758'); -- ---------------------------- -- Table structure for sys_login_log @@ -536,85 +508,82 @@ CREATE TABLE `sys_login_log` ( PRIMARY KEY (`id`) USING BTREE, INDEX `FK_3029712e0df6a28edaee46fd470`(`user_id`) USING BTREE, CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 74 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 72 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_login_log -- ---------------------------- -INSERT INTO `sys_login_log` VALUES (1, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-11 16:25:35.193443', '2024-03-11 16:25:35.193443', 1); -INSERT INTO `sys_login_log` VALUES (2, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-11 16:41:47.582236', '2024-03-11 16:41:47.582236', 1); -INSERT INTO `sys_login_log` VALUES (3, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-11 17:08:24.336525', '2024-03-11 17:08:24.336525', 1); -INSERT INTO `sys_login_log` VALUES (4, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-12 08:32:35.378738', '2024-03-12 08:32:35.378738', 1); -INSERT INTO `sys_login_log` VALUES (5, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-12 08:50:34.563830', '2024-03-12 08:50:34.563830', 1); -INSERT INTO `sys_login_log` VALUES (6, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-12 09:02:32.708992', '2024-03-12 09:02:32.708992', 1); -INSERT INTO `sys_login_log` VALUES (7, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-12 14:14:27.170328', '2024-03-12 14:14:27.170328', 1); -INSERT INTO `sys_login_log` VALUES (8, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-18 16:45:53.669633', '2024-03-18 16:45:53.669633', 1); -INSERT INTO `sys_login_log` VALUES (9, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-18 16:47:04.640742', '2024-03-18 16:47:04.640742', 1); -INSERT INTO `sys_login_log` VALUES (10, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:01:14.337676', '2024-03-19 09:01:14.337676', 1); -INSERT INTO `sys_login_log` VALUES (11, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:02:49.212590', '2024-03-19 09:02:49.212590', 1); -INSERT INTO `sys_login_log` VALUES (12, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:10:41.698998', '2024-03-19 09:10:41.698998', 1); -INSERT INTO `sys_login_log` VALUES (13, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:20:20.393109', '2024-03-19 09:20:20.393109', 1); -INSERT INTO `sys_login_log` VALUES (14, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:22:27.320019', '2024-03-19 09:22:27.320019', 1); -INSERT INTO `sys_login_log` VALUES (15, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:26:47.750336', '2024-03-19 09:26:47.750336', 1); -INSERT INTO `sys_login_log` VALUES (16, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:27:47.614307', '2024-03-19 09:27:47.614307', 1); -INSERT INTO `sys_login_log` VALUES (17, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:27:56.144948', '2024-03-19 09:27:56.144948', 1); -INSERT INTO `sys_login_log` VALUES (18, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:28:48.043487', '2024-03-19 09:28:48.043487', 1); -INSERT INTO `sys_login_log` VALUES (19, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:29:03.235340', '2024-03-19 09:29:03.235340', 1); -INSERT INTO `sys_login_log` VALUES (20, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:39:07.529231', '2024-03-19 09:39:07.529231', 1); -INSERT INTO `sys_login_log` VALUES (21, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:39:44.392483', '2024-03-19 09:39:44.392483', 1); -INSERT INTO `sys_login_log` VALUES (22, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:39:59.249030', '2024-03-19 09:39:59.249030', 1); -INSERT INTO `sys_login_log` VALUES (23, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 09:40:34.075596', '2024-03-19 09:40:34.075596', 1); -INSERT INTO `sys_login_log` VALUES (24, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 11:25:55.110382', '2024-03-19 11:25:55.110382', 1); -INSERT INTO `sys_login_log` VALUES (25, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 13:01:22.927432', '2024-03-19 13:01:22.927432', 1); -INSERT INTO `sys_login_log` VALUES (26, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-19 13:39:49.898149', '2024-03-19 13:39:49.898149', 1); -INSERT INTO `sys_login_log` VALUES (27, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 15:00:10.238618', '2024-03-19 15:00:10.238618', 1); -INSERT INTO `sys_login_log` VALUES (28, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 15:53:32.340056', '2024-03-19 15:53:32.340056', 1); -INSERT INTO `sys_login_log` VALUES (29, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 16:32:41.403566', '2024-03-19 16:32:41.403566', 1); -INSERT INTO `sys_login_log` VALUES (30, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 16:47:04.973345', '2024-03-19 16:47:04.973345', 1); -INSERT INTO `sys_login_log` VALUES (31, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-19 17:30:27.560965', '2024-03-19 17:30:27.560965', 1); -INSERT INTO `sys_login_log` VALUES (32, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-20 09:17:13.462569', '2024-03-20 09:17:13.462569', 1); -INSERT INTO `sys_login_log` VALUES (33, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-20 13:01:56.230866', '2024-03-20 13:01:56.230866', 1); -INSERT INTO `sys_login_log` VALUES (34, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-20 13:27:27.578581', '2024-03-20 13:27:27.578581', 1); -INSERT INTO `sys_login_log` VALUES (35, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 09:40:24.947391', '2024-03-21 09:40:24.947391', 1); -INSERT INTO `sys_login_log` VALUES (36, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 11:00:56.747380', '2024-03-21 11:00:56.747380', 1); -INSERT INTO `sys_login_log` VALUES (37, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 11:20:46.050676', '2024-03-21 11:20:46.050676', 1); -INSERT INTO `sys_login_log` VALUES (38, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', '内网IP', NULL, '2024-03-21 13:52:09.392800', '2024-03-21 13:52:09.392800', 1); -INSERT INTO `sys_login_log` VALUES (39, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 14:13:54.119595', '2024-03-21 14:13:54.119595', 1); -INSERT INTO `sys_login_log` VALUES (40, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 14:18:43.489028', '2024-03-21 14:18:43.489028', 1); -INSERT INTO `sys_login_log` VALUES (41, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 16:53:02.442100', '2024-03-21 16:53:02.442100', 1); -INSERT INTO `sys_login_log` VALUES (42, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-21 17:22:14.487551', '2024-03-21 17:22:14.487551', 1); -INSERT INTO `sys_login_log` VALUES (43, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:28:39.945316', '2024-03-22 08:28:39.945316', 1); -INSERT INTO `sys_login_log` VALUES (44, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:47:49.668244', '2024-03-22 08:47:49.668244', 1); -INSERT INTO `sys_login_log` VALUES (45, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:48:40.888213', '2024-03-22 08:48:40.888213', 1); -INSERT INTO `sys_login_log` VALUES (46, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:52:29.539297', '2024-03-22 08:52:29.539297', 1); -INSERT INTO `sys_login_log` VALUES (47, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 08:54:51.502428', '2024-03-22 08:54:51.502428', 1); -INSERT INTO `sys_login_log` VALUES (48, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', '内网IP', NULL, '2024-03-22 09:49:50.215940', '2024-03-22 09:49:50.215940', 1); -INSERT INTO `sys_login_log` VALUES (49, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-22 10:18:26.403105', '2024-03-22 10:18:26.403105', 1); -INSERT INTO `sys_login_log` VALUES (50, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-25 10:28:48.017616', '2024-03-25 10:28:48.017616', 1); -INSERT INTO `sys_login_log` VALUES (51, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-25 11:40:21.480492', '2024-03-25 11:40:21.480492', 1); -INSERT INTO `sys_login_log` VALUES (52, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:29:48.608014', '2024-03-26 15:29:48.608014', 1); -INSERT INTO `sys_login_log` VALUES (53, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:30:16.175586', '2024-03-26 15:30:16.175586', 1); -INSERT INTO `sys_login_log` VALUES (54, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:31:31.752879', '2024-03-26 15:31:31.752879', 1); -INSERT INTO `sys_login_log` VALUES (55, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:32:04.628186', '2024-03-26 15:32:04.628186', 1); -INSERT INTO `sys_login_log` VALUES (56, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:53:43.139332', '2024-03-26 15:53:43.139332', 1); -INSERT INTO `sys_login_log` VALUES (57, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:54:15.500462', '2024-03-26 15:54:15.500462', 1); -INSERT INTO `sys_login_log` VALUES (58, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:54:45.176950', '2024-03-26 15:54:45.176950', 1); -INSERT INTO `sys_login_log` VALUES (59, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 15:57:02.624723', '2024-03-26 15:57:02.624723', 1); -INSERT INTO `sys_login_log` VALUES (60, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-26 16:02:07.716342', '2024-03-26 16:02:07.716342', 1); -INSERT INTO `sys_login_log` VALUES (61, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 09:45:06.617531', '2024-03-27 09:45:06.617531', 1); -INSERT INTO `sys_login_log` VALUES (62, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-27 10:04:30.119475', '2024-03-27 10:04:30.119475', 1); -INSERT INTO `sys_login_log` VALUES (63, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-27 10:59:23.447855', '2024-03-27 10:59:23.447855', 1); -INSERT INTO `sys_login_log` VALUES (64, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 10:59:38.112830', '2024-03-27 10:59:38.112830', 1); -INSERT INTO `sys_login_log` VALUES (65, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:04:13.903132', '2024-03-27 11:04:13.903132', 1); -INSERT INTO `sys_login_log` VALUES (66, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:05:32.728343', '2024-03-27 11:05:32.728343', 1); -INSERT INTO `sys_login_log` VALUES (67, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:15:54.118365', '2024-03-27 11:15:54.118365', 1); -INSERT INTO `sys_login_log` VALUES (68, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:23:51.030200', '2024-03-27 11:23:51.030200', 1); -INSERT INTO `sys_login_log` VALUES (69, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-27 11:26:55.224029', '2024-03-27 11:26:55.224029', 1); -INSERT INTO `sys_login_log` VALUES (70, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 11:31:45.389808', '2024-03-27 11:31:45.389808', 1); -INSERT INTO `sys_login_log` VALUES (71, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 13:50:06.483334', '2024-03-27 13:50:06.483334', 1); -INSERT INTO `sys_login_log` VALUES (72, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 13:50:18.729275', '2024-03-27 13:50:18.729275', 1); -INSERT INTO `sys_login_log` VALUES (73, '192.168.60.130', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-03-27 13:54:12.303628', '2024-03-27 13:54:12.303628', 1); -INSERT INTO `sys_login_log` VALUES (74, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0', '内网IP', NULL, '2024-03-28 13:06:16.680834', '2024-03-28 13:06:16.680834', 1); +INSERT INTO `sys_login_log` VALUES (1, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-01 08:50:32.291716', '2024-04-01 08:50:32.291716', 1); +INSERT INTO `sys_login_log` VALUES (2, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 14:37:00.079842', '2024-04-01 14:37:00.079842', 1); +INSERT INTO `sys_login_log` VALUES (3, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:11:30.159405', '2024-04-01 15:11:30.159405', 1); +INSERT INTO `sys_login_log` VALUES (4, '144.0.23.133', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0', '山东省青岛市', NULL, '2024-04-01 15:15:45.157349', '2024-04-01 15:15:45.157349', 1); +INSERT INTO `sys_login_log` VALUES (5, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:34:38.228762', '2024-04-01 15:34:38.228762', 1); +INSERT INTO `sys_login_log` VALUES (6, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:41:22.061919', '2024-04-01 15:41:22.061919', 1); +INSERT INTO `sys_login_log` VALUES (7, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:44:13.373410', '2024-04-01 15:44:13.373410', 1); +INSERT INTO `sys_login_log` VALUES (8, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:47:23.396192', '2024-04-01 15:47:23.396192', 1); +INSERT INTO `sys_login_log` VALUES (9, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 16:16:31.291096', '2024-04-01 16:16:31.291096', 1); +INSERT INTO `sys_login_log` VALUES (10, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 16:21:30.780126', '2024-04-01 16:21:30.780126', 1); +INSERT INTO `sys_login_log` VALUES (11, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-01 16:49:57.352530', '2024-04-01 16:49:57.352530', 1); +INSERT INTO `sys_login_log` VALUES (12, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-01 16:50:13.073327', '2024-04-01 16:50:13.073327', 1); +INSERT INTO `sys_login_log` VALUES (13, '223.104.195.74', 'Dart/3.2 (dart:io)', '山东省潍坊市', NULL, '2024-04-01 17:16:04.439246', '2024-04-01 17:16:04.439246', 1); +INSERT INTO `sys_login_log` VALUES (14, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-01 17:30:31.958849', '2024-04-01 17:30:31.958849', 1); +INSERT INTO `sys_login_log` VALUES (15, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 10:09:56.131711', '2024-04-02 10:09:56.131711', 1); +INSERT INTO `sys_login_log` VALUES (16, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 10:19:42.684419', '2024-04-02 10:19:42.684419', 1); +INSERT INTO `sys_login_log` VALUES (17, '112.224.65.182', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省济南市', NULL, '2024-04-02 14:56:57.968284', '2024-04-02 14:56:57.968284', 1); +INSERT INTO `sys_login_log` VALUES (18, '112.224.65.182', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省济南市', NULL, '2024-04-02 14:59:28.837362', '2024-04-02 14:59:28.837362', 9); +INSERT INTO `sys_login_log` VALUES (19, '112.224.65.182', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省济南市', NULL, '2024-04-02 14:59:48.312224', '2024-04-02 14:59:48.312224', 1); +INSERT INTO `sys_login_log` VALUES (20, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-02 15:38:45.330867', '2024-04-02 15:38:45.330867', 1); +INSERT INTO `sys_login_log` VALUES (21, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-02 15:39:52.649278', '2024-04-02 15:39:52.649278', 10); +INSERT INTO `sys_login_log` VALUES (22, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 15:40:20.394755', '2024-04-02 15:40:20.394755', 10); +INSERT INTO `sys_login_log` VALUES (23, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:04:31.143028', '2024-04-02 16:04:31.143028', 1); +INSERT INTO `sys_login_log` VALUES (24, '112.224.65.182', 'Dart/3.3 (dart:io)', '山东省济南市', NULL, '2024-04-02 16:18:52.597148', '2024-04-02 16:18:52.597148', 1); +INSERT INTO `sys_login_log` VALUES (25, '112.224.65.182', 'Dart/3.3 (dart:io)', '山东省济南市', NULL, '2024-04-02 16:21:03.861179', '2024-04-02 16:21:03.861179', 1); +INSERT INTO `sys_login_log` VALUES (26, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:22:06.193594', '2024-04-02 16:22:06.193594', 1); +INSERT INTO `sys_login_log` VALUES (27, '221.1.97.166', 'Dart/3.3 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:27:03.753690', '2024-04-02 16:27:03.753690', 1); +INSERT INTO `sys_login_log` VALUES (28, '221.1.97.166', 'Dart/3.3 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:34:35.796027', '2024-04-02 16:34:35.796027', 1); +INSERT INTO `sys_login_log` VALUES (29, '221.1.97.166', 'Dart/3.3 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:35:12.372648', '2024-04-02 16:35:12.372648', 1); +INSERT INTO `sys_login_log` VALUES (30, '221.1.97.166', 'Dart/3.3 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:51:18.226019', '2024-04-02 16:51:18.226019', 1); +INSERT INTO `sys_login_log` VALUES (31, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:52:11.095661', '2024-04-02 16:52:11.095661', 1); +INSERT INTO `sys_login_log` VALUES (32, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:52:46.246857', '2024-04-02 16:52:46.246857', 1); +INSERT INTO `sys_login_log` VALUES (33, '223.104.195.90', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x1800302c) NetType/4G Language/zh_CN', '山东省潍坊市', NULL, '2024-04-02 18:06:51.031947', '2024-04-02 18:06:51.031947', 10); +INSERT INTO `sys_login_log` VALUES (34, '17.232.78.156', 'Dart/3.3 (dart:io)', '美国Apple', NULL, '2024-04-02 20:38:53.231472', '2024-04-02 20:38:53.231472', 1); +INSERT INTO `sys_login_log` VALUES (35, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x63090a13) XWEB/9079 Flue', '山东省潍坊市昌乐县', NULL, '2024-04-03 08:08:44.705984', '2024-04-03 08:08:44.705984', 1); +INSERT INTO `sys_login_log` VALUES (36, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x6309092b) XWEB/9079 Flue', '山东省潍坊市昌乐县', NULL, '2024-04-03 08:09:10.249023', '2024-04-03 08:09:10.249023', 9); +INSERT INTO `sys_login_log` VALUES (37, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-03 15:15:03.718007', '2024-04-03 15:15:03.718007', 1); +INSERT INTO `sys_login_log` VALUES (38, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-03 15:38:42.155118', '2024-04-03 15:38:42.155118', 1); +INSERT INTO `sys_login_log` VALUES (39, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-03 18:25:46.887892', '2024-04-03 18:25:46.887892', 1); +INSERT INTO `sys_login_log` VALUES (40, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-04 13:54:25.249419', '2024-04-04 13:54:25.249419', 1); +INSERT INTO `sys_login_log` VALUES (41, '112.224.195.64', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x1800302c) NetType/4G Language/zh_CN', '山东省济南市', NULL, '2024-04-04 13:55:42.245768', '2024-04-04 13:55:42.245768', 1); +INSERT INTO `sys_login_log` VALUES (42, '221.1.97.166', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.47(0x18002f2c) NetType/4G Language/zh_CN', '山东省潍坊市昌乐县', NULL, '2024-04-04 13:58:47.699965', '2024-04-04 13:58:47.699965', 1); +INSERT INTO `sys_login_log` VALUES (43, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:00:07.465436', '2024-04-04 14:00:07.465436', 1); +INSERT INTO `sys_login_log` VALUES (44, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:00:17.602620', '2024-04-04 14:00:17.602620', 1); +INSERT INTO `sys_login_log` VALUES (45, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:00:42.762900', '2024-04-04 14:00:42.762900', 1); +INSERT INTO `sys_login_log` VALUES (46, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:01:48.207951', '2024-04-04 14:01:48.207951', 1); +INSERT INTO `sys_login_log` VALUES (47, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:02:33.248728', '2024-04-04 14:02:33.248728', 1); +INSERT INTO `sys_login_log` VALUES (48, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:02:49.821556', '2024-04-04 14:02:49.821556', 1); +INSERT INTO `sys_login_log` VALUES (49, '221.1.97.166', 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:03:01.518252', '2024-04-04 14:03:01.518252', 1); +INSERT INTO `sys_login_log` VALUES (50, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:04:11.038817', '2024-04-04 14:04:11.038817', 1); +INSERT INTO `sys_login_log` VALUES (51, '221.1.97.166', 'Mozilla/5.0 (Linux; U; Android 13; zh-CN; 23043RP34C Build/TKQ1.221114.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.11.0.530 Mobile Safari/537.36', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:06:02.846849', '2024-04-04 14:06:02.846849', 1); +INSERT INTO `sys_login_log` VALUES (52, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:06:05.939892', '2024-04-04 14:06:05.939892', 1); +INSERT INTO `sys_login_log` VALUES (53, '223.104.195.112', 'Dart/3.2 (dart:io)', '山东省潍坊市', NULL, '2024-04-05 08:58:35.158404', '2024-04-05 08:58:35.158404', 1); +INSERT INTO `sys_login_log` VALUES (54, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-05 14:54:34.211950', '2024-04-05 14:54:34.211950', 1); +INSERT INTO `sys_login_log` VALUES (55, '112.226.20.42', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-06 23:33:40.648379', '2024-04-06 23:33:40.648379', 1); +INSERT INTO `sys_login_log` VALUES (56, '144.0.23.133', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省青岛市', NULL, '2024-04-07 10:25:18.662022', '2024-04-07 10:25:18.662022', 1); +INSERT INTO `sys_login_log` VALUES (57, '144.0.23.133', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省青岛市', NULL, '2024-04-07 10:43:27.092753', '2024-04-07 10:43:27.092753', 1); +INSERT INTO `sys_login_log` VALUES (58, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-04-07 10:48:59.426068', '2024-04-07 10:48:59.426068', 9); +INSERT INTO `sys_login_log` VALUES (59, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:49:55.017103', '2024-04-07 10:49:55.017103', 9); +INSERT INTO `sys_login_log` VALUES (60, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:50:14.432721', '2024-04-07 10:50:14.432721', 1); +INSERT INTO `sys_login_log` VALUES (61, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:51:14.143775', '2024-04-07 10:51:14.143775', 9); +INSERT INTO `sys_login_log` VALUES (62, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:52:21.472549', '2024-04-07 10:52:21.472549', 1); +INSERT INTO `sys_login_log` VALUES (63, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:53:25.410445', '2024-04-07 10:53:25.410445', 9); +INSERT INTO `sys_login_log` VALUES (64, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:54:11.422209', '2024-04-07 10:54:11.422209', 1); +INSERT INTO `sys_login_log` VALUES (65, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:58:09.630300', '2024-04-07 10:58:09.630300', 1); +INSERT INTO `sys_login_log` VALUES (66, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:58:48.213228', '2024-04-07 10:58:48.213228', 9); +INSERT INTO `sys_login_log` VALUES (67, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:59:16.336015', '2024-04-07 10:59:16.336015', 9); +INSERT INTO `sys_login_log` VALUES (68, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:59:52.597361', '2024-04-07 10:59:52.597361', 9); +INSERT INTO `sys_login_log` VALUES (69, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-04-07 11:00:39.570222', '2024-04-07 11:00:39.570222', 9); +INSERT INTO `sys_login_log` VALUES (70, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 11:01:59.666647', '2024-04-07 11:01:59.666647', 1); +INSERT INTO `sys_login_log` VALUES (71, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 11:07:30.060388', '2024-04-07 11:07:30.060388', 1); -- ---------------------------- -- Table structure for sys_menu @@ -648,11 +617,11 @@ INSERT INTO `sys_menu` VALUES (1, NULL, '/system', '系统管理', '', 0, 'ant-d INSERT INTO `sys_menu` VALUES (2, 1, '/system/user', '用户管理', 'system:user:list', 1, 'ant-design:user-outlined', 0, 'system/user/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:10:30.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (3, 1, '/system/role', '角色管理', 'system:role:list', 1, 'ep:user', 1, 'system/role/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:02.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (4, 1, '/system/menu', '菜单管理', 'system:menu:list', 1, 'ep:menu', 2, 'system/menu/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:18.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:44.567023', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-04-02 15:00:20.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (6, 5, '/system/monitor/online', '在线用户', 'system:online:list', 1, '', 0, 'system/monitor/online/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:13:59.519267', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (7, 5, '/sys/monitor/login-log', '登录日志', 'system:log:login:list', 1, '', 0, 'system/monitor/log/login/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:02.610719', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (8, 5, '/system/monitor/serve', '服务监控', 'system:serve:stat', 1, '', 4, 'system/monitor/serve/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:05.606355', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:52.967983', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-04-02 15:00:23.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (10, 9, '/system/task', '任务管理', '', 1, '', 0, 'system/schedule/task/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:14:39.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (11, 9, '/system/task/log', '任务日志', 'system:task:list', 1, '', 0, 'system/schedule/log/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:15:01.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (12, NULL, '/document', '文档', '', 0, 'ion:tv-outline', 2, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:51.000000', 0, 1, NULL); @@ -713,7 +682,7 @@ INSERT INTO `sys_menu` VALUES (110, 107, NULL, '删除', 'system:dict-item:delet INSERT INTO `sys_menu` VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL); INSERT INTO `sys_menu` VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 0, 1, '2024-02-10 08:00:02.394616', '2024-03-08 15:14:06.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (116, 48, 'manage', '云盘管理', 'netdisk:manage:list', 1, 'ant-design:cloud-server-outlined', 252, 'netdisk/manage', 0, 1, 1, '2024-02-10 08:03:49.837348', '2024-03-08 15:14:57.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (116, 48, 'manage', '云盘管理', 'netdisk:manage:list', 1, 'ant-design:cloud-server-outlined', 252, 'netdisk/manage', 0, 0, 1, '2024-02-10 08:03:49.837348', '2024-04-07 10:29:44.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL); @@ -725,10 +694,10 @@ INSERT INTO `sys_menu` VALUES (124, 116, NULL, '重命名文件或文件夹', 'n INSERT INTO `sys_menu` VALUES (125, 116, NULL, '复制文件或文件夹', 'netdisk:manage:copy', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:44.725391', '2024-02-10 08:45:48.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (126, 116, NULL, '剪切文件或文件夹', 'netdisk:manage:cut', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:45:21.660511', '2024-02-10 08:45:21.660511', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (127, 115, 'overview', '网盘概览', 'netdisk:overview:desc', 1, '', 254, 'netdisk/overview', 0, 1, 1, '2024-02-10 09:32:56.981190', '2024-02-10 09:34:18.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (128, NULL, '/contract', '合同管理', NULL, 0, 'ep:document', 1, NULL, 1, 1, 1, '2024-02-29 10:40:39.080419', '2024-02-29 10:42:37.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (128, NULL, '/contract', '合同管理', NULL, 0, 'ep:document', 1, NULL, 1, 0, 1, '2024-02-29 10:40:39.080419', '2024-04-02 14:52:28.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (129, 128, '/contract/index', '合同审核', 'app:contract:list', 1, 'ep:document', 1, 'contract/index', 0, 1, 1, '2024-02-29 10:46:09.245521', '2024-02-29 14:59:56.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (130, NULL, '/vehicle-usage/index', '车辆使用', NULL, 1, 'ant-design:car-outlined', 4, 'vehicle-usage/index', 0, 1, 1, '2024-02-29 10:48:35.035363', '2024-03-04 14:18:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (131, 150, '/materials-inventory/record-in-out', '出入库记录', 'materials_inventory:history_in_out:list', 1, 'ep:coin', 3, 'materials-inventory/in-out/index', 0, 1, 1, '2024-02-29 11:03:49.710130', '2024-03-05 15:22:06.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (130, NULL, '/vehicle-usage/index', '车辆使用', NULL, 1, 'ant-design:car-outlined', 4, 'vehicle-usage/index', 0, 0, 1, '2024-02-29 10:48:35.035363', '2024-04-02 14:53:14.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (131, NULL, '/materials-inventory/record-in-out', '出入库记录', 'materials_inventory:history_in_out:list', 1, 'ep:coin', 3, 'materials-inventory/in-out/index', 0, 1, 1, '2024-02-29 11:03:49.710130', '2024-04-02 14:52:54.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (132, 129, NULL, '更新', 'app:contract:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:00:39.641043', '2024-02-29 15:00:39.641043', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (133, 129, NULL, '删除', 'app:contract:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:00:59.376071', '2024-02-29 15:00:59.376071', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (134, 129, NULL, '查询', 'app:contract:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:01:14.209396', '2024-02-29 15:45:29.000000', 0, 1, NULL); @@ -747,20 +716,20 @@ INSERT INTO `sys_menu` VALUES (146, 145, NULL, '单个查询', 'app:product:read INSERT INTO `sys_menu` VALUES (147, 145, NULL, '新增', 'app:product:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:08.211188', '2024-03-04 16:45:08.211188', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (148, 145, NULL, '更新', 'app:product:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:25.457903', '2024-03-04 16:45:25.457903', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (149, 145, NULL, '删除', 'app:product:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:39.352621', '2024-03-04 16:45:39.352621', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (150, NULL, '/materials-inventory', '原材料盘点', NULL, 0, 'ant-design:dashboard-outlined', 3, NULL, 1, 1, 1, '2024-03-04 16:53:32.172674', '2024-03-04 16:53:32.172674', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (150, NULL, '/materials-inventory', '原材料盘点', NULL, 0, 'ant-design:dashboard-outlined', 3, NULL, 1, 0, 1, '2024-03-04 16:53:32.172674', '2024-04-02 14:52:58.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (151, 131, NULL, '导出', 'materials_inventory:history_in_out:export', 2, '', 5, NULL, 1, 1, 1, '2024-03-06 13:09:39.201093', '2024-03-06 13:09:39.201093', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (152, 150, '/materials-inventory/inventory-check', '原材料库存管理', 'app:materials_inventory:list', 1, 'ant-design:dashboard-outlined', 2, 'materials-inventory/inventory-check/index', 1, 1, 1, '2024-03-06 13:33:24.795599', '2024-03-10 18:46:33.000000', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (152, NULL, '/materials-inventory/inventory-check', '原材料库存管理', 'app:materials_inventory:list', 1, 'ant-design:dashboard-outlined', 2, 'materials-inventory/inventory-check/index', 1, 1, 1, '2024-03-06 13:33:24.795599', '2024-04-02 14:52:48.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (153, NULL, '/materials-inventory/project', '项目管理', 'app:project:list', 1, 'ep:memo', 4, 'materials-inventory/project/index', 0, 1, 1, '2024-03-07 09:28:19.234454', '2024-03-27 12:57:13.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (154, 153, NULL, '新增', 'app:project:create', 2, '', 1, NULL, 1, 1, 1, '2024-03-07 09:28:47.855064', '2024-03-07 09:28:47.855064', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (155, 153, NULL, '更新', 'app:project:update', 2, '', 2, NULL, 1, 1, 1, '2024-03-07 09:29:03.183084', '2024-03-07 09:29:03.183084', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (156, 153, NULL, '删除', 'app:project:delete', 2, '', 3, NULL, 1, 1, 1, '2024-03-07 09:29:16.684943', '2024-03-07 09:29:16.684943', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (157, 153, NULL, '单个信息', 'app:project:read', 2, '', 4, NULL, 1, 1, 1, '2024-03-07 09:29:33.424578', '2024-03-07 09:29:33.424578', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (158, 131, NULL, '导出原材料盘点表', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 11:46:54.468400', '2024-03-07 11:46:54.468400', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (158, 131, NULL, '导出原材料盘点表', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 0, '2024-03-07 11:46:54.468400', '2024-04-07 11:02:41.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (159, 130, NULL, '更新', 'app:vehicle_usage:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:04.324327', '2024-03-07 17:05:04.324327', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (160, 130, NULL, '删除', 'app:vehicle_usage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:13.776313', '2024-03-07 17:05:13.776313', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (161, 130, NULL, '新增', 'app:vehicle_usage:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:25.081691', '2024-03-07 17:05:25.081691', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (162, 130, NULL, '单个信息', 'app:vehicle_usage:read', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:48.310497', '2024-03-07 17:05:48.310497', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (163, 152, NULL, '导出', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:43:41.135585', '2024-03-11 13:43:41.135585', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (163, 152, NULL, '导出', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 0, '2024-03-11 13:43:41.135585', '2024-04-07 11:02:08.000000', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (164, 152, NULL, '更新', 'app:materials_inventory:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:44:23.144410', '2024-03-11 13:44:23.144410', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (165, 152, NULL, '删除', 'app:materials_inventory:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:44:47.383396', '2024-03-11 13:44:47.383396', 0, 1, NULL); INSERT INTO `sys_menu` VALUES (166, 128, '/contract/task', '任务管控', NULL, 1, 'ant-design:align-left-outlined', 255, 'task/index', 0, 1, 1, '2024-03-12 10:18:54.645756', '2024-03-12 10:18:54.645756', 0, 1, NULL); @@ -781,14 +750,14 @@ CREATE TABLE `sys_role` ( PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `IDX_223de54d6badbe43a5490450c3`(`name`) USING BTREE, UNIQUE INDEX `IDX_05edc0a51f41bb16b7d8137da9`(`value`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_role -- ---------------------------- -INSERT INTO `sys_role` VALUES (1, 'admin', '管理员', '超级管理员', 1, '2023-11-10 00:31:44.058463', '2024-01-28 21:08:39.000000', NULL); -INSERT INTO `sys_role` VALUES (2, 'user', '用户', '', 1, '2023-11-10 00:31:44.058463', '2024-01-30 18:44:45.000000', 1); -INSERT INTO `sys_role` VALUES (9, 'test', '测试', NULL, 1, '2024-01-23 22:46:52.408827', '2024-01-30 01:04:52.000000', NULL); +INSERT INTO `sys_role` VALUES (1, 'admin', '超级管理员', '超级管理员(拥有所有权限,请谨慎。)', 1, '2023-11-10 00:31:44.058463', '2024-04-07 11:08:14.419171', NULL); +INSERT INTO `sys_role` VALUES (2, 'user', '用户', '基础用户。目前没有设置任何菜单', 1, '2023-11-10 00:31:44.058463', '2024-04-07 11:05:32.000000', 1); +INSERT INTO `sys_role` VALUES (10, 'InventoryManager', '出入库管理员', '可以使用出入库相关的功能', 1, '2024-04-02 14:55:13.393542', '2024-04-07 11:09:03.195979', NULL); -- ---------------------------- -- Table structure for sys_role_menus @@ -807,172 +776,39 @@ CREATE TABLE `sys_role_menus` ( -- ---------------------------- -- Records of sys_role_menus -- ---------------------------- -INSERT INTO `sys_role_menus` VALUES (1, 1); -INSERT INTO `sys_role_menus` VALUES (1, 2); -INSERT INTO `sys_role_menus` VALUES (1, 3); -INSERT INTO `sys_role_menus` VALUES (1, 4); -INSERT INTO `sys_role_menus` VALUES (1, 5); -INSERT INTO `sys_role_menus` VALUES (1, 6); -INSERT INTO `sys_role_menus` VALUES (1, 7); -INSERT INTO `sys_role_menus` VALUES (1, 8); -INSERT INTO `sys_role_menus` VALUES (1, 9); -INSERT INTO `sys_role_menus` VALUES (1, 10); -INSERT INTO `sys_role_menus` VALUES (1, 11); -INSERT INTO `sys_role_menus` VALUES (1, 12); -INSERT INTO `sys_role_menus` VALUES (1, 14); -INSERT INTO `sys_role_menus` VALUES (1, 15); -INSERT INTO `sys_role_menus` VALUES (1, 20); -INSERT INTO `sys_role_menus` VALUES (1, 21); -INSERT INTO `sys_role_menus` VALUES (1, 22); -INSERT INTO `sys_role_menus` VALUES (1, 23); -INSERT INTO `sys_role_menus` VALUES (1, 24); -INSERT INTO `sys_role_menus` VALUES (1, 25); -INSERT INTO `sys_role_menus` VALUES (1, 26); -INSERT INTO `sys_role_menus` VALUES (1, 27); -INSERT INTO `sys_role_menus` VALUES (1, 28); -INSERT INTO `sys_role_menus` VALUES (1, 29); -INSERT INTO `sys_role_menus` VALUES (1, 30); -INSERT INTO `sys_role_menus` VALUES (1, 31); -INSERT INTO `sys_role_menus` VALUES (1, 32); -INSERT INTO `sys_role_menus` VALUES (1, 34); -INSERT INTO `sys_role_menus` VALUES (1, 35); -INSERT INTO `sys_role_menus` VALUES (1, 36); -INSERT INTO `sys_role_menus` VALUES (1, 37); -INSERT INTO `sys_role_menus` VALUES (1, 38); -INSERT INTO `sys_role_menus` VALUES (1, 39); -INSERT INTO `sys_role_menus` VALUES (1, 40); -INSERT INTO `sys_role_menus` VALUES (1, 41); -INSERT INTO `sys_role_menus` VALUES (1, 42); INSERT INTO `sys_role_menus` VALUES (1, 43); -INSERT INTO `sys_role_menus` VALUES (1, 48); -INSERT INTO `sys_role_menus` VALUES (1, 49); -INSERT INTO `sys_role_menus` VALUES (1, 50); -INSERT INTO `sys_role_menus` VALUES (1, 51); -INSERT INTO `sys_role_menus` VALUES (1, 52); -INSERT INTO `sys_role_menus` VALUES (1, 53); -INSERT INTO `sys_role_menus` VALUES (1, 54); -INSERT INTO `sys_role_menus` VALUES (1, 56); -INSERT INTO `sys_role_menus` VALUES (1, 57); -INSERT INTO `sys_role_menus` VALUES (1, 58); -INSERT INTO `sys_role_menus` VALUES (1, 59); -INSERT INTO `sys_role_menus` VALUES (1, 60); -INSERT INTO `sys_role_menus` VALUES (1, 61); -INSERT INTO `sys_role_menus` VALUES (1, 62); -INSERT INTO `sys_role_menus` VALUES (1, 63); -INSERT INTO `sys_role_menus` VALUES (1, 64); -INSERT INTO `sys_role_menus` VALUES (1, 65); -INSERT INTO `sys_role_menus` VALUES (1, 68); -INSERT INTO `sys_role_menus` VALUES (1, 69); -INSERT INTO `sys_role_menus` VALUES (1, 70); -INSERT INTO `sys_role_menus` VALUES (1, 86); -INSERT INTO `sys_role_menus` VALUES (1, 87); -INSERT INTO `sys_role_menus` VALUES (1, 88); -INSERT INTO `sys_role_menus` VALUES (1, 89); -INSERT INTO `sys_role_menus` VALUES (1, 92); -INSERT INTO `sys_role_menus` VALUES (1, 107); -INSERT INTO `sys_role_menus` VALUES (1, 108); -INSERT INTO `sys_role_menus` VALUES (1, 109); -INSERT INTO `sys_role_menus` VALUES (1, 110); -INSERT INTO `sys_role_menus` VALUES (1, 111); -INSERT INTO `sys_role_menus` VALUES (2, 1); -INSERT INTO `sys_role_menus` VALUES (2, 5); -INSERT INTO `sys_role_menus` VALUES (2, 6); -INSERT INTO `sys_role_menus` VALUES (2, 7); -INSERT INTO `sys_role_menus` VALUES (2, 8); -INSERT INTO `sys_role_menus` VALUES (2, 9); -INSERT INTO `sys_role_menus` VALUES (2, 10); -INSERT INTO `sys_role_menus` VALUES (2, 11); -INSERT INTO `sys_role_menus` VALUES (2, 12); -INSERT INTO `sys_role_menus` VALUES (2, 14); -INSERT INTO `sys_role_menus` VALUES (2, 15); -INSERT INTO `sys_role_menus` VALUES (2, 32); -INSERT INTO `sys_role_menus` VALUES (2, 34); -INSERT INTO `sys_role_menus` VALUES (2, 35); -INSERT INTO `sys_role_menus` VALUES (2, 36); -INSERT INTO `sys_role_menus` VALUES (2, 37); -INSERT INTO `sys_role_menus` VALUES (2, 38); -INSERT INTO `sys_role_menus` VALUES (2, 39); -INSERT INTO `sys_role_menus` VALUES (2, 40); -INSERT INTO `sys_role_menus` VALUES (2, 41); -INSERT INTO `sys_role_menus` VALUES (2, 42); INSERT INTO `sys_role_menus` VALUES (2, 43); -INSERT INTO `sys_role_menus` VALUES (2, 48); -INSERT INTO `sys_role_menus` VALUES (2, 49); -INSERT INTO `sys_role_menus` VALUES (2, 50); -INSERT INTO `sys_role_menus` VALUES (2, 51); -INSERT INTO `sys_role_menus` VALUES (2, 52); -INSERT INTO `sys_role_menus` VALUES (2, 53); -INSERT INTO `sys_role_menus` VALUES (2, 56); -INSERT INTO `sys_role_menus` VALUES (2, 57); -INSERT INTO `sys_role_menus` VALUES (2, 58); -INSERT INTO `sys_role_menus` VALUES (2, 59); -INSERT INTO `sys_role_menus` VALUES (2, 60); -INSERT INTO `sys_role_menus` VALUES (2, 68); -INSERT INTO `sys_role_menus` VALUES (2, 69); -INSERT INTO `sys_role_menus` VALUES (2, 70); -INSERT INTO `sys_role_menus` VALUES (2, 86); -INSERT INTO `sys_role_menus` VALUES (2, 87); -INSERT INTO `sys_role_menus` VALUES (2, 88); -INSERT INTO `sys_role_menus` VALUES (2, 89); -INSERT INTO `sys_role_menus` VALUES (2, 92); -INSERT INTO `sys_role_menus` VALUES (2, 107); -INSERT INTO `sys_role_menus` VALUES (2, 108); -INSERT INTO `sys_role_menus` VALUES (2, 109); -INSERT INTO `sys_role_menus` VALUES (2, 110); -INSERT INTO `sys_role_menus` VALUES (2, 111); -INSERT INTO `sys_role_menus` VALUES (2, 112); -INSERT INTO `sys_role_menus` VALUES (9, 1); -INSERT INTO `sys_role_menus` VALUES (9, 2); -INSERT INTO `sys_role_menus` VALUES (9, 3); -INSERT INTO `sys_role_menus` VALUES (9, 4); -INSERT INTO `sys_role_menus` VALUES (9, 5); -INSERT INTO `sys_role_menus` VALUES (9, 6); -INSERT INTO `sys_role_menus` VALUES (9, 7); -INSERT INTO `sys_role_menus` VALUES (9, 8); -INSERT INTO `sys_role_menus` VALUES (9, 9); -INSERT INTO `sys_role_menus` VALUES (9, 10); -INSERT INTO `sys_role_menus` VALUES (9, 11); -INSERT INTO `sys_role_menus` VALUES (9, 20); -INSERT INTO `sys_role_menus` VALUES (9, 21); -INSERT INTO `sys_role_menus` VALUES (9, 22); -INSERT INTO `sys_role_menus` VALUES (9, 23); -INSERT INTO `sys_role_menus` VALUES (9, 24); -INSERT INTO `sys_role_menus` VALUES (9, 25); -INSERT INTO `sys_role_menus` VALUES (9, 26); -INSERT INTO `sys_role_menus` VALUES (9, 27); -INSERT INTO `sys_role_menus` VALUES (9, 28); -INSERT INTO `sys_role_menus` VALUES (9, 29); -INSERT INTO `sys_role_menus` VALUES (9, 30); -INSERT INTO `sys_role_menus` VALUES (9, 31); -INSERT INTO `sys_role_menus` VALUES (9, 32); -INSERT INTO `sys_role_menus` VALUES (9, 34); -INSERT INTO `sys_role_menus` VALUES (9, 35); -INSERT INTO `sys_role_menus` VALUES (9, 36); -INSERT INTO `sys_role_menus` VALUES (9, 37); -INSERT INTO `sys_role_menus` VALUES (9, 38); -INSERT INTO `sys_role_menus` VALUES (9, 39); -INSERT INTO `sys_role_menus` VALUES (9, 40); -INSERT INTO `sys_role_menus` VALUES (9, 41); -INSERT INTO `sys_role_menus` VALUES (9, 42); -INSERT INTO `sys_role_menus` VALUES (9, 54); -INSERT INTO `sys_role_menus` VALUES (9, 56); -INSERT INTO `sys_role_menus` VALUES (9, 57); -INSERT INTO `sys_role_menus` VALUES (9, 58); -INSERT INTO `sys_role_menus` VALUES (9, 59); -INSERT INTO `sys_role_menus` VALUES (9, 60); -INSERT INTO `sys_role_menus` VALUES (9, 61); -INSERT INTO `sys_role_menus` VALUES (9, 62); -INSERT INTO `sys_role_menus` VALUES (9, 63); -INSERT INTO `sys_role_menus` VALUES (9, 64); -INSERT INTO `sys_role_menus` VALUES (9, 65); -INSERT INTO `sys_role_menus` VALUES (9, 68); -INSERT INTO `sys_role_menus` VALUES (9, 69); -INSERT INTO `sys_role_menus` VALUES (9, 70); -INSERT INTO `sys_role_menus` VALUES (9, 86); -INSERT INTO `sys_role_menus` VALUES (9, 87); -INSERT INTO `sys_role_menus` VALUES (9, 88); -INSERT INTO `sys_role_menus` VALUES (9, 89); -INSERT INTO `sys_role_menus` VALUES (9, 92); +INSERT INTO `sys_role_menus` VALUES (10, 48); +INSERT INTO `sys_role_menus` VALUES (10, 51); +INSERT INTO `sys_role_menus` VALUES (10, 52); +INSERT INTO `sys_role_menus` VALUES (10, 53); +INSERT INTO `sys_role_menus` VALUES (10, 131); +INSERT INTO `sys_role_menus` VALUES (10, 136); +INSERT INTO `sys_role_menus` VALUES (10, 137); +INSERT INTO `sys_role_menus` VALUES (10, 138); +INSERT INTO `sys_role_menus` VALUES (10, 139); +INSERT INTO `sys_role_menus` VALUES (10, 140); +INSERT INTO `sys_role_menus` VALUES (10, 141); +INSERT INTO `sys_role_menus` VALUES (10, 142); +INSERT INTO `sys_role_menus` VALUES (10, 143); +INSERT INTO `sys_role_menus` VALUES (10, 144); +INSERT INTO `sys_role_menus` VALUES (10, 145); +INSERT INTO `sys_role_menus` VALUES (10, 146); +INSERT INTO `sys_role_menus` VALUES (10, 147); +INSERT INTO `sys_role_menus` VALUES (10, 148); +INSERT INTO `sys_role_menus` VALUES (10, 149); +INSERT INTO `sys_role_menus` VALUES (10, 150); +INSERT INTO `sys_role_menus` VALUES (10, 151); +INSERT INTO `sys_role_menus` VALUES (10, 152); +INSERT INTO `sys_role_menus` VALUES (10, 153); +INSERT INTO `sys_role_menus` VALUES (10, 154); +INSERT INTO `sys_role_menus` VALUES (10, 155); +INSERT INTO `sys_role_menus` VALUES (10, 156); +INSERT INTO `sys_role_menus` VALUES (10, 157); +INSERT INTO `sys_role_menus` VALUES (10, 158); +INSERT INTO `sys_role_menus` VALUES (10, 163); +INSERT INTO `sys_role_menus` VALUES (10, 164); +INSERT INTO `sys_role_menus` VALUES (10, 165); -- ---------------------------- -- Table structure for sys_task @@ -1001,7 +837,7 @@ CREATE TABLE `sys_task` ( -- ---------------------------- -- Records of sys_task -- ---------------------------- -INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-03-28 13:02:28.000000'); +INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-04-07 10:57:58.000000'); INSERT INTO `sys_task` VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 0, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-03-22 14:12:52.000000'); INSERT INTO `sys_task` VALUES (4, '访问百度首页', 'HttpRequestJob.handle', 0, 0, NULL, NULL, 1, '* * * * * ?', NULL, '{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}', NULL, '访问百度首页', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); INSERT INTO `sys_task` VALUES (5, '发送邮箱', 'EmailJob.send', 0, 0, NULL, NULL, -1, '0 0 0 1 * ?', NULL, '{\"subject\":\"这是标题\",\"to\":\"18661983080@163.com\",\"content\":\"这是正文\"}', NULL, '每月发送邮箱', '2023-11-10 00:31:44.197779', '2024-03-07 11:14:53.000000'); @@ -1021,13 +857,14 @@ CREATE TABLE `sys_task_log` ( PRIMARY KEY (`id`) USING BTREE, INDEX `FK_f4d9c36052fdb188ff5c089454b`(`task_id`) USING BTREE, CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_task_log -- ---------------------------- INSERT INTO `sys_task_log` VALUES (1, 3, 1, NULL, 0, '2024-03-11 07:37:16.258223', '2024-03-11 07:37:16.258223'); INSERT INTO `sys_task_log` VALUES (2, 2, 1, NULL, 0, '2024-03-11 08:29:25.175865', '2024-03-11 08:29:25.175865'); +INSERT INTO `sys_task_log` VALUES (3, 2, 1, NULL, 0, '2024-04-01 03:00:00.202419', '2024-04-01 03:00:00.202419'); -- ---------------------------- -- Table structure for sys_user @@ -1052,14 +889,15 @@ CREATE TABLE `sys_user` ( UNIQUE INDEX `IDX_9e7164b2f1ea1348bc0eb0a7da`(`username`) USING BTREE, INDEX `FK_96bde34263e2ae3b46f011124ac`(`dept_id`) USING BTREE, CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_user -- ---------------------------- -INSERT INTO `sys_user` VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-27 11:32:42.000000', '朱明仁12', 2); -INSERT INTO `sys_user` VALUES (2, 'user', 'dbd89546dec743f82bb9073d6ac39361', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', 'luffy@qq.com', '10010', '王路飞', 'qlovDV7pL5dPYPI3QgFFo1HH74nP6sJe', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-01 11:31:11.000000', 'luffy', 8); -INSERT INTO `sys_user` VALUES (8, 'developer', 'f03fa2a99595127b9a39587421d471f6', '/upload/报名照片-202402281149824.jpg', 'nami@qq.com', '10000', '小贼猫', 'NbGM1z9Vhgo7f4dd2I7JGaGP12RidZdE', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-03-06 17:17:21.000000', '娜美', 2); +INSERT INTO `sys_user` VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-04-02 14:53:56.000000', '朱明仁', 2); +INSERT INTO `sys_user` VALUES (9, 'mengfei', '53a7b8157dbbbd51687c23cb783c5fe4', NULL, NULL, NULL, NULL, 'hm-VO0n2GO0qEZhEz6LdBBtFhIEMv0jo', 1, NULL, '2024-04-02 14:59:08.770239', '2024-04-07 10:50:24.000000', '孟菲', 2); +INSERT INTO `sys_user` VALUES (10, 'wangxinghao', '41c7f42c6e8a3eec7ac5b41ae0de84be', '[object Object]', NULL, NULL, NULL, '--IWP-ybu1ikzGpGOVCWEpkZ1hIheCVJ', 1, NULL, '2024-04-02 15:39:38.117227', '2024-04-07 11:07:49.000000', '王兴昊', 2); +INSERT INTO `sys_user` VALUES (11, 'zhangxueyong', 'c67a29c03f168650e2a43590328446b8', NULL, NULL, NULL, '张学勇', 'ucnRs7VTbXnwej2JQbKeYSoJ1gLy2Fwi', 1, NULL, '2024-04-03 08:10:08.192204', '2024-04-07 11:07:54.000000', '张学勇', 1); -- ---------------------------- -- Table structure for sys_user_roles @@ -1079,8 +917,9 @@ CREATE TABLE `sys_user_roles` ( -- Records of sys_user_roles -- ---------------------------- INSERT INTO `sys_user_roles` VALUES (1, 1); -INSERT INTO `sys_user_roles` VALUES (2, 2); -INSERT INTO `sys_user_roles` VALUES (8, 2); +INSERT INTO `sys_user_roles` VALUES (9, 10); +INSERT INTO `sys_user_roles` VALUES (10, 10); +INSERT INTO `sys_user_roles` VALUES (11, 10); -- ---------------------------- -- Table structure for todo @@ -1121,111 +960,11 @@ CREATE TABLE `tool_storage` ( `bussiness_module` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `bussiness_record_id` int NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 224 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 280 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of tool_storage -- ---------------------------- -INSERT INTO `tool_storage` VALUES (121, '2024-03-01 16:46:58.441084', '2024-03-01 16:46:58.441084', '1709172270328-202403011646430.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403011646430.jpg', '图片', '62.79 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (122, '2024-03-01 17:21:35.454311', '2024-03-01 17:21:35.454311', '盘点表-202403011721448.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403011721448.xlsx', '文档', '10.83 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (123, '2024-03-04 15:51:50.664699', '2024-03-04 15:51:50.664699', '20240304-202403041551657.sql', '20240304.sql', 'sql', '/upload/20240304-202403041551657.sql', '其他', '62.86 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (124, '2024-03-05 10:39:45.040659', '2024-03-05 10:39:45.040659', '盘点表-202403051039028.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403051039028.xlsx', '文档', '10.83 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (126, '2024-03-06 13:04:17.636919', '2024-03-06 13:04:17.636919', '盘点表-202403061304624.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403061304624.xlsx', '文档', '10.83 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (127, '2024-03-07 09:35:51.916017', '2024-03-07 09:35:51.916017', '盘点表-202403070935910.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403070935910.xlsx', '文档', '10.83 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (128, '2024-03-07 09:36:01.570104', '2024-03-07 09:36:01.570104', '1709172270328-202403070936565.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403070936565.jpg', '图片', '62.86 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (129, '2024-03-07 16:28:46.729632', '2024-03-07 16:28:46.729632', '盘点表-202403071628719.xlsx', '盘点表.xlsx', 'xlsx', '/upload/盘点表-202403071628719.xlsx', '文档', '10.83 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (130, '2024-03-08 08:29:50.602980', '2024-03-08 08:29:50.602980', '1709172270328-202403080829597.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080829597.jpg', '图片', '62.86 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (133, '2024-03-08 08:36:52.107852', '2024-03-08 08:36:52.107852', '1709172270328-202403080836097.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080836097.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (134, '2024-03-08 08:36:57.397270', '2024-03-08 08:36:57.397270', 'hxoa_2024-03-07_171919-202403080836391.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080836391.sql', '其他', '62.88 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (135, '2024-03-08 08:39:36.243846', '2024-03-08 08:39:36.243846', 'hxoa_2024-03-07_171919-202403080839235.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080839235.sql', '其他', '62.88 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (136, '2024-03-08 08:39:45.015279', '2024-03-08 08:39:45.015279', '1709172270328-202403080839012.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080839012.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (137, '2024-03-08 08:41:01.350779', '2024-03-08 08:41:01.350779', 'hxoa_2024-03-07_171919-202403080841340.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080841340.sql', '其他', '62.88 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (138, '2024-03-08 08:42:09.015239', '2024-03-08 08:42:09.015239', '1709172270328-202403080842003.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080842003.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (139, '2024-03-08 08:46:36.613712', '2024-03-08 08:46:36.613712', '1709172270328-202403080846601.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080846601.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (140, '2024-03-08 08:48:21.472060', '2024-03-08 08:48:21.472060', '1709172270328-202403080848461.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080848461.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (141, '2024-03-08 08:48:32.069968', '2024-03-08 08:48:32.069968', '1709172270328-202403080848060.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080848060.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (142, '2024-03-08 08:48:32.071978', '2024-03-08 08:48:32.071978', 'hxoa_2024-03-07_171919-202403080848065.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080848065.sql', '其他', '62.88 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (143, '2024-03-08 08:48:36.850064', '2024-03-08 08:48:36.850064', '1709172270328-202403080848844.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080848844.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (144, '2024-03-08 08:48:43.982565', '2024-03-08 08:48:43.982565', '1709172270328-202403080848977.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403080848977.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (145, '2024-03-08 08:48:49.327172', '2024-03-08 08:48:49.327172', 'hxoa_2024-03-07_171919-202403080848322.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403080848322.sql', '其他', '62.88 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (146, '2024-03-08 10:05:23.771674', '2024-03-08 10:05:23.771674', '1709172270328-202403081005760.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081005760.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (147, '2024-03-08 10:05:29.481209', '2024-03-08 10:05:29.481209', 'hxoa_2024-03-07_171919-202403081005473.sql', 'hxoa_2024-03-07_171919.sql', 'sql', '/upload/hxoa_2024-03-07_171919-202403081005473.sql', '其他', '62.88 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (148, '2024-03-08 10:57:38.555901', '2024-03-08 10:57:38.555901', '1709172270328-202403081057545.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081057545.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (149, '2024-03-08 11:06:36.879749', '2024-03-08 11:06:36.879749', '1709172270328-202403081106867.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081106867.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (150, '2024-03-08 11:07:11.217830', '2024-03-08 11:07:11.217830', '1709172270328-202403081107212.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081107212.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (151, '2024-03-08 11:08:24.817797', '2024-03-08 11:08:24.817797', '1709172270328-202403081108811.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081108811.jpg', '图片', '15.86 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (152, '2024-03-08 11:10:12.868485', '2024-03-08 11:10:12.868485', '1709172270328-202403081109532.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081109532.jpg', '图片', '62.86 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (153, '2024-03-08 11:10:32.700427', '2024-03-08 11:10:32.700427', '1709172270328-202403081110695.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081110695.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (154, '2024-03-08 11:11:25.391852', '2024-03-08 11:11:25.391852', '微信图片_20240308085222-202403081111382.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081111382.jpg', '图片', '62.89 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (155, '2024-03-08 11:17:01.314543', '2024-03-08 11:17:01.314543', '微信图片_20240308085222-202403081117276.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081117276.jpg', '图片', '62.89 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (156, '2024-03-08 11:23:33.233526', '2024-03-08 11:23:33.233526', '微信图片_20240308085222-202403081123218.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081123218.jpg', '图片', '62.89 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (157, '2024-03-08 11:28:30.444379', '2024-03-08 11:28:30.444379', '微信图片_20240308085222-202403081128414.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081128414.jpg', '图片', '62.89 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (158, '2024-03-08 11:31:22.373716', '2024-03-08 11:31:22.373716', '1709172270328-202403081131338.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081131338.jpg', '图片', '62.9 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (159, '2024-03-08 11:33:01.737383', '2024-03-08 11:33:01.737383', '微信图片_20240308085222-202403081133723.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081133723.jpg', '图片', '62.89 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (160, '2024-03-08 11:42:52.468709', '2024-03-08 11:42:52.468709', '1709172270328-202403081142459.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081142459.jpg', '图片', '81.56 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (161, '2024-03-08 11:43:32.556103', '2024-03-08 11:43:32.556103', '1709172270328-202403081143533.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081143533.jpg', '图片', '81.56 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (162, '2024-03-08 11:44:02.489693', '2024-03-08 11:44:02.489693', '1709172270328-202403081144477.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081144477.jpg', '图片', '81.56 KB', 1, NULL, NULL); -INSERT INTO `tool_storage` VALUES (163, '2024-03-08 11:44:54.626236', '2024-03-08 11:44:54.626236', '1709172270328-202403081144616.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081144616.jpg', '图片', '81.56 KB', 1, 'materialsInOut', 28); -INSERT INTO `tool_storage` VALUES (164, '2024-03-08 13:11:26.444515', '2024-03-08 13:11:26.444515', '1709172270328-202403081311434.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081311434.jpg', '图片', '81.56 KB', 1, 'contract', 2); -INSERT INTO `tool_storage` VALUES (165, '2024-03-08 13:11:26.750173', '2024-03-08 13:11:26.750173', '微信图片_20240308085222-202403081311745.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081311745.jpg', '图片', '314.97 KB', 1, 'contract', 2); -INSERT INTO `tool_storage` VALUES (166, '2024-03-08 13:16:11.698799', '2024-03-08 13:16:11.698799', '1709172270328-202403081316681.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081316681.jpg', '图片', '81.56 KB', 1, 'contract', 2); -INSERT INTO `tool_storage` VALUES (167, '2024-03-08 13:16:11.709257', '2024-03-08 13:16:11.709257', '微信图片_20240308085222-202403081316688.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081316688.jpg', '图片', '314.97 KB', 1, 'contract', 2); -INSERT INTO `tool_storage` VALUES (168, '2024-03-08 13:17:00.151497', '2024-03-08 13:17:00.151497', '1709172270328-202403081317138.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403081317138.jpg', '图片', '81.56 KB', 1, 'contract', 2); -INSERT INTO `tool_storage` VALUES (169, '2024-03-08 13:17:00.160739', '2024-03-08 13:17:00.160739', '微信图片_20240308085222-202403081317143.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403081317143.jpg', '图片', '314.97 KB', 1, 'contract', 2); -INSERT INTO `tool_storage` VALUES (170, '2024-03-11 15:29:12.145015', '2024-03-11 15:29:12.145015', '1709172270328-202403111529135.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403111529135.jpg', '图片', '81.56 KB', 1, 'contract', 7); -INSERT INTO `tool_storage` VALUES (171, '2024-03-11 15:29:12.147324', '2024-03-11 15:29:12.147324', '微信图片_20240308085222-202403111529139.jpg', '微信图片_20240308085222.jpg', 'jpg', '/upload/微信图片_20240308085222-202403111529139.jpg', '图片', '314.97 KB', 1, 'contract', 7); -INSERT INTO `tool_storage` VALUES (172, '2024-03-11 17:10:39.695036', '2024-03-11 17:10:39.695036', '1709172270328-202403111710690.jpg', '1709172270328.jpg', 'jpg', '/upload/1709172270328-202403111710690.jpg', '图片', '81.56 KB', 1, 'contract', 8); -INSERT INTO `tool_storage` VALUES (173, '2024-03-21 16:08:32.854324', '2024-03-21 16:08:32.854324', 'yanshi1-202403211608556.jpg', 'yanshi1.jpg', 'jpg', '/upload/yanshi1-202403211608556.jpg', '图片', '61.08 KB', 1, 'materialsInOut', 72); -INSERT INTO `tool_storage` VALUES (174, '2024-03-21 17:09:15.653321', '2024-03-21 17:09:15.653321', 'pos-x-202403211709234.jpg', 'pos-x.jpg', 'jpg', '/upload/pos-x-202403211709234.jpg', '图片', '63.04 KB', 1, 'materialsInOut', 71); -INSERT INTO `tool_storage` VALUES (175, '2024-03-21 17:09:32.944092', '2024-03-21 17:09:32.944092', '3d66Model-12494952-files-320-202403211709521.jpg', '3d66Model-12494952-files-320.jpg', 'jpg', '/upload/3d66Model-12494952-files-320-202403211709521.jpg', '图片', '63.66 KB', 1, 'materialsInOut', 70); -INSERT INTO `tool_storage` VALUES (176, '2024-03-21 17:14:06.418390', '2024-03-21 17:14:06.418390', '3d66Model-12494952-files-320-202403211714672.jpg', '3d66Model-12494952-files-320.jpg', 'jpg', '/upload/3d66Model-12494952-files-320-202403211714672.jpg', '图片', '63.66 KB', 1, 'product', 22); -INSERT INTO `tool_storage` VALUES (177, '2024-03-22 17:25:30.260926', '2024-03-22 17:25:30.260926', '3d66Model-12494952-files-320-202403221725405.jpg', '3d66Model-12494952-files-320.jpg', 'jpg', '/upload/3d66Model-12494952-files-320-202403221725405.jpg', '图片', '63.66 KB', 1, 'product', 27); -INSERT INTO `tool_storage` VALUES (178, '2024-03-25 16:06:10.167823', '2024-03-25 16:06:10.167823', '3d66Model-12494952-files-3-202403251606976.jpg', '3d66Model-12494952-files-3.jpg', 'jpg', '/upload/3d66Model-12494952-files-3-202403251606976.jpg', '图片', '98.35 KB', 1, 'materialsInOut', 97); -INSERT INTO `tool_storage` VALUES (179, '2024-03-25 16:27:59.456520', '2024-03-25 16:27:59.456520', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251627221.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251627221.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (180, '2024-03-25 16:28:19.171211', '2024-03-25 16:28:19.171211', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628947.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628947.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (181, '2024-03-25 16:28:24.271093', '2024-03-25 16:28:24.271093', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628046.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628046.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (182, '2024-03-25 16:28:51.337125', '2024-03-25 16:28:51.337125', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628103.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251628103.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (183, '2024-03-25 16:29:52.097157', '2024-03-25 16:29:52.097157', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251629862.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251629862.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (184, '2024-03-25 16:29:52.100139', '2024-03-25 16:29:52.100139', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251629866.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251629866.jpg', '图片', '57.89 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (185, '2024-03-25 16:30:12.681895', '2024-03-25 16:30:12.681895', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251630444.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251630444.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (186, '2024-03-25 16:30:12.683375', '2024-03-25 16:30:12.683375', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251630444.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251630444.jpg', '图片', '57.89 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (187, '2024-03-25 16:31:29.716111', '2024-03-25 16:31:29.716111', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251631475.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251631475.jpg', '图片', '57.89 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (188, '2024-03-25 16:31:29.716924', '2024-03-25 16:31:29.716924', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251631476.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251631476.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (189, '2024-03-25 16:32:42.493071', '2024-03-25 16:32:42.493071', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251632252.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251632252.jpg', '图片', '57.89 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (190, '2024-03-25 16:32:42.497067', '2024-03-25 16:32:42.497067', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251632256.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251632256.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (191, '2024-03-25 16:35:27.743107', '2024-03-25 16:35:27.743107', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251635499.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251635499.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (192, '2024-03-25 16:35:27.746570', '2024-03-25 16:35:27.746570', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251635504.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251635504.jpg', '图片', '57.89 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (193, '2024-03-25 16:35:57.996009', '2024-03-25 16:35:57.996009', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251635747.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251635747.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (194, '2024-03-25 16:35:57.997445', '2024-03-25 16:35:57.997445', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251635747.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251635747.jpg', '图片', '57.89 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (195, '2024-03-25 16:37:20.048302', '2024-03-25 16:37:20.048302', '3d66Model-12494952-files-3-202403251637797.jpg', '3d66Model-12494952-files-3.jpg', 'jpg', '/upload/3d66Model-12494952-files-3-202403251637797.jpg', '图片', '98.35 KB', 1, 'materialsInOut', 112); -INSERT INTO `tool_storage` VALUES (196, '2024-03-25 16:37:43.625126', '2024-03-25 16:37:43.625126', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251637373.jpg', 'scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450.jpg', 'jpg', '/upload/scaled_9390035c-08ed-4aa3-b057-f90dd30471d5570494671907680450-202403251637373.jpg', '图片', '53.94 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (197, '2024-03-25 16:37:43.626911', '2024-03-25 16:37:43.626911', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251637376.jpg', 'scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404.jpg', 'jpg', '/upload/scaled_5c75f319-8e09-4a35-8477-27241841e2b11618911960313675404-202403251637376.jpg', '图片', '57.89 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (198, '2024-03-25 16:38:32.095682', '2024-03-25 16:38:32.095682', '3d66Model-12494952-files-3-202403251638843.jpg', '3d66Model-12494952-files-3.jpg', 'jpg', '/upload/3d66Model-12494952-files-3-202403251638843.jpg', '图片', '98.35 KB', 1, 'materialsInOut', 112); -INSERT INTO `tool_storage` VALUES (199, '2024-03-25 16:43:55.437346', '2024-03-25 16:43:55.437346', 'scaled_80d5e223-5520-467f-a9f9-810853de652c6486872489674521978-202403251643174.jpg', 'scaled_80d5e223-5520-467f-a9f9-810853de652c6486872489674521978.jpg', 'jpg', '/upload/scaled_80d5e223-5520-467f-a9f9-810853de652c6486872489674521978-202403251643174.jpg', '图片', '55.62 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (200, '2024-03-25 16:43:55.439011', '2024-03-25 16:43:55.439011', 'scaled_a4227c96-596e-490d-a92f-e0f55cef400a8437526180198455207-202403251643177.jpg', 'scaled_a4227c96-596e-490d-a92f-e0f55cef400a8437526180198455207.jpg', 'jpg', '/upload/scaled_a4227c96-596e-490d-a92f-e0f55cef400a8437526180198455207-202403251643177.jpg', '图片', '53.7 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (201, '2024-03-26 09:11:43.924051', '2024-03-26 09:11:43.924051', 'scaled_1a637967-322f-4077-b571-67318831e47a5388336458357771115-202403260911463.jpg', 'scaled_1a637967-322f-4077-b571-67318831e47a5388336458357771115.jpg', 'jpg', '/upload/scaled_1a637967-322f-4077-b571-67318831e47a5388336458357771115-202403260911463.jpg', '图片', '49.49 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (202, '2024-03-26 09:21:05.571986', '2024-03-26 09:21:05.571986', 'scaled_dc17a892-6bbc-4749-8077-1be8ce3d2fa53123598047088265103-202403260921672.jpg', 'scaled_dc17a892-6bbc-4749-8077-1be8ce3d2fa53123598047088265103.jpg', 'jpg', '/upload/scaled_dc17a892-6bbc-4749-8077-1be8ce3d2fa53123598047088265103-202403260921672.jpg', '图片', '51.63 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (203, '2024-03-26 11:34:25.342170', '2024-03-26 11:34:25.342170', 'scaled_d681979b-ca63-453c-8809-e6997c5af7005514768979314679098-202403261134166.jpg', 'scaled_d681979b-ca63-453c-8809-e6997c5af7005514768979314679098.jpg', 'jpg', '/upload/scaled_d681979b-ca63-453c-8809-e6997c5af7005514768979314679098-202403261134166.jpg', '图片', '53.62 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (204, '2024-03-26 13:56:08.343969', '2024-03-26 13:56:08.343969', 'scaled_d2d451ee-173d-462a-952c-352a3e8fd0c37407975546900753888-202403261356811.jpg', 'scaled_d2d451ee-173d-462a-952c-352a3e8fd0c37407975546900753888.jpg', 'jpg', '/upload/scaled_d2d451ee-173d-462a-952c-352a3e8fd0c37407975546900753888-202403261356811.jpg', '图片', '74.12 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (205, '2024-03-26 13:56:08.343974', '2024-03-26 13:56:08.343974', 'scaled_2ea135c3-814d-4036-9937-09c88f4ead9f6687203029443796549-202403261356810.jpg', 'scaled_2ea135c3-814d-4036-9937-09c88f4ead9f6687203029443796549.jpg', 'jpg', '/upload/scaled_2ea135c3-814d-4036-9937-09c88f4ead9f6687203029443796549-202403261356810.jpg', '图片', '89.27 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (206, '2024-03-26 13:56:08.366744', '2024-03-26 13:56:08.366744', 'scaled_599c7a69-d994-460c-b79a-3cefd482e7434061251540207554146-202403261356811.jpg', 'scaled_599c7a69-d994-460c-b79a-3cefd482e7434061251540207554146.jpg', 'jpg', '/upload/scaled_599c7a69-d994-460c-b79a-3cefd482e7434061251540207554146-202403261356811.jpg', '图片', '89.49 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (207, '2024-03-26 13:56:57.545563', '2024-03-26 13:56:57.545563', 'scaled_f7a13c2e-134c-4de4-90ff-8a4680fd652f1553028774544341980-202403261357015.jpg', 'scaled_f7a13c2e-134c-4de4-90ff-8a4680fd652f1553028774544341980.jpg', 'jpg', '/upload/scaled_f7a13c2e-134c-4de4-90ff-8a4680fd652f1553028774544341980-202403261357015.jpg', '图片', '78.58 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (208, '2024-03-26 13:56:57.546780', '2024-03-26 13:56:57.546780', 'scaled_7118a807-43b8-44b8-b86a-2823da96b099726991530082479093-202403261357018.jpg', 'scaled_7118a807-43b8-44b8-b86a-2823da96b099726991530082479093.jpg', 'jpg', '/upload/scaled_7118a807-43b8-44b8-b86a-2823da96b099726991530082479093-202403261357018.jpg', '图片', '59.26 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (209, '2024-03-26 13:56:57.546783', '2024-03-26 13:56:57.546783', 'scaled_92d71763-5574-46ad-9176-330cabf18f268972942500955895967-202403261357018.jpg', 'scaled_92d71763-5574-46ad-9176-330cabf18f268972942500955895967.jpg', 'jpg', '/upload/scaled_92d71763-5574-46ad-9176-330cabf18f268972942500955895967-202403261357018.jpg', '图片', '67.25 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (210, '2024-03-26 13:56:57.567969', '2024-03-26 13:56:57.567969', 'scaled_7a9264a9-fbee-4f25-a0af-4672edf305626973828255576468559-202403261357018.jpg', 'scaled_7a9264a9-fbee-4f25-a0af-4672edf305626973828255576468559.jpg', 'jpg', '/upload/scaled_7a9264a9-fbee-4f25-a0af-4672edf305626973828255576468559-202403261357018.jpg', '图片', '85.57 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (211, '2024-03-26 13:56:57.570940', '2024-03-26 13:56:57.570940', 'scaled_3df02010-2270-4dcc-bb56-371de26897037809041329322071714-202403261357021.jpg', 'scaled_3df02010-2270-4dcc-bb56-371de26897037809041329322071714.jpg', 'jpg', '/upload/scaled_3df02010-2270-4dcc-bb56-371de26897037809041329322071714-202403261357021.jpg', '图片', '99.8 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (212, '2024-03-26 14:27:03.612654', '2024-03-26 14:27:03.612654', 'scaled_643dbbf9-d698-409d-b564-785ada1cf8393161751276012656173-202403261427036.jpg', 'scaled_643dbbf9-d698-409d-b564-785ada1cf8393161751276012656173.jpg', 'jpg', '/upload/scaled_643dbbf9-d698-409d-b564-785ada1cf8393161751276012656173-202403261427036.jpg', '图片', '475.95 KB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (213, '2024-03-26 14:27:51.534329', '2024-03-26 14:27:51.534329', 'scaled_87a5fb79-970e-40b8-8036-06b847ff5ed77569061097438195214-202403261427941.jpg', 'scaled_87a5fb79-970e-40b8-8036-06b847ff5ed77569061097438195214.jpg', 'jpg', '/upload/scaled_87a5fb79-970e-40b8-8036-06b847ff5ed77569061097438195214-202403261427941.jpg', '图片', '1.68 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (214, '2024-03-26 14:48:50.771816', '2024-03-26 14:48:50.771816', 'scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510-202403261448367.jpg', 'scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510.jpg', 'jpg', '/upload/scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510-202403261448367.jpg', '图片', '1.17 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (215, '2024-03-26 14:56:59.991816', '2024-03-26 14:56:59.991816', 'scaled_072d9fe8-fc60-4626-8a3b-06639e1a11102576129031638219701-202403261457570.jpg', 'scaled_072d9fe8-fc60-4626-8a3b-06639e1a11102576129031638219701.jpg', 'jpg', '/upload/scaled_072d9fe8-fc60-4626-8a3b-06639e1a11102576129031638219701-202403261457570.jpg', '图片', '1.79 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (216, '2024-03-26 14:57:00.859728', '2024-03-26 14:57:00.859728', 'scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510-202403261457443.jpg', 'scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510.jpg', 'jpg', '/upload/scaled_2b280b65-beec-4dcb-a39e-13427871926a589685855459616510-202403261457443.jpg', '图片', '1.17 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (217, '2024-03-26 15:00:10.928209', '2024-03-26 15:00:10.928209', 'scaled_cff03a18-0ec1-4fde-b940-01de3acc6d061416191473797538812-202403261500491.jpg', 'scaled_cff03a18-0ec1-4fde-b940-01de3acc6d061416191473797538812.jpg', 'jpg', '/upload/scaled_cff03a18-0ec1-4fde-b940-01de3acc6d061416191473797538812-202403261500491.jpg', '图片', '1.16 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (218, '2024-03-26 15:15:11.475534', '2024-03-26 15:15:11.475534', 'scaled_af448995-1a4c-4196-a11e-6dc95cd91ba05659735836510102013-202403261515014.jpg', 'scaled_af448995-1a4c-4196-a11e-6dc95cd91ba05659735836510102013.jpg', 'jpg', '/upload/scaled_af448995-1a4c-4196-a11e-6dc95cd91ba05659735836510102013-202403261515014.jpg', '图片', '2.26 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (219, '2024-03-26 15:16:10.017398', '2024-03-26 15:16:10.017398', 'scaled_4bc91ea6-1949-49d8-8d51-1b8573bf108e4784062601379129071-202403261516559.jpg', 'scaled_4bc91ea6-1949-49d8-8d51-1b8573bf108e4784062601379129071.jpg', 'jpg', '/upload/scaled_4bc91ea6-1949-49d8-8d51-1b8573bf108e4784062601379129071-202403261516559.jpg', '图片', '1.3 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (220, '2024-03-26 15:16:51.648859', '2024-03-26 15:16:51.648859', 'scaled_314eb685-b25d-4834-841f-e23751cd2a5f5192837258445318655-202403261516191.jpg', 'scaled_314eb685-b25d-4834-841f-e23751cd2a5f5192837258445318655.jpg', 'jpg', '/upload/scaled_314eb685-b25d-4834-841f-e23751cd2a5f5192837258445318655-202403261516191.jpg', '图片', '1.85 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (221, '2024-03-27 10:04:51.360595', '2024-03-27 10:04:51.360595', '3d66Model-12494952-files-3-202403271004438.jpg', '3d66Model-12494952-files-3.jpg', 'jpg', '/upload/3d66Model-12494952-files-3-202403271004438.jpg', '图片', '98.35 KB', 1, 'materialsInOut', 166); -INSERT INTO `tool_storage` VALUES (222, '2024-03-27 10:07:21.843030', '2024-03-27 10:07:21.843030', 'scaled_6926ce62-b333-43f0-b9d5-fa1cff94b0026152296259250934531-202403271007912.jpg', 'scaled_6926ce62-b333-43f0-b9d5-fa1cff94b0026152296259250934531.jpg', 'jpg', '/upload/scaled_6926ce62-b333-43f0-b9d5-fa1cff94b0026152296259250934531-202403271007912.jpg', '图片', '1.18 MB', 1, '', NULL); -INSERT INTO `tool_storage` VALUES (223, '2024-03-27 10:07:21.860260', '2024-03-27 10:07:21.860260', 'scaled_0de1e7f6-cca7-4172-9ad6-5288212ea3de160786817006652480-202403271007930.jpg', 'scaled_0de1e7f6-cca7-4172-9ad6-5288212ea3de160786817006652480.jpg', 'jpg', '/upload/scaled_0de1e7f6-cca7-4172-9ad6-5288212ea3de160786817006652480-202403271007930.jpg', '图片', '1.17 MB', 1, '', NULL); -- ---------------------------- -- Table structure for user_access_tokens @@ -1245,80 +984,23 @@ CREATE TABLE `user_access_tokens` ( -- ---------------------------- -- Records of user_access_tokens -- ---------------------------- -INSERT INTO `user_access_tokens` VALUES ('00866088-2523-44ab-b972-1edb92b5318a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTg4NTF9.lKuI2g6Zp8Xn_j9TkOpd92OZ3HEAVrXqsGFeBrqjeMg', '2024-03-28 13:54:12', '2024-03-27 13:54:12.187932', 1); -INSERT INTO `user_access_tokens` VALUES ('0233ea11-41af-453a-b8ed-b9731b252d05', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MzcxNTl9.3ZKGerjK3ohCnmZzWIi7DmpvH3DIFX6MGS2VESWnuAQ', '2024-03-20 16:32:40', '2024-03-19 16:32:41.320136', 1); -INSERT INTO `user_access_tokens` VALUES ('0a058377-1c0e-48d6-b143-21d5195ac859', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA3NTE2MjN9.0XL_QKSrMlqkMCMEnon4Yju6Iz44b62CAEWZ9Sd9PvI', '2024-03-19 16:47:04', '2024-03-18 16:47:04.547719', 1); -INSERT INTO `user_access_tokens` VALUES ('0a52047b-397f-4ff2-9bfc-9940e17b2fcb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDgzNzd9.xwBg4Y4QJz2T2t82G2y1EbDTOWHH18AkzvnphAih6vA', '2024-03-28 10:59:38', '2024-03-27 10:59:38.011777', 1); -INSERT INTO `user_access_tokens` VALUES ('0baa35e7-11a8-4280-be13-b12c9416283b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0Mzk2NTd9.u-QKP2oMbKGcItCznOakxzBPg67JJLZRiyWHBDxareM', '2024-03-27 15:54:18', '2024-03-26 15:54:15.402975', 1); -INSERT INTO `user_access_tokens` VALUES ('0bee723d-c635-4396-9656-8165ac9594b0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5OTEyNDR9.tVL8dDnSE5ZqgZpZ1a90cK5JqGN7Bh8pkSZj7OOvdaM', '2024-03-22 11:20:44', '2024-03-21 11:20:45.958604', 1); -INSERT INTO `user_access_tokens` VALUES ('0f9677c0-6a16-4b6b-ac83-2ee669fb7ced', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE2NjN9.vSDaVKDcN5HOGNX5d_rJC4SOo2d3P-oLUttQlkBrm0U', '2024-03-20 09:27:44', '2024-03-19 09:27:47.503333', 1); -INSERT INTO `user_access_tokens` VALUES ('191ada91-e54d-46c5-a20d-448022f5da60', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MzQ4MTB9.5sNHITFJWsfOUMrl2YL6gEnPp_9oo7Ri9-Uumzs_sMo', '2024-03-20 15:53:31', '2024-03-19 15:53:32.249226', 1); -INSERT INTO `user_access_tokens` VALUES ('1added11-2ae5-4e39-bfd1-3972d3d95ad6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTEzNDN9.onuHFlLcuUPtCmNJu30DgZ8KctDtHKpWhp7RWrATXVY', '2024-03-20 09:22:23', '2024-03-19 09:22:27.221110', 1); -INSERT INTO `user_access_tokens` VALUES ('1ca6754c-710d-4a23-8b17-79fe340e1e65', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5MTI0NDR9._218cyUmUipNcFenAz_Hn83D9hwmTQkAaG0ExC6Nar4', '2024-03-21 13:27:24', '2024-03-20 13:27:27.491644', 1); -INSERT INTO `user_access_tokens` VALUES ('202c3f1c-6068-427f-8c75-50b24f93a5c8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAxNDgxMDR9.n3nf3kBqLhSC6hEBcbTVLruzRGGWnab7XVp9irH35yc', '2024-03-12 17:08:24', '2024-03-11 17:08:24.296728', 1); -INSERT INTO `user_access_tokens` VALUES ('29c99bed-2ad0-455d-9085-9e59192e2426', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTEyMTZ9.qDQNz09cAjWStacu6ORM8Kfa2-1uT23GLdqIs1FKjas', '2024-03-20 09:20:16', '2024-03-19 09:20:20.305036', 1); -INSERT INTO `user_access_tokens` VALUES ('2a5358f2-a465-4241-834f-271f72fbf947', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDUwNzB9.-338fcBRpfrto1EubzOAaBWoAYYHAsKd5uMb3qi4P2Y', '2024-03-28 10:04:30', '2024-03-27 10:04:30.009272', 1); -INSERT INTO `user_access_tokens` VALUES ('2a71c8ce-d4d4-47c4-b004-928f5056676b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDM5MDZ9.iYzKUMMmvoYV_ggaTdyzBnq7AJ0zIzKoueEkQVtpM5U', '2024-03-28 09:45:07', '2024-03-27 09:45:06.513597', 1); -INSERT INTO `user_access_tokens` VALUES ('2af24ef8-886d-4510-b8be-816023f2e517', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0MzgyOTR9.-ZI7lT4i1y8DOXRvLIQp33pzRTowOXSmFB4rrh1Y4dc', '2024-03-27 15:31:34', '2024-03-26 15:31:31.651204', 1); -INSERT INTO `user_access_tokens` VALUES ('31cc91fe-e7b0-4d74-9ea2-f5f12063df86', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMTExNzl9.lCc6BsEz_kJAmVYYBKQJkVjimaDBaKns21FPHiGfECs', '2024-03-22 16:53:00', '2024-03-21 16:53:02.299107', 1); -INSERT INTO `user_access_tokens` VALUES ('34463fa6-75ab-4fc2-ae16-b2e537c8b36d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MzgwMjN9.aqnbHvymunC_kVJhftd7t6CiHQ0fEOJ_EcEEIdawEaY', '2024-03-20 16:47:03', '2024-03-19 16:47:04.883026', 1); -INSERT INTO `user_access_tokens` VALUES ('359f13d0-5507-4de6-8577-8c315892fa00', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTg3NTR9.0UaiKzzZ33lI-z_8J88B_xCBS8VaT1P0ixZ4kqKRy8w', '2024-03-20 11:25:54', '2024-03-19 11:25:54.977965', 1); -INSERT INTO `user_access_tokens` VALUES ('366b9989-b264-4d58-bd6a-4f8da8e0250d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjg4OTF9.4TFOAi6JO5b0K5o3as2q_oFz9TlzvQHtaPbzIdtSz2k', '2024-03-23 08:54:52', '2024-03-22 08:54:51.422041', 1); -INSERT INTO `user_access_tokens` VALUES ('3e0918fa-04c2-45df-858e-34d725c21a25', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMDE2MzF9.r1a4CTC4EmLEdQQ1-kzEkWySW5HaPqISQLafTfLMzSc', '2024-03-22 14:13:52', '2024-03-21 14:13:54.021153', 1); -INSERT INTO `user_access_tokens` VALUES ('3e6845c8-9cc3-492e-bcd7-9636b00d5631', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTIzODB9.F7U7AhNWV1gLdqpM_yHIU6qYadNb0AJap0SScckn9vw', '2024-03-20 09:39:40', '2024-03-19 09:39:44.297388', 1); -INSERT INTO `user_access_tokens` VALUES ('494de6e6-9ac1-47f2-b649-afac34468a64', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEzMzM3Mjh9.qjipswowmSD6jkKJO7UZhU7yTLIai4cXaCQIgq6Lj30', '2024-03-26 10:28:48', '2024-03-25 10:28:47.927310', 1); -INSERT INTO `user_access_tokens` VALUES ('5478eda6-191c-4643-9923-a1c1d83a25ee', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE2MDN9.Xh9jupN8Zq7Ubn23ZnjOd_ie-Gt9XLBvVVD2p4Hn-UQ', '2024-03-20 09:26:44', '2024-03-19 09:26:47.658484', 1); -INSERT INTO `user_access_tokens` VALUES ('585cba42-9d53-4003-9194-20ccb52080f1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MzE2MDh9.ee_VZphsUWW48bA0zsZtdgTdEMN8aIDu_3syty3xuZE', '2024-03-20 15:00:09', '2024-03-19 15:00:10.107179', 1); -INSERT INTO `user_access_tokens` VALUES ('58fd6fa7-fd30-4a3e-bc79-53d1575745c2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjg0Njl9.sefSuM1C_FgjcnLUSgrlcFAMEQrtSybPBu_hU3lePlo', '2024-03-23 08:47:50', '2024-03-22 08:47:49.546874', 1); -INSERT INTO `user_access_tokens` VALUES ('591e086d-96c4-4cab-bb9f-fabe6ee27d43', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTIzNDN9.J1gHFnd_56wTjz2SKw-LbeiFngJTNRo06B1ydUc4yHE', '2024-03-20 09:39:03', '2024-03-19 09:39:07.359019', 1); -INSERT INTO `user_access_tokens` VALUES ('59b32e05-ed47-4634-adf2-86076aac9274', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAyMDUzNTJ9.fDrd_FJd9fcbw6APV3BKOu3Lfkhi_5C7N36OnQTcmK8', '2024-03-13 09:02:33', '2024-03-12 09:02:32.681075', 1); -INSERT INTO `user_access_tokens` VALUES ('5bb267d3-8f88-4a1c-90cc-b36c5bf156fa', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNzM5MDZ9.SmlswCpWEJERkfIKSZr-geVEUAvG81X1mG3tki0olqU', '2024-03-23 10:18:26', '2024-03-22 10:18:26.249237', 1); -INSERT INTO `user_access_tokens` VALUES ('63e12288-1b49-4393-a07c-158189f687c7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE3MjN9.hE4dtpr1V2EQ5wGnqPIBsKu6-UROlpDSyBR6o--PLFw', '2024-03-20 09:28:44', '2024-03-19 09:28:47.957727', 1); -INSERT INTO `user_access_tokens` VALUES ('64dd9302-c031-49f6-8eb6-bdb6e079fd2a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDgzNjN9.Patr4gBYADBgtqO418I8VBk8apQIx8yRy3wZC0vqAEA', '2024-03-28 10:59:23', '2024-03-27 10:59:23.326845', 1); -INSERT INTO `user_access_tokens` VALUES ('66a3b775-98e7-4142-be24-415206c6973b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMDE5MjF9.dyIhc08fJqn4wgdEx1r-Tdfl-e6lOwN4Dpw8o4eq6RA', '2024-03-22 14:18:41', '2024-03-21 14:18:43.376336', 1); -INSERT INTO `user_access_tokens` VALUES ('692a1a4d-08fc-490e-97d2-f9997d6e734a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE2MDIzNzR9.QjRleFqx2KVuvhLwBOlRnzsjFS5smKZfw7lqoAv5hwg', '2024-03-29 13:06:15', '2024-03-28 13:06:16.370572', 1); -INSERT INTO `user_access_tokens` VALUES ('6c22c272-e33f-4fa4-a331-17b08293c97c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAxNDY1MDd9.eOQd3_5vhaIWY9GkICA5txh-IQC_hjCj1Hlxoxptfsw', '2024-03-12 16:41:48', '2024-03-11 16:41:47.547701', 1); -INSERT INTO `user_access_tokens` VALUES ('6e81f813-8166-434a-8b01-a3793bc9ba78', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTg2MDV9.Ime_F6N44mbpR0jSUjpV2ABui0vwjTi-ixfshGKaN30', '2024-03-28 13:50:06', '2024-03-27 13:50:06.302482', 1); -INSERT INTO `user_access_tokens` VALUES ('760fa21e-b944-4138-9a28-59ca4c41cc53', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTAwMTV9.H58vmmiqL1a6aqMSzmCOiMaU-SpE03SM4B6Y9fY5dhM', '2024-03-28 11:26:55', '2024-03-27 11:26:55.112351', 1); -INSERT INTO `user_access_tokens` VALUES ('7c7a14dc-5cc7-4fc9-922f-745b916a2e8b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAyMjQwNjd9.cjbEoVNMEGmORms-_qYdxOHlR4mW3HhpjGBQ7xMJ8hA', '2024-03-13 14:14:27', '2024-03-12 14:14:27.137231', 1); -INSERT INTO `user_access_tokens` VALUES ('801e4049-e49a-474c-9622-603e191c9767', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNzIxOTB9.f-mhCiL8RJIWLtfoMrGfsmDlzpMW99cuA1O37ER8aIw', '2024-03-23 09:49:50', '2024-03-22 09:49:50.132778', 1); -INSERT INTO `user_access_tokens` VALUES ('835e7377-9633-444e-9522-ec6df447ff79', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MjY3ODh9.dU8jiXPKYrXLad20GTLykqfX-n5Kf4Be1WEULAuWruk', '2024-03-20 13:39:49', '2024-03-19 13:39:49.803275', 1); -INSERT INTO `user_access_tokens` VALUES ('85ca2577-5bfe-42f2-b1a5-f2342ab0b42a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0Mzk2ODd9.x97vPdg1jJqN_8am7y8-6YpK_kz3-c9BR9bs3LHi-KY', '2024-03-27 15:54:48', '2024-03-26 15:54:45.087331', 1); -INSERT INTO `user_access_tokens` VALUES ('8d765f70-9ea8-4ca9-b072-b20be8fa6a77', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAxNDU1MzV9.NpEHySU1T6i7ATJnYr0ylma3ndEt3rIuDtca9s2e4fo', '2024-03-12 16:25:35', '2024-03-11 16:25:35.139483', 1); -INSERT INTO `user_access_tokens` VALUES ('9096c688-07d7-4709-a431-1bece82f3b93', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTAxNjV9.jtMsYwjq-0cceX-sHWDIb-i7W8xSQTxu_-aneefje94', '2024-03-20 09:02:45', '2024-03-19 09:02:49.121077', 1); -INSERT INTO `user_access_tokens` VALUES ('914b16d0-4508-462e-be5a-44d374c4fd61', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5OTAwNTR9.IpNJRRFAUKZjMuL4FgYHfviAwVdzrElh4nZaflvzY8g', '2024-03-22 11:00:55', '2024-03-21 11:00:56.647606', 1); -INSERT INTO `user_access_tokens` VALUES ('95cc9059-6e9b-4354-84ed-29fe0807fac7', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4NDA2MjV9.As1-SCnVDUOZNaohx-dezHJr3x44MZp2RkTB0BXm9YA', '2024-03-20 17:30:26', '2024-03-19 17:30:27.477349', 1); -INSERT INTO `user_access_tokens` VALUES ('9b8a542b-f702-4fa9-87ce-ca87d3063dac', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTg2MTh9.6TU8IYG03jtoA9m5fak3nJfv2FuZQpWFtr1Ghsl8Fdw', '2024-03-28 13:50:18', '2024-03-27 13:50:18.556242', 1); -INSERT INTO `user_access_tokens` VALUES ('a2822d05-b23a-4df0-a048-539fa79593c6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDg3MzJ9.6V07EvTyO9X2eTR-Y_MtuKcp6m0A9Mb4ZiprtagLw28', '2024-03-28 11:05:33', '2024-03-27 11:05:32.631459', 1); -INSERT INTO `user_access_tokens` VALUES ('a4925b1e-ddf5-4382-a643-b45d1bf8199c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE2NzJ9.u1p4PPH6WerSOVw3oVf2UpVLxCGDGjEBxQ8uTHrTZsA', '2024-03-20 09:27:52', '2024-03-19 09:27:56.058970', 1); -INSERT INTO `user_access_tokens` VALUES ('a8caae33-85eb-45f7-afc6-1e4bd187599c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MTAzMDV9.Zvbw5lWlok6Hr8R1MAit1J1ZtDU1tKPlaHvQku8lyGA', '2024-03-28 11:31:45', '2024-03-27 11:31:45.293488', 1); -INSERT INTO `user_access_tokens` VALUES ('aa08e3d0-b1ab-410f-8504-0ae8e2504864', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTI0Mjl9.WFKUbUGTJmkdNG3AoKChxp632unDVOvBYknJNVvKRtw', '2024-03-20 09:40:30', '2024-03-19 09:40:33.978807', 1); -INSERT INTO `user_access_tokens` VALUES ('ab1e415f-d811-47ea-9226-86684b71060f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAyMDM1NTV9.-MjD4XNqv-CHWKUJTE82rfFvkfl8vJG8I8SZ1LU5Qo0', '2024-03-13 08:32:35', '2024-03-12 08:32:35.316282', 1); -INSERT INTO `user_access_tokens` VALUES ('b063d499-6fd3-48ea-b75d-95d3bb301c13', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMTI5MzF9.F4i2_962VBcPPlmr2d_qw1-Nu7lOWS-WS8jAantuS_o', '2024-03-22 17:22:12', '2024-03-21 17:22:14.371446', 1); -INSERT INTO `user_access_tokens` VALUES ('b140cb61-64a4-4e5f-b278-5d1764f28524', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjg1MjF9.3tEn-p_MoFohcXRbyOF27WDH6ZiuSSRTtWpH5BEo2Qk', '2024-03-23 08:48:41', '2024-03-22 08:48:40.790543', 1); -INSERT INTO `user_access_tokens` VALUES ('b7f0d7b7-eb71-4eb1-a47d-88c444a44b2f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0MzgyMTh9.KtkYzRJ1NLAQUingFGjJuLCZ9yp5bbqITH_CA2qCLh8', '2024-03-27 15:30:19', '2024-03-26 15:30:16.074185', 1); -INSERT INTO `user_access_tokens` VALUES ('b85f77c8-fd9c-4f97-b680-666be5b2deb6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5ODUyMjN9.oxV57ydkinFtrwBbWI8xK-qrpkDDXi6kEINfCvDqe1c', '2024-03-22 09:40:23', '2024-03-21 09:40:24.862189', 1); -INSERT INTO `user_access_tokens` VALUES ('b92e568c-a942-4155-85be-4ade7c61d774', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTAyMDQ2MzR9.kyjByDKeCWQArLkl4x4FsOwOn2jU6zp91WoAfMf5RoY', '2024-03-13 08:50:35', '2024-03-12 08:50:34.530170', 1); -INSERT INTO `user_access_tokens` VALUES ('bb16d202-1f32-4f55-8fc1-7c653d44d874', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MjQ0ODF9.yV7rOObsnoSkTy2yMMZHoNLGcWMsw6P-jYTP_87Uffo', '2024-03-20 13:01:22', '2024-03-19 13:01:22.802716', 1); -INSERT INTO `user_access_tokens` VALUES ('bde4a4d2-ec77-4247-bfe9-2bdcb2b0c647', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDk4MzB9.jr6tcmZysoD5mO51Hvt9d2e4hHS6OZrhp0ynVPt8PNE', '2024-03-28 11:23:51', '2024-03-27 11:23:50.924952', 1); -INSERT INTO `user_access_tokens` VALUES ('bed60fee-ef30-48a1-a8a3-e1394ef69c66', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4OTc0MzB9.KwretnZz0QASH0KXtiJc9UEMqJIG5xhdsXOk1bfOxXQ', '2024-03-21 09:17:11', '2024-03-20 09:17:13.363786', 1); -INSERT INTO `user_access_tokens` VALUES ('c519656a-1312-4074-990d-e9577c000278', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjg3NDl9.-e6nu9CeUL-DFGQSrGA0xCU0bB9NPXOJ4H07E8MmnxQ', '2024-03-23 08:52:30', '2024-03-22 08:52:29.450325', 1); -INSERT INTO `user_access_tokens` VALUES ('c74cf0ed-b704-4e72-a93f-24a79cb7db07', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0NDAxMzB9.EhX-5QSjuuXBHOFXLFyzD6MVBtpo9JoI3zJzLj4oWf4', '2024-03-27 16:02:10', '2024-03-26 16:02:07.610864', 1); -INSERT INTO `user_access_tokens` VALUES ('cc1cc297-6be6-4267-9aec-fa45e59710eb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDg2NTN9.OWsAKScwNrk3-E1FasIJaQ-0mZJcRMMdH0TN35pR_T8', '2024-03-28 11:04:14', '2024-03-27 11:04:13.782127', 1); -INSERT INTO `user_access_tokens` VALUES ('cc472002-f409-4273-aa55-dde97cb1ac29', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0MzgxOTF9.-g-o-zdXpSyjaQEYu8zsfvonHZH4s37jRE09soxhPRs', '2024-03-27 15:29:51', '2024-03-26 15:29:48.495898', 1); -INSERT INTO `user_access_tokens` VALUES ('ce151b19-e87d-4cce-9493-fb0aa203198b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTA2Mzd9.JEvSNANVjeOede04Zjyv65RCZ8MIaAjzUZGK1j-HVlY', '2024-03-20 09:10:38', '2024-03-19 09:10:41.614145', 1); -INSERT INTO `user_access_tokens` VALUES ('d18de5f3-f114-4f19-8981-5ef576706a60', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA3NTE1NTJ9.5HZXP5ZKsPslkEK-kJmeKmYG0MWKaQthAWFUhyQ9TiE', '2024-03-19 16:45:53', '2024-03-18 16:45:53.566424', 1); -INSERT INTO `user_access_tokens` VALUES ('e087f105-324d-4b0d-b44a-4f9c6041a258', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0Mzk4MjV9.ObutDKkzqpGV5HYNckVJmnZG5lpgiPZWiT8h5ej3f_g', '2024-03-27 15:57:05', '2024-03-26 15:57:02.487339', 1); -INSERT INTO `user_access_tokens` VALUES ('e0b66a0d-2522-4423-8a04-81e282520f2f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEzMzgwMjF9.iEEjHupmnw-trA0cFbYj6nmRxVPKXSQ9NLnV5v6nrsA', '2024-03-26 11:40:22', '2024-03-25 11:40:21.378503', 1); -INSERT INTO `user_access_tokens` VALUES ('e0f833b8-4c69-43cc-ae17-51579379c09c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwMDAzMjd9.xhiRtcg6yq7q3lLIQdB2_tN7pNAhyj0mI7qMYhPxc64', '2024-03-22 13:52:07', '2024-03-21 13:52:09.245352', 1); -INSERT INTO `user_access_tokens` VALUES ('e23276d2-3f8b-4ce3-aa75-5f15ccc43512', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0Mzk2MjV9.xhByWA1kiCFNT2HaqzIEhU9QYnVyZ2oVjwigf6J1D3A', '2024-03-27 15:53:46', '2024-03-26 15:53:43.048420', 1); -INSERT INTO `user_access_tokens` VALUES ('e7ba638f-c89f-47e9-bd67-8918a7cb8cc3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTE3Mzl9.4vXdwhtGgsSzC9vBerAbXk04mDbpeDh5XdpB7_okDPE', '2024-03-20 09:28:59', '2024-03-19 09:29:03.147751', 1); -INSERT INTO `user_access_tokens` VALUES ('ecdd1694-db7c-442c-a998-ef708d6a2a67', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA5MTA5MTN9.9opzZI0y3C_-E3euH-XdwaDf7apnftlRtkk3ZIDqh3A', '2024-03-21 13:01:53', '2024-03-20 13:01:56.119760', 1); -INSERT INTO `user_access_tokens` VALUES ('ed53543e-51a0-4163-8954-f96a7e1fadcb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE1MDkzNTN9.E0xGR2iGcyef2BEUDMuaMka97NaLqNq_piRC_w66_x8', '2024-03-28 11:15:54', '2024-03-27 11:15:53.999628', 1); -INSERT INTO `user_access_tokens` VALUES ('eebefeae-565c-44c5-b43c-a2eee2077b5b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTAwNzB9.1CXhZmQZhmmT2x9KusTSclO9a0ZGpgzElvS-lJHhHsM', '2024-03-20 09:01:10', '2024-03-19 09:01:14.203313', 1); -INSERT INTO `user_access_tokens` VALUES ('f63b8aa7-d54e-42db-9b9b-79440133b131', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTA4MTIzOTV9.b1QALT2pgRe2tJIckPMtIhPNFTQzI5-UlyVCdSQIC2w', '2024-03-20 09:39:55', '2024-03-19 09:39:59.158099', 1); -INSERT INTO `user_access_tokens` VALUES ('f87de777-667c-4add-938b-d121d69361f6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTE0MzgzMjd9.uPz09Q5Sh0X78HV43G8GaGYD8brN6WlX1kWneX7M67s', '2024-03-27 15:32:07', '2024-03-26 15:32:04.529181', 1); -INSERT INTO `user_access_tokens` VALUES ('faa26fea-b099-4edf-a9ca-642f06a4e8c1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTEwNjczMTl9.QvHe_baU8fJwr9m4PtO-XpBZKFwgcMXM_g3K3h40hKc', '2024-03-23 08:28:40', '2024-03-22 08:28:39.849804', 1); +INSERT INTO `user_access_tokens` VALUES ('067fa54e-e37d-45eb-850c-11fe6e580555', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTgyMTR9.KxpU61iQtg1j2zziHZB0gKGdvCiViIlTKkuY59EpD4s', '2024-04-08 10:50:14', '2024-04-07 10:50:14.399413', 1); +INSERT INTO `user_access_tokens` VALUES ('06fa487c-c9f4-4dcc-8643-ef6146e42add', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODc5Mn0.mnEZ_Re_83_6NSgAf_E5Rp2akIOVsQQ_tQCgdk1QhZ0', '2024-04-08 10:59:53', '2024-04-07 10:59:52.570969', 9); +INSERT INTO `user_access_tokens` VALUES ('4b231512-42ac-41f6-9acc-9b3ac9590f12', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTY3MTd9._1XC-bHE_X0vWiCvexC5tXRIiZ5iwh6YX6DJO31KROk', '2024-04-08 10:25:18', '2024-04-07 10:25:17.962621', 1); +INSERT INTO `user_access_tokens` VALUES ('50be25ab-05a0-468f-919c-ec623ac41761', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0MTc2MjB9.otSnlbtKFKqoJUD5e0wM25Nxp6qjXs0r9FXnSO9zdOw', '2024-04-07 23:33:40', '2024-04-06 23:33:40.021878', 1); +INSERT INTO `user_access_tokens` VALUES ('64b831b0-32a9-4fad-ae3f-9f8f370f3bf2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODI3NH0.pmdigNgdbtUHrktkP8t4DhaxTKnBPogHUVdC5XEXSA4', '2024-04-08 10:51:14', '2024-04-07 10:51:14.116966', 9); +INSERT INTO `user_access_tokens` VALUES ('657d245b-b61f-4f84-9bb8-3358ad645546', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJ1c2VyIiwiSW52ZW50b3J5TWFuYWdlciJdLCJpYXQiOjE3MTI0NTgxOTR9.BY5SGgYS7TjfVeCtRxVzgMZQq9TN9bMiOjDCoNwAcF0', '2024-04-08 10:49:55', '2024-04-07 10:49:54.981842', 9); +INSERT INTO `user_access_tokens` VALUES ('841bd4fc-b3e5-4864-ad4e-6849261e5806', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJ1c2VyIiwiSW52ZW50b3J5TWFuYWdlciJdLCJpYXQiOjE3MTI0NTgxMzl9.fWCim-wvmY8b8HogIlThhsAeFf5oWYqT-evVpwv7-pc', '2024-04-08 10:48:59', '2024-04-07 10:48:59.360940', 9); +INSERT INTO `user_access_tokens` VALUES ('96ae5545-c90f-452a-bae7-377e8cf32169', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODcyOH0.RuLjO4hFD_y1CoCQDB3KZJYS-24AWP1Q2vpEo_mVPxM', '2024-04-08 10:58:48', '2024-04-07 10:58:48.182856', 9); +INSERT INTO `user_access_tokens` VALUES ('a35d093a-c004-459c-a080-c0abc346a115', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTg5MTl9.JL6Xjpun6vX5XbruGhXLSevvO7AYLstCHSn_-z48ll8', '2024-04-08 11:02:00', '2024-04-07 11:01:59.632272', 1); +INSERT INTO `user_access_tokens` VALUES ('aa9787ae-7565-4d09-a3cb-b216947cfcab', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODQwNX0.2CyoDt_tIsAzri02mhCKbVJEss-Ny2qdmZOPoZKUuxQ', '2024-04-08 10:53:25', '2024-04-07 10:53:25.379015', 9); +INSERT INTO `user_access_tokens` VALUES ('b6f1ce31-bf6b-4dc4-80eb-3ab37774e179', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTgzNDF9.bAvlmbaHNnL1_-ar9yVZI3Z_3Nm0qMF9MFN1bO29-rM', '2024-04-08 10:52:21', '2024-04-07 10:52:21.439166', 1); +INSERT INTO `user_access_tokens` VALUES ('c9e94b55-dd93-4ea1-aee1-c81d59a9e1bc', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODgzOX0.ShvQOexZc7zfP_NsprmVjb0aXDqurp5_6Bsed0oJsq8', '2024-04-08 11:00:40', '2024-04-07 11:00:39.515606', 9); +INSERT INTO `user_access_tokens` VALUES ('c9ee2211-fa30-4a74-9e96-1622e959c5a5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTkyNTB9.pX3TLszlGjQDyebNEXunGN6vwStARGHoOSLpULHfXWU', '2024-04-08 11:07:30', '2024-04-07 11:07:30.021625', 1); +INSERT INTO `user_access_tokens` VALUES ('cdb64fe0-ca48-466b-9caa-5812aa7a8634', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTc4MDZ9.jJ2SEjgLfYBJe80tV3CpD2WMMID8iAlCV_iJaaL6IXE', '2024-04-08 10:43:26', '2024-04-07 10:43:26.334882', 1); +INSERT INTO `user_access_tokens` VALUES ('d78ecadb-7bfd-4c65-a8c4-05e9473a80a5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODc1Nn0.HaB3ABe7S6Iik9lBEIa1ww_-QuxKsZOLkB8e4evcQ58', '2024-04-08 10:59:16', '2024-04-07 10:59:16.310195', 9); +INSERT INTO `user_access_tokens` VALUES ('d9f221ea-111d-47fe-932d-4123174dfce8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTg2ODl9.N4Q7H8KnGd-hNmO-2UECuDfBFuuthrTPdATxDBcuWTI', '2024-04-08 10:58:10', '2024-04-07 10:58:09.590782', 1); +INSERT INTO `user_access_tokens` VALUES ('f886261e-bb1d-45db-83a4-71ef0f2a9180', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTg0NTF9.Uf2OXdWp1UgBHHw4dvLJcEQtv_09VoBAAsDkMmZ10Lo', '2024-04-08 10:54:11', '2024-04-07 10:54:11.390379', 1); -- ---------------------------- -- Table structure for user_refresh_tokens @@ -1338,80 +1020,23 @@ CREATE TABLE `user_refresh_tokens` ( -- ---------------------------- -- Records of user_refresh_tokens -- ---------------------------- -INSERT INTO `user_refresh_tokens` VALUES ('010e06ef-1457-437b-ad87-13b027bfbabf', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMVA1WlRkVkNNSVdCeVZzUWNvUXhwIiwiaWF0IjoxNzExMDAxOTIxfQ.ymE1VCWUEP8GOFq-UBkrpmjW4YUIhGQKN4ehkUmPTvk', '2024-04-20 14:18:41', '2024-03-21 14:18:43.417025', '66a3b775-98e7-4142-be24-415206c6973b'); -INSERT INTO `user_refresh_tokens` VALUES ('07e96a3a-b62e-47c5-a7df-9389b7063480', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWVlWdzdPUmNLdEJUSXFBMWNFZms2IiwiaWF0IjoxNzExNTEwMzA1fQ.qVxTN6ysyP7YeWbAdvRpjvsGAxpcm43mcaPbkct95BY', '2024-04-26 11:31:45', '2024-03-27 11:31:45.326110', 'a8caae33-85eb-45f7-afc6-1e4bd187599c'); -INSERT INTO `user_refresh_tokens` VALUES ('08a09272-11ec-4d52-bff0-ce1fa066fae4', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNnZQTEhfM3l1QVViUlE0SHU3UnFLIiwiaWF0IjoxNzEwODExNjcyfQ.zxFU1bygRPc5MdSmD04A1_O8NlkafARR47dKoLhVLoY', '2024-04-18 09:27:52', '2024-03-19 09:27:56.089234', 'a4925b1e-ddf5-4382-a643-b45d1bf8199c'); -INSERT INTO `user_refresh_tokens` VALUES ('0a366a11-90e5-4287-9fbd-8de07fa02d8f', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiS2lpbGY0NVQtUm1OcGdBNkVDUEl0IiwiaWF0IjoxNzEwODEyMzk1fQ.oV2IKNaSrWDTsldxsya0jVkTv-rtXer5vwQ7Gb_Hmhg', '2024-04-18 09:39:55', '2024-03-19 09:39:59.185706', 'f63b8aa7-d54e-42db-9b9b-79440133b131'); -INSERT INTO `user_refresh_tokens` VALUES ('0d5a4d35-c3e2-4ee8-b9de-7b3ec878ba30', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZjNCYnRYUlV4aDNoRzc4NWZQMGx4IiwiaWF0IjoxNzEwMjA0NjM0fQ.WzjE8XNFVJxInsboM3ajmce7ajU3wOJwkbrwdHeBHW8', '2024-04-11 08:50:35', '2024-03-12 08:50:34.540128', 'b92e568c-a942-4155-85be-4ade7c61d774'); -INSERT INTO `user_refresh_tokens` VALUES ('0fbbe536-4bc1-4592-a5e2-fe77fbedc938', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZU9vaE90YzZCa203ck5HSzZGQTU0IiwiaWF0IjoxNzExNTA4MzYzfQ.qnxo7i0eckurIxmm1yne62qE7m5xy4wcDcMQQ2ruPvg', '2024-04-26 10:59:23', '2024-03-27 10:59:23.382098', '64dd9302-c031-49f6-8eb6-bdb6e079fd2a'); -INSERT INTO `user_refresh_tokens` VALUES ('11a3293f-ccc4-4ef5-af95-17cb96219743', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNmRJTDR1Mlo4cmlwVW1XUjhmV0VHIiwiaWF0IjoxNzEwODk3NDMwfQ.TkIFl5hIF1gllYasIfOgeCWd36YP1lM2kjtN79POb4A', '2024-04-19 09:17:11', '2024-03-20 09:17:13.399688', 'bed60fee-ef30-48a1-a8a3-e1394ef69c66'); -INSERT INTO `user_refresh_tokens` VALUES ('1ad2cc4b-fed9-412f-a115-b8ef75249bfe', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMnBMc3NHaTJpZkpoTzFqWHZZbXFYIiwiaWF0IjoxNzExMzMzNzI4fQ.P2wpOAdZ5p8_dQB9k2uei5tuR3fCXVZHwPZvnrZaHm0', '2024-04-24 10:28:48', '2024-03-25 10:28:47.958322', '494de6e6-9ac1-47f2-b649-afac34468a64'); -INSERT INTO `user_refresh_tokens` VALUES ('1bdb14fb-f898-4b72-a51e-cf4a5b7baaa6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOEZkaU1YckZPSmd4Q015STdmRUpDIiwiaWF0IjoxNzExNDM5NjI1fQ.Vepis6LXtFw7pprrQVheVG0u7wjrnbA9MQmJixRrhmM', '2024-04-25 15:53:46', '2024-03-26 15:53:43.079038', 'e23276d2-3f8b-4ce3-aa75-5f15ccc43512'); -INSERT INTO `user_refresh_tokens` VALUES ('1c9ec9aa-2e92-4081-babd-3d3eb805b1b6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN1UzeGN0bFdZYThobnZrY2JpemxmIiwiaWF0IjoxNzEwODEyMzQzfQ.BIUCD5cvBfTqmueu-U1wqTo6qUf2udjsUbRphWZNmZw', '2024-04-18 09:39:03', '2024-03-19 09:39:07.405051', '591e086d-96c4-4cab-bb9f-fabe6ee27d43'); -INSERT INTO `user_refresh_tokens` VALUES ('2416d21d-4ad8-4349-85e9-61c73056fa62', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMjZXb1p3LUpWS0ZYN1l2QzhHcFpNIiwiaWF0IjoxNzExNTA5ODMwfQ.H0BhMZ0h5MkwG-_1IVtJMovtcdZnKA8MJ3GzInsBuJM', '2024-04-26 11:23:51', '2024-03-27 11:23:50.959365', 'bde4a4d2-ec77-4247-bfe9-2bdcb2b0c647'); -INSERT INTO `user_refresh_tokens` VALUES ('25e1c246-0b08-4ec7-97c6-76c7180e6ccb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN1A3MFFWajY2NDJZSkMzaGFKS3VWIiwiaWF0IjoxNzEwMTQ2NTA3fQ.VnbDgerC7dqDX3uY7H-wRUbcz0sM73Be4_yB6exyTGY', '2024-04-10 16:41:48', '2024-03-11 16:41:47.561906', '6c22c272-e33f-4fa4-a331-17b08293c97c'); -INSERT INTO `user_refresh_tokens` VALUES ('2840181a-20db-4109-bd69-9ca8c13dd364', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLU9ZQVZhUzNtN1FvdjMwT0dkVVNTIiwiaWF0IjoxNzEwNzUxNjIzfQ.clJe-5xsDNNlll_sByUfsyTMCIPyStl1mofFCnImeI0', '2024-04-17 16:47:04', '2024-03-18 16:47:04.575279', '0a058377-1c0e-48d6-b143-21d5195ac859'); -INSERT INTO `user_refresh_tokens` VALUES ('2843df4e-1fe7-4df1-b6ed-c65b5bb832bd', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidWZjUnNzWFNSTDRLNHNyM3VReEowIiwiaWF0IjoxNzExMDAwMzI3fQ.PGX4sZTbGbqgdlCpSf7yua85jU8Sh5nHsSE0r_xngIw', '2024-04-20 13:52:07', '2024-03-21 13:52:09.304657', 'e0f833b8-4c69-43cc-ae17-51579379c09c'); -INSERT INTO `user_refresh_tokens` VALUES ('29aefbd4-2cd4-46f7-9dc8-fd5480709339', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZTB3THhUTF9HaE5KU0Z5Z05ybkhhIiwiaWF0IjoxNzExNDQwMTMwfQ.ox3LslTVukUV6di-G2mIocFtWIyUGs_9sJQrDRnU4jw', '2024-04-25 16:02:10', '2024-03-26 16:02:07.647313', 'c74cf0ed-b704-4e72-a93f-24a79cb7db07'); -INSERT INTO `user_refresh_tokens` VALUES ('302164ad-4379-4a94-b430-7be5eba3c0c8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZmVaQktfcUtSNktVZVdPRkZBWjRoIiwiaWF0IjoxNzEwODM0ODEwfQ.TMwCSR-QW0zQE7KEJfEkRC4jzBtYpHKOs_cCC_7QIFI', '2024-04-18 15:53:31', '2024-03-19 15:53:32.278476', '191ada91-e54d-46c5-a20d-448022f5da60'); -INSERT INTO `user_refresh_tokens` VALUES ('3265f1de-05b4-4e3f-b4aa-942df1fa6226', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicmtHS0IyNDBHOGhTODlUczlELVdvIiwiaWF0IjoxNzEwOTkxMjQ0fQ.Hl2W3hSqilfjl3w13bB2qNXt-Ob5Z2b0cB99IV-aIH4', '2024-04-20 11:20:44', '2024-03-21 11:20:45.989087', '0bee723d-c635-4396-9656-8165ac9594b0'); -INSERT INTO `user_refresh_tokens` VALUES ('37843616-e5f3-4706-a7e0-a2192eba8214', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWGpCal9pZkpDT0laTEpmUHZ3SlU0IiwiaWF0IjoxNzExNDM5NjU3fQ.PtETXpyt60eSVC5OYP9kFvbsmEQAPdHE9tvrGynAuaM', '2024-04-25 15:54:18', '2024-03-26 15:54:15.435171', '0baa35e7-11a8-4280-be13-b12c9416283b'); -INSERT INTO `user_refresh_tokens` VALUES ('3940e4d3-4c4a-4af1-b784-2956817790fe', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidG4teTFOcWE2d3lxQ1Nwd01mSXRRIiwiaWF0IjoxNzEwOTEyNDQ0fQ.Pamh4EJzVBx6snvXnhw5VJTjDLEYWEXRxYe8Tu1UjQA', '2024-04-19 13:27:25', '2024-03-20 13:27:27.521307', '1ca6754c-710d-4a23-8b17-79fe340e1e65'); -INSERT INTO `user_refresh_tokens` VALUES ('3abe7182-349c-4417-b3c6-b62c125f28a4', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoid3ZfRWU3MTk2N0c1TW9jRnV1djNqIiwiaWF0IjoxNzEwOTkwMDU1fQ.jhCbZTW7ljGarY2bGEBV9OMdr6J5If4Y5jukOQrwVbk', '2024-04-20 11:00:55', '2024-03-21 11:00:56.678623', '914b16d0-4508-462e-be5a-44d374c4fd61'); -INSERT INTO `user_refresh_tokens` VALUES ('422896a8-0305-4397-b74e-04441ad311e0', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQllhM2FOWFRTSFZjOHNKeW80QTNTIiwiaWF0IjoxNzExMDY4NDY5fQ.KCA5nqJXD3UjgvsVzUBsS7qXwRry0a0mzLd5Jen1Ftg', '2024-04-21 08:47:50', '2024-03-22 08:47:49.588066', '58fd6fa7-fd30-4a3e-bc79-53d1575745c2'); -INSERT INTO `user_refresh_tokens` VALUES ('42b83817-25fd-4a14-85e8-eb74e8f6b2d9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiU3VmUUxEMG5KREdQbVVSdV9FeU5VIiwiaWF0IjoxNzEwODExNzI0fQ._PgPOASqX3eHKfGTy1I_bPZmUCMWaftmBcTx1BCWbQE', '2024-04-18 09:28:44', '2024-03-19 09:28:47.982722', '63e12288-1b49-4393-a07c-158189f687c7'); -INSERT INTO `user_refresh_tokens` VALUES ('46fb00b3-3712-4d57-a233-a3db168ffe6b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZEJRS3hIQ0gxM0xFU0JBVWtFMVhIIiwiaWF0IjoxNzExMDExMTc5fQ.oYXUCnbSfBIQu8Jx2lpAwQV6-lf-vDy0jgGW8d5NMDk', '2024-04-20 16:53:00', '2024-03-21 16:53:02.355897', '31cc91fe-e7b0-4d74-9ea2-f5f12063df86'); -INSERT INTO `user_refresh_tokens` VALUES ('494874e7-8b3a-4e39-84bc-5aad8ff76e94', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ2JzVWhVbFljVGFhZlF6NVYxZVExIiwiaWF0IjoxNzEwODI0NDgxfQ.ICg5zZLHZ1ri5VH-q1XuCvkFVKo11imwYnYgYkt6N0o', '2024-04-18 13:01:22', '2024-03-19 13:01:22.829304', 'bb16d202-1f32-4f55-8fc1-7c653d44d874'); -INSERT INTO `user_refresh_tokens` VALUES ('49a01d59-5696-4101-9e40-fcc8898fb530', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLTdKQk53U29TYTVBakNsOVlqR3R4IiwiaWF0IjoxNzExMDEyOTMxfQ.Ra5Ok3GZPvFre5M12peezgUSxVyHjhU-RECOYdVlRDM', '2024-04-20 17:22:12', '2024-03-21 17:22:14.399610', 'b063d499-6fd3-48ea-b75d-95d3bb301c13'); -INSERT INTO `user_refresh_tokens` VALUES ('4c40606e-a096-4a58-a231-15feab4a50a3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiT1RlQVhMTWpyeW0zMGo1NEtKSDRkIiwiaWF0IjoxNzEwODExNjYzfQ.LPpYzJnkMRFzPGZDgLLgFGHeyV4WpT6_DK_5I0dqrXg', '2024-04-18 09:27:44', '2024-03-19 09:27:47.558544', '0f9677c0-6a16-4b6b-ac83-2ee669fb7ced'); -INSERT INTO `user_refresh_tokens` VALUES ('4c89232a-d38d-4156-84ef-420440c7d259', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNnl5TjhneVNkWUUyUnZiaVdGOEMzIiwiaWF0IjoxNzEwODEwNjM3fQ.5gD2k6T1ZkDWupC95Z7W3iuaFMIibDaYn37E3vQDqdk', '2024-04-18 09:10:38', '2024-03-19 09:10:41.639655', 'ce151b19-e87d-4cce-9493-fb0aa203198b'); -INSERT INTO `user_refresh_tokens` VALUES ('51df5f43-4d4e-4d0d-9ee8-bb9530aae045', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidGY5S1VsRXNzczVmaVhWamNVbW1uIiwiaWF0IjoxNzExNTE4NjA1fQ.YX59QyZ1i6IwBn7xl4_lBqDHanPLXCivM3UV7LpcJ3E', '2024-04-26 13:50:06', '2024-03-27 13:50:06.339976', '6e81f813-8166-434a-8b01-a3793bc9ba78'); -INSERT INTO `user_refresh_tokens` VALUES ('5743467d-c14d-4c38-aa04-2efa605594a5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQktMNk9jazllVjY4MGNiam1xV3JvIiwiaWF0IjoxNzExNjAyMzc0fQ.p6grGAQsZH1LDhV-eqenQ9hixg-wxmJfOMLoFd5t1GY', '2024-04-27 13:06:15', '2024-03-28 13:06:16.580783', '692a1a4d-08fc-490e-97d2-f9997d6e734a'); -INSERT INTO `user_refresh_tokens` VALUES ('5808a8cd-ae81-4bee-a009-f2260272ecfd', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiS1Y5a3ptZG9DS3ZJUlVVQzMwZFFNIiwiaWF0IjoxNzEwODExNjAzfQ.kijNuWLCfEetuAjzCwNhQseI-XQdTprcs92OqiRT_gE', '2024-04-18 09:26:44', '2024-03-19 09:26:47.686629', '5478eda6-191c-4643-9923-a1c1d83a25ee'); -INSERT INTO `user_refresh_tokens` VALUES ('5a888318-988c-4569-b2b6-cc0c904c072b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoienNtY1JVWE04OGhkMzNFUDlhaHktIiwiaWF0IjoxNzExNTA4NzMyfQ.lGSuPjMYJz3K8DdapAxatdaH2MiubuGaMzn0qnj2CAg', '2024-04-26 11:05:33', '2024-03-27 11:05:32.661859', 'a2822d05-b23a-4df0-a048-539fa79593c6'); -INSERT INTO `user_refresh_tokens` VALUES ('5b6b4d4f-6138-428c-b301-ce06fa9e8622', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiY3lTTzJRcGZGQUxvTUI3a3lkWkhYIiwiaWF0IjoxNzExNTA5MzUzfQ.RxyZd98QDBWpnadQHBlrKvG2OfPRj-TNs_rofEeSk08', '2024-04-26 11:15:54', '2024-03-27 11:15:54.036521', 'ed53543e-51a0-4163-8954-f96a7e1fadcb'); -INSERT INTO `user_refresh_tokens` VALUES ('5c368087-4b15-4dd5-9c82-780c26a835c4', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOU1ndU5qMU5BZkJWaG83WDBGeUV4IiwiaWF0IjoxNzEwODMxNjA4fQ._rSOd_AOpCJyXeaHR5EAYXRKMSAA2x-kOEbwfd7iCmc', '2024-04-18 15:00:09', '2024-03-19 15:00:10.133803', '585cba42-9d53-4003-9194-20ccb52080f1'); -INSERT INTO `user_refresh_tokens` VALUES ('6318b4a5-9c58-4a0e-819b-685cfbefe816', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoia1JUV0R2ZVJiVV93WW8xUTk2ak9ZIiwiaWF0IjoxNzExMDY4ODkxfQ.xvFPtqW5kSfb_eDp-ujCz5lbp3pQyafSfv2oarKVE9E', '2024-04-21 08:54:52', '2024-03-22 08:54:51.445971', '366b9989-b264-4d58-bd6a-4f8da8e0250d'); -INSERT INTO `user_refresh_tokens` VALUES ('66f1ff35-76de-4d8d-a972-8fcd31346701', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNWxzOUNvY3k2cENjSnJ5MzZmdElmIiwiaWF0IjoxNzExNDM5ODI1fQ.oyDCgJ9pCCuZZ5-2g7oTDe-Gs-M9WaIO7WPJcDOCVhk', '2024-04-25 15:57:05', '2024-03-26 15:57:02.544678', 'e087f105-324d-4b0d-b44a-4f9c6041a258'); -INSERT INTO `user_refresh_tokens` VALUES ('67a872e3-4271-4ed6-b86d-c924bff77718', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRmxWUWt0REVmRFNjbHNZTHl4VzVGIiwiaWF0IjoxNzExNTEwMDE1fQ.XrXiErlUhK5n1E1keKwCqbiisBfrQsopn9i_ovYD948', '2024-04-26 11:26:55', '2024-03-27 11:26:55.144420', '760fa21e-b944-4138-9a28-59ca4c41cc53'); -INSERT INTO `user_refresh_tokens` VALUES ('67df20a7-80e6-484d-a759-819220620225', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoic0RJM2hBWTNPbDBQaU1TeHhxVm1nIiwiaWF0IjoxNzExNTA4Mzc4fQ.TIFiY5glD17SYnMdi0GlgMaIyn7xAuqxG5ISWHq6e_0', '2024-04-26 10:59:38', '2024-03-27 10:59:38.044944', '0a52047b-397f-4ff2-9bfc-9940e17b2fcb'); -INSERT INTO `user_refresh_tokens` VALUES ('682f7771-7ef2-49df-bec7-b9bd8fd991c2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVDhWSk5idlByQmU2SmkyeTUwS0xtIiwiaWF0IjoxNzEwODE4NzU0fQ.cAt6yHsB2nfk8E92BvfMpYlibcHf7h6ktEqULOMlzqw', '2024-04-18 11:25:54', '2024-03-19 11:25:55.044706', '359f13d0-5507-4de6-8577-8c315892fa00'); -INSERT INTO `user_refresh_tokens` VALUES ('6dffecbb-34b5-4348-aee1-1fba4de70cbf', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidVEydGV5Nkd0aDVZcDlLa1JaaTRXIiwiaWF0IjoxNzExMDcyMTkwfQ.N1-1tKBDL-i4CKqU7t2OWeKNFLB-uNPJCmf7QsnqXcI', '2024-04-21 09:49:50', '2024-03-22 09:49:50.157484', '801e4049-e49a-474c-9622-603e191c9767'); -INSERT INTO `user_refresh_tokens` VALUES ('7a89b195-6441-40fc-8075-3036d89de7b2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTWdxN0hrTkxHZDlfOTYtZlY1YXR6IiwiaWF0IjoxNzEwMTQ1NTM1fQ.MXcDHgeIqIN43yBxa1eHRWogy71icRm3uG4LqBs2ITM', '2024-04-10 16:25:35', '2024-03-11 16:25:35.165240', '8d765f70-9ea8-4ca9-b072-b20be8fa6a77'); -INSERT INTO `user_refresh_tokens` VALUES ('83eeafc6-f0b2-41b1-b752-ba6e81ea2785', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMGg2cnZ0MVF5bUtCN2dXWUFLU3dtIiwiaWF0IjoxNzEwODEwMTY1fQ.80MvJxXaeLhb1_BsX55hbIzw7emkYTqcJkx1hddk7HA', '2024-04-18 09:02:45', '2024-03-19 09:02:49.147999', '9096c688-07d7-4709-a431-1bece82f3b93'); -INSERT INTO `user_refresh_tokens` VALUES ('8904ac6c-0fe7-46a7-9c2e-c887c02cfc7a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUng1TTlGbXVxSlJPU1NXbFhROGZYIiwiaWF0IjoxNzEwOTEwOTEzfQ.NIVSj-zZAP0m2aN4vxUgD34k7wGtkV_eckqk6egR_5M', '2024-04-19 13:01:53', '2024-03-20 13:01:56.153283', 'ecdd1694-db7c-442c-a998-ef708d6a2a67'); -INSERT INTO `user_refresh_tokens` VALUES ('8c17e219-f258-47db-abef-2a11a7186b2a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNXpBRXVON0ZfYlVJNjduTElpOGdxIiwiaWF0IjoxNzExNTAzOTA2fQ.Z2_HsrSAPvYM4xNFXQnvrCVEg411YBo_8HboDfwcubE', '2024-04-26 09:45:07', '2024-03-27 09:45:06.549262', '2a71c8ce-d4d4-47c4-b004-928f5056676b'); -INSERT INTO `user_refresh_tokens` VALUES ('90dfeaa3-3fcf-4331-8f82-db4fc82f9bff', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiV190N2NKM0E4OEFQU1FjZmR2UDR5IiwiaWF0IjoxNzExNTE4ODUxfQ.d-yOrwMWzGGdPKS5J3trIb_aFhAqOdLLwRnIdNzpbMc', '2024-04-26 13:54:12', '2024-03-27 13:54:12.227440', '00866088-2523-44ab-b972-1edb92b5318a'); -INSERT INTO `user_refresh_tokens` VALUES ('9438a093-f9c5-4109-a01e-e8a41e91cb8b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ0JGOFhSMEwzLTAzVmstbFc5TTFYIiwiaWF0IjoxNzEwODQwNjI1fQ.uLNqf8hiq8YfyCTCrZxBQB1XnuGu9XkSB01wOSfuKA8', '2024-04-18 17:30:26', '2024-03-19 17:30:27.503873', '95cc9059-6e9b-4354-84ed-29fe0807fac7'); -INSERT INTO `user_refresh_tokens` VALUES ('9546e470-dcf8-4c8f-880c-6664aaac22e2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiNklNblJ3amIwT2xTZjdhSWJCVTNUIiwiaWF0IjoxNzExNTA1MDcwfQ.5s8e-SxStipQf_7_8hC0kIaxAIDCgJqthJkLK22U0cE', '2024-04-26 10:04:30', '2024-03-27 10:04:30.047305', '2a5358f2-a465-4241-834f-271f72fbf947'); -INSERT INTO `user_refresh_tokens` VALUES ('97e4ba1d-8a09-447c-967c-e9110c7697f6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoieTYzNlY2R3JjS3cxeEZZQUJJU2VEIiwiaWF0IjoxNzEwMjA1MzUyfQ.95FHvqWgjxSPnmhcTXFlSc27f4uzUlFCoAjvdBqDLhk', '2024-04-11 09:02:33', '2024-03-12 09:02:32.691301', '59b32e05-ed47-4634-adf2-86076aac9274'); -INSERT INTO `user_refresh_tokens` VALUES ('9b349285-f4b9-41a6-944b-2c3c4e7dae60', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaGdlM0NJQTVKNTRDR2ZqbWkyMzJIIiwiaWF0IjoxNzExNTA4NjUzfQ.JNYJI2F7NUEaNjVLAf7iVaUVMiJ6vUNdpsCZrfuQHEg', '2024-04-26 11:04:14', '2024-03-27 11:04:13.816062', 'cc1cc297-6be6-4267-9aec-fa45e59710eb'); -INSERT INTO `user_refresh_tokens` VALUES ('a545b874-66c2-496b-80d6-2e2d7c80f54c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiaS1ZdEJnOWx1RVMwR2J4SkQxMTVIIiwiaWF0IjoxNzEwODExMzQzfQ.PQtYDGzsAjq-9UGwsT39xrZeqNQrf-7J4YFiby_4Pvo', '2024-04-18 09:22:23', '2024-03-19 09:22:27.254054', '1added11-2ae5-4e39-bfd1-3972d3d95ad6'); -INSERT INTO `user_refresh_tokens` VALUES ('ac390ee3-d586-43ff-aab0-e4820e585fc6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTXMybFdMWndZbUxsbnhrUlRKYmFoIiwiaWF0IjoxNzExNDM4MTkxfQ.HGoplU0yWFYkiSQB2nfkTsys-gydpCLMmJNmJIhHqOU', '2024-04-25 15:29:51', '2024-03-26 15:29:48.535514', 'cc472002-f409-4273-aa55-dde97cb1ac29'); -INSERT INTO `user_refresh_tokens` VALUES ('ae713a5b-7336-4a9b-953c-9f4c010229bb', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoial9hb184RGhfX2o0ZF9pdG50U0pCIiwiaWF0IjoxNzEwNzUxNTUyfQ.e6D-WvoL8W-EfuKS7cxo507YMY8agjj6B5VGldBwz24', '2024-04-17 16:45:53', '2024-03-18 16:45:53.602709', 'd18de5f3-f114-4f19-8981-5ef576706a60'); -INSERT INTO `user_refresh_tokens` VALUES ('b04b1fa0-c9a1-42c6-92ad-4c006acd47b5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQ3VzQkt2b01JSEtVVWJrNkVLTUJxIiwiaWF0IjoxNzEwODEwMDcwfQ.BAVsxgpTk1stXBo_7nPiGF7FRPBLfrOauisyQuJbM7g', '2024-04-18 09:01:10', '2024-03-19 09:01:14.248119', 'eebefeae-565c-44c5-b43c-a2eee2077b5b'); -INSERT INTO `user_refresh_tokens` VALUES ('b3e24d0e-75ba-4e52-80c8-efd4c359aa78', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMnV3dGhKVmthMndzNlFVYktVLXRCIiwiaWF0IjoxNzExMDAxNjMxfQ.Njo3WEb8vU_I0-kMDd0HWK0DhwIdUku9nxOP5h8NMHA', '2024-04-20 14:13:52', '2024-03-21 14:13:54.053415', '3e0918fa-04c2-45df-858e-34d725c21a25'); -INSERT INTO `user_refresh_tokens` VALUES ('b4c414c5-fbe1-44a6-99a6-429f29322b67', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibHk0ZmdacWtqcEtLX3FueWNVQ29SIiwiaWF0IjoxNzExNDM4MzI3fQ.uugw-52NPiCfw4vWPhVo8BUoYcAiMiTI7yNwvHuBh2g', '2024-04-25 15:32:07', '2024-03-26 15:32:04.561632', 'f87de777-667c-4add-938b-d121d69361f6'); -INSERT INTO `user_refresh_tokens` VALUES ('b9fbe7eb-638f-4c9a-8c21-d95062bd7bd9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQkRaNWd1ekJ0UVY0Vk43M1pScUJzIiwiaWF0IjoxNzEwMjI0MDY3fQ.IzUJ_rUGyqXyusRgZGW8LvRb4C9ELZQ8LIJ24JEWgx4', '2024-04-11 14:14:27', '2024-03-12 14:14:27.150768', '7c7a14dc-5cc7-4fc9-922f-745b916a2e8b'); -INSERT INTO `user_refresh_tokens` VALUES ('bc30efde-ded0-456e-95e3-8d6c6e0e321b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQUZBdk45ZTB6Rm1HcHNuYWVrdHp1IiwiaWF0IjoxNzEwODEyMzgwfQ.OXLTRX_0LPY_9jx0LJ8l4UE9hExvmTnASAbQgEofOtA', '2024-04-18 09:39:40', '2024-03-19 09:39:44.327657', '3e6845c8-9cc3-492e-bcd7-9636b00d5631'); -INSERT INTO `user_refresh_tokens` VALUES ('cacae50f-eb2f-43a6-82bd-1811a717c111', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiMTg1X0xTN2F6SVlOa2FmT1ZsWEVqIiwiaWF0IjoxNzExMzM4MDIxfQ.j-kiCfCcZELGLKWf9FRr_GbOXaMAWz6ysjwo2GPhDns', '2024-04-24 11:40:22', '2024-03-25 11:40:21.412052', 'e0b66a0d-2522-4423-8a04-81e282520f2f'); -INSERT INTO `user_refresh_tokens` VALUES ('d0215443-a830-47ee-a75e-13502f411433', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTEp0bGp3R0RFMGFkY0ZOX3h1dGJoIiwiaWF0IjoxNzEwODI2Nzg4fQ.XNw0G7z9AhKZKdk05InMQIeRJLNYRKEk1CCI-qHaMGU', '2024-04-18 13:39:49', '2024-03-19 13:39:49.834145', '835e7377-9633-444e-9522-ec6df447ff79'); -INSERT INTO `user_refresh_tokens` VALUES ('d0e92ef9-4ee0-41f8-873c-a680b20a43df', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZDRxNVR4ZU1rWWNJMGNYbnJET1JfIiwiaWF0IjoxNzEwODM3MTU5fQ.SmfXw9Imy_z2wV-jOcmQ6g1gtfBVDMf3egw0bMcFf9w', '2024-04-18 16:32:40', '2024-03-19 16:32:41.346471', '0233ea11-41af-453a-b8ed-b9731b252d05'); -INSERT INTO `user_refresh_tokens` VALUES ('d7d1c449-e07c-4700-bab0-f713cbcc3a78', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZkE4OWRwMVIweGFYWE9vV09KTWVfIiwiaWF0IjoxNzEwMTQ4MTA0fQ.ZhoxEyMwPEkJybPS32ERrwiaG0ghfGLwbUdy9otrQp4', '2024-04-10 17:08:24', '2024-03-11 17:08:24.311139', '202c3f1c-6068-427f-8c75-50b24f93a5c8'); -INSERT INTO `user_refresh_tokens` VALUES ('d9767c5f-e5f1-4fdc-a04c-25ff3efcffe1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiWXI1bjFsaDExcVN2TVBiOTJVcVJXIiwiaWF0IjoxNzExMDY3MzE5fQ.9iNQ4XzkiWcX2wh1hVtn04ZD11CA72bYnW73KMf3ng8', '2024-04-21 08:28:40', '2024-03-22 08:28:39.885024', 'faa26fea-b099-4edf-a9ca-642f06a4e8c1'); -INSERT INTO `user_refresh_tokens` VALUES ('e198a609-85fd-40da-b5dc-945e11714358', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicWNWRXJfYnpRbExxNjRCOVhMdFdOIiwiaWF0IjoxNzEwMjAzNTU1fQ.cYDa78wUzAus4AKBLJZ0mBCKcGpovG-45-zTUGnhYG0', '2024-04-11 08:32:35', '2024-03-12 08:32:35.344070', 'ab1e415f-d811-47ea-9226-86684b71060f'); -INSERT INTO `user_refresh_tokens` VALUES ('e208e8f8-dab2-499d-b8c1-868e27e455d1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOGlhYWlkcmotU2V6eGk0Vm05Z1g5IiwiaWF0IjoxNzExNTE4NjE4fQ.1JxM1QNvt0k4eq9npx55993acHLSRYJ2XXulKEGEl04', '2024-04-26 13:50:18', '2024-03-27 13:50:18.624158', '9b8a542b-f702-4fa9-87ce-ca87d3063dac'); -INSERT INTO `user_refresh_tokens` VALUES ('e3ae9984-0abc-4d14-a111-287b93dff0f3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZ0dtUkxxVXdpOXhTMkNMcFg4amR3IiwiaWF0IjoxNzEwODM4MDIzfQ.Jy_zuiNofB8w7iEKV-S72pm4RGYc6xgorQvSRR5ZP_U', '2024-04-18 16:47:03', '2024-03-19 16:47:04.913410', '34463fa6-75ab-4fc2-ae16-b2e537c8b36d'); -INSERT INTO `user_refresh_tokens` VALUES ('e4adc089-f257-478f-97f0-8b42ce129a8d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiSkJFOTg2a0xFaG94aVllRV9XT1NFIiwiaWF0IjoxNzExNDM4Mjk0fQ.nYOVp0WwjMl_tZaX-su3xtVuZsAnbN5s8YlX-gq1oDs', '2024-04-25 15:31:34', '2024-03-26 15:31:31.685709', '2af24ef8-886d-4510-b8be-816023f2e517'); -INSERT INTO `user_refresh_tokens` VALUES ('e6c586df-a32d-45e2-8f7b-6523d2cee2b2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUjZrQ0VpWTRrYWhPVVdIVzhHN0IzIiwiaWF0IjoxNzExNDM5Njg3fQ.HNxzaw5Ycf0zMImPhbKkV20uCrzn04Vd9Y0BU1g3i2s', '2024-04-25 15:54:48', '2024-03-26 15:54:45.113890', '85ca2577-5bfe-42f2-b1a5-f2342ab0b42a'); -INSERT INTO `user_refresh_tokens` VALUES ('ea7f6fe0-ec8a-43e1-8ebc-ab0c228cbed3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiM1RLN2hHRVRoRS1DXzBsdjBIcEV3IiwiaWF0IjoxNzExMDY4NTIxfQ.o-Hs0yjcCF-WbpIi72VM380Bri-awOx-nCNo9Yp9oRE', '2024-04-21 08:48:41', '2024-03-22 08:48:40.820075', 'b140cb61-64a4-4e5f-b278-5d1764f28524'); -INSERT INTO `user_refresh_tokens` VALUES ('eaaccc99-f345-4df0-8531-10fd5e0da753', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUGNKX3YyQU56ZTVPeDkta2Y3cU1qIiwiaWF0IjoxNzEwODEyNDMwfQ.jhwk0CSQ6rlzy17aqei586kNJkXb2kpSu-9wZ3YuUsY', '2024-04-18 09:40:30', '2024-03-19 09:40:34.009354', 'aa08e3d0-b1ab-410f-8504-0ae8e2504864'); -INSERT INTO `user_refresh_tokens` VALUES ('ec2dbc1b-aa6e-42bf-be3f-9c1fdd230340', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUU9US0l5LU9SZWFRdHI1TVdEM195IiwiaWF0IjoxNzEwODExNzM5fQ.3eXDFyZZPKxnHCuriw0dmeqf6GUOnc_S3uOdC4Oxuy4', '2024-04-18 09:28:59', '2024-03-19 09:29:03.177688', 'e7ba638f-c89f-47e9-bd67-8918a7cb8cc3'); -INSERT INTO `user_refresh_tokens` VALUES ('f1a3e670-9d12-44a6-8b83-b0e0778100ad', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibzJmWWxkQTZGV0w3M1lRR09lRnVHIiwiaWF0IjoxNzExMDY4NzQ5fQ.PD5lAFhnCc69EFqvR2JYBGej7cbn6sFuhG5sXp0gMFU', '2024-04-21 08:52:30', '2024-03-22 08:52:29.476656', 'c519656a-1312-4074-990d-e9577c000278'); -INSERT INTO `user_refresh_tokens` VALUES ('f4fce7f4-fbe3-46b2-a75b-ba11fb647e5c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiamhLa0xfb1NXdUJ5TkdOQ1FYRGd5IiwiaWF0IjoxNzEwODExMjE2fQ.dEeCaXdTog4G9lmJHCCkzWD_QVUQRO1rrlmlBVq_LXQ', '2024-04-18 09:20:16', '2024-03-19 09:20:20.331443', '29c99bed-2ad0-455d-9085-9e59192e2426'); -INSERT INTO `user_refresh_tokens` VALUES ('f7ef3cda-6ff7-4f45-aab8-b9be92fbcece', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiclpYRzhxdTh0d09DUkQ2VXdhSmN4IiwiaWF0IjoxNzExNDM4MjE4fQ.aIWdev__QoIH4swvCYYR3gopPB__Nnx6VohCCM3pWi4', '2024-04-25 15:30:19', '2024-03-26 15:30:16.107682', 'b7f0d7b7-eb71-4eb1-a47d-88c444a44b2f'); -INSERT INTO `user_refresh_tokens` VALUES ('f83618c0-574f-47bc-a98b-2758ff129956', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiLUVIWWhpSmNBRDRUaEVkVVZWRGJqIiwiaWF0IjoxNzEwOTg1MjIzfQ.pRRd0XCTS8qVOATSdgcPPNbhoA9t2v8AV8hUkn2n6UY', '2024-04-20 09:40:23', '2024-03-21 09:40:24.889886', 'b85f77c8-fd9c-4f97-b680-666be5b2deb6'); -INSERT INTO `user_refresh_tokens` VALUES ('fdb751bd-22fd-40bd-8fb6-bdfc1ea64a2a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiN2E5ZE9KVndsdzlYWVdLbmt5ck9IIiwiaWF0IjoxNzExMDczOTA2fQ.YUoi63l27GMv2A8QVPO73KrkRLkG9xUWV5scac3lhSQ', '2024-04-21 10:18:26', '2024-03-22 10:18:26.309973', '5bb267d3-8f88-4a1c-90cc-b36c5bf156fa'); +INSERT INTO `user_refresh_tokens` VALUES ('0aacf8f4-720a-4506-b6da-bf80ad7507ad', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidGFNWHY3SzNIcnpGN0hPVm0wNTlZIiwiaWF0IjoxNzEyNDU5MjUwfQ.VuYA4gG5Cbuc6hSYAduwhi7t0qNmcHaSihF43dGnl_s', '2024-05-07 11:07:30', '2024-04-07 11:07:30.035958', 'c9ee2211-fa30-4a74-9e96-1622e959c5a5'); +INSERT INTO `user_refresh_tokens` VALUES ('1bbfc866-7019-4244-8fc5-ce3573c18f5a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZFUtMUZlS3lCNzZDY19NQ0JCTlVGIiwiaWF0IjoxNzEyNDU4ODM5fQ.hgp34zbgIzzU7nTYxjY-nqi44SeJzmAusZEFoBPFDxk', '2024-05-07 11:00:40', '2024-04-07 11:00:39.541546', 'c9e94b55-dd93-4ea1-aee1-c81d59a9e1bc'); +INSERT INTO `user_refresh_tokens` VALUES ('325d7565-b020-4de0-9c99-d645075678be', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTDh1UV9XWEg3cXU5S2pRbUZGRWh3IiwiaWF0IjoxNzEyNDU4MjE0fQ.WHiXKLlA9p-Bjwud3EJ9sie1cpr5MImYuE7Vy3cwLjQ', '2024-05-07 10:50:14', '2024-04-07 10:50:14.409485', '067fa54e-e37d-45eb-850c-11fe6e580555'); +INSERT INTO `user_refresh_tokens` VALUES ('468da5b2-f67f-4d70-b043-7e479dc8d6a9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidXJCYUJKMkd0a0VfZGdOOC1hemh4IiwiaWF0IjoxNzEyNDU4OTE5fQ.Y2UvFoYF2i2rhzH12T_mggSi27pGk1jpX3WxqlRD6ho', '2024-05-07 11:02:00', '2024-04-07 11:01:59.643833', 'a35d093a-c004-459c-a080-c0abc346a115'); +INSERT INTO `user_refresh_tokens` VALUES ('4cd90a2b-ced3-455c-83a1-0b21b6fd5d8c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVnRqaHlEZnA2NURlMXh6WkxOYVl4IiwiaWF0IjoxNzEyNDU4MzQxfQ.RilIoFnJgeh-ze5Cy_ODvebySQyxIjJyIAoPB8S69Wc', '2024-05-07 10:52:21', '2024-04-07 10:52:21.451515', 'b6f1ce31-bf6b-4dc4-80eb-3ab37774e179'); +INSERT INTO `user_refresh_tokens` VALUES ('51c7573f-2d21-4206-a242-906d764a874d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTGROUjJ0blBZR1pEZXl4YTd6RXNGIiwiaWF0IjoxNzEyNDE3NjIwfQ.60gygI_tNscvCHdFI64XwRh9WUEWoFjscg-Q6Sl7EV8', '2024-05-06 23:33:40', '2024-04-06 23:33:40.032302', '50be25ab-05a0-468f-919c-ec623ac41761'); +INSERT INTO `user_refresh_tokens` VALUES ('7827a7fe-a694-4723-b11e-fd1fd542e81b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZDV6MXo5MjdMZ1V1bXJTUmY0WEloIiwiaWF0IjoxNzEyNDU4NzI4fQ.ViiYJc3DmYug_JbpcT4D6a65LVj23ebSkHuxWipFQkE', '2024-05-07 10:58:48', '2024-04-07 10:58:48.193052', '96ae5545-c90f-452a-bae7-377e8cf32169'); +INSERT INTO `user_refresh_tokens` VALUES ('7c504748-e2e6-41b6-a4b6-affc8bd8de72', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiajd1WmgxRjFmMnNqNWQ5Z0RienBwIiwiaWF0IjoxNzEyNDU4MTk0fQ.0BaG_gy41z-7y9EpZK05RV2-OgkDFHMEtJ9ofuVMLPs', '2024-05-07 10:49:55', '2024-04-07 10:49:54.993642', '657d245b-b61f-4f84-9bb8-3358ad645546'); +INSERT INTO `user_refresh_tokens` VALUES ('81c1b063-256a-4337-87db-e7bb01a9c793', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOThjSTdkSkJQZGdFZ3lJQjJRODI3IiwiaWF0IjoxNzEyNDU4MTM5fQ.fpDITw5fInmn4_fXde-O-i-SgV_9-lGrAqk5Rq-v1VY', '2024-05-07 10:48:59', '2024-04-07 10:48:59.389990', '841bd4fc-b3e5-4864-ad4e-6849261e5806'); +INSERT INTO `user_refresh_tokens` VALUES ('879409aa-4e60-491f-b155-cb4433832ae8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUnJBMFRQbGtnckpIRTNUVG83QjRPIiwiaWF0IjoxNzEyNDU4NzkyfQ.YFufOp1ytb6ZyzaMFscf972VMIaPb09GsG9Z0FBp7R8', '2024-05-07 10:59:53', '2024-04-07 10:59:52.580656', '06fa487c-c9f4-4dcc-8643-ef6146e42add'); +INSERT INTO `user_refresh_tokens` VALUES ('89b9df53-5d1b-4640-878c-821db4ddafe1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVkJlOEZ5OHJ3Um1HQkxfMVRNQzNlIiwiaWF0IjoxNzEyNDU4Njg5fQ.SwXWgzeLNKAvQU39SHrQYrGSQmYWAaAjOF0WVNveHGc', '2024-05-07 10:58:10', '2024-04-07 10:58:09.604051', 'd9f221ea-111d-47fe-932d-4123174dfce8'); +INSERT INTO `user_refresh_tokens` VALUES ('8ba5e525-a9eb-4823-b41e-ef9fb04624a2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiSVY5bW1WRDRzY0xwaE00ek90ZmRaIiwiaWF0IjoxNzEyNDU3ODA2fQ.UKJaAJvXJJg96almY9QNb8mrjyBPewQvw5ECgZGjRpI', '2024-05-07 10:43:26', '2024-04-07 10:43:26.350399', 'cdb64fe0-ca48-466b-9caa-5812aa7a8634'); +INSERT INTO `user_refresh_tokens` VALUES ('ab50f093-429b-4fb3-b1d4-f91e3205419a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiT0IwbzVHVFkxazNERVF0eFU4N0RGIiwiaWF0IjoxNzEyNDU4NzU2fQ.I9miRO1z2X_Uhf8QJxcjk6gKOisvrVISMZY3nqxUE1g', '2024-05-07 10:59:16', '2024-04-07 10:59:16.320141', 'd78ecadb-7bfd-4c65-a8c4-05e9473a80a5'); +INSERT INTO `user_refresh_tokens` VALUES ('b5913f11-9132-4342-a5fc-1823b4f8095e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRHo3cmRRck5nYkRGYTNra3lYZkY4IiwiaWF0IjoxNzEyNDU4NDUxfQ.ITzqgmr_hZo7yoaKeoYa7DCV51EHGSSW95aJOgHASqc', '2024-05-07 10:54:11', '2024-04-07 10:54:11.402462', 'f886261e-bb1d-45db-83a4-71ef0f2a9180'); +INSERT INTO `user_refresh_tokens` VALUES ('c996e647-0b40-4271-89a4-18c6fa361648', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibTd5VkxYUDRSWndNYTRBOE5qTk9CIiwiaWF0IjoxNzEyNDU4NDA1fQ.ImGnCiah6WX6tHltPV-xphw7i-CHP2IU-BwOElc2uy4', '2024-05-07 10:53:25', '2024-04-07 10:53:25.390342', 'aa9787ae-7565-4d09-a3cb-b216947cfcab'); +INSERT INTO `user_refresh_tokens` VALUES ('ce1d039d-ed44-41f6-9228-06720e288088', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicVZPU0xfd3hwRnZjRi1fNVdwVGZqIiwiaWF0IjoxNzEyNDU2NzE3fQ.u7EV8m_sg7svflMYEitqvkOgXnOlKG-pLjD6fQ4Hnc8', '2024-05-07 10:25:18', '2024-04-07 10:25:17.983231', '4b231512-42ac-41f6-9acc-9b3ac9590f12'); +INSERT INTO `user_refresh_tokens` VALUES ('ff56df48-89bb-46b3-a8d2-80426dba6e43', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoialZYS3JNUVZUMC1PbDJYMXJBZmE1IiwiaWF0IjoxNzEyNDU4Mjc0fQ.l24dDsJs64KhV9SPzltTGl-8o75aqYXTczGtgieJ8H8', '2024-05-07 10:51:14', '2024-04-07 10:51:14.126506', '64b831b0-32a9-4fad-ae3f-9f8f370f3bf2'); -- ---------------------------- -- Table structure for vehicle_usage @@ -1443,8 +1068,6 @@ CREATE TABLE `vehicle_usage` ( -- ---------------------------- -- Records of vehicle_usage -- ---------------------------- -INSERT INTO `vehicle_usage` VALUES (1, '2024-03-08 09:42:45.944580', '2024-03-08 10:23:13.000000', '邹姐', 1, NULL, 2023, 24, '卢大仙', '', 35001, '2024-03-08', '2024-03-15', '接客户', '2024-03-15', 35010, NULL); -INSERT INTO `vehicle_usage` VALUES (2, '2024-03-08 09:51:29.014799', '2024-03-08 09:51:29.014799', NULL, 0, NULL, 2024, 25, '卢大仙', NULL, NULL, '2024-02-27', '2024-02-27', NULL, NULL, NULL, NULL); -- ---------------------------- -- Table structure for vehicle_usage_storage @@ -1458,12 +1081,10 @@ CREATE TABLE `vehicle_usage_storage` ( INDEX `IDX_a8cbcb6835a9212dd2a49b50ed`(`file_id`) USING BTREE, CONSTRAINT `FK_1d122393de1ee773c383569e717` FOREIGN KEY (`vehicle_id`) REFERENCES `vehicle_usage` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_a8cbcb6835a9212dd2a49b50ed9` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of vehicle_usage_storage -- ---------------------------- -INSERT INTO `vehicle_usage_storage` VALUES (1, 146); -INSERT INTO `vehicle_usage_storage` VALUES (1, 147); SET FOREIGN_KEY_CHECKS = 1; From 2d71fdf001e568011bd9c27d2106f31b8d504b40 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Sun, 7 Apr 2024 16:28:14 +0800 Subject: [PATCH 54/64] feat: upload apk --- src/common/adapters/fastify.adapter.ts | 2 +- src/modules/tools/upload/upload.service.ts | 3 ++- src/utils/file.util.ts | 15 +++++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/common/adapters/fastify.adapter.ts b/src/common/adapters/fastify.adapter.ts index 65db5c9..a2caadc 100644 --- a/src/common/adapters/fastify.adapter.ts +++ b/src/common/adapters/fastify.adapter.ts @@ -13,7 +13,7 @@ app.register(FastifyMultipart, { attachFieldsToBody:true, limits: { fields: 10, // Max number of non-file fields - fileSize: 1024 * 1024 * 6, // limit size 6M + fileSize: 1024 * 1024 * 30, // limit size 6M files: 5 // Max number of file fields } }); diff --git a/src/modules/tools/upload/upload.service.ts b/src/modules/tools/upload/upload.service.ts index 8186817..0d79f9c 100644 --- a/src/modules/tools/upload/upload.service.ts +++ b/src/modules/tools/upload/upload.service.ts @@ -7,6 +7,7 @@ import { Repository } from 'typeorm'; import { Storage } from '~/modules/tools/storage/storage.entity'; import { + UploadFileType, fileRename, getExtname, getFilePath, @@ -37,7 +38,7 @@ export class UploadService { const size = getSize(file.file.bytesRead); const extName = getExtname(fileName); const type = getFileType(extName); - const name = fileRename(fileName); + const name = type !== UploadFileType.APK ? fileRename(fileName) : fileName; const path = getFilePath(name); saveLocalFile(await file.toBuffer(), name); diff --git a/src/utils/file.util.ts b/src/utils/file.util.ts index b7a41c5..73fe482 100644 --- a/src/utils/file.util.ts +++ b/src/utils/file.util.ts @@ -5,11 +5,12 @@ import { MultipartFile } from '@fastify/multipart'; import dayjs from 'dayjs'; -enum Type { +export enum UploadFileType { IMAGE = '图片', TXT = '文档', MUSIC = '音乐', VIDEO = '视频', + APK = 'apk', OTHER = '其他' } @@ -18,15 +19,17 @@ export function getFileType(extName: string) { const music = 'mp3 wav wma mpa ram ra aac aif m4a'; const video = 'avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg'; const image = 'bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg'; - if (image.includes(extName)) return Type.IMAGE; + const apk = 'apk'; + if (image.includes(extName)) return UploadFileType.IMAGE; - if (documents.includes(extName)) return Type.TXT; + if (documents.includes(extName)) return UploadFileType.TXT; - if (music.includes(extName)) return Type.MUSIC; + if (music.includes(extName)) return UploadFileType.MUSIC; - if (video.includes(extName)) return Type.VIDEO; + if (video.includes(extName)) return UploadFileType.VIDEO; - return Type.OTHER; + if (apk.includes(extName)) return UploadFileType.APK; + return UploadFileType.OTHER; } export function getName(fileName: string) { From 2df310b1c0b5985c34a852435a45b8b9a2eb796d Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Tue, 9 Apr 2024 15:24:54 +0800 Subject: [PATCH 55/64] fix: product create duplicated issue --- src/modules/product/product.service.ts | 12 +++++++++--- src/modules/system/dept/dept.service.ts | 1 - src/modules/user/user.entity.ts | 23 +++++++++++++++++++++++ src/modules/user/user.service.ts | 4 ++-- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/modules/product/product.service.ts b/src/modules/product/product.service.ts index eacf1e4..fc6c9fc 100644 --- a/src/modules/product/product.service.ts +++ b/src/modules/product/product.service.ts @@ -57,7 +57,6 @@ export class ProductService { keyword: `%${keyword}%` } ); - } return paginate(sqb, { page, @@ -69,9 +68,9 @@ export class ProductService { * 新增 */ async create(dto: ProductDto): Promise { - const { name, companyId } = dto; + const { name, companyId, productSpecification } = dto; const isExsit = await this.productRepository.findOne({ - where: { name, company: { id: companyId } } + where: { productSpecification, name, company: { id: companyId } } }); if (isExsit) { throw new BusinessException(ErrorEnum.PRODUCT_EXIST); @@ -86,6 +85,13 @@ export class ProductService { */ async update(id: number, { fileIds, ...data }: Partial): Promise { await this.entityManager.transaction(async manager => { + const { name, companyId, productSpecification } = data; + const isExsit = await this.productRepository.findOne({ + where: { productSpecification, name, company: { id: companyId } } + }); + if (isExsit) { + throw new BusinessException(ErrorEnum.PRODUCT_EXIST); + } await manager.update( ProductEntity, id, diff --git a/src/modules/system/dept/dept.service.ts b/src/modules/system/dept/dept.service.ts index 8d846c3..08f9cfa 100644 --- a/src/modules/system/dept/dept.service.ts +++ b/src/modules/system/dept/dept.service.ts @@ -116,7 +116,6 @@ export class DeptService { } const deptTree = await this.deptRepository.findTrees({ - depth: 2, relations: ['parent'] }); diff --git a/src/modules/user/user.entity.ts b/src/modules/user/user.entity.ts index 3df5839..e311827 100644 --- a/src/modules/user/user.entity.ts +++ b/src/modules/user/user.entity.ts @@ -1,5 +1,9 @@ +import { ApiHideProperty } from '@nestjs/swagger'; import { Exclude } from 'class-transformer'; +import pinyin from 'pinyin'; import { + BeforeInsert, + BeforeUpdate, Column, Entity, JoinColumn, @@ -32,6 +36,25 @@ export class UserEntity extends CommonEntity { @Column({ nullable: true }) nickname: string; + @ApiHideProperty() + @Column({ + name: 'name_pinyin', + type: 'varchar', + length: 255, + nullable: true, + comment: '产品名称的拼音' + }) + namePinyin: string; + + @BeforeInsert() + @BeforeUpdate() + updateNamePinyin() { + this.namePinyin = pinyin(this.nickname, { + style: pinyin.STYLE_NORMAL, + heteronym: false + }).join(''); + } + @Column({ name: 'avatar', nullable: true }) avatar: string; diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index aa4ee6c..e649a64 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -5,7 +5,7 @@ import Redis from 'ioredis'; import { isEmpty, isNil, isNumber } from 'lodash'; import { EntityManager, In, Like, Repository } from 'typeorm'; - +import pinyin from 'pinyin'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; import { ROOT_ROLE_ID, SYS_USER_INITPASSWORD } from '~/constants/system.constant'; @@ -268,7 +268,7 @@ export class UserService { if (keyword) { //关键字模糊查询product的name,productNumber,productSpecification queryBuilder.andWhere( - '(user.nickname like :keyword or dept.name like :keyword)', + '(user.nickname like :keyword or user.namePinyin like :keyword or dept.name like :keyword)', { keyword: `%${keyword}%` } From 563c08c233e00ef4f5c3f2b92f15efb29977aadd Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 10 Apr 2024 17:39:17 +0800 Subject: [PATCH 56/64] feat: menu device --- src/constants/enum/index.ts | 6 +++++ src/modules/auth/auth.service.ts | 4 ++-- .../auth/controllers/account.controller.ts | 9 ++++++-- .../in_out/materials_in_out.dto.ts | 3 +++ src/modules/system/menu/menu.controller.ts | 6 ++++- src/modules/system/menu/menu.dto.ts | 15 +++++++++++- src/modules/system/menu/menu.entity.ts | 15 +++++++----- src/modules/system/menu/menu.service.ts | 23 ++++++++++++++++--- src/modules/system/role/role.dto.ts | 5 ++++ src/modules/system/role/role.service.ts | 7 ++++-- src/modules/user/user.service.ts | 8 ++++++- 11 files changed, 83 insertions(+), 18 deletions(-) diff --git a/src/constants/enum/index.ts b/src/constants/enum/index.ts index 70ec17b..f150c1c 100644 --- a/src/constants/enum/index.ts +++ b/src/constants/enum/index.ts @@ -40,3 +40,9 @@ export enum HasInventoryStatusEnum { Yes = 1, // 有库存 No = 2 // 无库存 } + +// 权限资源设备类型 +export enum ResourceDeviceEnum { + APP = 0, + PC = 1 +} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 9526203..ab152f8 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -132,8 +132,8 @@ export class AuthService { /** * 获取菜单列表 */ - async getMenus(uid: number): Promise { - return this.menuService.getMenus(uid); + async getMenus(uid: number, isApp: number): Promise { + return this.menuService.getMenus(uid,isApp); } /** diff --git a/src/modules/auth/controllers/account.controller.ts b/src/modules/auth/controllers/account.controller.ts index ec3a4f9..c11feae 100644 --- a/src/modules/auth/controllers/account.controller.ts +++ b/src/modules/auth/controllers/account.controller.ts @@ -14,6 +14,8 @@ import { UserService } from '../../user/user.service'; import { AuthService } from '../auth.service'; import { AccountMenus, AccountUpdateDto } from '../dto/account.dto'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; +import { IsMobile } from '~/common/decorators/http.decorator'; +import { ResourceDeviceEnum } from '~/constants/enum'; @ApiTags('Account - 账户模块') @ApiSecurityAuth() @@ -45,8 +47,11 @@ export class AccountController { @ApiOperation({ summary: '获取菜单列表' }) @ApiResult({ type: [AccountMenus] }) @AllowAnon() - async menu(@AuthUser() user: IAuthUser): Promise { - return this.authService.getMenus(user.uid); + async menu(@AuthUser() user: IAuthUser, @IsMobile() isApp: boolean): Promise { + return this.authService.getMenus( + user.uid, + isApp ? ResourceDeviceEnum.APP : ResourceDeviceEnum.PC + ); } @Get('permissions') diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index a2f7fb7..5364295 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -53,6 +53,9 @@ export class MaterialsInOutDto { inOrOut: MaterialsInOrOutEnum; @ApiProperty({ description: '时间' }) + @Transform(params => { + return params.value ? new Date(params.value) : null; + }) @IsOptional() time: Date; diff --git a/src/modules/system/menu/menu.controller.ts b/src/modules/system/menu/menu.controller.ts index d14b8de..19cc977 100644 --- a/src/modules/system/menu/menu.controller.ts +++ b/src/modules/system/menu/menu.controller.ts @@ -23,6 +23,8 @@ import { import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto'; import { MenuItemInfo } from './menu.model'; import { MenuService } from './menu.service'; +import { IsMobile } from '~/common/decorators/http.decorator'; +import { ResourceDeviceEnum } from '~/constants/enum'; export const permissions = definePermission('system:menu', { LIST: 'list', @@ -43,7 +45,9 @@ export class MenuController { @ApiResult({ type: [MenuItemInfo] }) @Perm(permissions.LIST) async list(@Query() dto: MenuQueryDto) { - return this.menuService.list(dto); + return this.menuService.list({ + ...dto + }); } @Get(':id') diff --git a/src/modules/system/menu/menu.dto.ts b/src/modules/system/menu/menu.dto.ts index da2b024..76439b3 100644 --- a/src/modules/system/menu/menu.dto.ts +++ b/src/modules/system/menu/menu.dto.ts @@ -3,6 +3,7 @@ import { IsBoolean, IsIn, IsInt, + IsNumber, IsOptional, IsString, Min, @@ -15,6 +16,11 @@ export class MenuDto { @IsIn([0, 1, 2]) type: number; + @ApiProperty({ description: '客户端设备类型' }) + @IsOptional() + @IsIn([0, 1]) + device: number; + @ApiProperty({ description: '父级菜单' }) @IsOptional() parentId: number; @@ -85,4 +91,11 @@ export class MenuDto { export class MenuUpdateDto extends PartialType(MenuDto) {} -export class MenuQueryDto extends PartialType(MenuDto) {} +export class MenuQueryDto extends PartialType(MenuDto) { + + @ApiProperty({ description: 'App端的菜单权限' }) + @IsNumber() + @IsOptional() + isApp?: number; + +} diff --git a/src/modules/system/menu/menu.entity.ts b/src/modules/system/menu/menu.entity.ts index 938c497..48da32f 100644 --- a/src/modules/system/menu/menu.entity.ts +++ b/src/modules/system/menu/menu.entity.ts @@ -6,28 +6,28 @@ import { RoleEntity } from '../role/role.entity'; @Entity({ name: 'sys_menu' }) export class MenuEntity extends CommonEntity { - @Column({ name: 'parent_id', nullable: true }) + @Column({ name: 'parent_id', nullable: true, comment: '父级ID' }) parentId: number; @Column() name: string; - @Column({ nullable: true }) + @Column({ nullable: true, comment: '前端路径' }) path: string; - @Column({ nullable: true }) + @Column({ nullable: true, comment: '权限' }) permission: string; - @Column({ type: 'tinyint', default: 0 }) + @Column({ type: 'tinyint', default: 0, comment: '类型:0-目录 1-菜单 2-权限' }) type: number; - @Column({ nullable: true, default: '' }) + @Column({ nullable: true, default: '', comment: '图标' }) icon: string; @Column({ name: 'order_no', type: 'int', nullable: true, default: 0 }) orderNo: number; - @Column({ name: 'component', nullable: true }) + @Column({ name: 'component', nullable: true, comment: '前端组件文件地址' }) component: string; @Column({ name: 'is_ext', type: 'boolean', default: false }) @@ -48,6 +48,9 @@ export class MenuEntity extends CommonEntity { @Column({ type: 'tinyint', default: 1 }) status: number; + @Column({ type: 'tinyint', default: 1,comment: '用户端类型:0-APP 1-PC' }) + device: number; + @ManyToMany(() => RoleEntity, role => role.menus, { onDelete: 'CASCADE' }) diff --git a/src/modules/system/menu/menu.service.ts b/src/modules/system/menu/menu.service.ts index b156714..ba8c723 100644 --- a/src/modules/system/menu/menu.service.ts +++ b/src/modules/system/menu/menu.service.ts @@ -18,6 +18,7 @@ import { deleteEmptyChildren, generatorMenu, generatorRouters } from '~/utils'; import { RoleService } from '../role/role.service'; import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto'; +import { ResourceDeviceEnum } from '~/constants/enum'; @Injectable() export class MenuService { @@ -32,7 +33,14 @@ export class MenuService { /** * 获取所有菜单以及权限 */ - async list({ name, path, permission, component, status }: MenuQueryDto): Promise { + async list({ + name, + path, + permission, + component, + status, + isApp + }: MenuQueryDto): Promise { const menus = await this.menuRepository.find({ where: { ...(name && { name: Like(`%${name}%`) }), @@ -66,20 +74,29 @@ export class MenuService { /** * 根据角色获取所有菜单 */ - async getMenus(uid: number): Promise { + async getMenus(uid: number, deviceType: number): Promise { const roleIds = await this.roleService.getRoleIdsByUser(uid); let menus: MenuEntity[] = []; if (isEmpty(roleIds)) return generatorRouters([]); if (this.roleService.hasAdminRole(roleIds)) { - menus = await this.menuRepository.find({ order: { orderNo: 'ASC' } }); + menus = await this.menuRepository.find({ + order: { orderNo: 'ASC' }, + where: { + ...(isNumber(deviceType) ? { device: deviceType } : null) + } + }); } else { menus = await this.menuRepository .createQueryBuilder('menu') .innerJoinAndSelect('menu.roles', 'role') + .where({ + ...(isNumber(deviceType) ? { device: deviceType } : null) + }) .andWhere('role.id IN (:...roleIds)', { roleIds }) .orderBy('menu.order_no', 'ASC') + .getMany(); } diff --git a/src/modules/system/role/role.dto.ts b/src/modules/system/role/role.dto.ts index 7c48dcd..6fef513 100644 --- a/src/modules/system/role/role.dto.ts +++ b/src/modules/system/role/role.dto.ts @@ -35,4 +35,9 @@ export class RoleQueryDto extends IntersectionType(PagerDto, PartialTyp @IsInt() @IsOptional() status?: number; + + @ApiProperty({ description: '用于下拉框选择', required: false }) + @IsInt() + @IsOptional() + useForSelect: number; } diff --git a/src/modules/system/role/role.service.ts b/src/modules/system/role/role.service.ts index d8694cc..7b591ad 100644 --- a/src/modules/system/role/role.service.ts +++ b/src/modules/system/role/role.service.ts @@ -32,14 +32,17 @@ export class RoleService { pageSize, name, value, - status + status, + useForSelect }: RoleQueryDto): Promise> { const queryBuilder = this.roleRepository.createQueryBuilder('role').where({ ...(name ? { name: Like(`%${name}%`) } : null), ...(value ? { value: Like(`%${value}%`) } : null), ...(isNumber(status) ? { status } : null) }); - + if(useForSelect){ + queryBuilder.andWhere('role.id != :id', { id: ROOT_ROLE_ID }) + } return paginate(queryBuilder, { page, pageSize diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index e649a64..d3f7287 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -268,7 +268,13 @@ export class UserService { if (keyword) { //关键字模糊查询product的name,productNumber,productSpecification queryBuilder.andWhere( - '(user.nickname like :keyword or user.namePinyin like :keyword or dept.name like :keyword)', + `(user.nickname like :keyword or user.namePinyin like :keyword + or dept.name like :keyword + or user.phone like :keyword + or user.email like :keyword + or user.qq like :keyword + or user.remark like :keyword + )`, { keyword: `%${keyword}%` } From b4dc00ecd4ec9bd918adad22c019d26f77fcb780 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Thu, 11 Apr 2024 09:05:58 +0800 Subject: [PATCH 57/64] feat: file upload limit 50M --- src/common/adapters/fastify.adapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/adapters/fastify.adapter.ts b/src/common/adapters/fastify.adapter.ts index a2caadc..25ff8c2 100644 --- a/src/common/adapters/fastify.adapter.ts +++ b/src/common/adapters/fastify.adapter.ts @@ -13,7 +13,7 @@ app.register(FastifyMultipart, { attachFieldsToBody:true, limits: { fields: 10, // Max number of non-file fields - fileSize: 1024 * 1024 * 30, // limit size 6M + fileSize: 1024 * 1024 * 50, // limit size 50M files: 5 // Max number of file fields } }); From c815e5d561b67c1cc9850fb78b4c1f0ac9483b5b Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Fri, 12 Apr 2024 09:55:02 +0800 Subject: [PATCH 58/64] feat: sale_quotation --- src/app.module.ts | 6 +- src/constants/error-code.constant.ts | 5 +- src/modules/project/project.service.ts | 5 - .../sale_quotation_component.controller.ts | 69 ++++++++ .../component/sale_quotation_component.dto.ts | 59 +++++++ .../sale_quotation_component.entity.ts | 85 +++++++++ .../sale_quotation_component.module.ts | 14 ++ .../sale_quotation_component.service.ts | 161 ++++++++++++++++++ .../group/sale_quotation_group.controller.ts | 63 +++++++ .../group/sale_quotation_group.dto.ts | 27 +++ .../group/sale_quotation_group.entity.ts | 33 ++++ .../group/sale_quotation_group.module.ts | 14 ++ .../group/sale_quotation_group.service.ts | 83 +++++++++ .../sale_quotation/sale_quotation.module.ts | 25 +++ .../sale_quotation_template.controller.ts | 63 +++++++ .../template/sale_quotation_template.dto.ts | 29 ++++ .../sale_quotation_template.entity.ts | 32 ++++ .../sale_quotation_template.module.ts | 14 ++ .../sale_quotation_template.service.ts | 83 +++++++++ src/modules/tools/storage/storage.entity.ts | 6 + 20 files changed, 869 insertions(+), 7 deletions(-) create mode 100644 src/modules/sale_quotation/component/sale_quotation_component.controller.ts create mode 100644 src/modules/sale_quotation/component/sale_quotation_component.dto.ts create mode 100644 src/modules/sale_quotation/component/sale_quotation_component.entity.ts create mode 100644 src/modules/sale_quotation/component/sale_quotation_component.module.ts create mode 100644 src/modules/sale_quotation/component/sale_quotation_component.service.ts create mode 100644 src/modules/sale_quotation/group/sale_quotation_group.controller.ts create mode 100644 src/modules/sale_quotation/group/sale_quotation_group.dto.ts create mode 100644 src/modules/sale_quotation/group/sale_quotation_group.entity.ts create mode 100644 src/modules/sale_quotation/group/sale_quotation_group.module.ts create mode 100644 src/modules/sale_quotation/group/sale_quotation_group.service.ts create mode 100644 src/modules/sale_quotation/sale_quotation.module.ts create mode 100644 src/modules/sale_quotation/template/sale_quotation_template.controller.ts create mode 100644 src/modules/sale_quotation/template/sale_quotation_template.dto.ts create mode 100644 src/modules/sale_quotation/template/sale_quotation_template.entity.ts create mode 100644 src/modules/sale_quotation/template/sale_quotation_template.module.ts create mode 100644 src/modules/sale_quotation/template/sale_quotation_template.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 28b5c6d..992ae61 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -30,6 +30,7 @@ import { CompanyModule } from './modules/company/company.module'; import { ProductModule } from './modules/product/product.module'; import { ProjectModule } from './modules/project/project.module'; import { VehicleUsageModule } from './modules/vehicle_usage/vehicle_usage.module'; +import { SaleQuotationModule } from './modules/sale_quotation/sale_quotation.module'; @Module({ imports: [ @@ -73,7 +74,10 @@ import { VehicleUsageModule } from './modules/vehicle_usage/vehicle_usage.module ProjectModule, // 车辆管理 - VehicleUsageModule + VehicleUsageModule, + + //报价管理 + SaleQuotationModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 40cacd8..fd27d49 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -61,5 +61,8 @@ export enum ErrorEnum { INVENTORY_INSUFFICIENT = '1408:库存数量不足。请检查库存或重新操作', MATERIALS_IN_OUT_NOT_FOUND = '1409:出入库信息不存在', MATERIALS_IN_OUT_UNIT_PRICE_CANNOT_BE_MODIFIED = '1410:该价格的产品已经出库,单价不允许修改。若有疑问,请联系管理员', - MATERIALS_IN_OUT_UNIT_PRICE_MUST_ZERO_WHEN_MODIFIED = '1411:只能修改初始单价为0的入库记录。 若有疑问,请联系管理员' + MATERIALS_IN_OUT_UNIT_PRICE_MUST_ZERO_WHEN_MODIFIED = '1411:只能修改初始单价为0的入库记录。 若有疑问,请联系管理员', + + // SaleQuotation + SALE_QUOTATION_COMPONENT_DUPLICATED = '1412:存在名称,价格,规格都相同的配件,请检查是否重复录入' } diff --git a/src/modules/project/project.service.ts b/src/modules/project/project.service.ts index fcd4dd0..4b56820 100644 --- a/src/modules/project/project.service.ts +++ b/src/modules/project/project.service.ts @@ -56,11 +56,6 @@ export class ProjectService { await manager.update(ProjectEntity, id, { ...data }); - const project = await this.projectRepository - .createQueryBuilder('project') - .leftJoinAndSelect('project.files', 'files') - .where('project.id = :id', { id }) - .getOne(); if (fileIds?.length) { const count = await this.storageRepository .createQueryBuilder('storage') diff --git a/src/modules/sale_quotation/component/sale_quotation_component.controller.ts b/src/modules/sale_quotation/component/sale_quotation_component.controller.ts new file mode 100644 index 0000000..76d1fa5 --- /dev/null +++ b/src/modules/sale_quotation/component/sale_quotation_component.controller.ts @@ -0,0 +1,69 @@ +import { Body, Controller, Get, Query, Put, Delete, Post } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { SaleQuotationComponentDto, SaleQuotationComponentQueryDto, SaleQuotationComponentUpdateDto } from './sale_quotation_component.dto'; +import { SaleQuotationComponentService } from './sale_quotation_component.service'; +export const permissions = definePermission('sale_quotation:sale_quotation_component', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('SaleQuotationComponent - 报价配件') +@ApiSecurityAuth() +@Controller('sale_quotation_component') +export class SaleQuotationComponentController { + constructor(private saleQuotationComponentService: SaleQuotationComponentService) {} + + @Get() + @ApiOperation({ summary: '分页获取报价配件列表' }) + @ApiResult({ type: [SaleQuotationComponentDto], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: SaleQuotationComponentQueryDto) { + return this.saleQuotationComponentService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取报价配件信息' }) + @ApiResult({ type: SaleQuotationComponentDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.saleQuotationComponentService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增报价配件' }) + @Perm(permissions.CREATE) + async create(@Body() dto: SaleQuotationComponentDto): Promise { + await this.saleQuotationComponentService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新报价配件' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: SaleQuotationComponentUpdateDto): Promise { + await this.saleQuotationComponentService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除报价配件' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.saleQuotationComponentService.delete(id); + } + + @Put('unlink-attachments/:id') + @ApiOperation({ summary: '附件解除关联' }) + @Perm(permissions.UPDATE) + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: SaleQuotationComponentUpdateDto + ): Promise { + await this.saleQuotationComponentService.unlinkAttachments(id, fileIds); + } +} diff --git a/src/modules/sale_quotation/component/sale_quotation_component.dto.ts b/src/modules/sale_quotation/component/sale_quotation_component.dto.ts new file mode 100644 index 0000000..f0c6e94 --- /dev/null +++ b/src/modules/sale_quotation/component/sale_quotation_component.dto.ts @@ -0,0 +1,59 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { IsUnique } from '~/shared/database/constraints/unique.constraint'; +import { SaleQuotationComponentEntity } from './sale_quotation_component.entity'; + +export class SaleQuotationComponentDto { + @ApiProperty({ description: '报价配件名称' }) + @IsString() + name: string; + + @ApiProperty({ description: '配件规格' }) + @IsOptional() + @IsString() + componentSpecification: string; + + @ApiProperty({ description: '配件备注' }) + @IsOptional() + @IsString() + remark: string; + + @ApiProperty({ description: '单位(字典)' }) + @IsOptional() + @IsNumber() + unitId: number; + + @ApiProperty({ description: '单价' }) + @IsOptional() + @IsNumber() + unitPrice: number; + + @ApiProperty({ description: '附件' }) + files: Storage[]; +} + +export class SaleQuotationComponentUpdateDto extends PartialType(SaleQuotationComponentDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} + +export class ComapnyCreateDto extends PartialType(SaleQuotationComponentDto) { + @ApiProperty({ description: '附件' }) + @IsOptional() + @IsArray() + fileIds: number[]; +} + +export class SaleQuotationComponentQueryDto extends IntersectionType( + PagerDto, + PartialType(SaleQuotationComponentDto) +) { + @ApiProperty({ description: '报价配件名称' }) + @IsOptional() + @IsString() + name: string; +} diff --git a/src/modules/sale_quotation/component/sale_quotation_component.entity.ts b/src/modules/sale_quotation/component/sale_quotation_component.entity.ts new file mode 100644 index 0000000..5834376 --- /dev/null +++ b/src/modules/sale_quotation/component/sale_quotation_component.entity.ts @@ -0,0 +1,85 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { + Column, + Entity, + JoinColumn, + JoinTable, + ManyToMany, + ManyToOne, + OneToMany, + Relation +} from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { SaleQuotationGroupEntity } from '../group/sale_quotation_group.entity'; +import { DictItemEntity } from '~/modules/system/dict-item/dict-item.entity'; + +/** + * 报价配件实体类 + */ +@Entity({ name: 'sale_quotation_component' }) +export class SaleQuotationComponentEntity extends CommonEntity { + @Column({ + name: 'name', + type: 'varchar', + length: 255, + comment: '报价配件名称' + }) + @ApiProperty({ description: '报价配件名称' }) + name: string; + + @Column({ + name: 'component_specification', + type: 'varchar', + nullable: true, + length: 255, + comment: '产品规格' + }) + @ApiProperty({ description: '产品规格', nullable: true }) + componentSpecification?: string; + + @Column({ name: 'unit_id', type: 'int', comment: '单位(字典)', nullable: true }) + @ApiProperty({ description: '单位(字典)' }) + unitId: number; + + @ManyToOne(() => DictItemEntity) + @JoinColumn({ name: 'unit_id' }) + unit: DictItemEntity; + + @Column({ + name: 'unit_price', + type: 'decimal', + precision: 15, + default: 0, + scale: 10, + comment: '单价' + }) + @ApiProperty({ description: '单价' }) + unitPrice: number; + + @Column({ + name: 'remark', + type: 'varchar', + length: 255, + nullable: true, + comment: '备注' + }) + @ApiProperty({ description: '产品备注' }) + remark: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + + @ManyToMany(() => Storage, storage => storage.saleQuotationComponents) + @JoinTable({ + name: 'sale_quotation_component_storage', + joinColumn: { name: 'sale_quotation_component_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'file_id', referencedColumnName: 'id' } + }) + files: Relation; + + @ApiHideProperty() + @ManyToMany(() => SaleQuotationGroupEntity, group => group.components) + groups: Relation; +} diff --git a/src/modules/sale_quotation/component/sale_quotation_component.module.ts b/src/modules/sale_quotation/component/sale_quotation_component.module.ts new file mode 100644 index 0000000..5954eac --- /dev/null +++ b/src/modules/sale_quotation/component/sale_quotation_component.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { StorageModule } from '~/modules/tools/storage/storage.module'; +import { DatabaseModule } from '~/shared/database/database.module'; +import { SaleQuotationComponentService } from './sale_quotation_component.service'; +import { SaleQuotationComponentController } from './sale_quotation_component.controller'; +import { SaleQuotationComponentEntity } from './sale_quotation_component.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([SaleQuotationComponentEntity]), StorageModule, DatabaseModule], + controllers: [SaleQuotationComponentController], + providers: [SaleQuotationComponentService] +}) +export class SaleQuotationComponentModule {} diff --git a/src/modules/sale_quotation/component/sale_quotation_component.service.ts b/src/modules/sale_quotation/component/sale_quotation_component.service.ts new file mode 100644 index 0000000..4fd80a9 --- /dev/null +++ b/src/modules/sale_quotation/component/sale_quotation_component.service.ts @@ -0,0 +1,161 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { EntityManager, Not, Repository } from 'typeorm'; +import { Pagination } from '~/helper/paginate/pagination'; +import { paginate } from '~/helper/paginate'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { fieldSearch } from '~/shared/database/field-search'; +import { SaleQuotationComponentEntity } from './sale_quotation_component.entity'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { + SaleQuotationComponentDto, + SaleQuotationComponentQueryDto, + SaleQuotationComponentUpdateDto +} from './sale_quotation_component.dto'; + +@Injectable() +export class SaleQuotationComponentService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(SaleQuotationComponentEntity) + private saleQuotationComponentRepository: Repository, + @InjectRepository(Storage) + private storageRepository: Repository + ) {} + + buildSearchQuery() { + return this.saleQuotationComponentRepository + .createQueryBuilder('saleQuotationComponent') + .leftJoin('saleQuotationComponent.unit', 'unit') + .leftJoin('saleQuotationComponent.files', 'files') + .addSelect(['files.id', 'files.path', 'unit.id', 'unit.label']); + } + + /** + * 分页查询所有 + */ + async findAll({ + page, + pageSize, + ...fields + }: SaleQuotationComponentQueryDto): Promise> { + const queryBuilder = this.buildSearchQuery() + .where(fieldSearch(fields)) + .andWhere('saleQuotationComponent.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: SaleQuotationComponentDto): Promise { + const { unitPrice, name, componentSpecification } = dto; + const isExist = await this.saleQuotationComponentRepository.exist({ + where: { + unitPrice, + name, + componentSpecification + } + }); + if (isExist) { + throw new BusinessException(ErrorEnum.SALE_QUOTATION_COMPONENT_DUPLICATED); + } + await this.saleQuotationComponentRepository.insert(dto); + } + + /** + * 更新 + */ + async update( + id: number, + { fileIds, ...data }: Partial + ): Promise { + await this.entityManager.transaction(async manager => { + const beUpdateEntity = await manager.findOne(SaleQuotationComponentEntity, { + where: { + id + } + }); + const { unitPrice, name, componentSpecification } = beUpdateEntity; + const isExist = await this.saleQuotationComponentRepository.exist({ + where: { + id: Not(id), + unitPrice, + name, + componentSpecification + } + }); + if (isExist) { + throw new BusinessException(ErrorEnum.SALE_QUOTATION_COMPONENT_DUPLICATED); + } + await manager.update(SaleQuotationComponentEntity, id, { + ...data + }); + if (fileIds?.length) { + const count = await this.storageRepository + .createQueryBuilder('storage') + .where('storage.id in(:fileIds)', { fileIds }) + .getCount(); + if (count !== fileIds?.length) { + throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); + } + // 附件要批量插入 + await manager + .createQueryBuilder() + .relation(SaleQuotationComponentEntity, 'files') + .of(id) + .add(fileIds); + } + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 比较重要,做逻辑删除 + await this.saleQuotationComponentRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个信息 + */ + async info(id: number) { + const info = await this.buildSearchQuery() + .where({ + id + }) + .andWhere('saleQuotationComponent.isDelete = 0') + .getOne(); + return info; + } + + /** + * 解除附件关联 + * @param id 实体ID + * @param fileIds 附件ID + */ + async unlinkAttachments(id: number, fileIds: number[]) { + await this.entityManager.transaction(async manager => { + const saleQuotationComponent = await this.saleQuotationComponentRepository + .createQueryBuilder('saleQuotationComponent') + .leftJoinAndSelect('saleQuotationComponent.files', 'files') + .where('saleQuotationComponent.id = :id', { id }) + .getOne(); + const linkedFiles = saleQuotationComponent.files + .map(item => item.id) + .filter(item => !fileIds.includes(item)); + // 附件要批量更新 + await manager + .createQueryBuilder() + .relation(SaleQuotationComponentEntity, 'files') + .of(id) + .addAndRemove(linkedFiles, saleQuotationComponent.files); + }); + } +} diff --git a/src/modules/sale_quotation/group/sale_quotation_group.controller.ts b/src/modules/sale_quotation/group/sale_quotation_group.controller.ts new file mode 100644 index 0000000..ebf1e2b --- /dev/null +++ b/src/modules/sale_quotation/group/sale_quotation_group.controller.ts @@ -0,0 +1,63 @@ +import { Body, Controller, Get, Query, Put, Delete, Post } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { + SaleQuotationGroupDto, + SaleQuotationGroupQueryDto, + SaleQuotationGroupUpdateDto +} from './sale_quotation_group.dto'; +import { SaleQuotationGroupService } from './sale_quotation_group.service'; +export const permissions = definePermission('sale_quotation:sale_quotation_group', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('SaleQuotationGroup - 报价分组') +@ApiSecurityAuth() +@Controller('sale_quotation_group') +export class SaleQuotationGroupController { + constructor(private saleQuotationGroupService: SaleQuotationGroupService) {} + + @Get() + @ApiOperation({ summary: '分页获取报价分组列表' }) + @ApiResult({ type: [SaleQuotationGroupDto], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: SaleQuotationGroupQueryDto) { + return this.saleQuotationGroupService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取报价分组信息' }) + @ApiResult({ type: SaleQuotationGroupDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.saleQuotationGroupService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增报价分组' }) + @Perm(permissions.CREATE) + async create(@Body() dto: SaleQuotationGroupDto): Promise { + await this.saleQuotationGroupService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新报价分组' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: SaleQuotationGroupUpdateDto): Promise { + await this.saleQuotationGroupService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除报价分组' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.saleQuotationGroupService.delete(id); + } +} diff --git a/src/modules/sale_quotation/group/sale_quotation_group.dto.ts b/src/modules/sale_quotation/group/sale_quotation_group.dto.ts new file mode 100644 index 0000000..9b5d6f8 --- /dev/null +++ b/src/modules/sale_quotation/group/sale_quotation_group.dto.ts @@ -0,0 +1,27 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { IsUnique } from '~/shared/database/constraints/unique.constraint'; +import { SaleQuotationGroupEntity } from './sale_quotation_group.entity'; + +export class SaleQuotationGroupDto { + @ApiProperty({ description: '报价分组名称' }) + @IsUnique(SaleQuotationGroupEntity, { message: '已存在同名报价分组' }) + @IsString() + name: string; +} + +export class SaleQuotationGroupUpdateDto extends PartialType(SaleQuotationGroupDto) {} + +export class ComapnyCreateDto extends PartialType(SaleQuotationGroupDto) {} + +export class SaleQuotationGroupQueryDto extends IntersectionType( + PagerDto, + PartialType(SaleQuotationGroupDto) +) { + @ApiProperty({ description: '报价分组名称' }) + @IsOptional() + @IsString() + name: string; +} diff --git a/src/modules/sale_quotation/group/sale_quotation_group.entity.ts b/src/modules/sale_quotation/group/sale_quotation_group.entity.ts new file mode 100644 index 0000000..4f4038e --- /dev/null +++ b/src/modules/sale_quotation/group/sale_quotation_group.entity.ts @@ -0,0 +1,33 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, Relation } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { SaleQuotationComponentEntity } from '../component/sale_quotation_component.entity'; + +/** + * 报价分组实体类 + */ +@Entity({ name: 'sale_quotation_group' }) +export class SaleQuotationGroupEntity extends CommonEntity { + @Column({ + name: 'name', + type: 'varchar', + unique: true, + length: 255, + comment: '报价分组名称' + }) + @ApiProperty({ description: '报价分组名称' }) + name: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; + + @ManyToMany(() => SaleQuotationComponentEntity, component => component.groups) + @JoinTable({ + name: 'sale_quotation_group_component', + joinColumn: { name: 'group_id', referencedColumnName: 'id' }, + inverseJoinColumn: { name: 'component_id', referencedColumnName: 'id' } + }) + components: Relation; +} diff --git a/src/modules/sale_quotation/group/sale_quotation_group.module.ts b/src/modules/sale_quotation/group/sale_quotation_group.module.ts new file mode 100644 index 0000000..a5cc146 --- /dev/null +++ b/src/modules/sale_quotation/group/sale_quotation_group.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { StorageModule } from '~/modules/tools/storage/storage.module'; +import { DatabaseModule } from '~/shared/database/database.module'; +import { SaleQuotationGroupService } from './sale_quotation_group.service'; +import { SaleQuotationGroupController } from './sale_quotation_group.controller'; +import { SaleQuotationGroupEntity } from './sale_quotation_group.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([SaleQuotationGroupEntity]), DatabaseModule], + controllers: [SaleQuotationGroupController], + providers: [SaleQuotationGroupService] +}) +export class SaleQuotationGroupModule {} diff --git a/src/modules/sale_quotation/group/sale_quotation_group.service.ts b/src/modules/sale_quotation/group/sale_quotation_group.service.ts new file mode 100644 index 0000000..25fc1af --- /dev/null +++ b/src/modules/sale_quotation/group/sale_quotation_group.service.ts @@ -0,0 +1,83 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { EntityManager, Repository } from 'typeorm'; +import { Pagination } from '~/helper/paginate/pagination'; +import { paginate } from '~/helper/paginate'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { fieldSearch } from '~/shared/database/field-search'; +import { SaleQuotationGroupEntity } from './sale_quotation_group.entity'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { + SaleQuotationGroupDto, + SaleQuotationGroupQueryDto, + SaleQuotationGroupUpdateDto +} from './sale_quotation_group.dto'; + +@Injectable() +export class SaleQuotationGroupService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(SaleQuotationGroupEntity) + private saleQuotationGroupRepository: Repository + ) {} + + /** + * 分页查询所有 + */ + async findAll({ + page, + pageSize, + ...fields + }: SaleQuotationGroupQueryDto): Promise> { + const queryBuilder = this.saleQuotationGroupRepository + .createQueryBuilder('saleQuotationGroup') + .where(fieldSearch(fields)) + .andWhere('saleQuotationGroup.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: SaleQuotationGroupDto): Promise { + await this.saleQuotationGroupRepository.insert(dto); + } + + /** + * 更新 + */ + async update(id: number, data: Partial): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(SaleQuotationGroupEntity, id, { + ...data + }); + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 比较重要,做逻辑删除 + await this.saleQuotationGroupRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个信息 + */ + async info(id: number) { + const info = await this.saleQuotationGroupRepository + .createQueryBuilder('saleQuotationGroup') + .where({ + id + }) + .andWhere('saleQuotationGroup.isDelete = 0') + .getOne(); + return info; + } +} diff --git a/src/modules/sale_quotation/sale_quotation.module.ts b/src/modules/sale_quotation/sale_quotation.module.ts new file mode 100644 index 0000000..a928e8e --- /dev/null +++ b/src/modules/sale_quotation/sale_quotation.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { SaleQuotationGroupModule } from './group/sale_quotation_group.module'; +import { SaleQuotationTemplateModule } from './template/sale_quotation_template.module'; +import { SaleQuotationComponentModule } from './component/sale_quotation_component.module'; +import { RouterModule } from '@nestjs/core'; +const modules = [ + SaleQuotationComponentModule, + SaleQuotationGroupModule, + SaleQuotationTemplateModule +]; +@Module({ + imports: [ + ...modules, + RouterModule.register([ + { + path: 'sale_quotation', + module: SaleQuotationModule, + children: [...modules] + } + ]) + ], + controllers: [], + providers: [] +}) +export class SaleQuotationModule {} diff --git a/src/modules/sale_quotation/template/sale_quotation_template.controller.ts b/src/modules/sale_quotation/template/sale_quotation_template.controller.ts new file mode 100644 index 0000000..18a9d1a --- /dev/null +++ b/src/modules/sale_quotation/template/sale_quotation_template.controller.ts @@ -0,0 +1,63 @@ +import { Body, Controller, Get, Query, Put, Delete, Post } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { + SaleQuotationTemplateDto, + SaleQuotationTemplateQueryDto, + SaleQuotationTemplateUpdateDto +} from './sale_quotation_template.dto'; +import { SaleQuotationTemplateService } from './sale_quotation_template.service'; +export const permissions = definePermission('sale_quotation:sale_quotation_template', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('SaleQuotationTemplate - 报价模板') +@ApiSecurityAuth() +@Controller('sale_quotation_template') +export class SaleQuotationTemplateController { + constructor(private saleQuotationTemplateService: SaleQuotationTemplateService) {} + + @Get() + @ApiOperation({ summary: '分页获取报价模板列表' }) + @ApiResult({ type: [SaleQuotationTemplateDto], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: SaleQuotationTemplateQueryDto) { + return this.saleQuotationTemplateService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取报价模板信息' }) + @ApiResult({ type: SaleQuotationTemplateDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.saleQuotationTemplateService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增报价模板' }) + @Perm(permissions.CREATE) + async create(@Body() dto: SaleQuotationTemplateDto): Promise { + await this.saleQuotationTemplateService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新报价模板' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: SaleQuotationTemplateUpdateDto): Promise { + await this.saleQuotationTemplateService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除报价模板' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.saleQuotationTemplateService.delete(id); + } +} diff --git a/src/modules/sale_quotation/template/sale_quotation_template.dto.ts b/src/modules/sale_quotation/template/sale_quotation_template.dto.ts new file mode 100644 index 0000000..b916fd7 --- /dev/null +++ b/src/modules/sale_quotation/template/sale_quotation_template.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; +import { IsUnique } from '~/shared/database/constraints/unique.constraint'; +import { SaleQuotationTemplateEntity } from './sale_quotation_template.entity'; + +export class SaleQuotationTemplateDto { + @ApiProperty({ description: '报价模板名称' }) + @IsString() + name: string; + + @ApiProperty({ description: '报价模板' }) + @IsOptional() + template: JSON; +} + +export class SaleQuotationTemplateUpdateDto extends PartialType(SaleQuotationTemplateDto) {} + +export class ComapnyCreateDto extends PartialType(SaleQuotationTemplateDto) {} + +export class SaleQuotationTemplateQueryDto extends IntersectionType( + PagerDto, + PartialType(SaleQuotationTemplateDto) +) { + @ApiProperty({ description: '报价模板名称' }) + @IsOptional() + @IsString() + name: string; +} diff --git a/src/modules/sale_quotation/template/sale_quotation_template.entity.ts b/src/modules/sale_quotation/template/sale_quotation_template.entity.ts new file mode 100644 index 0000000..9e2aabe --- /dev/null +++ b/src/modules/sale_quotation/template/sale_quotation_template.entity.ts @@ -0,0 +1,32 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; + +/** + * 报价模板实体类 + */ +@Entity({ name: 'sale_quotation_template' }) +export class SaleQuotationTemplateEntity extends CommonEntity { + @Column({ + name: 'name', + type: 'varchar', + unique: true, + length: 255, + comment: '报价模板名称' + }) + @ApiProperty({ description: '报价模板名称' }) + name: string; + + @Column({ + name: 'template', + type: 'json', + comment: '报价模板', + nullable: true + }) + @ApiProperty({ description: '报价模板(JSON)' }) + template: JSON; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; +} diff --git a/src/modules/sale_quotation/template/sale_quotation_template.module.ts b/src/modules/sale_quotation/template/sale_quotation_template.module.ts new file mode 100644 index 0000000..2759ef7 --- /dev/null +++ b/src/modules/sale_quotation/template/sale_quotation_template.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { StorageModule } from '~/modules/tools/storage/storage.module'; +import { DatabaseModule } from '~/shared/database/database.module'; +import { SaleQuotationTemplateService } from './sale_quotation_template.service'; +import { SaleQuotationTemplateController } from './sale_quotation_template.controller'; +import { SaleQuotationTemplateEntity } from './sale_quotation_template.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([SaleQuotationTemplateEntity]), DatabaseModule], + controllers: [SaleQuotationTemplateController], + providers: [SaleQuotationTemplateService] +}) +export class SaleQuotationTemplateModule {} diff --git a/src/modules/sale_quotation/template/sale_quotation_template.service.ts b/src/modules/sale_quotation/template/sale_quotation_template.service.ts new file mode 100644 index 0000000..5ff5633 --- /dev/null +++ b/src/modules/sale_quotation/template/sale_quotation_template.service.ts @@ -0,0 +1,83 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { EntityManager, Repository } from 'typeorm'; +import { Pagination } from '~/helper/paginate/pagination'; +import { paginate } from '~/helper/paginate'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { fieldSearch } from '~/shared/database/field-search'; +import { SaleQuotationTemplateEntity } from './sale_quotation_template.entity'; +import { Storage } from '~/modules/tools/storage/storage.entity'; +import { + SaleQuotationTemplateDto, + SaleQuotationTemplateQueryDto, + SaleQuotationTemplateUpdateDto +} from './sale_quotation_template.dto'; + +@Injectable() +export class SaleQuotationTemplateService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(SaleQuotationTemplateEntity) + private saleQuotationTemplateRepository: Repository + ) {} + + /** + * 分页查询所有 + */ + async findAll({ + page, + pageSize, + ...fields + }: SaleQuotationTemplateQueryDto): Promise> { + const queryBuilder = this.saleQuotationTemplateRepository + .createQueryBuilder('saleQuotationTemplate') + .where(fieldSearch(fields)) + .andWhere('saleQuotationTemplate.isDelete = 0').addOrderBy('saleQuotationTemplate.createdAt', 'DESC'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create(dto: SaleQuotationTemplateDto): Promise { + await this.saleQuotationTemplateRepository.insert(dto); + } + + /** + * 更新 + */ + async update(id: number, data: Partial): Promise { + await this.entityManager.transaction(async manager => { + await manager.update(SaleQuotationTemplateEntity, id, { + ...data + }); + }); + } + + /** + * 删除 + */ + async delete(id: number): Promise { + // 比较重要,做逻辑删除 + await this.saleQuotationTemplateRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个信息 + */ + async info(id: number) { + const info = await this.saleQuotationTemplateRepository + .createQueryBuilder('saleQuotationTemplate') + .where({ + id + }) + .andWhere('saleQuotationTemplate.isDelete = 0') + .getOne(); + return info; + } +} diff --git a/src/modules/tools/storage/storage.entity.ts b/src/modules/tools/storage/storage.entity.ts index 6929c0e..ecdbad9 100644 --- a/src/modules/tools/storage/storage.entity.ts +++ b/src/modules/tools/storage/storage.entity.ts @@ -8,6 +8,7 @@ import { MaterialsInOutEntity } from '~/modules/materials_inventory/in_out/mater import { MaterialsInventoryEntity } from '~/modules/materials_inventory/materials_inventory.entity'; import { ProductEntity } from '~/modules/product/product.entity'; import { ProjectEntity } from '~/modules/project/project.entity'; +import { SaleQuotationComponentEntity } from '~/modules/sale_quotation/component/sale_quotation_component.entity'; import { VehicleUsageEntity } from '~/modules/vehicle_usage/vehicle_usage.entity'; @Entity({ name: 'tool_storage' }) @@ -77,4 +78,9 @@ export class Storage extends CommonEntity { @ApiHideProperty() @ManyToMany(() => VehicleUsageEntity, vu => vu.files) vehicleUsage: Relation; + + @ApiHideProperty() + @ManyToMany(() => SaleQuotationComponentEntity, component => component.files) + saleQuotationComponents: Relation; + } From aaba8dc173a120608bc5fed802ccc78883c13cae Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Mon, 15 Apr 2024 13:50:13 +0800 Subject: [PATCH 59/64] feat: sale_quotation --- src/constants/error-code.constant.ts | 4 +- .../group/sale_quotation_group.entity.ts | 2 + .../sale_quotation.controller.ts | 24 ++++ .../sale_quotation/sale_quotation.module.ts | 11 +- .../sale_quotation/sale_quotation.service.ts | 111 ++++++++++++++++++ .../sale_quotation_template.module.ts | 2 +- .../sale_quotation_template.service.ts | 17 ++- 7 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 src/modules/sale_quotation/sale_quotation.controller.ts create mode 100644 src/modules/sale_quotation/sale_quotation.service.ts diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index fd27d49..27b8e11 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -64,5 +64,7 @@ export enum ErrorEnum { MATERIALS_IN_OUT_UNIT_PRICE_MUST_ZERO_WHEN_MODIFIED = '1411:只能修改初始单价为0的入库记录。 若有疑问,请联系管理员', // SaleQuotation - SALE_QUOTATION_COMPONENT_DUPLICATED = '1412:存在名称,价格,规格都相同的配件,请检查是否重复录入' + SALE_QUOTATION_COMPONENT_DUPLICATED = '1412:存在名称,价格,规格都相同的配件,请检查是否重复录入', + SALE_QUOTATION_TEMPLATE_NAME_DUPLICATE = '1413:模板名已存在' + } diff --git a/src/modules/sale_quotation/group/sale_quotation_group.entity.ts b/src/modules/sale_quotation/group/sale_quotation_group.entity.ts index 4f4038e..6d8276b 100644 --- a/src/modules/sale_quotation/group/sale_quotation_group.entity.ts +++ b/src/modules/sale_quotation/group/sale_quotation_group.entity.ts @@ -23,6 +23,8 @@ export class SaleQuotationGroupEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + items:any[]; + @ManyToMany(() => SaleQuotationComponentEntity, component => component.groups) @JoinTable({ name: 'sale_quotation_group_component', diff --git a/src/modules/sale_quotation/sale_quotation.controller.ts b/src/modules/sale_quotation/sale_quotation.controller.ts new file mode 100644 index 0000000..9e16122 --- /dev/null +++ b/src/modules/sale_quotation/sale_quotation.controller.ts @@ -0,0 +1,24 @@ +import { Controller, Get, Query, Res } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { Perm, definePermission } from '~/modules/auth/decorators/permission.decorator'; +import { FastifyReply } from 'fastify'; +import { SaleQuotationService } from './sale_quotation.service'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +export const permissions = definePermission('sale_quotation:sale_quotation', { + EXPORT: 'export' +} as const); + +@ApiTags('SaleQuotation - 报价模块') +@ApiSecurityAuth() +@Controller('sale_quotation') +export class SaleQuotationController { + constructor(private saleQuotationService: SaleQuotationService) {} + + @Get('export/:id') + @ApiOperation({ summary: '导出报价配置明细' }) + @Perm(permissions.EXPORT) + async export(@IdParam() id: number, @Res() res: FastifyReply): Promise { + await this.saleQuotationService.export(id, res); + } +} diff --git a/src/modules/sale_quotation/sale_quotation.module.ts b/src/modules/sale_quotation/sale_quotation.module.ts index a928e8e..f997cac 100644 --- a/src/modules/sale_quotation/sale_quotation.module.ts +++ b/src/modules/sale_quotation/sale_quotation.module.ts @@ -3,6 +3,12 @@ import { SaleQuotationGroupModule } from './group/sale_quotation_group.module'; import { SaleQuotationTemplateModule } from './template/sale_quotation_template.module'; import { SaleQuotationComponentModule } from './component/sale_quotation_component.module'; import { RouterModule } from '@nestjs/core'; +import { SaleQuotationController } from './sale_quotation.controller'; +import { SaleQuotationService } from './sale_quotation.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SaleQuotationTemplateEntity } from './template/sale_quotation_template.entity'; +import { SaleQuotationGroupEntity } from './group/sale_quotation_group.entity'; +import { SaleQuotationComponentEntity } from './component/sale_quotation_component.entity'; const modules = [ SaleQuotationComponentModule, SaleQuotationGroupModule, @@ -11,6 +17,7 @@ const modules = [ @Module({ imports: [ ...modules, + TypeOrmModule.forFeature([SaleQuotationTemplateEntity, SaleQuotationGroupEntity,SaleQuotationComponentEntity]), RouterModule.register([ { path: 'sale_quotation', @@ -19,7 +26,7 @@ const modules = [ } ]) ], - controllers: [], - providers: [] + controllers: [SaleQuotationController], + providers: [SaleQuotationService] }) export class SaleQuotationModule {} diff --git a/src/modules/sale_quotation/sale_quotation.service.ts b/src/modules/sale_quotation/sale_quotation.service.ts new file mode 100644 index 0000000..d9aca58 --- /dev/null +++ b/src/modules/sale_quotation/sale_quotation.service.ts @@ -0,0 +1,111 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { EntityManager, Repository } from 'typeorm'; +import { SaleQuotationTemplateEntity } from './template/sale_quotation_template.entity'; +import { FastifyReply } from 'fastify'; +import * as ExcelJS from 'exceljs'; +import { SaleQuotationGroupEntity } from './group/sale_quotation_group.entity'; +@Injectable() +export class SaleQuotationService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(SaleQuotationTemplateEntity) + private saleQuotationTemplateRepository: Repository + ) {} + + /** + * 导出报价配置明细 + */ + async export(templateId: number, res: FastifyReply): Promise { + const ROW_HEIGHT = 20; + const HEADER_FONT_SIZE = 18; + const workbook = new ExcelJS.Workbook(); + let data = await this.saleQuotationTemplateRepository.findOneBy({ id: templateId }); + const template: JSON = data.template; + const sheet = workbook.addWorksheet(data.name); + if (template != null) { + sheet.mergeCells('A1:H1'); + // 设置标题 + sheet.getCell('A1').value = '电液控部分配置明细'; + // 设置副标题 + sheet.mergeCells('A2:F2'); + sheet.getCell('A2').value = '支架电液控系统配置明细(中间131架+过渡架4架)'; + sheet.getCell(`G2`).value = ''; + sheet.getCell(`H2`).value = ''; + const groups = template['data'] as SaleQuotationGroupEntity[]; + const headers = [ + '过渡', + '名称', + '规格、型号及说明', + '单位', + '数量', + '备 注', + '单价', + '总价' + ]; + + sheet.addRow(headers); + for (let i = 1; i <= 8; i++) { + sheet.getCell(`${String.fromCharCode(64 + i)}1`).style.font = { bold: true }; + sheet.getCell(`${String.fromCharCode(64 + i)}2`).style.font = { bold: true }; + sheet.getCell(`${String.fromCharCode(64 + i)}3`).style.font = { bold: true }; + } + + let rowIndex = 3; + for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) { + rowIndex++; + const group = groups[groupIndex]; + sheet.mergeCells(`A${rowIndex}:F${rowIndex}`); + sheet.getCell(`A${rowIndex}`).value = group.name; + sheet.getCell(`A${rowIndex}`).style.font = { bold: true }; + sheet.getCell(`G${rowIndex}`).value = ''; + sheet.getCell(`H${rowIndex}`).value = ''; + for (let componentIndex = 0; componentIndex < group.items.length; componentIndex++) { + const item = group.items[componentIndex]; + rowIndex++; + sheet.addRow([ + `${componentIndex + 1}`, + item.name ?? '', + item.componentSpecification ?? '', + item.unit ?? '', + item.quantity ?? '', + item.remark ?? '', + item.unitPrice ?? '', + item.amount ?? '' + ]); + } + } + sheet.getCell(`I${rowIndex - 1}`).value = '总价'; + sheet.getCell(`I${rowIndex - 1}`).style.font = { bold: true }; + sheet.getCell(`I${rowIndex}`).value = template['totalPrice']; + } + sheet.eachRow((row, index) => { + if (index >= 0) { + row.alignment = { vertical: 'middle', horizontal: 'center' }; + row.height = ROW_HEIGHT; + row.eachCell(cell => { + cell.border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + }); + } + }); + const columnWidthMap = { A: 8, B: 30, C: 25, F: 20 }; + sheet.columns.forEach((column, index: number) => { + column.width = columnWidthMap[String.fromCharCode(65 + index)] ?? 10; // Minimum width of 10 + }); + + //读取buffer进行传输 + const buffer = await workbook.xlsx.writeBuffer(); + res + .header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + .header( + 'Content-Disposition', + `attachment; filename="${encodeURIComponent('导出_excel' + new Date().getTime() + '.xls')}"` + ) + .send(buffer); + } +} diff --git a/src/modules/sale_quotation/template/sale_quotation_template.module.ts b/src/modules/sale_quotation/template/sale_quotation_template.module.ts index 2759ef7..01c4c03 100644 --- a/src/modules/sale_quotation/template/sale_quotation_template.module.ts +++ b/src/modules/sale_quotation/template/sale_quotation_template.module.ts @@ -9,6 +9,6 @@ import { SaleQuotationTemplateEntity } from './sale_quotation_template.entity'; @Module({ imports: [TypeOrmModule.forFeature([SaleQuotationTemplateEntity]), DatabaseModule], controllers: [SaleQuotationTemplateController], - providers: [SaleQuotationTemplateService] + providers: [SaleQuotationTemplateService], }) export class SaleQuotationTemplateModule {} diff --git a/src/modules/sale_quotation/template/sale_quotation_template.service.ts b/src/modules/sale_quotation/template/sale_quotation_template.service.ts index 5ff5633..dd7a83f 100644 --- a/src/modules/sale_quotation/template/sale_quotation_template.service.ts +++ b/src/modules/sale_quotation/template/sale_quotation_template.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; -import { EntityManager, Repository } from 'typeorm'; +import { EntityManager, Not, Repository } from 'typeorm'; import { Pagination } from '~/helper/paginate/pagination'; import { paginate } from '~/helper/paginate'; import { BusinessException } from '~/common/exceptions/biz.exception'; @@ -33,7 +33,8 @@ export class SaleQuotationTemplateService { const queryBuilder = this.saleQuotationTemplateRepository .createQueryBuilder('saleQuotationTemplate') .where(fieldSearch(fields)) - .andWhere('saleQuotationTemplate.isDelete = 0').addOrderBy('saleQuotationTemplate.createdAt', 'DESC'); + .andWhere('saleQuotationTemplate.isDelete = 0') + .addOrderBy('saleQuotationTemplate.createdAt', 'DESC'); return paginate(queryBuilder, { page, @@ -45,6 +46,12 @@ export class SaleQuotationTemplateService { * 新增 */ async create(dto: SaleQuotationTemplateDto): Promise { + const isDuplicated = await this.saleQuotationTemplateRepository.exist({ + where: { name: dto.name } + }); + if (isDuplicated) { + throw new BusinessException(ErrorEnum.SALE_QUOTATION_TEMPLATE_NAME_DUPLICATE); + } await this.saleQuotationTemplateRepository.insert(dto); } @@ -53,6 +60,12 @@ export class SaleQuotationTemplateService { */ async update(id: number, data: Partial): Promise { await this.entityManager.transaction(async manager => { + const isDuplicated = await this.saleQuotationTemplateRepository.exist({ + where: { name: data.name, id: Not(id) } + }); + if (isDuplicated) { + throw new BusinessException(ErrorEnum.SALE_QUOTATION_TEMPLATE_NAME_DUPLICATE); + } await manager.update(SaleQuotationTemplateEntity, id, { ...data }); From 8ad1a48c60dd1bc59dd5cb20a0e41b395305b9d6 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Tue, 16 Apr 2024 13:02:41 +0800 Subject: [PATCH 60/64] feat: search dept --- Dockerfile | 4 +++- src/modules/user/user.service.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f4acb8d..fa2b9dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,16 +28,18 @@ RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ # see https://pnpm.io/docker FROM base AS prod-deps +RUN pnpm config set registry https://registry.npmmirror.com RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile FROM base AS build +RUN pnpm config set registry https://registry.npmmirror.com RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm run build # mirror acceleration # RUN npm config set registry https://registry.npmmirror.com -# RUN pnpm config set registry https://registry.npmmirror.com + # RUN npm config rm proxy && npm config rm https-proxy FROM base diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index d3f7287..16842a7 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -71,6 +71,7 @@ export class UserService { const user: UserEntity = await this.userRepository .createQueryBuilder('user') .leftJoinAndSelect('user.roles', 'role') + .leftJoinAndSelect('user.dept', 'dept') .where(`user.id = :uid`, { uid }) .getOne(); From cd32cc1ac034884e0f8ab8c3741266211b6dc289 Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Tue, 16 Apr 2024 14:06:02 +0800 Subject: [PATCH 61/64] feat: Domain develop --- src/app.module.ts | 5 +- src/common/decorators/domain.decorator.ts | 18 ++++ src/common/decorators/http.decorator.ts | 1 + src/constants/error-code.constant.ts | 4 +- src/modules/auth/auth.controller.ts | 7 +- .../auth/controllers/account.controller.ts | 1 + src/modules/company/company.controller.ts | 11 ++- src/modules/company/company.dto.ts | 6 +- src/modules/company/company.entity.ts | 5 + src/modules/company/company.service.ts | 7 +- src/modules/contract/contract.controller.ts | 15 +-- src/modules/contract/contract.dto.ts | 9 +- src/modules/contract/contract.entity.ts | 5 + src/modules/contract/contract.service.ts | 1 + src/modules/domian/domain.controller.ts | 69 ++++++++++++++ src/modules/domian/domain.dto.ts | 12 +++ src/modules/domian/domain.entity.ts | 15 +++ src/modules/domian/domain.module.ts | 12 +++ src/modules/domian/domain.service.ts | 95 +++++++++++++++++++ .../in_out/materials_in_out.controller.ts | 9 +- .../in_out/materials_in_out.dto.ts | 8 +- .../in_out/materials_in_out.entity.ts | 5 + .../materials_inventory.controller.ts | 4 +- .../materials_inventory.dto.ts | 8 +- .../materials_inventory.entity.ts | 7 +- .../materials_inventory.service.ts | 2 +- src/modules/product/product.controller.ts | 9 +- src/modules/product/product.dto.ts | 8 +- src/modules/product/product.entity.ts | 5 + src/modules/user/user.controller.ts | 5 +- src/modules/user/user.entity.ts | 4 + src/modules/user/user.service.ts | 20 ++-- src/shared/database/field-search/index.ts | 3 +- 33 files changed, 334 insertions(+), 61 deletions(-) create mode 100644 src/common/decorators/domain.decorator.ts create mode 100644 src/modules/domian/domain.controller.ts create mode 100644 src/modules/domian/domain.dto.ts create mode 100644 src/modules/domian/domain.entity.ts create mode 100644 src/modules/domian/domain.module.ts create mode 100644 src/modules/domian/domain.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 992ae61..53348e8 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -31,6 +31,7 @@ import { ProductModule } from './modules/product/product.module'; import { ProjectModule } from './modules/project/project.module'; import { VehicleUsageModule } from './modules/vehicle_usage/vehicle_usage.module'; import { SaleQuotationModule } from './modules/sale_quotation/sale_quotation.module'; +import { DomainModule } from './modules/domian/domain.module'; @Module({ imports: [ @@ -77,7 +78,9 @@ import { SaleQuotationModule } from './modules/sale_quotation/sale_quotation.mod VehicleUsageModule, //报价管理 - SaleQuotationModule + SaleQuotationModule, + //域 + DomainModule ], providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter }, diff --git a/src/common/decorators/domain.decorator.ts b/src/common/decorators/domain.decorator.ts new file mode 100644 index 0000000..bcd02c2 --- /dev/null +++ b/src/common/decorators/domain.decorator.ts @@ -0,0 +1,18 @@ +import { ExecutionContext, createParamDecorator } from '@nestjs/common'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional } from 'class-validator'; +import type { FastifyRequest } from 'fastify'; +/** + * 当前域 + */ +export const Domain = createParamDecorator((_, context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + return request.headers['sk-domain']; +}); + +export type SkDomain = number; +export class DomainType { + @ApiProperty({ description: '所属域' }) + @IsOptional() + domain: SkDomain; +} diff --git a/src/common/decorators/http.decorator.ts b/src/common/decorators/http.decorator.ts index f90d311..c94d366 100644 --- a/src/common/decorators/http.decorator.ts +++ b/src/common/decorators/http.decorator.ts @@ -5,6 +5,7 @@ import type { FastifyRequest } from 'fastify'; import { getIp, getIsMobile } from '~/utils/ip.util'; + /** * 快速获取IP */ diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index 27b8e11..61b5117 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -65,6 +65,8 @@ export enum ErrorEnum { // SaleQuotation SALE_QUOTATION_COMPONENT_DUPLICATED = '1412:存在名称,价格,规格都相同的配件,请检查是否重复录入', - SALE_QUOTATION_TEMPLATE_NAME_DUPLICATE = '1413:模板名已存在' + SALE_QUOTATION_TEMPLATE_NAME_DUPLICATE = '1413:模板名已存在', + //domain + DOMAIN_TITLE_DUPLICATE = '1414:域标题已存在' } diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 4e2a593..66b5b59 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -14,6 +14,7 @@ import { LoginToken } from './models/auth.model'; import { CaptchaService } from './services/captcha.service'; import { AuthUser } from './decorators/auth-user.decorator'; import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { Domain, SkDomain } from '~/common/decorators/domain.decorator'; @ApiTags('Auth - 认证模块') @UseGuards(LocalGuard) @@ -35,7 +36,7 @@ export class AuthController { @IsMobile() isMobile: boolean, @Headers('user-agent') ua: string ): Promise { - if(!isMobile){ + if (!isMobile) { await this.captchaService.checkImgCaptcha(dto.captchaId, dto.verifyCode); } const token = await this.authService.login(dto.username, dto.password, ip, ua); @@ -53,7 +54,7 @@ export class AuthController { @Post('register') @ApiOperation({ summary: '注册' }) - async register(@Body() dto: RegisterDto): Promise { - await this.userService.register(dto); + async register(@Domain() domain: SkDomain, @Body() dto: RegisterDto): Promise { + await this.userService.register(dto, domain); } } diff --git a/src/modules/auth/controllers/account.controller.ts b/src/modules/auth/controllers/account.controller.ts index c11feae..65c0661 100644 --- a/src/modules/auth/controllers/account.controller.ts +++ b/src/modules/auth/controllers/account.controller.ts @@ -16,6 +16,7 @@ import { AccountMenus, AccountUpdateDto } from '../dto/account.dto'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; import { IsMobile } from '~/common/decorators/http.decorator'; import { ResourceDeviceEnum } from '~/constants/enum'; +import { Domain } from '~/common/decorators/domain.decorator'; @ApiTags('Account - 账户模块') @ApiSecurityAuth() diff --git a/src/modules/company/company.controller.ts b/src/modules/company/company.controller.ts index 0333d14..1ad2602 100644 --- a/src/modules/company/company.controller.ts +++ b/src/modules/company/company.controller.ts @@ -16,6 +16,7 @@ import { ApiResult } from '~/common/decorators/api-result.decorator'; import { CompanyEntity } from './company.entity'; import { CompanyDto, CompanyQueryDto, CompanyUpdateDto } from './company.dto'; import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Domain, SkDomain } from '~/common/decorators/domain.decorator'; export const permissions = definePermission('app:company', { LIST: 'list', CREATE: 'create', @@ -24,7 +25,7 @@ export const permissions = definePermission('app:company', { DELETE: 'delete' } as const); -@ApiTags('Company - 公司') +@ApiTags('Company - 公司') @ApiSecurityAuth() @Controller('company') export class CompanyController { @@ -34,8 +35,8 @@ export class CompanyController { @ApiOperation({ summary: '获取公司列表' }) @ApiResult({ type: [CompanyEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: CompanyQueryDto) { - return this.companyService.findAll(dto); + async list(@Domain() domain: SkDomain, @Query() dto: CompanyQueryDto) { + return this.companyService.findAll({ ...dto, domain }); } @Get(':id') @@ -49,8 +50,8 @@ export class CompanyController { @Post() @ApiOperation({ summary: '新增公司' }) @Perm(permissions.CREATE) - async create(@Body() dto: CompanyDto): Promise { - await this.companyService.create(dto); + async create(@Domain() domain: SkDomain, @Body() dto: CompanyDto): Promise { + await this.companyService.create({ ...dto, domain }); } @Put(':id') diff --git a/src/modules/company/company.dto.ts b/src/modules/company/company.dto.ts index 6b39ea5..6a9f350 100644 --- a/src/modules/company/company.dto.ts +++ b/src/modules/company/company.dto.ts @@ -15,8 +15,9 @@ import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; import { IsUnique } from '~/shared/database/constraints/unique.constraint'; import { CompanyEntity } from './company.entity'; +import { DomainType, SkDomain } from '~/common/decorators/domain.decorator'; -export class CompanyDto { +export class CompanyDto extends DomainType { @ApiProperty({ description: '公司名称' }) @IsUnique(CompanyEntity, { message: '已存在同名公司' }) @IsString() @@ -42,7 +43,8 @@ export class ComapnyCreateDto extends PartialType(CompanyDto) { export class CompanyQueryDto extends IntersectionType( PagerDto, - PartialType(CompanyDto) + PartialType(CompanyDto), + DomainType ) { @ApiProperty({ description: '公司名称' }) @IsOptional() diff --git a/src/modules/company/company.entity.ts b/src/modules/company/company.entity.ts index 6608cbc..2f75103 100644 --- a/src/modules/company/company.entity.ts +++ b/src/modules/company/company.entity.ts @@ -3,6 +3,7 @@ import { Column, Entity, JoinTable, ManyToMany, OneToMany, Relation } from 'type import { CommonEntity } from '~/common/entity/common.entity'; import { Storage } from '../tools/storage/storage.entity'; import { ProductEntity } from '../product/product.entity'; +import { SkDomain } from '~/common/decorators/domain.decorator'; @Entity({ name: 'company' }) export class CompanyEntity extends CommonEntity { @@ -20,6 +21,10 @@ export class CompanyEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + @Column({ type: 'int', default: 1, comment: '所属域' }) + @ApiProperty({ description: '所属域' }) + domain: SkDomain; + @ApiHideProperty() @OneToMany(() => ProductEntity, product => product.company) products: Relation; diff --git a/src/modules/company/company.service.ts b/src/modules/company/company.service.ts index aca6da4..8cb1fdd 100644 --- a/src/modules/company/company.service.ts +++ b/src/modules/company/company.service.ts @@ -9,6 +9,7 @@ import { Storage } from '../tools/storage/storage.entity'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; import { fieldSearch } from '~/shared/database/field-search'; +import { SkDomain } from '~/common/decorators/domain.decorator'; @Injectable() export class CompanyService { @@ -70,11 +71,7 @@ export class CompanyService { throw new BusinessException(ErrorEnum.STORAGE_NOT_FOUND); } // 附件要批量插入 - await manager - .createQueryBuilder() - .relation(CompanyEntity, 'files') - .of(id) - .add(fileIds); + await manager.createQueryBuilder().relation(CompanyEntity, 'files').of(id).add(fileIds); } }); } diff --git a/src/modules/contract/contract.controller.ts b/src/modules/contract/contract.controller.ts index a3e716f..d21578b 100644 --- a/src/modules/contract/contract.controller.ts +++ b/src/modules/contract/contract.controller.ts @@ -16,6 +16,7 @@ import { ApiResult } from '~/common/decorators/api-result.decorator'; import { ContractEntity } from './contract.entity'; import { ContractDto, ContractQueryDto, ContractUpdateDto } from './contract.dto'; import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Domain, SkDomain } from '~/common/decorators/domain.decorator'; export const permissions = definePermission('app:contract', { LIST: 'list', CREATE: 'create', @@ -34,8 +35,8 @@ export class ContractController { @ApiOperation({ summary: '获取合同列表' }) @ApiResult({ type: [ContractEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: ContractQueryDto) { - return this.contractService.findAll(dto); + async list(@Domain() domain: SkDomain, @Query() dto: ContractQueryDto) { + return this.contractService.findAll({ ...dto, domain }); } @Get(':id') @@ -49,8 +50,8 @@ export class ContractController { @Post() @ApiOperation({ summary: '新增合同' }) @Perm(permissions.CREATE) - async create(@Body() dto: ContractDto): Promise { - await this.contractService.create(dto); + async create(@Domain() domain: SkDomain, @Body() dto: ContractDto): Promise { + await this.contractService.create({ ...dto, domain }); } @Put(':id') @@ -70,8 +71,10 @@ export class ContractController { @Put('unlink-attachments/:id') @ApiOperation({ summary: '附件解除关联' }) @Perm(permissions.UPDATE) - async unlinkAttachments(@IdParam() id: number, @Body() {fileIds}: ContractUpdateDto): Promise { + async unlinkAttachments( + @IdParam() id: number, + @Body() { fileIds }: ContractUpdateDto + ): Promise { await this.contractService.unlinkAttachments(id, fileIds); } - } diff --git a/src/modules/contract/contract.dto.ts b/src/modules/contract/contract.dto.ts index 3ada817..46e6e85 100644 --- a/src/modules/contract/contract.dto.ts +++ b/src/modules/contract/contract.dto.ts @@ -15,8 +15,9 @@ import { import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; import { ContractStatusEnum } from '~/constants/enum'; +import { DomainType, SkDomain } from '~/common/decorators/domain.decorator'; -export class ContractDto { +export class ContractDto extends DomainType { @ApiProperty({ description: '合同编号' }) @Matches(/^[a-z0-9A-Z]+$/, { message: '合同编号只能包含字母和数字' }) @IsString() @@ -65,5 +66,7 @@ export class ContractUpdateDto extends PartialType(ContractDto) { } export class ContractQueryDto extends IntersectionType( PagerDto, - PartialType(ContractDto) -) {} + PartialType(ContractDto), + DomainType +) {} + diff --git a/src/modules/contract/contract.entity.ts b/src/modules/contract/contract.entity.ts index d75b05a..40856b7 100644 --- a/src/modules/contract/contract.entity.ts +++ b/src/modules/contract/contract.entity.ts @@ -2,6 +2,7 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; import { CommonEntity } from '~/common/entity/common.entity'; import { Storage } from '../tools/storage/storage.entity'; +import { SkDomain } from '~/common/decorators/domain.decorator'; @Entity({ name: 'contract' }) export class ContractEntity extends CommonEntity { @@ -47,6 +48,10 @@ export class ContractEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + @Column({ type: 'int', default: 1, comment: '所属域' }) + @ApiProperty({ description: '所属域' }) + domain: SkDomain; + @ManyToMany(() => Storage, storage => storage.contracts) @JoinTable({ name: 'contract_storage', diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index ae961dd..267ef0b 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -10,6 +10,7 @@ import { Storage } from '../tools/storage/storage.entity'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; import { fieldSearch } from '~/shared/database/field-search'; +import { SkDomain } from '~/common/decorators/domain.decorator'; @Injectable() export class ContractService { diff --git a/src/modules/domian/domain.controller.ts b/src/modules/domian/domain.controller.ts new file mode 100644 index 0000000..e0ca43b --- /dev/null +++ b/src/modules/domian/domain.controller.ts @@ -0,0 +1,69 @@ +import { + Body, + Controller, + Get, + Query, + Put, + Delete, + Post, + BadRequestException +} from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Perm, definePermission } from '../auth/decorators/permission.decorator'; +import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; +import { DomainService } from './domain.service'; +import { ApiResult } from '~/common/decorators/api-result.decorator'; +import { DomainEntity } from './domain.entity'; +import { DomainDto, DomainQueryDto, DomainUpdateDto } from './domain.dto'; +import { IdParam } from '~/common/decorators/id-param.decorator'; +export const permissions = definePermission('app:domain', { + LIST: 'list', + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete' +} as const); + +@ApiTags('Domain - 域') +@ApiSecurityAuth() +@Controller('domain') +export class DomainController { + constructor(private domainService: DomainService) {} + + @Get() + @ApiOperation({ summary: '获取域列表' }) + @ApiResult({ type: [DomainEntity], isPage: true }) + @Perm(permissions.LIST) + async list(@Query() dto: DomainQueryDto) { + return this.domainService.findAll(dto); + } + + @Get(':id') + @ApiOperation({ summary: '获取域信息' }) + @ApiResult({ type: DomainDto }) + @Perm(permissions.READ) + async info(@IdParam() id: number) { + return this.domainService.info(id); + } + + @Post() + @ApiOperation({ summary: '新增域' }) + @Perm(permissions.CREATE) + async create(@Body() dto: DomainDto): Promise { + await this.domainService.create(dto); + } + + @Put(':id') + @ApiOperation({ summary: '更新域' }) + @Perm(permissions.UPDATE) + async update(@IdParam() id: number, @Body() dto: DomainUpdateDto): Promise { + await this.domainService.update(id, dto); + } + + @Delete(':id') + @ApiOperation({ summary: '删除域' }) + @Perm(permissions.DELETE) + async delete(@IdParam() id: number): Promise { + await this.domainService.delete(id); + } +} diff --git a/src/modules/domian/domain.dto.ts b/src/modules/domian/domain.dto.ts new file mode 100644 index 0000000..61d94fb --- /dev/null +++ b/src/modules/domian/domain.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; +import { PagerDto } from '~/common/dto/pager.dto'; + +export class DomainDto { + @ApiProperty({ description: '域标题' }) + @IsString() + title: string; +} + +export class DomainUpdateDto extends PartialType(DomainDto) {} +export class DomainQueryDto extends IntersectionType(PagerDto, PartialType(DomainDto)) {} diff --git a/src/modules/domian/domain.entity.ts b/src/modules/domian/domain.entity.ts new file mode 100644 index 0000000..8ed3c48 --- /dev/null +++ b/src/modules/domian/domain.entity.ts @@ -0,0 +1,15 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Column, Entity, JoinTable, ManyToMany, Relation } from 'typeorm'; +import { CommonEntity } from '~/common/entity/common.entity'; +import { Storage } from '../tools/storage/storage.entity'; + +@Entity({ name: 'domain' }) +export class DomainEntity extends CommonEntity { + @Column({ name: 'title', type: 'varchar', length: 255, comment: '域标题' }) + @ApiProperty({ description: '域标题' }) + title: string; + + @Column({ name: 'is_delete', type: 'tinyint', default: 0, comment: '是否删除' }) + @ApiProperty({ description: '删除状态:0未删除,1已删除' }) + isDelete: number; +} diff --git a/src/modules/domian/domain.module.ts b/src/modules/domian/domain.module.ts new file mode 100644 index 0000000..9aaa16f --- /dev/null +++ b/src/modules/domian/domain.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { DomainController } from './domain.controller'; +import { DomainService } from './domain.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { DomainEntity } from './domain.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([DomainEntity])], + controllers: [DomainController], + providers: [DomainService] +}) +export class DomainModule {} diff --git a/src/modules/domian/domain.service.ts b/src/modules/domian/domain.service.ts new file mode 100644 index 0000000..7fdfecd --- /dev/null +++ b/src/modules/domian/domain.service.ts @@ -0,0 +1,95 @@ +import { Injectable } from '@nestjs/common'; +import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; +import { DomainEntity } from './domain.entity'; +import { EntityManager, Like, Not, Repository } from 'typeorm'; +import { DomainDto, DomainQueryDto, DomainUpdateDto } from './domain.dto'; +import { Pagination } from '~/helper/paginate/pagination'; +import { isNumber } from 'lodash'; +import { paginate } from '~/helper/paginate'; +import { Storage } from '../tools/storage/storage.entity'; +import { BusinessException } from '~/common/exceptions/biz.exception'; +import { ErrorEnum } from '~/constants/error-code.constant'; +import { fieldSearch } from '~/shared/database/field-search'; + +@Injectable() +export class DomainService { + constructor( + @InjectEntityManager() private entityManager: EntityManager, + @InjectRepository(DomainEntity) + private domainRepository: Repository + ) {} + + /** + * 查找所有域 + */ + async findAll({ page, pageSize, ...fields }: DomainQueryDto): Promise> { + const queryBuilder = this.domainRepository + .createQueryBuilder('domain') + .where(fieldSearch(fields)) + .andWhere('domain.isDelete = 0'); + + return paginate(queryBuilder, { + page, + pageSize + }); + } + + /** + * 新增 + */ + async create({ title, ...ext }: DomainDto): Promise { + if (await this.checkIsDomainExsit(title)) { + throw new BusinessException(ErrorEnum.DOMAIN_TITLE_DUPLICATE); + } + await this.domainRepository.insert(this.domainRepository.create({ title, ...ext })); + } + + /** + * 更新 + */ + async update(id: number, { title, ...ext }: Partial): Promise { + await this.entityManager.transaction(async manager => { + if (title && (await this.checkIsDomainExsit(title, id))) { + throw new BusinessException(ErrorEnum.CONTRACT_NUMBER_EXIST); + } + await manager.update(DomainEntity, id, { + ...ext, + title + }); + }); + } + + /** + * 是否存在相同的域 + * @param title 域编号 + */ + async checkIsDomainExsit(title: string, id?: number): Promise { + return !!(await this.domainRepository.findOne({ + where: { + title: title, + id: Not(id) + } + })); + } + /** + * 删除 + */ + async delete(id: number): Promise { + // 域比较重要,做逻辑删除 + await this.domainRepository.update(id, { isDelete: 1 }); + } + + /** + * 获取单个域信息 + */ + async info(id: number) { + const info = await this.domainRepository + .createQueryBuilder('domain') + .where({ + id + }) + .andWhere('domain.isDelete = 0') + .getOne(); + return info; + } +} diff --git a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts index 2bf709b..842e065 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts @@ -11,6 +11,7 @@ import { MaterialsInOutDto, MaterialsInOutUpdateDto } from './materials_in_out.dto'; +import { Domain, DomainType, SkDomain } from '~/common/decorators/domain.decorator'; export const permissions = definePermission('materials_inventory:history_in_out', { LIST: 'list', @@ -29,8 +30,8 @@ export class MaterialsInOutController { @ApiOperation({ summary: '获取原材料出入库记录列表' }) @ApiResult({ type: [MaterialsInOutEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: MaterialsInOutQueryDto) { - return this.materialsInOutService.findAll(dto); + async list(@Domain() domain: SkDomain, @Query() dto: MaterialsInOutQueryDto) { + return this.materialsInOutService.findAll({ ...dto, domain }); } @Get(':id') @@ -44,8 +45,8 @@ export class MaterialsInOutController { @Post() @ApiOperation({ summary: '新增原材料出入库记录' }) @Perm(permissions.CREATE) - async create(@Body() dto: MaterialsInOutDto): Promise { - return this.materialsInOutService.create(dto); + async create(@Domain() domain: SkDomain, @Body() dto: MaterialsInOutDto): Promise { + return this.materialsInOutService.create({ ...dto, domain }); } @Put(':id') diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 5364295..517c41e 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -17,12 +17,13 @@ import { isNumber } from 'class-validator'; import dayjs from 'dayjs'; +import { DomainType } from '~/common/decorators/domain.decorator'; import { PagerDto } from '~/common/dto/pager.dto'; import { MaterialsInOrOutEnum } from '~/constants/enum'; import { Storage } from '~/modules/tools/storage/storage.entity'; import { formatToDate } from '~/utils'; -export class MaterialsInOutDto { +export class MaterialsInOutDto extends DomainType { @IsOptional() @IsNumber() @ApiProperty({ description: '项目Id' }) @@ -100,7 +101,10 @@ export class MaterialsInOutUpdateDto extends PartialType(MaterialsInOutDto) { @IsArray() fileIds: number[]; } -export class MaterialsInOutQueryDto extends PagerDto { +export class MaterialsInOutQueryDto extends IntersectionType( + PagerDto, + DomainType +) { @ApiProperty({ description: '出入库时间YYYY-MM-DD' }) @IsOptional() // @IsString() diff --git a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts index 6feef1f..b8e36b0 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.entity.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.entity.ts @@ -20,6 +20,7 @@ import { ProjectEntity } from '~/modules/project/project.entity'; import { ParamConfigEntity } from '~/modules/system/param-config/param-config.entity'; import { Storage } from '~/modules/tools/storage/storage.entity'; import { MaterialsInventoryEntity } from '../materials_inventory.entity'; +import { SkDomain } from '~/common/decorators/domain.decorator'; @Entity({ name: 'materials_in_out' }) export class MaterialsInOutEntity extends CommonEntity { @Column({ @@ -129,6 +130,10 @@ export class MaterialsInOutEntity extends CommonEntity { @JoinColumn({ name: 'product_id' }) product: ProductEntity; + @Column({ type: 'int', default: 1, comment: '所属域' }) + @ApiProperty({ description: '所属域' }) + domain: SkDomain; + @ManyToMany(() => Storage, storage => storage.materialsInOuts) @JoinTable({ name: 'materials_in_out_storage', diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts index da479d1..d7392e4 100644 --- a/src/modules/materials_inventory/materials_inventory.controller.ts +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -13,6 +13,7 @@ import { MaterialsInventoryService } from './materials_inventory.service'; import { MaterialsInventoryEntity } from './materials_inventory.entity'; import { ApiSecurityAuth } from '~/common/decorators/swagger.decorator'; import { FastifyReply } from 'fastify'; +import { Domain, DomainType, SkDomain } from '~/common/decorators/domain.decorator'; export const permissions = definePermission('app:materials_inventory', { LIST: 'list', @@ -33,10 +34,11 @@ export class MaterialsInventoryController { @ApiOperation({ summary: '导出原材料盘点表' }) @Perm(permissions.EXPORT) async exportMaterialsInventoryCheck( + @Domain() domain: SkDomain, @Query() dto: MaterialsInventoryExportDto, @Res() res: FastifyReply ): Promise { - await this.miService.exportMaterialsInventoryCheck(dto, res); + await this.miService.exportMaterialsInventoryCheck({ ...dto, domain }, res); } @Get() diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts index 256167b..5d4d89f 100644 --- a/src/modules/materials_inventory/materials_inventory.dto.ts +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -18,13 +18,15 @@ import { Transform } from 'class-transformer'; import dayjs from 'dayjs'; import { formatToDate } from '~/utils'; import { HasInventoryStatusEnum } from '~/constants/enum'; +import { DomainType } from '~/common/decorators/domain.decorator'; -export class MaterialsInventoryDto {} +export class MaterialsInventoryDto extends DomainType {} export class MaterialsInventoryUpdateDto extends PartialType(MaterialsInventoryDto) {} export class MaterialsInventoryQueryDto extends IntersectionType( PagerDto, - PartialType(MaterialsInventoryDto) + PartialType(MaterialsInventoryDto), + DomainType ) { @ApiProperty({ description: '产品名' }) @IsOptional() @@ -46,7 +48,7 @@ export class MaterialsInventoryQueryDto extends IntersectionType( @IsNumber() projectId: number; } -export class MaterialsInventoryExportDto { +export class MaterialsInventoryExportDto extends DomainType { @ApiProperty({ description: '项目' }) @IsOptional() @IsNumber() diff --git a/src/modules/materials_inventory/materials_inventory.entity.ts b/src/modules/materials_inventory/materials_inventory.entity.ts index 69dfc7b..0625be9 100644 --- a/src/modules/materials_inventory/materials_inventory.entity.ts +++ b/src/modules/materials_inventory/materials_inventory.entity.ts @@ -13,6 +13,7 @@ import { CommonEntity } from '~/common/entity/common.entity'; import { ProductEntity } from '../product/product.entity'; import { ProjectEntity } from '../project/project.entity'; import { MaterialsInOutEntity } from './in_out/materials_in_out.entity'; +import { SkDomain } from '~/common/decorators/domain.decorator'; @Entity({ name: 'materials_inventory' }) export class MaterialsInventoryEntity extends CommonEntity { @@ -70,6 +71,10 @@ export class MaterialsInventoryEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + @Column({ type: 'int', default: 1, comment: '所属域' }) + @ApiProperty({ description: '所属域' }) + domain: SkDomain; + @ManyToOne(() => ProjectEntity) @JoinColumn({ name: 'project_id' }) project: ProjectEntity; @@ -89,5 +94,5 @@ export class MaterialsInventoryEntity extends CommonEntity { @ApiHideProperty() @OneToMany(() => MaterialsInOutEntity, inout => inout.inventory) - materialsInOuts: Relation; + materialsInOuts: Relation; } diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index bcae7e7..a87d29a 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -41,7 +41,7 @@ export class MaterialsInventoryService { * 导出原材料盘点表 */ async exportMaterialsInventoryCheck( - { time, projectId }: MaterialsInventoryExportDto, + { time, projectId, domain }: MaterialsInventoryExportDto, res: FastifyReply ): Promise { const ROW_HEIGHT = 20; diff --git a/src/modules/product/product.controller.ts b/src/modules/product/product.controller.ts index f1c28c2..d91bb61 100644 --- a/src/modules/product/product.controller.ts +++ b/src/modules/product/product.controller.ts @@ -16,6 +16,7 @@ import { ApiResult } from '~/common/decorators/api-result.decorator'; import { ProductEntity } from './product.entity'; import { ProductDto, ProductQueryDto, ProductUpdateDto } from './product.dto'; import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Domain, SkDomain } from '~/common/decorators/domain.decorator'; export const permissions = definePermission('app:product', { LIST: 'list', CREATE: 'create', @@ -34,8 +35,8 @@ export class ProductController { @ApiOperation({ summary: '获取产品列表' }) @ApiResult({ type: [ProductEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: ProductQueryDto) { - return this.productService.findAll(dto); + async list(@Domain() domain: SkDomain, @Query() dto: ProductQueryDto) { + return this.productService.findAll({ ...dto, domain }); } @Get(':id') @@ -49,8 +50,8 @@ export class ProductController { @Post() @ApiOperation({ summary: '新增产品' }) @Perm(permissions.CREATE) - async create(@Body() dto: ProductDto): Promise { - await this.productService.create(dto); + async create(@Domain() domain: SkDomain, @Body() dto: ProductDto): Promise { + await this.productService.create({ ...dto, domain }); } @Put(':id') diff --git a/src/modules/product/product.dto.ts b/src/modules/product/product.dto.ts index 85ca3fd..f476345 100644 --- a/src/modules/product/product.dto.ts +++ b/src/modules/product/product.dto.ts @@ -2,13 +2,13 @@ import { ApiProperty, IntersectionType, PartialType } from '@nestjs/swagger'; import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; +import { DomainType } from '~/common/decorators/domain.decorator'; -export class ProductDto { +export class ProductDto extends DomainType { @ApiProperty({ description: '产品名称' }) @IsString() name: string; - @ApiProperty({ description: '产品规格' }) @IsOptional() @IsString() @@ -42,7 +42,8 @@ export class ProductUpdateDto extends PartialType(ProductDto) { export class ProductQueryDto extends IntersectionType( PagerDto, - PartialType(ProductDto) + PartialType(ProductDto), + DomainType ) { @ApiProperty({ description: '所属公司名称' }) @IsOptional() @@ -58,5 +59,4 @@ export class ProductQueryDto extends IntersectionType( @IsOptional() @IsString() keyword?: string; - } diff --git a/src/modules/product/product.entity.ts b/src/modules/product/product.entity.ts index f7bfad6..a424b87 100644 --- a/src/modules/product/product.entity.ts +++ b/src/modules/product/product.entity.ts @@ -15,6 +15,7 @@ import { Storage } from '../tools/storage/storage.entity'; import { CompanyEntity } from '../company/company.entity'; import pinyin from 'pinyin'; import { DictItemEntity } from '../system/dict-item/dict-item.entity'; +import { SkDomain } from '~/common/decorators/domain.decorator'; @Entity({ name: 'product' }) export class ProductEntity extends CommonEntity { @Column({ @@ -70,6 +71,10 @@ export class ProductEntity extends CommonEntity { @ManyToOne(() => DictItemEntity) @JoinColumn({ name: 'unit_id' }) unit: DictItemEntity; + + @Column({ type: 'int', default: 1, comment: '所属域' }) + @ApiProperty({ description: '所属域' }) + domain: SkDomain; @ApiHideProperty() @Column({ diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index fca5e25..e844f8b 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -22,6 +22,7 @@ import { UserPasswordDto } from './dto/password.dto'; import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto'; import { UserEntity } from './user.entity'; import { UserService } from './user.service'; +import { Domain, SkDomain } from '~/common/decorators/domain.decorator'; export const permissions = definePermission('system:user', { LIST: 'list', @@ -47,8 +48,8 @@ export class UserController { @ApiOperation({ summary: '获取用户列表' }) @ApiResult({ type: [UserEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: UserQueryDto) { - return this.userService.list(dto); + async list(@Domain() domain: SkDomain, @Query() dto: UserQueryDto) { + return this.userService.list(dto, domain); } @Get(':id') diff --git a/src/modules/user/user.entity.ts b/src/modules/user/user.entity.ts index e311827..5069c63 100644 --- a/src/modules/user/user.entity.ts +++ b/src/modules/user/user.entity.ts @@ -13,6 +13,7 @@ import { OneToMany, Relation } from 'typeorm'; +import { SkDomain } from '~/common/decorators/domain.decorator'; import { CommonEntity } from '~/common/entity/common.entity'; @@ -73,6 +74,9 @@ export class UserEntity extends CommonEntity { @Column({ type: 'tinyint', nullable: true, default: 1 }) status: number; + @Column({ type: 'int', default: 1, comment: '所属域' }) + domain: SkDomain; + @ManyToMany(() => RoleEntity, role => role.users) @JoinTable({ name: 'sys_user_roles', diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 16842a7..9be9d0f 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -28,6 +28,7 @@ import { PasswordUpdateDto } from './dto/password.dto'; import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto'; import { UserEntity } from './user.entity'; import { AccountInfo } from './user.model'; +import { SkDomain } from '~/common/decorators/domain.decorator'; @Injectable() export class UserService { @@ -243,16 +244,10 @@ export class UserService { /** * 查询用户列表 */ - async list({ - page, - pageSize, - username, - nickname, - deptId, - email, - status, - keyword - }: UserQueryDto): Promise> { + async list( + { page, pageSize, username, nickname, deptId, email, status, keyword }: UserQueryDto, + domain: SkDomain + ): Promise> { const queryBuilder = this.userRepository .createQueryBuilder('user') .leftJoinAndSelect('user.dept', 'dept') @@ -337,7 +332,7 @@ export class UserService { /** * 注册 */ - async register({ username, ...data }: RegisterDto): Promise { + async register({ username, ...data }: RegisterDto, domain: SkDomain): Promise { const exists = await this.userRepository.findOneBy({ username }); @@ -352,7 +347,8 @@ export class UserService { username, password, status: 1, - psalt: salt + psalt: salt, + domain }); const user = await manager.save(u); diff --git a/src/shared/database/field-search/index.ts b/src/shared/database/field-search/index.ts index 1067765..8b4d05d 100644 --- a/src/shared/database/field-search/index.ts +++ b/src/shared/database/field-search/index.ts @@ -1,5 +1,6 @@ import { isNumber } from 'lodash'; -import { Between, Like, ObjectLiteral, ObjectType } from 'typeorm'; +import { Between, Like, ObjectLiteral } from 'typeorm'; +import { SkDomain } from '~/common/decorators/domain.decorator'; export const fieldSearch = (entity: Partial): ObjectLiteral => { let result = {}; for (let key in entity) { From f958ab7af933ffefac7991e05e80ed51a06d11fd Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 17 Apr 2024 09:59:18 +0800 Subject: [PATCH 62/64] feat: develop domain --- src/modules/domian/domain.controller.ts | 2 - .../in_out/materials_in_out.controller.ts | 25 +++- .../in_out/materials_in_out.dto.ts | 44 +++++- .../in_out/materials_in_out.service.ts | 128 +++++++++++++++++- .../materials_inventory.controller.ts | 4 +- .../materials_inventory.dto.ts | 9 +- .../materials_inventory.service.ts | 118 +++++++++------- src/modules/project/project.controller.ts | 9 +- src/modules/project/project.dto.ts | 6 +- src/modules/project/project.entity.ts | 5 + src/modules/user/dto/user.dto.ts | 7 +- src/modules/user/user.controller.ts | 8 +- src/shared/database/field-search/index.ts | 4 +- 13 files changed, 286 insertions(+), 83 deletions(-) diff --git a/src/modules/domian/domain.controller.ts b/src/modules/domian/domain.controller.ts index e0ca43b..0bd02a3 100644 --- a/src/modules/domian/domain.controller.ts +++ b/src/modules/domian/domain.controller.ts @@ -17,7 +17,6 @@ import { DomainEntity } from './domain.entity'; import { DomainDto, DomainQueryDto, DomainUpdateDto } from './domain.dto'; import { IdParam } from '~/common/decorators/id-param.decorator'; export const permissions = definePermission('app:domain', { - LIST: 'list', CREATE: 'create', READ: 'read', UPDATE: 'update', @@ -33,7 +32,6 @@ export class DomainController { @Get() @ApiOperation({ summary: '获取域列表' }) @ApiResult({ type: [DomainEntity], isPage: true }) - @Perm(permissions.LIST) async list(@Query() dto: DomainQueryDto) { return this.domainService.findAll(dto); } diff --git a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts index 842e065..c41a04d 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.controller.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Post, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Post, Put, Query, Res } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiResult } from '~/common/decorators/api-result.decorator'; import { IdParam } from '~/common/decorators/id-param.decorator'; @@ -9,23 +9,38 @@ import { definePermission, Perm } from '~/modules/auth/decorators/permission.dec import { MaterialsInOutQueryDto, MaterialsInOutDto, - MaterialsInOutUpdateDto + MaterialsInOutUpdateDto, + MaterialsInOutExportDto } from './materials_in_out.dto'; import { Domain, DomainType, SkDomain } from '~/common/decorators/domain.decorator'; - +import { FastifyReply } from 'fastify'; export const permissions = definePermission('materials_inventory:history_in_out', { LIST: 'list', CREATE: 'create', READ: 'read', UPDATE: 'update', - DELETE: 'delete' + DELETE: 'delete', + EXPORT: 'export' } as const); @ApiTags('Materials In Out History - 原材料出入库记录') @ApiSecurityAuth() @Controller('materials-in-out') export class MaterialsInOutController { - constructor(private materialsInOutService: MaterialsInOutService) {} + constructor(private materialsInOutService: MaterialsInOutService) { } + + @Get('export') + @ApiOperation({ summary: '导出原材料盘点表' }) + @Perm(permissions.EXPORT) + async exportMaterialsInventoryCheck( + @Domain() domain: SkDomain, + @Query() dto: MaterialsInOutExportDto, + @Res() res: FastifyReply + ): Promise { + await this.materialsInOutService.exportMaterialsInventoryCheck({ ...dto, domain }, res); + } + + @Get() @ApiOperation({ summary: '获取原材料出入库记录列表' }) @ApiResult({ type: [MaterialsInOutEntity], isPage: true }) diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 517c41e..f4faab5 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -112,9 +112,9 @@ export class MaterialsInOutQueryDto extends IntersectionType( // 开始和结束时间用的是一天的开始和一天的结束的时分秒 return params.value ? [ - params.value[0] ? `${formatToDate(params.value[0], 'YYYY-MM-DD')} 00:00:00` : null, - params.value[1] ? `${formatToDate(params.value[1], 'YYYY-MM-DD')} 23:59:59` : null - ] + params.value[0] ? `${formatToDate(params.value[0], 'YYYY-MM-DD')} 00:00:00` : null, + params.value[1] ? `${formatToDate(params.value[1], 'YYYY-MM-DD')} 23:59:59` : null + ] : []; }) time?: string[]; @@ -159,3 +159,41 @@ export class MaterialsInOutQueryDto extends IntersectionType( @ApiProperty({ description: '是否是用于创建出库记录' }) isCreateOut?: boolean; } +export class MaterialsInOutExportDto extends IntersectionType( + + DomainType +) { + + @ApiProperty({ description: '导出时间YYYY-MM-DD' }) + @IsOptional() + @IsArray() + @Transform(params => { + // 开始和结束时间用的是一月的开始和一月的结束的时分秒 + const date = params.value; + return [ + date ? `${date[0]} 00:00:00` : null, + date ? `${date[1]} 23:59:59` : null + ]; + }) + time?: string[]; + + @ApiProperty({ description: '导出文件名' }) + @IsOptional() + @IsString() + filename?: string + + @ApiProperty({ description: '入库或出库 0:入库 1:出库' }) + @IsOptional() + @IsEnum(MaterialsInOrOutEnum) + inOrOut?: MaterialsInOrOutEnum; + + @ApiProperty({ description: '产品名称' }) + @IsOptional() + @IsString() + product?: string; + + @ApiProperty({ description: '经办人' }) + @IsOptional() + @IsString() + agent?: string; +} \ No newline at end of file diff --git a/src/modules/materials_inventory/in_out/materials_in_out.service.ts b/src/modules/materials_inventory/in_out/materials_in_out.service.ts index 3b3c318..5cc4ef6 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.service.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; -import { EntityManager, In, Repository } from 'typeorm'; +import { Between, EntityManager, In, Repository } from 'typeorm'; import { Pagination } from '~/helper/paginate/pagination'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; @@ -10,7 +10,8 @@ import { Storage } from '~/modules/tools/storage/storage.entity'; import { MaterialsInOutQueryDto, MaterialsInOutDto, - MaterialsInOutUpdateDto + MaterialsInOutUpdateDto, + MaterialsInOutExportDto } from './materials_in_out.dto'; import { MaterialsInOutEntity } from './materials_in_out.entity'; import { fieldSearch } from '~/shared/database/field-search'; @@ -19,7 +20,9 @@ import { MaterialsInOrOutEnum, ParamConfigEnum } from '~/constants/enum'; import { MaterialsInventoryEntity } from '../materials_inventory.entity'; import { MaterialsInventoryService } from '../materials_inventory.service'; import { isDefined } from 'class-validator'; - +import { FastifyReply } from 'fastify'; +import * as ExcelJS from 'exceljs'; +import dayjs from 'dayjs'; @Injectable() export class MaterialsInOutService { constructor( @@ -31,7 +34,119 @@ export class MaterialsInOutService { @InjectRepository(ParamConfigEntity) private paramConfigRepository: Repository, private materialsInventoryService: MaterialsInventoryService - ) {} + ) { } + + + /** + * 导出出入库记录表 + */ + async exportMaterialsInventoryCheck( + { time, domain, filename, ...ext }: MaterialsInOutExportDto, + res: FastifyReply + ): Promise { + const ROW_HEIGHT = 20; + const HEADER_FONT_SIZE = 18; + + // 生成数据 + const sqb = this.buildSearchQuery() + .where(fieldSearch(ext)) + .andWhere({ + time: Between(time[0], time[1]) + }) + .andWhere('materialsInOut.isDelete = 0'); + const data = await sqb.addOrderBy('materialsInOut.time', 'DESC').getMany(); + const workbook = new ExcelJS.Workbook(); + const sheet = workbook.addWorksheet('出入库记录'); + sheet.mergeCells('A1:T1'); + // 设置标题 + sheet.getCell('A1').value = '山东矿机华信智能科技有限公司出入库记录表'; + // 设置日期 + sheet.mergeCells('A2:C2'); + sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月D日')}-${dayjs(time[1]).format('YYYY年M月D日')}`; + // 设置表头 + const headers = [ + '出入库单号', + '出入库', + '项目', + '公司名称', + '产品名称', + '规格型号', + '时间', + '单位', + '数量', + '单价', + '金额', + '经办人', + '领料单号', + '备注' + ]; + sheet.addRow(headers); + for (let index = 0; index < data.length; index++) { + const record = data[index]; + sheet.addRow([ + `${record.inventoryInOutNumber}`, + record.project?.name || '', + record.inOrOut === MaterialsInOrOutEnum.In ? '入库' : "出库", + record.product?.company?.name || '', + record.product?.name || '', + record.product?.productSpecification || '', + `${dayjs(record.time).format('YYYY-MM-DD HH:mm')}`, + record.product.unit.label || '', + record.quantity, + parseFloat(`${record.unitPrice || 0}`), + parseFloat(`${record.amount || 0}`), + `${record?.agent || ''}`, + record?.issuanceNumber || '', + record?.remark || '' + ]); + } + // 固定信息样式设定 + sheet.eachRow((row, index) => { + if (index >= 3) { + row.alignment = { vertical: 'middle', horizontal: 'center' }; + row.height = ROW_HEIGHT; + row.eachCell(cell => { + cell.border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + }); + } + }); + + sheet.columns.forEach((column, index: number) => { + let maxColumnLength = 0; + const autoWidth = ['B', 'C', 'S', 'U']; + if (String.fromCharCode(65 + index) === 'B') maxColumnLength = 20; + if (autoWidth.includes(String.fromCharCode(65 + index))) { + column.eachCell({ includeEmpty: true }, (cell, rowIndex) => { + if (rowIndex >= 5) { + const columnLength = `${cell.value || ''}`.length; + if (columnLength > maxColumnLength) { + maxColumnLength = columnLength; + } + } + }); + column.width = maxColumnLength < 12 ? 12 : maxColumnLength; // Minimum width of 10 + } else { + column.width = 12; + } + }); + //读取buffer进行传输 + const buffer = await workbook.xlsx.writeBuffer(); + res + .header('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + .header( + 'Content-Disposition', + `attachment; filename="${filename}.xls"` + ) + .send(buffer); + } + + + /** * 查询所有出入库记录 */ @@ -104,7 +219,8 @@ export class MaterialsInOutService { position, unitPrice, quantity, - productId + productId, + domain } = dto; inventoryInOutNumber = await this.generateInventoryInOutNumber(inOrOut); let newRecordId; @@ -115,7 +231,7 @@ export class MaterialsInOutService { Object.is(inOrOut, MaterialsInOrOutEnum.In) ? this.materialsInventoryService.inInventory.bind(this.materialsInventoryService) : this.materialsInventoryService.outInventory.bind(this.materialsInventoryService) - )({ productId, quantity, unitPrice, projectId, inventoryId, position }, manager); + )({ productId, quantity, unitPrice, projectId, inventoryId, position }, manager, domain); // 2.生成出入库记录 const { id } = await manager.save(MaterialsInOutEntity, { ...this.materialsInOutRepository.create({ ...dto, inventoryId: inventoryEntity?.id }), diff --git a/src/modules/materials_inventory/materials_inventory.controller.ts b/src/modules/materials_inventory/materials_inventory.controller.ts index d7392e4..8e53f13 100644 --- a/src/modules/materials_inventory/materials_inventory.controller.ts +++ b/src/modules/materials_inventory/materials_inventory.controller.ts @@ -45,8 +45,8 @@ export class MaterialsInventoryController { @ApiOperation({ summary: '获取原材料库存列表' }) @ApiResult({ type: [MaterialsInventoryEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: MaterialsInventoryQueryDto) { - return this.miService.findAll(dto); + async list(@Domain() domain: SkDomain, @Query() dto: MaterialsInventoryQueryDto) { + return this.miService.findAll({ ...dto, domain }); } @Get(':id') diff --git a/src/modules/materials_inventory/materials_inventory.dto.ts b/src/modules/materials_inventory/materials_inventory.dto.ts index 5d4d89f..b05e466 100644 --- a/src/modules/materials_inventory/materials_inventory.dto.ts +++ b/src/modules/materials_inventory/materials_inventory.dto.ts @@ -61,9 +61,14 @@ export class MaterialsInventoryExportDto extends DomainType { // 开始和结束时间用的是一月的开始和一月的结束的时分秒 const date = params.value; return [ - date ? `${formatToDate(dayjs(date).startOf('month'))} 00:00:00` : null, - date ? `${formatToDate(dayjs(date).endOf('month'))} 23:59:59` : null + date ? `${date[0]} 00:00:00` : null, + date ? `${date[1]} 23:59:59` : null ]; }) time?: string[]; + + @ApiProperty({ description: '文件名' }) + @IsOptional() + @IsString() + filename: string; } diff --git a/src/modules/materials_inventory/materials_inventory.service.ts b/src/modules/materials_inventory/materials_inventory.service.ts index a87d29a..a8b0516 100644 --- a/src/modules/materials_inventory/materials_inventory.service.ts +++ b/src/modules/materials_inventory/materials_inventory.service.ts @@ -23,6 +23,7 @@ import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; import { ParamConfigEntity } from '../system/param-config/param-config.entity'; import { isDefined } from 'class-validator'; +import { DomainType } from '~/common/decorators/domain.decorator'; @Injectable() export class MaterialsInventoryService { constructor( @@ -35,7 +36,7 @@ export class MaterialsInventoryService { private projectRepository: Repository, @InjectRepository(ParamConfigEntity) private paramConfigRepository: Repository - ) {} + ) { } /** * 导出原材料盘点表 @@ -55,7 +56,8 @@ export class MaterialsInventoryService { const inventoriesInProjects = await this.materialsInventoryRepository.find({ where: { ...(projects?.length ? { projectId: In(projects.map(item => item.id)) } : null) - } + }, + relations: ['product', 'product.company', 'product.unit'] }); // 生成数据 @@ -86,7 +88,7 @@ export class MaterialsInventoryService { const data = await sqb.addOrderBy('mio.time', 'DESC').getMany(); if (!projectId) { projects = uniqBy( - data.map(item => item.project), + data.filter(item => item.inOrOut === MaterialsInOrOutEnum.Out).map(item => item.project), 'id' ); } @@ -95,7 +97,9 @@ export class MaterialsInventoryService { const currentProjectInventories = inventoriesInProjects.filter(({ projectId }) => Object.is(projectId, project.id) ); - const currentProjectData = data.filter(item => item.projectId === project.id); + const currentProjectData = data.filter( + item => item.projectId === project.id || item.inOrOut === MaterialsInOrOutEnum.Out + ); const currentMonthProjectData = currentProjectData.filter(item => { return ( dayjs(item.time).isAfter(dayjs(time[0])) && dayjs(item.time).isBefore(dayjs(time[1])) @@ -110,7 +114,7 @@ export class MaterialsInventoryService { sheet.getCell('A2').value = `日期:${dayjs(time[0]).format('YYYY年M月')}`; // 设置表头 const headers = [ - '出入库单号', + '序号', '公司名称', '产品名称', '单位', @@ -128,8 +132,6 @@ export class MaterialsInventoryService { '结存数量', '单价', '金额', - '经办人', - '领料单号', '备注' ]; sheet.addRow(headers); @@ -152,8 +154,6 @@ export class MaterialsInventoryService { '', '', '', - '', - '', '' ]); for (let i = 1; i <= 7; i++) { @@ -175,7 +175,7 @@ export class MaterialsInventoryService { }; } - for (let i = 16; i <= 21; i++) { + for (let i = 16; i <= 19; i++) { sheet.mergeCells(`${String.fromCharCode(64 + i)}3:${String.fromCharCode(64 + i)}4`); } @@ -196,24 +196,40 @@ export class MaterialsInventoryService { } }); + // 根据库存Id分组 const groupedData = groupBy( currentMonthProjectData, - record => `${record.productId}-${record.unitPrice}` + record => record.inventoryId ); let number = 0; - const groupedInventories = groupBy( - currentProjectInventories, - item => `${item.projectId}_${item.productId}` - ); - for (const key in groupedData) { - // 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。 - const inRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.In); - const outRecord = groupedData[key].find(item => item.inOrOut === MaterialsInOrOutEnum.Out); - const currInventories = - groupedInventories[`${inRecord.projectId}_${inRecord.productId}`]?.shift(); - const allDataFromMonth = data.filter( - res => res.projectId === inRecord.projectId && res.productId === inRecord.productId + const groupedInventories = groupBy(currentProjectInventories, item => item.id); + let orderNo = 0; + + for (const key in groupedInventories) { + orderNo++; + // 目前暂定逻辑出库只有一次或者没有出库。不会对一个入库的记录多次出库,故而用find。---废弃 + // 2024.04.16 改成 + const inventory = groupedInventories[key][0]; + const outRecords = groupedData[key].filter( + item => item.inOrOut === MaterialsInOrOutEnum.Out ); + const inRecords = groupedData[key].filter(item => item.inOrOut === MaterialsInOrOutEnum.In); + const outRecordQuantity = outRecords + .map(item => item.quantity) + .reduce((acc, cur) => { + return calcNumber(acc, cur, 'add'); + }, 0); + + const inRecordQuantity = inRecords + .map(item => item.quantity) + .reduce((acc, cur) => { + return calcNumber(acc, cur, 'add'); + }, 0); + // 这里的单价默认入库价格和出库价格一致,所以直接用总数量*入库单价 + const outRecordAmount = calcNumber(outRecordQuantity, inventory.unitPrice || 0, 'multiply'); + const inRecordAmount = calcNumber(inRecordQuantity, inventory.unitPrice || 0, 'multiply'); + const currInventories = groupedInventories[key]?.shift(); + const allDataFromMonth = data.filter(res => res.inventoryId == Number(key)); let currentQuantity = 0; let balanceQuantity = 0; // 月初库存数量 @@ -234,32 +250,33 @@ export class MaterialsInventoryService { // 结存库存数量 balanceQuantity = calcNumber( currentQuantity, - calcNumber(inRecord.quantity, outRecord?.quantity || 0, 'subtract'), + calcNumber(inRecordQuantity, outRecordQuantity, 'subtract'), 'add' ); number++; sheet.addRow([ - `${inRecord.inventoryInOutNumber || ''}`, - inRecord.product.company.name || '', - inRecord.product.name || '', - inRecord.product.unit.label || '', + `${orderNo}`, + inventory.product?.company?.name || '', + inventory.product?.name || '', + inventory.product.unit.label || '', currentQuantity, - parseFloat(`${inRecord.unitPrice || 0}`), - calcNumber(currentQuantity, inRecord.unitPrice || 0, 'multiply'), - inRecord.time, - inRecord.quantity || 0, - parseFloat(`${inRecord.unitPrice || 0}`), - parseFloat(`${inRecord.amount || 0}`), - outRecord?.time || '', - outRecord?.quantity || 0, - parseFloat(`${outRecord?.unitPrice || 0}`), - parseFloat(`${outRecord?.amount || 0}`), + parseFloat(`${inventory.unitPrice || 0}`), + calcNumber(currentQuantity, inventory.unitPrice || 0, 'multiply'), + // inRecord.time, + '', + inRecordQuantity, + parseFloat(`${inventory.unitPrice || 0}`), + parseFloat(`${inRecordAmount}`), + // outRecord?.time || '', + '', + outRecordQuantity, + parseFloat(`${inventory?.unitPrice || 0}`), + parseFloat(`${outRecordAmount}`), balanceQuantity, - parseFloat(`${inRecord?.unitPrice || 0}`), - calcNumber(balanceQuantity, inRecord?.unitPrice || 0, 'multiply'), - `${inRecord?.agent || ''}/${outRecord?.agent || ''}`, - outRecord?.issuanceNumber || '', - `${inRecord?.remark || ''}/${outRecord?.remark || ''}` + parseFloat(`${inventory?.unitPrice || 0}`), + calcNumber(balanceQuantity, inventory?.unitPrice || 0, 'multiply'), + // `${inRecord?.agent || ''}/${outRecord?.agent || ''}`, + '' ]); } sheet.getCell('A1').font = { size: HEADER_FONT_SIZE }; @@ -319,7 +336,8 @@ export class MaterialsInventoryService { product, keyword, projectId, - isHasInventory + isHasInventory, + domain }: MaterialsInventoryQueryDto): Promise> { const queryBuilder = this.materialsInventoryRepository .createQueryBuilder('materialsInventory') @@ -339,7 +357,8 @@ export class MaterialsInventoryService { 'product.productSpecification', 'product.productNumber' ]) - .where('materialsInventory.isDelete = 0'); + .where(fieldSearch({ domain })) + .andWhere('materialsInventory.isDelete = 0'); if (product) { queryBuilder.andWhere('product.name like :product', { product: `%${product}%` }); } @@ -402,7 +421,8 @@ export class MaterialsInventoryService { unitPrice?: number; changedUnitPrice?: number; }, - manager: EntityManager + manager: EntityManager, + domain?: DomainType ): Promise { const { projectId, @@ -415,9 +435,9 @@ export class MaterialsInventoryService { } = data; let searchPayload: any = {}; if (isDefined(inventoryId)) { - searchPayload = { id: inventoryId }; + searchPayload = { id: inventoryId, domain }; } else { - searchPayload = { projectId, productId, unitPrice }; + searchPayload = { projectId, productId, unitPrice, domain }; } const exsitedInventory = await manager.findOne(MaterialsInventoryEntity, { where: searchPayload, // 根据项目,产品,价格查出之前的实时库存情况 @@ -461,7 +481,7 @@ export class MaterialsInventoryService { quantity: number; inventoryId?: number; }, - manager: EntityManager + manager: EntityManager, ): Promise { const { quantity: outQuantity, inventoryId } = data; // 开启悲观行锁,防止脏读和修改 diff --git a/src/modules/project/project.controller.ts b/src/modules/project/project.controller.ts index 605090b..bd92a28 100644 --- a/src/modules/project/project.controller.ts +++ b/src/modules/project/project.controller.ts @@ -16,6 +16,7 @@ import { ApiResult } from '~/common/decorators/api-result.decorator'; import { ProjectEntity } from './project.entity'; import { ProjectDto, ProjectQueryDto, ProjectUpdateDto } from './project.dto'; import { IdParam } from '~/common/decorators/id-param.decorator'; +import { Domain, SkDomain } from '~/common/decorators/domain.decorator'; export const permissions = definePermission('app:project', { LIST: 'list', CREATE: 'create', @@ -34,8 +35,8 @@ export class ProjectController { @ApiOperation({ summary: '分页获取项目列表' }) @ApiResult({ type: [ProjectEntity], isPage: true }) @Perm(permissions.LIST) - async list(@Query() dto: ProjectQueryDto) { - return this.projectService.findAll(dto); + async list(@Domain() domain: SkDomain, @Query() dto: ProjectQueryDto) { + return this.projectService.findAll({ ...dto, domain }); } @Get(':id') @@ -49,8 +50,8 @@ export class ProjectController { @Post() @ApiOperation({ summary: '新增项目' }) @Perm(permissions.CREATE) - async create(@Body() dto: ProjectDto): Promise { - await this.projectService.create(dto); + async create(@Domain() domain: SkDomain, @Body() dto: ProjectDto): Promise { + await this.projectService.create({ ...dto, domain }); } @Put(':id') diff --git a/src/modules/project/project.dto.ts b/src/modules/project/project.dto.ts index 6c96b6e..3e02fc5 100644 --- a/src/modules/project/project.dto.ts +++ b/src/modules/project/project.dto.ts @@ -4,8 +4,9 @@ import { PagerDto } from '~/common/dto/pager.dto'; import { Storage } from '../tools/storage/storage.entity'; import { IsUnique } from '~/shared/database/constraints/unique.constraint'; import { ProjectEntity } from './project.entity'; +import { DomainType } from '~/common/decorators/domain.decorator'; -export class ProjectDto { +export class ProjectDto extends DomainType { @ApiProperty({ description: '项目名称' }) @IsUnique(ProjectEntity, { message: '已存在同名项目' }) @IsString() @@ -31,7 +32,8 @@ export class ComapnyCreateDto extends PartialType(ProjectDto) { export class ProjectQueryDto extends IntersectionType( PagerDto, - PartialType(ProjectDto) + PartialType(ProjectDto), + DomainType ) { @ApiProperty({ description: '项目名称' }) @IsOptional() diff --git a/src/modules/project/project.entity.ts b/src/modules/project/project.entity.ts index a80b296..72a7ec9 100644 --- a/src/modules/project/project.entity.ts +++ b/src/modules/project/project.entity.ts @@ -4,6 +4,7 @@ import { CommonEntity } from '~/common/entity/common.entity'; import { Storage } from '../tools/storage/storage.entity'; import { ProductEntity } from '../product/product.entity'; import { MaterialsInOutEntity } from '../materials_inventory/in_out/materials_in_out.entity'; +import { SkDomain } from '~/common/decorators/domain.decorator'; /** * 项目实体类 @@ -24,6 +25,10 @@ export class ProjectEntity extends CommonEntity { @ApiProperty({ description: '删除状态:0未删除,1已删除' }) isDelete: number; + @Column({ type: 'int', default: 1, comment: '所属域' }) + @ApiProperty({ description: '所属域' }) + domain: SkDomain; + @ApiHideProperty() @OneToMany(() => MaterialsInOutEntity, product => product.project) materialsInOuts: Relation; diff --git a/src/modules/user/dto/user.dto.ts b/src/modules/user/dto/user.dto.ts index f7efc73..24193b5 100644 --- a/src/modules/user/dto/user.dto.ts +++ b/src/modules/user/dto/user.dto.ts @@ -15,10 +15,11 @@ import { ValidateIf } from 'class-validator'; import { isEmpty } from 'lodash'; +import { DomainType } from '~/common/decorators/domain.decorator'; import { PagerDto } from '~/common/dto/pager.dto'; -export class UserDto { +export class UserDto extends DomainType { @ApiProperty({ description: '头像' }) @IsOptional() @IsString() @@ -83,9 +84,9 @@ export class UserDto { status: number; } -export class UserUpdateDto extends PartialType(UserDto) {} +export class UserUpdateDto extends PartialType(UserDto) { } -export class UserQueryDto extends IntersectionType(PagerDto, PartialType(UserDto)) { +export class UserQueryDto extends IntersectionType(PagerDto, PartialType(UserDto), DomainType) { @ApiProperty({ description: '归属大区', example: 1, required: false }) @IsInt() @IsOptional() diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index e844f8b..f09a413 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -22,7 +22,7 @@ import { UserPasswordDto } from './dto/password.dto'; import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto'; import { UserEntity } from './user.entity'; import { UserService } from './user.service'; -import { Domain, SkDomain } from '~/common/decorators/domain.decorator'; +import { Domain, DomainType, SkDomain } from '~/common/decorators/domain.decorator'; export const permissions = definePermission('system:user', { LIST: 'list', @@ -42,7 +42,7 @@ export class UserController { constructor( private userService: UserService, private menuService: MenuService - ) {} + ) { } @Get() @ApiOperation({ summary: '获取用户列表' }) @@ -62,8 +62,8 @@ export class UserController { @Post() @ApiOperation({ summary: '新增用户' }) @Perm(permissions.CREATE) - async create(@Body() dto: UserDto): Promise { - await this.userService.create(dto); + async create(@Domain() domain: SkDomain, @Body() dto: UserDto): Promise { + await this.userService.create({ ...dto, domain }); } @Put(':id') diff --git a/src/shared/database/field-search/index.ts b/src/shared/database/field-search/index.ts index 8b4d05d..d8bdc44 100644 --- a/src/shared/database/field-search/index.ts +++ b/src/shared/database/field-search/index.ts @@ -4,7 +4,9 @@ import { SkDomain } from '~/common/decorators/domain.decorator'; export const fieldSearch = (entity: Partial): ObjectLiteral => { let result = {}; for (let key in entity) { - if (entity.hasOwnProperty(key)) { + if (key == 'domain') { + result = { ...result, domain: entity['domain'] || -1 }; + } else if (entity.hasOwnProperty(key)) { switch (typeof entity[key]) { case 'number': result = { ...result, ...(isNumber(entity[key]) ? { [key]: entity[key] } : null) }; From 2eebead81cf2e3acc5524b8bee0d9f2b8e85612d Mon Sep 17 00:00:00 2001 From: louis <869322496@qq.com> Date: Wed, 17 Apr 2024 10:24:09 +0800 Subject: [PATCH 63/64] feat: default domain --- src/common/decorators/domain.decorator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/decorators/domain.decorator.ts b/src/common/decorators/domain.decorator.ts index bcd02c2..1ecb045 100644 --- a/src/common/decorators/domain.decorator.ts +++ b/src/common/decorators/domain.decorator.ts @@ -7,7 +7,7 @@ import type { FastifyRequest } from 'fastify'; */ export const Domain = createParamDecorator((_, context: ExecutionContext) => { const request = context.switchToHttp().getRequest(); - return request.headers['sk-domain']; + return request.headers['sk-domain'] ?? 1; }); export type SkDomain = number; From 7e8645781937a22da2ba476f2dfa73048575153f Mon Sep 17 00:00:00 2001 From: yx Date: Fri, 26 Apr 2024 09:24:14 +0800 Subject: [PATCH 64/64] sql --- init_data/sql/hxoa.sql | 1509 +++++++++++++++++++++++++++++++--------- 1 file changed, 1165 insertions(+), 344 deletions(-) diff --git a/init_data/sql/hxoa.sql b/init_data/sql/hxoa.sql index 1edf3cd..57b7b35 100644 --- a/init_data/sql/hxoa.sql +++ b/init_data/sql/hxoa.sql @@ -1,17 +1,17 @@ /* Navicat Premium Data Transfer - Source Server : localhost + Source Server : 本机oa Source Server Type : MySQL - Source Server Version : 80036 + Source Server Version : 80036 (8.0.36) Source Host : localhost:13307 Source Schema : hxoa Target Server Type : MySQL - Target Server Version : 80036 + Target Server Version : 80036 (8.0.36) File Encoding : 65001 - Date: 07/04/2024 11:11:06 + Date: 26/04/2024 09:22:30 */ SET NAMES utf8mb4; @@ -27,29 +27,14 @@ CREATE TABLE `company` ( `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公司名称', `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + `domain` int NOT NULL DEFAULT 1 COMMENT '所属域', PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_a76c5cd486f7779bd9c319afd2`(`name`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + UNIQUE INDEX `IDX_a76c5cd486f7779bd9c319afd2`(`name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 25 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of company -- ---------------------------- -INSERT INTO `company` VALUES (1, '2024-03-04 15:44:43.005593', '2024-03-08 08:48:37.000000', '深圳市立创电子商务有限公司', 0); -INSERT INTO `company` VALUES (4, '2024-03-04 16:05:34.701780', '2024-04-07 09:54:56.000000', '深圳市诚亨泰科技有限公司', 0); -INSERT INTO `company` VALUES (5, '2024-03-04 16:05:38.867786', '2024-03-04 16:05:38.867786', '东莞市顶源电子有限公司', 0); -INSERT INTO `company` VALUES (6, '2024-03-04 16:05:42.479027', '2024-03-04 16:05:42.479027', '深圳市福田区赛格电子市场金佳电子经营部', 0); -INSERT INTO `company` VALUES (7, '2024-03-04 16:05:46.775364', '2024-03-04 16:05:46.775364', '深圳市思界电子科技有限公司', 0); -INSERT INTO `company` VALUES (8, '2024-03-04 16:05:55.806537', '2024-03-04 16:05:55.806537', '广州市星翼电信科技有限公司', 0); -INSERT INTO `company` VALUES (9, '2024-03-04 16:06:03.003860', '2024-03-04 16:09:49.000000', '快递费', 1); -INSERT INTO `company` VALUES (10, '2024-03-04 16:06:09.788572', '2024-03-04 16:06:09.788572', '青岛丰喆精密模具有限公司', 0); -INSERT INTO `company` VALUES (11, '2024-03-04 16:06:12.872983', '2024-03-04 16:06:12.872983', '深圳嘉立创科技集团股份有限公司', 0); -INSERT INTO `company` VALUES (12, '2024-03-04 16:06:19.823410', '2024-03-04 16:06:19.823410', '北京特倍福电子技术有限公司', 0); -INSERT INTO `company` VALUES (13, '2024-03-04 16:06:25.937749', '2024-03-04 16:06:25.937749', '上海脉芯网络科技有限公司', 0); -INSERT INTO `company` VALUES (14, '2024-03-22 11:01:20.588144', '2024-03-22 11:01:20.588144', '深圳市声能达科技有限公司', 0); -INSERT INTO `company` VALUES (15, '2024-03-26 10:29:45.595059', '2024-03-26 10:29:45.595059', '深圳市新得润电子有限公司', 0); -INSERT INTO `company` VALUES (16, '2024-04-05 08:47:49.227114', '2024-04-05 08:47:49.227114', '山东矿机华信智能科技有限公司', 0); -INSERT INTO `company` VALUES (17, '2024-04-05 10:03:44.190698', '2024-04-05 10:03:44.190698', '广东润宇传感器股份有限公司', 0); -INSERT INTO `company` VALUES (18, '2024-04-05 15:21:21.989529', '2024-04-05 15:21:21.989529', '宝鸡兴宇腾测控设备有限公司', 0); -- ---------------------------- -- Table structure for company_storage @@ -59,8 +44,8 @@ CREATE TABLE `company_storage` ( `company_id` int NOT NULL, `file_id` int NOT NULL, PRIMARY KEY (`company_id`, `file_id`) USING BTREE, - INDEX `IDX_0958ee6ca6f52985840624bb91`(`company_id`) USING BTREE, - INDEX `IDX_bdd3a301229b9dec4b95549dfe`(`file_id`) USING BTREE, + INDEX `IDX_0958ee6ca6f52985840624bb91`(`company_id` ASC) USING BTREE, + INDEX `IDX_bdd3a301229b9dec4b95549dfe`(`file_id` ASC) USING BTREE, CONSTRAINT `FK_0958ee6ca6f52985840624bb916` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_bdd3a301229b9dec4b95549dfe7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; @@ -86,8 +71,9 @@ CREATE TABLE `contract` ( `delivery_deadline` date NULL DEFAULT NULL, `status` tinyint NOT NULL DEFAULT 0 COMMENT '审核状态(字典)', `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + `domain` int NOT NULL DEFAULT 1 COMMENT '所属域', PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_a2f8461960ce0fcbd0d6551009`(`contract_number`) USING BTREE + UNIQUE INDEX `IDX_a2f8461960ce0fcbd0d6551009`(`contract_number` ASC) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- @@ -102,8 +88,8 @@ CREATE TABLE `contract_storage` ( `contract_id` int NOT NULL, `file_id` int NOT NULL, PRIMARY KEY (`contract_id`, `file_id`) USING BTREE, - INDEX `IDX_b0a3f22af56decbc128c674447`(`contract_id`) USING BTREE, - INDEX `IDX_2fe7cda0f292b099b7e13f8f61`(`file_id`) USING BTREE, + INDEX `IDX_b0a3f22af56decbc128c674447`(`contract_id` ASC) USING BTREE, + INDEX `IDX_2fe7cda0f292b099b7e13f8f61`(`file_id` ASC) USING BTREE, CONSTRAINT `FK_2fe7cda0f292b099b7e13f8f612` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_b0a3f22af56decbc128c674447e` FOREIGN KEY (`contract_id`) REFERENCES `contract` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; @@ -112,6 +98,24 @@ CREATE TABLE `contract_storage` ( -- Records of contract_storage -- ---------------------------- +-- ---------------------------- +-- Table structure for domain +-- ---------------------------- +DROP TABLE IF EXISTS `domain`; +CREATE TABLE `domain` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '域标题', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of domain +-- ---------------------------- +INSERT INTO `domain` VALUES (1, '2024-04-16 14:06:39.905159', '2024-04-26 09:18:38.920023', '山东矿机华信智能科技', 0); + -- ---------------------------- -- Table structure for materials_in_out -- ---------------------------- @@ -133,24 +137,19 @@ CREATE TABLE `materials_in_out` ( `project_id` int NULL DEFAULT NULL COMMENT '项目', `inventory_id` int NOT NULL COMMENT '库存', `time` datetime NULL DEFAULT NULL COMMENT '时间', + `domain` int NOT NULL DEFAULT 1 COMMENT '所属域', PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_770f1c4afd9631499ccc08bd58b`(`product_id`) USING BTREE, - INDEX `FK_7a5bd19f8fd458f6336efedf765`(`project_id`) USING BTREE, - INDEX `FK_f5dc1f1e4db2f990ef89f0398fa`(`inventory_id`) USING BTREE, + INDEX `FK_770f1c4afd9631499ccc08bd58b`(`product_id` ASC) USING BTREE, + INDEX `FK_7a5bd19f8fd458f6336efedf765`(`project_id` ASC) USING BTREE, + INDEX `FK_f5dc1f1e4db2f990ef89f0398fa`(`inventory_id` ASC) USING BTREE, CONSTRAINT `FK_770f1c4afd9631499ccc08bd58b` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_7a5bd19f8fd458f6336efedf765` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_f5dc1f1e4db2f990ef89f0398fa` FOREIGN KEY (`inventory_id`) REFERENCES `materials_inventory` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 196 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 352 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of materials_in_out -- ---------------------------- -INSERT INTO `materials_in_out` VALUES (190, '2024-04-05 09:13:45.815541', '2024-04-05 09:13:45.000000', 36, 0, 100, 0.0000000000, 0.0000000000, '孟菲', NULL, '', 0, 'SI1017', 13, 68, NULL); -INSERT INTO `materials_in_out` VALUES (191, '2024-04-05 09:19:39.482730', '2024-04-05 09:19:39.000000', 35, 0, 100, 0.0000000000, 0.0000000000, '孟菲', NULL, '', 0, 'SI1018', 13, 69, NULL); -INSERT INTO `materials_in_out` VALUES (192, '2024-04-05 10:22:32.422926', '2024-04-05 10:22:32.000000', 37, 0, 83, 557.5200000000, 46274.1600000000, '孟菲', NULL, '', 0, 'SI1019', 13, 70, NULL); -INSERT INTO `materials_in_out` VALUES (193, '2024-04-05 14:58:36.121528', '2024-04-05 14:58:48.000000', 36, 0, 300, 0.0000000000, 0.0000000000, '王兴昊', NULL, NULL, 0, 'SI1020', 13, 68, NULL); -INSERT INTO `materials_in_out` VALUES (194, '2024-04-05 15:00:20.218109', '2024-04-05 15:00:20.218109', 35, 0, 300, 0.0000000000, 0.0000000000, '王兴昊', NULL, NULL, 0, 'SI1021', 13, 69, NULL); -INSERT INTO `materials_in_out` VALUES (195, '2024-04-05 15:26:13.804510', '2024-04-05 15:26:13.000000', 38, 0, 130, 0.0000000000, 0.0000000000, '王兴昊', NULL, '', 0, 'SI1022', 13, 71, NULL); -- ---------------------------- -- Table structure for materials_in_out_storage @@ -160,8 +159,8 @@ CREATE TABLE `materials_in_out_storage` ( `materials_in_out_id` int NOT NULL, `file_id` int NOT NULL, PRIMARY KEY (`materials_in_out_id`, `file_id`) USING BTREE, - INDEX `IDX_9df13ab4d4747575c310668581`(`materials_in_out_id`) USING BTREE, - INDEX `IDX_96c00bfbcd71e93a6cc070e8e6`(`file_id`) USING BTREE, + INDEX `IDX_9df13ab4d4747575c310668581`(`materials_in_out_id` ASC) USING BTREE, + INDEX `IDX_96c00bfbcd71e93a6cc070e8e6`(`file_id` ASC) USING BTREE, CONSTRAINT `FK_96c00bfbcd71e93a6cc070e8e6c` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_9df13ab4d4747575c3106685810` FOREIGN KEY (`materials_in_out_id`) REFERENCES `materials_in_out` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; @@ -186,20 +185,17 @@ CREATE TABLE `materials_inventory` ( `project_id` int NOT NULL COMMENT '项目', `position` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '库存位置', `inventory_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '库存编号', + `domain` int NOT NULL DEFAULT 1 COMMENT '所属域', PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_3915e159f03408035a62721d5be`(`project_id`) USING BTREE, - INDEX `FK_413008d6a91b215b66971c9a9e8`(`product_id`) USING BTREE, + INDEX `FK_3915e159f03408035a62721d5be`(`project_id` ASC) USING BTREE, + INDEX `FK_413008d6a91b215b66971c9a9e8`(`product_id` ASC) USING BTREE, CONSTRAINT `FK_3915e159f03408035a62721d5be` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_413008d6a91b215b66971c9a9e8` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 72 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 137 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of materials_inventory -- ---------------------------- -INSERT INTO `materials_inventory` VALUES (68, '2024-04-05 09:13:45.802134', '2024-04-05 14:58:36.000000', NULL, 0, 36, 400, 0.0000000000, 13, '库房一, 第三排, 第二层', 'S1014'); -INSERT INTO `materials_inventory` VALUES (69, '2024-04-05 09:19:39.479124', '2024-04-05 15:00:20.000000', NULL, 0, 35, 400, 0.0000000000, 13, '库房一, 第三排, 第二层', 'S1015'); -INSERT INTO `materials_inventory` VALUES (70, '2024-04-05 10:22:32.418802', '2024-04-05 10:22:32.418802', NULL, 0, 37, 83, 557.5200000000, 13, NULL, 'S1016'); -INSERT INTO `materials_inventory` VALUES (71, '2024-04-05 15:26:13.800776', '2024-04-05 15:26:13.800776', NULL, 0, 38, 130, 0.0000000000, 13, NULL, 'S1017'); -- ---------------------------- -- Table structure for product @@ -217,21 +213,17 @@ CREATE TABLE `product` ( `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `product_specification` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '产品规格', `product_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '产品编号', + `domain` int NOT NULL DEFAULT 1 COMMENT '所属域', PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_a0503db1630a5b8a4d7deabd556`(`company_id`) USING BTREE, - INDEX `FK_b15422982adca3bf53adfb535de`(`unit_id`) USING BTREE, + INDEX `FK_a0503db1630a5b8a4d7deabd556`(`company_id` ASC) USING BTREE, + INDEX `FK_b15422982adca3bf53adfb535de`(`unit_id` ASC) USING BTREE, CONSTRAINT `FK_a0503db1630a5b8a4d7deabd556` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_b15422982adca3bf53adfb535de` FOREIGN KEY (`unit_id`) REFERENCES `sys_dict_item` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 39 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 125 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of product -- ---------------------------- -INSERT INTO `product` VALUES (34, '2024-04-04 14:11:01.924609', '2024-04-04 14:11:01.924609', '网络型控制器按键保护板', 0, 1, 'wangluoxingkongzhiqianjianbaohuban', 15, '网络型控制器保护板', '283*15*4', 'P1027'); -INSERT INTO `product` VALUES (35, '2024-04-05 08:49:29.136458', '2024-04-05 08:49:29.136458', '急停基座', 0, 16, 'jitingjizuo', 15, 'ERP库存名急停端子', 'ZB2BZ102C', 'P1028'); -INSERT INTO `product` VALUES (36, '2024-04-05 08:57:45.214384', '2024-04-05 08:57:45.214384', '急停按钮', 0, 16, 'jitinganniu', 15, NULL, 'ZB2BT4C', 'P1029'); -INSERT INTO `product` VALUES (37, '2024-04-05 10:13:36.340912', '2024-04-05 10:13:36.340912', '传感器', 0, 17, 'chuanganqi', 14, NULL, 'GUC960-700mm', 'P1030'); -INSERT INTO `product` VALUES (38, '2024-04-05 15:21:41.040381', '2024-04-05 15:21:41.040381', '压力传感器', 0, 18, 'yalichuanganqi', 14, NULL, 'GPD60', 'P1031'); -- ---------------------------- -- Table structure for product_storage @@ -241,8 +233,8 @@ CREATE TABLE `product_storage` ( `product_id` int NOT NULL, `file_id` int NOT NULL, PRIMARY KEY (`product_id`, `file_id`) USING BTREE, - INDEX `IDX_6dd288598f0a0ea3f72f31cb42`(`product_id`) USING BTREE, - INDEX `IDX_eecbd68d7d4d565baecee2d76c`(`file_id`) USING BTREE, + INDEX `IDX_6dd288598f0a0ea3f72f31cb42`(`product_id` ASC) USING BTREE, + INDEX `IDX_eecbd68d7d4d565baecee2d76c`(`file_id` ASC) USING BTREE, CONSTRAINT `FK_6dd288598f0a0ea3f72f31cb422` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_eecbd68d7d4d565baecee2d76c7` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; @@ -261,21 +253,14 @@ CREATE TABLE `project` ( `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目名称', `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + `domain` int NOT NULL DEFAULT 1 COMMENT '所属域', PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_dedfea394088ed136ddadeee89`(`name`) USING BTREE + UNIQUE INDEX `IDX_dedfea394088ed136ddadeee89`(`name` ASC) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of project -- ---------------------------- -INSERT INTO `project` VALUES (1, '2024-03-07 09:35:15.276345', '2024-03-08 08:48:21.000000', '星火项目', 0); -INSERT INTO `project` VALUES (2, '2024-03-07 09:35:20.004729', '2024-03-07 09:35:20.004729', '东大项目', 0); -INSERT INTO `project` VALUES (3, '2024-03-07 09:35:29.213057', '2024-03-07 09:35:29.213057', '沙湾煤业项目', 0); -INSERT INTO `project` VALUES (13, '2024-04-02 13:53:11.973237', '2024-04-04 14:15:15.000000', '未分类项目', 0); -INSERT INTO `project` VALUES (14, '2024-04-04 14:13:50.885496', '2024-04-04 14:13:50.885496', '七台河煤矿', 0); -INSERT INTO `project` VALUES (15, '2024-04-04 14:14:02.655982', '2024-04-04 14:14:02.655982', '红旗煤矿', 0); -INSERT INTO `project` VALUES (16, '2024-04-04 14:14:19.407108', '2024-04-04 14:14:19.407108', '首旺煤矿', 0); -INSERT INTO `project` VALUES (17, '2024-04-04 14:14:43.054552', '2024-04-04 14:14:43.054552', '沫凤项目', 0); -- ---------------------------- -- Table structure for project_storage @@ -285,8 +270,8 @@ CREATE TABLE `project_storage` ( `project_id` int NOT NULL, `file_id` int NOT NULL, PRIMARY KEY (`project_id`, `file_id`) USING BTREE, - INDEX `IDX_9058e954f8f09e2cfa2261c1f2`(`project_id`) USING BTREE, - INDEX `IDX_ac08ac8e4f973873f03dafaca2`(`file_id`) USING BTREE, + INDEX `IDX_9058e954f8f09e2cfa2261c1f2`(`project_id` ASC) USING BTREE, + INDEX `IDX_ac08ac8e4f973873f03dafaca2`(`file_id` ASC) USING BTREE, CONSTRAINT `FK_9058e954f8f09e2cfa2261c1f26` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_ac08ac8e4f973873f03dafaca2b` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; @@ -295,6 +280,102 @@ CREATE TABLE `project_storage` ( -- Records of project_storage -- ---------------------------- +-- ---------------------------- +-- Table structure for sale_quotation_component +-- ---------------------------- +DROP TABLE IF EXISTS `sale_quotation_component`; +CREATE TABLE `sale_quotation_component` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '报价配件名称', + `component_specification` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '产品规格', + `unit_id` int NULL DEFAULT NULL COMMENT '单位(字典)', + `unit_price` decimal(15, 10) NOT NULL DEFAULT 0.0000000000 COMMENT '单价', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_c72786ab8ebfa50d0c4d04dc849`(`unit_id` ASC) USING BTREE, + CONSTRAINT `FK_c72786ab8ebfa50d0c4d04dc849` FOREIGN KEY (`unit_id`) REFERENCES `sys_dict_item` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sale_quotation_component +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sale_quotation_component_storage +-- ---------------------------- +DROP TABLE IF EXISTS `sale_quotation_component_storage`; +CREATE TABLE `sale_quotation_component_storage` ( + `sale_quotation_component_id` int NOT NULL, + `file_id` int NOT NULL, + PRIMARY KEY (`sale_quotation_component_id`, `file_id`) USING BTREE, + INDEX `IDX_d0cec9d3031b742a2ed6d432aa`(`sale_quotation_component_id` ASC) USING BTREE, + INDEX `IDX_a1f492fbfa21c87875d798f9cc`(`file_id` ASC) USING BTREE, + CONSTRAINT `FK_a1f492fbfa21c87875d798f9ccf` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_d0cec9d3031b742a2ed6d432aa5` FOREIGN KEY (`sale_quotation_component_id`) REFERENCES `sale_quotation_component` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sale_quotation_component_storage +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sale_quotation_group +-- ---------------------------- +DROP TABLE IF EXISTS `sale_quotation_group`; +CREATE TABLE `sale_quotation_group` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '报价分组名称', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_36b6e3b1403d41179974bd96c2`(`name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sale_quotation_group +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sale_quotation_group_component +-- ---------------------------- +DROP TABLE IF EXISTS `sale_quotation_group_component`; +CREATE TABLE `sale_quotation_group_component` ( + `group_id` int NOT NULL, + `component_id` int NOT NULL, + PRIMARY KEY (`group_id`, `component_id`) USING BTREE, + INDEX `IDX_77eedaa2f083d9dfd78b888642`(`group_id` ASC) USING BTREE, + INDEX `IDX_383f9e30f008e42701a7187d88`(`component_id` ASC) USING BTREE, + CONSTRAINT `FK_383f9e30f008e42701a7187d880` FOREIGN KEY (`component_id`) REFERENCES `sale_quotation_component` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `FK_77eedaa2f083d9dfd78b888642a` FOREIGN KEY (`group_id`) REFERENCES `sale_quotation_group` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sale_quotation_group_component +-- ---------------------------- + +-- ---------------------------- +-- Table structure for sale_quotation_template +-- ---------------------------- +DROP TABLE IF EXISTS `sale_quotation_template`; +CREATE TABLE `sale_quotation_template` ( + `id` int NOT NULL AUTO_INCREMENT, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '报价模板名称', + `is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除', + `template` json NULL COMMENT '报价模板', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `IDX_d92d8a344eb4b47500ab96f7f3`(`name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 37 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of sale_quotation_template +-- ---------------------------- + -- ---------------------------- -- Table structure for sys_captcha_log -- ---------------------------- @@ -308,7 +389,7 @@ CREATE TABLE `sys_captcha_log` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_captcha_log @@ -327,20 +408,20 @@ CREATE TABLE `sys_config` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_2c363c25cf99bcaab3a7f389ba`(`key`) USING BTREE + UNIQUE INDEX `IDX_2c363c25cf99bcaab3a7f389ba`(`key` ASC) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_config -- ---------------------------- -INSERT INTO `sys_config` VALUES (1, 'sys_user_initPassword', '初始密码', '123456', '创建管理员账号的初始密码', '2023-11-10 00:31:44.154921', '2023-11-10 00:31:44.161263'); +INSERT INTO `sys_config` VALUES (1, 'sys_user_initPassword', '初始密码', 'a123456', '创建管理员账号的初始密码', '2023-11-10 00:31:44.154921', '2024-04-08 08:36:10.000000'); INSERT INTO `sys_config` VALUES (2, 'sys_api_token', 'API Token', 'huaxin-admin', '用于请求 @ApiToken 的控制器', '2023-11-10 00:31:44.154921', '2024-01-29 09:52:27.000000'); INSERT INTO `sys_config` VALUES (3, 'companyName', '公司名称', '华信智能', '菜单侧栏公司的名称', '2024-03-06 13:06:47.347660', '2024-03-06 13:07:18.000000'); INSERT INTO `sys_config` VALUES (4, 'inventory_inout_number_prefix_in', '人库单号前缀', 'SI', '人库单号前缀', '2024-03-06 14:50:04.844992', '2024-03-25 15:52:28.000000'); INSERT INTO `sys_config` VALUES (5, 'inventory_inout_number_prefix_out', '出库单号前缀', 'SO', '出库单号前缀', '2024-03-22 13:37:21.165879', '2024-03-25 15:52:32.000000'); INSERT INTO `sys_config` VALUES (6, 'product_number_prefix', '产品编号前缀', 'P', '产品编号前缀', '2024-03-22 15:51:10.960064', '2024-03-22 15:51:10.960064'); INSERT INTO `sys_config` VALUES (7, 'inventory_number_prefix', '库存编号', 'S', '库存编号', '2024-03-25 15:54:08.836711', '2024-03-25 15:54:08.836711'); -INSERT INTO `sys_config` VALUES (8, 'app_version', 'app版本号', '1.0.1', 'app版本号', '2024-04-07 10:32:41.513615', '2024-04-07 10:32:41.513615'); +INSERT INTO `sys_config` VALUES (8, 'app_version', 'app版本号', '1.0.4', 'app版本号', '2024-04-07 10:32:41.513615', '2024-04-15 15:19:30.000000'); INSERT INTO `sys_config` VALUES (9, 'is_app_force_upgrade', 'app是否强制更新', '1', 'app是否强制更新。一般用于必须升级的更新需求。', '2024-04-07 10:33:07.732646', '2024-04-07 10:33:07.732646'); -- ---------------------------- @@ -356,7 +437,7 @@ CREATE TABLE `sys_dept` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_c75280b01c49779f2323536db67`(`parentId`) USING BTREE, + INDEX `FK_c75280b01c49779f2323536db67`(`parentId` ASC) USING BTREE, CONSTRAINT `FK_c75280b01c49779f2323536db67` FOREIGN KEY (`parentId`) REFERENCES `sys_dept` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT ) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; @@ -382,8 +463,8 @@ CREATE TABLE `sys_dict` ( `status` tinyint NOT NULL DEFAULT 1, `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_d112365748f740ee260b65ce91`(`name`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + UNIQUE INDEX `IDX_d112365748f740ee260b65ce91`(`name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict @@ -408,9 +489,9 @@ CREATE TABLE `sys_dict_item` ( `orderNo` int NULL DEFAULT NULL COMMENT '字典项排序', `deleted_at` datetime(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_d68ea74fcb041c8cfd1fd659844`(`type_id`) USING BTREE, + INDEX `FK_d68ea74fcb041c8cfd1fd659844`(`type_id` ASC) USING BTREE, CONSTRAINT `FK_d68ea74fcb041c8cfd1fd659844` FOREIGN KEY (`type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 49 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 53 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict_item @@ -459,6 +540,10 @@ INSERT INTO `sys_dict_item` VALUES (45, '2024-04-02 12:26:59.178581', '2024-04-0 INSERT INTO `sys_dict_item` VALUES (46, '2024-04-02 12:27:05.494866', '2024-04-02 12:27:05.494866', 1, 1, '煤机定位部分', 'sales_quotation_group_7', NULL, 1, NULL, 10, 6, '2024-04-02 12:27:05.494866'); INSERT INTO `sys_dict_item` VALUES (47, '2024-04-02 12:27:12.313251', '2024-04-02 12:27:12.313251', 1, 1, '姿态检测部分', 'sales_quotation_group_8', NULL, 1, NULL, 10, 7, '2024-04-02 12:27:12.313251'); INSERT INTO `sys_dict_item` VALUES (48, '2024-04-02 15:01:53.004626', '2024-04-02 15:01:53.004626', 1, 1, 'A区', 'areaA', NULL, 1, NULL, 8, 1, '2024-04-02 15:01:53.004626'); +INSERT INTO `sys_dict_item` VALUES (49, '2024-04-09 15:20:07.803534', '2024-04-09 15:20:07.803534', 1, 1, '米', 'unit-mi', NULL, 1, NULL, 5, 0, '2024-04-09 15:20:07.803534'); +INSERT INTO `sys_dict_item` VALUES (50, '2024-04-10 08:30:04.153002', '2024-04-10 08:30:04.153002', 1, 1, '组', 'unit-zu', NULL, 1, NULL, 5, 0, '2024-04-10 08:30:04.153002'); +INSERT INTO `sys_dict_item` VALUES (51, '2024-04-11 10:56:36.109160', '2024-04-11 10:56:36.109160', 1, 1, '台', 'unit_tai', NULL, 1, NULL, 5, 0, '2024-04-11 10:56:36.109160'); +INSERT INTO `sys_dict_item` VALUES (52, '2024-04-11 11:00:36.578102', '2024-04-11 11:00:36.578102', 1, 1, '根', 'unit_gen', NULL, 1, NULL, 5, 10, '2024-04-11 11:00:36.578102'); -- ---------------------------- -- Table structure for sys_dict_type @@ -476,7 +561,7 @@ CREATE TABLE `sys_dict_type` ( `code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `deleted_at` datetime(6) NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_74d0045ff7fab9f67adc0b1bda`(`code`) USING BTREE + UNIQUE INDEX `IDX_74d0045ff7fab9f67adc0b1bda`(`code` ASC) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- @@ -506,84 +591,22 @@ CREATE TABLE `sys_login_log` ( `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_id` int NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_3029712e0df6a28edaee46fd470`(`user_id`) USING BTREE, + INDEX `FK_3029712e0df6a28edaee46fd470`(`user_id` ASC) USING BTREE, CONSTRAINT `FK_3029712e0df6a28edaee46fd470` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 72 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_login_log -- ---------------------------- -INSERT INTO `sys_login_log` VALUES (1, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-01 08:50:32.291716', '2024-04-01 08:50:32.291716', 1); -INSERT INTO `sys_login_log` VALUES (2, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 14:37:00.079842', '2024-04-01 14:37:00.079842', 1); -INSERT INTO `sys_login_log` VALUES (3, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:11:30.159405', '2024-04-01 15:11:30.159405', 1); -INSERT INTO `sys_login_log` VALUES (4, '144.0.23.133', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0', '山东省青岛市', NULL, '2024-04-01 15:15:45.157349', '2024-04-01 15:15:45.157349', 1); -INSERT INTO `sys_login_log` VALUES (5, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:34:38.228762', '2024-04-01 15:34:38.228762', 1); -INSERT INTO `sys_login_log` VALUES (6, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:41:22.061919', '2024-04-01 15:41:22.061919', 1); -INSERT INTO `sys_login_log` VALUES (7, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:44:13.373410', '2024-04-01 15:44:13.373410', 1); -INSERT INTO `sys_login_log` VALUES (8, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 15:47:23.396192', '2024-04-01 15:47:23.396192', 1); -INSERT INTO `sys_login_log` VALUES (9, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 16:16:31.291096', '2024-04-01 16:16:31.291096', 1); -INSERT INTO `sys_login_log` VALUES (10, '144.0.23.133', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-01 16:21:30.780126', '2024-04-01 16:21:30.780126', 1); -INSERT INTO `sys_login_log` VALUES (11, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-01 16:49:57.352530', '2024-04-01 16:49:57.352530', 1); -INSERT INTO `sys_login_log` VALUES (12, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-01 16:50:13.073327', '2024-04-01 16:50:13.073327', 1); -INSERT INTO `sys_login_log` VALUES (13, '223.104.195.74', 'Dart/3.2 (dart:io)', '山东省潍坊市', NULL, '2024-04-01 17:16:04.439246', '2024-04-01 17:16:04.439246', 1); -INSERT INTO `sys_login_log` VALUES (14, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-01 17:30:31.958849', '2024-04-01 17:30:31.958849', 1); -INSERT INTO `sys_login_log` VALUES (15, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 10:09:56.131711', '2024-04-02 10:09:56.131711', 1); -INSERT INTO `sys_login_log` VALUES (16, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 10:19:42.684419', '2024-04-02 10:19:42.684419', 1); -INSERT INTO `sys_login_log` VALUES (17, '112.224.65.182', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省济南市', NULL, '2024-04-02 14:56:57.968284', '2024-04-02 14:56:57.968284', 1); -INSERT INTO `sys_login_log` VALUES (18, '112.224.65.182', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省济南市', NULL, '2024-04-02 14:59:28.837362', '2024-04-02 14:59:28.837362', 9); -INSERT INTO `sys_login_log` VALUES (19, '112.224.65.182', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省济南市', NULL, '2024-04-02 14:59:48.312224', '2024-04-02 14:59:48.312224', 1); -INSERT INTO `sys_login_log` VALUES (20, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-02 15:38:45.330867', '2024-04-02 15:38:45.330867', 1); -INSERT INTO `sys_login_log` VALUES (21, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-02 15:39:52.649278', '2024-04-02 15:39:52.649278', 10); -INSERT INTO `sys_login_log` VALUES (22, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 15:40:20.394755', '2024-04-02 15:40:20.394755', 10); -INSERT INTO `sys_login_log` VALUES (23, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:04:31.143028', '2024-04-02 16:04:31.143028', 1); -INSERT INTO `sys_login_log` VALUES (24, '112.224.65.182', 'Dart/3.3 (dart:io)', '山东省济南市', NULL, '2024-04-02 16:18:52.597148', '2024-04-02 16:18:52.597148', 1); -INSERT INTO `sys_login_log` VALUES (25, '112.224.65.182', 'Dart/3.3 (dart:io)', '山东省济南市', NULL, '2024-04-02 16:21:03.861179', '2024-04-02 16:21:03.861179', 1); -INSERT INTO `sys_login_log` VALUES (26, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:22:06.193594', '2024-04-02 16:22:06.193594', 1); -INSERT INTO `sys_login_log` VALUES (27, '221.1.97.166', 'Dart/3.3 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:27:03.753690', '2024-04-02 16:27:03.753690', 1); -INSERT INTO `sys_login_log` VALUES (28, '221.1.97.166', 'Dart/3.3 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:34:35.796027', '2024-04-02 16:34:35.796027', 1); -INSERT INTO `sys_login_log` VALUES (29, '221.1.97.166', 'Dart/3.3 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:35:12.372648', '2024-04-02 16:35:12.372648', 1); -INSERT INTO `sys_login_log` VALUES (30, '221.1.97.166', 'Dart/3.3 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:51:18.226019', '2024-04-02 16:51:18.226019', 1); -INSERT INTO `sys_login_log` VALUES (31, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:52:11.095661', '2024-04-02 16:52:11.095661', 1); -INSERT INTO `sys_login_log` VALUES (32, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-02 16:52:46.246857', '2024-04-02 16:52:46.246857', 1); -INSERT INTO `sys_login_log` VALUES (33, '223.104.195.90', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x1800302c) NetType/4G Language/zh_CN', '山东省潍坊市', NULL, '2024-04-02 18:06:51.031947', '2024-04-02 18:06:51.031947', 10); -INSERT INTO `sys_login_log` VALUES (34, '17.232.78.156', 'Dart/3.3 (dart:io)', '美国Apple', NULL, '2024-04-02 20:38:53.231472', '2024-04-02 20:38:53.231472', 1); -INSERT INTO `sys_login_log` VALUES (35, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x63090a13) XWEB/9079 Flue', '山东省潍坊市昌乐县', NULL, '2024-04-03 08:08:44.705984', '2024-04-03 08:08:44.705984', 1); -INSERT INTO `sys_login_log` VALUES (36, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B) WindowsWechat(0x6309092b) XWEB/9079 Flue', '山东省潍坊市昌乐县', NULL, '2024-04-03 08:09:10.249023', '2024-04-03 08:09:10.249023', 9); -INSERT INTO `sys_login_log` VALUES (37, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-03 15:15:03.718007', '2024-04-03 15:15:03.718007', 1); -INSERT INTO `sys_login_log` VALUES (38, '144.0.23.133', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-03 15:38:42.155118', '2024-04-03 15:38:42.155118', 1); -INSERT INTO `sys_login_log` VALUES (39, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-03 18:25:46.887892', '2024-04-03 18:25:46.887892', 1); -INSERT INTO `sys_login_log` VALUES (40, '221.1.97.166', 'Dart/3.2 (dart:io)', '山东省潍坊市昌乐县', NULL, '2024-04-04 13:54:25.249419', '2024-04-04 13:54:25.249419', 1); -INSERT INTO `sys_login_log` VALUES (41, '112.224.195.64', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x1800302c) NetType/4G Language/zh_CN', '山东省济南市', NULL, '2024-04-04 13:55:42.245768', '2024-04-04 13:55:42.245768', 1); -INSERT INTO `sys_login_log` VALUES (42, '221.1.97.166', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.47(0x18002f2c) NetType/4G Language/zh_CN', '山东省潍坊市昌乐县', NULL, '2024-04-04 13:58:47.699965', '2024-04-04 13:58:47.699965', 1); -INSERT INTO `sys_login_log` VALUES (43, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:00:07.465436', '2024-04-04 14:00:07.465436', 1); -INSERT INTO `sys_login_log` VALUES (44, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:00:17.602620', '2024-04-04 14:00:17.602620', 1); -INSERT INTO `sys_login_log` VALUES (45, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:00:42.762900', '2024-04-04 14:00:42.762900', 1); -INSERT INTO `sys_login_log` VALUES (46, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:01:48.207951', '2024-04-04 14:01:48.207951', 1); -INSERT INTO `sys_login_log` VALUES (47, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:02:33.248728', '2024-04-04 14:02:33.248728', 1); -INSERT INTO `sys_login_log` VALUES (48, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:02:49.821556', '2024-04-04 14:02:49.821556', 1); -INSERT INTO `sys_login_log` VALUES (49, '221.1.97.166', 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:03:01.518252', '2024-04-04 14:03:01.518252', 1); -INSERT INTO `sys_login_log` VALUES (50, '221.1.97.166', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/534.24 Device/pipa Model/23043RP34C XiaoMi/MiuiBrowser/14.7.76', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:04:11.038817', '2024-04-04 14:04:11.038817', 1); -INSERT INTO `sys_login_log` VALUES (51, '221.1.97.166', 'Mozilla/5.0 (Linux; U; Android 13; zh-CN; 23043RP34C Build/TKQ1.221114.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.11.0.530 Mobile Safari/537.36', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:06:02.846849', '2024-04-04 14:06:02.846849', 1); -INSERT INTO `sys_login_log` VALUES (52, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-04 14:06:05.939892', '2024-04-04 14:06:05.939892', 1); -INSERT INTO `sys_login_log` VALUES (53, '223.104.195.112', 'Dart/3.2 (dart:io)', '山东省潍坊市', NULL, '2024-04-05 08:58:35.158404', '2024-04-05 08:58:35.158404', 1); -INSERT INTO `sys_login_log` VALUES (54, '221.1.97.166', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省潍坊市昌乐县', NULL, '2024-04-05 14:54:34.211950', '2024-04-05 14:54:34.211950', 1); -INSERT INTO `sys_login_log` VALUES (55, '112.226.20.42', 'Dart/3.3 (dart:io)', '山东省青岛市', NULL, '2024-04-06 23:33:40.648379', '2024-04-06 23:33:40.648379', 1); -INSERT INTO `sys_login_log` VALUES (56, '144.0.23.133', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省青岛市', NULL, '2024-04-07 10:25:18.662022', '2024-04-07 10:25:18.662022', 1); -INSERT INTO `sys_login_log` VALUES (57, '144.0.23.133', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省青岛市', NULL, '2024-04-07 10:43:27.092753', '2024-04-07 10:43:27.092753', 1); -INSERT INTO `sys_login_log` VALUES (58, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-04-07 10:48:59.426068', '2024-04-07 10:48:59.426068', 9); -INSERT INTO `sys_login_log` VALUES (59, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:49:55.017103', '2024-04-07 10:49:55.017103', 9); -INSERT INTO `sys_login_log` VALUES (60, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:50:14.432721', '2024-04-07 10:50:14.432721', 1); -INSERT INTO `sys_login_log` VALUES (61, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:51:14.143775', '2024-04-07 10:51:14.143775', 9); -INSERT INTO `sys_login_log` VALUES (62, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:52:21.472549', '2024-04-07 10:52:21.472549', 1); -INSERT INTO `sys_login_log` VALUES (63, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:53:25.410445', '2024-04-07 10:53:25.410445', 9); -INSERT INTO `sys_login_log` VALUES (64, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:54:11.422209', '2024-04-07 10:54:11.422209', 1); -INSERT INTO `sys_login_log` VALUES (65, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:58:09.630300', '2024-04-07 10:58:09.630300', 1); -INSERT INTO `sys_login_log` VALUES (66, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:58:48.213228', '2024-04-07 10:58:48.213228', 9); -INSERT INTO `sys_login_log` VALUES (67, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:59:16.336015', '2024-04-07 10:59:16.336015', 9); -INSERT INTO `sys_login_log` VALUES (68, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 10:59:52.597361', '2024-04-07 10:59:52.597361', 9); -INSERT INTO `sys_login_log` VALUES (69, '127.0.0.1', 'Dart/3.2 (dart:io)', '内网IP', NULL, '2024-04-07 11:00:39.570222', '2024-04-07 11:00:39.570222', 9); -INSERT INTO `sys_login_log` VALUES (70, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 11:01:59.666647', '2024-04-07 11:01:59.666647', 1); -INSERT INTO `sys_login_log` VALUES (71, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '内网IP', NULL, '2024-04-07 11:07:30.060388', '2024-04-07 11:07:30.060388', 1); +INSERT INTO `sys_login_log` VALUES (1, '144.0.23.35', 'Dart/3.2 (dart:io)', '山东省青岛市', NULL, '2024-04-24 11:40:22.296748', '2024-04-24 11:40:22.296748', 1); +INSERT INTO `sys_login_log` VALUES (2, '144.0.23.35', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省青岛市', NULL, '2024-04-24 16:43:10.796260', '2024-04-24 16:43:10.796260', 1); +INSERT INTO `sys_login_log` VALUES (3, '144.0.23.35', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0', '山东省青岛市', NULL, '2024-04-24 16:51:35.305803', '2024-04-24 16:51:35.305803', 1); +INSERT INTO `sys_login_log` VALUES (4, '144.0.23.35', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', '山东省青岛市', NULL, '2024-04-25 10:16:26.940392', '2024-04-25 10:16:26.940392', 1); +INSERT INTO `sys_login_log` VALUES (5, '144.0.23.35', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', '山东省青岛市', NULL, '2024-04-25 10:16:37.951803', '2024-04-25 10:16:37.951803', 1); +INSERT INTO `sys_login_log` VALUES (6, '144.0.23.35', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0', '山东省青岛市', NULL, '2024-04-25 10:21:25.946468', '2024-04-25 10:21:25.946468', 1); +INSERT INTO `sys_login_log` VALUES (7, '144.0.23.35', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', '山东省青岛市', NULL, '2024-04-26 08:36:53.552759', '2024-04-26 08:36:53.552759', 1); +INSERT INTO `sys_login_log` VALUES (8, '144.0.23.35', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0', '山东省青岛市', NULL, '2024-04-26 08:45:41.724538', '2024-04-26 08:45:41.724538', 1); +INSERT INTO `sys_login_log` VALUES (9, '172.18.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', '内网IP', NULL, '2024-04-26 09:19:12.821231', '2024-04-26 09:19:12.821231', 1); -- ---------------------------- -- Table structure for sys_menu @@ -591,14 +614,14 @@ INSERT INTO `sys_login_log` VALUES (71, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10 DROP TABLE IF EXISTS `sys_menu`; CREATE TABLE `sys_menu` ( `id` int NOT NULL AUTO_INCREMENT, - `parent_id` int NULL DEFAULT NULL, - `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `parent_id` int NULL DEFAULT NULL COMMENT '父级ID', + `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '前端路径', `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, - `type` tinyint NOT NULL DEFAULT 0, - `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '', + `permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限', + `type` tinyint NOT NULL DEFAULT 0 COMMENT '类型:0-目录 1-菜单 2-权限', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '图标', `order_no` int NULL DEFAULT 0, - `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '前端组件文件地址', `keep_alive` tinyint NOT NULL DEFAULT 1, `show` tinyint NOT NULL DEFAULT 1, `status` tinyint NOT NULL DEFAULT 1, @@ -607,132 +630,155 @@ CREATE TABLE `sys_menu` ( `is_ext` tinyint NOT NULL DEFAULT 0, `ext_open_mode` tinyint NOT NULL DEFAULT 1, `active_menu` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `device` tinyint NOT NULL DEFAULT 1 COMMENT '用户端类型:0-APP 1-PC', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 167 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 189 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_menu -- ---------------------------- -INSERT INTO `sys_menu` VALUES (1, NULL, '/system', '系统管理', '', 0, 'ant-design:setting-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-29 10:41:29.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (2, 1, '/system/user', '用户管理', 'system:user:list', 1, 'ant-design:user-outlined', 0, 'system/user/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:10:30.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (3, 1, '/system/role', '角色管理', 'system:role:list', 1, 'ep:user', 1, 'system/role/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:02.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (4, 1, '/system/menu', '菜单管理', 'system:menu:list', 1, 'ep:menu', 2, 'system/menu/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:18.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-04-02 15:00:20.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (6, 5, '/system/monitor/online', '在线用户', 'system:online:list', 1, '', 0, 'system/monitor/online/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:13:59.519267', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (7, 5, '/sys/monitor/login-log', '登录日志', 'system:log:login:list', 1, '', 0, 'system/monitor/log/login/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:02.610719', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (8, 5, '/system/monitor/serve', '服务监控', 'system:serve:stat', 1, '', 4, 'system/monitor/serve/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:05.606355', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-04-02 15:00:23.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (10, 9, '/system/task', '任务管理', '', 1, '', 0, 'system/schedule/task/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:14:39.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (11, 9, '/system/task/log', '任务日志', 'system:task:list', 1, '', 0, 'system/schedule/log/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:15:01.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (12, NULL, '/document', '文档', '', 0, 'ion:tv-outline', 2, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:51.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (14, 12, 'https://www.typeorm.org/', 'Typeorm中文文档(外链)', NULL, 1, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:39:53.000000', 1, 1, NULL); -INSERT INTO `sys_menu` VALUES (15, 12, 'https://docs.nestjs.cn/', 'Nest.js中文文档(内嵌)', '', 1, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:40:43.000000', 1, 2, NULL); -INSERT INTO `sys_menu` VALUES (20, 2, NULL, '新增', 'system:user:create', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (21, 2, '', '删除', 'system:user:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (22, 2, '', '更新', 'system:user:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (23, 2, '', '查询', 'system:user:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (24, 3, '', '新增', 'system:role:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (25, 3, '', '删除', 'system:role:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (26, 3, '', '修改', 'system:role:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (27, 3, '', '查询', 'system:role:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (28, 4, NULL, '新增', 'system:menu:create', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (29, 4, NULL, '删除', 'system:menu:delete', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (30, 4, '', '修改', 'system:menu:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (31, 4, NULL, '查询', 'system:menu:read', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (32, 6, '', '下线', 'system:online:kick', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (34, 10, '', '新增', 'system:task:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (35, 10, '', '删除', 'system:task:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (36, 10, '', '执行一次', 'system:task:once', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (37, 10, '', '查询', 'system:task:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (38, 10, '', '运行', 'system:task:start', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (39, 10, '', '暂停', 'system:task:stop', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (40, 10, '', '更新', 'system:task:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (41, 7, '', '查询登录日志', 'system:log:login:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (42, 7, '', '查询任务日志', 'system:log:task:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (43, NULL, '/about', '关于', '', 1, 'ant-design:info-circle-outlined', 260, 'account/about', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-10 09:35:41.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (48, NULL, '/tool', '系统工具', NULL, 0, 'ant-design:tool-outlined', 255, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-29 10:41:25.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (49, 48, '/tool/email', '邮件工具', 'system:tools:email', 1, 'ant-design:send-outlined', 1, 'tool/email/index', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:38.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (50, 49, NULL, '发送邮件', 'tools:email:send', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (51, 48, '/tool/storage', '本地存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-03-08 15:13:32.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (52, 51, NULL, '文件上传', 'upload:upload', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 01:04:08.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (53, 51, NULL, '文件删除', 'tool:storage:delete', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:56:01.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (54, 2, NULL, '修改密码', 'system:user:password', 2, '', 5, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (56, 1, '/system/dict-type', '字典管理', 'system:dict-type:list', 1, 'ant-design:book-outlined', 4, 'system/dict-type/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:12.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (57, 56, NULL, '新增', 'system:dict-type:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:20.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (58, 56, NULL, '更新', 'system:dict-type:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:26.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (59, 56, NULL, '删除', 'system:dict-type:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:42.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (60, 56, NULL, '查询', 'system:dict-type:info', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (61, 1, '/system/dept', '部门管理', 'system:dept:list', 1, 'ant-design:deployment-unit-outlined', 3, 'system/dept/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:55.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (62, 61, NULL, '新增', 'system:dept:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (63, 61, NULL, '更新', 'system:dept:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (64, 61, NULL, '删除', 'system:dept:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (65, 61, NULL, '查询', 'system:dept:read', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (68, 5, '/health', '健康检查', '', 1, '', 4, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:33.352155', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (69, 68, NULL, '网络', 'app:health:network', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (70, 68, NULL, '数据库', 'app:health: database', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (86, 1, '/param-config', '参数配置', 'system:param-config:list', 1, 'ep:edit', 255, 'system/param-config/index', 0, 1, 1, '2024-01-10 17:34:52.569663', '2024-01-19 02:11:27.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (87, 86, NULL, '查询', 'system:param-config:read', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:20.983241', '2024-01-10 17:39:20.983241', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (88, 86, NULL, '新增', 'system:param-config:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:57.543510', '2024-01-10 17:39:57.543510', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (89, 86, NULL, '更新', 'system:param-config:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:40:27.355944', '2024-01-10 17:40:27.355944', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (92, 86, NULL, '删除', 'system:param-config:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:57:32.059887', '2024-01-10 17:57:32.059887', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (107, 1, 'system/dict-item/:id', '字典项管理', 'system:dict-item:list', 1, 'ant-design:facebook-outlined', 255, 'system/dict-item/index', 0, 0, 1, '2024-01-28 09:21:17.409532', '2024-01-30 13:09:47.000000', 0, 1, '字典管理'); -INSERT INTO `sys_menu` VALUES (108, 107, NULL, '新增', 'system:dict-item:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:22:39.401758', '2024-01-28 22:38:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (109, 107, NULL, '更新', 'system:dict-item:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:26:43.911886', '2024-01-28 09:26:43.911886', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (110, 107, NULL, '删除', 'system:dict-item:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:28.535225', '2024-01-28 09:27:28.535225', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL); -INSERT INTO `sys_menu` VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 0, 1, '2024-02-10 08:00:02.394616', '2024-03-08 15:14:06.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (116, 48, 'manage', '云盘管理', 'netdisk:manage:list', 1, 'ant-design:cloud-server-outlined', 252, 'netdisk/manage', 0, 0, 1, '2024-02-10 08:03:49.837348', '2024-04-07 10:29:44.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (120, 116, NULL, '删除', 'netdisk:manage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:09.480601', '2024-02-10 08:42:09.480601', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (121, 116, NULL, '获取文件上传token', 'netdisk:manage:token', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:57.688104', '2024-02-10 08:42:57.688104', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (122, 116, NULL, '添加文件备注', 'netdisk:manage:mark', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:43:40.117321', '2024-02-10 08:43:40.117321', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (123, 116, NULL, '下载文件', 'netdisk:manage:download', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:01.338984', '2024-02-10 08:44:01.338984', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (124, 116, NULL, '重命名文件或文件夹', 'netdisk:manage:rename', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:27.233379', '2024-02-10 08:45:36.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (125, 116, NULL, '复制文件或文件夹', 'netdisk:manage:copy', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:44.725391', '2024-02-10 08:45:48.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (126, 116, NULL, '剪切文件或文件夹', 'netdisk:manage:cut', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:45:21.660511', '2024-02-10 08:45:21.660511', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (127, 115, 'overview', '网盘概览', 'netdisk:overview:desc', 1, '', 254, 'netdisk/overview', 0, 1, 1, '2024-02-10 09:32:56.981190', '2024-02-10 09:34:18.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (128, NULL, '/contract', '合同管理', NULL, 0, 'ep:document', 1, NULL, 1, 0, 1, '2024-02-29 10:40:39.080419', '2024-04-02 14:52:28.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (129, 128, '/contract/index', '合同审核', 'app:contract:list', 1, 'ep:document', 1, 'contract/index', 0, 1, 1, '2024-02-29 10:46:09.245521', '2024-02-29 14:59:56.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (130, NULL, '/vehicle-usage/index', '车辆使用', NULL, 1, 'ant-design:car-outlined', 4, 'vehicle-usage/index', 0, 0, 1, '2024-02-29 10:48:35.035363', '2024-04-02 14:53:14.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (131, NULL, '/materials-inventory/record-in-out', '出入库记录', 'materials_inventory:history_in_out:list', 1, 'ep:coin', 3, 'materials-inventory/in-out/index', 0, 1, 1, '2024-02-29 11:03:49.710130', '2024-04-02 14:52:54.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (132, 129, NULL, '更新', 'app:contract:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:00:39.641043', '2024-02-29 15:00:39.641043', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (133, 129, NULL, '删除', 'app:contract:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:00:59.376071', '2024-02-29 15:00:59.376071', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (134, 129, NULL, '查询', 'app:contract:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:01:14.209396', '2024-02-29 15:45:29.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (135, 129, NULL, '新增', 'app:contract:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:44:46.950582', '2024-02-29 15:44:46.950582', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (136, 131, NULL, '新增', 'materials_inventory:history_in_out:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:02.597782', '2024-03-06 10:54:28.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (137, 131, NULL, '更新', 'materials_inventory:history_in_out:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:15.192910', '2024-03-06 10:54:57.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (138, 131, NULL, '查询单个', 'app:contract:read', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:32.488892', '2024-03-01 17:17:32.488892', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (139, 131, NULL, '删除', 'materials_inventory:history_in_out:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:43.455773', '2024-03-06 10:55:06.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (140, NULL, '/materials-inventory/company', '乙方公司管理', 'app:company:list', 1, 'ep:office-building', 6, 'materials-inventory/company/index', 0, 1, 1, '2024-03-04 15:44:30.769048', '2024-03-27 12:57:31.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (141, 140, NULL, '单个查询', 'app:company:read', 2, '', 1, NULL, 1, 1, 1, '2024-03-04 15:45:55.979802', '2024-03-04 15:45:55.979802', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (142, 140, NULL, '新增', 'app:company:create', 2, '', 2, NULL, 1, 1, 1, '2024-03-04 15:46:11.260636', '2024-03-04 15:46:11.260636', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (143, 140, NULL, '更新', 'app:company:update', 2, '', 3, NULL, 1, 1, 1, '2024-03-04 15:46:25.098204', '2024-03-04 15:46:25.098204', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (144, 140, NULL, '删除', 'app:company:delete', 2, '', 4, NULL, 1, 1, 1, '2024-03-04 15:46:50.812446', '2024-03-04 15:46:50.812446', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (145, NULL, '/materials-inventory/product', '产品目录', 'app:product:list', 1, 'ant-design:product-outlined', 6, 'materials-inventory/product/index', 0, 1, 1, '2024-03-04 16:43:22.749281', '2024-03-27 12:56:52.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (146, 145, NULL, '单个查询', 'app:product:read', 2, '', 1, NULL, 1, 1, 1, '2024-03-04 16:44:56.482508', '2024-03-04 16:44:56.482508', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (147, 145, NULL, '新增', 'app:product:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:08.211188', '2024-03-04 16:45:08.211188', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (148, 145, NULL, '更新', 'app:product:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:25.457903', '2024-03-04 16:45:25.457903', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (149, 145, NULL, '删除', 'app:product:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:39.352621', '2024-03-04 16:45:39.352621', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (150, NULL, '/materials-inventory', '原材料盘点', NULL, 0, 'ant-design:dashboard-outlined', 3, NULL, 1, 0, 1, '2024-03-04 16:53:32.172674', '2024-04-02 14:52:58.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (151, 131, NULL, '导出', 'materials_inventory:history_in_out:export', 2, '', 5, NULL, 1, 1, 1, '2024-03-06 13:09:39.201093', '2024-03-06 13:09:39.201093', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (152, NULL, '/materials-inventory/inventory-check', '原材料库存管理', 'app:materials_inventory:list', 1, 'ant-design:dashboard-outlined', 2, 'materials-inventory/inventory-check/index', 1, 1, 1, '2024-03-06 13:33:24.795599', '2024-04-02 14:52:48.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (153, NULL, '/materials-inventory/project', '项目管理', 'app:project:list', 1, 'ep:memo', 4, 'materials-inventory/project/index', 0, 1, 1, '2024-03-07 09:28:19.234454', '2024-03-27 12:57:13.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (154, 153, NULL, '新增', 'app:project:create', 2, '', 1, NULL, 1, 1, 1, '2024-03-07 09:28:47.855064', '2024-03-07 09:28:47.855064', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (155, 153, NULL, '更新', 'app:project:update', 2, '', 2, NULL, 1, 1, 1, '2024-03-07 09:29:03.183084', '2024-03-07 09:29:03.183084', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (156, 153, NULL, '删除', 'app:project:delete', 2, '', 3, NULL, 1, 1, 1, '2024-03-07 09:29:16.684943', '2024-03-07 09:29:16.684943', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (157, 153, NULL, '单个信息', 'app:project:read', 2, '', 4, NULL, 1, 1, 1, '2024-03-07 09:29:33.424578', '2024-03-07 09:29:33.424578', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (158, 131, NULL, '导出原材料盘点表', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 0, '2024-03-07 11:46:54.468400', '2024-04-07 11:02:41.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (159, 130, NULL, '更新', 'app:vehicle_usage:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:04.324327', '2024-03-07 17:05:04.324327', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (160, 130, NULL, '删除', 'app:vehicle_usage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:13.776313', '2024-03-07 17:05:13.776313', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (161, 130, NULL, '新增', 'app:vehicle_usage:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:25.081691', '2024-03-07 17:05:25.081691', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (162, 130, NULL, '单个信息', 'app:vehicle_usage:read', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:48.310497', '2024-03-07 17:05:48.310497', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (163, 152, NULL, '导出', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 0, '2024-03-11 13:43:41.135585', '2024-04-07 11:02:08.000000', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (164, 152, NULL, '更新', 'app:materials_inventory:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:44:23.144410', '2024-03-11 13:44:23.144410', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (165, 152, NULL, '删除', 'app:materials_inventory:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:44:47.383396', '2024-03-11 13:44:47.383396', 0, 1, NULL); -INSERT INTO `sys_menu` VALUES (166, 128, '/contract/task', '任务管控', NULL, 1, 'ant-design:align-left-outlined', 255, 'task/index', 0, 1, 1, '2024-03-12 10:18:54.645756', '2024-03-12 10:18:54.645756', 0, 1, NULL); +INSERT INTO `sys_menu` VALUES (1, NULL, '/system', '系统管理', '', 0, 'ant-design:setting-outlined', 254, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-29 10:41:29.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (2, 1, '/system/user', '用户管理', 'system:user:list', 1, 'ant-design:user-outlined', 0, 'system/user/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:10:30.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (3, 1, '/system/role', '角色管理', 'system:role:list', 1, 'ep:user', 1, 'system/role/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:02.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (4, 1, '/system/menu', '菜单管理', 'system:menu:list', 1, 'ep:menu', 2, 'system/menu/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:18.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (5, 1, '/system/monitor', '系统监控', '', 0, 'ep:monitor', 5, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-04-02 15:00:20.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (6, 5, '/system/monitor/online', '在线用户', 'system:online:list', 1, '', 0, 'system/monitor/online/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:13:59.519267', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (7, 5, '/sys/monitor/login-log', '登录日志', 'system:log:login:list', 1, '', 0, 'system/monitor/log/login/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:02.610719', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (8, 5, '/system/monitor/serve', '服务监控', 'system:serve:stat', 1, '', 4, 'system/monitor/serve/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-15 22:14:05.606355', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (9, 1, '/system/schedule', '任务调度', '', 0, 'ant-design:schedule-filled', 6, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-04-02 15:00:23.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (10, 9, '/system/task', '任务管理', '', 1, '', 0, 'system/schedule/task/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:14:39.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (11, 9, '/system/task/log', '任务日志', 'system:task:list', 1, '', 0, 'system/schedule/log/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:15:01.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (12, NULL, '/document', '文档', '', 0, 'ion:tv-outline', 2, '', 1, 0, 0, '2023-11-10 00:31:44.023393', '2024-04-25 10:19:48.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (14, 12, 'https://www.typeorm.org/', 'Typeorm中文文档(外链)', NULL, 1, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:39:53.000000', 1, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (15, 12, 'https://docs.nestjs.cn/', 'Nest.js中文文档(内嵌)', '', 1, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-30 18:40:43.000000', 1, 2, NULL, 1); +INSERT INTO `sys_menu` VALUES (20, 2, NULL, '新增', 'system:user:create', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (21, 2, '', '删除', 'system:user:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (22, 2, '', '更新', 'system:user:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (23, 2, '', '查询', 'system:user:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (24, 3, '', '新增', 'system:role:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (25, 3, '', '删除', 'system:role:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (26, 3, '', '修改', 'system:role:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (27, 3, '', '查询', 'system:role:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (28, 4, NULL, '新增', 'system:menu:create', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (29, 4, NULL, '删除', 'system:menu:delete', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (30, 4, '', '修改', 'system:menu:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (31, 4, NULL, '查询', 'system:menu:read', 2, NULL, 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (32, 6, '', '下线', 'system:online:kick', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (34, 10, '', '新增', 'system:task:create', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (35, 10, '', '删除', 'system:task:delete', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (36, 10, '', '执行一次', 'system:task:once', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (37, 10, '', '查询', 'system:task:read', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (38, 10, '', '运行', 'system:task:start', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (39, 10, '', '暂停', 'system:task:stop', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (40, 10, '', '更新', 'system:task:update', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (41, 7, '', '查询登录日志', 'system:log:login:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (42, 7, '', '查询任务日志', 'system:log:task:list', 2, '', 0, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (43, NULL, '/about', '关于', '', 1, 'ant-design:info-circle-outlined', 260, 'account/about', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-10 09:35:41.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (48, NULL, '/tool', '系统工具', NULL, 0, 'ant-design:tool-outlined', 255, '', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-02-29 10:41:25.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (49, 48, '/tool/email', '邮件工具', 'system:tools:email', 1, 'ant-design:send-outlined', 1, 'tool/email/index', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-02-28 11:51:38.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (50, 49, NULL, '发送邮件', 'tools:email:send', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (51, 48, '/tool/storage', '本地存储管理', 'tool:storage:list', 1, 'ant-design:appstore-outlined', 2, 'tool/storage/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-03-08 15:13:32.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (52, 51, NULL, '文件上传', 'upload:upload', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 01:04:08.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (53, 51, NULL, '文件删除', 'tool:storage:delete', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-25 00:56:01.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (54, 2, NULL, '修改密码', 'system:user:password', 2, '', 5, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (56, 1, '/system/dict-type', '字典管理', 'system:dict-type:list', 1, 'ant-design:book-outlined', 4, 'system/dict-type/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:12.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (57, 56, NULL, '新增', 'system:dict-type:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:20.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (58, 56, NULL, '更新', 'system:dict-type:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:26.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (59, 56, NULL, '删除', 'system:dict-type:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:42.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (60, 56, NULL, '查询', 'system:dict-type:info', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-28 09:07:36.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (61, 1, '/system/dept', '部门管理', 'system:dept:list', 1, 'ant-design:deployment-unit-outlined', 3, 'system/dept/index', 1, 1, 1, '2023-11-10 00:31:44.023393', '2024-01-17 03:11:55.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (62, 61, NULL, '新增', 'system:dept:create', 2, '', 1, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (63, 61, NULL, '更新', 'system:dept:update', 2, '', 2, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (64, 61, NULL, '删除', 'system:dept:delete', 2, '', 3, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (65, 61, NULL, '查询', 'system:dept:read', 2, '', 4, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (68, 5, '/health', '健康检查', '', 1, '', 4, '', 1, 0, 1, '2023-11-10 00:31:44.023393', '2024-01-27 18:53:33.352155', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (69, 68, NULL, '网络', 'app:health:network', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (70, 68, NULL, '数据库', 'app:health: database', 2, '', 0, NULL, 1, 1, 1, '2023-11-10 00:31:44.023393', '2023-11-10 00:31:44.034474', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (86, 1, '/param-config', '参数配置', 'system:param-config:list', 1, 'ep:edit', 255, 'system/param-config/index', 0, 1, 1, '2024-01-10 17:34:52.569663', '2024-01-19 02:11:27.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (87, 86, NULL, '查询', 'system:param-config:read', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:20.983241', '2024-01-10 17:39:20.983241', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (88, 86, NULL, '新增', 'system:param-config:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:39:57.543510', '2024-01-10 17:39:57.543510', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (89, 86, NULL, '更新', 'system:param-config:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:40:27.355944', '2024-01-10 17:40:27.355944', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (92, 86, NULL, '删除', 'system:param-config:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-10 17:57:32.059887', '2024-01-10 17:57:32.059887', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (107, 1, 'system/dict-item/:id', '字典项管理', 'system:dict-item:list', 1, 'ant-design:facebook-outlined', 255, 'system/dict-item/index', 0, 0, 1, '2024-01-28 09:21:17.409532', '2024-01-30 13:09:47.000000', 0, 1, '字典管理', 1); +INSERT INTO `sys_menu` VALUES (108, 107, NULL, '新增', 'system:dict-item:create', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:22:39.401758', '2024-01-28 22:38:36.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (109, 107, NULL, '更新', 'system:dict-item:update', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:26:43.911886', '2024-01-28 09:26:43.911886', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (110, 107, NULL, '删除', 'system:dict-item:delete', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:28.535225', '2024-01-28 09:27:28.535225', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (111, 107, NULL, '查询', 'system:dict-item:info', 2, '', 255, NULL, 1, 1, 1, '2024-01-28 09:27:43.894820', '2024-01-28 09:27:43.894820', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (112, 12, 'https://antdv.com/components/overview-cn', 'antdv文档(内嵌)', NULL, 1, '', 255, NULL, 1, 1, 1, '2024-01-29 09:23:08.407723', '2024-01-30 18:41:19.000000', 1, 2, NULL, 1); +INSERT INTO `sys_menu` VALUES (115, NULL, 'netdisk', '网盘管理', NULL, 0, 'ant-design:cloud-server-outlined', 255, NULL, 1, 0, 0, '2024-02-10 08:00:02.394616', '2024-04-26 08:45:09.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (116, 48, 'manage', '云盘管理', 'netdisk:manage:list', 1, 'ant-design:cloud-server-outlined', 252, 'netdisk/manage', 0, 0, 1, '2024-02-10 08:03:49.837348', '2024-04-07 10:29:44.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (117, 116, NULL, '创建文件或文件夹', 'netdisk:manage:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:40:22.317257', '2024-02-10 08:40:22.317257', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (118, 116, NULL, '查看文件', 'netdisk:manage:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:22.008015', '2024-02-10 08:41:22.008015', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (119, 116, NULL, '更新', 'netdisk:manage:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:41:50.691643', '2024-02-10 08:41:50.691643', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (120, 116, NULL, '删除', 'netdisk:manage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:09.480601', '2024-02-10 08:42:09.480601', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (121, 116, NULL, '获取文件上传token', 'netdisk:manage:token', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:42:57.688104', '2024-02-10 08:42:57.688104', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (122, 116, NULL, '添加文件备注', 'netdisk:manage:mark', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:43:40.117321', '2024-02-10 08:43:40.117321', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (123, 116, NULL, '下载文件', 'netdisk:manage:download', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:01.338984', '2024-02-10 08:44:01.338984', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (124, 116, NULL, '重命名文件或文件夹', 'netdisk:manage:rename', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:27.233379', '2024-02-10 08:45:36.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (125, 116, NULL, '复制文件或文件夹', 'netdisk:manage:copy', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:44:44.725391', '2024-02-10 08:45:48.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (126, 116, NULL, '剪切文件或文件夹', 'netdisk:manage:cut', 2, '', 255, NULL, 1, 1, 1, '2024-02-10 08:45:21.660511', '2024-02-10 08:45:21.660511', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (127, 115, 'overview', '网盘概览', 'netdisk:overview:desc', 1, '', 254, 'netdisk/overview', 0, 1, 1, '2024-02-10 09:32:56.981190', '2024-02-10 09:34:18.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (128, NULL, '/contract', '合同管理', NULL, 0, 'ep:document', 1, NULL, 1, 0, 0, '2024-02-29 10:40:39.080419', '2024-04-25 10:17:41.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (129, 128, '/contract/index', '合同审核', 'app:contract:list', 1, 'ep:document', 1, 'contract/index', 0, 1, 1, '2024-02-29 10:46:09.245521', '2024-02-29 14:59:56.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (130, NULL, '/vehicle-usage/index', '车辆使用', NULL, 1, 'ant-design:car-outlined', 4, 'vehicle-usage/index', 0, 1, 1, '2024-02-29 10:48:35.035363', '2024-04-24 17:05:29.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (131, NULL, '/materials-inventory/record-in-out', '出入库记录', 'materials_inventory:history_in_out:list', 1, 'ep:coin', 3, 'materials-inventory/in-out/index', 0, 1, 1, '2024-02-29 11:03:49.710130', '2024-04-02 14:52:54.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (132, 129, NULL, '更新', 'app:contract:update', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:00:39.641043', '2024-02-29 15:00:39.641043', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (133, 129, NULL, '删除', 'app:contract:delete', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:00:59.376071', '2024-02-29 15:00:59.376071', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (134, 129, NULL, '查询', 'app:contract:read', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:01:14.209396', '2024-02-29 15:45:29.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (135, 129, NULL, '新增', 'app:contract:create', 2, '', 255, NULL, 1, 1, 1, '2024-02-29 15:44:46.950582', '2024-02-29 15:44:46.950582', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (136, 131, NULL, '新增', 'materials_inventory:history_in_out:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:02.597782', '2024-03-06 10:54:28.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (137, 131, NULL, '更新', 'materials_inventory:history_in_out:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:15.192910', '2024-03-06 10:54:57.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (138, 131, NULL, '查询单个', 'materials_inventory:history_in_out:read', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:32.488892', '2024-04-08 09:51:40.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (139, 131, NULL, '删除', 'materials_inventory:history_in_out:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-01 17:17:43.455773', '2024-03-06 10:55:06.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (140, NULL, '/materials-inventory/company', '乙方公司管理', 'app:company:list', 1, 'ep:office-building', 6, 'materials-inventory/company/index', 0, 1, 1, '2024-03-04 15:44:30.769048', '2024-03-27 12:57:31.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (141, 140, NULL, '单个查询', 'app:company:read', 2, '', 1, NULL, 1, 1, 1, '2024-03-04 15:45:55.979802', '2024-03-04 15:45:55.979802', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (142, 140, NULL, '新增', 'app:company:create', 2, '', 2, NULL, 1, 1, 1, '2024-03-04 15:46:11.260636', '2024-03-04 15:46:11.260636', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (143, 140, NULL, '更新', 'app:company:update', 2, '', 3, NULL, 1, 1, 1, '2024-03-04 15:46:25.098204', '2024-03-04 15:46:25.098204', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (144, 140, NULL, '删除', 'app:company:delete', 2, '', 4, NULL, 1, 1, 1, '2024-03-04 15:46:50.812446', '2024-03-04 15:46:50.812446', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (145, NULL, '/materials-inventory/product', '产品目录', 'app:product:list', 1, 'ant-design:product-outlined', 6, 'materials-inventory/product/index', 0, 1, 1, '2024-03-04 16:43:22.749281', '2024-03-27 12:56:52.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (146, 145, NULL, '单个查询', 'app:product:read', 2, '', 1, NULL, 1, 1, 1, '2024-03-04 16:44:56.482508', '2024-03-04 16:44:56.482508', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (147, 145, NULL, '新增', 'app:product:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:08.211188', '2024-03-04 16:45:08.211188', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (148, 145, NULL, '更新', 'app:product:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:25.457903', '2024-03-04 16:45:25.457903', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (149, 145, NULL, '删除', 'app:product:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-04 16:45:39.352621', '2024-03-04 16:45:39.352621', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (150, NULL, '/materials-inventory', '原材料盘点', NULL, 0, 'ant-design:dashboard-outlined', 3, NULL, 1, 0, 1, '2024-03-04 16:53:32.172674', '2024-04-25 13:41:13.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (151, 131, NULL, '导出', 'materials_inventory:history_in_out:export', 2, '', 5, NULL, 1, 1, 1, '2024-03-06 13:09:39.201093', '2024-04-17 08:47:13.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (152, NULL, '/materials-inventory/inventory-check', '原材料库存管理', 'app:materials_inventory:list', 1, 'ant-design:dashboard-outlined', 2, 'materials-inventory/inventory-check/index', 1, 1, 1, '2024-03-06 13:33:24.795599', '2024-04-02 14:52:48.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (153, NULL, '/materials-inventory/project', '项目管理', 'app:project:list', 1, 'ep:memo', 4, 'materials-inventory/project/index', 0, 1, 1, '2024-03-07 09:28:19.234454', '2024-03-27 12:57:13.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (154, 153, NULL, '新增', 'app:project:create', 2, '', 1, NULL, 1, 1, 1, '2024-03-07 09:28:47.855064', '2024-03-07 09:28:47.855064', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (155, 153, NULL, '更新', 'app:project:update', 2, '', 2, NULL, 1, 1, 1, '2024-03-07 09:29:03.183084', '2024-03-07 09:29:03.183084', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (156, 153, NULL, '删除', 'app:project:delete', 2, '', 3, NULL, 1, 1, 1, '2024-03-07 09:29:16.684943', '2024-03-07 09:29:16.684943', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (157, 153, NULL, '单个信息', 'app:project:read', 2, '', 4, NULL, 1, 1, 1, '2024-03-07 09:29:33.424578', '2024-03-07 09:29:33.424578', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (158, 131, NULL, '导出原材料盘点表', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 0, '2024-03-07 11:46:54.468400', '2024-04-07 11:02:41.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (159, 130, NULL, '更新', 'app:vehicle_usage:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:04.324327', '2024-03-07 17:05:04.324327', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (160, 130, NULL, '删除', 'app:vehicle_usage:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:13.776313', '2024-03-07 17:05:13.776313', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (161, 130, NULL, '新增', 'app:vehicle_usage:create', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:25.081691', '2024-03-07 17:05:25.081691', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (162, 130, NULL, '单个信息', 'app:vehicle_usage:read', 2, '', 255, NULL, 1, 1, 1, '2024-03-07 17:05:48.310497', '2024-03-07 17:05:48.310497', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (163, 152, NULL, '导出', 'app:materials_inventory:export', 2, '', 255, NULL, 1, 1, 0, '2024-03-11 13:43:41.135585', '2024-04-07 11:02:08.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (164, 152, NULL, '更新', 'app:materials_inventory:update', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:44:23.144410', '2024-03-11 13:44:23.144410', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (165, 152, NULL, '删除', 'app:materials_inventory:delete', 2, '', 255, NULL, 1, 1, 1, '2024-03-11 13:44:47.383396', '2024-03-11 13:44:47.383396', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (166, 128, '/contract/task', '任务管控', NULL, 1, 'ant-design:align-left-outlined', 255, 'task/index', 0, 1, 1, '2024-03-12 10:18:54.645756', '2024-03-12 10:18:54.645756', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (167, NULL, 'app', 'App', NULL, 0, 'ep:cellphone', 255, NULL, 1, 1, 1, '2024-04-10 13:43:25.086522', '2024-04-25 10:18:37.000000', 0, 1, NULL, 0); +INSERT INTO `sys_menu` VALUES (168, 167, '/workbench/sale_quotation', '报价计算', NULL, 1, 'ep:iphone', 255, NULL, 0, 0, 1, '2024-04-10 13:52:49.700256', '2024-04-10 17:06:50.000000', 0, 1, NULL, 0); +INSERT INTO `sys_menu` VALUES (169, 167, '/workbench/hr_manage', '人事', NULL, 1, 'ep:user', 255, NULL, 0, 1, 1, '2024-04-10 17:06:39.053696', '2024-04-10 17:06:39.053696', 0, 1, NULL, 0); +INSERT INTO `sys_menu` VALUES (170, 167, '/workbench/inventory', '库存', NULL, 1, 'ant-design:appstore-twotone', 255, NULL, 0, 1, 1, '2024-04-10 17:30:21.869844', '2024-04-10 17:30:21.869844', 0, 1, NULL, 0); +INSERT INTO `sys_menu` VALUES (171, NULL, '/sale_quotation', '报价管理', NULL, 0, 'ant-design:calculator-outlined', 7, NULL, 1, 1, 1, '2024-04-11 09:43:45.555624', '2024-04-11 09:45:14.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (172, 171, '/sale_quotation/component', '配件管理', 'sale_quotation:sale_quotation_component:list', 1, 'ant-design:product-outlined', 255, 'sale_quotation/component/index', 0, 1, 1, '2024-04-11 09:44:21.543912', '2024-04-11 11:26:04.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (173, 171, '/sale_quotation/group', '分组管理', 'sale_quotation:sale_quotation_group:list', 1, 'ant-design:ungroup-outlined', 255, 'sale_quotation/group/index', 0, 1, 1, '2024-04-11 10:22:28.242913', '2024-04-11 10:35:38.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (174, 171, '/sale_quotation/template', '模板管理', 'sale_quotation:sale_quotation_template:list', 1, 'ant-design:down-square-outlined', 255, 'sale_quotation/template/index', 0, 1, 1, '2024-04-11 10:23:47.537725', '2024-04-11 10:24:23.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (175, 173, NULL, '新增', 'sale_quotation:sale_quotation_group:create', 2, '', 1, NULL, 1, 1, 1, '2024-04-11 10:36:06.932095', '2024-04-11 10:36:06.932095', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (176, 173, NULL, '更新', 'sale_quotation:sale_quotation_group:update', 2, '', 255, NULL, 1, 1, 1, '2024-04-11 10:36:20.947053', '2024-04-11 10:36:20.947053', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (177, 173, NULL, '删除', 'sale_quotation:sale_quotation_group:delete', 2, '', 255, NULL, 1, 1, 1, '2024-04-11 10:36:40.636387', '2024-04-11 10:36:40.636387', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (178, 173, NULL, '单个信息', 'sale_quotation:sale_quotation_group:read', 2, '', 255, NULL, 1, 1, 1, '2024-04-11 10:38:09.474606', '2024-04-11 10:38:09.474606', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (179, 172, NULL, '新增', 'sale_quotation:sale_quotation_component:create', 2, '', 1, NULL, 1, 1, 1, '2024-04-11 10:38:24.363414', '2024-04-11 11:26:16.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (180, 172, NULL, '更新', 'sale_quotation:sale_quotation_component:update', 2, '', 1, NULL, 1, 1, 1, '2024-04-11 10:38:41.646325', '2024-04-11 11:26:24.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (181, 172, NULL, '删除', 'sale_quotation:sale_quotation_component:delete', 2, '', 255, NULL, 1, 1, 1, '2024-04-11 10:38:55.707364', '2024-04-11 11:26:28.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (182, 172, NULL, '单个信息', 'sale_quotation:sale_quotation_component:read', 2, '', 255, NULL, 1, 1, 1, '2024-04-11 10:39:12.506069', '2024-04-11 11:26:32.000000', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (183, 174, NULL, '新增', 'sale_quotation:sale_quotation_template:create', 2, '', 255, NULL, 1, 1, 1, '2024-04-11 10:39:28.924135', '2024-04-11 10:39:28.924135', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (184, 174, NULL, '更新', 'sale_quotation:sale_quotation_template:update', 2, '', 1, NULL, 1, 1, 1, '2024-04-11 10:39:41.896568', '2024-04-11 10:39:41.896568', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (185, 174, NULL, '删除', 'sale_quotation:sale_quotation_template:delete', 2, '', 255, NULL, 1, 1, 1, '2024-04-11 10:39:54.839289', '2024-04-11 10:39:54.839289', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (186, 174, NULL, '单个信息', 'sale_quotation:sale_quotation_template:read', 2, '', 255, NULL, 1, 1, 1, '2024-04-11 10:40:10.487030', '2024-04-11 10:40:10.487030', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (187, 171, NULL, '导出', 'sale_quotation:sale_quotation:export', 2, '', 255, NULL, 1, 1, 1, '2024-04-12 10:08:48.237388', '2024-04-12 10:08:48.237388', 0, 1, NULL, 1); +INSERT INTO `sys_menu` VALUES (188, 1, NULL, '切换公司', 'system:domain:change', 2, '', 255, NULL, 1, 1, 1, '2024-04-16 14:35:24.448212', '2024-04-16 14:35:24.448212', 0, 1, NULL, 1); -- ---------------------------- -- Table structure for sys_role @@ -748,16 +794,20 @@ CREATE TABLE `sys_role` ( `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `default` tinyint NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_223de54d6badbe43a5490450c3`(`name`) USING BTREE, - UNIQUE INDEX `IDX_05edc0a51f41bb16b7d8137da9`(`value`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + UNIQUE INDEX `IDX_223de54d6badbe43a5490450c3`(`name` ASC) USING BTREE, + UNIQUE INDEX `IDX_05edc0a51f41bb16b7d8137da9`(`value` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_role -- ---------------------------- -INSERT INTO `sys_role` VALUES (1, 'admin', '超级管理员', '超级管理员(拥有所有权限,请谨慎。)', 1, '2023-11-10 00:31:44.058463', '2024-04-07 11:08:14.419171', NULL); -INSERT INTO `sys_role` VALUES (2, 'user', '用户', '基础用户。目前没有设置任何菜单', 1, '2023-11-10 00:31:44.058463', '2024-04-07 11:05:32.000000', 1); -INSERT INTO `sys_role` VALUES (10, 'InventoryManager', '出入库管理员', '可以使用出入库相关的功能', 1, '2024-04-02 14:55:13.393542', '2024-04-07 11:09:03.195979', NULL); +INSERT INTO `sys_role` VALUES (1, 'admin', '超级管理员', '超级管理员(拥有所有权限,请谨慎。)', 1, '2023-11-10 00:31:44.058463', '2024-04-12 10:08:58.000000', NULL); +INSERT INTO `sys_role` VALUES (2, 'user', '员工', '基础用户。目前没有设置任何菜单', 1, '2023-11-10 00:31:44.058463', '2024-04-08 09:12:20.000000', 1); +INSERT INTO `sys_role` VALUES (10, 'InventoryManager', '出入库管理员', '可以使用出入库相关的功能', 1, '2024-04-02 14:55:13.393542', '2024-04-10 17:30:29.000000', NULL); +INSERT INTO `sys_role` VALUES (11, 'quotationCalculator', '评估报价员', '可以使用app端的报价计算功能', 1, '2024-04-10 13:18:45.043650', '2024-04-12 10:09:05.000000', NULL); +INSERT INTO `sys_role` VALUES (12, 'hr', '人力资源管理', '可以进行员工管理,修改员工资料', 1, '2024-04-10 17:04:53.572685', '2024-04-10 17:24:48.000000', NULL); +INSERT INTO `sys_role` VALUES (13, 'generalManager', '总经理', '总经理(拥有所有权限)', 1, '2024-04-15 14:03:39.182990', '2024-04-15 14:03:54.000000', NULL); +INSERT INTO `sys_role` VALUES (14, 'chiefEngineer', '总工程师', '总工程师(拥有所有权限)', 1, '2024-04-15 14:54:38.660651', '2024-04-15 14:54:48.000000', NULL); -- ---------------------------- -- Table structure for sys_role_menus @@ -767,8 +817,8 @@ CREATE TABLE `sys_role_menus` ( `role_id` int NOT NULL, `menu_id` int NOT NULL, PRIMARY KEY (`role_id`, `menu_id`) USING BTREE, - INDEX `IDX_35ce749b04d57e226d059e0f63`(`role_id`) USING BTREE, - INDEX `IDX_2b95fdc95b329d66c18f5baed6`(`menu_id`) USING BTREE, + INDEX `IDX_35ce749b04d57e226d059e0f63`(`role_id` ASC) USING BTREE, + INDEX `IDX_2b95fdc95b329d66c18f5baed6`(`menu_id` ASC) USING BTREE, CONSTRAINT `FK_2b95fdc95b329d66c18f5baed6d` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, CONSTRAINT `FK_35ce749b04d57e226d059e0f633` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; @@ -776,12 +826,157 @@ CREATE TABLE `sys_role_menus` ( -- ---------------------------- -- Records of sys_role_menus -- ---------------------------- +INSERT INTO `sys_role_menus` VALUES (1, 1); +INSERT INTO `sys_role_menus` VALUES (1, 2); +INSERT INTO `sys_role_menus` VALUES (1, 3); +INSERT INTO `sys_role_menus` VALUES (1, 4); +INSERT INTO `sys_role_menus` VALUES (1, 5); +INSERT INTO `sys_role_menus` VALUES (1, 6); +INSERT INTO `sys_role_menus` VALUES (1, 7); +INSERT INTO `sys_role_menus` VALUES (1, 8); +INSERT INTO `sys_role_menus` VALUES (1, 9); +INSERT INTO `sys_role_menus` VALUES (1, 10); +INSERT INTO `sys_role_menus` VALUES (1, 11); +INSERT INTO `sys_role_menus` VALUES (1, 12); +INSERT INTO `sys_role_menus` VALUES (1, 14); +INSERT INTO `sys_role_menus` VALUES (1, 15); +INSERT INTO `sys_role_menus` VALUES (1, 20); +INSERT INTO `sys_role_menus` VALUES (1, 21); +INSERT INTO `sys_role_menus` VALUES (1, 22); +INSERT INTO `sys_role_menus` VALUES (1, 23); +INSERT INTO `sys_role_menus` VALUES (1, 24); +INSERT INTO `sys_role_menus` VALUES (1, 25); +INSERT INTO `sys_role_menus` VALUES (1, 26); +INSERT INTO `sys_role_menus` VALUES (1, 27); +INSERT INTO `sys_role_menus` VALUES (1, 28); +INSERT INTO `sys_role_menus` VALUES (1, 29); +INSERT INTO `sys_role_menus` VALUES (1, 30); +INSERT INTO `sys_role_menus` VALUES (1, 31); +INSERT INTO `sys_role_menus` VALUES (1, 32); +INSERT INTO `sys_role_menus` VALUES (1, 34); +INSERT INTO `sys_role_menus` VALUES (1, 35); +INSERT INTO `sys_role_menus` VALUES (1, 36); +INSERT INTO `sys_role_menus` VALUES (1, 37); +INSERT INTO `sys_role_menus` VALUES (1, 38); +INSERT INTO `sys_role_menus` VALUES (1, 39); +INSERT INTO `sys_role_menus` VALUES (1, 40); +INSERT INTO `sys_role_menus` VALUES (1, 41); +INSERT INTO `sys_role_menus` VALUES (1, 42); INSERT INTO `sys_role_menus` VALUES (1, 43); +INSERT INTO `sys_role_menus` VALUES (1, 48); +INSERT INTO `sys_role_menus` VALUES (1, 49); +INSERT INTO `sys_role_menus` VALUES (1, 50); +INSERT INTO `sys_role_menus` VALUES (1, 51); +INSERT INTO `sys_role_menus` VALUES (1, 52); +INSERT INTO `sys_role_menus` VALUES (1, 53); +INSERT INTO `sys_role_menus` VALUES (1, 54); +INSERT INTO `sys_role_menus` VALUES (1, 56); +INSERT INTO `sys_role_menus` VALUES (1, 57); +INSERT INTO `sys_role_menus` VALUES (1, 58); +INSERT INTO `sys_role_menus` VALUES (1, 59); +INSERT INTO `sys_role_menus` VALUES (1, 60); +INSERT INTO `sys_role_menus` VALUES (1, 61); +INSERT INTO `sys_role_menus` VALUES (1, 62); +INSERT INTO `sys_role_menus` VALUES (1, 63); +INSERT INTO `sys_role_menus` VALUES (1, 64); +INSERT INTO `sys_role_menus` VALUES (1, 65); +INSERT INTO `sys_role_menus` VALUES (1, 68); +INSERT INTO `sys_role_menus` VALUES (1, 69); +INSERT INTO `sys_role_menus` VALUES (1, 70); +INSERT INTO `sys_role_menus` VALUES (1, 86); +INSERT INTO `sys_role_menus` VALUES (1, 87); +INSERT INTO `sys_role_menus` VALUES (1, 88); +INSERT INTO `sys_role_menus` VALUES (1, 89); +INSERT INTO `sys_role_menus` VALUES (1, 92); +INSERT INTO `sys_role_menus` VALUES (1, 107); +INSERT INTO `sys_role_menus` VALUES (1, 108); +INSERT INTO `sys_role_menus` VALUES (1, 109); +INSERT INTO `sys_role_menus` VALUES (1, 110); +INSERT INTO `sys_role_menus` VALUES (1, 111); +INSERT INTO `sys_role_menus` VALUES (1, 112); +INSERT INTO `sys_role_menus` VALUES (1, 115); +INSERT INTO `sys_role_menus` VALUES (1, 116); +INSERT INTO `sys_role_menus` VALUES (1, 117); +INSERT INTO `sys_role_menus` VALUES (1, 118); +INSERT INTO `sys_role_menus` VALUES (1, 119); +INSERT INTO `sys_role_menus` VALUES (1, 120); +INSERT INTO `sys_role_menus` VALUES (1, 121); +INSERT INTO `sys_role_menus` VALUES (1, 122); +INSERT INTO `sys_role_menus` VALUES (1, 123); +INSERT INTO `sys_role_menus` VALUES (1, 124); +INSERT INTO `sys_role_menus` VALUES (1, 125); +INSERT INTO `sys_role_menus` VALUES (1, 126); +INSERT INTO `sys_role_menus` VALUES (1, 127); +INSERT INTO `sys_role_menus` VALUES (1, 128); +INSERT INTO `sys_role_menus` VALUES (1, 129); +INSERT INTO `sys_role_menus` VALUES (1, 130); +INSERT INTO `sys_role_menus` VALUES (1, 131); +INSERT INTO `sys_role_menus` VALUES (1, 132); +INSERT INTO `sys_role_menus` VALUES (1, 133); +INSERT INTO `sys_role_menus` VALUES (1, 134); +INSERT INTO `sys_role_menus` VALUES (1, 135); +INSERT INTO `sys_role_menus` VALUES (1, 136); +INSERT INTO `sys_role_menus` VALUES (1, 137); +INSERT INTO `sys_role_menus` VALUES (1, 138); +INSERT INTO `sys_role_menus` VALUES (1, 139); +INSERT INTO `sys_role_menus` VALUES (1, 140); +INSERT INTO `sys_role_menus` VALUES (1, 141); +INSERT INTO `sys_role_menus` VALUES (1, 142); +INSERT INTO `sys_role_menus` VALUES (1, 143); +INSERT INTO `sys_role_menus` VALUES (1, 144); +INSERT INTO `sys_role_menus` VALUES (1, 145); +INSERT INTO `sys_role_menus` VALUES (1, 146); +INSERT INTO `sys_role_menus` VALUES (1, 147); +INSERT INTO `sys_role_menus` VALUES (1, 148); +INSERT INTO `sys_role_menus` VALUES (1, 149); +INSERT INTO `sys_role_menus` VALUES (1, 150); +INSERT INTO `sys_role_menus` VALUES (1, 151); +INSERT INTO `sys_role_menus` VALUES (1, 152); +INSERT INTO `sys_role_menus` VALUES (1, 153); +INSERT INTO `sys_role_menus` VALUES (1, 154); +INSERT INTO `sys_role_menus` VALUES (1, 155); +INSERT INTO `sys_role_menus` VALUES (1, 156); +INSERT INTO `sys_role_menus` VALUES (1, 157); +INSERT INTO `sys_role_menus` VALUES (1, 159); +INSERT INTO `sys_role_menus` VALUES (1, 160); +INSERT INTO `sys_role_menus` VALUES (1, 161); +INSERT INTO `sys_role_menus` VALUES (1, 162); +INSERT INTO `sys_role_menus` VALUES (1, 164); +INSERT INTO `sys_role_menus` VALUES (1, 165); +INSERT INTO `sys_role_menus` VALUES (1, 166); +INSERT INTO `sys_role_menus` VALUES (1, 167); +INSERT INTO `sys_role_menus` VALUES (1, 168); +INSERT INTO `sys_role_menus` VALUES (1, 169); +INSERT INTO `sys_role_menus` VALUES (1, 170); +INSERT INTO `sys_role_menus` VALUES (1, 171); +INSERT INTO `sys_role_menus` VALUES (1, 172); +INSERT INTO `sys_role_menus` VALUES (1, 173); +INSERT INTO `sys_role_menus` VALUES (1, 174); +INSERT INTO `sys_role_menus` VALUES (1, 175); +INSERT INTO `sys_role_menus` VALUES (1, 176); +INSERT INTO `sys_role_menus` VALUES (1, 177); +INSERT INTO `sys_role_menus` VALUES (1, 178); +INSERT INTO `sys_role_menus` VALUES (1, 179); +INSERT INTO `sys_role_menus` VALUES (1, 180); +INSERT INTO `sys_role_menus` VALUES (1, 181); +INSERT INTO `sys_role_menus` VALUES (1, 182); +INSERT INTO `sys_role_menus` VALUES (1, 183); +INSERT INTO `sys_role_menus` VALUES (1, 184); +INSERT INTO `sys_role_menus` VALUES (1, 185); +INSERT INTO `sys_role_menus` VALUES (1, 186); +INSERT INTO `sys_role_menus` VALUES (1, 187); +INSERT INTO `sys_role_menus` VALUES (1, 188); INSERT INTO `sys_role_menus` VALUES (2, 43); +INSERT INTO `sys_role_menus` VALUES (10, 1); +INSERT INTO `sys_role_menus` VALUES (10, 2); +INSERT INTO `sys_role_menus` VALUES (10, 20); +INSERT INTO `sys_role_menus` VALUES (10, 23); INSERT INTO `sys_role_menus` VALUES (10, 48); INSERT INTO `sys_role_menus` VALUES (10, 51); INSERT INTO `sys_role_menus` VALUES (10, 52); INSERT INTO `sys_role_menus` VALUES (10, 53); +INSERT INTO `sys_role_menus` VALUES (10, 61); +INSERT INTO `sys_role_menus` VALUES (10, 65); INSERT INTO `sys_role_menus` VALUES (10, 131); INSERT INTO `sys_role_menus` VALUES (10, 136); INSERT INTO `sys_role_menus` VALUES (10, 137); @@ -805,10 +1000,309 @@ INSERT INTO `sys_role_menus` VALUES (10, 154); INSERT INTO `sys_role_menus` VALUES (10, 155); INSERT INTO `sys_role_menus` VALUES (10, 156); INSERT INTO `sys_role_menus` VALUES (10, 157); -INSERT INTO `sys_role_menus` VALUES (10, 158); -INSERT INTO `sys_role_menus` VALUES (10, 163); INSERT INTO `sys_role_menus` VALUES (10, 164); INSERT INTO `sys_role_menus` VALUES (10, 165); +INSERT INTO `sys_role_menus` VALUES (10, 167); +INSERT INTO `sys_role_menus` VALUES (10, 170); +INSERT INTO `sys_role_menus` VALUES (11, 171); +INSERT INTO `sys_role_menus` VALUES (11, 172); +INSERT INTO `sys_role_menus` VALUES (11, 173); +INSERT INTO `sys_role_menus` VALUES (11, 174); +INSERT INTO `sys_role_menus` VALUES (11, 175); +INSERT INTO `sys_role_menus` VALUES (11, 176); +INSERT INTO `sys_role_menus` VALUES (11, 177); +INSERT INTO `sys_role_menus` VALUES (11, 178); +INSERT INTO `sys_role_menus` VALUES (11, 179); +INSERT INTO `sys_role_menus` VALUES (11, 180); +INSERT INTO `sys_role_menus` VALUES (11, 181); +INSERT INTO `sys_role_menus` VALUES (11, 182); +INSERT INTO `sys_role_menus` VALUES (11, 183); +INSERT INTO `sys_role_menus` VALUES (11, 184); +INSERT INTO `sys_role_menus` VALUES (11, 185); +INSERT INTO `sys_role_menus` VALUES (11, 186); +INSERT INTO `sys_role_menus` VALUES (11, 187); +INSERT INTO `sys_role_menus` VALUES (12, 167); +INSERT INTO `sys_role_menus` VALUES (12, 169); +INSERT INTO `sys_role_menus` VALUES (13, 1); +INSERT INTO `sys_role_menus` VALUES (13, 2); +INSERT INTO `sys_role_menus` VALUES (13, 3); +INSERT INTO `sys_role_menus` VALUES (13, 4); +INSERT INTO `sys_role_menus` VALUES (13, 5); +INSERT INTO `sys_role_menus` VALUES (13, 6); +INSERT INTO `sys_role_menus` VALUES (13, 7); +INSERT INTO `sys_role_menus` VALUES (13, 8); +INSERT INTO `sys_role_menus` VALUES (13, 9); +INSERT INTO `sys_role_menus` VALUES (13, 10); +INSERT INTO `sys_role_menus` VALUES (13, 11); +INSERT INTO `sys_role_menus` VALUES (13, 12); +INSERT INTO `sys_role_menus` VALUES (13, 14); +INSERT INTO `sys_role_menus` VALUES (13, 15); +INSERT INTO `sys_role_menus` VALUES (13, 20); +INSERT INTO `sys_role_menus` VALUES (13, 21); +INSERT INTO `sys_role_menus` VALUES (13, 22); +INSERT INTO `sys_role_menus` VALUES (13, 23); +INSERT INTO `sys_role_menus` VALUES (13, 24); +INSERT INTO `sys_role_menus` VALUES (13, 25); +INSERT INTO `sys_role_menus` VALUES (13, 26); +INSERT INTO `sys_role_menus` VALUES (13, 27); +INSERT INTO `sys_role_menus` VALUES (13, 28); +INSERT INTO `sys_role_menus` VALUES (13, 29); +INSERT INTO `sys_role_menus` VALUES (13, 30); +INSERT INTO `sys_role_menus` VALUES (13, 31); +INSERT INTO `sys_role_menus` VALUES (13, 32); +INSERT INTO `sys_role_menus` VALUES (13, 34); +INSERT INTO `sys_role_menus` VALUES (13, 35); +INSERT INTO `sys_role_menus` VALUES (13, 36); +INSERT INTO `sys_role_menus` VALUES (13, 37); +INSERT INTO `sys_role_menus` VALUES (13, 38); +INSERT INTO `sys_role_menus` VALUES (13, 39); +INSERT INTO `sys_role_menus` VALUES (13, 40); +INSERT INTO `sys_role_menus` VALUES (13, 41); +INSERT INTO `sys_role_menus` VALUES (13, 42); +INSERT INTO `sys_role_menus` VALUES (13, 43); +INSERT INTO `sys_role_menus` VALUES (13, 48); +INSERT INTO `sys_role_menus` VALUES (13, 49); +INSERT INTO `sys_role_menus` VALUES (13, 50); +INSERT INTO `sys_role_menus` VALUES (13, 51); +INSERT INTO `sys_role_menus` VALUES (13, 52); +INSERT INTO `sys_role_menus` VALUES (13, 53); +INSERT INTO `sys_role_menus` VALUES (13, 54); +INSERT INTO `sys_role_menus` VALUES (13, 56); +INSERT INTO `sys_role_menus` VALUES (13, 57); +INSERT INTO `sys_role_menus` VALUES (13, 58); +INSERT INTO `sys_role_menus` VALUES (13, 59); +INSERT INTO `sys_role_menus` VALUES (13, 60); +INSERT INTO `sys_role_menus` VALUES (13, 61); +INSERT INTO `sys_role_menus` VALUES (13, 62); +INSERT INTO `sys_role_menus` VALUES (13, 63); +INSERT INTO `sys_role_menus` VALUES (13, 64); +INSERT INTO `sys_role_menus` VALUES (13, 65); +INSERT INTO `sys_role_menus` VALUES (13, 68); +INSERT INTO `sys_role_menus` VALUES (13, 69); +INSERT INTO `sys_role_menus` VALUES (13, 70); +INSERT INTO `sys_role_menus` VALUES (13, 86); +INSERT INTO `sys_role_menus` VALUES (13, 87); +INSERT INTO `sys_role_menus` VALUES (13, 88); +INSERT INTO `sys_role_menus` VALUES (13, 89); +INSERT INTO `sys_role_menus` VALUES (13, 92); +INSERT INTO `sys_role_menus` VALUES (13, 107); +INSERT INTO `sys_role_menus` VALUES (13, 108); +INSERT INTO `sys_role_menus` VALUES (13, 109); +INSERT INTO `sys_role_menus` VALUES (13, 110); +INSERT INTO `sys_role_menus` VALUES (13, 111); +INSERT INTO `sys_role_menus` VALUES (13, 112); +INSERT INTO `sys_role_menus` VALUES (13, 115); +INSERT INTO `sys_role_menus` VALUES (13, 116); +INSERT INTO `sys_role_menus` VALUES (13, 117); +INSERT INTO `sys_role_menus` VALUES (13, 118); +INSERT INTO `sys_role_menus` VALUES (13, 119); +INSERT INTO `sys_role_menus` VALUES (13, 120); +INSERT INTO `sys_role_menus` VALUES (13, 121); +INSERT INTO `sys_role_menus` VALUES (13, 122); +INSERT INTO `sys_role_menus` VALUES (13, 123); +INSERT INTO `sys_role_menus` VALUES (13, 124); +INSERT INTO `sys_role_menus` VALUES (13, 125); +INSERT INTO `sys_role_menus` VALUES (13, 126); +INSERT INTO `sys_role_menus` VALUES (13, 127); +INSERT INTO `sys_role_menus` VALUES (13, 128); +INSERT INTO `sys_role_menus` VALUES (13, 129); +INSERT INTO `sys_role_menus` VALUES (13, 130); +INSERT INTO `sys_role_menus` VALUES (13, 131); +INSERT INTO `sys_role_menus` VALUES (13, 132); +INSERT INTO `sys_role_menus` VALUES (13, 133); +INSERT INTO `sys_role_menus` VALUES (13, 134); +INSERT INTO `sys_role_menus` VALUES (13, 135); +INSERT INTO `sys_role_menus` VALUES (13, 136); +INSERT INTO `sys_role_menus` VALUES (13, 137); +INSERT INTO `sys_role_menus` VALUES (13, 138); +INSERT INTO `sys_role_menus` VALUES (13, 139); +INSERT INTO `sys_role_menus` VALUES (13, 140); +INSERT INTO `sys_role_menus` VALUES (13, 141); +INSERT INTO `sys_role_menus` VALUES (13, 142); +INSERT INTO `sys_role_menus` VALUES (13, 143); +INSERT INTO `sys_role_menus` VALUES (13, 144); +INSERT INTO `sys_role_menus` VALUES (13, 145); +INSERT INTO `sys_role_menus` VALUES (13, 146); +INSERT INTO `sys_role_menus` VALUES (13, 147); +INSERT INTO `sys_role_menus` VALUES (13, 148); +INSERT INTO `sys_role_menus` VALUES (13, 149); +INSERT INTO `sys_role_menus` VALUES (13, 150); +INSERT INTO `sys_role_menus` VALUES (13, 151); +INSERT INTO `sys_role_menus` VALUES (13, 152); +INSERT INTO `sys_role_menus` VALUES (13, 153); +INSERT INTO `sys_role_menus` VALUES (13, 154); +INSERT INTO `sys_role_menus` VALUES (13, 155); +INSERT INTO `sys_role_menus` VALUES (13, 156); +INSERT INTO `sys_role_menus` VALUES (13, 157); +INSERT INTO `sys_role_menus` VALUES (13, 159); +INSERT INTO `sys_role_menus` VALUES (13, 160); +INSERT INTO `sys_role_menus` VALUES (13, 161); +INSERT INTO `sys_role_menus` VALUES (13, 162); +INSERT INTO `sys_role_menus` VALUES (13, 164); +INSERT INTO `sys_role_menus` VALUES (13, 165); +INSERT INTO `sys_role_menus` VALUES (13, 166); +INSERT INTO `sys_role_menus` VALUES (13, 167); +INSERT INTO `sys_role_menus` VALUES (13, 168); +INSERT INTO `sys_role_menus` VALUES (13, 169); +INSERT INTO `sys_role_menus` VALUES (13, 170); +INSERT INTO `sys_role_menus` VALUES (13, 171); +INSERT INTO `sys_role_menus` VALUES (13, 172); +INSERT INTO `sys_role_menus` VALUES (13, 173); +INSERT INTO `sys_role_menus` VALUES (13, 174); +INSERT INTO `sys_role_menus` VALUES (13, 175); +INSERT INTO `sys_role_menus` VALUES (13, 176); +INSERT INTO `sys_role_menus` VALUES (13, 177); +INSERT INTO `sys_role_menus` VALUES (13, 178); +INSERT INTO `sys_role_menus` VALUES (13, 179); +INSERT INTO `sys_role_menus` VALUES (13, 180); +INSERT INTO `sys_role_menus` VALUES (13, 181); +INSERT INTO `sys_role_menus` VALUES (13, 182); +INSERT INTO `sys_role_menus` VALUES (13, 183); +INSERT INTO `sys_role_menus` VALUES (13, 184); +INSERT INTO `sys_role_menus` VALUES (13, 185); +INSERT INTO `sys_role_menus` VALUES (13, 186); +INSERT INTO `sys_role_menus` VALUES (13, 187); +INSERT INTO `sys_role_menus` VALUES (13, 188); +INSERT INTO `sys_role_menus` VALUES (14, 1); +INSERT INTO `sys_role_menus` VALUES (14, 2); +INSERT INTO `sys_role_menus` VALUES (14, 3); +INSERT INTO `sys_role_menus` VALUES (14, 4); +INSERT INTO `sys_role_menus` VALUES (14, 5); +INSERT INTO `sys_role_menus` VALUES (14, 6); +INSERT INTO `sys_role_menus` VALUES (14, 7); +INSERT INTO `sys_role_menus` VALUES (14, 8); +INSERT INTO `sys_role_menus` VALUES (14, 9); +INSERT INTO `sys_role_menus` VALUES (14, 10); +INSERT INTO `sys_role_menus` VALUES (14, 11); +INSERT INTO `sys_role_menus` VALUES (14, 12); +INSERT INTO `sys_role_menus` VALUES (14, 14); +INSERT INTO `sys_role_menus` VALUES (14, 15); +INSERT INTO `sys_role_menus` VALUES (14, 20); +INSERT INTO `sys_role_menus` VALUES (14, 21); +INSERT INTO `sys_role_menus` VALUES (14, 22); +INSERT INTO `sys_role_menus` VALUES (14, 23); +INSERT INTO `sys_role_menus` VALUES (14, 24); +INSERT INTO `sys_role_menus` VALUES (14, 25); +INSERT INTO `sys_role_menus` VALUES (14, 26); +INSERT INTO `sys_role_menus` VALUES (14, 27); +INSERT INTO `sys_role_menus` VALUES (14, 28); +INSERT INTO `sys_role_menus` VALUES (14, 29); +INSERT INTO `sys_role_menus` VALUES (14, 30); +INSERT INTO `sys_role_menus` VALUES (14, 31); +INSERT INTO `sys_role_menus` VALUES (14, 32); +INSERT INTO `sys_role_menus` VALUES (14, 34); +INSERT INTO `sys_role_menus` VALUES (14, 35); +INSERT INTO `sys_role_menus` VALUES (14, 36); +INSERT INTO `sys_role_menus` VALUES (14, 37); +INSERT INTO `sys_role_menus` VALUES (14, 38); +INSERT INTO `sys_role_menus` VALUES (14, 39); +INSERT INTO `sys_role_menus` VALUES (14, 40); +INSERT INTO `sys_role_menus` VALUES (14, 41); +INSERT INTO `sys_role_menus` VALUES (14, 42); +INSERT INTO `sys_role_menus` VALUES (14, 43); +INSERT INTO `sys_role_menus` VALUES (14, 48); +INSERT INTO `sys_role_menus` VALUES (14, 49); +INSERT INTO `sys_role_menus` VALUES (14, 50); +INSERT INTO `sys_role_menus` VALUES (14, 51); +INSERT INTO `sys_role_menus` VALUES (14, 52); +INSERT INTO `sys_role_menus` VALUES (14, 53); +INSERT INTO `sys_role_menus` VALUES (14, 54); +INSERT INTO `sys_role_menus` VALUES (14, 56); +INSERT INTO `sys_role_menus` VALUES (14, 57); +INSERT INTO `sys_role_menus` VALUES (14, 58); +INSERT INTO `sys_role_menus` VALUES (14, 59); +INSERT INTO `sys_role_menus` VALUES (14, 60); +INSERT INTO `sys_role_menus` VALUES (14, 61); +INSERT INTO `sys_role_menus` VALUES (14, 62); +INSERT INTO `sys_role_menus` VALUES (14, 63); +INSERT INTO `sys_role_menus` VALUES (14, 64); +INSERT INTO `sys_role_menus` VALUES (14, 65); +INSERT INTO `sys_role_menus` VALUES (14, 68); +INSERT INTO `sys_role_menus` VALUES (14, 69); +INSERT INTO `sys_role_menus` VALUES (14, 70); +INSERT INTO `sys_role_menus` VALUES (14, 86); +INSERT INTO `sys_role_menus` VALUES (14, 87); +INSERT INTO `sys_role_menus` VALUES (14, 88); +INSERT INTO `sys_role_menus` VALUES (14, 89); +INSERT INTO `sys_role_menus` VALUES (14, 92); +INSERT INTO `sys_role_menus` VALUES (14, 107); +INSERT INTO `sys_role_menus` VALUES (14, 108); +INSERT INTO `sys_role_menus` VALUES (14, 109); +INSERT INTO `sys_role_menus` VALUES (14, 110); +INSERT INTO `sys_role_menus` VALUES (14, 111); +INSERT INTO `sys_role_menus` VALUES (14, 112); +INSERT INTO `sys_role_menus` VALUES (14, 115); +INSERT INTO `sys_role_menus` VALUES (14, 116); +INSERT INTO `sys_role_menus` VALUES (14, 117); +INSERT INTO `sys_role_menus` VALUES (14, 118); +INSERT INTO `sys_role_menus` VALUES (14, 119); +INSERT INTO `sys_role_menus` VALUES (14, 120); +INSERT INTO `sys_role_menus` VALUES (14, 121); +INSERT INTO `sys_role_menus` VALUES (14, 122); +INSERT INTO `sys_role_menus` VALUES (14, 123); +INSERT INTO `sys_role_menus` VALUES (14, 124); +INSERT INTO `sys_role_menus` VALUES (14, 125); +INSERT INTO `sys_role_menus` VALUES (14, 126); +INSERT INTO `sys_role_menus` VALUES (14, 127); +INSERT INTO `sys_role_menus` VALUES (14, 128); +INSERT INTO `sys_role_menus` VALUES (14, 129); +INSERT INTO `sys_role_menus` VALUES (14, 130); +INSERT INTO `sys_role_menus` VALUES (14, 131); +INSERT INTO `sys_role_menus` VALUES (14, 132); +INSERT INTO `sys_role_menus` VALUES (14, 133); +INSERT INTO `sys_role_menus` VALUES (14, 134); +INSERT INTO `sys_role_menus` VALUES (14, 135); +INSERT INTO `sys_role_menus` VALUES (14, 136); +INSERT INTO `sys_role_menus` VALUES (14, 137); +INSERT INTO `sys_role_menus` VALUES (14, 138); +INSERT INTO `sys_role_menus` VALUES (14, 139); +INSERT INTO `sys_role_menus` VALUES (14, 140); +INSERT INTO `sys_role_menus` VALUES (14, 141); +INSERT INTO `sys_role_menus` VALUES (14, 142); +INSERT INTO `sys_role_menus` VALUES (14, 143); +INSERT INTO `sys_role_menus` VALUES (14, 144); +INSERT INTO `sys_role_menus` VALUES (14, 145); +INSERT INTO `sys_role_menus` VALUES (14, 146); +INSERT INTO `sys_role_menus` VALUES (14, 147); +INSERT INTO `sys_role_menus` VALUES (14, 148); +INSERT INTO `sys_role_menus` VALUES (14, 149); +INSERT INTO `sys_role_menus` VALUES (14, 150); +INSERT INTO `sys_role_menus` VALUES (14, 151); +INSERT INTO `sys_role_menus` VALUES (14, 152); +INSERT INTO `sys_role_menus` VALUES (14, 153); +INSERT INTO `sys_role_menus` VALUES (14, 154); +INSERT INTO `sys_role_menus` VALUES (14, 155); +INSERT INTO `sys_role_menus` VALUES (14, 156); +INSERT INTO `sys_role_menus` VALUES (14, 157); +INSERT INTO `sys_role_menus` VALUES (14, 159); +INSERT INTO `sys_role_menus` VALUES (14, 160); +INSERT INTO `sys_role_menus` VALUES (14, 161); +INSERT INTO `sys_role_menus` VALUES (14, 162); +INSERT INTO `sys_role_menus` VALUES (14, 164); +INSERT INTO `sys_role_menus` VALUES (14, 165); +INSERT INTO `sys_role_menus` VALUES (14, 166); +INSERT INTO `sys_role_menus` VALUES (14, 167); +INSERT INTO `sys_role_menus` VALUES (14, 168); +INSERT INTO `sys_role_menus` VALUES (14, 169); +INSERT INTO `sys_role_menus` VALUES (14, 170); +INSERT INTO `sys_role_menus` VALUES (14, 171); +INSERT INTO `sys_role_menus` VALUES (14, 172); +INSERT INTO `sys_role_menus` VALUES (14, 173); +INSERT INTO `sys_role_menus` VALUES (14, 174); +INSERT INTO `sys_role_menus` VALUES (14, 175); +INSERT INTO `sys_role_menus` VALUES (14, 176); +INSERT INTO `sys_role_menus` VALUES (14, 177); +INSERT INTO `sys_role_menus` VALUES (14, 178); +INSERT INTO `sys_role_menus` VALUES (14, 179); +INSERT INTO `sys_role_menus` VALUES (14, 180); +INSERT INTO `sys_role_menus` VALUES (14, 181); +INSERT INTO `sys_role_menus` VALUES (14, 182); +INSERT INTO `sys_role_menus` VALUES (14, 183); +INSERT INTO `sys_role_menus` VALUES (14, 184); +INSERT INTO `sys_role_menus` VALUES (14, 185); +INSERT INTO `sys_role_menus` VALUES (14, 186); +INSERT INTO `sys_role_menus` VALUES (14, 187); +INSERT INTO `sys_role_menus` VALUES (14, 188); -- ---------------------------- -- Table structure for sys_task @@ -831,13 +1325,13 @@ CREATE TABLE `sys_task` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_ef8e5ab5ef2fe0ddb1428439ef`(`name`) USING BTREE + UNIQUE INDEX `IDX_ef8e5ab5ef2fe0ddb1428439ef`(`name` ASC) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_task -- ---------------------------- -INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-04-07 10:57:58.000000'); +INSERT INTO `sys_task` VALUES (2, '定时清空登录日志', 'LogClearJob.clearLoginLog', 0, 1, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:2:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":2}', '定时清空登录日志', '2023-11-10 00:31:44.197779', '2024-04-17 10:24:05.000000'); INSERT INTO `sys_task` VALUES (3, '定时清空任务日志', 'LogClearJob.clearTaskLog', 0, 0, NULL, NULL, 0, '0 0 3 ? * 1', 0, '', '{\"count\":1,\"key\":\"__default__:3:::0 0 3 ? * 1\",\"cron\":\"0 0 3 ? * 1\",\"jobId\":3}', '定时清空任务日志', '2023-11-10 00:31:44.197779', '2024-03-22 14:12:52.000000'); INSERT INTO `sys_task` VALUES (4, '访问百度首页', 'HttpRequestJob.handle', 0, 0, NULL, NULL, 1, '* * * * * ?', NULL, '{\"url\":\"https://www.baidu.com\",\"method\":\"get\"}', NULL, '访问百度首页', '2023-11-10 00:31:44.197779', '2023-11-10 00:31:44.206935'); INSERT INTO `sys_task` VALUES (5, '发送邮箱', 'EmailJob.send', 0, 0, NULL, NULL, -1, '0 0 0 1 * ?', NULL, '{\"subject\":\"这是标题\",\"to\":\"18661983080@163.com\",\"content\":\"这是正文\"}', NULL, '每月发送邮箱', '2023-11-10 00:31:44.197779', '2024-03-07 11:14:53.000000'); @@ -855,9 +1349,9 @@ CREATE TABLE `sys_task_log` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_f4d9c36052fdb188ff5c089454b`(`task_id`) USING BTREE, + INDEX `FK_f4d9c36052fdb188ff5c089454b`(`task_id` ASC) USING BTREE, CONSTRAINT `FK_f4d9c36052fdb188ff5c089454b` FOREIGN KEY (`task_id`) REFERENCES `sys_task` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_task_log @@ -865,6 +1359,9 @@ CREATE TABLE `sys_task_log` ( INSERT INTO `sys_task_log` VALUES (1, 3, 1, NULL, 0, '2024-03-11 07:37:16.258223', '2024-03-11 07:37:16.258223'); INSERT INTO `sys_task_log` VALUES (2, 2, 1, NULL, 0, '2024-03-11 08:29:25.175865', '2024-03-11 08:29:25.175865'); INSERT INTO `sys_task_log` VALUES (3, 2, 1, NULL, 0, '2024-04-01 03:00:00.202419', '2024-04-01 03:00:00.202419'); +INSERT INTO `sys_task_log` VALUES (4, 2, 1, NULL, 0, '2024-04-08 03:00:00.202888', '2024-04-08 03:00:00.202888'); +INSERT INTO `sys_task_log` VALUES (5, 2, 1, NULL, 0, '2024-04-15 03:00:00.186687', '2024-04-15 03:00:00.186687'); +INSERT INTO `sys_task_log` VALUES (6, 2, 1, NULL, 0, '2024-04-22 03:00:00.226492', '2024-04-22 03:00:00.226492'); -- ---------------------------- -- Table structure for sys_user @@ -885,19 +1382,19 @@ CREATE TABLE `sys_user` ( `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `dept_id` int NULL DEFAULT NULL, + `name_pinyin` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '产品名称的拼音', + `domain` int NOT NULL DEFAULT 1 COMMENT '所属域', PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `IDX_9e7164b2f1ea1348bc0eb0a7da`(`username`) USING BTREE, - INDEX `FK_96bde34263e2ae3b46f011124ac`(`dept_id`) USING BTREE, + UNIQUE INDEX `IDX_9e7164b2f1ea1348bc0eb0a7da`(`username` ASC) USING BTREE, + INDEX `FK_96bde34263e2ae3b46f011124ac`(`dept_id` ASC) USING BTREE, CONSTRAINT `FK_96bde34263e2ae3b46f011124ac` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT -) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_user -- ---------------------------- -INSERT INTO `sys_user` VALUES (1, 'admin', 'a11571e778ee85e82caae2d980952546', 'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777', '1743369777@qq.com', '10086', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-04-02 14:53:56.000000', '朱明仁', 2); -INSERT INTO `sys_user` VALUES (9, 'mengfei', '53a7b8157dbbbd51687c23cb783c5fe4', NULL, NULL, NULL, NULL, 'hm-VO0n2GO0qEZhEz6LdBBtFhIEMv0jo', 1, NULL, '2024-04-02 14:59:08.770239', '2024-04-07 10:50:24.000000', '孟菲', 2); -INSERT INTO `sys_user` VALUES (10, 'wangxinghao', '41c7f42c6e8a3eec7ac5b41ae0de84be', '[object Object]', NULL, NULL, NULL, '--IWP-ybu1ikzGpGOVCWEpkZ1hIheCVJ', 1, NULL, '2024-04-02 15:39:38.117227', '2024-04-07 11:07:49.000000', '王兴昊', 2); -INSERT INTO `sys_user` VALUES (11, 'zhangxueyong', 'c67a29c03f168650e2a43590328446b8', NULL, NULL, NULL, '张学勇', 'ucnRs7VTbXnwej2JQbKeYSoJ1gLy2Fwi', 1, NULL, '2024-04-03 08:10:08.192204', '2024-04-07 11:07:54.000000', '张学勇', 1); +INSERT INTO `sys_user` VALUES (1, 'admin', '7bca9f937f79f70497497cc9f959b164', NULL, '1743369777@qq.com', '18661983080', '管理员', 'xQYCspvFb8cAW6GG1pOoUGTLqsuUSO3d', 1, '1743369777', '2023-11-10 00:31:44.104382', '2024-04-15 14:55:26.000000', '管理员', 11, 'zhumingren', 1); +INSERT INTO `sys_user` VALUES (20, 'lxf', '63233c441c8418fc66f6e71106cf313f', NULL, NULL, NULL, NULL, 'maYvmwzLfFXm1PY10lZIeeRchgXg1ZaM', 1, NULL, '2024-04-26 09:20:02.312768', '2024-04-26 09:20:02.312768', '刘晓飞', 10, 'liuxiaofei', 1); -- ---------------------------- -- Table structure for sys_user_roles @@ -907,8 +1404,8 @@ CREATE TABLE `sys_user_roles` ( `user_id` int NOT NULL, `role_id` int NOT NULL, PRIMARY KEY (`user_id`, `role_id`) USING BTREE, - INDEX `IDX_96311d970191a044ec048011f4`(`user_id`) USING BTREE, - INDEX `IDX_6d61c5b3f76a3419d93a421669`(`role_id`) USING BTREE, + INDEX `IDX_96311d970191a044ec048011f4`(`user_id` ASC) USING BTREE, + INDEX `IDX_6d61c5b3f76a3419d93a421669`(`role_id` ASC) USING BTREE, CONSTRAINT `FK_6d61c5b3f76a3419d93a4216695` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT `FK_96311d970191a044ec048011f44` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; @@ -917,9 +1414,7 @@ CREATE TABLE `sys_user_roles` ( -- Records of sys_user_roles -- ---------------------------- INSERT INTO `sys_user_roles` VALUES (1, 1); -INSERT INTO `sys_user_roles` VALUES (9, 10); -INSERT INTO `sys_user_roles` VALUES (10, 10); -INSERT INTO `sys_user_roles` VALUES (11, 10); +INSERT INTO `sys_user_roles` VALUES (20, 10); -- ---------------------------- -- Table structure for todo @@ -933,7 +1428,7 @@ CREATE TABLE `todo` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_9cb7989853c4cb7fe427db4b260`(`user_id`) USING BTREE, + INDEX `FK_9cb7989853c4cb7fe427db4b260`(`user_id` ASC) USING BTREE, CONSTRAINT `FK_9cb7989853c4cb7fe427db4b260` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; @@ -960,11 +1455,359 @@ CREATE TABLE `tool_storage` ( `bussiness_module` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `bussiness_record_id` int NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 280 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; +) ENGINE = InnoDB AUTO_INCREMENT = 632 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of tool_storage -- ---------------------------- +INSERT INTO `tool_storage` VALUES (284, '2024-04-07 16:43:22.248400', '2024-04-07 16:43:22.248400', 'shankuangtong_v1.0.1.apk', 'shankuangtong_v1.0.1.apk', 'apk', '/upload/shankuangtong_v1.0.1.apk', 'apk', '28.83 MB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (285, '2024-04-08 09:35:16.320627', '2024-04-08 09:35:16.320627', 'scaled_b1556bb5-5017-4f84-89d3-4d1c0d7daa3e6300246894040611335-202404080935318.jpg', 'scaled_b1556bb5-5017-4f84-89d3-4d1c0d7daa3e6300246894040611335.jpg', 'jpg', '/upload/scaled_b1556bb5-5017-4f84-89d3-4d1c0d7daa3e6300246894040611335-202404080935318.jpg', '图片', '3.01 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (286, '2024-04-08 09:35:16.471971', '2024-04-08 09:35:16.471971', 'scaled_9ed9a043-4f9c-4e1f-aad5-656e3917eaae5283915690099580112-202404080935469.jpg', 'scaled_9ed9a043-4f9c-4e1f-aad5-656e3917eaae5283915690099580112.jpg', 'jpg', '/upload/scaled_9ed9a043-4f9c-4e1f-aad5-656e3917eaae5283915690099580112-202404080935469.jpg', '图片', '3.05 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (287, '2024-04-08 09:37:12.941126', '2024-04-08 09:37:12.941126', 'scaled_5a9b01ad-770b-44c5-982c-70fcb9f7d4725759829019760881895-202404080937939.jpg', 'scaled_5a9b01ad-770b-44c5-982c-70fcb9f7d4725759829019760881895.jpg', 'jpg', '/upload/scaled_5a9b01ad-770b-44c5-982c-70fcb9f7d4725759829019760881895-202404080937939.jpg', '图片', '3.01 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (288, '2024-04-08 09:37:13.316531', '2024-04-08 09:37:13.316531', 'scaled_ebc0239f-827d-425a-8383-8aa43edc33394493841877362950107-202404080937315.jpg', 'scaled_ebc0239f-827d-425a-8383-8aa43edc33394493841877362950107.jpg', 'jpg', '/upload/scaled_ebc0239f-827d-425a-8383-8aa43edc33394493841877362950107-202404080937315.jpg', '图片', '2.89 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (289, '2024-04-08 10:22:06.583353', '2024-04-08 10:22:06.583353', 'scaled_960d47c1-659f-4a69-a5bc-c744cff153578857833863321022338-202404081022581.jpg', 'scaled_960d47c1-659f-4a69-a5bc-c744cff153578857833863321022338.jpg', 'jpg', '/upload/scaled_960d47c1-659f-4a69-a5bc-c744cff153578857833863321022338-202404081022581.jpg', '图片', '2.64 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (290, '2024-04-08 10:22:07.047337', '2024-04-08 10:22:07.047337', 'scaled_ec6bb249-f322-4590-aac2-6f9032d72f9c2321286719266172535-202404081022045.jpg', 'scaled_ec6bb249-f322-4590-aac2-6f9032d72f9c2321286719266172535.jpg', 'jpg', '/upload/scaled_ec6bb249-f322-4590-aac2-6f9032d72f9c2321286719266172535-202404081022045.jpg', '图片', '2.64 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (291, '2024-04-08 13:50:38.694652', '2024-04-08 13:50:38.694652', 'scaled_fbfa6369-79be-49b1-b1c2-15dde14d8e7f5302193102973318143-202404081350692.jpg', 'scaled_fbfa6369-79be-49b1-b1c2-15dde14d8e7f5302193102973318143.jpg', 'jpg', '/upload/scaled_fbfa6369-79be-49b1-b1c2-15dde14d8e7f5302193102973318143-202404081350692.jpg', '图片', '2.79 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (292, '2024-04-08 13:50:39.254540', '2024-04-08 13:50:39.254540', 'scaled_db3721ca-3026-46b1-91c9-53665e86030a374899700869040512-202404081350252.jpg', 'scaled_db3721ca-3026-46b1-91c9-53665e86030a374899700869040512.jpg', 'jpg', '/upload/scaled_db3721ca-3026-46b1-91c9-53665e86030a374899700869040512-202404081350252.jpg', '图片', '2.74 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (293, '2024-04-08 13:52:49.639488', '2024-04-08 13:52:49.639488', 'scaled_297e01ee-cf30-40dc-aac5-184d660eb6811563575712762243812-202404081352637.jpg', 'scaled_297e01ee-cf30-40dc-aac5-184d660eb6811563575712762243812.jpg', 'jpg', '/upload/scaled_297e01ee-cf30-40dc-aac5-184d660eb6811563575712762243812-202404081352637.jpg', '图片', '1.75 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (294, '2024-04-08 13:52:49.935202', '2024-04-08 13:52:49.935202', 'scaled_9713e04c-8c7b-4952-9f86-29d33fa68dd72433642152206640844-202404081352933.jpg', 'scaled_9713e04c-8c7b-4952-9f86-29d33fa68dd72433642152206640844.jpg', 'jpg', '/upload/scaled_9713e04c-8c7b-4952-9f86-29d33fa68dd72433642152206640844-202404081352933.jpg', '图片', '2 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (295, '2024-04-08 13:54:16.895732', '2024-04-08 13:54:16.895732', 'scaled_2fdd48c8-dbe1-4179-b943-cb4ad7017daf2012436397245671198-202404081354893.jpg', 'scaled_2fdd48c8-dbe1-4179-b943-cb4ad7017daf2012436397245671198.jpg', 'jpg', '/upload/scaled_2fdd48c8-dbe1-4179-b943-cb4ad7017daf2012436397245671198-202404081354893.jpg', '图片', '2.65 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (296, '2024-04-08 13:54:18.111741', '2024-04-08 13:54:18.111741', 'scaled_46af45f9-3552-430e-bdc3-37afcf7a67f7156362432174474703-202404081354110.jpg', 'scaled_46af45f9-3552-430e-bdc3-37afcf7a67f7156362432174474703.jpg', 'jpg', '/upload/scaled_46af45f9-3552-430e-bdc3-37afcf7a67f7156362432174474703-202404081354110.jpg', '图片', '2.34 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (297, '2024-04-08 14:01:50.512885', '2024-04-08 14:01:50.512885', 'scaled_9d132569-7c49-4032-a2dc-48921cf4ed1c3856355396710587488-202404081401507.jpg', 'scaled_9d132569-7c49-4032-a2dc-48921cf4ed1c3856355396710587488.jpg', 'jpg', '/upload/scaled_9d132569-7c49-4032-a2dc-48921cf4ed1c3856355396710587488-202404081401507.jpg', '图片', '2.85 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (298, '2024-04-08 14:01:50.516799', '2024-04-08 14:01:50.516799', 'scaled_16513941-5578-451a-8431-18956c903df08538569229355513677-202404081401514.jpg', 'scaled_16513941-5578-451a-8431-18956c903df08538569229355513677.jpg', 'jpg', '/upload/scaled_16513941-5578-451a-8431-18956c903df08538569229355513677-202404081401514.jpg', '图片', '2.92 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (299, '2024-04-08 14:48:59.751022', '2024-04-08 14:48:59.751022', 'scaled_c6b974c3-edfd-4c5c-95fa-6d9bd484d47a4706709892115185004-202404081448747.jpg', 'scaled_c6b974c3-edfd-4c5c-95fa-6d9bd484d47a4706709892115185004.jpg', 'jpg', '/upload/scaled_c6b974c3-edfd-4c5c-95fa-6d9bd484d47a4706709892115185004-202404081448747.jpg', '图片', '3 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (300, '2024-04-08 14:49:00.108519', '2024-04-08 14:49:00.108519', 'scaled_8863d0da-9c79-4250-ab13-09c9d15531bc8838826845173508353-202404081449107.jpg', 'scaled_8863d0da-9c79-4250-ab13-09c9d15531bc8838826845173508353.jpg', 'jpg', '/upload/scaled_8863d0da-9c79-4250-ab13-09c9d15531bc8838826845173508353-202404081449107.jpg', '图片', '3.04 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (301, '2024-04-09 13:08:10.856607', '2024-04-09 13:08:10.856607', 'scaled_a301f23a-9c0e-408f-a5de-d8f6fd85952e1678131979815766184-202404091308849.jpg', 'scaled_a301f23a-9c0e-408f-a5de-d8f6fd85952e1678131979815766184.jpg', 'jpg', '/upload/scaled_a301f23a-9c0e-408f-a5de-d8f6fd85952e1678131979815766184-202404091308849.jpg', '图片', '3.37 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (302, '2024-04-09 13:08:11.384698', '2024-04-09 13:08:11.384698', 'scaled_9b7dabac-1260-420e-8b41-60e5b013e5d16306405704494328029-202404091308383.jpg', 'scaled_9b7dabac-1260-420e-8b41-60e5b013e5d16306405704494328029.jpg', 'jpg', '/upload/scaled_9b7dabac-1260-420e-8b41-60e5b013e5d16306405704494328029-202404091308383.jpg', '图片', '2.77 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (303, '2024-04-09 14:55:49.512089', '2024-04-09 14:55:49.512089', 'scaled_d6d54a44-b4dd-402e-9970-fff9f918d9e0893527563411015056-202404091455510.jpg', 'scaled_d6d54a44-b4dd-402e-9970-fff9f918d9e0893527563411015056.jpg', 'jpg', '/upload/scaled_d6d54a44-b4dd-402e-9970-fff9f918d9e0893527563411015056-202404091455510.jpg', '图片', '1.74 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (304, '2024-04-09 14:55:49.697570', '2024-04-09 14:55:49.697570', 'scaled_885b6463-c10f-42bb-a443-f5d74320e6778321398576351468952-202404091455693.jpg', 'scaled_885b6463-c10f-42bb-a443-f5d74320e6778321398576351468952.jpg', 'jpg', '/upload/scaled_885b6463-c10f-42bb-a443-f5d74320e6778321398576351468952-202404091455693.jpg', '图片', '2.96 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (305, '2024-04-09 14:57:45.112216', '2024-04-09 14:57:45.112216', 'scaled_d426a536-fa0c-45de-afb0-8b83d9e6b6b89040374279156759705-202404091457110.jpg', 'scaled_d426a536-fa0c-45de-afb0-8b83d9e6b6b89040374279156759705.jpg', 'jpg', '/upload/scaled_d426a536-fa0c-45de-afb0-8b83d9e6b6b89040374279156759705-202404091457110.jpg', '图片', '2.91 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (306, '2024-04-09 14:57:45.754073', '2024-04-09 14:57:45.754073', 'scaled_0b9b6029-8b75-4ddb-8aca-98d794efb5596052353158250166225-202404091457752.jpg', 'scaled_0b9b6029-8b75-4ddb-8aca-98d794efb5596052353158250166225.jpg', 'jpg', '/upload/scaled_0b9b6029-8b75-4ddb-8aca-98d794efb5596052353158250166225-202404091457752.jpg', '图片', '2.76 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (307, '2024-04-09 15:50:11.332034', '2024-04-09 15:50:11.332034', 'scaled_ea348a96-f326-490a-8efa-dbfcaf6f6df37817303120843971532-202404091550328.jpg', 'scaled_ea348a96-f326-490a-8efa-dbfcaf6f6df37817303120843971532.jpg', 'jpg', '/upload/scaled_ea348a96-f326-490a-8efa-dbfcaf6f6df37817303120843971532-202404091550328.jpg', '图片', '1.74 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (308, '2024-04-09 15:50:11.679642', '2024-04-09 15:50:11.679642', 'scaled_2c0cbea2-08c3-4187-a286-140882b624a84860588713500381093-202404091550677.jpg', 'scaled_2c0cbea2-08c3-4187-a286-140882b624a84860588713500381093.jpg', 'jpg', '/upload/scaled_2c0cbea2-08c3-4187-a286-140882b624a84860588713500381093-202404091550677.jpg', '图片', '2.54 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (309, '2024-04-09 15:50:56.807403', '2024-04-09 15:50:56.807403', 'scaled_a665092f-55ea-497d-85c8-8b0bdc0199775706350879100230825-202404091550804.jpg', 'scaled_a665092f-55ea-497d-85c8-8b0bdc0199775706350879100230825.jpg', 'jpg', '/upload/scaled_a665092f-55ea-497d-85c8-8b0bdc0199775706350879100230825-202404091550804.jpg', '图片', '2.64 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (310, '2024-04-09 15:50:57.100072', '2024-04-09 15:50:57.100072', 'scaled_2db08e90-c03b-4243-9326-616a6314be132536040167590865219-202404091550098.jpg', 'scaled_2db08e90-c03b-4243-9326-616a6314be132536040167590865219.jpg', 'jpg', '/upload/scaled_2db08e90-c03b-4243-9326-616a6314be132536040167590865219-202404091550098.jpg', '图片', '2.5 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (311, '2024-04-09 15:51:35.936997', '2024-04-09 15:51:35.936997', 'scaled_768bc2ca-00f5-49d2-bd2e-fbf94478cde31095914785993701417-202404091551935.jpg', 'scaled_768bc2ca-00f5-49d2-bd2e-fbf94478cde31095914785993701417.jpg', 'jpg', '/upload/scaled_768bc2ca-00f5-49d2-bd2e-fbf94478cde31095914785993701417-202404091551935.jpg', '图片', '2.35 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (312, '2024-04-09 15:51:36.965256', '2024-04-09 15:51:36.965256', 'scaled_25bdda95-bc41-4842-b001-16924df9d8dc8995912777611297598-202404091551962.jpg', 'scaled_25bdda95-bc41-4842-b001-16924df9d8dc8995912777611297598.jpg', 'jpg', '/upload/scaled_25bdda95-bc41-4842-b001-16924df9d8dc8995912777611297598-202404091551962.jpg', '图片', '2.61 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (313, '2024-04-09 15:52:09.359206', '2024-04-09 15:52:09.359206', 'scaled_babdc9d4-2a03-4d5c-94ed-58448bae12c76941112683893927743-202404091552357.jpg', 'scaled_babdc9d4-2a03-4d5c-94ed-58448bae12c76941112683893927743.jpg', 'jpg', '/upload/scaled_babdc9d4-2a03-4d5c-94ed-58448bae12c76941112683893927743-202404091552357.jpg', '图片', '2.31 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (314, '2024-04-09 15:52:09.563354', '2024-04-09 15:52:09.563354', 'scaled_bb67351b-1c29-4526-bfee-084f837442f64959555304270022161-202404091552561.jpg', 'scaled_bb67351b-1c29-4526-bfee-084f837442f64959555304270022161.jpg', 'jpg', '/upload/scaled_bb67351b-1c29-4526-bfee-084f837442f64959555304270022161-202404091552561.jpg', '图片', '2.68 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (315, '2024-04-09 15:52:47.109559', '2024-04-09 15:52:47.109559', 'scaled_accce6eb-7203-4e91-93b9-8cc44d3fdcc82942104465021127615-202404091552107.jpg', 'scaled_accce6eb-7203-4e91-93b9-8cc44d3fdcc82942104465021127615.jpg', 'jpg', '/upload/scaled_accce6eb-7203-4e91-93b9-8cc44d3fdcc82942104465021127615-202404091552107.jpg', '图片', '2.31 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (316, '2024-04-09 15:52:47.151584', '2024-04-09 15:52:47.151584', 'scaled_7ba2b25f-7a8e-4c97-89bb-da45429f45da5561236046280901560-202404091552148.jpg', 'scaled_7ba2b25f-7a8e-4c97-89bb-da45429f45da5561236046280901560.jpg', 'jpg', '/upload/scaled_7ba2b25f-7a8e-4c97-89bb-da45429f45da5561236046280901560-202404091552148.jpg', '图片', '2.69 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (317, '2024-04-09 15:53:23.804678', '2024-04-09 15:53:23.804678', 'scaled_d7e41ca1-be0a-4542-9428-8cf862ebb8d6713850376803579339-202404091553803.jpg', 'scaled_d7e41ca1-be0a-4542-9428-8cf862ebb8d6713850376803579339.jpg', 'jpg', '/upload/scaled_d7e41ca1-be0a-4542-9428-8cf862ebb8d6713850376803579339-202404091553803.jpg', '图片', '2.32 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (318, '2024-04-09 15:53:24.066090', '2024-04-09 15:53:24.066090', 'scaled_f6dec42c-cf93-483e-b9e6-9f529f78d9d37792686427991960021-202404091553064.jpg', 'scaled_f6dec42c-cf93-483e-b9e6-9f529f78d9d37792686427991960021.jpg', 'jpg', '/upload/scaled_f6dec42c-cf93-483e-b9e6-9f529f78d9d37792686427991960021-202404091553064.jpg', '图片', '2.43 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (319, '2024-04-09 15:53:59.579424', '2024-04-09 15:53:59.579424', 'scaled_858cd6df-3bbc-47db-96a9-c0b86eb8e4c95982774152196032539-202404091553576.jpg', 'scaled_858cd6df-3bbc-47db-96a9-c0b86eb8e4c95982774152196032539.jpg', 'jpg', '/upload/scaled_858cd6df-3bbc-47db-96a9-c0b86eb8e4c95982774152196032539-202404091553576.jpg', '图片', '1.57 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (320, '2024-04-09 15:54:00.014095', '2024-04-09 15:54:00.014095', 'scaled_2421097c-d4b7-4daa-8d08-4e569eef3b178450990362963486574-202404091554012.jpg', 'scaled_2421097c-d4b7-4daa-8d08-4e569eef3b178450990362963486574.jpg', 'jpg', '/upload/scaled_2421097c-d4b7-4daa-8d08-4e569eef3b178450990362963486574-202404091554012.jpg', '图片', '2.71 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (321, '2024-04-09 15:54:46.156175', '2024-04-09 15:54:46.156175', 'scaled_64ab105b-4f28-4c1d-91d0-d354fb7c426e1635385142902961628-202404091554154.jpg', 'scaled_64ab105b-4f28-4c1d-91d0-d354fb7c426e1635385142902961628.jpg', 'jpg', '/upload/scaled_64ab105b-4f28-4c1d-91d0-d354fb7c426e1635385142902961628-202404091554154.jpg', '图片', '2.68 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (322, '2024-04-09 15:54:46.695927', '2024-04-09 15:54:46.695927', 'scaled_14955c99-8989-4d96-93dd-dbba19b4dc08500782504278128298-202404091554694.jpg', 'scaled_14955c99-8989-4d96-93dd-dbba19b4dc08500782504278128298.jpg', 'jpg', '/upload/scaled_14955c99-8989-4d96-93dd-dbba19b4dc08500782504278128298-202404091554694.jpg', '图片', '2.63 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (323, '2024-04-09 15:56:43.157403', '2024-04-09 15:56:43.157403', 'scaled_f74dab8a-2d32-4fb1-9015-1787084463483070624010782781977-202404091556156.jpg', 'scaled_f74dab8a-2d32-4fb1-9015-1787084463483070624010782781977.jpg', 'jpg', '/upload/scaled_f74dab8a-2d32-4fb1-9015-1787084463483070624010782781977-202404091556156.jpg', '图片', '2.75 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (324, '2024-04-09 15:56:43.933007', '2024-04-09 15:56:43.933007', 'scaled_cea6fcfc-915e-4d56-a0bd-9549038d8ed94378764780906657532-202404091556931.jpg', 'scaled_cea6fcfc-915e-4d56-a0bd-9549038d8ed94378764780906657532.jpg', 'jpg', '/upload/scaled_cea6fcfc-915e-4d56-a0bd-9549038d8ed94378764780906657532-202404091556931.jpg', '图片', '3.1 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (325, '2024-04-09 15:57:27.925846', '2024-04-09 15:57:27.925846', 'scaled_6dc8858c-fb7c-4892-9112-5ae9f8391fc56014665190068314121-202404091557923.jpg', 'scaled_6dc8858c-fb7c-4892-9112-5ae9f8391fc56014665190068314121.jpg', 'jpg', '/upload/scaled_6dc8858c-fb7c-4892-9112-5ae9f8391fc56014665190068314121-202404091557923.jpg', '图片', '2.87 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (326, '2024-04-09 15:57:28.150905', '2024-04-09 15:57:28.150905', 'scaled_ce2601d5-04ed-4fed-91ab-d63dadbb67ec6281464930860070312-202404091557149.jpg', 'scaled_ce2601d5-04ed-4fed-91ab-d63dadbb67ec6281464930860070312.jpg', 'jpg', '/upload/scaled_ce2601d5-04ed-4fed-91ab-d63dadbb67ec6281464930860070312-202404091557149.jpg', '图片', '2.86 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (327, '2024-04-09 15:58:02.941077', '2024-04-09 15:58:02.941077', 'scaled_5592bf6c-9b3f-41f7-92ec-c38ff1d7ab9b365399783187135094-202404091558939.jpg', 'scaled_5592bf6c-9b3f-41f7-92ec-c38ff1d7ab9b365399783187135094.jpg', 'jpg', '/upload/scaled_5592bf6c-9b3f-41f7-92ec-c38ff1d7ab9b365399783187135094-202404091558939.jpg', '图片', '2.79 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (328, '2024-04-09 15:58:03.473938', '2024-04-09 15:58:03.473938', 'scaled_ed953464-463b-4bfe-8ab3-3ad13f71e6133161259791744032920-202404091558472.jpg', 'scaled_ed953464-463b-4bfe-8ab3-3ad13f71e6133161259791744032920.jpg', 'jpg', '/upload/scaled_ed953464-463b-4bfe-8ab3-3ad13f71e6133161259791744032920-202404091558472.jpg', '图片', '2.78 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (329, '2024-04-11 08:56:31.082834', '2024-04-11 08:56:31.082834', 'scaled_408d9d2b-de98-4d04-a7db-3bf4ca386d795076386747072070617-202404110856074.jpg', 'scaled_408d9d2b-de98-4d04-a7db-3bf4ca386d795076386747072070617.jpg', 'jpg', '/upload/scaled_408d9d2b-de98-4d04-a7db-3bf4ca386d795076386747072070617-202404110856074.jpg', '图片', '2.57 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (330, '2024-04-11 08:56:31.269928', '2024-04-11 08:56:31.269928', 'scaled_42d8c09f-b3c2-4324-8dca-4e6cc498d6a51348007937833186523-202404110856264.jpg', 'scaled_42d8c09f-b3c2-4324-8dca-4e6cc498d6a51348007937833186523.jpg', 'jpg', '/upload/scaled_42d8c09f-b3c2-4324-8dca-4e6cc498d6a51348007937833186523-202404110856264.jpg', '图片', '2.51 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (331, '2024-04-11 08:56:32.189197', '2024-04-11 08:56:32.189197', 'scaled_a81ba751-dec5-4456-b53a-e664da4a2fca2373941979374200993-202404110856184.jpg', 'scaled_a81ba751-dec5-4456-b53a-e664da4a2fca2373941979374200993.jpg', 'jpg', '/upload/scaled_a81ba751-dec5-4456-b53a-e664da4a2fca2373941979374200993-202404110856184.jpg', '图片', '2.75 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (332, '2024-04-11 09:14:52.432379', '2024-04-11 09:14:52.432379', 'shankuangtong_v1.0.1.apk', 'shankuangtong_v1.0.1.apk', 'apk', '/upload/shankuangtong_v1.0.1.apk', 'apk', '30.19 MB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (333, '2024-04-11 13:54:04.720552', '2024-04-11 13:54:04.720552', 'scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334-202404111354714.jpg', 'scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334.jpg', 'jpg', '/upload/scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334-202404111354714.jpg', '图片', '2.72 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (334, '2024-04-11 13:54:04.967593', '2024-04-11 13:54:04.967593', 'scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701-202404111354964.jpg', 'scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701.jpg', 'jpg', '/upload/scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701-202404111354964.jpg', '图片', '2.62 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (335, '2024-04-11 13:54:23.270387', '2024-04-11 13:54:23.270387', 'scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701-202404111354268.jpg', 'scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701.jpg', 'jpg', '/upload/scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701-202404111354268.jpg', '图片', '2.62 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (336, '2024-04-11 13:54:23.290942', '2024-04-11 13:54:23.290942', 'scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334-202404111354289.jpg', 'scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334.jpg', 'jpg', '/upload/scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334-202404111354289.jpg', '图片', '2.72 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (337, '2024-04-11 13:54:51.399718', '2024-04-11 13:54:51.399718', 'scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334-202404111354397.jpg', 'scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334.jpg', 'jpg', '/upload/scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334-202404111354397.jpg', '图片', '2.72 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (338, '2024-04-11 13:54:51.473352', '2024-04-11 13:54:51.473352', 'scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701-202404111354471.jpg', 'scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701.jpg', 'jpg', '/upload/scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701-202404111354471.jpg', '图片', '2.62 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (339, '2024-04-11 13:55:06.736662', '2024-04-11 13:55:06.736662', 'scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334-202404111355734.jpg', 'scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334.jpg', 'jpg', '/upload/scaled_6683b364-bb1d-4bc4-b363-33514e4779544152577526772746334-202404111355734.jpg', '图片', '2.72 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (340, '2024-04-11 13:55:06.893717', '2024-04-11 13:55:06.893717', 'scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701-202404111355891.jpg', 'scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701.jpg', 'jpg', '/upload/scaled_13da0853-7a43-4a2e-a1cf-98fd32125fe54742660164263510701-202404111355891.jpg', '图片', '2.62 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (341, '2024-04-11 14:06:33.898416', '2024-04-11 14:06:33.898416', 'shankuangtong_v1.0.2.apk', 'shankuangtong_v1.0.2.apk', 'apk', '/upload/shankuangtong_v1.0.2.apk', 'apk', '30.19 MB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (342, '2024-04-11 14:12:43.883640', '2024-04-11 14:12:43.883640', 'scaled_81893efd-fc87-4cff-aab8-1ca4f699c3d9624912710427357704-202404111412881.jpg', 'scaled_81893efd-fc87-4cff-aab8-1ca4f699c3d9624912710427357704.jpg', 'jpg', '/upload/scaled_81893efd-fc87-4cff-aab8-1ca4f699c3d9624912710427357704-202404111412881.jpg', '图片', '3.06 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (343, '2024-04-11 14:12:44.825256', '2024-04-11 14:12:44.825256', 'scaled_1583586b-9892-46a0-81ba-23c0bcad82b04308587098580958549-202404111412823.jpg', 'scaled_1583586b-9892-46a0-81ba-23c0bcad82b04308587098580958549.jpg', 'jpg', '/upload/scaled_1583586b-9892-46a0-81ba-23c0bcad82b04308587098580958549-202404111412823.jpg', '图片', '2.76 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (344, '2024-04-11 14:14:32.832372', '2024-04-11 14:14:32.832372', 'scaled_369b997f-963f-4354-99a5-783cba751dcd3958562103118127251-202404111414830.jpg', 'scaled_369b997f-963f-4354-99a5-783cba751dcd3958562103118127251.jpg', 'jpg', '/upload/scaled_369b997f-963f-4354-99a5-783cba751dcd3958562103118127251-202404111414830.jpg', '图片', '2.55 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (345, '2024-04-11 14:14:33.332025', '2024-04-11 14:14:33.332025', 'scaled_25afbb5a-8748-435a-b9fc-9dd42e1738872636744872549725356-202404111414330.jpg', 'scaled_25afbb5a-8748-435a-b9fc-9dd42e1738872636744872549725356.jpg', 'jpg', '/upload/scaled_25afbb5a-8748-435a-b9fc-9dd42e1738872636744872549725356-202404111414330.jpg', '图片', '3.1 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (346, '2024-04-11 14:17:00.603190', '2024-04-11 14:17:00.603190', 'scaled_c14cb36c-0f6c-4d8f-94e4-04551f7a24ff7094585102962798512-202404111417601.jpg', 'scaled_c14cb36c-0f6c-4d8f-94e4-04551f7a24ff7094585102962798512.jpg', 'jpg', '/upload/scaled_c14cb36c-0f6c-4d8f-94e4-04551f7a24ff7094585102962798512-202404111417601.jpg', '图片', '1.69 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (347, '2024-04-11 14:17:00.668322', '2024-04-11 14:17:00.668322', 'scaled_34895eb3-06b8-4d83-b5f1-1b2e4a63b1e22090898519081181573-202404111417666.jpg', 'scaled_34895eb3-06b8-4d83-b5f1-1b2e4a63b1e22090898519081181573.jpg', 'jpg', '/upload/scaled_34895eb3-06b8-4d83-b5f1-1b2e4a63b1e22090898519081181573-202404111417666.jpg', '图片', '2.73 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (348, '2024-04-11 14:19:16.167951', '2024-04-11 14:19:16.167951', 'scaled_71487edb-ed77-47cc-859a-39ebe73861631239028050629950553-202404111419166.jpg', 'scaled_71487edb-ed77-47cc-859a-39ebe73861631239028050629950553.jpg', 'jpg', '/upload/scaled_71487edb-ed77-47cc-859a-39ebe73861631239028050629950553-202404111419166.jpg', '图片', '2.74 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (349, '2024-04-11 14:19:16.817619', '2024-04-11 14:19:16.817619', 'scaled_abc8d57c-450f-4603-98b8-fb2d5e9c8cf12688210089139470699-202404111419814.jpg', 'scaled_abc8d57c-450f-4603-98b8-fb2d5e9c8cf12688210089139470699.jpg', 'jpg', '/upload/scaled_abc8d57c-450f-4603-98b8-fb2d5e9c8cf12688210089139470699-202404111419814.jpg', '图片', '2.85 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (350, '2024-04-11 14:21:23.650907', '2024-04-11 14:21:23.650907', 'scaled_20d4015e-5007-4820-a568-3f9561a9cda51881160785610959043-202404111421648.jpg', 'scaled_20d4015e-5007-4820-a568-3f9561a9cda51881160785610959043.jpg', 'jpg', '/upload/scaled_20d4015e-5007-4820-a568-3f9561a9cda51881160785610959043-202404111421648.jpg', '图片', '2.18 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (351, '2024-04-11 14:21:24.252711', '2024-04-11 14:21:24.252711', 'scaled_5dd8d7f0-48b3-4a10-b4cb-0a3c5c7031921694820848929277259-202404111421251.jpg', 'scaled_5dd8d7f0-48b3-4a10-b4cb-0a3c5c7031921694820848929277259.jpg', 'jpg', '/upload/scaled_5dd8d7f0-48b3-4a10-b4cb-0a3c5c7031921694820848929277259-202404111421251.jpg', '图片', '2.97 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (352, '2024-04-11 14:24:57.254425', '2024-04-11 14:24:57.254425', 'scaled_d8cf6041-babe-47b6-993a-a61f016cdcd51927397056489173153-202404111424252.jpg', 'scaled_d8cf6041-babe-47b6-993a-a61f016cdcd51927397056489173153.jpg', 'jpg', '/upload/scaled_d8cf6041-babe-47b6-993a-a61f016cdcd51927397056489173153-202404111424252.jpg', '图片', '2.6 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (353, '2024-04-11 14:24:57.630924', '2024-04-11 14:24:57.630924', 'scaled_1efa1109-b2fd-4b85-bc90-396b60c8a1c75070638035529239422-202404111424628.jpg', 'scaled_1efa1109-b2fd-4b85-bc90-396b60c8a1c75070638035529239422.jpg', 'jpg', '/upload/scaled_1efa1109-b2fd-4b85-bc90-396b60c8a1c75070638035529239422-202404111424628.jpg', '图片', '2.56 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (354, '2024-04-12 14:33:17.694901', '2024-04-12 14:33:17.694901', 'scaled_e27887f2-bccb-4b3c-b129-7f8f7c49b0ab2586079543310331386-202404121433692.jpg', 'scaled_e27887f2-bccb-4b3c-b129-7f8f7c49b0ab2586079543310331386.jpg', 'jpg', '/upload/scaled_e27887f2-bccb-4b3c-b129-7f8f7c49b0ab2586079543310331386-202404121433692.jpg', '图片', '2.37 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (355, '2024-04-12 14:33:17.863593', '2024-04-12 14:33:17.863593', 'scaled_05da09c5-0cbd-4aae-a4b9-bb38586f9fce6735256319921008823-202404121433862.jpg', 'scaled_05da09c5-0cbd-4aae-a4b9-bb38586f9fce6735256319921008823.jpg', 'jpg', '/upload/scaled_05da09c5-0cbd-4aae-a4b9-bb38586f9fce6735256319921008823-202404121433862.jpg', '图片', '2.72 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (356, '2024-04-12 16:00:08.316987', '2024-04-12 16:00:08.316987', 'scaled_1b2e225f-af49-40a9-bdd4-c542105115de3166905000744992452-202404121600314.jpg', 'scaled_1b2e225f-af49-40a9-bdd4-c542105115de3166905000744992452.jpg', 'jpg', '/upload/scaled_1b2e225f-af49-40a9-bdd4-c542105115de3166905000744992452-202404121600314.jpg', '图片', '2.88 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (357, '2024-04-12 16:00:09.427441', '2024-04-12 16:00:09.427441', 'scaled_fd6d4864-04d3-4bbb-bbbf-a9527f828757440396575894135094-202404121600425.jpg', 'scaled_fd6d4864-04d3-4bbb-bbbf-a9527f828757440396575894135094.jpg', 'jpg', '/upload/scaled_fd6d4864-04d3-4bbb-bbbf-a9527f828757440396575894135094-202404121600425.jpg', '图片', '3.03 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (358, '2024-04-12 16:00:50.412598', '2024-04-12 16:00:50.412598', 'scaled_d128255a-8386-4d33-8e7c-07809244cfff4339837726064476473-202404121600410.jpg', 'scaled_d128255a-8386-4d33-8e7c-07809244cfff4339837726064476473.jpg', 'jpg', '/upload/scaled_d128255a-8386-4d33-8e7c-07809244cfff4339837726064476473-202404121600410.jpg', '图片', '2.5 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (359, '2024-04-12 16:00:50.856864', '2024-04-12 16:00:50.856864', 'scaled_c8ef84a9-7c89-4ae6-bd58-7269c44be4362665885842007864370-202404121600855.jpg', 'scaled_c8ef84a9-7c89-4ae6-bd58-7269c44be4362665885842007864370.jpg', 'jpg', '/upload/scaled_c8ef84a9-7c89-4ae6-bd58-7269c44be4362665885842007864370-202404121600855.jpg', '图片', '2.94 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (360, '2024-04-13 08:05:22.375452', '2024-04-13 08:05:22.375452', 'scaled_a8bf4850-8e71-426a-bfcf-7cbccfb9c8ad6424182621452226334-202404130805373.jpg', 'scaled_a8bf4850-8e71-426a-bfcf-7cbccfb9c8ad6424182621452226334.jpg', 'jpg', '/upload/scaled_a8bf4850-8e71-426a-bfcf-7cbccfb9c8ad6424182621452226334-202404130805373.jpg', '图片', '3.23 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (361, '2024-04-13 08:05:22.755577', '2024-04-13 08:05:22.755577', 'scaled_498ddc09-23ef-4424-b938-2757599eba865145766805745104497-202404130805754.jpg', 'scaled_498ddc09-23ef-4424-b938-2757599eba865145766805745104497.jpg', 'jpg', '/upload/scaled_498ddc09-23ef-4424-b938-2757599eba865145766805745104497-202404130805754.jpg', '图片', '3 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (362, '2024-04-13 08:27:58.371854', '2024-04-13 08:27:58.371854', 'scaled_358f624c-3e0f-45fb-9f92-8a4fb37e4f427045473812357743627-202404130827370.jpg', 'scaled_358f624c-3e0f-45fb-9f92-8a4fb37e4f427045473812357743627.jpg', 'jpg', '/upload/scaled_358f624c-3e0f-45fb-9f92-8a4fb37e4f427045473812357743627-202404130827370.jpg', '图片', '2.8 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (363, '2024-04-13 08:27:58.494237', '2024-04-13 08:27:58.494237', 'scaled_559f822d-7745-40c8-a272-1bbd08d0a5024106502618044994684-202404130827493.jpg', 'scaled_559f822d-7745-40c8-a272-1bbd08d0a5024106502618044994684.jpg', 'jpg', '/upload/scaled_559f822d-7745-40c8-a272-1bbd08d0a5024106502618044994684-202404130827493.jpg', '图片', '2.52 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (364, '2024-04-13 09:44:59.207633', '2024-04-13 09:44:59.207633', 'scaled_b79fcd3b-14c5-43bb-87ae-e85a7ff747271569057675766682490-202404130944205.jpg', 'scaled_b79fcd3b-14c5-43bb-87ae-e85a7ff747271569057675766682490.jpg', 'jpg', '/upload/scaled_b79fcd3b-14c5-43bb-87ae-e85a7ff747271569057675766682490-202404130944205.jpg', '图片', '2.58 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (365, '2024-04-13 09:44:59.437013', '2024-04-13 09:44:59.437013', 'scaled_241ca42f-23ed-407f-824d-c8b6b9e90b721150586666835864975-202404130944435.jpg', 'scaled_241ca42f-23ed-407f-824d-c8b6b9e90b721150586666835864975.jpg', 'jpg', '/upload/scaled_241ca42f-23ed-407f-824d-c8b6b9e90b721150586666835864975-202404130944435.jpg', '图片', '2.85 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (366, '2024-04-13 14:53:39.542602', '2024-04-13 14:53:39.542602', 'scaled_4eef73f4-4cb7-42a9-b4de-3941cbd269c97137526446146235162-202404131453540.jpg', 'scaled_4eef73f4-4cb7-42a9-b4de-3941cbd269c97137526446146235162.jpg', 'jpg', '/upload/scaled_4eef73f4-4cb7-42a9-b4de-3941cbd269c97137526446146235162-202404131453540.jpg', '图片', '2.35 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (367, '2024-04-13 14:53:39.915862', '2024-04-13 14:53:39.915862', 'scaled_079e050f-fcb7-4614-b90d-1591fe8ab5227609710228337958244-202404131453914.jpg', 'scaled_079e050f-fcb7-4614-b90d-1591fe8ab5227609710228337958244.jpg', 'jpg', '/upload/scaled_079e050f-fcb7-4614-b90d-1591fe8ab5227609710228337958244-202404131453914.jpg', '图片', '2.81 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (368, '2024-04-13 15:16:02.285418', '2024-04-13 15:16:02.285418', 'scaled_773b290d-b96d-42dc-90bf-4c79a7c060747050543531263253083-202404131516282.jpg', 'scaled_773b290d-b96d-42dc-90bf-4c79a7c060747050543531263253083.jpg', 'jpg', '/upload/scaled_773b290d-b96d-42dc-90bf-4c79a7c060747050543531263253083-202404131516282.jpg', '图片', '1.8 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (369, '2024-04-13 15:16:02.714905', '2024-04-13 15:16:02.714905', 'scaled_cb5c49a3-5adf-4df5-b879-d6799cc7819e5630311866858635321-202404131516713.jpg', 'scaled_cb5c49a3-5adf-4df5-b879-d6799cc7819e5630311866858635321.jpg', 'jpg', '/upload/scaled_cb5c49a3-5adf-4df5-b879-d6799cc7819e5630311866858635321-202404131516713.jpg', '图片', '3.12 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (370, '2024-04-13 15:42:54.305550', '2024-04-13 15:42:54.305550', 'scaled_88d9bf55-32c7-471e-abe0-f2ba682f7cfa8888895513510985628-202404131542302.jpg', 'scaled_88d9bf55-32c7-471e-abe0-f2ba682f7cfa8888895513510985628.jpg', 'jpg', '/upload/scaled_88d9bf55-32c7-471e-abe0-f2ba682f7cfa8888895513510985628-202404131542302.jpg', '图片', '3.94 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (371, '2024-04-13 15:42:54.598859', '2024-04-13 15:42:54.598859', 'scaled_1638638c-b693-4526-85de-59329ee4b4273245340884132531736-202404131542597.jpg', 'scaled_1638638c-b693-4526-85de-59329ee4b4273245340884132531736.jpg', 'jpg', '/upload/scaled_1638638c-b693-4526-85de-59329ee4b4273245340884132531736-202404131542597.jpg', '图片', '2.79 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (372, '2024-04-13 17:50:37.639004', '2024-04-13 17:50:37.639004', 'scaled_b63b1afc-3d1e-4f50-8f63-e76af32cca234448864035731519055-202404131750636.jpg', 'scaled_b63b1afc-3d1e-4f50-8f63-e76af32cca234448864035731519055.jpg', 'jpg', '/upload/scaled_b63b1afc-3d1e-4f50-8f63-e76af32cca234448864035731519055-202404131750636.jpg', '图片', '1.93 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (373, '2024-04-13 17:50:38.163498', '2024-04-13 17:50:38.163498', 'scaled_eb179130-c330-4658-9625-e9708ebab8a02436122792831160719-202404131750159.jpg', 'scaled_eb179130-c330-4658-9625-e9708ebab8a02436122792831160719.jpg', 'jpg', '/upload/scaled_eb179130-c330-4658-9625-e9708ebab8a02436122792831160719-202404131750159.jpg', '图片', '2.98 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (374, '2024-04-13 17:52:02.945026', '2024-04-13 17:52:02.945026', 'scaled_682c6563-750e-409c-afe4-9816162eeb6f6724127407987337864-202404131752943.jpg', 'scaled_682c6563-750e-409c-afe4-9816162eeb6f6724127407987337864.jpg', 'jpg', '/upload/scaled_682c6563-750e-409c-afe4-9816162eeb6f6724127407987337864-202404131752943.jpg', '图片', '2.62 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (375, '2024-04-13 17:52:03.088175', '2024-04-13 17:52:03.088175', 'scaled_93723fd6-6d6a-4ea6-9257-c8af88e9e05f7005079073878634051-202404131752086.jpg', 'scaled_93723fd6-6d6a-4ea6-9257-c8af88e9e05f7005079073878634051.jpg', 'jpg', '/upload/scaled_93723fd6-6d6a-4ea6-9257-c8af88e9e05f7005079073878634051-202404131752086.jpg', '图片', '2.77 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (376, '2024-04-15 08:33:12.941848', '2024-04-15 08:33:12.941848', 'scaled_d0d9f0fb-3d13-45b3-9611-0d2798e95a7b4752937053925462819-202404150833939.jpg', 'scaled_d0d9f0fb-3d13-45b3-9611-0d2798e95a7b4752937053925462819.jpg', 'jpg', '/upload/scaled_d0d9f0fb-3d13-45b3-9611-0d2798e95a7b4752937053925462819-202404150833939.jpg', '图片', '3.08 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (377, '2024-04-15 08:33:13.121296', '2024-04-15 08:33:13.121296', 'scaled_f8d75a9c-0481-4e49-aa9c-df3ce7aae712107138623188476879-202404150833119.jpg', 'scaled_f8d75a9c-0481-4e49-aa9c-df3ce7aae712107138623188476879.jpg', 'jpg', '/upload/scaled_f8d75a9c-0481-4e49-aa9c-df3ce7aae712107138623188476879-202404150833119.jpg', '图片', '3 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (378, '2024-04-15 08:33:50.547641', '2024-04-15 08:33:50.547641', 'scaled_5d5edfa7-275d-4002-a02a-b47f1e7448e31622340258235894218-202404150833545.jpg', 'scaled_5d5edfa7-275d-4002-a02a-b47f1e7448e31622340258235894218.jpg', 'jpg', '/upload/scaled_5d5edfa7-275d-4002-a02a-b47f1e7448e31622340258235894218-202404150833545.jpg', '图片', '2.61 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (379, '2024-04-15 08:33:51.743537', '2024-04-15 08:33:51.743537', 'scaled_df60e14f-8143-48cc-8d61-f2f39f9459994379139240309516356-202404150833742.jpg', 'scaled_df60e14f-8143-48cc-8d61-f2f39f9459994379139240309516356.jpg', 'jpg', '/upload/scaled_df60e14f-8143-48cc-8d61-f2f39f9459994379139240309516356-202404150833742.jpg', '图片', '3.04 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (380, '2024-04-15 08:34:52.306609', '2024-04-15 08:34:52.306609', 'scaled_e415dc1c-c80a-4f85-8e05-69e778f889bf5900906227279135599-202404150834304.jpg', 'scaled_e415dc1c-c80a-4f85-8e05-69e778f889bf5900906227279135599.jpg', 'jpg', '/upload/scaled_e415dc1c-c80a-4f85-8e05-69e778f889bf5900906227279135599-202404150834304.jpg', '图片', '2.61 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (381, '2024-04-15 08:34:52.850514', '2024-04-15 08:34:52.850514', 'scaled_76ce63e9-3d8c-4fc6-a7c2-ff0a5b81ba2b6140515400511427884-202404150834848.jpg', 'scaled_76ce63e9-3d8c-4fc6-a7c2-ff0a5b81ba2b6140515400511427884.jpg', 'jpg', '/upload/scaled_76ce63e9-3d8c-4fc6-a7c2-ff0a5b81ba2b6140515400511427884-202404150834848.jpg', '图片', '2.36 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (382, '2024-04-15 08:36:02.502053', '2024-04-15 08:36:02.502053', 'scaled_dd1dff7a-1f46-4106-ab38-24ae8bf527fe509827711586221938-202404150836500.jpg', 'scaled_dd1dff7a-1f46-4106-ab38-24ae8bf527fe509827711586221938.jpg', 'jpg', '/upload/scaled_dd1dff7a-1f46-4106-ab38-24ae8bf527fe509827711586221938-202404150836500.jpg', '图片', '3.17 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (383, '2024-04-15 08:36:02.969908', '2024-04-15 08:36:02.969908', 'scaled_cb4bb21b-7ae3-43e7-a60b-9b8d5a6859dc421349058283961753-202404150836968.jpg', 'scaled_cb4bb21b-7ae3-43e7-a60b-9b8d5a6859dc421349058283961753.jpg', 'jpg', '/upload/scaled_cb4bb21b-7ae3-43e7-a60b-9b8d5a6859dc421349058283961753-202404150836968.jpg', '图片', '3.16 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (384, '2024-04-15 13:28:43.581280', '2024-04-15 13:28:43.581280', 'scaled_4624cc92-ba24-40fc-a54d-a39a7e11a65f3250666466491990094-202404151328578.jpg', 'scaled_4624cc92-ba24-40fc-a54d-a39a7e11a65f3250666466491990094.jpg', 'jpg', '/upload/scaled_4624cc92-ba24-40fc-a54d-a39a7e11a65f3250666466491990094-202404151328578.jpg', '图片', '3.27 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (385, '2024-04-15 13:28:43.664376', '2024-04-15 13:28:43.664376', 'scaled_3db21ab1-5381-40de-ace2-37848e8e80cc1053217279223818496-202404151328663.jpg', 'scaled_3db21ab1-5381-40de-ace2-37848e8e80cc1053217279223818496.jpg', 'jpg', '/upload/scaled_3db21ab1-5381-40de-ace2-37848e8e80cc1053217279223818496-202404151328663.jpg', '图片', '2.61 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (386, '2024-04-15 13:31:22.776456', '2024-04-15 13:31:22.776456', 'scaled_a7549398-a1c8-45a8-9d04-dcb994e7bbf73032086655558452806-202404151331774.jpg', 'scaled_a7549398-a1c8-45a8-9d04-dcb994e7bbf73032086655558452806.jpg', 'jpg', '/upload/scaled_a7549398-a1c8-45a8-9d04-dcb994e7bbf73032086655558452806-202404151331774.jpg', '图片', '2.84 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (387, '2024-04-15 13:31:23.762820', '2024-04-15 13:31:23.762820', 'scaled_0e249100-2fd2-4488-b90a-366f2e09b1f41495289022752425565-202404151331761.jpg', 'scaled_0e249100-2fd2-4488-b90a-366f2e09b1f41495289022752425565.jpg', 'jpg', '/upload/scaled_0e249100-2fd2-4488-b90a-366f2e09b1f41495289022752425565-202404151331761.jpg', '图片', '2.72 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (388, '2024-04-15 13:41:15.518791', '2024-04-15 13:41:15.518791', 'shankuangtong_v1.0.3.apk', 'shankuangtong_v1.0.3.apk', 'apk', '/upload/shankuangtong_v1.0.3.apk', 'apk', '31.3 MB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (389, '2024-04-15 15:19:21.460005', '2024-04-15 15:19:21.460005', 'shankuangtong_v1.0.4.apk', 'shankuangtong_v1.0.4.apk', 'apk', '/upload/shankuangtong_v1.0.4.apk', 'apk', '31.29 MB', 1, NULL, NULL); +INSERT INTO `tool_storage` VALUES (390, '2024-04-15 15:27:40.693618', '2024-04-15 15:27:40.693618', 'scaled_19fae54e-f7d2-4ae4-bfbc-212b4bada38c7751038347022065782-202404151527691.jpg', 'scaled_19fae54e-f7d2-4ae4-bfbc-212b4bada38c7751038347022065782.jpg', 'jpg', '/upload/scaled_19fae54e-f7d2-4ae4-bfbc-212b4bada38c7751038347022065782-202404151527691.jpg', '图片', '1.35 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (391, '2024-04-15 15:27:41.901363', '2024-04-15 15:27:41.901363', 'scaled_ebb876ac-61d6-47d4-9082-3ecf064d633b4061123539108416669-202404151527900.jpg', 'scaled_ebb876ac-61d6-47d4-9082-3ecf064d633b4061123539108416669.jpg', 'jpg', '/upload/scaled_ebb876ac-61d6-47d4-9082-3ecf064d633b4061123539108416669-202404151527900.jpg', '图片', '2.9 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (392, '2024-04-15 15:29:14.638764', '2024-04-15 15:29:14.638764', 'scaled_aafeb3e7-e536-43fc-93f3-b457ca4a85a73209158567651539426-202404151529637.jpg', 'scaled_aafeb3e7-e536-43fc-93f3-b457ca4a85a73209158567651539426.jpg', 'jpg', '/upload/scaled_aafeb3e7-e536-43fc-93f3-b457ca4a85a73209158567651539426-202404151529637.jpg', '图片', '2.15 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (393, '2024-04-15 15:29:15.214251', '2024-04-15 15:29:15.214251', 'scaled_ad07859e-1d15-4d71-895b-2abfec19ccf41583639405136618651-202404151529210.jpg', 'scaled_ad07859e-1d15-4d71-895b-2abfec19ccf41583639405136618651.jpg', 'jpg', '/upload/scaled_ad07859e-1d15-4d71-895b-2abfec19ccf41583639405136618651-202404151529210.jpg', '图片', '2.43 MB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (394, '2024-04-16 09:12:36.347213', '2024-04-16 09:12:36.347213', 'scaled_e483816d-9f4d-4929-a3b5-b010997277d64408385168773918982-202404160912345.jpg', 'scaled_e483816d-9f4d-4929-a3b5-b010997277d64408385168773918982.jpg', 'jpg', '/upload/scaled_e483816d-9f4d-4929-a3b5-b010997277d64408385168773918982-202404160912345.jpg', '图片', '199.72 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (395, '2024-04-16 09:12:36.462129', '2024-04-16 09:12:36.462129', 'scaled_37541ff5-6215-494d-9616-cf8a6551f42f7668895171674140376-202404160912460.jpg', 'scaled_37541ff5-6215-494d-9616-cf8a6551f42f7668895171674140376.jpg', 'jpg', '/upload/scaled_37541ff5-6215-494d-9616-cf8a6551f42f7668895171674140376-202404160912460.jpg', '图片', '224.64 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (396, '2024-04-16 13:17:29.093012', '2024-04-16 13:17:29.093012', 'scaled_d60987d6-f347-4894-9063-ce3e9a953aaf1786066022386479636-202404161317089.jpg', 'scaled_d60987d6-f347-4894-9063-ce3e9a953aaf1786066022386479636.jpg', 'jpg', '/upload/scaled_d60987d6-f347-4894-9063-ce3e9a953aaf1786066022386479636-202404161317089.jpg', '图片', '103.25 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (397, '2024-04-16 13:17:29.170271', '2024-04-16 13:17:29.170271', 'scaled_54ff1b30-2a33-49c2-a395-eb0b4c4e1633737545488236960666-202404161317167.jpg', 'scaled_54ff1b30-2a33-49c2-a395-eb0b4c4e1633737545488236960666.jpg', 'jpg', '/upload/scaled_54ff1b30-2a33-49c2-a395-eb0b4c4e1633737545488236960666-202404161317167.jpg', '图片', '123.48 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (398, '2024-04-16 13:19:22.337011', '2024-04-16 13:19:22.337011', 'scaled_faa46e77-f2f5-405a-9616-248a178054df568519709688935008-202404161319334.jpg', 'scaled_faa46e77-f2f5-405a-9616-248a178054df568519709688935008.jpg', 'jpg', '/upload/scaled_faa46e77-f2f5-405a-9616-248a178054df568519709688935008-202404161319334.jpg', '图片', '148.2 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (399, '2024-04-16 13:19:22.443538', '2024-04-16 13:19:22.443538', 'scaled_761b9a57-5516-4046-a5c7-86272a8d575b1298270238027270103-202404161319441.jpg', 'scaled_761b9a57-5516-4046-a5c7-86272a8d575b1298270238027270103.jpg', 'jpg', '/upload/scaled_761b9a57-5516-4046-a5c7-86272a8d575b1298270238027270103-202404161319441.jpg', '图片', '247.43 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (400, '2024-04-16 14:34:17.940912', '2024-04-16 14:34:17.940912', 'scaled_0f3f9ea9-697e-4fae-b101-06945a41308d5669962813173333432-202404161434936.jpg', 'scaled_0f3f9ea9-697e-4fae-b101-06945a41308d5669962813173333432.jpg', 'jpg', '/upload/scaled_0f3f9ea9-697e-4fae-b101-06945a41308d5669962813173333432-202404161434936.jpg', '图片', '127.91 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (401, '2024-04-16 14:34:17.941788', '2024-04-16 14:34:17.941788', 'scaled_e5e19250-76ea-4d15-8812-b5a1dc1c3cf77865880900200306810-202404161434938.jpg', 'scaled_e5e19250-76ea-4d15-8812-b5a1dc1c3cf77865880900200306810.jpg', 'jpg', '/upload/scaled_e5e19250-76ea-4d15-8812-b5a1dc1c3cf77865880900200306810-202404161434938.jpg', '图片', '184.75 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (402, '2024-04-16 14:34:49.999075', '2024-04-16 14:34:49.999075', 'scaled_10f06788-be75-48bb-b5e8-f790fb239dad6953916720350183633-202404161434996.jpg', 'scaled_10f06788-be75-48bb-b5e8-f790fb239dad6953916720350183633.jpg', 'jpg', '/upload/scaled_10f06788-be75-48bb-b5e8-f790fb239dad6953916720350183633-202404161434996.jpg', '图片', '124.34 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (403, '2024-04-16 14:34:50.109528', '2024-04-16 14:34:50.109528', 'scaled_b3948d5e-2fcd-4cda-b306-b51d6b266046410561571437324568-202404161434107.jpg', 'scaled_b3948d5e-2fcd-4cda-b306-b51d6b266046410561571437324568.jpg', 'jpg', '/upload/scaled_b3948d5e-2fcd-4cda-b306-b51d6b266046410561571437324568-202404161434107.jpg', '图片', '165.74 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (404, '2024-04-16 15:22:55.423560', '2024-04-16 15:22:55.423560', 'scaled_42996bf0-910f-4fc7-bd27-6d74d05c97df2112113632090361848-202404161522420.jpg', 'scaled_42996bf0-910f-4fc7-bd27-6d74d05c97df2112113632090361848.jpg', 'jpg', '/upload/scaled_42996bf0-910f-4fc7-bd27-6d74d05c97df2112113632090361848-202404161522420.jpg', '图片', '126.14 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (405, '2024-04-16 15:22:55.551212', '2024-04-16 15:22:55.551212', 'scaled_18fff132-f10e-4a07-bea0-c368fd543e5b1923865909304370317-202404161522549.jpg', 'scaled_18fff132-f10e-4a07-bea0-c368fd543e5b1923865909304370317.jpg', 'jpg', '/upload/scaled_18fff132-f10e-4a07-bea0-c368fd543e5b1923865909304370317-202404161522549.jpg', '图片', '253.73 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (406, '2024-04-16 16:18:10.901844', '2024-04-16 16:18:10.901844', 'scaled_2a791dc5-673d-4445-a663-452c705533d9119086128058102121-202404161618899.jpg', 'scaled_2a791dc5-673d-4445-a663-452c705533d9119086128058102121.jpg', 'jpg', '/upload/scaled_2a791dc5-673d-4445-a663-452c705533d9119086128058102121-202404161618899.jpg', '图片', '124.74 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (407, '2024-04-16 16:18:10.903399', '2024-04-16 16:18:10.903399', 'scaled_a46782fe-93ff-40f9-af1d-48c8da05b42c8322468362599961523-202404161618902.jpg', 'scaled_a46782fe-93ff-40f9-af1d-48c8da05b42c8322468362599961523.jpg', 'jpg', '/upload/scaled_a46782fe-93ff-40f9-af1d-48c8da05b42c8322468362599961523-202404161618902.jpg', '图片', '163.3 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (408, '2024-04-16 16:18:39.336728', '2024-04-16 16:18:39.336728', 'scaled_508d5c06-7dc5-4010-8757-edaae2ccfceb3631218817673488880-202404161618334.jpg', 'scaled_508d5c06-7dc5-4010-8757-edaae2ccfceb3631218817673488880.jpg', 'jpg', '/upload/scaled_508d5c06-7dc5-4010-8757-edaae2ccfceb3631218817673488880-202404161618334.jpg', '图片', '92.71 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (409, '2024-04-16 16:18:39.408078', '2024-04-16 16:18:39.408078', 'scaled_b2dce7d8-e521-47b9-a11e-3775f9e2b6931069853169274826107-202404161618405.jpg', 'scaled_b2dce7d8-e521-47b9-a11e-3775f9e2b6931069853169274826107.jpg', 'jpg', '/upload/scaled_b2dce7d8-e521-47b9-a11e-3775f9e2b6931069853169274826107-202404161618405.jpg', '图片', '126.86 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (410, '2024-04-16 16:51:48.063644', '2024-04-16 16:51:48.063644', 'scaled_0dad468e-1627-4fb3-8eff-0deb1e5064be5270192513527564109-202404161651060.jpg', 'scaled_0dad468e-1627-4fb3-8eff-0deb1e5064be5270192513527564109.jpg', 'jpg', '/upload/scaled_0dad468e-1627-4fb3-8eff-0deb1e5064be5270192513527564109-202404161651060.jpg', '图片', '114.16 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (411, '2024-04-16 16:51:48.130391', '2024-04-16 16:51:48.130391', 'scaled_4e7bcce6-b4ee-41cb-8ccf-88a3188ff10f649613119472495561-202404161651128.jpg', 'scaled_4e7bcce6-b4ee-41cb-8ccf-88a3188ff10f649613119472495561.jpg', 'jpg', '/upload/scaled_4e7bcce6-b4ee-41cb-8ccf-88a3188ff10f649613119472495561-202404161651128.jpg', '图片', '224.75 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (412, '2024-04-16 18:36:08.730241', '2024-04-16 18:36:08.730241', 'scaled_51525554-befd-47f1-b03b-6c0f986620aa4431255902472398306-202404161836727.jpg', 'scaled_51525554-befd-47f1-b03b-6c0f986620aa4431255902472398306.jpg', 'jpg', '/upload/scaled_51525554-befd-47f1-b03b-6c0f986620aa4431255902472398306-202404161836727.jpg', '图片', '97.71 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (413, '2024-04-16 18:36:08.760387', '2024-04-16 18:36:08.760387', 'scaled_5abf0a69-4492-423c-a084-a105ef93aece524680559368771505-202404161836757.jpg', 'scaled_5abf0a69-4492-423c-a084-a105ef93aece524680559368771505.jpg', 'jpg', '/upload/scaled_5abf0a69-4492-423c-a084-a105ef93aece524680559368771505-202404161836757.jpg', '图片', '247.69 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (414, '2024-04-16 18:37:34.225721', '2024-04-16 18:37:34.225721', 'scaled_ad542e52-5ba2-4e8d-a010-8635e67118e46679701019241346090-202404161837223.jpg', 'scaled_ad542e52-5ba2-4e8d-a010-8635e67118e46679701019241346090.jpg', 'jpg', '/upload/scaled_ad542e52-5ba2-4e8d-a010-8635e67118e46679701019241346090-202404161837223.jpg', '图片', '100.41 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (415, '2024-04-16 18:37:34.236251', '2024-04-16 18:37:34.236251', 'scaled_3942c5a1-98bf-4d16-9f5e-5253639106ea8317616649556828665-202404161837234.jpg', 'scaled_3942c5a1-98bf-4d16-9f5e-5253639106ea8317616649556828665.jpg', 'jpg', '/upload/scaled_3942c5a1-98bf-4d16-9f5e-5253639106ea8317616649556828665-202404161837234.jpg', '图片', '114.6 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (416, '2024-04-16 18:39:39.483095', '2024-04-16 18:39:39.483095', 'scaled_eb0ffff5-5521-4ceb-a661-3b91dfe69f96453927595817336589-202404161839481.jpg', 'scaled_eb0ffff5-5521-4ceb-a661-3b91dfe69f96453927595817336589.jpg', 'jpg', '/upload/scaled_eb0ffff5-5521-4ceb-a661-3b91dfe69f96453927595817336589-202404161839481.jpg', '图片', '100.93 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (417, '2024-04-16 18:39:39.777436', '2024-04-16 18:39:39.777436', 'scaled_22ec5a26-8c26-442c-8889-dae09b8b4fd1367789691077511170-202404161839775.jpg', 'scaled_22ec5a26-8c26-442c-8889-dae09b8b4fd1367789691077511170.jpg', 'jpg', '/upload/scaled_22ec5a26-8c26-442c-8889-dae09b8b4fd1367789691077511170-202404161839775.jpg', '图片', '81.01 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (418, '2024-04-16 18:40:16.038593', '2024-04-16 18:40:16.038593', 'scaled_9ba2e288-3527-4f59-962f-b0da1aaf28947427532908339880962-202404161840036.jpg', 'scaled_9ba2e288-3527-4f59-962f-b0da1aaf28947427532908339880962.jpg', 'jpg', '/upload/scaled_9ba2e288-3527-4f59-962f-b0da1aaf28947427532908339880962-202404161840036.jpg', '图片', '101.22 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (419, '2024-04-16 18:40:16.058758', '2024-04-16 18:40:16.058758', 'scaled_53103b8e-1d3b-406e-bbb4-a537e8d4fbdc8669065313466903247-202404161840057.jpg', 'scaled_53103b8e-1d3b-406e-bbb4-a537e8d4fbdc8669065313466903247.jpg', 'jpg', '/upload/scaled_53103b8e-1d3b-406e-bbb4-a537e8d4fbdc8669065313466903247-202404161840057.jpg', '图片', '107.33 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (420, '2024-04-16 18:41:10.362391', '2024-04-16 18:41:10.362391', 'scaled_ed2762bb-06ae-47de-bf5d-02a386eccc381867036726109934473-202404161841360.jpg', 'scaled_ed2762bb-06ae-47de-bf5d-02a386eccc381867036726109934473.jpg', 'jpg', '/upload/scaled_ed2762bb-06ae-47de-bf5d-02a386eccc381867036726109934473-202404161841360.jpg', '图片', '99.74 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (421, '2024-04-16 18:41:10.369905', '2024-04-16 18:41:10.369905', 'scaled_4883d847-4677-451c-8a7b-b131f882766d720185054094316179-202404161841368.jpg', 'scaled_4883d847-4677-451c-8a7b-b131f882766d720185054094316179.jpg', 'jpg', '/upload/scaled_4883d847-4677-451c-8a7b-b131f882766d720185054094316179-202404161841368.jpg', '图片', '110.34 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (422, '2024-04-17 08:42:44.949972', '2024-04-17 08:42:44.949972', 'scaled_a4c04efc-01f5-4630-92d3-9355f378dfc55698405880040826510-202404170842944.jpg', 'scaled_a4c04efc-01f5-4630-92d3-9355f378dfc55698405880040826510.jpg', 'jpg', '/upload/scaled_a4c04efc-01f5-4630-92d3-9355f378dfc55698405880040826510-202404170842944.jpg', '图片', '142.37 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (423, '2024-04-17 08:42:45.117332', '2024-04-17 08:42:45.117332', 'scaled_7dfd9e76-2986-4da3-ac97-95c727e2d8443601961325600883424-202404170842115.jpg', 'scaled_7dfd9e76-2986-4da3-ac97-95c727e2d8443601961325600883424.jpg', 'jpg', '/upload/scaled_7dfd9e76-2986-4da3-ac97-95c727e2d8443601961325600883424-202404170842115.jpg', '图片', '382.71 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (424, '2024-04-17 08:43:12.204604', '2024-04-17 08:43:12.204604', 'scaled_3bab4f63-c921-4878-a8c4-143b63f50f3e2187010350056505572-202404170843202.jpg', 'scaled_3bab4f63-c921-4878-a8c4-143b63f50f3e2187010350056505572.jpg', 'jpg', '/upload/scaled_3bab4f63-c921-4878-a8c4-143b63f50f3e2187010350056505572-202404170843202.jpg', '图片', '101.37 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (425, '2024-04-17 08:43:12.429914', '2024-04-17 08:43:12.429914', 'scaled_d29a8dbf-8aa8-49a8-bc3d-2f9baf9275331120191137643628165-202404170843427.jpg', 'scaled_d29a8dbf-8aa8-49a8-bc3d-2f9baf9275331120191137643628165.jpg', 'jpg', '/upload/scaled_d29a8dbf-8aa8-49a8-bc3d-2f9baf9275331120191137643628165-202404170843427.jpg', '图片', '314.42 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (426, '2024-04-17 13:57:28.801565', '2024-04-17 13:57:28.801565', 'scaled_1144732e-91bc-4d8a-831c-39412d95ac763162032930032657524-202404171357795.jpg', 'scaled_1144732e-91bc-4d8a-831c-39412d95ac763162032930032657524.jpg', 'jpg', '/upload/scaled_1144732e-91bc-4d8a-831c-39412d95ac763162032930032657524-202404171357795.jpg', '图片', '122.55 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (427, '2024-04-17 13:57:28.861743', '2024-04-17 13:57:28.861743', 'scaled_7ec681b9-3072-45f0-985d-2af2530161715188342609595034207-202404171357859.jpg', 'scaled_7ec681b9-3072-45f0-985d-2af2530161715188342609595034207.jpg', 'jpg', '/upload/scaled_7ec681b9-3072-45f0-985d-2af2530161715188342609595034207-202404171357859.jpg', '图片', '207.96 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (428, '2024-04-17 13:59:51.072566', '2024-04-17 13:59:51.072566', 'scaled_e78bd695-66b8-4bc9-a17f-813ab9cceedd8645492406488231106-202404171359069.jpg', 'scaled_e78bd695-66b8-4bc9-a17f-813ab9cceedd8645492406488231106.jpg', 'jpg', '/upload/scaled_e78bd695-66b8-4bc9-a17f-813ab9cceedd8645492406488231106-202404171359069.jpg', '图片', '111.73 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (429, '2024-04-17 13:59:51.255467', '2024-04-17 13:59:51.255467', 'scaled_298a1123-f886-4c18-9ece-2a43c649a8432206495194880461729-202404171359252.jpg', 'scaled_298a1123-f886-4c18-9ece-2a43c649a8432206495194880461729.jpg', 'jpg', '/upload/scaled_298a1123-f886-4c18-9ece-2a43c649a8432206495194880461729-202404171359252.jpg', '图片', '239.5 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (430, '2024-04-17 14:01:05.244106', '2024-04-17 14:01:05.244106', 'scaled_e7c6dc80-8247-4c4d-bf73-4138e0fdcc1f7824264474053808143-202404171401240.jpg', 'scaled_e7c6dc80-8247-4c4d-bf73-4138e0fdcc1f7824264474053808143.jpg', 'jpg', '/upload/scaled_e7c6dc80-8247-4c4d-bf73-4138e0fdcc1f7824264474053808143-202404171401240.jpg', '图片', '94.26 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (431, '2024-04-17 14:01:05.348762', '2024-04-17 14:01:05.348762', 'scaled_395b7760-0ba4-49dd-90ad-814d1c028a135656204523629479589-202404171401346.jpg', 'scaled_395b7760-0ba4-49dd-90ad-814d1c028a135656204523629479589.jpg', 'jpg', '/upload/scaled_395b7760-0ba4-49dd-90ad-814d1c028a135656204523629479589-202404171401346.jpg', '图片', '209.47 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (432, '2024-04-17 14:02:03.916256', '2024-04-17 14:02:03.916256', 'scaled_f4688357-19fd-4275-bee5-0f0a0f25351c5546436361090157634-202404171402913.jpg', 'scaled_f4688357-19fd-4275-bee5-0f0a0f25351c5546436361090157634.jpg', 'jpg', '/upload/scaled_f4688357-19fd-4275-bee5-0f0a0f25351c5546436361090157634-202404171402913.jpg', '图片', '201.92 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (433, '2024-04-17 14:02:04.243754', '2024-04-17 14:02:04.243754', 'scaled_f512202a-8d80-4c51-acb8-c982c467a0d88392338019959419981-202404171402241.jpg', 'scaled_f512202a-8d80-4c51-acb8-c982c467a0d88392338019959419981.jpg', 'jpg', '/upload/scaled_f512202a-8d80-4c51-acb8-c982c467a0d88392338019959419981-202404171402241.jpg', '图片', '208.67 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (434, '2024-04-17 15:16:47.390051', '2024-04-17 15:16:47.390051', 'scaled_d242e61d-3fab-45b9-8e7d-86cc02aebf4d4980116399025779488-202404171516386.jpg', 'scaled_d242e61d-3fab-45b9-8e7d-86cc02aebf4d4980116399025779488.jpg', 'jpg', '/upload/scaled_d242e61d-3fab-45b9-8e7d-86cc02aebf4d4980116399025779488-202404171516386.jpg', '图片', '230.87 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (435, '2024-04-17 15:16:47.390640', '2024-04-17 15:16:47.390640', 'scaled_f8f10e12-651b-4291-afa9-d56647818ebc3212398894138115071-202404171516388.jpg', 'scaled_f8f10e12-651b-4291-afa9-d56647818ebc3212398894138115071.jpg', 'jpg', '/upload/scaled_f8f10e12-651b-4291-afa9-d56647818ebc3212398894138115071-202404171516388.jpg', '图片', '240.16 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (436, '2024-04-17 15:19:28.688066', '2024-04-17 15:19:28.688066', 'scaled_77a74267-7988-4f65-a5ae-ece034b188c72245921316855250469-202404171519685.jpg', 'scaled_77a74267-7988-4f65-a5ae-ece034b188c72245921316855250469.jpg', 'jpg', '/upload/scaled_77a74267-7988-4f65-a5ae-ece034b188c72245921316855250469-202404171519685.jpg', '图片', '221.71 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (437, '2024-04-17 15:19:29.128793', '2024-04-17 15:19:29.128793', 'scaled_ce7a3090-3d10-4ae7-aba1-2b0143be51b05820712808569333380-202404171519126.jpg', 'scaled_ce7a3090-3d10-4ae7-aba1-2b0143be51b05820712808569333380.jpg', 'jpg', '/upload/scaled_ce7a3090-3d10-4ae7-aba1-2b0143be51b05820712808569333380-202404171519126.jpg', '图片', '260.26 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (438, '2024-04-17 15:20:07.657211', '2024-04-17 15:20:07.657211', 'scaled_cc9fb57e-5159-40b4-91e4-552e6e862dbd2565986746189324485-202404171520654.jpg', 'scaled_cc9fb57e-5159-40b4-91e4-552e6e862dbd2565986746189324485.jpg', 'jpg', '/upload/scaled_cc9fb57e-5159-40b4-91e4-552e6e862dbd2565986746189324485-202404171520654.jpg', '图片', '364.83 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (439, '2024-04-17 15:20:07.700297', '2024-04-17 15:20:07.700297', 'scaled_8d14d139-3ad7-4443-a121-0e3e85da25891344693001463278557-202404171520698.jpg', 'scaled_8d14d139-3ad7-4443-a121-0e3e85da25891344693001463278557.jpg', 'jpg', '/upload/scaled_8d14d139-3ad7-4443-a121-0e3e85da25891344693001463278557-202404171520698.jpg', '图片', '311.35 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (440, '2024-04-17 15:21:01.205371', '2024-04-17 15:21:01.205371', 'scaled_abdb5f85-5eb7-43dd-af03-d94c66282b5e259669487222541603-202404171521202.jpg', 'scaled_abdb5f85-5eb7-43dd-af03-d94c66282b5e259669487222541603.jpg', 'jpg', '/upload/scaled_abdb5f85-5eb7-43dd-af03-d94c66282b5e259669487222541603-202404171521202.jpg', '图片', '305.21 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (441, '2024-04-17 15:21:01.267757', '2024-04-17 15:21:01.267757', 'scaled_30412069-fce6-4512-b951-9f876017a3e86997879162170748195-202404171521265.jpg', 'scaled_30412069-fce6-4512-b951-9f876017a3e86997879162170748195.jpg', 'jpg', '/upload/scaled_30412069-fce6-4512-b951-9f876017a3e86997879162170748195-202404171521265.jpg', '图片', '421.34 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (442, '2024-04-18 10:44:52.017917', '2024-04-18 10:44:52.017917', 'scaled_754e4449-958e-4006-b53a-31a17ddadc9c7984111098897052522-202404181044015.jpg', 'scaled_754e4449-958e-4006-b53a-31a17ddadc9c7984111098897052522.jpg', 'jpg', '/upload/scaled_754e4449-958e-4006-b53a-31a17ddadc9c7984111098897052522-202404181044015.jpg', '图片', '259.89 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (443, '2024-04-18 10:44:52.223411', '2024-04-18 10:44:52.223411', 'scaled_1e25f872-810a-4f03-bbbb-25bc5d247d534028826374941799075-202404181044220.jpg', 'scaled_1e25f872-810a-4f03-bbbb-25bc5d247d534028826374941799075.jpg', 'jpg', '/upload/scaled_1e25f872-810a-4f03-bbbb-25bc5d247d534028826374941799075-202404181044220.jpg', '图片', '387.62 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (444, '2024-04-18 10:45:27.946943', '2024-04-18 10:45:27.946943', 'scaled_3c6f3bc0-7ea5-40b5-a422-801e7c47541e862628589308868494-202404181045944.jpg', 'scaled_3c6f3bc0-7ea5-40b5-a422-801e7c47541e862628589308868494.jpg', 'jpg', '/upload/scaled_3c6f3bc0-7ea5-40b5-a422-801e7c47541e862628589308868494-202404181045944.jpg', '图片', '255.49 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (445, '2024-04-18 10:45:28.087171', '2024-04-18 10:45:28.087171', 'scaled_e9a65182-585e-4375-b692-499c83bcced53651543889261978894-202404181045085.jpg', 'scaled_e9a65182-585e-4375-b692-499c83bcced53651543889261978894.jpg', 'jpg', '/upload/scaled_e9a65182-585e-4375-b692-499c83bcced53651543889261978894-202404181045085.jpg', '图片', '352.84 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (446, '2024-04-18 14:45:16.330900', '2024-04-18 14:45:16.330900', 'scaled_df140d50-c3b3-47f4-85b4-a2ade7fe92d7783180630008492057-202404181445327.jpg', 'scaled_df140d50-c3b3-47f4-85b4-a2ade7fe92d7783180630008492057.jpg', 'jpg', '/upload/scaled_df140d50-c3b3-47f4-85b4-a2ade7fe92d7783180630008492057-202404181445327.jpg', '图片', '98.85 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (447, '2024-04-18 14:45:16.525003', '2024-04-18 14:45:16.525003', 'scaled_6cc412d0-f9f2-41ac-8593-c519a2df654d1664116696053773428-202404181445522.jpg', 'scaled_6cc412d0-f9f2-41ac-8593-c519a2df654d1664116696053773428.jpg', 'jpg', '/upload/scaled_6cc412d0-f9f2-41ac-8593-c519a2df654d1664116696053773428-202404181445522.jpg', '图片', '313.82 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (448, '2024-04-18 14:45:46.201649', '2024-04-18 14:45:46.201649', 'scaled_8f461b00-1209-4fcf-8f40-86fac4a8fe979185320579066942110-202404181445199.jpg', 'scaled_8f461b00-1209-4fcf-8f40-86fac4a8fe979185320579066942110.jpg', 'jpg', '/upload/scaled_8f461b00-1209-4fcf-8f40-86fac4a8fe979185320579066942110-202404181445199.jpg', '图片', '330.9 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (449, '2024-04-18 14:45:46.317935', '2024-04-18 14:45:46.317935', 'scaled_081b1a0e-fc99-496a-9eec-db53d1384e414620207428474385574-202404181445315.jpg', 'scaled_081b1a0e-fc99-496a-9eec-db53d1384e414620207428474385574.jpg', 'jpg', '/upload/scaled_081b1a0e-fc99-496a-9eec-db53d1384e414620207428474385574-202404181445315.jpg', '图片', '263.98 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (450, '2024-04-18 16:46:50.973252', '2024-04-18 16:46:50.973252', 'scaled_7410232a-cfb8-4eb2-bb61-9412bd6d1ae87694463575506506481-202404181646970.jpg', 'scaled_7410232a-cfb8-4eb2-bb61-9412bd6d1ae87694463575506506481.jpg', 'jpg', '/upload/scaled_7410232a-cfb8-4eb2-bb61-9412bd6d1ae87694463575506506481-202404181646970.jpg', '图片', '114.17 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (451, '2024-04-18 16:46:51.258697', '2024-04-18 16:46:51.258697', 'scaled_147ecc94-ede0-4d92-a838-733459cd29914965585161111896694-202404181646256.jpg', 'scaled_147ecc94-ede0-4d92-a838-733459cd29914965585161111896694.jpg', 'jpg', '/upload/scaled_147ecc94-ede0-4d92-a838-733459cd29914965585161111896694-202404181646256.jpg', '图片', '318.09 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (452, '2024-04-18 16:47:18.991606', '2024-04-18 16:47:18.991606', 'scaled_be9c8497-c83c-4fca-a752-82fd5fcb0c7f1345730777966322800-202404181647989.jpg', 'scaled_be9c8497-c83c-4fca-a752-82fd5fcb0c7f1345730777966322800.jpg', 'jpg', '/upload/scaled_be9c8497-c83c-4fca-a752-82fd5fcb0c7f1345730777966322800-202404181647989.jpg', '图片', '110.86 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (453, '2024-04-18 16:47:19.204070', '2024-04-18 16:47:19.204070', 'scaled_27820613-be35-42e8-acf6-15981005555e3602094898412377530-202404181647202.jpg', 'scaled_27820613-be35-42e8-acf6-15981005555e3602094898412377530.jpg', 'jpg', '/upload/scaled_27820613-be35-42e8-acf6-15981005555e3602094898412377530-202404181647202.jpg', '图片', '298.72 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (454, '2024-04-18 17:01:15.859249', '2024-04-18 17:01:15.859249', 'scaled_dcdc70de-be46-4528-9598-7d67562f99c88582247696548975650-202404181701856.jpg', 'scaled_dcdc70de-be46-4528-9598-7d67562f99c88582247696548975650.jpg', 'jpg', '/upload/scaled_dcdc70de-be46-4528-9598-7d67562f99c88582247696548975650-202404181701856.jpg', '图片', '220.32 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (455, '2024-04-18 17:01:15.937976', '2024-04-18 17:01:15.937976', 'scaled_984c8624-a2db-4af9-813a-9bec9dafa89a4161719840623574676-202404181701936.jpg', 'scaled_984c8624-a2db-4af9-813a-9bec9dafa89a4161719840623574676.jpg', 'jpg', '/upload/scaled_984c8624-a2db-4af9-813a-9bec9dafa89a4161719840623574676-202404181701936.jpg', '图片', '306.56 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (456, '2024-04-18 17:01:51.972958', '2024-04-18 17:01:51.972958', 'scaled_dcdc70de-be46-4528-9598-7d67562f99c88582247696548975650-202404181701970.jpg', 'scaled_dcdc70de-be46-4528-9598-7d67562f99c88582247696548975650.jpg', 'jpg', '/upload/scaled_dcdc70de-be46-4528-9598-7d67562f99c88582247696548975650-202404181701970.jpg', '图片', '220.32 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (457, '2024-04-18 17:01:52.212436', '2024-04-18 17:01:52.212436', 'scaled_984c8624-a2db-4af9-813a-9bec9dafa89a4161719840623574676-202404181701211.jpg', 'scaled_984c8624-a2db-4af9-813a-9bec9dafa89a4161719840623574676.jpg', 'jpg', '/upload/scaled_984c8624-a2db-4af9-813a-9bec9dafa89a4161719840623574676-202404181701211.jpg', '图片', '306.56 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (458, '2024-04-18 17:17:46.586378', '2024-04-18 17:17:46.586378', 'scaled_e2d04ae8-14f4-451a-b63d-13c1be0c70d4547852082418803527-202404181717584.jpg', 'scaled_e2d04ae8-14f4-451a-b63d-13c1be0c70d4547852082418803527.jpg', 'jpg', '/upload/scaled_e2d04ae8-14f4-451a-b63d-13c1be0c70d4547852082418803527-202404181717584.jpg', '图片', '180.28 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (459, '2024-04-18 17:17:46.654880', '2024-04-18 17:17:46.654880', 'scaled_04eb7f54-e69d-4ab4-b6bc-29a19e2f7ccf2492277346552073182-202404181717653.jpg', 'scaled_04eb7f54-e69d-4ab4-b6bc-29a19e2f7ccf2492277346552073182.jpg', 'jpg', '/upload/scaled_04eb7f54-e69d-4ab4-b6bc-29a19e2f7ccf2492277346552073182-202404181717653.jpg', '图片', '216.21 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (460, '2024-04-18 17:42:40.060201', '2024-04-18 17:42:40.060201', 'scaled_20acfd4e-5bd1-445f-98ba-1f7cae0b64975116613478670122113-202404181742057.jpg', 'scaled_20acfd4e-5bd1-445f-98ba-1f7cae0b64975116613478670122113.jpg', 'jpg', '/upload/scaled_20acfd4e-5bd1-445f-98ba-1f7cae0b64975116613478670122113-202404181742057.jpg', '图片', '211.93 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (461, '2024-04-18 17:42:40.088186', '2024-04-18 17:42:40.088186', 'scaled_79bc05d0-3086-48b7-a083-b83229b491e6235955211461791250-202404181742086.jpg', 'scaled_79bc05d0-3086-48b7-a083-b83229b491e6235955211461791250.jpg', 'jpg', '/upload/scaled_79bc05d0-3086-48b7-a083-b83229b491e6235955211461791250-202404181742086.jpg', '图片', '123.88 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (462, '2024-04-18 17:43:03.165558', '2024-04-18 17:43:03.165558', 'scaled_7718f06c-901f-45ce-bbef-6d86d978b8037271083941403461503-202404181743163.jpg', 'scaled_7718f06c-901f-45ce-bbef-6d86d978b8037271083941403461503.jpg', 'jpg', '/upload/scaled_7718f06c-901f-45ce-bbef-6d86d978b8037271083941403461503-202404181743163.jpg', '图片', '114.89 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (463, '2024-04-18 17:43:03.311393', '2024-04-18 17:43:03.311393', 'scaled_6f4c483b-6425-45a8-8c67-4e78f57c6fab6565067825153436288-202404181743308.jpg', 'scaled_6f4c483b-6425-45a8-8c67-4e78f57c6fab6565067825153436288.jpg', 'jpg', '/upload/scaled_6f4c483b-6425-45a8-8c67-4e78f57c6fab6565067825153436288-202404181743308.jpg', '图片', '212.16 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (464, '2024-04-18 18:02:26.212711', '2024-04-18 18:02:26.212711', 'scaled_742dffc2-0019-49bb-bdb8-3f62b07062302101976326653508427-202404181802209.jpg', 'scaled_742dffc2-0019-49bb-bdb8-3f62b07062302101976326653508427.jpg', 'jpg', '/upload/scaled_742dffc2-0019-49bb-bdb8-3f62b07062302101976326653508427-202404181802209.jpg', '图片', '119.71 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (465, '2024-04-18 18:02:26.227281', '2024-04-18 18:02:26.227281', 'scaled_5fdb0dd6-a5d8-4428-a86d-1dcd81e747555314713348898324859-202404181802225.jpg', 'scaled_5fdb0dd6-a5d8-4428-a86d-1dcd81e747555314713348898324859.jpg', 'jpg', '/upload/scaled_5fdb0dd6-a5d8-4428-a86d-1dcd81e747555314713348898324859-202404181802225.jpg', '图片', '257.11 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (466, '2024-04-18 18:02:47.680946', '2024-04-18 18:02:47.680946', 'scaled_21816cff-729c-4d61-b362-a511e7dc01368643339176287918427-202404181802678.jpg', 'scaled_21816cff-729c-4d61-b362-a511e7dc01368643339176287918427.jpg', 'jpg', '/upload/scaled_21816cff-729c-4d61-b362-a511e7dc01368643339176287918427-202404181802678.jpg', '图片', '119.02 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (467, '2024-04-18 18:02:47.689828', '2024-04-18 18:02:47.689828', 'scaled_80edea02-6867-460a-9aed-1bd7288fbb164057612298083528964-202404181802688.jpg', 'scaled_80edea02-6867-460a-9aed-1bd7288fbb164057612298083528964.jpg', 'jpg', '/upload/scaled_80edea02-6867-460a-9aed-1bd7288fbb164057612298083528964-202404181802688.jpg', '图片', '277.44 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (468, '2024-04-19 10:40:38.561388', '2024-04-19 10:40:38.561388', 'scaled_072a1827-420a-4cdf-8c7d-d0ec098c28231272698529902755871-202404191040556.jpg', 'scaled_072a1827-420a-4cdf-8c7d-d0ec098c28231272698529902755871.jpg', 'jpg', '/upload/scaled_072a1827-420a-4cdf-8c7d-d0ec098c28231272698529902755871-202404191040556.jpg', '图片', '157.12 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (469, '2024-04-19 10:40:38.572998', '2024-04-19 10:40:38.572998', 'scaled_931423e1-349c-4248-ba1d-9fedbc27bd382683341561267528835-202404191040565.jpg', 'scaled_931423e1-349c-4248-ba1d-9fedbc27bd382683341561267528835.jpg', 'jpg', '/upload/scaled_931423e1-349c-4248-ba1d-9fedbc27bd382683341561267528835-202404191040565.jpg', '图片', '186.66 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (470, '2024-04-19 11:52:40.558716', '2024-04-19 11:52:40.558716', 'scaled_408aea9a-be8a-48e0-af8f-4ad5ce7727d95990198918349862207-202404191152555.jpg', 'scaled_408aea9a-be8a-48e0-af8f-4ad5ce7727d95990198918349862207.jpg', 'jpg', '/upload/scaled_408aea9a-be8a-48e0-af8f-4ad5ce7727d95990198918349862207-202404191152555.jpg', '图片', '101.19 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (471, '2024-04-19 11:52:40.675187', '2024-04-19 11:52:40.675187', 'scaled_30ad8ed2-27fd-4203-ab94-051104063a0d5100177331624418765-202404191152673.jpg', 'scaled_30ad8ed2-27fd-4203-ab94-051104063a0d5100177331624418765.jpg', 'jpg', '/upload/scaled_30ad8ed2-27fd-4203-ab94-051104063a0d5100177331624418765-202404191152673.jpg', '图片', '105.4 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (472, '2024-04-19 15:07:16.532332', '2024-04-19 15:07:16.532332', 'scaled_9d4fee2c-bbd0-4f8a-b432-82a9bb9711fe2592367935060555802-202404191507529.jpg', 'scaled_9d4fee2c-bbd0-4f8a-b432-82a9bb9711fe2592367935060555802.jpg', 'jpg', '/upload/scaled_9d4fee2c-bbd0-4f8a-b432-82a9bb9711fe2592367935060555802-202404191507529.jpg', '图片', '109.25 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (473, '2024-04-19 15:07:16.617581', '2024-04-19 15:07:16.617581', 'scaled_19d428db-11e8-42fe-a848-d57d5085856c2292059880150020880-202404191507615.jpg', 'scaled_19d428db-11e8-42fe-a848-d57d5085856c2292059880150020880.jpg', 'jpg', '/upload/scaled_19d428db-11e8-42fe-a848-d57d5085856c2292059880150020880-202404191507615.jpg', '图片', '182.11 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (474, '2024-04-19 15:13:24.463137', '2024-04-19 15:13:24.463137', 'scaled_11ecb9b3-4abc-452c-bedd-443cc17ce28e5877905908291209143-202404191513459.jpg', 'scaled_11ecb9b3-4abc-452c-bedd-443cc17ce28e5877905908291209143.jpg', 'jpg', '/upload/scaled_11ecb9b3-4abc-452c-bedd-443cc17ce28e5877905908291209143-202404191513459.jpg', '图片', '111.45 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (475, '2024-04-19 15:13:24.469685', '2024-04-19 15:13:24.469685', 'scaled_54a4a760-5416-4f89-9ee4-92fcd5387912308460513563057534-202404191513467.jpg', 'scaled_54a4a760-5416-4f89-9ee4-92fcd5387912308460513563057534.jpg', 'jpg', '/upload/scaled_54a4a760-5416-4f89-9ee4-92fcd5387912308460513563057534-202404191513467.jpg', '图片', '157.31 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (476, '2024-04-19 15:27:58.312439', '2024-04-19 15:27:58.312439', 'scaled_64f9d5ed-26ac-486f-bf92-1628ffad16513588618887350149063-202404191527309.jpg', 'scaled_64f9d5ed-26ac-486f-bf92-1628ffad16513588618887350149063.jpg', 'jpg', '/upload/scaled_64f9d5ed-26ac-486f-bf92-1628ffad16513588618887350149063-202404191527309.jpg', '图片', '107.98 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (477, '2024-04-19 15:27:58.370060', '2024-04-19 15:27:58.370060', 'scaled_3a39a101-c4cc-4202-820d-79e3c86c25a15219572573413417781-202404191527367.jpg', 'scaled_3a39a101-c4cc-4202-820d-79e3c86c25a15219572573413417781.jpg', 'jpg', '/upload/scaled_3a39a101-c4cc-4202-820d-79e3c86c25a15219572573413417781-202404191527367.jpg', '图片', '188.09 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (478, '2024-04-19 15:28:42.274261', '2024-04-19 15:28:42.274261', 'scaled_84a1ca9f-dbc6-42f1-9021-550ec86e6a1f2835969683099319763-202404191528271.jpg', 'scaled_84a1ca9f-dbc6-42f1-9021-550ec86e6a1f2835969683099319763.jpg', 'jpg', '/upload/scaled_84a1ca9f-dbc6-42f1-9021-550ec86e6a1f2835969683099319763-202404191528271.jpg', '图片', '92.9 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (479, '2024-04-19 15:28:42.412565', '2024-04-19 15:28:42.412565', 'scaled_970ec332-8b09-4a56-ba08-b8640b8e9a4a563190886685882632-202404191528410.jpg', 'scaled_970ec332-8b09-4a56-ba08-b8640b8e9a4a563190886685882632.jpg', 'jpg', '/upload/scaled_970ec332-8b09-4a56-ba08-b8640b8e9a4a563190886685882632-202404191528410.jpg', '图片', '176.56 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (480, '2024-04-20 09:37:47.311239', '2024-04-20 09:37:47.311239', 'scaled_e2206e14-0041-4daf-a8cc-c1efcbb6a8717629451295084640330-202404200937307.jpg', 'scaled_e2206e14-0041-4daf-a8cc-c1efcbb6a8717629451295084640330.jpg', 'jpg', '/upload/scaled_e2206e14-0041-4daf-a8cc-c1efcbb6a8717629451295084640330-202404200937307.jpg', '图片', '111.81 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (481, '2024-04-20 09:37:47.332695', '2024-04-20 09:37:47.332695', 'scaled_1d6af01c-9d51-4bff-863b-f73d4646f20b3868983217180435704-202404200937331.jpg', 'scaled_1d6af01c-9d51-4bff-863b-f73d4646f20b3868983217180435704.jpg', 'jpg', '/upload/scaled_1d6af01c-9d51-4bff-863b-f73d4646f20b3868983217180435704-202404200937331.jpg', '图片', '100.33 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (482, '2024-04-20 10:06:22.492315', '2024-04-20 10:06:22.492315', 'scaled_de272e88-b9e6-46eb-ac31-a84dfcaf93711826073506211339935-202404201006488.jpg', 'scaled_de272e88-b9e6-46eb-ac31-a84dfcaf93711826073506211339935.jpg', 'jpg', '/upload/scaled_de272e88-b9e6-46eb-ac31-a84dfcaf93711826073506211339935-202404201006488.jpg', '图片', '108.08 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (483, '2024-04-20 10:06:22.681264', '2024-04-20 10:06:22.681264', 'scaled_d8d850e3-68c4-4c2a-9b6c-a7d4fe6e851b4397077696648364538-202404201006679.jpg', 'scaled_d8d850e3-68c4-4c2a-9b6c-a7d4fe6e851b4397077696648364538.jpg', 'jpg', '/upload/scaled_d8d850e3-68c4-4c2a-9b6c-a7d4fe6e851b4397077696648364538-202404201006679.jpg', '图片', '374.31 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (484, '2024-04-20 10:06:55.041464', '2024-04-20 10:06:55.041464', 'scaled_b556eacb-b6f3-4c95-8a0d-01d82309e6587546420344292209244-202404201006039.jpg', 'scaled_b556eacb-b6f3-4c95-8a0d-01d82309e6587546420344292209244.jpg', 'jpg', '/upload/scaled_b556eacb-b6f3-4c95-8a0d-01d82309e6587546420344292209244-202404201006039.jpg', '图片', '209.04 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (485, '2024-04-20 10:06:55.316440', '2024-04-20 10:06:55.316440', 'scaled_f0ca45a7-704b-46ef-831d-7863b5137a618743275274922312420-202404201006314.jpg', 'scaled_f0ca45a7-704b-46ef-831d-7863b5137a618743275274922312420.jpg', 'jpg', '/upload/scaled_f0ca45a7-704b-46ef-831d-7863b5137a618743275274922312420-202404201006314.jpg', '图片', '475.39 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (486, '2024-04-20 10:29:28.949761', '2024-04-20 10:29:28.949761', 'scaled_19edb406-15c3-44d4-b24c-1b40a5b264401503696770607501464-202404201029946.jpg', 'scaled_19edb406-15c3-44d4-b24c-1b40a5b264401503696770607501464.jpg', 'jpg', '/upload/scaled_19edb406-15c3-44d4-b24c-1b40a5b264401503696770607501464-202404201029946.jpg', '图片', '113.08 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (487, '2024-04-20 10:29:29.155827', '2024-04-20 10:29:29.155827', 'scaled_f8922ab3-8287-4721-8f0b-651f7b25a3166501332652202168649-202404201029154.jpg', 'scaled_f8922ab3-8287-4721-8f0b-651f7b25a3166501332652202168649.jpg', 'jpg', '/upload/scaled_f8922ab3-8287-4721-8f0b-651f7b25a3166501332652202168649-202404201029154.jpg', '图片', '212.79 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (488, '2024-04-20 10:30:14.831488', '2024-04-20 10:30:14.831488', 'scaled_594cb7bb-abe3-48dc-a81b-241ae66d42208800304858582483087-202404201030829.jpg', 'scaled_594cb7bb-abe3-48dc-a81b-241ae66d42208800304858582483087.jpg', 'jpg', '/upload/scaled_594cb7bb-abe3-48dc-a81b-241ae66d42208800304858582483087-202404201030829.jpg', '图片', '159.8 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (489, '2024-04-20 10:30:14.984439', '2024-04-20 10:30:14.984439', 'scaled_ff464053-2785-424e-bb02-be002e0239173953020823095347129-202404201030982.jpg', 'scaled_ff464053-2785-424e-bb02-be002e0239173953020823095347129.jpg', 'jpg', '/upload/scaled_ff464053-2785-424e-bb02-be002e0239173953020823095347129-202404201030982.jpg', '图片', '282.45 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (490, '2024-04-20 10:30:57.416195', '2024-04-20 10:30:57.416195', 'scaled_193af12b-7b74-48a3-a819-3d2dc5e5264d1879878126050291400-202404201030413.jpg', 'scaled_193af12b-7b74-48a3-a819-3d2dc5e5264d1879878126050291400.jpg', 'jpg', '/upload/scaled_193af12b-7b74-48a3-a819-3d2dc5e5264d1879878126050291400-202404201030413.jpg', '图片', '96.47 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (491, '2024-04-20 10:30:57.506573', '2024-04-20 10:30:57.506573', 'scaled_7fdffc70-6fee-43d7-8ec1-00e890d7656d5505861237182542643-202404201030504.jpg', 'scaled_7fdffc70-6fee-43d7-8ec1-00e890d7656d5505861237182542643.jpg', 'jpg', '/upload/scaled_7fdffc70-6fee-43d7-8ec1-00e890d7656d5505861237182542643-202404201030504.jpg', '图片', '158.75 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (492, '2024-04-20 10:41:25.281148', '2024-04-20 10:41:25.281148', 'scaled_060c47c9-a957-4209-9d4a-aa6df3f1401a3330848962598403171-202404201041277.jpg', 'scaled_060c47c9-a957-4209-9d4a-aa6df3f1401a3330848962598403171.jpg', 'jpg', '/upload/scaled_060c47c9-a957-4209-9d4a-aa6df3f1401a3330848962598403171-202404201041277.jpg', '图片', '127.19 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (493, '2024-04-20 10:41:25.497573', '2024-04-20 10:41:25.497573', 'scaled_7c138723-0e68-42f6-a168-cb5eb4b4e48e8896080252513787812-202404201041496.jpg', 'scaled_7c138723-0e68-42f6-a168-cb5eb4b4e48e8896080252513787812.jpg', 'jpg', '/upload/scaled_7c138723-0e68-42f6-a168-cb5eb4b4e48e8896080252513787812-202404201041496.jpg', '图片', '221.77 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (494, '2024-04-20 14:49:24.474828', '2024-04-20 14:49:24.474828', 'scaled_272e6561-425c-4927-aaef-5b4f783dc6e98455239697652513905-202404201449470.jpg', 'scaled_272e6561-425c-4927-aaef-5b4f783dc6e98455239697652513905.jpg', 'jpg', '/upload/scaled_272e6561-425c-4927-aaef-5b4f783dc6e98455239697652513905-202404201449470.jpg', '图片', '90.76 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (495, '2024-04-20 14:49:24.503408', '2024-04-20 14:49:24.503408', 'scaled_d7c0f750-406b-48ba-bde8-55129428c1a38018800145279044981-202404201449501.jpg', 'scaled_d7c0f750-406b-48ba-bde8-55129428c1a38018800145279044981.jpg', 'jpg', '/upload/scaled_d7c0f750-406b-48ba-bde8-55129428c1a38018800145279044981-202404201449501.jpg', '图片', '128.15 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (496, '2024-04-20 14:50:37.337051', '2024-04-20 14:50:37.337051', 'scaled_18ecc505-499b-4f7d-b1f2-f5d9d077db988950279665621352791-202404201450334.jpg', 'scaled_18ecc505-499b-4f7d-b1f2-f5d9d077db988950279665621352791.jpg', 'jpg', '/upload/scaled_18ecc505-499b-4f7d-b1f2-f5d9d077db988950279665621352791-202404201450334.jpg', '图片', '104.17 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (497, '2024-04-20 14:50:37.363126', '2024-04-20 14:50:37.363126', 'scaled_3db166d0-ee6f-48fe-8800-2cd347c4db07354447289836176871-202404201450361.jpg', 'scaled_3db166d0-ee6f-48fe-8800-2cd347c4db07354447289836176871.jpg', 'jpg', '/upload/scaled_3db166d0-ee6f-48fe-8800-2cd347c4db07354447289836176871-202404201450361.jpg', '图片', '93.18 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (498, '2024-04-20 17:07:33.322960', '2024-04-20 17:07:33.322960', 'scaled_1217f767-cd00-4402-be3a-94ec602aa4fc473325181396331652-202404201707320.jpg', 'scaled_1217f767-cd00-4402-be3a-94ec602aa4fc473325181396331652.jpg', 'jpg', '/upload/scaled_1217f767-cd00-4402-be3a-94ec602aa4fc473325181396331652-202404201707320.jpg', '图片', '95.33 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (499, '2024-04-20 17:07:33.323458', '2024-04-20 17:07:33.323458', 'scaled_1afab23c-aabc-479d-8555-e0d6427f5f099050676208238523278-202404201707321.jpg', 'scaled_1afab23c-aabc-479d-8555-e0d6427f5f099050676208238523278.jpg', 'jpg', '/upload/scaled_1afab23c-aabc-479d-8555-e0d6427f5f099050676208238523278-202404201707321.jpg', '图片', '118.03 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (500, '2024-04-21 14:18:49.092171', '2024-04-21 14:18:49.092171', 'scaled_b25dab76-6837-4669-9390-933adc6ca50d5959379921184705863-202404211418088.jpg', 'scaled_b25dab76-6837-4669-9390-933adc6ca50d5959379921184705863.jpg', 'jpg', '/upload/scaled_b25dab76-6837-4669-9390-933adc6ca50d5959379921184705863-202404211418088.jpg', '图片', '128.89 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (501, '2024-04-21 14:18:49.168321', '2024-04-21 14:18:49.168321', 'scaled_8aed678f-c6ac-468d-97d0-7f530a622216924004963574390059-202404211418166.jpg', 'scaled_8aed678f-c6ac-468d-97d0-7f530a622216924004963574390059.jpg', 'jpg', '/upload/scaled_8aed678f-c6ac-468d-97d0-7f530a622216924004963574390059-202404211418166.jpg', '图片', '127.71 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (502, '2024-04-21 15:46:08.697748', '2024-04-21 15:46:08.697748', 'scaled_1903ec82-4561-4dea-9935-12700cb4c28c3087343161410604693-202404211546694.jpg', 'scaled_1903ec82-4561-4dea-9935-12700cb4c28c3087343161410604693.jpg', 'jpg', '/upload/scaled_1903ec82-4561-4dea-9935-12700cb4c28c3087343161410604693-202404211546694.jpg', '图片', '117.33 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (503, '2024-04-21 15:46:08.802999', '2024-04-21 15:46:08.802999', 'scaled_500a63a1-e18d-463f-8e08-a24ff55a276d8383911974484789935-202404211546801.jpg', 'scaled_500a63a1-e18d-463f-8e08-a24ff55a276d8383911974484789935.jpg', 'jpg', '/upload/scaled_500a63a1-e18d-463f-8e08-a24ff55a276d8383911974484789935-202404211546801.jpg', '图片', '281.61 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (504, '2024-04-21 17:59:18.309930', '2024-04-21 17:59:18.309930', 'scaled_a14cd051-4044-42f5-b9d9-7454831ecd163570164115382513671-202404211759306.jpg', 'scaled_a14cd051-4044-42f5-b9d9-7454831ecd163570164115382513671.jpg', 'jpg', '/upload/scaled_a14cd051-4044-42f5-b9d9-7454831ecd163570164115382513671-202404211759306.jpg', '图片', '212.25 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (505, '2024-04-21 17:59:18.363201', '2024-04-21 17:59:18.363201', 'scaled_c2077d9a-5293-4ca8-a59c-d0578f96ded72922535518443988108-202404211759361.jpg', 'scaled_c2077d9a-5293-4ca8-a59c-d0578f96ded72922535518443988108.jpg', 'jpg', '/upload/scaled_c2077d9a-5293-4ca8-a59c-d0578f96ded72922535518443988108-202404211759361.jpg', '图片', '200.49 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (506, '2024-04-21 18:00:05.361524', '2024-04-21 18:00:05.361524', 'scaled_6cd206ed-5318-46e4-b032-39620393915b5652260045876901531-202404211800359.jpg', 'scaled_6cd206ed-5318-46e4-b032-39620393915b5652260045876901531.jpg', 'jpg', '/upload/scaled_6cd206ed-5318-46e4-b032-39620393915b5652260045876901531-202404211800359.jpg', '图片', '99.43 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (507, '2024-04-21 18:00:05.419177', '2024-04-21 18:00:05.419177', 'scaled_8181d507-892c-4a86-a263-d5ddda961ce87732163798141272574-202404211800417.jpg', 'scaled_8181d507-892c-4a86-a263-d5ddda961ce87732163798141272574.jpg', 'jpg', '/upload/scaled_8181d507-892c-4a86-a263-d5ddda961ce87732163798141272574-202404211800417.jpg', '图片', '116.47 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (508, '2024-04-22 08:10:24.203576', '2024-04-22 08:10:24.203576', 'scaled_b2a23663-380e-4bc2-8246-c45c130ff00d7821619053980022029-202404220810199.jpg', 'scaled_b2a23663-380e-4bc2-8246-c45c130ff00d7821619053980022029.jpg', 'jpg', '/upload/scaled_b2a23663-380e-4bc2-8246-c45c130ff00d7821619053980022029-202404220810199.jpg', '图片', '124.45 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (509, '2024-04-22 08:10:24.249543', '2024-04-22 08:10:24.249543', 'scaled_d53bac42-88b2-48ba-af14-2e209f73620b404270381518686394-202404220810247.jpg', 'scaled_d53bac42-88b2-48ba-af14-2e209f73620b404270381518686394.jpg', 'jpg', '/upload/scaled_d53bac42-88b2-48ba-af14-2e209f73620b404270381518686394-202404220810247.jpg', '图片', '177.77 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (510, '2024-04-22 08:11:06.182563', '2024-04-22 08:11:06.182563', 'scaled_ab1802d8-1b32-4a3e-ae26-d83fd047b8036329515945158665446-202404220811180.jpg', 'scaled_ab1802d8-1b32-4a3e-ae26-d83fd047b8036329515945158665446.jpg', 'jpg', '/upload/scaled_ab1802d8-1b32-4a3e-ae26-d83fd047b8036329515945158665446-202404220811180.jpg', '图片', '83.16 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (511, '2024-04-22 08:11:06.236422', '2024-04-22 08:11:06.236422', 'scaled_9f956a62-4079-4f5b-8f9f-934968c558e31757406008779363301-202404220811234.jpg', 'scaled_9f956a62-4079-4f5b-8f9f-934968c558e31757406008779363301.jpg', 'jpg', '/upload/scaled_9f956a62-4079-4f5b-8f9f-934968c558e31757406008779363301-202404220811234.jpg', '图片', '127.13 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (512, '2024-04-22 08:35:03.611726', '2024-04-22 08:35:03.611726', 'scaled_35d1bea6-d8db-4a0b-8dd0-ad146d7888387365760625209343192-202404220835609.jpg', 'scaled_35d1bea6-d8db-4a0b-8dd0-ad146d7888387365760625209343192.jpg', 'jpg', '/upload/scaled_35d1bea6-d8db-4a0b-8dd0-ad146d7888387365760625209343192-202404220835609.jpg', '图片', '225.44 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (513, '2024-04-22 08:35:03.632288', '2024-04-22 08:35:03.632288', 'scaled_4a1ee7e8-6bcb-44ea-8323-fd3d01ab30b66466331216736298812-202404220835631.jpg', 'scaled_4a1ee7e8-6bcb-44ea-8323-fd3d01ab30b66466331216736298812.jpg', 'jpg', '/upload/scaled_4a1ee7e8-6bcb-44ea-8323-fd3d01ab30b66466331216736298812-202404220835631.jpg', '图片', '211.94 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (514, '2024-04-22 08:42:18.733657', '2024-04-22 08:42:18.733657', 'scaled_312870d3-1715-44bf-965f-677b119dfea72397374372366690264-202404220842731.jpg', 'scaled_312870d3-1715-44bf-965f-677b119dfea72397374372366690264.jpg', 'jpg', '/upload/scaled_312870d3-1715-44bf-965f-677b119dfea72397374372366690264-202404220842731.jpg', '图片', '108.3 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (515, '2024-04-22 08:42:18.784922', '2024-04-22 08:42:18.784922', 'scaled_b4e84df8-515b-4251-bf04-97beed2d0cdd3412702606965631702-202404220842783.jpg', 'scaled_b4e84df8-515b-4251-bf04-97beed2d0cdd3412702606965631702.jpg', 'jpg', '/upload/scaled_b4e84df8-515b-4251-bf04-97beed2d0cdd3412702606965631702-202404220842783.jpg', '图片', '157.58 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (516, '2024-04-22 08:42:49.861429', '2024-04-22 08:42:49.861429', 'scaled_d69738b4-f767-4014-bba4-e52f1830f0de3496940803070699644-202404220842858.jpg', 'scaled_d69738b4-f767-4014-bba4-e52f1830f0de3496940803070699644.jpg', 'jpg', '/upload/scaled_d69738b4-f767-4014-bba4-e52f1830f0de3496940803070699644-202404220842858.jpg', '图片', '165.58 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (517, '2024-04-22 08:42:49.963625', '2024-04-22 08:42:49.963625', 'scaled_bfa2b307-7bae-43b4-9af2-024ac9f44b794111705243654657966-202404220842960.jpg', 'scaled_bfa2b307-7bae-43b4-9af2-024ac9f44b794111705243654657966.jpg', 'jpg', '/upload/scaled_bfa2b307-7bae-43b4-9af2-024ac9f44b794111705243654657966-202404220842960.jpg', '图片', '252.2 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (518, '2024-04-22 08:44:40.839993', '2024-04-22 08:44:40.839993', 'scaled_98751a5a-5f4a-4709-b1ba-c5d688b868c78411717740147622992-202404220844838.jpg', 'scaled_98751a5a-5f4a-4709-b1ba-c5d688b868c78411717740147622992.jpg', 'jpg', '/upload/scaled_98751a5a-5f4a-4709-b1ba-c5d688b868c78411717740147622992-202404220844838.jpg', '图片', '118.21 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (519, '2024-04-22 08:44:40.908970', '2024-04-22 08:44:40.908970', 'scaled_f7f531ac-5381-4583-9abb-617e587be15d6818819127287000678-202404220844907.jpg', 'scaled_f7f531ac-5381-4583-9abb-617e587be15d6818819127287000678.jpg', 'jpg', '/upload/scaled_f7f531ac-5381-4583-9abb-617e587be15d6818819127287000678-202404220844907.jpg', '图片', '252.17 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (520, '2024-04-22 09:13:58.922448', '2024-04-22 09:13:58.922448', 'scaled_c00aa82f-e5a5-404b-809d-4c72a6cacf089166908590799026334-202404220913919.jpg', 'scaled_c00aa82f-e5a5-404b-809d-4c72a6cacf089166908590799026334.jpg', 'jpg', '/upload/scaled_c00aa82f-e5a5-404b-809d-4c72a6cacf089166908590799026334-202404220913919.jpg', '图片', '110.14 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (521, '2024-04-22 09:13:58.974136', '2024-04-22 09:13:58.974136', 'scaled_2261f5c0-5325-4147-b446-8faf4a5caa162010928778103491464-202404220913972.jpg', 'scaled_2261f5c0-5325-4147-b446-8faf4a5caa162010928778103491464.jpg', 'jpg', '/upload/scaled_2261f5c0-5325-4147-b446-8faf4a5caa162010928778103491464-202404220913972.jpg', '图片', '197.85 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (522, '2024-04-22 09:15:31.445674', '2024-04-22 09:15:31.445674', 'scaled_03da4455-d9ac-45c0-8ef3-172a2d5c81446124230175071503208-202404220915443.jpg', 'scaled_03da4455-d9ac-45c0-8ef3-172a2d5c81446124230175071503208.jpg', 'jpg', '/upload/scaled_03da4455-d9ac-45c0-8ef3-172a2d5c81446124230175071503208-202404220915443.jpg', '图片', '118.44 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (523, '2024-04-22 09:15:31.473161', '2024-04-22 09:15:31.473161', 'scaled_6965db83-21d2-4bd5-89b3-a5cd02569ba64043266870270810336-202404220915471.jpg', 'scaled_6965db83-21d2-4bd5-89b3-a5cd02569ba64043266870270810336.jpg', 'jpg', '/upload/scaled_6965db83-21d2-4bd5-89b3-a5cd02569ba64043266870270810336-202404220915471.jpg', '图片', '188.92 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (524, '2024-04-22 09:25:49.281241', '2024-04-22 09:25:49.281241', 'scaled_9628745b-8c27-48c9-a5e1-b0c3d943eca03155586497919451409-202404220925278.jpg', 'scaled_9628745b-8c27-48c9-a5e1-b0c3d943eca03155586497919451409.jpg', 'jpg', '/upload/scaled_9628745b-8c27-48c9-a5e1-b0c3d943eca03155586497919451409-202404220925278.jpg', '图片', '95.68 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (525, '2024-04-22 09:25:49.314516', '2024-04-22 09:25:49.314516', 'scaled_9b2cda75-49da-4e06-90b6-e4d1234694af9050316021269715403-202404220925312.jpg', 'scaled_9b2cda75-49da-4e06-90b6-e4d1234694af9050316021269715403.jpg', 'jpg', '/upload/scaled_9b2cda75-49da-4e06-90b6-e4d1234694af9050316021269715403-202404220925312.jpg', '图片', '142.22 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (526, '2024-04-22 17:22:24.646471', '2024-04-22 17:22:24.646471', 'scaled_190b8b4c-f963-4697-9792-03ea67db00f74226499110953026981-202404221722642.jpg', 'scaled_190b8b4c-f963-4697-9792-03ea67db00f74226499110953026981.jpg', 'jpg', '/upload/scaled_190b8b4c-f963-4697-9792-03ea67db00f74226499110953026981-202404221722642.jpg', '图片', '124.82 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (527, '2024-04-22 17:22:24.792892', '2024-04-22 17:22:24.792892', 'scaled_8c8aeff7-dd4e-4800-9229-da76fa2902783449142820786739026-202404221722791.jpg', 'scaled_8c8aeff7-dd4e-4800-9229-da76fa2902783449142820786739026.jpg', 'jpg', '/upload/scaled_8c8aeff7-dd4e-4800-9229-da76fa2902783449142820786739026-202404221722791.jpg', '图片', '311.83 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (528, '2024-04-22 17:23:08.888792', '2024-04-22 17:23:08.888792', 'scaled_050e4cb2-7e06-45aa-9141-710850bbaf0b202288873418398574-202404221723886.jpg', 'scaled_050e4cb2-7e06-45aa-9141-710850bbaf0b202288873418398574.jpg', 'jpg', '/upload/scaled_050e4cb2-7e06-45aa-9141-710850bbaf0b202288873418398574-202404221723886.jpg', '图片', '109.3 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (529, '2024-04-22 17:23:08.991615', '2024-04-22 17:23:08.991615', 'scaled_21cf7a37-39bb-49a3-aa5b-fe67078802293355675967499235006-202404221723989.jpg', 'scaled_21cf7a37-39bb-49a3-aa5b-fe67078802293355675967499235006.jpg', 'jpg', '/upload/scaled_21cf7a37-39bb-49a3-aa5b-fe67078802293355675967499235006-202404221723989.jpg', '图片', '176.48 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (530, '2024-04-22 17:23:48.785158', '2024-04-22 17:23:48.785158', 'scaled_23752a58-6def-41cd-a14d-5a0a511b024a1589061997111045743-202404221723783.jpg', 'scaled_23752a58-6def-41cd-a14d-5a0a511b024a1589061997111045743.jpg', 'jpg', '/upload/scaled_23752a58-6def-41cd-a14d-5a0a511b024a1589061997111045743-202404221723783.jpg', '图片', '202.52 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (531, '2024-04-22 17:23:48.888222', '2024-04-22 17:23:48.888222', 'scaled_ea6a5443-d631-43d5-8bbc-17cda788296b7432958578999255822-202404221723886.jpg', 'scaled_ea6a5443-d631-43d5-8bbc-17cda788296b7432958578999255822.jpg', 'jpg', '/upload/scaled_ea6a5443-d631-43d5-8bbc-17cda788296b7432958578999255822-202404221723886.jpg', '图片', '197.38 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (532, '2024-04-22 17:24:25.682529', '2024-04-22 17:24:25.682529', 'scaled_7e1eb662-a2f7-47ca-bf4b-04231438017c4112696384490170628-202404221724679.jpg', 'scaled_7e1eb662-a2f7-47ca-bf4b-04231438017c4112696384490170628.jpg', 'jpg', '/upload/scaled_7e1eb662-a2f7-47ca-bf4b-04231438017c4112696384490170628-202404221724679.jpg', '图片', '126.41 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (533, '2024-04-22 17:24:25.710131', '2024-04-22 17:24:25.710131', 'scaled_12370063-8c4e-4c5b-8173-b7dc752637bb2206074786260565296-202404221724708.jpg', 'scaled_12370063-8c4e-4c5b-8173-b7dc752637bb2206074786260565296.jpg', 'jpg', '/upload/scaled_12370063-8c4e-4c5b-8173-b7dc752637bb2206074786260565296-202404221724708.jpg', '图片', '174.32 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (534, '2024-04-23 08:16:53.983563', '2024-04-23 08:16:53.983563', 'scaled_1aee74c3-9242-4ab8-a642-876d535308417471165634389233502-202404230816980.jpg', 'scaled_1aee74c3-9242-4ab8-a642-876d535308417471165634389233502.jpg', 'jpg', '/upload/scaled_1aee74c3-9242-4ab8-a642-876d535308417471165634389233502-202404230816980.jpg', '图片', '124.26 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (535, '2024-04-23 08:16:54.040124', '2024-04-23 08:16:54.040124', 'scaled_f1534357-8b2f-4f1b-8b79-cacbb4a07f044252624328106439756-202404230816038.jpg', 'scaled_f1534357-8b2f-4f1b-8b79-cacbb4a07f044252624328106439756.jpg', 'jpg', '/upload/scaled_f1534357-8b2f-4f1b-8b79-cacbb4a07f044252624328106439756-202404230816038.jpg', '图片', '234.12 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (536, '2024-04-23 08:22:29.890921', '2024-04-23 08:22:29.890921', 'scaled_341c52d1-8874-496f-96f1-358ffb80a87b8387041967170139672-202404230822887.jpg', 'scaled_341c52d1-8874-496f-96f1-358ffb80a87b8387041967170139672.jpg', 'jpg', '/upload/scaled_341c52d1-8874-496f-96f1-358ffb80a87b8387041967170139672-202404230822887.jpg', '图片', '229.24 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (537, '2024-04-23 08:22:30.159009', '2024-04-23 08:22:30.159009', 'scaled_dbb46437-e79e-45c2-af08-81af5d00dc601914227168573273819-202404230822156.jpg', 'scaled_dbb46437-e79e-45c2-af08-81af5d00dc601914227168573273819.jpg', 'jpg', '/upload/scaled_dbb46437-e79e-45c2-af08-81af5d00dc601914227168573273819-202404230822156.jpg', '图片', '129.64 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (538, '2024-04-23 08:23:05.292217', '2024-04-23 08:23:05.292217', 'scaled_63f72378-c11b-46b4-9b95-5d124641d84d6051360504725439176-202404230823288.jpg', 'scaled_63f72378-c11b-46b4-9b95-5d124641d84d6051360504725439176.jpg', 'jpg', '/upload/scaled_63f72378-c11b-46b4-9b95-5d124641d84d6051360504725439176-202404230823288.jpg', '图片', '110.47 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (539, '2024-04-23 08:23:05.292908', '2024-04-23 08:23:05.292908', 'scaled_1d3b5258-11be-4715-a864-d6168028eaab125359511274210616-202404230823290.jpg', 'scaled_1d3b5258-11be-4715-a864-d6168028eaab125359511274210616.jpg', 'jpg', '/upload/scaled_1d3b5258-11be-4715-a864-d6168028eaab125359511274210616-202404230823290.jpg', '图片', '136.72 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (540, '2024-04-23 13:36:17.621334', '2024-04-23 13:36:17.621334', 'scaled_ef903768-61b0-4a4b-8f47-6fd4e0fd9cb6281525132962981305-202404231336618.jpg', 'scaled_ef903768-61b0-4a4b-8f47-6fd4e0fd9cb6281525132962981305.jpg', 'jpg', '/upload/scaled_ef903768-61b0-4a4b-8f47-6fd4e0fd9cb6281525132962981305-202404231336618.jpg', '图片', '100.58 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (541, '2024-04-23 13:36:17.636779', '2024-04-23 13:36:17.636779', 'scaled_361403fd-b6e6-4e4e-bd9d-177c0d8ead7d4369563806930260434-202404231336635.jpg', 'scaled_361403fd-b6e6-4e4e-bd9d-177c0d8ead7d4369563806930260434.jpg', 'jpg', '/upload/scaled_361403fd-b6e6-4e4e-bd9d-177c0d8ead7d4369563806930260434-202404231336635.jpg', '图片', '183.48 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (542, '2024-04-23 13:37:52.735668', '2024-04-23 13:37:52.735668', 'scaled_61f3e6eb-dcc8-4321-8d1d-a560bbcbc0d87702259787150836738-202404231337733.jpg', 'scaled_61f3e6eb-dcc8-4321-8d1d-a560bbcbc0d87702259787150836738.jpg', 'jpg', '/upload/scaled_61f3e6eb-dcc8-4321-8d1d-a560bbcbc0d87702259787150836738-202404231337733.jpg', '图片', '96.84 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (543, '2024-04-23 13:37:52.793786', '2024-04-23 13:37:52.793786', 'scaled_96395f31-c850-43db-b929-0f2e377229ea5727349960198612319-202404231337792.jpg', 'scaled_96395f31-c850-43db-b929-0f2e377229ea5727349960198612319.jpg', 'jpg', '/upload/scaled_96395f31-c850-43db-b929-0f2e377229ea5727349960198612319-202404231337792.jpg', '图片', '137.23 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (544, '2024-04-23 13:57:59.319809', '2024-04-23 13:57:59.319809', 'scaled_731e4118-904f-47dc-b78c-96a4576f4c401632713156008366416-202404231357317.jpg', 'scaled_731e4118-904f-47dc-b78c-96a4576f4c401632713156008366416.jpg', 'jpg', '/upload/scaled_731e4118-904f-47dc-b78c-96a4576f4c401632713156008366416-202404231357317.jpg', '图片', '102.59 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (545, '2024-04-23 13:57:59.338547', '2024-04-23 13:57:59.338547', 'scaled_a8631741-5004-48ca-aa7f-b257b1e9552b3871668034910003303-202404231357337.jpg', 'scaled_a8631741-5004-48ca-aa7f-b257b1e9552b3871668034910003303.jpg', 'jpg', '/upload/scaled_a8631741-5004-48ca-aa7f-b257b1e9552b3871668034910003303-202404231357337.jpg', '图片', '185.21 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (546, '2024-04-23 13:58:28.804042', '2024-04-23 13:58:28.804042', 'scaled_2250b240-fdb3-4a48-8078-8ee30d74bf086733324785955626954-202404231358802.jpg', 'scaled_2250b240-fdb3-4a48-8078-8ee30d74bf086733324785955626954.jpg', 'jpg', '/upload/scaled_2250b240-fdb3-4a48-8078-8ee30d74bf086733324785955626954-202404231358802.jpg', '图片', '185.27 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (547, '2024-04-23 13:58:28.823168', '2024-04-23 13:58:28.823168', 'scaled_7699f154-1f34-4efc-8764-197965d11b795011161147345474684-202404231358821.jpg', 'scaled_7699f154-1f34-4efc-8764-197965d11b795011161147345474684.jpg', 'jpg', '/upload/scaled_7699f154-1f34-4efc-8764-197965d11b795011161147345474684-202404231358821.jpg', '图片', '122.26 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (548, '2024-04-23 13:58:58.405258', '2024-04-23 13:58:58.405258', 'scaled_145cc6e8-be0e-47e0-b02d-0e394f89d5a86913696378147147813-202404231358403.jpg', 'scaled_145cc6e8-be0e-47e0-b02d-0e394f89d5a86913696378147147813.jpg', 'jpg', '/upload/scaled_145cc6e8-be0e-47e0-b02d-0e394f89d5a86913696378147147813-202404231358403.jpg', '图片', '99.36 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (549, '2024-04-23 13:58:58.412298', '2024-04-23 13:58:58.412298', 'scaled_0e4ab4bd-d385-4bda-876b-3329f167f7523563282390458223094-202404231358410.jpg', 'scaled_0e4ab4bd-d385-4bda-876b-3329f167f7523563282390458223094.jpg', 'jpg', '/upload/scaled_0e4ab4bd-d385-4bda-876b-3329f167f7523563282390458223094-202404231358410.jpg', '图片', '101.36 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (550, '2024-04-23 13:59:27.910679', '2024-04-23 13:59:27.910679', 'scaled_33a43327-eee9-4e31-b2c7-2ce67cfbca381147766980564371171-202404231359908.jpg', 'scaled_33a43327-eee9-4e31-b2c7-2ce67cfbca381147766980564371171.jpg', 'jpg', '/upload/scaled_33a43327-eee9-4e31-b2c7-2ce67cfbca381147766980564371171-202404231359908.jpg', '图片', '143.2 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (551, '2024-04-23 13:59:28.045839', '2024-04-23 13:59:28.045839', 'scaled_6f9a46c7-189d-4184-9040-331b61a9d1a11672096418705833727-202404231359043.jpg', 'scaled_6f9a46c7-189d-4184-9040-331b61a9d1a11672096418705833727.jpg', 'jpg', '/upload/scaled_6f9a46c7-189d-4184-9040-331b61a9d1a11672096418705833727-202404231359043.jpg', '图片', '258.49 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (552, '2024-04-23 15:17:01.237085', '2024-04-23 15:17:01.237085', 'scaled_f04738f9-4dfa-4a07-916b-fd84088989de4898348419713701077-202404231517233.jpg', 'scaled_f04738f9-4dfa-4a07-916b-fd84088989de4898348419713701077.jpg', 'jpg', '/upload/scaled_f04738f9-4dfa-4a07-916b-fd84088989de4898348419713701077-202404231517233.jpg', '图片', '229.11 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (553, '2024-04-23 15:17:01.282146', '2024-04-23 15:17:01.282146', 'scaled_e5aae539-adb7-4df8-9fe1-394c74effe4b1518505688709937014-202404231517280.jpg', 'scaled_e5aae539-adb7-4df8-9fe1-394c74effe4b1518505688709937014.jpg', 'jpg', '/upload/scaled_e5aae539-adb7-4df8-9fe1-394c74effe4b1518505688709937014-202404231517280.jpg', '图片', '367.81 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (554, '2024-04-23 15:17:31.612764', '2024-04-23 15:17:31.612764', 'scaled_423b577e-6520-4500-9894-96e2be0224419081675397401398922-202404231517611.jpg', 'scaled_423b577e-6520-4500-9894-96e2be0224419081675397401398922.jpg', 'jpg', '/upload/scaled_423b577e-6520-4500-9894-96e2be0224419081675397401398922-202404231517611.jpg', '图片', '235.54 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (555, '2024-04-23 15:17:31.694960', '2024-04-23 15:17:31.694960', 'scaled_818ad835-5736-41d7-a480-8349fe96d1e35953147042969839739-202404231517692.jpg', 'scaled_818ad835-5736-41d7-a480-8349fe96d1e35953147042969839739.jpg', 'jpg', '/upload/scaled_818ad835-5736-41d7-a480-8349fe96d1e35953147042969839739-202404231517692.jpg', '图片', '345.08 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (556, '2024-04-23 16:55:53.462914', '2024-04-23 16:55:53.462914', 'scaled_0790d07e-448c-4d9c-b2ab-ee47b3a209212282455367299767488-202404231655460.jpg', 'scaled_0790d07e-448c-4d9c-b2ab-ee47b3a209212282455367299767488.jpg', 'jpg', '/upload/scaled_0790d07e-448c-4d9c-b2ab-ee47b3a209212282455367299767488-202404231655460.jpg', '图片', '229.84 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (557, '2024-04-23 16:55:53.468817', '2024-04-23 16:55:53.468817', 'scaled_173c6f12-622b-41cd-9746-98173df366819214330071363390087-202404231655467.jpg', 'scaled_173c6f12-622b-41cd-9746-98173df366819214330071363390087.jpg', 'jpg', '/upload/scaled_173c6f12-622b-41cd-9746-98173df366819214330071363390087-202404231655467.jpg', '图片', '282.14 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (558, '2024-04-23 18:18:50.840910', '2024-04-23 18:18:50.840910', 'scaled_79cdc813-5681-4e99-bad0-abd11e32a6108842055912522484918-202404231818838.jpg', 'scaled_79cdc813-5681-4e99-bad0-abd11e32a6108842055912522484918.jpg', 'jpg', '/upload/scaled_79cdc813-5681-4e99-bad0-abd11e32a6108842055912522484918-202404231818838.jpg', '图片', '115.8 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (559, '2024-04-23 18:18:51.051166', '2024-04-23 18:18:51.051166', 'scaled_d8f9fa79-20e3-4289-9eb2-f997f8bd6cd44241740643789555136-202404231818048.jpg', 'scaled_d8f9fa79-20e3-4289-9eb2-f997f8bd6cd44241740643789555136.jpg', 'jpg', '/upload/scaled_d8f9fa79-20e3-4289-9eb2-f997f8bd6cd44241740643789555136-202404231818048.jpg', '图片', '205.57 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (560, '2024-04-24 09:11:40.070737', '2024-04-24 09:11:40.070737', 'scaled_a1a1cf4f-28fd-4e39-a276-1214a22d8c1e140545134496872510-202404240911068.jpg', 'scaled_a1a1cf4f-28fd-4e39-a276-1214a22d8c1e140545134496872510.jpg', 'jpg', '/upload/scaled_a1a1cf4f-28fd-4e39-a276-1214a22d8c1e140545134496872510-202404240911068.jpg', '图片', '110.01 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (561, '2024-04-24 09:11:40.196297', '2024-04-24 09:11:40.196297', 'scaled_6d8515d8-6bfe-410c-8c9e-98db77898acf728838884208635570-202404240911194.jpg', 'scaled_6d8515d8-6bfe-410c-8c9e-98db77898acf728838884208635570.jpg', 'jpg', '/upload/scaled_6d8515d8-6bfe-410c-8c9e-98db77898acf728838884208635570-202404240911194.jpg', '图片', '185.39 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (562, '2024-04-24 09:11:40.271132', '2024-04-24 09:11:40.271132', 'scaled_d2ac8474-ec4a-4f24-8c85-09a252131f661048178321656735070-202404240911269.jpg', 'scaled_d2ac8474-ec4a-4f24-8c85-09a252131f661048178321656735070.jpg', 'jpg', '/upload/scaled_d2ac8474-ec4a-4f24-8c85-09a252131f661048178321656735070-202404240911269.jpg', '图片', '202.08 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (563, '2024-04-24 09:11:40.288862', '2024-04-24 09:11:40.288862', 'scaled_e1327e43-24ab-48ea-bd8c-ceee61d1873d539940721749710626-202404240911283.jpg', 'scaled_e1327e43-24ab-48ea-bd8c-ceee61d1873d539940721749710626.jpg', 'jpg', '/upload/scaled_e1327e43-24ab-48ea-bd8c-ceee61d1873d539940721749710626-202404240911283.jpg', '图片', '253.42 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (564, '2024-04-24 09:11:40.312616', '2024-04-24 09:11:40.312616', 'scaled_5e5f1553-a8b4-4401-8f21-35df723205f07274911573622474150-202404240911310.jpg', 'scaled_5e5f1553-a8b4-4401-8f21-35df723205f07274911573622474150.jpg', 'jpg', '/upload/scaled_5e5f1553-a8b4-4401-8f21-35df723205f07274911573622474150-202404240911310.jpg', '图片', '370.34 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (565, '2024-04-24 09:11:40.318513', '2024-04-24 09:11:40.318513', 'scaled_6c5c847f-1899-4a73-a414-d568000b4d6a4328323964909346004-202404240911317.jpg', 'scaled_6c5c847f-1899-4a73-a414-d568000b4d6a4328323964909346004.jpg', 'jpg', '/upload/scaled_6c5c847f-1899-4a73-a414-d568000b4d6a4328323964909346004-202404240911317.jpg', '图片', '386.98 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (566, '2024-04-24 09:11:40.375031', '2024-04-24 09:11:40.375031', 'scaled_09e0a311-67e7-48c8-8f4e-206984cdb9b03202395353057393849-202404240911373.jpg', 'scaled_09e0a311-67e7-48c8-8f4e-206984cdb9b03202395353057393849.jpg', 'jpg', '/upload/scaled_09e0a311-67e7-48c8-8f4e-206984cdb9b03202395353057393849-202404240911373.jpg', '图片', '436.66 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (567, '2024-04-24 09:13:13.635016', '2024-04-24 09:13:13.635016', 'scaled_9b51ce7b-7246-4806-a946-2fc6d320ddd35929420750109345920-202404240913631.jpg', 'scaled_9b51ce7b-7246-4806-a946-2fc6d320ddd35929420750109345920.jpg', 'jpg', '/upload/scaled_9b51ce7b-7246-4806-a946-2fc6d320ddd35929420750109345920-202404240913631.jpg', '图片', '174.24 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (568, '2024-04-24 09:13:13.733708', '2024-04-24 09:13:13.733708', 'scaled_893f23d7-d2e7-400d-8c07-ff822683ac023705838376703372477-202404240913731.jpg', 'scaled_893f23d7-d2e7-400d-8c07-ff822683ac023705838376703372477.jpg', 'jpg', '/upload/scaled_893f23d7-d2e7-400d-8c07-ff822683ac023705838376703372477-202404240913731.jpg', '图片', '308.97 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (569, '2024-04-24 09:13:51.955894', '2024-04-24 09:13:51.955894', 'scaled_351750a7-b1bc-4d0c-a325-1770c96552a04241218366668583244-202404240913954.jpg', 'scaled_351750a7-b1bc-4d0c-a325-1770c96552a04241218366668583244.jpg', 'jpg', '/upload/scaled_351750a7-b1bc-4d0c-a325-1770c96552a04241218366668583244-202404240913954.jpg', '图片', '158.39 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (570, '2024-04-24 09:13:52.060347', '2024-04-24 09:13:52.060347', 'scaled_3481edbb-571c-48e4-9a93-4f69d7be4bd18468921084490902751-202404240913058.jpg', 'scaled_3481edbb-571c-48e4-9a93-4f69d7be4bd18468921084490902751.jpg', 'jpg', '/upload/scaled_3481edbb-571c-48e4-9a93-4f69d7be4bd18468921084490902751-202404240913058.jpg', '图片', '244.38 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (571, '2024-04-24 09:20:52.689443', '2024-04-24 09:20:52.689443', 'scaled_4862ed75-86f6-4a52-8eaf-c78cb218f7d53084830250827887545-202404240920686.jpg', 'scaled_4862ed75-86f6-4a52-8eaf-c78cb218f7d53084830250827887545.jpg', 'jpg', '/upload/scaled_4862ed75-86f6-4a52-8eaf-c78cb218f7d53084830250827887545-202404240920686.jpg', '图片', '150.46 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (572, '2024-04-24 09:20:52.716202', '2024-04-24 09:20:52.716202', 'scaled_490c8d9d-87e8-478d-9676-fca8f82ca6757641990173013664142-202404240920713.jpg', 'scaled_490c8d9d-87e8-478d-9676-fca8f82ca6757641990173013664142.jpg', 'jpg', '/upload/scaled_490c8d9d-87e8-478d-9676-fca8f82ca6757641990173013664142-202404240920713.jpg', '图片', '129.19 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (573, '2024-04-24 09:20:52.735161', '2024-04-24 09:20:52.735161', 'scaled_a8c04abf-c32b-460c-a07d-49f117c5a2662451938725005389687-202404240920733.jpg', 'scaled_a8c04abf-c32b-460c-a07d-49f117c5a2662451938725005389687.jpg', 'jpg', '/upload/scaled_a8c04abf-c32b-460c-a07d-49f117c5a2662451938725005389687-202404240920733.jpg', '图片', '206.09 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (574, '2024-04-24 09:20:52.766441', '2024-04-24 09:20:52.766441', 'scaled_b981f2d0-647c-4067-967d-544c8036f8113736063919320056421-202404240920765.jpg', 'scaled_b981f2d0-647c-4067-967d-544c8036f8113736063919320056421.jpg', 'jpg', '/upload/scaled_b981f2d0-647c-4067-967d-544c8036f8113736063919320056421-202404240920765.jpg', '图片', '328.51 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (575, '2024-04-24 09:20:52.779093', '2024-04-24 09:20:52.779093', 'scaled_a7664d19-85cd-44e3-b8ab-7837b000f5718102810033426536698-202404240920777.jpg', 'scaled_a7664d19-85cd-44e3-b8ab-7837b000f5718102810033426536698.jpg', 'jpg', '/upload/scaled_a7664d19-85cd-44e3-b8ab-7837b000f5718102810033426536698-202404240920777.jpg', '图片', '255.24 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (576, '2024-04-24 09:23:58.639179', '2024-04-24 09:23:58.639179', 'scaled_0a4dfb38-1704-4399-8639-cca0522d309f3283612657784807658-202404240923636.jpg', 'scaled_0a4dfb38-1704-4399-8639-cca0522d309f3283612657784807658.jpg', 'jpg', '/upload/scaled_0a4dfb38-1704-4399-8639-cca0522d309f3283612657784807658-202404240923636.jpg', '图片', '99.99 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (577, '2024-04-24 09:23:58.765934', '2024-04-24 09:23:58.765934', 'scaled_388a1ea8-dd4b-4d0f-a73c-28ed9f0fbbef3545564662085316465-202404240923764.jpg', 'scaled_388a1ea8-dd4b-4d0f-a73c-28ed9f0fbbef3545564662085316465.jpg', 'jpg', '/upload/scaled_388a1ea8-dd4b-4d0f-a73c-28ed9f0fbbef3545564662085316465-202404240923764.jpg', '图片', '241.06 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (578, '2024-04-24 09:23:58.766330', '2024-04-24 09:23:58.766330', 'scaled_1df0c3f2-19a2-4e83-81f2-f3f34820c2988417691367043800082-202404240923765.jpg', 'scaled_1df0c3f2-19a2-4e83-81f2-f3f34820c2988417691367043800082.jpg', 'jpg', '/upload/scaled_1df0c3f2-19a2-4e83-81f2-f3f34820c2988417691367043800082-202404240923765.jpg', '图片', '196.93 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (579, '2024-04-24 09:23:58.789150', '2024-04-24 09:23:58.789150', 'scaled_7435a7a7-1dba-411d-8f67-34c2805542b28553298041484816412-202404240923787.jpg', 'scaled_7435a7a7-1dba-411d-8f67-34c2805542b28553298041484816412.jpg', 'jpg', '/upload/scaled_7435a7a7-1dba-411d-8f67-34c2805542b28553298041484816412-202404240923787.jpg', '图片', '274.26 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (580, '2024-04-24 09:23:58.831998', '2024-04-24 09:23:58.831998', 'scaled_ee0ee39d-2840-46f4-bc6b-60a3e5bfe33f3421487922163298531-202404240923830.jpg', 'scaled_ee0ee39d-2840-46f4-bc6b-60a3e5bfe33f3421487922163298531.jpg', 'jpg', '/upload/scaled_ee0ee39d-2840-46f4-bc6b-60a3e5bfe33f3421487922163298531-202404240923830.jpg', '图片', '374.04 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (581, '2024-04-24 09:26:39.990070', '2024-04-24 09:26:39.990070', 'scaled_88a9d295-8768-447b-b85e-e08b80b720826103046251905633721-202404240926987.jpg', 'scaled_88a9d295-8768-447b-b85e-e08b80b720826103046251905633721.jpg', 'jpg', '/upload/scaled_88a9d295-8768-447b-b85e-e08b80b720826103046251905633721-202404240926987.jpg', '图片', '95.07 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (582, '2024-04-24 09:26:40.132387', '2024-04-24 09:26:40.132387', 'scaled_88832ad6-7d22-43ec-b63b-45deaea57bb85826189681500036281-202404240926130.jpg', 'scaled_88832ad6-7d22-43ec-b63b-45deaea57bb85826189681500036281.jpg', 'jpg', '/upload/scaled_88832ad6-7d22-43ec-b63b-45deaea57bb85826189681500036281-202404240926130.jpg', '图片', '254.51 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (583, '2024-04-24 09:26:40.136361', '2024-04-24 09:26:40.136361', 'scaled_6b04be20-5bbb-4fda-8530-09fd7bfc4e883336400309773512655-202404240926134.jpg', 'scaled_6b04be20-5bbb-4fda-8530-09fd7bfc4e883336400309773512655.jpg', 'jpg', '/upload/scaled_6b04be20-5bbb-4fda-8530-09fd7bfc4e883336400309773512655-202404240926134.jpg', '图片', '280.64 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (584, '2024-04-24 09:26:40.143158', '2024-04-24 09:26:40.143158', 'scaled_3527e4f8-bf68-4947-a8a3-eb630b6e922e1662822809024469437-202404240926136.jpg', 'scaled_3527e4f8-bf68-4947-a8a3-eb630b6e922e1662822809024469437.jpg', 'jpg', '/upload/scaled_3527e4f8-bf68-4947-a8a3-eb630b6e922e1662822809024469437-202404240926136.jpg', '图片', '245.41 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (585, '2024-04-24 09:26:40.165064', '2024-04-24 09:26:40.165064', 'scaled_3bf58592-8ffa-4880-a1eb-8325e03c98a36792061104556981533-202404240926163.jpg', 'scaled_3bf58592-8ffa-4880-a1eb-8325e03c98a36792061104556981533.jpg', 'jpg', '/upload/scaled_3bf58592-8ffa-4880-a1eb-8325e03c98a36792061104556981533-202404240926163.jpg', '图片', '285.51 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (586, '2024-04-24 09:26:40.173300', '2024-04-24 09:26:40.173300', 'scaled_9976d006-7b09-47e1-a51e-64660ff933f54845778111605172190-202404240926172.jpg', 'scaled_9976d006-7b09-47e1-a51e-64660ff933f54845778111605172190.jpg', 'jpg', '/upload/scaled_9976d006-7b09-47e1-a51e-64660ff933f54845778111605172190-202404240926172.jpg', '图片', '357.72 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (587, '2024-04-24 17:05:33.319185', '2024-04-24 17:05:33.319185', 'scaled_740087b8-ddf7-48c3-b4dc-d6f88a6a61e28560564800445701142-202404241705315.jpg', 'scaled_740087b8-ddf7-48c3-b4dc-d6f88a6a61e28560564800445701142.jpg', 'jpg', '/upload/scaled_740087b8-ddf7-48c3-b4dc-d6f88a6a61e28560564800445701142-202404241705315.jpg', '图片', '233.98 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (588, '2024-04-24 17:05:34.014998', '2024-04-24 17:05:34.014998', 'scaled_a6c3f2e4-af23-48d2-a704-58524721e42a6357657804816754784-202404241705012.jpg', 'scaled_a6c3f2e4-af23-48d2-a704-58524721e42a6357657804816754784.jpg', 'jpg', '/upload/scaled_a6c3f2e4-af23-48d2-a704-58524721e42a6357657804816754784-202404241705012.jpg', '图片', '144.26 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (589, '2024-04-24 18:06:08.267241', '2024-04-24 18:06:08.267241', 'scaled_0b8e5850-d26b-45ee-881e-9a720b573e298930179317680929428-202404241806263.jpg', 'scaled_0b8e5850-d26b-45ee-881e-9a720b573e298930179317680929428.jpg', 'jpg', '/upload/scaled_0b8e5850-d26b-45ee-881e-9a720b573e298930179317680929428-202404241806263.jpg', '图片', '196.79 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (590, '2024-04-24 18:06:08.321714', '2024-04-24 18:06:08.321714', 'scaled_9bd5dc72-d2d6-4b24-a592-218b306208e44378050266466085976-202404241806319.jpg', 'scaled_9bd5dc72-d2d6-4b24-a592-218b306208e44378050266466085976.jpg', 'jpg', '/upload/scaled_9bd5dc72-d2d6-4b24-a592-218b306208e44378050266466085976-202404241806319.jpg', '图片', '110.98 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (591, '2024-04-24 18:24:10.864673', '2024-04-24 18:24:10.864673', 'scaled_e3c84bef-2755-4cfd-bb8a-72ffd423a2378385967569892693009-202404241824861.jpg', 'scaled_e3c84bef-2755-4cfd-bb8a-72ffd423a2378385967569892693009.jpg', 'jpg', '/upload/scaled_e3c84bef-2755-4cfd-bb8a-72ffd423a2378385967569892693009-202404241824861.jpg', '图片', '129.89 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (592, '2024-04-24 18:24:10.869522', '2024-04-24 18:24:10.869522', 'scaled_78862cb1-34b5-453f-9ffd-90c2dacacc9d3962572108578188649-202404241824868.jpg', 'scaled_78862cb1-34b5-453f-9ffd-90c2dacacc9d3962572108578188649.jpg', 'jpg', '/upload/scaled_78862cb1-34b5-453f-9ffd-90c2dacacc9d3962572108578188649-202404241824868.jpg', '图片', '98.6 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (593, '2024-04-24 21:04:12.329357', '2024-04-24 21:04:12.329357', 'scaled_2a0ff917-9425-4477-a66b-5f0ef8ec51183459578458359566266-202404242104324.jpg', 'scaled_2a0ff917-9425-4477-a66b-5f0ef8ec51183459578458359566266.jpg', 'jpg', '/upload/scaled_2a0ff917-9425-4477-a66b-5f0ef8ec51183459578458359566266-202404242104324.jpg', '图片', '135.78 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (594, '2024-04-24 21:04:12.351135', '2024-04-24 21:04:12.351135', 'scaled_dfe09d28-235a-4e60-94b8-0584a981d1841603480318224049067-202404242104349.jpg', 'scaled_dfe09d28-235a-4e60-94b8-0584a981d1841603480318224049067.jpg', 'jpg', '/upload/scaled_dfe09d28-235a-4e60-94b8-0584a981d1841603480318224049067-202404242104349.jpg', '图片', '87.73 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (595, '2024-04-25 08:33:45.617349', '2024-04-25 08:33:45.617349', 'scaled_4c0087ea-41c3-4576-a652-974c02c43ce75618755429072194071-202404250833611.jpg', 'scaled_4c0087ea-41c3-4576-a652-974c02c43ce75618755429072194071.jpg', 'jpg', '/upload/scaled_4c0087ea-41c3-4576-a652-974c02c43ce75618755429072194071-202404250833611.jpg', '图片', '202.91 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (596, '2024-04-25 08:33:45.619153', '2024-04-25 08:33:45.619153', 'scaled_faef47cd-55bb-4ec1-a114-884537e439ae7075970609129125394-202404250833617.jpg', 'scaled_faef47cd-55bb-4ec1-a114-884537e439ae7075970609129125394.jpg', 'jpg', '/upload/scaled_faef47cd-55bb-4ec1-a114-884537e439ae7075970609129125394-202404250833617.jpg', '图片', '253.72 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (597, '2024-04-25 08:41:14.686048', '2024-04-25 08:41:14.686048', 'scaled_85b67af7-bc5b-483b-8572-23c3fb2ecc8c6950920606442496696-202404250841683.jpg', 'scaled_85b67af7-bc5b-483b-8572-23c3fb2ecc8c6950920606442496696.jpg', 'jpg', '/upload/scaled_85b67af7-bc5b-483b-8572-23c3fb2ecc8c6950920606442496696-202404250841683.jpg', '图片', '102.64 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (598, '2024-04-25 08:41:14.727370', '2024-04-25 08:41:14.727370', 'scaled_799498a8-a80c-40e9-8d4e-9c9b8f61cf09734449660664205413-202404250841725.jpg', 'scaled_799498a8-a80c-40e9-8d4e-9c9b8f61cf09734449660664205413.jpg', 'jpg', '/upload/scaled_799498a8-a80c-40e9-8d4e-9c9b8f61cf09734449660664205413-202404250841725.jpg', '图片', '220.74 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (599, '2024-04-25 09:54:43.658939', '2024-04-25 09:54:43.658939', 'scaled_5e22127b-fa6b-414f-9311-9a8d652014cd1385284837160261606-202404250954656.jpg', 'scaled_5e22127b-fa6b-414f-9311-9a8d652014cd1385284837160261606.jpg', 'jpg', '/upload/scaled_5e22127b-fa6b-414f-9311-9a8d652014cd1385284837160261606-202404250954656.jpg', '图片', '120.03 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (600, '2024-04-25 09:54:43.709427', '2024-04-25 09:54:43.709427', 'scaled_01d190a8-85f5-4864-b6a6-905d2e153fa72798301231104594679-202404250954708.jpg', 'scaled_01d190a8-85f5-4864-b6a6-905d2e153fa72798301231104594679.jpg', 'jpg', '/upload/scaled_01d190a8-85f5-4864-b6a6-905d2e153fa72798301231104594679-202404250954708.jpg', '图片', '237.39 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (601, '2024-04-25 09:57:07.323115', '2024-04-25 09:57:07.323115', 'scaled_aef6883c-7026-4be1-a9aa-cbe77c6cae145451153143391421871-202404250957320.jpg', 'scaled_aef6883c-7026-4be1-a9aa-cbe77c6cae145451153143391421871.jpg', 'jpg', '/upload/scaled_aef6883c-7026-4be1-a9aa-cbe77c6cae145451153143391421871-202404250957320.jpg', '图片', '127.72 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (602, '2024-04-25 09:57:07.409260', '2024-04-25 09:57:07.409260', 'scaled_20ef9449-98f4-4583-8af6-ad4c4e487f878967813643127414288-202404250957407.jpg', 'scaled_20ef9449-98f4-4583-8af6-ad4c4e487f878967813643127414288.jpg', 'jpg', '/upload/scaled_20ef9449-98f4-4583-8af6-ad4c4e487f878967813643127414288-202404250957407.jpg', '图片', '294.45 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (603, '2024-04-25 10:51:51.998541', '2024-04-25 10:51:51.998541', 'scaled_eab89373-9584-4445-a5f2-f77c0061800d5115067707649979415-202404251051997.jpg', 'scaled_eab89373-9584-4445-a5f2-f77c0061800d5115067707649979415.jpg', 'jpg', '/upload/scaled_eab89373-9584-4445-a5f2-f77c0061800d5115067707649979415-202404251051997.jpg', '图片', '187.38 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (604, '2024-04-25 10:51:52.093307', '2024-04-25 10:51:52.093307', 'scaled_519d37e4-d8e2-45bb-87b0-7029e4d1f58d8227382196236773280-202404251051091.jpg', 'scaled_519d37e4-d8e2-45bb-87b0-7029e4d1f58d8227382196236773280.jpg', 'jpg', '/upload/scaled_519d37e4-d8e2-45bb-87b0-7029e4d1f58d8227382196236773280-202404251051091.jpg', '图片', '421.36 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (605, '2024-04-25 10:51:52.099290', '2024-04-25 10:51:52.099290', 'scaled_357027e6-18e3-411b-9013-13b82da07d456839762236071830855-202404251051098.jpg', 'scaled_357027e6-18e3-411b-9013-13b82da07d456839762236071830855.jpg', 'jpg', '/upload/scaled_357027e6-18e3-411b-9013-13b82da07d456839762236071830855-202404251051098.jpg', '图片', '429.66 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (606, '2024-04-25 10:52:37.999687', '2024-04-25 10:52:37.999687', 'scaled_d898b627-9d07-4d41-9f6d-351ca398b4a63729954103143038453-202404251052997.jpg', 'scaled_d898b627-9d07-4d41-9f6d-351ca398b4a63729954103143038453.jpg', 'jpg', '/upload/scaled_d898b627-9d07-4d41-9f6d-351ca398b4a63729954103143038453-202404251052997.jpg', '图片', '367.35 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (607, '2024-04-25 10:52:38.066082', '2024-04-25 10:52:38.066082', 'scaled_aec36c38-b1ed-42b6-a1e7-a6bebc9c55384954638267285655809-202404251052064.jpg', 'scaled_aec36c38-b1ed-42b6-a1e7-a6bebc9c55384954638267285655809.jpg', 'jpg', '/upload/scaled_aec36c38-b1ed-42b6-a1e7-a6bebc9c55384954638267285655809-202404251052064.jpg', '图片', '215.07 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (608, '2024-04-25 10:52:38.089538', '2024-04-25 10:52:38.089538', 'scaled_8e6d4c3b-8932-4189-99fd-3bfc0a5b53ce3228693631187296822-202404251052087.jpg', 'scaled_8e6d4c3b-8932-4189-99fd-3bfc0a5b53ce3228693631187296822.jpg', 'jpg', '/upload/scaled_8e6d4c3b-8932-4189-99fd-3bfc0a5b53ce3228693631187296822-202404251052087.jpg', '图片', '389.26 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (609, '2024-04-25 10:53:49.963341', '2024-04-25 10:53:49.963341', 'scaled_ee9219d6-4b47-4b07-ae70-6ef298bb48f93577527407533109207-202404251053961.jpg', 'scaled_ee9219d6-4b47-4b07-ae70-6ef298bb48f93577527407533109207.jpg', 'jpg', '/upload/scaled_ee9219d6-4b47-4b07-ae70-6ef298bb48f93577527407533109207-202404251053961.jpg', '图片', '228.06 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (610, '2024-04-25 10:53:50.022396', '2024-04-25 10:53:50.022396', 'scaled_178d6955-0c3e-4bfe-9fa6-9bfe30dcc8a86772220141433290502-202404251053020.jpg', 'scaled_178d6955-0c3e-4bfe-9fa6-9bfe30dcc8a86772220141433290502.jpg', 'jpg', '/upload/scaled_178d6955-0c3e-4bfe-9fa6-9bfe30dcc8a86772220141433290502-202404251053020.jpg', '图片', '240.32 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (611, '2024-04-25 10:54:19.224006', '2024-04-25 10:54:19.224006', 'scaled_cbf82812-8aa3-4152-a0e9-b9154e96b7216511877097271142267-202404251054221.jpg', 'scaled_cbf82812-8aa3-4152-a0e9-b9154e96b7216511877097271142267.jpg', 'jpg', '/upload/scaled_cbf82812-8aa3-4152-a0e9-b9154e96b7216511877097271142267-202404251054221.jpg', '图片', '233.82 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (612, '2024-04-25 10:54:19.257498', '2024-04-25 10:54:19.257498', 'scaled_572c8510-453b-4b23-891a-64f775a305c85604408466225504025-202404251054256.jpg', 'scaled_572c8510-453b-4b23-891a-64f775a305c85604408466225504025.jpg', 'jpg', '/upload/scaled_572c8510-453b-4b23-891a-64f775a305c85604408466225504025-202404251054256.jpg', '图片', '390.53 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (613, '2024-04-25 10:55:22.529530', '2024-04-25 10:55:22.529530', 'scaled_80ec4152-4322-4e5e-97d0-a1df2cb221bc8677078797236426043-202404251055527.jpg', 'scaled_80ec4152-4322-4e5e-97d0-a1df2cb221bc8677078797236426043.jpg', 'jpg', '/upload/scaled_80ec4152-4322-4e5e-97d0-a1df2cb221bc8677078797236426043-202404251055527.jpg', '图片', '175.69 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (614, '2024-04-25 10:55:22.572977', '2024-04-25 10:55:22.572977', 'scaled_26235d9d-d6b2-4835-ae92-816770a639c737018727733137146-202404251055571.jpg', 'scaled_26235d9d-d6b2-4835-ae92-816770a639c737018727733137146.jpg', 'jpg', '/upload/scaled_26235d9d-d6b2-4835-ae92-816770a639c737018727733137146-202404251055571.jpg', '图片', '377.53 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (615, '2024-04-25 10:55:22.619864', '2024-04-25 10:55:22.619864', 'scaled_20c21e98-b1b5-4c39-a678-93c7441ac6a7916249026278213579-202404251055617.jpg', 'scaled_20c21e98-b1b5-4c39-a678-93c7441ac6a7916249026278213579.jpg', 'jpg', '/upload/scaled_20c21e98-b1b5-4c39-a678-93c7441ac6a7916249026278213579-202404251055617.jpg', '图片', '191.18 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (616, '2024-04-25 10:55:22.683391', '2024-04-25 10:55:22.683391', 'scaled_bb4b7df8-e00e-4b53-83af-982530ca484f951319868795526122-202404251055681.jpg', 'scaled_bb4b7df8-e00e-4b53-83af-982530ca484f951319868795526122.jpg', 'jpg', '/upload/scaled_bb4b7df8-e00e-4b53-83af-982530ca484f951319868795526122-202404251055681.jpg', '图片', '361.56 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (617, '2024-04-25 10:56:02.561298', '2024-04-25 10:56:02.561298', 'scaled_33deb9b0-ffc8-412a-b3f5-c0b20486eda42562558839662710540-202404251056559.jpg', 'scaled_33deb9b0-ffc8-412a-b3f5-c0b20486eda42562558839662710540.jpg', 'jpg', '/upload/scaled_33deb9b0-ffc8-412a-b3f5-c0b20486eda42562558839662710540-202404251056559.jpg', '图片', '185.1 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (618, '2024-04-25 10:56:02.588194', '2024-04-25 10:56:02.588194', 'scaled_59e2baa3-b2ca-4ce9-a529-3b1ca947687e7873488531297466878-202404251056586.jpg', 'scaled_59e2baa3-b2ca-4ce9-a529-3b1ca947687e7873488531297466878.jpg', 'jpg', '/upload/scaled_59e2baa3-b2ca-4ce9-a529-3b1ca947687e7873488531297466878-202404251056586.jpg', '图片', '190.23 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (619, '2024-04-25 10:56:02.609821', '2024-04-25 10:56:02.609821', 'scaled_57a2dab2-c47c-4a27-b728-e649e67399bd8604156090326485233-202404251056607.jpg', 'scaled_57a2dab2-c47c-4a27-b728-e649e67399bd8604156090326485233.jpg', 'jpg', '/upload/scaled_57a2dab2-c47c-4a27-b728-e649e67399bd8604156090326485233-202404251056607.jpg', '图片', '290.44 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (620, '2024-04-25 13:33:14.680392', '2024-04-25 13:33:14.680392', 'scaled_84d14e44-85fb-4f62-b8d6-530d21adda554899146327049230398-202404251333676.jpg', 'scaled_84d14e44-85fb-4f62-b8d6-530d21adda554899146327049230398.jpg', 'jpg', '/upload/scaled_84d14e44-85fb-4f62-b8d6-530d21adda554899146327049230398-202404251333676.jpg', '图片', '137.3 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (621, '2024-04-25 13:33:14.740928', '2024-04-25 13:33:14.740928', 'scaled_a0afe243-ca62-4be4-a1e3-96ad96908e451190955768137654381-202404251333739.jpg', 'scaled_a0afe243-ca62-4be4-a1e3-96ad96908e451190955768137654381.jpg', 'jpg', '/upload/scaled_a0afe243-ca62-4be4-a1e3-96ad96908e451190955768137654381-202404251333739.jpg', '图片', '343.02 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (622, '2024-04-25 18:16:52.769985', '2024-04-25 18:16:52.769985', 'scaled_201aafb1-f607-4e23-be4e-55f3265f73c83545534634870484985-202404251816766.jpg', 'scaled_201aafb1-f607-4e23-be4e-55f3265f73c83545534634870484985.jpg', 'jpg', '/upload/scaled_201aafb1-f607-4e23-be4e-55f3265f73c83545534634870484985-202404251816766.jpg', '图片', '218.37 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (623, '2024-04-25 18:16:52.835841', '2024-04-25 18:16:52.835841', 'scaled_6b3d3b67-800e-46e4-b329-684decff9b3d8965861724197310003-202404251816834.jpg', 'scaled_6b3d3b67-800e-46e4-b329-684decff9b3d8965861724197310003.jpg', 'jpg', '/upload/scaled_6b3d3b67-800e-46e4-b329-684decff9b3d8965861724197310003-202404251816834.jpg', '图片', '328.8 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (624, '2024-04-25 18:17:41.062307', '2024-04-25 18:17:41.062307', 'scaled_b72a6f1c-e67d-4329-8c4c-bffb2979d9406234270011895346930-202404251817061.jpg', 'scaled_b72a6f1c-e67d-4329-8c4c-bffb2979d9406234270011895346930.jpg', 'jpg', '/upload/scaled_b72a6f1c-e67d-4329-8c4c-bffb2979d9406234270011895346930-202404251817061.jpg', '图片', '111.09 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (625, '2024-04-25 18:17:41.164451', '2024-04-25 18:17:41.164451', 'scaled_264c265a-1199-4554-9bcc-be3a72cda54b8053232583901252266-202404251817162.jpg', 'scaled_264c265a-1199-4554-9bcc-be3a72cda54b8053232583901252266.jpg', 'jpg', '/upload/scaled_264c265a-1199-4554-9bcc-be3a72cda54b8053232583901252266-202404251817162.jpg', '图片', '404.76 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (626, '2024-04-25 18:18:15.169223', '2024-04-25 18:18:15.169223', 'scaled_6091d9fb-e25d-4f91-b2c8-8742bc701d2360308547817264786-202404251818167.jpg', 'scaled_6091d9fb-e25d-4f91-b2c8-8742bc701d2360308547817264786.jpg', 'jpg', '/upload/scaled_6091d9fb-e25d-4f91-b2c8-8742bc701d2360308547817264786-202404251818167.jpg', '图片', '389.23 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (627, '2024-04-25 18:18:15.338742', '2024-04-25 18:18:15.338742', 'scaled_972d32a8-884a-4ebf-a9de-487c2da659832258087753270890233-202404251818337.jpg', 'scaled_972d32a8-884a-4ebf-a9de-487c2da659832258087753270890233.jpg', 'jpg', '/upload/scaled_972d32a8-884a-4ebf-a9de-487c2da659832258087753270890233-202404251818337.jpg', '图片', '211.05 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (628, '2024-04-26 08:05:21.443284', '2024-04-26 08:05:21.443284', 'scaled_7cb9d738-6b51-4fe0-84c0-1167882e249a4465883671459059605-202404260805440.jpg', 'scaled_7cb9d738-6b51-4fe0-84c0-1167882e249a4465883671459059605.jpg', 'jpg', '/upload/scaled_7cb9d738-6b51-4fe0-84c0-1167882e249a4465883671459059605-202404260805440.jpg', '图片', '118.56 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (629, '2024-04-26 08:05:21.601483', '2024-04-26 08:05:21.601483', 'scaled_1bfdf871-acee-4743-bdb4-f72b138ed6bb2686512625996507059-202404260805599.jpg', 'scaled_1bfdf871-acee-4743-bdb4-f72b138ed6bb2686512625996507059.jpg', 'jpg', '/upload/scaled_1bfdf871-acee-4743-bdb4-f72b138ed6bb2686512625996507059-202404260805599.jpg', '图片', '312.62 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (630, '2024-04-26 08:05:56.315230', '2024-04-26 08:05:56.315230', 'scaled_897825f7-a5ae-40f2-a08c-a5615e8fb3485091336286324362357-202404260805312.jpg', 'scaled_897825f7-a5ae-40f2-a08c-a5615e8fb3485091336286324362357.jpg', 'jpg', '/upload/scaled_897825f7-a5ae-40f2-a08c-a5615e8fb3485091336286324362357-202404260805312.jpg', '图片', '169.97 KB', 9, '', NULL); +INSERT INTO `tool_storage` VALUES (631, '2024-04-26 08:05:56.316280', '2024-04-26 08:05:56.316280', 'scaled_456a8c88-1457-4e04-880b-74180164c8319166392390718299181-202404260805313.jpg', 'scaled_456a8c88-1457-4e04-880b-74180164c8319166392390718299181.jpg', 'jpg', '/upload/scaled_456a8c88-1457-4e04-880b-74180164c8319166392390718299181-202404260805313.jpg', '图片', '105.34 KB', 9, '', NULL); -- ---------------------------- -- Table structure for user_access_tokens @@ -977,30 +1820,19 @@ CREATE TABLE `user_access_tokens` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', `user_id` int NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_e9d9d0c303432e4e5e48c1c3e90`(`user_id`) USING BTREE, + INDEX `FK_e9d9d0c303432e4e5e48c1c3e90`(`user_id` ASC) USING BTREE, CONSTRAINT `FK_e9d9d0c303432e4e5e48c1c3e90` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of user_access_tokens -- ---------------------------- -INSERT INTO `user_access_tokens` VALUES ('067fa54e-e37d-45eb-850c-11fe6e580555', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTgyMTR9.KxpU61iQtg1j2zziHZB0gKGdvCiViIlTKkuY59EpD4s', '2024-04-08 10:50:14', '2024-04-07 10:50:14.399413', 1); -INSERT INTO `user_access_tokens` VALUES ('06fa487c-c9f4-4dcc-8643-ef6146e42add', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODc5Mn0.mnEZ_Re_83_6NSgAf_E5Rp2akIOVsQQ_tQCgdk1QhZ0', '2024-04-08 10:59:53', '2024-04-07 10:59:52.570969', 9); -INSERT INTO `user_access_tokens` VALUES ('4b231512-42ac-41f6-9acc-9b3ac9590f12', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTY3MTd9._1XC-bHE_X0vWiCvexC5tXRIiZ5iwh6YX6DJO31KROk', '2024-04-08 10:25:18', '2024-04-07 10:25:17.962621', 1); -INSERT INTO `user_access_tokens` VALUES ('50be25ab-05a0-468f-919c-ec623ac41761', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0MTc2MjB9.otSnlbtKFKqoJUD5e0wM25Nxp6qjXs0r9FXnSO9zdOw', '2024-04-07 23:33:40', '2024-04-06 23:33:40.021878', 1); -INSERT INTO `user_access_tokens` VALUES ('64b831b0-32a9-4fad-ae3f-9f8f370f3bf2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODI3NH0.pmdigNgdbtUHrktkP8t4DhaxTKnBPogHUVdC5XEXSA4', '2024-04-08 10:51:14', '2024-04-07 10:51:14.116966', 9); -INSERT INTO `user_access_tokens` VALUES ('657d245b-b61f-4f84-9bb8-3358ad645546', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJ1c2VyIiwiSW52ZW50b3J5TWFuYWdlciJdLCJpYXQiOjE3MTI0NTgxOTR9.BY5SGgYS7TjfVeCtRxVzgMZQq9TN9bMiOjDCoNwAcF0', '2024-04-08 10:49:55', '2024-04-07 10:49:54.981842', 9); -INSERT INTO `user_access_tokens` VALUES ('841bd4fc-b3e5-4864-ad4e-6849261e5806', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJ1c2VyIiwiSW52ZW50b3J5TWFuYWdlciJdLCJpYXQiOjE3MTI0NTgxMzl9.fWCim-wvmY8b8HogIlThhsAeFf5oWYqT-evVpwv7-pc', '2024-04-08 10:48:59', '2024-04-07 10:48:59.360940', 9); -INSERT INTO `user_access_tokens` VALUES ('96ae5545-c90f-452a-bae7-377e8cf32169', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODcyOH0.RuLjO4hFD_y1CoCQDB3KZJYS-24AWP1Q2vpEo_mVPxM', '2024-04-08 10:58:48', '2024-04-07 10:58:48.182856', 9); -INSERT INTO `user_access_tokens` VALUES ('a35d093a-c004-459c-a080-c0abc346a115', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTg5MTl9.JL6Xjpun6vX5XbruGhXLSevvO7AYLstCHSn_-z48ll8', '2024-04-08 11:02:00', '2024-04-07 11:01:59.632272', 1); -INSERT INTO `user_access_tokens` VALUES ('aa9787ae-7565-4d09-a3cb-b216947cfcab', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODQwNX0.2CyoDt_tIsAzri02mhCKbVJEss-Ny2qdmZOPoZKUuxQ', '2024-04-08 10:53:25', '2024-04-07 10:53:25.379015', 9); -INSERT INTO `user_access_tokens` VALUES ('b6f1ce31-bf6b-4dc4-80eb-3ab37774e179', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTgzNDF9.bAvlmbaHNnL1_-ar9yVZI3Z_3Nm0qMF9MFN1bO29-rM', '2024-04-08 10:52:21', '2024-04-07 10:52:21.439166', 1); -INSERT INTO `user_access_tokens` VALUES ('c9e94b55-dd93-4ea1-aee1-c81d59a9e1bc', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODgzOX0.ShvQOexZc7zfP_NsprmVjb0aXDqurp5_6Bsed0oJsq8', '2024-04-08 11:00:40', '2024-04-07 11:00:39.515606', 9); -INSERT INTO `user_access_tokens` VALUES ('c9ee2211-fa30-4a74-9e96-1622e959c5a5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTkyNTB9.pX3TLszlGjQDyebNEXunGN6vwStARGHoOSLpULHfXWU', '2024-04-08 11:07:30', '2024-04-07 11:07:30.021625', 1); -INSERT INTO `user_access_tokens` VALUES ('cdb64fe0-ca48-466b-9caa-5812aa7a8634', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTc4MDZ9.jJ2SEjgLfYBJe80tV3CpD2WMMID8iAlCV_iJaaL6IXE', '2024-04-08 10:43:26', '2024-04-07 10:43:26.334882', 1); -INSERT INTO `user_access_tokens` VALUES ('d78ecadb-7bfd-4c65-a8c4-05e9473a80a5', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjksInB2IjoxLCJyb2xlcyI6WyJJbnZlbnRvcnlNYW5hZ2VyIl0sImlhdCI6MTcxMjQ1ODc1Nn0.HaB3ABe7S6Iik9lBEIa1ww_-QuxKsZOLkB8e4evcQ58', '2024-04-08 10:59:16', '2024-04-07 10:59:16.310195', 9); -INSERT INTO `user_access_tokens` VALUES ('d9f221ea-111d-47fe-932d-4123174dfce8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTg2ODl9.N4Q7H8KnGd-hNmO-2UECuDfBFuuthrTPdATxDBcuWTI', '2024-04-08 10:58:10', '2024-04-07 10:58:09.590782', 1); -INSERT INTO `user_access_tokens` VALUES ('f886261e-bb1d-45db-83a4-71ef0f2a9180', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTI0NTg0NTF9.Uf2OXdWp1UgBHHw4dvLJcEQtv_09VoBAAsDkMmZ10Lo', '2024-04-08 10:54:11', '2024-04-07 10:54:11.390379', 1); +INSERT INTO `user_access_tokens` VALUES ('474dbe03-adb0-41f4-9dab-2e35d1734705', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTQwOTQzNTJ9.BUbqoWqkw5GORWW0GQbtn9rAFglE5212cHVZOJM-fsE', '2024-04-27 09:19:13', '2024-04-26 09:19:12.767299', 1); +INSERT INTO `user_access_tokens` VALUES ('5d962c46-0167-4e07-bbfa-541a63f764c6', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTQwMTE2ODV9.2IhkVSDPalNKyG6HhHxoYi2w7hhKqPcuXbo6jjMsyCA', '2024-04-26 10:21:25', '2024-04-25 10:21:25.428347', 1); +INSERT INTO `user_access_tokens` VALUES ('71d63ec5-b281-43bd-bfdc-e2d4d0bcc145', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTQwMTEzODZ9.VTrUKvDqhvUb2oXm_6xpH1ie5_jOJEqO_OoxKTuA0OQ', '2024-04-26 10:16:26', '2024-04-25 10:16:26.176497', 1); +INSERT INTO `user_access_tokens` VALUES ('841fc0a7-ff1c-436f-b76e-a608e06433c4', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTQwMTEzOTN9.SkulT6BzgYnMBiVKnivRr18P1ZodG1wlNVJRjM0F3iY', '2024-04-26 10:16:34', '2024-04-25 10:16:33.640361', 1); +INSERT INTO `user_access_tokens` VALUES ('b3727978-c0d6-4b2f-a8d8-ece1afb171bc', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTQwOTIzMzd9.PVkqb6tO4_8VOaexUygTGNLXpa8eQgcfklAj173HXq4', '2024-04-27 08:45:37', '2024-04-26 08:45:37.261586', 1); +INSERT INTO `user_access_tokens` VALUES ('e8e4de38-f928-4b8c-87d7-83a669dc56ed', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInB2IjoxLCJyb2xlcyI6WyJhZG1pbiJdLCJpYXQiOjE3MTQwOTE4MDh9.1Gu3duyvdESNTwyrGpuSphAD7gzbm8lhVH3x-jI4EM0', '2024-04-27 08:36:49', '2024-04-26 08:36:48.920428', 1); -- ---------------------------- -- Table structure for user_refresh_tokens @@ -1013,30 +1845,19 @@ CREATE TABLE `user_refresh_tokens` ( `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '令牌创建时间', `accessTokenId` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `REL_1dfd080c2abf42198691b60ae3`(`accessTokenId`) USING BTREE, + UNIQUE INDEX `REL_1dfd080c2abf42198691b60ae3`(`accessTokenId` ASC) USING BTREE, CONSTRAINT `FK_1dfd080c2abf42198691b60ae39` FOREIGN KEY (`accessTokenId`) REFERENCES `user_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of user_refresh_tokens -- ---------------------------- -INSERT INTO `user_refresh_tokens` VALUES ('0aacf8f4-720a-4506-b6da-bf80ad7507ad', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidGFNWHY3SzNIcnpGN0hPVm0wNTlZIiwiaWF0IjoxNzEyNDU5MjUwfQ.VuYA4gG5Cbuc6hSYAduwhi7t0qNmcHaSihF43dGnl_s', '2024-05-07 11:07:30', '2024-04-07 11:07:30.035958', 'c9ee2211-fa30-4a74-9e96-1622e959c5a5'); -INSERT INTO `user_refresh_tokens` VALUES ('1bbfc866-7019-4244-8fc5-ce3573c18f5a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZFUtMUZlS3lCNzZDY19NQ0JCTlVGIiwiaWF0IjoxNzEyNDU4ODM5fQ.hgp34zbgIzzU7nTYxjY-nqi44SeJzmAusZEFoBPFDxk', '2024-05-07 11:00:40', '2024-04-07 11:00:39.541546', 'c9e94b55-dd93-4ea1-aee1-c81d59a9e1bc'); -INSERT INTO `user_refresh_tokens` VALUES ('325d7565-b020-4de0-9c99-d645075678be', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTDh1UV9XWEg3cXU5S2pRbUZGRWh3IiwiaWF0IjoxNzEyNDU4MjE0fQ.WHiXKLlA9p-Bjwud3EJ9sie1cpr5MImYuE7Vy3cwLjQ', '2024-05-07 10:50:14', '2024-04-07 10:50:14.409485', '067fa54e-e37d-45eb-850c-11fe6e580555'); -INSERT INTO `user_refresh_tokens` VALUES ('468da5b2-f67f-4d70-b043-7e479dc8d6a9', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoidXJCYUJKMkd0a0VfZGdOOC1hemh4IiwiaWF0IjoxNzEyNDU4OTE5fQ.Y2UvFoYF2i2rhzH12T_mggSi27pGk1jpX3WxqlRD6ho', '2024-05-07 11:02:00', '2024-04-07 11:01:59.643833', 'a35d093a-c004-459c-a080-c0abc346a115'); -INSERT INTO `user_refresh_tokens` VALUES ('4cd90a2b-ced3-455c-83a1-0b21b6fd5d8c', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVnRqaHlEZnA2NURlMXh6WkxOYVl4IiwiaWF0IjoxNzEyNDU4MzQxfQ.RilIoFnJgeh-ze5Cy_ODvebySQyxIjJyIAoPB8S69Wc', '2024-05-07 10:52:21', '2024-04-07 10:52:21.451515', 'b6f1ce31-bf6b-4dc4-80eb-3ab37774e179'); -INSERT INTO `user_refresh_tokens` VALUES ('51c7573f-2d21-4206-a242-906d764a874d', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTGROUjJ0blBZR1pEZXl4YTd6RXNGIiwiaWF0IjoxNzEyNDE3NjIwfQ.60gygI_tNscvCHdFI64XwRh9WUEWoFjscg-Q6Sl7EV8', '2024-05-06 23:33:40', '2024-04-06 23:33:40.032302', '50be25ab-05a0-468f-919c-ec623ac41761'); -INSERT INTO `user_refresh_tokens` VALUES ('7827a7fe-a694-4723-b11e-fd1fd542e81b', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZDV6MXo5MjdMZ1V1bXJTUmY0WEloIiwiaWF0IjoxNzEyNDU4NzI4fQ.ViiYJc3DmYug_JbpcT4D6a65LVj23ebSkHuxWipFQkE', '2024-05-07 10:58:48', '2024-04-07 10:58:48.193052', '96ae5545-c90f-452a-bae7-377e8cf32169'); -INSERT INTO `user_refresh_tokens` VALUES ('7c504748-e2e6-41b6-a4b6-affc8bd8de72', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiajd1WmgxRjFmMnNqNWQ5Z0RienBwIiwiaWF0IjoxNzEyNDU4MTk0fQ.0BaG_gy41z-7y9EpZK05RV2-OgkDFHMEtJ9ofuVMLPs', '2024-05-07 10:49:55', '2024-04-07 10:49:54.993642', '657d245b-b61f-4f84-9bb8-3358ad645546'); -INSERT INTO `user_refresh_tokens` VALUES ('81c1b063-256a-4337-87db-e7bb01a9c793', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiOThjSTdkSkJQZGdFZ3lJQjJRODI3IiwiaWF0IjoxNzEyNDU4MTM5fQ.fpDITw5fInmn4_fXde-O-i-SgV_9-lGrAqk5Rq-v1VY', '2024-05-07 10:48:59', '2024-04-07 10:48:59.389990', '841bd4fc-b3e5-4864-ad4e-6849261e5806'); -INSERT INTO `user_refresh_tokens` VALUES ('879409aa-4e60-491f-b155-cb4433832ae8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiUnJBMFRQbGtnckpIRTNUVG83QjRPIiwiaWF0IjoxNzEyNDU4NzkyfQ.YFufOp1ytb6ZyzaMFscf972VMIaPb09GsG9Z0FBp7R8', '2024-05-07 10:59:53', '2024-04-07 10:59:52.580656', '06fa487c-c9f4-4dcc-8643-ef6146e42add'); -INSERT INTO `user_refresh_tokens` VALUES ('89b9df53-5d1b-4640-878c-821db4ddafe1', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiVkJlOEZ5OHJ3Um1HQkxfMVRNQzNlIiwiaWF0IjoxNzEyNDU4Njg5fQ.SwXWgzeLNKAvQU39SHrQYrGSQmYWAaAjOF0WVNveHGc', '2024-05-07 10:58:10', '2024-04-07 10:58:09.604051', 'd9f221ea-111d-47fe-932d-4123174dfce8'); -INSERT INTO `user_refresh_tokens` VALUES ('8ba5e525-a9eb-4823-b41e-ef9fb04624a2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiSVY5bW1WRDRzY0xwaE00ek90ZmRaIiwiaWF0IjoxNzEyNDU3ODA2fQ.UKJaAJvXJJg96almY9QNb8mrjyBPewQvw5ECgZGjRpI', '2024-05-07 10:43:26', '2024-04-07 10:43:26.350399', 'cdb64fe0-ca48-466b-9caa-5812aa7a8634'); -INSERT INTO `user_refresh_tokens` VALUES ('ab50f093-429b-4fb3-b1d4-f91e3205419a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiT0IwbzVHVFkxazNERVF0eFU4N0RGIiwiaWF0IjoxNzEyNDU4NzU2fQ.I9miRO1z2X_Uhf8QJxcjk6gKOisvrVISMZY3nqxUE1g', '2024-05-07 10:59:16', '2024-04-07 10:59:16.320141', 'd78ecadb-7bfd-4c65-a8c4-05e9473a80a5'); -INSERT INTO `user_refresh_tokens` VALUES ('b5913f11-9132-4342-a5fc-1823b4f8095e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRHo3cmRRck5nYkRGYTNra3lYZkY4IiwiaWF0IjoxNzEyNDU4NDUxfQ.ITzqgmr_hZo7yoaKeoYa7DCV51EHGSSW95aJOgHASqc', '2024-05-07 10:54:11', '2024-04-07 10:54:11.402462', 'f886261e-bb1d-45db-83a4-71ef0f2a9180'); -INSERT INTO `user_refresh_tokens` VALUES ('c996e647-0b40-4271-89a4-18c6fa361648', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoibTd5VkxYUDRSWndNYTRBOE5qTk9CIiwiaWF0IjoxNzEyNDU4NDA1fQ.ImGnCiah6WX6tHltPV-xphw7i-CHP2IU-BwOElc2uy4', '2024-05-07 10:53:25', '2024-04-07 10:53:25.390342', 'aa9787ae-7565-4d09-a3cb-b216947cfcab'); -INSERT INTO `user_refresh_tokens` VALUES ('ce1d039d-ed44-41f6-9228-06720e288088', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoicVZPU0xfd3hwRnZjRi1fNVdwVGZqIiwiaWF0IjoxNzEyNDU2NzE3fQ.u7EV8m_sg7svflMYEitqvkOgXnOlKG-pLjD6fQ4Hnc8', '2024-05-07 10:25:18', '2024-04-07 10:25:17.983231', '4b231512-42ac-41f6-9acc-9b3ac9590f12'); -INSERT INTO `user_refresh_tokens` VALUES ('ff56df48-89bb-46b3-a8d2-80426dba6e43', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoialZYS3JNUVZUMC1PbDJYMXJBZmE1IiwiaWF0IjoxNzEyNDU4Mjc0fQ.l24dDsJs64KhV9SPzltTGl-8o75aqYXTczGtgieJ8H8', '2024-05-07 10:51:14', '2024-04-07 10:51:14.126506', '64b831b0-32a9-4fad-ae3f-9f8f370f3bf2'); +INSERT INTO `user_refresh_tokens` VALUES ('07923292-ba87-454c-a608-2584c1a3bce8', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiM0xtSEo0SERkNEtPYzF1anZndDZhIiwiaWF0IjoxNzE0MDExMzkzfQ.CttZxXRugLTzHUwlO2RNxbh5gz6G9VW9Uy15Uhm1sA4', '2024-05-25 10:16:34', '2024-04-25 10:16:33.656897', '841fc0a7-ff1c-436f-b76e-a608e06433c4'); +INSERT INTO `user_refresh_tokens` VALUES ('5c22399f-d9f0-41c2-ac80-3e713ec3f48e', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiQUo4blNfWnVhZ0dmUlNMMTZNUXo4IiwiaWF0IjoxNzE0MDExMzg2fQ.l-TMgZTg80AEofP0nQnx42jPfT3qCYNLl6phHZNnjOM', '2024-05-25 10:16:26', '2024-04-25 10:16:26.191654', '71d63ec5-b281-43bd-bfdc-e2d4d0bcc145'); +INSERT INTO `user_refresh_tokens` VALUES ('9b6f5c98-7834-47c5-9df7-89709732f387', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiRTRURmdPcDNBV21WM24tMlZDYnZtIiwiaWF0IjoxNzE0MDExNjg1fQ.55LLrldyS5To7Df9Hqn4jTejMDwPdv0sb1iWSVShSTQ', '2024-05-25 10:21:25', '2024-04-25 10:21:25.443755', '5d962c46-0167-4e07-bbfa-541a63f764c6'); +INSERT INTO `user_refresh_tokens` VALUES ('a14b37d7-f150-4e76-8dd2-6017d5b4329a', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiTWRUMW9zbVFoc2JyRkNDSkREZlhjIiwiaWF0IjoxNzE0MDkxODA4fQ.kmR0yYrUMToTgSaxk8HeyXUbjBkftl4WDXcRk0R6DDY', '2024-05-26 08:36:49', '2024-04-26 08:36:48.935183', 'e8e4de38-f928-4b8c-87d7-83a669dc56ed'); +INSERT INTO `user_refresh_tokens` VALUES ('bddf5ca8-fa5e-4860-b29c-038e748e37aa', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiZlZXTUdPVTNnWko3QXhTaWgtQnB3IiwiaWF0IjoxNzE0MDkyMzM3fQ.tPXA0yeMauAtm9nilEyaE_yf4kpVVrSGQzci02lAUwE', '2024-05-26 08:45:37', '2024-04-26 08:45:37.277182', 'b3727978-c0d6-4b2f-a8d8-ece1afb171bc'); +INSERT INTO `user_refresh_tokens` VALUES ('bf02ef0c-8d2f-48a3-8706-1dc896ad3da2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWlkIjoiclZ5dVVHRjFqbS1Ca1NHREpvdUpYIiwiaWF0IjoxNzE0MDk0MzUyfQ.OdmsqJMP0dWMeW6HTEIcoK86iNdwnAov2yus8Crk1bw', '2024-05-26 09:19:13', '2024-04-26 09:19:12.785634', '474dbe03-adb0-41f4-9dab-2e35d1734705'); -- ---------------------------- -- Table structure for vehicle_usage @@ -1061,7 +1882,7 @@ CREATE TABLE `vehicle_usage` ( `return_mileage` int NULL DEFAULT NULL COMMENT '回城车辆里程数(KM)', `partner` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '随行人员', PRIMARY KEY (`id`) USING BTREE, - INDEX `FK_6aff0ec40ff474e6228c1125f5c`(`vehicle_id`) USING BTREE, + INDEX `FK_6aff0ec40ff474e6228c1125f5c`(`vehicle_id` ASC) USING BTREE, CONSTRAINT `FK_6aff0ec40ff474e6228c1125f5c` FOREIGN KEY (`vehicle_id`) REFERENCES `sys_dict_item` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; @@ -1077,8 +1898,8 @@ CREATE TABLE `vehicle_usage_storage` ( `vehicle_id` int NOT NULL, `file_id` int NOT NULL, PRIMARY KEY (`vehicle_id`, `file_id`) USING BTREE, - INDEX `IDX_1d122393de1ee773c383569e71`(`vehicle_id`) USING BTREE, - INDEX `IDX_a8cbcb6835a9212dd2a49b50ed`(`file_id`) USING BTREE, + INDEX `IDX_1d122393de1ee773c383569e71`(`vehicle_id` ASC) USING BTREE, + INDEX `IDX_a8cbcb6835a9212dd2a49b50ed`(`file_id` ASC) USING BTREE, CONSTRAINT `FK_1d122393de1ee773c383569e717` FOREIGN KEY (`vehicle_id`) REFERENCES `vehicle_usage` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_a8cbcb6835a9212dd2a49b50ed9` FOREIGN KEY (`file_id`) REFERENCES `tool_storage` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;