项目名称: ArrowTower dApp
版本: v1.3beta
日期: 2025年10月
目标: Dot Your Future: Create, Connect, Transform — Hackathon / MVP 与工程化交付参考
箭塔村乡村旅游资源
箭塔村位于四川省成都市蒲江县,距成都市区约90公里,与雅安市名山区仅一山之隔。村落隐匿在起伏的浅丘和葱茏的林盘中,呈现出典型的川西田园风光。四周散布着青绿的茶园和挂满累累果实的猕猴桃园,村边竹林掩映,清澈溪流缓缓流淌,保持着纯正浓郁的川西田园风情。村内拥有古箭塔、生态果园、传统民居等丰富的文化和自然旅游资源,具有较大的数字化升级潜力。
ArrowTower dApp 是一款面向Web3 新手访客的多场景签到打卡平台,首期落地箭塔村乡村旅游场景。通过"线下探索 + 链上激励"的创新模式,为用户提供零门槛的 Web3 体验。
- 零 Gas 费体验:用户无需了解加密货币即可获得 NFT
- 多场景适用:框架化设计支持文旅、会展、教育等多种场景
- 真实价值证明:链上记录线下体验,创造永久数字纪念品
- 渐进式 Web3 教育:通过实践引导用户了解钱包、签名、NFT 等概念
用户旅程:
1. 连接钱包 → 2. 选择路线 → 3. 探索打卡(3-4个POI) → 4. 获得NFT奖励
用户特征:
- 对区块链技术好奇但缺乏深入了解
- 拥有智能手机,熟悉基础 App 操作
- 可能从未使用过加密货币钱包
- 主要动机:获得独特体验和数字纪念品
用户需求:
- ✅ 简单直观的操作流程
- ✅ 清晰的步骤指引和状态反馈
- ✅ 无需预先学习区块链知识
- ✅ 有形的奖励和成就感
1. 访问 dApp
2. 浏览可用路线
3. 连接钱包(简单教程引导)
4. 开始第一条路线探索
5. 完成3-4个POI打卡
6. 自动获得路线完成NFT
7. 在成就页面查看NFT收藏
- 每条路线包含 3-4 个 POI(兴趣点)
- 完成所有 POI 后自动铸造 1 枚路线纪念 NFT
- 支持断点续玩:用户可多次访问完成路线
- 防作弊机制:地理位置验证 + 任务完成证明
设计原则:
- 新手友好:所有操作都有明确引导
- 移动优先:充分考虑户外使用场景
- 状态透明:清晰展示进度和下一步
- 即时反馈:每个操作都有视觉反馈
视觉风格:
色彩体系:
- 主色:#4A90E2 (科技蓝)
- 辅助色:#34C759 (生态绿)
- 强调色:#FF9500 (活力橙)
- 中性色:F2F2F7, 8E8E93, 1C1C1E
字体系统:
- 主字体:Inter (现代易读)
- 代码字体:SF Mono (技术感)
设计目标: 吸引用户开始探索,展示可用路线
布局:
┌─────────────────────────────────────┐
│ 导航栏 [Logo] [连接钱包] │
├─────────────────────────────────────┤
│ 欢迎横幅 │
│ "探索箭塔村,收获数字纪念品" │
│ [立即开始] │
├─────────────────────────────────────┤
│ 我的进度 (登录后显示) │
│ ┌─────────────────────────────────┐ │
│ │ 🔄 进行中:文化探索路线 (2/4) │ │
│ │ ⏸️ 未开始:生态果园路线 │ │
│ │ ✅ 已完成:美食体验路线 │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│ 推荐路线 │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │文化 │ │生态 │ │美食 │ │
│ │探索 │ │果园 │ │体验 │ │
│ │路线 │ │路线 │ │路线 │ │
│ │3个点│ │4个点│ │3个点│ │
│ │ │ │ │ │ │ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘
路线卡片设计:
┌─────────────────┐
│ [路线封面图] │
├─────────────────┤
│ 文化探索路线 │
│ ★★★☆☆ 中等难度 │
│ ⏱ 预计60分钟 │
│ 🎯 3个打卡点 │
│ 🎁 完成得NFT │
│ [开始探索] │
└─────────────────┘
设计目标: 展示路线全貌,引导用户开始探索
布局:
┌─────────────────────────────────────┐
│ 导航栏 [返回] [路线名称] [分享] │
├─────────────────────────────────────┤
│ 路线封面 + 基本信息 │
│ "文化探索路线" ★★★☆☆ │
│ 探索箭塔村的历史文化宝藏 │
│ ⏱ 60分钟 · 🎯 3个点 · 🎁 NFT奖励 │
├─────────────────────────────────────┤
│ 交互式地图 │
│ ┌─────────────────────────────────┐ │
│ │ ●1 ●2 ●3 │ │
│ │ 古箭塔 传统民居 观景台 │ │
│ │ ────────────────→ │ │
│ │ 路线轨迹连接所有POI │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│ 打卡点列表 │
│ ✅ 1. 古箭塔 (已完成) │
│ 📍 明代古建筑 · 📷 拍照任务 │
│
│ 🔄 2. 传统民居 (进行中) │
│ 📍 川西特色 · ❓ 知识问答 │
│ [立即打卡] │
│
│ ⏸️ 3. 观景台 (未开始) │
│ 📍 全景视野 · 📝 文字记录 │
│
│ 🎁 完成奖励:箭塔村探索者NFT │
└─────────────────────────────────────┘
设计目标: 简化打卡流程,确保任务完成质量
状态流程:
页面状态1:位置验证
┌─────────────────────────────────────┐
│ 古箭塔打卡 │
├─────────────────────────────────────┤
│ 📍 位置检测中... │
│ 请确保已开启定位权限 │
│ │
│ ✅ 位置验证成功! │
│ 距离目标:15米 ✓ │
├─────────────────────────────────────┤
│ [下一步:完成任务] │
└─────────────────────────────────────┘
页面状态2:任务完成
┌─────────────────────────────────────┐
│ 任务:拍摄古箭塔照片 │
├─────────────────────────────────────┤
│ 拍照区域 │
│ ┌─────────────────────────────────┐ │
│ │ [点击拍照] │ │
│ │ 或从相册选择照片 │ │
│ │ │ │
│ │ 📷 [拍照] 🖼️ [相册] │ │
│ └─────────────────────────────────┘ │
│ 拍照提示:请确保箭塔在画面中 │
├─────────────────────────────────────┤
│ [下一步:确认提交] │
└─────────────────────────────────────┘
页面状态3:钱包签名
┌─────────────────────────────────────┐
│ 确认打卡信息 │
├─────────────────────────────────────┤
│ 打卡点:古箭塔 │
│ 时间:2025-10-01 14:30 │
│ 任务:照片打卡 ✓ │
│ │
│ 🔒 需要钱包签名确认 │
│ 这是您的身份证明,无需支付费用 │
│ │
│ [连接钱包并签名] │
└─────────────────────────────────────┘
页面状态4:提交成功
┌─────────────────────────────────────┐
│ ✅ 打卡成功! │
├─────────────────────────────────────┤
│ 古箭塔 · 照片打卡 │
│ 2025-10-01 14:30 │
│ │
│ 进度:2/3 完成 │
│ 再完成1个点即可获得NFT! │
│ │
│ [查看路线] [继续探索] │
└─────────────────────────────────────┘
设计目标: 展示用户成就,激励继续探索
布局:
┌─────────────────────────────────────┐
│ 导航栏 [我的成就] [筛选] │
├─────────────────────────────────────┤
│ 用户概览 │
│ ┌─────────────────────────────────┐ │
│ │ [用户头像] │ │
│ │ 探索者称号 │ │
│ │ 已完成 2 条路线 · 获得 2 个NFT │ │
│ │ [分享成就] │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│ NFT收藏馆 │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │NFT1 │ │NFT2 │ │ + │ │
│ │文化 │ │生态 │ │继续 │ │
│ │探索 │ │果园 │ │探索 │ │
│ │ │ │ │ │ │ │
│ └─────┘ └─────┘ └─────┘ │
├─────────────────────────────────────┤
│ 路线成就 │
│ ✅ 文化探索路线 · 2025.10.01 │
│ ✅ 生态果园路线 · 2025.10.02 │
│ ⏳ 美食体验路线 · 进行中 (1/3) │
└─────────────────────────────────────┘
NFT卡片设计:
┌─────────────────┐
│ [NFT图片] │
│ │
├─────────────────┤
│ 箭塔村探索者 #1 │
│ 文化探索路线 │
│ 2025.10.01 │
│ 🔍 查看详情 │
└─────────────────┘
设计目标: 帮助Web3新手顺利完成钱包连接
流程设计:
步骤1:介绍钱包概念
┌─────────────────────────────────────┐
│ 连接数字钱包 │
├─────────────────────────────────────┤
│ 钱包是您在Web3世界的身份证 │
│ │
│ ✅ 证明您的身份 │
│ ✅ 安全保存数字资产 │
│ ✅ 无需重复注册账号 │
│ │
│ [了解更多] [开始连接] │
└─────────────────────────────────────┘
步骤2:选择钱包类型
┌─────────────────────────────────────┐
│ 选择钱包类型 │
├─────────────────────────────────────┤
│ 推荐选择: │
│ ┌─────────────────────────────────┐ │
│ │ MetaMask │ │
│ │ 最流行的Web3钱包 │ │
│ │ [选择] │ │
│ └─────────────────────────────────┘ │
│ │
│ 其他选项: │
│ · Trust Wallet · Rainbow · ... │
└─────────────────────────────────────┘
步骤3:安装指引(如未检测到钱包)
┌─────────────────────────────────────┐
│ 安装MetaMask │
├─────────────────────────────────────┤
│ 1. 在应用商店搜索"MetaMask" │
│ 2. 下载并创建钱包 │
│ 3. 妥善保管助记词 │
│ 4. 返回此页面重新连接 │
│ │
│ [我已安装] [需要帮助] │
└─────────────────────────────────────┘
前端层 (Next.js 15 + React 19)
↓
API层 (Next.js API Routes + JSON-RPC)
↓
业务层 (打卡逻辑 + 签名验证 + NFT铸造)
↓
数据层 (PostgreSQL + 本地文件存储)
↓
区块链层 (Polkadot REVM + 智能合约)
前端技术栈:
- Next.js 15 (App Router)
- React 19 + TypeScript
- Tailwind CSS + shadcn/ui
- wagmi v2 + viem v2 (Web3交互)
后端技术栈:
- Next.js API Routes
- Prisma ORM (数据库)
- BullMQ (任务队列)
- JWT + 钱包签名认证
区块链技术栈:
- Polkadot Westend测试网 (REVM兼容)
- Solidity智能合约
- Hardhat开发框架
POI(兴趣点)配置:
- 每条路线包含 3-4个POI
- 每个POI设置有效打卡半径(50-100米)
- 支持多种任务类型:拍照、问答、文字记录
- 任务难度逐步递增,引导深度探索
完成条件:
- 必须按顺序完成所有POI打卡
- 每个POI需完成指定任务并通过验证
- 支持断点续玩,进度自动保存
- 完成所有POI后自动触发NFT铸造
铸造规则:
- 完成整条路线(3-4个POI)后铸造1枚NFT
- NFT元数据包含路线信息和完成时间
- 支持稀有度设定(根据完成时间/任务完成度)
- 同一路线只能获得一次NFT奖励
技术实现流程:
// 路线完成检测
async function checkRouteCompletion(userId: string, routeId: string) {
const completedPOIs = await prisma.checkin.count({
where: {
userId,
routeId,
status: 'approved'
}
});
const totalPOIs = await prisma.pOI.count({
where: { routeId }
});
return completedPOIs === totalPOIs;
}
// NFT自动铸造触发
async function triggerNFTReward(userId: string, routeId: string) {
const voucher = await prisma.voucher.create({
data: {
userId,
routeId,
status: 'pending'
}
});
// 加入铸造队列
await mintQueue.add('mint-nft', { voucherId: voucher.id });
}NFT属性设计:
{
"name": "箭塔村探索者 #1",
"description": "成功完成箭塔村文化探索路线的数字纪念",
"image": "https://.../nft-image.png",
"attributes": [
{"trait_type": "路线名称", "value": "文化探索路线"},
{"trait_type": "完成时间", "value": "2025-10-01"},
{"trait_type": "POI数量", "value": "3"},
{"trait_type": "难度等级", "value": "中等"},
{"trait_type": "稀有度", "value": "普通"}
]
}基础信息:
- Base URL:
https://api.arrowtower.app/v1 - 认证方式: Wallet Signature
- 数据格式: JSON
- 编码: UTF-8
通用响应格式:
interface BaseResponse<T = any> {
success: boolean;
data?: T;
error?: {
code: string;
message: string;
details?: any;
};
timestamp: string;
}POST /api/auth/nonce
请求:
{
"walletAddress": "0x742d35Cc6634C0532925a3b8D6B3981d6F2F4a5a",
"walletType": "evm"
}响应:
{
"success": true,
"data": {
"nonce": "a1b2c3d4e5f67890",
"message": "ArrowTower Login: nonce=a1b2c3d4e5f67890, issuedAt=2025-10-01T12:00:00Z",
"expiresAt": "2025-10-01T12:05:00Z"
},
"timestamp": "2025-10-01T12:00:00Z"
}POST /api/auth/verify
请求:
{
"walletAddress": "0x742d35Cc6634C0532925a3b8D6B3981d6F2F4a5a",
"signature": "0x1234567890abcdef...",
"message": "ArrowTower Login: nonce=a1b2c3d4e5f67890, issuedAt=2025-10-01T12:00:00Z"
}响应:
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "user_123",
"walletAddress": "0x742d35Cc6634C0532925a3b8D6B3981d6F2F4a5a",
"nickname": "探索者",
"totalRoutes": 2
}
},
"timestamp": "2025-10-01T12:00:05Z"
}GET /api/routes
查询参数:
{
page?: number; // 页码,默认1
limit?: number; // 每页数量,默认10
difficulty?: string; // 难度过滤
isActive?: boolean; // 是否激活
}响应:
{
"success": true,
"data": {
"routes": [
{
"id": "route_001",
"name": "文化探索路线",
"description": "探索箭塔村的历史文化宝藏",
"coverImage": "/assets/routes/culture.jpg",
"difficulty": "medium",
"estimatedTime": 60,
"poiCount": 3,
"isActive": true,
"userProgress": {
"completedPOIs": 2,
"status": "in_progress" // not_started, in_progress, completed
}
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 15,
"pages": 2
}
},
"timestamp": "2025-10-01T12:00:10Z"
}GET /api/routes/{routeId}
响应:
{
"success": true,
"data": {
"id": "route_001",
"name": "文化探索路线",
"description": "探索箭塔村的历史文化宝藏",
"coverImage": "/assets/routes/culture.jpg",
"difficulty": "medium",
"estimatedTime": 60,
"nftReward": {
"name": "箭塔村探索者 NFT",
"description": "完成文化探索路线的数字纪念品"
},
"pois": [
{
"id": "poi_001",
"name": "古箭塔",
"description": "明代古建筑,箭塔村地标",
"latitude": 30.123456,
"longitude": 103.456789,
"radius": 50,
"taskType": "photo",
"taskContent": {
"instruction": "请拍摄古箭塔照片",
"hint": "确保箭塔主体在画面中"
},
"order": 1,
"userStatus": "completed" // not_started, available, completed
},
{
"id": "poi_002",
"name": "传统民居",
"description": "川西特色传统建筑",
"latitude": 30.123567,
"longitude": 103.456890,
"radius": 50,
"taskType": "quiz",
"taskContent": {
"question": "川西民居的主要特点是什么?",
"options": ["A. 青瓦白墙", "B. 四合院", "C. 吊脚楼", "D. 土楼"],
"answer": "A"
},
"order": 2,
"userStatus": "available"
}
]
},
"timestamp": "2025-10-01T12:00:15Z"
}POST /api/checkins
请求:
{
"routeId": "route_001",
"poiId": "poi_002",
"walletAddress": "0x742d35Cc6634C0532925a3b8D6B3981d6F2F4a5a",
"signature": "0x1234567890abcdef...",
"message": "ArrowTower Checkin: poi=poi_002, nonce=xyz123...",
"location": {
"latitude": 30.123567,
"longitude": 103.456890,
"accuracy": 12.5,
"timestamp": "2025-10-01T12:30:00Z"
},
"taskData": {
"type": "quiz",
"answer": "A",
"photoUrl": "/uploads/checkin_123_photo.jpg" // 可选,拍照任务时提供
},
"deviceInfo": {
"fingerprint": "device_fp_123",
"userAgent": "Mozilla/5.0..."
}
}响应:
{
"success": true,
"data": {
"checkinId": "checkin_123",
"status": "approved", // pending, approved, rejected, flagged
"poi": {
"id": "poi_002",
"name": "传统民居",
"order": 2
},
"routeProgress": {
"completed": 2,
"total": 3,
"nextPOI": "poi_003",
"isRouteCompleted": false
},
"nftStatus": {
"willMint": false, // 本次打卡不会触发NFT铸造
"remainingPOIs": 1
},
"timestamp": "2025-10-01T12:30:05Z"
},
"timestamp": "2025-10-01T12:30:05Z"
}POST /api/upload/photo
请求 (multipart/form-data):
{
file: File; // 图片文件
checkinId: string; // 打卡记录ID
poiId: string; // POI ID
}响应:
{
"success": true,
"data": {
"url": "/uploads/checkin_123_photo.jpg",
"filename": "checkin_123_photo.jpg",
"size": 2048576,
"mimeType": "image/jpeg"
},
"timestamp": "2025-10-01T12:29:50Z"
}GET /api/checkins
查询参数:
{
routeId?: string;
poiId?: string;
status?: string;
page?: number;
limit?: number;
}响应:
{
"success": true,
"data": {
"checkins": [
{
"id": "checkin_123",
"poi": {
"id": "poi_002",
"name": "传统民居",
"order": 2
},
"route": {
"id": "route_001",
"name": "文化探索路线"
},
"status": "approved",
"taskData": {
"type": "quiz",
"answer": "A",
"correct": true
},
"location": {
"latitude": 30.123567,
"longitude": 103.456890,
"distance": 15.2
},
"createdAt": "2025-10-01T12:30:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 45,
"pages": 3
}
},
"timestamp": "2025-10-01T12:35:00Z"
}GET /api/nfts
查询参数:
{
walletAddress: string;
page?: number;
limit?: number;
}响应:
{
"success": true,
"data": {
"nfts": [
{
"id": "nft_001",
"tokenId": "1",
"name": "箭塔村探索者 #1",
"description": "完成文化探索路线的数字纪念",
"image": "/api/metadata/nft_001/image",
"metadata": "/api/metadata/nft_001",
"route": {
"id": "route_001",
"name": "文化探索路线"
},
"chain": "polkadot-revm",
"txHash": "0x789abcdef0123456789...",
"mintedAt": "2025-10-01T12:35:00Z",
"attributes": [
{
"trait_type": "路线名称",
"value": "文化探索路线"
},
{
"trait_type": "完成时间",
"value": "2025-10-01"
},
{
"trait_type": "POI数量",
"value": "3"
}
]
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 5,
"pages": 1
}
},
"timestamp": "2025-10-01T12:40:00Z"
}GET /api/metadata/{nftId}
响应:
{
"name": "箭塔村探索者 #1",
"description": "成功完成箭塔村文化探索路线的数字纪念",
"image": "https://api.arrowtower.app/api/metadata/nft_001/image",
"external_url": "https://arrowtower.app/achievements/nft_001",
"attributes": [
{
"trait_type": "路线名称",
"value": "文化探索路线"
},
{
"trait_type": "完成时间",
"value": "2025-10-01"
},
{
"trait_type": "POI数量",
"value": "3"
},
{
"trait_type": "难度等级",
"value": "中等"
},
{
"trait_type": "探索评分",
"value": "95"
}
],
"background_color": "4A90E2"
}GET /api/vouchers/{voucherId}
响应:
{
"success": true,
"data": {
"id": "voucher_123",
"route": {
"id": "route_001",
"name": "文化探索路线"
},
"status": "minted", // pending, minting, minted, failed
"nft": {
"tokenId": "1",
"txHash": "0x789abcdef0123456789...",
"mintedAt": "2025-10-01T12:35:00Z"
},
"createdAt": "2025-10-01T12:30:05Z",
"updatedAt": "2025-10-01T12:35:00Z"
},
"timestamp": "2025-10-01T12:45:00Z"
}GET /api/users/{walletAddress}/stats
响应:
{
"success": true,
"data": {
"user": {
"walletAddress": "0x742d35Cc6634C0532925a3b8D6B3981d6F2F4a5a",
"nickname": "探索者",
"joinedAt": "2025-10-01T10:00:00Z"
},
"stats": {
"totalCheckins": 12,
"completedRoutes": 4,
"totalNFTs": 4,
"explorationScore": 85,
"rank": "资深探索者"
},
"recentActivity": [
{
"type": "nft_minted",
"routeName": "文化探索路线",
"timestamp": "2025-10-01T12:35:00Z"
},
{
"type": "checkin",
"poiName": "传统民居",
"timestamp": "2025-10-01T12:30:00Z"
}
]
},
"timestamp": "2025-10-01T12:50:00Z"
}GET /api/admin/checkins/pending
请求头:
Authorization: Bearer {admin_token}响应:
{
"success": true,
"data": {
"checkins": [
{
"id": "checkin_456",
"user": {
"walletAddress": "0x123...",
"previousCheckins": 5
},
"poi": {
"name": "古箭塔",
"expectedLocation": {
"latitude": 30.123456,
"longitude": 103.456789
}
},
"submittedLocation": {
"latitude": 30.123467,
"longitude": 103.456800,
"distance": 15.8,
"accuracy": 25.0
},
"taskData": {
"type": "photo",
"photoUrl": "/uploads/checkin_456_photo.jpg"
},
"flaggedReasons": ["location_discrepancy"],
"submittedAt": "2025-10-01T11:30:00Z"
}
]
},
"timestamp": "2025-10-01T13:00:00Z"
}POST /api/admin/checkins/{checkinId}/review
请求:
{
"action": "approve", // approve, reject
"reason": "位置验证通过,照片符合要求",
"adminNotes": "用户历史记录良好"
}响应:
{
"success": true,
"data": {
"checkinId": "checkin_456",
"newStatus": "approved",
"routeCompleted": true,
"nftVoucherCreated": true
},
"timestamp": "2025-10-01T13:05:00Z"
}错误响应格式:
{
"success": false,
"error": {
"code": "LOCATION_OUT_OF_RANGE",
"message": "您距离目标点太远,请靠近POI后重试",
"details": {
"requiredRadius": 50,
"actualDistance": 125.6,
"poiName": "古箭塔"
}
},
"timestamp": "2025-10-01T12:25:00Z"
}常见错误码:
INVALID_SIGNATURE: 签名验证失败LOCATION_OUT_OF_RANGE: 位置超出有效范围POI_ALREADY_COMPLETED: POI已完成打卡ROUTE_NOT_ACTIVE: 路线未激活WALLET_NOT_CONNECTED: 钱包未连接INSUFFICIENT_PERMISSIONS: 权限不足
// 用户表
model User {
id String @id @default(cuid())
walletAddress String @unique
walletType String @default("evm")
nickname String?
avatar String?
totalRoutes Int @default(0)
createdAt DateTime @default(now())
checkins Checkin[]
vouchers Voucher[]
}
// 路线表
model Route {
id String @id @default(cuid())
name String
description String?
coverImage String?
difficulty String @default("medium")
estimatedTime Int
poiCount Int @default(3)
nftCollection String?
isActive Boolean @default(true)
pois POI[]
checkins Checkin[]
vouchers Voucher[]
}
// 兴趣点表
model POI {
id String @id @default(cuid())
routeId String
name String
description String?
latitude Float
longitude Float
radius Int @default(50)
taskType String @default("photo")
taskContent Json?
order Int
route Route @relation(fields: [routeId], references: [id])
checkins Checkin[]
}
// 打卡记录表
model Checkin {
id String @id @default(cuid())
userId String
routeId String
poiId String
signature String
message String
taskData Json?
status String @default("pending")
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
route Route @relation(fields: [routeId], references: [id])
poi POI @relation(fields: [poiId], references: [id])
}
// NFT凭证表
model Voucher {
id String @id @default(cuid())
userId String
routeId String
status String @default("pending")
nftTokenId String?
mintTxHash String?
metadata Json?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
route Route @relation(fields: [routeId], references: [id])
}arrowtower-dapp/
├─ package.json
├─ next.config.js
├─ tsconfig.json
├─ tailwind.config.ts
├─ .env.local
├─ .env.example
├─ README.md
│
├─ prisma/
│ ├─ schema.prisma
│ ├─ migrations/
│ └─ seed.ts
│
├─ contracts/
│ ├─ ArrowTowerNFT.sol
│ └─ hardhat.config.ts
│
├─ scripts/
│ ├─ deploy.ts
│ ├─ mint-worker.ts
│ ├─ metadata-generator.ts
│ └─ faucet-helper.ts
│
├─ public/
│ ├─ uploads/ # 用户上传文件
│ ├─ metadata/ # NFT 元数据文件
│ ├─ assets/
│ │ ├─ maps/ # SVG 地图资源
│ │ ├─ pois/ # POI 图标资源
│ │ └─ nfts/ # NFT 样例图片
│ └─ favicon.ico
│
└─ src/
├─ app/
│ ├─ layout.tsx
│ ├─ page.tsx
│ ├─ providers.tsx
│ ├─ api/
│ │ ├─ auth/
│ │ ├─ checkin/
│ │ ├─ upload/
│ │ ├─ voucher/
│ │ ├─ metadata/
│ │ └─ admin/
│ ├─ routes/
│ ├─ checkin/
│ ├─ achievements/
│ ├─ profile/
│ └─ admin/
│
├─ components/
│ ├─ ui/
│ ├─ providers/
│ ├─ map/
│ ├─ checkin/
│ ├─ wallet/
│ ├─ nft/
│ └─ admin/
│
├─ hooks/
│ ├─ useWalletSignature.ts
│ ├─ useMintWorker.ts
│ ├─ useGeolocation.ts
│ ├─ useCheckin.ts
│ └─ useNFTs.ts
│
├─ lib/
│ ├─ db.ts
│ ├─ web3/
│ │ ├─ wagmi-config.ts
│ │ ├─ polkadot-client.ts
│ │ └─ signature-verify.ts
│ ├─ geolocation.ts
│ └─ storage.ts # 本地存储管理
│
├─ types/
│ ├─ api.ts
│ ├─ models.ts
│ ├─ web3.ts
│ └─ nft.ts
│
├─ utils/
│ ├─ distance-calculator.ts
│ ├─ qr-generator.ts
│ └─ metadata-builder.ts
│
└─ styles/
└─ globals.css
ArrowTower dApp v1.3 针对Web3新手访客进行了深度优化,通过简化的3-4次打卡获得NFT的规则设计,大幅降低了用户参与门槛。页面设计充分考虑了移动端使用场景和渐进式学习曲线,确保即使完全没有区块链知识的用户也能顺利完成整个探索流程。
核心优势:
- 极简用户体验:从连接到获得NFT仅需4个步骤
- 零Gas费门槛:用户无需了解加密货币概念
- 真实价值创造:链上永久记录线下体验
- 框架化设计:支持快速扩展到其他应用场景
- 渐进式教育:在实践中学习Web3基础知识