create-aiko 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +7 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +174 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/scaffold.d.ts +7 -0
- package/dist/scaffold.js +310 -0
- package/package.json +37 -0
- package/template/README.md +63 -0
- package/template/docs/better-sqlite3-design.md +101 -0
- package/template/docs/shared-vs-shared-auth.md +54 -0
- package/template/package.json +23 -0
- package/template/packages/admin/.env.dev +1 -0
- package/template/packages/admin/.env.prod +4 -0
- package/template/packages/admin/.env.stage +4 -0
- package/template/packages/admin/Dockerfile +37 -0
- package/template/packages/admin/README.MD +27 -0
- package/template/packages/admin/components.json +21 -0
- package/template/packages/admin/eslint.config.js +28 -0
- package/template/packages/admin/index.html +50 -0
- package/template/packages/admin/package.json +100 -0
- package/template/packages/admin/public/vite.svg +1 -0
- package/template/packages/admin/src/App.css +82 -0
- package/template/packages/admin/src/App.tsx +128 -0
- package/template/packages/admin/src/app.config.ts +3 -0
- package/template/packages/admin/src/components/admin-ui/approval-flow.tsx +205 -0
- package/template/packages/admin/src/components/admin-ui/data-table/data-table-pagination.tsx +148 -0
- package/template/packages/admin/src/components/admin-ui/data-table/data-table-sorter.tsx +50 -0
- package/template/packages/admin/src/components/admin-ui/data-table/index.tsx +296 -0
- package/template/packages/admin/src/components/admin-ui/editable-table.tsx +292 -0
- package/template/packages/admin/src/components/admin-ui/form/input-password.tsx +38 -0
- package/template/packages/admin/src/components/admin-ui/form/sign-in-form.tsx +104 -0
- package/template/packages/admin/src/components/admin-ui/layout/error-component.tsx +71 -0
- package/template/packages/admin/src/components/admin-ui/layout/header.tsx +148 -0
- package/template/packages/admin/src/components/admin-ui/layout/language-switcher.tsx +47 -0
- package/template/packages/admin/src/components/admin-ui/layout/layout.tsx +42 -0
- package/template/packages/admin/src/components/admin-ui/layout/loading-overlay.tsx +36 -0
- package/template/packages/admin/src/components/admin-ui/layout/shell-bar.tsx +232 -0
- package/template/packages/admin/src/components/admin-ui/layout/sidebar.tsx +193 -0
- package/template/packages/admin/src/components/admin-ui/layout/user-avatar.tsx +31 -0
- package/template/packages/admin/src/components/admin-ui/list-report.tsx +313 -0
- package/template/packages/admin/src/components/admin-ui/master-detail.tsx +382 -0
- package/template/packages/admin/src/components/admin-ui/notification/toaster.tsx +23 -0
- package/template/packages/admin/src/components/admin-ui/notification/undoable-notification.tsx +84 -0
- package/template/packages/admin/src/components/admin-ui/object-page.tsx +539 -0
- package/template/packages/admin/src/components/admin-ui/process-stepper.tsx +204 -0
- package/template/packages/admin/src/components/admin-ui/theme/theme-provider.tsx +160 -0
- package/template/packages/admin/src/components/admin-ui/theme/theme-select.tsx +129 -0
- package/template/packages/admin/src/components/admin-ui/theme/theme-toggle.tsx +90 -0
- package/template/packages/admin/src/components/admin-ui/timeline.tsx +137 -0
- package/template/packages/admin/src/components/admin-ui/tree-navigator.tsx +243 -0
- package/template/packages/admin/src/components/ui/accordion.tsx +64 -0
- package/template/packages/admin/src/components/ui/alert-dialog.tsx +157 -0
- package/template/packages/admin/src/components/ui/alert.tsx +66 -0
- package/template/packages/admin/src/components/ui/aspect-ratio.tsx +9 -0
- package/template/packages/admin/src/components/ui/avatar.tsx +53 -0
- package/template/packages/admin/src/components/ui/badge.tsx +47 -0
- package/template/packages/admin/src/components/ui/breadcrumb.tsx +111 -0
- package/template/packages/admin/src/components/ui/button.tsx +59 -0
- package/template/packages/admin/src/components/ui/calendar.tsx +74 -0
- package/template/packages/admin/src/components/ui/card.tsx +92 -0
- package/template/packages/admin/src/components/ui/carousel.tsx +237 -0
- package/template/packages/admin/src/components/ui/chart.tsx +351 -0
- package/template/packages/admin/src/components/ui/checkbox.tsx +32 -0
- package/template/packages/admin/src/components/ui/collapsible.tsx +33 -0
- package/template/packages/admin/src/components/ui/command.tsx +182 -0
- package/template/packages/admin/src/components/ui/context-menu.tsx +252 -0
- package/template/packages/admin/src/components/ui/dialog.tsx +141 -0
- package/template/packages/admin/src/components/ui/drawer.tsx +130 -0
- package/template/packages/admin/src/components/ui/dropdown-menu.tsx +255 -0
- package/template/packages/admin/src/components/ui/form.tsx +166 -0
- package/template/packages/admin/src/components/ui/hover-card.tsx +42 -0
- package/template/packages/admin/src/components/ui/input-otp.tsx +77 -0
- package/template/packages/admin/src/components/ui/input.tsx +21 -0
- package/template/packages/admin/src/components/ui/label.tsx +22 -0
- package/template/packages/admin/src/components/ui/menubar.tsx +274 -0
- package/template/packages/admin/src/components/ui/navigation-menu.tsx +168 -0
- package/template/packages/admin/src/components/ui/pagination.tsx +127 -0
- package/template/packages/admin/src/components/ui/popover.tsx +48 -0
- package/template/packages/admin/src/components/ui/progress.tsx +29 -0
- package/template/packages/admin/src/components/ui/radio-group.tsx +45 -0
- package/template/packages/admin/src/components/ui/resizable.tsx +54 -0
- package/template/packages/admin/src/components/ui/scroll-area.tsx +58 -0
- package/template/packages/admin/src/components/ui/select.tsx +183 -0
- package/template/packages/admin/src/components/ui/separator.tsx +26 -0
- package/template/packages/admin/src/components/ui/sheet.tsx +139 -0
- package/template/packages/admin/src/components/ui/sidebar.tsx +740 -0
- package/template/packages/admin/src/components/ui/skeleton.tsx +13 -0
- package/template/packages/admin/src/components/ui/slider.tsx +63 -0
- package/template/packages/admin/src/components/ui/sonner.tsx +23 -0
- package/template/packages/admin/src/components/ui/switch.tsx +31 -0
- package/template/packages/admin/src/components/ui/table.tsx +114 -0
- package/template/packages/admin/src/components/ui/tabs.tsx +66 -0
- package/template/packages/admin/src/components/ui/textarea.tsx +18 -0
- package/template/packages/admin/src/components/ui/toggle-group.tsx +73 -0
- package/template/packages/admin/src/components/ui/toggle.tsx +45 -0
- package/template/packages/admin/src/components/ui/tooltip.tsx +59 -0
- package/template/packages/admin/src/hooks/use-mobile.ts +21 -0
- package/template/packages/admin/src/i18n.ts +20 -0
- package/template/packages/admin/src/index.tsx +18 -0
- package/template/packages/admin/src/layouts/menu-layout.tsx +211 -0
- package/template/packages/admin/src/layouts/tile-layout.tsx +355 -0
- package/template/packages/admin/src/lib/utils.ts +6 -0
- package/template/packages/admin/src/locales/en.json +68 -0
- package/template/packages/admin/src/locales/zh.json +68 -0
- package/template/packages/admin/src/pages/dashboard.tsx +12 -0
- package/template/packages/admin/src/pages/goods-receipt/CreatePage.tsx +302 -0
- package/template/packages/admin/src/pages/goods-receipt/EditPage.tsx +221 -0
- package/template/packages/admin/src/pages/goods-receipt/ListPage.tsx +283 -0
- package/template/packages/admin/src/pages/goods-receipt/ViewPage.tsx +280 -0
- package/template/packages/admin/src/pages/goods-receipt/index.ts +4 -0
- package/template/packages/admin/src/pages/home-page.tsx +244 -0
- package/template/packages/admin/src/pages/login-page.tsx +91 -0
- package/template/packages/admin/src/pages/master-data/cost-centers/index.tsx +461 -0
- package/template/packages/admin/src/pages/master-data/currencies/index.tsx +255 -0
- package/template/packages/admin/src/pages/master-data/materials/ListPage.tsx +271 -0
- package/template/packages/admin/src/pages/master-data/materials/ViewPage.tsx +240 -0
- package/template/packages/admin/src/pages/master-data/materials/index.ts +2 -0
- package/template/packages/admin/src/pages/master-data/plants/ListPage.tsx +279 -0
- package/template/packages/admin/src/pages/master-data/plants/ViewPage.tsx +380 -0
- package/template/packages/admin/src/pages/master-data/plants/index.ts +2 -0
- package/template/packages/admin/src/pages/master-data/purchase-organizations/index.tsx +341 -0
- package/template/packages/admin/src/pages/master-data/units-of-measure/index.tsx +295 -0
- package/template/packages/admin/src/pages/master-data/vendors/ListPage.tsx +266 -0
- package/template/packages/admin/src/pages/master-data/vendors/ViewPage.tsx +274 -0
- package/template/packages/admin/src/pages/master-data/vendors/index.ts +2 -0
- package/template/packages/admin/src/pages/placeholder-page.tsx +13 -0
- package/template/packages/admin/src/pages/purchase-orders/ListPage.tsx +289 -0
- package/template/packages/admin/src/pages/purchase-orders/ViewPage.tsx +343 -0
- package/template/packages/admin/src/pages/purchase-orders/index.ts +2 -0
- package/template/packages/admin/src/pages/purchase-requisitions/CreatePage.tsx +398 -0
- package/template/packages/admin/src/pages/purchase-requisitions/EditPage.tsx +473 -0
- package/template/packages/admin/src/pages/purchase-requisitions/ListPage.tsx +307 -0
- package/template/packages/admin/src/pages/purchase-requisitions/ViewPage.tsx +304 -0
- package/template/packages/admin/src/pages/purchase-requisitions/constants.ts +51 -0
- package/template/packages/admin/src/pages/purchase-requisitions/index.ts +4 -0
- package/template/packages/admin/src/pages/reports/PurchaseOrderReport.tsx +312 -0
- package/template/packages/admin/src/pages/reports/PurchaseRequisitionReport.tsx +303 -0
- package/template/packages/admin/src/pages/reports/index.ts +2 -0
- package/template/packages/admin/src/pages/settings-page.tsx +335 -0
- package/template/packages/admin/src/providers/app-config.tsx +50 -0
- package/template/packages/admin/src/routes/auth.ts +6 -0
- package/template/packages/admin/src/routes/index.ts +85 -0
- package/template/packages/admin/src/routes/menu.ts +176 -0
- package/template/packages/admin/src/routes/modules/goods-receipt.ts +31 -0
- package/template/packages/admin/src/routes/modules/master-data.ts +41 -0
- package/template/packages/admin/src/routes/modules/purchase-orders.ts +27 -0
- package/template/packages/admin/src/routes/modules/purchase-requisitions.ts +39 -0
- package/template/packages/admin/src/routes/modules/reports.ts +33 -0
- package/template/packages/admin/src/routes/modules/settings.ts +19 -0
- package/template/packages/admin/src/routes/withSuspense.tsx +21 -0
- package/template/packages/admin/src/theme/amber.css +27 -0
- package/template/packages/admin/src/theme/blue.css +27 -0
- package/template/packages/admin/src/theme/default.css +75 -0
- package/template/packages/admin/src/theme/fiori.css +180 -0
- package/template/packages/admin/src/theme/green.css +27 -0
- package/template/packages/admin/src/theme/index.css +12 -0
- package/template/packages/admin/src/theme/rose.css +27 -0
- package/template/packages/admin/src/theme/violet.css +27 -0
- package/template/packages/admin/src/vite-env.d.ts +1 -0
- package/template/packages/admin/tsconfig.json +28 -0
- package/template/packages/admin/tsconfig.node.json +21 -0
- package/template/packages/admin/vite.config.ts +26 -0
- package/template/packages/api/.eslintrc.json +6 -0
- package/template/packages/api/.swcrc +17 -0
- package/template/packages/api/app.config.ts +160 -0
- package/template/packages/api/docs/api-document.md +497 -0
- package/template/packages/api/examples/security/README.md +664 -0
- package/template/packages/api/examples/security/complete/.env.example +26 -0
- package/template/packages/api/examples/security/complete/PROJECT_STRUCTURE.md +220 -0
- package/template/packages/api/examples/security/complete/README.md +847 -0
- package/template/packages/api/examples/security/complete/app.config.ts +69 -0
- package/template/packages/api/examples/security/complete/app.ts +63 -0
- package/template/packages/api/examples/security/complete/controller/auth.controller.ts +131 -0
- package/template/packages/api/examples/security/complete/controller/index.ts +4 -0
- package/template/packages/api/examples/security/complete/controller/permission.controller.ts +41 -0
- package/template/packages/api/examples/security/complete/controller/role.controller.ts +53 -0
- package/template/packages/api/examples/security/complete/controller/user.controller.ts +53 -0
- package/template/packages/api/examples/security/complete/dto/change-password.dto.ts +10 -0
- package/template/packages/api/examples/security/complete/dto/create-permission.dto.ts +14 -0
- package/template/packages/api/examples/security/complete/dto/create-role.dto.ts +11 -0
- package/template/packages/api/examples/security/complete/dto/create-user.dto.ts +15 -0
- package/template/packages/api/examples/security/complete/dto/index.ts +7 -0
- package/template/packages/api/examples/security/complete/dto/login.dto.ts +10 -0
- package/template/packages/api/examples/security/complete/dto/oauth-profile.dto.ts +7 -0
- package/template/packages/api/examples/security/complete/dto/register.dto.ts +17 -0
- package/template/packages/api/examples/security/complete/entity/index.ts +6 -0
- package/template/packages/api/examples/security/complete/entity/oauth-account.entity.ts +39 -0
- package/template/packages/api/examples/security/complete/entity/permission.entity.ts +31 -0
- package/template/packages/api/examples/security/complete/entity/role-permission.entity.ts +19 -0
- package/template/packages/api/examples/security/complete/entity/role.entity.ts +25 -0
- package/template/packages/api/examples/security/complete/entity/user-role.entity.ts +19 -0
- package/template/packages/api/examples/security/complete/entity/user.entity.ts +46 -0
- package/template/packages/api/examples/security/complete/init.sql +81 -0
- package/template/packages/api/examples/security/complete/middleware/auth.interceptor.ts +39 -0
- package/template/packages/api/examples/security/complete/middleware/index.ts +2 -0
- package/template/packages/api/examples/security/complete/middleware/permission.interceptor.ts +61 -0
- package/template/packages/api/examples/security/complete/package.json +54 -0
- package/template/packages/api/examples/security/complete/seed.sql +42 -0
- package/template/packages/api/examples/security/complete/service/auth.service.ts +41 -0
- package/template/packages/api/examples/security/complete/service/index.ts +5 -0
- package/template/packages/api/examples/security/complete/service/oauth.service.ts +82 -0
- package/template/packages/api/examples/security/complete/service/permission.service.ts +113 -0
- package/template/packages/api/examples/security/complete/service/role.service.ts +85 -0
- package/template/packages/api/examples/security/complete/service/user.service.ts +132 -0
- package/template/packages/api/examples/security/complete/tests/TEST_REPORT.md +318 -0
- package/template/packages/api/examples/security/complete/tests/generate-report.js +335 -0
- package/template/packages/api/examples/security/complete/tests/helpers/api-helpers.ts +116 -0
- package/template/packages/api/examples/security/complete/tests/helpers/index.ts +2 -0
- package/template/packages/api/examples/security/complete/tests/helpers/test-helpers.ts +129 -0
- package/template/packages/api/examples/security/complete/tests/integration/auth.api.test.ts +429 -0
- package/template/packages/api/examples/security/complete/tests/integration/role.api.test.ts +400 -0
- package/template/packages/api/examples/security/complete/tests/integration/user.api.test.ts +459 -0
- package/template/packages/api/examples/security/complete/tests/jest.config.js +40 -0
- package/template/packages/api/examples/security/complete/tests/run-all-tests.js +135 -0
- package/template/packages/api/examples/security/complete/tests/run-tests.js +109 -0
- package/template/packages/api/examples/security/complete/tests/setup.ts +19 -0
- package/template/packages/api/examples/security/complete/tests/unit/auth.service.test.ts +199 -0
- package/template/packages/api/examples/security/complete/tests/unit/permission.service.test.ts +377 -0
- package/template/packages/api/examples/security/complete/tests/unit/user.service.test.ts +288 -0
- package/template/packages/api/examples/security/complete/tsconfig.json +35 -0
- package/template/packages/api/examples/security/jwt/README.md +424 -0
- package/template/packages/api/examples/security/local/README.md +499 -0
- package/template/packages/api/examples/security/oauth2/README.md +637 -0
- package/template/packages/api/examples/security/permission/README.md +943 -0
- package/template/packages/api/examples/security/session/README.md +753 -0
- package/template/packages/api/package.json +56 -0
- package/template/packages/api/src/controller/auth.controller.ts +127 -0
- package/template/packages/api/src/controller/cache.controller.ts +106 -0
- package/template/packages/api/src/controller/menu.controller.ts +46 -0
- package/template/packages/api/src/controller/mq.controller.ts +35 -0
- package/template/packages/api/src/controller/role.controller.ts +51 -0
- package/template/packages/api/src/controller/upload.controller.ts +85 -0
- package/template/packages/api/src/controller/user.controller.ts +57 -0
- package/template/packages/api/src/dto/auth.dto.ts +30 -0
- package/template/packages/api/src/dto/cache.dto.ts +24 -0
- package/template/packages/api/src/dto/menu.dto.ts +37 -0
- package/template/packages/api/src/dto/mq.dto.ts +16 -0
- package/template/packages/api/src/dto/role.dto.ts +16 -0
- package/template/packages/api/src/dto/user.dto.ts +35 -0
- package/template/packages/api/src/entity/menu.entity.ts +34 -0
- package/template/packages/api/src/entity/role-menu.entity.ts +13 -0
- package/template/packages/api/src/entity/role.entity.ts +22 -0
- package/template/packages/api/src/entity/user-role.entity.ts +13 -0
- package/template/packages/api/src/entity/user.entity.ts +31 -0
- package/template/packages/api/src/mapper/menu.mapper.ts +6 -0
- package/template/packages/api/src/mapper/role-menu.mapper.ts +6 -0
- package/template/packages/api/src/mapper/role.mapper.ts +6 -0
- package/template/packages/api/src/mapper/user-role.mapper.ts +6 -0
- package/template/packages/api/src/mapper/user.mapper.ts +11 -0
- package/template/packages/api/src/scripts/init-db.ts +185 -0
- package/template/packages/api/src/server.ts +76 -0
- package/template/packages/api/src/service/auth.service.ts +106 -0
- package/template/packages/api/src/service/cache.service.ts +80 -0
- package/template/packages/api/src/service/log.request.service.ts +158 -0
- package/template/packages/api/src/service/log.service.ts +123 -0
- package/template/packages/api/src/service/menu.service.ts +94 -0
- package/template/packages/api/src/service/mq.consumer.service.ts +26 -0
- package/template/packages/api/src/service/role.service.ts +88 -0
- package/template/packages/api/src/service/user.service.ts +170 -0
- package/template/packages/api/src/types/sqljs.d.ts +18 -0
- package/template/packages/api/src/utils/auth.utils.js +0 -0
- package/template/packages/api/src/utils/jwt.util.ts +29 -0
- package/template/packages/api/tsconfig.json +17 -0
- package/template/packages/api/tsup.config.ts +9 -0
- package/template/packages/api/uploads/.gitkeep +0 -0
- package/template/packages/core/package.json +31 -0
- package/template/packages/core/src/auth/auth-client-middleware.ts +22 -0
- package/template/packages/core/src/auth/auth-service.ts +65 -0
- package/template/packages/core/src/auth/default-auth-provider.ts +37 -0
- package/template/packages/core/src/auth/index.ts +10 -0
- package/template/packages/core/src/auth/types.ts +38 -0
- package/template/packages/core/src/authorization/authorization-client-middleware.ts +38 -0
- package/template/packages/core/src/authorization/authorization-config.ts +13 -0
- package/template/packages/core/src/authorization/authorization-provider.tsx +116 -0
- package/template/packages/core/src/authorization/default-authorization-provider.ts +26 -0
- package/template/packages/core/src/authorization/index.ts +15 -0
- package/template/packages/core/src/authorization/types.ts +42 -0
- package/template/packages/core/src/index.ts +3 -0
- package/template/packages/core/src/utils/promise-result-cache.ts +18 -0
- package/template/packages/core/tsconfig.json +19 -0
- package/template/packages/mobile/README.md +55 -0
- package/template/packages/mobile/index.html +13 -0
- package/template/packages/mobile/package.json +30 -0
- package/template/packages/mobile/postcss.config.mjs +7 -0
- package/template/packages/mobile/src/App.tsx +5 -0
- package/template/packages/mobile/src/app/globals.css +1 -0
- package/template/packages/mobile/src/components/LoginForm.tsx +76 -0
- package/template/packages/mobile/src/hooks/index.ts +5 -0
- package/template/packages/mobile/src/lib/utils.ts +7 -0
- package/template/packages/mobile/src/main.tsx +13 -0
- package/template/packages/mobile/src/pages/HomePage.tsx +35 -0
- package/template/packages/mobile/src/pages/LoginPage.tsx +35 -0
- package/template/packages/mobile/src/pages/index.ts +2 -0
- package/template/packages/mobile/src/routes/ProtectedRoute.tsx +29 -0
- package/template/packages/mobile/src/routes/index.tsx +24 -0
- package/template/packages/mobile/src/routes/routes.ts +11 -0
- package/template/packages/mobile/src/types/index.ts +5 -0
- package/template/packages/mobile/src/vite-env.d.ts +1 -0
- package/template/packages/mobile/tsconfig.json +23 -0
- package/template/packages/mobile/tsconfig.node.json +11 -0
- package/template/packages/mobile/vite.config.ts +19 -0
- package/template/packages/shared/package.json +20 -0
- package/template/packages/shared/src/constants.ts +8 -0
- package/template/packages/shared/src/index.ts +5 -0
- package/template/packages/shared/tsconfig.json +13 -0
- package/template/packages/shared-auth/package.json +29 -0
- package/template/packages/shared-auth/src/AuthContext.tsx +177 -0
- package/template/packages/shared-auth/src/AuthProviderWrapper.tsx +29 -0
- package/template/packages/shared-auth/src/index.ts +10 -0
- package/template/packages/shared-auth/tsconfig.json +14 -0
- package/template/pnpm-lock.yaml +16349 -0
- package/template/pnpm-workspace.yaml +3 -0
- package/template/scripts/postinstall.cjs +42 -0
- package/template/scripts/rebuild-sqlite.cjs +23 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { getLogger } from '@ai-partner-x/aiko-boot-starter-log';
|
|
3
|
+
|
|
4
|
+
const logger = getLogger('http');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* HTTP请求日志服务
|
|
8
|
+
* 提供HTTP请求日志记录功能
|
|
9
|
+
*/
|
|
10
|
+
export class RequestLogService {
|
|
11
|
+
/**
|
|
12
|
+
* 完整的HTTP请求日志中间件
|
|
13
|
+
* 记录所有HTTP请求的详细信息
|
|
14
|
+
*/
|
|
15
|
+
static requestLogMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
|
|
18
|
+
// 获取请求信息
|
|
19
|
+
const requestInfo = {
|
|
20
|
+
method: req.method,
|
|
21
|
+
url: req.url,
|
|
22
|
+
path: req.path,
|
|
23
|
+
query: req.query,
|
|
24
|
+
ip: req.ip || req.connection.remoteAddress,
|
|
25
|
+
userAgent: req.get('user-agent') || 'unknown',
|
|
26
|
+
contentType: req.get('content-type'),
|
|
27
|
+
contentLength: req.get('content-length'),
|
|
28
|
+
referer: req.get('referer'),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// 记录请求开始
|
|
32
|
+
logger.http('Request started', requestInfo);
|
|
33
|
+
|
|
34
|
+
// 监听响应完成事件
|
|
35
|
+
res.on('finish', () => {
|
|
36
|
+
const duration = Date.now() - startTime;
|
|
37
|
+
|
|
38
|
+
const responseInfo = {
|
|
39
|
+
...requestInfo,
|
|
40
|
+
status: res.statusCode,
|
|
41
|
+
statusMessage: res.statusMessage,
|
|
42
|
+
duration: `${duration}ms`,
|
|
43
|
+
responseTime: duration,
|
|
44
|
+
timestamp: new Date().toISOString(),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// 根据状态码选择日志级别
|
|
48
|
+
if (res.statusCode >= 500) {
|
|
49
|
+
logger.error('Request completed with server error', responseInfo);
|
|
50
|
+
} else if (res.statusCode >= 400) {
|
|
51
|
+
logger.warn('Request completed with client error', responseInfo);
|
|
52
|
+
} else {
|
|
53
|
+
logger.http('Request completed successfully', responseInfo);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 监听响应关闭事件(客户端提前断开连接)
|
|
58
|
+
res.on('close', () => {
|
|
59
|
+
if (!res.writableFinished) {
|
|
60
|
+
const duration = Date.now() - startTime;
|
|
61
|
+
logger.warn('Request closed by client before completion', {
|
|
62
|
+
...requestInfo,
|
|
63
|
+
duration: `${duration}ms`,
|
|
64
|
+
timestamp: new Date().toISOString(),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
next();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 简化的请求日志中间件(仅记录基本信息)
|
|
74
|
+
*/
|
|
75
|
+
static simpleRequestLogMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
76
|
+
const startTime = Date.now();
|
|
77
|
+
|
|
78
|
+
res.on('finish', () => {
|
|
79
|
+
const duration = Date.now() - startTime;
|
|
80
|
+
|
|
81
|
+
logger.info(`${req.method} ${req.url} ${res.statusCode} ${duration}ms`, {
|
|
82
|
+
method: req.method,
|
|
83
|
+
url: req.url,
|
|
84
|
+
status: res.statusCode,
|
|
85
|
+
duration: `${duration}ms`,
|
|
86
|
+
ip: req.ip,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
next();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 错误请求日志中间件(专门记录错误请求)
|
|
95
|
+
*/
|
|
96
|
+
static errorRequestLogMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
97
|
+
const startTime = Date.now();
|
|
98
|
+
|
|
99
|
+
res.on('finish', () => {
|
|
100
|
+
if (res.statusCode >= 400) {
|
|
101
|
+
const duration = Date.now() - startTime;
|
|
102
|
+
|
|
103
|
+
logger.warn('Error request detected', {
|
|
104
|
+
method: req.method,
|
|
105
|
+
url: req.url,
|
|
106
|
+
status: res.statusCode,
|
|
107
|
+
duration: `${duration}ms`,
|
|
108
|
+
ip: req.ip,
|
|
109
|
+
userAgent: req.get('user-agent'),
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
next();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 记录HTTP请求信息(直接调用,非中间件)
|
|
120
|
+
*/
|
|
121
|
+
static logRequest(req: Request, res: Response, duration: number) {
|
|
122
|
+
const requestInfo = {
|
|
123
|
+
method: req.method,
|
|
124
|
+
url: req.url,
|
|
125
|
+
path: req.path,
|
|
126
|
+
query: req.query,
|
|
127
|
+
ip: req.ip || req.connection.remoteAddress,
|
|
128
|
+
userAgent: req.get('user-agent') || 'unknown',
|
|
129
|
+
status: res.statusCode,
|
|
130
|
+
statusMessage: res.statusMessage,
|
|
131
|
+
duration: `${duration}ms`,
|
|
132
|
+
responseTime: duration,
|
|
133
|
+
timestamp: new Date().toISOString(),
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if (res.statusCode >= 500) {
|
|
137
|
+
logger.error('Request completed with server error', requestInfo);
|
|
138
|
+
} else if (res.statusCode >= 400) {
|
|
139
|
+
logger.warn('Request completed with client error', requestInfo);
|
|
140
|
+
} else {
|
|
141
|
+
logger.http('Request completed successfully', requestInfo);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 记录客户端提前断开连接
|
|
147
|
+
*/
|
|
148
|
+
static logClientDisconnect(req: Request, duration: number) {
|
|
149
|
+
logger.warn('Request closed by client before completion', {
|
|
150
|
+
method: req.method,
|
|
151
|
+
url: req.url,
|
|
152
|
+
path: req.path,
|
|
153
|
+
ip: req.ip || req.connection.remoteAddress,
|
|
154
|
+
duration: `${duration}ms`,
|
|
155
|
+
timestamp: new Date().toISOString(),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { getLogger, defaultLogger, ILogger } from '@ai-partner-x/aiko-boot-starter-log';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 日志服务适配器
|
|
5
|
+
* 提供统一的日志接口,封装 aiko-boot-starter-log 的功能
|
|
6
|
+
*/
|
|
7
|
+
export class LogService {
|
|
8
|
+
private static instance: LogService;
|
|
9
|
+
private logger: ILogger;
|
|
10
|
+
|
|
11
|
+
private constructor() {
|
|
12
|
+
// 使用命名logger,便于区分不同模块
|
|
13
|
+
this.logger = getLogger('scaffold-api');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 获取日志服务单例
|
|
18
|
+
*/
|
|
19
|
+
static getInstance(): LogService {
|
|
20
|
+
if (!LogService.instance) {
|
|
21
|
+
LogService.instance = new LogService();
|
|
22
|
+
}
|
|
23
|
+
return LogService.instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 记录错误日志
|
|
28
|
+
* @param message 错误消息
|
|
29
|
+
* @param error 错误对象(可选)
|
|
30
|
+
* @param meta 附加元数据(可选)
|
|
31
|
+
*/
|
|
32
|
+
error(message: string, error?: Error, meta?: Record<string, any>) {
|
|
33
|
+
this.logger.error(message, error, meta);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 记录警告日志
|
|
38
|
+
* @param message 警告消息
|
|
39
|
+
* @param meta 附加元数据(可选)
|
|
40
|
+
*/
|
|
41
|
+
warn(message: string, meta?: Record<string, any>) {
|
|
42
|
+
this.logger.warn(message, meta);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 记录信息日志
|
|
47
|
+
* @param message 信息消息
|
|
48
|
+
* @param meta 附加元数据(可选)
|
|
49
|
+
*/
|
|
50
|
+
info(message: string, meta?: Record<string, any>) {
|
|
51
|
+
this.logger.info(message, meta);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 记录调试日志
|
|
56
|
+
* @param message 调试消息
|
|
57
|
+
* @param meta 附加元数据(可选)
|
|
58
|
+
*/
|
|
59
|
+
debug(message: string, meta?: Record<string, any>) {
|
|
60
|
+
this.logger.debug(message, meta);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 记录HTTP请求日志
|
|
65
|
+
* @param message HTTP消息
|
|
66
|
+
* @param meta 附加元数据(可选)
|
|
67
|
+
*/
|
|
68
|
+
http(message: string, meta?: Record<string, any>) {
|
|
69
|
+
this.logger.http(message, meta);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 记录详细日志
|
|
74
|
+
* @param message 详细消息
|
|
75
|
+
* @param meta 附加元数据(可选)
|
|
76
|
+
*/
|
|
77
|
+
verbose(message: string, meta?: Record<string, any>) {
|
|
78
|
+
this.logger.verbose(message, meta);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 记录最详细日志
|
|
83
|
+
* @param message 最详细消息
|
|
84
|
+
* @param meta 附加元数据(可选)
|
|
85
|
+
*/
|
|
86
|
+
silly(message: string, meta?: Record<string, any>) {
|
|
87
|
+
this.logger.silly(message, meta);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 检查是否启用指定级别的日志
|
|
92
|
+
* @param level 日志级别
|
|
93
|
+
*/
|
|
94
|
+
isLevelEnabled(level: string): boolean {
|
|
95
|
+
return this.logger.isLevelEnabled(level);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 获取原始logger实例
|
|
100
|
+
*/
|
|
101
|
+
getLogger(): ILogger {
|
|
102
|
+
return this.logger;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 创建子logger(用于模块划分)
|
|
107
|
+
* @param name 子logger名称
|
|
108
|
+
*/
|
|
109
|
+
createChildLogger(name: string): ILogger {
|
|
110
|
+
return this.logger.child(name);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 添加上下文信息
|
|
115
|
+
* @param context 上下文对象
|
|
116
|
+
*/
|
|
117
|
+
withContext(context: Record<string, any>): ILogger {
|
|
118
|
+
return this.logger.withContext(context);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 导出单例实例
|
|
123
|
+
export const logService = LogService.getInstance();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { Service, Transactional } from '@ai-partner-x/aiko-boot';
|
|
3
|
+
import { Autowired } from '@ai-partner-x/aiko-boot/di/server';
|
|
4
|
+
import { MenuMapper } from '../mapper/menu.mapper.js';
|
|
5
|
+
import { Menu } from '../entity/menu.entity.js';
|
|
6
|
+
import type { CreateMenuDto, UpdateMenuDto, MenuTreeVo } from '../dto/menu.dto.js';
|
|
7
|
+
|
|
8
|
+
@Service()
|
|
9
|
+
export class MenuService {
|
|
10
|
+
@Autowired()
|
|
11
|
+
private menuMapper!: MenuMapper;
|
|
12
|
+
|
|
13
|
+
async getFullTree(): Promise<MenuTreeVo[]> {
|
|
14
|
+
const all = await this.menuMapper.selectList();
|
|
15
|
+
return this.buildTree(all, 0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async getUserMenuTree(permissions: string[]): Promise<MenuTreeVo[]> {
|
|
19
|
+
const all = await this.menuMapper.selectList({ status: 1 });
|
|
20
|
+
const visible: any[] = [];
|
|
21
|
+
for (let i = 0; i < all.length; i++) {
|
|
22
|
+
const m = all[i];
|
|
23
|
+
if (m.menuType === 3) {
|
|
24
|
+
if (m.permission && permissions.includes(m.permission)) {
|
|
25
|
+
visible.push(m);
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
visible.push(m);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return this.buildTree(visible, 0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async getById(id: number) {
|
|
35
|
+
const menu = await this.menuMapper.selectById(id);
|
|
36
|
+
if (!menu) throw new Error('菜单不存在');
|
|
37
|
+
return menu;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Transactional()
|
|
41
|
+
async createMenu(dto: CreateMenuDto) {
|
|
42
|
+
const menu: Menu = {
|
|
43
|
+
id: 0,
|
|
44
|
+
parentId: dto.parentId !== undefined ? dto.parentId : 0,
|
|
45
|
+
menuName: dto.menuName,
|
|
46
|
+
menuType: dto.menuType,
|
|
47
|
+
path: dto.path,
|
|
48
|
+
component: dto.component,
|
|
49
|
+
permission: dto.permission,
|
|
50
|
+
icon: dto.icon,
|
|
51
|
+
sortOrder: dto.sortOrder !== undefined ? dto.sortOrder : 0,
|
|
52
|
+
status: dto.status !== undefined ? dto.status : 1,
|
|
53
|
+
};
|
|
54
|
+
const result:number = await this.menuMapper.insert(menu);
|
|
55
|
+
console.log("result", result);
|
|
56
|
+
if (result !== 1) {
|
|
57
|
+
throw new Error('创建菜单失败');
|
|
58
|
+
}
|
|
59
|
+
const menus = await this.menuMapper.selectList({ id: menu.id });
|
|
60
|
+
return menus[0] || null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Transactional()
|
|
64
|
+
async updateMenu(id: number, dto: UpdateMenuDto) {
|
|
65
|
+
const menu = await this.menuMapper.selectById(id);
|
|
66
|
+
if (!menu) throw new Error('菜单不存在');
|
|
67
|
+
Object.assign(menu, dto);
|
|
68
|
+
await this.menuMapper.updateById(menu);
|
|
69
|
+
return menu;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async deleteMenu(id: number): Promise<boolean> {
|
|
73
|
+
const children = await this.menuMapper.selectList({ parentId: id });
|
|
74
|
+
if (children.length) throw new Error('存在子菜单,无法删除');
|
|
75
|
+
return this.menuMapper.deleteById(id);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private buildTree(menus: any[], parentId: number): MenuTreeVo[] {
|
|
79
|
+
const result: MenuTreeVo[] = [];
|
|
80
|
+
for (let i = 0; i < menus.length; i++) {
|
|
81
|
+
const m = menus[i];
|
|
82
|
+
if (m.parentId === parentId) {
|
|
83
|
+
const children = this.buildTree(menus, m.id);
|
|
84
|
+
const item: MenuTreeVo = { ...m };
|
|
85
|
+
if (children.length > 0) {
|
|
86
|
+
item.children = children;
|
|
87
|
+
}
|
|
88
|
+
result.push(item);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
result.sort((a, b) => a.sortOrder - b.sortOrder);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { Service } from '@ai-partner-x/aiko-boot';
|
|
3
|
+
import { MqListener, type MqListenerMeta } from '@ai-partner-x/aiko-boot-starter-mq';
|
|
4
|
+
import type { UserEventDto } from '../dto/mq.dto.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MQ 消费者服务 - 订阅 user 相关消息
|
|
8
|
+
* 复用 User 实体字段结构(id, username, email, createdAt, updatedAt)
|
|
9
|
+
*/
|
|
10
|
+
@Service()
|
|
11
|
+
export class MqConsumerService {
|
|
12
|
+
static readonly MQ_LISTENERS: MqListenerMeta[] = [
|
|
13
|
+
{ topic: 'user.created', tag: 'add', group: 'scaffold-user-group', method: 'onUserCreated' },
|
|
14
|
+
{ topic: 'user.updated', tag: 'update', group: 'scaffold-user-group', method: 'onUserUpdated' },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
@MqListener({ topic: 'user.created', tag: 'add', group: 'scaffold-user-group' })
|
|
18
|
+
async onUserCreated(event: UserEventDto): Promise<void> {
|
|
19
|
+
console.log('[MqConsumer] user.created:', JSON.stringify(event));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@MqListener({ topic: 'user.updated', tag: 'update', group: 'scaffold-user-group' })
|
|
23
|
+
async onUserUpdated(event: UserEventDto): Promise<void> {
|
|
24
|
+
console.log('[MqConsumer] user.updated:', JSON.stringify(event));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { Service, Transactional } from '@ai-partner-x/aiko-boot';
|
|
3
|
+
import { Autowired } from '@ai-partner-x/aiko-boot/di/server';
|
|
4
|
+
import { RoleMapper } from '../mapper/role.mapper.js';
|
|
5
|
+
import { RoleMenuMapper } from '../mapper/role-menu.mapper.js';
|
|
6
|
+
import type { CreateRoleDto, UpdateRoleDto } from '../dto/role.dto.js';
|
|
7
|
+
|
|
8
|
+
@Service()
|
|
9
|
+
export class RoleService {
|
|
10
|
+
@Autowired()
|
|
11
|
+
private roleMapper!: RoleMapper;
|
|
12
|
+
|
|
13
|
+
@Autowired()
|
|
14
|
+
private roleMenuMapper!: RoleMenuMapper;
|
|
15
|
+
|
|
16
|
+
async listRoles() {
|
|
17
|
+
return this.roleMapper.selectList();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async getById(id: number) {
|
|
21
|
+
const role = await this.roleMapper.selectById(id);
|
|
22
|
+
if (!role) throw new Error('角色不存在');
|
|
23
|
+
const roleMenus = await this.roleMenuMapper.selectList({ roleId: id });
|
|
24
|
+
const menuIds: number[] = [];
|
|
25
|
+
for (let i = 0; i < roleMenus.length; i++) {
|
|
26
|
+
const rm = roleMenus[i];
|
|
27
|
+
menuIds.push(rm.menuId);
|
|
28
|
+
}
|
|
29
|
+
return { ...role, menuIds: menuIds };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Transactional()
|
|
33
|
+
async createRole(dto: CreateRoleDto) {
|
|
34
|
+
console.log("dto", dto);
|
|
35
|
+
const exists = await this.roleMapper.selectList({ roleCode: dto.roleCode });
|
|
36
|
+
if (exists.length) throw new Error('角色编码已存在');
|
|
37
|
+
const status = dto.status !== undefined ? dto.status : 1;
|
|
38
|
+
await this.roleMapper.insert({
|
|
39
|
+
roleCode: dto.roleCode,
|
|
40
|
+
roleName: dto.roleName,
|
|
41
|
+
description: dto.description,
|
|
42
|
+
status: status,
|
|
43
|
+
createdAt: new Date().toISOString(),
|
|
44
|
+
});
|
|
45
|
+
const roles = await this.roleMapper.selectList({ roleCode: dto.roleCode });
|
|
46
|
+
const role = roles[0];
|
|
47
|
+
console.error("role", role);
|
|
48
|
+
if (!role) throw new Error('创建角色失败');
|
|
49
|
+
if (dto.menuIds !== undefined && dto.menuIds.length > 0) await this.assignMenus(role.id, dto.menuIds);
|
|
50
|
+
return role;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@Transactional()
|
|
54
|
+
async updateRole(id: number, dto: UpdateRoleDto) {
|
|
55
|
+
const role = await this.roleMapper.selectById(id);
|
|
56
|
+
if (!role) throw new Error('角色不存在');
|
|
57
|
+
if (dto.roleName !== undefined) role.roleName = dto.roleName;
|
|
58
|
+
if (dto.description !== undefined) role.description = dto.description;
|
|
59
|
+
if (dto.status !== undefined) role.status = dto.status;
|
|
60
|
+
await this.roleMapper.updateById(role);
|
|
61
|
+
if (dto.menuIds !== undefined) await this.assignMenus(id, dto.menuIds);
|
|
62
|
+
return role;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async deleteRole(id: number): Promise<boolean> {
|
|
66
|
+
const role = await this.roleMapper.selectById(id);
|
|
67
|
+
if (!role) throw new Error('角色不存在');
|
|
68
|
+
await this.roleMenuMapper.delete({ roleId: id });
|
|
69
|
+
return this.roleMapper.deleteById(id);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getRoleMenuIds(roleId: number): Promise<number[]> {
|
|
73
|
+
const roleMenus = await this.roleMenuMapper.selectList({ roleId });
|
|
74
|
+
const menuIds: number[] = [];
|
|
75
|
+
for (let i = 0; i < roleMenus.length; i++) {
|
|
76
|
+
const rm = roleMenus[i];
|
|
77
|
+
menuIds.push(rm.menuId);
|
|
78
|
+
}
|
|
79
|
+
return menuIds;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private async assignMenus(roleId: number, menuIds: number[]) {
|
|
83
|
+
await this.roleMenuMapper.delete({ roleId });
|
|
84
|
+
for (const menuId of menuIds) {
|
|
85
|
+
await this.roleMenuMapper.insert({ roleId, menuId });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { Injectable, Autowired } from '@ai-partner-x/aiko-boot/di/server';
|
|
3
|
+
import bcrypt from 'bcryptjs';
|
|
4
|
+
import { UserMapper } from '../mapper/user.mapper.js';
|
|
5
|
+
import { UserRoleMapper } from '../mapper/user-role.mapper.js';
|
|
6
|
+
import { RoleMapper } from '../mapper/role.mapper.js';
|
|
7
|
+
import type { CreateUserDto, UpdateUserDto, UserPageDto, UserVo } from '../dto/user.dto.js';
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class UserService {
|
|
11
|
+
@Autowired()
|
|
12
|
+
private userMapper!: UserMapper;
|
|
13
|
+
|
|
14
|
+
@Autowired()
|
|
15
|
+
private userRoleMapper!: UserRoleMapper;
|
|
16
|
+
|
|
17
|
+
@Autowired()
|
|
18
|
+
private roleMapper!: RoleMapper;
|
|
19
|
+
|
|
20
|
+
async pageUsers(params: UserPageDto) {
|
|
21
|
+
const allUsers = await this.userMapper.selectList();
|
|
22
|
+
let filtered = allUsers;
|
|
23
|
+
if (params.username) {
|
|
24
|
+
const username = params.username;
|
|
25
|
+
const temp: typeof allUsers = [];
|
|
26
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
27
|
+
const u = filtered[i];
|
|
28
|
+
if (u.username.includes(username)) {
|
|
29
|
+
temp.push(u);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
filtered = temp;
|
|
33
|
+
}
|
|
34
|
+
if (params.status !== undefined) {
|
|
35
|
+
const status = params.status;
|
|
36
|
+
const temp: typeof allUsers = [];
|
|
37
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
38
|
+
const u = filtered[i];
|
|
39
|
+
if (u.status === status) {
|
|
40
|
+
temp.push(u);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
filtered = temp;
|
|
44
|
+
}
|
|
45
|
+
const pageNo = params.pageNo || 1;
|
|
46
|
+
const pageSize = params.pageSize || 10;
|
|
47
|
+
const total = filtered.length;
|
|
48
|
+
const start = (pageNo - 1) * pageSize;
|
|
49
|
+
const end = start + pageSize;
|
|
50
|
+
const records = filtered.slice(start, end);
|
|
51
|
+
const usersWithRoles: UserVo[] = [];
|
|
52
|
+
for (let i = 0; i < records.length; i++) {
|
|
53
|
+
const u = records[i];
|
|
54
|
+
const vo = await this.toVo(this.parseEntityDates(u));
|
|
55
|
+
usersWithRoles.push(vo);
|
|
56
|
+
}
|
|
57
|
+
return { records: usersWithRoles, total, pageNo, pageSize, totalPages: Math.ceil(total / pageSize) };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getById(id: number): Promise<UserVo> {
|
|
61
|
+
const user = await this.userMapper.selectById(id);
|
|
62
|
+
if (!user) throw new Error('用户不存在');
|
|
63
|
+
return this.toVo(this.parseEntityDates(user));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async createUser(dto: CreateUserDto): Promise<UserVo> {
|
|
69
|
+
const exists = await this.userMapper.selectByUsername(dto.username);
|
|
70
|
+
if (exists) throw new Error('用户名已存在');
|
|
71
|
+
const hashed = await bcrypt.hash(dto.password, 10);
|
|
72
|
+
const status = dto.status !== undefined ? dto.status : 1;
|
|
73
|
+
await this.userMapper.insert({
|
|
74
|
+
username: dto.username,
|
|
75
|
+
passwordHash: hashed,
|
|
76
|
+
realName: dto.realName,
|
|
77
|
+
email: dto.email,
|
|
78
|
+
phone: dto.phone,
|
|
79
|
+
status: status,
|
|
80
|
+
createdAt: new Date().toISOString(),
|
|
81
|
+
updatedAt: new Date().toISOString(),
|
|
82
|
+
});
|
|
83
|
+
const user = await this.userMapper.selectByUsername(dto.username);
|
|
84
|
+
if (!user) throw new Error('创建用户失败');
|
|
85
|
+
if (dto.roleIds !== undefined && dto.roleIds.length > 0) await this.assignRoles(user.id, dto.roleIds);
|
|
86
|
+
return this.toVo(this.parseEntityDates(user));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async updateUser(id: number, dto: UpdateUserDto): Promise<UserVo> {
|
|
92
|
+
let user = await this.userMapper.selectById(id);
|
|
93
|
+
if (!user) throw new Error('用户不存在');
|
|
94
|
+
user = this.parseEntityDates(user);
|
|
95
|
+
if (dto.realName !== undefined) user.realName = dto.realName;
|
|
96
|
+
if (dto.email !== undefined) user.email = dto.email;
|
|
97
|
+
if (dto.phone !== undefined) user.phone = dto.phone;
|
|
98
|
+
if (dto.status !== undefined) user.status = dto.status;
|
|
99
|
+
user.updatedAt = new Date().toISOString();
|
|
100
|
+
await this.userMapper.updateById(this.formatEntityDates(user));
|
|
101
|
+
if (dto.roleIds !== undefined) await this.assignRoles(id, dto.roleIds);
|
|
102
|
+
return this.toVo(this.parseEntityDates(user));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async deleteUser(id: number): Promise<boolean> {
|
|
108
|
+
let user = await this.userMapper.selectById(id);
|
|
109
|
+
if (!user) throw new Error('用户不存在');
|
|
110
|
+
await this.userRoleMapper.delete({ userId: id });
|
|
111
|
+
return this.userMapper.deleteById(id);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async resetPassword(id: number, newPassword: string): Promise<void> {
|
|
116
|
+
let user = await this.userMapper.selectById(id);
|
|
117
|
+
if (!user) throw new Error('用户不存在');
|
|
118
|
+
user = this.parseEntityDates(user);
|
|
119
|
+
user.passwordHash = await bcrypt.hash(newPassword, 10);
|
|
120
|
+
user.updatedAt = new Date().toISOString();
|
|
121
|
+
await this.userMapper.updateById(this.formatEntityDates(user));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
private async assignRoles(userId: number, roleIds: number[]) {
|
|
126
|
+
await this.userRoleMapper.delete({ userId });
|
|
127
|
+
for (const roleId of roleIds) {
|
|
128
|
+
await this.userRoleMapper.insert({ userId, roleId });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private parseEntityDates(entity: any): any {
|
|
133
|
+
const parsed = { ...entity };
|
|
134
|
+
if (parsed.createdAt && typeof parsed.createdAt === 'string') {
|
|
135
|
+
parsed.createdAt = new Date(parsed.createdAt);
|
|
136
|
+
}
|
|
137
|
+
if (parsed.updatedAt && typeof parsed.updatedAt === 'string') {
|
|
138
|
+
parsed.updatedAt = new Date(parsed.updatedAt);
|
|
139
|
+
}
|
|
140
|
+
return parsed;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private formatEntityDates(entity: any): any {
|
|
144
|
+
const formatted = { ...entity };
|
|
145
|
+
if (formatted.createdAt && formatted.createdAt instanceof Date) {
|
|
146
|
+
formatted.createdAt = formatted.createdAt.toISOString();
|
|
147
|
+
}
|
|
148
|
+
if (formatted.updatedAt && formatted.updatedAt instanceof Date) {
|
|
149
|
+
formatted.updatedAt = formatted.updatedAt.toISOString();
|
|
150
|
+
}
|
|
151
|
+
return formatted;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private async toVo(user: any): Promise<UserVo> {
|
|
155
|
+
const userRoles = await this.userRoleMapper.selectList({ userId: user.id });
|
|
156
|
+
const roles: string[] = [];
|
|
157
|
+
for (let i = 0; i < userRoles.length; i++) {
|
|
158
|
+
const ur = userRoles[i];
|
|
159
|
+
const role = await this.roleMapper.selectById(ur.roleId);
|
|
160
|
+
if (role) roles.push(role.roleCode);
|
|
161
|
+
}
|
|
162
|
+
const safe: any = {};
|
|
163
|
+
for (const key in user) {
|
|
164
|
+
if (key !== 'password') {
|
|
165
|
+
safe[key] = user[key];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { ...safe, roles };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare module 'sql.js' {
|
|
2
|
+
export interface SqlJsInitConfig {
|
|
3
|
+
locateFile?: (file: string, prefix: string) => string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SqlJsDatabase {
|
|
7
|
+
run(sql: string, params?: unknown[]): void;
|
|
8
|
+
export(): Uint8Array;
|
|
9
|
+
close(): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SqlJsStatic {
|
|
13
|
+
Database: new () => SqlJsDatabase;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function initSqlJs(config?: SqlJsInitConfig): Promise<SqlJsStatic>;
|
|
17
|
+
}
|
|
18
|
+
|
|
File without changes
|