~cytrogen/blog-public

blog-public/posts/5a4b.html -rw-r--r-- 106.4 KiB
88eebf3dCytrogen Deploy 2026-02-19 08:34:27 3 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
<!DOCTYPE html><html lang="zh" data-theme="dark"><head><meta charset="utf-8"><meta name="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>React + NestJS 购物平台练习【3】数据设计与实现 · Cytrogen 的个人博客</title><meta name="description" content="本文是 React + NestJS 全栈购物平台实践的第三篇,专注于后端数据库的设计与实现。文章从零开始,详细讲解了如何为购物平台定义核心实体(如用户、商品、订单),并使用 TypeORM 装饰器实现数据模型及其复杂的关联关系。接着,教程演示了如何配置和使用 TypeORM 的数据库迁移(Migration)功能,通过自定义脚本来自动化生成、应用和回滚数据库结构变更。最后,文章还探讨了数据库性能优化的关键——索引(Indexing),并为关键查询字段添加了索引。本教程为使用 NestJS 和 TypeORM 进行数据建模与管理提供了完整的实战指南。"><link rel="icon" href="../favicon.png"><link rel="canonical" href="https://cytrogen.icu/posts/5a4b.html"><link rel="webmention" href="https://webmention.io/cytrogen.icu/webmention"><link rel="me" href="https://m.otter.homes/@Cytrogen"><link rel="me" href="https://github.com/cytrogen"><meta name="fediverse:creator" content="@Cytrogen@m.otter.homes"><link rel="preload" href="../fonts/opensans-regular-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous"><style>@font-face {
  font-family: 'Open Sans';
  src: url('../fonts/opensans-regular-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  size-adjust: 107%;
  ascent-override: 97%;
  descent-override: 25%;
  line-gap-override: 0%;
}
</style><script>(function() {
  try {
    // 优先级:用户选择 > 系统偏好 > 默认浅色
    const saved = localStorage.getItem('theme');
    const theme = saved || 
      (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.style.colorScheme = theme;
  } catch (error) {
    // 失败时使用默认主题,不阻塞渲染
    document.documentElement.setAttribute('data-theme', 'light');
  }
})();
</script><link rel="stylesheet" href="../css/ares.css"><script data-netlify-skip-bundle="true">(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const theme = document.documentElement.getAttribute('data-theme');
    const pageWrapper = document.getElementById('page-wrapper');
    if (pageWrapper && theme) {
      pageWrapper.setAttribute('data-theme', theme);
    }
  });
})();

</script><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 8.1.1"><link rel="alternate" href="atom.xml" title="Cytrogen 的个人博客" type="application/atom+xml">
</head><body><div id="page-wrapper"><a class="skip-link" href="#main-content">跳到主要内容</a><div class="wrap"><header><a class="logo-link" href="../index.html"><img src="../favicon.png" alt="logo"></a><div class="h-card visually-hidden"><img class="u-photo" src="https://cytrogen.icu/favicon.png" alt="Cytrogen"><a class="p-name u-url u-uid" href="https://cytrogen.icu">Cytrogen</a><p class="p-note">Cytrogen 的个人博客,Cytrogen's Blog</p><a class="u-url" rel="me noopener" target="_blank" href="https://m.otter.homes/@Cytrogen">Mastodon</a><a class="u-url" rel="me noopener" target="_blank" href="https://github.com/cytrogen">GitHub</a></div><nav class="site-nav"><div class="nav-main"><div class="nav-primary"><ul class="nav-list hidden-mobile"><li class="nav-item"><a class="nav-link" href="../index.html">首页</a></li></ul><div class="nav-tools"><div class="language-menu"><button class="language-toggle" type="button"><svg class="icon icon-globe" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855A7.97 7.97 0 0 0 10.855 12H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z"></path></svg><span>中文</span></button><div class="language-dropdown"></div></div></div><div class="nav-controls"><div class="more-menu hidden-mobile"><button class="more-toggle" type="button"><span>更多</span><svg class="icon icon-chevron-down" width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 8.825c-.2 0-.4-.1-.5-.2l-3.3-3.3c-.3-.3-.3-.8 0-1.1s.8-.3 1.1 0l2.7 2.7 2.7-2.7c.3-.3.8-.3 1.1 0s.3.8 0 1.1l-3.3 3.3c-.1.1-.3.2-.5.2z"></path></svg></button><div class="more-dropdown"><ul class="dropdown-list"><li class="dropdown-item"><a class="nav-link" href="../archives/index.html">归档</a></li><li class="dropdown-item"><a class="nav-link" href="../categories/index.html">分类</a></li><li class="dropdown-item"><a class="nav-link" href="../tags/index.html">标签</a></li><li class="dropdown-item"><a class="nav-link" href="../about/index.html">关于</a></li><li class="dropdown-item"><a class="nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></div><div class="theme-switcher"><button class="theme-toggle" type="button" role="switch" aria-pressed="false" aria-label="切换主题"><div class="theme-icon moon-icon"><svg class="icon icon-moon" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path></svg></div><div class="theme-icon sun-icon"><svg class="icon icon-sun" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"></path></svg></div></button></div><details class="mobile-menu-details hidden-desktop"><summary class="hamburger-menu" aria-label="nav.menu"><svg class="icon icon-bars" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"></path></svg><span class="menu-text">nav.menu</span></summary><div class="mobile-menu-dropdown"><ul class="mobile-nav-list"><li class="mobile-nav-item"><a class="mobile-nav-link" href="../index.html">首页</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../archives/index.html">归档</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../categories/index.html">分类</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../tags/index.html">标签</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../about/index.html">关于</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></details></div></div></div></nav></header><main class="container" id="main-content" tabindex="-1"><div class="post"><article class="post-block h-entry"><div class="post-meta p-author h-card visually-hidden"><img class="author-avatar u-photo" src="../favicon.png" alt="Cytrogen"><span class="p-name">Cytrogen</span><a class="u-url" href="https://cytrogen.icu">https://cytrogen.icu</a></div><a class="post-permalink u-url u-uid visually-hidden" href="https://cytrogen.icu/posts/5a4b.html">永久链接</a><div class="p-summary visually-hidden"><p>搭建完前端与后端的基础结构后,我们就可以开始着手于数据库设计了。</p></div><div class="visually-hidden"><a class="p-category" href="../categories/%E7%BC%96%E7%A8%8B%E7%AC%94%E8%AE%B0/">编程笔记</a><a class="p-category" href="../tags/%E5%85%A8%E6%A0%88%E5%AE%9E%E8%B7%B5/">全栈实践</a><a class="p-category" href="../tags/Node-js/">Node.js</a><a class="p-category" href="../tags/React-js/">React.js</a><a class="p-category" href="../tags/TypeScript/">TypeScript</a><a class="p-category" href="../tags/NestJS/">NestJS</a></div><h1 class="post-title p-name">React + NestJS 购物平台练习【3】数据设计与实现</h1><div class="post-info"><time class="post-date dt-published" datetime="2024-11-07T05:00:00.000Z">11/7/2024</time><time class="dt-updated visually-hidden" datetime="2026-02-09T17:16:54.997Z"></time></div><div class="post-content e-content"><html><head></head><body><p>搭建完前端与后端的基础结构后,我们就可以开始着手于数据库设计了。</p>
<span id="more"></span>
<h1 id="1-设计基础实体"><a class="markdownIt-Anchor" href="#1-设计基础实体"></a> 1. 设计基础实体</h1>
<p>在设计我们的购物平台的过程中,实体及其关系是核心的数据模型。我们从业务逻辑的角度来看,每个实体的意义和作用,这样可以更好地理解其在系统中的角色。</p>
<p>这里,我们以「用户」、「商品」、「订单」等为主线,讲解如何建立这些实体以及它们之间的关系,并举例说明其在业务场景中的应用。</p>
<h2 id="11-用户"><a class="markdownIt-Anchor" href="#11-用户"></a> 1.1. 用户</h2>
<p>用户实体是系统的核心,因为大部分操作都需要绑定到用户。每个用户都有其唯一的 ID、用户名、邮箱和密码,这些信息用于身份验证和授权。</p>
<blockquote>
<p>示例,一个用户 <code>John Doe</code> 创建了账号,并通过邮箱 <code>johndoe@example.com</code> 登录系统。数据库会在 <code>Users</code> 表中新增一条记录,保存 John 的邮箱、加密密码和创建时间。</p>
</blockquote>
<h4 id="111-代码实现分析"><a class="markdownIt-Anchor" href="#111-代码实现分析"></a> 1.1.1. 代码实现分析</h4>
<p>让我们深入分析 <code>Users</code> 实体的代码实现:</p>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> {</span><br><span class="line">  <span class="title class_">Entity</span>,</span><br><span class="line">  <span class="title class_">PrimaryGeneratedColumn</span>,</span><br><span class="line">  <span class="title class_">Column</span>,</span><br><span class="line">  <span class="title class_">CreateDateColumn</span>,</span><br><span class="line">  <span class="title class_">UpdateDateColumn</span>,</span><br><span class="line">  <span class="title class_">OneToMany</span>,</span><br><span class="line">  <span class="title class_">OneToOne</span></span><br><span class="line">} <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Users</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>({ <span class="attr">unique</span>: <span class="literal">true</span> })</span><br><span class="line">  <span class="attr">username</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>({ <span class="attr">unique</span>: <span class="literal">true</span> })</span><br><span class="line">  <span class="attr">email</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">password</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@CreateDateColumn</span>()</span><br><span class="line">  <span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@UpdateDateColumn</span>()</span><br><span class="line">  <span class="attr">updated_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这部分使用了 TypeORM 的装饰器来定义表结构:</p>
<ul>
<li><code>@Entity()</code> 装饰器将这个类标记为一个数据库实体</li>
<li><code>@PrimaryGeneratedColumn('uuid')</code> 表示自动生成 <code>UUID</code> 作为主键</li>
<li><code>@Column({ unique: true })</code> 为用户名和邮箱添加了唯一性约束,防止重复注册</li>
<li><code>@CreateDateColumn()</code> 会自动记录实体的创建时间</li>
<li><code>@UpdateDateColumn()</code> 会自动记录实体的更新时间</li>
</ul>
<p>用户表的字段及其业务含义如下:</p>
<ul>
<li><code>id</code>:主键,用于唯一标识每个用户,类型为 <code>UUID</code>,确保安全性和唯一性</li>
<li><code>username</code>:用户名,系统中用于显示的名称,通常在用户之间是唯一的</li>
<li><code>email</code>:用户的邮箱,通常作为主要联系手段,同时也是登录的标识之一</li>
<li><code>password</code>:用户密码,以加密方式存储,用于用户验证</li>
<li><code>created_at</code><code>updated_at</code>:创建时间和更新时间,记录用户注册和资料更新的时间</li>
</ul>
<h4 id="112-关联关系分析"><a class="markdownIt-Anchor" href="#112-关联关系分析"></a> 1.1.2. 关联关系分析</h4>
<p><code>Users</code> 实体与其他实体之间建立了多个关联关系:</p>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Orders</span> } <span class="keyword">from</span> <span class="string">'./orders.entity'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">Carts</span> } <span class="keyword">from</span> <span class="string">'./carts.entity'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">Addresses</span> } <span class="keyword">from</span> <span class="string">'./addresses.entity'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">Payments</span> } <span class="keyword">from</span> <span class="string">'./payments.entity'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Users</span> {</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">Orders</span>, <span class="function"><span class="params">order</span> =&gt;</span> order.<span class="property">user</span>)</span><br><span class="line">    <span class="attr">orders</span>: <span class="title class_">Orders</span>[];</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OneToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Carts</span>, <span class="function"><span class="params">cart</span> =&gt;</span> cart.<span class="property">user</span>)</span><br><span class="line">    <span class="attr">cart</span>: <span class="title class_">Carts</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">Addresses</span>, <span class="function"><span class="params">address</span> =&gt;</span> address.<span class="property">user</span>)</span><br><span class="line">    <span class="attr">addresses</span>: <span class="title class_">Addresses</span>[];</span><br><span class="line"></span><br><span class="line">    <span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">Payments</span>, <span class="function"><span class="params">payment</span> =&gt;</span> payment.<span class="property">user</span>)</span><br><span class="line">    <span class="attr">payments</span>: <span class="title class_">Payments</span>[];</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这些关联展示了用户实体在系统中的核心地位:</p>
<ol>
<li>用户 - 订单关系(<code>@OneToMany</code><ul>
<li>一个用户可以有多个订单</li>
<li>这种一对多的关系允许系统追踪用户的所有购买历史</li>
</ul>
</li>
<li>用户 - 购物车关系(<code>@OneToOne</code><ul>
<li>每个用户只能有一个购物车</li>
<li>一对一的关系确保购物车数据的独立性和安全性</li>
</ul>
</li>
<li>用户 - 地址关系(<code>@OneToMany</code><ul>
<li>用户可以保存多个收货地址</li>
<li>方便用户在下单时快速选择收货地址</li>
</ul>
</li>
<li>用户 - 支付关系(<code>@OneToMany</code><ul>
<li>记录用户的所有支付记录</li>
<li>用于追踪交易历史和财务统计</li>
</ul>
</li>
</ol>
<h4 id="113-用户角色"><a class="markdownIt-Anchor" href="#113-用户角色"></a> 1.1.3. 用户角色</h4>
<p>在开发应用程序时,管理用户的权限和访问控制是至关重要的。这是因为不同类型的用户可能需要访问系统的不同功能。</p>
<p>例如,一个购物平台可能有以下几种角色:</p>
<ul>
<li>管理员:可以管理用户、查看所有订单、编辑商品等</li>
<li>普通用户:只能查看商品、下订单、查看个人资料等</li>
<li>游客:仅限浏览,不进行任何交互</li>
</ul>
<p>在这种场景下,我们需要为用户管理系统添加一个角色字段,以便区分不同的用户类型。</p>
<p>角色字段通常有助于完成以下任务:</p>
<ul>
<li>不同权限管理:每种角色都有不同的权限。管理员可能有权访问系统的所有数据,而普通用户只能查看他们自己的数据。通过角色字段,我们可以在数据库中存储每个用户的角色信息</li>
<li>角色控制的用户界面:角色字段可以帮助系统为不同角色的用户显示不同的页面或功能。例如,管理员可以访问管理控制台,而普通用户只能看到他们的订单历史</li>
<li>安全性增强:通过角色字段,系统可以在后端进行权限验证,确保不同角色的用户只能执行允许他们执行的操作,避免了恶意操作或权限滥用</li>
</ul>
<p>为了明确区分不同角色的值,通常我们会使用枚举(<code>enum</code>)来为角色提供固定的值。</p>
<blockquote>
<p><code>enum</code> 是一种特殊的类,用来表示一组固定的常量。</p>
</blockquote>
<p>通过将 <code>role</code> 字段设置为枚举类型,我们可以确保角色值仅限于我们定义的固定值,这些值会自动被 TypeORM 映射到数据库字段中。</p>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 添加角色的枚举类型</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">enum</span> <span class="title class_">UserRole</span> {</span><br><span class="line">  <span class="variable constant_">ADMIN</span> = <span class="string">'admin'</span>,</span><br><span class="line">  <span class="variable constant_">USER</span> = <span class="string">'user'</span>,</span><br><span class="line">  <span class="variable constant_">GUEST</span> = <span class="string">'guest'</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Users</span> {</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 在 Users 实体中添加 role 字段</span></span><br><span class="line">  <span class="meta">@Column</span>({</span><br><span class="line">    <span class="attr">type</span>: <span class="string">'enum'</span>,</span><br><span class="line">    <span class="attr">enum</span>: <span class="title class_">UserRole</span>,</span><br><span class="line">    <span class="attr">default</span>: <span class="title class_">UserRole</span>.<span class="property">USER</span>,  <span class="comment">// 默认值为普通用户</span></span><br><span class="line">  })</span><br><span class="line">  <span class="attr">role</span>: <span class="title class_">UserRole</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="12-商品"><a class="markdownIt-Anchor" href="#12-商品"></a> 1.2. 商品</h2>
<p>商品实体是平台中与交易直接相关的核心实体,它承载了商品的基本信息、库存管理、定价等重要功能。一个设计良好的商品实体不仅要满足基本的展示需求,还要支持库存管理、订单处理等复杂业务场景。</p>
<p>商品表的作用是提供系统中可供购买的商品清单,记录商品价格和库存。在库存管理方面,当库存减少到零时,商品需要标记为「缺货」或「下架」。</p>
<blockquote>
<p>示例,商家上架了一款新商品「智能手表」,库存数量为 100,售价为 200 元,商品类别为「电子产品」。这款商品的信息会存储在 <code>Products</code> 表中,供用户选择和购买。</p>
</blockquote>
<h4 id="121-代码实现分析"><a class="markdownIt-Anchor" href="#121-代码实现分析"></a> 1.2.1. 代码实现分析</h4>
<p>让我们详细分析 <code>Products</code> 实体的代码实现:</p>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> {</span><br><span class="line">  <span class="title class_">Entity</span>,</span><br><span class="line">  <span class="title class_">PrimaryGeneratedColumn</span>,</span><br><span class="line">  <span class="title class_">Column</span>,</span><br><span class="line">  <span class="title class_">CreateDateColumn</span>,</span><br><span class="line">  <span class="title class_">UpdateDateColumn</span>,</span><br><span class="line">  <span class="title class_">ManyToOne</span>,</span><br><span class="line">  <span class="title class_">OneToMany</span></span><br><span class="line">} <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Products</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">name</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">'text'</span>)</span><br><span class="line">  <span class="attr">description</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">'decimal'</span>)</span><br><span class="line">  <span class="attr">price</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">stock</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@CreateDateColumn</span>()</span><br><span class="line">  <span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@UpdateDateColumn</span>()</span><br><span class="line">  <span class="attr">updated_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>代码实现的特点:</p>
<ul>
<li>使用 <code>text</code> 类型存储描述,支持长文本内容</li>
<li>使用 <code>decimal</code> 类型存储价格,避免浮点数计算误差</li>
<li><code>stock</code> 字段直接使用普通的数值类型,便于进行库存相关的计算</li>
</ul>
<p>商品表的字段及其业务含义如下:</p>
<ul>
<li><code>id</code>:主键,用于唯一标识每个商品,类型为 <code>UUID</code></li>
<li><code>name</code>:商品名称,帮助用户识别商品</li>
<li><code>description</code>:商品描述,用于展示商品的详细信息</li>
<li><code>price</code>:商品价格,定义用户在购买该商品时的成本</li>
<li><code>stock</code>:商品库存数量,代表当前商品的剩余数量</li>
<li><code>created_at</code><code>updated_at</code>:创建时间和更新时间,记录商品上架和更新的时间</li>
</ul>
<h4 id="122-关联关系分析"><a class="markdownIt-Anchor" href="#122-关联关系分析"></a> 1.2.2. 关联关系分析</h4>
<p><code>Products</code> 实体与其他实体建立了多个关联关系:</p>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Categories</span> } <span class="keyword">from</span> <span class="string">'./categories.entity'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">OrderItems</span> } <span class="keyword">from</span> <span class="string">'./order-items.entity'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">CartItems</span> } <span class="keyword">from</span> <span class="string">'./cart-items.entity'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">InventoryLogs</span> } <span class="keyword">from</span> <span class="string">'./inventory-logs.entity'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Products</span> {</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">  <span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Categories</span>, <span class="function"><span class="params">category</span> =&gt;</span> category.<span class="property">products</span>)</span><br><span class="line">  <span class="attr">category</span>: <span class="title class_">Categories</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">OrderItems</span>, <span class="function"><span class="params">orderItem</span> =&gt;</span> orderItem.<span class="property">product</span>)</span><br><span class="line">  <span class="attr">orderItems</span>: <span class="title class_">OrderItems</span>[];</span><br><span class="line"></span><br><span class="line">  <span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">CartItems</span>, <span class="function"><span class="params">cartItem</span> =&gt;</span> cartItem.<span class="property">product</span>)</span><br><span class="line">  <span class="attr">cartItems</span>: <span class="title class_">CartItems</span>[];</span><br><span class="line"></span><br><span class="line">  <span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">InventoryLogs</span>, <span class="function"><span class="params">inventoryLog</span> =&gt;</span> inventoryLog.<span class="property">product</span>)</span><br><span class="line">  <span class="attr">inventoryLogs</span>: <span class="title class_">InventoryLogs</span>[];</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ol>
<li>商品 - 类别关系(<code>@ManyToOne</code>)
<ul>
<li>一个商品属于一个类别</li>
<li>支持商品分类管理和分类检索</li>
<li>多对一的关系使得类别可以包含多个商品</li>
</ul>
</li>
<li>商品 - 订单项关系(<code>@OneToMany</code>)
<ul>
<li>一个商品可以出现在多个订单中</li>
<li>通过 <code>OrderItems</code> 中间表存储具体的购买数量和价格</li>
<li>支持订单历史查询和销售统计</li>
</ul>
</li>
<li>商品 - 购物车项关系(<code>@OneToMany</code>)
<ul>
<li>一个商品可以被加入多个用户的购物车</li>
<li>通过 <code>CartItems</code> 中间表记录购物车中的商品数量</li>
</ul>
</li>
<li>商品 - 库存日志关系(<code>@OneToMany</code>)
<ul>
<li>记录商品库存变动历史</li>
<li>支持库存追踪和审计</li>
</ul>
</li>
</ol>
<h2 id="13-类别"><a class="markdownIt-Anchor" href="#13-类别"></a> 1.3. 类别</h2>
<p>类别实体是商品分类的基础,它帮助我们组织和管理商品,提供更好的浏览和检索体验。一个良好的类别系统能够帮助用户更快地找到所需商品,同时也便于商家进行商品管理。</p>
<blockquote>
<p>当平台增加新类别「智能家居」时,它会被添加到 <code>Categories</code> 表中。用户在浏览智能家居产品时,只需点击此类别,即可看到所有相关商品。</p>
</blockquote>
<h4 id="131-代码实现分析"><a class="markdownIt-Anchor" href="#131-代码实现分析"></a> 1.3.1. 代码实现分析</h4>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Entity</span>, <span class="title class_">PrimaryGeneratedColumn</span>, <span class="title class_">Column</span>, <span class="title class_">CreateDateColumn</span>, <span class="title class_">UpdateDateColumn</span>, <span class="title class_">OneToMany</span> } <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Categories</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">name</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">'text'</span>)</span><br><span class="line">  <span class="attr">description</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@CreateDateColumn</span>()</span><br><span class="line">  <span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@UpdateDateColumn</span>()</span><br><span class="line">  <span class="attr">updated_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>类别表的字段及其业务含义如下:</p>
<ul>
<li><code>id</code>:主键,唯一标识类别,类型为 <code>UUID</code></li>
<li><code>name</code>:类别名称,例如「电子产品」或「家具」</li>
<li><code>description</code>:类别描述,进一步解释类别内容</li>
<li><code>created_at</code><code>updated_at</code>:创建和更新时间,记录类别的管理信息</li>
</ul>
<h4 id="132-关联关系分析"><a class="markdownIt-Anchor" href="#132-关联关系分析"></a> 1.3.2. 关联关系分析</h4>
<p><code>Categories</code> 实体主要与 <code>Products</code> 实体建立了一对多的关联关系:</p>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Products</span> } <span class="keyword">from</span> <span class="string">'./products.entity'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Categories</span> {</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">  <span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">Products</span>, <span class="function"><span class="params">product</span> =&gt;</span> product.<span class="property">category</span>)</span><br><span class="line">  <span class="attr">products</span>: <span class="title class_">Products</span>[];</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ol>
<li>类别 - 商品关系(<code>@OneToMany</code>)
<ul>
<li>一个类别可以包含多个商品</li>
<li>体现了分类组织的层次结构</li>
<li>支持按类别查询和统计商品</li>
</ul>
</li>
</ol>
<h2 id="14-订单"><a class="markdownIt-Anchor" href="#14-订单"></a> 1.4. 订单</h2>
<p>订单是电商系统中最核心的业务实体之一,它记录了交易的全过程,连接了用户、商品、支付等多个业务环节。一个完善的订单系统需要处理订单状态流转、支付流程、商品库存等复杂的业务场景。</p>
<blockquote>
<p>当用户下单购买一部智能手表,总价为 200 元,订单状态为「待支付」。在用户支付成功后,订单状态更新为「已支付」。</p>
</blockquote>
<h4 id="141-代码实现分析"><a class="markdownIt-Anchor" href="#141-代码实现分析"></a> 1.4.1. 代码实现分析</h4>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> {</span><br><span class="line">  <span class="title class_">Entity</span>,</span><br><span class="line">  <span class="title class_">PrimaryGeneratedColumn</span>,</span><br><span class="line">  <span class="title class_">Column</span>,</span><br><span class="line">  <span class="title class_">CreateDateColumn</span>,</span><br><span class="line">  <span class="title class_">UpdateDateColumn</span>,</span><br><span class="line">  <span class="title class_">ManyToOne</span>,</span><br><span class="line">  <span class="title class_">OneToMany</span></span><br><span class="line">} <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Orders</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">'decimal'</span>)</span><br><span class="line">  <span class="attr">total_price</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">status</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@CreateDateColumn</span>()</span><br><span class="line">  <span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@UpdateDateColumn</span>()</span><br><span class="line">  <span class="attr">updated_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li><code>id</code>:订单的唯一标识符,使用 <code>UUID</code> 类型</li>
<li><code>total_price</code>:订单总价,使用 <code>decimal</code> 类型确保精确计算</li>
<li><code>status</code>:订单状态,用于追踪订单的处理流程</li>
<li><code>created_at</code><code>updated_at</code>:记录订单的创建和更新时间</li>
</ul>
<h4 id="142-关联关系分析"><a class="markdownIt-Anchor" href="#142-关联关系分析"></a> 1.4.2. 关联关系分析</h4>
<p><code>Orders</code> 实体与其他实体建立了多个关联关系:</p>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Users</span> } <span class="keyword">from</span> <span class="string">'./users.entity'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">OrderItems</span> } <span class="keyword">from</span> <span class="string">'./order-items.entity'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Orders</span> {</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">  <span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Users</span>, <span class="function"><span class="params">user</span> =&gt;</span> user.<span class="property">orders</span>)</span><br><span class="line">  <span class="attr">user</span>: <span class="title class_">Users</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">OrderItems</span>, <span class="function"><span class="params">orderItem</span> =&gt;</span> orderItem.<span class="property">order</span>)</span><br><span class="line">  <span class="attr">orderItems</span>: <span class="title class_">OrderItems</span>[];</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这些关联关系支持了完整的订单业务流程:</p>
<ol>
<li>订单 - 用户关系(<code>@ManyToOne</code>)
<ul>
<li>每个订单必须属于一个用户</li>
<li>支持用户订单历史查询</li>
<li>便于进行用户消费分析</li>
</ul>
</li>
<li>订单 - 订单项关系(<code>@OneToMany</code>)
<ul>
<li>一个订单可以包含多个商品</li>
<li>通过 <code>OrderItems</code> 存储具体的商品信息和数量</li>
<li>支持订单明细查询和统计</li>
</ul>
</li>
</ol>
<h2 id="15-订单项"><a class="markdownIt-Anchor" href="#15-订单项"></a> 1.5. 订单项</h2>
<p>订单项实体记录了订单中每个商品的具体购买信息,包括数量、单价和总价等。它不仅连接了订单和商品,还保存了购买时的价格快照,这对于订单历史记录和财务核算都很重要。</p>
<h4 id="151-代码实现分析"><a class="markdownIt-Anchor" href="#151-代码实现分析"></a> 1.5.1. 代码实现分析</h4>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Entity</span>, <span class="title class_">PrimaryGeneratedColumn</span>, <span class="title class_">Column</span>, <span class="title class_">ManyToOne</span> } <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">OrderItems</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">quantity</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">'decimal'</span>)</span><br><span class="line">  <span class="attr">price</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">'decimal'</span>)</span><br><span class="line">  <span class="attr">total_price</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li><code>id</code>:订单项的唯一标识符,使用 <code>UUID</code> 类型</li>
<li><code>quantity</code>:购买数量</li>
<li><code>price</code>:商品单价的快照,使用 <code>decimal</code> 类型</li>
<li><code>total_price</code>:该项商品的总价,使用 <code>decimal</code> 类型</li>
</ul>
<h4 id="152-关联关系分析"><a class="markdownIt-Anchor" href="#152-关联关系分析"></a> 1.5.2. 关联关系分析</h4>
<p><code>OrderItems</code> 实体与其他实体建立了多个多对一的关联关系:</p>
<ol>
<li>
<p>订单项 - 订单关系(<code>@ManyToOne</code>)</p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Orders</span>, <span class="function"><span class="params">order</span> =&gt;</span> order.<span class="property">orderItems</span>)</span><br><span class="line"><span class="attr">order</span>: <span class="title class_">Orders</span>;</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li>多个订单项属于同一个订单</li>
<li>通过这个关系可以获取订单的完整商品清单</li>
<li>支持订单总价计算和商品统计</li>
</ul>
</li>
<li>
<p>订单项 - 商品关系(<code>@ManyToOne</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Products</span>, <span class="function"><span class="params">product</span> =&gt;</span> product.<span class="property">orderItems</span>)</span><br><span class="line"><span class="attr">product</span>: <span class="title class_">Products</span>;</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li>记录该订单项对应的具体商品</li>
<li>支持商品销售统计和分析</li>
<li>可以追踪商品的购买历史</li>
</ul>
</li>
</ol>
<h2 id="16-购物车"><a class="markdownIt-Anchor" href="#16-购物车"></a> 1.6. 购物车</h2>
<p>每个用户都有一个购物车,用于暂存待购买的商品信息。</p>
<p>购物车实体记录了用户在线上选择和加入的商品,是下单前的临时存储。它与订单实体有着类似的结构,但服务于不同的业务场景。</p>
<h4 id="161-代码实现分析"><a class="markdownIt-Anchor" href="#161-代码实现分析"></a> 1.6.1. 代码实现分析</h4>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Entity</span>, <span class="title class_">PrimaryGeneratedColumn</span>, <span class="title class_">CreateDateColumn</span>, <span class="title class_">UpdateDateColumn</span>, <span class="title class_">OneToOne</span>, <span class="title class_">OneToMany</span> } <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">Users</span> } <span class="keyword">from</span> <span class="string">'./users.entity'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">CartItems</span> } <span class="keyword">from</span> <span class="string">'./cart-items.entity'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Carts</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@CreateDateColumn</span>()</span><br><span class="line">  <span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@UpdateDateColumn</span>()</span><br><span class="line">  <span class="attr">updated_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>代码实现的特点:</p>
<ul>
<li>使用一对一关系与用户实体关联,确保每个用户只有一个购物车</li>
<li>包含基本的时间戳字段,跟踪购物车的创建和更新</li>
</ul>
<p>购物车表的字段及其业务含义如下:</p>
<ul>
<li><code>id</code>:购物车的唯一标识符,使用 <code>UUID</code> 类型</li>
<li><code>created_at</code><code>updated_at</code>:记录购物车的创建和更新时间</li>
</ul>
<h4 id="162-关联关系分析"><a class="markdownIt-Anchor" href="#162-关联关系分析"></a> 1.6.2. 关联关系分析</h4>
<p><code>Carts</code> 实体与其他实体建立了以下关联关系:</p>
<ol>
<li>
<p>购物车 - 用户关系(<code>@OneToOne</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@OneToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Users</span>, <span class="function"><span class="params">user</span> =&gt;</span> user.<span class="property">cart</span>)</span><br><span class="line"><span class="attr">user</span>: <span class="title class_">Users</span>;</span><br></pre></td></tr></tbody></table></figure>
</li>
<li>
<p>购物车 - 购物车项关系(<code>@OneToMany</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@OneToMany</span>(<span class="function">() =&gt;</span> <span class="title class_">CartItems</span>, <span class="function"><span class="params">cartItem</span> =&gt;</span> cartItem.<span class="property">cart</span>)</span><br><span class="line"><span class="attr">cartItems</span>: <span class="title class_">CartItems</span>[];</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li>一个购物车可以包含多个购物车项</li>
<li>通过这个关系可以获取购物车中的商品列表</li>
</ul>
</li>
</ol>
<h2 id="17-购物车项"><a class="markdownIt-Anchor" href="#17-购物车项"></a> 1.7. 购物车项</h2>
<p>购物车项实体记录了用户在购物车中选择的商品及其数量,它与订单项实体有着类似的结构。</p>
<blockquote>
<p>用户在购物车中添加了两部智能手表,购物车项记录该商品 ID、数量为 2,以及关联的购物车 ID。</p>
</blockquote>
<h4 id="171-代码实现分析"><a class="markdownIt-Anchor" href="#171-代码实现分析"></a> 1.7.1. 代码实现分析</h4>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Entity</span>, <span class="title class_">PrimaryGeneratedColumn</span>, <span class="title class_">Column</span>, <span class="title class_">ManyToOne</span> } <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">CartItems</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">quantity</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p><code>CartItems</code> 实体的特点:</p>
<ul>
<li>使用多对一关系连接购物车和商品实体</li>
<li>记录了商品的购买数量,用于计算购物车总价</li>
</ul>
<h4 id="172-关联关系分析"><a class="markdownIt-Anchor" href="#172-关联关系分析"></a> 1.7.2. 关联关系分析</h4>
<ol>
<li>
<p>购物车项 - 购物车关系(<code>@ManyToOne</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Carts</span>, <span class="function"><span class="params">cart</span> =&gt;</span> cart.<span class="property">cartItems</span>)</span><br><span class="line"><span class="attr">cart</span>: <span class="title class_">Carts</span>;</span><br></pre></td></tr></tbody></table></figure>
</li>
<li>
<p>购物车项 - 商品关系(<code>@ManyToOne</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Products</span>, <span class="function"><span class="params">product</span> =&gt;</span> product.<span class="property">cartItems</span>)</span><br><span class="line"><span class="attr">product</span>: <span class="title class_">Products</span>;</span><br></pre></td></tr></tbody></table></figure>
</li>
</ol>
<h2 id="18-支付"><a class="markdownIt-Anchor" href="#18-支付"></a> 1.8. 支付</h2>
<p>支付实体记录了用户在完成下单后的支付信息,包括支付金额、支付状态等。它与订单实体有着直接的关联关系。</p>
<h4 id="181-代码实现分析"><a class="markdownIt-Anchor" href="#181-代码实现分析"></a> 1.8.1. 代码实现分析</h4>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Entity</span>, <span class="title class_">PrimaryGeneratedColumn</span>, <span class="title class_">Column</span>, <span class="title class_">CreateDateColumn</span>, <span class="title class_">ManyToOne</span> } <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Payments</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>(<span class="string">'decimal'</span>)</span><br><span class="line">  <span class="attr">amount</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">status</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@CreateDateColumn</span>()</span><br><span class="line">  <span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p><code>Payments</code> 实体的特点:</p>
<ul>
<li>使用多对一关系连接订单和用户实体</li>
<li>记录了支付的金额和状态</li>
<li>包含支付的创建时间戳</li>
</ul>
<h4 id="182-关联关系分析"><a class="markdownIt-Anchor" href="#182-关联关系分析"></a> 1.8.2. 关联关系分析</h4>
<ol>
<li>
<p>支付 - 订单关系(<code>@ManyToOne</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Orders</span>, <span class="function"><span class="params">order</span> =&gt;</span> order.<span class="property">id</span>)</span><br><span class="line"><span class="attr">order</span>: <span class="title class_">Orders</span>;</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li>一笔订单可以有多个支付记录</li>
<li>通过这个关系可以获取支付所属的订单信息</li>
</ul>
</li>
<li>
<p>支付 - 用户关系(<code>@ManyToOne</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Users</span>, <span class="function"><span class="params">user</span> =&gt;</span> user.<span class="property">payments</span>)</span><br><span class="line"><span class="attr">user</span>: <span class="title class_">Users</span>;</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li>一个用户可以有多笔支付记录</li>
<li>通过这个关系可以获取支付所属的用户信息</li>
</ul>
</li>
</ol>
<h2 id="19-地址"><a class="markdownIt-Anchor" href="#19-地址"></a> 1.9. 地址</h2>
<p>地址实体记录了用户的收货地址信息,包括街道、城市、州 / 省、邮编和国家等详细信息。这些信息在用户下单时需要用到,也可以用于生成发货标签等。</p>
<h4 id="191-代码实现分析"><a class="markdownIt-Anchor" href="#191-代码实现分析"></a> 1.9.1. 代码实现分析</h4>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Entity</span>, <span class="title class_">PrimaryGeneratedColumn</span>, <span class="title class_">Column</span>, <span class="title class_">CreateDateColumn</span>, <span class="title class_">UpdateDateColumn</span>, <span class="title class_">ManyToOne</span> } <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">Addresses</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">street</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">city</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">state</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">postal_code</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">country</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@CreateDateColumn</span>()</span><br><span class="line">  <span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@UpdateDateColumn</span>()</span><br><span class="line">  <span class="attr">updated_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h4 id="192-关联关系分析"><a class="markdownIt-Anchor" href="#192-关联关系分析"></a> 1.9.2. 关联关系分析</h4>
<ol>
<li>
<p>地址 - 用户关系(<code>@ManyToOne</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Users</span>, <span class="function"><span class="params">user</span> =&gt;</span> user.<span class="property">addresses</span>)</span><br><span class="line"><span class="attr">user</span>: <span class="title class_">Users</span>; </span><br></pre></td></tr></tbody></table></figure>
<ul>
<li>一个用户可以有多个地址信息</li>
<li>通过这个关系可以获取地址所属的用户信息</li>
</ul>
</li>
</ol>
<h2 id="110-库存日志"><a class="markdownIt-Anchor" href="#110-库存日志"></a> 1.10. 库存日志</h2>
<p>库存日志实体记录了商品库存的变动历史,包括每次库存增减的数量和时间。这些记录对于库存管理、库存盘点和异常排查都非常重要。</p>
<h4 id="1101-代码实现分析"><a class="markdownIt-Anchor" href="#1101-代码实现分析"></a> 1.10.1. 代码实现分析</h4>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">Entity</span>, <span class="title class_">PrimaryGeneratedColumn</span>, <span class="title class_">Column</span>, <span class="title class_">CreateDateColumn</span>, <span class="title class_">ManyToOne</span> } <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span>()</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> <span class="title class_">InventoryLogs</span> {</span><br><span class="line">  <span class="meta">@PrimaryGeneratedColumn</span>(<span class="string">'uuid'</span>)</span><br><span class="line">  <span class="attr">id</span>: <span class="built_in">string</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Column</span>()</span><br><span class="line">  <span class="attr">change</span>: <span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@CreateDateColumn</span>()</span><br><span class="line">  <span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p><code>InventoryLogs</code> 实体的特点:</p>
<ul>
<li>使用多对一关系与商品实体关联</li>
<li>记录了库存变动数量(<code>change</code>),正数表示入库,负数表示出库</li>
<li>包含时间戳,记录每次库存变动的具体时间点</li>
</ul>
<h4 id="1102-关联关系分析"><a class="markdownIt-Anchor" href="#1102-关联关系分析"></a> 1.10.2. 关联关系分析</h4>
<ol>
<li>
<p>库存日志 - 商品关系(<code>@ManyToOne</code></p>
 <figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ManyToOne</span>(<span class="function">() =&gt;</span> <span class="title class_">Products</span>, <span class="function"><span class="params">product</span> =&gt;</span> product.<span class="property">inventoryLogs</span>)</span><br><span class="line"><span class="attr">product</span>: <span class="title class_">Products</span>;</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li>一个商品可以有多条库存变动记录</li>
<li>通过这个关系可以追踪特定商品的库存变动历史</li>
</ul>
</li>
</ol>
<h1 id="2-创建数据库迁移文件"><a class="markdownIt-Anchor" href="#2-创建数据库迁移文件"></a> 2. 创建数据库迁移文件</h1>
<p>在开发应用程序时,数据库模式的更新是一项常见而重要的工作,尤其是在应用不断演进的过程中。</p>
<p>假设我们在开发一个应用,并需要更新数据库的表结构,比如添加新字段、修改字段类型等。如何在不丢失现有数据的情况下进行这些修改呢?</p>
<p>TypeORM 可以帮助我们轻松处理数据库操作。它内置了强大的迁移功能,使我们能够定义数据库结构变更并自动应用到数据库中。</p>
<h2 id="21-添加自定义命令"><a class="markdownIt-Anchor" href="#21-添加自定义命令"></a> 2.1. 添加自定义命令</h2>
<p>首先,我们在 <code>package.json</code> 中添加一些脚本来管理 TypeORM 的迁移操作。这些脚本将自动构建项目并运行相应的迁移命令,方便我们快速执行迁移操作:</p>
<figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line">  <span class="attr">"scripts"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="attr">"typeorm"</span><span class="punctuation">:</span> <span class="string">"typeorm-ts-node-commonjs -d src/config/data-source.ts"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"migration:initialize"</span><span class="punctuation">:</span> <span class="string">"yarn build &amp;&amp; yarn typeorm migration:generate src/migrations/InitialMigration"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"migration:generate"</span><span class="punctuation">:</span> <span class="string">"yarn build &amp;&amp; yarn typeorm migration:generate"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"migration:run"</span><span class="punctuation">:</span> <span class="string">"yarn build &amp;&amp; yarn typeorm migration:run"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"migration:revert"</span><span class="punctuation">:</span> <span class="string">"yarn build &amp;&amp; yarn typeorm migration:revert"</span></span><br><span class="line">  <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure>
<p>让我们一行一行地分析这些脚本的作用:</p>
<ul>
<li><code>typeorm</code>:指定 TypeORM 的配置文件路径(<code>src/config/data-source.ts</code>),它是所有 TypeORM 操作的基础</li>
<li><code>migration:initialize</code>:生成 <code>InitialMigration</code> 迁移文件</li>
<li><code>migration:generate</code>:生成新的迁移文件
<ul>
<li>用法:<code>yarn migration:generate src/migrations/xxx</code></li>
</ul>
</li>
<li><code>migration:run</code>:执行所有还未运行的迁移,应用到数据库中</li>
<li><code>migration:revert</code>:撤销上一次迁移,通常用于回滚数据库结构到上一个状态</li>
</ul>
<h2 id="22-配置-typeorm-数据源"><a class="markdownIt-Anchor" href="#22-配置-typeorm-数据源"></a> 2.2. 配置 TypeORM 数据源</h2>
<p>接下来,我们需要设置 TypeORM 的数据库配置。</p>
<p>在项目的 <code>src/config/data-source.ts</code> 文件中,我们使用 <code>DataSourceOptions</code> 来定义数据库连接的详细信息。</p>
<p>这里我们还使用了 <code>@nestjs/config</code> 模块来动态读取环境变量,这样在不同环境中可以使用不同的数据库配置。</p>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { <span class="title class_">ConfigService</span> } <span class="keyword">from</span> <span class="string">'@nestjs/config'</span>;</span><br><span class="line"><span class="keyword">import</span> { <span class="title class_">DataSource</span>, <span class="title class_">DataSourceOptions</span> } <span class="keyword">from</span> <span class="string">'typeorm'</span>;</span><br><span class="line"><span class="keyword">import</span> { config } <span class="keyword">from</span> <span class="string">'dotenv'</span>;</span><br><span class="line"></span><br><span class="line"><span class="title function_">config</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> configService = <span class="keyword">new</span> <span class="title class_">ConfigService</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="attr">dataSourceOptions</span>: <span class="title class_">DataSourceOptions</span> = {</span><br><span class="line">  <span class="attr">type</span>: configService.<span class="property">get</span>&lt;<span class="built_in">any</span>&gt;(<span class="string">'DB_TYPE'</span>, <span class="string">'mysql'</span>),</span><br><span class="line">  <span class="attr">host</span>: configService.<span class="property">get</span>&lt;<span class="built_in">string</span>&gt;(<span class="string">'DB_HOST'</span>),</span><br><span class="line">  <span class="attr">port</span>: configService.<span class="property">get</span>&lt;<span class="built_in">number</span>&gt;(<span class="string">'DB_PORT'</span>),</span><br><span class="line">  <span class="attr">username</span>: configService.<span class="property">get</span>&lt;<span class="built_in">string</span>&gt;(<span class="string">'DB_USER'</span>),</span><br><span class="line">  <span class="attr">password</span>: configService.<span class="property">get</span>&lt;<span class="built_in">string</span>&gt;(<span class="string">'DB_PASSWORD'</span>),</span><br><span class="line">  <span class="attr">database</span>: configService.<span class="property">get</span>&lt;<span class="built_in">string</span>&gt;(<span class="string">'DB_NAME'</span>),</span><br><span class="line">  <span class="attr">synchronize</span>: configService.<span class="property">get</span>&lt;<span class="built_in">string</span>&gt;(<span class="string">'APP_ENV'</span>) === <span class="string">'development'</span>,</span><br><span class="line">  <span class="attr">entities</span>: [<span class="string">'dist/entities/*.entity{.ts,.js}'</span>],</span><br><span class="line">  <span class="attr">migrations</span>: [<span class="string">'dist/migrations/*{.ts,.js}'</span>],</span><br><span class="line">  <span class="attr">subscribers</span>: []</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">new</span> <span class="title class_">DataSource</span>(dataSourceOptions);</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li><code>type</code>:指定数据库类型,这里我们通过环境变量 <code>DB_TYPE</code> 来设置,默认为 MySQL</li>
<li><code>host</code><code>port</code><code>username</code><code>password</code><code>database</code>:这些参数是数据库连接的必要信息,都可以通过环境变量配置</li>
<li><code>synchronize</code>:如果 <code>APP_ENV</code><code>development</code>,则会启用同步模式,让 TypeORM 自动更新数据库表结构
<ul>
<li>注意:生产环境下建议禁用此项</li>
</ul>
</li>
<li><code>entities</code><code>migrations</code>:指定实体和迁移文件的路径。TypeORM 会使用这些路径找到相关文件</li>
</ul>
<h2 id="23-生成数据库迁移文件"><a class="markdownIt-Anchor" href="#23-生成数据库迁移文件"></a> 2.3. 生成数据库迁移文件</h2>
<p>当配置完成后,我们可以通过以下命令来生成用于初始化数据库的迁移文件:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn migration:initialize</span><br></pre></td></tr></tbody></table></figure>
<p>这条命令会自动构建项目,并使用 TypeORM 生成一个迁移文件(位于 <code>src/migrations</code>,且默认命名为 <code>数字数字数字-InitialMigration.ts</code>)。</p>
<p>这个文件会包含创建、修改或删除表结构的 SQL 语句。例如,如果你添加了一个新的字段,生成的迁移文件就会包含一个 <code>ALTER TABLE</code> 语句。</p>
<h2 id="24-执行迁移"><a class="markdownIt-Anchor" href="#24-执行迁移"></a> 2.4. 执行迁移</h2>
<p>生成迁移文件后,使用以下命令将其应用到数据库:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn migration:run</span><br></pre></td></tr></tbody></table></figure>
<p>此命令会检查并执行所有尚未在数据库中应用的迁移。这样可以确保你的数据库结构与最新的迁移文件一致。</p>
<h2 id="25-回滚迁移"><a class="markdownIt-Anchor" href="#25-回滚迁移"></a> 2.5. 回滚迁移</h2>
<p>在开发过程中,偶尔可能需要回滚上次迁移,例如在测试或调试时。使用以下命令可以撤销上一次迁移:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn migration:revert</span><br></pre></td></tr></tbody></table></figure>
<p>TypeORM 会自动识别上次执行的迁移,并撤销相应的更改,恢复到之前的数据库结构。</p>
<h1 id="3-设置数据库索引"><a class="markdownIt-Anchor" href="#3-设置数据库索引"></a> 3. 设置数据库索引</h1>
<p>在开发应用程序时,尤其是当数据库中的数据量越来越大时,查询性能变得至关重要。</p>
<p>数据库索引是一种数据结构,它帮助数据库管理系统(DBMS)高效地检索数据。索引就像书籍中的目录,它可以快速定位到某个数据的位置,而不是扫描整个表。当我们对数据库表进行查询时,索引可以显著提高查询性能,尤其是在处理大量数据时。</p>
<p>在 MySQL 中,索引通常应用于那些需要频繁查询的字段。常见的索引类型有:</p>
<ul>
<li>主键索引:自动为表的主键字段创建</li>
<li>唯一索引:确保每个值唯一</li>
<li>普通索引:加速数据检索,但不强制唯一性</li>
<li>全文索引:用于加速文本内容的检索</li>
</ul>
<p>没有索引的情况下,数据库在执行查询时必须扫描整个表,逐行比较数据。这种方式在小型表中可能没什么问题,但在数据量较大时,会导致性能急剧下降。</p>
<blockquote>
<p>假设我们的购物平台,数据库中存储了数百万条订单记录。当用户搜索特定订单时,如果没有适当的索引,系统可能会扫描整个订单表来找到匹配的记录。随着数据量增加,查询速度变得非常慢,甚至可能导致系统响应延迟。</p>
<p>使用索引后,数据库不再需要扫描整个表,而是通过索引快速定位到目标数据,从而显著提升查询速度。</p>
</blockquote>
<p>在 NestJS 中,我们可以通过 TypeORM 为实体字段添加索引。TypeORM 提供了 <code>@Index()</code> 装饰器,允许我们在数据库表的字段上添加索引。</p>
<h2 id="31-用户"><a class="markdownIt-Anchor" href="#31-用户"></a> 3.1. 用户</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@CreateDateColumn</span>()</span><br><span class="line"><span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br></pre></td></tr></tbody></table></figure>
<p><code>Users</code> 实体中,我们使用了 <code>@Index()</code> 装饰器来为 <code>created_at</code> 字段添加索引。</p>
<p>在大多数应用程序中,<code>created_at</code> 字段通常用于按照时间排序或进行时间范围查询。</p>
<blockquote>
<p>例如,用户可能需要查看某一时间段内的所有注册用户,或者获取最近创建的用户列表。</p>
</blockquote>
<p>假设在应用程序中,用户经常执行以下查询:</p>
<ul>
<li>查询某个时间段内注册的用户。</li>
<li>按照注册时间排序显示用户列表。</li>
</ul>
<p>如果没有为 <code>created_at</code> 字段添加索引,数据库会需要扫描整个用户表来执行这些查询,这样会导致性能问题。添加索引后,数据库可以通过索引快速查找和排序数据,显著提高查询速度,尤其是在数据量较大的情况下。</p>
<h4 id="311-关系字段为何不需要索引"><a class="markdownIt-Anchor" href="#311-关系字段为何不需要索引"></a> 3.1.1. 关系字段为何不需要索引?</h4>
<p>这时候就有人问了:「难道像 <code>orders</code><code>cart</code> 这些字段,用户就不会查询了吗?如果经常用到,为什么不为它们加个索引呢?」答案是:确实不需要。</p>
<p>在 TypeORM 中,关系字段(比如 <code>orders</code><code>cart</code>)通常是外键字段,数据库会自动为这些字段创建索引。例如,在 <code>Users</code> 实体中,<code>orders</code> 字段是通过 <code>@OneToMany</code><code>Orders</code> 实体关联的。虽然我们没有显式地为 <code>orders</code> 字段添加索引,但在 <code>Orders</code> 表中,<code>user</code> 字段作为外键会自动被索引。</p>
<p>这意味着,当我们查询某个用户的所有订单时,数据库会直接利用 <code>Orders</code> 表中自动创建的 <code>user</code> 索引,来加速查询。而我们不需要额外为 <code>orders</code> 字段添加索引,TypeORM 和数据库已经为我们处理好了这部分的优化。</p>
<h2 id="32-商品"><a class="markdownIt-Anchor" href="#32-商品"></a> 3.2. 商品</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@Column</span>(<span class="string">'decimal'</span>)</span><br><span class="line"><span class="attr">price</span>: <span class="built_in">number</span>;</span><br></pre></td></tr></tbody></table></figure>
<p><code>price</code> 字段通常会用于以下几种查询:</p>
<ul>
<li>按照价格范围查询产品,例如查找价格低于某个值的所有产品</li>
<li>按照价格排序,例如将产品列表按价格从低到高或从高到低排序</li>
</ul>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@Column</span>()</span><br><span class="line"><span class="attr">stock</span>: <span class="built_in">number</span>;</span><br></pre></td></tr></tbody></table></figure>
<p><code>stock</code> 字段通常用于以下查询场景:</p>
<ul>
<li>查找库存数量为零或低于某个值的产品(例如「缺货」产品或「库存预警」产品)</li>
<li>按照库存数量排序,帮助商家快速查看库存最少的产品</li>
</ul>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@CreateDateColumn</span>()</span><br><span class="line"><span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br></pre></td></tr></tbody></table></figure>
<p>所有的 <code>created_at</code> 字段都需要添加索引。</p>
<h2 id="33-支付"><a class="markdownIt-Anchor" href="#33-支付"></a> 3.3. 支付</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@CreateDateColumn</span>()</span><br><span class="line"><span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br></pre></td></tr></tbody></table></figure>
<h2 id="34-订单"><a class="markdownIt-Anchor" href="#34-订单"></a> 3.4. 订单</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@Column</span>()</span><br><span class="line"><span class="attr">status</span>: <span class="built_in">string</span>;</span><br></pre></td></tr></tbody></table></figure>
<p><code>status</code> 字段通常用于查询订单的状态,例如:</p>
<ul>
<li>查询某一状态的订单(如「待处理」、「已发货」、「已完成」等)</li>
<li>按照订单状态统计订单数量</li>
</ul>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@CreateDateColumn</span>()</span><br><span class="line"><span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br></pre></td></tr></tbody></table></figure>
<h2 id="35-订单项"><a class="markdownIt-Anchor" href="#35-订单项"></a> 3.5. 订单项</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@Column</span>()</span><br><span class="line"><span class="attr">quantity</span>: <span class="built_in">number</span>;</span><br></pre></td></tr></tbody></table></figure>
<p><code>quantity</code> 字段通常在以下情况中用于查询:</p>
<ul>
<li>查询具有特定数量的订单项(例如查询某种商品的批量购买情况)</li>
<li>按照数量对订单项进行筛选或排序</li>
</ul>
<h2 id="36-类别"><a class="markdownIt-Anchor" href="#36-类别"></a> 3.6. 类别</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@Column</span>()</span><br><span class="line"><span class="attr">name</span>: <span class="built_in">string</span>;</span><br></pre></td></tr></tbody></table></figure>
<p><code>name</code> 字段是分类的唯一标识,用于以下情况:</p>
<ul>
<li>按类别名称搜索产品类别(例如,用户希望快速找到特定名称的类别)</li>
<li>在名称字段上进行排序或进行自动完成的匹配查询</li>
</ul>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@CreateDateColumn</span>()</span><br><span class="line"><span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br></pre></td></tr></tbody></table></figure>
<h2 id="37-购物车"><a class="markdownIt-Anchor" href="#37-购物车"></a> 3.7. 购物车</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@CreateDateColumn</span>()</span><br><span class="line"><span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br></pre></td></tr></tbody></table></figure>
<h2 id="38-地址"><a class="markdownIt-Anchor" href="#38-地址"></a> 3.8. 地址</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@Column</span>()</span><br><span class="line"><span class="attr">postal_code</span>: <span class="built_in">string</span>;</span><br></pre></td></tr></tbody></table></figure>
<p>在地址系统中,<code>postal_code</code>(邮政编码)字段可能用于以下场景:</p>
<ul>
<li>按邮政编码筛选地址。例如,在电商系统中,按邮政编码筛选用户地址,以确定是否支持配送</li>
<li>邮政编码的批量统计或区域分析,例如确定特定邮政编码区域的用户密度</li>
</ul>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@CreateDateColumn</span>()</span><br><span class="line"><span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br></pre></td></tr></tbody></table></figure>
<h2 id="39-库存日志"><a class="markdownIt-Anchor" href="#39-库存日志"></a> 3.9. 库存日志</h2>
<figure class="highlight ts"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Index</span>()</span><br><span class="line"><span class="meta">@CreateDateColumn</span>()</span><br><span class="line"><span class="attr">created_at</span>: <span class="title class_">Date</span>;</span><br></pre></td></tr></tbody></table></figure>
<p>接下来,我们可以使用以下命令生成数据库迁移文件:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn migration:generate src/migrations/CreateIndex</span><br></pre></td></tr></tbody></table></figure>
<p>然后使用以下命令应用迁移并更新数据库:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn migration:run</span><br></pre></td></tr></tbody></table></figure>
</body></html></div></article></div></main><footer><div class="paginator"><a class="prev" href="8853.html">上一篇</a><a class="next" href="8e94.html">下一篇</a></div><!-- Webmention 显示区域--><div class="webmention-section webmention-empty" data-page-url="posts/5a4b.html" data-full-url="https://cytrogen.icu/posts/5a4b.html" data-mode="static">
              <h3 class="webmention-title">Webmentions (<span class="webmention-count">0</span>)</h3>
              <div class="webmention-list"></div>
              <span>暂无 Webmentions</span>
            </div><div class="copyright"><p class="footer-links"><a href="../friends/index.html">友链</a><span class="footer-separator"> ·</span><a href="../links/index.html">邻邦</a><span class="footer-separator"> ·</span><a href="../contact/index.html">联络</a><span class="footer-separator"> ·</span><a href="../colophon/index.html">营造记</a><span class="footer-separator"> ·</span><a href="../atom.xml">RSS订阅</a></p><p>© 2025 - 2026 <a href="https://cytrogen.icu">Cytrogen</a>, powered by <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/cytrogen/hexo-theme-ares" target="_blank">hexo-theme-ares</a>.</p><p><a href="https://blogscn.fun" target="_blank" rel="noopener">BLOGS·CN</a></p></div></footer></div></div><a class="back-to-top" href="#top" aria-label="返回顶部"><svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M3.293 9.707a1 1 0 010-1.414L9.586 2a2 2 0 012.828 0l6.293 6.293a1 1 0 01-1.414 1.414L11 3.414V17a1 1 0 11-2 0V3.414L2.707 9.707a1 1 0 01-1.414 0z"></path></svg></a><script>document.addEventListener('DOMContentLoaded', function() {
  const codeBlocks = document.querySelectorAll('figure.highlight');
  
  codeBlocks.forEach(block => {
    let caption = block.querySelector('figcaption');
    if (!caption) {
      caption = document.createElement('figcaption');
      block.insertBefore(caption, block.firstChild);
    }

    const info = document.createElement('div');
    info.className = 'info';
    
    const filename = caption.querySelector('span');
    if (filename) {
      filename.className = 'filename';
      info.appendChild(filename);
    }
    
    const lang = block.className.split(' ')[1];
    if (lang) {
      const langSpan = document.createElement('span');
      langSpan.className = 'lang-name';
      langSpan.textContent = lang;
      info.appendChild(langSpan);
    }

    const sourceLink = caption.querySelector('a');
    if (sourceLink) {
      sourceLink.className = 'source-link';
      info.appendChild(sourceLink);
    }

    const actions = document.createElement('div');
    actions.className = 'actions';

    const codeHeight = block.scrollHeight;
    const threshold = 300;

    if (codeHeight > threshold) {
      block.classList.add('folded');
      
      const toggleBtn = document.createElement('button');
      toggleBtn.textContent = '展开';
      toggleBtn.addEventListener('click', () => {
        block.classList.toggle('folded');
        toggleBtn.textContent = block.classList.contains('folded') ? '展开' : '折叠';
      });
      actions.appendChild(toggleBtn);
    }

    const copyBtn = document.createElement('button');
    copyBtn.textContent = '复制';
    copyBtn.addEventListener('click', async () => {
      const codeLines = block.querySelectorAll('.code .line');
      const code = Array.from(codeLines)
        .map(line => line.textContent)
        .join('\n')
        .replace(/\n\n/g, '\n');
      
      try {
        await navigator.clipboard.writeText(code);
        copyBtn.textContent = '已复制';
        copyBtn.classList.add('copied');
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
          copyBtn.classList.remove('copied');
        }, 3000);
      } catch (err) {
        console.error('复制失败:', err);
        copyBtn.textContent = '复制失败';
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
        }, 3000);
      }
    });
    actions.appendChild(copyBtn);

    caption.innerHTML = '';
    caption.appendChild(info);
    caption.appendChild(actions);

    const markedLines = block.getAttribute('data-marked-lines');
    if (markedLines) {
      const lines = markedLines.split(',');
      lines.forEach(range => {
        if (range.includes('-')) {
          const [start, end] = range.split('-').map(Number);
          for (let i = start; i <= end; i++) {
            const line = block.querySelector(`.line-${i}`);
            if (line) line.classList.add('marked');
          }
        } else {
          const line = block.querySelector(`.line-${range}`);
          if (line) line.classList.add('marked');
        }
      });
    }
  });
});</script><script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script"></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const themeToggle = document.querySelector('.theme-toggle');
    
    if (!themeToggle) return;
    
    const getCurrentTheme = () => {
      return document.documentElement.getAttribute('data-theme') || 'light';
    };
    
    const updateUI = (theme) => {
      const isDark = theme === 'dark';
      themeToggle.setAttribute('aria-pressed', isDark.toString());
    };
    
    const setTheme = (theme) => {
      document.documentElement.setAttribute('data-theme', theme);
      document.documentElement.style.colorScheme = theme;
      
      const pageWrapper = document.getElementById('page-wrapper');
      if (pageWrapper) {
        pageWrapper.setAttribute('data-theme', theme);
      }
      
      // Find and remove the temporary anti-flicker style tag if it exists.
      // This ensures the main stylesheet takes full control after the initial load.
      const antiFlickerStyle = document.getElementById('anti-flicker-style');
      if (antiFlickerStyle) {
        antiFlickerStyle.remove();
      }
      
      localStorage.setItem('theme', theme);
      updateUI(theme);
    };
    
    const toggleTheme = () => {
      const current = getCurrentTheme();
      const newTheme = current === 'light' ? 'dark' : 'light';
      setTheme(newTheme);
    };
    
    updateUI(getCurrentTheme());
    
    themeToggle.addEventListener('click', toggleTheme);
    
    if (window.matchMedia) {
      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
      mediaQuery.addEventListener('change', function(e) {
        if (!localStorage.getItem('theme')) {
          const theme = e.matches ? 'dark' : 'light';
          setTheme(theme);
        }
      });
    }
  });
})();
</script><script src="../js/details-toggle.js" defer></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const backToTopBtn = document.querySelector('.back-to-top');
    
    if (!backToTopBtn) return;
    
    const toggleButtonVisibility = () => {
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const shouldShow = scrollTop > 200;
      
      if (shouldShow) {
        backToTopBtn.classList.add('is-visible');
      } else {
        backToTopBtn.classList.remove('is-visible');
      }
    };
    
    let ticking = false;
    const handleScroll = () => {
      if (!ticking) {
        requestAnimationFrame(() => {
          toggleButtonVisibility();
          ticking = false;
        });
        ticking = true;
      }
    };
    
    const scrollToTop = (event) => {
      event.preventDefault();
      window.scrollTo({
        top: 0,
        behavior: 'smooth'
      });
    };
    
    window.addEventListener('scroll', handleScroll);
    backToTopBtn.addEventListener('click', scrollToTop);
    
    toggleButtonVisibility();
  });
})();</script></body></html>