支持多平台linux-amd64 linux-arm64 win

This commit is contained in:
BBIT-Kai
2026-01-04 11:44:10 +08:00
parent 7b669ffbd0
commit 209d087fa5
24 changed files with 227 additions and 49 deletions
-1
View File
@@ -258,7 +258,6 @@ def updateGetMaxCodeByDeptId(
def getUploadUrl( def getUploadUrl(
deviceID: str | None = None, deviceID: str | None = None,
): ):
# 生成唯一文件名,避免覆盖
return BaseResponse(data=get_update_package(deviceID)) return BaseResponse(data=get_update_package(deviceID))
+1
View File
@@ -1,6 +1,7 @@
<component name="ProjectDictionaryState"> <component name="ProjectDictionaryState">
<dictionary name="project"> <dictionary name="project">
<words> <words>
<w>linux</w>
<w>从海康sdk获取图片以及信息</w> <w>从海康sdk获取图片以及信息</w>
</words> </words>
</dictionary> </dictionary>
+7 -1
View File
@@ -1,6 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4"> <module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" /> <component name="Go" enabled="true">
<buildTags>
<option name="os" value="linux" />
<option name="arch" value="arm64" />
<option name="cgo" value="NO" />
</buildTags>
</component>
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.
+44
View File
@@ -0,0 +1,44 @@
# build_all.ps1
# 一键打包 main 和 updater,三平台: Windows 64, Linux amd64, Linux arm64
# -------------------------
# 项目根目录
$RootDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
Set-Location $RootDir
# -------------------------
# 定义要打包的平台
$targets = @(
@{ OS="windows"; ARCH="amd64"; Dir="build/win"; MainOut="main.exe"; UpdaterOut="updater.exe" },
@{ OS="linux"; ARCH="amd64"; Dir="build/linux_amd64"; MainOut="main"; UpdaterOut="updater" },
@{ OS="linux"; ARCH="arm64"; Dir="build/linux_arm64"; MainOut="main"; UpdaterOut="updater" }
)
# -------------------------
# 循环打包
foreach ($t in $targets) {
Write-Host "=== Build $($t.OS)-$($t.ARCH) Version ==="
# 设置环境变量
$env:GOOS = $t.OS
$env:GOARCH = $t.ARCH
$env:CGO_ENABLED = "0"
# 创建输出目录
if (!(Test-Path $t.Dir)) {
New-Item -ItemType Directory -Path $t.Dir | Out-Null
}
# 编译 main
Write-Host "Building main..."
go build -ldflags="-s -w" -o "$($t.Dir)/$($t.MainOut)" ./main
# 编译 updater
Write-Host "Building updater..."
go build -ldflags="-s -w" -o "$($t.Dir)/$($t.UpdaterOut)" ./updater
Write-Host "$($t.OS)-$($t.ARCH) Build complete, Output dir: $($t.Dir)"
Write-Host ""
}
Write-Host "All builds finished."
Binary file not shown.
Binary file not shown.
+38 -20
View File
@@ -4,13 +4,11 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"os/exec" "os/exec"
"runtime"
"sentinel/pkg/config" "sentinel/pkg/config"
"sentinel/pkg/log" "sentinel/pkg/log"
model2 "sentinel/pkg/model" model2 "sentinel/pkg/model"
"sentinel/pkg/utils" "sentinel/pkg/utils"
"strconv" "strconv"
"syscall"
"time" "time"
) )
@@ -154,38 +152,58 @@ func (b *BusinessService) handleRestart() {
_ = cmd.Start() _ = cmd.Start()
os.Exit(0) os.Exit(0)
} }
// handleCheckUpdate 触发更新流程(主程序侧)
func (b *BusinessService) handleCheckUpdate() { func (b *BusinessService) handleCheckUpdate() {
args := []string{ args := []string{
"--version", strconv.Itoa(config.APP_VERSION), "--version", strconv.Itoa(config.APP_VERSION),
} }
cmd := exec.Command("./updater.exe", args...) launcher := newUpdaterLauncher()
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// OS 级脱离父进程 if err := launcher.Start(args); err != nil {
switch runtime.GOOS {
case "windows":
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
}
if err := cmd.Start(); err != nil {
log.Println("[BUS] failed to start updater:", err) log.Println("[BUS] failed to start updater:", err)
return return
} }
log.Println( log.Println(
"[BUS] updater started (pid=%d), exiting main program\n", "[BUS] updater started, exiting main program",
cmd.Process.Pid,
) )
// 给 updater 留出启动窗口(尤其是 systemd / docker 环境)
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
os.Exit(0) os.Exit(0)
} }
// handleCheckUpdate 触发更新流程(主程序侧)
//func (b *BusinessService) handleCheckUpdate() {
//
// args := []string{
// "--version", strconv.Itoa(config.APP_VERSION),
// }
//
// cmd := exec.Command("./updater.exe", args...)
// cmd.Stdout = os.Stdout
// cmd.Stderr = os.Stderr
//
// // OS 级脱离父进程
// switch runtime.GOOS {
// case "windows":
// cmd.SysProcAttr = &syscall.SysProcAttr{
// CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
// }
// }
//
// if err := cmd.Start(); err != nil {
// log.Println("[BUS] failed to start updater:", err)
// return
// }
//
// log.Println(
// "[BUS] updater started (pid=%d), exiting main program\n",
// cmd.Process.Pid,
// )
//
// // 给 updater 留出启动窗口(尤其是 systemd / docker 环境)
// time.Sleep(500 * time.Millisecond)
//
// os.Exit(0)
//}
+7
View File
@@ -0,0 +1,7 @@
package main
import "sentinel/pkg/platform"
func newUpdaterLauncher() platform.UpdaterLauncher {
return &updaterLauncher{}
}
+20
View File
@@ -0,0 +1,20 @@
//go:build linux
// +build linux
package main
import (
"os"
"os/exec"
)
type updaterLauncher struct{}
func (u *updaterLauncher) Start(args []string) error {
cmd := exec.Command("./updater", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// Linux 下先不做进程组处理,保证能跑
return cmd.Start()
}
+24
View File
@@ -0,0 +1,24 @@
//go:build windows
// +build windows
package main
import (
"os"
"os/exec"
"syscall"
)
type updaterLauncher struct{}
func (u *updaterLauncher) Start(args []string) error {
cmd := exec.Command("./updater.exe", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
return cmd.Start()
}
+3 -1
View File
@@ -12,7 +12,8 @@ import (
"sentinel/pkg/log" "sentinel/pkg/log"
) )
const baseURL = "http://127.0.0.1:13011" // const baseURL = "http://127.0.0.1:13011"
const baseURL = "https://ai.ronsunny.cn:8090"
var client = &http.Client{ var client = &http.Client{
Timeout: 5 * time.Second, Timeout: 5 * time.Second,
@@ -59,6 +60,7 @@ func do(method, path string, query map[string]string, body any, out any) error {
return err return err
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("apikey", "NzusyzcLIUoZ22tflHN2sOjHrry3W7zJ")
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
@@ -0,0 +1,11 @@
package platform
type UpdaterLauncher interface {
Start(args []string) error
}
type MainProgramStarter interface {
Start(targetExe string) error
GetMainName() string
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

@@ -7,14 +7,11 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime"
"sentinel/pkg/device" "sentinel/pkg/device"
"sentinel/pkg/log" "sentinel/pkg/log"
"sentinel/pkg/net" "sentinel/pkg/net"
"strconv" "strconv"
"syscall"
"time" "time"
) )
@@ -60,20 +57,14 @@ func RunUpdate(deviceID string, version int) error {
return err return err
} }
selfDir := filepath.Dir(selfPath) selfDir := filepath.Dir(selfPath)
targetExe := filepath.Join(selfDir, "main.exe") // Windows 固定名,可根据实际改
starter := newMainProgramStarter()
targetExe := filepath.Join(selfDir, starter.GetMainName())
// 2. 对比版本号,没有新版本则直接启动原程序 // 2. 对比版本号,没有新版本则直接启动原程序
if info.Version <= version { if info.Version <= version {
fmt.Println("[updater] 暂未发现新版本,启动原程序") fmt.Println("[updater] 暂未发现新版本,启动原程序")
cmd := exec.Command(targetExe) if err := starter.Start(targetExe); err != nil {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if runtime.GOOS == "windows" {
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
}
if err := cmd.Start(); err != nil {
return err return err
} }
os.Exit(0) os.Exit(0)
@@ -131,19 +122,11 @@ func RunUpdate(deviceID string, version int) error {
} }
// 6. 启动主程序,同时完全退出自己 // 6. 启动主程序,同时完全退出自己
cmd := exec.Command(targetExe) if err := starter.Start(targetExe); err != nil {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if runtime.GOOS == "windows" {
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
}
if err := cmd.Start(); err != nil {
return err return err
} }
fmt.Printf("[updater] 更新完成,新程序已启动,退出更新程序")
fmt.Printf("[updater] 更新完成,新程序已启动 (pid=%d),退出更新程序\n", cmd.Process.Pid)
os.Exit(0) os.Exit(0)
return nil return nil
} }
+7
View File
@@ -0,0 +1,7 @@
package main
import "sentinel/pkg/platform"
func newMainProgramStarter() platform.MainProgramStarter {
return &mainProgramStarter{}
}
+27
View File
@@ -0,0 +1,27 @@
//go:build linux
// +build linux
package main
import (
"os"
"os/exec"
)
type mainProgramStarter struct{}
func (s *mainProgramStarter) GetMainName() string {
return "main"
}
func (s *mainProgramStarter) Start(targetExe string) error {
if err := os.Chmod(targetExe, 0755); err != nil {
return err
}
cmd := exec.Command(targetExe)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// Linux 下:先保持最简单,保证能跑
return cmd.Start()
}
+29
View File
@@ -0,0 +1,29 @@
//go:build windows
// +build windows
package main
import (
"os"
"os/exec"
"syscall"
)
type mainProgramStarter struct{}
func (s *mainProgramStarter) GetMainName() string {
return "main.exe"
}
func (s *mainProgramStarter) Start(targetExe string) error {
cmd := exec.Command(targetExe)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// Windows:新进程组,脱离 Ctrl+C / 父进程
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
return cmd.Start()
}