chanjs 2.6.8 → 2.6.10
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/App.js +0 -1
- package/README.md +493 -78
- package/helper/cache.js +23 -19
- package/index.js +1 -1
- package/middleware/template.js +1 -1
- package/package.json +1 -1
package/App.js
CHANGED
package/README.md
CHANGED
|
@@ -4,67 +4,62 @@ Chanjs 是一个基于 Express 5+ 构建的轻量级 MVC 框架,完全使用 J
|
|
|
4
4
|
|
|
5
5
|
## 特点
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- 文件操作
|
|
64
|
-
- 路径处理
|
|
65
|
-
- 树形结构转换
|
|
66
|
-
- 代码生成
|
|
67
|
-
- 过滤器
|
|
7
|
+
核心架构
|
|
8
|
+
✅ Express 5+ 原生
|
|
9
|
+
✅ Node.js 24+
|
|
10
|
+
✅ ES Modules (import / export)
|
|
11
|
+
✅ 多模块 MVC 架构
|
|
12
|
+
✅ 轻量级内核
|
|
13
|
+
✅ 约定优于配置
|
|
14
|
+
模块化(完全保留)
|
|
15
|
+
✅ 多模块路由
|
|
16
|
+
✅ 模块化 views
|
|
17
|
+
✅ 模块化 controllers
|
|
18
|
+
✅ 模块化 services
|
|
19
|
+
✅ 模块化 middleware
|
|
20
|
+
插件系统(增强)
|
|
21
|
+
✅ 插件式架构
|
|
22
|
+
✅ 插件独立 MVC
|
|
23
|
+
✅ 热插拔
|
|
24
|
+
✅ 支持独立 npm 插件(扩展口子)
|
|
25
|
+
数据库(补齐)
|
|
26
|
+
✅ Knex.js 查询构建器
|
|
27
|
+
✅ SQLite / MySQL / PostgreSQL
|
|
28
|
+
✅ 连接池
|
|
29
|
+
✅ 事务
|
|
30
|
+
✅ 自动时间格式化
|
|
31
|
+
安全能力(补齐)
|
|
32
|
+
✅ WAF 防火墙
|
|
33
|
+
✅ XSS 防护
|
|
34
|
+
✅ 关键词过滤
|
|
35
|
+
✅ 请求限流
|
|
36
|
+
✅ 路由白名单
|
|
37
|
+
✅ Cookie 安全
|
|
38
|
+
中间件生态(补齐)
|
|
39
|
+
✅ CORS
|
|
40
|
+
✅ 请求解析
|
|
41
|
+
✅ Cookie
|
|
42
|
+
✅ 静态资源
|
|
43
|
+
✅ 日志
|
|
44
|
+
✅ Art-template 模板引擎
|
|
45
|
+
高级特性(全部补齐)
|
|
46
|
+
✅ DI 依赖注入(Map 实现)
|
|
47
|
+
✅ AOP 切面(before/after/error)
|
|
48
|
+
✅ 事件系统
|
|
49
|
+
✅ 自动加载控制器
|
|
50
|
+
✅ 定时任务
|
|
51
|
+
✅ WebSocket
|
|
52
|
+
✅ 缓存系统 (Redis 和 内存缓存 map实现)
|
|
53
|
+
✅ 分页器
|
|
54
|
+
✅ 国际化 i18n
|
|
55
|
+
✅ 请求验证器
|
|
56
|
+
✅ 全局异常处理
|
|
57
|
+
✅ 接口文档自动生成
|
|
58
|
+
✅ 文件上传 / OSS 扩展口
|
|
59
|
+
开发体验
|
|
60
|
+
✅ 多环境配置
|
|
61
|
+
✅ 统一响应
|
|
62
|
+
✅ 工具函数
|
|
68
63
|
|
|
69
64
|
## 约定优于配置
|
|
70
65
|
|
|
@@ -100,6 +95,39 @@ Chanjs 是一个基于 Express 5+ 构建的轻量级 MVC 框架,完全使用 J
|
|
|
100
95
|
|- package.json
|
|
101
96
|
```
|
|
102
97
|
|
|
98
|
+
|
|
99
|
+
## npm包参考
|
|
100
|
+
|
|
101
|
+
"art-template": "^4.13.4",
|
|
102
|
+
"body-parser": "^2.2.0",
|
|
103
|
+
"cookie-parser": "^1.4.7",
|
|
104
|
+
"cors": "^2.8.5",
|
|
105
|
+
"dotenv": "^17.2.1",
|
|
106
|
+
"dayjs": "^1.11.13",
|
|
107
|
+
"express": "^5.1.0",
|
|
108
|
+
"express-art-template": "^1.0.1",
|
|
109
|
+
"knex": "^3.1.0",
|
|
110
|
+
"morgan": "^1.10.0",
|
|
111
|
+
"mysql2": "^3.14.1",
|
|
112
|
+
"marked": "^17.0.3"
|
|
113
|
+
"adm-zip": "^0.5.16",
|
|
114
|
+
"bcryptjs": "^3.0.2",
|
|
115
|
+
"better-sqlite3": "^12.8.0",
|
|
116
|
+
"chanjs": "^2.6.8",
|
|
117
|
+
"cheerio": "^1.1.2",
|
|
118
|
+
"crypto-js": "^4.2.0",
|
|
119
|
+
"dayjs": "^1.11.13",
|
|
120
|
+
"dotenv": "^17.2.1",
|
|
121
|
+
"iconv-lite": "^0.6.3",
|
|
122
|
+
"jsonwebtoken": "^9.0.2",
|
|
123
|
+
"multer": "^2.0.2",
|
|
124
|
+
"nodemailer": "^7.0.6",
|
|
125
|
+
"qiniu": "^7.14.0",
|
|
126
|
+
"serve-favicon": "^2.5.1",
|
|
127
|
+
"xml2js": "^0.6.2",
|
|
128
|
+
"xss": "^1.0.15",
|
|
129
|
+
"zod": "^4.1.11"
|
|
130
|
+
|
|
103
131
|
## 初始化过程
|
|
104
132
|
|
|
105
133
|
```mermaid
|
|
@@ -141,14 +169,7 @@ class UserService extends Service {
|
|
|
141
169
|
super("user");
|
|
142
170
|
}
|
|
143
171
|
|
|
144
|
-
|
|
145
|
-
const user = await this.findById(userId);
|
|
146
|
-
// 从依赖注入容器中获取 ArticleService 实例
|
|
147
|
-
const articleService = await this.get("article", "Article");
|
|
148
|
-
const articles = await articleService.findByUserId(userId);
|
|
149
|
-
|
|
150
|
-
return { user, articles };
|
|
151
|
-
}
|
|
172
|
+
|
|
152
173
|
}
|
|
153
174
|
|
|
154
175
|
class UserController extends Controller {
|
|
@@ -157,15 +178,7 @@ class UserController extends Controller {
|
|
|
157
178
|
}
|
|
158
179
|
|
|
159
180
|
async getUser(req, res, next) {
|
|
160
|
-
|
|
161
|
-
const { id } = req.params;
|
|
162
|
-
// 从依赖注入容器中获取 UserService 实例
|
|
163
|
-
const userService = await this.get("user", "User");
|
|
164
|
-
const result = await userService.findById(id);
|
|
165
|
-
res.json(this.success({ data: result.data }));
|
|
166
|
-
} catch (err) {
|
|
167
|
-
next(err);
|
|
168
|
-
}
|
|
181
|
+
|
|
169
182
|
}
|
|
170
183
|
}
|
|
171
184
|
```
|
|
@@ -346,3 +359,405 @@ app.run((port) => {
|
|
|
346
359
|
```
|
|
347
360
|
|
|
348
361
|
该框架专为寻求简单与功能之间平衡的开发者设计,为构建 Web 应用程序提供了一个强大的基础。
|
|
362
|
+
# Chanjs 框架官方最终文档
|
|
363
|
+
**基于 Express 5+ | Node.js 24+ | ESM | 纯 JS | 多模块 | 插件化 | 内置 ORM + 双缓存**
|
|
364
|
+
|
|
365
|
+
> 专为寻求「简洁高效」与「功能完整」平衡的开发者设计,保留函数式编程思想,约定优于配置,开箱即用。
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## 目录
|
|
370
|
+
1. [框架概述](#1-框架概述)
|
|
371
|
+
2. [核心特性](#2-核心特性)
|
|
372
|
+
3. [目录结构(约定优于配置)](#3-目录结构约定优于配置)
|
|
373
|
+
4. [快速开始](#4-快速开始)
|
|
374
|
+
5. [核心架构](#5-核心架构)
|
|
375
|
+
6. [核心功能详解](#6-核心功能详解)
|
|
376
|
+
7. [多模块系统](#7-多模块系统)
|
|
377
|
+
8. [插件系统](#8-插件系统)
|
|
378
|
+
9. [内置 ORM 数据层](#9-内置-orm-数据层)
|
|
379
|
+
10. [缓存系统(内存 + Redis 双驱动)](#10-缓存系统内存--redis-双驱动)
|
|
380
|
+
11. [高级特性](#11-高级特性)
|
|
381
|
+
12. [安全防护](#12-安全防护)
|
|
382
|
+
13. [开发工具与最佳实践](#13-开发工具与最佳实践)
|
|
383
|
+
14. [部署与运维](#14-部署与运维)
|
|
384
|
+
15. [扩展规范](#15-扩展规范)
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 1. 框架概述
|
|
389
|
+
### 1.1 定位
|
|
390
|
+
**Chanjs** 是基于 **Express 5+** 构建的轻量级 MVC 框架,采用纯 JavaScript(ES Modules)开发,融合函数式编程理念。框架以「约定优于配置」为核心,提供**多模块架构、插件化扩展、内置 ORM、双缓存支持**,兼顾开发效率与生产稳定性。
|
|
391
|
+
|
|
392
|
+
### 1.2 设计理念
|
|
393
|
+
- **轻量内核**:核心代码精简,无冗余依赖,启动快、性能优
|
|
394
|
+
- **约定优先**:标准化目录结构,减少配置代码,专注业务逻辑
|
|
395
|
+
- **多模块隔离**:业务模块独立开发、独立部署、互不干扰
|
|
396
|
+
- **插件化扩展**:统一扩展接口,支持第三方插件、自定义插件热插拔
|
|
397
|
+
- **统一生态**:内置 ORM、缓存、安全、工具函数,开箱即用
|
|
398
|
+
|
|
399
|
+
### 1.3 适用场景
|
|
400
|
+
- 中小型 Web 应用、API 服务
|
|
401
|
+
- 多模块业务系统
|
|
402
|
+
- 分布式部署应用(支持 Redis 缓存、多实例)
|
|
403
|
+
- 追求「简洁代码 + 完整功能」的开发场景
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## 2. 核心特性
|
|
408
|
+
### 基础核心
|
|
409
|
+
- ✅ **Express 5+ 原生**:兼容 Express 生态,高性能 HTTP 引擎
|
|
410
|
+
- ✅ **Node.js 24+ 支持**:ES Modules `import/export`,原生异步/await
|
|
411
|
+
- ✅ **纯 JS 开发**:无 TypeScript 依赖,降低学习成本,快速上手
|
|
412
|
+
- ✅ **MVC 架构**:清晰分层(Controller/Service/Model/View),职责明确
|
|
413
|
+
|
|
414
|
+
### 多模块与插件
|
|
415
|
+
- ✅ **多模块化设计**:模块独立路由、控制器、服务、中间件、视图
|
|
416
|
+
- ✅ **插件式架构**:热插拔插件,支持独立 npm 包插件扩展
|
|
417
|
+
- ✅ **模块独立部署**:单模块可独立启动,适配微服务拆分
|
|
418
|
+
|
|
419
|
+
### 数据层
|
|
420
|
+
- ✅ **内置轻量 ORM**:支持 SQLite/MySQL/PostgreSQL,链式查询,事务支持
|
|
421
|
+
- ✅ **自动时间戳**:create_time/update_time 自动维护
|
|
422
|
+
- ✅ **软删除**:默认支持,保留数据历史
|
|
423
|
+
- ✅ **关联查询**:hasOne/hasMany/belongsTo 关联,关联查询友好
|
|
424
|
+
|
|
425
|
+
### 缓存系统
|
|
426
|
+
- ✅ **双缓存驱动**:内存缓存(开发/轻量)+ Redis 缓存(生产/分布式)
|
|
427
|
+
- ✅ **统一 API**:同一套代码切换缓存驱动,无业务修改
|
|
428
|
+
- ✅ **多模块隔离**:自动加模块前缀,避免缓存键冲突
|
|
429
|
+
- ✅ **过期时间支持**:灵活设置缓存有效期,自动过期清理
|
|
430
|
+
|
|
431
|
+
### 高级特性
|
|
432
|
+
- ✅ **依赖注入(DI)**:基于 Map 实现,轻量高效,自动加载 Service/Controller
|
|
433
|
+
- ✅ **面向切面编程(AOP)**:支持 before/after/error 切面,统一处理业务逻辑
|
|
434
|
+
- ✅ **事件系统**:基于 EventEmitter,支持发布订阅,解耦业务逻辑
|
|
435
|
+
- ✅ **定时任务**:支持 Cron 表达式,自动执行统计、清理、同步任务
|
|
436
|
+
- ✅ **WebSocket 支持**:内置 WebSocket 服务,实时通信
|
|
437
|
+
- ✅ **分页封装**:统一分页查询格式,支持多条件分页
|
|
438
|
+
- ✅ **国际化 i18n**:多语言包支持,适配多场景
|
|
439
|
+
- ✅ **视图引擎**:内置 art-template 模板引擎,支持模块化视图
|
|
440
|
+
- ✅ **统一响应格式**:成功/失败/分页 统一返回,前端适配友好
|
|
441
|
+
- ✅ **全局异常处理**:404/500 统一捕获,错误信息标准化
|
|
442
|
+
|
|
443
|
+
### 安全防护
|
|
444
|
+
- ✅ **WAF 基础防护**:拦截恶意请求、异常请求参数
|
|
445
|
+
- ✅ **XSS 防护**:自动转义用户输入,防止 XSS 攻击
|
|
446
|
+
- ✅ **请求限流**:Rate Limiting 支持,防止暴力攻击
|
|
447
|
+
- ✅ **Cookie 安全**:自动处理 Cookie 安全属性,防止劫持
|
|
448
|
+
- ✅ **路径白名单**:限制敏感路径访问,提升访问安全
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## 3. 目录结构(约定优于配置)
|
|
453
|
+
```
|
|
454
|
+
chanjs-project/
|
|
455
|
+
├── app/ # 应用核心目录
|
|
456
|
+
│ ├── common/ # 公共类/工具
|
|
457
|
+
│ │ └── helper.js # 全局工具函数
|
|
458
|
+
│ ├── middleware/ # 全局中间件
|
|
459
|
+
│ │ └── cors.js # 跨域中间件示例
|
|
460
|
+
│ ├── modules/ # 业务模块(核心)
|
|
461
|
+
│ │ ├── user/ # 用户模块示例
|
|
462
|
+
│ │ │ ├── controller/ # 模块控制器
|
|
463
|
+
│ │ │ │ └── UserController.js
|
|
464
|
+
│ │ │ ├── service/ # 模块服务
|
|
465
|
+
│ │ │ │ └── UserService.js
|
|
466
|
+
│ │ │ ├── model/ # 模块ORM模型
|
|
467
|
+
│ │ │ │ └── User.js
|
|
468
|
+
│ │ │ ├── middleware/ # 模块级中间件
|
|
469
|
+
│ │ │ │ └── auth.js
|
|
470
|
+
│ │ │ ├── router.js # 模块路由
|
|
471
|
+
│ │ │ ├── views/ # 模块视图
|
|
472
|
+
│ │ │ │ └── info.art
|
|
473
|
+
│ │ │ └── lang/ # 模块多语言
|
|
474
|
+
│ │ │ └── zh-CN.js
|
|
475
|
+
│ │ └── product/ # 商品模块(同上结构)
|
|
476
|
+
│ ├── plugins/ # 插件目录(可npm包)
|
|
477
|
+
│ │ └── oss/ # OSS插件示例
|
|
478
|
+
│ │ ├── controller/
|
|
479
|
+
│ │ ├── router.js
|
|
480
|
+
│ │ └── config.js
|
|
481
|
+
│ └── router.js # 全局公共路由
|
|
482
|
+
├── config/ # 配置目录
|
|
483
|
+
│ ├── app.js # 应用配置
|
|
484
|
+
│ ├── database.js # 数据库配置
|
|
485
|
+
│ ├── cache.js # 缓存配置
|
|
486
|
+
│ ├── security.js # 安全配置
|
|
487
|
+
│ └── plugin.js # 插件配置
|
|
488
|
+
├── data/ # 数据目录(SQLite默认存储)
|
|
489
|
+
│ └── chanjs.db
|
|
490
|
+
├── doc/ # 文档目录
|
|
491
|
+
├── public/ # 静态资源目录
|
|
492
|
+
├── view/ # 全局视图目录
|
|
493
|
+
├── core/ # 框架内核(核心源码)
|
|
494
|
+
│ ├── chanjs.js # 框架入口
|
|
495
|
+
│ ├── orm.js # 内置ORM内核
|
|
496
|
+
│ ├── cache.js # 缓存系统
|
|
497
|
+
│ ├── di.js # 依赖注入
|
|
498
|
+
│ ├── aop.js # 面向切面
|
|
499
|
+
│ ├── event.js # 事件系统
|
|
500
|
+
│ ├── cron.js # 定时任务
|
|
501
|
+
│ ├── websocket.js # WebSocket
|
|
502
|
+
│ ├── router.js # 路由管理
|
|
503
|
+
│ ├── plugin.js # 插件管理器
|
|
504
|
+
│ ├── security.js # 安全防护
|
|
505
|
+
│ └── utils/ # 工具函数
|
|
506
|
+
├── storage/ # 存储目录(日志/上传文件)
|
|
507
|
+
├── app.js # 项目启动入口
|
|
508
|
+
├── .env.dev # 开发环境变量
|
|
509
|
+
├── .env.prd # 生产环境变量
|
|
510
|
+
├── pm2.json # PM2 部署配置
|
|
511
|
+
└── package.json # 项目依赖
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## 4. 快速开始
|
|
517
|
+
### 4.1 环境要求
|
|
518
|
+
- Node.js 24.0.0 及以上
|
|
519
|
+
- npm 9.0.0 及以上
|
|
520
|
+
- 支持 SQLite/MySQL/PostgreSQL(生产环境推荐 MySQL/PostgreSQL)
|
|
521
|
+
|
|
522
|
+
### 4.2 初始化项目
|
|
523
|
+
#### 方式一:使用脚手架(推荐)
|
|
524
|
+
```bash
|
|
525
|
+
# 安装脚手架
|
|
526
|
+
npm install -g chanjs-cli
|
|
527
|
+
|
|
528
|
+
# 创建项目
|
|
529
|
+
chanjs create my-chanjs-app
|
|
530
|
+
|
|
531
|
+
# 进入项目
|
|
532
|
+
cd my-chanjs-app
|
|
533
|
+
|
|
534
|
+
# 安装依赖
|
|
535
|
+
npm install
|
|
536
|
+
|
|
537
|
+
# 启动开发环境
|
|
538
|
+
npm run dev
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### 方式二:手动初始化
|
|
542
|
+
```bash
|
|
543
|
+
# 创建项目目录
|
|
544
|
+
mkdir chanjs-demo && cd chanjs-demo
|
|
545
|
+
|
|
546
|
+
# 初始化package.json
|
|
547
|
+
npm init -y
|
|
548
|
+
|
|
549
|
+
# 安装核心依赖
|
|
550
|
+
npm install express@5 knex sqlite3 mysql2 redis art-template cors helmet express-rate-limit ws
|
|
551
|
+
|
|
552
|
+
# 创建核心目录
|
|
553
|
+
mkdir -p app/modules/user/{controller,service,model,middleware,views,lang} app/{common,middleware} config core data public view storage
|
|
554
|
+
|
|
555
|
+
# 创建入口文件
|
|
556
|
+
touch app.js
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### 4.3 最小启动示例
|
|
560
|
+
#### 1. 配置文件(config/app.js)
|
|
561
|
+
```javascript
|
|
562
|
+
export default {
|
|
563
|
+
// 应用名称
|
|
564
|
+
name: 'ChanjsApp',
|
|
565
|
+
// 运行端口
|
|
566
|
+
port: process.env.PORT || 3000,
|
|
567
|
+
// 环境
|
|
568
|
+
env: process.env.NODE_ENV || 'development',
|
|
569
|
+
// 日志配置
|
|
570
|
+
log: {
|
|
571
|
+
level: 'info', // debug/info/warn/error
|
|
572
|
+
path: './storage/logs'
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
#### 2. 框架入口(core/chanjs.js)
|
|
578
|
+
```javascript
|
|
579
|
+
import express from 'express';
|
|
580
|
+
import { fileURLToPath } from 'url';
|
|
581
|
+
import { dirname, join } from 'path';
|
|
582
|
+
import { loadEnv } from './utils/env.js';
|
|
583
|
+
import { initDB } from './orm.js';
|
|
584
|
+
import { initCache } from './cache.js';
|
|
585
|
+
import { initPlugin } from './plugin.js';
|
|
586
|
+
import { initMiddleware } from './middleware.js';
|
|
587
|
+
import { initRouter } from './router.js';
|
|
588
|
+
import { initAOP } from './aop.js';
|
|
589
|
+
import { initEvent } from './event.js';
|
|
590
|
+
import { initCron } from './cron.js';
|
|
591
|
+
import { initWebSocket } from './websocket.js';
|
|
592
|
+
|
|
593
|
+
// 环境变量加载
|
|
594
|
+
loadEnv();
|
|
595
|
+
|
|
596
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
597
|
+
const __dirname = dirname(__filename);
|
|
598
|
+
|
|
599
|
+
class Chanjs {
|
|
600
|
+
constructor() {
|
|
601
|
+
this.app = express();
|
|
602
|
+
this.config = {};
|
|
603
|
+
this.modules = {};
|
|
604
|
+
this.plugins = {};
|
|
605
|
+
this.beforeStartFns = [];
|
|
606
|
+
this.afterStartFns = [];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// 加载配置
|
|
610
|
+
async loadConfig() {
|
|
611
|
+
const configFiles = await import.meta.glob('../config/**/*.js', { eager: true });
|
|
612
|
+
for (const [path, module] of Object.entries(configFiles)) {
|
|
613
|
+
const key = path.replace('../config/', '').replace('.js', '').replace('/', '/');
|
|
614
|
+
this.config[key] = module.default;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// 启动前钩子
|
|
619
|
+
beforeStart(fn) {
|
|
620
|
+
this.beforeStartFns.push(fn);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// 启动后钩子
|
|
624
|
+
afterStart(fn) {
|
|
625
|
+
this.afterStartFns.push(fn);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// 核心初始化
|
|
629
|
+
async start() {
|
|
630
|
+
// 1. 加载配置
|
|
631
|
+
await this.loadConfig();
|
|
632
|
+
// 2. 执行启动前钩子
|
|
633
|
+
for (const fn of this.beforeStartFns) {
|
|
634
|
+
await fn(this);
|
|
635
|
+
}
|
|
636
|
+
// 3. 初始化数据库
|
|
637
|
+
await initDB(this);
|
|
638
|
+
// 4. 初始化缓存
|
|
639
|
+
await initCache(this);
|
|
640
|
+
// 5. 初始化AOP
|
|
641
|
+
initAOP(this);
|
|
642
|
+
// 6. 初始化事件
|
|
643
|
+
initEvent(this);
|
|
644
|
+
// 7. 初始化定时任务
|
|
645
|
+
initCron(this);
|
|
646
|
+
// 8. 加载插件
|
|
647
|
+
await initPlugin(this);
|
|
648
|
+
// 9. 加载中间件
|
|
649
|
+
initMiddleware(this);
|
|
650
|
+
// 10. 加载模块路由
|
|
651
|
+
await initRouter(this);
|
|
652
|
+
// 11. 初始化WebSocket
|
|
653
|
+
initWebSocket(this);
|
|
654
|
+
// 12. 执行启动后钩子
|
|
655
|
+
for (const fn of this.afterStartFns) {
|
|
656
|
+
await fn(this);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// 运行服务器
|
|
661
|
+
run(callback) {
|
|
662
|
+
const port = this.config.app.port || 3000;
|
|
663
|
+
this.server = this.app.listen(port, () => {
|
|
664
|
+
callback && callback(port);
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// 关闭服务器
|
|
669
|
+
close() {
|
|
670
|
+
this.server.close();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export default Chanjs;
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
#### 3. 项目入口(app.js)
|
|
678
|
+
```javascript
|
|
679
|
+
import Chanjs from './core/chanjs.js';
|
|
680
|
+
|
|
681
|
+
// 创建应用实例
|
|
682
|
+
const app = new Chanjs();
|
|
683
|
+
|
|
684
|
+
// 启动前钩子
|
|
685
|
+
app.beforeStart(async (chan) => {
|
|
686
|
+
console.log(`🚀 Chanjs 准备启动 | 环境:${chan.config.app.env}`);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// 启动后钩子
|
|
690
|
+
app.afterStart(async (chan) => {
|
|
691
|
+
console.log(`✅ Chanjs 启动成功 | 地址:http://localhost:${chan.config.app.port}`);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// 初始化并启动
|
|
695
|
+
await app.start();
|
|
696
|
+
|
|
697
|
+
// 运行服务
|
|
698
|
+
app.run((port) => {
|
|
699
|
+
console.log(`🚀 服务器运行在 http://localhost:${port}`);
|
|
700
|
+
});
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### 4.4 启动项目
|
|
704
|
+
```bash
|
|
705
|
+
# 开发环境(热重载)
|
|
706
|
+
npm run dev
|
|
707
|
+
|
|
708
|
+
# 生产环境
|
|
709
|
+
npm start
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
访问 `http://localhost:3000`,默认返回 Chanjs 欢迎页,初始化完成。
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## 5. 核心架构
|
|
717
|
+
### 5.1 整体架构图
|
|
718
|
+
```
|
|
719
|
+
用户请求 → Express 引擎 → 全局中间件 → 模块路由 → 模块中间件 → 控制器
|
|
720
|
+
↓
|
|
721
|
+
控制器 → 依赖注入获取服务 → 服务调用ORM/缓存/插件 → 响应结果
|
|
722
|
+
↓
|
|
723
|
+
全局异常处理 → 日志记录 → 返回统一格式数据
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### 5.2 分层职责
|
|
727
|
+
| 层级 | 职责 | 核心文件 |
|
|
728
|
+
|------|------|----------|
|
|
729
|
+
| **应用层** | 框架入口、配置加载、模块/插件管理 | core/chanjs.js |
|
|
730
|
+
| **路由层** | 全局路由、模块路由分发 | core/router.js |
|
|
731
|
+
| **控制器层** | 处理请求、调用服务、返回响应 | app/modules/[module]/controller |
|
|
732
|
+
| **服务层** | 业务逻辑、数据处理、调用ORM/缓存 | app/modules/[module]/service |
|
|
733
|
+
| **数据层** | 数据库操作、模型定义、事务处理 | core/orm.js + app/modules/[module]/model |
|
|
734
|
+
| **缓存层** | 内存/Redis缓存统一管理 | core/cache.js |
|
|
735
|
+
| **插件层** | 扩展功能、热插拔管理 | core/plugin.js |
|
|
736
|
+
| **安全层** | 防护XSS/限流/WAF | core/security.js |
|
|
737
|
+
| **工具层** | 通用工具、环境变量、时间格式化 | core/utils/ |
|
|
738
|
+
|
|
739
|
+
### 5.3 初始化流程
|
|
740
|
+
```mermaid
|
|
741
|
+
graph TD
|
|
742
|
+
A[初始化 Chanjs 实例] --> B[加载配置文件]
|
|
743
|
+
B --> C[执行 beforeStart 钩子]
|
|
744
|
+
C --> D[初始化数据库 ORM]
|
|
745
|
+
D --> E[初始化缓存系统]
|
|
746
|
+
E --> F[初始化 AOP 切面]
|
|
747
|
+
F --> G[初始化事件系统]
|
|
748
|
+
G --> H[初始化定时任务]
|
|
749
|
+
H --> I[加载插件系统]
|
|
750
|
+
I --> J[加载全局中间件]
|
|
751
|
+
J --> K[加载模块路由]
|
|
752
|
+
K --> L[初始化 WebSocket]
|
|
753
|
+
L --> M[执行 afterStart 钩子]
|
|
754
|
+
M --> N[启动 Express 服务器]
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## 6. 核心功能详解
|
|
760
|
+
### 6.1 依赖注入(DI)
|
|
761
|
+
#### 6.1.1 核心设计
|
|
762
|
+
基于 Map 实现轻量级 DI 容器,支持 Service/Controller 自动加载,无需手动实例化,实现业务解耦。
|
|
763
|
+
|
package/helper/cache.js
CHANGED
|
@@ -34,16 +34,21 @@ class Cache {
|
|
|
34
34
|
* @param {number} [ttl=this.defaultTTL] - 过期时间(毫秒)
|
|
35
35
|
* @description
|
|
36
36
|
* 将键值对存入缓存,如果缓存已满则淘汰最久未使用的条目
|
|
37
|
-
* 每次设置都会更新访问时间
|
|
37
|
+
* 每次设置都会更新访问时间 + 过期时间
|
|
38
38
|
* @example
|
|
39
39
|
* cache.set('key1', 'value1', 60000); // 60 秒后过期
|
|
40
40
|
* cache.set('key2', { data: 123 }); // 使用默认过期时间
|
|
41
41
|
*/
|
|
42
42
|
set(key, value, ttl = this.defaultTTL) {
|
|
43
|
+
// 修复:缓存满 + 是新 key 才淘汰
|
|
43
44
|
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
44
45
|
this._evictLRU();
|
|
45
46
|
}
|
|
47
|
+
|
|
48
|
+
// 修复:无论 key 是否存在,都更新 过期时间 + 访问时间
|
|
46
49
|
this.cache.set(key, { value, expireAt: Date.now() + ttl });
|
|
50
|
+
// LRU:先删除再插入,保证 Map 尾部是最新访问
|
|
51
|
+
this.accessOrder.delete(key);
|
|
47
52
|
this.accessOrder.set(key, Date.now());
|
|
48
53
|
}
|
|
49
54
|
|
|
@@ -54,7 +59,7 @@ class Cache {
|
|
|
54
59
|
* @description
|
|
55
60
|
* 获取指定键的缓存值
|
|
56
61
|
* 如果值已过期,会自动删除该条目
|
|
57
|
-
* 每次获取都会更新访问时间
|
|
62
|
+
* 每次获取都会更新访问时间 + 刷新过期时间
|
|
58
63
|
* @example
|
|
59
64
|
* const value = cache.get('key1');
|
|
60
65
|
* if (value !== null) {
|
|
@@ -64,14 +69,22 @@ class Cache {
|
|
|
64
69
|
get(key) {
|
|
65
70
|
const item = this.cache.get(key);
|
|
66
71
|
if (!item) return null;
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
// 已过期直接删除
|
|
75
|
+
if (now > item.expireAt) {
|
|
69
76
|
this.cache.delete(key);
|
|
70
77
|
this.accessOrder.delete(key);
|
|
71
78
|
return null;
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
|
|
81
|
+
// 修复:命中缓存 → 刷新 LRU 顺序 + 刷新 TTL
|
|
82
|
+
this.accessOrder.delete(key);
|
|
83
|
+
this.accessOrder.set(key, now);
|
|
84
|
+
// 重新计算过期时间(访问续命,标准行为)
|
|
85
|
+
const ttl = item.expireAt - (now - (item.expireAt - this.defaultTTL));
|
|
86
|
+
this.cache.set(key, { value: item.value, expireAt: now + ttl });
|
|
87
|
+
|
|
75
88
|
return item.value;
|
|
76
89
|
}
|
|
77
90
|
|
|
@@ -141,20 +154,11 @@ class Cache {
|
|
|
141
154
|
* 淘汰最久未使用的条目(内部方法)
|
|
142
155
|
* @private
|
|
143
156
|
* @description
|
|
144
|
-
*
|
|
145
|
-
* 当缓存达到最大容量时自动调用
|
|
157
|
+
* Map 头部就是最早访问,直接删除(O(1))
|
|
146
158
|
*/
|
|
147
159
|
_evictLRU() {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
for (const [key, time] of this.accessOrder.entries()) {
|
|
152
|
-
if (time < oldestTime) {
|
|
153
|
-
oldestTime = time;
|
|
154
|
-
oldestKey = key;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
160
|
+
// 修复:Map 有序,keys().next() 直接拿到最久未使用的 key,性能 O(1)
|
|
161
|
+
const oldestKey = this.accessOrder.keys().next().value;
|
|
158
162
|
if (oldestKey) {
|
|
159
163
|
this.cache.delete(oldestKey);
|
|
160
164
|
this.accessOrder.delete(oldestKey);
|
|
@@ -180,4 +184,4 @@ class Cache {
|
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
export const cache = new Cache();
|
|
183
|
-
export default Cache;
|
|
187
|
+
export default Cache;
|
package/index.js
CHANGED
|
@@ -14,7 +14,7 @@ export * as common from "./common/index.js";
|
|
|
14
14
|
export * as extend from "./extend/index.js";
|
|
15
15
|
|
|
16
16
|
// 常用工具函数直接导出
|
|
17
|
-
export { loadConfig, loaderSort, loadController } from "./helper/index.js";
|
|
17
|
+
export { loadConfig, loaderSort, loadController ,cache} from "./helper/index.js";
|
|
18
18
|
|
|
19
19
|
// 路径配置
|
|
20
20
|
export { Paths } from "./config/paths.js";
|
package/middleware/template.js
CHANGED
|
@@ -24,7 +24,7 @@ import { importjs } from "../global/import.js";
|
|
|
24
24
|
export let setTemplate = (app, config) => {
|
|
25
25
|
const { views, NODE_ENV } = config;
|
|
26
26
|
const isProduction = NODE_ENV === "production" || NODE_ENV === "prd";
|
|
27
|
-
console.log("
|
|
27
|
+
console.log("模板缓存->", isProduction);
|
|
28
28
|
const all = [...views];
|
|
29
29
|
app.set("view options", {
|
|
30
30
|
debug: !isProduction,
|