@robsun/create-keystone-app 0.2.12 → 0.2.15

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.
Files changed (25) hide show
  1. package/README.md +1 -0
  2. package/dist/create-keystone-app.js +0 -0
  3. package/dist/create-module.js +584 -2
  4. package/package.json +22 -23
  5. package/template/.claude/skills/keystone-dev/SKILL.md +90 -103
  6. package/template/.claude/skills/keystone-dev/references/ADVANCED_PATTERNS.md +716 -0
  7. package/template/.claude/skills/keystone-dev/references/APPROVAL.md +47 -0
  8. package/template/.claude/skills/keystone-dev/references/CAPABILITIES.md +60 -5
  9. package/template/.claude/skills/keystone-dev/references/CHECKLIST.md +285 -0
  10. package/template/.claude/skills/keystone-dev/references/GOTCHAS.md +390 -0
  11. package/template/.claude/skills/keystone-dev/references/PATTERNS.md +605 -0
  12. package/template/.claude/skills/keystone-dev/references/TEMPLATES.md +2562 -384
  13. package/template/.codex/skills/keystone-dev/SKILL.md +90 -103
  14. package/template/.codex/skills/keystone-dev/references/ADVANCED_PATTERNS.md +716 -0
  15. package/template/.codex/skills/keystone-dev/references/APPROVAL.md +47 -0
  16. package/template/.codex/skills/keystone-dev/references/CAPABILITIES.md +60 -5
  17. package/template/.codex/skills/keystone-dev/references/CHECKLIST.md +285 -0
  18. package/template/.codex/skills/keystone-dev/references/GOTCHAS.md +390 -0
  19. package/template/.codex/skills/keystone-dev/references/PATTERNS.md +605 -0
  20. package/template/.codex/skills/keystone-dev/references/TEMPLATES.md +2562 -384
  21. package/template/README.md +8 -1
  22. package/template/apps/server/go.mod +97 -97
  23. package/template/apps/server/go.sum +283 -283
  24. package/template/docs/CONVENTIONS.md +11 -8
  25. package/template/package.json +3 -3
@@ -0,0 +1,716 @@
1
+ # Keystone 高级开发模式
2
+
3
+ > 本文档补充复杂业务场景的设计模式和最佳实践。
4
+
5
+ ## 模块间依赖管理
6
+
7
+ ### 1. 跨模块服务调用
8
+
9
+ 当模块 A 需要调用模块 B 的服务时,通过依赖注入避免循环依赖。
10
+
11
+ ```go
12
+ // apps/server/internal/app/container.go
13
+ type Container struct {
14
+ OrderService *order.OrderService
15
+ ProductService *product.ProductService
16
+ // ...
17
+ }
18
+
19
+ func NewContainer(db *gorm.DB) *Container {
20
+ // 先创建基础服务
21
+ productRepo := product.NewProductRepository(db)
22
+ productSvc := product.NewProductService(productRepo)
23
+
24
+ // 再创建依赖其他模块的服务
25
+ orderRepo := order.NewOrderRepository(db)
26
+ orderSvc := order.NewOrderService(orderRepo, productSvc) // 注入 productSvc
27
+
28
+ return &Container{
29
+ OrderService: orderSvc,
30
+ ProductService: productSvc,
31
+ }
32
+ }
33
+ ```
34
+
35
+ ### 2. 接口隔离原则
36
+
37
+ 在 Service 中只依赖需要的接口,而非整个服务:
38
+
39
+ ```go
40
+ // order/domain/service/service.go
41
+ package service
42
+
43
+ // 只定义需要的方法接口
44
+ type ProductPricer interface {
45
+ GetPrice(ctx context.Context, productID uint) (float64, error)
46
+ }
47
+
48
+ type OrderService struct {
49
+ repo OrderRepository
50
+ pricer ProductPricer // 只依赖接口
51
+ }
52
+
53
+ func NewOrderService(repo OrderRepository, pricer ProductPricer) *OrderService {
54
+ return &OrderService{repo: repo, pricer: pricer}
55
+ }
56
+ ```
57
+
58
+ ### 3. 事件驱动解耦
59
+
60
+ 对于松耦合场景,使用事件总线:
61
+
62
+ ```go
63
+ // infra/events/bus.go
64
+ type Event interface {
65
+ Name() string
66
+ }
67
+
68
+ type Handler func(ctx context.Context, event Event) error
69
+
70
+ type Bus struct {
71
+ handlers map[string][]Handler
72
+ mu sync.RWMutex
73
+ }
74
+
75
+ func (b *Bus) Subscribe(eventName string, handler Handler) {
76
+ b.mu.Lock()
77
+ defer b.mu.Unlock()
78
+ b.handlers[eventName] = append(b.handlers[eventName], handler)
79
+ }
80
+
81
+ func (b *Bus) Publish(ctx context.Context, event Event) error {
82
+ b.mu.RLock()
83
+ handlers := b.handlers[event.Name()]
84
+ b.mu.RUnlock()
85
+
86
+ for _, h := range handlers {
87
+ if err := h(ctx, event); err != nil {
88
+ slog.Error("事件处理失败", "event", event.Name(), "error", err)
89
+ }
90
+ }
91
+ return nil
92
+ }
93
+
94
+ // 使用示例
95
+ type OrderCreatedEvent struct {
96
+ OrderID uint
97
+ TenantID uint
98
+ Amount float64
99
+ CreatedAt time.Time
100
+ }
101
+
102
+ func (e OrderCreatedEvent) Name() string { return "order.created" }
103
+
104
+ // 在 Order 模块发布事件
105
+ func (s *OrderService) Create(ctx context.Context, ...) (*Order, error) {
106
+ // ... 创建订单逻辑
107
+
108
+ s.bus.Publish(ctx, OrderCreatedEvent{
109
+ OrderID: order.ID,
110
+ TenantID: order.TenantID,
111
+ Amount: order.TotalAmount,
112
+ CreatedAt: order.CreatedAt,
113
+ })
114
+
115
+ return order, nil
116
+ }
117
+
118
+ // 在 Statistics 模块订阅事件
119
+ func (m *StatisticsModule) RegisterEventHandlers(bus *events.Bus) {
120
+ bus.Subscribe("order.created", func(ctx context.Context, e events.Event) error {
121
+ event := e.(OrderCreatedEvent)
122
+ return m.svc.UpdateDailyStats(ctx, event.TenantID, event.Amount)
123
+ })
124
+ }
125
+ ```
126
+
127
+ ---
128
+
129
+ ## 状态机模式
130
+
131
+ 适用于有复杂状态流转的业务实体(订单、审批、工单等)。
132
+
133
+ ### 1. 状态机定义
134
+
135
+ ```go
136
+ // domain/models/states.go
137
+ package models
138
+
139
+ type OrderStatus string
140
+
141
+ const (
142
+ OrderStatusDraft OrderStatus = "draft"
143
+ OrderStatusSubmitted OrderStatus = "submitted"
144
+ OrderStatusApproved OrderStatus = "approved"
145
+ OrderStatusRejected OrderStatus = "rejected"
146
+ OrderStatusPaid OrderStatus = "paid"
147
+ OrderStatusShipped OrderStatus = "shipped"
148
+ OrderStatusCompleted OrderStatus = "completed"
149
+ OrderStatusCancelled OrderStatus = "cancelled"
150
+ )
151
+
152
+ // 状态转换规则
153
+ var orderTransitions = map[OrderStatus][]OrderStatus{
154
+ OrderStatusDraft: {OrderStatusSubmitted, OrderStatusCancelled},
155
+ OrderStatusSubmitted: {OrderStatusApproved, OrderStatusRejected},
156
+ OrderStatusApproved: {OrderStatusPaid, OrderStatusCancelled},
157
+ OrderStatusRejected: {OrderStatusDraft},
158
+ OrderStatusPaid: {OrderStatusShipped},
159
+ OrderStatusShipped: {OrderStatusCompleted},
160
+ }
161
+
162
+ func (s OrderStatus) CanTransitionTo(target OrderStatus) bool {
163
+ allowed, exists := orderTransitions[s]
164
+ if !exists {
165
+ return false
166
+ }
167
+ for _, a := range allowed {
168
+ if a == target {
169
+ return true
170
+ }
171
+ }
172
+ return false
173
+ }
174
+
175
+ func (o *Order) TransitionTo(newStatus OrderStatus) error {
176
+ if !o.Status.CanTransitionTo(newStatus) {
177
+ return fmt.Errorf("无法从 %s 转换到 %s", o.Status, newStatus)
178
+ }
179
+ o.Status = newStatus
180
+ return nil
181
+ }
182
+ ```
183
+
184
+ ### 2. 状态操作服务
185
+
186
+ ```go
187
+ // domain/service/transitions.go
188
+ package service
189
+
190
+ func (s *OrderService) Submit(ctx context.Context, tenantID, id, userID uint) error {
191
+ return s.transition(ctx, tenantID, id, userID, models.OrderStatusSubmitted, nil)
192
+ }
193
+
194
+ func (s *OrderService) Approve(ctx context.Context, tenantID, id, userID uint) error {
195
+ return s.transition(ctx, tenantID, id, userID, models.OrderStatusApproved, nil)
196
+ }
197
+
198
+ func (s *OrderService) Reject(ctx context.Context, tenantID, id, userID uint, reason string) error {
199
+ return s.transition(ctx, tenantID, id, userID, models.OrderStatusRejected, func(o *models.Order) {
200
+ o.RejectReason = reason
201
+ })
202
+ }
203
+
204
+ func (s *OrderService) transition(
205
+ ctx context.Context,
206
+ tenantID, id, userID uint,
207
+ newStatus models.OrderStatus,
208
+ beforeSave func(*models.Order),
209
+ ) error {
210
+ order, err := s.repo.FindByID(tenantID, id)
211
+ if err != nil {
212
+ return err
213
+ }
214
+
215
+ oldStatus := order.Status
216
+ if err := order.TransitionTo(newStatus); err != nil {
217
+ return err
218
+ }
219
+
220
+ if beforeSave != nil {
221
+ beforeSave(order)
222
+ }
223
+
224
+ // 记录状态变更日志
225
+ log := &models.OrderStatusLog{
226
+ OrderID: order.ID,
227
+ FromStatus: oldStatus,
228
+ ToStatus: newStatus,
229
+ OperatorID: userID,
230
+ CreatedAt: time.Now(),
231
+ }
232
+
233
+ return s.db.Transaction(func(tx *gorm.DB) error {
234
+ if err := tx.Save(order).Error; err != nil {
235
+ return err
236
+ }
237
+ return tx.Create(log).Error
238
+ })
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 多租户高级模式
245
+
246
+ ### 1. 自动租户过滤中间件
247
+
248
+ ```go
249
+ // infra/middleware/tenant.go
250
+ package middleware
251
+
252
+ func TenantScope(db *gorm.DB) gin.HandlerFunc {
253
+ return func(c *gin.Context) {
254
+ tenantID, ok := hcommon.GetTenantID(c)
255
+ if !ok {
256
+ response.Unauthorized(c, "租户信息缺失")
257
+ c.Abort()
258
+ return
259
+ }
260
+
261
+ // 创建带租户作用域的 DB 实例
262
+ scopedDB := db.Scopes(func(d *gorm.DB) *gorm.DB {
263
+ return d.Where("tenant_id = ?", tenantID)
264
+ })
265
+
266
+ // 存入 context
267
+ c.Set("scopedDB", scopedDB)
268
+ c.Next()
269
+ }
270
+ }
271
+
272
+ // 在 handler 中使用
273
+ func GetScopedDB(c *gin.Context) *gorm.DB {
274
+ if db, ok := c.Get("scopedDB"); ok {
275
+ return db.(*gorm.DB)
276
+ }
277
+ return nil
278
+ }
279
+ ```
280
+
281
+ ### 2. 租户数据隔离验证
282
+
283
+ ```go
284
+ // 在 Repository 中添加验证
285
+ func (r *Repository) FindByID(tenantID, id uint) (*models.Entity, error) {
286
+ var entity models.Entity
287
+ err := r.db.Where("id = ?", id).First(&entity).Error
288
+ if errors.Is(err, gorm.ErrRecordNotFound) {
289
+ return nil, service.ErrNotFound
290
+ }
291
+ if err != nil {
292
+ return nil, err
293
+ }
294
+
295
+ // 验证租户归属
296
+ if entity.TenantID != tenantID {
297
+ slog.Warn("跨租户访问尝试",
298
+ "requestedTenant", tenantID,
299
+ "actualTenant", entity.TenantID,
300
+ "entityID", id,
301
+ )
302
+ return nil, service.ErrNotFound // 不暴露真实错误
303
+ }
304
+
305
+ return &entity, nil
306
+ }
307
+ ```
308
+
309
+ ---
310
+
311
+ ## 缓存模式
312
+
313
+ ### 1. 读穿透缓存
314
+
315
+ ```go
316
+ // infra/cache/cache.go
317
+ package cache
318
+
319
+ import (
320
+ "context"
321
+ "encoding/json"
322
+ "time"
323
+
324
+ "github.com/redis/go-redis/v9"
325
+ )
326
+
327
+ type Cache struct {
328
+ client *redis.Client
329
+ }
330
+
331
+ func (c *Cache) GetOrLoad[T any](
332
+ ctx context.Context,
333
+ key string,
334
+ ttl time.Duration,
335
+ loader func() (T, error),
336
+ ) (T, error) {
337
+ var result T
338
+
339
+ // 尝试从缓存读取
340
+ data, err := c.client.Get(ctx, key).Bytes()
341
+ if err == nil {
342
+ if err := json.Unmarshal(data, &result); err == nil {
343
+ return result, nil
344
+ }
345
+ }
346
+
347
+ // 缓存未命中,加载数据
348
+ result, err = loader()
349
+ if err != nil {
350
+ return result, err
351
+ }
352
+
353
+ // 写入缓存
354
+ data, _ = json.Marshal(result)
355
+ c.client.Set(ctx, key, data, ttl)
356
+
357
+ return result, nil
358
+ }
359
+
360
+ // 使用示例
361
+ func (s *ProductService) GetByID(ctx context.Context, tenantID, id uint) (*models.Product, error) {
362
+ key := fmt.Sprintf("product:%d:%d", tenantID, id)
363
+
364
+ return s.cache.GetOrLoad(ctx, key, 5*time.Minute, func() (*models.Product, error) {
365
+ return s.repo.FindByID(tenantID, id)
366
+ })
367
+ }
368
+ ```
369
+
370
+ ### 2. 缓存失效策略
371
+
372
+ ```go
373
+ // 更新时主动失效
374
+ func (s *ProductService) Update(ctx context.Context, tenantID, id uint, input UpdateInput) (*models.Product, error) {
375
+ product, err := s.repo.Update(ctx, tenantID, id, input)
376
+ if err != nil {
377
+ return nil, err
378
+ }
379
+
380
+ // 删除缓存
381
+ key := fmt.Sprintf("product:%d:%d", tenantID, id)
382
+ s.cache.Delete(ctx, key)
383
+
384
+ // 发布缓存失效事件(用于分布式场景)
385
+ s.bus.Publish(ctx, CacheInvalidatedEvent{Key: key})
386
+
387
+ return product, nil
388
+ }
389
+ ```
390
+
391
+ ---
392
+
393
+ ## 并发控制模式
394
+
395
+ ### 1. 分布式锁
396
+
397
+ ```go
398
+ // infra/lock/redis.go
399
+ package lock
400
+
401
+ import (
402
+ "context"
403
+ "time"
404
+
405
+ "github.com/redis/go-redis/v9"
406
+ )
407
+
408
+ type RedisLock struct {
409
+ client *redis.Client
410
+ }
411
+
412
+ func (l *RedisLock) TryLock(ctx context.Context, key string, ttl time.Duration) (bool, error) {
413
+ return l.client.SetNX(ctx, "lock:"+key, "1", ttl).Result()
414
+ }
415
+
416
+ func (l *RedisLock) Unlock(ctx context.Context, key string) error {
417
+ return l.client.Del(ctx, "lock:"+key).Err()
418
+ }
419
+
420
+ // 使用示例:防止重复提交
421
+ func (s *OrderService) Submit(ctx context.Context, tenantID, id, userID uint) error {
422
+ lockKey := fmt.Sprintf("order:submit:%d:%d", tenantID, id)
423
+
424
+ acquired, err := s.lock.TryLock(ctx, lockKey, 30*time.Second)
425
+ if err != nil {
426
+ return err
427
+ }
428
+ if !acquired {
429
+ return ErrOperationInProgress
430
+ }
431
+ defer s.lock.Unlock(ctx, lockKey)
432
+
433
+ // 执行提交逻辑
434
+ return s.doSubmit(ctx, tenantID, id, userID)
435
+ }
436
+ ```
437
+
438
+ ### 2. 幂等性处理
439
+
440
+ ```go
441
+ // infra/idempotency/store.go
442
+ package idempotency
443
+
444
+ type Store interface {
445
+ Check(ctx context.Context, key string) (exists bool, result interface{}, err error)
446
+ Save(ctx context.Context, key string, result interface{}, ttl time.Duration) error
447
+ }
448
+
449
+ // 使用示例
450
+ func (h *Handler) Create(c *gin.Context) {
451
+ // 从请求头获取幂等键
452
+ idempotencyKey := c.GetHeader("X-Idempotency-Key")
453
+ if idempotencyKey == "" {
454
+ response.BadRequest(c, "缺少幂等键")
455
+ return
456
+ }
457
+
458
+ // 检查是否已处理
459
+ key := fmt.Sprintf("idempotency:%s:%s", tenantID, idempotencyKey)
460
+ exists, result, err := h.idempotency.Check(c.Request.Context(), key)
461
+ if err != nil {
462
+ response.InternalError(c, err.Error())
463
+ return
464
+ }
465
+ if exists {
466
+ response.Success(c, result) // 返回之前的结果
467
+ return
468
+ }
469
+
470
+ // 处理请求
471
+ entity, err := h.svc.Create(c.Request.Context(), tenantID, input)
472
+ if err != nil {
473
+ response.Error(c, err)
474
+ return
475
+ }
476
+
477
+ // 保存结果
478
+ h.idempotency.Save(c.Request.Context(), key, entity, 24*time.Hour)
479
+ response.Created(c, entity)
480
+ }
481
+ ```
482
+
483
+ ---
484
+
485
+ ## 异步任务编排
486
+
487
+ ### 1. 任务链模式
488
+
489
+ ```go
490
+ // domain/jobs/chain.go
491
+ package jobs
492
+
493
+ type ChainJob struct {
494
+ steps []Step
495
+ }
496
+
497
+ type Step struct {
498
+ Name string
499
+ Execute func(ctx context.Context, params jobs.Params, prevResult interface{}) (interface{}, error)
500
+ }
501
+
502
+ func (j *ChainJob) Execute(ctx context.Context, params jobs.Params, progress jobs.ProgressReporter) error {
503
+ var prevResult interface{}
504
+ totalSteps := len(j.steps)
505
+
506
+ for i, step := range j.steps {
507
+ progress.Update(
508
+ int(float64(i)/float64(totalSteps)*100),
509
+ fmt.Sprintf("执行步骤: %s", step.Name),
510
+ )
511
+
512
+ result, err := step.Execute(ctx, params, prevResult)
513
+ if err != nil {
514
+ return fmt.Errorf("步骤 %s 失败: %w", step.Name, err)
515
+ }
516
+ prevResult = result
517
+ }
518
+
519
+ progress.Update(100, "任务完成")
520
+ progress.SetResult(prevResult)
521
+ return nil
522
+ }
523
+
524
+ // 使用示例:订单导出任务链
525
+ func NewOrderExportChain(repo *OrderRepository, storage storage.Service) *ChainJob {
526
+ return &ChainJob{
527
+ steps: []Step{
528
+ {
529
+ Name: "查询数据",
530
+ Execute: func(ctx context.Context, params jobs.Params, _ interface{}) (interface{}, error) {
531
+ tenantID := params.GetUint("tenant_id")
532
+ return repo.ListAll(ctx, tenantID)
533
+ },
534
+ },
535
+ {
536
+ Name: "生成 Excel",
537
+ Execute: func(ctx context.Context, params jobs.Params, prev interface{}) (interface{}, error) {
538
+ orders := prev.([]models.Order)
539
+ return generateExcel(orders)
540
+ },
541
+ },
542
+ {
543
+ Name: "上传文件",
544
+ Execute: func(ctx context.Context, params jobs.Params, prev interface{}) (interface{}, error) {
545
+ data := prev.([]byte)
546
+ return storage.Upload(ctx, "orders.xlsx", data)
547
+ },
548
+ },
549
+ },
550
+ }
551
+ }
552
+ ```
553
+
554
+ ---
555
+
556
+ ## 前端状态管理模式
557
+
558
+ ### 1. 模块级状态 Store
559
+
560
+ ```typescript
561
+ // stores/orderStore.ts
562
+ import { create } from 'zustand'
563
+ import { persist } from 'zustand/middleware'
564
+ import type { Order, OrderFilter } from '../types'
565
+ import { listOrders, getOrder, createOrder } from '../services/api'
566
+
567
+ interface OrderState {
568
+ // 数据
569
+ orders: Order[]
570
+ currentOrder: Order | null
571
+ total: number
572
+
573
+ // 加载状态
574
+ loading: boolean
575
+ error: string | null
576
+
577
+ // 过滤和分页
578
+ filter: OrderFilter
579
+ page: number
580
+ pageSize: number
581
+
582
+ // Actions
583
+ fetchOrders: () => Promise<void>
584
+ fetchOrder: (id: number) => Promise<void>
585
+ createOrder: (input: CreateOrderInput) => Promise<Order>
586
+ setFilter: (filter: Partial<OrderFilter>) => void
587
+ setPage: (page: number) => void
588
+ reset: () => void
589
+ }
590
+
591
+ export const useOrderStore = create<OrderState>()(
592
+ persist(
593
+ (set, get) => ({
594
+ orders: [],
595
+ currentOrder: null,
596
+ total: 0,
597
+ loading: false,
598
+ error: null,
599
+ filter: {},
600
+ page: 1,
601
+ pageSize: 10,
602
+
603
+ fetchOrders: async () => {
604
+ const { filter, page, pageSize } = get()
605
+ set({ loading: true, error: null })
606
+
607
+ try {
608
+ const result = await listOrders({ ...filter, page, page_size: pageSize })
609
+ set({ orders: result.items, total: result.total })
610
+ } catch (err) {
611
+ set({ error: err instanceof Error ? err.message : '加载失败' })
612
+ } finally {
613
+ set({ loading: false })
614
+ }
615
+ },
616
+
617
+ fetchOrder: async (id: number) => {
618
+ set({ loading: true, error: null })
619
+ try {
620
+ const order = await getOrder(id)
621
+ set({ currentOrder: order })
622
+ } catch (err) {
623
+ set({ error: err instanceof Error ? err.message : '加载失败' })
624
+ } finally {
625
+ set({ loading: false })
626
+ }
627
+ },
628
+
629
+ createOrder: async (input) => {
630
+ set({ loading: true, error: null })
631
+ try {
632
+ const order = await createOrder(input)
633
+ // 刷新列表
634
+ get().fetchOrders()
635
+ return order
636
+ } finally {
637
+ set({ loading: false })
638
+ }
639
+ },
640
+
641
+ setFilter: (newFilter) => {
642
+ set((state) => ({
643
+ filter: { ...state.filter, ...newFilter },
644
+ page: 1, // 重置页码
645
+ }))
646
+ get().fetchOrders()
647
+ },
648
+
649
+ setPage: (page) => {
650
+ set({ page })
651
+ get().fetchOrders()
652
+ },
653
+
654
+ reset: () => {
655
+ set({
656
+ orders: [],
657
+ currentOrder: null,
658
+ total: 0,
659
+ filter: {},
660
+ page: 1,
661
+ error: null,
662
+ })
663
+ },
664
+ }),
665
+ {
666
+ name: 'order-store',
667
+ partialize: (state) => ({ filter: state.filter, pageSize: state.pageSize }),
668
+ }
669
+ )
670
+ )
671
+ ```
672
+
673
+ ### 2. 在组件中使用
674
+
675
+ ```tsx
676
+ // pages/OrderListPage.tsx
677
+ import { useEffect } from 'react'
678
+ import { useOrderStore } from '../stores/orderStore'
679
+
680
+ export function OrderListPage() {
681
+ const {
682
+ orders,
683
+ total,
684
+ loading,
685
+ error,
686
+ page,
687
+ pageSize,
688
+ filter,
689
+ fetchOrders,
690
+ setFilter,
691
+ setPage
692
+ } = useOrderStore()
693
+
694
+ useEffect(() => {
695
+ fetchOrders()
696
+ }, [fetchOrders])
697
+
698
+ // ... 渲染逻辑
699
+ }
700
+ ```
701
+
702
+ ---
703
+
704
+ ## 快速参考
705
+
706
+ | 模式 | 使用场景 | 关键点 |
707
+ |------|---------|--------|
708
+ | 跨模块调用 | 模块间服务依赖 | 依赖注入 + 接口隔离 |
709
+ | 事件驱动 | 松耦合通知 | 事件总线 + 异步处理 |
710
+ | 状态机 | 复杂状态流转 | 转换规则 + 状态日志 |
711
+ | 多租户 | 数据隔离 | 中间件 + 双重验证 |
712
+ | 缓存 | 性能优化 | 读穿透 + 主动失效 |
713
+ | 分布式锁 | 并发控制 | Redis SETNX + TTL |
714
+ | 幂等性 | 重复请求 | 幂等键 + 结果缓存 |
715
+ | 任务链 | 复杂异步任务 | 步骤拆分 + 进度汇报 |
716
+ | 状态管理 | 前端复杂状态 | Zustand + 持久化 |