Files
nxxmdata/backend/models/ElectronicFence.js

249 lines
6.1 KiB
JavaScript
Raw Normal View History

2025-09-12 20:08:42 +08:00
const { DataTypes } = require('sequelize')
const BaseModel = require('./BaseModel')
const { sequelize } = require('../config/database-simple')
/**
* 电子围栏模型
*/
class ElectronicFence extends BaseModel {
static init(sequelize) {
return super.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '围栏ID'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '围栏名称'
},
type: {
type: DataTypes.ENUM('collector', 'grazing', 'safety'),
allowNull: false,
defaultValue: 'collector',
comment: '围栏类型: collector-采集器电子围栏, grazing-放牧围栏, safety-安全围栏'
},
description: {
type: DataTypes.TEXT,
allowNull: true,
comment: '围栏描述'
},
coordinates: {
type: DataTypes.JSON,
allowNull: false,
comment: '围栏坐标点数组'
},
center_lng: {
type: DataTypes.DECIMAL(10, 7),
allowNull: false,
comment: '围栏中心经度'
},
center_lat: {
type: DataTypes.DECIMAL(10, 7),
allowNull: false,
comment: '围栏中心纬度'
},
area: {
type: DataTypes.DECIMAL(10, 4),
allowNull: true,
comment: '围栏面积(平方米)'
},
grazing_status: {
type: DataTypes.ENUM('grazing', 'not_grazing'),
allowNull: false,
defaultValue: 'not_grazing',
comment: '放牧状态: grazing-放牧中, not_grazing-未放牧'
},
inside_count: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '安全区域内动物数量'
},
outside_count: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '安全区域外动物数量'
},
is_active: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否启用'
},
2025-09-15 18:18:41 +08:00
farm_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '关联农场ID'
},
2025-09-12 20:08:42 +08:00
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '创建人ID'
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '更新人ID'
}
}, {
sequelize,
modelName: 'ElectronicFence',
tableName: 'electronic_fences',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
comment: '电子围栏表',
indexes: [
{
name: 'idx_fence_name',
fields: ['name']
},
{
name: 'idx_fence_type',
fields: ['type']
},
{
name: 'idx_fence_center',
fields: ['center_lng', 'center_lat']
},
{
name: 'idx_fence_active',
fields: ['is_active']
}
]
})
}
/**
* 定义关联关系已在 models/index.js 中定义
2025-09-12 20:08:42 +08:00
*/
// static associate(models) {
// // 围栏与农场关联(可选)
// this.belongsTo(models.Farm, {
// foreignKey: 'farm_id',
// as: 'farm'
// })
// }
2025-09-12 20:08:42 +08:00
/**
* 获取围栏类型文本
*/
getTypeText() {
const typeMap = {
'collector': '采集器电子围栏',
'grazing': '放牧围栏',
'safety': '安全围栏'
}
return typeMap[this.type] || '未知类型'
}
/**
* 获取放牧状态文本
*/
getGrazingStatusText() {
const statusMap = {
'grazing': '放牧中',
'not_grazing': '未放牧'
}
return statusMap[this.grazing_status] || '未知状态'
}
/**
* 计算围栏面积简化计算
*/
calculateArea() {
if (!this.coordinates || this.coordinates.length < 3) {
return 0
}
// 使用Shoelace公式计算多边形面积
let area = 0
const coords = this.coordinates
for (let i = 0; i < coords.length; i++) {
const j = (i + 1) % coords.length
area += coords[i].lng * coords[j].lat
area -= coords[j].lng * coords[i].lat
}
// 转换为平方米(粗略计算)
return Math.abs(area) * 111000 * 111000 / 2
}
/**
* 计算围栏中心点
*/
calculateCenter() {
if (!this.coordinates || this.coordinates.length === 0) {
return { lng: 0, lat: 0 }
}
let lngSum = 0
let latSum = 0
this.coordinates.forEach(coord => {
lngSum += coord.lng
latSum += coord.lat
})
return {
lng: lngSum / this.coordinates.length,
lat: latSum / this.coordinates.length
}
}
/**
* 检查点是否在围栏内
*/
isPointInside(lng, lat) {
if (!this.coordinates || this.coordinates.length < 3) {
return false
}
let inside = false
const coords = this.coordinates
for (let i = 0, j = coords.length - 1; i < coords.length; j = i++) {
if (((coords[i].lat > lat) !== (coords[j].lat > lat)) &&
(lng < (coords[j].lng - coords[i].lng) * (lat - coords[i].lat) / (coords[j].lat - coords[i].lat) + coords[i].lng)) {
inside = !inside
}
}
return inside
}
/**
* 转换为前端格式
*/
toFrontendFormat() {
return {
id: this.id,
name: this.name,
type: this.getTypeText(),
description: this.description,
coordinates: this.coordinates,
center: {
lng: this.center_lng,
lat: this.center_lat
},
area: this.area,
grazingStatus: this.getGrazingStatusText(),
insideCount: this.inside_count,
outsideCount: this.outside_count,
isActive: this.is_active,
createdAt: this.created_at,
updatedAt: this.updated_at
}
}
}
// 初始化模型
ElectronicFence.init(sequelize)
module.exports = ElectronicFence