@laitszkin/apollo-toolkit 3.9.6 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +23 -0
- package/README.md +6 -0
- package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
- package/cjk-pdf/agents/openai.yaml +5 -0
- package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
- package/generate-spec/SKILL.md +24 -4
- package/generate-spec/agents/openai.yaml +1 -0
- package/generate-spec/references/TEMPLATE_SPEC.md +98 -0
- package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
- package/init-project-html/SKILL.md +181 -0
- package/init-project-html/agents/openai.yaml +13 -0
- package/init-project-html/references/TEMPLATE_SPEC.md +98 -0
- package/init-project-html/references/architecture-page.template.html +35 -0
- package/init-project-html/references/architecture.css +1059 -0
- package/init-project-html/sample-demo/resources/project-architecture/assets/architecture.css +1059 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/index.html +54 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-code-generator.html +165 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-issuance-service.html +198 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/postgresql.html +165 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/public-api.html +152 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/web-get-invite-ui.html +140 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/index.html +53 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/postgresql.html +161 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/public-api.html +145 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/registration-service.html +190 -0
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/web-register-ui.html +140 -0
- package/init-project-html/sample-demo/resources/project-architecture/index.html +337 -0
- package/init-project-html/scripts/architecture.js +496 -0
- package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
- package/lib/cli.js +2 -0
- package/lib/tool-runner.js +7 -0
- package/merge-changes-from-local-branches/SKILL.md +75 -128
- package/merge-changes-from-local-branches/agents/openai.yaml +1 -1
- package/merge-conflict-resolver/agents/openai.yaml +5 -0
- package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
- package/package.json +1 -1
- package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
- package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
- package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
- package/spec-to-project-html/SKILL.md +116 -0
- package/spec-to-project-html/agents/openai.yaml +12 -0
- package/spec-to-project-html/references/TEMPLATE_SPEC.md +98 -0
- package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-Hant">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="generator" content="apollo-toolkit:init-project-html (sample demo)" />
|
|
7
|
+
<meta name="architecture-root" content="resources/project-architecture" />
|
|
8
|
+
<title>Acme App — 宏觀架構(功能模組 × 子模組)</title>
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
11
|
+
<link
|
|
12
|
+
href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;600&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap"
|
|
13
|
+
rel="stylesheet"
|
|
14
|
+
/>
|
|
15
|
+
<link rel="stylesheet" href="assets/architecture.css" />
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<main class="atlas-page atlas-page--macro">
|
|
19
|
+
<header class="atlas-header">
|
|
20
|
+
<p class="atlas-kicker">Architecture atlas — macro</p>
|
|
21
|
+
<h1 class="atlas-title">Acme App — 功能模組 × 子模組(同一張圖)</h1>
|
|
22
|
+
<p class="atlas-meta">
|
|
23
|
+
更新於 <time datetime="2026-05-11T12:00:00Z">2026-05-11(UTC)</time>
|
|
24
|
+
· 此頁同時呈現「功能模組之間」與「子模組之間」的多對多交互;子模組頁本身只描述<strong>自身函式 I/O 與內部資料流</strong>。
|
|
25
|
+
</p>
|
|
26
|
+
</header>
|
|
27
|
+
|
|
28
|
+
<section class="atlas-summary">
|
|
29
|
+
<h2>為何把兩層放同一張圖</h2>
|
|
30
|
+
<p>
|
|
31
|
+
功能模組(對使用者可見的能力)的「宏觀交互」實際上是由其底下的子模組互相呼叫與資料交換達成的。為了讓讀者能在<strong>同一張圖</strong>同時看到「功能 A → 功能 B」與「達成這條路徑的具體子模組是誰呼叫誰、誰讀誰寫」,本宏觀圖以兩個功能模組為<strong>群組(cluster)</strong>,群組內畫出子模組節點與彼此的呼叫、回傳與寫讀資料邊。
|
|
32
|
+
</p>
|
|
33
|
+
<p>
|
|
34
|
+
一個子模組可能<strong>同時是生產者與消費者</strong>。本示範刻意保留:<code>postgresql</code> 的 <code>invite_codes</code> 表在「獲取邀請碼」功能裡被<strong>寫入</strong>,產生的列在「邀請碼註冊」功能裡被<strong>讀取/核銷</strong>;<code>registration-service</code> 對 <code>postgresql</code> 同時是<strong>讀者(SELECT FOR UPDATE)</strong>與<strong>寫者(INSERT user / UPDATE invite consumed)</strong>;<code>invite-issuance-service</code> 把 <code>invite-code-generator</code> 當成多次呼叫的內嵌函式並回收其結果。
|
|
35
|
+
</p>
|
|
36
|
+
</section>
|
|
37
|
+
|
|
38
|
+
<section class="atlas-legend">
|
|
39
|
+
<h2>圖例</h2>
|
|
40
|
+
<ul>
|
|
41
|
+
<li><strong>虛線大方框</strong>:一個<strong>功能模組</strong>(feature module)= 對使用者可見的能力。標題即連到該功能總覽。</li>
|
|
42
|
+
<li><strong>實心小方塊</strong>:<strong>子模組</strong>。每一塊點擊進入該子模組頁(內容只談自身函式 I/O 與資料流)。</li>
|
|
43
|
+
<li><strong>實線箭頭</strong>:函式呼叫/HTTP 請求(caller → callee)。</li>
|
|
44
|
+
<li><strong>虛線細箭頭</strong>:回傳值/HTTP 回應(callee → caller)。</li>
|
|
45
|
+
<li><strong>暖色粗虛線</strong>:跨功能<strong>資料行</strong>級別的生產者→消費者關係(與函式呼叫不同:透過 DB 行傳遞)。</li>
|
|
46
|
+
<li><strong>同一對節點可有多條邊</strong>:例如 <code>registration-service</code> 對 <code>postgresql</code> 有 SELECT、INSERT、UPDATE 三條獨立邊。</li>
|
|
47
|
+
</ul>
|
|
48
|
+
</section>
|
|
49
|
+
|
|
50
|
+
<section class="flow-section flow-section--macro">
|
|
51
|
+
<h2>宏觀圖:功能模組 × 子模組</h2>
|
|
52
|
+
<figure class="flow-chart flow-chart--macro flow-chart--svg" data-flow-id="macro-graph" aria-label="功能模組與子模組多對多交互">
|
|
53
|
+
<div class="macro-svg__wrap">
|
|
54
|
+
<svg
|
|
55
|
+
class="macro-svg"
|
|
56
|
+
viewBox="0 0 1200 1020"
|
|
57
|
+
role="img"
|
|
58
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
59
|
+
preserveAspectRatio="xMidYMid meet"
|
|
60
|
+
>
|
|
61
|
+
<defs>
|
|
62
|
+
<marker id="mk-solid" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto">
|
|
63
|
+
<path d="M0,0 L10,5 L0,10 Z" fill="currentColor"></path>
|
|
64
|
+
</marker>
|
|
65
|
+
<marker id="mk-thin" markerWidth="9" markerHeight="9" refX="8" refY="4.5" orient="auto">
|
|
66
|
+
<path d="M0,0 L9,4.5 L0,9 Z" fill="currentColor"></path>
|
|
67
|
+
</marker>
|
|
68
|
+
<marker id="mk-cross" markerWidth="11" markerHeight="11" refX="10" refY="5.5" orient="auto">
|
|
69
|
+
<path d="M0,0 L11,5.5 L0,11 Z" fill="#e07a5f"></path>
|
|
70
|
+
</marker>
|
|
71
|
+
</defs>
|
|
72
|
+
|
|
73
|
+
<!-- Actors -->
|
|
74
|
+
<g class="m-actor" data-node-id="actor-visitor">
|
|
75
|
+
<rect x="220" y="40" width="200" height="56" rx="28"></rect>
|
|
76
|
+
<text x="320" y="74" text-anchor="middle">訪客(未註冊)</text>
|
|
77
|
+
</g>
|
|
78
|
+
<g class="m-actor" data-node-id="actor-member">
|
|
79
|
+
<rect x="800" y="40" width="200" height="56" rx="28"></rect>
|
|
80
|
+
<text x="900" y="74" text-anchor="middle">已註冊/已登入</text>
|
|
81
|
+
</g>
|
|
82
|
+
|
|
83
|
+
<!-- Feature A cluster: invite-code-registration -->
|
|
84
|
+
<g class="m-cluster" data-feature-id="invite-code-registration">
|
|
85
|
+
<rect x="60" y="130" width="520" height="850" rx="18" class="m-cluster__rect"></rect>
|
|
86
|
+
<a href="features/invite-code-registration/index.html">
|
|
87
|
+
<text x="78" y="160" class="m-cluster__title">功能模組 ▸ 邀請碼註冊</text>
|
|
88
|
+
</a>
|
|
89
|
+
<text x="78" y="180" class="m-cluster__sub">訪客憑邀請碼建立帳號(消費邀請碼)</text>
|
|
90
|
+
|
|
91
|
+
<a href="features/invite-code-registration/web-register-ui.html">
|
|
92
|
+
<g class="m-sub" id="m-A1" data-submodule-id="web-register-ui">
|
|
93
|
+
<rect x="180" y="210" width="280" height="64" rx="10"></rect>
|
|
94
|
+
<text x="320" y="237" text-anchor="middle" class="m-sub__title">web-register-ui</text>
|
|
95
|
+
<text x="320" y="256" text-anchor="middle" class="m-sub__kind">React 頁面 · 表單提交</text>
|
|
96
|
+
</g>
|
|
97
|
+
</a>
|
|
98
|
+
|
|
99
|
+
<a href="features/invite-code-registration/public-api.html">
|
|
100
|
+
<g class="m-sub" id="m-A2" data-submodule-id="public-api">
|
|
101
|
+
<rect x="180" y="320" width="280" height="64" rx="10"></rect>
|
|
102
|
+
<text x="320" y="347" text-anchor="middle" class="m-sub__title">public-api(reg)</text>
|
|
103
|
+
<text x="320" y="366" text-anchor="middle" class="m-sub__kind">Go HTTP handler</text>
|
|
104
|
+
</g>
|
|
105
|
+
</a>
|
|
106
|
+
|
|
107
|
+
<a href="features/invite-code-registration/registration-service.html">
|
|
108
|
+
<g class="m-sub" id="m-A3" data-submodule-id="registration-service">
|
|
109
|
+
<rect x="180" y="450" width="280" height="64" rx="10"></rect>
|
|
110
|
+
<text x="320" y="477" text-anchor="middle" class="m-sub__title">registration-service</text>
|
|
111
|
+
<text x="320" y="496" text-anchor="middle" class="m-sub__kind">交易編排 · 網域規則</text>
|
|
112
|
+
</g>
|
|
113
|
+
</a>
|
|
114
|
+
|
|
115
|
+
<a href="features/invite-code-registration/postgresql.html">
|
|
116
|
+
<g class="m-sub m-sub--db" id="m-A4" data-submodule-id="postgresql">
|
|
117
|
+
<rect x="180" y="700" width="280" height="84" rx="10"></rect>
|
|
118
|
+
<text x="320" y="728" text-anchor="middle" class="m-sub__title">postgresql(reg 視角)</text>
|
|
119
|
+
<text x="320" y="748" text-anchor="middle" class="m-sub__kind">表:invite_codes · users</text>
|
|
120
|
+
<text x="320" y="768" text-anchor="middle" class="m-sub__kind">作用:讀邀請碼 · 寫 user · 核銷</text>
|
|
121
|
+
</g>
|
|
122
|
+
</a>
|
|
123
|
+
</g>
|
|
124
|
+
|
|
125
|
+
<!-- Feature B cluster: get-invite-codes -->
|
|
126
|
+
<g class="m-cluster" data-feature-id="get-invite-codes">
|
|
127
|
+
<rect x="620" y="130" width="520" height="850" rx="18" class="m-cluster__rect"></rect>
|
|
128
|
+
<a href="features/get-invite-codes/index.html">
|
|
129
|
+
<text x="638" y="160" class="m-cluster__title">功能模組 ▸ 獲取邀請碼</text>
|
|
130
|
+
</a>
|
|
131
|
+
<text x="638" y="180" class="m-cluster__sub">已登入使用者請求發放新邀請碼(生產邀請碼)</text>
|
|
132
|
+
|
|
133
|
+
<a href="features/get-invite-codes/web-get-invite-ui.html">
|
|
134
|
+
<g class="m-sub" id="m-B1" data-submodule-id="web-get-invite-ui">
|
|
135
|
+
<rect x="740" y="210" width="280" height="64" rx="10"></rect>
|
|
136
|
+
<text x="880" y="237" text-anchor="middle" class="m-sub__title">web-get-invite-ui</text>
|
|
137
|
+
<text x="880" y="256" text-anchor="middle" class="m-sub__kind">React 頁面 · 列表 + 生成按鈕</text>
|
|
138
|
+
</g>
|
|
139
|
+
</a>
|
|
140
|
+
|
|
141
|
+
<a href="features/get-invite-codes/public-api.html">
|
|
142
|
+
<g class="m-sub" id="m-B2" data-submodule-id="public-api">
|
|
143
|
+
<rect x="740" y="320" width="280" height="64" rx="10"></rect>
|
|
144
|
+
<text x="880" y="347" text-anchor="middle" class="m-sub__title">public-api(iss)</text>
|
|
145
|
+
<text x="880" y="366" text-anchor="middle" class="m-sub__kind">Go HTTP handler</text>
|
|
146
|
+
</g>
|
|
147
|
+
</a>
|
|
148
|
+
|
|
149
|
+
<a href="features/get-invite-codes/invite-issuance-service.html">
|
|
150
|
+
<g class="m-sub" id="m-B3" data-submodule-id="invite-issuance-service">
|
|
151
|
+
<rect x="740" y="450" width="280" height="64" rx="10"></rect>
|
|
152
|
+
<text x="880" y="477" text-anchor="middle" class="m-sub__title">invite-issuance-service</text>
|
|
153
|
+
<text x="880" y="496" text-anchor="middle" class="m-sub__kind">額度 · 交易 · 重試</text>
|
|
154
|
+
</g>
|
|
155
|
+
</a>
|
|
156
|
+
|
|
157
|
+
<a href="features/get-invite-codes/invite-code-generator.html">
|
|
158
|
+
<g class="m-sub" id="m-B4" data-submodule-id="invite-code-generator">
|
|
159
|
+
<rect x="740" y="570" width="280" height="64" rx="10"></rect>
|
|
160
|
+
<text x="880" y="597" text-anchor="middle" class="m-sub__title">invite-code-generator</text>
|
|
161
|
+
<text x="880" y="616" text-anchor="middle" class="m-sub__kind">純函式 · crypto/rand → 字串</text>
|
|
162
|
+
</g>
|
|
163
|
+
</a>
|
|
164
|
+
|
|
165
|
+
<a href="features/get-invite-codes/postgresql.html">
|
|
166
|
+
<g class="m-sub m-sub--db" id="m-B5" data-submodule-id="postgresql">
|
|
167
|
+
<rect x="740" y="700" width="280" height="84" rx="10"></rect>
|
|
168
|
+
<text x="880" y="728" text-anchor="middle" class="m-sub__title">postgresql(iss 視角)</text>
|
|
169
|
+
<text x="880" y="748" text-anchor="middle" class="m-sub__kind">表:invite_codes</text>
|
|
170
|
+
<text x="880" y="768" text-anchor="middle" class="m-sub__kind">作用:INSERT 新行(屬於 owner)</text>
|
|
171
|
+
</g>
|
|
172
|
+
</a>
|
|
173
|
+
</g>
|
|
174
|
+
|
|
175
|
+
<!-- ============ INTRA-FEATURE A EDGES ============ -->
|
|
176
|
+
<!-- visitor -> A1 -->
|
|
177
|
+
<g class="m-edge m-edge--call" data-edge-id="e-A-1">
|
|
178
|
+
<path d="M 320 96 L 320 206" marker-end="url(#mk-solid)"></path>
|
|
179
|
+
<text x="332" y="160" class="m-edge__label">提交註冊表單</text>
|
|
180
|
+
</g>
|
|
181
|
+
<!-- A1 -> A2 -->
|
|
182
|
+
<g class="m-edge m-edge--call" data-edge-id="e-A-2">
|
|
183
|
+
<path d="M 320 274 L 320 316" marker-end="url(#mk-solid)"></path>
|
|
184
|
+
<text x="332" y="300" class="m-edge__label">HTTP POST /register · {email, password, inviteCode}</text>
|
|
185
|
+
</g>
|
|
186
|
+
<!-- A2 -> A3 -->
|
|
187
|
+
<g class="m-edge m-edge--call" data-edge-id="e-A-3">
|
|
188
|
+
<path d="M 320 384 L 320 446" marker-end="url(#mk-solid)"></path>
|
|
189
|
+
<text x="332" y="420" class="m-edge__label">RegisterWithInvite(ctx, RegisterInput)</text>
|
|
190
|
+
</g>
|
|
191
|
+
<!-- A3 -> A4 (read invite) on RIGHT side -->
|
|
192
|
+
<g class="m-edge m-edge--call" data-edge-id="e-A-4-read">
|
|
193
|
+
<path d="M 415 514 C 510 540, 510 670, 415 700" marker-end="url(#mk-solid)"></path>
|
|
194
|
+
<text x="520" y="610" class="m-edge__label">SELECT … FOR UPDATE (code)</text>
|
|
195
|
+
</g>
|
|
196
|
+
<!-- A4 -> A3 (return InviteRow) on right inner -->
|
|
197
|
+
<g class="m-edge m-edge--return" data-edge-id="e-A-4-row">
|
|
198
|
+
<path d="M 360 700 C 360 600, 360 580, 360 514" marker-end="url(#mk-thin)"></path>
|
|
199
|
+
<text x="370" y="600" class="m-edge__label">⇠ InviteRow | NotFound</text>
|
|
200
|
+
</g>
|
|
201
|
+
<!-- A3 -> A4 (insert user) on LEFT -->
|
|
202
|
+
<g class="m-edge m-edge--call" data-edge-id="e-A-4-insert-user">
|
|
203
|
+
<path d="M 225 514 C 130 560, 130 660, 225 700" marker-end="url(#mk-solid)"></path>
|
|
204
|
+
<text x="78" y="600" class="m-edge__label">INSERT users(...)</text>
|
|
205
|
+
</g>
|
|
206
|
+
<!-- A3 -> A4 (update invite consumed) center-left -->
|
|
207
|
+
<g class="m-edge m-edge--call" data-edge-id="e-A-4-update">
|
|
208
|
+
<path d="M 280 514 C 240 560, 240 660, 280 700" marker-end="url(#mk-solid)"></path>
|
|
209
|
+
<text x="158" y="660" class="m-edge__label">UPDATE invite_codes SET consumed_at=now()</text>
|
|
210
|
+
</g>
|
|
211
|
+
<!-- A2 -> visitor (response) -->
|
|
212
|
+
<g class="m-edge m-edge--return" data-edge-id="e-A-resp">
|
|
213
|
+
<path d="M 270 320 C 180 220, 180 130, 245 96" marker-end="url(#mk-thin)"></path>
|
|
214
|
+
<text x="72" y="220" class="m-edge__label">⇠ 201 / 409</text>
|
|
215
|
+
</g>
|
|
216
|
+
|
|
217
|
+
<!-- ============ INTRA-FEATURE B EDGES ============ -->
|
|
218
|
+
<!-- member -> B1 -->
|
|
219
|
+
<g class="m-edge m-edge--call" data-edge-id="e-B-1">
|
|
220
|
+
<path d="M 880 96 L 880 206" marker-end="url(#mk-solid)"></path>
|
|
221
|
+
<text x="892" y="160" class="m-edge__label">點擊「產生邀請碼」</text>
|
|
222
|
+
</g>
|
|
223
|
+
<!-- B1 -> B2 -->
|
|
224
|
+
<g class="m-edge m-edge--call" data-edge-id="e-B-2">
|
|
225
|
+
<path d="M 880 274 L 880 316" marker-end="url(#mk-solid)"></path>
|
|
226
|
+
<text x="892" y="300" class="m-edge__label">HTTP POST /me/invite-codes</text>
|
|
227
|
+
</g>
|
|
228
|
+
<!-- B2 -> B3 -->
|
|
229
|
+
<g class="m-edge m-edge--call" data-edge-id="e-B-3">
|
|
230
|
+
<path d="M 880 384 L 880 446" marker-end="url(#mk-solid)"></path>
|
|
231
|
+
<text x="892" y="420" class="m-edge__label">IssueInviteCode(ctx, userID)</text>
|
|
232
|
+
</g>
|
|
233
|
+
<!-- B3 -> B4 (call Next) on LEFT inner -->
|
|
234
|
+
<g class="m-edge m-edge--call" data-edge-id="e-B-3-gen-call">
|
|
235
|
+
<path d="M 820 514 C 770 540, 770 560, 820 566" marker-end="url(#mk-solid)"></path>
|
|
236
|
+
<text x="650" y="555" class="m-edge__label">Next()</text>
|
|
237
|
+
</g>
|
|
238
|
+
<!-- B4 -> B3 (return candidate) on RIGHT inner -->
|
|
239
|
+
<g class="m-edge m-edge--return" data-edge-id="e-B-3-gen-ret">
|
|
240
|
+
<path d="M 950 570 C 1010 560, 1010 540, 950 514" marker-end="url(#mk-thin)"></path>
|
|
241
|
+
<text x="1020" y="555" class="m-edge__label">⇠ 候選字串</text>
|
|
242
|
+
</g>
|
|
243
|
+
<!-- B3 -> B5 (INSERT) -->
|
|
244
|
+
<g class="m-edge m-edge--call" data-edge-id="e-B-5-insert">
|
|
245
|
+
<path d="M 860 514 C 860 600, 860 660, 860 700" marker-end="url(#mk-solid)"></path>
|
|
246
|
+
<text x="872" y="610" class="m-edge__label">INSERT invite_codes(owner=userID, code)</text>
|
|
247
|
+
</g>
|
|
248
|
+
<!-- B5 -> B3 (return ok / unique_violation) -->
|
|
249
|
+
<g class="m-edge m-edge--return" data-edge-id="e-B-5-ret">
|
|
250
|
+
<path d="M 920 700 C 980 660, 980 560, 920 514" marker-end="url(#mk-thin)"></path>
|
|
251
|
+
<text x="990" y="610" class="m-edge__label">⇠ ok | unique_violation</text>
|
|
252
|
+
</g>
|
|
253
|
+
<!-- B2 -> member (response) -->
|
|
254
|
+
<g class="m-edge m-edge--return" data-edge-id="e-B-resp">
|
|
255
|
+
<path d="M 940 320 C 1030 220, 1030 130, 965 96" marker-end="url(#mk-thin)"></path>
|
|
256
|
+
<text x="1040" y="220" class="m-edge__label">⇠ 201 / 429</text>
|
|
257
|
+
</g>
|
|
258
|
+
|
|
259
|
+
<!-- ============ CROSS-FEATURE DATA-ROW EDGE ============ -->
|
|
260
|
+
<!-- B5 produces row → A4 consumes row -->
|
|
261
|
+
<g class="m-edge m-edge--cross" data-edge-id="x-row-flow">
|
|
262
|
+
<path
|
|
263
|
+
d="M 740 742 C 600 880, 600 880, 460 742"
|
|
264
|
+
marker-end="url(#mk-cross)"
|
|
265
|
+
></path>
|
|
266
|
+
<text x="600" y="900" text-anchor="middle" class="m-edge__label m-edge__label--cross">
|
|
267
|
+
資料行流向:B5 寫入 <tspan font-style="italic">invite_codes</tspan> 行 → A4 之後讀取/核銷該行(非函式呼叫)
|
|
268
|
+
</text>
|
|
269
|
+
</g>
|
|
270
|
+
</svg>
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<ol class="flow-edge-manifest flow-edge-manifest--macro">
|
|
274
|
+
<li data-edge-id="e-A-1" data-edge-kind="call"><span class="flow-edge-from">#actor-visitor</span> → <span class="flow-edge-to">#m-A1</span> <span class="flow-edge-label">提交註冊</span></li>
|
|
275
|
+
<li data-edge-id="e-A-2" data-edge-kind="call"><span class="flow-edge-from">#m-A1</span> → <span class="flow-edge-to">#m-A2</span> <span class="flow-edge-label">HTTP POST</span></li>
|
|
276
|
+
<li data-edge-id="e-A-3" data-edge-kind="call"><span class="flow-edge-from">#m-A2</span> → <span class="flow-edge-to">#m-A3</span> <span class="flow-edge-label">RegisterWithInvite(ctx, in)</span></li>
|
|
277
|
+
<li data-edge-id="e-A-4-read" data-edge-kind="call"><span class="flow-edge-from">#m-A3</span> → <span class="flow-edge-to">#m-A4</span> <span class="flow-edge-label">SELECT FOR UPDATE invite_codes</span></li>
|
|
278
|
+
<li data-edge-id="e-A-4-row" data-edge-kind="return"><span class="flow-edge-from">#m-A4</span> ⇠ <span class="flow-edge-to">#m-A3</span> <span class="flow-edge-label">InviteRow | NotFound</span></li>
|
|
279
|
+
<li data-edge-id="e-A-4-insert-user" data-edge-kind="call"><span class="flow-edge-from">#m-A3</span> → <span class="flow-edge-to">#m-A4</span> <span class="flow-edge-label">INSERT users</span></li>
|
|
280
|
+
<li data-edge-id="e-A-4-update" data-edge-kind="call"><span class="flow-edge-from">#m-A3</span> → <span class="flow-edge-to">#m-A4</span> <span class="flow-edge-label">UPDATE invite_codes SET consumed_at</span></li>
|
|
281
|
+
<li data-edge-id="e-A-resp" data-edge-kind="return"><span class="flow-edge-from">#m-A2</span> ⇠ <span class="flow-edge-to">#actor-visitor</span> <span class="flow-edge-label">201 / 409</span></li>
|
|
282
|
+
|
|
283
|
+
<li data-edge-id="e-B-1" data-edge-kind="call"><span class="flow-edge-from">#actor-member</span> → <span class="flow-edge-to">#m-B1</span> <span class="flow-edge-label">點生成</span></li>
|
|
284
|
+
<li data-edge-id="e-B-2" data-edge-kind="call"><span class="flow-edge-from">#m-B1</span> → <span class="flow-edge-to">#m-B2</span> <span class="flow-edge-label">HTTP POST</span></li>
|
|
285
|
+
<li data-edge-id="e-B-3" data-edge-kind="call"><span class="flow-edge-from">#m-B2</span> → <span class="flow-edge-to">#m-B3</span> <span class="flow-edge-label">IssueInviteCode(ctx, userID)</span></li>
|
|
286
|
+
<li data-edge-id="e-B-3-gen-call" data-edge-kind="call"><span class="flow-edge-from">#m-B3</span> → <span class="flow-edge-to">#m-B4</span> <span class="flow-edge-label">Next() · 純函式</span></li>
|
|
287
|
+
<li data-edge-id="e-B-3-gen-ret" data-edge-kind="return"><span class="flow-edge-from">#m-B4</span> ⇠ <span class="flow-edge-to">#m-B3</span> <span class="flow-edge-label">候選字串</span></li>
|
|
288
|
+
<li data-edge-id="e-B-5-insert" data-edge-kind="call"><span class="flow-edge-from">#m-B3</span> → <span class="flow-edge-to">#m-B5</span> <span class="flow-edge-label">INSERT invite_codes</span></li>
|
|
289
|
+
<li data-edge-id="e-B-5-ret" data-edge-kind="return"><span class="flow-edge-from">#m-B5</span> ⇠ <span class="flow-edge-to">#m-B3</span> <span class="flow-edge-label">ok / unique_violation(碰撞 → 重新 Next)</span></li>
|
|
290
|
+
<li data-edge-id="e-B-resp" data-edge-kind="return"><span class="flow-edge-from">#m-B2</span> ⇠ <span class="flow-edge-to">#actor-member</span> <span class="flow-edge-label">201 / 429</span></li>
|
|
291
|
+
|
|
292
|
+
<li data-edge-id="x-row-flow" data-edge-kind="data-row"><span class="flow-edge-from">#m-B5</span> ⇒ <span class="flow-edge-to">#m-A4</span> <span class="flow-edge-label">同一 invite_codes 表:B 寫入行被 A 讀取/核銷(非函式呼叫,純資料行傳遞)</span></li>
|
|
293
|
+
</ol>
|
|
294
|
+
<figcaption class="flow-caption">
|
|
295
|
+
「同一對節點 N 條邊」是真實的(例如 A3 → A4 同時有 SELECT、INSERT、UPDATE 三條;B3 ↔ B4、B3 ↔ B5 皆有呼叫/回傳對)。子模組頁不重述這些跨界邊;它們只在此處統一呈現。
|
|
296
|
+
</figcaption>
|
|
297
|
+
</figure>
|
|
298
|
+
</section>
|
|
299
|
+
|
|
300
|
+
<nav class="atlas-submodule-index">
|
|
301
|
+
<h2>子模組索引(每一個子模組各一頁)</h2>
|
|
302
|
+
<ul>
|
|
303
|
+
<li>
|
|
304
|
+
<strong>邀請碼註冊 ▸</strong>
|
|
305
|
+
<a href="features/invite-code-registration/web-register-ui.html">web-register-ui</a> ·
|
|
306
|
+
<a href="features/invite-code-registration/public-api.html">public-api</a> ·
|
|
307
|
+
<a href="features/invite-code-registration/registration-service.html">registration-service</a> ·
|
|
308
|
+
<a href="features/invite-code-registration/postgresql.html">postgresql</a>
|
|
309
|
+
</li>
|
|
310
|
+
<li>
|
|
311
|
+
<strong>獲取邀請碼 ▸</strong>
|
|
312
|
+
<a href="features/get-invite-codes/web-get-invite-ui.html">web-get-invite-ui</a> ·
|
|
313
|
+
<a href="features/get-invite-codes/public-api.html">public-api</a> ·
|
|
314
|
+
<a href="features/get-invite-codes/invite-issuance-service.html">invite-issuance-service</a> ·
|
|
315
|
+
<a href="features/get-invite-codes/invite-code-generator.html">invite-code-generator</a> ·
|
|
316
|
+
<a href="features/get-invite-codes/postgresql.html">postgresql</a>
|
|
317
|
+
</li>
|
|
318
|
+
</ul>
|
|
319
|
+
<p class="atlas-meta">
|
|
320
|
+
每一頁只描述<strong>該子模組自身的函式輸入/輸出/副作用</strong>與<strong>內部資料流</strong>;跨子模組的呼叫關係請回到上方宏觀圖。
|
|
321
|
+
</p>
|
|
322
|
+
</nav>
|
|
323
|
+
|
|
324
|
+
<nav class="atlas-features">
|
|
325
|
+
<h2>功能模組總覽(中層)</h2>
|
|
326
|
+
<ul>
|
|
327
|
+
<li><a href="features/invite-code-registration/index.html">邀請碼註冊(功能總覽)</a></li>
|
|
328
|
+
<li><a href="features/get-invite-codes/index.html">獲取邀請碼(功能總覽)</a></li>
|
|
329
|
+
</ul>
|
|
330
|
+
</nav>
|
|
331
|
+
|
|
332
|
+
<footer class="atlas-meta" style="margin-top: 2rem">
|
|
333
|
+
<p>示範:<code>init-project-html/sample-demo/resources/project-architecture/</code></p>
|
|
334
|
+
</footer>
|
|
335
|
+
</main>
|
|
336
|
+
</body>
|
|
337
|
+
</html>
|