package service

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"gadmin/config"
	"gadmin/internal/admin/consts"
	"gadmin/internal/admin/forms"
	"gadmin/internal/admin/library/docker"
	"gadmin/internal/admin/ws"
	"gadmin/internal/gorm/model"
	"gadmin/internal/gorm/query"
	"gadmin/utility"
	"gadmin/utility/character"
	"gadmin/utility/serializer"
	"gadmin/utility/token"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/docker/docker/api/types"

	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"gorm.io/gorm"
)

var Deploy = new(sDeploy)

type sDeploy struct{}

// 一个容器部署完成后的消息上报
func (s *sDeploy) Notify(ctx *gin.Context, req forms.DeployNotifyReq) serializer.Response {
	var (
		q      = query.Use(config.DB).ServerDeploy
		ql     = query.Use(config.DB).ServerDeployLog
		qs     = query.Use(config.DB).ServerDeployStat
		stat   *model.ServerDeployStat
		err    error
		status = consts.DeployStatusSuc
	)

	//  本次部署针对的容器 及相关配置
	taskItem, err := ql.Where(ql.TraceID.Eq(req.TraceID)).First()
	if err != nil {
		logrus.Warnf("first err:%+v", err)
		return serializer.DBErr(err.Error(), err)
	}

	if taskItem == nil {
		return serializer.DBErr("操作记录不存在", nil)
	}

	// 取出容器配置
	deploy, err := q.Where(q.ID.Eq(taskItem.DeployID)).First()
	if err != nil {

		logrus.Warnf("deploy err:%+v", err)
		return serializer.DBErr(err.Error(), err)
	}

	if deploy == nil {
		return serializer.DBErr("服务器不存在", nil)
	}

	// 取本次部署统计信息 一次部署可能是多个容器
	if taskItem.PublishType == consts.DeployPublishTypeVersion {
		stat, err = qs.Where(qs.BatchID.Eq(taskItem.BatchID)).First()
		if err != nil {
			logrus.Warnf("stat err:%+v", err)
			return serializer.DBErr(err.Error(), err)
		}

		if stat == nil {
			return serializer.DBErr("批量记录不存在", nil)
		}

		// 解析一个容器上报数据
		parsingError := func(log *forms.DeployLogViewModel) {

			if log == nil {
				return
			}

			logrus.Warnf("parsingError log:%+v\n", log)

			if strings.Contains(log.LogContent, `level=error`) {
				req.Code = 1
				req.Msg = fmt.Sprintf("拉取json配置异常，请检查")
				return
			}

			// 提取本次启动的进程
			ProcessFlag := "要开始的进程内容："
			line, err1 := extractFromFlagToEnd(log.LogContent, ProcessFlag)
			if err1 != nil {
				req.Code = 1
				req.Msg = fmt.Sprintf("未找到本次需要启动的进程信息，请检查")
				return
			}
			logrus.Infof("本次启动进程: %v\n", line)

			extra := make(map[string]interface{})
			err := json.Unmarshal([]byte(taskItem.Extra), &extra)
			if err != nil {
				logrus.Infof("parsingError 1: %v\n", err)
				return
			}
			deployServer, ok := extra["deployServ"]
			if !ok {
				logrus.Infof("parsingError 2: %v\n", extra)
				return
			}
			var ds []string
			ds1, ok := deployServer.([]interface{})
			if !ok {
				logrus.Infof("parsingError 3: %v %T\n", deployServer, deployServer)
				return
			}
			for _, v := range ds1 {
				ds = append(ds, v.(string))
			}
			// err = json.Unmarshal([]byte(ds1), &ds)
			// if err != nil {
			// 	logrus.Infof("parsingError 32: %v\n", err)
			// 	return
			// }

			logrus.Infof("parsingError 4: %v\n", ds)

			// 检查进程数量是否正常
			var monitor = make(map[string]bool)
			// _, startServ, _ := docker.ParseDeployServArgs(deploy.ServerType, ds, deploy.IsMproom)
			var serverList []string
			if len(ds) == 1 && ds[0] == "all" {
				serverList = strings.Split(line, ",")
			} else {
				serverList = ds
			}
			for _, v := range serverList {
				if v == consts.DeployPackJsonConvert {
					continue
				}
				// if v == consts.DeployPackMPRoom {
				// 	if deploy.IsMproom != consts.INTTRUE {
				// 		continue
				// 	}
				// }
				n := "./" + v
				monitor[n] = false
			}

			// if consts.DeployTypeGrave == deploy.ServerType {
			// 	if deploy.IsMproom == consts.INTTRUE {
			// 		monitor = map[string]bool{
			// 			"./archive": false,
			// 			// "./chapter": false,
			// 			"./chapterd": false,
			// 			//"./gate":     false,
			// 			"./store":  false,
			// 			"./world":  false,
			// 			"./mproom": false,
			// 		}
			// 	} else {
			// 		monitor = map[string]bool{
			// 			"./archive": false,
			// 			// "./chapter": false,
			// 			"./chapterd": false,
			// 			"./store":    false,
			// 			"./world":    false,
			// 		}
			// 	}
			// }

			// if consts.DeployTypeLogin == deploy.ServerType {
			// 	monitor = map[string]bool{
			// 		"./login": false,
			// 	}
			// }

			// if consts.DeployTypeGate == deploy.ServerType {
			// 	monitor = map[string]bool{
			// 		"./gate": false,
			// 	}
			// }

			logrus.Infof("parsingError 5: %v\n", monitor)

			if len(monitor) == 0 {
				logrus.Warn("GameAlarm monitor is not set")
				return
			}

			process, err := docker.GetGraveProcess(docker.GraveProcessInput{Ctx: ctx, Deploy: deploy})
			if err != nil {
				req.Code = 2
				req.Msg = fmt.Sprintf("docker通讯异常:[%v]", err.Error())
				return
			}

			logrus.Infof("parsingError 7: %v\n", string(process))

			for _, line := range strings.Split(string(process), "\n") {
				// logrus.Infof("line:%s\n", line)
				awk := strings.Split(line, " ")
				awk2 := awk[len(awk)-1]
				if _, ok := monitor[awk2]; ok {
					monitor[awk2] = true
				}
			}

			logrus.WithField("method", "Notify").Infof("monitor： %+v\n", monitor)

			for s, b := range monitor {
				if !b {
					ps := strings.ReplaceAll(s, "./", "")
					req.Code = 2
					req.Msg = fmt.Sprintf("服务启动异常，未监测进程到:[%v]", ps)
					return
				}
			}

		}

		// 解析日志中是否存在错误
		log, err := s.getDeployLog(ctx, forms.DeployLogViewReq{Id: taskItem.ID, LogFile: req.LogFile})
		if err != nil {
			logrus.Warnf("解析日志失败:%+v", err.Error())
		}
		parsingError(log)
	}

	if req.Code != 0 {
		status = consts.DeployStatusErr
	}

	_, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(taskItem.DeployID)).Updates(model.ServerDeploy{
		DeployStatus: status,
	})
	if err != nil {

		logrus.Warnf("Updates ServerDeploy err:%+v", err)
		return serializer.DBErr(err.Error(), err)
	}

	_, err = query.Use(config.DB).ServerDeployLog.WithContext(ctx).Where(ql.ID.Eq(taskItem.ID)).Updates(model.ServerDeployLog{
		LogFile:  req.LogFile,
		ErrorMsg: req.Msg,
		Status:   status,
		EndAt:    time.Now(),
	})
	if err != nil {
		logrus.Warnf("Updates ServerDeployLog err:%+v", err)
		return serializer.DBErr(err.Error(), err)
	}

	msg := fmt.Sprintf("应用[%v]部署成功。远程服务器提示：%v", taskItem.Name, req.Msg)
	code := consts.CodeSuccess
	if req.Code != 0 {
		msg = fmt.Sprintf("应用[%v]部署失败。远程服务器提示：%v", taskItem.Name, req.Msg)
		code = consts.CodeParamErr
	}

	if taskItem.AdminID > 0 {
		msgData := ws.Msg{
			Id:   taskItem.ID,
			Type: consts.SocketDeployNotify,
			Code: code,
			Msg:  msg,
		}
		go ws.SendToAdmin(taskItem.AdminID, msgData)
	}

	if taskItem.PublishType != consts.DeployPublishTypeVersion {
		return serializer.Suc(nil)
	}

	// 更新统计
	if req.Code != 0 {
		tx := config.DB.Exec("UPDATE server_deploy_stat SET fail=fail+? WHERE batch_id=?", 1, stat.BatchID)
		if tx.Error != nil {
			logrus.Warnf("Notify Updates server_deploy_stat fail err:%+v", err)
		}
		stat.Fail += 1
	} else {
		tx := config.DB.Exec("UPDATE server_deploy_stat SET success=success+? WHERE batch_id=?", 1, stat.BatchID)
		if tx.Error != nil {
			logrus.Warnf("Notify Updates server_deploy_stat success err:%+v", err)
		}
		stat.Success += 1
	}

	// 满足通知条件
	if stat.Success+stat.Fail+stat.Timeout >= stat.Total {
		msgData := ws.Msg{
			Id:   taskItem.ID,
			Type: consts.SocketDeployNotify,
			Code: consts.CodeSuccess,
			Msg:  fmt.Sprintf("批量部署任务全部执行完毕，成功:%v,失败:%v,超时:%v", stat.Success, stat.Fail, stat.Timeout),
		}
		go ws.SendToAdmin(taskItem.AdminID, msgData)

		// 更新通知状态
		_, err = query.Use(config.DB).ServerDeployStat.WithContext(ctx).Where(qs.ID.Eq(stat.ID)).Updates(model.ServerDeployStat{
			IsNotify: consts.DeployNotifyOk,
			NotifyAt: time.Now(),
		})
		if err != nil {
			logrus.Warnf("Updates ServerDeployStat err:%+v", err)
			return serializer.DBErr(err.Error(), err)
		}
	}
	return serializer.Suc(nil)
}

func extractFromFlagToEnd(input string, startFlag string) (string, error) {
	// 使用bufio.NewScanner模拟逐行读取
	scanner := bufio.NewScanner(strings.NewReader(input))
	var line string
	found := false

	for scanner.Scan() {
		line = scanner.Text()
		if strings.Contains(line, startFlag) {
			// 找到标记，提取整行
			found = true
			break
		}
	}

	if err := scanner.Err(); err != nil {
		return "", err
	}

	if !found {
		return "", fmt.Errorf("startFlag not found")
	}

	startIndex := strings.Index(line, startFlag)
	if startIndex == -1 {
		return "", errors.New("未找到指定的字符串")
	}

	// 计算截取的开始位置（即"要开始的进程内容："之后的位置）
	// 注意：由于我们要跳过"要开始的进程内容："这个字符串，所以需要加上它的长度
	start := startIndex + len(startFlag)

	// 截取字符串
	// 这里我们没有指定end，所以它会截取到originalString的末尾
	result := line[start:]

	return result, nil
}

func (s *sDeploy) getDeployLog(ctx *gin.Context, req forms.DeployLogViewReq) (taskItem *forms.DeployLogViewModel, err error) {
	var (
		q  = query.Use(config.DB).ServerDeploy
		ql = query.Use(config.DB).ServerDeployLog
	)

	if err = ql.Where(ql.ID.Eq(req.Id)).Scan(&taskItem); err != nil {
		return
	}

	if taskItem == nil {
		err = errors.New("操作记录不存在")
		return
	}

	deploy, err := q.Where(q.ID.Eq(taskItem.DeployID)).First()
	if err != nil {
		return
	}

	if deploy == nil {
		err = errors.New("操作记录不存在")
		return
	}

	if taskItem.LogFile == "" && req.LogFile != "" {
		taskItem.LogFile = req.LogFile
	}

	log, err := docker.ReadDeployLog(docker.ReadDeployLogInput{
		Ctx:     ctx,
		Deploy:  deploy,
		LogFile: taskItem.LogFile,
	})
	if err != nil {
		return
	}

	taskItem.LogContent = string(log)
	taskItem.LogContent = utility.Rmu0000(taskItem.LogContent)
	taskItem.LogContent = strings.Replace(taskItem.LogContent, ``, "", -1)
	taskItem.LogContent = strings.Replace(taskItem.LogContent, `[36mINFO[0m`, "", -1)
	return
}

func (s *sDeploy) LogView(ctx *gin.Context, req forms.DeployLogViewReq) serializer.Response {
	taskItem, err := s.getDeployLog(ctx, req)
	if err != nil {
		return serializer.ParamErr(err.Error(), err)
	}
	return serializer.Suc(taskItem)
}

func (s *sDeploy) LogList(ctx *gin.Context, req forms.DeployLogListReq) serializer.Response {
	var (
		q            = query.Use(config.DB).ServerDeployLog
		m            = q.WithContext(ctx).Where(q.DeployID.Eq(req.DeployId))
		offset int64 = 0
		models forms.UserAccountListRes
		lists  []forms.DeployLogListModel
	)

	m = m.Order(q.ID.Desc())

	req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)

	count, err := m.Count()
	if err != nil {
		return serializer.Err(consts.CodeParamErr, "查询出错 count", err)
	}

	if count > 0 {
		if err = m.Limit(int(req.PerPage)).Offset(int(offset)).Scan(&lists); err != nil {
			return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
		}
	}

	for k, v := range lists {
		if v.AdminID > 0 {
			user, err := User.GetUser(v.AdminID)
			if err != nil {
				logrus.Warnf("Deploy LogList GetUser err:%+v", err)
				continue
			}

			if user != nil {
				lists[k].AdminName = fmt.Sprintf("%v(%v)", user.UserName, v.AdminID)
			}
		}
	}

	models.List = lists
	models.Page = req.Page
	models.PerPage = req.PerPage
	models.PageCount = (count + req.PerPage - 1) / req.PerPage
	return serializer.Suc(models)
}

// 获取部署的服务器列表
func (s *sDeploy) List(ctx *gin.Context, req forms.DeployListReq) serializer.Response {
	var (
		q            = query.Use(config.DB).ServerDeploy
		m            = q.WithContext(ctx).Where(q.Environment.Eq(os.Getenv("GIN_MODE")))
		offset int64 = 0
		models forms.UserAccountListRes
		lists  []forms.DeployListData
	)

	if req.Name != "" {
		m = m.Where(q.Name.Like(req.Name))
	}

	if req.ContainerName != "" {
		m = m.Where(q.ContainerName.Like(req.ContainerName))
	}

	if len(req.ServerType) > 0 {
		m = m.Where(q.ServerType.In(req.ServerType...))
	}

	m = m.Order(q.ServerType, q.ID.Desc())

	req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)

	count, err := m.Count()
	if err != nil {
		return serializer.Err(consts.CodeParamErr, "查询出错 count", err)
	}

	if count > 0 {
		//if err = m.Limit(int(req.PerPage)).Offset(int(offset)).Scan(&lists); err != nil {
		if err = m.Scan(&lists); err != nil {
			return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
		}
	}

	//区分多少docker地址
	dockerMap := make(map[string]map[string]string)
	for _, item := range lists {
		if _, ok := dockerMap[item.DockerAddr]; ok {
			continue
		}
		dockerInfoMap := make(map[string]string)
		dockerInfoMap["addr"] = item.DockerAddr
		dockerInfoMap["caPem"] = item.CaPem
		dockerInfoMap["certPem"] = item.CertPem
		dockerInfoMap["keyPem"] = item.KeyPem
		dockerMap[item.DockerAddr] = dockerInfoMap
	}
	logrus.Infof("dockerMap:%+v", dockerMap)

	//获取docker列表
	containerMap := make(map[string]types.Container)
	for addr, info := range dockerMap {
		logrus.Infof("info:%+v", info)
		containerList, err := docker.GetContainerList(ctx, info["addr"], info["caPem"], info["certPem"], info["keyPem"])
		if err != nil {
			logrus.Errorf("获取%s docker列表失败:%v", addr, err)
			// return serializer.Response{}
		}
		for _, item := range containerList {
			containerMap[item.Names[0]] = item
		}
	}
	nt := time.Now()
	for k, v := range lists {
		if v.AdminID > 0 {
			user, err := User.GetUser(v.AdminID)
			if err != nil {
				logrus.Warnf("Deploy LogList GetUser err:%+v", err)
				continue
			}
			if user != nil {
				lists[k].AdminName = fmt.Sprintf("%v(%v)", user.UserName, v.AdminID)
			}
		}
		if v.LastSyncAt.Add(consts.DeployRunSyncTime).Before(nt) {
			runStatus := consts.DeployRunStart
			container := containerMap["/"+v.ContainerName]

			if container.State != "running" {
				runStatus = consts.DeployRunStop
			}
			if container.ID == "" {
				runStatus = consts.DeployRunNoFound
			}

			_, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(v.ID)).Updates(model.ServerDeploy{
				RunStatus:  runStatus,
				LastSyncAt: time.Now(),
			})

			if err != nil {
				logrus.Warnf("ServerDeploy Updates LastSyncAt err:%+v", err)
			}

		}

		lists[k].DeployStatus = s.getLastDeployStatus(ctx, v.ID)
	}
	//for k, v := range lists {
	//	if v.AdminID > 0 {
	//		user, err := User.GetUser(v.AdminID)
	//		if err != nil {
	//			logrus.Warnf("Deploy LogList GetUser err:%+v", err)
	//			continue
	//		}
	//		if user != nil {
	//			lists[k].AdminName = fmt.Sprintf("%v(%v)", user.UserName, v.AdminID)
	//		}
	//	}
	//	if v.LastSyncAt.Add(consts.DeployRunSyncTime).Before(nt) {
	//		runStatus := consts.DeployRunStart
	//		container, err := docker.GetContainer(ctx, v.DockerAddr, v.CaPem, v.CertPem, v.KeyPem, v.ContainerName)
	//		if err != nil {
	//			logrus.Warnf("ContainerErr: %+v", err)
	//		}
	//
	//		if container != nil {
	//			if container.State != "running" {
	//				runStatus = consts.DeployRunStop
	//			}
	//		} else {
	//			runStatus = consts.DeployRunNoFound
	//			logrus.Warnf("ContainerInfo: %+v;\n", container)
	//		}
	//
	//		_, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(v.ID)).Updates(model.ServerDeploy{
	//			RunStatus:  runStatus,
	//			LastSyncAt: time.Now(),
	//		})
	//
	//		if err != nil {
	//			logrus.Warnf("ServerDeploy Updates LastSyncAt err:%+v", err)
	//		}
	//
	//	}
	//
	//	lists[k].DeployStatus = s.getLastDeployStatus(ctx, v.ID)
	//}
	newList1 := make([]forms.DeployListData, 0)
	if len(req.RunStatus) > 0 {
		for _, item := range lists {
			if character.InSlice(req.RunStatus, strconv.Itoa(int(item.RunStatus))) {
				newList1 = append(newList1, item)
			}
		}
	} else {
		newList1 = lists
	}

	newList2 := make([]forms.DeployListData, 0)
	if len(req.DeployStatus) > 0 {
		for _, item := range newList1 {
			if character.InSlice(req.DeployStatus, strconv.Itoa(int(item.DeployStatus))) {
				newList2 = append(newList2, item)
			}
		}
	} else {
		newList2 = newList1
	}
	pageCount := int64(len(newList2))
	pageList := make([]forms.DeployListData, 0)
	if offset+req.PerPage <= pageCount-1 {
		pageList = newList2[offset : req.PerPage+offset]
	} else if offset+req.PerPage > pageCount-1 && offset <= pageCount-1 {
		pageList = newList2[offset:]
	}

	models.List = pageList
	//models.List = lists
	models.Page = req.Page
	models.PerPage = req.PerPage
	// todo 分页逻辑处理
	models.PageCount = (pageCount + req.PerPage - 1) / req.PerPage
	return serializer.Suc(models)
}

// getLastDeployStatus 获取最后的部署状态
func (s *sDeploy) getLastDeployStatus(ctx *gin.Context, deployId int64) int32 {
	var ql = query.Use(config.DB).ServerDeployLog
	log, _ := ql.WithContext(ctx).Where(ql.DeployID.Eq(deployId)).Order(ql.ID.Desc()).First()
	if log == nil {
		return consts.DeployStatusWait
	}
	return log.Status
}

// 下发一键重启等部署任务
func (s *sDeploy) Task(ctx *gin.Context, req forms.DeployTaskReq) serializer.Response {
	var (
		batchId  = character.Md5Content([]byte(character.RandStringRunes(32)))
		taskList = make(map[string]docker.PushDeployTaskInput, len(req.Ids))
	)

	err := config.DB.Transaction(func(tx *gorm.DB) error {
		for _, id := range req.Ids {
			var (
				q   = query.Use(config.DB).ServerDeploy
				ql  = query.Use(config.DB).ServerDeployLog
				err error
			)
			first, err := q.Where(q.ID.Eq(id)).First()
			if err != nil {
				return err
			}

			if first == nil {
				return errors.New(fmt.Sprintf("服务器不存在 id:%v", id))
			}

			log, _ := ql.Where(ql.DeployID.Eq(first.ID)).Order(ql.CreatedAt.Desc()).First()
			// 只要正式服验证时间
			if os.Getenv("GIN_MODE") == "release" && log != nil && log.CreatedAt.Unix()+30 > time.Now().Unix() {
				return errors.New(fmt.Sprintf("距离上次部署时间过近，请稍后%v秒后再试  id:%v", log.CreatedAt.Unix()+30-time.Now().Unix(), id))
			}

			extra := make(map[string]interface{})
			extra["deployServ"] = req.DeployServ
			strExtra, err := json.Marshal(extra)
			if err != nil {
				return err
			}

			// 新建部署任务记录
			createData := &model.ServerDeployLog{
				AdminID:       token.GetUID(ctx),
				DeployID:      first.ID,
				BatchID:       batchId,
				TraceID:       character.Md5Content([]byte(character.RandStringRunes(32))),
				ContainerName: first.ContainerName,
				Name:          first.Name,
				PublishType:   consts.DeployPublishTypeVersion,
				Version:       req.Version,
				Extra:         string(strExtra),
				LogFile:       "",
				Status:        consts.DeployStatusIng,
				EndAt:         time.Now().AddDate(-20, 0, 0),
				CreatedAt:     time.Now(),
			}

			if err = query.Use(config.DB).ServerDeployLog.WithContext(ctx).Create(createData); err != nil {
				return err
			}

			if createData.ID < 1 {
				return errors.New(fmt.Sprintf("创建失败，请稍后重试！ id:%v", id))
			}

			logrus.Warnf("DeployTask createData:%+v", createData)

			taskList[createData.TraceID] = docker.PushDeployTaskInput{
				Ctx:        ctx,
				Deploy:     first,
				Log:        createData,
				DeployServ: req.DeployServ,
			}
		}

		// 插入批量记录
		if err := query.Use(config.DB).ServerDeployStat.WithContext(ctx).Create(&model.ServerDeployStat{
			AdminID:   token.GetUID(ctx),
			BatchID:   batchId,
			Total:     int64(len(req.Ids)),
			Success:   0,
			Fail:      0,
			Timeout:   0,
			NotifyAt:  time.Now().AddDate(-20, 0, 0),
			IsNotify:  consts.DeployNotifyNo,
			CreatedAt: time.Now(),
		}); err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		return serializer.DBErr(err.Error(), err)
	}

	for _, task := range taskList {
		err = docker.PushDeployTask(task)
		if err != nil {
			logrus.Infof("task.PushDeployTask return err:%+v", err)
			var (
				q  = query.Use(config.DB).ServerDeploy
				ql = query.Use(config.DB).ServerDeployLog
			)

			_, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(task.Deploy.ID)).Updates(model.ServerDeploy{
				RunStatus:  consts.DeployStatusErr,
				LastSyncAt: time.Now(),
			})
			if err != nil {
				logrus.Warnf("task.ServerDeploy Updates LastSyncAt err:%+v", err)
			}

			errorMsg := fmt.Sprintf("创建远程任务失败:%v ", err.Error())

			_, err = query.Use(config.DB).ServerDeployLog.WithContext(ctx).Where(ql.ID.Eq(task.Log.ID)).Updates(model.ServerDeployLog{
				ErrorMsg: errorMsg,
				Status:   consts.DeployStatusErr,
				EndAt:    time.Now(),
			})

			if err != nil {
				logrus.Warnf("task.ServerDeployLog Updates LastSyncAt err:%+v", err)
			}

			tx := config.DB.Exec("UPDATE server_deploy_stat SET fail=fail+? WHERE batch_id=?", 1, batchId)
			if tx.Error != nil {
				logrus.Warnf("task.ServerDeployLog Updates server_deploy_stat err:%+v", err)
			}

			// 推送失败通知
			if task.Log.AdminID > 0 {
				msgData := ws.Msg{
					Id:   task.Deploy.ID,
					Type: consts.SocketDeployNotify,
					Code: consts.CodeParamErr,
					Msg:  fmt.Sprintf("应用[%v]部署失败。远程服务器提示：%v", task.Deploy.Name, errorMsg),
				}
				go ws.SendToAdmin(task.Log.AdminID, msgData)
			}

		}
	}

	return serializer.Suc(nil)
}

func (s *sDeploy) Stop(ctx *gin.Context, req forms.DeployStopReq) serializer.Response {
	for _, id := range req.Ids {
		var (
			q   = query.Use(config.DB).ServerDeploy
			ql  = query.Use(config.DB).ServerDeployLog
			err error
		)
		first, err := q.Where(q.ID.Eq(id)).First()
		if err != nil {
			return serializer.DBErr(err.Error(), err)
		}

		if first == nil {
			return serializer.DBErr(fmt.Sprintf("Stop 服务器不存在 id:%v", id), nil)
		}

		log, _ := ql.Where(ql.DeployID.Eq(first.ID)).Order(ql.CreatedAt.Desc()).First()
		if log != nil && log.CreatedAt.Unix()+30 > time.Now().Unix() {
			return serializer.DBErr(fmt.Sprintf("Stop 距离上次部署时间过近，请稍后%v秒后再试  id:%v", log.CreatedAt.Unix()+30-time.Now().Unix(), id), nil)
		}

		// 新建部署任务记录
		createData := &model.ServerDeployLog{
			AdminID:       token.GetUID(ctx),
			DeployID:      first.ID,
			TraceID:       character.Md5Content([]byte(character.RandStringRunes(32))),
			ContainerName: first.ContainerName,
			Name:          first.Name,
			PublishType:   consts.DeployPublishTypeStop,
			Extra:         fmt.Sprintf(`{"stopType":%v}`, req.StopType),
			LogFile:       "",
			Status:        consts.DeployStatusIng,
			EndAt:         time.Now(),
			CreatedAt:     time.Now(),
		}

		if err = query.Use(config.DB).ServerDeployLog.WithContext(ctx).Create(createData); err != nil {
			return serializer.DBErr(err.Error(), err)
		}

		if createData.ID < 1 {
			return serializer.ParamErr(fmt.Sprintf("Stop 创建失败，请稍后重试！ id:%v", id), nil)
		}

		err = docker.PushDeployStop(docker.PushDeployStopInput{
			Ctx:      ctx,
			Deploy:   first,
			Log:      createData,
			StopType: req.StopType,
		})
		if err != nil {
			return serializer.ParamErr(fmt.Sprintf("Stop 创建远程任务失败:%v id:%v", err.Error(), id), nil)
		}
	}

	return serializer.Suc(nil)
}

// 编辑某服的配置
func (s *sDeploy) Edit(ctx *gin.Context, req forms.DeployEditReq) serializer.Response {
	var (
		q   = query.Use(config.DB).ServerDeploy
		err error
	)

	// 编辑
	if req.ID > 0 {
		_, err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(q.ID.Eq(req.ID)).Updates(model.ServerDeploy{
			GroupID:       req.GroupID,
			ServerType:    req.ServerType,
			ContainerName: req.ContainerName,
			Name:          req.Name,
			DockerAddr:    req.DockerAddr,
			CaPem:         req.CaPem,
			CertPem:       req.CertPem,
			KeyPem:        req.KeyPem,
			Remark:        req.Remark,
			IsMproom:      req.IsMproom,
			LastSyncAt:    time.Time{},
			CreatedAt:     time.Now(),
		})
		if err != nil {
			return serializer.DBErr(err.Error(), err)
		}
		return serializer.Suc(nil)
	}

	// 新增
	createData := model.ServerDeploy{
		Environment:   os.Getenv("GIN_MODE"),
		AdminID:       token.GetUID(ctx),
		GroupID:       req.GroupID,
		ContainerName: req.ContainerName,
		Name:          req.Name,
		ServerType:    req.ServerType,
		DockerAddr:    req.DockerAddr,
		CaPem:         req.CaPem,
		CertPem:       req.CertPem,
		KeyPem:        req.KeyPem,
		Remark:        req.Remark,
		RunStatus:     consts.DeployRunStop,
		DeployStatus:  consts.DeployStatusWait,
		CreatedAt:     time.Now(),
	}

	if err = query.Use(config.DB).ServerDeploy.WithContext(ctx).Omit(q.LastSyncAt.Desc()).Create(&createData); err != nil {
		return serializer.DBErr(err.Error(), err)
	}

	if createData.ID < 1 {
		return serializer.ParamErr("创建失败，请稍后重试！", nil)
	}

	return serializer.Suc(nil)
}

func (s *sDeploy) Delete(ctx *gin.Context, req forms.DeployDeleteReq) serializer.Response {
	// 编辑
	if req.ID <= 0 {
		return serializer.ParamErr("ID不能空，删除失败！", nil)
	}

	if _, err := query.Use(config.DB).ServerDeploy.WithContext(ctx).Where(query.Use(config.DB).ServerDeploy.ID.Eq(req.ID)).Delete(); err != nil {
		return serializer.DBErr(err.Error(), err)
	}
	return serializer.Suc(nil)
}
