@fastcar/cli 0.1.0 → 0.1.2
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/bin/cli.js +12 -9
- package/package.json +2 -2
- package/skills/fastcar-database/SKILL.md +360 -50
- package/skills/fastcar-framework/SKILL.md +230 -35
- package/src/reverse.js +86 -12
- package/src/skill-targets.js +3 -3
- package/src/skill.js +5 -3
package/bin/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const init = require("../src/init");
|
|
4
4
|
const setModules = require("../src/setModules");
|
|
@@ -26,7 +26,7 @@ Commands:
|
|
|
26
26
|
clean node_modules 删除冗余的 node_modules 目录
|
|
27
27
|
compress node_modules 压缩 node_modules 目录
|
|
28
28
|
reverse 数据库表逆向生成
|
|
29
|
-
reverse:init 生成 reverse.config.json 配置文件
|
|
29
|
+
reverse:init 生成 reverse.config.yml/json 配置文件
|
|
30
30
|
pack [pm] 打包项目(排除 devDependencies)
|
|
31
31
|
pm: 包管理器 (npm/yarn/pnpm),可选,默认自动检测
|
|
32
32
|
|
|
@@ -54,7 +54,7 @@ Examples:
|
|
|
54
54
|
$ fastcar-cli init web my-project # 使用 web 模板创建 my-project
|
|
55
55
|
$ fastcar-cli clean node_modules
|
|
56
56
|
$ fastcar-cli reverse # 数据库表逆向生成
|
|
57
|
-
$ fastcar-cli reverse:init #
|
|
57
|
+
$ fastcar-cli reverse:init # 生成默认配置文件(默认 YAML 格式)
|
|
58
58
|
$ fastcar-cli pack # 打包项目(自动检测包管理器)
|
|
59
59
|
$ fastcar-cli pack yarn # 使用 yarn 安装依赖
|
|
60
60
|
$ fastcar-cli pack pnpm # 使用 pnpm 安装依赖
|
|
@@ -67,7 +67,7 @@ Examples:
|
|
|
67
67
|
$ fastcar-cli skill targets # 列出支持的 agents
|
|
68
68
|
|
|
69
69
|
Reverse 命令参数说明:
|
|
70
|
-
通过配置文件传入参数,在项目根目录创建 reverse.config.json:
|
|
70
|
+
通过配置文件传入参数,在项目根目录创建 reverse.config.yml 或 reverse.config.json:
|
|
71
71
|
|
|
72
72
|
{
|
|
73
73
|
"tables": ["test"], // 要逆向生成的表名数组(必填)
|
|
@@ -100,7 +100,7 @@ function showVersion() {
|
|
|
100
100
|
console.log(`fastcar-cli version ${packageINFO.version}`);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
function run(argv) {
|
|
103
|
+
async function run(argv) {
|
|
104
104
|
// 命令入口
|
|
105
105
|
if (!argv || argv.length === 0) {
|
|
106
106
|
showHelp();
|
|
@@ -122,7 +122,7 @@ function run(argv) {
|
|
|
122
122
|
break;
|
|
123
123
|
}
|
|
124
124
|
case "init": {
|
|
125
|
-
init(body);
|
|
125
|
+
await init(body);
|
|
126
126
|
break;
|
|
127
127
|
}
|
|
128
128
|
case "clean":
|
|
@@ -139,11 +139,11 @@ function run(argv) {
|
|
|
139
139
|
break;
|
|
140
140
|
}
|
|
141
141
|
case "reverse": {
|
|
142
|
-
reverseGenerate(body);
|
|
142
|
+
await reverseGenerate(body);
|
|
143
143
|
break;
|
|
144
144
|
}
|
|
145
145
|
case "reverse:init": {
|
|
146
|
-
initReverseConfig();
|
|
146
|
+
await initReverseConfig();
|
|
147
147
|
break;
|
|
148
148
|
}
|
|
149
149
|
case "pack": {
|
|
@@ -220,4 +220,7 @@ function run(argv) {
|
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
run(process.argv.slice(2))
|
|
223
|
+
run(process.argv.slice(2)).catch((err) => {
|
|
224
|
+
console.error(err);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastcar/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"homepage": "https://william_zhong.coding.net/public/fast-car/fastcar-cli/git/files",
|
|
5
5
|
"description": "fastcar-cli 脚手架快速搭建",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"commander": "^8.3.0",
|
|
25
25
|
"compressing": "^1.5.1",
|
|
26
26
|
"inquirer": "^8.2.0",
|
|
27
|
-
"yaml": "^
|
|
27
|
+
"yaml": "^2.8.3"
|
|
28
28
|
},
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|
|
@@ -26,7 +26,7 @@ export default new APP();
|
|
|
26
26
|
#### 定义实体模型
|
|
27
27
|
|
|
28
28
|
```typescript
|
|
29
|
-
import { Table, Field, DBType, PrimaryKey, NotNull, Size
|
|
29
|
+
import { Table, Field, DBType, PrimaryKey, NotNull, Size } from "@fastcar/core/annotation";
|
|
30
30
|
|
|
31
31
|
@Table("users")
|
|
32
32
|
class User {
|
|
@@ -43,11 +43,25 @@ class User {
|
|
|
43
43
|
|
|
44
44
|
@Field("profile")
|
|
45
45
|
@DBType("json")
|
|
46
|
-
|
|
47
|
-
profile: any;
|
|
46
|
+
profile!: any;
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
@Field("created_at")
|
|
49
|
+
@DBType("datetime")
|
|
50
|
+
createdAt!: Date;
|
|
51
|
+
|
|
52
|
+
constructor(args?: Partial<User>) {
|
|
53
|
+
if (args) {
|
|
54
|
+
Object.assign(this, args);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
toObject() {
|
|
59
|
+
return {
|
|
60
|
+
id: this.id,
|
|
61
|
+
name: this.name,
|
|
62
|
+
profile: this.profile,
|
|
63
|
+
createdAt: this.createdAt,
|
|
64
|
+
};
|
|
51
65
|
}
|
|
52
66
|
}
|
|
53
67
|
```
|
|
@@ -66,11 +80,11 @@ class UserMapper extends MysqlMapper<User> {}
|
|
|
66
80
|
export default UserMapper;
|
|
67
81
|
```
|
|
68
82
|
|
|
69
|
-
####
|
|
83
|
+
#### MysqlMapper 完整 API 参考
|
|
70
84
|
|
|
71
85
|
```typescript
|
|
72
86
|
import { Service, Autowired } from "@fastcar/core/annotation";
|
|
73
|
-
import { OrderEnum } from "@fastcar/core/db";
|
|
87
|
+
import { OrderEnum, OperatorEnum } from "@fastcar/core/db";
|
|
74
88
|
import UserMapper from "./UserMapper";
|
|
75
89
|
import User from "./User";
|
|
76
90
|
|
|
@@ -79,73 +93,232 @@ class UserService {
|
|
|
79
93
|
@Autowired
|
|
80
94
|
private userMapper!: UserMapper;
|
|
81
95
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
96
|
+
// ==================== 查询方法 ====================
|
|
97
|
+
|
|
98
|
+
// 1. select(conditions) - 查询列表
|
|
99
|
+
// 返回: T[] - 实体对象数组
|
|
100
|
+
async getAllUsers() {
|
|
101
|
+
return this.userMapper.select({});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 2. selectOne(conditions) - 查询单个
|
|
105
|
+
// 返回: T | null
|
|
106
|
+
async getUserById(id: number) {
|
|
107
|
+
return this.userMapper.selectOne({
|
|
108
|
+
where: { id }
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 3. selectByPrimaryKey(row) - 根据主键查询
|
|
113
|
+
// 参数: 包含主键字段的对象
|
|
114
|
+
// 返回: T | null
|
|
115
|
+
async getUserByPK(id: number) {
|
|
116
|
+
return this.userMapper.selectByPrimaryKey({ id } as User);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 4. count(where) - 统计记录数
|
|
120
|
+
// 返回: number
|
|
121
|
+
async countUsers() {
|
|
122
|
+
return this.userMapper.count({ status: 1 });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 5. exist(where) - 判断是否存在
|
|
126
|
+
// 返回: boolean
|
|
127
|
+
async checkUserExists(name: string) {
|
|
128
|
+
return this.userMapper.exist({ name });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ==================== 条件查询详解 ====================
|
|
132
|
+
|
|
133
|
+
// 简单等于条件
|
|
134
|
+
async queryByStatus(status: number) {
|
|
135
|
+
return this.userMapper.select({
|
|
136
|
+
where: { status }
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 多条件 AND
|
|
141
|
+
async queryByConditions(name: string, status: number) {
|
|
142
|
+
return this.userMapper.select({
|
|
143
|
+
where: {
|
|
144
|
+
name,
|
|
145
|
+
status,
|
|
146
|
+
delStatus: false
|
|
147
|
+
}
|
|
148
|
+
});
|
|
85
149
|
}
|
|
86
150
|
|
|
87
|
-
//
|
|
88
|
-
async
|
|
151
|
+
// 比较运算符
|
|
152
|
+
async queryByAgeRange(minAge: number, maxAge: number) {
|
|
89
153
|
return this.userMapper.select({
|
|
90
154
|
where: {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
},
|
|
94
|
-
orders: { createdAt: OrderEnum.desc },
|
|
95
|
-
limit: 10,
|
|
155
|
+
age: { $gte: minAge, $lte: maxAge }
|
|
156
|
+
}
|
|
96
157
|
});
|
|
97
158
|
}
|
|
98
159
|
|
|
99
|
-
//
|
|
160
|
+
// 支持的运算符:
|
|
161
|
+
// $eq - 等于 (默认)
|
|
162
|
+
// $gt / $gte - 大于 / 大于等于
|
|
163
|
+
// $lt / $lte - 小于 / 小于等于
|
|
164
|
+
// $ne - 不等于
|
|
165
|
+
// $in - IN 查询 (数组)
|
|
166
|
+
// $nin - NOT IN 查询
|
|
167
|
+
// $isNull - IS NULL
|
|
168
|
+
// $isNotNull - IS NOT NULL
|
|
169
|
+
|
|
170
|
+
// IN 查询
|
|
100
171
|
async queryByIds(ids: number[]) {
|
|
101
172
|
return this.userMapper.select({
|
|
102
|
-
where: { id: {
|
|
173
|
+
where: { id: { $in: ids } }
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// IS NULL 查询
|
|
178
|
+
async queryDeletedUsers() {
|
|
179
|
+
return this.userMapper.select({
|
|
180
|
+
where: { deletedAt: { $isNull: true } }
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ==================== 排序和分页 ====================
|
|
185
|
+
|
|
186
|
+
// 排序 - 必须使用 OrderEnum
|
|
187
|
+
async getUsersOrdered() {
|
|
188
|
+
return this.userMapper.select({
|
|
189
|
+
where: { status: 1 },
|
|
190
|
+
orders: { createdAt: OrderEnum.desc } // 正确: 使用 OrderEnum.desc
|
|
191
|
+
// 错误: orders: { createdAt: "DESC" }
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// OrderEnum 定义:
|
|
196
|
+
// OrderEnum.asc = "ASC"
|
|
197
|
+
// OrderEnum.desc = "DESC"
|
|
198
|
+
|
|
199
|
+
// 多字段排序
|
|
200
|
+
async getUsersMultiOrder() {
|
|
201
|
+
return this.userMapper.select({
|
|
202
|
+
orders: {
|
|
203
|
+
status: OrderEnum.asc,
|
|
204
|
+
createdAt: OrderEnum.desc
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 分页
|
|
210
|
+
async getUsersPaged(page: number, pageSize: number) {
|
|
211
|
+
return this.userMapper.select({
|
|
212
|
+
orders: { id: OrderEnum.desc },
|
|
213
|
+
offest: (page - 1) * pageSize,
|
|
214
|
+
limit: pageSize
|
|
103
215
|
});
|
|
104
216
|
}
|
|
105
217
|
|
|
106
|
-
//
|
|
218
|
+
// 只取前N条
|
|
219
|
+
async getTopUsers(limit: number) {
|
|
220
|
+
return this.userMapper.select({
|
|
221
|
+
orders: { score: OrderEnum.desc },
|
|
222
|
+
limit
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ==================== 插入方法 ====================
|
|
227
|
+
|
|
228
|
+
// 1. saveOne(row) - 插入单条
|
|
229
|
+
// 返回: number - 插入的主键ID
|
|
107
230
|
async createUser(name: string) {
|
|
108
231
|
const user = new User({ name, createdAt: new Date() });
|
|
109
|
-
|
|
232
|
+
const insertId = await this.userMapper.saveOne(user);
|
|
233
|
+
return insertId;
|
|
110
234
|
}
|
|
111
235
|
|
|
112
|
-
//
|
|
236
|
+
// 2. saveList(rows) - 批量插入
|
|
237
|
+
// 返回: boolean
|
|
113
238
|
async createUsers(users: User[]) {
|
|
114
239
|
return this.userMapper.saveList(users);
|
|
115
240
|
}
|
|
116
241
|
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
242
|
+
// 3. saveORUpdate(rows) - 插入或更新 (UPSERT)
|
|
243
|
+
// 主键冲突时更新,否则插入
|
|
244
|
+
// 返回: number - 主键ID
|
|
245
|
+
async saveOrUpdateUser(user: User) {
|
|
246
|
+
return this.userMapper.saveORUpdate(user);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ==================== 更新方法 ====================
|
|
250
|
+
|
|
251
|
+
// 1. update({ row, where, limit }) - 条件更新
|
|
252
|
+
// 返回: boolean
|
|
253
|
+
async updateUserName(id: number, name: string) {
|
|
254
|
+
return this.userMapper.update({
|
|
255
|
+
where: { id },
|
|
256
|
+
row: { name, updatedAt: new Date() }
|
|
257
|
+
});
|
|
120
258
|
}
|
|
121
259
|
|
|
122
|
-
//
|
|
260
|
+
// 2. updateOne({ row, where }) - 更新单条
|
|
261
|
+
// 自动限制 limit: 1
|
|
262
|
+
async updateOneUser(where: any, row: any) {
|
|
263
|
+
return this.userMapper.updateOne({ where, row });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 3. updateByPrimaryKey(row) - 根据主键更新
|
|
267
|
+
// 根据实体对象的主键字段更新
|
|
268
|
+
// 返回: boolean
|
|
123
269
|
async updateById(user: User) {
|
|
124
270
|
return this.userMapper.updateByPrimaryKey(user);
|
|
125
271
|
}
|
|
126
272
|
|
|
127
|
-
//
|
|
273
|
+
// 更新示例:软删除
|
|
274
|
+
async softDeleteUser(id: number) {
|
|
275
|
+
return this.userMapper.update({
|
|
276
|
+
where: { id },
|
|
277
|
+
row: { delStatus: true, deletedAt: new Date() }
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ==================== 删除方法 ====================
|
|
282
|
+
|
|
283
|
+
// 1. delete({ where, limit }) - 条件删除
|
|
284
|
+
// 返回: boolean
|
|
128
285
|
async deleteUser(id: number) {
|
|
129
|
-
return this.userMapper.delete({
|
|
286
|
+
return this.userMapper.delete({
|
|
287
|
+
where: { id }
|
|
288
|
+
});
|
|
130
289
|
}
|
|
131
290
|
|
|
132
|
-
//
|
|
133
|
-
async
|
|
134
|
-
return this.userMapper.
|
|
291
|
+
// 2. deleteOne(where) - 删除单条
|
|
292
|
+
async deleteOneUser(where: any) {
|
|
293
|
+
return this.userMapper.deleteOne(where);
|
|
135
294
|
}
|
|
136
295
|
|
|
137
|
-
//
|
|
138
|
-
async
|
|
139
|
-
return this.userMapper.
|
|
296
|
+
// 3. deleteByPrimaryKey(row) - 根据主键删除
|
|
297
|
+
async deleteById(id: number) {
|
|
298
|
+
return this.userMapper.deleteByPrimaryKey({ id } as User);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ==================== 高级查询 ====================
|
|
302
|
+
|
|
303
|
+
// 自定义 SQL 查询
|
|
304
|
+
// query(sql, args) - 执行查询 SQL
|
|
305
|
+
async customQuery() {
|
|
306
|
+
return this.userMapper.query(
|
|
307
|
+
"SELECT * FROM users WHERE status = ? AND created_at > ?",
|
|
308
|
+
[1, "2024-01-01"]
|
|
309
|
+
);
|
|
140
310
|
}
|
|
141
311
|
|
|
142
|
-
//
|
|
143
|
-
async
|
|
144
|
-
return this.userMapper.execute(
|
|
312
|
+
// execute(sql, args) - 执行任意 SQL
|
|
313
|
+
async customExecute() {
|
|
314
|
+
return this.userMapper.execute(
|
|
315
|
+
"UPDATE users SET login_count = login_count + 1 WHERE id = ?",
|
|
316
|
+
[1]
|
|
317
|
+
);
|
|
145
318
|
}
|
|
146
319
|
|
|
147
|
-
// 左连接查询
|
|
148
|
-
async
|
|
320
|
+
// 左连接查询 - 使用 selectByCustom
|
|
321
|
+
async leftJoinQuery() {
|
|
149
322
|
return this.userMapper.selectByCustom({
|
|
150
323
|
join: [
|
|
151
324
|
{
|
|
@@ -155,34 +328,133 @@ class UserService {
|
|
|
155
328
|
},
|
|
156
329
|
],
|
|
157
330
|
tableAlias: "t",
|
|
331
|
+
where: { "t.status": 1 }
|
|
158
332
|
});
|
|
159
333
|
}
|
|
160
334
|
|
|
161
335
|
// 强制索引
|
|
162
|
-
async
|
|
336
|
+
async forceIndexQuery() {
|
|
163
337
|
return this.userMapper.select({
|
|
164
338
|
forceIndex: ["idx_name"],
|
|
165
|
-
|
|
166
|
-
limit: 1,
|
|
339
|
+
where: { status: 1 }
|
|
167
340
|
});
|
|
168
341
|
}
|
|
169
342
|
|
|
170
|
-
//
|
|
171
|
-
async
|
|
343
|
+
// 指定查询字段
|
|
344
|
+
async selectFields() {
|
|
172
345
|
return this.userMapper.select({
|
|
173
|
-
fields: [
|
|
346
|
+
fields: ["id", "name", "email"],
|
|
347
|
+
where: { status: 1 }
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// 分组查询
|
|
352
|
+
async groupByStatus() {
|
|
353
|
+
return this.userMapper.selectByCustom({
|
|
354
|
+
fields: ["status", "COUNT(*) as count"],
|
|
355
|
+
groups: ["status"]
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// 使用数据库函数
|
|
360
|
+
async useDbFunction() {
|
|
361
|
+
return this.userMapper.selectByCustom({
|
|
362
|
+
fields: [
|
|
363
|
+
'id',
|
|
364
|
+
'name',
|
|
365
|
+
'DATE_FORMAT(created_at, "%Y-%m-%d") as createdDate'
|
|
366
|
+
]
|
|
174
367
|
});
|
|
175
368
|
}
|
|
176
369
|
}
|
|
177
370
|
```
|
|
178
371
|
|
|
179
|
-
####
|
|
372
|
+
#### 常用查询条件速查表
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// 等于 (默认)
|
|
376
|
+
{ where: { status: 1 } }
|
|
377
|
+
|
|
378
|
+
// 不等于
|
|
379
|
+
{ where: { status: { $ne: 1 } } }
|
|
380
|
+
|
|
381
|
+
// 大于 / 大于等于
|
|
382
|
+
{ where: { age: { $gt: 18 } } }
|
|
383
|
+
{ where: { age: { $gte: 18 } } }
|
|
384
|
+
|
|
385
|
+
// 小于 / 小于等于
|
|
386
|
+
{ where: { age: { $lt: 60 } } }
|
|
387
|
+
{ where: { age: { $lte: 60 } } }
|
|
388
|
+
|
|
389
|
+
// 范围查询
|
|
390
|
+
{ where: { age: { $gte: 18, $lte: 60 } } }
|
|
391
|
+
|
|
392
|
+
// IN 查询
|
|
393
|
+
{ where: { id: { $in: [1, 2, 3] } } }
|
|
394
|
+
|
|
395
|
+
// NOT IN 查询
|
|
396
|
+
{ where: { id: { $nin: [1, 2, 3] } } }
|
|
397
|
+
|
|
398
|
+
// IS NULL
|
|
399
|
+
{ where: { deletedAt: { $isNull: true } } }
|
|
400
|
+
|
|
401
|
+
// IS NOT NULL
|
|
402
|
+
{ where: { deletedAt: { $isNotNull: true } } }
|
|
403
|
+
|
|
404
|
+
// 多条件 AND (默认)
|
|
405
|
+
{ where: { status: 1, delStatus: false } }
|
|
406
|
+
|
|
407
|
+
// 排序 (必须使用 OrderEnum)
|
|
408
|
+
import { OrderEnum } from "@fastcar/core/db";
|
|
409
|
+
{ orders: { createdAt: OrderEnum.desc } }
|
|
410
|
+
{ orders: { createdAt: OrderEnum.asc } }
|
|
411
|
+
|
|
412
|
+
// 分页
|
|
413
|
+
{ offest: 0, limit: 10 }
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### 事务处理
|
|
180
417
|
|
|
181
|
-
|
|
418
|
+
```typescript
|
|
419
|
+
import { SqlSession } from "@fastcar/core/annotation";
|
|
420
|
+
import { MysqlDataSourceManager } from "@fastcar/mysql";
|
|
421
|
+
|
|
422
|
+
@Service
|
|
423
|
+
class OrderService {
|
|
424
|
+
@Autowired
|
|
425
|
+
private dsm!: MysqlDataSourceManager;
|
|
182
426
|
|
|
183
|
-
|
|
427
|
+
@Autowired
|
|
428
|
+
private orderMapper!: OrderMapper;
|
|
184
429
|
|
|
185
|
-
|
|
430
|
+
@Autowired
|
|
431
|
+
private inventoryMapper!: InventoryMapper;
|
|
432
|
+
|
|
433
|
+
async createOrderWithTransaction(order: Order, inventoryUpdate: any) {
|
|
434
|
+
const sessionId = await this.dsm.beginTransaction();
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
// 插入订单
|
|
438
|
+
await this.orderMapper.saveOne(order, undefined, sessionId);
|
|
439
|
+
|
|
440
|
+
// 更新库存
|
|
441
|
+
await this.inventoryMapper.update({
|
|
442
|
+
where: { id: inventoryUpdate.id },
|
|
443
|
+
row: { quantity: inventoryUpdate.quantity }
|
|
444
|
+
}, undefined, sessionId);
|
|
445
|
+
|
|
446
|
+
// 提交事务
|
|
447
|
+
await this.dsm.commit(sessionId);
|
|
448
|
+
|
|
449
|
+
return true;
|
|
450
|
+
} catch (error) {
|
|
451
|
+
// 回滚事务
|
|
452
|
+
await this.dsm.rollback(sessionId);
|
|
453
|
+
throw error;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
186
458
|
|
|
187
459
|
### PostgreSQL (@fastcar/pgsql)
|
|
188
460
|
|
|
@@ -208,6 +480,8 @@ import User from "./User";
|
|
|
208
480
|
@Entity(User)
|
|
209
481
|
@Repository
|
|
210
482
|
class UserMapper extends PgsqlMapper<User> {}
|
|
483
|
+
|
|
484
|
+
export default UserMapper;
|
|
211
485
|
```
|
|
212
486
|
|
|
213
487
|
用法与 `MysqlMapper` 基本一致,支持相同的查询条件和 CRUD 操作。
|
|
@@ -236,6 +510,8 @@ import User from "./User";
|
|
|
236
510
|
@Entity(User)
|
|
237
511
|
@Repository
|
|
238
512
|
class UserMapper extends MongoMapper<User> {}
|
|
513
|
+
|
|
514
|
+
export default UserMapper;
|
|
239
515
|
```
|
|
240
516
|
|
|
241
517
|
### Redis (@fastcar/redis)
|
|
@@ -252,7 +528,7 @@ class APP {}
|
|
|
252
528
|
export default new APP();
|
|
253
529
|
```
|
|
254
530
|
|
|
255
|
-
#### 使用
|
|
531
|
+
#### 使用 RedisClient
|
|
256
532
|
|
|
257
533
|
```typescript
|
|
258
534
|
import { Service, Autowired } from "@fastcar/core/annotation";
|
|
@@ -274,6 +550,28 @@ class CacheService {
|
|
|
274
550
|
async del(key: string) {
|
|
275
551
|
await this.redis.del(key);
|
|
276
552
|
}
|
|
553
|
+
|
|
554
|
+
async expire(key: string, seconds: number) {
|
|
555
|
+
await this.redis.expire(key, seconds);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Hash 操作
|
|
559
|
+
async hset(key: string, field: string, value: string) {
|
|
560
|
+
await this.redis.hset(key, field, value);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
async hget(key: string, field: string) {
|
|
564
|
+
return this.redis.hget(key, field);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// List 操作
|
|
568
|
+
async lpush(key: string, value: string) {
|
|
569
|
+
await this.redis.lpush(key, value);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async rpop(key: string) {
|
|
573
|
+
return this.redis.rpop(key);
|
|
574
|
+
}
|
|
277
575
|
}
|
|
278
576
|
```
|
|
279
577
|
|
|
@@ -329,6 +627,10 @@ mysql:
|
|
|
329
627
|
database: mydb
|
|
330
628
|
username: root
|
|
331
629
|
password: password
|
|
630
|
+
# 连接池配置
|
|
631
|
+
connectionLimit: 10
|
|
632
|
+
# 是否使用预处理语句
|
|
633
|
+
useServerPrepStmts: true
|
|
332
634
|
|
|
333
635
|
pgsql:
|
|
334
636
|
host: localhost
|
|
@@ -372,3 +674,11 @@ npm i @fastcar/core @fastcar/mysql @fastcar/redis
|
|
|
372
674
|
# 4. 启动应用
|
|
373
675
|
npm run debug
|
|
374
676
|
```
|
|
677
|
+
|
|
678
|
+
## 注意事项
|
|
679
|
+
|
|
680
|
+
1. **排序必须使用 OrderEnum**:`orders: { createdAt: OrderEnum.desc }`,不能使用字符串 `"DESC"`
|
|
681
|
+
2. **主键查询**:`selectByPrimaryKey` 和 `updateByPrimaryKey` 需要传入包含主键字段的对象
|
|
682
|
+
3. **时间范围查询**:使用 `$gte` 和 `$lte` 运算符
|
|
683
|
+
4. **批量插入**:`saveList` 会自动分批处理(每批1000条)
|
|
684
|
+
5. **软删除**:建议使用 `update` 方法更新 `delStatus` 字段,而不是物理删除
|
|
@@ -64,58 +64,210 @@ app.start();
|
|
|
64
64
|
|
|
65
65
|
### Web 开发 (@fastcar/koa)
|
|
66
66
|
|
|
67
|
+
**正确的路由装饰器使用方式:**
|
|
68
|
+
|
|
67
69
|
```typescript
|
|
68
|
-
import { GET, POST, REQUEST
|
|
70
|
+
import { GET, POST, REQUEST } from "@fastcar/koa/annotation";
|
|
69
71
|
|
|
70
72
|
@Controller
|
|
71
73
|
@REQUEST("/api/users")
|
|
72
74
|
class UserController {
|
|
73
|
-
|
|
75
|
+
// GET 请求 - 无路径参数时必须有括号
|
|
76
|
+
@GET()
|
|
74
77
|
async list() {
|
|
75
78
|
return { data: [] };
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
// GET 请求 - 有路径参数
|
|
78
82
|
@GET("/:id")
|
|
79
|
-
async getById(
|
|
83
|
+
async getById(id: string) {
|
|
80
84
|
return { id };
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
// POST 请求
|
|
88
|
+
@POST()
|
|
89
|
+
async create(body: UserDTO) {
|
|
85
90
|
return { created: true };
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
93
|
```
|
|
89
94
|
|
|
95
|
+
**⚠️ 重要:FastCar 没有 `@Body`, `@Param`, `@Query` 装饰器**
|
|
96
|
+
|
|
97
|
+
- 请求参数直接作为方法参数传入
|
|
98
|
+
- GET 请求参数通过方法参数直接获取
|
|
99
|
+
- POST 请求体通过 `body` 参数获取
|
|
100
|
+
- 路径参数通过方法参数直接获取
|
|
101
|
+
|
|
90
102
|
### 数据库 (@fastcar/mysql)
|
|
91
103
|
|
|
104
|
+
**实体定义:**
|
|
105
|
+
|
|
92
106
|
```typescript
|
|
93
|
-
import {
|
|
107
|
+
import { Table, Field, DBType, PrimaryKey, NotNull, Size } from "@fastcar/core/annotation";
|
|
94
108
|
|
|
95
109
|
@Table("users")
|
|
96
110
|
class User {
|
|
97
|
-
@PrimaryKey
|
|
98
111
|
@Field("id")
|
|
112
|
+
@DBType("int")
|
|
113
|
+
@PrimaryKey
|
|
99
114
|
id!: number;
|
|
100
115
|
|
|
101
116
|
@Field("name")
|
|
117
|
+
@DBType("varchar")
|
|
118
|
+
@NotNull
|
|
119
|
+
@Size({ maxSize: 50 })
|
|
102
120
|
name!: string;
|
|
103
121
|
}
|
|
122
|
+
```
|
|
104
123
|
|
|
124
|
+
**Mapper 定义:**
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { Entity, Repository } from "@fastcar/core/annotation";
|
|
128
|
+
import { MysqlMapper } from "@fastcar/mysql";
|
|
129
|
+
|
|
130
|
+
@Entity(User)
|
|
105
131
|
@Repository
|
|
106
|
-
class
|
|
107
|
-
|
|
108
|
-
|
|
132
|
+
class UserMapper extends MysqlMapper<User> {}
|
|
133
|
+
|
|
134
|
+
export default UserMapper;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Service 中使用:**
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { Service, Autowired } from "@fastcar/core/annotation";
|
|
141
|
+
import { OrderEnum } from "@fastcar/core/db";
|
|
142
|
+
import UserMapper from "./UserMapper";
|
|
143
|
+
|
|
144
|
+
@Service
|
|
145
|
+
class UserService {
|
|
146
|
+
@Autowired
|
|
147
|
+
private userMapper!: UserMapper;
|
|
148
|
+
|
|
149
|
+
// 查询列表
|
|
150
|
+
async getUsers() {
|
|
151
|
+
return this.userMapper.select({
|
|
152
|
+
where: { status: 1 },
|
|
153
|
+
orders: { createTime: OrderEnum.desc },
|
|
154
|
+
limit: 10
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 查询单个
|
|
159
|
+
async getUser(id: number) {
|
|
160
|
+
return this.userMapper.selectOne({ where: { id } });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 根据主键查询
|
|
164
|
+
async getUserById(id: number) {
|
|
165
|
+
return this.userMapper.selectByPrimaryKey({ id } as User);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 插入
|
|
169
|
+
async createUser(user: User) {
|
|
170
|
+
return this.userMapper.saveOne(user);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 更新
|
|
174
|
+
async updateUser(id: number, data: Partial<User>) {
|
|
175
|
+
return this.userMapper.update({ where: { id }, row: data });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 根据主键更新
|
|
179
|
+
async updateById(user: User) {
|
|
180
|
+
return this.userMapper.updateByPrimaryKey(user);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 删除
|
|
184
|
+
async deleteUser(id: number) {
|
|
185
|
+
return this.userMapper.delete({ where: { id } });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 统计
|
|
189
|
+
async count() {
|
|
190
|
+
return this.userMapper.count({});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 表单验证 (@fastcar/core)
|
|
196
|
+
|
|
197
|
+
**正确的表单验证方式:**
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { ValidForm, NotNull, Size, Rule } from "@fastcar/core/annotation";
|
|
201
|
+
|
|
202
|
+
// DTO 类定义在单独的文件中,如 dto/UserDTO.ts
|
|
203
|
+
class UserDTO {
|
|
204
|
+
@NotNull
|
|
205
|
+
name!: string;
|
|
109
206
|
|
|
110
|
-
|
|
111
|
-
|
|
207
|
+
@Size({ minSize: 1, maxSize: 150 })
|
|
208
|
+
age!: number;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@Controller
|
|
212
|
+
@REQUEST("/api/users")
|
|
213
|
+
class UserController {
|
|
214
|
+
|
|
215
|
+
// GET 请求 - 无需表单验证
|
|
216
|
+
@GET()
|
|
217
|
+
async list(page: number = 1, pageSize: number = 10) {
|
|
218
|
+
return { page, pageSize, data: [] };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// POST 请求 - 使用 @ValidForm + @Rule 进行表单验证
|
|
222
|
+
@ValidForm
|
|
223
|
+
@POST()
|
|
224
|
+
async create(@Rule() body: UserDTO) {
|
|
225
|
+
// 参数会自动校验,如果校验失败会抛出异常
|
|
226
|
+
const { name, age } = body;
|
|
227
|
+
return this.userService.create({ name, age });
|
|
112
228
|
}
|
|
113
229
|
}
|
|
114
230
|
```
|
|
115
231
|
|
|
232
|
+
**表单验证规则:**
|
|
233
|
+
|
|
234
|
+
| 装饰器 | 用途 | 示例 |
|
|
235
|
+
|--------|------|------|
|
|
236
|
+
| `@ValidForm` | 开启方法参数校验 | 放在方法上 |
|
|
237
|
+
| `@Rule()` | 标记校验对象 | 放在 DTO 参数前 |
|
|
238
|
+
| `@NotNull` | 参数不能为空 | 放在 DTO 字段上 |
|
|
239
|
+
| `@Size({min, max})` | 大小限制 | 放在 DTO 字段上 |
|
|
240
|
+
|
|
241
|
+
**⚠️ 常见错误:**
|
|
242
|
+
|
|
243
|
+
❌ **错误** - 使用不存在的装饰器:
|
|
244
|
+
```typescript
|
|
245
|
+
// 这些装饰器在 FastCar 中不存在!
|
|
246
|
+
import { Body, Param, Query } from "@fastcar/koa/annotation"; // ❌ 错误
|
|
247
|
+
|
|
248
|
+
@GET("/:id")
|
|
249
|
+
async getById(@Param("id") id: string) { ... } // ❌ 错误
|
|
250
|
+
|
|
251
|
+
@POST()
|
|
252
|
+
async create(@Body body: UserDTO) { ... } // ❌ 错误
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
✅ **正确** - 直接使用方法参数:
|
|
256
|
+
```typescript
|
|
257
|
+
@GET("/:id")
|
|
258
|
+
async getById(id: string) { ... } // ✅ 正确
|
|
259
|
+
|
|
260
|
+
@POST()
|
|
261
|
+
async create(body: UserDTO) { ... } // ✅ 正确
|
|
262
|
+
|
|
263
|
+
@GET()
|
|
264
|
+
async list(page: number = 1) { ... } // ✅ 正确
|
|
265
|
+
```
|
|
266
|
+
|
|
116
267
|
### Redis (@fastcar/redis)
|
|
117
268
|
|
|
118
269
|
```typescript
|
|
270
|
+
import { Service, Autowired } from "@fastcar/core/annotation";
|
|
119
271
|
import { RedisClient } from "@fastcar/redis/annotation";
|
|
120
272
|
|
|
121
273
|
@Service
|
|
@@ -262,6 +414,7 @@ export default new APP();
|
|
|
262
414
|
template/
|
|
263
415
|
├── src/
|
|
264
416
|
│ ├── controller/ # 控制器(web/cos)
|
|
417
|
+
│ ├── dto/ # DTO 类(表单验证)
|
|
265
418
|
│ ├── middleware/ # 中间件(web/cos)
|
|
266
419
|
│ ├── model/ # 数据模型
|
|
267
420
|
│ └── app.ts # 应用入口
|
|
@@ -533,29 +686,6 @@ class LifecycleService {
|
|
|
533
686
|
}
|
|
534
687
|
```
|
|
535
688
|
|
|
536
|
-
## 表单验证
|
|
537
|
-
|
|
538
|
-
```typescript
|
|
539
|
-
import { ValidForm, NotNull, Size, Rule } from "@fastcar/core/annotation";
|
|
540
|
-
|
|
541
|
-
class UserDTO {
|
|
542
|
-
@NotNull
|
|
543
|
-
name!: string;
|
|
544
|
-
|
|
545
|
-
@Size({ minSize: 1, maxSize: 150 })
|
|
546
|
-
age!: number;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
@Controller
|
|
550
|
-
class UserController {
|
|
551
|
-
@ValidForm
|
|
552
|
-
createUser(@Rule() @NotNull user: UserDTO) {
|
|
553
|
-
// 参数自动校验
|
|
554
|
-
return this.userService.create(user);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
```
|
|
558
|
-
|
|
559
689
|
## 工具类
|
|
560
690
|
|
|
561
691
|
```typescript
|
|
@@ -655,6 +785,71 @@ npx tsc --init
|
|
|
655
785
|
# 5. 创建入口文件和配置文件,开始开发
|
|
656
786
|
```
|
|
657
787
|
|
|
788
|
+
## 常见错误与注意事项
|
|
789
|
+
|
|
790
|
+
### 1. 路由装饰器必须有括号
|
|
791
|
+
|
|
792
|
+
❌ **错误:**
|
|
793
|
+
```typescript
|
|
794
|
+
@GET
|
|
795
|
+
async list() { }
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
✅ **正确:**
|
|
799
|
+
```typescript
|
|
800
|
+
@GET()
|
|
801
|
+
async list() { }
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
### 2. 不要使用不存在的装饰器
|
|
805
|
+
|
|
806
|
+
❌ **错误:**
|
|
807
|
+
```typescript
|
|
808
|
+
import { Body, Param, Query } from "@fastcar/koa/annotation";
|
|
809
|
+
|
|
810
|
+
@GET("/:id")
|
|
811
|
+
async getById(@Param("id") id: string) { }
|
|
812
|
+
|
|
813
|
+
@POST()
|
|
814
|
+
async create(@Body body: UserDTO) { }
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
✅ **正确:**
|
|
818
|
+
```typescript
|
|
819
|
+
@GET("/:id")
|
|
820
|
+
async getById(id: string) { }
|
|
821
|
+
|
|
822
|
+
@POST()
|
|
823
|
+
async create(body: UserDTO) { }
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### 3. 表单验证使用 @ValidForm + @Rule
|
|
827
|
+
|
|
828
|
+
❌ **错误:**
|
|
829
|
+
```typescript
|
|
830
|
+
@POST()
|
|
831
|
+
async create(@Body body: UserDTO) { }
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
✅ **正确:**
|
|
835
|
+
```typescript
|
|
836
|
+
@ValidForm
|
|
837
|
+
@POST()
|
|
838
|
+
async create(@Rule() body: UserDTO) { }
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### 4. DTO 类放在单独文件夹
|
|
842
|
+
|
|
843
|
+
推荐项目结构:
|
|
844
|
+
```
|
|
845
|
+
src/
|
|
846
|
+
├── controller/ # 控制器
|
|
847
|
+
├── dto/ # DTO 类(表单验证)
|
|
848
|
+
├── service/ # 服务层
|
|
849
|
+
├── model/ # 数据模型
|
|
850
|
+
└── app.ts
|
|
851
|
+
```
|
|
852
|
+
|
|
658
853
|
## 参考资源
|
|
659
854
|
|
|
660
855
|
- 详细 API 文档:[references/api-reference.md](references/api-reference.md)
|
package/src/reverse.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const process = require("process");
|
|
4
|
+
const yaml = require("yaml");
|
|
5
|
+
const inquirer = require("inquirer");
|
|
4
6
|
|
|
5
7
|
// 默认配置文件模板
|
|
6
8
|
const defaultConfig = {
|
|
@@ -25,18 +27,70 @@ const defaultConfig = {
|
|
|
25
27
|
ignoreCamelcase: false,
|
|
26
28
|
};
|
|
27
29
|
|
|
30
|
+
// 支持的配置文件名
|
|
31
|
+
const CONFIG_FILES = {
|
|
32
|
+
json: "reverse.config.json",
|
|
33
|
+
yml: "reverse.config.yml",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// 查找已存在的配置文件
|
|
37
|
+
function findExistingConfig(cwd) {
|
|
38
|
+
for (const ext of ["yml", "json"]) {
|
|
39
|
+
const configPath = path.join(cwd, CONFIG_FILES[ext]);
|
|
40
|
+
if (fs.existsSync(configPath)) {
|
|
41
|
+
return { ext, configPath };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 读取配置文件
|
|
48
|
+
function readConfig(configPath, ext) {
|
|
49
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
50
|
+
if (ext === "yml") {
|
|
51
|
+
return yaml.parse(content);
|
|
52
|
+
}
|
|
53
|
+
return JSON.parse(content);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 写入配置文件
|
|
57
|
+
function writeConfig(configPath, ext, config) {
|
|
58
|
+
if (ext === "yml") {
|
|
59
|
+
fs.writeFileSync(configPath, yaml.stringify(config));
|
|
60
|
+
} else {
|
|
61
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
28
65
|
// 生成默认配置文件
|
|
29
|
-
function initReverseConfig() {
|
|
66
|
+
async function initReverseConfig() {
|
|
30
67
|
const cwd = process.cwd();
|
|
31
|
-
const configPath = path.join(cwd, "reverse.config.json");
|
|
32
68
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
69
|
+
// 检查是否已存在任何格式的配置文件
|
|
70
|
+
const existing = findExistingConfig(cwd);
|
|
71
|
+
if (existing) {
|
|
72
|
+
console.log(`⚠️ ${path.basename(existing.configPath)} 配置文件已存在`);
|
|
36
73
|
console.log(" 如需重新生成,请先删除现有文件");
|
|
37
74
|
return;
|
|
38
75
|
}
|
|
39
76
|
|
|
77
|
+
// 交互式选择配置格式
|
|
78
|
+
const answer = await inquirer.prompt([
|
|
79
|
+
{
|
|
80
|
+
type: "list",
|
|
81
|
+
name: "format",
|
|
82
|
+
message: "选择配置文件格式:",
|
|
83
|
+
choices: [
|
|
84
|
+
{ name: "YAML (reverse.config.yml)", value: "yml" },
|
|
85
|
+
{ name: "JSON (reverse.config.json)", value: "json" },
|
|
86
|
+
],
|
|
87
|
+
default: "yml",
|
|
88
|
+
},
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
const ext = answer.format;
|
|
92
|
+
const configPath = path.join(cwd, CONFIG_FILES[ext]);
|
|
93
|
+
|
|
40
94
|
// 填充默认路径
|
|
41
95
|
const config = {
|
|
42
96
|
...defaultConfig,
|
|
@@ -45,8 +99,8 @@ function initReverseConfig() {
|
|
|
45
99
|
};
|
|
46
100
|
|
|
47
101
|
try {
|
|
48
|
-
|
|
49
|
-
console.log(
|
|
102
|
+
writeConfig(configPath, ext, config);
|
|
103
|
+
console.log(`✅ 已生成 ${CONFIG_FILES[ext]} 配置文件`);
|
|
50
104
|
console.log("📁 文件路径:", configPath);
|
|
51
105
|
console.log("\n💡 请根据需要修改以下配置:");
|
|
52
106
|
console.log(" • tables: 要逆向生成的表名数组");
|
|
@@ -72,11 +126,31 @@ async function reverseGenerate(args = []) {
|
|
|
72
126
|
}
|
|
73
127
|
|
|
74
128
|
// 读取配置文件
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
77
|
-
console.log("❌ 未找到 reverse.config.json 配置文件");
|
|
129
|
+
const existing = findExistingConfig(cwd);
|
|
130
|
+
if (!existing) {
|
|
131
|
+
console.log("❌ 未找到 reverse.config.yml 或 reverse.config.json 配置文件");
|
|
78
132
|
console.log(" 请在项目根目录创建该文件,格式如下:");
|
|
79
133
|
console.log(`
|
|
134
|
+
YAML 格式 (reverse.config.yml):
|
|
135
|
+
tables: [test]
|
|
136
|
+
modelDir: ${cwd.replace(/\\/g, "/")}/src/models
|
|
137
|
+
mapperDir: ${cwd.replace(/\\/g, "/")}/src/mappers
|
|
138
|
+
dbConfig:
|
|
139
|
+
host: localhost
|
|
140
|
+
port: 3306
|
|
141
|
+
user: root
|
|
142
|
+
password: password
|
|
143
|
+
database: test_db
|
|
144
|
+
style:
|
|
145
|
+
tabWidth: 4
|
|
146
|
+
printWidth: 200
|
|
147
|
+
trailingComma: es5
|
|
148
|
+
useTabs: true
|
|
149
|
+
parser: typescript
|
|
150
|
+
endOfLine: crlf
|
|
151
|
+
ignoreCamelcase: false
|
|
152
|
+
|
|
153
|
+
JSON 格式 (reverse.config.json):
|
|
80
154
|
{
|
|
81
155
|
"tables": ["test"],
|
|
82
156
|
"modelDir": "${cwd.replace(/\\/g, "/")}/src/models",
|
|
@@ -104,9 +178,9 @@ async function reverseGenerate(args = []) {
|
|
|
104
178
|
|
|
105
179
|
let config;
|
|
106
180
|
try {
|
|
107
|
-
config =
|
|
181
|
+
config = readConfig(existing.configPath, existing.ext);
|
|
108
182
|
} catch (error) {
|
|
109
|
-
console.log(
|
|
183
|
+
console.log(`❌ 配置文件 ${path.basename(existing.configPath)} 解析失败:`, error.message);
|
|
110
184
|
return;
|
|
111
185
|
}
|
|
112
186
|
|
package/src/skill-targets.js
CHANGED
|
@@ -9,9 +9,9 @@ const TARGETS = {
|
|
|
9
9
|
name: 'Kimi Code CLI',
|
|
10
10
|
description: 'Kimi Code extension for VS Code',
|
|
11
11
|
globalPaths: {
|
|
12
|
-
win32: path.join(os.homedir(), '
|
|
13
|
-
darwin: path.join(os.homedir(), '.
|
|
14
|
-
linux: path.join(os.homedir(), '.
|
|
12
|
+
win32: path.join(os.homedir(), '.kimi/skills'),
|
|
13
|
+
darwin: path.join(os.homedir(), '.kimi/skills'),
|
|
14
|
+
linux: path.join(os.homedir(), '.kimi/skills')
|
|
15
15
|
},
|
|
16
16
|
localPath: '.agents/skills'
|
|
17
17
|
},
|
package/src/skill.js
CHANGED
|
@@ -197,9 +197,11 @@ async function installSkill(skillName, options = {}) {
|
|
|
197
197
|
console.log(`✅ 成功 ${modeText} 安装 ${skillName}`);
|
|
198
198
|
console.log(` 位置: ${destPath}`);
|
|
199
199
|
console.log();
|
|
200
|
-
console.log('
|
|
201
|
-
console.log(
|
|
202
|
-
console.log(
|
|
200
|
+
console.log('⚠️ 重要: 请重启你的 AI agent 以加载新安装的 skill!');
|
|
201
|
+
console.log();
|
|
202
|
+
console.log('重启后,你可以在对话中:');
|
|
203
|
+
console.log(` • 直接询问关于 "${skillName}" 的内容`);
|
|
204
|
+
console.log(` • 使用 /skill:${skillName} 强制加载该 skill`);
|
|
203
205
|
} else {
|
|
204
206
|
console.log('❌ 安装验证失败');
|
|
205
207
|
}
|