Files
nxxmdata/docs/DEVELOPMENT.md
2025-09-02 23:22:10 +08:00

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. 检查事件监听器

最佳实践

代码组织

  1. 模块化开发: 将功能拆分成独立的模块
  2. 组件复用: 创建可复用的组件库
  3. 状态管理: 合理使用 Pinia 管理应用状态
  4. 错误处理: 统一的错误处理机制
  5. 日志记录: 详细的日志记录便于调试

性能优化

  1. 懒加载: 组件和路由懒加载
  2. 缓存策略: 合理使用缓存提升性能
  3. 数据库优化: 优化查询语句和索引
  4. 图片优化: 压缩图片和使用适当格式
  5. 打包优化: 优化 Webpack/Vite 配置

安全考虑

  1. 输入验证: 严格验证用户输入
  2. 权限控制: 实现细粒度的权限控制
  3. SQL注入防护: 使用参数化查询
  4. XSS防护: 过滤和转义用户输入
  5. 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 增强

学习资源


联系支持

如有开发相关问题,请联系:

最后更新: 2025年1月