@fastcar/cli 0.1.1 → 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 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.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": "^1.10.2"
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, CustomType } from "@fastcar/core/annotation";
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
- @CustomType("json")
47
- profile: any;
46
+ profile!: any;
48
47
 
49
- constructor(...args: any) {
50
- Object.assign(this, ...args);
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
- #### Service 中使用
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
- async getUser(id: number) {
84
- return this.userMapper.selectOne({ where: { id } });
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 queryUsers(name: string) {
151
+ // 比较运算符
152
+ async queryByAgeRange(minAge: number, maxAge: number) {
89
153
  return this.userMapper.select({
90
154
  where: {
91
- name: { value: name },
92
- createdAt: { ">=": "2024-01-01", "<=": "2024-12-31" },
93
- },
94
- orders: { createdAt: OrderEnum.desc },
95
- limit: 10,
155
+ age: { $gte: minAge, $lte: maxAge }
156
+ }
96
157
  });
97
158
  }
98
159
 
99
- // 数组 IN 查询
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: { IN: ids } },
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
- return this.userMapper.saveOne(user);
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
- async updateName(id: number, name: string) {
119
- return this.userMapper.update({ where: { id }, row: { name } });
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({ where: { id } });
286
+ return this.userMapper.delete({
287
+ where: { id }
288
+ });
130
289
  }
131
290
 
132
- // 判断存在
133
- async exists(name: string) {
134
- return this.userMapper.exist({ name });
291
+ // 2. deleteOne(where) - 删除单条
292
+ async deleteOneUser(where: any) {
293
+ return this.userMapper.deleteOne(where);
135
294
  }
136
295
 
137
- // 统计
138
- async count() {
139
- return this.userMapper.count({});
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
- // 执行原生 SQL
143
- async executeSql() {
144
- return this.userMapper.execute("SELECT * FROM users WHERE id = 1");
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 leftJoin() {
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 forceIndex() {
336
+ async forceIndexQuery() {
163
337
  return this.userMapper.select({
164
338
  forceIndex: ["idx_name"],
165
- orders: { name: OrderEnum.desc },
166
- limit: 1,
339
+ where: { status: 1 }
167
340
  });
168
341
  }
169
342
 
170
- // 使用函数
171
- async formatDate() {
343
+ // 指定查询字段
344
+ async selectFields() {
172
345
  return this.userMapper.select({
173
- fields: ['DATE_FORMAT(created_at, "%Y-%m-%d %H:%i:%s") as createdAt'],
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
- 在 `application.yml` 中配置多个数据源,Service 中通过指定数据源名称切换。
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
- 使用 `@Transactional`(如果框架提供)或手动通过 `SqlSession` 控制事务边界。
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
- #### 使用 RedisTemplate
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` 字段,而不是物理删除