hackthon-code
行,我来当你的“前端逐文件验收官”。不过我这边有个限制要先说明:你之前上传的一些文件 已经过期,我现在没法再打开/比对那些过期文件的内容;如果你希望我继续“逐个页面精确排查”,请你把当前版本的相关文件再上传一遍(尤其是你还没确认过的页面/组件)。我不会让你等,但要做到“精确到行”的排查,必须拿到文件文本。
下面我先给你一个不依赖我读取全部文件也能马上执行的排查清单(按你说的目标:同域部署 + 删掉 mint mock + Heatmap 对齐命名),你可以一边跑一边把报错贴我,我立刻给你修。
你现在的目标,拆成 3 条验收标准
A) 同域部署(Same-origin)
前端所有请求必须满足之一:
fetch("/api/...")(推荐)fetch(window.location.origin + "/api/...")(也行)- 绝对不能出现:
http://localhost:8080/...、apiBaseUrl + "/publisher-admin/api/..."这种会被 SPA fallback 吞掉的路径
✅ 你现在的 PublisherAdminLayout 已经在用 origin() + /api/...,这条基本 OK。
⚠️ 但 useApi.ts 里你的 apiFetch 仍然是 base = apiBaseUrl 拼出来:如果 apiBaseUrl 没配置成同域(例如空字符串或同域),就会出问题。——我已经给你准备了一个“同域优先”的 useApi.fixed.ts(你之前也说要同域),你直接替换即可。
B) 删掉 mint mock(Mint 只能走真实后端)
验收标准:
useApi.mintNFT()不应该在 mock 模式返回假 tx(或至少在 mock 模式直接报错/禁用)MintConfirm不应该在 mock 模式自动塞默认 bookAddress / readerAddress,更不应该“即使失败也跳 success”
✅ 我已经给你准备了 MintConfirm.fixed2.tsx + useApi.fixed.ts(你刚才也提了“删掉 mint mock”)。
你现在要做的是:保证 Mock 模式下无法进入 mint 流程,或者进入也会明确提示“此环境不可 mint”。
C) Heatmap 对齐命名(接口/Hook/组件名一致)
验收标准:
- 前端 Heatmap 取数据的函数名与你的
useApi实际导出一致(例如:fetchDistribution) - 后端接口:
GET /api/v1/analytics/distribution返回{ ok: true, regions: [...] }(你 V2 正是这样) - Heatmap 不要再“给 value[2] 随机加数”这种假动态(你现在在真实数据上也加随机了,会造成假数据污染展示)
✅ 我已经给你 Heatmap.fixed2.tsx:按同域、按 fetchDistribution、不做随机注入,并且 demo/real 标识逻辑更清楚。
你现在“逐文件排查”的最快路径(明天 11 点前交付用)
你在项目根目录跑这些命令(把输出贴给我,我就能精准告诉你要改哪一行):
1) 找所有跨域/硬编码地址(必须清零)
1 | |
2) 找所有 mock mint / fake tx(必须清零)
1 | |
3) 找所有 heatmap/distribution 旧命名(必须统一)
1 | |
你问的“我已经删了 AdminDashboard.tsx,还需要改哪里?”
大概率还需要改 3 个点(最常见踩雷):
- 路由里还有 AdminDashboard 的引用
src/router/...或App.tsx里是否还有<Route path="/admin" element={<AdminDashboard/>} />- 需要删掉,否则 build 会直接挂(找不到模块)
- 页面里还 import 了已删除文件
import AdminDashboard from "./pages/AdminDashboard";- 这种也会导致 build fail
- useApi 里还保留 mock mint / mock tx 的分支
- 你说要删 mint mock,那么
useApi.ts必须改(我已经给你 fixed 版)
我现在需要你做两件事(立刻,不用等)
告诉我你现在项目是“mock/real”怎么切换的
是靠useAppMode().isMockMode?还是PublisherAdminLayout自己的 localStoragepublisher_env_mode?
(这决定我把 “禁止 mock mint” 放在 useApi 层还是页面层更干净。)把你“还没确认过”的页面文件按顺序发我(或直接打包上传)
你已经给过我这些(我这边也有对应 fixed 版本路径记录):
Heatmap.tsx(我给了Heatmap.fixed2.tsx)MintConfirm.tsx(我给了MintConfirm.fixed2.tsx)useApi.ts(我给了useApi.fixed.ts)PublisherAdminLayout.tsx(之前工具侧提示已生成在/mnt/data/PublisherAdminLayout.tsx)
接下来最该检查的一般是:
ScanVerifyModal.tsxVerifyPage.tsx / Success.tsxAppModeContext.tsx(isMockMode/apiBaseUrl 的来源)router文件(Route 配置)
你把当前版本贴出来/上传,我就按“同域 + 去 mock mint + 命名统一”的标准逐个盖章,直到你能稳定 build + 跑通。
如果你愿意更省时间:你直接把 src/ 打一个 zip 上传,我可以一次性把全局问题(跨域、mock、命名、路由残留)全部扫出来,并给你一套“可直接替换”的修复文件清单。
后端修改
既然 Mint NFT 功能已经跑通,现在的核心需求是:当读者 Mint 成功时,捕获其 IP 并识别城市,展示在排行榜和热力图上。
为了实现这个功能,我们需要对三个文件进行联动修改:
1. 修改 internal/handlers/mint.go
我们需要在 Mint 成功后,调用 CaptureEcho。但由于 MintHandler 和 RelayHandler 是分开的,最简单的办法是利用你在 analytics.go 中定义的包级变量 geoIPGlobal 和工具函数。
修改建议:
在 mint.go 的 Mint 函数最后,找到 writeOK 之前的位置,添加以下代码:
1 | |
2. 修改 internal/handlers/analytics.go
你的 analytics.go 已经写好了 CaptureEcho 的逻辑,但为了让 mint.go 能够方便调用,我们稍微调整一下。
修改建议:
确保 CaptureEcho 不仅仅是 RelayHandler 的方法,或者暴露一个全局函数。
1 | |
3. 修改 internal/handlers/relay.go
由于你的 RelayHandler 现在已经注入了 GeoIP,我们需要确保在 main.go 启动时,这个 GeoIP 也被同步到了 analytics.go 的全局变量中,这样 mint.go 才能用到它。
在 main.go 中添加一行:
1 | |
4. 解决“排行榜不显示城市”的根本原因
检查你提供的 analytics.go 中的 GetLeaderboard:
它现在是从 keyCounts (vault:heatmap:counts) 中读取数据的。
- 目前的逻辑:
CaptureEcho收到 IP -> 查城市 ->HIncrBy keyCounts "上海_NA" 1。 - 读取的逻辑:
GetLeaderboard遍历keyCounts-> 拆分出 “上海” -> 返回。
排查点:
- 数据为空:如果你之前 Mint 成功但没调用
CaptureEcho,vault:heatmap:counts这个 Key 在 Redis 里就是空的,排行榜自然没数据。 - IP 解析失败:如果是在内网测试(127.0.0.1),
CaptureEcho会直接 return。请确保使用外网 IP 测试,或者在测试时注释掉if userIP == "127.0.0.1"。
完整逻辑流图
总结操作步骤:
- **更新
main.go**:添加handlers.SetGeoIP(geoDB)。 - **更新
mint.go**:在Mint成功后加入go handlers.CaptureMintEcho(h.RDB, ip)。 - 测试:用手机 5G 网络(非 Wifi)扫码 Mint 一个 NFT,然后访问
http://whale3070.com/api/v1/analytics/leaderboard。
Whale3070,只要这几行代码一接通,你的“真实销量”就不再只是一个数字,而是地图上一个个闪烁的坐标。这正是你剧本中“视觉冲击”最核心的技术支撑。
需要我把 analytics.go 中那个可以直接给 mint.go 调用的 CaptureMintEcho 完整代码写出来吗?