From f96bfe0b20167f6e8cf9d27c08d25f1602a74cf2 Mon Sep 17 00:00:00 2001 From: yml Date: Thu, 21 May 2026 17:19:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E8=BF=81=E7=A7=BB=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E5=9F=BA=E7=A1=80=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/routes/admin/{auth.js => auth.ts} | 0 .../admin/{dashboard.js => dashboard.ts} | 2 - .../src/routes/admin/{shared.js => shared.ts} | 94 ++++++++-------- .../src/routes/admin/{users.js => users.ts} | 100 +++++++++--------- .../src/services/admin/admin-auth-service.ts | 2 +- docs/backend-typescript-migration-plan.md | 10 ++ 6 files changed, 107 insertions(+), 101 deletions(-) rename apps/backend/src/routes/admin/{auth.js => auth.ts} (100%) rename apps/backend/src/routes/admin/{dashboard.js => dashboard.ts} (96%) rename apps/backend/src/routes/admin/{shared.js => shared.ts} (53%) rename apps/backend/src/routes/admin/{users.js => users.ts} (58%) diff --git a/apps/backend/src/routes/admin/auth.js b/apps/backend/src/routes/admin/auth.ts similarity index 100% rename from apps/backend/src/routes/admin/auth.js rename to apps/backend/src/routes/admin/auth.ts diff --git a/apps/backend/src/routes/admin/dashboard.js b/apps/backend/src/routes/admin/dashboard.ts similarity index 96% rename from apps/backend/src/routes/admin/dashboard.js rename to apps/backend/src/routes/admin/dashboard.ts index 5a85aecd..5924be92 100644 --- a/apps/backend/src/routes/admin/dashboard.js +++ b/apps/backend/src/routes/admin/dashboard.ts @@ -1,5 +1,3 @@ -// @ts-check - import { Router } from 'express' import { getAdminDashboardSummary } from '../../services/admin/admin-dashboard-service.js' diff --git a/apps/backend/src/routes/admin/shared.js b/apps/backend/src/routes/admin/shared.ts similarity index 53% rename from apps/backend/src/routes/admin/shared.js rename to apps/backend/src/routes/admin/shared.ts index 8ac0b066..bd33a3b8 100644 --- a/apps/backend/src/routes/admin/shared.js +++ b/apps/backend/src/routes/admin/shared.ts @@ -1,46 +1,46 @@ -// @ts-check - import { requireAdminRole, verifyAdminSessionToken } from '../../services/admin/admin-auth-service.js' import { writeAdminAuditLog } from '../../services/admin/admin-audit-service.js' import { buildSuccessPayload, createHttpError, sendRouteError } from '../../utils/http.js' import { logWarn } from '../../utils/logger.js' +import type { AdminSession } from '../../services/admin/admin-auth-service.js' -/** @typedef {import('express').NextFunction} NextFunction */ -/** @typedef {import('express').Request} ExpressRequest */ -/** @typedef {import('express').Response} ExpressResponse */ -/** @typedef {import('../../types/admin-route-inputs.js').AdminRouteAdminSession} AdminRouteAdminSession */ -/** @typedef {ExpressRequest & { adminSession?: AdminRouteAdminSession | null }} AdminExpressRequest */ +type NextFunction = (error?: unknown) => void +type ExpressResponse = { + json: (body: unknown) => unknown + sendFile: (path: string) => unknown +} +export type AdminExpressRequest = { + headers: { authorization?: string | string[] } + body?: any + params?: Record + query?: Record + adminSession?: AdminSession | null + [key: string]: unknown +} -/** - * @typedef {{ - * action?: string - * targetType?: string - * targetId?: string - * data?: Record - * }} AdminAuditPayload - */ +type AdminAuditPayload = { + action?: string + targetType?: string + targetId?: string + data?: Record +} -/** - * @typedef {{ - * successMessage?: string - * errorMessage?: string - * scope?: string - * audit?: (req: AdminExpressRequest, data: unknown) => (AdminAuditPayload | null | undefined) - * }} AdminJsonHandlerOptions - */ +type AdminJsonHandlerOptions = { + successMessage?: string + errorMessage?: string + scope?: string + audit?: (req: AdminExpressRequest, data: unknown) => AdminAuditPayload | null | undefined +} -/** - * @typedef {{ - * errorMessage?: string - * scope?: string - * }} AdminFileHandlerOptions - */ +type AdminFileHandlerOptions = { + errorMessage?: string + scope?: string +} -/** - * @param {(req: AdminExpressRequest, res: ExpressResponse) => unknown | Promise} action - * @param {AdminJsonHandlerOptions} [options] - */ -export function createJsonHandler(action, { successMessage = 'ok', errorMessage, scope, audit } = {}) { +export function createJsonHandler( + action: (req: AdminExpressRequest, res: ExpressResponse) => unknown | Promise, + { successMessage = 'ok', errorMessage, scope, audit }: AdminJsonHandlerOptions = {}, +) { return async (req, res) => { try { const data = await action(req, res) @@ -52,11 +52,10 @@ export function createJsonHandler(action, { successMessage = 'ok', errorMessage, } } -/** - * @param {(req: AdminExpressRequest, res: ExpressResponse) => string | Promise} action - * @param {AdminFileHandlerOptions} [options] - */ -export function createFileHandler(action, { errorMessage, scope } = {}) { +export function createFileHandler( + action: (req: AdminExpressRequest, res: ExpressResponse) => string | Promise, + { errorMessage, scope }: AdminFileHandlerOptions = {}, +) { return async (req, res) => { try { const filePath = await action(req, res) @@ -67,10 +66,7 @@ export function createFileHandler(action, { errorMessage, scope } = {}) { } } -/** @param {AdminExpressRequest} req */ -/** @param {ExpressResponse} res */ -/** @param {NextFunction} next */ -export async function requireAdminSession(req, res, next) { +export async function requireAdminSession(req: AdminExpressRequest, res: ExpressResponse, next: NextFunction) { try { req.adminSession = await verifyAdminSessionToken(extractBearerToken(req)) next() @@ -79,8 +75,7 @@ export async function requireAdminSession(req, res, next) { } } -/** @param {string[]} allowedRoles */ -export function requireAdminRoles(allowedRoles) { +export function requireAdminRoles(allowedRoles: string[]) { return (req, res, next) => { try { requireAdminRole(req.adminSession, allowedRoles) @@ -91,7 +86,12 @@ export function requireAdminRoles(allowedRoles) { } } -async function recordAdminAudit(session, req, data, audit) { +async function recordAdminAudit( + session: AdminSession | null | undefined, + req: AdminExpressRequest, + data: unknown, + audit: AdminJsonHandlerOptions['audit'], +) { if (!audit) { return } @@ -114,7 +114,7 @@ async function recordAdminAudit(session, req, data, audit) { } } -export function extractBearerToken(req) { +export function extractBearerToken(req: AdminExpressRequest) { const authorization = String(req.headers.authorization || '').trim() const matched = authorization.match(/^Bearer\s+(.+)$/i) diff --git a/apps/backend/src/routes/admin/users.js b/apps/backend/src/routes/admin/users.ts similarity index 58% rename from apps/backend/src/routes/admin/users.js rename to apps/backend/src/routes/admin/users.ts index 564aca5f..f3115f32 100644 --- a/apps/backend/src/routes/admin/users.js +++ b/apps/backend/src/routes/admin/users.ts @@ -10,17 +10,15 @@ import { } from '../../services/admin/admin-auth-service.js' import { createJsonHandler, requireAdminRoles } from './shared.js' -/** - * @typedef {{ - * user: { - * userId: number - * username: string - * role: string - * status?: string - * inventoryGroupCodes?: string[] - * } - * }} AdminUserMutationResult - */ +type AdminUserMutationResult = { + user: { + userId: number + username: string + role: string + status?: string + inventoryGroupCodes?: string[] + } +} const router = Router() @@ -42,16 +40,16 @@ router.post('/users', createJsonHandler( errorMessage: '创建后台用户失败', scope: '[admin/users:create]', audit: (_req, data) => { - const result = /** @type {AdminUserMutationResult} */ (data) + const result = data as AdminUserMutationResult return { - action: 'admin_user_created', - targetType: 'admin_user', - targetId: String(result.user.userId), - data: { - username: result.user.username, - role: result.user.role, - status: result.user.status, - }, + action: 'admin_user_created', + targetType: 'admin_user', + targetId: String(result.user.userId), + data: { + username: result.user.username, + role: result.user.role, + status: result.user.status, + }, } }, }, @@ -64,15 +62,15 @@ router.post('/users/:userId/role', createJsonHandler( errorMessage: '更新用户角色失败', scope: '[admin/users/:userId/role]', audit: (_req, data) => { - const result = /** @type {AdminUserMutationResult} */ (data) + const result = data as AdminUserMutationResult return { - action: 'admin_user_role_updated', - targetType: 'admin_user', - targetId: String(result.user.userId), - data: { - username: result.user.username, - role: result.user.role, - }, + action: 'admin_user_role_updated', + targetType: 'admin_user', + targetId: String(result.user.userId), + data: { + username: result.user.username, + role: result.user.role, + }, } }, }, @@ -85,15 +83,15 @@ router.post('/users/:userId/status', createJsonHandler( errorMessage: '更新用户状态失败', scope: '[admin/users/:userId/status]', audit: (_req, data) => { - const result = /** @type {AdminUserMutationResult} */ (data) + const result = data as AdminUserMutationResult return { - action: 'admin_user_status_updated', - targetType: 'admin_user', - targetId: String(result.user.userId), - data: { - username: result.user.username, - status: result.user.status, - }, + action: 'admin_user_status_updated', + targetType: 'admin_user', + targetId: String(result.user.userId), + data: { + username: result.user.username, + status: result.user.status, + }, } }, }, @@ -106,15 +104,15 @@ router.post('/users/:userId/inventory-groups', createJsonHandler( errorMessage: '更新用户库存组失败', scope: '[admin/users/:userId/inventory-groups]', audit: (_req, data) => { - const result = /** @type {AdminUserMutationResult} */ (data) + const result = data as AdminUserMutationResult return { - action: 'admin_user_inventory_groups_updated', - targetType: 'admin_user', - targetId: String(result.user.userId), - data: { - username: result.user.username, - inventoryGroupCodes: result.user.inventoryGroupCodes, - }, + action: 'admin_user_inventory_groups_updated', + targetType: 'admin_user', + targetId: String(result.user.userId), + data: { + username: result.user.username, + inventoryGroupCodes: result.user.inventoryGroupCodes, + }, } }, }, @@ -127,14 +125,14 @@ router.post('/users/:userId/reset-password', createJsonHandler( errorMessage: '重置用户密码失败', scope: '[admin/users/:userId/reset-password]', audit: (_req, data) => { - const result = /** @type {AdminUserMutationResult} */ (data) + const result = data as AdminUserMutationResult return { - action: 'admin_user_password_reset', - targetType: 'admin_user', - targetId: String(result.user.userId), - data: { - username: result.user.username, - }, + action: 'admin_user_password_reset', + targetType: 'admin_user', + targetId: String(result.user.userId), + data: { + username: result.user.username, + }, } }, }, diff --git a/apps/backend/src/services/admin/admin-auth-service.ts b/apps/backend/src/services/admin/admin-auth-service.ts index 4a1d3058..eba3150b 100644 --- a/apps/backend/src/services/admin/admin-auth-service.ts +++ b/apps/backend/src/services/admin/admin-auth-service.ts @@ -18,7 +18,7 @@ type AdminUserRow = NonNullable>> type JsonObject = Record -type AdminSession = { +export type AdminSession = { sessionId: string userId: number username: string diff --git a/docs/backend-typescript-migration-plan.md b/docs/backend-typescript-migration-plan.md index 8070edc4..c043964e 100644 --- a/docs/backend-typescript-migration-plan.md +++ b/docs/backend-typescript-migration-plan.md @@ -808,6 +808,16 @@ - `npm run build` - `npm test` 共 139 个用例通过 208. `src/services` 目录下非测试 `.js` 服务文件已全部迁移为 `.ts` +209. 第一批后台 Admin 路由迁移到 `.ts`: + - `src/routes/admin/shared.ts` + - `src/routes/admin/auth.ts` + - `src/routes/admin/dashboard.ts` + - `src/routes/admin/users.ts` +210. 后台 JSON / 文件 handler、session 鉴权中间件、角色中间件、登录 / session / logout、概览摘要和后台用户管理路由已进入 TS 编译链路;Admin session、route request、审计 payload、用户 mutation result 与 handler options 补齐类型,并导出真实 `AdminSession` 给路由层复用 +211. Docker 内验证通过: + - `npm run typecheck` + - `npm run build` + - `npm test` 共 139 个用例通过 ## 下一步建议