@fastcar/cli 0.1.2 → 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.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: fastcar-rpc-microservices
3
- description: FastCar RPC 与微服务开发指南。Use when working with FastCar framework for: (1) Building RPC servers and clients with @fastcar/rpc, (2) Using WebSocket/SocketIO/MQTT/gRPC for service communication, (3) Setting up microservices architecture (center/connector/chat/web/base), (4) Configuring RPC endpoints, authentication, retry policies, (5) Using protobuf with RPC.
3
+ description: FastCar RPC 与微服务开发指南。Use when working with FastCar framework for: (1) Building RPC servers and clients with @fastcar/rpc, (2) Using WebSocket/SocketIO/MQTT/gRPC for service communication, (3) Setting up microservices architecture, (4) Configuring RPC endpoints, authentication, retry policies, (5) Using protobuf with RPC.
4
4
  ---
5
5
 
6
6
  # FastCar RPC & Microservices
7
7
 
8
- FastCar RPC 模块提供基于多种协议(WS、SocketIO、MQTT、gRPC)的远程调用能力,并支持构建 center / connector / chat / web / base 多服务微服务架构。
8
+ FastCar RPC 模块提供基于多种协议(WS、SocketIO、MQTT、gRPC)的远程调用能力,并支持构建多服务微服务架构。
9
9
 
10
10
  ## RPC 核心概念
11
11
 
@@ -23,50 +23,24 @@ export default new APP();
23
23
 
24
24
  ### 服务端配置
25
25
 
26
- 通过 `ApplicationSetting` 注入 RPC 服务器列表配置类:
27
-
28
26
  ```typescript
29
27
  import { Application, ApplicationSetting } from "@fastcar/core/annotation";
30
28
  import { EnableRPC } from "@fastcar/rpc/annotation";
31
- import RpcServerList from "./RpcServerList";
29
+ import { SocketEnum } from "@fastcar/rpc/constant/SocketEnum";
32
30
 
33
31
  @Application
34
32
  @EnableRPC
35
- @ApplicationSetting(RpcServerList)
36
- class APP {}
37
- ```
38
-
39
- `RpcServerList.ts` 示例:
40
-
41
- ```typescript
42
- import { SocketEnum } from "@fastcar/rpc/constant/SocketEnum";
43
- import { Protocol } from "@fastcar/server";
44
- import { CodeProtocolEnum } from "@fastcar/rpc/types/CodeProtocolEnum";
45
- import * as path from "path";
46
-
47
- export default {
33
+ @ApplicationSetting({
48
34
  rpc: {
49
35
  list: [
50
36
  { id: "rpc-1", type: SocketEnum.WS, server: { port: 1238 }, serviceType: "rpc" },
51
37
  { id: "rpc-2", type: SocketEnum.SocketIO, server: { port: 1235 }, serviceType: "rpc" },
52
- { id: "rpc-3", type: SocketEnum.MQTT, server: { port: 1236, protocol: Protocol.net }, serviceType: "rpc" },
53
- {
54
- id: "rpc-4",
55
- type: SocketEnum.MQTT,
56
- server: { port: 1239, protocol: Protocol.https, ssl: { key: "./ssl/server.key", cert: "./ssl/server.crt" } },
57
- serviceType: "rpc",
58
- },
59
- {
60
- id: "rpc-5",
61
- type: SocketEnum.Grpc,
62
- server: { port: 1240, ssl: { ca: path.join(__dirname, "cert/ca.crt"), key: "cert/server.key", cert: "cert/server.crt" } },
63
- serviceType: "rpc",
64
- codeProtocol: CodeProtocolEnum.PROTOBUF,
65
- extra: { checkClientCertificate: true },
66
- },
38
+ { id: "rpc-3", type: SocketEnum.MQTT, server: { port: 1236 }, serviceType: "rpc" },
39
+ { id: "rpc-4", type: SocketEnum.Grpc, server: { port: 1240 }, serviceType: "rpc" },
67
40
  ],
68
41
  },
69
- };
42
+ })
43
+ class APP {}
70
44
  ```
71
45
 
72
46
  支持的协议类型:
@@ -77,15 +51,13 @@ export default {
77
51
 
78
52
  ### 安全认证
79
53
 
80
- 在服务端配置 `secure`:
81
-
82
54
  ```typescript
83
55
  {
84
56
  id: "rpc-auth",
85
57
  type: SocketEnum.WS,
86
58
  server: { port: 1238 },
87
59
  serviceType: "rpc",
88
- secure: { username: "user", password: "123456" },
60
+ secure: { username: "user", password: "your-password" },
89
61
  }
90
62
  ```
91
63
 
@@ -93,7 +65,7 @@ export default {
93
65
 
94
66
  ```typescript
95
67
  import { Controller } from "@fastcar/core/annotation";
96
- import { RPC, RPCMethod, RPCMiddleware } from "@fastcar/rpc/annotation";
68
+ import { RPC, RPCMethod } from "@fastcar/rpc/annotation";
97
69
 
98
70
  @Controller
99
71
  @RPC("/hello")
@@ -130,14 +102,13 @@ const client = new RpcClient(
130
102
  {
131
103
  url: "ws://localhost:1238",
132
104
  type: SocketEnum.WS,
133
- secure: { username: "user", password: "123456" },
105
+ secure: { username: "user", password: "your-password" },
134
106
  },
135
107
  new NotifyHandle()
136
108
  );
137
109
 
138
110
  await client.start();
139
111
  const result = await client.request("/hello");
140
- console.log(result);
141
112
  ```
142
113
 
143
114
  ### 断线重连与重试策略
@@ -157,19 +128,6 @@ const client = new RpcClient(
157
128
  );
158
129
  ```
159
130
 
160
- ### SSL 连接
161
-
162
- ```typescript
163
- const client = new RpcClient(
164
- {
165
- url: "wss://localhost:1239",
166
- type: SocketEnum.MQTT,
167
- extra: { rejectUnauthorized: false },
168
- },
169
- new NotifyHandle()
170
- );
171
- ```
172
-
173
131
  ### Protobuf 调用
174
132
 
175
133
  ```typescript
@@ -177,11 +135,10 @@ import { RpcClient } from "@fastcar/rpc";
177
135
  import { SocketEnum } from "@fastcar/rpc/constant/SocketEnum";
178
136
  import { CodeProtocolEnum } from "@fastcar/rpc/types/CodeProtocolEnum";
179
137
  import { ClientRequestStatic } from "@fastcar/rpc/service/rpc/RequestStatic";
180
- import * as path from "path";
181
138
 
182
139
  const client = new RpcClient(
183
140
  {
184
- url: "local.dev.com:1240",
141
+ url: "localhost:1240",
185
142
  type: SocketEnum.Grpc,
186
143
  codeProtocol: CodeProtocolEnum.PROTOBUF,
187
144
  ssl: {
@@ -189,15 +146,8 @@ const client = new RpcClient(
189
146
  key: path.join(__dirname, "cert/client.key"),
190
147
  cert: path.join(__dirname, "cert/client.crt"),
191
148
  },
192
- extra: {
193
- options: {
194
- "grpc.ssl_target_name_override": "example",
195
- "grpc.default_authority": "example",
196
- },
197
- },
198
149
  },
199
- new NotifyHandle(),
200
- { retryCount: 0 }
150
+ new NotifyHandle()
201
151
  );
202
152
 
203
153
  client.addProtoBuf({
@@ -224,7 +174,7 @@ FastCar 微服务模板将系统拆分为以下服务模块:
224
174
  |------|------|
225
175
  | center | 服务中心,提供服务注册与发现 |
226
176
  | connector | 连接器服务,处理客户端连接(通常标记 `front: true`) |
227
- | chat | 聊天服务,处理实时消息 |
177
+ | message | 消息服务,处理实时消息 |
228
178
  | web | Web 服务,提供 HTTP 接口 |
229
179
  | base | 基础服务,提供公共功能 |
230
180
 
@@ -234,7 +184,7 @@ FastCar 微服务模板将系统拆分为以下服务模块:
234
184
  settings:
235
185
  microservices:
236
186
  center:
237
- token: "nW0tT4bZ6qM7mF7wD2rT2pR9dT7gK3hZ"
187
+ token: "your-token-here"
238
188
  servers:
239
189
  - host: "localhost"
240
190
  clusters: 1
@@ -246,7 +196,7 @@ settings:
246
196
  disconnectInterval: 1000
247
197
  retry: { retryCount: 3, retryInterval: 3000, timeout: 30000, maxMsgNum: 10000, increase: true }
248
198
  connector:
249
- token: "x3TGsWC9uloZu235LA07eAiJ61nQ1A5f"
199
+ token: "your-token-here"
250
200
  servers:
251
201
  - host: "localhost"
252
202
  clusters: 1
@@ -254,8 +204,8 @@ settings:
254
204
  - front: true
255
205
  type: "ws"
256
206
  server: { port: 60100 }
257
- chat:
258
- token: "go0kbkNM3wQ4e2Vgo0kbkNM3wQ4e2V"
207
+ message:
208
+ token: "your-token-here"
259
209
  servers:
260
210
  - host: "localhost"
261
211
  clusters: 1
@@ -263,7 +213,7 @@ settings:
263
213
  - type: "ws"
264
214
  server: { port: 60200 }
265
215
  web:
266
- token: "go0kbkNM3wQ4e2Vgo0kbkNM3wQ4e2V"
216
+ token: "your-token-here"
267
217
  koa:
268
218
  koaBodyParser:
269
219
  enableTypes: ["json", "form", "text"]
@@ -17,58 +17,58 @@ FastCar Serverless 框架支持将 FastCar 应用部署到阿里云函数计算
17
17
  import { ServerlessApp, Service, Handler, HttpTrigger, TimerTrigger, EventTrigger } from "@fastcar/serverless";
18
18
 
19
19
  @Service
20
- class OrderService {
21
- async createOrder(data: any) {
22
- return { orderId: Date.now(), ...data };
20
+ class BizService {
21
+ async process(data: any) {
22
+ return { id: Date.now(), ...data };
23
23
  }
24
24
  }
25
25
 
26
26
  @ServerlessApp({
27
- name: "order-service",
27
+ name: "example-service",
28
28
  version: "1.0.0",
29
- components: [OrderService],
29
+ components: [BizService],
30
30
  init: async (app) => {
31
- console.log("Order service initializing...");
31
+ console.log("Service initializing...");
32
32
  },
33
33
  })
34
- class OrderApp {
35
- @HttpTrigger({ path: "/orders", method: "POST" })
34
+ class ExampleApp {
35
+ @HttpTrigger({ path: "/items", method: "POST" })
36
36
  @Handler()
37
- async createOrder(event: any, context: any) {
38
- const orderService = (this as any).app.getFastCarApp().getComponentByName("OrderService") as OrderService;
39
- const order = await orderService.createOrder(event.body);
37
+ async createItem(event: any, context: any) {
38
+ const service = (this as any).app.getFastCarApp().getComponentByName("BizService") as BizService;
39
+ const result = await service.process(event.body);
40
40
  return {
41
41
  statusCode: 200,
42
42
  headers: { "Content-Type": "application/json" },
43
- body: { success: true, data: order },
43
+ body: { success: true, data: result },
44
44
  };
45
45
  }
46
46
 
47
- @HttpTrigger({ path: "/orders/:id", method: "GET" })
47
+ @HttpTrigger({ path: "/items/:id", method: "GET" })
48
48
  @Handler()
49
- async getOrder(event: any, context: any) {
49
+ async getItem(event: any, context: any) {
50
50
  return {
51
51
  statusCode: 200,
52
- body: { orderId: event.params?.id },
52
+ body: { id: event.params?.id },
53
53
  };
54
54
  }
55
55
 
56
56
  @TimerTrigger({ cron: "0 0 * * * *" })
57
57
  @Handler()
58
- async hourlyReport(event: any, context: any) {
59
- console.log(`[${context.requestId}] Generating hourly report`);
58
+ async hourlyTask(event: any, context: any) {
59
+ console.log(`[${context.requestId}] Running scheduled task`);
60
60
  return { success: true };
61
61
  }
62
62
 
63
63
  @EventTrigger({ eventSource: "oss" })
64
64
  @Handler()
65
- async handleFileUpload(event: any, context: any) {
66
- console.log(`[${context.requestId}] Processing file upload event`);
65
+ async handleEvent(event: any, context: any) {
66
+ console.log(`[${context.requestId}] Processing event`);
67
67
  return { success: true };
68
68
  }
69
69
  }
70
70
 
71
- const orderApp = new OrderApp();
71
+ const app = new ExampleApp();
72
72
  ```
73
73
 
74
74
  ### 触发器类型
@@ -100,51 +100,51 @@ import {
100
100
  import { startLocalDev } from "@fastcar/serverless/local";
101
101
 
102
102
  @Service
103
- class UserService {
104
- private users = [
105
- { id: "1", name: "张三" },
106
- { id: "2", name: "李四" },
103
+ class DataService {
104
+ private data = [
105
+ { id: "1", name: "示例1" },
106
+ { id: "2", name: "示例2" },
107
107
  ];
108
- findAll() { return this.users; }
109
- findById(id: string) { return this.users.find(u => u.id === id); }
110
- create(user: any) {
111
- const newUser = { id: String(Date.now()), ...user };
112
- this.users.push(newUser);
113
- return newUser;
108
+ findAll() { return this.data; }
109
+ findById(id: string) { return this.data.find(d => d.id === id); }
110
+ create(item: any) {
111
+ const newItem = { id: String(Date.now()), ...item };
112
+ this.data.push(newItem);
113
+ return newItem;
114
114
  }
115
115
  }
116
116
 
117
117
  @Controller
118
- class UserController {
118
+ class ApiController {
119
119
  @Autowired
120
- private userService!: UserService;
120
+ private service!: DataService;
121
121
 
122
- @HttpTrigger({ path: "/users", method: "GET" })
122
+ @HttpTrigger({ path: "/items", method: "GET" })
123
123
  @Handler()
124
- async listUsers(ctx: ServerlessContext) {
125
- ctx.json({ code: 0, data: this.userService.findAll(), requestId: ctx.requestId });
124
+ async list(ctx: ServerlessContext) {
125
+ ctx.json({ code: 0, data: this.service.findAll(), requestId: ctx.requestId });
126
126
  }
127
127
 
128
- @HttpTrigger({ path: "/users/:id", method: "GET" })
128
+ @HttpTrigger({ path: "/items/:id", method: "GET" })
129
129
  @Handler()
130
- async getUser(ctx: ServerlessContext) {
131
- const user = this.userService.findById(ctx.params.id);
132
- if (!user) ctx.throw(404, "User not found");
133
- ctx.json({ code: 0, data: user });
130
+ async getOne(ctx: ServerlessContext) {
131
+ const item = this.service.findById(ctx.params.id);
132
+ if (!item) ctx.throw(404, "Not found");
133
+ ctx.json({ code: 0, data: item });
134
134
  }
135
135
 
136
- @HttpTrigger({ path: "/users", method: "POST" })
136
+ @HttpTrigger({ path: "/items", method: "POST" })
137
137
  @Handler()
138
- async createUser(ctx: ServerlessContext) {
139
- const user = this.userService.create(ctx.bodyData);
138
+ async create(ctx: ServerlessContext) {
139
+ const item = this.service.create(ctx.bodyData);
140
140
  ctx.status = 201;
141
- ctx.json({ code: 0, data: user });
141
+ ctx.json({ code: 0, data: item });
142
142
  }
143
143
  }
144
144
 
145
145
  async function main() {
146
146
  const app = new ServerlessApplication();
147
- app.register(UserController, UserService);
147
+ app.register(ApiController, DataService);
148
148
  app.use(ErrorMiddleware({ includeStack: true }));
149
149
  app.use(LoggerMiddleware({ logQuery: true }));
150
150
  app.use(CorsMiddleware({ origin: "*" }));
@@ -167,21 +167,21 @@ main().catch(console.error);
167
167
 
168
168
  ```typescript
169
169
  import { createFCAdapter } from "@fastcar/serverless";
170
- export const handler = createFCAdapter((orderApp as any).app);
170
+ export const handler = createFCAdapter((app as any).app);
171
171
  ```
172
172
 
173
173
  ### 腾讯云 SCF
174
174
 
175
175
  ```typescript
176
176
  import { createSCFAdapter } from "@fastcar/serverless";
177
- export const main_handler = createSCFAdapter((orderApp as any).app);
177
+ export const main_handler = createSCFAdapter((app as any).app);
178
178
  ```
179
179
 
180
180
  ### AWS Lambda
181
181
 
182
182
  ```typescript
183
183
  import { createLambdaAdapter } from "@fastcar/serverless";
184
- export const handler = createLambdaAdapter((orderApp as any).app);
184
+ export const handler = createLambdaAdapter((app as any).app);
185
185
  ```
186
186
 
187
187
  ## 常用中间件
@@ -28,41 +28,35 @@ import { Service, Autowired } from "@fastcar/core/annotation";
28
28
  import { CacheApplication } from "@fastcar/cache";
29
29
 
30
30
  @Service
31
- class UserCacheService {
31
+ class CacheService {
32
32
  @Autowired
33
33
  private cache!: CacheApplication;
34
34
 
35
- setUser(id: string, user: any) {
35
+ setItem(id: string, data: any) {
36
36
  // ttl 单位秒,0 为不过期
37
- this.cache.set("userStore", id, user, { ttl: 60 });
37
+ this.cache.set("dataStore", id, data, { ttl: 60 });
38
38
  }
39
39
 
40
- getUser(id: string) {
41
- return this.cache.get("userStore", id);
40
+ getItem(id: string) {
41
+ return this.cache.get("dataStore", id);
42
42
  }
43
43
 
44
- hasUser(id: string) {
45
- return this.cache.has("userStore", id);
44
+ hasItem(id: string) {
45
+ return this.cache.has("dataStore", id);
46
46
  }
47
47
 
48
- deleteUser(id: string) {
49
- return this.cache.delete("userStore", id);
48
+ deleteItem(id: string) {
49
+ return this.cache.delete("dataStore", id);
50
50
  }
51
51
 
52
- getTTL(id: string) {
53
- return this.cache.getTTL("userStore", id);
54
- }
55
-
56
- getAllUsers() {
57
- return this.cache.getDictionary("userStore");
52
+ getAll() {
53
+ return this.cache.getDictionary("dataStore");
58
54
  }
59
55
  }
60
56
  ```
61
57
 
62
58
  ### 持久化缓存配置
63
59
 
64
- 通过 `@CacheMapping` 配置缓存节点,支持文件持久化或数据库持久化:
65
-
66
60
  ```typescript
67
61
  import { CacheMapping } from "@fastcar/cache";
68
62
  import { FSClient } from "@fastcar/cache";
@@ -80,11 +74,13 @@ class CacheConfig {}
80
74
 
81
75
  ## 定时任务 (@fastcar/timer)
82
76
 
77
+ > **推荐使用 `@fastcar/timer/scheduling2` 模块**
78
+
83
79
  ### 开启定时任务
84
80
 
85
81
  ```typescript
86
82
  import { Application } from "@fastcar/core/annotation";
87
- import { EnableScheduling } from "@fastcar/timer";
83
+ import { EnableScheduling } from "@fastcar/timer/scheduling2";
88
84
 
89
85
  @Application
90
86
  @EnableScheduling
@@ -94,7 +90,7 @@ class APP {}
94
90
  ### 间隔任务
95
91
 
96
92
  ```typescript
97
- import { ScheduledInterval } from "@fastcar/timer";
93
+ import { ScheduledInterval } from "@fastcar/timer/scheduling2";
98
94
  import { Component } from "@fastcar/core/annotation";
99
95
 
100
96
  @Component
@@ -109,7 +105,7 @@ class HeartbeatTask {
109
105
  ### Cron 任务
110
106
 
111
107
  ```typescript
112
- import { ScheduledCron } from "@fastcar/timer";
108
+ import { ScheduledCron } from "@fastcar/timer/scheduling2";
113
109
  import { Component } from "@fastcar/core/annotation";
114
110
 
115
111
  @Component
@@ -123,7 +119,7 @@ class ReportTask {
123
119
 
124
120
  ## 时间轮 (@fastcar/timewheel)
125
121
 
126
- 适用于需要大量延时任务的场景(如订单超时取消、消息延时投递)。
122
+ 适用于需要大量延时任务的场景(如超时取消、消息延时投递)。
127
123
 
128
124
  ```typescript
129
125
  import { HashedWheelTimer } from "@fastcar/timewheel";
@@ -135,7 +131,7 @@ const timer = new HashedWheelTimer<string>({
135
131
  });
136
132
 
137
133
  // 添加一个 5 秒后触发的任务
138
- timer.addId("order-123", 5000);
134
+ timer.addId("job-123", 5000);
139
135
 
140
136
  // 配合心跳循环处理
141
137
  setInterval(() => {
@@ -148,7 +144,7 @@ setInterval(() => {
148
144
  }, 100);
149
145
 
150
146
  // 取消任务
151
- timer.removeId("order-123", slotId);
147
+ timer.removeId("job-123", slotId);
152
148
  ```
153
149
 
154
150
  ## 工作线程池 (@fastcar/workerpool)
@@ -156,7 +152,7 @@ timer.removeId("order-123", slotId);
156
152
  将 CPU 密集型操作卸载到 worker 线程执行,避免阻塞主线程。
157
153
 
158
154
  ```typescript
159
- import { WorkerPool, TaskType, TaskSyncType } from "@fastcar/workerpool";
155
+ import { WorkerPool } from "@fastcar/workerpool";
160
156
 
161
157
  const pool = new WorkerPool({
162
158
  minWorkers: 2,
@@ -167,7 +163,7 @@ const pool = new WorkerPool({
167
163
  const result = await pool.exec("heavyComputation", [1, 2, 3, 4, 5]);
168
164
  ```
169
165
 
170
- 在 FastCar 应用中通常通过 `@fastcar/core` 的 `@WorkerPool` / `@WorkerTask` 注解使用(参考 fastcar-framework skill)。
166
+ 在 FastCar 应用中通过 `@WorkerPool` / `@WorkerTask` 注解使用(参考 fastcar-framework skill)。
171
167
 
172
168
  ## 文件监听 (@fastcar/watchfile)
173
169
 
@@ -206,7 +202,7 @@ const sign = getSign(
206
202
  {
207
203
  appid: account.appid,
208
204
  expireTime: Math.floor((Date.now() + 5 * 60 * 1000) / 1000),
209
- dir_path: "/", // 授权路径
205
+ dir_path: "/",
210
206
  mode: 7, // 1可读 2可写 4可查
211
207
  },
212
208
  account.serectkey
@@ -232,7 +228,6 @@ await cos.uploadfile("/test/text.txt", file);
232
228
 
233
229
  // 下载文件
234
230
  const res = await cos.getFile("/test/hello/test.txt");
235
- console.log(res.data);
236
231
 
237
232
  // 带鉴权下载
238
233
  await cos.getFile("/test.txt", true);
@@ -256,10 +251,6 @@ await cos.rename("/test/old.txt", "/test/new.txt");
256
251
 
257
252
  // 设置重定向
258
253
  await cos.setRedirect({ redirectUrl: "/test/hello.txt", flag: false, bucket: "test" });
259
-
260
- // 查询重定向
261
- await cos.getRedirect();
262
- await cos.queryRedirect({ bucketUrl: "http://xxx" });
263
254
  ```
264
255
 
265
256
  ## 完整模块列表
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: typescript-coding-style
3
+ description: TypeScript 编码规范与最佳实践。Use when writing TypeScript code for: (1) Defining reusable type aliases for complex intersection types, (2) Using enums instead of string literals for status fields, (3) Naming conventions for types and interfaces, (4) Code organization and maintainability tips.
4
+ ---
5
+
6
+ # TypeScript 编码规范
7
+
8
+ ## 1. 复用复杂类型别名
9
+
10
+ ### 问题场景
11
+ 当同一个交叉类型在多处使用时,直接重复书写会导致代码冗余、维护困难和可读性差。
12
+
13
+ ### 反例
14
+ ```typescript
15
+ // ❌ 重复书写复杂的交叉类型
16
+ private async getData(): Promise<(Detail & { related: Related })[]> {
17
+ const result: (Detail & { related: Related })[] = [];
18
+ }
19
+
20
+ private groupById(
21
+ items: (Detail & { related: Related })[]
22
+ ): Map<string, (Detail & { related: Related })[]> {
23
+ }
24
+ ```
25
+
26
+ ### 正例
27
+ ```typescript
28
+ // ✅ 定义类型别名,一处定义多处使用
29
+ type DetailWithRelated = Detail & { related: Related };
30
+
31
+ // 或者使用 interface
32
+ interface DetailWithRelated extends Detail {
33
+ related: Related;
34
+ }
35
+
36
+ // 使用
37
+ private async getData(): Promise<DetailWithRelated[]> {
38
+ const result: DetailWithRelated[] = [];
39
+ }
40
+
41
+ private groupById(items: DetailWithRelated[]): Map<string, DetailWithRelated[]> {
42
+ }
43
+ ```
44
+
45
+ ### 规范建议
46
+ 1. **当交叉类型在 2 处及以上使用时**,应提取为类型别名或接口
47
+ 2. **命名规范**:使用 `With` 连接,如 `DetailWithRelated`
48
+ 3. **文档注释**:为复杂类型添加 JSDoc 说明其用途
49
+
50
+ ---
51
+
52
+ ## 2. 使用枚举代替字符串字面量
53
+
54
+ ### 问题场景
55
+ 状态字段使用字符串字面量会导致拼写错误、IDE 无法自动补全、重构困难和类型安全性差。
56
+
57
+ ### 反例
58
+ ```typescript
59
+ // ❌ 使用字符串字面量
60
+ const result = await this.mapper.select({
61
+ where: { status: "pending" }
62
+ });
63
+
64
+ await this.mapper.updateOne({
65
+ where: { id },
66
+ row: { status: "running" }
67
+ });
68
+
69
+ switch (detail.status) {
70
+ case "success":
71
+ break;
72
+ case "failed":
73
+ break;
74
+ }
75
+ ```
76
+
77
+ ### 正例
78
+ ```typescript
79
+ // ✅ 定义枚举
80
+ export enum JobStatus {
81
+ pending = "pending", // 待执行
82
+ running = "running", // 执行中
83
+ success = "success", // 成功
84
+ failed = "failed", // 失败
85
+ }
86
+
87
+ // 使用枚举
88
+ const result = await this.mapper.select({
89
+ where: { status: JobStatus.pending }
90
+ });
91
+
92
+ await this.mapper.updateOne({
93
+ where: { id },
94
+ row: { status: JobStatus.running }
95
+ });
96
+
97
+ switch (detail.status) {
98
+ case JobStatus.success:
99
+ break;
100
+ case JobStatus.failed:
101
+ break;
102
+ }
103
+ ```
104
+
105
+ ### 规范建议
106
+ 1. **状态字段优先使用枚举**:任何表示状态的字段都应该使用枚举
107
+ 2. **枚举命名**:使用 PascalCase,如 `JobStatus`、`ItemType`
108
+ 3. **枚举值**:字符串枚举推荐,便于调试和序列化
109
+ 4. **注释说明**:为每个枚举值添加 JSDoc 注释
110
+
111
+ ### 完整示例
112
+ ```typescript
113
+ // types/Enums.ts
114
+ export enum JobStatus {
115
+ pending = "pending",
116
+ running = "running",
117
+ success = "success",
118
+ failed = "failed",
119
+ }
120
+
121
+ export enum ItemType {
122
+ typeA = "typeA",
123
+ typeB = "typeB",
124
+ typeC = "typeC",
125
+ }
126
+
127
+ export enum ExecuteMode {
128
+ now = "now",
129
+ schedule = "schedule",
130
+ }
131
+
132
+ // 使用
133
+ import { JobStatus, ItemType } from "@/types/Enums";
134
+
135
+ class Service {
136
+ async create(type: ItemType) {
137
+ const item = new Record({
138
+ itemType: type,
139
+ status: JobStatus.pending,
140
+ executeMode: ExecuteMode.now,
141
+ });
142
+ }
143
+ }
144
+ ```