Initial commit: 宁夏智慧养殖监管平台

This commit is contained in:
shenquanyi
2025-08-25 15:00:46 +08:00
commit ec72c6a8b5
177 changed files with 37263 additions and 0 deletions

147
backend/models/Alert.js Normal file
View File

@@ -0,0 +1,147 @@
/**
* Alert 模型定义
* @file Alert.js
* @description 定义预警模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
/**
* 预警模型
* @typedef {Object} Alert
* @property {number} id - 预警唯一标识
* @property {string} type - 预警类型
* @property {string} level - 预警级别
* @property {string} message - 预警消息
* @property {string} status - 预警状态
* @property {number} farmId - 所属养殖场ID
* @property {number} deviceId - 关联设备ID
* @property {Date} created_at - 创建时间
* @property {Date} updated_at - 更新时间
*/
class Alert extends BaseModel {
/**
* 获取预警所属的养殖场
* @returns {Promise<Object>} 养殖场信息
*/
async getFarm() {
return await this.getFarm();
}
/**
* 获取预警关联的设备
* @returns {Promise<Object>} 设备信息
*/
async getDevice() {
return await this.getDevice();
}
/**
* 更新预警状态
* @param {String} status 新状态
* @returns {Promise<Boolean>} 更新结果
*/
async updateStatus(status) {
try {
this.status = status;
if (status === 'resolved') {
this.resolved_at = new Date();
}
await this.save();
return true;
} catch (error) {
console.error('更新预警状态失败:', error);
return false;
}
}
/**
* 解决预警
* @param {Number} userId 解决人ID
* @param {String} notes 解决说明
* @returns {Promise<Boolean>} 解决结果
*/
async resolve(userId, notes) {
try {
this.status = 'resolved';
this.resolved_at = new Date();
this.resolved_by = userId;
this.resolution_notes = notes;
await this.save();
return true;
} catch (error) {
console.error('解决预警失败:', error);
return false;
}
}
}
// 初始化Alert模型
Alert.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
type: {
type: DataTypes.STRING(50),
allowNull: false
},
level: {
type: DataTypes.ENUM('low', 'medium', 'high', 'critical'),
defaultValue: 'medium'
},
message: {
type: DataTypes.TEXT,
allowNull: false
},
status: {
type: DataTypes.ENUM('active', 'acknowledged', 'resolved'),
defaultValue: 'active'
},
farm_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'farms',
key: 'id'
}
},
device_id: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'devices',
key: 'id'
}
},
resolved_at: {
type: DataTypes.DATE,
allowNull: true
},
resolved_by: {
type: DataTypes.INTEGER,
allowNull: true
},
resolution_notes: {
type: DataTypes.TEXT,
allowNull: true
}
}, {
sequelize,
tableName: 'alerts',
modelName: 'Alert',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
/**
* 导出预警模型
* @exports Alert
*/
module.exports = Alert;

115
backend/models/Animal.js Normal file
View File

@@ -0,0 +1,115 @@
/**
* Animal 模型定义
* @file Animal.js
* @description 定义动物模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
/**
* 动物模型
* @typedef {Object} Animal
* @property {number} id - 动物唯一标识
* @property {string} type - 动物类型
* @property {number} count - 数量
* @property {number} farmId - 所属养殖场ID
* @property {Date} created_at - 创建时间
* @property {Date} updated_at - 更新时间
*/
class Animal extends BaseModel {
/**
* 获取动物所属的养殖场
* @returns {Promise<Object>} 养殖场信息
*/
async getFarm() {
return await this.getFarm();
}
/**
* 更新动物数量
* @param {Number} count 新数量
* @returns {Promise<Boolean>} 更新结果
*/
async updateCount(count) {
try {
if (count < 0) {
throw new Error('数量不能为负数');
}
this.count = count;
await this.save();
return true;
} catch (error) {
console.error('更新动物数量失败:', error);
return false;
}
}
/**
* 更新健康状态
* @param {String} status 新状态
* @returns {Promise<Boolean>} 更新结果
*/
async updateHealthStatus(status) {
try {
this.health_status = status;
await this.save();
return true;
} catch (error) {
console.error('更新健康状态失败:', error);
return false;
}
}
}
// 初始化Animal模型
Animal.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
type: {
type: DataTypes.STRING(50),
allowNull: false
},
count: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
farm_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'farms',
key: 'id'
}
},
health_status: {
type: DataTypes.ENUM('healthy', 'sick', 'quarantine', 'treatment'),
defaultValue: 'healthy'
},
last_inspection: {
type: DataTypes.DATE,
allowNull: true
},
notes: {
type: DataTypes.TEXT,
allowNull: true
}
}, {
sequelize,
tableName: 'animals',
modelName: 'Animal',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
/**
* 导出动物模型
* @exports Animal
*/
module.exports = Animal;

187
backend/models/BaseModel.js Normal file
View File

@@ -0,0 +1,187 @@
/**
* 数据库模型基类
* @file BaseModel.js
* @description 提供所有模型的基础功能和通用方法
*/
const { Model, DataTypes } = require('sequelize');
const queryOptimizer = require('../utils/query-optimizer');
class BaseModel extends Model {
/**
* 初始化模型
* @param {Object} sequelize Sequelize实例
* @param {Object} attributes 模型属性
* @param {Object} options 模型选项
*/
static init(attributes, options = {}) {
// 确保options中包含sequelize实例
if (!options.sequelize) {
throw new Error('必须提供sequelize实例');
}
// 默认配置
const defaultOptions = {
// 使用下划线命名法
underscored: true,
// 默认时间戳字段
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
// 默认不使用软删除
paranoid: false,
// 字符集和排序规则
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
};
// 合并选项
const mergedOptions = { ...defaultOptions, ...options };
// 调用父类的init方法
return super.init(attributes, mergedOptions);
}
/**
* 分页查询
* @param {Object} options 查询选项
* @param {Number} page 页码
* @param {Number} pageSize 每页记录数
* @returns {Promise<Object>} 分页结果
*/
static async paginate(options = {}, page = 1, pageSize = 10) {
// 确保页码和每页记录数为正整数
page = Math.max(1, parseInt(page));
pageSize = Math.max(1, parseInt(pageSize));
// 计算偏移量
const offset = (page - 1) * pageSize;
// 合并分页参数
const paginateOptions = {
...options,
limit: pageSize,
offset
};
// 执行查询
const { count, rows } = await this.findAndCountAll(paginateOptions);
// 计算总页数
const totalPages = Math.ceil(count / pageSize);
// 返回分页结果
return {
data: rows,
pagination: {
total: count,
page,
pageSize,
totalPages,
hasMore: page < totalPages
}
};
}
/**
* 批量创建或更新记录
* @param {Array} records 记录数组
* @param {Array|String} uniqueFields 唯一字段或字段数组
* @returns {Promise<Array>} 创建或更新的记录
*/
static async bulkUpsert(records, uniqueFields) {
if (!Array.isArray(records) || records.length === 0) {
return [];
}
// 确保uniqueFields是数组
const fields = Array.isArray(uniqueFields) ? uniqueFields : [uniqueFields];
// 获取所有字段
const allFields = Object.keys(this.rawAttributes);
// 批量创建或更新
const result = await this.bulkCreate(records, {
updateOnDuplicate: allFields.filter(field => {
// 排除主键和时间戳字段
return (
field !== 'id' &&
field !== 'created_at' &&
field !== 'updated_at' &&
field !== 'deleted_at'
);
}),
// 返回创建或更新后的记录
returning: true
});
return result;
}
/**
* 执行原始SQL查询
* @param {String} sql SQL语句
* @param {Object} replacements 替换参数
* @param {String} type 查询类型
* @returns {Promise<Array|Object>} 查询结果
*/
static async rawQuery(sql, replacements = {}, type = 'SELECT') {
const sequelize = this.sequelize;
const QueryTypes = sequelize.QueryTypes;
// 执行查询前分析查询性能
if (process.env.NODE_ENV === 'development') {
try {
const explainResult = await queryOptimizer.explainQuery(sql, replacements);
console.log('查询分析结果:', explainResult);
} catch (error) {
console.warn('查询分析失败:', error.message);
}
}
// 执行查询
return sequelize.query(sql, {
replacements,
type: QueryTypes[type],
model: this,
mapToModel: type === 'SELECT'
});
}
/**
* 获取模型的表信息
* @returns {Promise<Object>} 表信息
*/
static async getTableInfo() {
const tableName = this.getTableName();
return queryOptimizer.getTableInfo(tableName);
}
/**
* 获取模型的索引信息
* @returns {Promise<Array>} 索引信息
*/
static async getIndexes() {
const tableName = this.getTableName();
return queryOptimizer.getTableIndexes(tableName);
}
/**
* 分析表结构
* @returns {Promise<Object>} 分析结果
*/
static async analyzeTable() {
const tableName = this.getTableName();
return queryOptimizer.analyzeTable(tableName);
}
/**
* 优化表
* @returns {Promise<Object>} 优化结果
*/
static async optimizeTable() {
const tableName = this.getTableName();
return queryOptimizer.optimizeTable(tableName);
}
}
module.exports = BaseModel;

123
backend/models/Device.js Normal file
View File

@@ -0,0 +1,123 @@
/**
* Device 模型定义
* @file Device.js
* @description 定义设备模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
/**
* 设备模型
* @typedef {Object} Device
* @property {number} id - 设备唯一标识
* @property {string} type - 设备类型
* @property {string} status - 设备状态
* @property {number} farmId - 所属养殖场ID
* @property {Date} created_at - 创建时间
* @property {Date} updated_at - 更新时间
*/
class Device extends BaseModel {
/**
* 获取设备所属的养殖场
* @returns {Promise<Object>} 养殖场信息
*/
async getFarm() {
return await this.getFarm();
}
/**
* 获取设备的所有预警
* @returns {Promise<Array>} 预警列表
*/
async getAlerts() {
return await this.getAlerts();
}
/**
* 更新设备状态
* @param {String} status 新状态
* @returns {Promise<Boolean>} 更新结果
*/
async updateStatus(status) {
try {
this.status = status;
await this.save();
return true;
} catch (error) {
console.error('更新设备状态失败:', error);
return false;
}
}
/**
* 记录设备维护
* @returns {Promise<Boolean>} 记录结果
*/
async recordMaintenance() {
try {
this.last_maintenance = new Date();
this.status = 'online';
await this.save();
return true;
} catch (error) {
console.error('记录设备维护失败:', error);
return false;
}
}
}
// 初始化Device模型
Device.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
type: {
type: DataTypes.STRING(50),
allowNull: false
},
status: {
type: DataTypes.ENUM('online', 'offline', 'maintenance'),
defaultValue: 'offline'
},
farm_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'farms',
key: 'id'
}
},
last_maintenance: {
type: DataTypes.DATE,
allowNull: true
},
installation_date: {
type: DataTypes.DATE,
allowNull: true
},
metrics: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: {}
}
}, {
sequelize,
tableName: 'devices',
modelName: 'Device',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
/**
* 导出设备模型
* @exports Device
*/
module.exports = Device;

97
backend/models/Farm.js Normal file
View File

@@ -0,0 +1,97 @@
/**
* Farm 模型定义
* @file Farm.js
* @description 定义养殖场模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
/**
* 养殖场模型
* @typedef {Object} Farm
* @property {number} id - 养殖场唯一标识
* @property {string} name - 养殖场名称
* @property {string} type - 养殖场类型
* @property {Object} location - 地理位置
* @property {number} location.lat - 纬度
* @property {number} location.lng - 经度
* @property {Date} created_at - 创建时间
* @property {Date} updated_at - 更新时间
*/
class Farm extends BaseModel {
/**
* 获取养殖场的所有动物
* @returns {Promise<Array>} 动物列表
*/
async getAnimals() {
return await this.getAnimals();
}
/**
* 获取养殖场的所有设备
* @returns {Promise<Array>} 设备列表
*/
async getDevices() {
return await this.getDevices();
}
/**
* 获取养殖场的所有预警
* @returns {Promise<Array>} 预警列表
*/
async getAlerts() {
return await this.getAlerts();
}
}
// 初始化Farm模型
Farm.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
type: {
type: DataTypes.STRING(50),
allowNull: false
},
location: {
type: DataTypes.JSON,
allowNull: false,
defaultValue: {}
},
address: {
type: DataTypes.STRING(255),
allowNull: true
},
contact: {
type: DataTypes.STRING(50),
allowNull: true
},
phone: {
type: DataTypes.STRING(20),
allowNull: true
},
status: {
type: DataTypes.ENUM('active', 'inactive', 'maintenance'),
defaultValue: 'active'
}
}, {
sequelize,
tableName: 'farms',
modelName: 'Farm',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
/**
* 导出养殖场模型
* @exports Farm
*/
module.exports = Farm;

146
backend/models/Order.js Normal file
View File

@@ -0,0 +1,146 @@
/**
* Order 模型定义
* @file Order.js
* @description 定义订单模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
/**
* 订单模型
* @typedef {Object} Order
* @property {number} id - 订单唯一标识
* @property {number} user_id - 用户ID
* @property {number} total_amount - 订单总金额(单位:分)
* @property {string} status - 订单状态
* @property {string} payment_status - 支付状态
* @property {string} shipping_address - 收货地址
* @property {Date} created_at - 创建时间
* @property {Date} updated_at - 更新时间
*/
class Order extends BaseModel {
/**
* 获取用户的所有订单
* @param {Number} userId 用户ID
* @param {Object} options 查询选项
* @returns {Promise<Array>} 订单列表
*/
static async getUserOrders(userId, options = {}) {
return await this.findAll({
where: { user_id: userId },
...options
});
}
/**
* 获取订单详情,包括订单项
* @returns {Promise<Object>} 订单详情
*/
async getOrderDetails() {
return await Order.findByPk(this.id, {
include: [{ model: sequelize.models.OrderItem }]
});
}
/**
* 计算订单总金额
* @returns {Promise<Number>} 订单总金额
*/
async calculateTotal() {
const orderItems = await this.getOrderItems();
let total = 0;
for (const item of orderItems) {
total += item.price * item.quantity;
}
this.total_amount = total;
await this.save();
return total;
}
/**
* 更新订单状态
* @param {String} status 新状态
* @returns {Promise<Boolean>} 更新结果
*/
async updateStatus(status) {
try {
this.status = status;
await this.save();
return true;
} catch (error) {
console.error('更新订单状态失败:', error);
return false;
}
}
/**
* 更新支付状态
* @param {String} paymentStatus 新支付状态
* @returns {Promise<Boolean>} 更新结果
*/
async updatePaymentStatus(paymentStatus) {
try {
this.payment_status = paymentStatus;
await this.save();
return true;
} catch (error) {
console.error('更新支付状态失败:', error);
return false;
}
}
}
// 初始化Order模型
Order.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id'
},
onDelete: 'CASCADE'
},
total_amount: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '单位:分'
},
status: {
type: DataTypes.ENUM('pending', 'processing', 'shipped', 'delivered', 'cancelled'),
allowNull: false,
defaultValue: 'pending'
},
payment_status: {
type: DataTypes.ENUM('unpaid', 'paid', 'refunded'),
allowNull: false,
defaultValue: 'unpaid'
},
shipping_address: {
type: DataTypes.TEXT,
allowNull: true
}
}, {
sequelize,
tableName: 'orders',
modelName: 'Order',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
/**
* 导出订单模型
* @exports Order
*/
module.exports = Order;

140
backend/models/OrderItem.js Normal file
View File

@@ -0,0 +1,140 @@
/**
* OrderItem 模型定义
* @file OrderItem.js
* @description 定义订单项模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
/**
* 订单项模型
* @typedef {Object} OrderItem
* @property {number} id - 订单项唯一标识
* @property {number} order_id - 订单ID
* @property {number} product_id - 产品ID
* @property {number} quantity - 数量
* @property {number} price - 单价(单位:分)
* @property {Date} created_at - 创建时间
*/
class OrderItem extends BaseModel {
/**
* 获取订单的所有订单项
* @param {Number} orderId 订单ID
* @returns {Promise<Array>} 订单项列表
*/
static async getOrderItems(orderId) {
return await this.findAll({
where: { order_id: orderId },
include: [{ model: sequelize.models.Product }]
});
}
/**
* 计算订单项总金额
* @returns {Number} 总金额
*/
getTotalPrice() {
return this.price * this.quantity;
}
/**
* 更新订单项数量
* @param {Number} quantity 新数量
* @returns {Promise<Boolean>} 更新结果
*/
async updateQuantity(quantity) {
try {
if (quantity <= 0) {
throw new Error('数量必须大于0');
}
// 检查产品库存
const product = await sequelize.models.Product.findByPk(this.product_id);
if (!product) {
throw new Error('产品不存在');
}
const quantityDiff = quantity - this.quantity;
if (quantityDiff > 0 && !product.hasEnoughStock(quantityDiff)) {
throw new Error('产品库存不足');
}
// 使用事务确保数据一致性
const result = await sequelize.transaction(async (t) => {
// 更新订单项数量
this.quantity = quantity;
await this.save({ transaction: t });
// 更新产品库存
await product.updateStock(-quantityDiff, { transaction: t });
// 更新订单总金额
const order = await sequelize.models.Order.findByPk(this.order_id, { transaction: t });
await order.calculateTotal({ transaction: t });
return true;
});
return result;
} catch (error) {
console.error('更新订单项数量失败:', error);
return false;
}
}
}
// 初始化OrderItem模型
OrderItem.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
order_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'orders',
key: 'id'
},
onDelete: 'CASCADE'
},
product_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'products',
key: 'id'
},
onDelete: 'RESTRICT'
},
quantity: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 1,
validate: {
min: 1
}
},
price: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '单位:分'
}
}, {
sequelize,
tableName: 'order_items',
modelName: 'OrderItem',
timestamps: true,
createdAt: 'created_at',
updatedAt: false
});
/**
* 导出订单项模型
* @exports OrderItem
*/
module.exports = OrderItem;

127
backend/models/Product.js Normal file
View File

@@ -0,0 +1,127 @@
/**
* Product 模型定义
* @file Product.js
* @description 定义产品模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
/**
* 产品模型
* @typedef {Object} Product
* @property {number} id - 产品唯一标识
* @property {string} name - 产品名称
* @property {string} description - 产品描述
* @property {number} price - 产品价格(单位:分)
* @property {number} stock - 库存数量
* @property {string} image_url - 产品图片URL
* @property {boolean} is_active - 是否激活
* @property {Date} created_at - 创建时间
* @property {Date} updated_at - 更新时间
*/
class Product extends BaseModel {
/**
* 获取激活的产品列表
* @param {Object} options 查询选项
* @returns {Promise<Array>} 产品列表
*/
static async getActiveProducts(options = {}) {
return await this.findAll({
where: { is_active: true },
...options
});
}
/**
* 更新产品库存
* @param {Number} quantity 变更数量,正数增加库存,负数减少库存
* @returns {Promise<Boolean>} 更新结果
*/
async updateStock(quantity) {
try {
// 使用事务和乐观锁确保库存操作的原子性
const result = await sequelize.transaction(async (t) => {
const product = await Product.findByPk(this.id, { transaction: t, lock: true });
if (!product) {
throw new Error('产品不存在');
}
const newStock = product.stock + quantity;
if (newStock < 0) {
throw new Error('库存不足');
}
product.stock = newStock;
await product.save({ transaction: t });
return true;
});
return result;
} catch (error) {
console.error('更新库存失败:', error);
return false;
}
}
/**
* 检查产品库存是否充足
* @param {Number} quantity 需要的数量
* @returns {Boolean} 是否充足
*/
hasEnoughStock(quantity) {
return this.stock >= quantity;
}
}
// 初始化Product模型
Product.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
price: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '单位:分'
},
stock: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
image_url: {
type: DataTypes.STRING(255),
allowNull: true
},
is_active: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
}
}, {
sequelize,
tableName: 'products',
modelName: 'Product',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
/**
* 导出产品模型
* @exports Product
*/
module.exports = Product;

105
backend/models/Role.js Normal file
View File

@@ -0,0 +1,105 @@
/**
* Role 模型定义
* @file Role.js
* @description 定义角色模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
/**
* 角色模型
* @typedef {Object} Role
* @property {number} id - 角色唯一标识
* @property {string} name - 角色名称,唯一
* @property {string} description - 角色描述
* @property {Date} created_at - 创建时间
*/
class Role extends BaseModel {
/**
* 获取具有此角色的所有用户
* @returns {Promise<Array>} 用户列表
*/
async getUsers() {
return await this.getUsers();
}
/**
* 检查角色是否已分配给指定用户
* @param {Number} userId 用户ID
* @returns {Promise<Boolean>} 检查结果
*/
async isAssignedToUser(userId) {
const users = await this.getUsers({ where: { id: userId } });
return users.length > 0;
}
/**
* 为角色分配用户
* @param {Number|Array} userId 用户ID或用户ID数组
* @returns {Promise<Boolean>} 分配结果
*/
async assignToUser(userId) {
try {
if (Array.isArray(userId)) {
await this.addUsers(userId);
} else {
await this.addUser(userId);
}
return true;
} catch (error) {
console.error('分配用户失败:', error);
return false;
}
}
/**
* 从用户中移除此角色
* @param {Number|Array} userId 用户ID或用户ID数组
* @returns {Promise<Boolean>} 移除结果
*/
async removeFromUser(userId) {
try {
if (Array.isArray(userId)) {
await this.removeUsers(userId);
} else {
await this.removeUser(userId);
}
return true;
} catch (error) {
console.error('移除用户失败:', error);
return false;
}
}
}
// 初始化Role模型
Role.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true
},
description: {
type: DataTypes.TEXT,
allowNull: true
}
}, {
sequelize,
tableName: 'roles',
modelName: 'Role',
timestamps: true,
createdAt: 'created_at',
updatedAt: false
});
/**
* 导出角色模型
* @exports Role
*/
module.exports = Role;

View File

@@ -0,0 +1,214 @@
/**
* 传感器数据模型
* @file SensorData.js
* @description 环境监控数据表模型,存储温度、湿度等传感器数据
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
class SensorData extends BaseModel {
static init(sequelize) {
return super.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '传感器数据ID'
},
device_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '设备ID',
references: {
model: 'devices',
key: 'id'
}
},
farm_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '养殖场ID',
references: {
model: 'farms',
key: 'id'
}
},
sensor_type: {
type: DataTypes.ENUM('temperature', 'humidity', 'ph', 'oxygen', 'ammonia', 'light'),
allowNull: false,
comment: '传感器类型temperature-温度, humidity-湿度, ph-酸碱度, oxygen-氧气, ammonia-氨气, light-光照'
},
value: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
comment: '传感器数值'
},
unit: {
type: DataTypes.STRING(20),
allowNull: false,
comment: '数值单位°C, %, ppm等'
},
location: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '传感器位置描述'
},
status: {
type: DataTypes.ENUM('normal', 'warning', 'error'),
defaultValue: 'normal',
comment: '数据状态normal-正常, warning-警告, error-异常'
},
recorded_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
comment: '数据记录时间'
}
}, {
sequelize,
modelName: 'SensorData',
tableName: 'sensor_data',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
indexes: [
{
fields: ['device_id']
},
{
fields: ['farm_id']
},
{
fields: ['sensor_type']
},
{
fields: ['recorded_at']
},
{
fields: ['farm_id', 'sensor_type', 'recorded_at']
}
],
comment: '传感器环境监控数据表'
});
}
static associate(models) {
// 传感器数据属于某个设备
this.belongsTo(models.Device, {
foreignKey: 'device_id',
as: 'device'
});
// 传感器数据属于某个养殖场
this.belongsTo(models.Farm, {
foreignKey: 'farm_id',
as: 'farm'
});
}
/**
* 获取指定养殖场的最新传感器数据
* @param {number} farmId - 养殖场ID
* @param {string} sensorType - 传感器类型
* @param {number} limit - 数据条数限制
* @returns {Promise<Array>} 传感器数据列表
*/
static async getLatestData(farmId, sensorType, limit = 24) {
try {
const whereClause = {
farm_id: farmId
};
if (sensorType) {
whereClause.sensor_type = sensorType;
}
return await this.findAll({
where: whereClause,
order: [['recorded_at', 'DESC']],
limit: limit,
include: [{
model: this.sequelize.models.Device,
as: 'device',
attributes: ['id', 'name', 'type']
}]
});
} catch (error) {
console.error('获取最新传感器数据失败:', error);
throw error;
}
}
/**
* 获取指定时间范围内的传感器数据
* @param {number} farmId - 养殖场ID
* @param {string} sensorType - 传感器类型
* @param {Date} startTime - 开始时间
* @param {Date} endTime - 结束时间
* @returns {Promise<Array>} 传感器数据列表
*/
static async getDataByTimeRange(farmId, sensorType, startTime, endTime) {
try {
return await this.findAll({
where: {
farm_id: farmId,
sensor_type: sensorType,
recorded_at: {
[this.sequelize.Sequelize.Op.between]: [startTime, endTime]
}
},
order: [['recorded_at', 'ASC']],
include: [{
model: this.sequelize.models.Device,
as: 'device',
attributes: ['id', 'name', 'type']
}]
});
} catch (error) {
console.error('获取时间范围内传感器数据失败:', error);
throw error;
}
}
/**
* 获取传感器数据统计信息
* @param {number} farmId - 养殖场ID
* @param {string} sensorType - 传感器类型
* @param {Date} startTime - 开始时间
* @param {Date} endTime - 结束时间
* @returns {Promise<Object>} 统计信息
*/
static async getDataStats(farmId, sensorType, startTime, endTime) {
const { Op, fn, col } = require('sequelize');
const stats = await this.findOne({
where: {
farm_id: farmId,
sensor_type: sensorType,
recorded_at: {
[Op.between]: [startTime, endTime]
}
},
attributes: [
[fn('AVG', col('value')), 'avgValue'],
[fn('MIN', col('value')), 'minValue'],
[fn('MAX', col('value')), 'maxValue'],
[fn('COUNT', col('id')), 'dataCount']
],
raw: true
});
return {
average: parseFloat(stats.avgValue || 0).toFixed(2),
minimum: parseFloat(stats.minValue || 0).toFixed(2),
maximum: parseFloat(stats.maxValue || 0).toFixed(2),
count: parseInt(stats.dataCount || 0)
};
}
}
// 初始化模型
const { sequelize } = require('../config/database-simple');
SensorData.init(sequelize);
module.exports = SensorData;

147
backend/models/User.js Normal file
View File

@@ -0,0 +1,147 @@
/**
* User 模型定义
* @file User.js
* @description 定义用户模型,用于数据库操作
*/
const { DataTypes } = require('sequelize');
const bcrypt = require('bcrypt');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-pool');
class User extends BaseModel {
/**
* 验证密码
* @param {String} password 待验证的密码
* @returns {Promise<Boolean>} 验证结果
*/
async validPassword(password) {
return await bcrypt.compare(password, this.password);
}
/**
* 获取用户角色
* @returns {Promise<Array>} 用户角色列表
*/
async getRoles() {
return await this.getRoles();
}
/**
* 检查用户是否具有指定角色
* @param {String|Array} roleName 角色名称或角色名称数组
* @returns {Promise<Boolean>} 检查结果
*/
async hasRole(roleName) {
const roles = await this.getRoles();
const roleNames = roles.map(role => role.name);
if (Array.isArray(roleName)) {
return roleName.some(name => roleNames.includes(name));
}
return roleNames.includes(roleName);
}
/**
* 为用户分配角色
* @param {Number|Array} roleId 角色ID或角色ID数组
* @returns {Promise<Boolean>} 分配结果
*/
async assignRole(roleId) {
try {
if (Array.isArray(roleId)) {
await this.addRoles(roleId);
} else {
await this.addRole(roleId);
}
return true;
} catch (error) {
console.error('分配角色失败:', error);
return false;
}
}
/**
* 移除用户角色
* @param {Number|Array} roleId 角色ID或角色ID数组
* @returns {Promise<Boolean>} 移除结果
*/
async removeRole(roleId) {
try {
if (Array.isArray(roleId)) {
await this.removeRoles(roleId);
} else {
await this.removeRole(roleId);
}
return true;
} catch (error) {
console.error('移除角色失败:', error);
return false;
}
}
/**
* 获取用户安全信息(不包含密码)
* @returns {Object} 用户安全信息
*/
getSafeInfo() {
const { password, ...safeInfo } = this.get({ plain: true });
return safeInfo;
}
}
// 初始化User模型
User.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING(255),
allowNull: false
},
phone: {
type: DataTypes.STRING(20),
allowNull: true
},
avatar: {
type: DataTypes.STRING(255),
allowNull: true
},
status: {
type: DataTypes.ENUM('active', 'inactive', 'suspended'),
defaultValue: 'active'
}
}, {
sequelize,
tableName: 'users',
modelName: 'User',
hooks: {
beforeCreate: async (user) => {
if (user.password) {
user.password = await bcrypt.hash(user.password, 10);
}
},
beforeUpdate: async (user) => {
if (user.changed('password')) {
user.password = await bcrypt.hash(user.password, 10);
}
}
}
});
module.exports = User;

123
backend/models/UserRole.js Normal file
View File

@@ -0,0 +1,123 @@
/**
* UserRole 模型定义
* @file UserRole.js
* @description 定义用户角色关联模型,用于实现用户和角色的多对多关系
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
class UserRole extends BaseModel {
/**
* 获取用户角色分配记录
* @param {Number} userId 用户ID
* @param {Number} roleId 角色ID
* @returns {Promise<UserRole|null>} 用户角色分配记录
*/
static async findUserRole(userId, roleId) {
return await this.findOne({
where: {
user_id: userId,
role_id: roleId
}
});
}
/**
* 获取用户的所有角色分配记录
* @param {Number} userId 用户ID
* @returns {Promise<Array>} 用户角色分配记录列表
*/
static async findUserRoles(userId) {
return await this.findAll({
where: {
user_id: userId
}
});
}
/**
* 获取角色的所有用户分配记录
* @param {Number} roleId 角色ID
* @returns {Promise<Array>} 角色用户分配记录列表
*/
static async findRoleUsers(roleId) {
return await this.findAll({
where: {
role_id: roleId
}
});
}
/**
* 分配用户角色
* @param {Number} userId 用户ID
* @param {Number} roleId 角色ID
* @returns {Promise<UserRole>} 用户角色分配记录
*/
static async assignRole(userId, roleId) {
const [userRole, created] = await this.findOrCreate({
where: {
user_id: userId,
role_id: roleId
},
defaults: {
assigned_at: new Date()
}
});
return { userRole, created };
}
/**
* 移除用户角色
* @param {Number} userId 用户ID
* @param {Number} roleId 角色ID
* @returns {Promise<Boolean>} 移除结果
*/
static async removeRole(userId, roleId) {
const deleted = await this.destroy({
where: {
user_id: userId,
role_id: roleId
}
});
return deleted > 0;
}
}
// 初始化UserRole模型
UserRole.init({
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
references: {
model: 'users',
key: 'id'
},
onDelete: 'CASCADE'
},
role_id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
references: {
model: 'roles',
key: 'id'
},
onDelete: 'CASCADE'
},
assigned_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW
}
}, {
sequelize,
tableName: 'user_roles',
modelName: 'UserRole',
timestamps: false
});
module.exports = UserRole;

171
backend/models/index.js Normal file
View File

@@ -0,0 +1,171 @@
/**
* 模型索引文件
* @file index.js
* @description 导出所有模型并建立关联关系
*/
const { sequelize } = require('../config/database-simple');
const BaseModel = require('./BaseModel');
const Farm = require('./Farm');
const Animal = require('./Animal');
const Device = require('./Device');
const Alert = require('./Alert');
const User = require('./User');
const Role = require('./Role');
const UserRole = require('./UserRole');
const Product = require('./Product');
const Order = require('./Order');
const OrderItem = require('./OrderItem');
const SensorData = require('./SensorData');
// 建立模型之间的关联关系
// 养殖场与动物的一对多关系
Farm.hasMany(Animal, {
foreignKey: 'farm_id',
as: 'animals',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
Animal.belongsTo(Farm, {
foreignKey: 'farm_id',
as: 'farm'
});
// 养殖场与设备的一对多关系
Farm.hasMany(Device, {
foreignKey: 'farm_id',
as: 'devices',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
Device.belongsTo(Farm, {
foreignKey: 'farm_id',
as: 'farm'
});
// 养殖场与预警的一对多关系
Farm.hasMany(Alert, {
foreignKey: 'farm_id',
as: 'alerts',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
Alert.belongsTo(Farm, {
foreignKey: 'farm_id',
as: 'farm'
});
// 设备与预警的一对多关系
Device.hasMany(Alert, {
foreignKey: 'device_id',
as: 'alerts',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
Alert.belongsTo(Device, {
foreignKey: 'device_id',
as: 'device'
});
// 设备与传感器数据的一对多关系
Device.hasMany(SensorData, {
foreignKey: 'device_id',
as: 'sensorData',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
SensorData.belongsTo(Device, {
foreignKey: 'device_id',
as: 'device'
});
// 养殖场与传感器数据的一对多关系
Farm.hasMany(SensorData, {
foreignKey: 'farm_id',
as: 'sensorData',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
SensorData.belongsTo(Farm, {
foreignKey: 'farm_id',
as: 'farm'
});
// 用户与角色的多对多关系
User.belongsToMany(Role, {
through: UserRole,
foreignKey: 'user_id',
otherKey: 'role_id',
as: 'roles'
});
Role.belongsToMany(User, {
through: UserRole,
foreignKey: 'role_id',
otherKey: 'user_id',
as: 'users'
});
// 同步所有模型
const syncModels = async (options = {}) => {
try {
await sequelize.sync(options);
console.log('所有模型已同步到数据库');
return true;
} catch (error) {
console.error('模型同步失败:', error);
return false;
}
};
// 用户与订单的一对多关系
User.hasMany(Order, {
foreignKey: 'user_id',
as: 'orders',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
Order.belongsTo(User, {
foreignKey: 'user_id',
as: 'user'
});
// 订单与订单项的一对多关系
Order.hasMany(OrderItem, {
foreignKey: 'order_id',
as: 'orderItems',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
OrderItem.belongsTo(Order, {
foreignKey: 'order_id',
as: 'order'
});
// 产品与订单项的一对多关系
Product.hasMany(OrderItem, {
foreignKey: 'product_id',
as: 'orderItems',
onDelete: 'RESTRICT',
onUpdate: 'CASCADE'
});
OrderItem.belongsTo(Product, {
foreignKey: 'product_id',
as: 'product'
});
module.exports = {
sequelize,
BaseModel,
Farm,
Animal,
Device,
Alert,
User,
Role,
UserRole,
Product,
Order,
OrderItem,
SensorData,
syncModels
};