Skip to content

Latest commit

 

History

History
1228 lines (1093 loc) · 34.9 KB

File metadata and controls

1228 lines (1093 loc) · 34.9 KB

ArrowTower dApp 项目方案书 — v1.3

项目名称: ArrowTower dApp
版本: v1.3beta
日期: 2025年10月
目标: Dot Your Future: Create, Connect, Transform — Hackathon / MVP 与工程化交付参考


一、项目总览

1.1 项目背景

箭塔村乡村旅游资源

箭塔村位于四川省成都市蒲江县,距成都市区约90公里,与雅安市名山区仅一山之隔。村落隐匿在起伏的浅丘和葱茏的林盘中,呈现出典型的川西田园风光。四周散布着青绿的茶园和挂满累累果实的猕猴桃园,村边竹林掩映,清澈溪流缓缓流淌,保持着纯正浓郁的川西田园风情。村内拥有古箭塔、生态果园、传统民居等丰富的文化和自然旅游资源,具有较大的数字化升级潜力。

1.2 项目简介

ArrowTower dApp 是一款面向Web3 新手访客的多场景签到打卡平台,首期落地箭塔村乡村旅游场景。通过"线下探索 + 链上激励"的创新模式,为用户提供零门槛的 Web3 体验。

1.3 核心价值主张

  • 零 Gas 费体验:用户无需了解加密货币即可获得 NFT
  • 多场景适用:框架化设计支持文旅、会展、教育等多种场景
  • 真实价值证明:链上记录线下体验,创造永久数字纪念品
  • 渐进式 Web3 教育:通过实践引导用户了解钱包、签名、NFT 等概念

1.4 核心业务流程

用户旅程:
1. 连接钱包 → 2. 选择路线 → 3. 探索打卡(3-4个POI) → 4. 获得NFT奖励

二、用户画像与体验设计

2.1 目标用户:Web3 新手访客

用户特征:

  • 对区块链技术好奇但缺乏深入了解
  • 拥有智能手机,熟悉基础 App 操作
  • 可能从未使用过加密货币钱包
  • 主要动机:获得独特体验和数字纪念品

用户需求:

  • ✅ 简单直观的操作流程
  • ✅ 清晰的步骤指引和状态反馈
  • ✅ 无需预先学习区块链知识
  • ✅ 有形的奖励和成就感

2.2 核心使用流程

2.2.1 首次使用流程

1. 访问 dApp
2. 浏览可用路线
3. 连接钱包(简单教程引导)
4. 开始第一条路线探索
5. 完成3-4个POI打卡
6. 自动获得路线完成NFT
7. 在成就页面查看NFT收藏

2.2.2 打卡规则设计

  • 每条路线包含 3-4 个 POI(兴趣点)
  • 完成所有 POI 后自动铸造 1 枚路线纪念 NFT
  • 支持断点续玩:用户可多次访问完成路线
  • 防作弊机制:地理位置验证 + 任务完成证明

2.3 页面设计与用户体验

2.3.1 设计系统

设计原则:

  • 新手友好:所有操作都有明确引导
  • 移动优先:充分考虑户外使用场景
  • 状态透明:清晰展示进度和下一步
  • 即时反馈:每个操作都有视觉反馈

视觉风格:

色彩体系:
- 主色:#4A90E2 (科技蓝)
- 辅助色:#34C759 (生态绿) 
- 强调色:#FF9500 (活力橙)
- 中性色:F2F2F7, 8E8E93, 1C1C1E

字体系统:
- 主字体:Inter (现代易读)
- 代码字体:SF Mono (技术感)

2.3.2 首页 / 路线大厅

设计目标: 吸引用户开始探索,展示可用路线

布局:

┌─────────────────────────────────────┐
│ 导航栏 [Logo] [连接钱包]            │
├─────────────────────────────────────┤
│ 欢迎横幅                            │
│ "探索箭塔村,收获数字纪念品"        │
│ [立即开始]                          │
├─────────────────────────────────────┤
│ 我的进度 (登录后显示)                │
│ ┌─────────────────────────────────┐ │
│ │ 🔄 进行中:文化探索路线 (2/4)   │ │
│ │ ⏸️  未开始:生态果园路线        │ │
│ │ ✅ 已完成:美食体验路线         │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│ 推荐路线                            │
│ ┌─────┐ ┌─────┐ ┌─────┐            │
│ │文化 │ │生态 │ │美食 │            │
│ │探索 │ │果园 │ │体验 │            │
│ │路线 │ │路线 │ │路线 │            │
│ │3个点│ │4个点│ │3个点│            │
│ │    │ │    │ │    │            │
│ └─────┘ └─────┘ └─────┘            │
└─────────────────────────────────────┘

路线卡片设计:

┌─────────────────┐
│   [路线封面图]   │
├─────────────────┤
│ 文化探索路线     │
│ ★★★☆☆ 中等难度  │
│ ⏱ 预计60分钟     │
│ 🎯 3个打卡点     │
│ 🎁 完成得NFT     │
│ [开始探索]       │
└─────────────────┘

2.3.3 路线详情页

设计目标: 展示路线全貌,引导用户开始探索

布局:

┌─────────────────────────────────────┐
│ 导航栏 [返回] [路线名称] [分享]      │
├─────────────────────────────────────┤
│ 路线封面 + 基本信息                 │
│ "文化探索路线" ★★★☆☆              │
│ 探索箭塔村的历史文化宝藏            │
│ ⏱ 60分钟 · 🎯 3个点 · 🎁 NFT奖励  │
├─────────────────────────────────────┤
│ 交互式地图                          │
│ ┌─────────────────────────────────┐ │
│ │  ●1    ●2    ●3                │ │
│ │  古箭塔 传统民居 观景台         │ │
│ │  ────────────────→             │ │
│ │  路线轨迹连接所有POI            │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│ 打卡点列表                          │
│ ✅ 1. 古箭塔 (已完成)              │
│    📍 明代古建筑 · 📷 拍照任务    │
│    
│ 🔄 2. 传统民居 (进行中)            │
│    📍 川西特色 · ❓ 知识问答      │
│    [立即打卡]                      │
│    
│ ⏸️  3. 观景台 (未开始)             │
│    📍 全景视野 · 📝 文字记录      │
│    
│ 🎁 完成奖励:箭塔村探索者NFT       │
└─────────────────────────────────────┘

2.3.4 打卡页面

设计目标: 简化打卡流程,确保任务完成质量

状态流程:

页面状态1:位置验证
┌─────────────────────────────────────┐
│ 古箭塔打卡                           │
├─────────────────────────────────────┤
│ 📍 位置检测中...                    │
│ 请确保已开启定位权限                │
│                                     │
│ ✅ 位置验证成功!                   │
│ 距离目标:15米 ✓                    │
├─────────────────────────────────────┤
│ [下一步:完成任务]                   │
└─────────────────────────────────────┘

页面状态2:任务完成  
┌─────────────────────────────────────┐
│ 任务:拍摄古箭塔照片                 │
├─────────────────────────────────────┤
│ 拍照区域                            │
│ ┌─────────────────────────────────┐ │
│ │        [点击拍照]               │ │
│ │  或从相册选择照片                │ │
│ │                                 │ │
│ │  📷 [拍照]  🖼️ [相册]          │ │
│ └─────────────────────────────────┘ │
│ 拍照提示:请确保箭塔在画面中         │
├─────────────────────────────────────┤
│ [下一步:确认提交]                   │
└─────────────────────────────────────┘

页面状态3:钱包签名
┌─────────────────────────────────────┐
│ 确认打卡信息                         │
├─────────────────────────────────────┤
│ 打卡点:古箭塔                       │
│ 时间:2025-10-01 14:30              │
│ 任务:照片打卡 ✓                     │
│                                     │
│ 🔒 需要钱包签名确认                  │
│ 这是您的身份证明,无需支付费用       │
│                                     │
│ [连接钱包并签名]                     │
└─────────────────────────────────────┘

页面状态4:提交成功
┌─────────────────────────────────────┐
│ ✅ 打卡成功!                        │
├─────────────────────────────────────┤
│ 古箭塔 · 照片打卡                    │
│ 2025-10-01 14:30                    │
│                                     │
│ 进度:2/3 完成                       │
│ 再完成1个点即可获得NFT!             │
│                                     │
│ [查看路线] [继续探索]                │
└─────────────────────────────────────┘

2.3.5 成就展示页

设计目标: 展示用户成就,激励继续探索

布局:

┌─────────────────────────────────────┐
│ 导航栏 [我的成就] [筛选]            │
├─────────────────────────────────────┤
│ 用户概览                            │
│ ┌─────────────────────────────────┐ │
│ │        [用户头像]               │ │
│ │      探索者称号                 │ │
│ │ 已完成 2 条路线 · 获得 2 个NFT  │ │
│ │ [分享成就]                      │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│ NFT收藏馆                           │
│ ┌─────┐ ┌─────┐ ┌─────┐            │
│ │NFT1 │ │NFT2 │ │  +  │            │
│ │文化 │ │生态 │ │继续 │            │
│ │探索 │ │果园 │ │探索 │            │
│ │     │ │     │ │     │            │
│ └─────┘ └─────┘ └─────┘            │
├─────────────────────────────────────┤
│ 路线成就                            │
│ ✅ 文化探索路线 · 2025.10.01       │
│ ✅ 生态果园路线 · 2025.10.02       │
│ ⏳ 美食体验路线 · 进行中 (1/3)     │
└─────────────────────────────────────┘

NFT卡片设计:

┌─────────────────┐
│   [NFT图片]     │
│                 │
├─────────────────┤
│ 箭塔村探索者 #1  │
│ 文化探索路线     │
│ 2025.10.01      │
│ 🔍 查看详情      │
└─────────────────┘

2.3.6 钱包连接引导

设计目标: 帮助Web3新手顺利完成钱包连接

流程设计:

步骤1:介绍钱包概念
┌─────────────────────────────────────┐
│ 连接数字钱包                         │
├─────────────────────────────────────┤
│ 钱包是您在Web3世界的身份证          │
│                                     │
│ ✅ 证明您的身份                     │
│ ✅ 安全保存数字资产                 │
│ ✅ 无需重复注册账号                 │
│                                     │
│ [了解更多] [开始连接]               │
└─────────────────────────────────────┘

步骤2:选择钱包类型
┌─────────────────────────────────────┐
│ 选择钱包类型                         │
├─────────────────────────────────────┤
│ 推荐选择:                          │
│ ┌─────────────────────────────────┐ │
│ │ MetaMask                        │ │
│ │ 最流行的Web3钱包                 │ │
│ │ [选择]                          │ │
│ └─────────────────────────────────┘ │
│                                     │
│ 其他选项:                          │
│ · Trust Wallet · Rainbow · ...     │
└─────────────────────────────────────┘

步骤3:安装指引(如未检测到钱包)
┌─────────────────────────────────────┐
│ 安装MetaMask                         │
├─────────────────────────────────────┤
│ 1. 在应用商店搜索"MetaMask"         │
│ 2. 下载并创建钱包                    │
│ 3. 妥善保管助记词                   │
│ 4. 返回此页面重新连接               │
│                                     │
│ [我已安装] [需要帮助]               │
└─────────────────────────────────────┘

三、技术架构与实现

3.1 系统架构总览

前端层 (Next.js 15 + React 19)
    ↓
API层 (Next.js API Routes + JSON-RPC)
    ↓
业务层 (打卡逻辑 + 签名验证 + NFT铸造)
    ↓
数据层 (PostgreSQL + 本地文件存储)
    ↓
区块链层 (Polkadot REVM + 智能合约)

3.2 核心技术栈

前端技术栈:

  • 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开发框架

四、业务逻辑与规则

4.1 打卡规则设计

POI(兴趣点)配置:

  • 每条路线包含 3-4个POI
  • 每个POI设置有效打卡半径(50-100米)
  • 支持多种任务类型:拍照、问答、文字记录
  • 任务难度逐步递增,引导深度探索

完成条件:

  • 必须按顺序完成所有POI打卡
  • 每个POI需完成指定任务并通过验证
  • 支持断点续玩,进度自动保存
  • 完成所有POI后自动触发NFT铸造

4.2 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": "普通"}
  ]
}

五、API接口设计

5.1 接口规范

基础信息:

  • 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;
}

5.2 认证接口

5.2.1 获取签名Nonce

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"
}

5.2.2 验证签名

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"
}

5.3 路线接口

5.3.1 获取路线列表

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"
}

5.3.2 获取路线详情

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"
}

5.4 打卡接口

5.4.1 提交打卡

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"
}

5.4.2 上传打卡照片

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"
}

5.4.3 获取打卡记录

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"
}

5.5 NFT相关接口

5.5.1 获取用户NFT

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"
}

5.5.2 获取NFT元数据

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"
}

5.5.3 获取铸造状态

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"
}

5.6 用户数据接口

5.6.1 获取用户统计

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"
}

5.7 管理接口

5.7.1 获取待审核打卡

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"
}

5.7.2 审核打卡

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"
}

5.8 错误处理

错误响应格式:

{
  "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: 权限不足

六、数据模型设计

6.1 核心数据表

// 用户表
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的规则设计,大幅降低了用户参与门槛。页面设计充分考虑了移动端使用场景和渐进式学习曲线,确保即使完全没有区块链知识的用户也能顺利完成整个探索流程。

核心优势:

  1. 极简用户体验:从连接到获得NFT仅需4个步骤
  2. 零Gas费门槛:用户无需了解加密货币概念
  3. 真实价值创造:链上永久记录线下体验
  4. 框架化设计:支持快速扩展到其他应用场景
  5. 渐进式教育:在实践中学习Web3基础知识