20 KiB
20 KiB
开发指南
概述
本文档为开发者提供宁夏智慧养殖监管平台的详细开发指南,包括开发环境搭建、代码规范、开发流程、调试技巧等内容。
开发环境搭建
系统要求
- Node.js: 18.0+ (推荐使用 LTS 版本)
- npm: 8.0+ 或 yarn: 1.22+
- MySQL: 8.0+
- Git: 2.25+
- 编辑器: VSCode (推荐)
工具推荐
- API测试: Postman 或 Insomnia
- 数据库管理: MySQL Workbench 或 phpMyAdmin
- 版本控制: Git + GitHub/GitLab
- 终端: iTerm2 (macOS) 或 Windows Terminal
环境搭建步骤
1. 克隆项目
git clone <repository-url>
cd nxxmdata
2. 安装依赖
# 后端依赖
cd backend
npm install
# 前端依赖 - 管理后台
cd ../admin-system/frontend
npm install
# 前端依赖 - 官网大屏
cd ../../website/data-screen
npm install
# 微信小程序(可选)
cd ../../mini_program/farm-monitor-dashboard
npm install
3. 数据库配置
# 创建数据库
mysql -u root -p
CREATE DATABASE nxxmdata CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 导入数据结构
cd backend
mysql -u root -p nxxmdata < ../create_tables.sql
4. 环境变量配置
# 后端环境变量
cp backend/.env.example backend/.env
# 编辑 .env 文件,配置数据库连接等信息
# 前端环境变量
cp admin-system/frontend/.env.example admin-system/frontend/.env
# 配置API基础URL等
5. 启动开发服务器
# 启动后端 (Terminal 1)
cd backend
npm run dev
# 启动管理后台前端 (Terminal 2)
cd admin-system/frontend
npm run dev
# 启动官网大屏 (Terminal 3)
cd website/data-screen
npm run dev
项目结构详解
后端架构 (backend/)
backend/
├── config/ # 配置文件
│ ├── database.js # 数据库配置
│ ├── swagger.js # API文档配置
│ └── performance-config.js # 性能监控配置
├── controllers/ # 控制器层
│ ├── farmController.js # 农场业务逻辑
│ ├── deviceController.js # 设备业务逻辑
│ └── userController.js # 用户业务逻辑
├── models/ # 数据模型层
│ ├── Farm.js # 农场模型
│ ├── Device.js # 设备模型
│ └── User.js # 用户模型
├── routes/ # 路由层
│ ├── farms.js # 农场相关路由
│ ├── devices.js # 设备相关路由
│ └── auth.js # 认证相关路由
├── middleware/ # 中间件
│ ├── auth.js # 认证中间件
│ └── performance-middleware.js # 性能监控中间件
├── utils/ # 工具类
│ ├── logger.js # 日志工具
│ └── performance-monitor.js # 性能监控工具
├── migrations/ # 数据库迁移
├── seeds/ # 种子数据
├── logs/ # 日志文件
└── server.js # 服务器入口
前端架构 (admin-system/frontend/)
frontend/
├── src/
│ ├── components/ # 可复用组件
│ │ ├── BaiduMap.vue # 百度地图组件
│ │ ├── EChart.vue # 图表组件
│ │ └── Dashboard.vue # 仪表盘组件
│ ├── views/ # 页面组件
│ │ ├── Home.vue # 首页
│ │ ├── MapView.vue # 地图视图
│ │ └── Analytics.vue # 数据分析
│ ├── stores/ # 状态管理 (Pinia)
│ │ ├── user.js # 用户状态
│ │ └── data.js # 数据状态
│ ├── router/ # 路由配置
│ │ ├── index.js # 路由实例
│ │ └── routes.js # 路由定义
│ ├── utils/ # 工具函数
│ │ └── api.js # API调用封装
│ ├── styles/ # 样式文件
│ └── config/ # 配置文件
├── public/ # 静态资源
└── vite.config.js # Vite配置
开发规范
代码风格规范
JavaScript/Vue.js 规范
// 1. 使用 ES6+ 语法
const apiUrl = process.env.VITE_API_BASE_URL;
// 2. 函数命名使用驼峰命名
function getUserInfo() {}
// 3. 常量使用大写字母和下划线
const API_BASE_URL = 'http://localhost:5350/api';
// 4. 类名使用 PascalCase
class UserService {}
// 5. 文件名使用 kebab-case
// user-service.js, farm-detail.vue
Vue.js 组件规范
<template>
<!-- 1. 模板根元素必须有唯一的根节点 -->
<div class="component-name">
<!-- 2. 使用语义化的HTML标签 -->
<header class="component-header">
<h1>{{ title }}</h1>
</header>
<main class="component-content">
<!-- 3. 事件处理使用 handle 前缀 -->
<button @click="handleSubmit">提交</button>
</main>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
export default {
name: 'ComponentName', // 4. 组件名使用 PascalCase
props: {
// 5. Props 定义要详细
title: {
type: String,
required: true,
default: ''
}
},
emits: ['submit'], // 6. 声明所有 emit 事件
setup(props, { emit }) {
// 7. 响应式数据使用 ref 或 reactive
const isLoading = ref(false)
// 8. 计算属性使用 computed
const displayTitle = computed(() => {
return props.title.toUpperCase()
})
// 9. 方法使用 handle 前缀
const handleSubmit = () => {
emit('submit')
}
// 10. 生命周期钩子
onMounted(() => {
// 组件挂载后的逻辑
})
return {
isLoading,
displayTitle,
handleSubmit
}
}
}
</script>
<style scoped>
/* 11. 样式使用 scoped 避免污染 */
.component-name {
/* 12. 使用 BEM 命名法 */
}
</style>
Node.js 后端规范
// 1. 使用严格模式
'use strict';
// 2. 模块导入顺序:内置模块 -> 第三方模块 -> 本地模块
const path = require('path');
const express = require('express');
const { Farm } = require('../models');
// 3. 异步函数使用 async/await
const getFarms = async (req, res) => {
try {
// 4. 输入验证
const { page = 1, limit = 10 } = req.query;
// 5. 业务逻辑
const farms = await Farm.findAll({
limit: parseInt(limit),
offset: (parseInt(page) - 1) * parseInt(limit)
});
// 6. 统一响应格式
res.json({
success: true,
data: farms,
message: '获取成功'
});
} catch (error) {
// 7. 错误处理
res.status(500).json({
success: false,
error: {
message: error.message
}
});
}
};
module.exports = {
getFarms
};
Git 工作流规范
分支命名规范
# 功能分支
feature/user-management
feature/device-monitoring
# 修复分支
fix/login-error
fix/map-display-issue
# 发布分支
release/v1.0.0
# 热修复分支
hotfix/security-patch
提交信息规范
# 格式: type(scope): description
# 类型 (type):
# feat: 新功能
# fix: 修复bug
# docs: 文档更新
# style: 代码格式调整
# refactor: 重构
# test: 测试相关
# chore: 构建过程或辅助工具的变动
# 示例:
git commit -m "feat(user): add user registration functionality"
git commit -m "fix(map): resolve baidu map display issue"
git commit -m "docs(api): update API documentation"
数据库设计规范
表命名规范
-- 表名使用小写字母和下划线
CREATE TABLE users (...);
CREATE TABLE user_roles (...);
CREATE TABLE device_sensors (...);
-- 字段名使用小写字母和下划线
ALTER TABLE users ADD COLUMN created_at DATETIME;
ALTER TABLE users ADD COLUMN updated_at DATETIME;
-- 索引命名
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_farms_location ON farms(location);
模型定义规范
// models/User.js
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '用户唯一标识'
},
username: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: '用户名'
},
email: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
validate: {
isEmail: true
},
comment: '邮箱地址'
}
}, {
tableName: 'users',
timestamps: true,
underscored: true,
comment: '用户表'
});
// 定义关联关系
User.associate = (models) => {
User.belongsToMany(models.Role, {
through: models.UserRole,
foreignKey: 'user_id',
otherKey: 'role_id'
});
};
return User;
};
开发流程
功能开发流程
1. 需求分析
- 理解业务需求
- 确认技术方案
- 评估开发时间
2. 设计阶段
# 数据库设计
# 1. 创建或修改表结构
# 2. 更新模型定义
# 3. 编写迁移脚本
# API设计
# 1. 定义路由
# 2. 设计请求/响应格式
# 3. 更新API文档
# 前端设计
# 1. UI组件设计
# 2. 状态管理设计
# 3. 路由规划
3. 开发阶段
# 创建功能分支
git checkout -b feature/new-feature
# 后端开发
# 1. 创建/修改模型
# 2. 编写控制器逻辑
# 3. 添加路由
# 4. 编写单元测试
# 前端开发
# 1. 创建Vue组件
# 2. 实现业务逻辑
# 3. 添加样式
# 4. 集成API
# 提交代码
git add .
git commit -m "feat: implement new feature"
git push origin feature/new-feature
4. 测试阶段
# 单元测试
npm test
# 集成测试
npm run test:integration
# 端到端测试
npm run test:e2e
# 手动测试
# 1. 功能测试
# 2. 兼容性测试
# 3. 性能测试
5. 代码审查
- 创建 Pull Request
- 代码审查
- 修改反馈问题
- 合并到主分支
调试技巧
后端调试
1. 使用 console.log 调试
// 在关键位置添加日志
console.log('User data:', userData);
console.log('Database query result:', result);
// 使用 JSON.stringify 查看对象
console.log('Request body:', JSON.stringify(req.body, null, 2));
2. 使用 Winston 日志
const logger = require('../utils/logger');
// 不同级别的日志
logger.info('User logged in', { userId: user.id });
logger.warn('Invalid input', { input: req.body });
logger.error('Database error', { error: error.message });
3. 使用 Node.js 调试器
# 启动调试模式
node --inspect-brk server.js
# 在 Chrome 中打开 chrome://inspect
# 连接到 Node.js 进程进行调试
4. 数据库查询调试
// 启用 Sequelize 日志
const sequelize = new Sequelize(config, {
logging: console.log, // 显示 SQL 查询
benchmark: true // 显示查询时间
});
// 手动执行 SQL 查询
const [results, metadata] = await sequelize.query(
'SELECT * FROM users WHERE id = ?',
{ replacements: [userId] }
);
前端调试
1. 使用 Vue DevTools
# 安装 Vue DevTools 浏览器扩展
# 查看组件状态、props、events
# 检查 Pinia store 状态
2. 使用 console.log
// 在组件中添加调试信息
export default {
setup() {
const userData = ref(null);
const fetchUser = async () => {
console.log('Fetching user data...');
const response = await api.getUser();
console.log('User response:', response);
userData.value = response.data;
};
return { userData, fetchUser };
}
}
3. 使用浏览器开发者工具
// 在代码中设置断点
debugger;
// 查看网络请求
// Network 标签页 -> 检查 API 请求和响应
// 查看控制台错误
// Console 标签页 -> 查看 JavaScript 错误
4. API 调试
// 创建 API 调试工具
const apiDebug = {
log: (method, url, data, response) => {
console.group(`API ${method.toUpperCase()} ${url}`);
console.log('Request data:', data);
console.log('Response:', response);
console.groupEnd();
}
};
// 在 API 调用中使用
const response = await axios.post('/api/farms', farmData);
apiDebug.log('POST', '/api/farms', farmData, response.data);
性能优化
后端性能优化
1. 数据库查询优化
// 使用索引
// 在经常查询的字段上添加索引
CREATE INDEX idx_farms_status ON farms(status);
// 避免 N+1 查询
const farms = await Farm.findAll({
include: [{
model: Device,
as: 'devices'
}]
});
// 使用分页
const farms = await Farm.findAndCountAll({
limit: 10,
offset: (page - 1) * 10
});
// 只查询需要的字段
const farms = await Farm.findAll({
attributes: ['id', 'name', 'status']
});
2. 缓存策略
// 使用内存缓存
const cache = new Map();
const getCachedData = (key) => {
if (cache.has(key)) {
return cache.get(key);
}
return null;
};
const setCachedData = (key, data, ttl = 300000) => {
cache.set(key, data);
setTimeout(() => cache.delete(key), ttl);
};
3. 异步处理
// 使用 Promise.all 并行处理
const [farms, devices, users] = await Promise.all([
Farm.findAll(),
Device.findAll(),
User.findAll()
]);
// 使用 stream 处理大量数据
const stream = Farm.findAll({ stream: true });
stream.on('data', (farm) => {
// 处理单个农场数据
});
前端性能优化
1. 组件懒加载
// router/routes.js
const routes = [
{
path: '/farms',
component: () => import('../views/Farms.vue') // 懒加载
}
];
2. 图片优化
<template>
<!-- 图片懒加载 -->
<img v-lazy="imageUrl" alt="Farm image">
<!-- 响应式图片 -->
<picture>
<source media="(min-width: 800px)" :srcset="largeImage">
<img :src="smallImage" alt="Farm">
</picture>
</template>
3. 虚拟滚动
<template>
<!-- 大列表虚拟滚动 -->
<virtual-scroll
:items="farms"
:item-height="60"
#default="{ item }"
>
<farm-item :farm="item" />
</virtual-scroll>
</template>
测试指南
单元测试
后端单元测试 (Jest)
// tests/controllers/farmController.test.js
const { getFarms } = require('../../controllers/farmController');
const { Farm } = require('../../models');
jest.mock('../../models');
describe('Farm Controller', () => {
describe('getFarms', () => {
it('should return farms list', async () => {
const mockFarms = [
{ id: 1, name: 'Farm 1' },
{ id: 2, name: 'Farm 2' }
];
Farm.findAll.mockResolvedValue(mockFarms);
const req = { query: {} };
const res = {
json: jest.fn(),
status: jest.fn().mockReturnThis()
};
await getFarms(req, res);
expect(res.json).toHaveBeenCalledWith({
success: true,
data: mockFarms,
message: '获取成功'
});
});
});
});
前端单元测试 (Vitest)
// tests/components/FarmList.test.js
import { mount } from '@vue/test-utils';
import FarmList from '@/components/FarmList.vue';
describe('FarmList', () => {
it('renders farm list correctly', () => {
const farms = [
{ id: 1, name: 'Farm 1', status: 'active' },
{ id: 2, name: 'Farm 2', status: 'inactive' }
];
const wrapper = mount(FarmList, {
props: { farms }
});
expect(wrapper.findAll('.farm-item')).toHaveLength(2);
expect(wrapper.text()).toContain('Farm 1');
expect(wrapper.text()).toContain('Farm 2');
});
it('emits select event when farm is clicked', async () => {
const farms = [{ id: 1, name: 'Farm 1', status: 'active' }];
const wrapper = mount(FarmList, {
props: { farms }
});
await wrapper.find('.farm-item').trigger('click');
expect(wrapper.emitted('select')).toBeTruthy();
expect(wrapper.emitted('select')[0]).toEqual([farms[0]]);
});
});
集成测试
// tests/integration/farms.test.js
const request = require('supertest');
const app = require('../../server');
describe('Farms API', () => {
it('GET /api/farms should return farms list', async () => {
const response = await request(app)
.get('/api/farms')
.set('Authorization', 'Bearer valid_token')
.expect(200);
expect(response.body.success).toBe(true);
expect(Array.isArray(response.body.data)).toBe(true);
});
it('POST /api/farms should create new farm', async () => {
const farmData = {
name: 'Test Farm',
type: 'cattle',
longitude: 106.26667,
latitude: 38.46667
};
const response = await request(app)
.post('/api/farms')
.set('Authorization', 'Bearer admin_token')
.send(farmData)
.expect(201);
expect(response.body.success).toBe(true);
expect(response.body.data.name).toBe(farmData.name);
});
});
部署和运维
本地开发部署
# 1. 启动所有服务
npm run dev:all
# 2. 查看日志
tail -f backend/logs/app.log
# 3. 数据库管理
npm run db:migrate
npm run db:seed
生产环境部署
# 1. 构建项目
npm run build
# 2. 使用 PM2 部署
pm2 start ecosystem.config.js --env production
# 3. 监控服务
pm2 monit
pm2 logs
常见问题解决
开发环境问题
1. 端口占用
# 查找占用端口的进程
lsof -i :5350
netstat -ano | findstr :5350 # Windows
# 杀死进程
kill -9 <PID>
taskkill /PID <PID> /F # Windows
2. 依赖安装失败
# 清除 npm 缓存
npm cache clean --force
# 删除 node_modules 重新安装
rm -rf node_modules package-lock.json
npm install
# 使用 yarn 代替 npm
yarn install
3. 数据库连接失败
# 检查 MySQL 服务状态
brew services list | grep mysql # macOS
systemctl status mysql # Linux
net start mysql # Windows
# 测试数据库连接
mysql -u root -p -h localhost -P 3306
运行时问题
1. API 请求失败
// 检查网络请求
// 1. 打开浏览器开发者工具
// 2. 查看 Network 标签页
// 3. 检查请求状态码和响应内容
// 添加请求拦截器
axios.interceptors.request.use(
config => {
console.log('Request:', config);
return config;
},
error => {
console.error('Request Error:', error);
return Promise.reject(error);
}
);
2. 组件渲染问题
// 检查 Vue 组件状态
// 1. 安装 Vue DevTools
// 2. 检查组件 props 和 data
// 3. 查看 computed 属性
// 4. 检查事件监听器
最佳实践
代码组织
- 模块化开发: 将功能拆分成独立的模块
- 组件复用: 创建可复用的组件库
- 状态管理: 合理使用 Pinia 管理应用状态
- 错误处理: 统一的错误处理机制
- 日志记录: 详细的日志记录便于调试
性能优化
- 懒加载: 组件和路由懒加载
- 缓存策略: 合理使用缓存提升性能
- 数据库优化: 优化查询语句和索引
- 图片优化: 压缩图片和使用适当格式
- 打包优化: 优化 Webpack/Vite 配置
安全考虑
- 输入验证: 严格验证用户输入
- 权限控制: 实现细粒度的权限控制
- SQL注入防护: 使用参数化查询
- XSS防护: 过滤和转义用户输入
- CSRF防护: 实现 CSRF Token 机制
工具和资源
开发工具
- IDE: VSCode
- API测试: Postman
- 数据库: MySQL Workbench
- 版本控制: Git
- 包管理: npm/yarn
有用的 VSCode 插件
- Vue Language Features (Volar): Vue 3 支持
- ESLint: 代码质量检查
- Prettier: 代码格式化
- Auto Rename Tag: 自动重命名标签
- Bracket Pair Colorizer: 括号高亮
- GitLens: Git 增强
学习资源
- Vue.js 官方文档: https://vuejs.org/
- Node.js 官方文档: https://nodejs.org/
- Sequelize 文档: https://sequelize.org/
- Ant Design Vue: https://antdv.com/
- ECharts 文档: https://echarts.apache.org/
联系支持
如有开发相关问题,请联系:
- 技术支持: dev-support@nxxmdata.com
- 文档反馈: docs@nxxmdata.com
- Bug 报告: bugs@nxxmdata.com
最后更新: 2025年1月