2025-09-12 16:28:28 +08:00
|
|
|
|
package repository
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-11-05 23:00:07 +08:00
|
|
|
|
"context"
|
2025-09-12 16:28:28 +08:00
|
|
|
|
"fmt"
|
|
|
|
|
|
"strconv"
|
|
|
|
|
|
|
2025-11-05 23:00:07 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
2025-09-12 16:28:28 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
2025-11-05 23:00:07 +08:00
|
|
|
|
|
2025-09-12 16:28:28 +08:00
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// DeviceRepository 定义了与设备模型相关的数据库操作接口
|
|
|
|
|
|
// 这一层抽象使得上层业务逻辑无需关心底层数据库的具体实现。
|
|
|
|
|
|
type DeviceRepository interface {
|
|
|
|
|
|
// Create 创建一个新设备记录
|
2025-11-05 23:00:07 +08:00
|
|
|
|
Create(ctx context.Context, device *models.Device) error
|
2025-09-12 16:28:28 +08:00
|
|
|
|
|
|
|
|
|
|
// FindByID 根据主键 ID 查找设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
FindByID(ctx context.Context, id uint) (*models.Device, error)
|
2025-09-12 16:28:28 +08:00
|
|
|
|
|
2025-11-03 16:29:57 +08:00
|
|
|
|
// FindByIDString 根据字符串形式的主键 ID 查找设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
FindByIDString(ctx context.Context, id string) (*models.Device, error)
|
2025-09-12 16:28:28 +08:00
|
|
|
|
|
2025-09-12 17:18:14 +08:00
|
|
|
|
// ListAll 获取所有设备的列表
|
2025-11-05 23:00:07 +08:00
|
|
|
|
ListAll(ctx context.Context) ([]*models.Device, error)
|
2025-09-12 17:18:14 +08:00
|
|
|
|
|
2025-10-26 15:10:38 +08:00
|
|
|
|
// ListAllSensors 获取所有传感器类型的设备列表
|
2025-11-05 23:00:07 +08:00
|
|
|
|
ListAllSensors(ctx context.Context) ([]*models.Device, error)
|
2025-10-26 15:10:38 +08:00
|
|
|
|
|
2025-11-03 16:29:57 +08:00
|
|
|
|
// ListByAreaControllerID 根据区域主控 ID 列出所有子设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
ListByAreaControllerID(ctx context.Context, areaControllerID uint) ([]*models.Device, error)
|
2025-09-12 16:28:28 +08:00
|
|
|
|
|
2025-09-30 22:07:55 +08:00
|
|
|
|
// FindByDeviceTemplateID 根据设备模板ID查找所有使用该模板的设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
FindByDeviceTemplateID(ctx context.Context, deviceTemplateID uint) ([]*models.Device, error)
|
2025-09-30 22:07:55 +08:00
|
|
|
|
|
2025-09-12 16:28:28 +08:00
|
|
|
|
// Update 更新一个已有的设备信息
|
2025-11-05 23:00:07 +08:00
|
|
|
|
Update(ctx context.Context, device *models.Device) error
|
2025-09-12 16:28:28 +08:00
|
|
|
|
|
|
|
|
|
|
// Delete 根据主键 ID 删除一个设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
Delete(ctx context.Context, id uint) error
|
2025-09-24 21:53:18 +08:00
|
|
|
|
|
2025-09-29 17:27:42 +08:00
|
|
|
|
// FindByAreaControllerAndPhysicalAddress 根据区域主控ID和物理地址(总线号、总线地址)查找设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
FindByAreaControllerAndPhysicalAddress(ctx context.Context, areaControllerID uint, busNumber int, busAddress int) (*models.Device, error)
|
2025-11-03 16:29:57 +08:00
|
|
|
|
|
|
|
|
|
|
// GetDevicesByIDsTx 在指定事务中根据ID列表获取设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
GetDevicesByIDsTx(ctx context.Context, tx *gorm.DB, ids []uint) ([]models.Device, error)
|
2025-11-03 16:46:23 +08:00
|
|
|
|
|
|
|
|
|
|
// IsDeviceInUse 检查设备是否被任何任务使用
|
2025-11-05 23:00:07 +08:00
|
|
|
|
IsDeviceInUse(ctx context.Context, deviceID uint) (bool, error)
|
2025-11-03 17:11:51 +08:00
|
|
|
|
|
|
|
|
|
|
// IsAreaControllerInUse 检查区域主控是否被任何设备使用
|
2025-11-05 23:00:07 +08:00
|
|
|
|
IsAreaControllerInUse(ctx context.Context, areaControllerID uint) (bool, error)
|
2025-09-12 16:28:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// gormDeviceRepository 是 DeviceRepository 的 GORM 实现
|
|
|
|
|
|
type gormDeviceRepository struct {
|
2025-11-05 23:00:07 +08:00
|
|
|
|
ctx context.Context
|
|
|
|
|
|
db *gorm.DB
|
2025-09-12 16:28:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewGormDeviceRepository 创建一个新的 DeviceRepository GORM 实现实例
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func NewGormDeviceRepository(ctx context.Context, db *gorm.DB) DeviceRepository {
|
|
|
|
|
|
return &gormDeviceRepository{ctx: ctx, db: db}
|
2025-09-12 16:28:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Create 创建一个新的设备记录
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) Create(ctx context.Context, device *models.Device) error {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "Create")
|
|
|
|
|
|
// BeforeSave 钩子会在这里被自动触发
|
|
|
|
|
|
return r.db.WithContext(repoCtx).Create(device).Error
|
2025-09-12 16:28:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FindByID 根据 ID 查找设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) FindByID(ctx context.Context, id uint) (*models.Device, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByID")
|
2025-09-12 16:28:28 +08:00
|
|
|
|
var device models.Device
|
2025-11-05 23:00:07 +08:00
|
|
|
|
if err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate").First(&device, id).Error; err != nil {
|
2025-09-12 16:28:28 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
return &device, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-03 16:29:57 +08:00
|
|
|
|
// GetDevicesByIDsTx 在指定事务中根据ID列表获取设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) GetDevicesByIDsTx(ctx context.Context, tx *gorm.DB, ids []uint) ([]models.Device, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "GetDevicesByIDsTx")
|
2025-11-03 16:29:57 +08:00
|
|
|
|
var devices []models.Device
|
|
|
|
|
|
if len(ids) == 0 {
|
|
|
|
|
|
return devices, nil
|
|
|
|
|
|
}
|
2025-11-05 23:00:07 +08:00
|
|
|
|
if err := tx.WithContext(repoCtx).Where("id IN ?", ids).Find(&devices).Error; err != nil {
|
2025-11-03 16:29:57 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
return devices, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-12 16:28:28 +08:00
|
|
|
|
// FindByIDString 根据字符串形式的主键 ID 查找设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) FindByIDString(ctx context.Context, id string) (*models.Device, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByIDString")
|
2025-09-12 16:28:28 +08:00
|
|
|
|
// 将字符串ID转换为uint64
|
|
|
|
|
|
idInt, err := strconv.ParseUint(id, 10, 64)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
// 如果转换失败,说明ID格式不正确,返回一个明确的错误
|
|
|
|
|
|
return nil, fmt.Errorf("无效的设备ID格式: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 调用已有的 FindByID 方法
|
2025-11-05 23:00:07 +08:00
|
|
|
|
return r.FindByID(repoCtx, uint(idInt))
|
2025-09-12 16:28:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-12 17:18:14 +08:00
|
|
|
|
// ListAll 获取所有设备的列表
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) ListAll(ctx context.Context) ([]*models.Device, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListAll")
|
2025-09-12 17:18:14 +08:00
|
|
|
|
var devices []*models.Device
|
2025-11-05 23:00:07 +08:00
|
|
|
|
if err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate").Find(&devices).Error; err != nil {
|
2025-09-12 17:18:14 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
return devices, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 15:10:38 +08:00
|
|
|
|
// ListAllSensors 检索归类为传感器的所有设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) ListAllSensors(ctx context.Context) ([]*models.Device, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListAllSensors")
|
2025-10-26 15:10:38 +08:00
|
|
|
|
var sensors []*models.Device
|
2025-11-05 23:00:07 +08:00
|
|
|
|
err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate").
|
2025-10-26 15:10:38 +08:00
|
|
|
|
Joins("JOIN device_templates ON device_templates.id = devices.device_template_id").
|
|
|
|
|
|
Where("device_templates.category = ?", models.CategorySensor).
|
|
|
|
|
|
Find(&sensors).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("查询所有传感器失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return sensors, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-29 17:27:42 +08:00
|
|
|
|
// ListByAreaControllerID 根据区域主控 ID 列出所有子设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) ListByAreaControllerID(ctx context.Context, areaControllerID uint) ([]*models.Device, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListByAreaControllerID")
|
2025-09-12 16:28:28 +08:00
|
|
|
|
var devices []*models.Device
|
2025-11-05 23:00:07 +08:00
|
|
|
|
err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate").Where("area_controller_id = ?", areaControllerID).Find(&devices).Error
|
2025-09-12 16:28:28 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
return devices, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-30 22:07:55 +08:00
|
|
|
|
// FindByDeviceTemplateID 根据设备模板ID查找所有使用该模板的设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) FindByDeviceTemplateID(ctx context.Context, deviceTemplateID uint) ([]*models.Device, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByDeviceTemplateID")
|
2025-09-30 22:07:55 +08:00
|
|
|
|
var devices []*models.Device
|
2025-11-05 23:00:07 +08:00
|
|
|
|
err := r.db.WithContext(repoCtx).Where("device_template_id = ?", deviceTemplateID).Find(&devices).Error
|
2025-09-30 22:07:55 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("查询使用设备模板ID %d 的设备失败: %w", deviceTemplateID, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return devices, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-12 16:28:28 +08:00
|
|
|
|
// Update 更新一个已有的设备信息
|
|
|
|
|
|
// GORM 的 Save 方法会自动处理主键存在时更新,不存在时创建的逻辑,但这里我们明确用于更新。
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) Update(ctx context.Context, device *models.Device) error {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "Update")
|
|
|
|
|
|
return r.db.WithContext(repoCtx).Save(device).Error
|
2025-09-12 16:28:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Delete 根据 ID 删除一个设备
|
|
|
|
|
|
// GORM 使用软删除,记录不会从数据库中物理移除,而是设置 DeletedAt 字段。
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) Delete(ctx context.Context, id uint) error {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "Delete")
|
|
|
|
|
|
return r.db.WithContext(repoCtx).Delete(&models.Device{}, id).Error
|
2025-09-12 16:28:28 +08:00
|
|
|
|
}
|
2025-09-24 21:53:18 +08:00
|
|
|
|
|
2025-09-29 17:27:42 +08:00
|
|
|
|
// FindByAreaControllerAndPhysicalAddress 根据区域主控ID和物理地址(总线号、总线地址)查找设备
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) FindByAreaControllerAndPhysicalAddress(ctx context.Context, areaControllerID uint, busNumber int, busAddress int) (*models.Device, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "FindByAreaControllerAndPhysicalAddress")
|
2025-09-26 15:26:21 +08:00
|
|
|
|
var device models.Device
|
2025-11-05 23:00:07 +08:00
|
|
|
|
err := r.db.WithContext(repoCtx).Preload("AreaController").Preload("DeviceTemplate").
|
2025-09-29 17:27:42 +08:00
|
|
|
|
Where("area_controller_id = ?", areaControllerID).
|
|
|
|
|
|
Where("properties->>'bus_number' = ?", strconv.Itoa(busNumber)).
|
|
|
|
|
|
Where("properties->>'bus_address' = ?", strconv.Itoa(busAddress)).
|
2025-09-26 15:26:21 +08:00
|
|
|
|
First(&device).Error
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
2025-09-29 17:27:42 +08:00
|
|
|
|
return nil, fmt.Errorf("根据区域主控ID %d 和物理地址 (总线号: %d, 总线地址: %d) 查找设备失败: %w", areaControllerID, busNumber, busAddress, err)
|
2025-09-26 15:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
return &device, nil
|
|
|
|
|
|
}
|
2025-11-03 16:46:23 +08:00
|
|
|
|
|
|
|
|
|
|
// IsDeviceInUse 检查设备是否被任何任务使用
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) IsDeviceInUse(ctx context.Context, deviceID uint) (bool, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "IsDeviceInUse")
|
2025-11-03 16:46:23 +08:00
|
|
|
|
var count int64
|
|
|
|
|
|
// 直接对 device_tasks 关联表进行 COUNT 操作,性能最高
|
2025-11-05 23:00:07 +08:00
|
|
|
|
err := r.db.WithContext(repoCtx).Model(&models.DeviceTask{}).Where("device_id = ?", deviceID).Count(&count).Error
|
2025-11-03 16:46:23 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return false, fmt.Errorf("查询设备任务关联失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return count > 0, nil
|
|
|
|
|
|
}
|
2025-11-03 17:11:51 +08:00
|
|
|
|
|
|
|
|
|
|
// IsAreaControllerInUse 检查区域主控是否被任何设备使用
|
2025-11-05 23:00:07 +08:00
|
|
|
|
func (r *gormDeviceRepository) IsAreaControllerInUse(ctx context.Context, areaControllerID uint) (bool, error) {
|
|
|
|
|
|
repoCtx := logs.AddFuncName(ctx, r.ctx, "IsAreaControllerInUse")
|
2025-11-03 17:11:51 +08:00
|
|
|
|
var count int64
|
2025-11-05 23:00:07 +08:00
|
|
|
|
if err := r.db.WithContext(repoCtx).Model(&models.Device{}).Where("area_controller_id = ?", areaControllerID).Count(&count).Error; err != nil {
|
2025-11-03 17:11:51 +08:00
|
|
|
|
return false, fmt.Errorf("检查区域主控使用情况失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return count > 0, nil
|
|
|
|
|
|
}
|