优化: 大文件拆分+代码分割+按需加载+console清理+any类型替换
- Money.tsx (2260行→54行): 拆分为 money/ 子目录4个组件, React.lazy二级分割 - AIAssistant.tsx (2038行→63行): 拆分为 ai-assistant/ 子目录6个组件, React.lazy二级分割 - xlsx改为动态导入, OvertimeTab从345KB降至12.7KB - api-services.ts: 请求参数 any→Record<string,unknown> - 移除前端3处console.log残留 - 后端console替换为pino logger - 前后端未使用import/变量清理 - Zod schema验证: termination/platform/special-status/work-process - 新增 leave.routes.ts, acceptance-test.routes.ts - UI组件: PageGuide, QueryError, Stepper
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
* 功能:存储、验证、过期清理、失败次数限制
|
||||
*/
|
||||
|
||||
import logger from './logger'
|
||||
|
||||
interface CodeEntry {
|
||||
code: string
|
||||
expiresAt: number
|
||||
@@ -28,11 +30,11 @@ async function getRedis() {
|
||||
try {
|
||||
const { createClient } = await import('redis')
|
||||
redisClient = createClient({ url: REDIS_URL })
|
||||
redisClient.on('error', (err: any) => console.error('[Redis] error:', err))
|
||||
redisClient.on('error', (err: any) => logger.error({ err }, 'Redis error'))
|
||||
await redisClient.connect()
|
||||
console.log('[Redis] 验证码存储已连接')
|
||||
logger.info('Redis 验证码存储已连接')
|
||||
} catch (err) {
|
||||
console.warn('[Redis] 连接失败,降级为内存存储:', err)
|
||||
logger.warn({ err }, 'Redis 连接失败,降级为内存存储')
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -59,7 +61,7 @@ export async function setCode(key: string, code: string, ttlMs: number = 5 * 60
|
||||
await redis.set(`${KEY_PREFIX}${key}`, JSON.stringify(entry), { PX: ttlMs })
|
||||
return
|
||||
} catch (err) {
|
||||
console.warn('[Redis] set 失败,降级内存:', err)
|
||||
logger.warn({ err }, 'Redis set 失败,降级内存')
|
||||
}
|
||||
}
|
||||
memoryStore.set(key, entry)
|
||||
@@ -81,7 +83,7 @@ export async function getCode(key: string): Promise<CodeEntry | null> {
|
||||
}
|
||||
return entry
|
||||
} catch (err) {
|
||||
console.warn('[Redis] get 失败,降级内存:', err)
|
||||
logger.warn({ err }, 'Redis get 失败,降级内存')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +110,7 @@ export async function updateCode(key: string, updates: Partial<CodeEntry>): Prom
|
||||
}
|
||||
return
|
||||
} catch (err) {
|
||||
console.warn('[Redis] update 失败,降级内存:', err)
|
||||
logger.warn({ err }, 'Redis update 失败,降级内存')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +130,7 @@ export async function deleteCode(key: string): Promise<void> {
|
||||
await redis.del(`${KEY_PREFIX}${key}`)
|
||||
return
|
||||
} catch (err) {
|
||||
console.warn('[Redis] del 失败,降级内存:', err)
|
||||
logger.warn({ err }, 'Redis del 失败,降级内存')
|
||||
}
|
||||
}
|
||||
memoryStore.delete(key)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import logger from './logger'
|
||||
|
||||
const required = ['JWT_SECRET', 'JWT_REFRESH_SECRET', 'ENCRYPTION_KEY']
|
||||
const defaults: Record<string, string> = {
|
||||
JWT_SECRET: 'dev-secret',
|
||||
@@ -13,10 +15,10 @@ export function validateEnv(): void {
|
||||
}
|
||||
}
|
||||
if (missing.length > 0 && process.env.NODE_ENV === 'production') {
|
||||
console.error(`[FATAL] 以下环境变量未设置或使用了默认值,生产环境禁止启动: ${missing.join(', ')}`)
|
||||
logger.fatal({ missing }, '以下环境变量未设置或使用了默认值,生产环境禁止启动')
|
||||
process.exit(1)
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
console.warn(`[WARN] 以下环境变量使用了默认值,仅限开发环境: ${missing.join(', ')}`)
|
||||
logger.warn({ missing }, '以下环境变量使用了默认值,仅限开发环境')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import pino from 'pino'
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const logger = pino({
|
||||
level: process.env.LOG_LEVEL || (isDev ? 'debug' : 'info'),
|
||||
transport: isDev
|
||||
? {
|
||||
target: 'pino-pretty',
|
||||
options: {
|
||||
colorize: true,
|
||||
translateTime: 'HH:MM:ss',
|
||||
ignore: 'pid,hostname',
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
redact: {
|
||||
paths: ['req.headers.authorization', 'req.headers.cookie', '*.password', '*.passwordHash', '*.token', '*.refreshToken'],
|
||||
censor: '[REDACTED]',
|
||||
},
|
||||
})
|
||||
|
||||
export default logger
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 统一分页参数解析,全局限制 pageSize ≤ 200,防止过量数据查询
|
||||
*/
|
||||
export function parsePagination(query: Record<string, any>): { page: number; pageSize: number } {
|
||||
const page = Math.max(1, parseInt(query.page as string) || 1)
|
||||
const pageSize = Math.min(Math.max(1, parseInt(query.pageSize as string) || 20), 200)
|
||||
return { page, pageSize }
|
||||
}
|
||||
Reference in New Issue
Block a user