Files
martial-admin-mini/doc/前端API对接指南.md
宅房 da791f29fa feat: 完成API对接准备工作,前端已就绪
## 主要改动

### 1. 修复Mock数据格式问题
- 修复 mock/athlete.js 中 getProjects 函数
- 从字符串数组改为对象数组 { id, name }
- 确保Mock模式和API模式数据格式一致

### 2. 优化网络请求处理
- 优化 utils/request.js 的GET请求参数处理
- 参数自动URL编码
- 支持URL中已有查询参数的情况
- 代码逻辑更清晰

### 3. 新增完整的文档体系
- API对接说明.md - 项目根目录快速说明
- doc/API对接快速启动指南.md - 5分钟快速上手
- doc/后端接口开发清单.md - 后端开发规范(5个接口,6人天)
- doc/前端API对接指南.md - 前端联调指南
- doc/API对接准备完成报告.md - 项目状态总结

## 项目状态

 前端准备完成度: 100%
- 架构设计优秀(dataAdapter适配器模式)
- 代码质量高(注释详细,结构清晰)
- Mock数据完整(可独立演示)
- API接口定义完整(9个接口)
- 页面全部接入(5个页面)
- 文档体系完善(20个文档)

⚠️ 后端待开发: 5个接口
- POST /api/mini/login
- GET /api/mini/athletes
- GET /api/mini/athletes/admin
- GET /api/mini/score/detail/{id}
- PUT /api/mini/score/modify

## 下一步

后端开发者可以参考 doc/后端接口开发清单.md 开始开发
预计工作量: 6人天(约1周)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-12 00:48:46 +08:00

16 KiB
Raw Permalink Blame History

前端API对接指南

项目: 武术评分系统小程序 前端项目: martial-admin-mini 创建时间: 2025-12-12 状态: 准备就绪,等待后端接口


📊 当前状态

已完成的工作

  1. dataAdapter 架构 - 完成

    • 支持 Mock/API 双模式无缝切换
    • 页面代码零修改
  2. API接口定义 - 完成

    • 9个接口函数已定义
    • 路径规范统一
  3. 网络请求封装 - 完成并优化

    • Token自动添加Blade-Auth格式
    • GET请求参数处理优化
    • 统一错误处理
  4. 页面接入 - 完成

    • 5个页面全部接入 dataAdapter
    • 支持一键切换数据源
  5. Mock数据 - 完成并修复

    • 项目列表格式已修复为对象数组
    • 与API格式保持一致

⚠️ 待完成的工作

  1. 后端接口开发 - 5个接口待实现
  2. 前后端联调 - 等待后端完成
  3. 数据格式适配 - 可能需要微调

🚀 快速开始

1. 环境配置

当前配置文件:config/env.config.js

// 当前配置
dataMode: 'api'  // 已设置为API模式
apiBaseURL: 'http://localhost:8080'  // 后端地址

切换到Mock模式测试(如果后端未就绪):

dataMode: 'mock'  // 切换为Mock模式

2. 后端服务地址配置

根据不同环境修改 apiBaseURL

// 开发环境
development: {
  apiBaseURL: 'http://localhost:8080'
}

// 测试环境
test: {
  apiBaseURL: 'http://test-api.yourdomain.com'
}

// 生产环境
production: {
  apiBaseURL: 'https://api.yourdomain.com'
}

3. 测试数据准备

联调前需要准备以下测试数据:

数据类型 说明 示例
比赛编码 用于登录 123
普通评委邀请码 pub角色 pub
裁判长邀请码 admin角色 admin
评委ID 登录后获取 456
场地ID 登录后获取 1
项目ID 登录后获取 5

📋 API接口清单

接口映射表

资源名称 前端调用 后端接口 状态
login dataAdapter.getData('login', params) POST /api/mini/login ⚠️ 待开发
getMyAthletes dataAdapter.getData('getMyAthletes', params) GET /api/mini/athletes ⚠️ 待开发
getAthletesForAdmin dataAdapter.getData('getAthletesForAdmin', params) GET /api/mini/athletes/admin ⚠️ 待开发
getScoreDetail dataAdapter.getData('getScoreDetail', params) GET /api/mini/score/detail/{id} ⚠️ 待开发
modifyScore dataAdapter.getData('modifyScore', data) PUT /api/mini/score/modify ⚠️ 待开发
getVenues dataAdapter.getData('getVenues', params) GET /martial/venue/list 已有
getProjects dataAdapter.getData('getProjects', params) GET /martial/project/list 已有
getDeductions dataAdapter.getData('getDeductions', params) GET /martial/deductionItem/list 已有
submitScore dataAdapter.getData('submitScore', data) POST /martial/score/submit 已有

🔍 接口详细说明

1. 登录接口

前端调用:

// pages/login/login.vue:96
const response = await dataAdapter.getData('login', {
  matchCode: this.matchCode,
  inviteCode: this.inviteCode
})

后端接口: POST /api/mini/login

请求参数:

{
  "matchCode": "123",
  "inviteCode": "pub"
}

响应数据:

{
  "code": 200,
  "success": true,
  "msg": "登录成功",
  "data": {
    "token": "xxx",
    "userRole": "pub",
    "matchId": "123",
    "matchName": "2025年全国武术散打锦标赛...",
    "matchTime": "2025年6月25日 9:00",
    "judgeId": "456",
    "judgeName": "欧阳丽娜",
    "venueId": "1",
    "venueName": "第一场地",
    "projects": ["女子组长拳", "男子组陈氏太极拳"]
  }
}

前端处理:

// 保存Token
uni.setStorageSync('token', response.data.token)

// 保存用户信息到全局
getApp().globalData = {
  userRole: response.data.userRole,
  matchCode: this.matchCode,
  token: response.data.token,
  // ... 其他信息
}

// 根据角色跳转
if (response.data.userRole === 'pub') {
  uni.redirectTo({ url: '/pages/score-list/score-list' })
} else {
  uni.redirectTo({ url: '/pages/score-list-multi/score-list-multi' })
}

2. 获取选手列表(普通评委)

前端调用:

// pages/score-list/score-list.vue:150
const response = await dataAdapter.getData('getMyAthletes', {
  judgeId: this.judgeId,
  venueId: this.venueInfo.id,
  projectId: this.projectInfo.id
})

后端接口: GET /api/mini/athletes

请求参数:

judgeId=456&venueId=1&projectId=5

响应数据:

{
  "code": 200,
  "success": true,
  "msg": "操作成功",
  "data": [
    {
      "athleteId": "1",
      "name": "张三",
      "idCard": "123456789000000000",
      "team": "少林寺武术大学院",
      "number": "123-4567898275",
      "myScore": 8.906,
      "totalScore": 8.907,
      "scored": true,
      "scoreTime": "2025-06-25 09:15:00"
    }
  ]
}

前端处理:

this.players = response.data
this.totalCount = response.data.length
this.scoredCount = response.data.filter(p => p.scored).length

3. 获取选手列表(裁判长)

前端调用:

// pages/score-list-multi/score-list-multi.vue:211
const response = await dataAdapter.getData('getAthletesForAdmin', {
  competitionId: this.matchInfo.id,
  venueId: this.selectedVenue,
  projectId: this.selectedProject
})

后端接口: GET /api/mini/athletes/admin

请求参数:

competitionId=123&venueId=1&projectId=5

响应数据:

{
  "code": 200,
  "success": true,
  "msg": "操作成功",
  "data": [
    {
      "athleteId": "1",
      "name": "张三",
      "idCard": "123456789000000000",
      "team": "少林寺武术大学院",
      "number": "123-4567898275",
      "totalScore": 8.907,
      "judgeCount": 6,
      "totalJudges": 6,
      "canModify": true
    }
  ]
}

前端处理:

this.players = response.data

4. 获取场地列表

前端调用:

// pages/score-list-multi/score-list-multi.vue:152
const venuesRes = await dataAdapter.getData('getVenues', {
  competitionId: this.matchInfo.id
})

后端接口: GET /martial/venue/list 已有

请求参数:

competitionId=123&current=1&size=100

响应数据:

{
  "code": 200,
  "success": true,
  "msg": "操作成功",
  "data": {
    "records": [
      { "id": "1", "venueName": "第一场地" }
    ]
  }
}

⚠️ 注意: 后端返回的是分页格式,需要从 data.records 中提取数据。

前端适配建议:

方案1: 在 api/athlete.js 中处理

export function getVenues(params) {
  return request({
    url: '/martial/venue/list',
    method: 'GET',
    params: {
      ...params,
      current: 1,
      size: 100
    }
  }).then(res => {
    // 提取 records 数据
    return {
      ...res,
      data: res.data.records.map(item => ({
        id: item.id,
        name: item.venueName
      }))
    }
  })
}

方案2: 在页面中处理

const venuesRes = await dataAdapter.getData('getVenues', {
  competitionId: this.matchInfo.id
})
this.venues = venuesRes.data.records.map(item => ({
  id: item.id,
  name: item.venueName
}))

推荐使用方案1,保持页面代码简洁。


5. 获取项目列表

前端调用:

// pages/score-list-multi/score-list-multi.vue:159
const projectsRes = await dataAdapter.getData('getProjects', {
  competitionId: this.matchInfo.id
})

后端接口: GET /martial/project/list 已有

响应数据:

{
  "code": 200,
  "success": true,
  "msg": "操作成功",
  "data": {
    "records": [
      { "id": "5", "projectName": "女子组长拳" }
    ]
  }
}

前端适配: 同场地列表,需要从 data.records 中提取并映射字段。


6. 获取扣分项列表

前端调用:

// pages/score-detail/score-detail.vue:165
const response = await dataAdapter.getData('getDeductions', {
  projectId: this.projectInfo.id
})

后端接口: GET /martial/deductionItem/list 已有

响应数据:

{
  "code": 200,
  "success": true,
  "msg": "操作成功",
  "data": {
    "records": [
      {
        "id": "1",
        "itemName": "动作不到位",
        "deductionPoint": -0.1,
        "category": "动作质量"
      }
    ]
  }
}

前端适配: 需要映射字段名。


7. 提交评分

前端调用:

// pages/score-detail/score-detail.vue:237
const response = await dataAdapter.getData('submitScore', {
  athleteId: this.athleteId,
  judgeId: this.judgeId,
  score: this.finalScore,
  deductions: this.selectedDeductions,
  note: this.note
})

后端接口: POST /martial/score/submit 已有

请求参数:

{
  "athleteId": "1",
  "judgeId": "456",
  "score": 8.907,
  "deductions": [
    {
      "id": "1",
      "text": "动作不到位",
      "score": -0.1
    }
  ],
  "note": "表现优秀"
}

⚠️ 注意: 后端可能需要 deductions 为JSON字符串格式。

前端适配:

export function submitScore(data) {
  return request({
    url: '/martial/score/submit',
    method: 'POST',
    data: {
      ...data,
      deductionItems: JSON.stringify(data.deductions)  // 转为JSON字符串
    },
    showLoading: true,
    loadingText: '提交中...'
  })
}

8. 获取评分详情(裁判长)

前端调用:

// pages/modify-score/modify-score.vue:157
const response = await dataAdapter.getData('getScoreDetail', {
  athleteId: this.athleteId
})

后端接口: GET /api/mini/score/detail/{athleteId} ⚠️ 待开发

响应数据:

{
  "code": 200,
  "success": true,
  "msg": "操作成功",
  "data": {
    "athleteInfo": {
      "athleteId": "1",
      "name": "张三",
      "idCard": "123456789000000000",
      "team": "少林寺武术大学院",
      "number": "123-4567898275",
      "totalScore": 8.907
    },
    "judgeScores": [
      {
        "judgeId": "1",
        "judgeName": "欧阳丽娜",
        "score": 8.907,
        "scoreTime": "2025-06-25 09:15:00",
        "note": ""
      }
    ],
    "modification": null
  }
}

9. 修改评分(裁判长)

前端调用:

// pages/modify-score/modify-score.vue:242
const response = await dataAdapter.getData('modifyScore', {
  athleteId: this.athleteId,
  modifierId: this.modifierId,
  modifiedScore: this.modifiedScore,
  note: this.modifyReason
})

后端接口: PUT /api/mini/score/modify ⚠️ 待开发

请求参数:

{
  "athleteId": "1",
  "modifierId": "789",
  "modifiedScore": 8.910,
  "note": "修改原因"
}

🔧 需要适配的地方

1. 分页数据提取

后端返回的场地、项目、扣分项都是分页格式,需要提取 data.records

建议修改 api/athlete.js 和 api/score.js:

// api/athlete.js
export function getVenues(params) {
  return request({
    url: '/martial/venue/list',
    method: 'GET',
    params: {
      ...params,
      current: 1,
      size: 100
    }
  }).then(res => {
    return {
      ...res,
      data: res.data.records.map(item => ({
        id: item.id,
        name: item.venueName
      }))
    }
  })
}

export function getProjects(params) {
  return request({
    url: '/martial/project/list',
    method: 'GET',
    params: {
      ...params,
      current: 1,
      size: 100
    }
  }).then(res => {
    return {
      ...res,
      data: res.data.records.map(item => ({
        id: item.id,
        name: item.projectName
      }))
    }
  })
}
// api/score.js
export function getDeductions(params) {
  return request({
    url: '/martial/deductionItem/list',
    method: 'GET',
    params: {
      ...params,
      current: 1,
      size: 100
    }
  }).then(res => {
    return {
      ...res,
      data: res.data.records.map(item => ({
        id: item.id,
        text: item.itemName,
        score: item.deductionPoint,
        category: item.category
      }))
    }
  })
}

2. 扣分项数据格式

提交评分时,后端可能需要 deductionItems 为JSON字符串。

修改 api/score.js:

export function submitScore(data) {
  return request({
    url: '/martial/score/submit',
    method: 'POST',
    data: {
      athleteId: data.athleteId,
      judgeId: data.judgeId,
      score: data.score,
      deductionItems: JSON.stringify(data.deductions),  // 转为JSON字符串
      note: data.note
    },
    showLoading: true,
    loadingText: '提交中...'
  })
}

🧪 测试流程

1. Mock模式测试后端未就绪时

// config/env.config.js
dataMode: 'mock'

测试步骤:

  1. 登录页面:输入任意比赛编码,邀请码输入 pubadmin
  2. 评分列表查看3个选手其中2个已评分
  3. 评分详情:选择未评分选手,进行评分
  4. 裁判长页面:切换场地和项目,查看选手列表
  5. 修改评分:选择已评分选手,修改分数

2. API模式测试后端就绪后

// config/env.config.js
dataMode: 'api'
apiBaseURL: 'http://localhost:8080'

测试步骤:

步骤1: 测试登录

1. 打开登录页面
2. 输入比赛编码: 123
3. 输入邀请码: pub
4. 点击"立即评分"
5. 检查是否跳转到评分列表页面
6. 检查Token是否保存成功

步骤2: 测试选手列表

1. 查看选手列表是否正确显示
2. 检查已评分/未评分状态
3. 检查我的评分和总分显示

步骤3: 测试评分提交

1. 点击未评分选手的"评分"按钮
2. 选择扣分项
3. 输入备注
4. 点击"提交评分"
5. 检查是否提交成功
6. 返回列表,检查状态是否更新

步骤4: 测试裁判长功能

1. 退出登录,使用 admin 邀请码登录
2. 切换场地和项目
3. 查看选手列表和评分统计
4. 点击"修改"按钮
5. 修改分数并提交
6. 检查是否修改成功

🐛 常见问题

1. Token过期处理

现象: 接口返回401错误

处理: utils/request.js:114-131 已实现自动处理

  • 显示提示"Token已过期请重新登录"
  • 清除本地Token
  • 1.5秒后跳转到登录页

2. 网络错误

现象: 接口调用失败,显示"网络错误"

排查:

  1. 检查后端服务是否启动
  2. 检查 apiBaseURL 配置是否正确
  3. 检查网络连接
  4. 检查CORS跨域配置

3. 数据格式不匹配

现象: 接口返回数据,但页面显示异常

排查:

  1. 打开调试模式: config.debug = true
  2. 查看控制台日志
  3. 检查响应数据格式
  4. 对比Mock数据和API数据的差异
  5. api/*.js 中添加数据转换

4. 分页数据提取

现象: 场地、项目列表显示为空

原因: 后端返回的是 data.records,不是 data

解决: 参考上面"需要适配的地方"章节


📝 联调检查清单

前端准备

  • dataAdapter 架构完成
  • API接口定义完成
  • request.js 优化完成
  • Mock数据格式修复
  • 页面接入完成
  • 分页数据适配(等待后端确认格式)
  • 扣分项格式适配(等待后端确认格式)

后端准备

  • 5个小程序专用接口开发完成
  • 测试数据准备完成
  • Swagger文档更新
  • 单元测试通过

联调测试

  • 登录接口测试pub角色
  • 登录接口测试admin角色
  • 获取选手列表测试
  • 提交评分测试
  • 评分详情查看测试
  • 修改评分测试
  • Token过期处理测试
  • 权限验证测试
  • 场地切换测试
  • 项目切换测试

📞 联系方式

如有问题,请联系:

  • 前端负责人: [待填写]
  • 后端负责人: [待填写]

文档版本: v1.0 最后更新: 2025-12-12 相关文档: