Compare commits

..

3 Commits

Author SHA1 Message Date
906d371df6 优化注释 2025-09-12 17:51:09 +08:00
9e9bf7b8a0 实现swagger 2025-09-12 17:43:42 +08:00
fe9b0db985 初步实现device_controller 2025-09-12 17:18:14 +08:00
9 changed files with 986 additions and 189 deletions

View File

@@ -23,6 +23,153 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/devices": {
"get": {
"description": "获取系统中所有设备的列表",
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "获取设备列表",
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
},
"post": {
"description": "根据提供的信息创建一个新设备",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "创建新设备",
"parameters": [
{
"description": "设备信息",
"name": "device",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/device.CreateDeviceRequest"
}
}
],
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
}
},
"/devices/{id}": {
"get": {
"description": "根据设备ID获取单个设备的详细信息",
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "获取设备信息",
"parameters": [
{
"type": "string",
"description": "设备ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
},
"put": {
"description": "根据设备ID更新一个已存在的设备信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "更新设备信息",
"parameters": [
{
"type": "string",
"description": "设备ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "要更新的设备信息",
"name": "device",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/device.UpdateDeviceRequest"
}
}
],
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
},
"delete": {
"description": "根据设备ID删除一个设备软删除",
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "删除设备",
"parameters": [
{
"type": "string",
"description": "设备ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
}
},
"/users": {
"post": {
"description": "根据用户名和密码创建一个新的系统用户。",
@@ -49,37 +196,7 @@ const docTemplate = `{
],
"responses": {
"200": {
"description": "用户创建成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/user.CreateUserResponse"
}
}
}
]
}
},
"400": {
"description": "请求参数错误",
"schema": {
"$ref": "#/definitions/controller.Response"
}
},
"409": {
"description": "用户名已存在",
"schema": {
"$ref": "#/definitions/controller.Response"
}
},
"500": {
"description": "服务器内部错误",
"description": "业务失败具体错误码和信息见响应体例如400, 409, 500",
"schema": {
"$ref": "#/definitions/controller.Response"
}
@@ -113,37 +230,7 @@ const docTemplate = `{
],
"responses": {
"200": {
"description": "登录成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/user.LoginResponse"
}
}
}
]
}
},
"400": {
"description": "请求参数错误",
"schema": {
"$ref": "#/definitions/controller.Response"
}
},
"401": {
"description": "用户名或密码不正确",
"schema": {
"$ref": "#/definitions/controller.Response"
}
},
"500": {
"description": "服务器内部错误",
"description": "业务失败具体错误码和信息见响应体例如400, 401, 500",
"schema": {
"$ref": "#/definitions/controller.Response"
}
@@ -153,6 +240,9 @@ const docTemplate = `{
}
},
"definitions": {
"controller.Properties": {
"type": "object"
},
"controller.Response": {
"type": "object",
"properties": {
@@ -169,6 +259,100 @@ const docTemplate = `{
}
}
},
"device.CreateDeviceRequest": {
"type": "object"
},
"device.DeviceResponse": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"location": {
"type": "string"
},
"name": {
"type": "string"
},
"parent_id": {
"type": "integer"
},
"properties": {
"$ref": "#/definitions/controller.Properties"
},
"sub_type": {
"$ref": "#/definitions/models.DeviceSubType"
},
"type": {
"$ref": "#/definitions/models.DeviceType"
},
"updated_at": {
"type": "string"
}
}
},
"device.UpdateDeviceRequest": {
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"location": {
"type": "string"
},
"name": {
"type": "string"
},
"parent_id": {
"type": "integer"
},
"properties": {
"$ref": "#/definitions/controller.Properties"
},
"sub_type": {
"$ref": "#/definitions/models.DeviceSubType"
},
"type": {
"$ref": "#/definitions/models.DeviceType"
}
}
},
"models.DeviceSubType": {
"type": "string",
"enum": [
"",
"temperature",
"humidity",
"ammonia",
"feed_valve",
"fan",
"water_curtain"
],
"x-enum-varnames": [
"SubTypeNone",
"SubTypeSensorTemp",
"SubTypeSensorHumidity",
"SubTypeSensorAmmonia",
"SubTypeValveFeed",
"SubTypeFan",
"SubTypeWaterCurtain"
]
},
"models.DeviceType": {
"type": "string",
"enum": [
"area_controller",
"device"
],
"x-enum-varnames": [
"DeviceTypeAreaController",
"DeviceTypeDevice"
]
},
"user.CreateUserRequest": {
"type": "object",
"required": [
@@ -240,7 +424,7 @@ const docTemplate = `{
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "1.0",
Host: "localhost:8080",
Host: "localhost:8086",
BasePath: "/api/v1",
Schemes: []string{},
Title: "猪场管理系统 API",

View File

@@ -14,9 +14,156 @@
},
"version": "1.0"
},
"host": "localhost:8080",
"host": "localhost:8086",
"basePath": "/api/v1",
"paths": {
"/devices": {
"get": {
"description": "获取系统中所有设备的列表",
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "获取设备列表",
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
},
"post": {
"description": "根据提供的信息创建一个新设备",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "创建新设备",
"parameters": [
{
"description": "设备信息",
"name": "device",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/device.CreateDeviceRequest"
}
}
],
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
}
},
"/devices/{id}": {
"get": {
"description": "根据设备ID获取单个设备的详细信息",
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "获取设备信息",
"parameters": [
{
"type": "string",
"description": "设备ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
},
"put": {
"description": "根据设备ID更新一个已存在的设备信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "更新设备信息",
"parameters": [
{
"type": "string",
"description": "设备ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "要更新的设备信息",
"name": "device",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/device.UpdateDeviceRequest"
}
}
],
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
},
"delete": {
"description": "根据设备ID删除一个设备软删除",
"produces": [
"application/json"
],
"tags": [
"设备管理"
],
"summary": "删除设备",
"parameters": [
{
"type": "string",
"description": "设备ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "业务失败,具体错误码和信息见响应体",
"schema": {
"$ref": "#/definitions/controller.Response"
}
}
}
}
},
"/users": {
"post": {
"description": "根据用户名和密码创建一个新的系统用户。",
@@ -43,37 +190,7 @@
],
"responses": {
"200": {
"description": "用户创建成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/user.CreateUserResponse"
}
}
}
]
}
},
"400": {
"description": "请求参数错误",
"schema": {
"$ref": "#/definitions/controller.Response"
}
},
"409": {
"description": "用户名已存在",
"schema": {
"$ref": "#/definitions/controller.Response"
}
},
"500": {
"description": "服务器内部错误",
"description": "业务失败具体错误码和信息见响应体例如400, 409, 500",
"schema": {
"$ref": "#/definitions/controller.Response"
}
@@ -107,37 +224,7 @@
],
"responses": {
"200": {
"description": "登录成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/user.LoginResponse"
}
}
}
]
}
},
"400": {
"description": "请求参数错误",
"schema": {
"$ref": "#/definitions/controller.Response"
}
},
"401": {
"description": "用户名或密码不正确",
"schema": {
"$ref": "#/definitions/controller.Response"
}
},
"500": {
"description": "服务器内部错误",
"description": "业务失败具体错误码和信息见响应体例如400, 401, 500",
"schema": {
"$ref": "#/definitions/controller.Response"
}
@@ -147,6 +234,9 @@
}
},
"definitions": {
"controller.Properties": {
"type": "object"
},
"controller.Response": {
"type": "object",
"properties": {
@@ -163,6 +253,100 @@
}
}
},
"device.CreateDeviceRequest": {
"type": "object"
},
"device.DeviceResponse": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"location": {
"type": "string"
},
"name": {
"type": "string"
},
"parent_id": {
"type": "integer"
},
"properties": {
"$ref": "#/definitions/controller.Properties"
},
"sub_type": {
"$ref": "#/definitions/models.DeviceSubType"
},
"type": {
"$ref": "#/definitions/models.DeviceType"
},
"updated_at": {
"type": "string"
}
}
},
"device.UpdateDeviceRequest": {
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"location": {
"type": "string"
},
"name": {
"type": "string"
},
"parent_id": {
"type": "integer"
},
"properties": {
"$ref": "#/definitions/controller.Properties"
},
"sub_type": {
"$ref": "#/definitions/models.DeviceSubType"
},
"type": {
"$ref": "#/definitions/models.DeviceType"
}
}
},
"models.DeviceSubType": {
"type": "string",
"enum": [
"",
"temperature",
"humidity",
"ammonia",
"feed_valve",
"fan",
"water_curtain"
],
"x-enum-varnames": [
"SubTypeNone",
"SubTypeSensorTemp",
"SubTypeSensorHumidity",
"SubTypeSensorAmmonia",
"SubTypeValveFeed",
"SubTypeFan",
"SubTypeWaterCurtain"
]
},
"models.DeviceType": {
"type": "string",
"enum": [
"area_controller",
"device"
],
"x-enum-varnames": [
"DeviceTypeAreaController",
"DeviceTypeDevice"
]
},
"user.CreateUserRequest": {
"type": "object",
"required": [

View File

@@ -1,5 +1,7 @@
basePath: /api/v1
definitions:
controller.Properties:
type: object
controller.Response:
properties:
code:
@@ -11,6 +13,73 @@ definitions:
description: 提示信息
type: string
type: object
device.CreateDeviceRequest:
type: object
device.DeviceResponse:
properties:
created_at:
type: string
id:
type: integer
location:
type: string
name:
type: string
parent_id:
type: integer
properties:
$ref: '#/definitions/controller.Properties'
sub_type:
$ref: '#/definitions/models.DeviceSubType'
type:
$ref: '#/definitions/models.DeviceType'
updated_at:
type: string
type: object
device.UpdateDeviceRequest:
properties:
location:
type: string
name:
type: string
parent_id:
type: integer
properties:
$ref: '#/definitions/controller.Properties'
sub_type:
$ref: '#/definitions/models.DeviceSubType'
type:
$ref: '#/definitions/models.DeviceType'
required:
- name
- type
type: object
models.DeviceSubType:
enum:
- ""
- temperature
- humidity
- ammonia
- feed_valve
- fan
- water_curtain
type: string
x-enum-varnames:
- SubTypeNone
- SubTypeSensorTemp
- SubTypeSensorHumidity
- SubTypeSensorAmmonia
- SubTypeValveFeed
- SubTypeFan
- SubTypeWaterCurtain
models.DeviceType:
enum:
- area_controller
- device
type: string
x-enum-varnames:
- DeviceTypeAreaController
- DeviceTypeDevice
user.CreateUserRequest:
properties:
password:
@@ -57,7 +126,7 @@ definitions:
example: testuser
type: string
type: object
host: localhost:8080
host: localhost:8086
info:
contact:
email: divano@example.com
@@ -70,6 +139,103 @@ info:
title: 猪场管理系统 API
version: "1.0"
paths:
/devices:
get:
description: 获取系统中所有设备的列表
produces:
- application/json
responses:
"200":
description: 业务失败,具体错误码和信息见响应体
schema:
$ref: '#/definitions/controller.Response'
summary: 获取设备列表
tags:
- 设备管理
post:
consumes:
- application/json
description: 根据提供的信息创建一个新设备
parameters:
- description: 设备信息
in: body
name: device
required: true
schema:
$ref: '#/definitions/device.CreateDeviceRequest'
produces:
- application/json
responses:
"200":
description: 业务失败,具体错误码和信息见响应体
schema:
$ref: '#/definitions/controller.Response'
summary: 创建新设备
tags:
- 设备管理
/devices/{id}:
delete:
description: 根据设备ID删除一个设备软删除
parameters:
- description: 设备ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: 业务失败,具体错误码和信息见响应体
schema:
$ref: '#/definitions/controller.Response'
summary: 删除设备
tags:
- 设备管理
get:
description: 根据设备ID获取单个设备的详细信息
parameters:
- description: 设备ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: 业务失败,具体错误码和信息见响应体
schema:
$ref: '#/definitions/controller.Response'
summary: 获取设备信息
tags:
- 设备管理
put:
consumes:
- application/json
description: 根据设备ID更新一个已存在的设备信息
parameters:
- description: 设备ID
in: path
name: id
required: true
type: string
- description: 要更新的设备信息
in: body
name: device
required: true
schema:
$ref: '#/definitions/device.UpdateDeviceRequest'
produces:
- application/json
responses:
"200":
description: 业务失败,具体错误码和信息见响应体
schema:
$ref: '#/definitions/controller.Response'
summary: 更新设备信息
tags:
- 设备管理
/users:
post:
consumes:
@@ -86,24 +252,7 @@ paths:
- application/json
responses:
"200":
description: 用户创建成功
schema:
allOf:
- $ref: '#/definitions/controller.Response'
- properties:
data:
$ref: '#/definitions/user.CreateUserResponse'
type: object
"400":
description: 请求参数错误
schema:
$ref: '#/definitions/controller.Response'
"409":
description: 用户名已存在
schema:
$ref: '#/definitions/controller.Response'
"500":
description: 服务器内部错误
description: 业务失败具体错误码和信息见响应体例如400, 409, 500
schema:
$ref: '#/definitions/controller.Response'
summary: 创建新用户
@@ -125,24 +274,7 @@ paths:
- application/json
responses:
"200":
description: 登录成功
schema:
allOf:
- $ref: '#/definitions/controller.Response'
- properties:
data:
$ref: '#/definitions/user.LoginResponse'
type: object
"400":
description: 请求参数错误
schema:
$ref: '#/definitions/controller.Response'
"401":
description: 用户名或密码不正确
schema:
$ref: '#/definitions/controller.Response'
"500":
description: 服务器内部错误
description: 业务失败具体错误码和信息见响应体例如400, 401, 500
schema:
$ref: '#/definitions/controller.Response'
summary: 用户登录

View File

@@ -6,6 +6,7 @@ import (
"net/http"
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/device"
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/user"
"git.huangwc.com/pig/pig-farm-controller/internal/app/service/token"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/config"
@@ -20,18 +21,19 @@ import (
// API 结构体定义了 HTTP 服务器及其依赖
type API struct {
engine *gin.Engine // Gin 引擎实例,用于处理 HTTP 请求
logger *logs.Logger // 日志记录器,用于输出日志信息
userRepo repository.UserRepository // 用户数据仓库接口,用于用户数据操作
tokenService token.TokenService // Token 服务接口,用于 JWT token 的生成和解析
httpServer *http.Server // 标准库的 HTTP 服务器实例,用于启动和停止服务
config config.ServerConfig // API 服务器的配置,使用 infra/config 包中的 ServerConfig
userController *user.Controller // 用户控制器实例
engine *gin.Engine // Gin 引擎实例,用于处理 HTTP 请求
logger *logs.Logger // 日志记录器,用于输出日志信息
userRepo repository.UserRepository // 用户数据仓库接口,用于用户数据操作
tokenService token.TokenService // Token 服务接口,用于 JWT token 的生成和解析
httpServer *http.Server // 标准库的 HTTP 服务器实例,用于启动和停止服务
config config.ServerConfig // API 服务器的配置,使用 infra/config 包中的 ServerConfig
userController *user.Controller // 用户控制器实例
deviceController *device.Controller // 设备控制器实例
}
// NewAPI 创建并返回一个新的 API 实例
// 负责初始化 Gin 引擎、设置全局中间件,并注入所有必要的依赖。
func NewAPI(cfg config.ServerConfig, logger *logs.Logger, userRepo repository.UserRepository, tokenService token.TokenService) *API {
func NewAPI(cfg config.ServerConfig, logger *logs.Logger, userRepo repository.UserRepository, deviceRepository repository.DeviceRepository, tokenService token.TokenService) *API {
// 设置 Gin 模式,例如 gin.ReleaseMode (生产模式) 或 gin.DebugMode (开发模式)
// 从配置中获取 Gin 模式
gin.SetMode(cfg.Mode)
@@ -53,6 +55,8 @@ func NewAPI(cfg config.ServerConfig, logger *logs.Logger, userRepo repository.Us
config: cfg,
// 在 NewAPI 中初始化用户控制器,并将其作为 API 结构体的成员
userController: user.NewController(userRepo, logger, tokenService),
// 在 NewAPI 中初始化设备控制器,并将其作为 API 结构体的成员
deviceController: device.NewController(deviceRepository, logger),
}
api.setupRoutes() // 设置所有路由
@@ -71,6 +75,16 @@ func (a *API) setupRoutes() {
userGroup.POST("", a.userController.CreateUser) // 注册创建用户接口 (POST /api/v1/users)
userGroup.POST("/login", a.userController.Login) // 注册用户登录接口 (POST /api/v1/users/login)
}
// 设备相关路由组
deviceGroup := v1.Group("/devices")
{
deviceGroup.POST("", a.deviceController.CreateDevice)
deviceGroup.GET("", a.deviceController.ListDevices)
deviceGroup.GET("/:id", a.deviceController.GetDevice)
deviceGroup.PUT("/:id", a.deviceController.UpdateDevice)
deviceGroup.DELETE("/:id", a.deviceController.DeleteDevice)
}
}
// 添加 Swagger UI 路由

View File

@@ -0,0 +1,268 @@
package device
import (
"errors"
"net/http"
"strconv"
"strings"
"time"
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
"github.com/gin-gonic/gin"
"gorm.io/datatypes"
"gorm.io/gorm"
)
// Controller 设备控制器,封装了所有与设备相关的业务逻辑
type Controller struct {
repo repository.DeviceRepository
logger *logs.Logger
}
// NewController 创建一个新的设备控制器实例
func NewController(repo repository.DeviceRepository, logger *logs.Logger) *Controller {
return &Controller{
repo: repo,
logger: logger,
}
}
// --- Request DTOs ---
// CreateDeviceRequest 定义了创建设备时需要传入的参数
type CreateDeviceRequest struct {
Name string `json:"name" binding:"required"`
Type models.DeviceType `json:"type" binding:"required"`
SubType models.DeviceSubType `json:"sub_type,omitempty"`
ParentID *uint `json:"parent_id,omitempty"`
Location string `json:"location,omitempty"`
Properties controller.Properties `json:"properties,omitempty"`
}
// UpdateDeviceRequest 定义了更新设备时需要传入的参数
type UpdateDeviceRequest struct {
Name string `json:"name" binding:"required"`
Type models.DeviceType `json:"type" binding:"required"`
SubType models.DeviceSubType `json:"sub_type,omitempty"`
ParentID *uint `json:"parent_id,omitempty"`
Location string `json:"location,omitempty"`
Properties controller.Properties `json:"properties,omitempty"`
}
// --- Response DTOs ---
// DeviceResponse 定义了返回给客户端的单个设备信息的结构
type DeviceResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Type models.DeviceType `json:"type"`
SubType models.DeviceSubType `json:"sub_type"`
ParentID *uint `json:"parent_id"`
Location string `json:"location"`
Properties controller.Properties `json:"properties"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// --- DTO 转换函数 ---
// newDeviceResponse 从数据库模型创建一个新的设备响应 DTO
func newDeviceResponse(device *models.Device) *DeviceResponse {
if device == nil {
return nil
}
return &DeviceResponse{
ID: device.ID,
Name: device.Name,
Type: device.Type,
SubType: device.SubType,
ParentID: device.ParentID,
Location: device.Location,
Properties: controller.Properties(device.Properties),
CreatedAt: device.CreatedAt.Format(time.RFC3339),
UpdatedAt: device.UpdatedAt.Format(time.RFC3339),
}
}
// newListDeviceResponse 从数据库模型切片创建一个新的设备列表响应 DTO 切片
func newListDeviceResponse(devices []*models.Device) []*DeviceResponse {
list := make([]*DeviceResponse, 0, len(devices))
for _, device := range devices {
list = append(list, newDeviceResponse(device))
}
return list
}
// --- Controller Methods ---
// CreateDevice godoc
// @Summary 创建新设备
// @Description 根据提供的信息创建一个新设备
// @Tags 设备管理
// @Accept json
// @Produce json
// @Param device body CreateDeviceRequest true "设备信息"
// @Success 200 {object} controller.Response{data=DeviceResponse} "业务码为201代表创建成功"
// @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体"
// @Router /devices [post]
func (c *Controller) CreateDevice(ctx *gin.Context) {
var req CreateDeviceRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("创建设备: 参数绑定失败: %v", err)
controller.SendErrorResponse(ctx, http.StatusBadRequest, err.Error())
return
}
device := &models.Device{
Name: req.Name,
Type: req.Type,
SubType: req.SubType,
ParentID: req.ParentID,
Location: req.Location,
Properties: datatypes.JSON(req.Properties),
}
if err := c.repo.Create(device); err != nil {
c.logger.Errorf("创建设备: 数据库操作失败: %v", err)
controller.SendErrorResponse(ctx, http.StatusInternalServerError, "创建设备失败")
return
}
controller.SendResponse(ctx, http.StatusCreated, "设备创建成功", newDeviceResponse(device))
}
// GetDevice godoc
// @Summary 获取设备信息
// @Description 根据设备ID获取单个设备的详细信息
// @Tags 设备管理
// @Produce json
// @Param id path string true "设备ID"
// @Success 200 {object} controller.Response{data=DeviceResponse} "业务码为200代表获取成功"
// @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体"
// @Router /devices/{id} [get]
func (c *Controller) GetDevice(ctx *gin.Context) {
deviceID := ctx.Param("id")
device, err := c.repo.FindByIDString(deviceID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
controller.SendErrorResponse(ctx, http.StatusNotFound, "设备未找到")
return
}
if strings.Contains(err.Error(), "无效的设备ID格式") {
controller.SendErrorResponse(ctx, http.StatusBadRequest, err.Error())
return
}
c.logger.Errorf("获取设备: 数据库操作失败: %v", err)
controller.SendErrorResponse(ctx, http.StatusInternalServerError, "获取设备信息失败")
return
}
controller.SendResponse(ctx, http.StatusOK, "获取设备信息成功", newDeviceResponse(device))
}
// ListDevices godoc
// @Summary 获取设备列表
// @Description 获取系统中所有设备的列表
// @Tags 设备管理
// @Produce json
// @Success 200 {object} controller.Response{data=[]DeviceResponse} "业务码为200代表获取成功"
// @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体"
// @Router /devices [get]
func (c *Controller) ListDevices(ctx *gin.Context) {
devices, err := c.repo.ListAll()
if err != nil {
c.logger.Errorf("获取设备列表: 数据库操作失败: %v", err)
controller.SendErrorResponse(ctx, http.StatusInternalServerError, "获取设备列表失败")
return
}
controller.SendResponse(ctx, http.StatusOK, "获取设备列表成功", newListDeviceResponse(devices))
}
// UpdateDevice godoc
// @Summary 更新设备信息
// @Description 根据设备ID更新一个已存在的设备信息
// @Tags 设备管理
// @Accept json
// @Produce json
// @Param id path string true "设备ID"
// @Param device body UpdateDeviceRequest true "要更新的设备信息"
// @Success 200 {object} controller.Response{data=DeviceResponse} "业务码为200代表更新成功"
// @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体"
// @Router /devices/{id} [put]
func (c *Controller) UpdateDevice(ctx *gin.Context) {
deviceID := ctx.Param("id")
// 1. 检查设备是否存在
existingDevice, err := c.repo.FindByIDString(deviceID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
controller.SendErrorResponse(ctx, http.StatusNotFound, "设备未找到")
return
}
if strings.Contains(err.Error(), "无效的设备ID格式") {
controller.SendErrorResponse(ctx, http.StatusBadRequest, err.Error())
return
}
c.logger.Errorf("更新设备: 查找设备失败: %v", err)
controller.SendErrorResponse(ctx, http.StatusInternalServerError, "更新设备失败")
return
}
// 2. 绑定请求参数
var req UpdateDeviceRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("更新设备: 参数绑定失败: %v", err)
controller.SendErrorResponse(ctx, http.StatusBadRequest, err.Error())
return
}
// 3. 更新从数据库中查出的现有设备对象的字段
existingDevice.Name = req.Name
existingDevice.Type = req.Type
existingDevice.SubType = req.SubType
existingDevice.ParentID = req.ParentID
existingDevice.Location = req.Location
existingDevice.Properties = datatypes.JSON(req.Properties)
// 4. 将修改后的 existingDevice 对象保存回数据库
if err := c.repo.Update(existingDevice); err != nil {
c.logger.Errorf("更新设备: 数据库操作失败: %v", err)
controller.SendErrorResponse(ctx, http.StatusInternalServerError, "更新设备失败")
return
}
controller.SendResponse(ctx, http.StatusOK, "设备更新成功", newDeviceResponse(existingDevice))
}
// DeleteDevice godoc
// @Summary 删除设备
// @Description 根据设备ID删除一个设备软删除
// @Tags 设备管理
// @Produce json
// @Param id path string true "设备ID"
// @Success 200 {object} controller.Response "业务码为200代表删除成功"
// @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体"
// @Router /devices/{id} [delete]
func (c *Controller) DeleteDevice(ctx *gin.Context) {
deviceID := ctx.Param("id")
// 我们需要先将字符串ID转换为uint因为Delete方法需要uint类型
idUint, err := strconv.ParseUint(deviceID, 10, 64)
if err != nil {
controller.SendErrorResponse(ctx, http.StatusBadRequest, "无效的设备ID格式")
return
}
if err := c.repo.Delete(uint(idUint)); err != nil {
c.logger.Errorf("删除设备: 数据库操作失败: %v", err)
controller.SendErrorResponse(ctx, http.StatusInternalServerError, "删除设备失败")
return
}
controller.SendResponse(ctx, http.StatusOK, "设备删除成功", nil)
}

View File

@@ -1,11 +1,14 @@
package controller
import (
"encoding/json"
"net/http"
"github.com/gin-gonic/gin"
)
// --- 通用响应结构 ---
// Response 定义统一的API响应结构体
type Response struct {
Code int `json:"code"` // 业务状态码
@@ -14,7 +17,6 @@ type Response struct {
}
// SendResponse 发送统一格式的JSON响应
// httpStatus 参数现在将几乎总是 http.StatusOK业务状态码通过 Response.Code 传递
func SendResponse(ctx *gin.Context, code int, message string, data interface{}) {
ctx.JSON(http.StatusOK, Response{
Code: code,
@@ -24,7 +26,9 @@ func SendResponse(ctx *gin.Context, code int, message string, data interface{})
}
// SendErrorResponse 发送统一格式的错误响应
// 错误响应通常不包含业务数据,因此 data 参数固定为 nil
func SendErrorResponse(ctx *gin.Context, code int, message string) {
SendResponse(ctx, code, message, nil)
}
// Properties 是一个自定义类型,用于在 Swagger 中正确表示 JSON 对象
type Properties json.RawMessage

View File

@@ -60,10 +60,8 @@ type LoginResponse struct {
// @Accept json
// @Produce json
// @Param user body CreateUserRequest true "用户信息"
// @Success 200 {object} controller.Response{data=CreateUserResponse} "用户创建成功"
// @Failure 400 {object} controller.Response "请求参数错误"
// @Failure 409 {object} controller.Response "用户名已存在"
// @Failure 500 {object} controller.Response "服务器内部错误"
// @Success 200 {object} controller.Response{data=user.CreateUserResponse} "业务码为201代表创建成功"
// @Failure 200 {object} controller.Response "业务失败具体错误码和信息见响应体例如400, 409, 500"
// @Router /users [post]
func (c *Controller) CreateUser(ctx *gin.Context) {
var req CreateUserRequest
@@ -93,7 +91,7 @@ func (c *Controller) CreateUser(ctx *gin.Context) {
return
}
controller.SendResponse(ctx, http.StatusOK, "用户创建成功", CreateUserResponse{
controller.SendResponse(ctx, http.StatusCreated, "用户创建成功", CreateUserResponse{
Username: user.Username,
ID: user.ID,
})
@@ -106,10 +104,8 @@ func (c *Controller) CreateUser(ctx *gin.Context) {
// @Accept json
// @Produce json
// @Param credentials body LoginRequest true "登录凭证"
// @Success 200 {object} controller.Response{data=LoginResponse} "登录成功"
// @Failure 400 {object} controller.Response "请求参数错误"
// @Failure 401 {object} controller.Response "用户名或密码不正确"
// @Failure 500 {object} controller.Response "服务器内部错误"
// @Success 200 {object} controller.Response{data=user.LoginResponse} "业务码为200代表登录成功"
// @Failure 200 {object} controller.Response "业务失败具体错误码和信息见响应体例如400, 401, 500"
// @Router /users/login [post]
func (c *Controller) Login(ctx *gin.Context) {
var req LoginRequest

View File

@@ -46,16 +46,19 @@ func NewApplication(configPath string) (*Application, error) {
// 4. 初始化任务执行器
executor := task.NewExecutor(cfg.Heartbeat.Concurrency, logger)
// 5. 初始化用户仓库
userRepo := repository.NewGormUserRepository(storage.GetDB())
// 6. 初始化 Token 服务
// 5. 初始化 Token 服务
tokenService := token.NewTokenService([]byte(cfg.App.JWTSecret))
// 7. 初始化 API 服务器
apiServer := api.NewAPI(cfg.Server, logger, userRepo, tokenService)
// 6. 初始化用户仓库
userRepo := repository.NewGormUserRepository(storage.GetDB())
// 8. 组装 Application 对象
// 7. 初始化设备仓库
deviceRepo := repository.NewGormDeviceRepository(storage.GetDB())
// 8. 初始化 API 服务器
apiServer := api.NewAPI(cfg.Server, logger, userRepo, deviceRepo, tokenService)
// 9. 组装 Application 对象
app := &Application{
Config: cfg,
Logger: logger,

View File

@@ -20,6 +20,9 @@ type DeviceRepository interface {
// FindByIDString 根据字符串形式的主键 ID 查找设备,方便控制器调用
FindByIDString(id string) (*models.Device, error)
// ListAll 获取所有设备的列表
ListAll() ([]*models.Device, error)
// ListByParentID 根据父级 ID 列出所有子设备。
// 如果 parentID 为 nil则列出所有顶层设备如区域主控
ListByParentID(parentID *uint) ([]*models.Device, error)
@@ -67,6 +70,15 @@ func (r *gormDeviceRepository) FindByIDString(id string) (*models.Device, error)
return r.FindByID(uint(idInt))
}
// ListAll 获取所有设备的列表
func (r *gormDeviceRepository) ListAll() ([]*models.Device, error) {
var devices []*models.Device
if err := r.db.Find(&devices).Error; err != nil {
return nil, err
}
return devices, nil
}
// ListByParentID 根据父级 ID 列出所有子设备
func (r *gormDeviceRepository) ListByParentID(parentID *uint) ([]*models.Device, error) {
var devices []*models.Device