diff --git a/.qoder/quests/clean-src-directory.md b/.qoder/quests/clean-src-directory.md
new file mode 100644
index 0000000..4f62a59
--- /dev/null
+++ b/.qoder/quests/clean-src-directory.md
@@ -0,0 +1,330 @@
+# src 目录整理设计
+
+## 概述
+
+本设计旨在优化 koa3-demo 项目的 src 目录结构,在保持现有 MVC 架构不变的前提下,通过最小化的代码改动来提升代码组织的清晰度和可维护性。
+
+## 当前目录结构分析
+
+### 现状问题
+- 目录层级较为扁平,业务模块分散
+- controllers 按类型(Api/Page)分组,但缺乏按业务领域组织
+- services 和 models 分离在不同的顶级目录
+- 缺乏统一的模块组织规范
+
+### 优势保持
+- MVC 架构清晰,分层明确
+- 中间件系统完善
+- 路由自动发现机制完整
+- 数据库操作层设计合理
+
+## 整理方案
+
+### 目标架构
+
+采用**领域驱动的模块化架构**,在保持 MVC 分层的同时,按业务领域组织代码:
+
+```mermaid
+graph TB
+ subgraph "src 目录结构"
+ A[src/] --> B[app/]
+ A --> C[modules/]
+ A --> D[shared/]
+ A --> E[presentation/]
+
+ B --> B1[config/]
+ B --> B2[providers/]
+ B --> B3[bootstrap/]
+
+ C --> C1[auth/]
+ C --> C2[article/]
+ C --> C3[user/]
+ C --> C4[site-config/]
+
+ D --> D1[utils/]
+ D --> D2[constants/]
+ D --> D3[helpers/]
+
+ E --> E1[middlewares/]
+ E --> E2[routes/]
+ E --> E3[views/]
+
+ subgraph "模块内部结构"
+ C1 --> C1A[controllers/]
+ C1 --> C1B[services/]
+ C1 --> C1C[models/]
+ C1 --> C1D[validators/]
+ end
+ end
+```
+
+### 详细目录规划
+
+#### 1. app/ - 应用核心
+```
+app/
+├── config/ # 配置管理
+│ ├── index.js # 当前 src/config/index.js
+│ └── database.js # 数据库配置(从 db/ 提取)
+├── providers/ # 服务提供者
+│ ├── DatabaseProvider.js # 数据库连接管理
+│ └── index.js # 提供者注册
+└── bootstrap/ # 应用引导
+ ├── app.js # 应用实例(当前 global.js)
+ ├── logger.js # 日志配置
+ └── index.js # 引导入口
+```
+
+#### 2. modules/ - 业务模块
+```
+modules/
+├── auth/ # 认证模块
+│ ├── controllers/
+│ │ ├── AuthController.js # API 控制器
+│ │ └── AuthPageController.js # 页面控制器
+│ ├── services/
+│ │ └── AuthService.js # 从 userService.js 提取认证相关
+│ ├── models/
+│ │ └── UserModel.js # 用户模型
+│ └── validators/
+│ └── AuthValidator.js # 认证验证规则
+├── user/ # 用户管理模块
+│ ├── controllers/
+│ │ └── ProfileController.js # 用户资料控制器
+│ ├── services/
+│ │ └── UserService.js # 用户业务服务
+│ └── models/
+│ └── UserModel.js # 用户模型(共享)
+├── article/ # 文章管理模块
+│ ├── controllers/
+│ │ └── ArticleController.js
+│ ├── services/
+│ │ └── ArticleService.js
+│ └── models/
+│ └── ArticleModel.js
+├── bookmark/ # 书签管理模块
+│ ├── services/
+│ │ └── BookmarkService.js
+│ └── models/
+│ └── BookmarkModel.js
+├── site-config/ # 站点配置模块
+│ ├── services/
+│ │ └── SiteConfigService.js
+│ └── models/
+│ └── SiteConfigModel.js
+└── common/ # 通用模块
+ ├── controllers/
+ │ ├── ApiController.js
+ │ ├── StatusController.js
+ │ └── UploadController.js
+ └── services/
+ └── JobService.js
+```
+
+#### 3. shared/ - 共享资源
+```
+shared/
+├── utils/ # 工具函数
+│ ├── error/
+│ ├── router/
+│ ├── bcrypt.js
+│ ├── helper.js
+│ └── scheduler.js
+├── constants/ # 常量定义
+│ ├── errors.js
+│ └── status.js
+└── base/ # 基础类
+ ├── BaseController.js
+ ├── BaseService.js
+ └── BaseModel.js
+```
+
+#### 4. presentation/ - 表现层
+```
+presentation/
+├── middlewares/ # 中间件(当前 middlewares/)
+├── routes/ # 路由管理
+│ ├── api.js # API 路由
+│ ├── web.js # 页面路由
+│ └── index.js # 路由注册
+└── views/ # 视图模板(当前 views/)
+```
+
+#### 5. infrastructure/ - 基础设施
+```
+infrastructure/
+├── database/ # 数据库相关
+│ ├── migrations/ # 迁移文件
+│ ├── seeds/ # 种子数据
+│ ├── docs/ # 数据库文档
+│ └── index.js # 数据库连接
+└── jobs/ # 定时任务
+ ├── exampleJob.js
+ └── index.js
+```
+
+## 迁移策略
+
+### 阶段一:创建新目录结构
+1. 创建 `app/`, `modules/`, `shared/`, `presentation/`, `infrastructure/` 目录
+2. 建立各模块的子目录结构
+
+### 阶段二:文件迁移与重组
+1. **配置文件迁移**
+ - `config/` → `app/config/`
+ - 从 `db/index.js` 提取数据库配置到 `app/config/database.js`
+
+2. **核心文件重组**
+ - `global.js` → `app/bootstrap/app.js`
+ - `logger.js` → `app/bootstrap/logger.js`
+ - 创建 `app/bootstrap/index.js` 统一引导
+
+3. **业务模块化**
+ - 按业务领域创建模块目录
+ - 将相关的 controllers, services, models 组织到对应模块
+
+4. **基础设施迁移**
+ - `db/` → `infrastructure/database/`
+ - `jobs/` → `infrastructure/jobs/`
+ - `middlewares/` → `presentation/middlewares/`
+ - `views/` → `presentation/views/`
+
+### 阶段三:更新引用路径
+1. 更新 `main.js` 中的导入路径
+2. 更新中间件注册路径
+3. 更新服务间的依赖引用
+4. 更新路由自动发现配置
+
+## 实现细节
+
+### 模块内 MVC 组织
+
+每个业务模块内部采用标准的 MVC 分层:
+
+```mermaid
+graph LR
+ subgraph "auth 模块"
+ AC[AuthController] --> AS[AuthService]
+ AS --> UM[UserModel]
+ APC[AuthPageController] --> AS
+ end
+
+ subgraph "共享层"
+ BC[BaseController]
+ BS[BaseService]
+ BM[BaseModel]
+ end
+
+ AC -.-> BC
+ AS -.-> BS
+ UM -.-> BM
+```
+
+### 路径映射表
+
+| 当前路径 | 新路径 | 说明 |
+|---------|-------|------|
+| `src/config/` | `src/app/config/` | 配置管理 |
+| `src/global.js` | `src/app/bootstrap/app.js` | 应用实例 |
+| `src/logger.js` | `src/app/bootstrap/logger.js` | 日志配置 |
+| `src/controllers/Api/AuthController.js` | `src/modules/auth/controllers/AuthController.js` | 认证API控制器 |
+| `src/controllers/Page/AuthPageController.js` | `src/modules/auth/controllers/AuthPageController.js` | 认证页面控制器 |
+| `src/services/userService.js` | `src/modules/auth/services/AuthService.js` + `src/modules/user/services/UserService.js` | 拆分认证和用户服务 |
+| `src/db/models/UserModel.js` | `src/modules/auth/models/UserModel.js` | 用户模型 |
+| `src/middlewares/` | `src/presentation/middlewares/` | 中间件 |
+| `src/views/` | `src/presentation/views/` | 视图模板 |
+| `src/db/migrations/` | `src/infrastructure/database/migrations/` | 数据库迁移 |
+| `src/jobs/` | `src/infrastructure/jobs/` | 定时任务 |
+
+### 导入路径更新
+
+#### main.js 更新示例
+```javascript
+// 原始导入
+import { app } from "./global"
+import { logger } from "./logger.js"
+import "./jobs/index.js"
+import LoadMiddlewares from "./middlewares/install.js"
+
+// 更新后导入
+import { app } from "./app/bootstrap/app.js"
+import { logger } from "./app/bootstrap/logger.js"
+import "./infrastructure/jobs/index.js"
+import LoadMiddlewares from "./presentation/middlewares/install.js"
+```
+
+### 自动路由发现适配
+
+更新路由扫描配置以适配新的模块结构:
+
+```javascript
+// 扫描路径配置
+const scanPaths = [
+ 'src/modules/*/controllers/**/*.js',
+ 'src/modules/common/controllers/**/*.js'
+];
+```
+
+## 文件操作清单
+
+### 需要移动的文件
+- [ ] `src/config/` → `src/app/config/`
+- [ ] `src/global.js` → `src/app/bootstrap/app.js`
+- [ ] `src/logger.js` → `src/app/bootstrap/logger.js`
+- [ ] `src/db/` → `src/infrastructure/database/`
+- [ ] `src/jobs/` → `src/infrastructure/jobs/`
+- [ ] `src/middlewares/` → `src/presentation/middlewares/`
+- [ ] `src/views/` → `src/presentation/views/`
+- [ ] `src/utils/` → `src/shared/utils/`
+
+### 需要按模块重组的文件
+- [ ] 认证相关:`AuthController.js`, `AuthPageController.js`, `userService.js`(部分), `UserModel.js`
+- [ ] 用户管理:`ProfileController.js`, `userService.js`(部分)
+- [ ] 文章管理:`ArticleController.js`, `ArticleService.js`, `ArticleModel.js`
+- [ ] 书签管理:`BookmarkService.js`, `BookmarkModel.js`
+- [ ] 站点配置:`SiteConfigService.js`, `SiteConfigModel.js`
+- [ ] 通用功能:`ApiController.js`, `StatusController.js`, `UploadController.js`, `JobController.js`
+
+### 需要更新导入的文件
+- [ ] `src/main.js`
+- [ ] `src/presentation/middlewares/install.js`
+- [ ] 各个控制器文件中的服务导入
+- [ ] 各个服务文件中的模型导入
+
+## 预期效果
+
+### 代码组织改善
+1. **业务内聚性**:相关的 controller、service、model 集中在同一模块
+2. **职责清晰**:每个模块负责特定的业务领域
+3. **依赖明确**:模块间依赖关系更加清晰可见
+
+### 开发体验提升
+1. **文件查找**:按业务功能快速定位相关文件
+2. **代码维护**:修改某个功能时,相关文件集中在一个目录
+3. **团队协作**:不同开发者可以专注于不同的业务模块
+
+### 架构扩展性
+1. **新增模块**:按照统一规范快速创建新的业务模块
+2. **功能拆分**:大模块可以进一步拆分成子模块
+3. **微服务准备**:为将来可能的微服务拆分做好准备
+
+## 风险评估与注意事项
+
+### 低风险操作
+- 目录创建和文件移动
+- 路径引用更新
+- 配置文件调整
+
+### 需要谨慎的操作
+- 路由自动发现逻辑调整
+- 中间件注册顺序维护
+- 数据库连接管理迁移
+
+### 测试验证点
+1. 应用启动正常
+2. 路由注册成功
+3. 数据库连接正常
+4. 中间件功能完整
+5. 各业务模块功能正常
+
+本方案通过最小化改动实现目录结构优化,保持原有架构优势的同时提升代码组织质量,为项目的长期维护和扩展奠定良好基础。
\ No newline at end of file
diff --git a/src/app/bootstrap/app.js b/src/app/bootstrap/app.js
new file mode 100644
index 0000000..0d9b2ab
--- /dev/null
+++ b/src/app/bootstrap/app.js
@@ -0,0 +1,27 @@
+/**
+ * 应用实例
+ *
+ * 创建和配置 Koa 应用实例
+ */
+
+import Koa from "koa";
+import { logger } from "./logger.js";
+import { validateEnvironment } from "../../shared/utils/envValidator.js";
+
+// 启动前验证环境变量
+if (!validateEnvironment()) {
+ logger.error("环境变量验证失败,应用退出");
+ process.exit(1);
+}
+
+const app = new Koa({ asyncLocalStorage: true });
+
+app.keys = [];
+
+// SESSION_SECRET 已通过环境变量验证确保存在
+process.env.SESSION_SECRET.split(",").forEach(secret => {
+ app.keys.push(secret.trim());
+});
+
+export { app };
+export default app;
\ No newline at end of file
diff --git a/src/app/bootstrap/index.js b/src/app/bootstrap/index.js
new file mode 100644
index 0000000..25ee3da
--- /dev/null
+++ b/src/app/bootstrap/index.js
@@ -0,0 +1,22 @@
+/**
+ * 应用引导索引文件
+ *
+ * 统一导出应用引导相关模块
+ */
+
+import app from "./app.js";
+import { logger, jobLogger, errorLogger } from "./logger.js";
+
+export {
+ app,
+ logger,
+ jobLogger,
+ errorLogger
+};
+
+export default {
+ app,
+ logger,
+ jobLogger,
+ errorLogger
+};
\ No newline at end of file
diff --git a/src/logger.js b/src/app/bootstrap/logger.js
similarity index 94%
rename from src/logger.js
rename to src/app/bootstrap/logger.js
index 06392df..c0523f6 100644
--- a/src/logger.js
+++ b/src/app/bootstrap/logger.js
@@ -1,3 +1,8 @@
+/**
+ * 日志配置
+ *
+ * 配置和导出日志记录器
+ */
import log4js from "log4js";
@@ -60,4 +65,4 @@ log4js.configure({
// 导出常用 logger 实例,便于直接引用
export const logger = log4js.getLogger(); // default
export const jobLogger = log4js.getLogger('jobs');
-export const errorLogger = log4js.getLogger('error');
+export const errorLogger = log4js.getLogger('error');
\ No newline at end of file
diff --git a/src/app/config/database.js b/src/app/config/database.js
new file mode 100644
index 0000000..ac3fcc8
--- /dev/null
+++ b/src/app/config/database.js
@@ -0,0 +1,55 @@
+/**
+ * 数据库配置文件
+ *
+ * 提供数据库连接配置和相关设置
+ */
+
+const environment = process.env.NODE_ENV || "development";
+
+// 数据库配置映射
+const databaseConfig = {
+ development: {
+ client: "sqlite3",
+ connection: {
+ filename: "./database/development.sqlite3"
+ },
+ useNullAsDefault: true,
+ migrations: {
+ directory: "./src/infrastructure/database/migrations"
+ },
+ seeds: {
+ directory: "./src/infrastructure/database/seeds"
+ }
+ },
+
+ test: {
+ client: "sqlite3",
+ connection: {
+ filename: ":memory:"
+ },
+ useNullAsDefault: true,
+ migrations: {
+ directory: "./src/infrastructure/database/migrations"
+ },
+ seeds: {
+ directory: "./src/infrastructure/database/seeds"
+ }
+ },
+
+ production: {
+ client: "sqlite3",
+ connection: {
+ filename: process.env.DATABASE_URL || "./database/production.sqlite3"
+ },
+ useNullAsDefault: true,
+ migrations: {
+ directory: "./src/infrastructure/database/migrations"
+ },
+ seeds: {
+ directory: "./src/infrastructure/database/seeds"
+ }
+ }
+};
+
+export default databaseConfig[environment];
+export { databaseConfig };
\ No newline at end of file
diff --git a/src/config/index.js b/src/app/config/index.js
similarity index 100%
rename from src/config/index.js
rename to src/app/config/index.js
diff --git a/src/app/providers/DatabaseProvider.js b/src/app/providers/DatabaseProvider.js
new file mode 100644
index 0000000..7a7b953
--- /dev/null
+++ b/src/app/providers/DatabaseProvider.js
@@ -0,0 +1,216 @@
+/**
+ * 数据库服务提供者
+ *
+ * 负责数据库连接管理、迁移管理和查询缓存
+ */
+
+import buildKnex from "knex";
+import databaseConfig from "../config/database.js";
+
+// 简单内存缓存(支持 TTL 与按前缀清理)
+const queryCache = new Map();
+
+const getNow = () => Date.now();
+
+const computeExpiresAt = (ttlMs) => {
+ if (!ttlMs || ttlMs <= 0) return null;
+ return getNow() + ttlMs;
+};
+
+const isExpired = (entry) => {
+ if (!entry) return true;
+ if (entry.expiresAt == null) return false;
+ return entry.expiresAt <= getNow();
+};
+
+const getCacheKeyForBuilder = (builder) => {
+ if (builder._customCacheKey) return String(builder._customCacheKey);
+ return builder.toString();
+};
+
+// 全局工具,便于在 QL 外部操作缓存
+export const DbQueryCache = {
+ get(key) {
+ const entry = queryCache.get(String(key));
+ if (!entry) return undefined;
+ if (isExpired(entry)) {
+ queryCache.delete(String(key));
+ return undefined;
+ }
+ return entry.value;
+ },
+ set(key, value, ttlMs) {
+ const expiresAt = computeExpiresAt(ttlMs);
+ queryCache.set(String(key), { value, expiresAt });
+ return value;
+ },
+ has(key) {
+ const entry = queryCache.get(String(key));
+ return !!entry && !isExpired(entry);
+ },
+ delete(key) {
+ return queryCache.delete(String(key));
+ },
+ clear() {
+ queryCache.clear();
+ },
+ clearByPrefix(prefix) {
+ const p = String(prefix);
+ for (const k of queryCache.keys()) {
+ if (k.startsWith(p)) queryCache.delete(k);
+ }
+ },
+ stats() {
+ let valid = 0;
+ let expired = 0;
+ for (const [k, entry] of queryCache.entries()) {
+ if (isExpired(entry)) expired++;
+ else valid++;
+ }
+ return { size: queryCache.size, valid, expired };
+ }
+};
+
+class DatabaseProvider {
+ constructor() {
+ this.db = null;
+ this._isExtended = false;
+ }
+
+ /**
+ * 注册数据库连接
+ */
+ register() {
+ if (this.db) {
+ return this.db;
+ }
+
+ // 创建 Knex 实例
+ this.db = buildKnex(databaseConfig);
+
+ // 扩展 QueryBuilder 缓存功能(只在第一次初始化时扩展)
+ if (!this._isExtended) {
+ this._extendQueryBuilder();
+ this._isExtended = true;
+ }
+
+ return this.db;
+ }
+
+ /**
+ * 扩展 QueryBuilder 的缓存功能
+ */
+ _extendQueryBuilder() {
+ // 1) cache(ttlMs?): 读取缓存,不存在则执行并写入
+ buildKnex.QueryBuilder.extend("cache", async function (ttlMs) {
+ const key = getCacheKeyForBuilder(this);
+ const entry = queryCache.get(key);
+ if (entry && !isExpired(entry)) {
+ return entry.value;
+ }
+ const data = await this;
+ queryCache.set(key, { value: data, expiresAt: computeExpiresAt(ttlMs) });
+ return data;
+ });
+
+ // 2) cacheAs(customKey): 设置自定义 key
+ buildKnex.QueryBuilder.extend("cacheAs", function (customKey) {
+ this._customCacheKey = String(customKey);
+ return this;
+ });
+
+ // 3) cacheSet(value, ttlMs?): 手动设置当前查询 key 的缓存
+ buildKnex.QueryBuilder.extend("cacheSet", function (value, ttlMs) {
+ const key = getCacheKeyForBuilder(this);
+ queryCache.set(key, { value, expiresAt: computeExpiresAt(ttlMs) });
+ return value;
+ });
+
+ // 4) cacheGet(): 仅从缓存读取当前查询 key 的值
+ buildKnex.QueryBuilder.extend("cacheGet", function () {
+ const key = getCacheKeyForBuilder(this);
+ const entry = queryCache.get(key);
+ if (!entry || isExpired(entry)) return undefined;
+ return entry.value;
+ });
+
+ // 5) cacheInvalidate(): 使当前查询 key 的缓存失效
+ buildKnex.QueryBuilder.extend("cacheInvalidate", function () {
+ const key = getCacheKeyForBuilder(this);
+ queryCache.delete(key);
+ return this;
+ });
+
+ // 6) cacheInvalidateByPrefix(prefix): 按前缀清理
+ buildKnex.QueryBuilder.extend("cacheInvalidateByPrefix", function (prefix) {
+ const p = String(prefix);
+ for (const k of queryCache.keys()) {
+ if (k.startsWith(p)) queryCache.delete(k);
+ }
+ return this;
+ });
+ }
+
+ /**
+ * 运行数据库迁移
+ */
+ async runMigrations() {
+ if (!this.db) {
+ throw new Error("Database not initialized. Call register() first.");
+ }
+
+ try {
+ const [batch, log] = await this.db.migrate.latest();
+ if (log.length === 0) {
+ console.log("Database is already up to date");
+ } else {
+ console.log(`Migrated ${log.length} files:`, log);
+ }
+ return { batch, log };
+ } catch (error) {
+ console.error("Migration failed:", error);
+ throw error;
+ }
+ }
+
+ /**
+ * 运行种子数据
+ */
+ async runSeeds() {
+ if (!this.db) {
+ throw new Error("Database not initialized. Call register() first.");
+ }
+
+ try {
+ const [log] = await this.db.seed.run();
+ console.log("Seeds completed:", log);
+ return log;
+ } catch (error) {
+ console.error("Seeding failed:", error);
+ throw error;
+ }
+ }
+
+ /**
+ * 获取数据库实例
+ */
+ getDatabase() {
+ return this.db;
+ }
+
+ /**
+ * 关闭数据库连接
+ */
+ async close() {
+ if (this.db) {
+ await this.db.destroy();
+ this.db = null;
+ }
+ }
+}
+
+// 导出单例实例
+const databaseProvider = new DatabaseProvider();
+
+export default databaseProvider;
+export { DatabaseProvider };
\ No newline at end of file
diff --git a/src/app/providers/index.js b/src/app/providers/index.js
new file mode 100644
index 0000000..15ad712
--- /dev/null
+++ b/src/app/providers/index.js
@@ -0,0 +1,15 @@
+/**
+ * 服务提供者索引文件
+ *
+ * 统一导出所有服务提供者
+ */
+
+import DatabaseProvider from "./DatabaseProvider.js";
+
+export {
+ DatabaseProvider
+};
+
+export default {
+ DatabaseProvider
+};
\ No newline at end of file
diff --git a/src/controllers/Page/BasePageController.js b/src/controllers/Page/BasePageController.js
deleted file mode 100644
index 411a431..0000000
--- a/src/controllers/Page/BasePageController.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import Router from "utils/router.js"
-import ArticleService from "services/ArticleService.js"
-import { logger } from "@/logger.js"
-
-/**
- * 基础页面控制器
- * 负责处理首页、静态页面、联系表单等基础功能
- */
-class BasePageController {
- constructor() {
- this.articleService = new ArticleService()
- }
-
- // 首页
- async indexGet(ctx) {
- const blogs = await this.articleService.getPublishedArticles()
- return await ctx.render(
- "page/index/index",
- {
- apiList: [
- {
- name: "随机图片",
- desc: "随机图片,点击查看。
右键可复制链接",
- url: "https://pic.xieyaxin.top/random.php",
- },
- ],
- blogs: blogs.slice(0, 4),
- },
- { includeSite: true, includeUser: true }
- )
- }
-
- // 处理联系表单提交
- async contactPost(ctx) {
- const { name, email, subject, message } = ctx.request.body
-
- // 简单的表单验证
- if (!name || !email || !subject || !message) {
- ctx.status = 400
- ctx.body = { success: false, message: "请填写所有必填字段" }
- return
- }
-
- // 这里可以添加邮件发送逻辑或数据库存储逻辑
- // 目前只是简单的成功响应
- logger.info(`收到联系表单: ${name} (${email}) - ${subject}: ${message}`)
-
- ctx.body = {
- success: true,
- message: "感谢您的留言,我们会尽快回复您!",
- }
- }
-
- /**
- * 通用页面渲染方法
- * @param {string} name - 模板名称
- * @param {Object} data - 页面数据
- * @returns {Function} 页面渲染函数
- */
- pageGet(name, data) {
- return async ctx => {
- return await ctx.render(
- name,
- {
- ...(data || {}),
- },
- { includeSite: true, includeUser: true }
- )
- }
- }
-
- /**
- * 创建基础页面相关路由
- * @returns {Router} 路由实例
- */
- static createRoutes() {
- const controller = new BasePageController()
- const router = new Router({ auth: "try" })
-
- // 首页
- router.get("/", controller.indexGet.bind(controller), { auth: false })
-
- // 静态页面
- router.get("/about", controller.pageGet("page/about/index"), { auth: false })
- router.get("/terms", controller.pageGet("page/extra/terms"), { auth: false })
- router.get("/privacy", controller.pageGet("page/extra/privacy"), { auth: false })
- router.get("/faq", controller.pageGet("page/extra/faq"), { auth: false })
- router.get("/feedback", controller.pageGet("page/extra/feedback"), { auth: false })
- router.get("/help", controller.pageGet("page/extra/help"), { auth: false })
- router.get("/contact", controller.pageGet("page/extra/contact"), { auth: false })
-
- // 需要登录的页面
- router.get("/notice", controller.pageGet("page/notice/index"), { auth: true })
-
- // 联系表单处理
- router.post("/contact", controller.contactPost.bind(controller), { auth: false })
-
- return router
- }
-}
-
-export default BasePageController
\ No newline at end of file
diff --git a/src/controllers/Page/_Demo/HtmxController.js b/src/controllers/Page/_Demo/HtmxController.js
deleted file mode 100644
index 9908a22..0000000
--- a/src/controllers/Page/_Demo/HtmxController.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Router from "utils/router.js"
-
-class HtmxController {
- async index(ctx) {
- return await ctx.render("index", { name: "bluescurry" })
- }
-
- page(name, data) {
- return async ctx => {
- return await ctx.render(name, data)
- }
- }
-
- static createRoutes() {
- const controller = new HtmxController()
- const router = new Router({ auth: "try" })
- router.get("/htmx/timeline", async ctx => {
- return await ctx.render("htmx/timeline", {
- timeLine: [
- {
- icon: "第一份工作",
- title: "???",
- desc: `做游戏的。`,
- },
- {
- icon: "大学毕业",
- title: "2014年09月",
- desc: `我从江西师范大学毕业,
- 获得了软件工程(虚拟现实与技术)专业的学士学位。`,
- },
- {
- icon: "高中",
- title: "???",
- desc: `宜春中学`,
- },
- {
- icon: "初中",
- title: "???",
- desc: `宜春实验中学`,
- },
- {
- icon: "小学(4-6年级)",
- title: "???",
- desc: `宜春二小`,
- },
- {
- icon: "小学(1-3年级)",
- title: "???",
- desc: `丰城市泉港镇小学`,
- },
- {
- icon: "出生",
- title: "1996年06月",
- desc: `我出生于江西省丰城市泉港镇`,
- },
- ],
- })
- })
- return router
- }
-}
-
-export default HtmxController
diff --git a/src/db/index.js b/src/db/index.js
deleted file mode 100644
index fcab69a..0000000
--- a/src/db/index.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import buildKnex from "knex"
-import knexConfig from "../../knexfile.mjs"
-
-// 简单内存缓存(支持 TTL 与按前缀清理)
-const queryCache = new Map()
-
-const getNow = () => Date.now()
-
-const computeExpiresAt = (ttlMs) => {
- if (!ttlMs || ttlMs <= 0) return null
- return getNow() + ttlMs
-}
-
-const isExpired = (entry) => {
- if (!entry) return true
- if (entry.expiresAt == null) return false
- return entry.expiresAt <= getNow()
-}
-
-const getCacheKeyForBuilder = (builder) => {
- if (builder._customCacheKey) return String(builder._customCacheKey)
- return builder.toString()
-}
-
-// 全局工具,便于在 QL 外部操作缓存
-export const DbQueryCache = {
- get(key) {
- const entry = queryCache.get(String(key))
- if (!entry) return undefined
- if (isExpired(entry)) {
- queryCache.delete(String(key))
- return undefined
- }
- return entry.value
- },
- set(key, value, ttlMs) {
- const expiresAt = computeExpiresAt(ttlMs)
- queryCache.set(String(key), { value, expiresAt })
- return value
- },
- has(key) {
- const entry = queryCache.get(String(key))
- return !!entry && !isExpired(entry)
- },
- delete(key) {
- return queryCache.delete(String(key))
- },
- clear() {
- queryCache.clear()
- },
- clearByPrefix(prefix) {
- const p = String(prefix)
- for (const k of queryCache.keys()) {
- if (k.startsWith(p)) queryCache.delete(k)
- }
- },
- stats() {
- let valid = 0
- let expired = 0
- for (const [k, entry] of queryCache.entries()) {
- if (isExpired(entry)) expired++
- else valid++
- }
- return { size: queryCache.size, valid, expired }
- }
-}
-
-// QueryBuilder 扩展
-// 1) cache(ttlMs?): 读取缓存,不存在则执行并写入
-buildKnex.QueryBuilder.extend("cache", async function (ttlMs) {
- const key = getCacheKeyForBuilder(this)
- const entry = queryCache.get(key)
- if (entry && !isExpired(entry)) {
- return entry.value
- }
- const data = await this
- queryCache.set(key, { value: data, expiresAt: computeExpiresAt(ttlMs) })
- return data
-})
-
-// 2) cacheAs(customKey): 设置自定义 key
-buildKnex.QueryBuilder.extend("cacheAs", function (customKey) {
- this._customCacheKey = String(customKey)
- return this
-})
-
-// 3) cacheSet(value, ttlMs?): 手动设置当前查询 key 的缓存
-buildKnex.QueryBuilder.extend("cacheSet", function (value, ttlMs) {
- const key = getCacheKeyForBuilder(this)
- queryCache.set(key, { value, expiresAt: computeExpiresAt(ttlMs) })
- return value
-})
-
-// 4) cacheGet(): 仅从缓存读取当前查询 key 的值
-buildKnex.QueryBuilder.extend("cacheGet", function () {
- const key = getCacheKeyForBuilder(this)
- const entry = queryCache.get(key)
- if (!entry || isExpired(entry)) return undefined
- return entry.value
-})
-
-// 5) cacheInvalidate(): 使当前查询 key 的缓存失效
-buildKnex.QueryBuilder.extend("cacheInvalidate", function () {
- const key = getCacheKeyForBuilder(this)
- queryCache.delete(key)
- return this
-})
-
-// 6) cacheInvalidateByPrefix(prefix): 按前缀清理
-buildKnex.QueryBuilder.extend("cacheInvalidateByPrefix", function (prefix) {
- const p = String(prefix)
- for (const k of queryCache.keys()) {
- if (k.startsWith(p)) queryCache.delete(k)
- }
- return this
-})
-
-const environment = process.env.NODE_ENV || "development"
-const db = buildKnex(knexConfig[environment])
-
-export default db
-
-// async function createDatabase() {
-// try {
-// // SQLite会自动创建数据库文件,只需验证连接
-// await db.raw("SELECT 1")
-// console.log("SQLite数据库连接成功")
-
-// // 检查users表是否存在(示例)
-// const [tableExists] = await db.raw(`
-// SELECT name
-// FROM sqlite_master
-// WHERE type='table' AND name='users'
-// `)
-
-// if (tableExists) {
-// console.log("表 users 已存在")
-// } else {
-// console.log("表 users 不存在,需要创建(通过迁移)")
-// }
-
-// await db.destroy()
-// } catch (error) {
-// console.error("数据库操作失败:", error)
-// process.exit(1)
-// }
-// }
-
-// createDatabase()
diff --git a/src/global.js b/src/global.js
deleted file mode 100644
index c5274e9..0000000
--- a/src/global.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import Koa from "koa"
-import { logger } from "./logger.js"
-import { validateEnvironment } from "./utils/envValidator.js"
-
-// 启动前验证环境变量
-if (!validateEnvironment()) {
- logger.error("环境变量验证失败,应用退出")
- process.exit(1)
-}
-
-const app = new Koa({ asyncLocalStorage: true })
-
-app.keys = []
-
-// SESSION_SECRET 已通过环境变量验证确保存在
-process.env.SESSION_SECRET.split(",").forEach(secret => {
- app.keys.push(secret.trim())
-})
-
-export { app }
-export default app
\ No newline at end of file
diff --git a/src/db/docs/ArticleModel.md b/src/infrastructure/database/docs/ArticleModel.md
similarity index 100%
rename from src/db/docs/ArticleModel.md
rename to src/infrastructure/database/docs/ArticleModel.md
diff --git a/src/db/docs/BookmarkModel.md b/src/infrastructure/database/docs/BookmarkModel.md
similarity index 100%
rename from src/db/docs/BookmarkModel.md
rename to src/infrastructure/database/docs/BookmarkModel.md
diff --git a/src/db/docs/README.md b/src/infrastructure/database/docs/README.md
similarity index 100%
rename from src/db/docs/README.md
rename to src/infrastructure/database/docs/README.md
diff --git a/src/db/docs/SiteConfigModel.md b/src/infrastructure/database/docs/SiteConfigModel.md
similarity index 100%
rename from src/db/docs/SiteConfigModel.md
rename to src/infrastructure/database/docs/SiteConfigModel.md
diff --git a/src/db/docs/UserModel.md b/src/infrastructure/database/docs/UserModel.md
similarity index 100%
rename from src/db/docs/UserModel.md
rename to src/infrastructure/database/docs/UserModel.md
diff --git a/src/infrastructure/database/index.js b/src/infrastructure/database/index.js
new file mode 100644
index 0000000..0d884be
--- /dev/null
+++ b/src/infrastructure/database/index.js
@@ -0,0 +1,13 @@
+/**
+ * 数据库基础设施
+ *
+ * 统一导出数据库相关功能
+ */
+
+import DatabaseProvider from "../../app/providers/DatabaseProvider.js";
+
+// 初始化数据库连接
+const db = DatabaseProvider.register();
+
+export default db;
+export { DatabaseProvider };
\ No newline at end of file
diff --git a/src/db/migrations/20250616065041_create_users_table.mjs b/src/infrastructure/database/migrations/20250616065041_create_users_table.mjs
similarity index 100%
rename from src/db/migrations/20250616065041_create_users_table.mjs
rename to src/infrastructure/database/migrations/20250616065041_create_users_table.mjs
diff --git a/src/db/migrations/20250621013128_site_config.mjs b/src/infrastructure/database/migrations/20250621013128_site_config.mjs
similarity index 100%
rename from src/db/migrations/20250621013128_site_config.mjs
rename to src/infrastructure/database/migrations/20250621013128_site_config.mjs
diff --git a/src/db/migrations/20250830014825_create_articles_table.mjs b/src/infrastructure/database/migrations/20250830014825_create_articles_table.mjs
similarity index 100%
rename from src/db/migrations/20250830014825_create_articles_table.mjs
rename to src/infrastructure/database/migrations/20250830014825_create_articles_table.mjs
diff --git a/src/db/migrations/20250830015422_create_bookmarks_table.mjs b/src/infrastructure/database/migrations/20250830015422_create_bookmarks_table.mjs
similarity index 100%
rename from src/db/migrations/20250830015422_create_bookmarks_table.mjs
rename to src/infrastructure/database/migrations/20250830015422_create_bookmarks_table.mjs
diff --git a/src/db/migrations/20250830020000_add_article_fields.mjs b/src/infrastructure/database/migrations/20250830020000_add_article_fields.mjs
similarity index 100%
rename from src/db/migrations/20250830020000_add_article_fields.mjs
rename to src/infrastructure/database/migrations/20250830020000_add_article_fields.mjs
diff --git a/src/db/migrations/20250901000000_add_profile_fields.mjs b/src/infrastructure/database/migrations/20250901000000_add_profile_fields.mjs
similarity index 100%
rename from src/db/migrations/20250901000000_add_profile_fields.mjs
rename to src/infrastructure/database/migrations/20250901000000_add_profile_fields.mjs
diff --git a/src/db/seeds/20250616071157_users_seed.mjs b/src/infrastructure/database/seeds/20250616071157_users_seed.mjs
similarity index 100%
rename from src/db/seeds/20250616071157_users_seed.mjs
rename to src/infrastructure/database/seeds/20250616071157_users_seed.mjs
diff --git a/src/db/seeds/20250621013324_site_config_seed.mjs b/src/infrastructure/database/seeds/20250621013324_site_config_seed.mjs
similarity index 100%
rename from src/db/seeds/20250621013324_site_config_seed.mjs
rename to src/infrastructure/database/seeds/20250621013324_site_config_seed.mjs
diff --git a/src/db/seeds/20250830020000_articles_seed.mjs b/src/infrastructure/database/seeds/20250830020000_articles_seed.mjs
similarity index 100%
rename from src/db/seeds/20250830020000_articles_seed.mjs
rename to src/infrastructure/database/seeds/20250830020000_articles_seed.mjs
diff --git a/src/jobs/exampleJob.js b/src/infrastructure/jobs/exampleJob.js
similarity index 78%
rename from src/jobs/exampleJob.js
rename to src/infrastructure/jobs/exampleJob.js
index 4e0387c..ca4976d 100644
--- a/src/jobs/exampleJob.js
+++ b/src/infrastructure/jobs/exampleJob.js
@@ -1,4 +1,4 @@
-import { jobLogger } from "@/logger"
+import { jobLogger } from "../../app/bootstrap/logger.js"
export default {
id: "example",
diff --git a/src/infrastructure/jobs/index.js b/src/infrastructure/jobs/index.js
new file mode 100644
index 0000000..c288f07
--- /dev/null
+++ b/src/infrastructure/jobs/index.js
@@ -0,0 +1,62 @@
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import scheduler from '../../shared/utils/scheduler.js';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+const jobsDir = __dirname;
+const jobModules = {};
+
+// 异步加载所有 job 文件
+async function loadJobs() {
+ const files = fs.readdirSync(jobsDir);
+ for (const file of files) {
+ if (file === 'index.js' || !file.endsWith('Job.js')) continue;
+ try {
+ const jobModule = await import(`file://${path.join(jobsDir, file)}`);
+ const job = jobModule.default || jobModule;
+ if (job && job.id && job.cronTime && typeof job.task === 'function') {
+ jobModules[job.id] = job;
+ scheduler.add(job.id, job.cronTime, job.task, job.options);
+ if (job.autoStart) scheduler.start(job.id);
+ }
+ } catch (error) {
+ console.error(`[Jobs] 加载任务文件 ${file} 失败:`, error);
+ }
+ }
+}
+
+// 立即加载所有任务
+loadJobs().catch(console.error);
+
+function callHook(id, hookName) {
+ const job = jobModules[id];
+ if (job && typeof job[hookName] === 'function') {
+ try {
+ job[hookName]();
+ } catch (e) {
+ console.error(`[Job:${id}] ${hookName} 执行异常:`, e);
+ }
+ }
+}
+
+export default {
+ start: id => {
+ callHook(id, 'beforeStart');
+ scheduler.start(id);
+ },
+ stop: id => {
+ scheduler.stop(id);
+ callHook(id, 'afterStop');
+ },
+ updateCronTime: (id, cronTime) => scheduler.updateCronTime(id, cronTime),
+ list: () => scheduler.list(),
+ reload: id => {
+ const job = jobModules[id];
+ if (job) {
+ scheduler.remove(id);
+ scheduler.add(job.id, job.cronTime, job.task, job.options);
+ }
+ }
+};
diff --git a/src/jobs/index.js b/src/jobs/index.js
deleted file mode 100644
index bf8006c..0000000
--- a/src/jobs/index.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import fs from 'fs';
-import path from 'path';
-import scheduler from 'utils/scheduler.js';
-
-const jobsDir = __dirname;
-const jobModules = {};
-
-fs.readdirSync(jobsDir).forEach(file => {
- if (file === 'index.js' || !file.endsWith('Job.js')) return;
- const jobModule = require(path.join(jobsDir, file));
- const job = jobModule.default || jobModule;
- if (job && job.id && job.cronTime && typeof job.task === 'function') {
- jobModules[job.id] = job;
- scheduler.add(job.id, job.cronTime, job.task, job.options);
- if (job.autoStart) scheduler.start(job.id);
- }
-});
-
-function callHook(id, hookName) {
- const job = jobModules[id];
- if (job && typeof job[hookName] === 'function') {
- try {
- job[hookName]();
- } catch (e) {
- console.error(`[Job:${id}] ${hookName} 执行异常:`, e);
- }
- }
-}
-
-export default {
- start: id => {
- callHook(id, 'beforeStart');
- scheduler.start(id);
- },
- stop: id => {
- scheduler.stop(id);
- callHook(id, 'afterStop');
- },
- updateCronTime: (id, cronTime) => scheduler.updateCronTime(id, cronTime),
- list: () => scheduler.list(),
- reload: id => {
- const job = jobModules[id];
- if (job) {
- scheduler.remove(id);
- scheduler.add(job.id, job.cronTime, job.task, job.options);
- }
- }
-};
diff --git a/src/main.js b/src/main.js
index 7f27c89..39de094 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,41 +1,52 @@
-import { app } from "./global"
+import { app } from "./app/bootstrap/app.js"
// 日志、全局插件、定时任务等基础设施
-import { logger } from "./logger.js"
-import "./jobs/index.js"
+import { logger } from "./app/bootstrap/logger.js"
+import "./infrastructure/jobs/index.js"
// 第三方依赖
import os from "os"
// 应用插件与自动路由
-import LoadMiddlewares from "./middlewares/install.js"
+import LoadMiddlewares from "./presentation/middlewares/install.js"
-// 注册插件
-LoadMiddlewares(app)
+// 异步启动函数
+async function startServer() {
+ // 注册插件
+ await LoadMiddlewares(app)
-const PORT = process.env.PORT || 3000
+ const PORT = process.env.PORT || 3000
-const server = app.listen(PORT, () => {
- const port = server.address().port
- // 获取本地 IP
- const getLocalIP = () => {
- const interfaces = os.networkInterfaces()
- for (const name of Object.keys(interfaces)) {
- for (const iface of interfaces[name]) {
- if (iface.family === "IPv4" && !iface.internal) {
- return iface.address
+ const server = app.listen(PORT, () => {
+ const port = server.address().port
+ // 获取本地 IP
+ const getLocalIP = () => {
+ const interfaces = os.networkInterfaces()
+ for (const name of Object.keys(interfaces)) {
+ for (const iface of interfaces[name]) {
+ if (iface.family === "IPv4" && !iface.internal) {
+ return iface.address
+ }
}
}
+ return "localhost"
}
- return "localhost"
- }
- const localIP = getLocalIP()
- logger.trace(`──────────────────── 服务器已启动 ────────────────────`)
- logger.trace(` `)
- logger.trace(` 本地访问: http://localhost:${port} `)
- logger.trace(` 局域网: http://${localIP}:${port} `)
- logger.trace(` `)
- logger.trace(` 服务启动时间: ${new Date().toLocaleString()} `)
- logger.trace(`──────────────────────────────────────────────────────\n`)
+ const localIP = getLocalIP()
+ logger.trace(`──────────────────── 服务器已启动 ────────────────────`)
+ logger.trace(` `)
+ logger.trace(` 本地访问: http://localhost:${port} `)
+ logger.trace(` 局域网: http://${localIP}:${port} `)
+ logger.trace(` `)
+ logger.trace(` 服务启动时间: ${new Date().toLocaleString()} `)
+ logger.trace(`──────────────────────────────────────────────────\n`)
+ })
+
+ return server
+}
+
+// 启动服务器
+startServer().catch(error => {
+ logger.error('服务器启动失败:', error)
+ process.exit(1)
})
-export default app
+export default app
\ No newline at end of file
diff --git a/src/middlewares/ErrorHandler/index.js b/src/middlewares/ErrorHandler/index.js
deleted file mode 100644
index 816dce4..0000000
--- a/src/middlewares/ErrorHandler/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { logger } from "@/logger"
-// src/plugins/errorHandler.js
-// 错误处理中间件插件
-
-async function formatError(ctx, status, message, stack) {
- const accept = ctx.accepts("json", "html", "text")
- const isDev = process.env.NODE_ENV === "development"
- if (accept === "json") {
- ctx.type = "application/json"
- ctx.body = isDev && stack ? { success: false, error: message, stack } : { success: false, error: message }
- } else if (accept === "html") {
- ctx.type = "html"
- await ctx.render("error/index", { status, message, stack, isDev })
- } else {
- ctx.type = "text"
- ctx.body = isDev && stack ? `${status} - ${message}\n${stack}` : `${status} - ${message}`
- }
- ctx.status = status
-}
-
-export default function errorHandler() {
- return async (ctx, next) => {
- // 拦截 Chrome DevTools 探测请求,直接返回 204
- if (ctx.path === "/.well-known/appspecific/com.chrome.devtools.json") {
- ctx.status = 204
- ctx.body = ""
- return
- }
- try {
- await next()
- if (ctx.status === 404) {
- await formatError(ctx, 404, "Resource not found")
- }
- } catch (err) {
- logger.error(err)
- const isDev = process.env.NODE_ENV === "development"
- if (isDev && err.stack) {
- console.error(err.stack)
- }
- await formatError(ctx, err.statusCode || 500, err.message || err || "Internal server error", isDev ? err.stack : undefined)
- }
- }
-}
diff --git a/src/controllers/Page/ArticleController.js b/src/modules/article/controllers/ArticleController.js
similarity index 97%
rename from src/controllers/Page/ArticleController.js
rename to src/modules/article/controllers/ArticleController.js
index 8809814..94f5bd4 100644
--- a/src/controllers/Page/ArticleController.js
+++ b/src/modules/article/controllers/ArticleController.js
@@ -1,5 +1,5 @@
-import { ArticleModel } from "../../db/models/ArticleModel.js"
-import Router from "utils/router.js"
+import { ArticleModel } from "../models/ArticleModel.js"
+import Router from "../../../shared/utils/router.js"
import { marked } from "marked"
class ArticleController {
diff --git a/src/db/models/ArticleModel.js b/src/modules/article/models/ArticleModel.js
similarity index 99%
rename from src/db/models/ArticleModel.js
rename to src/modules/article/models/ArticleModel.js
index 4bf5fa9..a635334 100644
--- a/src/db/models/ArticleModel.js
+++ b/src/modules/article/models/ArticleModel.js
@@ -1,4 +1,4 @@
-import db from "../index.js"
+import db from "../../../infrastructure/database/index.js"
class ArticleModel {
static async findAll() {
diff --git a/src/services/ArticleService.js b/src/modules/article/services/ArticleService.js
similarity index 98%
rename from src/services/ArticleService.js
rename to src/modules/article/services/ArticleService.js
index 1364348..df96e91 100644
--- a/src/services/ArticleService.js
+++ b/src/modules/article/services/ArticleService.js
@@ -1,5 +1,5 @@
-import ArticleModel from "db/models/ArticleModel.js"
-import CommonError from "utils/error/CommonError.js"
+import ArticleModel from "../models/ArticleModel.js"
+import CommonError from "../../../shared/utils/error/CommonError.js"
class ArticleService {
// 获取所有文章
diff --git a/src/controllers/Api/AuthController.js b/src/modules/auth/controllers/AuthController.js
similarity index 88%
rename from src/controllers/Api/AuthController.js
rename to src/modules/auth/controllers/AuthController.js
index 4c4e5cd..1e72e3b 100644
--- a/src/controllers/Api/AuthController.js
+++ b/src/modules/auth/controllers/AuthController.js
@@ -1,6 +1,6 @@
-import UserService from "services/userService.js"
-import { R } from "utils/helper.js"
-import Router from "utils/router.js"
+import UserService from "../services/userService.js"
+import { R } from "../../../shared/utils/helper.js"
+import Router from "../../../shared/utils/router.js"
class AuthController {
constructor() {
diff --git a/src/controllers/Page/AuthPageController.js b/src/modules/auth/controllers/AuthPageController.js
similarity index 93%
rename from src/controllers/Page/AuthPageController.js
rename to src/modules/auth/controllers/AuthPageController.js
index 1fd68b0..4b9780b 100644
--- a/src/controllers/Page/AuthPageController.js
+++ b/src/modules/auth/controllers/AuthPageController.js
@@ -1,8 +1,8 @@
-import Router from "utils/router.js"
-import UserService from "services/userService.js"
+import Router from "../../../shared/utils/router.js"
+import UserService from "../services/userService.js"
import svgCaptcha from "svg-captcha"
-import CommonError from "@/utils/error/CommonError"
-import { logger } from "@/logger.js"
+import CommonError from "../../../shared/utils/error/CommonError.js"
+import { logger } from "../../../app/bootstrap/logger.js"
/**
* 认证相关页面控制器
diff --git a/src/db/models/UserModel.js b/src/modules/auth/models/UserModel.js
similarity index 93%
rename from src/db/models/UserModel.js
rename to src/modules/auth/models/UserModel.js
index bf9fc03..ef8131c 100644
--- a/src/db/models/UserModel.js
+++ b/src/modules/auth/models/UserModel.js
@@ -1,4 +1,4 @@
-import db from "../index.js"
+import db from "../../../infrastructure/database/index.js"
class UserModel {
static async findAll() {
diff --git a/src/services/userService.js b/src/modules/auth/services/userService.js
similarity index 97%
rename from src/services/userService.js
rename to src/modules/auth/services/userService.js
index edd9981..de8d4c6 100644
--- a/src/services/userService.js
+++ b/src/modules/auth/services/userService.js
@@ -1,8 +1,8 @@
-import UserModel from "db/models/UserModel.js"
-import { hashPassword, comparePassword } from "utils/bcrypt.js"
-import CommonError from "utils/error/CommonError.js"
-import { JWT_SECRET } from "@/middlewares/Auth/auth.js"
-import jwt from "@/middlewares/Auth/jwt.js"
+import UserModel from "../models/UserModel.js"
+import { hashPassword, comparePassword } from "../../../shared/utils/bcrypt.js"
+import CommonError from "../../../shared/utils/error/CommonError.js"
+import { JWT_SECRET } from "../../../presentation/middlewares/Auth/auth.js"
+import jwt from "../../../presentation/middlewares/Auth/jwt.js"
class UserService {
// 根据ID获取用户
diff --git a/src/db/models/BookmarkModel.js b/src/modules/bookmark/models/BookmarkModel.js
similarity index 97%
rename from src/db/models/BookmarkModel.js
rename to src/modules/bookmark/models/BookmarkModel.js
index 3fb6968..268bdc3 100644
--- a/src/db/models/BookmarkModel.js
+++ b/src/modules/bookmark/models/BookmarkModel.js
@@ -1,4 +1,4 @@
-import db from "../index.js"
+import db from "../../../infrastructure/database/index.js"
class BookmarkModel {
static async findAllByUser(userId) {
diff --git a/src/services/BookmarkService.js b/src/modules/bookmark/services/BookmarkService.js
similarity index 98%
rename from src/services/BookmarkService.js
rename to src/modules/bookmark/services/BookmarkService.js
index 249591c..b95e949 100644
--- a/src/services/BookmarkService.js
+++ b/src/modules/bookmark/services/BookmarkService.js
@@ -1,5 +1,5 @@
-import BookmarkModel from "db/models/BookmarkModel.js"
-import CommonError from "utils/error/CommonError.js"
+import BookmarkModel from "../models/BookmarkModel.js"
+import CommonError from "../../../shared/utils/error/CommonError.js"
class BookmarkService {
// 获取用户的所有书签
diff --git a/src/controllers/Api/ApiController.js b/src/modules/common/controllers/ApiController.js
similarity index 94%
rename from src/controllers/Api/ApiController.js
rename to src/modules/common/controllers/ApiController.js
index 602e56e..cd64808 100644
--- a/src/controllers/Api/ApiController.js
+++ b/src/modules/common/controllers/ApiController.js
@@ -1,5 +1,5 @@
-import { R } from "utils/helper.js"
-import Router from "utils/router.js"
+import { R } from "../../../shared/utils/helper.js"
+import Router from "../../../shared/utils/router.js"
class AuthController {
constructor() {}
diff --git a/src/controllers/Api/JobController.js b/src/modules/common/controllers/JobController.js
similarity index 89%
rename from src/controllers/Api/JobController.js
rename to src/modules/common/controllers/JobController.js
index 719fddf..722a362 100644
--- a/src/controllers/Api/JobController.js
+++ b/src/modules/common/controllers/JobController.js
@@ -1,7 +1,7 @@
// Job Controller 示例:如何调用 service 层动态控制和查询定时任务
-import JobService from "services/JobService.js"
-import { R } from "utils/helper.js"
-import Router from "utils/router.js"
+import JobService from "../services/JobService.js"
+import { R } from "../../../shared/utils/helper.js"
+import Router from "../../../shared/utils/router.js"
class JobController {
constructor() {
diff --git a/src/modules/common/controllers/PageController.js b/src/modules/common/controllers/PageController.js
new file mode 100644
index 0000000..23332db
--- /dev/null
+++ b/src/modules/common/controllers/PageController.js
@@ -0,0 +1,25 @@
+import { R } from "../../../shared/utils/helper.js"
+import Router from "../../../shared/utils/router.js"
+
+class Pageontroller {
+ constructor() {}
+
+ async indexGet () {
+ console.log(234);
+
+ return R.ResponseSuccess(Math.random())
+ }
+
+ /**
+ * 路由注册
+ */
+ static createRoutes() {
+ const controller = new Pageontroller()
+ const router = new Router()
+ router.get("", controller.indexGet.bind(controller), { auth: "try" })
+ router.get("/", controller.indexGet.bind(controller), { auth: "try" })
+ return router
+ }
+}
+
+export default Pageontroller
diff --git a/src/controllers/Api/StatusController.js b/src/modules/common/controllers/StatusController.js
similarity index 89%
rename from src/controllers/Api/StatusController.js
rename to src/modules/common/controllers/StatusController.js
index d9cef1c..b31a490 100644
--- a/src/controllers/Api/StatusController.js
+++ b/src/modules/common/controllers/StatusController.js
@@ -1,4 +1,4 @@
-import Router from "utils/router.js"
+import Router from "../../../shared/utils/router.js"
class StatusController {
async status(ctx) {
diff --git a/src/controllers/Page/UploadController.js b/src/modules/common/controllers/UploadController.js
similarity index 97%
rename from src/controllers/Page/UploadController.js
rename to src/modules/common/controllers/UploadController.js
index e172b7f..5e33b4e 100644
--- a/src/controllers/Page/UploadController.js
+++ b/src/modules/common/controllers/UploadController.js
@@ -1,10 +1,10 @@
-import Router from "utils/router.js"
+import Router from "../../../shared/utils/router.js"
import formidable from "formidable"
import fs from "fs/promises"
import path from "path"
import { fileURLToPath } from "url"
-import { logger } from "@/logger.js"
-import { R } from "@/utils/helper"
+import { logger } from "../../../app/bootstrap/logger.js"
+import { R } from "../../../shared/utils/helper.js"
/**
* 文件上传控制器
diff --git a/src/services/JobService.js b/src/modules/common/services/JobService.js
similarity index 83%
rename from src/services/JobService.js
rename to src/modules/common/services/JobService.js
index 35a04a3..b6209b9 100644
--- a/src/services/JobService.js
+++ b/src/modules/common/services/JobService.js
@@ -1,4 +1,4 @@
-import jobs from "../jobs"
+import jobs from "../../../infrastructure/jobs/index.js"
class JobService {
startJob(id) {
diff --git a/src/db/models/SiteConfigModel.js b/src/modules/site-config/models/SiteConfigModel.js
similarity index 93%
rename from src/db/models/SiteConfigModel.js
rename to src/modules/site-config/models/SiteConfigModel.js
index 7e69fe0..1981720 100644
--- a/src/db/models/SiteConfigModel.js
+++ b/src/modules/site-config/models/SiteConfigModel.js
@@ -1,4 +1,4 @@
-import db from "../index.js"
+import db from "../../../infrastructure/database/index.js"
class SiteConfigModel {
// 获取指定key的配置
diff --git a/src/services/SiteConfigService.js b/src/modules/site-config/services/SiteConfigService.js
similarity index 98%
rename from src/services/SiteConfigService.js
rename to src/modules/site-config/services/SiteConfigService.js
index 59537fd..9355fd5 100644
--- a/src/services/SiteConfigService.js
+++ b/src/modules/site-config/services/SiteConfigService.js
@@ -1,5 +1,5 @@
-import SiteConfigModel from "../db/models/SiteConfigModel.js"
-import CommonError from "utils/error/CommonError.js"
+import SiteConfigModel from "../models/SiteConfigModel.js"
+import CommonError from "../../../shared/utils/error/CommonError.js"
class SiteConfigService {
// 获取指定key的配置
diff --git a/src/controllers/Page/ProfileController.js b/src/modules/user/controllers/ProfileController.js
similarity index 96%
rename from src/controllers/Page/ProfileController.js
rename to src/modules/user/controllers/ProfileController.js
index 3a3678c..266a683 100644
--- a/src/controllers/Page/ProfileController.js
+++ b/src/modules/user/controllers/ProfileController.js
@@ -1,11 +1,11 @@
-import Router from "utils/router.js"
-import UserService from "services/userService.js"
+import Router from "../../../shared/utils/router.js"
+import UserService from "../../auth/services/userService.js"
import formidable from "formidable"
import fs from "fs/promises"
import path from "path"
import { fileURLToPath } from "url"
-import CommonError from "@/utils/error/CommonError"
-import { logger } from "@/logger.js"
+import CommonError from "../../../shared/utils/error/CommonError.js"
+import { logger } from "../../../app/bootstrap/logger.js"
import imageThumbnail from "image-thumbnail"
/**
diff --git a/src/middlewares/Auth/auth.js b/src/presentation/middlewares/Auth/auth.js
similarity index 97%
rename from src/middlewares/Auth/auth.js
rename to src/presentation/middlewares/Auth/auth.js
index 81bfc70..9b77215 100644
--- a/src/middlewares/Auth/auth.js
+++ b/src/presentation/middlewares/Auth/auth.js
@@ -1,4 +1,4 @@
-import { logger } from "@/logger"
+import { logger } from "../../../app/bootstrap/logger.js"
import jwt from "./jwt"
import { minimatch } from "minimatch"
diff --git a/src/middlewares/Auth/index.js b/src/presentation/middlewares/Auth/index.js
similarity index 100%
rename from src/middlewares/Auth/index.js
rename to src/presentation/middlewares/Auth/index.js
diff --git a/src/middlewares/Auth/jwt.js b/src/presentation/middlewares/Auth/jwt.js
similarity index 100%
rename from src/middlewares/Auth/jwt.js
rename to src/presentation/middlewares/Auth/jwt.js
diff --git a/src/middlewares/errorHandler/index.js b/src/presentation/middlewares/ErrorHandler/index.js
similarity index 96%
rename from src/middlewares/errorHandler/index.js
rename to src/presentation/middlewares/ErrorHandler/index.js
index 816dce4..90ac80c 100644
--- a/src/middlewares/errorHandler/index.js
+++ b/src/presentation/middlewares/ErrorHandler/index.js
@@ -1,4 +1,4 @@
-import { logger } from "@/logger"
+import { logger } from "../../../app/bootstrap/logger.js"
// src/plugins/errorHandler.js
// 错误处理中间件插件
diff --git a/src/middlewares/ResponseTime/index.js b/src/presentation/middlewares/ResponseTime/index.js
similarity index 97%
rename from src/middlewares/ResponseTime/index.js
rename to src/presentation/middlewares/ResponseTime/index.js
index 8312814..86e5ab5 100644
--- a/src/middlewares/ResponseTime/index.js
+++ b/src/presentation/middlewares/ResponseTime/index.js
@@ -1,4 +1,4 @@
-import { logger } from "@/logger"
+import { logger } from "../../../app/bootstrap/logger.js"
// 静态资源扩展名列表
const staticExts = [".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg", ".map", ".woff", ".woff2", ".ttf", ".eot"]
diff --git a/src/middlewares/Send/index.js b/src/presentation/middlewares/Send/index.js
similarity index 100%
rename from src/middlewares/Send/index.js
rename to src/presentation/middlewares/Send/index.js
diff --git a/src/middlewares/Send/resolve-path.js b/src/presentation/middlewares/Send/resolve-path.js
similarity index 100%
rename from src/middlewares/Send/resolve-path.js
rename to src/presentation/middlewares/Send/resolve-path.js
diff --git a/src/middlewares/Session/index.js b/src/presentation/middlewares/Session/index.js
similarity index 100%
rename from src/middlewares/Session/index.js
rename to src/presentation/middlewares/Session/index.js
diff --git a/src/middlewares/Toast/index.js b/src/presentation/middlewares/Toast/index.js
similarity index 100%
rename from src/middlewares/Toast/index.js
rename to src/presentation/middlewares/Toast/index.js
diff --git a/src/middlewares/Views/index.js b/src/presentation/middlewares/Views/index.js
similarity index 92%
rename from src/middlewares/Views/index.js
rename to src/presentation/middlewares/Views/index.js
index 8250bf6..82861af 100644
--- a/src/middlewares/Views/index.js
+++ b/src/presentation/middlewares/Views/index.js
@@ -1,13 +1,13 @@
import { resolve } from "path"
-import { app } from "@/global"
+import { app } from "../../../app/bootstrap/app.js"
import consolidate from "consolidate"
import send from "../Send"
import getPaths from "get-paths"
// import pretty from "pretty"
-import { logger } from "@/logger"
-import SiteConfigService from "services/SiteConfigService.js"
+import { logger } from "../../../app/bootstrap/logger.js"
+import SiteConfigService from "../../../modules/site-config/services/SiteConfigService.js"
import assign from "lodash/assign"
-import config from "config/index.js"
+import config from "../../../app/config/index.js"
export default viewsMiddleware
diff --git a/src/middlewares/install.js b/src/presentation/middlewares/install.js
similarity index 86%
rename from src/middlewares/install.js
rename to src/presentation/middlewares/install.js
index 0f90e83..aa9ccdd 100644
--- a/src/middlewares/install.js
+++ b/src/presentation/middlewares/install.js
@@ -10,12 +10,12 @@ import Views from "./Views"
import Session from "./Session"
import etag from "@koa/etag"
import conditional from "koa-conditional-get"
-import { autoRegisterControllers } from "@/utils/ForRegister.js"
+import { autoRegisterControllers } from "../../shared/utils/ForRegister.js"
const __dirname = path.dirname(fileURLToPath(import.meta.url))
-const publicPath = resolve(__dirname, "../../public")
+const publicPath = resolve(__dirname, "../../../public")
-export default app => {
+export default async app => {
// 错误处理
app.use(ErrorHandler())
// 响应时间
@@ -50,9 +50,10 @@ export default app => {
// 请求体解析
app.use(bodyParser())
// 自动注册控制器
- autoRegisterControllers(app, path.resolve(__dirname, "../controllers"))
+ await autoRegisterControllers(app, path.resolve(__dirname, "../../modules"))
// 注册完成之后静态资源设置
app.use(async (ctx, next) => {
+ console.log(11, ctx.body);
if (ctx.body) return await next()
if (ctx.status === 200) return await next()
if (ctx.method.toLowerCase() === "get") {
diff --git a/src/views/error/index.pug b/src/presentation/views/error/index.pug
similarity index 100%
rename from src/views/error/index.pug
rename to src/presentation/views/error/index.pug
diff --git a/src/views/htmx/footer.pug b/src/presentation/views/htmx/footer.pug
similarity index 100%
rename from src/views/htmx/footer.pug
rename to src/presentation/views/htmx/footer.pug
diff --git a/src/views/htmx/login.pug b/src/presentation/views/htmx/login.pug
similarity index 100%
rename from src/views/htmx/login.pug
rename to src/presentation/views/htmx/login.pug
diff --git a/src/views/htmx/navbar.pug b/src/presentation/views/htmx/navbar.pug
similarity index 100%
rename from src/views/htmx/navbar.pug
rename to src/presentation/views/htmx/navbar.pug
diff --git a/src/views/htmx/timeline.pug b/src/presentation/views/htmx/timeline.pug
similarity index 100%
rename from src/views/htmx/timeline.pug
rename to src/presentation/views/htmx/timeline.pug
diff --git a/src/views/layouts/base.pug b/src/presentation/views/layouts/base.pug
similarity index 100%
rename from src/views/layouts/base.pug
rename to src/presentation/views/layouts/base.pug
diff --git a/src/views/layouts/bg-page.pug b/src/presentation/views/layouts/bg-page.pug
similarity index 100%
rename from src/views/layouts/bg-page.pug
rename to src/presentation/views/layouts/bg-page.pug
diff --git a/src/views/layouts/empty.pug b/src/presentation/views/layouts/empty.pug
similarity index 100%
rename from src/views/layouts/empty.pug
rename to src/presentation/views/layouts/empty.pug
diff --git a/src/views/layouts/page.pug b/src/presentation/views/layouts/page.pug
similarity index 100%
rename from src/views/layouts/page.pug
rename to src/presentation/views/layouts/page.pug
diff --git a/src/views/layouts/pure.pug b/src/presentation/views/layouts/pure.pug
similarity index 100%
rename from src/views/layouts/pure.pug
rename to src/presentation/views/layouts/pure.pug
diff --git a/src/views/layouts/root.pug b/src/presentation/views/layouts/root.pug
similarity index 100%
rename from src/views/layouts/root.pug
rename to src/presentation/views/layouts/root.pug
diff --git a/src/views/layouts/utils.pug b/src/presentation/views/layouts/utils.pug
similarity index 100%
rename from src/views/layouts/utils.pug
rename to src/presentation/views/layouts/utils.pug
diff --git a/src/views/page/about/index.pug b/src/presentation/views/page/about/index.pug
similarity index 100%
rename from src/views/page/about/index.pug
rename to src/presentation/views/page/about/index.pug
diff --git a/src/views/page/articles/article.pug b/src/presentation/views/page/articles/article.pug
similarity index 100%
rename from src/views/page/articles/article.pug
rename to src/presentation/views/page/articles/article.pug
diff --git a/src/views/page/articles/category.pug b/src/presentation/views/page/articles/category.pug
similarity index 100%
rename from src/views/page/articles/category.pug
rename to src/presentation/views/page/articles/category.pug
diff --git a/src/views/page/articles/index.pug b/src/presentation/views/page/articles/index.pug
similarity index 100%
rename from src/views/page/articles/index.pug
rename to src/presentation/views/page/articles/index.pug
diff --git a/src/views/page/articles/search.pug b/src/presentation/views/page/articles/search.pug
similarity index 100%
rename from src/views/page/articles/search.pug
rename to src/presentation/views/page/articles/search.pug
diff --git a/src/views/page/articles/tag.pug b/src/presentation/views/page/articles/tag.pug
similarity index 100%
rename from src/views/page/articles/tag.pug
rename to src/presentation/views/page/articles/tag.pug
diff --git a/src/views/page/auth/no-auth.pug b/src/presentation/views/page/auth/no-auth.pug
similarity index 100%
rename from src/views/page/auth/no-auth.pug
rename to src/presentation/views/page/auth/no-auth.pug
diff --git a/src/views/page/extra/contact.pug b/src/presentation/views/page/extra/contact.pug
similarity index 100%
rename from src/views/page/extra/contact.pug
rename to src/presentation/views/page/extra/contact.pug
diff --git a/src/views/page/extra/faq.pug b/src/presentation/views/page/extra/faq.pug
similarity index 100%
rename from src/views/page/extra/faq.pug
rename to src/presentation/views/page/extra/faq.pug
diff --git a/src/views/page/extra/feedback.pug b/src/presentation/views/page/extra/feedback.pug
similarity index 100%
rename from src/views/page/extra/feedback.pug
rename to src/presentation/views/page/extra/feedback.pug
diff --git a/src/views/page/extra/help.pug b/src/presentation/views/page/extra/help.pug
similarity index 100%
rename from src/views/page/extra/help.pug
rename to src/presentation/views/page/extra/help.pug
diff --git a/src/views/page/extra/privacy.pug b/src/presentation/views/page/extra/privacy.pug
similarity index 100%
rename from src/views/page/extra/privacy.pug
rename to src/presentation/views/page/extra/privacy.pug
diff --git a/src/views/page/extra/terms.pug b/src/presentation/views/page/extra/terms.pug
similarity index 100%
rename from src/views/page/extra/terms.pug
rename to src/presentation/views/page/extra/terms.pug
diff --git a/src/views/page/index copy/index.pug b/src/presentation/views/page/index copy/index.pug
similarity index 100%
rename from src/views/page/index copy/index.pug
rename to src/presentation/views/page/index copy/index.pug
diff --git a/src/views/page/index/index copy 2.pug b/src/presentation/views/page/index/index copy 2.pug
similarity index 100%
rename from src/views/page/index/index copy 2.pug
rename to src/presentation/views/page/index/index copy 2.pug
diff --git a/src/views/page/index/index copy.pug b/src/presentation/views/page/index/index copy.pug
similarity index 100%
rename from src/views/page/index/index copy.pug
rename to src/presentation/views/page/index/index copy.pug
diff --git a/src/views/page/index/index.pug b/src/presentation/views/page/index/index.pug
similarity index 100%
rename from src/views/page/index/index.pug
rename to src/presentation/views/page/index/index.pug
diff --git a/src/views/page/index/person.pug b/src/presentation/views/page/index/person.pug
similarity index 100%
rename from src/views/page/index/person.pug
rename to src/presentation/views/page/index/person.pug
diff --git a/src/views/page/login/index.pug b/src/presentation/views/page/login/index.pug
similarity index 100%
rename from src/views/page/login/index.pug
rename to src/presentation/views/page/login/index.pug
diff --git a/src/views/page/notice/index.pug b/src/presentation/views/page/notice/index.pug
similarity index 100%
rename from src/views/page/notice/index.pug
rename to src/presentation/views/page/notice/index.pug
diff --git a/src/views/page/profile/index.pug b/src/presentation/views/page/profile/index.pug
similarity index 100%
rename from src/views/page/profile/index.pug
rename to src/presentation/views/page/profile/index.pug
diff --git a/src/views/page/register/index.pug b/src/presentation/views/page/register/index.pug
similarity index 100%
rename from src/views/page/register/index.pug
rename to src/presentation/views/page/register/index.pug
diff --git a/src/services/README.md b/src/services/README.md
deleted file mode 100644
index a9b4f8f..0000000
--- a/src/services/README.md
+++ /dev/null
@@ -1,222 +0,0 @@
-# 服务层 (Services)
-
-本目录包含了应用的所有业务逻辑服务层,负责处理业务规则、数据验证和错误处理。
-
-## 服务列表
-
-### 1. UserService - 用户服务
-处理用户相关的所有业务逻辑,包括用户注册、登录、密码管理等。
-
-**主要功能:**
-- 用户注册和登录
-- 用户信息管理(增删改查)
-- 密码加密和验证
-- 用户统计和搜索
-- 批量操作支持
-
-**使用示例:**
-```javascript
-import { userService } from '../services/index.js'
-
-// 用户注册
-const newUser = await userService.register({
- username: 'testuser',
- email: 'test@example.com',
- password: 'password123'
-})
-
-// 用户登录
-const loginResult = await userService.login({
- username: 'testuser',
- password: 'password123'
-})
-```
-
-### 2. ArticleService - 文章服务
-处理文章相关的所有业务逻辑,包括文章的发布、编辑、搜索等。
-
-**主要功能:**
-- 文章的增删改查
-- 文章状态管理(草稿/发布)
-- 文章搜索和分类
-- 阅读量统计
-- 相关文章推荐
-- 分页支持
-
-**使用示例:**
-```javascript
-import { articleService } from '../services/index.js'
-
-// 创建文章
-const article = await articleService.createArticle({
- title: '测试文章',
- content: '文章内容...',
- category: '技术',
- tags: 'JavaScript,Node.js'
-})
-
-// 获取已发布文章
-const publishedArticles = await articleService.getPublishedArticles()
-
-// 搜索文章
-const searchResults = await articleService.searchArticles('JavaScript')
-```
-
-### 3. BookmarkService - 书签服务
-处理用户书签的管理,包括添加、编辑、删除和搜索书签。
-
-**主要功能:**
-- 书签的增删改查
-- URL格式验证
-- 批量操作支持
-- 书签统计和搜索
-- 分页支持
-
-**使用示例:**
-```javascript
-import { bookmarkService } from '../services/index.js'
-
-// 添加书签
-const bookmark = await bookmarkService.createBookmark({
- user_id: 1,
- title: 'Google',
- url: 'https://www.google.com',
- description: '搜索引擎'
-})
-
-// 获取用户书签
-const userBookmarks = await bookmarkService.getUserBookmarks(1)
-
-// 搜索书签
-const searchResults = await bookmarkService.searchUserBookmarks(1, 'Google')
-```
-
-### 4. SiteConfigService - 站点配置服务
-管理站点的各种配置信息,如站点名称、描述、主题等。
-
-**主要功能:**
-- 配置的增删改查
-- 配置值验证
-- 批量操作支持
-- 默认配置初始化
-- 配置统计和搜索
-
-**使用示例:**
-```javascript
-import { siteConfigService } from '../services/index.js'
-
-// 获取配置
-const siteName = await siteConfigService.get('site_name')
-
-// 设置配置
-await siteConfigService.set('site_name', '我的新网站')
-
-// 批量设置配置
-await siteConfigService.setMany({
- 'site_description': '网站描述',
- 'posts_per_page': 20
-})
-
-// 初始化默认配置
-await siteConfigService.initializeDefaultConfigs()
-```
-
-### 5. JobService - 任务服务
-处理后台任务和定时任务的管理。
-
-**主要功能:**
-- 任务调度和管理
-- 任务状态监控
-- 任务日志记录
-
-## 错误处理
-
-所有服务都使用统一的错误处理机制:
-
-```javascript
-import CommonError from 'utils/error/CommonError.js'
-
-try {
- const result = await userService.getUserById(1)
-} catch (error) {
- if (error instanceof CommonError) {
- // 业务逻辑错误
- console.error(error.message)
- } else {
- // 系统错误
- console.error('系统错误:', error.message)
- }
-}
-```
-
-## 数据验证
-
-服务层负责数据验证,确保数据的完整性和正确性:
-
-- **输入验证**:检查必填字段、格式验证等
-- **业务验证**:检查业务规则,如用户名唯一性
-- **权限验证**:确保用户只能操作自己的数据
-
-## 事务支持
-
-对于涉及多个数据库操作的方法,服务层支持事务处理:
-
-```javascript
-// 在需要事务的方法中使用
-async createUserWithProfile(userData, profileData) {
- // 这里可以添加事务支持
- const user = await this.createUser(userData)
- // 创建用户档案...
- return user
-}
-```
-
-## 缓存策略
-
-服务层可以集成缓存机制来提高性能:
-
-```javascript
-// 示例:缓存用户信息
-async getUserById(id) {
- const cacheKey = `user:${id}`
- let user = await cache.get(cacheKey)
-
- if (!user) {
- user = await UserModel.findById(id)
- await cache.set(cacheKey, user, 3600) // 缓存1小时
- }
-
- return user
-}
-```
-
-## 使用建议
-
-1. **控制器层调用服务**:控制器应该调用服务层方法,而不是直接操作模型
-2. **错误处理**:在控制器中捕获服务层抛出的错误并返回适当的HTTP响应
-3. **数据转换**:服务层负责数据格式转换,控制器负责HTTP响应格式
-4. **业务逻辑**:复杂的业务逻辑应该放在服务层,保持控制器的简洁性
-
-## 扩展指南
-
-添加新的服务:
-
-1. 创建新的服务文件(如 `NewService.js`)
-2. 继承或实现基础服务接口
-3. 在 `index.js` 中导出新服务
-4. 添加相应的测试用例
-5. 更新文档
-
-```javascript
-// 新服务示例
-class NewService {
- async doSomething(data) {
- try {
- // 业务逻辑
- return result
- } catch (error) {
- throw new CommonError(`操作失败: ${error.message}`)
- }
- }
-}
-```
diff --git a/src/services/index.js b/src/services/index.js
deleted file mode 100644
index db42d64..0000000
--- a/src/services/index.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// 服务层统一导出
-import UserService from "./UserService.js"
-import ArticleService from "./ArticleService.js"
-import BookmarkService from "./BookmarkService.js"
-import SiteConfigService from "./SiteConfigService.js"
-import JobService from "./JobService.js"
-
-// 导出所有服务类
-export {
- UserService,
- ArticleService,
- BookmarkService,
- SiteConfigService,
- JobService
-}
-
-// 导出默认实例(单例模式)
-export const userService = new UserService()
-export const articleService = new ArticleService()
-export const bookmarkService = new BookmarkService()
-export const siteConfigService = new SiteConfigService()
-export const jobService = new JobService()
-
-// 默认导出
-export default {
- UserService,
- ArticleService,
- BookmarkService,
- SiteConfigService,
- JobService,
- userService,
- articleService,
- bookmarkService,
- siteConfigService,
- jobService
-}
diff --git a/src/utils/BaseSingleton.js b/src/shared/utils/BaseSingleton.js
similarity index 100%
rename from src/utils/BaseSingleton.js
rename to src/shared/utils/BaseSingleton.js
diff --git a/src/utils/ForRegister.js b/src/shared/utils/ForRegister.js
similarity index 92%
rename from src/utils/ForRegister.js
rename to src/shared/utils/ForRegister.js
index 39b1b70..635d30c 100644
--- a/src/utils/ForRegister.js
+++ b/src/shared/utils/ForRegister.js
@@ -2,7 +2,7 @@
// 兼容传统 routes 方式和自动注册 controller 方式
import fs from "fs"
import path from "path"
-import { logger } from "@/logger.js"
+import { logger } from "../../app/bootstrap/logger.js"
// 保证不会被摇树(tree-shaking),即使在生产环境也会被打包
if (import.meta.env.PROD) {
@@ -20,10 +20,10 @@ if (import.meta.env.PROD) {
* @param {string} prefix - 路由前缀
* @param {Set} [manualControllers] - 可选,手动传入已注册 controller 文件名集合,优先于自动扫描
*/
-export function autoRegisterControllers(app, controllersDir) {
+export async function autoRegisterControllers(app, controllersDir) {
let allRouter = []
- function scan(dir, routePrefix = "") {
+ async function scan(dir, routePrefix = "") {
try {
for (const file of fs.readdirSync(dir)) {
const fullPath = path.join(dir, file)
@@ -31,12 +31,12 @@ export function autoRegisterControllers(app, controllersDir) {
if (stat.isDirectory()) {
if (!file.startsWith("_")) {
- scan(fullPath, routePrefix + "/" + file)
+ await scan(fullPath, routePrefix + "/" + file)
}
} else if (file.endsWith("Controller.js") && !file.startsWith("_")) {
try {
- // 使用同步的import方式,确保ES模块兼容性
- const controllerModule = require(fullPath)
+ // 使用动态import方式,确保ES模块兼容性
+ const controllerModule = await import(`file://${fullPath}`)
const controller = controllerModule.default || controllerModule
if (!controller) {
@@ -72,7 +72,7 @@ export function autoRegisterControllers(app, controllersDir) {
}
try {
- scan(controllersDir)
+ await scan(controllersDir)
if (allRouter.length === 0) {
logger.warn("[路由注册] ⚠️ 未发现任何可注册的控制器")
diff --git a/src/utils/bcrypt.js b/src/shared/utils/bcrypt.js
similarity index 100%
rename from src/utils/bcrypt.js
rename to src/shared/utils/bcrypt.js
diff --git a/src/utils/envValidator.js b/src/shared/utils/envValidator.js
similarity index 98%
rename from src/utils/envValidator.js
rename to src/shared/utils/envValidator.js
index fc9fb03..590c829 100644
--- a/src/utils/envValidator.js
+++ b/src/shared/utils/envValidator.js
@@ -1,4 +1,4 @@
-import { logger } from "@/logger.js"
+import { logger } from "../../app/bootstrap/logger.js"
/**
* 环境变量验证配置
diff --git a/src/utils/error/CommonError.js b/src/shared/utils/error/CommonError.js
similarity index 100%
rename from src/utils/error/CommonError.js
rename to src/shared/utils/error/CommonError.js
diff --git a/src/utils/helper.js b/src/shared/utils/helper.js
similarity index 91%
rename from src/utils/helper.js
rename to src/shared/utils/helper.js
index ffa829b..d3333cb 100644
--- a/src/utils/helper.js
+++ b/src/shared/utils/helper.js
@@ -1,4 +1,4 @@
-import { app } from "@/global"
+import { app } from "../../app/bootstrap/app.js"
function ResponseSuccess(data = null, message = null) {
return { success: true, error: message, data }
diff --git a/src/utils/router.js b/src/shared/utils/router.js
similarity index 100%
rename from src/utils/router.js
rename to src/shared/utils/router.js
diff --git a/src/utils/router/RouteAuth.js b/src/shared/utils/router/RouteAuth.js
similarity index 90%
rename from src/utils/router/RouteAuth.js
rename to src/shared/utils/router/RouteAuth.js
index d1a4e83..bbe6f4c 100644
--- a/src/utils/router/RouteAuth.js
+++ b/src/shared/utils/router/RouteAuth.js
@@ -1,5 +1,5 @@
-import jwt from "@/middlewares/Auth/jwt.js"
-import { JWT_SECRET } from "@/middlewares/Auth/auth.js"
+import jwt from "../../../presentation/middlewares/Auth/jwt.js"
+import { JWT_SECRET } from "../../../presentation/middlewares/Auth/auth.js"
/**
* 路由级权限中间件
diff --git a/src/utils/scheduler.js b/src/shared/utils/scheduler.js
similarity index 100%
rename from src/utils/scheduler.js
rename to src/shared/utils/scheduler.js