优化工具类函数

This commit is contained in:
Jerry 2025-04-06 02:13:20 +08:00
parent 55de8a14bb
commit 267f81a008
27 changed files with 463 additions and 127 deletions

62
.idea/codeStyles/Project.xml generated Normal file
View File

@ -0,0 +1,62 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<HTMLCodeStyleSettings>
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="PREFER_EXPLICIT_TYPES_VARS_FIELDS" value="true" />
<option name="PREFER_EXPLICIT_TYPES_FUNCTION_RETURNS" value="true" />
<option name="PREFER_EXPLICIT_TYPES_FUNCTION_EXPRESSION_RETURNS" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</TypeScriptCodeStyleSettings>
<VueCodeStyleSettings>
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
</VueCodeStyleSettings>
<codeStyleSettings language="HTML">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Vue">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

8
.idea/prettier.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="MANUAL" />
<option name="myRunOnSave" value="true" />
<option name="myRunOnReformat" value="true" />
</component>
</project>

14
.idea/webResources.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebResourcesPaths">
<contentEntries>
<entry url="file://$PROJECT_DIR$">
<entryData>
<resourceRoots>
<path value="file://$PROJECT_DIR$/src" />
</resourceRoots>
</entryData>
</entry>
</contentEntries>
</component>
</project>

View File

@ -1,10 +1,10 @@
import express from 'express'; import express from 'express';
import logger from './utils/logger'; import logger from './utils/core/logger';
import paths from './utils/path'; import paths from './utils/core/path';
import sampleController from './modules/sample/sample.controller'; import sampleController from './modules/sample/sample.controller';
import imageController from './modules/image/image.controller'; import imageController from './modules/image/image.controller';
import config from './utils/config'; import Config from './utils/core/config';
import fc from './utils/file'; import fc from './utils/core/file';
const apps = { const apps = {
createApp() { createApp() {
@ -12,6 +12,8 @@ const apps = {
logger.info('晶灵核心初始化..'); logger.info('晶灵核心初始化..');
Config.check(['PORT', 'DEBUG']);
app.use(express.json()); app.use(express.json());
logger.debug('成功加载express.json()中间件'); logger.debug('成功加载express.json()中间件');
@ -28,7 +30,7 @@ const apps = {
app.use(module.path, module.controller.getRouter()); app.use(module.path, module.controller.getRouter());
logger.debug(`模块路由挂载: ${module.path.padEnd(12)}${module.name}`); logger.debug(`模块路由挂载: ${module.path.padEnd(12)}${module.name}`);
if (config.get('DEBUG', false)) { if (Config.get('DEBUG', false)) {
module.controller.getRouter().stack.forEach((layer) => { module.controller.getRouter().stack.forEach((layer) => {
if (layer.route) { if (layer.route) {
const methods = Object.keys(layer.route) const methods = Object.keys(layer.route)

View File

@ -1,5 +1,5 @@
import apps from './app'; import apps from './app';
import logger from './utils/logger'; import logger from './utils/core/logger';
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;

View File

@ -1,6 +1,6 @@
import express from 'express'; import express from 'express';
import ImageService from './image.service'; import ImageService from './image.service';
import logger from '../../utils/logger'; import logger from '../../utils/core/logger';
class ImageController { class ImageController {
private readonly router: express.Router; private readonly router: express.Router;

View File

@ -1,7 +1,7 @@
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import paths from '../../utils/path'; import paths from '../../utils/core/path';
import logger from '../../utils/logger'; import logger from '../../utils/core/logger';
class ImageService { class ImageService {
private readonly imageDir: string; private readonly imageDir: string;

1
src/modules/monitor/kep Normal file
View File

@ -0,0 +1 @@
keep

View File

@ -1,5 +1,6 @@
import express from 'express'; import express from 'express';
import sampleService from './sample.service'; import sampleService from './sample.service';
import response from '../../utils/core/response';
class SampleController { class SampleController {
private readonly router: express.Router; private readonly router: express.Router;
@ -21,37 +22,24 @@ class SampleController {
private getHello = (req: express.Request, res: express.Response): void => { private getHello = (req: express.Request, res: express.Response): void => {
try { try {
const result = sampleService.getHello(); const result = sampleService.getHello();
this.sendSuccess(res, result); response.success(res, result);
} catch (error) { } catch (error) {
this.sendError(res, error); response.error(res, '请求失败了..', 500, error);
} }
}; };
private postGreet = (req: express.Request, res: express.Response): void => { private postGreet = (req: express.Request, res: express.Response): void => {
try { try {
const { name } = req.body; const { name } = req.body;
if (!name) {
return response.error(res, '姓名不能为空!', 400);
}
const result = sampleService.generateGreeting(name); const result = sampleService.generateGreeting(name);
this.sendSuccess(res, result); response.success(res, result);
} catch (error) { } catch (error) {
this.sendError(res, error); response.error(res, '请求失败了..', 500, error);
} }
}; };
private sendSuccess(res: express.Response, data: any, statusCode = 200): void {
res.status(statusCode).json({
success: true,
data,
timestamp: new Date().toISOString(),
});
}
private sendError(res: express.Response, error: any, statusCode = 500): void {
res.status(statusCode).json({
success: false,
message: error.message,
timestamp: new Date().toISOString(),
});
}
} }
export default new SampleController(); export default new SampleController();

View File

@ -1,4 +1,4 @@
import logger from '../../utils/logger'; import logger from '../../utils/core/logger';
class SampleService { class SampleService {
getHello() { getHello() {
@ -6,7 +6,7 @@ class SampleService {
return { message: 'Hello World!' }; return { message: 'Hello World!' };
} }
generateGreeting(name: string) { generateGreeting(name: string): object {
logger.debug(`有个小可爱正在请求generateGreeting方法..`); logger.debug(`有个小可爱正在请求generateGreeting方法..`);
if (!name) { if (!name) {
logger.warn('Name is required'); logger.warn('Name is required');

1
src/modules/trss/keep Normal file
View File

@ -0,0 +1 @@
keep

View File

@ -1,23 +0,0 @@
import dotenv from 'dotenv';
dotenv.config();
let config = {
get: (key: string, defaultValue: any) => {
const value = process.env[key];
if (value === undefined) {
if (defaultValue !== undefined) return defaultValue;
throw new Error(`环境变量${key}未定义..`);
}
if (typeof defaultValue === 'number') return Number(value);
if (typeof defaultValue === 'boolean') return value === 'true';
return value;
},
set: (key: string, value: any) => {
process.env[key] = value;
},
};
export default config;

1
src/utils/constants/keep Normal file
View File

@ -0,0 +1 @@
keep

72
src/utils/core/config.ts Normal file
View File

@ -0,0 +1,72 @@
import dotenv from 'dotenv';
import logger from './logger';
class ConfigManger {
private static instance: ConfigManger;
private env: NodeJS.ProcessEnv;
private constructor() {
dotenv.config();
this.env = process.env;
}
/**
*
*/
public static getInstance(): ConfigManger {
if (!ConfigManger.instance) {
ConfigManger.instance = new ConfigManger();
}
return ConfigManger.instance;
}
/**
*
* @param key
* @param defaultValue
*/
public get<T = string>(key: string, defaultValue?: T): T {
const value = this.env[key];
if (value === undefined) {
if (defaultValue !== undefined) return defaultValue;
logger.error(`环境变量${key}未定义!`);
return undefined as T;
}
switch (typeof defaultValue) {
case 'number':
return Number(value) as T;
case 'boolean':
return (value === 'true') as T;
default:
return value as T;
}
}
/**
*
* @param key
* @param value
*/
public set(key: string, value: string | number | boolean): void {
this.env[key] = String(value);
logger.debug(`成功更改环境变量${key}${value}!`);
}
/**
*
*/
public check(keys: string[]): void {
keys.forEach((key) => {
if (!(key in this.env)) {
logger.error(`必须环境变量缺失:${key}`);
} else {
logger.debug(`检测到环境变量${key}!`);
}
});
}
}
const Config = ConfigManger.getInstance();
export default Config;

74
src/utils/core/date.ts Normal file
View File

@ -0,0 +1,74 @@
class date {
/**
* (格式: YYYYMMDD)
*/
public static getCurrentDate(): string {
const now = new Date();
return [
now.getFullYear(),
(now.getMonth() + 1).toString().padStart(2, '0'),
now.getDate().toString().padStart(2, '0'),
].join('');
}
/**
* (格式: HH:mm:ss)
*/
public static getCurrentTime(): string {
return new Date().toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
}
/**
*
* @param formatStr (YYYY-, MM-, DD-, HH-, mm-, ss-)
* @example format('YYYY-MM-DD HH:mm:ss') => '2023-10-15 14:30:45'
*/
public static format(formatStr: string = 'YYYY-MM-DD HH:mm:ss'): string {
const now = new Date();
const replacements: Record<string, string> = {
YYYY: now.getFullYear().toString(),
MM: (now.getMonth() + 1).toString().padStart(2, '0'),
DD: now.getDate().toString().padStart(2, '0'),
HH: now.getHours().toString().padStart(2, '0'),
mm: now.getMinutes().toString().padStart(2, '0'),
ss: now.getSeconds().toString().padStart(2, '0'),
};
return formatStr.replace(/YYYY|MM|DD|HH|mm|ss/g, (match) => replacements[match]);
}
/**
*
* @param start
* @param end ()
* @param unit ('days' | 'hours' | 'minutes' | 'seconds')
*/
public static diff(
start: Date,
end: Date = new Date(),
unit: 'days' | 'hours' | 'minutes' | 'seconds' = 'days'
): number {
const msDiff = end.getTime() - start.getTime();
switch (unit) {
case 'seconds':
return Math.floor(msDiff / 1000);
case 'minutes':
return Math.floor(msDiff / (1000 * 60));
case 'hours':
return Math.floor(msDiff / (1000 * 60 * 60));
case 'days':
return Math.floor(msDiff / (1000 * 60 * 60 * 24));
default:
return msDiff;
}
}
}
export default date;

View File

@ -1,13 +1,14 @@
import path from 'path'; import path from 'path';
import paths from './path';
import date from './date';
import fs from 'fs'; import fs from 'fs';
import chalk from 'chalk'; import chalk from 'chalk';
import paths from './path';
import date from './date';
import logger from './logger'; import logger from './logger';
let fc = { class fc {
createDir: (targetPath: string = '', includeFile: boolean = false) => { public static createDir(targetPath: string = '', includeFile: boolean = false): void {
const root = paths.get('root'); const root = paths.get('root');
if (path.isAbsolute(targetPath)) { if (path.isAbsolute(targetPath)) {
if (includeFile) { if (includeFile) {
const parentDir = path.dirname(targetPath); const parentDir = path.dirname(targetPath);
@ -17,6 +18,7 @@ let fc = {
} }
return; return;
} }
if (!fs.existsSync(targetPath)) { if (!fs.existsSync(targetPath)) {
fs.mkdirSync(targetPath, { recursive: true }); fs.mkdirSync(targetPath, { recursive: true });
logger.debug(`成功创建绝对目录: ${targetPath}`); logger.debug(`成功创建绝对目录: ${targetPath}`);
@ -27,20 +29,21 @@ let fc = {
const fullPath = includeFile const fullPath = includeFile
? path.join(root, path.dirname(targetPath)) ? path.join(root, path.dirname(targetPath))
: path.join(root, targetPath); : path.join(root, targetPath);
if (!fs.existsSync(fullPath)) { if (!fs.existsSync(fullPath)) {
fs.mkdirSync(fullPath, { recursive: true }); fs.mkdirSync(fullPath, { recursive: true });
logger.debug(`成功创建相对目录: ${fullPath}`); logger.debug(`成功创建相对目录: ${fullPath}`);
} }
}, }
logToFile: (level: string, message: string) => { public static logToFile(level: string, message: string): void {
const logFile = path.join(paths.get('log'), `${date.getCurrentDate()}.log`); const logFile = path.join(paths.get('log'), `${date.getCurrentDate()}.log`);
const logMessage = `[${date.getCurrentTime()}] [${level}] ${message}\n`; const logMessage = `[${date.getCurrentTime()}] [${level}] ${message}\n`;
fs.appendFile(logFile, logMessage, (err) => { fs.appendFile(logFile, logMessage, (err) => {
if (err) console.error(chalk.red('[LOGGER] 写入日志失败:'), err); if (err) console.error(chalk.red('[LOGGER] 写入日志失败:'), err);
}); });
}, }
}; }
export default fc; export default fc;

68
src/utils/core/logger.ts Normal file
View File

@ -0,0 +1,68 @@
import chalk from 'chalk';
import Config from './config';
import fc from './file';
class Logger {
private static instance: Logger;
private readonly isDebug: boolean;
private constructor() {
this.isDebug = Config.get('DEBUG', false);
}
public static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
public debug(...args: any[]): void {
if (this.isDebug) {
const message = this.formatMessage('DEBUG', args);
console.log(chalk.cyan(message));
}
}
public info(...args: any[]): void {
const message = this.formatMessage('INFO', args);
console.log(chalk.green(message));
this.logToFile('INFO', message);
}
public warn(...args: any[]): void {
const message = this.formatMessage('WARN', args);
console.log(chalk.yellow(message));
this.logToFile('WARN', message);
}
public error(...args: any[]): void {
const message = this.formatMessage('ERROR', args);
console.error(chalk.red(message));
this.logToFile('ERROR', message);
}
public fatal(args: any[], exitCode: number = 1): never {
const message = this.formatMessage('FATAL', args);
console.error(chalk.red.bold(message));
this.logToFile('FATAL', message);
process.exit(exitCode);
}
private formatMessage(level: string, args: any[]): string {
return `[${level}] ${args
.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : arg))
.join(' ')}`;
}
private logToFile(level: string, message: string): void {
try {
fc.logToFile(level, `${new Date().toISOString()} ${message}`);
} catch (err: any) {
console.error(chalk.red(`[LOGGER] 写入日志失败: ${err.message}`));
}
}
}
const logger = Logger.getInstance();
export default logger;

62
src/utils/core/path.ts Normal file
View File

@ -0,0 +1,62 @@
import path from 'path';
import fs from 'fs';
class PathManager {
private static instance: PathManager;
private readonly baseDir: string;
private constructor() {
this.baseDir = path.join(__dirname, '../..');
}
/**
*
*/
public static getInstance(): PathManager {
if (!PathManager.instance) {
PathManager.instance = new PathManager();
}
return PathManager.instance;
}
/**
*
* @param type
*/
public get(type?: PathType): string {
const mappings: Record<PathType, string> = {
root: this.baseDir,
public: path.join(this.baseDir, 'public'),
images: path.join(this.baseDir, 'public/images'),
log: path.join(this.baseDir, 'logs'),
config: path.join(this.baseDir, 'config'),
temp: path.join(this.baseDir, 'temp'),
};
return type ? mappings[type] : this.baseDir;
}
/**
*
* @param segments
*/
public resolve(...segments: string[]): string {
return path.join(this.baseDir, ...segments);
}
/**
*
*/
public existsSync(targetPath: string): boolean {
try {
return fs.existsSync(targetPath);
} catch {
return false;
}
}
}
type PathType = 'root' | 'public' | 'images' | 'log' | 'config' | 'temp';
const paths = PathManager.getInstance();
export default paths;

View File

@ -0,0 +1,61 @@
import { Response } from 'express';
import logger from './logger';
class response {
/**
*
* @param res Express响应对象
* @param data
* @param statusCode HTTP状态码200
*/
static success(res: Response, data: any, statusCode = 200) {
res.status(statusCode).json({
success: true,
data,
timestamp: new Date().toISOString(),
});
}
/**
*
* @param res Express响应对象
* @param message
* @param statusCode HTTP状态码500
* @param error
*/
static error(res: Response, message: string, statusCode = 500, error?: any) {
const response: Record<string, any> = {
success: false,
message,
timestamp: new Date().toISOString(),
};
logger.debug(error instanceof Error ? error.stack : error);
res.status(statusCode).json(response);
}
/**
*
* @param res Express响应对象
* @param data
* @param total
* @param page
* @param pageSize
*/
static pagination(res: Response, data: any[], total: number, page: number, pageSize: number) {
res.status(200).json({
success: true,
data,
pagination: {
total,
page,
pageSize,
totalPages: Math.ceil(total / pageSize),
},
timestamp: new Date().toISOString(),
});
}
}
export default response;

View File

@ -1,11 +0,0 @@
let date = {
getCurrentDate: () => {
const now = new Date();
return `${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}`;
},
getCurrentTime: () => {
return new Date().toLocaleTimeString('en-US', { hour12: false });
},
};
export default date;

1
src/utils/helpers/keep Normal file
View File

@ -0,0 +1 @@
keep

0
src/utils/index.ts Normal file
View File

1
src/utils/lib/keep Normal file
View File

@ -0,0 +1 @@
keep

View File

@ -1,30 +0,0 @@
import chalk from 'chalk';
import config from './config';
import fc from './file';
const logger = {
debug: (...args: any[]) => {
if (config.get('DEBUG', false)) {
//const message = args.join(' ');
console.log(chalk.cyan('[DEBUG]'), ...args);
//fc.logToFile('DEBUG', message);
}
},
info: (...args: any[]) => {
const message = args.join(' ');
console.log(chalk.green('[INFO]'), ...args);
fc.logToFile('INFO', message);
},
warn: (...args: any[]) => {
const message = args.join(' ');
console.log(chalk.yellow('[WARN]'), ...args);
fc.logToFile('WARN', message);
},
error: (...args: any[]) => {
const message = args.join(' ');
console.log(chalk.red('[ERROR]'), ...args);
fc.logToFile('ERROR', message);
},
};
export default logger;

View File

@ -1,24 +0,0 @@
import path from 'path';
const paths = {
get: (value?: string) => {
switch (value) {
case 'root':
return path.join(__dirname, '..');
case 'public':
return path.join(__dirname, '../public');
case 'images':
return path.join(__dirname, '../../public/images/');
case 'log':
return path.join(__dirname, '../../logs');
default:
return path.join(__dirname, '..');
}
},
resolve: (segments: string) => {
return path.join(__dirname, segments);
},
};
export default paths;