go-view-server/internal/logic/admin/project.go

391 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package admin
// @Description
// @Author Ms <133814250@qq.com>
package admin
import (
"context"
"encoding/json"
"fmt"
"github.com/bndr/gojenkins"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/gogf/gf/v2/util/guid"
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/model"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
"hotgo/internal/utils"
"io"
"net/http"
"os"
"path/filepath"
"strings"
)
type sAdminProject struct{}
func NewAdminProject() *sAdminProject {
return &sAdminProject{}
}
func init() {
service.RegisterAdminProject(NewAdminProject())
}
const (
DEPLOY_BUILD_KEY = "deploy.build"
DEPLOY_BUILD_URL_KEY = DEPLOY_BUILD_KEY + "." + "url"
DEPLOY_BUILD_JOBNAME_KEY = DEPLOY_BUILD_KEY + "." + "jobName"
DEPLOY_BUILD_JOBDOWN_KEY = DEPLOY_BUILD_KEY + "." + "jobdown"
DEPLOY_BUILD_CALLBACK_KEY = DEPLOY_BUILD_KEY + "." + "callback"
DEPLOY_BUILD_MINIOPATH_KEY = DEPLOY_BUILD_KEY + "." + "ossPathPre"
DEPLOY_BUILD_UPLOAD_KEY = DEPLOY_BUILD_KEY + "." + "upload"
DEPLOY_BUILD_CONFIG_PATH = DEPLOY_BUILD_KEY + "." + "confPath"
DEPLOY_BUILD_FRONT_PATH = DEPLOY_BUILD_KEY + "." + "frontPath"
DEPLOY_BUILD_JOBTOKEN_KEY = DEPLOY_BUILD_KEY + "." + "jobToken"
DEPLOY_BUILD_JOBUSER_KEY = DEPLOY_BUILD_KEY + "." + "jobUser"
DEPLOY_BUILD_JOBPASSWORD_KEY = DEPLOY_BUILD_KEY + "." + "jobPassword"
)
// Model ORM模型
func (s *sAdminProject) Model(ctx context.Context) *gdb.Model {
return dao.AdminProject.Ctx(ctx).Handler(func(m *gdb.Model) *gdb.Model {
return m.Where(dao.AdminProject.Columns().CreatedBy, contexts.GetUserId(ctx))
})
}
// List 查询列表
func (s *sAdminProject) List(ctx context.Context, in *adminin.ProjectListInp) (list []*adminin.ProjectListModel, totalCount int, err error) {
mod := s.Model(ctx)
totalCount, err = mod.Count()
if err != nil {
return
}
if totalCount == 0 {
return
}
if err = mod.Page(in.Page, in.PerPage).OrderDesc(dao.AdminProject.Columns().Id).Scan(&list); err != nil {
return
}
for _, v := range list {
v.State = v.Status
v.CreateUserId = v.CreatedBy
v.CreateTime = v.CreatedAt
}
return
}
// Delete 删除
func (s *sAdminProject) Delete(ctx context.Context, in *adminin.ProjectDeleteInp) (err error) {
if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists {
err = gerror.New("没有找到项目!")
return err
}
var project *entity.AdminProject
err = s.Model(ctx).WherePri(in.Id).Scan(&project)
if project.Status == 1 {
err = gerror.New("项目已发布,无法删除!")
return err
}
_, err = s.Model(ctx).WherePri(in.Id).Delete()
return
}
// Create 新增
func (s *sAdminProject) Create(ctx context.Context, in *adminin.ProjectCreateInp) (res *adminin.ProjectCreateModel, err error) {
data := &entity.AdminProject{
Id: gconv.Int64(fmt.Sprintf("%v%v", gtime.Now().Format("YmdH"), grand.N(10000, 99999))),
ProjectName: in.ProjectName,
IndexImage: in.IndexImage,
Status: -1,
Remarks: in.Remarks,
CreatedBy: contexts.GetUserId(ctx),
}
if _, err = dao.AdminProject.Ctx(ctx).Data(data).Insert(); err != nil {
return nil, err
}
res = &adminin.ProjectCreateModel{
Id: data.Id,
ProjectName: data.ProjectName,
State: data.Status,
CreateTime: gtime.Now(),
CreateUserId: contexts.GetUserId(ctx),
IndexImage: data.IndexImage,
Remarks: data.Remarks,
}
return
}
// Edit 修改
func (s *sAdminProject) Edit(ctx context.Context, in *adminin.ProjectEditInp) (err error) {
if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists {
err = gerror.New("没有找到项目!")
return err
}
data := make(g.Map)
if len(in.IndexImage) > 0 {
data["index_image"] = in.IndexImage
}
if len(in.ProjectName) > 0 {
data["project_name"] = in.ProjectName
}
if len(in.Remarks) > 0 {
data["remarks"] = in.Remarks
}
if len(data) > 0 {
_, err = s.Model(ctx).WherePri(in.Id).Data(data).Update()
}
return
}
// SaveData 保存数据
func (s *sAdminProject) SaveData(ctx context.Context, in *adminin.ProjectSaveDataInp) (err error) {
if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists {
err = gerror.New("没有找到项目!")
return err
}
_, err = s.Model(ctx).WherePri(in.Id).Data("content", in.Content).Update()
return
}
// Publish 修改发布状态
func (s *sAdminProject) Publish(ctx context.Context, in *adminin.ProjectPublishInp) (err error) {
if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists {
err = gerror.New("没有找到项目!")
return err
}
_, err = s.Model(ctx).WherePri(in.Id).Data("status", in.State).Update()
return
}
// GetData 获取指定信息
func (s *sAdminProject) GetData(ctx context.Context, in *adminin.ProjectGetDataInp) (res *adminin.ProjectGetDataModel, err error) {
if err = dao.AdminProject.Ctx(ctx).WherePri(in.Id).Scan(&res); err != nil {
return nil, err
}
if res == nil {
err = gerror.New("项目不存在")
return
}
res.State = res.Status
return
}
// ExistById 判断指定项目是否存在
func (s *sAdminProject) ExistById(ctx context.Context, id int64) (exists bool, err error) {
count, err := s.Model(ctx).WherePri(id).Count()
if err != nil {
return
}
exists = count > 0
return
}
func (s *sAdminProject) Deploy(ctx context.Context, in *adminin.ProjectDeployInp) (err error) {
var tempName = guid.S()
var basePath = "resource/tmp/" + tempName
var project *entity.AdminProject
var jsonObj *model.JsonObj
err = s.Model(ctx).WherePri(in.Id).Scan(&project)
if project == nil {
err = gerror.New("项目不存在")
return
}
gconv.Struct(project.Content, &jsonObj)
fmt.Println(jsonObj.EditCanvasConfig.BackgroundImage)
if jsonObj.EditCanvasConfig.BackgroundImage != "" {
backimageUrl := gconv.String(jsonObj.EditCanvasConfig.BackgroundImage)
urlarr := strings.Split(backimageUrl, "?")
filename := filepath.Base(urlarr[0])
DownloadImage(ctx, urlarr[0], basePath+"/data/image/")
jsonObj.EditCanvasConfig.BackgroundImage = "/data/image/" + filename + "?" + urlarr[1]
}
componentList := jsonObj.ComponentList
for _, component := range componentList {
if component.Key == "Image" && component.Option.Dataset != nil {
fmt.Println("Image")
fmt.Println(component.Option.Dataset)
fileUrl := gconv.String(component.Option.Dataset)
filename := filepath.Base(fileUrl)
DownloadImage(ctx, fileUrl, basePath+"/data/image/")
component.Option.Dataset = "/data/image/" + filename
}
if component.Key == "Video" && component.Option.Dataset != nil {
fmt.Println("Video")
fmt.Println(component.Option.Dataset)
fileUrl := gconv.String(component.Option.Dataset)
filename := filepath.Base(fileUrl)
DownloadImage(ctx, fileUrl, basePath+"/data/video/")
component.Option.Dataset = "/data/video/" + filename
}
if component.Key == "ImageCarousel" && component.Option.Dataset != nil {
fmt.Println("ImageCarousel")
fmt.Println(component.Option.Dataset)
var datalist = gconv.SliceStr(component.Option.Dataset)
for i, fileUrl := range datalist {
filename := filepath.Base(fileUrl)
DownloadImage(ctx, fileUrl, basePath+"/data/image/")
datalist[i] = "/data/image/" + filename
}
component.Option.Dataset = datalist
}
}
jsonstr, err := json.Marshal(jsonObj)
os.WriteFile(basePath+"/project.json", jsonstr, 0666)
err = utils.ZipCompress(basePath, basePath+".zip")
if err != nil {
return err
}
g.Log().Debug(ctx, "压缩文件完成")
s.BuildTask(ctx, in.Url, basePath+".zip", in.Id)
//serverFile := basePath + tempName + ".zip"
return nil
}
func DownloadImage(ctx context.Context, url string, path string) error {
// 发送 GET 请求获取图片
resp, err := http.Get(url)
if err != nil {
g.Log().Error(ctx, err)
return fmt.Errorf("failed to fetch the image: %w", err)
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
g.Log().Error(ctx, err)
return fmt.Errorf("request returned status code %d", resp.StatusCode)
}
// 获取图片扩展名
filename := filepath.Base(url)
// 确保目录存在
err = os.MkdirAll(path, os.ModePerm)
if err != nil {
g.Log().Error(ctx, err)
return fmt.Errorf("failed to create directory: %w", err)
}
// 打开或创建文件
file, err := os.Create(path + filename)
if err != nil {
g.Log().Error(ctx, err)
return fmt.Errorf("failed to create file: %w", err)
}
defer file.Close()
// 将图片数据写入文件
_, err = io.Copy(file, resp.Body)
if err != nil {
g.Log().Error(ctx, err)
return fmt.Errorf("failed to write data to file: %w", err)
}
return nil
}
func (s *sAdminProject) BuildTask(ctx context.Context, options string, resource string, projectId int64) (err error) {
serviceurl := s.GetCfgEnv(ctx, DEPLOY_BUILD_URL_KEY)
jobName := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBNAME_KEY)
callback := s.GetCfgEnv(ctx, DEPLOY_BUILD_CALLBACK_KEY)
uploadPath := s.GetCfgEnv(ctx, DEPLOY_BUILD_UPLOAD_KEY)
last := strings.LastIndex(resource, "/")
if last > 0 {
filename := resource
var uploadFile = ""
if gstr.ToLower("windows") == "linux" {
uploadFile = "bin/linux_amd64/hc-base-gf"
} else {
uploadFile = "bin/windows_amd64/hc-base-gf.exe"
}
paras := map[string]string{
"jobName": jobName,
"remote_path": "obj.ResUrl",
"project_id": gconv.String(projectId),
// "target_path": "target/project",
"target_path": "/projects/1panel/1panel/apps/jenkins/jenkins/data/_data/jenkins_output",
"file_name": filename,
"callback_url": callback, //"http://192.168.10.241:30621/port/getJobBuildBack",
"upload_path": uploadPath,
"deploy_env": "windows",
"auto_open_enable": "true",
"upload_file": uploadFile,
"front_path": "",
}
num, err, errMsg := s.buildJenkinsJob(ctx, serviceurl, paras, jobName)
g.Log().Info(ctx, fmt.Sprintf("构建任务:%s, num%d, err%s, errMsg%s", jobName, num, err, errMsg))
}
return err
}
func (s *sAdminProject) GetCfgEnv(ctx context.Context, key string) string {
v, _ := g.Cfg().GetWithEnv(ctx, key)
return v.String()
}
func (s *sAdminProject) buildJenkinsJob(ctx context.Context, baseUrl string, parameters map[string]string, pipelineJobName string) (num int64, err error, errMsg string) {
glog.Info(ctx, fmt.Sprintf("开始构建部署任务baseUrl%s, parameters%spipelineJobName%s", baseUrl, parameters, pipelineJobName))
// 替换为有效的用户名和 API Token
jobuser := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBUSER_KEY)
jobpassword := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBPASSWORD_KEY)
// 创建 HTTP 客户端
client := &http.Client{}
glog.Info(ctx, fmt.Sprintf("初始化Jenkins客户端baseUrl%s用户%s", baseUrl, jobuser))
jenkins, err := gojenkins.CreateJenkins(client, baseUrl, jobuser, jobpassword).Init(ctx)
if err != nil {
errMsg = fmt.Sprintf("Jenkins 客户端初始化失败baseUrl%s用户%s", baseUrl, jobuser)
glog.Errorf(ctx, errMsg, err)
return 0, err, errMsg
}
jobarr := strings.Split(pipelineJobName, "/")
newjobname := strings.Join(jobarr, "/job/")
glog.Info(ctx, fmt.Sprintf("触发流水线执行,任务名:%s", newjobname))
queId, err := jenkins.BuildJob(ctx, newjobname, parameters)
glog.Debug(ctx, queId)
if err != nil {
errMsg = fmt.Sprintf("触发流水线执行失败:流水线名:%s 错误信息:%s", newjobname, err.Error())
glog.Error(ctx, errMsg, err)
return 0, err, errMsg
}
//通过队列号获取不到构建,只用来刷新构建号获取值
build, err := jenkins.GetBuildFromQueueID(ctx, queId)
if build != nil {
glog.Debug(ctx, build.GetBuildNumber())
} else {
glog.Debug(ctx, queId)
}
job, err := jenkins.GetJob(ctx, newjobname)
lastbuild, err := job.GetLastBuild(ctx)
glog.Debug(ctx, "流水线执行已触发,构建号:", lastbuild.GetBuildNumber())
glog.Info(ctx, "部署任务执行成功,构建号:", lastbuild.GetBuildNumber())
return lastbuild.GetBuildNumber(), err, ""
}