修改管理后台
This commit is contained in:
243
backend/models/ElectronicFence.js
Normal file
243
backend/models/ElectronicFence.js
Normal file
@@ -0,0 +1,243 @@
|
||||
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: '是否启用'
|
||||
},
|
||||
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']
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义关联关系
|
||||
*/
|
||||
static associate(models) {
|
||||
// 围栏与农场关联(可选)
|
||||
this.belongsTo(models.Farm, {
|
||||
foreignKey: 'farm_id',
|
||||
as: 'farm'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取围栏类型文本
|
||||
*/
|
||||
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
|
||||
Reference in New Issue
Block a user