摩托车油耗管理小程序后端开发文档
1. 项目概述
摩托车油耗管理小程序是一个专为摩托车用户设计的油耗记录和管理系统,帮助用户记录加油信息、计算油耗、分析燃油使用情况,并提供摩托车租赁信息查询功能。
1.1 主要功能
用户认证与授权
摩托车信息管理
油耗记录管理
油耗统计分析
摩托车租赁信息查询
2. 技术栈
2.1 后端技术
Node.js:运行环境
Express:Web 框架
Sequelize:ORM 框架
MySQL:数据库
JWT:用户认证
Zod:数据验证
Node-cache:内存缓存
Swagger:API 文档
2.2 依赖包
3. 项目结构
backend/
├── config/ # 配置文件
│ ├── database.js # 数据库配置
│ └── swagger.js # Swagger 配置
├── controllers/ # 控制器
│ ├── authController.js # 认证控制器
│ ├── fuelRecordController.js # 油耗记录控制器
│ ├── motorcycleController.js # 摩托车控制器
│ └── rentalController.js # 租赁控制器
├── middleware/ # 中间件
│ ├── auth.js # 认证中间件
│ ├── errorHandler.js # 错误处理中间件
│ └── rateLimit.js # 速率限制中间件
├── models/ # 数据模型
│ ├── FuelRecord.js # 油耗记录模型
│ ├── Motorcycle.js # 摩托车模型
│ ├── RentalMotorcycle.js # 租赁摩托车模型
│ ├── RentalShop.js # 租赁店铺模型
│ ├── User.js # 用户模型
│ └── index.js # 模型导出
├── routes/ # 路由
│ ├── auth.js # 认证路由
│ ├── fuelRecords.js # 油耗记录路由
│ ├── index.js # 路由导出
│ ├── motorcycles.js # 摩托车路由
│ └── rentals.js # 租赁路由
├── scripts/ # 脚本
│ └── init-db.js # 数据库初始化脚本
├── utils/ # 工具函数
│ ├── cache.js # 缓存工具
│ ├── fuelCalculations.js # 油耗计算工具
│ ├── response.js # 响应工具
│ └── schemas.js # 数据验证模式
├── .env # 环境变量
├── .env.example # 环境变量示例
├── app.js # 应用入口
└── package.json # 项目配置
4. API 接口说明
4.1 认证接口
4.1.1 用户认证流程
微信小程序登录:用户通过微信小程序获取 code,调用
/api/auth/login接口进行登录获取 JWT 令牌:服务器验证 code 后,返回 JWT 令牌和用户信息
使用令牌:客户端在后续请求中,在请求头中携带
Authorization: Bearer <token>进行身份验证
4.1.2 接口鉴权
需要认证的接口:所有
/api下的接口(除了/api/auth/login)都需要携带有效的 JWT 令牌管理员权限接口:摩托车的增删改操作需要管理员权限
示例请求:
# 登录获取令牌
POST /api/auth/login
Content-Type: application/json
{
"code": "微信登录 code"
}
使用令牌访问需要认证的接口
GET /api/motorcycles
Authorization: Bearer <token>
4.1.3 分页说明
大部分列表接口支持分页查询,通过以下查询参数控制:
page:页码,默认 1limit:每页数量,默认 20
示例请求:
GET /api/fuel-records?page=2&limit=10
Authorization: Bearer <token>
分页响应结构:
{
"success": true,
"message": "获取油耗记录成功",
"data": {
"records": [
// 记录列表
],
"pagination": {
"page": 2,
"limit": 10,
"total": 50,
"pages": 5
}
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.1 认证接口
4.1.1 微信登录
请求参数:
{
"code": "微信登录 code"
}
响应示例:
{
"success": true,
"message": "登录成功",
"data": {
"token": "JWT 令牌",
"user": {
"id": 1,
"openid": "用户微信 openid",
"nickname": "用户昵称",
"avatar": "用户头像 URL"
}
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.1.2 获取用户资料
响应示例:
{
"success": true,
"message": "获取用户资料成功",
"data": {
"id": 1,
"openid": "用户微信 openid",
"nickname": "用户昵称",
"avatar": "用户头像 URL"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.1.3 更新用户资料
请求参数:
{
"nickname": "新昵称",
"avatar": "新头像 URL"
}
响应示例:
{
"success": true,
"message": "更新用户资料成功",
"data": {
"id": 1,
"openid": "用户微信 openid",
"nickname": "新昵称",
"avatar": "新头像 URL"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.2 摩托车接口
4.2.1 获取摩托车列表
响应示例:
{
"success": true,
"message": "获取摩托车列表成功",
"data": [
{
"id": 1,
"userId": 1,
"brand": "本田",
"model": "CB190R",
"year": 2022,
"fuelType": "gasoline",
"plateNumber": "京A12345",
"currentMileage": 5000,
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z"
}
],
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.2.2 创建摩托车
请求参数:
{
"brand": "本田",
"model": "CB190R",
"year": 2022,
"fuelType": "gasoline",
"plateNumber": "京A12345",
"currentMileage": 0
}
响应示例:
{
"success": true,
"message": "创建摩托车成功",
"data": {
"id": 1,
"userId": 1,
"brand": "本田",
"model": "CB190R",
"year": 2022,
"fuelType": "gasoline",
"plateNumber": "京A12345",
"currentMileage": 0,
"createdAt": "2026-04-07T00:00:00.000Z",
"updatedAt": "2026-04-07T00:00:00.000Z"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.2.3 获取摩托车详情
响应示例:
{
"success": true,
"message": "获取摩托车详情成功",
"data": {
"id": 1,
"userId": 1,
"brand": "本田",
"model": "CB190R",
"year": 2022,
"fuelType": "gasoline",
"plateNumber": "京A12345",
"currentMileage": 5000,
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.2.4 更新摩托车
请求参数:
{
"brand": "本田",
"model": "CB190R",
"year": 2022,
"fuelType": "gasoline",
"plateNumber": "京A12345",
"currentMileage": 6000
}
响应示例:
{
"success": true,
"message": "更新摩托车成功",
"data": {
"id": 1,
"userId": 1,
"brand": "本田",
"model": "CB190R",
"year": 2022,
"fuelType": "gasoline",
"plateNumber": "京A12345",
"currentMileage": 6000,
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-07T00:00:00.000Z"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.2.5 删除摩托车
响应示例:
{
"success": true,
"message": "删除摩托车成功",
"data": null,
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.3 油耗记录接口
4.3.1 获取油耗记录列表
查询参数:
motorcycleId:摩托车 ID(可选)
page:页码,默认 1
limit:每页数量,默认 20
响应示例:
{
"success": true,
"message": "获取油耗记录成功",
"data": {
"records": [
{
"id": 1,
"userId": 1,
"motorcycleId": 1,
"date": "2026-04-01T00:00:00.000Z",
"mileage": 1000,
"fuelAmount": 10,
"fuelPrice": 8.5,
"totalCost": 85,
"avgConsumption": 2.5,
"station": "中石化加油站",
"notes": "",
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z",
"Motorcycle": {
"brand": "本田",
"model": "CB190R"
}
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1,
"pages": 1
}
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.3.2 创建油耗记录
请求参数:
{
"motorcycleId": 1,
"date": "2026-04-01T00:00:00.000Z",
"mileage": 1000,
"fuelAmount": 10,
"fuelPrice": 8.5,
"station": "中石化加油站",
"notes": ""
}
响应示例:
{
"success": true,
"message": "创建油耗记录成功",
"data": {
"id": 1,
"userId": 1,
"motorcycleId": 1,
"date": "2026-04-01T00:00:00.000Z",
"mileage": 1000,
"fuelAmount": 10,
"fuelPrice": 8.5,
"totalCost": 85,
"avgConsumption": 0,
"station": "中石化加油站",
"notes": "",
"createdAt": "2026-04-07T00:00:00.000Z",
"updatedAt": "2026-04-07T00:00:00.000Z"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.3.3 获取油耗统计
查询参数:
motorcycleId:摩托车 ID(可选)
startDate:开始日期(可选)
endDate:结束日期(可选)
响应示例:
{
"success": true,
"message": "获取油耗统计成功",
"data": {
"byMotorcycle": [
{
"motorcycleId": 1,
"recordCount": 5,
"totalFuelAmount": 50,
"totalCost": 425,
"avgConsumption": 2.5,
"Motorcycle": {
"brand": "本田",
"model": "CB190R"
}
}
],
"overall": {
"totalRecords": 5,
"totalFuel": 50,
"totalCost": 425,
"overallAvgConsumption": 2.5
},
"byMonth": [
{
"month": "2026-04",
"recordCount": 5,
"totalFuelAmount": 50,
"totalCost": 425,
"avgConsumption": 2.5
}
],
"byStation": [
{
"station": "中石化加油站",
"recordCount": 3,
"totalFuelAmount": 30,
"totalCost": 255,
"avgFuelPrice": 8.5
}
],
"consumptionTrend": [
{
"month": "2026-04",
"avgConsumption": 2.5
}
]
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.3.4 获取油耗记录详情
响应示例:
{
"success": true,
"message": "获取油耗记录详情成功",
"data": {
"id": 1,
"userId": 1,
"motorcycleId": 1,
"date": "2026-04-01T00:00:00.000Z",
"mileage": 1000,
"fuelAmount": 10,
"fuelPrice": 8.5,
"totalCost": 85,
"avgConsumption": 2.5,
"station": "中石化加油站",
"notes": "",
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z",
"Motorcycle": {
"brand": "本田",
"model": "CB190R"
}
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.3.5 更新油耗记录
请求参数:
{
"date": "2026-04-01T00:00:00.000Z",
"mileage": 1100,
"fuelAmount": 10,
"fuelPrice": 8.5,
"station": "中石化加油站",
"notes": ""
}
响应示例:
{
"success": true,
"message": "更新油耗记录成功",
"data": {
"id": 1,
"userId": 1,
"motorcycleId": 1,
"date": "2026-04-01T00:00:00.000Z",
"mileage": 1100,
"fuelAmount": 10,
"fuelPrice": 8.5,
"totalCost": 85,
"avgConsumption": 2.2,
"station": "中石化加油站",
"notes": "",
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-07T00:00:00.000Z"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.3.6 删除油耗记录
响应示例:
{
"success": true,
"message": "删除成功",
"data": null,
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.4 租赁接口
4.4.1 获取租赁店铺列表
响应示例:
{
"success": true,
"message": "获取租赁店铺列表成功",
"data": [
{
"id": 1,
"name": "摩托租赁店",
"address": "北京市朝阳区",
"phone": "13800138000",
"latitude": 39.9042,
"longitude": 116.4074,
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z"
}
],
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.4.2 获取租赁店铺详情
响应示例:
{
"success": true,
"message": "获取租赁店铺详情成功",
"data": {
"id": 1,
"name": "摩托租赁店",
"address": "北京市朝阳区",
"phone": "13800138000",
"latitude": 39.9042,
"longitude": 116.4074,
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.4.3 获取租赁摩托车列表
响应示例:
{
"success": true,
"message": "获取租赁摩托车列表成功",
"data": [
{
"id": 1,
"shopId": 1,
"brand": "本田",
"model": "CB190R",
"year": 2022,
"dailyPrice": 150,
"status": "available",
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z"
}
],
"timestamp": "2026-04-07T00:00:00.000Z"
}
4.4.4 获取租赁摩托车详情
响应示例:
{
"success": true,
"message": "获取租赁摩托车详情成功",
"data": {
"id": 1,
"shopId": 1,
"brand": "本田",
"model": "CB190R",
"year": 2022,
"dailyPrice": 150,
"status": "available",
"createdAt": "2026-04-01T00:00:00.000Z",
"updatedAt": "2026-04-01T00:00:00.000Z"
},
"timestamp": "2026-04-07T00:00:00.000Z"
}
5. 功能说明
5.1 用户认证
基于微信小程序的登录认证
使用 JWT 令牌进行身份验证
支持用户资料的获取和更新
5.2 摩托车管理
添加、编辑、删除摩托车信息
记录摩托车的基本信息和当前里程
支持按用户查询摩托车列表
5.3 油耗记录管理
记录每次加油的详细信息(日期、里程、加油量、油价等)
自动计算油耗和总费用
支持按摩托车筛选记录
支持分页查询
5.4 油耗统计分析
按摩托车统计油耗数据
按月份统计油耗数据
按加油站统计油耗数据
分析最近6个月的油耗趋势
提供总体油耗统计
5.5 摩托车租赁
查询租赁店铺信息
查询租赁摩托车信息
支持未登录用户访问
6. 部署说明
6.1 环境要求
Node.js >= 16.0.0
MySQL >= 5.7
6.2 配置步骤
克隆代码
git clone <repository-url> cd moto/backend安装依赖
npm install配置环境变量
复制
.env.example文件为.env编辑
.env文件,填写数据库连接信息和其他配置
初始化数据库
node scripts/init-db.js创建管理员用户(可选)
node scripts/create-admin.js启动服务
# 开发环境 npm run dev生产环境npm start
6.3 环境变量配置
7. 开发流程
7.1 代码风格
使用 ES6+ 语法
采用模块化设计
遵循 RESTful API 设计规范
使用驼峰命名法
7.2 开发工具
编辑器:VS Code
版本控制:Git
包管理:npm
数据库:MySQL
7.3 测试
使用 Jest 进行单元测试
使用 Supertest 进行 API 测试
7.4 文档
API 文档:访问
/api/docs开发文档:
DEVELOPMENT.md
8. 安全措施
使用 JWT 进行身份验证
实现请求速率限制,防止暴力攻击
配置安全的 CORS 策略
使用 Helmet 设置安全头部
对输入数据进行严格验证
防止 SQL 注入攻击
9. 鉴权操作详解
9.1 认证原理
系统采用基于 JWT (JSON Web Token) 的认证机制,具体流程如下:
用户登录:用户通过微信小程序获取 code,调用
/api/auth/login接口验证微信 code:服务器通过微信 API 验证 code,获取 openid
生成 JWT 令牌:服务器为用户生成包含用户 ID 和角色信息的 JWT 令牌
使用令牌:客户端在后续请求中携带令牌进行身份验证
9.2 认证中间件
9.2.1 强制认证中间件 (authenticateToken)
代码实现:
// middleware/auth.js
const jwt = require('jsonwebtoken');
const { unauthorizedResponse, errorResponse } = require('../utils/response');
const authenticateToken = (req, res, next) => {
const authHeader = req.headers[‘authorization’];
const token = authHeader && authHeader.split(’ ')[1];
if (!token) {
return unauthorizedResponse(res, ‘访问令牌缺失’);
}
// 确保 JWT_SECRET 存在
if (!process.env.JWT_SECRET) {
console.error(‘JWT_SECRET 环境变量未设置’);
return errorResponse(res, ‘服务器配置错误’);
}
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
if (err.name === ‘TokenExpiredError’) {
return unauthorizedResponse(res, ‘访问令牌已过期’);
}
if (err.name === ‘JsonWebTokenError’) {
return unauthorizedResponse(res, ‘无效的访问令牌’);
}
return unauthorizedResponse(res, ‘访问令牌验证失败’);
}
req.user = decoded;
next();
});
};
使用方式:
// routes/motorcycles.js
const { authenticateToken } = require('../middleware/auth');
const router = express.Router();
router.use(authenticateToken);
9.2.2 可选认证中间件 (optionalAuth)
代码实现:
// middleware/auth.js
const optionalAuth = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (token) {
// 确保 JWT_SECRET 存在
if (process.env.JWT_SECRET) {
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (!err) {
req.user = decoded;
}
});
}
}
next();
};
9.3 管理员权限验证
代码实现:
// middleware/adminAuth.js
const { unauthorizedResponse, errorResponse } = require('../utils/response');
// 管理员权限验证中间件
const requireAdmin = (req, res, next) => {
// 首先确保用户已认证
if (!req.user) {
return unauthorizedResponse(res, ‘请先登录’);
}
// 检查用户角色是否为管理员
if (req.user.role !== ‘admin’) {
return errorResponse(res, ‘权限不足,需要管理员权限’, 403);
}
next();
};
使用方式:
// routes/motorcycles.js
const requireAdmin = require('../middleware/adminAuth');
// 以下操作需要管理员权限
router.post(‘/’, requireAdmin, motorcycleController.createMotorcycle);
router.put(‘/:id’, requireAdmin, motorcycleController.updateMotorcycle);
router.delete(‘/:id’, requireAdmin, motorcycleController.deleteMotorcycle);
9.4 JWT 令牌生成
代码实现:
// controllers/authController.js
const generateToken = (userId, role) => {
return jwt.sign(
{ userId, role },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
);
};
使用方式:
// controllers/authController.js
const token = generateToken(user.id, user.role);
res.json({
success: true,
data: {
token,
user: {
id: user.id,
nickname: user.nickname,
avatar: user.avatar,
role: user.role
}
}
});
9.5 权限控制规则
9.6 鉴权错误处理
系统对鉴权过程中的错误进行了详细处理,包括:
令牌缺失:返回 401 状态码,提示 “访问令牌缺失”
令牌过期:返回 401 状态码,提示 “访问令牌已过期”
令牌无效:返回 401 状态码,提示 “无效的访问令牌”
权限不足:返回 403 状态码,提示 “权限不足,需要管理员权限”
9.7 实际使用示例
9.7.1 客户端登录并获取令牌
// 微信小程序代码
wx.login({
success: async (res) => {
if (res.code) {
const response = await wx.request({
url: 'http://localhost:3000/api/auth/login',
method: 'POST',
data: {
code: res.code
}
});
if (response.data.success) {
// 存储令牌
wx.setStorageSync('token', response.data.data.token);
// 存储用户信息
wx.setStorageSync('user', response.data.data.user);
}
}
}
});
9.7.2 客户端携带令牌访问接口
// 微信小程序代码
const token = wx.getStorageSync('token');
wx.request({
url: ‘http://localhost:3000/api/motorcycles’,
method: ‘GET’,
header: {
‘Authorization’: Bearer ${token}
},
success: (res) => {
console.log(res.data);
}
});
9.7.3 管理员调用需要权限的接口
// 微信小程序代码(管理员)
const token = wx.getStorageSync('token');
wx.request({
url: ‘http://localhost:3000/api/motorcycles’,
method: ‘POST’,
header: {
‘Authorization’: Bearer ${token},
‘Content-Type’: ‘application/json’
},
data: {
brand: ‘本田’,
model: ‘CB190R’,
year: 2022,
fuelType: ‘gasoline’,
plateNumber: ‘京A12345’,
currentMileage: 0
},
success: (res) => {
console.log(res.data);
}
});
9. 性能优化
使用内存缓存(node-cache)提高查询性能
为数据库表添加适当的索引
优化数据库查询,减少不必要的查询
使用分页查询,避免一次性加载大量数据
10. 故障排查
10.1 常见问题
数据库连接失败:检查数据库配置和网络连接
JWT 验证失败:检查 JWT_SECRET 配置和令牌是否过期
API 响应错误:查看服务器日志,检查错误信息
缓存问题:尝试清除缓存或重启服务
10.2 日志查看
服务器日志:通过终端查看
数据库日志:查看 MySQL 日志
11. 版本历史
12. 联系方式
技术支持:[email protected]
项目地址: