391 lines
12 KiB
Go
391 lines
12 KiB
Go
// 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:%s,pipelineJobName:%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, ""
|
||
}
|