chanjs 1.2.5 → 2.0.0
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/common/global.js +54 -0
- package/core/controller.js +29 -0
- package/core/index.js +3 -0
- package/core/service.js +297 -0
- package/extend/art-template.js +28 -0
- package/extend/import.js +6 -0
- package/index.js +117 -161
- package/middleware/cookie.js +4 -0
- package/middleware/cors.js +4 -0
- package/middleware/favicon.js +5 -0
- package/middleware/header.js +9 -0
- package/middleware/index.js +21 -0
- package/middleware/log.js +4 -0
- package/middleware/setBody.js +11 -0
- package/middleware/static.js +10 -0
- package/middleware/template.js +14 -0
- package/middleware/validator.js +23 -0
- package/package.json +3 -3
- package/utils/bind.js +21 -0
- package/utils/db.js +75 -0
- package/utils/index.js +11 -0
- package/utils/loader.js +63 -0
- package/utils/response.js +26 -0
- package/core/config.js +0 -52
- package/core/helper.js +0 -105
- package/core/lib/cache.js +0 -133
- package/core/lib/controller.js +0 -56
- package/core/lib/index.js +0 -32
- package/core/lib/service.js +0 -269
- package/core/lib/task.js +0 -140
- package/core/lib/view.js +0 -47
package/core/config.js
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
import path from 'path';
|
2
|
-
import { createRequire } from 'module';
|
3
|
-
import { pathToFileURL } from 'url';
|
4
|
-
|
5
|
-
const require = createRequire(import.meta.url);
|
6
|
-
|
7
|
-
/**
|
8
|
-
* @description 根目录(ESM 兼容写法)
|
9
|
-
*/
|
10
|
-
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
11
|
-
const ROOT_PATH = process.cwd();
|
12
|
-
|
13
|
-
// 使用 require 读取 package.json(更简单可靠)
|
14
|
-
const pkg = require(path.join(ROOT_PATH, 'package.json'));
|
15
|
-
const { version = '1.0.0', author = '明空' } = pkg;
|
16
|
-
/**
|
17
|
-
* @description 程序目录
|
18
|
-
*/
|
19
|
-
const APP_PATH = path.join(ROOT_PATH, 'app');
|
20
|
-
|
21
|
-
const baseConfig = {
|
22
|
-
JSON_LIMIT: "100kb",
|
23
|
-
logger: {
|
24
|
-
level: 'dev',
|
25
|
-
},
|
26
|
-
version,
|
27
|
-
author,
|
28
|
-
env: 'dev',
|
29
|
-
template: 'default',
|
30
|
-
views: [],
|
31
|
-
static: [{
|
32
|
-
prefix: "/public/",
|
33
|
-
dir: ["app/public"],
|
34
|
-
maxAge: 0,
|
35
|
-
}],
|
36
|
-
database: {
|
37
|
-
client: "mysql2",
|
38
|
-
host: "localhost",
|
39
|
-
port: "3306",
|
40
|
-
user: "root",
|
41
|
-
password: "123456",
|
42
|
-
database: "chancms",
|
43
|
-
charset: "utf8mb4",
|
44
|
-
},
|
45
|
-
};
|
46
|
-
|
47
|
-
// 导出配置对象
|
48
|
-
export default {
|
49
|
-
ROOT_PATH,
|
50
|
-
APP_PATH,
|
51
|
-
...baseConfig
|
52
|
-
};
|
package/core/helper.js
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
import knex from 'knex';
|
2
|
-
import merge from 'deepmerge';
|
3
|
-
|
4
|
-
/**
|
5
|
-
* @description 实例化一个类,并将该类的所有方法绑定到一个新的对象上。
|
6
|
-
* @param {Function} className - 需要实例化的类。
|
7
|
-
*@returns {Object} 包含绑定方法的对象。
|
8
|
-
*/
|
9
|
-
export const bindClass = function(className) {
|
10
|
-
let obj = {};
|
11
|
-
const cls = new className();
|
12
|
-
Object.getOwnPropertyNames(cls.constructor.prototype).forEach(
|
13
|
-
(methodName) => {
|
14
|
-
if (
|
15
|
-
methodName !== "constructor" &&
|
16
|
-
typeof cls[methodName] === "function"
|
17
|
-
) {
|
18
|
-
obj[methodName] = cls[methodName].bind(cls);
|
19
|
-
}
|
20
|
-
}
|
21
|
-
);
|
22
|
-
return obj;
|
23
|
-
}
|
24
|
-
|
25
|
-
|
26
|
-
export const createKnex = function(opt) {
|
27
|
-
let config = {
|
28
|
-
host:"localhost",
|
29
|
-
port:"3306",
|
30
|
-
client:"mysql2",
|
31
|
-
charset:"utf8mb4",
|
32
|
-
debug:false,
|
33
|
-
...opt
|
34
|
-
};
|
35
|
-
return knex({
|
36
|
-
client: config.client,
|
37
|
-
connection: {
|
38
|
-
host:config.host,
|
39
|
-
port:config.port,
|
40
|
-
user:config.user,
|
41
|
-
password:config.password,
|
42
|
-
database:config.database,
|
43
|
-
charset:config.charset,
|
44
|
-
},
|
45
|
-
debug: config.debug,
|
46
|
-
//默认为{min: 2, max: 10}
|
47
|
-
pool: {
|
48
|
-
min: 0,
|
49
|
-
max: 2,
|
50
|
-
},
|
51
|
-
log: {
|
52
|
-
warn(message) {
|
53
|
-
console.error("[knex warn]", message);
|
54
|
-
},
|
55
|
-
error(message) {
|
56
|
-
console.error("[knex error]", message);
|
57
|
-
},
|
58
|
-
},
|
59
|
-
});
|
60
|
-
}
|
61
|
-
/**
|
62
|
-
*
|
63
|
-
* @param {*} module 模块目录
|
64
|
-
* @returns Array
|
65
|
-
* @description 将web模块放到最后加载
|
66
|
-
*/
|
67
|
-
export const loadWebToEnd = function(module=[]){
|
68
|
-
const index = module.indexOf('web');
|
69
|
-
if (index !== -1) {
|
70
|
-
const web = module.splice(index, 1);
|
71
|
-
module.push(web[0]);
|
72
|
-
}
|
73
|
-
return module;
|
74
|
-
}
|
75
|
-
|
76
|
-
|
77
|
-
// import { z } from "zod";
|
78
|
-
|
79
|
-
export const zodValidator = (schemas) => (req, res, next) => {
|
80
|
-
// 分别验证 headers/params/query/body
|
81
|
-
const sections = {
|
82
|
-
headers: schemas.headers,
|
83
|
-
params: schemas.params,
|
84
|
-
query: schemas.query,
|
85
|
-
body: schemas.body
|
86
|
-
};
|
87
|
-
|
88
|
-
for (const [key, schema] of Object.entries(sections)) {
|
89
|
-
if (!schema) continue;
|
90
|
-
|
91
|
-
const result = schema.safeParse(req[key]);
|
92
|
-
if (!result.success) {
|
93
|
-
// 返回首个错误信息
|
94
|
-
const firstError = result.error.errors[0];
|
95
|
-
return res.status(400).json({ error: firstError.message });
|
96
|
-
}
|
97
|
-
// 将验证后的数据挂载到 req 对象
|
98
|
-
req[key] = result.data;
|
99
|
-
}
|
100
|
-
next();
|
101
|
-
};
|
102
|
-
|
103
|
-
|
104
|
-
// 重新导出 deepmerge 的命名导出
|
105
|
-
export { default as merge } from 'deepmerge';
|
package/core/lib/cache.js
DELETED
@@ -1,133 +0,0 @@
|
|
1
|
-
export default class Cache {
|
2
|
-
constructor(maxSize = 1000) {
|
3
|
-
this.cache = new Map(); // 使用 Map 存储缓存数据
|
4
|
-
this.maxSize = maxSize; // 缓存最大容量
|
5
|
-
this.hits = 0; // 缓存命中次数
|
6
|
-
this.misses = 0; // 缓存未命中次数
|
7
|
-
}
|
8
|
-
|
9
|
-
/**
|
10
|
-
* 设置缓存
|
11
|
-
* @param {string} key - 缓存键
|
12
|
-
* @param {any} value - 缓存值
|
13
|
-
* @param {number} ttl - 缓存时间(毫秒),可选
|
14
|
-
*/
|
15
|
-
set(key, value, ttl = null) {
|
16
|
-
// 如果缓存已满,删除最早的一个缓存项
|
17
|
-
if (this.cache.size >= this.maxSize) {
|
18
|
-
const firstKey = this.cache.keys().next().value;
|
19
|
-
this.cache.delete(firstKey);
|
20
|
-
}
|
21
|
-
|
22
|
-
const cacheItem = {
|
23
|
-
value,
|
24
|
-
expires: ttl ? Date.now() + ttl : null, // 设置过期时间
|
25
|
-
updatedAt: Date.now(), // 记录更新时间
|
26
|
-
};
|
27
|
-
this.cache.set(key, cacheItem);
|
28
|
-
}
|
29
|
-
|
30
|
-
/**
|
31
|
-
* 获取缓存
|
32
|
-
* @param {string} key - 缓存键
|
33
|
-
* @returns {any} - 缓存值,如果过期或不存在则返回 null
|
34
|
-
*/
|
35
|
-
get(key) {
|
36
|
-
if (!this.cache.has(key)) {
|
37
|
-
this.misses++; // 未命中
|
38
|
-
return null;
|
39
|
-
}
|
40
|
-
|
41
|
-
const cacheItem = this.cache.get(key);
|
42
|
-
|
43
|
-
// 检查是否过期
|
44
|
-
if (cacheItem.expires && Date.now() > cacheItem.expires) {
|
45
|
-
this.cache.delete(key); // 删除过期缓存
|
46
|
-
this.misses++; // 未命中
|
47
|
-
return null;
|
48
|
-
}
|
49
|
-
|
50
|
-
this.hits++; // 命中
|
51
|
-
return cacheItem.value; // 返回缓存值
|
52
|
-
}
|
53
|
-
|
54
|
-
/**
|
55
|
-
* 清除指定缓存
|
56
|
-
* @param {string} key - 缓存键
|
57
|
-
*/
|
58
|
-
clear(key) {
|
59
|
-
this.cache.delete(key);
|
60
|
-
}
|
61
|
-
|
62
|
-
/**
|
63
|
-
* 清除所有缓存
|
64
|
-
*/
|
65
|
-
clearAll() {
|
66
|
-
this.cache.clear();
|
67
|
-
this.hits = 0;
|
68
|
-
this.misses = 0;
|
69
|
-
}
|
70
|
-
|
71
|
-
/**
|
72
|
-
* 获取缓存命中率
|
73
|
-
* @returns {number} - 命中率(0 到 1 之间)
|
74
|
-
*/
|
75
|
-
getHitRate() {
|
76
|
-
const total = this.hits + this.misses;
|
77
|
-
return total === 0 ? 0 : this.hits / total;
|
78
|
-
}
|
79
|
-
|
80
|
-
/**
|
81
|
-
* 获取缓存大小
|
82
|
-
* @returns {number} - 缓存项数量
|
83
|
-
*/
|
84
|
-
size() {
|
85
|
-
return this.cache.size;
|
86
|
-
}
|
87
|
-
|
88
|
-
/**
|
89
|
-
* 获取所有缓存键
|
90
|
-
* @returns {string[]} - 缓存键列表
|
91
|
-
*/
|
92
|
-
keys() {
|
93
|
-
return Array.from(this.cache.keys());
|
94
|
-
}
|
95
|
-
|
96
|
-
/**
|
97
|
-
* 遍历缓存项
|
98
|
-
* @param {function} callback - 回调函数,接收 key 和 value 作为参数
|
99
|
-
*/
|
100
|
-
forEach(callback) {
|
101
|
-
this.cache.forEach((cacheItem, key) => {
|
102
|
-
callback(key, cacheItem.value);
|
103
|
-
});
|
104
|
-
}
|
105
|
-
}
|
106
|
-
|
107
|
-
|
108
|
-
// // 示例用法
|
109
|
-
// const cache = new SimpleCache(100); // 设置缓存最大容量为 100
|
110
|
-
|
111
|
-
// // 设置缓存,有效期为 5 秒
|
112
|
-
// cache.set('name', 'Alice', 5000);
|
113
|
-
// cache.set('name', 'Alice', 5000);
|
114
|
-
// // 获取缓存
|
115
|
-
// console.log(cache.get('name')); // 输出: Alice
|
116
|
-
|
117
|
-
// // 获取缓存命中率
|
118
|
-
// console.log(cache.getHitRate()); // 输出: 1 (100% 命中率)
|
119
|
-
|
120
|
-
// // 获取缓存大小
|
121
|
-
// console.log(cache.size()); // 输出: 1
|
122
|
-
|
123
|
-
// // 获取所有缓存键
|
124
|
-
// console.log(cache.keys()); // 输出: ['name']
|
125
|
-
|
126
|
-
// // 遍历缓存项
|
127
|
-
// cache.forEach((key, value) => {
|
128
|
-
// console.log(`Key: ${key}, Value: ${value}`);
|
129
|
-
// });
|
130
|
-
|
131
|
-
// // 清除所有缓存
|
132
|
-
// cache.clearAll();
|
133
|
-
// console.log(cache.size()); // 输出: 0
|
package/core/lib/controller.js
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
export default class Controller {
|
2
|
-
constructor() {
|
3
|
-
}
|
4
|
-
|
5
|
-
parseJsonFields(obj) {
|
6
|
-
const result = {};
|
7
|
-
for (const key in obj) {
|
8
|
-
if (!obj.hasOwnProperty(key)) continue;
|
9
|
-
const value = obj[key];
|
10
|
-
// 如果是字符串,并且看起来像 JSON(以 { 或 [ 开头)
|
11
|
-
if (typeof value === 'string' && (value.startsWith('{') || value.startsWith('['))) {
|
12
|
-
try {
|
13
|
-
result[key] = JSON.parse(value);
|
14
|
-
} catch (e) {
|
15
|
-
console.warn(`JSON parse failed for field: ${key}`, e);
|
16
|
-
result[key] = value; // 保留原始值
|
17
|
-
}
|
18
|
-
} else {
|
19
|
-
result[key] = value;
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
return result;
|
24
|
-
}
|
25
|
-
|
26
|
-
success(data) {
|
27
|
-
return {
|
28
|
-
success: true,
|
29
|
-
msg: '操作成功',
|
30
|
-
code: 200,
|
31
|
-
data
|
32
|
-
};
|
33
|
-
}
|
34
|
-
|
35
|
-
fail(data, code = 201) {
|
36
|
-
return {
|
37
|
-
success: false,
|
38
|
-
msg: '操作失败',
|
39
|
-
code,
|
40
|
-
data
|
41
|
-
};
|
42
|
-
}
|
43
|
-
|
44
|
-
|
45
|
-
err(data={}, code = 500){
|
46
|
-
return {
|
47
|
-
success: false,
|
48
|
-
msg: '系统异常',
|
49
|
-
code,
|
50
|
-
data
|
51
|
-
};
|
52
|
-
}
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
}
|
package/core/lib/index.js
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
import express from "express";
|
2
|
-
import cookieParser from "cookie-parser";
|
3
|
-
import favicon from "serve-favicon";
|
4
|
-
|
5
|
-
import morgan from "morgan";
|
6
|
-
import path from "path";
|
7
|
-
import view from "./view.js";
|
8
|
-
export default async function (app, config) {
|
9
|
-
const { logger, APP_PATH, cookieKey, statics, JSON_LIMIT, appName, version } =
|
10
|
-
config;
|
11
|
-
|
12
|
-
app.use(morgan(logger.level));
|
13
|
-
app.use(favicon(path.join(APP_PATH, "public/favicon.ico")));
|
14
|
-
app.use(cookieParser(cookieKey));
|
15
|
-
app.use(express.json({ limit: JSON_LIMIT }));
|
16
|
-
app.use(express.urlencoded({ extended: false }));
|
17
|
-
view(app, config);
|
18
|
-
if (statics.length > 0) {
|
19
|
-
statics.forEach((item) => {
|
20
|
-
const { prefix, dir, maxAge } = item;
|
21
|
-
app.use(prefix, express.static(dir, { maxAge: maxAge || 0 }));
|
22
|
-
});
|
23
|
-
}
|
24
|
-
|
25
|
-
app.use((req, res, next) => {
|
26
|
-
res.setHeader("Create-By", "Chanjs");
|
27
|
-
res.setHeader("X-Powered-By", "ChanCMS");
|
28
|
-
res.setHeader("ChanCMS", version);
|
29
|
-
res.setHeader("Server", appName);
|
30
|
-
next();
|
31
|
-
});
|
32
|
-
};
|
package/core/lib/service.js
DELETED
@@ -1,269 +0,0 @@
|
|
1
|
-
const errCode = {
|
2
|
-
"ECONNREFUSED": "数据库连接被拒绝,请检查数据库服务是否正常运行。",
|
3
|
-
"ER_ACCESS_DENIED_ERROR": "无权限访问,账号或密码错误。",
|
4
|
-
"ER_ROW_IS_REFERENCED_2": "无法删除或更新记录,存在关联数据。",
|
5
|
-
"ER_BAD_FIELD_ERROR": "SQL语句中包含无效字段,请检查查询条件或列名。",
|
6
|
-
"ER_DUP_ENTRY": "插入失败:数据重复,违反唯一性约束。",
|
7
|
-
"ER_NO_SUCH_TABLE": "操作失败:目标表不存在。",
|
8
|
-
"ETIMEOUT": "数据库操作超时,请稍后再试。"
|
9
|
-
}
|
10
|
-
const getDefaultErrorMessage = (error) => {
|
11
|
-
if (error.message.includes('syntax') ||
|
12
|
-
error.message.includes('SQL')) {
|
13
|
-
return '数据库语法错误,请检查您的查询语句。';
|
14
|
-
} else if (error.message.includes('Connection closed')) {
|
15
|
-
return '数据库连接已关闭,请重试。';
|
16
|
-
} else if (error.message.includes('permission')) {
|
17
|
-
return '数据库权限不足,请检查配置。';
|
18
|
-
}
|
19
|
-
return '数据库发生未知错误,请稍后重试。';
|
20
|
-
}
|
21
|
-
|
22
|
-
class BaseService {
|
23
|
-
/**
|
24
|
-
* @description 构造函数
|
25
|
-
* @param {*} knex - knex实例
|
26
|
-
*/
|
27
|
-
constructor(DB = Chan.knex) {
|
28
|
-
this.knex = DB;
|
29
|
-
this.DB = DB;
|
30
|
-
this.model = ''; // 默认为空字符串
|
31
|
-
}
|
32
|
-
|
33
|
-
/**
|
34
|
-
* @description 查询表所有记录,慎用
|
35
|
-
* @param {Object} query - 包含查询参数的对象
|
36
|
-
* @returns {Promise} 返回所有记录
|
37
|
-
*/
|
38
|
-
async all(query = {}) {
|
39
|
-
try {
|
40
|
-
let dbQuery = this.DB(this.model);
|
41
|
-
if (Object.keys(query).length > 0) {
|
42
|
-
dbQuery = dbQuery.where(query);
|
43
|
-
}
|
44
|
-
let res = await dbQuery.select();
|
45
|
-
return this.success(res);
|
46
|
-
} catch (err) {
|
47
|
-
return this.error(err);
|
48
|
-
}
|
49
|
-
}
|
50
|
-
|
51
|
-
/**
|
52
|
-
* @description 根据指定条件查询记录
|
53
|
-
* @param {Object} options - 包含查询参数的对象
|
54
|
-
* @param {string} options.id - 查询字段名,默认为 'id'
|
55
|
-
* @param {*} options.value - 查询字段值,默认为 0
|
56
|
-
* @param {Array} options.field - 需要返回的字段数组,默认为空(即选择所有字段)
|
57
|
-
* @param {number} options.len - 期望获取的记录数量,默认为 1(如果为 1 则使用 `.first()`,否则使用 `.limit(len)`)
|
58
|
-
* @returns {Promise} 返回匹配条件的记录或记录列表
|
59
|
-
*/
|
60
|
-
async findById({ query, field = [], len = 1 }) {
|
61
|
-
try {
|
62
|
-
let dataQuery = this.DB(this.model).where(query);
|
63
|
-
if (field.length > 0) {
|
64
|
-
dataQuery = dataQuery.select(field);
|
65
|
-
}
|
66
|
-
if (len === 1) {
|
67
|
-
dataQuery = dataQuery.first();
|
68
|
-
} else if (len > 1) {
|
69
|
-
dataQuery = dataQuery.limit(len);
|
70
|
-
}
|
71
|
-
let res = await dataQuery;
|
72
|
-
console.log('111',res);
|
73
|
-
return this.success(res || (len === 1 ? {} : []));
|
74
|
-
} catch (err) {
|
75
|
-
return this.error(err);
|
76
|
-
}
|
77
|
-
}
|
78
|
-
|
79
|
-
|
80
|
-
/**
|
81
|
-
* @description 创建新记录
|
82
|
-
* @param {Object} data - 包含要插入的数据对象
|
83
|
-
* @returns {Promise<boolean>} 返回操作是否成功
|
84
|
-
*/
|
85
|
-
async insert(data = {}) {
|
86
|
-
try {
|
87
|
-
if (Object.keys(data).length === 0) {
|
88
|
-
return this.fail('插入数据不能为空');
|
89
|
-
}
|
90
|
-
const result = await this.DB(this.model).insert(data);
|
91
|
-
return this.success(result?.length > 0 || !!result)
|
92
|
-
} catch (err) {
|
93
|
-
return this.error(err); // 假设你有 this.error() 的封装
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
|
-
/**
|
98
|
-
* @description 插入多条记录
|
99
|
-
* @param {Array} records - 包含要插入的数据对象数组 [{<key>:<value>}, {<key>:<value>}, ...]
|
100
|
-
* @returns {Promise<Array|Number>} 返回插入后的记录或受影响行数,具体取决于数据库驱动
|
101
|
-
*/
|
102
|
-
async insertMany(records = []) {
|
103
|
-
try {
|
104
|
-
if (records.length === 0) {
|
105
|
-
return this.fail('插入数据不能为空');
|
106
|
-
}
|
107
|
-
const result = await this.DB(this.model).insert(records);
|
108
|
-
return this.success(result)
|
109
|
-
} catch (err) {
|
110
|
-
return this.error(err); // 假设你有 this.error() 的封装
|
111
|
-
}
|
112
|
-
}
|
113
|
-
|
114
|
-
/**
|
115
|
-
* @description 根据指定条件删除记录
|
116
|
-
* @param {Object} query - 包含查询条件的对象
|
117
|
-
* @returns {Promise<boolean>} 返回操作是否成功(即是否有任何记录被删除)
|
118
|
-
*/
|
119
|
-
async delete(query = {}) {
|
120
|
-
try {
|
121
|
-
if (Object.keys(query).length === 0) {
|
122
|
-
return this.error("请指定删除条件");
|
123
|
-
}
|
124
|
-
const affectedRows = await this.DB(this.model).where(query).del();
|
125
|
-
return this.success(affectedRows > 0);
|
126
|
-
} catch (err) {
|
127
|
-
return this.error(err); // 假设你有 this.error() 的封装
|
128
|
-
}
|
129
|
-
}
|
130
|
-
/**
|
131
|
-
* @description 根据指定条件更新记录
|
132
|
-
* @param {Object} query - 查询条件
|
133
|
-
* @param {Object} params - 要更新的数据
|
134
|
-
* @returns {Promise<boolean>} 返回操作是否成功(是否有行被更新)
|
135
|
-
*/
|
136
|
-
async update({ query, params } = {}) {
|
137
|
-
try {
|
138
|
-
console.log('update--->',query, params)
|
139
|
-
if (!query || !params || Object.keys(query).length === 0) {
|
140
|
-
return this.fail("参数错误");
|
141
|
-
}
|
142
|
-
const result = await this.DB(this.model).where(query).update(params);
|
143
|
-
return this.success(!!result);
|
144
|
-
} catch (err) {
|
145
|
-
return this.error(err); // 假设你有 this.error() 的封装
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
/**
|
150
|
-
* @description 批量更新多条记录(基于事务保证原子性)
|
151
|
-
* @param {Array<{query: Object, params: Object}>} updates - 更新操作数组
|
152
|
-
* @returns {Promise<boolean>} 返回是否所有记录都更新成功
|
153
|
-
*/
|
154
|
-
async updateMany(updates = []) {
|
155
|
-
if (!Array.isArray(updates)) {
|
156
|
-
return this.fail('参数必须为数组格式');
|
157
|
-
}
|
158
|
-
// 获取事务对象
|
159
|
-
const trx = await this.DB.transaction();
|
160
|
-
try {
|
161
|
-
for (const { query, params } of updates) {
|
162
|
-
const result = await trx(this.model).where(query).update(params);
|
163
|
-
if (result === 0) {
|
164
|
-
await trx.rollback(); // 有一条失败就回滚
|
165
|
-
return this.fail('更新失败');
|
166
|
-
}
|
167
|
-
}
|
168
|
-
|
169
|
-
await trx.commit();
|
170
|
-
return this.success(true);
|
171
|
-
} catch (err) {
|
172
|
-
await trx.rollback();
|
173
|
-
return this.error(error);
|
174
|
-
}
|
175
|
-
}
|
176
|
-
|
177
|
-
/**
|
178
|
-
* 查找所有符合条件的记录,并提供分页信息。
|
179
|
-
* @param {Object} options - 包含查询参数的对象
|
180
|
-
* @param {number} options.current - 当前页码,默认第一页
|
181
|
-
* @param {number} options.pageSize - 每页大小,默认10条记录
|
182
|
-
* @param {Object} options.query - 查询条件
|
183
|
-
* @param {Array} options.field - 需要返回的字段
|
184
|
-
* @returns {Promise<Object>} 返回包含数据列表、总记录数、当前页、每页大小的对象
|
185
|
-
*/
|
186
|
-
async query({ current = 1, pageSize = 10, query = {}, field = [] }) {
|
187
|
-
try {
|
188
|
-
const offset = (current - 1) * pageSize;
|
189
|
-
let countQuery = this.DB(this.model).count("* as total");
|
190
|
-
let dataQuery = this.DB(this.model);
|
191
|
-
// 应用查询条件
|
192
|
-
if (Object.keys(query).length > 0) {
|
193
|
-
Object.entries(query).forEach(([key, value]) => {
|
194
|
-
dataQuery = dataQuery.where(key, value);
|
195
|
-
countQuery = countQuery.where(key, value);
|
196
|
-
})
|
197
|
-
}
|
198
|
-
|
199
|
-
//字段筛选
|
200
|
-
if (field.length > 0) {
|
201
|
-
dataQuery = dataQuery.select(field);
|
202
|
-
}
|
203
|
-
//并行执行获取总记录数和分页数据的查询
|
204
|
-
const [totalResult, list] = await Promise.all([countQuery.first(), dataQuery.offset(offset).limit(pageSize)]);
|
205
|
-
// 提取总记录数
|
206
|
-
const total = totalResult?.total || 0;
|
207
|
-
return this.success({ list, total, current, pageSize });
|
208
|
-
} catch (err) { }
|
209
|
-
return this.error(err);
|
210
|
-
}
|
211
|
-
|
212
|
-
|
213
|
-
/**
|
214
|
-
* @description 查询指定条件的记录数量
|
215
|
-
* @param {Array} query - 数组形式的多个条件 [{<key>:<value>}, {<key>:<value>}, ...]
|
216
|
-
* @returns {Promise<number>} 返回符合条件的记录数量
|
217
|
-
*/
|
218
|
-
async count(query = []) {
|
219
|
-
try {
|
220
|
-
let dataQuery = this.DB(this.model);
|
221
|
-
if (query.length > 0) {
|
222
|
-
query.forEach((condition) => {
|
223
|
-
dataQuery = dataQuery.where(condition);
|
224
|
-
});
|
225
|
-
}
|
226
|
-
const result = await dataQuery.count("* as total").first();
|
227
|
-
return this.success(result?.total || 0);
|
228
|
-
} catch (error) {
|
229
|
-
return this.error(error);
|
230
|
-
}
|
231
|
-
}
|
232
|
-
|
233
|
-
|
234
|
-
error(err) {
|
235
|
-
console.error('-->',err);
|
236
|
-
// 从映射表中查找对应的错误信息
|
237
|
-
const message = errCode[err.code] || getDefaultErrorMessage(err);
|
238
|
-
return {
|
239
|
-
success: false,
|
240
|
-
msg: message,
|
241
|
-
code: 500, // 或者根据错误类型区分
|
242
|
-
data: {
|
243
|
-
sql: err.sql,
|
244
|
-
sqlMessage: err.sqlMessage
|
245
|
-
}
|
246
|
-
};
|
247
|
-
}
|
248
|
-
|
249
|
-
fail(msg = "操作失败", data = {}) {
|
250
|
-
console.error(msg);
|
251
|
-
return {
|
252
|
-
success: false,
|
253
|
-
msg,
|
254
|
-
code: 201, // 或者根据错误类型区分
|
255
|
-
data: data
|
256
|
-
}
|
257
|
-
}
|
258
|
-
|
259
|
-
success(data = {}, msg = "操作成功") {
|
260
|
-
return {
|
261
|
-
success: true,
|
262
|
-
msg,
|
263
|
-
code: 200,
|
264
|
-
data
|
265
|
-
}
|
266
|
-
}
|
267
|
-
|
268
|
-
}
|
269
|
-
export default BaseService;
|