longan 11 ヶ月 前
コミット
6bb4cc5cd4

+ 26 - 0
Dockerfile

@@ -0,0 +1,26 @@
+# syntax=docker/dockerfile:1
+FROM golang:1.20-alpine as builder
+WORKDIR /go/src/lcfns
+COPY . .
+RUN go env -w GO111MODULE=on \
+    && go env -w GOPROXY=https://goproxy.cn,direct \
+    && go env -w CGO_ENABLED=0 \
+    && go env \
+    && go mod tidy \
+    && go build -o /go/src/lcfns/server .
+
+FROM alpine:latest
+LABEL authors="longan"
+WORKDIR /go/src/lcfns
+COPY --from=0 /go/src/lcfns/server ./
+COPY --from=0 /go/src/lcfns/config.docker.yaml ./
+RUN chmod 0644 /go/src/lcfns/config.docker.yaml && chmod 0644 /go/src/lcfns/server
+# web服务端口
+EXPOSE 8889
+#事件服务端口
+EXPOSE 8850
+#网关服务端口
+EXPOSE 9001
+
+#ENTRYPOINT /go/src/lcfns/server -c config.docker.yaml
+ENTRYPOINT ["/go/src/lcfns/server", "-c", "config.docker.yaml"]

+ 1 - 1
README.md

@@ -60,8 +60,8 @@ lcfns
 ```
 
 ### 部署
+#### linux
 
-暂无
 
 ### 使用到的框架
 

+ 30 - 5
api/v1/app/event.go

@@ -8,6 +8,7 @@ import (
 	"lcfns/model/common/response"
 	"lcfns/model/system"
 	"lcfns/utils"
+	"strconv"
 )
 
 type EventApi struct {
@@ -16,12 +17,36 @@ type EventApi struct {
 // List 事件信息,以及图片链接。(事件类型,开始时间,处理时间,图片链接)
 func (ea *EventApi) List(c *gin.Context) {
 	var req request.Event
-	req.UId = utils.GetUserID(c)
-	err := c.ShouldBindJSON(&req)
+	req.GatewayName = c.Query("gatewayName")
+	t := c.Query("eventType")
+	atoi, err := strconv.Atoi(t)
+	if err == nil {
+		req.EventType = uint(atoi)
+	}
+	req.CreatedAt = c.Query("startTime")
+	req.EndAt = c.Query("endTime")
+	req.Order = c.Query("order")
+	b := c.Query("isHandled")
+	parseBool, err := strconv.ParseBool(b)
 	if err != nil {
-		logrus.Error()
+		req.IsHandled = &parseBool
+	}
+	page := c.Query("Page")
+	atoi, err = strconv.Atoi(page)
+	if err == nil {
+		req.Page = atoi
 	}
-	list, err := eventService.List(req)
+	pageSize := c.Query("PageSize")
+	atoi, err = strconv.Atoi(pageSize)
+	if err == nil {
+		req.PageSize = atoi
+	}
+	req.UId = utils.GetUserID(c)
+	//err = c.ShouldBindJSON(&req)
+	//if err != nil {
+	//	logrus.Error()
+	//}
+	list, total, err := eventService.List(req)
 	if err != nil {
 		logrus.Error(err)
 		response.FailWithMessage("查询失败", c)
@@ -29,7 +54,7 @@ func (ea *EventApi) List(c *gin.Context) {
 	}
 	response.OkWithData(response.PageResult{
 		List:     list,
-		Total:    int64(len(list)),
+		Total:    total,
 		Page:     req.Page,
 		PageSize: req.PageSize,
 	}, c)

+ 2 - 2
api/v1/app/gateway.go

@@ -82,13 +82,13 @@ func (api *GatewayApi) UserSet(c *gin.Context) {
 
 // DeleteGateways 删除网关及其关联设备
 func (api *GatewayApi) DeleteGateways(c *gin.Context) {
-	var req app.Gateway
+	var req request.Ids
 	err := c.ShouldBindJSON(&req)
 	if err != nil {
 		response.FailWithDetailed(err.Error(), "请求参数错误", c)
 		return
 	}
-	err = gs.DeleteGateways(req)
+	err = gs.DeleteGateways(&req)
 	if err != nil {
 		response.FailWithDetailed(err.Error(), "删除失败", c)
 		return

+ 8 - 7
api/v1/app/picture.go

@@ -12,7 +12,7 @@ type PictureApi struct {
 }
 
 func (pa *PictureApi) GetPicture(c *gin.Context) {
-	code := c.Query("picode")
+	code := c.Query("eventCode")
 	var dataId int
 	err := global.Db.Select("p.data_id").
 		Table("event e JOIN picture p ON e.id = p.event_id").
@@ -35,10 +35,11 @@ func (pa *PictureApi) GetPicture(c *gin.Context) {
 	}
 	c.Header("Content-Type", "image/jpeg;charset=UTF-8")
 
-	_, err = c.Writer.Write(picData.Data)
-	if err != nil {
-		logrus.Error(err)
-		response.Fail(c)
-		return
-	}
+	//_, err = c.Writer.Write(picData.Data)
+	//if err != nil {
+	//	logrus.Error(err)
+	//	response.FailWithMessage("获取失败", c)
+	//	return
+	//}
+	response.OkWithDetailed(picData.Data, "获取成功", c)
 }

+ 2 - 0
api/v1/ipcast/enter.go

@@ -1,6 +1,7 @@
 package ipcast
 
 import (
+	"fmt"
 	"github.com/gin-gonic/gin"
 	"lcfns/gatewayServer"
 	"lcfns/service"
@@ -18,6 +19,7 @@ var (
 func IpcastBaseUrl(c *gin.Context) string {
 	header := c.GetHeader("isn")
 	conn, ok := gatewayServer.ConnMap[header]
+	fmt.Println("connmap:", gatewayServer.ConnMap)
 	if !ok {
 		return ""
 	}

+ 57 - 19
api/v1/ipcast/ipcast.go

@@ -3,6 +3,7 @@ package ipcast
 import (
 	"crypto/rand"
 	"encoding/hex"
+	"errors"
 	"fmt"
 	"github.com/gin-gonic/gin"
 	"github.com/goccy/go-json"
@@ -11,6 +12,7 @@ import (
 	"lcfns/model/common/response"
 	ipResp "lcfns/model/ipcast/response"
 	"net/http"
+	"os"
 	"strconv"
 	"time"
 )
@@ -19,7 +21,11 @@ type IpcastApi struct {
 }
 
 func (api *IpcastApi) Play(c *gin.Context) {
-	url := GetIpcastUrl(c, Play)
+	url, err := GetIpcastUrl(c, Play)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
 	data, err := c.GetRawData()
 	if err != nil {
 		response.FailWithMessage("获取请求参数出错", c)
@@ -38,7 +44,11 @@ func (api *IpcastApi) Play(c *gin.Context) {
 }
 
 func (api *IpcastApi) Stop(c *gin.Context) {
-	url := GetIpcastUrl(c, Stop)
+	url, err := GetIpcastUrl(c, Stop)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
 	all, err := ipcastService.Common(url, http.MethodDelete, nil)
 	if err != nil {
 		response.Fail(c)
@@ -51,7 +61,11 @@ func (api *IpcastApi) Stop(c *gin.Context) {
 }
 
 func (api *IpcastApi) Status(c *gin.Context) {
-	url := GetIpcastUrl(c, Status)
+	url, err := GetIpcastUrl(c, Status)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
 	all, err := ipcastService.Common(url, http.MethodGet, nil)
 	if err != nil {
 		response.Fail(c)
@@ -64,7 +78,11 @@ func (api *IpcastApi) Status(c *gin.Context) {
 }
 
 func (api *IpcastApi) Ping(c *gin.Context) {
-	url := GetIpcastUrl(c, Ping)
+	url, err := GetIpcastUrl(c, Ping)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
 	all, err := ipcastService.Common(url, http.MethodGet, nil)
 	if err != nil {
 		response.Fail(c)
@@ -77,7 +95,11 @@ func (api *IpcastApi) Ping(c *gin.Context) {
 }
 
 func (api *IpcastApi) Volume(c *gin.Context) {
-	url := GetIpcastUrl(c, Volume)
+	url, err := GetIpcastUrl(c, Volume)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
 	data, err := c.GetRawData()
 	if err != nil {
 		response.FailWithMessage("获取请求参数出错", c)
@@ -113,7 +135,7 @@ var ipcastData = `
     "url": "http://192.168.110.69:8889/ipcast/AudioSource/%s",
     "sync": false,
     "queue": true,
-	"volume": 50,
+	"volume": 70,
     "loop": {
         "duration": %s,
         "times": 1,
@@ -124,6 +146,7 @@ var ipcastData = `
 
 // PlayAudio 前端传递mp3资源
 func (api *IpcastApi) PlayAudio(c *gin.Context) {
+	fmt.Println("headers:", c.Request.Header)
 	//生成uuid
 	uuid := make([]byte, 8)
 	rand.Read(uuid)
@@ -136,21 +159,36 @@ func (api *IpcastApi) PlayAudio(c *gin.Context) {
 		}()
 	}()
 	//读取mp3数据
-	//mp3, err := c.GetRawData()
-	mp3, err := c.FormFile("mp3")
+	mp3, err := c.FormFile("file")
 	if err != nil {
 		response.FailWithMessage("获取请求参数出错", c)
 		logrus.Error(err)
 		return
 	}
 	open, err := mp3.Open()
+	if err != nil {
+		logrus.Error("open err ", err)
+		return
+	}
 	readAll, err := io.ReadAll(open)
+	if err != nil {
+		logrus.Error("ReadAll err ", err)
+		return
+	}
 	//保存到内存
 	audio[uuidString] = readAll
+	os.WriteFile("map.wav", readAll, 0644)
 	//播放
 	t := c.PostForm("timeLength")
 	data := []byte(fmt.Sprintf(ipcastData, uuidString, t))
-	all, err := ipcastService.Common(GetIpcastUrl(c, Play), http.MethodPost, data)
+	fmt.Println("data:", string(data))
+	url, err := GetIpcastUrl(c, Play)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	fmt.Println("url:", url)
+	all, err := ipcastService.Common(url, http.MethodPost, data)
 	if err != nil {
 		response.Fail(c)
 		logrus.Error(err)
@@ -170,12 +208,12 @@ func (api *IpcastApi) AudioSource(c *gin.Context) {
 	id := c.Param("id")
 	fmt.Println("id:", id)
 	// 设置响应头,指定内容类型为 audio/mpeg
-	c.Header("Content-Type", "audio/mp3")
+	c.Header("Content-Type", "audio/wav")
 	c.Header("Access-Control-Allow-Origin", "*")
 	c.Header("Content-Length", strconv.Itoa(len(audio[id])))
 
 	// 将内存中的 MP3 数据作为响应返回给浏览器
-	c.Data(http.StatusOK, "audio/mp3", audio[id])
+	c.Data(http.StatusOK, "audio/wav", audio[id])
 }
 
 //↑实现语音喊话功能↑
@@ -188,21 +226,21 @@ const (
 	Volume
 )
 
-func GetIpcastUrl(c *gin.Context, expr byte) string {
+func GetIpcastUrl(c *gin.Context, expr byte) (string, error) {
 	baseUrl := IpcastBaseUrl(c)
 	if baseUrl == "" {
-		response.FailWithMessage("获取baseUrl失败", c)
-		return ""
+		return "", errors.New("获取baseUrl失败")
 	}
 	switch expr {
 	case Stop, Play:
-		return baseUrl + "/v1/speech"
+		return baseUrl + "/v1/speech", nil
 	case Status:
-		return baseUrl + "/v1/play_status"
+		return baseUrl + "/v1/play_status", nil
 	case Ping:
-		return baseUrl + "/v1/check_alive"
+		return baseUrl + "/v1/check_alive", nil
 	case Volume:
-		return baseUrl + "/v1/volume"
+		return baseUrl + "/v1/volume", nil
+	default:
+		return "", errors.New("获取baseUrl失败")
 	}
-	return ""
 }

+ 60 - 0
config.docker.yaml

@@ -0,0 +1,60 @@
+#系统/后台相关
+system:
+  env: public
+  addr: 8889
+  db-type: mysql
+  oss-type: local
+  use-multipoint: false
+  use-redis: true
+  iplimit-count: 15000
+  iplimit-time: 3600
+  router-prefix: ""
+jwt:
+  signing-key: 86e7a688-8d7d-42a5-af86-ec61120601d5
+  expires-time: 1d
+  buffer-time: 1d
+  issuer: qmPlus
+captcha:
+  key-long: 6     #验证码长度
+  img-width: 240  #宽
+  img-height: 80  #高
+  open-captcha: 0 #开启验证码 1关闭,0开启
+  open-captcha-timeout: 3600  #有效期
+logrus:
+mysql:
+  path: 172.18.0.2 #容器名
+  port: "3306"
+  config: charset=utf8mb4&parseTime=True&loc=Local
+  db-name: lcfns
+  username: root
+  password: root
+  prefix: ""
+  singular: false
+  engine: ""
+  max-idle-conns: 10
+  max-open-conns: 100
+  log-mode: info
+  log-zap: false
+#海康摄像头事件监听服务器,最多配三个,只需修改id,url,ipAddress和portNo
+HttpHostNotificationList:
+  HttpHostNotification:
+    -
+      id: 1
+      url: /event
+      protocolType: HTTP
+      parameterFormatType: XML
+      addressingFormatType: ipaddress
+      ipAddress: 8.138.100.203
+      portNo: 8850
+      httpBroken: true
+      httpAuthenticationMethod: none
+#调用外部接口
+foreign:
+  securityRewindUrl: "http://106.52.134.22:9099"
+  gatewayServer: "0.0.0.0:9001"
+  eventServer: "0.0.0.0:8850"
+#海康摄像头统一配置
+hikvision:
+  user: admin
+  password: kk176@lc
+  streamBaseUrl: "webrtc://106.52.134.22/live/"

+ 21 - 21
config/config.go

@@ -1,46 +1,46 @@
 package config
 
 import (
+	"flag"
+	"fmt"
 	"github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v3"
 	"io/ioutil"
-	"lcfns/isapi"
 	"os"
 )
 
 var Config = initConfig()
 
 func initConfig() config {
-	var c config
-	open, err := os.Open("./config.yaml")
+	var file *os.File
+	var err error
+	s := flag.String("c", "config.yaml", "")
+	flag.Parse()
+	fmt.Println("arg:", *s)
+	switch *s {
+	case "config.docker.yaml":
+		file, err = os.Open("./config.docker.yaml")
+	case "config.yaml":
+		file, err = os.Open("./config.yaml")
+	}
 	if err != nil {
 		logrus.Fatal("打开文件读取错误", err)
+		return config{}
 	}
-	data, err := ioutil.ReadAll(open)
+	var c config
+	data, err := ioutil.ReadAll(file)
 	if err != nil {
 		logrus.Fatal("读取配置文件错误", err)
 	}
 	if yaml.Unmarshal(data, &c) != nil {
 		logrus.Fatal("解析配置文件错误", err)
 	}
-	logrus.Info("配置文件读取成功:%+v", c)
-	return c
-}
-func initSipConf() isapi.SIPServerList {
-	var sip isapi.SIPServerList
-	open, err := os.Open("./sip.yaml")
-	if err != nil {
-		logrus.Fatal("打开文件读取错误", err)
+	if *s == "config.docker.yaml" {
+		path := os.Getenv("DB_HOST")
+		c.Mysql.Path = path
 	}
-	data, err := ioutil.ReadAll(open)
-	if err != nil {
-		logrus.Fatal("读取配置文件错误", err)
-	}
-	if yaml.Unmarshal(data, &sip) != nil {
-		logrus.Fatal("解析配置文件错误", err)
-	}
-	logrus.Info("配置文件读取成功:%+v", sip)
-	return sip
+	logrus.Infof("配置文件读取成功:%+v", c)
+	return c
 }
 
 type config struct {

+ 36 - 0
docker-compose.yml

@@ -0,0 +1,36 @@
+version: '3.8'
+services:
+  app:
+    image: lcfns:1.0  # 指定已有的应用程序镜像名称和标签
+    container_name: lcfns-server
+    volumes:
+      - lcfns:/go/src/lcfns
+    ports:
+      - "8889:8889"
+      - "8850:8850"
+      - "9001:9001"
+    environment:
+      - DB_HOST=db
+      - DB_USER=lc
+      - DB_PASSWORD=root
+    depends_on:
+      - db  # 表示 app 依赖于 db,即启动 app 之前会先启动 db
+    restart: on-failure
+    networks:
+      - lcfns-net
+  db:
+    image: mysql:8  # 指定已有的数据库镜像名称和标签
+    container_name: lcfns-mysql
+    environment:
+      MYSQL_ROOT_PASSWORD: root
+      MYSQL_DATABASE: lcfns
+      MYSQL_USER: lc
+      MYSQL_PASSWORD: root
+    ports:
+      - "3306:3306"  # 将数据库的 3306 端口映射到宿主机的 3306 端口
+    networks:
+      - lcfns-net
+networks:
+  lcfns-net:
+volumes:
+  lcfns:

+ 1 - 0
eventServer/eventServer.go

@@ -140,6 +140,7 @@ func handleMultipart(r *http.Request) {
 		//	"",
 		//	f)
 	}
+	fmt.Println("event:", eventAlert)
 }
 
 func handleEvent_(event EventNotificationAlert) string {

+ 27 - 0
initialize/logrus.go

@@ -0,0 +1,27 @@
+package initialize
+
+import (
+	rotatelogs "github.com/lestrrat/go-file-rotatelogs"
+	"github.com/sirupsen/logrus"
+	"os"
+	"path"
+	"time"
+)
+
+func InitLogrus() {
+	err := os.MkdirAll("./log", os.ModeDir)
+	if err != nil {
+		panic(err)
+	}
+	fileName := path.Join("./log", "info")
+	writer, _ := rotatelogs.New(
+		fileName+".%Y%m%d.log",
+		rotatelogs.WithMaxAge(15*24*time.Hour),    // 文件最大保存时间
+		rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔
+	)
+	logrus.SetFormatter(&logrus.JSONFormatter{})
+	logrus.SetLevel(logrus.DebugLevel)
+	logrus.SetOutput(os.Stdout)
+	logrus.SetReportCaller(true)
+	logrus.SetOutput(writer)
+}

+ 1 - 21
main.go

@@ -3,7 +3,6 @@ package main
 import (
 	"fmt"
 	"github.com/gin-gonic/gin"
-	rotatelogs "github.com/lestrrat/go-file-rotatelogs"
 	"github.com/sirupsen/logrus"
 	"lcfns/eventServer"
 	"lcfns/gatewayServer"
@@ -11,13 +10,11 @@ import (
 	"lcfns/initialize"
 	"lcfns/service/system"
 	"net/http"
-	"os"
-	"path"
 	"time"
 )
 
 func main() {
-	initLogrus()
+	initialize.InitLogrus()
 	go eventServer.StartEventServer() //摄像头事件监听服务端
 	go gatewayServer.GatewayServe()   //网关服务端
 	initialize.OtherInit()            //初始化缓存
@@ -56,20 +53,3 @@ func initServer(address string, router *gin.Engine) server {
 		MaxHeaderBytes: 1 << 20,
 	}
 }
-
-func initLogrus() {
-	err := os.MkdirAll("./log", os.ModeDir)
-	if err != nil {
-		panic(err)
-	}
-	fileName := path.Join("./log", "info")
-	writer, _ := rotatelogs.New(
-		fileName+".%Y%m%d.log",
-		rotatelogs.WithMaxAge(15*24*time.Hour),    // 文件最大保存时间
-		rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔
-	)
-	logrus.SetLevel(logrus.DebugLevel)
-	logrus.SetOutput(os.Stdout)
-	logrus.SetReportCaller(true)
-	logrus.SetOutput(writer)
-}

+ 2 - 0
model/app/request/event.go

@@ -11,7 +11,9 @@ type Event struct {
 	EventType uint   `json:"eventType"`
 	EventCode string `json:"eventCode"`
 	CreatedAt string `json:"startTime"`
+	EndAt     string `json:"endTime"`
 	IsHandled *bool  `json:"isHandled"`
+	Order     string `json:"order"`
 	request.PageInfo
 	//HandleTime  time.Time `json:"handleTime"`
 	//HandleBy    string    `json:"handleBy"`

+ 4 - 0
model/app/request/gateway.go

@@ -13,3 +13,7 @@ type GatewayRequest struct {
 	IpcastId int    `json:"ipcastId"`
 	request.PageInfo
 }
+
+type Ids struct {
+	IDS []int `json:"ids"`
+}

+ 1 - 1
model/app/response/event.go

@@ -7,5 +7,5 @@ type Event struct {
 	EventCode   string `json:"eventCode"`
 	StartTime   string `json:"startTime"`
 	HandleTime  string `json:"handleTime"`
-	PictureUrl  string `json:"pictureUrl"`
+	//PictureCode string `json:"pictureUrl"`
 }

+ 16 - 4
service/app/event.go

@@ -1,6 +1,7 @@
 package app
 
 import (
+	"fmt"
 	"github.com/sirupsen/logrus"
 	"lcfns/global"
 	"lcfns/model/app"
@@ -13,7 +14,7 @@ var picService = PictureService{}
 type EventService struct {
 }
 
-func (EventService) List(req request.Event) (events []response.Event, err error) {
+func (EventService) List(req request.Event) (events []response.Event, total int64, err error) {
 	//SELECT id,event_type,event_code,start_time,handle_time
 	//FROM `event` WHERE mac_address IN (SELECT c.mac_address FROM gateway g JOIN camera c ON g.id = c.gid WHERE g.uid = 1);
 	//查找用户所有摄像头的mac_address:
@@ -32,25 +33,36 @@ func (EventService) List(req request.Event) (events []response.Event, err error)
 	if req.IsHandled != nil && *req.IsHandled {
 		db.Where("NOT ISNULL(e.handle_time)")
 	}
+	fmt.Println("-------------------------------req.CreatedAt", req.CreatedAt)
 	if req.CreatedAt != "" {
 		db.Where("e.start_time > ?", req.CreatedAt)
 	}
+	if req.EndAt != "" {
+		db.Where("e.start_time < ?", req.EndAt)
+	}
 	if req.GatewayName != "" {
 		db.Where("g.gateway_name LIKE ?", "%"+req.GatewayName+"%")
 	}
+	db.Count(&total)
+	if req.Order != "" {
+		if req.Order == "1" {
+			db.Order("e.start_time DESC")
+		} else {
+			db.Order("e.start_time ASC")
+		}
+	}
 	err = db.Where("e.mac_address IN (?)", sub).
 		Offset((req.Page - 1) * req.PageSize).
 		Limit(req.PageSize).
 		Find(&events).Debug().Error
 	if err != nil {
 		logrus.Error(err)
-		return nil, err
+		return nil, 0, err
 	}
 	for i, v := range events {
 		events[i].EventType = sMap[v.EventType]
-		events[i].PictureUrl = "http:192.168.110.69:8889/picture/get?picode=" + v.EventCode
 	}
-	return events, err
+	return events, total, err
 }
 
 func (EventService) Create(event app.Event) error {

+ 31 - 28
service/app/gateway.go

@@ -44,7 +44,7 @@ func (gs *GatewayService) List(req request.GatewayRequest) (list []response.Gate
 		//"i.device_id AS ipcast_id," +
 		"i.state AS ipcast_state").
 		Table("gateway g LEFT JOIN camera c ON g.id = c.gid LEFT JOIN ipcast i ON  g.id = i.gid").
-		Where("g.is_deleted = 0")
+		Where("g.is_deleted = 0 AND c.deleted_at IS NULL AND i.is_deleted = 0")
 	//条件查询设备,网关名,网关osn,摄像头sn,ip音柱devid,
 	if req.Uid > 1 {
 		db.Where("g.uid = ?", req.Uid)
@@ -61,11 +61,12 @@ func (gs *GatewayService) List(req request.GatewayRequest) (list []response.Gate
 	if req.Name != "" {
 		db.Where("gateway_name LIKE ?", "%"+req.Name+"%")
 	}
+	db.Count(&total)
 	if req.Page != 0 || req.PageSize != 0 {
 		db.Offset(req.PageSize * (req.Page - 1)).Limit(req.PageSize)
 	}
 	err = db.Debug().Find(&list).Error
-	global.Db.Table("gateway").Where("is_deleted = 0").Count(&total)
+	//global.Db.Table("gateway").Where("is_deleted = 0").Count(&total)
 	return
 }
 
@@ -98,33 +99,35 @@ func (gs *GatewayService) UpdateState(gw *app.Gateway) error {
 }
 
 // DeleteGateways 删除网关及关联设备
-func (gs *GatewayService) DeleteGateways(gw app.Gateway) (err error) {
-	devs := gs.DevIds(gw.ID)
-	err = global.Db.Transaction(func(tx *gorm.DB) error {
-		//删除网关
-		if err := tx.Debug().Model(&app.Gateway{}).Where("id = ?", gw.ID).Update("is_deleted", 1).Error; err != nil {
-			return err
-		}
-		if err = tx.Debug().Delete(&gw).Error; err != nil {
-			return err
-		}
-		return nil
-	})
-	//删除摄像头
-	cs := CameraService{}
-	if devs.Cid != 0 {
-		var c app.Camera
-		c.ID = devs.Cid
-		err = cs.DeleteCamera(c)
-		if err != nil {
-			logrus.Error("删除摄像头失败", err)
+func (gs *GatewayService) DeleteGateways(ids *request.Ids) (err error) {
+	for _, id := range ids.IDS {
+		devs := gs.DevIds(uint(id))
+		err = global.Db.Transaction(func(tx *gorm.DB) error {
+			//删除网关
+			if err := tx.Debug().Model(&app.Gateway{}).Where("id = ?", id).Update("is_deleted", 1).Error; err != nil {
+				return err
+			}
+			if err = tx.Debug().Model(&app.Gateway{}).Delete("id = ?", id).Error; err != nil {
+				return err
+			}
+			return nil
+		})
+		//删除摄像头
+		cs := CameraService{}
+		if devs.Cid != 0 {
+			var c app.Camera
+			c.ID = devs.Cid
+			err = cs.DeleteCamera(c)
+			if err != nil {
+				logrus.Error("删除摄像头失败", err)
+			}
 		}
-	}
-	//删除ip音柱
-	if devs.Iid != 0 {
-		err = global.Db.Model(&app.Ipcast{}).Where("id = ?", devs.Iid).Update("is_deleted", 1).Error
-		if err != nil {
-			logrus.Error("删除ip音柱失败", err)
+		//删除ip音柱
+		if devs.Iid != 0 {
+			err = global.Db.Model(&app.Ipcast{}).Where("id = ?", devs.Iid).Update("is_deleted", 1).Error
+			if err != nil {
+				logrus.Error("删除ip音柱失败", err)
+			}
 		}
 	}
 	return err