Răsfoiți Sursa

Add 添加设备维护基础表

YueYunyun 2 ani în urmă
părinte
comite
7be66e512c

+ 1 - 1
SERVER/IotAdmin/_scripts/Cmd/CreateApp.run.xml

@@ -3,7 +3,7 @@
     <module name="IotAdmin" />
     <working_directory value="$PROJECT_DIR$" />
     <go_parameters value="-tags=createApp " />
-    <parameters value="-n=" />
+    <parameters value="-n=iot" />
     <kind value="PACKAGE" />
     <package value="IotAdmin" />
     <directory value="$PROJECT_DIR$" />

+ 197 - 0
SERVER/IotAdmin/app/iot/apis/device.go

@@ -0,0 +1,197 @@
+package apis
+
+import (
+    "fmt"
+
+	"github.com/gin-gonic/gin"
+	"IotAdmin/core/sdk/api"
+	"IotAdmin/core/sdk/pkg/jwt-auth/user"
+	_ "IotAdmin/core/sdk/pkg/response"
+
+	"IotAdmin/app/iot/models"
+	"IotAdmin/app/iot/service"
+	"IotAdmin/app/iot/service/dto"
+	"IotAdmin/common/permission"
+)
+
+// IotDeviceApi 设备接口
+type IotDeviceApi struct {
+	api.Api
+}
+
+// GetPage 获取设备列表
+// @Summary 获取设备列表
+// @Description 获取设备列表
+// @Tags 设备管理
+// @Param groupId query string false "设备分组"
+// @Param sn query string false "设备编码"
+// @Param name query string false "设备名称"
+// @Param type query string false "设备类型"
+// @Param mode query string false "设备模式"
+// @Param pageSize query int false "页条数"
+// @Param pageIndex query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]models.IotDevice}} "{"code": 200, "data": [...]}"
+// @Router /api/iot-device [get]
+// @Security Bearer
+func (e IotDeviceApi) GetPage(c *gin.Context) {
+    req := dto.IotDeviceGetPageReq{}
+    s := service.IotDeviceService{}
+    err := e.MakeContext(c).
+        MakeOrm().
+        Bind(&req).
+        MakeService(&s.Service).
+        Errors
+   	if err != nil {
+   		e.Logger.Error(err)
+   		e.Error(500, err, err.Error())
+   		return
+   	}
+
+	p :=  permission.GetPermissionFromContext(c)
+	list := make([]models.IotDevice, 0)
+	var count int64
+
+	err = s.GetPage(&req, p, &list, &count)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("获取设备失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+
+// Get 获取设备
+// @Summary 获取设备
+// @Description 获取设备
+// @Tags 设备管理
+// @Param id path int false "id"
+// @Success 200 {object} response.Response{data=models.IotDevice} "{"code": 200, "data": [...]}"
+// @Router /api/iot-device/{id} [get]
+// @Security Bearer
+func (e IotDeviceApi) Get(c *gin.Context) {
+	req := dto.IotDeviceGetReq{}
+	s := service.IotDeviceService{}
+    err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	var object models.IotDevice
+
+	p :=  permission.GetPermissionFromContext(c)
+	err = s.Get(&req, p, &object)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("获取设备失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+
+	e.OK( object, "查询成功")
+}
+
+// Insert 添加设备
+// @Summary 添加设备
+// @Description 添加设备
+// @Tags 设备管理
+// @Accept application/json
+// @Product application/json
+// @Param data body dto.IotDeviceInsertReq true "data"
+// @Success 200 {object} response.Response	"{"code": 200, "message": "添加成功"}"
+// @Router /api/iot-device [post]
+// @Security Bearer
+func (e IotDeviceApi) Insert(c *gin.Context) {
+    req := dto.IotDeviceInsertReq{}
+    s := service.IotDeviceService{}
+    err := e.MakeContext(c).
+        MakeOrm().
+        Bind(&req).
+        MakeService(&s.Service).
+        Errors
+    if err != nil {
+        e.Logger.Error(err)
+        e.Error(500, err, err.Error())
+        return
+    }
+	// 设置创建人
+	req.SetCreateBy(user.GetUserId(c))
+
+	err = s.Insert(&req)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("添加设备失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+
+	e.OK(req.GetId(), "添加成功")
+}
+
+// Update 修改设备
+// @Summary 修改设备
+// @Description 修改设备
+// @Tags 设备管理
+// @Accept application/json
+// @Product application/json
+// @Param id path int true "id"
+// @Param data body dto.IotDeviceUpdateReq true "body"
+// @Success 200 {object} response.Response	"{"code": 200, "message": "修改成功"}"
+// @Router /api/iot-device/{id} [put]
+// @Security Bearer
+func (e IotDeviceApi) Update(c *gin.Context) {
+    req := dto.IotDeviceUpdateReq{}
+    s := service.IotDeviceService{}
+    err := e.MakeContext(c).
+        MakeOrm().
+        Bind(&req).
+        MakeService(&s.Service).
+        Errors
+    if err != nil {
+        e.Logger.Error(err)
+        e.Error(500, err, err.Error())
+        return
+    }
+	req.SetUpdateBy(user.GetUserId(c))
+	p :=  permission.GetPermissionFromContext(c)
+
+	err = s.Update(&req, p)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("修改设备失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+	e.OK( req.GetId(), "修改成功")
+}
+
+// Delete 删除设备
+// @Summary 删除设备
+// @Description 删除设备
+// @Tags 设备管理
+// @Param data body dto.IotDeviceDeleteReq true "body"
+// @Success 200 {object} response.Response	"{"code": 200, "message": "删除成功"}"
+// @Router /api/iot-device [delete]
+// @Security Bearer
+func (e IotDeviceApi) Delete(c *gin.Context) {
+    s := service.IotDeviceService{}
+    req := dto.IotDeviceDeleteReq{}
+    err := e.MakeContext(c).
+        MakeOrm().
+        Bind(&req).
+        MakeService(&s.Service).
+        Errors
+    if err != nil {
+        e.Logger.Error(err)
+        e.Error(500, err, err.Error())
+        return
+    }
+
+	// req.SetUpdateBy(user.GetUserId(c))
+	p :=  permission.GetPermissionFromContext(c)
+
+	err = s.Remove(&req, p)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("删除设备失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+	e.OK( req.GetId(), "删除成功")
+}

+ 195 - 0
SERVER/IotAdmin/app/iot/apis/group.go

@@ -0,0 +1,195 @@
+package apis
+
+import (
+    "fmt"
+
+	"github.com/gin-gonic/gin"
+	"IotAdmin/core/sdk/api"
+	"IotAdmin/core/sdk/pkg/jwt-auth/user"
+	_ "IotAdmin/core/sdk/pkg/response"
+
+	"IotAdmin/app/iot/models"
+	"IotAdmin/app/iot/service"
+	"IotAdmin/app/iot/service/dto"
+	"IotAdmin/common/permission"
+)
+
+// IotGroupApi 分组接口
+type IotGroupApi struct {
+	api.Api
+}
+
+// GetPage 获取分组列表
+// @Summary 获取分组列表
+// @Description 获取分组列表
+// @Tags 分组管理
+// @Param path query string false "分组路径"
+// @Param name query string false "分组名称"
+// @Param description query string false "分组描述"
+// @Param pageSize query int false "页条数"
+// @Param pageIndex query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]models.IotGroup}} "{"code": 200, "data": [...]}"
+// @Router /api/iot-group [get]
+// @Security Bearer
+func (e IotGroupApi) GetPage(c *gin.Context) {
+    req := dto.IotGroupGetPageReq{}
+    s := service.IotGroupService{}
+    err := e.MakeContext(c).
+        MakeOrm().
+        Bind(&req).
+        MakeService(&s.Service).
+        Errors
+   	if err != nil {
+   		e.Logger.Error(err)
+   		e.Error(500, err, err.Error())
+   		return
+   	}
+
+	p :=  permission.GetPermissionFromContext(c)
+	list := make([]models.IotGroup, 0)
+	var count int64
+
+	err = s.GetPage(&req, p, &list, &count)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("获取分组失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+
+// Get 获取分组
+// @Summary 获取分组
+// @Description 获取分组
+// @Tags 分组管理
+// @Param id path int false "id"
+// @Success 200 {object} response.Response{data=models.IotGroup} "{"code": 200, "data": [...]}"
+// @Router /api/iot-group/{id} [get]
+// @Security Bearer
+func (e IotGroupApi) Get(c *gin.Context) {
+	req := dto.IotGroupGetReq{}
+	s := service.IotGroupService{}
+    err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	var object models.IotGroup
+
+	p :=  permission.GetPermissionFromContext(c)
+	err = s.Get(&req, p, &object)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("获取分组失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+
+	e.OK( object, "查询成功")
+}
+
+// Insert 添加分组
+// @Summary 添加分组
+// @Description 添加分组
+// @Tags 分组管理
+// @Accept application/json
+// @Product application/json
+// @Param data body dto.IotGroupInsertReq true "data"
+// @Success 200 {object} response.Response	"{"code": 200, "message": "添加成功"}"
+// @Router /api/iot-group [post]
+// @Security Bearer
+func (e IotGroupApi) Insert(c *gin.Context) {
+    req := dto.IotGroupInsertReq{}
+    s := service.IotGroupService{}
+    err := e.MakeContext(c).
+        MakeOrm().
+        Bind(&req).
+        MakeService(&s.Service).
+        Errors
+    if err != nil {
+        e.Logger.Error(err)
+        e.Error(500, err, err.Error())
+        return
+    }
+	// 设置创建人
+	req.SetCreateBy(user.GetUserId(c))
+
+	err = s.Insert(&req)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("添加分组失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+
+	e.OK(req.GetId(), "添加成功")
+}
+
+// Update 修改分组
+// @Summary 修改分组
+// @Description 修改分组
+// @Tags 分组管理
+// @Accept application/json
+// @Product application/json
+// @Param id path int true "id"
+// @Param data body dto.IotGroupUpdateReq true "body"
+// @Success 200 {object} response.Response	"{"code": 200, "message": "修改成功"}"
+// @Router /api/iot-group/{id} [put]
+// @Security Bearer
+func (e IotGroupApi) Update(c *gin.Context) {
+    req := dto.IotGroupUpdateReq{}
+    s := service.IotGroupService{}
+    err := e.MakeContext(c).
+        MakeOrm().
+        Bind(&req).
+        MakeService(&s.Service).
+        Errors
+    if err != nil {
+        e.Logger.Error(err)
+        e.Error(500, err, err.Error())
+        return
+    }
+	req.SetUpdateBy(user.GetUserId(c))
+	p :=  permission.GetPermissionFromContext(c)
+
+	err = s.Update(&req, p)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("修改分组失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+	e.OK( req.GetId(), "修改成功")
+}
+
+// Delete 删除分组
+// @Summary 删除分组
+// @Description 删除分组
+// @Tags 分组管理
+// @Param data body dto.IotGroupDeleteReq true "body"
+// @Success 200 {object} response.Response	"{"code": 200, "message": "删除成功"}"
+// @Router /api/iot-group [delete]
+// @Security Bearer
+func (e IotGroupApi) Delete(c *gin.Context) {
+    s := service.IotGroupService{}
+    req := dto.IotGroupDeleteReq{}
+    err := e.MakeContext(c).
+        MakeOrm().
+        Bind(&req).
+        MakeService(&s.Service).
+        Errors
+    if err != nil {
+        e.Logger.Error(err)
+        e.Error(500, err, err.Error())
+        return
+    }
+
+	// req.SetUpdateBy(user.GetUserId(c))
+	p :=  permission.GetPermissionFromContext(c)
+
+	err = s.Remove(&req, p)
+	if err != nil {
+		e.Error(500, err, fmt.Sprintf("删除分组失败,\r\n失败信息 %s", err.Error()))
+        return
+	}
+	e.OK( req.GetId(), "删除成功")
+}

+ 41 - 0
SERVER/IotAdmin/app/iot/models/device.go

@@ -0,0 +1,41 @@
+package models
+
+import (
+	"IotAdmin/common/models"
+)
+
+// IotDevice 设备实体
+type IotDevice struct {
+	models.Model
+	ParentId    string    `json:"parentId" gorm:"type:bigint(20);comment:父ID"`
+	GroupId     string    `json:"groupId" gorm:"type:bigint(20);comment:设备分组"`
+	Sn          string    `json:"sn" gorm:"type:varchar(50);comment:设备编码"`
+	Name        string    `json:"name" gorm:"type:varchar(50);comment:设备名称"`
+	Type        string    `json:"type" gorm:"type:bigint(20);comment:设备类型"`
+	Mode        string    `json:"mode" gorm:"type:bigint(20);comment:设备模式"`
+	Cycle       string    `json:"cycle" gorm:"type:bigint(20);comment:上报周期"`
+	Dsn         string    `json:"dsn" gorm:"type:varchar(255);comment:设备Dsn"`
+	Description string    `json:"description" gorm:"type:varchar(255);comment:设备描述"`
+	Protocol    string    `json:"protocol" gorm:"type:varchar(255);comment:表计协议"`
+	Address     string    `json:"address" gorm:"type:bigint(20);comment:表计地址"`
+	LvRef       string    `json:"lvRef" gorm:"type:float;comment:表计线基准电压"`
+	PvRef       string    `json:"pvRef" gorm:"type:float;comment:表计相基准电压"`
+	BmYz        string    `json:"bmYz" gorm:"type:varchar(255);comment:编码因子"`
+	Config      string    `json:"config" gorm:"type:varchar(255);comment:其他配置"`
+	Group       *IotGroup `json:"group" gorm:"ForeignKey:GroupId;AssociationForeignKey:Id"`
+	models.ControlBy
+	models.ModelTime
+}
+
+func (*IotDevice) TableName() string {
+	return "iot_device"
+}
+
+func (e *IotDevice) Generate() models.ActiveRecord {
+	o := *e
+	return &o
+}
+
+func (e *IotDevice) GetId() interface{} {
+	return e.Id
+}

+ 31 - 0
SERVER/IotAdmin/app/iot/models/group.go

@@ -0,0 +1,31 @@
+package models
+
+import (
+
+	"IotAdmin/common/models"
+
+)
+
+// IotGroup 分组实体
+type IotGroup struct {
+    models.Model
+    ParentId string `json:"parentId" gorm:"type:bigint(20);comment:父ID"` 
+    Path string `json:"path" gorm:"type:varchar(255);comment:分组路径"` 
+    Name string `json:"name" gorm:"type:varchar(255);comment:分组名称"` 
+    Description string `json:"description" gorm:"type:varchar(255);comment:分组描述"` 
+    models.ControlBy
+    models.ModelTime
+}
+
+func (*IotGroup) TableName() string {
+    return "iot_group"
+}
+
+func (e *IotGroup) Generate() models.ActiveRecord {
+	o := *e
+	return &o
+}
+
+func (e *IotGroup) GetId() interface{} {
+	return e.Id
+}

+ 26 - 0
SERVER/IotAdmin/app/iot/router/device.go

@@ -0,0 +1,26 @@
+package router
+
+import (
+	"github.com/gin-gonic/gin"
+	jwt "IotAdmin/core/sdk/pkg/jwt-auth"
+
+	"IotAdmin/app/iot/apis"
+	"IotAdmin/common/middleware"
+)
+
+func init() {
+	routerCheckRole = append(routerCheckRole, registerIotDeviceRouter)
+}
+
+// registerIotDeviceRouter 注册设备路由,添加AuthCheckRole中间件检查权限
+func registerIotDeviceRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
+	api := apis.IotDeviceApi{}
+	r := v1.Group("/iot-device").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
+	{
+		r.GET("", api.GetPage)
+		r.GET("/:id", api.Get)
+		r.POST("", api.Insert)
+		r.PUT("/:id", api.Update)
+		r.DELETE("", api.Delete)
+	}
+}

+ 26 - 0
SERVER/IotAdmin/app/iot/router/group.go

@@ -0,0 +1,26 @@
+package router
+
+import (
+	"github.com/gin-gonic/gin"
+	jwt "IotAdmin/core/sdk/pkg/jwt-auth"
+
+	"IotAdmin/app/iot/apis"
+	"IotAdmin/common/middleware"
+)
+
+func init() {
+	routerCheckRole = append(routerCheckRole, registerIotGroupRouter)
+}
+
+// registerIotGroupRouter 注册分组路由,添加AuthCheckRole中间件检查权限
+func registerIotGroupRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
+	api := apis.IotGroupApi{}
+	r := v1.Group("/iot-group").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole())
+	{
+		r.GET("", api.GetPage)
+		r.GET("/:id", api.Get)
+		r.POST("", api.Insert)
+		r.PUT("/:id", api.Update)
+		r.DELETE("", api.Delete)
+	}
+}

+ 63 - 0
SERVER/IotAdmin/app/iot/router/router.go

@@ -0,0 +1,63 @@
+package router
+
+import (
+	"IotAdmin/common/middleware"
+	"IotAdmin/core/logger"
+	"IotAdmin/core/sdk"
+	jwt "IotAdmin/core/sdk/pkg/jwt-auth"
+	"os"
+
+	"github.com/gin-gonic/gin"
+)
+
+var (
+	routerNoCheckRole = make([]func(*gin.RouterGroup), 0)
+	routerCheckRole   = make([]func(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware), 0)
+)
+
+// InitRouter 路由初始化
+func InitRouter() {
+	var r *gin.Engine
+	h := sdk.Runtime.GetEngine()
+	if h == nil {
+		logger.Fatal("未发现Engine...")
+		os.Exit(-1)
+	}
+	switch h.(type) {
+	case *gin.Engine:
+		r = h.(*gin.Engine)
+	default:
+		logger.Fatal("不支持其他engine")
+		os.Exit(-1)
+	}
+
+	// jwt middleware
+	authMiddleware, err := middleware.AuthInit()
+	if err != nil {
+		logger.Fatalf("JWT初始化错误, %s", err.Error())
+	}
+
+	// 无需认证的路由
+	noCheckRoleRouter(r)
+	// 需要认证的路由
+	checkRoleRouter(r, authMiddleware)
+}
+
+// noCheckRoleRouter 无需认证的路由示例
+func noCheckRoleRouter(r *gin.Engine) {
+	// 可根据业务需求来设置接口版本
+	v1 := r.Group("/api")
+	for _, f := range routerNoCheckRole {
+		f(v1)
+	}
+}
+
+// checkRoleRouter 需要认证的路由示例
+func checkRoleRouter(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware) {
+	// 可根据业务需求来设置接口版本
+	v1 := r.Group("/api")
+
+	for _, f := range routerCheckRole {
+		f(v1, authMiddleware)
+	}
+}

+ 110 - 0
SERVER/IotAdmin/app/iot/service/device.go

@@ -0,0 +1,110 @@
+package service
+
+import (
+	"errors"
+
+    "IotAdmin/core/sdk/service"
+	"gorm.io/gorm"
+
+	"IotAdmin/app/iot/models"
+	"IotAdmin/app/iot/service/dto"
+	cDto "IotAdmin/common/dto"
+	"IotAdmin/common/permission"
+)
+
+// IotDeviceService 设备服务
+type IotDeviceService struct {
+	service.Service
+}
+
+// GetPage 获取设备列表
+func (e *IotDeviceService) GetPage(c *dto.IotDeviceGetPageReq, p * permission.DataPermission, list *[]models.IotDevice, count *int64) error {
+	var err error
+	var data models.IotDevice
+
+	err = e.Orm.Model(&data).
+		Scopes(
+			cDto.MakeCondition(c.GetNeedSearch()),
+			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
+			 permission.Permission(data.TableName(), p),
+		).
+		Find(list).Limit(-1).Offset(-1).
+		Count(count).Error
+	if err != nil {
+		e.Log.Errorf("IotDeviceService GetPage error:%s \r\n", err)
+		return err
+	}
+	return nil
+}
+
+// Get 获取设备对象
+func (e *IotDeviceService) Get(d *dto.IotDeviceGetReq, p * permission.DataPermission, model *models.IotDevice) error {
+	var data models.IotDevice
+
+	err := e.Orm.Model(&data).
+		Scopes(
+			 permission.Permission(data.TableName(), p),
+		).
+		First(model, d.GetId()).Error
+	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
+		err = errors.New("查看对象不存在或无权查看")
+		e.Log.Errorf("Service GetIotDevice error:%s \r\n", err)
+		return err
+	}
+	if err != nil {
+		e.Log.Errorf("db error:%s", err)
+		return err
+	}
+	return nil
+}
+
+// Insert 添加设备对象
+func (e *IotDeviceService) Insert(c *dto.IotDeviceInsertReq) error {
+    var err error
+    var data models.IotDevice
+    c.Generate(&data)
+	err = e.Orm.Create(&data).Error
+	if err != nil {
+		e.Log.Errorf("IotDeviceService Insert error:%s \r\n", err)
+		return err
+	}
+	return nil
+}
+
+// Update 修改设备对象
+func (e *IotDeviceService) Update(c *dto.IotDeviceUpdateReq, p * permission.DataPermission) error {
+    var err error
+    var data = models.IotDevice{}
+    e.Orm.Scopes(
+             permission.Permission(data.TableName(), p),
+        ).First(&data, c.GetId())
+    c.Generate(&data)
+
+    db := e.Orm.Save(&data)
+    if err = db.Error; err != nil {
+        e.Log.Errorf("IotDeviceService Save error:%s \r\n", err)
+        return err
+    }
+    if db.RowsAffected == 0 {
+        return errors.New("无权更新该数据")
+    }
+    return nil
+}
+
+// Remove 删除设备
+func (e *IotDeviceService) Remove(d *dto.IotDeviceDeleteReq, p * permission.DataPermission) error {
+	var data models.IotDevice
+
+	db := e.Orm.Model(&data).
+		Scopes(
+			 permission.Permission(data.TableName(), p),
+		).Delete(&data, d.GetId())
+	if err := db.Error; err != nil {
+        e.Log.Errorf("Service RemoveIotDevice error:%s \r\n", err)
+        return err
+    }
+    if db.RowsAffected == 0 {
+        return errors.New("无权删除该数据")
+    }
+	return nil
+}

+ 167 - 0
SERVER/IotAdmin/app/iot/service/dto/device.go

@@ -0,0 +1,167 @@
+package dto
+
+import (
+	"IotAdmin/app/iot/models"
+	"IotAdmin/common/dto"
+	comModels "IotAdmin/common/models"
+
+	"time"
+)
+
+type IotDeviceGetPageReq struct {
+	dto.Pagination `search:"-"`
+	BeginTime      string `form:"beginTime" search:"type:gte;column:created_at;table:iot_device" comment:"创建时间"`
+	EndTime        string `form:"endTime" search:"type:lte;column:created_at;table:iot_device" comment:"创建时间"`
+	IotDeviceOrder
+}
+
+type IotDeviceOrder struct {
+	Sn   string `form:"snOrder"  search:"type:order;column:sn;table:iot_device"`
+	Name string `form:"nameOrder"  search:"type:order;column:name;table:iot_device"`
+	Type string `form:"typeOrder"  search:"type:order;column:type;table:iot_device"`
+	Mode string `form:"modeOrder"  search:"type:order;column:mode;table:iot_device"`
+}
+
+func (m *IotDeviceGetPageReq) GetNeedSearch() interface{} {
+	return *m
+}
+
+// IotDeviceInsertReq 添加设备请求参数
+type IotDeviceInsertReq struct {
+	Id          int    `json:"-" comment:"ID"` // ID
+	GroupId     string `json:"groupId" comment:"设备分组"`
+	Sn          string `json:"sn" comment:"设备编码"`
+	Name        string `json:"name" comment:"设备名称"`
+	Type        string `json:"type" comment:"设备类型"`
+	Mode        string `json:"mode" comment:"设备模式"`
+	Cycle       string `json:"cycle" comment:"上报周期"`
+	Description string `json:"description" comment:"设备描述"`
+	Protocol    string `json:"protocol" comment:"表计协议"`
+	Address     string `json:"address" comment:"表计地址"`
+	LvRef       string `json:"lvRef" comment:"表计线基准电压"`
+	PvRef       string `json:"pvRef" comment:"表计相基准电压"`
+	comModels.ControlBy
+}
+
+func (s *IotDeviceInsertReq) Generate(model *models.IotDevice) {
+	if s.Id == 0 {
+		model.Model = comModels.Model{Id: s.Id}
+	}
+	model.GroupId = s.GroupId
+	model.Sn = s.Sn
+	model.Name = s.Name
+	model.Type = s.Type
+	model.Mode = s.Mode
+	model.Cycle = s.Cycle
+	model.Description = s.Description
+	model.Protocol = s.Protocol
+	model.Address = s.Address
+	model.LvRef = s.LvRef
+	model.PvRef = s.PvRef
+	model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
+}
+
+func (s *IotDeviceInsertReq) GetId() interface{} {
+	return s.Id
+}
+
+// IotDeviceUpdateReq 修改设备请求参数
+type IotDeviceUpdateReq struct {
+	Id          int    `uri:"id" comment:"ID"` // ID
+	GroupId     string `json:"groupId" comment:"设备分组"`
+	Sn          string `json:"sn" comment:"设备编码"`
+	Name        string `json:"name" comment:"设备名称"`
+	Type        string `json:"type" comment:"设备类型"`
+	Mode        string `json:"mode" comment:"设备模式"`
+	Cycle       string `json:"cycle" comment:"上报周期"`
+	Description string `json:"description" comment:"设备描述"`
+	Protocol    string `json:"protocol" comment:"表计协议"`
+	Address     string `json:"address" comment:"表计地址"`
+	LvRef       string `json:"lvRef" comment:"表计线基准电压"`
+	PvRef       string `json:"pvRef" comment:"表计相基准电压"`
+	comModels.ControlBy
+}
+
+func (s *IotDeviceUpdateReq) Generate(model *models.IotDevice) {
+	if s.Id == 0 {
+		model.Model = comModels.Model{Id: s.Id}
+	}
+	model.GroupId = s.GroupId
+	model.Sn = s.Sn
+	model.Name = s.Name
+	model.Type = s.Type
+	model.Mode = s.Mode
+	model.Cycle = s.Cycle
+	model.Description = s.Description
+	model.Protocol = s.Protocol
+	model.Address = s.Address
+	model.LvRef = s.LvRef
+	model.PvRef = s.PvRef
+	model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
+}
+
+func (s *IotDeviceUpdateReq) GetId() interface{} {
+	return s.Id
+}
+
+// IotDeviceGetReq 获取设备请求参数
+type IotDeviceGetReq struct {
+	Id int `uri:"id"`
+}
+
+func (s *IotDeviceGetReq) GetId() interface{} {
+	return s.Id
+}
+
+// IotDeviceDeleteReq 删除设备请求参数
+type IotDeviceDeleteReq struct {
+	Ids []int `json:"ids"`
+}
+
+type Dsn struct {
+	Host     string `json:"host"`
+	Protocol string `json:"protocol"`
+	ST       string `json:"st"`
+	MN       string `json:"mn"`
+	User     string `json:"user"`
+	Pwd      string `json:"pwd"`
+}
+
+func (s *IotDeviceDeleteReq) GetId() interface{} {
+	return s.Ids
+}
+
+// IotDeviceResp 获取设备响应参数
+type IotDeviceResp struct {
+	Id          int       `uri:"id" comment:"ID"` // ID
+	GroupId     string    `json:"groupId" comment:"设备分组"`
+	Sn          string    `json:"sn" comment:"设备编码"`
+	Name        string    `json:"name" comment:"设备名称"`
+	Type        string    `json:"type" comment:"设备类型"`
+	Mode        string    `json:"mode" comment:"设备模式"`
+	Cycle       string    `json:"cycle" comment:"上报周期"`
+	Description string    `json:"description" comment:"设备描述"`
+	Protocol    string    `json:"protocol" comment:"表计协议"`
+	Address     string    `json:"address" comment:"表计地址"`
+	LvRef       string    `json:"lvRef" comment:"表计线基准电压"`
+	PvRef       string    `json:"pvRef" comment:"表计相基准电压"`
+	CreateBy    int       `json:"createBy" comment:"创建者"`
+	CreatedAt   time.Time `json:"createdAt" comment:"创建时间"`
+}
+
+func (s *IotDeviceResp) Generate(model *models.IotDevice) {
+	s.Id = model.Id
+	s.GroupId = model.GroupId
+	s.Sn = model.Sn
+	s.Name = model.Name
+	s.Type = model.Type
+	s.Mode = model.Mode
+	s.Cycle = model.Cycle
+	s.Description = model.Description
+	s.Protocol = model.Protocol
+	s.Address = model.Address
+	s.LvRef = model.LvRef
+	s.PvRef = model.PvRef
+	s.CreateBy = model.CreateBy
+	s.CreatedAt = model.CreatedAt
+}

+ 106 - 0
SERVER/IotAdmin/app/iot/service/dto/group.go

@@ -0,0 +1,106 @@
+package dto
+
+import (
+	"IotAdmin/app/iot/models"
+	"IotAdmin/common/dto"
+	comModels "IotAdmin/common/models"
+
+    "time"
+)
+
+type IotGroupGetPageReq struct {
+	dto.Pagination     `search:"-"`
+    BeginTime      string `form:"beginTime" search:"type:gte;column:created_at;table:iot_group" comment:"创建时间"`
+    EndTime        string `form:"endTime" search:"type:lte;column:created_at;table:iot_group" comment:"创建时间"`
+    IotGroupOrder
+}
+
+type IotGroupOrder struct {
+    Name string `form:"nameOrder"  search:"type:order;column:name;table:iot_group"`
+}
+
+func (m *IotGroupGetPageReq) GetNeedSearch() interface{} {
+	return *m
+}
+
+// IotGroupInsertReq 添加分组请求参数
+type IotGroupInsertReq struct {
+    Id int `json:"-" comment:"ID"` // ID
+    ParentId string `json:"parentId" comment:"父ID"`
+    Name string `json:"name" comment:"分组名称"`
+    Description string `json:"description" comment:"分组描述"`
+    comModels.ControlBy
+}
+
+func (s *IotGroupInsertReq) Generate(model *models.IotGroup)  {
+    if s.Id == 0 {
+        model.Model = comModels.Model{ Id: s.Id }
+    }
+    model.ParentId = s.ParentId
+    model.Name = s.Name
+    model.Description = s.Description
+    model.CreateBy = s.CreateBy // 添加这而,需要记录是被谁创建的
+}
+
+func (s *IotGroupInsertReq) GetId() interface{} {
+	return s.Id
+}
+
+// IotGroupUpdateReq 修改分组请求参数
+type IotGroupUpdateReq struct {
+    Id int `uri:"id" comment:"ID"` // ID
+    ParentId string `json:"parentId" comment:"父ID"`
+    Name string `json:"name" comment:"分组名称"`
+    Description string `json:"description" comment:"分组描述"`
+    comModels.ControlBy
+}
+
+func (s *IotGroupUpdateReq) Generate(model *models.IotGroup)  {
+    if s.Id == 0 {
+        model.Model = comModels.Model{ Id: s.Id }
+    }
+    model.ParentId = s.ParentId
+    model.Name = s.Name
+    model.Description = s.Description
+    model.UpdateBy = s.UpdateBy // 添加这而,需要记录是被谁更新的
+}
+
+func (s *IotGroupUpdateReq) GetId() interface{} {
+	return s.Id
+}
+
+// IotGroupGetReq 获取分组请求参数
+type IotGroupGetReq struct {
+     Id int `uri:"id"`
+}
+func (s *IotGroupGetReq) GetId() interface{} {
+	return s.Id
+}
+
+// IotGroupDeleteReq 删除分组请求参数
+type IotGroupDeleteReq struct {
+	Ids []int `json:"ids"`
+}
+
+func (s *IotGroupDeleteReq) GetId() interface{} {
+	return s.Ids
+}
+
+// IotGroupResp 获取分组响应参数
+type IotGroupResp struct {
+    Id int `uri:"id" comment:"ID"` // ID
+    ParentId string `json:"parentId" comment:"父ID"`
+    Name string `json:"name" comment:"分组名称"`
+    Description string `json:"description" comment:"分组描述"`
+    CreateBy int `json:"createBy" comment:"创建者"`
+    CreatedAt time.Time `json:"createdAt" comment:"创建时间"`
+}
+
+func (s *IotGroupResp) Generate(model *models.IotGroup)  {
+    s.Id = model.Id
+    s.ParentId = model.ParentId
+    s.Name = model.Name
+    s.Description = model.Description
+    s.CreateBy = model.CreateBy
+    s.CreatedAt = model.CreatedAt
+}

+ 110 - 0
SERVER/IotAdmin/app/iot/service/group.go

@@ -0,0 +1,110 @@
+package service
+
+import (
+	"errors"
+
+    "IotAdmin/core/sdk/service"
+	"gorm.io/gorm"
+
+	"IotAdmin/app/iot/models"
+	"IotAdmin/app/iot/service/dto"
+	cDto "IotAdmin/common/dto"
+	"IotAdmin/common/permission"
+)
+
+// IotGroupService 分组服务
+type IotGroupService struct {
+	service.Service
+}
+
+// GetPage 获取分组列表
+func (e *IotGroupService) GetPage(c *dto.IotGroupGetPageReq, p * permission.DataPermission, list *[]models.IotGroup, count *int64) error {
+	var err error
+	var data models.IotGroup
+
+	err = e.Orm.Model(&data).
+		Scopes(
+			cDto.MakeCondition(c.GetNeedSearch()),
+			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
+			 permission.Permission(data.TableName(), p),
+		).
+		Find(list).Limit(-1).Offset(-1).
+		Count(count).Error
+	if err != nil {
+		e.Log.Errorf("IotGroupService GetPage error:%s \r\n", err)
+		return err
+	}
+	return nil
+}
+
+// Get 获取分组对象
+func (e *IotGroupService) Get(d *dto.IotGroupGetReq, p * permission.DataPermission, model *models.IotGroup) error {
+	var data models.IotGroup
+
+	err := e.Orm.Model(&data).
+		Scopes(
+			 permission.Permission(data.TableName(), p),
+		).
+		First(model, d.GetId()).Error
+	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
+		err = errors.New("查看对象不存在或无权查看")
+		e.Log.Errorf("Service GetIotGroup error:%s \r\n", err)
+		return err
+	}
+	if err != nil {
+		e.Log.Errorf("db error:%s", err)
+		return err
+	}
+	return nil
+}
+
+// Insert 添加分组对象
+func (e *IotGroupService) Insert(c *dto.IotGroupInsertReq) error {
+    var err error
+    var data models.IotGroup
+    c.Generate(&data)
+	err = e.Orm.Create(&data).Error
+	if err != nil {
+		e.Log.Errorf("IotGroupService Insert error:%s \r\n", err)
+		return err
+	}
+	return nil
+}
+
+// Update 修改分组对象
+func (e *IotGroupService) Update(c *dto.IotGroupUpdateReq, p * permission.DataPermission) error {
+    var err error
+    var data = models.IotGroup{}
+    e.Orm.Scopes(
+             permission.Permission(data.TableName(), p),
+        ).First(&data, c.GetId())
+    c.Generate(&data)
+
+    db := e.Orm.Save(&data)
+    if err = db.Error; err != nil {
+        e.Log.Errorf("IotGroupService Save error:%s \r\n", err)
+        return err
+    }
+    if db.RowsAffected == 0 {
+        return errors.New("无权更新该数据")
+    }
+    return nil
+}
+
+// Remove 删除分组
+func (e *IotGroupService) Remove(d *dto.IotGroupDeleteReq, p * permission.DataPermission) error {
+	var data models.IotGroup
+
+	db := e.Orm.Model(&data).
+		Scopes(
+			 permission.Permission(data.TableName(), p),
+		).Delete(&data, d.GetId())
+	if err := db.Error; err != nil {
+        e.Log.Errorf("Service RemoveIotGroup error:%s \r\n", err)
+        return err
+    }
+    if db.RowsAffected == 0 {
+        return errors.New("无权删除该数据")
+    }
+	return nil
+}

+ 8 - 0
SERVER/IotAdmin/app/router/iot.go

@@ -0,0 +1,8 @@
+package router
+
+import "IotAdmin/app/iot/router"
+
+func init() {
+	//注册路由 fixme 其他应用的路由,在本目录新建文件放在init方法
+	appRouters = append(appRouters, router.InitRouter)
+}

+ 3 - 2
SERVER/IotAdmin/config/settings.yml

@@ -9,7 +9,8 @@ settings:
     description: "Hello World!"
     version: 1.0
     # 端口号
-    port: 6071
+    port: 14001
+    tcpPort: 14002
     readTimeout: 1
     writerTimeout: 2
     # 数据权限功能开关
@@ -19,7 +20,7 @@ settings:
     driver: mysql
     # 数据库连接字符串 mysql 缺省信息 charset=utf8&parseTime=True&loc=Local&timeout=1000ms
     # sqlserver: sqlserver://用户名:密码@地址?database=数据库名
-    source: root:123456@tcp(192.168.0.82:3306)/IotAdmin_V1?charset=utf8&parseTime=True&loc=Local&timeout=1000ms
+    source: root:123456@tcp(192.168.0.104:3306)/IotAdmin_V1?charset=utf8&parseTime=True&loc=Local&timeout=1000ms
     connMaxIdleTime: 3600 #最大空闲时间 单位秒
     connMaxLifetime: 7200 #最大存活时间 单位秒
     maxIdleConns: 25

+ 8 - 0
SERVER/IotAdmin/config/sql/db.sql

@@ -373,6 +373,14 @@ INSERT INTO sys_dict_data VALUES (41, 0, '生成代码', '11', 'sys_oper_type',
 INSERT INTO sys_dict_data VALUES (42, 0, '清空数据', '12', 'sys_oper_type', '', 'primary', '', '2', '', '获取验证码', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
 INSERT INTO sys_dict_data VALUES (43, 0, '刷新数据', '13', 'sys_oper_type', '', 'primary', '', '2', '', '获取验证码', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
 
+INSERT INTO sys_dict_type VALUES (20, '设备类型', 'iot_device_type', '2', '', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
+INSERT INTO sys_dict_data VALUES (51, 1, '网关', '1', 'iot_device_type', '', 'primary', '', '2', '', '新增操作', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
+INSERT INTO sys_dict_data VALUES (52, 2, '表计', '2', 'iot_device_type', '', 'success', '', '2', '', '修改操作', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
+
+INSERT INTO sys_dict_type VALUES (21, '设备模式', 'iot_device_mode', '2', '', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
+INSERT INTO sys_dict_data VALUES (53, 1, '下发指令查询', '1', 'iot_device_mode', '', 'primary', '', '2', '', '新增操作', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
+INSERT INTO sys_dict_data VALUES (54, 2, '表计主动上报', '2', 'iot_device_mode', '', 'success', '', '2', '', '修改操作', 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);
+
 
 
 INSERT INTO sys_job VALUES (1, '接口测试', 'SYSTEM', 1, '0/5 * * * * ', 'http://localhost:6071', '', 1, 1, 1, 0, 1, 1, '2024-03-14 00:00:00.000', '2024-03-14 00:00:00.000', NULL);

+ 1 - 0
SERVER/IotAdmin/core/sdk/config/application.go

@@ -5,6 +5,7 @@ var ApplicationConfig = new(Application)
 type Application struct {
 	Host         string
 	Port         int
+	TcpPort      int
 	ReadTimeout  int
 	WriteTimeout int
 	Name         string

+ 12 - 14
SERVER/IotAdmin/migration/models/iot/iot_device.go

@@ -5,23 +5,21 @@ import (
 )
 
 type Device struct {
-	ID          int64   `gorm:"primary_key;auto_increment;not null;comment:ID"`
-	ParentId    int64   `gorm:"type:bigint;not null;comment:父ID"`
-	GroupId     int64   `gorm:"type:bigint;not null;comment:分组ID"`
-	SN          string  `gorm:"type:varchar(50);not null;comment:设备编码"`
-	Name        string  `gorm:"type:varchar(50);not null;comment:设备名称"`
-	Type        int     `gorm:"type:int;not null;comment:设备类型 1:dtu 2:表计"`
-	Mode        int     `gorm:"type:int;not null;comment:设备模式 1:下发指令查询 2:表计主动上报"`
-	ST          string  `gorm:"type:varchar(255);comment:设备ST"`
-	CN          string  `gorm:"type:varchar(255);comment:设备CN"`
-	Cycle       int     `gorm:"type:int;comment:上报周期"`
-	Dsn         string  `gorm:"type:varchar(255);comment:设备Dsn"` // 设备上报IP、端口、协议、用户、密码 192.168.0.104:8080@YcHj212,192.168.0.104:8081@YcHj212@user@123456
+	ID       int64  `gorm:"primary_key;auto_increment;not null;comment:ID"`
+	ParentId int64  `gorm:"type:bigint;not null;comment:父ID"`
+	GroupId  int64  `gorm:"type:bigint;not null;comment:分组ID"`
+	SN       string `gorm:"type:varchar(50);not null;comment:设备编码"`
+	Name     string `gorm:"type:varchar(50);not null;comment:设备名称"`
+	Type     int    `gorm:"type:int;not null;comment:设备类型 1:网关 2:表计"`
+	Mode     int    `gorm:"type:int;not null;comment:设备模式 1:下发指令查询 2:表计主动上报"`
+	Cycle    int    `gorm:"type:int;comment:上报周期"`
+	// DSN 表计上报IP、端口、协议、ST、MN、用户、密码 192.168.0.104:8080@YcHj212@1@mn,192.168.0.104:8081@YcHj212@user@123456
+	DSN         string  `gorm:"type:varchar(255);comment:设备Dsn"`
 	Description string  `gorm:"type:varchar(255);comment:设备描述"`
-	MN          string  `gorm:"type:varchar(255);comment:表计MN"` // 表计编码(对应平台设备)
-	LvRef       float32 `gorm:"type:float;comment:表计线基准电压"`
-	PvRef       float32 `gorm:"type:float;comment:表计相基准电压"`
 	Protocol    string  `gorm:"type:varchar(255);comment:表计协议"`
 	Address     int     `gorm:"type:int;not null;comment:表计地址"`
+	LvRef       float32 `gorm:"type:float;comment:表计线基准电压"`
+	PvRef       float32 `gorm:"type:float;comment:表计相基准电压"`
 	BmYz        string  `gorm:"type:varchar(255);comment:编码因子"`
 	Config      string  `gorm:"type:varchar(255);comment:其他配置"`
 	models.ControlBy

+ 2 - 0
SERVER/IotAdmin/migration/models/iot/iot_group.go

@@ -4,6 +4,8 @@ import "IotAdmin/common/models"
 
 type Group struct {
 	ID          int64  `gorm:"primary_key;auto_increment;not null;comment:ID"`
+	ParentId    int64  `gorm:"type:bigint;not null;comment:父ID"`
+	Path        string `gorm:"type:varchar(255);not null;comment:分组路径"`
 	Name        string `gorm:"type:varchar(255);not null;comment:分组名称"`
 	Description string `gorm:"type:varchar(255);not null;comment:分组描述"`
 	models.ControlBy

+ 1 - 9
SERVER/IotAdmin/migration/version/1711599710047_migrate.go

@@ -18,15 +18,7 @@ func init() {
 func _1711599710047Test(db *gorm.DB, version string) error {
 	return db.Transaction(func(tx *gorm.DB) error {
 
-		// TODO: 这里开始写入要变更的内容
-
-		// TODO: 例如 修改表字段 使用过程中请删除此段代码
-		//err := tx.Migrator().RenameColumn(&models.SysConfig{}, "config_id", "id")
-		//if err != nil {
-		// 	return err
-		//}
-
-		// TODO: 例如 新增表结构 使用过程中请删除此段代码
+		// 新增表结构 使用过程中请删除此段代码
 		err := tx.Debug().Migrator().AutoMigrate(
 			new(iot.Group),
 			new(iot.Device),

+ 2 - 2
SERVER/IotAdmin/template/v4/vue-view.go.template

@@ -427,7 +427,7 @@ onMounted(init)
                             link
                             type="primary"
                             @click="handleUpdate(row)"
-                            v-hasPermission="{{.PackageName}}:{{.BusinessName}}:edit">
+                            v-hasPermission="'{{.PackageName}}:{{.BusinessName}}:edit'">
                         <template #icon>
                             <VbIcon icon-name="notepad-edit" icon-type="duotone" class="fs-3"></VbIcon>
                         </template>
@@ -438,7 +438,7 @@ onMounted(init)
                             link
                             type="primary"
                             @click="handleDelete([row])"
-                            v-hasPermission="{{.PackageName}}:{{.BusinessName}}:remove">
+                            v-hasPermission="'{{.PackageName}}:{{.BusinessName}}:remove'">
                         <template #icon>
                             <VbIcon icon-name="trash-square" icon-type="duotone" class="fs-3"></VbIcon>
                         </template>

+ 4 - 1
UI/IOTADMIN.VUE/src/api/index.ts

@@ -3,6 +3,7 @@ import Sys from "./_sys"
 import system, { type ISystemApi } from "./system"
 import tool, { type IToolApi } from "./tools"
 import schedule, { type IScheduleApi } from "./schedule"
+import iot, { type IIotApi } from "./iot"
 
 export interface IAppApi {
 	loginApi: Login
@@ -10,6 +11,7 @@ export interface IAppApi {
 	system: ISystemApi
 	tool: IToolApi
 	schedule: IScheduleApi
+	iot: IIotApi
 }
 
 export const apis: IAppApi = {
@@ -17,7 +19,8 @@ export const apis: IAppApi = {
 	sysApi: new Sys(),
 	system,
 	tool,
-	schedule
+	schedule,
+	iot
 }
 
 export default apis

+ 46 - 0
UI/IOTADMIN.VUE/src/api/iot/_device.ts

@@ -0,0 +1,46 @@
+import Rs from "@@/services/RequestService"
+
+class deviceApi {
+  // 查询Device列表
+  listDevice = (query: any) => {
+    return Rs.get({
+        url: '/iot-device',
+        params: query
+    })
+  }
+
+  // 查询Device详细
+  getDevice = (id: any) => {
+    return Rs.get({
+        url: '/iot-device/' + id,
+    })
+  }
+
+
+  // 新增Device
+  addDevice = (data: any) => {
+    return Rs.post({
+        url: '/iot-device',
+        data: data
+    })
+  }
+
+  // 修改Device
+  updateDevice = (data: any) => {
+    return Rs.put({
+        url: '/iot-device/'+data.id,
+        data: data
+    })
+  }
+
+  // 删除Device
+  delDevice = (ids: any) => {
+    return Rs.del({
+        url: '/iot-device',
+        data: { ids }
+    })
+  }
+
+}
+
+export default deviceApi

+ 46 - 0
UI/IOTADMIN.VUE/src/api/iot/_group.ts

@@ -0,0 +1,46 @@
+import Rs from "@@/services/RequestService"
+
+class groupApi {
+  // 查询Group列表
+  listGroup = (query: any) => {
+    return Rs.get({
+        url: '/iot-group',
+        params: query
+    })
+  }
+
+  // 查询Group详细
+  getGroup = (id: any) => {
+    return Rs.get({
+        url: '/iot-group/' + id,
+    })
+  }
+
+
+  // 新增Group
+  addGroup = (data: any) => {
+    return Rs.post({
+        url: '/iot-group',
+        data: data
+    })
+  }
+
+  // 修改Group
+  updateGroup = (data: any) => {
+    return Rs.put({
+        url: '/iot-group/'+data.id,
+        data: data
+    })
+  }
+
+  // 删除Group
+  delGroup = (ids: any) => {
+    return Rs.del({
+        url: '/iot-group',
+        data: { ids }
+    })
+  }
+
+}
+
+export default groupApi

+ 14 - 0
UI/IOTADMIN.VUE/src/api/iot/index.ts

@@ -0,0 +1,14 @@
+import Group from "./_group"
+import Device from "./_device"
+
+export interface IIotApi {
+	groupApi: Group
+	deviceApi: Device
+}
+
+export const apis: IIotApi = {
+	groupApi: new Group(),
+	deviceApi: new Device()
+}
+
+export default apis

+ 345 - 0
UI/IOTADMIN.VUE/src/views/iot/device/index.vue

@@ -0,0 +1,345 @@
+<script setup lang="ts" name="Device">
+import apis from "@a"
+import message from "@@/utils/message"
+import dayjs from "dayjs"
+
+const groupIdOptions = computed(() => {
+	return [
+		{ label: "请选择", value: "" },
+		{ label: "", value: "" }
+	]
+})
+
+const tableRef = ref()
+const modalRef = ref()
+const opts = reactive<any>({
+	columns: [
+		{ field: "id", name: "ID", width: 100, visible: false, isSort: false, tooltip: true },
+		{ field: "groupId", name: "设备分组", width: "auto", isSort: false, visible: true },
+		{ field: "sn", name: "设备编码", width: "auto", isSort: true, visible: true },
+		{ field: "name", name: "设备名称", width: "auto", isSort: true, visible: true },
+		{ field: "type", name: "设备类型", width: 100, isSort: true, visible: true },
+		{ field: "mode", name: "设备模式", width: 100, isSort: true, visible: true },
+		{ field: "cycle", name: "上报周期", width: "auto", isSort: false, visible: true },
+		{ field: "createdAt", name: "创建时间", width: 185, isSort: true, visible: true },
+		{ field: "actions", name: `操作`, width: 150 }
+	],
+	queryParams: {
+		groupId: undefined,
+		sn: undefined,
+		name: undefined,
+		type: undefined,
+		mode: undefined,
+		dateRange: []
+	},
+	searchFormItems: [
+		{
+			field: "groupId",
+			label: "设备分组",
+			class: "w-100",
+			component: "VS",
+			placeholder: "请选择设备分组",
+			data: () => groupIdOptions.value,
+			props: {
+				type: "select",
+				clearable: true
+			}
+		},
+		{
+			field: "sn",
+			label: "设备编码",
+			class: "w-100",
+			component: "I",
+			listeners: {
+				keyup: (e: any) => {
+					if (e.code == "Enter") {
+						handleQuery()
+					}
+				}
+			}
+		},
+		{
+			field: "name",
+			label: "设备名称",
+			class: "w-100",
+			component: "I",
+			listeners: {
+				keyup: (e: any) => {
+					if (e.code == "Enter") {
+						handleQuery()
+					}
+				}
+			}
+		},
+		{
+			field: "type",
+			label: "设备类型",
+			class: "w-100",
+			component: "Dict",
+			props: {
+				type: "radio",
+				placeholder: "请选择设备类型",
+				dictType: "iot_device_type"
+			}
+		},
+		{
+			field: "mode",
+			label: "设备模式",
+			class: "w-100",
+			component: "Dict",
+			props: {
+				type: "radio",
+				placeholder: "请选择设备模式",
+				dictType: "iot_device_mode"
+			}
+		},
+		{
+			field: "dateRange",
+			label: "创建时间",
+			class: "w-100",
+			component: "D",
+			placeholder: "请选择创建时间",
+			props: {
+				type: "daterange",
+				valueFormat: "YYYY-MM-DD",
+				rangeSeparator: "-",
+				startPlaceholder: "开始日期",
+				endPlaceholder: "结束日期"
+			},
+			listeners: {
+				change: (v: any) => {
+					queryParams.value.dateRange = v
+				}
+			}
+		}
+	] as any,
+	permission: "iot:device",
+	handleFuns: {},
+	customBtns: [],
+	tableListFun: apis.iot.deviceApi.listDevice,
+	getEntityFun: apis.iot.deviceApi.getDevice,
+	deleteEntityFun: apis.iot.deviceApi.delDevice,
+	formItems: [
+		{
+			field: "groupId",
+			label: "设备分组",
+			class: "w-100",
+			required: false,
+			component: "VS",
+			placeholder: "请选择设备分组",
+			data: () => groupIdOptions.value,
+			props: {
+				type: "select",
+				clearable: true
+			}
+		},
+		{
+			field: "sn",
+			label: "设备编码",
+			class: "w-100",
+			component: "I",
+			required: true
+		},
+		{
+			field: "name",
+			label: "设备名称",
+			class: "w-100",
+			component: "I",
+			required: true
+		},
+		{
+			field: "type",
+			label: "设备类型",
+			class: "w-100",
+			required: true,
+			component: "Dict",
+			props: {
+				type: "radio",
+				clearable: true,
+				placeholder: "请选择设备类型",
+				dictType: "iot_device_type"
+			}
+		},
+		{
+			field: "mode",
+			label: "设备模式",
+			class: "w-100",
+			required: true,
+			component: "Dict",
+			props: {
+				type: "radio",
+				clearable: true,
+				placeholder: "请选择设备模式",
+				dictType: "iot_device_mode"
+			}
+		},
+		{
+			field: "cycle",
+			label: "上报周期",
+			class: "w-100",
+			component: "I",
+			required: false
+		},
+		{
+			field: "description",
+			label: "设备描述",
+			class: "w-100",
+			component: "I",
+			required: false
+		},
+		{
+			field: "protocol",
+			label: "表计协议",
+			class: "w-100",
+			component: "I",
+			required: false
+		},
+		{
+			field: "address",
+			label: "表计地址",
+			class: "w-100",
+			component: "I",
+			required: false
+		},
+		{
+			field: "lvRef",
+			label: "表计线基准电压",
+			class: "w-100",
+			component: "I",
+			required: false
+		},
+		{
+			field: "pvRef",
+			label: "表计相基准电压",
+			class: "w-100",
+			component: "I",
+			required: false
+		}
+	] as any,
+	resetForm: () => {
+		form.value = emptyFormData.value
+	},
+	emptyFormData: {
+		id: undefined,
+		groupId: undefined,
+		sn: undefined,
+		name: undefined,
+		type: undefined,
+		mode: undefined,
+		cycle: undefined,
+		description: undefined,
+		protocol: undefined,
+		address: undefined,
+		lvRef: undefined,
+		pvRef: undefined
+	}
+})
+const { queryParams, emptyFormData } = toRefs(opts)
+const form = ref<any>(emptyFormData.value)
+
+/** 修改按钮操作 */
+function handleUpdate(row: any) {
+	tableRef.value.defaultHandleFuns.handleUpdate("", row)
+}
+/** 删除按钮操作 */
+function handleDelete(rows: any[]) {
+	tableRef.value.defaultHandleFuns.handleDelete("", rows)
+}
+/** 提交按钮 */
+function submitForm() {
+	if (form.value.id != undefined) {
+		apis.iot.deviceApi.updateDevice(form.value).then(() => {
+			message.msgSuccess("修改成功")
+			handleQuery()
+		})
+	} else {
+		apis.iot.deviceApi.addDevice(form.value).then(() => {
+			message.msgSuccess("新增成功")
+			handleQuery()
+		})
+	}
+}
+/** 查询按钮 */
+function handleQuery() {
+	addDateRange(queryParams.value, queryParams.value.dateRange)
+	tableRef.value?.search()
+}
+/** 查询重置按钮 */
+function resetQuery() {
+	queryParams.value.dateRange = []
+	addDateRange(queryParams.value, queryParams.value.dateRange)
+}
+
+function init() {}
+
+onMounted(init)
+</script>
+
+<template>
+	<div class="app-container">
+		<VbDataTable
+			ref="tableRef"
+			:handle-perm="opts.permission"
+			:handle-funs="opts.handleFuns"
+			:search-form-items="opts.searchFormItems"
+			:columns="opts.columns"
+			:custom-btns="opts.customBtns"
+			:remote-fun="opts.tableListFun"
+			:get-entity-fun="opts.getEntityFun"
+			:delete-entity-fun="opts.deleteEntityFun"
+			sortField="createdAt"
+			sort-order="desc"
+			:modal="modalRef"
+			:reset-form-fun="opts.resetForm"
+			v-model:form-data="form"
+			:query-params="queryParams"
+			:check-multiple="true"
+			:has-checkbox="true"
+			:reset-search-form-fun="resetQuery"
+			:custom-search-fun="handleQuery">
+			<template #groupId="{ row }">
+				<vb-tag :value="row.groupId" :data="[]"></vb-tag>
+			</template>
+			<template #type="{ row }">
+				<DictTag :value="row.type" type="iot_device_type"></DictTag>
+			</template>
+			<template #mode="{ row }">
+				<DictTag :value="row.mode" type="iot_device_mode"></DictTag>
+			</template>
+			<template #createdAt="{ row }">
+				<span>{{ dayjs(row.createdAt).format("YYYY-MM-DD HH:mm:ss") }}</span>
+			</template>
+			<template #actions="{ row }">
+				<vb-tooltip content="修改" placement="top">
+					<el-button
+						link
+						type="primary"
+						@click="handleUpdate(row)"
+						v-hasPermission="'iot:device:edit'">
+						<template #icon>
+							<VbIcon icon-name="notepad-edit" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+				<vb-tooltip content="删除" placement="top">
+					<el-button
+						link
+						type="primary"
+						@click="handleDelete([row])"
+						v-hasPermission="'iot:device:remove'">
+						<template #icon>
+							<VbIcon icon-name="trash-square" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+			</template>
+		</VbDataTable>
+		<VbModal
+			v-model:modal="modalRef"
+			:title="opts.modalTitle"
+			:form-items="opts.formItems as any"
+			:form-data="form"
+			@confirm="submitForm"
+			append-to-body></VbModal>
+	</div>
+</template>

+ 207 - 0
UI/IOTADMIN.VUE/src/views/iot/group/index.vue

@@ -0,0 +1,207 @@
+<script setup lang="ts" name="Group">
+import apis from "@a"
+import message from "@@/utils/message"
+import dayjs from "dayjs"
+
+const tableRef = ref()
+const modalRef = ref()
+const opts = reactive<any>({
+	columns: [
+		{ field: "id", name: "ID", width: 100, visible: false, isSort: false, tooltip: true },
+		{ field: "parentId", name: "父ID", width: "auto", isSort: false, visible: true },
+		{ field: "name", name: "分组名称", width: "auto", isSort: true, visible: true },
+		{ field: "description", name: "分组描述", width: "auto", isSort: false, visible: true },
+		{ field: "createdAt", name: "创建时间", width: 185, isSort: true, visible: true },
+		{ field: "actions", name: `操作`, width: 150 }
+	],
+	queryParams: {
+		name: undefined,
+		description: undefined,
+		dateRange: []
+	},
+	searchFormItems: [
+		{
+			field: "name",
+			label: "分组名称",
+			class: "w-100",
+			component: "I",
+			listeners: {
+				keyup: (e: any) => {
+					if (e.code == "Enter") {
+						handleQuery()
+					}
+				}
+			}
+		},
+		{
+			field: "description",
+			label: "分组描述",
+			class: "w-100",
+			component: "I",
+			listeners: {
+				keyup: (e: any) => {
+					if (e.code == "Enter") {
+						handleQuery()
+					}
+				}
+			}
+		},
+		{
+			field: "dateRange",
+			label: "创建时间",
+			class: "w-100",
+			component: "D",
+			placeholder: "请选择创建时间",
+			props: {
+				type: "daterange",
+				valueFormat: "YYYY-MM-DD",
+				rangeSeparator: "-",
+				startPlaceholder: "开始日期",
+				endPlaceholder: "结束日期"
+			},
+			listeners: {
+				change: (v: any) => {
+					queryParams.value.dateRange = v
+				}
+			}
+		}
+	] as any,
+	permission: "iot:group",
+	handleFuns: {},
+	customBtns: [],
+	tableListFun: apis.iot.groupApi.listGroup,
+	getEntityFun: apis.iot.groupApi.getGroup,
+	deleteEntityFun: apis.iot.groupApi.delGroup,
+	formItems: [
+		{
+			field: "parentId",
+			label: "父ID",
+			class: "w-100",
+			component: "I",
+			required: true
+		},
+		{
+			field: "name",
+			label: "分组名称",
+			class: "w-100",
+			component: "I",
+			required: true
+		},
+		{
+			field: "description",
+			label: "分组描述",
+			class: "w-100",
+			component: "I",
+			required: true
+		}
+	] as any,
+	resetForm: () => {
+		form.value = emptyFormData.value
+	},
+	emptyFormData: {
+		id: undefined,
+		parentId: undefined,
+		name: undefined,
+		description: undefined
+	}
+})
+const { queryParams, emptyFormData } = toRefs(opts)
+const form = ref<any>(emptyFormData.value)
+
+/** 修改按钮操作 */
+function handleUpdate(row: any) {
+	tableRef.value.defaultHandleFuns.handleUpdate("", row)
+}
+/** 删除按钮操作 */
+function handleDelete(rows: any[]) {
+	tableRef.value.defaultHandleFuns.handleDelete("", rows)
+}
+/** 提交按钮 */
+function submitForm() {
+	if (form.value.id != undefined) {
+		apis.iot.groupApi.updateGroup(form.value).then(() => {
+			message.msgSuccess("修改成功")
+			handleQuery()
+		})
+	} else {
+		apis.iot.groupApi.addGroup(form.value).then(() => {
+			message.msgSuccess("新增成功")
+			handleQuery()
+		})
+	}
+}
+/** 查询按钮 */
+function handleQuery() {
+	addDateRange(queryParams.value, queryParams.value.dateRange)
+	tableRef.value?.search()
+}
+/** 查询重置按钮 */
+function resetQuery() {
+	queryParams.value.dateRange = []
+	addDateRange(queryParams.value, queryParams.value.dateRange)
+}
+
+function init() {}
+
+onMounted(init)
+</script>
+
+<template>
+	<div class="app-container">
+		<VbDataTable
+			ref="tableRef"
+			:handle-perm="opts.permission"
+			:handle-funs="opts.handleFuns"
+			:search-form-items="opts.searchFormItems"
+			:columns="opts.columns"
+			:custom-btns="opts.customBtns"
+			:remote-fun="opts.tableListFun"
+			:get-entity-fun="opts.getEntityFun"
+			:delete-entity-fun="opts.deleteEntityFun"
+			sortField="createdAt"
+			sort-order="desc"
+			:modal="modalRef"
+			:reset-form-fun="opts.resetForm"
+			v-model:form-data="form"
+			:query-params="queryParams"
+			:check-multiple="true"
+			:has-checkbox="true"
+			:reset-search-form-fun="resetQuery"
+			:custom-search-fun="handleQuery">
+			<template #createdAt="{ row }">
+				<span>{{ dayjs(row.createdAt).format("YYYY-MM-DD HH:mm:ss") }}</span>
+			</template>
+			<template #actions="{ row }">
+				<vb-tooltip content="修改" placement="top">
+					<el-button
+						link
+						type="primary"
+						@click="handleUpdate(row)"
+						v-hasPermission="'iot:group:edit'">
+						<template #icon>
+							<VbIcon icon-name="notepad-edit" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+				<vb-tooltip content="删除" placement="top">
+					<el-button
+						link
+						type="primary"
+						@click="handleDelete([row])"
+						v-hasPermission="'iot:group:remove'">
+						<template #icon>
+							<VbIcon icon-name="trash-square" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+			</template>
+		</VbDataTable>
+		<VbModal
+			v-model:modal="modalRef"
+			:title="opts.modalTitle"
+			:form-items="opts.formItems as any"
+			:form-data="form"
+			@confirm="submitForm"
+			append-to-body></VbModal>
+	</div>
+</template>

+ 3 - 3
UI/IOTADMIN.VUE/vite.config.ts

@@ -107,7 +107,7 @@ export default defineConfig(({ mode, command }) => {
 		},
 		// vite 相关配置
 		server: {
-			port: 6070,
+			port: 14000,
 			// host: true,
 			host: "0.0.0.0",
 			// 热更新
@@ -117,12 +117,12 @@ export default defineConfig(({ mode, command }) => {
 			open: true,
 			proxy: {
 				"/dev-api/static": {
-					target: "http://localhost:6071",
+					target: "http://localhost:14001",
 					changeOrigin: true,
 					rewrite: (p) => p.replace(/^\/dev-api/, "")
 				},
 				"/dev-api": {
-					target: "http://localhost:6071",
+					target: "http://localhost:14001",
 					changeOrigin: true,
 					rewrite: (p) => p.replace(/^\/dev-api/, "")
 				}