@fastcar/cli 0.1.1 → 0.1.3

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.
@@ -5,7 +5,7 @@ description: FastCar 数据库与缓存开发指南。Use when working with Fast
5
5
 
6
6
  # FastCar Database
7
7
 
8
- FastCar 数据库模块提供基于装饰器的 ORM 支持,涵盖 MySQL、PostgreSQL、MongoDB 和 Redis,并支持通过 `mysql-tool` 逆向生成 Model 和 Mapper
8
+ FastCar 数据库模块提供基于装饰器的 ORM 支持,涵盖 MySQL、PostgreSQL、MongoDB 和 Redis。
9
9
 
10
10
  ## 模块速查
11
11
 
@@ -26,10 +26,10 @@ 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
- @Table("users")
32
- class User {
31
+ @Table("entities")
32
+ class Entity {
33
33
  @Field("id")
34
34
  @DBType("int")
35
35
  @PrimaryKey
@@ -41,13 +41,16 @@ class User {
41
41
  @Size({ maxSize: 50 })
42
42
  name!: string;
43
43
 
44
- @Field("profile")
44
+ @Field("data")
45
45
  @DBType("json")
46
- @CustomType("json")
47
- profile: any;
46
+ data!: 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<Entity>) {
53
+ if (args) Object.assign(this, args);
51
54
  }
52
55
  }
53
56
  ```
@@ -57,206 +60,315 @@ class User {
57
60
  ```typescript
58
61
  import { Entity, Repository } from "@fastcar/core/annotation";
59
62
  import { MysqlMapper } from "@fastcar/mysql";
60
- import User from "./User";
63
+ import Entity from "./Entity";
61
64
 
62
- @Entity(User)
65
+ @Entity(Entity)
63
66
  @Repository
64
- class UserMapper extends MysqlMapper<User> {}
65
-
66
- export default UserMapper;
67
+ class EntityMapper extends MysqlMapper<Entity> {}
68
+ export default EntityMapper;
67
69
  ```
68
70
 
69
- #### Service 中使用
71
+ #### MysqlMapper 核心 API
70
72
 
71
73
  ```typescript
72
74
  import { Service, Autowired } from "@fastcar/core/annotation";
73
- import { OrderEnum } from "@fastcar/core/db";
74
- import UserMapper from "./UserMapper";
75
- import User from "./User";
75
+ import { OrderEnum, OperatorEnum } from "@fastcar/core/db";
76
+ import EntityMapper from "./EntityMapper";
77
+ import Entity from "./Entity";
76
78
 
77
79
  @Service
78
- class UserService {
80
+ class EntityService {
79
81
  @Autowired
80
- private userMapper!: UserMapper;
81
-
82
- // 查询单条
83
- async getUser(id: number) {
84
- return this.userMapper.selectOne({ where: { id } });
85
- }
86
-
87
- // 条件查询
88
- async queryUsers(name: string) {
89
- return this.userMapper.select({
90
- where: {
91
- name: { value: name },
92
- createdAt: { ">=": "2024-01-01", "<=": "2024-12-31" },
93
- },
94
- orders: { createdAt: OrderEnum.desc },
95
- limit: 10,
82
+ private mapper!: EntityMapper;
83
+
84
+ // ===== 查询方法 =====
85
+
86
+ // 查询列表
87
+ async list() {
88
+ return this.mapper.select({});
89
+ }
90
+
91
+ // 查询单个
92
+ async getOne(id: number) {
93
+ return this.mapper.selectOne({ where: { id } });
94
+ }
95
+
96
+ // 根据主键查询
97
+ async getByPK(id: number) {
98
+ return this.mapper.selectByPrimaryKey({ id } as Entity);
99
+ }
100
+
101
+ // 统计记录数
102
+ async count() {
103
+ return this.mapper.count({ status: 1 });
104
+ }
105
+
106
+ // 判断是否存在
107
+ async exists(name: string) {
108
+ return this.mapper.exist({ name });
109
+ }
110
+
111
+ // ===== 条件查询 =====
112
+
113
+ // 简单等于条件
114
+ async queryByStatus(status: number) {
115
+ return this.mapper.select({ where: { status } });
116
+ }
117
+
118
+ // 多条件 AND
119
+ async queryByConditions(name: string, status: number) {
120
+ return this.mapper.select({
121
+ where: { name, status, delStatus: false }
96
122
  });
97
123
  }
98
124
 
99
- // 数组 IN 查询
125
+ // 比较运算符
126
+ async queryByRange(min: number, max: number) {
127
+ return this.mapper.select({
128
+ where: { value: { [OperatorEnum.gte]: min, [OperatorEnum.lte]: max } }
129
+ });
130
+ }
131
+
132
+ // IN 查询
100
133
  async queryByIds(ids: number[]) {
101
- return this.userMapper.select({
102
- where: { id: { IN: ids } },
134
+ return this.mapper.select({
135
+ where: { id: { [OperatorEnum.in]: ids } }
136
+ });
137
+ }
138
+
139
+ // IS NULL 查询
140
+ async queryDeleted() {
141
+ return this.mapper.select({
142
+ where: { deletedAt: { [OperatorEnum.isNUll]: true } }
143
+ });
144
+ }
145
+
146
+ // ===== 排序和分页 =====
147
+
148
+ async getOrdered() {
149
+ return this.mapper.select({
150
+ where: { status: 1 },
151
+ orders: { createdAt: OrderEnum.desc }
103
152
  });
104
153
  }
105
154
 
106
- // 新增
107
- async createUser(name: string) {
108
- const user = new User({ name, createdAt: new Date() });
109
- return this.userMapper.saveOne(user);
155
+ async getPaged(page: number, pageSize: number) {
156
+ return this.mapper.select({
157
+ orders: { id: OrderEnum.desc },
158
+ offset: (page - 1) * pageSize,
159
+ limit: pageSize
160
+ });
110
161
  }
111
162
 
112
- // 批量新增
113
- async createUsers(users: User[]) {
114
- return this.userMapper.saveList(users);
163
+ // ===== 插入方法 =====
164
+
165
+ // 插入单条
166
+ async create(name: string) {
167
+ const entity = new Entity({ name, createdAt: new Date() });
168
+ return this.mapper.saveOne(entity);
115
169
  }
116
170
 
117
- // 更新
171
+ // 批量插入
172
+ async createBatch(list: Entity[]) {
173
+ return this.mapper.saveList(list);
174
+ }
175
+
176
+ // 插入或更新
177
+ async saveOrUpdate(entity: Entity) {
178
+ return this.mapper.saveORUpdate(entity);
179
+ }
180
+
181
+ // ===== 更新方法 =====
182
+
183
+ // 条件更新
118
184
  async updateName(id: number, name: string) {
119
- return this.userMapper.update({ where: { id }, row: { name } });
185
+ return this.mapper.update({
186
+ where: { id },
187
+ row: { name, updatedAt: new Date() }
188
+ });
120
189
  }
121
190
 
122
- // 按主键更新
123
- async updateById(user: User) {
124
- return this.userMapper.updateByPrimaryKey(user);
191
+ // 更新单条
192
+ async updateOne(where: any, row: any) {
193
+ return this.mapper.updateOne({ where, row });
125
194
  }
126
195
 
127
- // 删除
128
- async deleteUser(id: number) {
129
- return this.userMapper.delete({ where: { id } });
196
+ // 根据主键更新
197
+ async updateById(entity: Entity) {
198
+ return this.mapper.updateByPrimaryKey(entity);
130
199
  }
131
200
 
132
- // 判断存在
133
- async exists(name: string) {
134
- return this.userMapper.exist({ name });
201
+ // 软删除
202
+ async softDelete(id: number) {
203
+ return this.mapper.update({
204
+ where: { id },
205
+ row: { delStatus: true, deletedAt: new Date() }
206
+ });
135
207
  }
136
208
 
137
- // 统计
138
- async count() {
139
- return this.userMapper.count({});
209
+ // ===== 删除方法 =====
210
+
211
+ async deleteById(id: number) {
212
+ return this.mapper.delete({ where: { id } });
140
213
  }
141
214
 
142
- // 执行原生 SQL
143
- async executeSql() {
144
- return this.userMapper.execute("SELECT * FROM users WHERE id = 1");
215
+ async deleteOne(where: any) {
216
+ return this.mapper.deleteOne(where);
145
217
  }
146
218
 
147
- // 左连接查询
148
- async leftJoin() {
149
- return this.userMapper.selectByCustom({
150
- join: [
151
- {
152
- type: "LEFT",
153
- table: "orders o",
154
- on: "o.user_id = t.id",
155
- },
156
- ],
219
+ // ===== 高级查询 =====
220
+
221
+ // selectByCustom 支持 JOIN、分组、聚合
222
+ async advancedQuery() {
223
+ // 指定字段 + 泛型类型
224
+ const results = await this.mapper.selectByCustom<{
225
+ id: number;
226
+ name: string;
227
+ relatedName: string;
228
+ }>({
157
229
  tableAlias: "t",
230
+ fields: ["t.id", "t.name", "r.name as relatedName"],
231
+ join: [{
232
+ type: "LEFT",
233
+ table: "related_table r",
234
+ on: "r.entity_id = t.id"
235
+ }],
236
+ where: { "t.status": 1 },
237
+ camelcaseStyle: true,
158
238
  });
159
- }
160
239
 
161
- // 强制索引
162
- async forceIndex() {
163
- return this.userMapper.select({
164
- forceIndex: ["idx_name"],
165
- orders: { name: OrderEnum.desc },
166
- limit: 1,
240
+ // 聚合查询
241
+ const stats = await this.mapper.selectByCustom({
242
+ fields: [
243
+ "status",
244
+ "COUNT(*) as totalCount",
245
+ "MAX(created_at) as lastCreated"
246
+ ],
247
+ groups: ["status"],
248
+ orders: { totalCount: OrderEnum.desc }
167
249
  });
250
+
251
+ return { results, stats };
168
252
  }
169
253
 
170
- // 使用函数
171
- async formatDate() {
172
- return this.userMapper.select({
173
- fields: ['DATE_FORMAT(created_at, "%Y-%m-%d %H:%i:%s") as createdAt'],
174
- });
254
+ // 自定义 SQL
255
+ async customQuery() {
256
+ return this.mapper.query(
257
+ "SELECT * FROM entities WHERE status = ? AND created_at > ?",
258
+ [1, "2024-01-01"]
259
+ );
175
260
  }
176
261
  }
177
262
  ```
178
263
 
179
- #### 多数据源
264
+ #### 常用查询条件速查表
180
265
 
181
- 在 `application.yml` 中配置多个数据源,Service 中通过指定数据源名称切换。
266
+ ```typescript
267
+ import { OperatorEnum, OrderEnum } from "@fastcar/core/db";
182
268
 
183
- #### 事务
269
+ // 等于 (默认)
270
+ { where: { status: 1 } }
184
271
 
185
- 使用 `@Transactional`(如果框架提供)或手动通过 `SqlSession` 控制事务边界。
272
+ // 不等于
273
+ { where: { status: { [OperatorEnum.neq]: 1 } } }
186
274
 
187
- ### PostgreSQL (@fastcar/pgsql)
275
+ // 大于 / 大于等于 / 小于 / 小于等于
276
+ { where: { age: { [OperatorEnum.gt]: 18 } } }
277
+ { where: { age: { [OperatorEnum.gte]: 18, [OperatorEnum.lte]: 60 } } }
278
+
279
+ // IN / NOT IN
280
+ { where: { id: { [OperatorEnum.in]: [1, 2, 3] } } }
281
+ { where: { id: { [OperatorEnum.notin]: [1, 2, 3] } } }
188
282
 
189
- #### 开启 PostgreSQL
283
+ // IS NULL / IS NOT NULL
284
+ { where: { deletedAt: { [OperatorEnum.isNUll]: true } } }
285
+ { where: { deletedAt: { [OperatorEnum.isNotNull]: true } } }
286
+
287
+ // 排序
288
+ { orders: { createdAt: OrderEnum.desc } }
289
+
290
+ // 分页
291
+ { offset: 0, limit: 10 }
292
+ ```
293
+
294
+ #### 事务处理
190
295
 
191
296
  ```typescript
192
- import { Application } from "@fastcar/core/annotation";
193
- import { EnablePgsql } from "@fastcar/pgsql/annotation";
297
+ import { SqlSession } from "@fastcar/core/annotation";
298
+ import { MysqlDataSourceManager } from "@fastcar/mysql";
194
299
 
195
- @Application
196
- @EnablePgsql
197
- class APP {}
198
- export default new APP();
300
+ @Service
301
+ class BizService {
302
+ @Autowired
303
+ private dsm!: MysqlDataSourceManager;
304
+
305
+ @Autowired
306
+ private mapperA!: MapperA;
307
+
308
+ @Autowired
309
+ private mapperB!: MapperB;
310
+
311
+ async transactionExample(dataA: any, dataB: any) {
312
+ const sessionId = await this.dsm.beginTransaction();
313
+
314
+ try {
315
+ await this.mapperA.saveOne(dataA, undefined, sessionId);
316
+ await this.mapperB.update({
317
+ where: { id: dataB.id },
318
+ row: { status: dataB.status }
319
+ }, undefined, sessionId);
320
+
321
+ await this.dsm.commit(sessionId);
322
+ return true;
323
+ } catch (error) {
324
+ await this.dsm.rollback(sessionId);
325
+ throw error;
326
+ }
327
+ }
328
+ }
199
329
  ```
200
330
 
201
- #### 定义 Mapper
331
+ ### PostgreSQL (@fastcar/pgsql)
202
332
 
203
333
  ```typescript
204
- import { Entity, Repository } from "@fastcar/core/annotation";
334
+ import { EnablePgsql } from "@fastcar/pgsql/annotation";
205
335
  import { PgsqlMapper } from "@fastcar/pgsql";
206
- import User from "./User";
207
336
 
208
- @Entity(User)
337
+ @Application
338
+ @EnablePgsql
339
+ class APP {}
340
+
341
+ @Entity(Entity)
209
342
  @Repository
210
- class UserMapper extends PgsqlMapper<User> {}
343
+ class EntityMapper extends PgsqlMapper<Entity> {}
211
344
  ```
212
345
 
213
- 用法与 `MysqlMapper` 基本一致,支持相同的查询条件和 CRUD 操作。
346
+ 用法与 `MysqlMapper` 基本一致。
214
347
 
215
348
  ### MongoDB (@fastcar/mongo)
216
349
 
217
- #### 开启 MongoDB
218
-
219
350
  ```typescript
220
- import { Application } from "@fastcar/core/annotation";
221
351
  import { EnableMongo } from "@fastcar/mongo/annotation";
352
+ import { MongoMapper } from "@fastcar/mongo";
222
353
 
223
354
  @Application
224
355
  @EnableMongo
225
356
  class APP {}
226
- export default new APP();
227
- ```
228
357
 
229
- #### 定义 Mapper
230
-
231
- ```typescript
232
- import { Entity, Repository } from "@fastcar/core/annotation";
233
- import { MongoMapper } from "@fastcar/mongo";
234
- import User from "./User";
235
-
236
- @Entity(User)
358
+ @Entity(Entity)
237
359
  @Repository
238
- class UserMapper extends MongoMapper<User> {}
360
+ class EntityMapper extends MongoMapper<Entity> {}
239
361
  ```
240
362
 
241
363
  ### Redis (@fastcar/redis)
242
364
 
243
- #### 开启 Redis
244
-
245
365
  ```typescript
246
- import { Application } from "@fastcar/core/annotation";
247
366
  import { EnableRedis } from "@fastcar/redis/annotation";
367
+ import { RedisClient } from "@fastcar/redis/annotation";
248
368
 
249
369
  @Application
250
370
  @EnableRedis
251
371
  class APP {}
252
- export default new APP();
253
- ```
254
-
255
- #### 使用 RedisTemplate
256
-
257
- ```typescript
258
- import { Service, Autowired } from "@fastcar/core/annotation";
259
- import { RedisClient } from "@fastcar/redis/annotation";
260
372
 
261
373
  @Service
262
374
  class CacheService {
@@ -274,22 +386,42 @@ class CacheService {
274
386
  async del(key: string) {
275
387
  await this.redis.del(key);
276
388
  }
389
+
390
+ // Hash 操作
391
+ async hset(key: string, field: string, value: string) {
392
+ await this.redis.hset(key, field, value);
393
+ }
394
+
395
+ async hget(key: string, field: string) {
396
+ return this.redis.hget(key, field);
397
+ }
398
+
399
+ // List 操作
400
+ async lpush(key: string, value: string) {
401
+ await this.redis.lpush(key, value);
402
+ }
403
+
404
+ async rpop(key: string) {
405
+ return this.redis.rpop(key);
406
+ }
277
407
  }
278
408
  ```
279
409
 
280
410
  ## 数据库逆向生成 (@fastcar/mysql-tool)
281
411
 
282
- ### 生成配置文件
283
-
284
412
  ```bash
413
+ # 生成配置文件
285
414
  fastcar-cli reverse:init
415
+
416
+ # 执行逆向生成
417
+ fastcar-cli reverse
286
418
  ```
287
419
 
288
- ### 配置文件示例
420
+ 配置文件示例:
289
421
 
290
422
  ```json
291
423
  {
292
- "tables": ["users", "orders"],
424
+ "tables": ["table1", "table2"],
293
425
  "modelDir": "D:/project/src/model",
294
426
  "mapperDir": "D:/project/src/mapper",
295
427
  "dbConfig": {
@@ -298,25 +430,10 @@ fastcar-cli reverse:init
298
430
  "user": "root",
299
431
  "password": "password",
300
432
  "database": "test_db"
301
- },
302
- "style": {
303
- "tabWidth": 4,
304
- "printWidth": 200,
305
- "trailingComma": "es5",
306
- "useTabs": true,
307
- "parser": "typescript",
308
- "endOfLine": "crlf"
309
- },
310
- "ignoreCamelcase": false
433
+ }
311
434
  }
312
435
  ```
313
436
 
314
- ### 执行逆向生成
315
-
316
- ```bash
317
- fastcar-cli reverse
318
- ```
319
-
320
437
  ## application.yml 数据库配置
321
438
 
322
439
  ```yaml
@@ -329,6 +446,7 @@ mysql:
329
446
  database: mydb
330
447
  username: root
331
448
  password: password
449
+ connectionLimit: 10
332
450
 
333
451
  pgsql:
334
452
  host: localhost
@@ -372,3 +490,118 @@ npm i @fastcar/core @fastcar/mysql @fastcar/redis
372
490
  # 4. 启动应用
373
491
  npm run debug
374
492
  ```
493
+
494
+ ## 注意事项
495
+
496
+ 1. **排序必须使用 OrderEnum**:`orders: { createdAt: OrderEnum.desc }`,不能使用字符串 `"DESC"`
497
+ 2. **主键查询**:`selectByPrimaryKey` 和 `updateByPrimaryKey` 需要传入包含主键字段的对象
498
+ 3. **批量插入**:`saveList` 会自动分批处理(每批1000条)
499
+ 4. **软删除**:建议使用 `update` 方法更新 `delStatus` 字段,而不是物理删除
500
+
501
+ ## 编码规范
502
+
503
+ ### 1. 实体对象创建规范
504
+
505
+ ```typescript
506
+ // ✅ 正确:使用 key-value 形式创建对象
507
+ const entity = new Entity({
508
+ name: "示例",
509
+ status: 1,
510
+ createTime: new Date(),
511
+ });
512
+
513
+ // ❌ 错误:逐行赋值创建对象
514
+ const entity = new Entity();
515
+ entity.name = "示例";
516
+ entity.status = 1;
517
+ ```
518
+
519
+ ### 2. 分页查询规范
520
+
521
+ ```typescript
522
+ // ✅ 正确:使用 SQL limit/offset 分页
523
+ const total = await this.mapper.count(where);
524
+ const list = await this.mapper.select({
525
+ where: where,
526
+ orders: { createTime: OrderEnum.desc },
527
+ offset: (page - 1) * pageSize,
528
+ limit: pageSize,
529
+ });
530
+
531
+ // ❌ 错误:先全表查询再用 JS slice 分页
532
+ const list = await this.mapper.select({ where });
533
+ const pageData = list.slice((page - 1) * pageSize, page * pageSize);
534
+ ```
535
+
536
+ ### 3. 查询条件规范
537
+
538
+ ```typescript
539
+ import { OperatorEnum } from "@fastcar/core/db";
540
+
541
+ // ✅ 正确:使用 OperatorEnum
542
+ const list = await this.mapper.select({
543
+ where: {
544
+ age: { [OperatorEnum.gte]: 18, [OperatorEnum.lte]: 60 },
545
+ status: { [OperatorEnum.in]: [1, 2, 3] },
546
+ },
547
+ });
548
+ ```
549
+
550
+ ### 4. 接口返回规范
551
+
552
+ ```typescript
553
+ // ✅ 正确:返回空数据
554
+ if (records.length === 0) {
555
+ return Result.ok({ list: [], total: 0 });
556
+ }
557
+
558
+ // ❌ 错误:返回模拟数据
559
+ if (records.length === 0) {
560
+ return Result.ok({ list: [{ name: "模拟数据1" }] });
561
+ }
562
+ ```
563
+
564
+ ### 5. 更新操作规范
565
+
566
+ ```typescript
567
+ // ✅ 正确:更新少于3个字段时使用 update/updateOne
568
+ await this.mapper.updateOne({
569
+ where: { id },
570
+ row: { lastLoginTime: new Date() },
571
+ });
572
+
573
+ // ❌ 错误:为了更新1-2个字段而查询整个实体对象
574
+ const entity = await this.mapper.selectByPrimaryKey({ id });
575
+ entity.lastLoginTime = new Date();
576
+ await this.mapper.updateByPrimaryKey(entity);
577
+ ```
578
+
579
+ ### 6. 复杂查询优化
580
+
581
+ ```typescript
582
+ // ✅ 正确:使用 selectByCustom + JOIN 一条 SQL 完成
583
+ interface QueryResult {
584
+ id: number;
585
+ name: string;
586
+ relatedName: string;
587
+ }
588
+
589
+ const results = await this.mapper.selectByCustom<QueryResult>({
590
+ tableAlias: "t",
591
+ fields: ["t.id", "t.name", "r.name as relatedName"],
592
+ join: [{
593
+ type: "INNER",
594
+ table: "related_table r",
595
+ on: "r.entity_id = t.id",
596
+ }],
597
+ where: { "t.status": 1 },
598
+ camelcaseStyle: true,
599
+ });
600
+
601
+ // ❌ 错误:多次查询 + 内存组装(N+1 问题)
602
+ const list = await this.mapper.select({});
603
+ for (const item of list) {
604
+ const related = await this.relatedMapper.selectOne({ ... });
605
+ // 内存组装...
606
+ }
607
+ ```