Browse Source

移除重启相关代码

删除了与程序重启相关的代码,包括 `restart` 包及其在 `root` 和 `subcmd` 中的引用,并更新了 `tiger/v1/main.go` 和 `configparser/json.go` 以适应这些更改。
SongZihuan 1 week ago
parent
commit
873d80b4bb

+ 4 - 0
CHANGELOG.md

@@ -13,6 +13,10 @@
 
 - 完善文档关于命令行参数的讲解。
 
+### 重构
+
+- 重构了自动重启机制。
+
 ## [0.8.0] - 2025-04-22
 
 ### 新增

+ 11 - 4
go.mod

@@ -5,25 +5,32 @@ go 1.23.0
 toolchain go1.23.4
 
 require (
+	github.com/fsnotify/fsnotify v1.9.0
 	github.com/kardianos/service v1.2.2
 	github.com/mattn/go-isatty v0.0.20
+	github.com/shirou/gopsutil/v4 v4.25.3
+	github.com/spf13/cobra v1.9.1
+	github.com/spf13/viper v1.20.1
 	gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
-	github.com/fsnotify/fsnotify v1.9.0 // indirect
+	github.com/ebitengine/purego v0.8.2 // indirect
+	github.com/go-ole/go-ole v1.2.6 // indirect
 	github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
+	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
 	github.com/pelletier/go-toml/v2 v2.2.4 // indirect
+	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 	github.com/sagikazarmark/locafero v0.9.0 // indirect
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.14.0 // indirect
 	github.com/spf13/cast v1.7.1 // indirect
-	github.com/spf13/cobra v1.9.1 // indirect
 	github.com/spf13/pflag v1.0.6 // indirect
-	github.com/spf13/viper v1.20.1 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
-	go.uber.org/atomic v1.11.0 // indirect
+	github.com/tklauser/go-sysconf v0.3.12 // indirect
+	github.com/tklauser/numcpus v0.6.1 // indirect
+	github.com/yusufpapurcu/wmi v1.2.4 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/sys v0.32.0 // indirect
 	golang.org/x/text v0.24.0 // indirect

+ 37 - 5
go.sum

@@ -1,21 +1,44 @@
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
+github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
 github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
 github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
 github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
 github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
 github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
+github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
+github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
 github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
 github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
 github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
@@ -28,22 +51,31 @@ github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
 github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
-go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
-go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
 golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
 golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 47 - 44
src/cmd/catv1/main.go

@@ -6,11 +6,13 @@ package main
 
 import (
 	"github.com/SongZihuan/BackendServerTemplate/src/cmd/globalmain"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/root"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/subcmd"
-	_ "github.com/SongZihuan/BackendServerTemplate/src/global"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/check"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/license"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/report"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/version"
+	"github.com/SongZihuan/BackendServerTemplate/src/global"
 	catv1 "github.com/SongZihuan/BackendServerTemplate/src/mainfunc/cat/v1"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/cleanstringutils"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
 	"github.com/spf13/cobra"
 )
@@ -24,18 +26,48 @@ const (
 	args1Restart    = "restart"
 )
 
+var name string = global.Name
+var inputConfigFilePath string = "config.yaml"
+
 func main() {
-	defer logger.Recover()
+	err := globalmain.PreRun()
+	if err != nil {
+		exitutils.Exit(err)
+	}
+	defer globalmain.PostRun()
 
-	cmd := root.GetRootCMD("System service registration tool",
-		"Register this software as a system service, mainly used in Windows.",
-		nil,
-		false,
-		catv1.MainV1)
+	cmd := &cobra.Command{
+		Use:           global.Name,
+		Short:         "System service registration tool",
+		Long:          "Register this software as a system service, mainly used in Windows",
+		SilenceUsage:  false,
+		SilenceErrors: false,
+		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
 
-	subcmd.AddSubCMDOfRoot(cmd)
-	cmd.Flags().StringVarP(&catv1.InputConfigFilePath, "config", "c", catv1.InputConfigFilePath, "the file path of the configuration file")
-	cmd.Flags().StringVarP(&catv1.OutputConfigFilePath, "output-config", "o", catv1.OutputConfigFilePath, "the file path of the output configuration file")
+			if name = cleanstringutils.GetStringOneLine(name); cmd.Flags().Changed("name") && name != "" {
+				global.Name = name
+				global.NameFlagChanged = true
+			} else {
+				global.NameFlagChanged = false
+			}
+
+			return nil
+		},
+		RunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = true
+			cmd.SilenceErrors = true
+			return catv1.MainV1(cmd, args, inputConfigFilePath)
+		},
+		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+			return nil
+		},
+	}
+
+	cmd.Flags().StringVarP(&inputConfigFilePath, "config", "c", inputConfigFilePath, "the file path of the configuration file")
 
 	install := &cobra.Command{
 		Use:           args1Install,
@@ -51,12 +83,6 @@ func main() {
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
 			cmd.SilenceErrors = true
-
-			err := globalmain.PreRun(false)
-			if err != nil {
-				return err
-			}
-
 			return catv1.MainV1Install(cmd, args)
 		},
 		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
@@ -65,6 +91,7 @@ func main() {
 			return nil
 		},
 	}
+	install.FParseErrWhitelist.UnknownFlags = true
 
 	uninstall := &cobra.Command{
 		Use:           args1Uninstall1,
@@ -81,12 +108,6 @@ func main() {
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
 			cmd.SilenceErrors = true
-
-			err := globalmain.PreRun(false)
-			if err != nil {
-				return err
-			}
-
 			return catv1.MainV1UnInstall(cmd, args)
 		},
 		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
@@ -110,12 +131,6 @@ func main() {
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
 			cmd.SilenceErrors = true
-
-			err := globalmain.PreRun(false)
-			if err != nil {
-				return err
-			}
-
 			return catv1.MainV1Start(cmd, args)
 		},
 		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
@@ -139,12 +154,6 @@ func main() {
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
 			cmd.SilenceErrors = true
-
-			err := globalmain.PreRun(false)
-			if err != nil {
-				return err
-			}
-
 			return catv1.MainV1Stop(cmd, args)
 		},
 		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
@@ -168,12 +177,6 @@ func main() {
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
 			cmd.SilenceErrors = true
-
-			err := globalmain.PreRun(false)
-			if err != nil {
-				return err
-			}
-
 			return catv1.MainV1Restart(cmd, args)
 		},
 		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
@@ -183,6 +186,6 @@ func main() {
 		},
 	}
 
-	cmd.AddCommand(install, uninstall, start, stop, restart)
+	cmd.AddCommand(version.CMD, license.CMD, report.CMD, check.CMD, install, uninstall, start, stop, restart)
 	exitutils.ExitQuite(cmd.Execute())
 }

+ 6 - 12
src/cmd/globalmain/main.go

@@ -11,19 +11,12 @@ import (
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
 )
 
-func PreRun(hasConsole bool) (exitCode error) {
+func PreRun() (exitCode error) {
 	var err error
 
-	if hasConsole {
-		err = consoleutils.SetConsoleCPSafe(consoleutils.CodePageUTF8)
-		if err != nil {
-			return exitutils.InitFailedErrorForWin32ConsoleModule(err.Error())
-		}
-
-		err = consoleutils.BindStdToConsole()
-		if err != nil {
-			return exitutils.InitFailedErrorForWin32ConsoleModule(err.Error())
-		}
+	err = consoleutils.SetConsoleCPSafe(consoleutils.CodePageUTF8)
+	if err != nil {
+		return exitutils.InitFailedErrorForWin32ConsoleModule(err.Error())
 	}
 
 	err = logger.InitBaseLogger(loglevel.LevelDebug, true, nil, nil, nil, nil)
@@ -35,5 +28,6 @@ func PreRun(hasConsole bool) (exitCode error) {
 }
 
 func PostRun() {
-	defer logger.CloseLogger()
+	logger.CloseLogger()
+	logger.Recover()
 }

+ 84 - 13
src/cmd/lionv1/main.go

@@ -5,26 +5,97 @@
 package main
 
 import (
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/root"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/subcmd"
-	_ "github.com/SongZihuan/BackendServerTemplate/src/global"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmd/globalmain"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/check"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/license"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/report"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/version"
+	"github.com/SongZihuan/BackendServerTemplate/src/global"
 	lionv1 "github.com/SongZihuan/BackendServerTemplate/src/mainfunc/lion/v1"
+	restartv1 "github.com/SongZihuan/BackendServerTemplate/src/mainfunc/restart/v1"
+	"github.com/SongZihuan/BackendServerTemplate/src/restart"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/cleanstringutils"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
+	"github.com/spf13/cobra"
 )
 
+var inputConfigFilePath string = "config.yaml"
+var outputConfigFilePath string = ""
+var name string = global.Name
+var reload bool = false
+var ppid int = 0
+
 func main() {
-	defer logger.Recover()
+	err := globalmain.PreRun()
+	if err != nil {
+		exitutils.Exit(err)
+	}
+	defer globalmain.PostRun()
+
+	cmd := &cobra.Command{
+		Use:           global.Name,
+		Short:         "Multi-tasking background system",
+		Long:          "A multi-task background system controlled by a controller to run multiple tasks concurrently",
+		SilenceUsage:  false,
+		SilenceErrors: false,
+		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+
+			if name = cleanstringutils.GetStringOneLine(name); cmd.Flags().Changed("name") && name != "" {
+				global.Name = name
+				global.NameFlagChanged = true
+			} else {
+				global.NameFlagChanged = false
+			}
+
+			return nil
+		},
+		PreRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+
+			if reload && cmd.Flags().Changed(restart.RestartFlag) {
+				if ppid == 0 {
+					return fmt.Errorf("`restart` cannot be specified as 0")
+				}
+				reload = false
+			}
+
+			return nil
+		},
+		RunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = true
+			cmd.SilenceErrors = true
+
+			if reload {
+				return restartv1.MainV1(cmd, args, inputConfigFilePath, outputConfigFilePath)
+			}
+
+			return lionv1.MainV1(cmd, args, inputConfigFilePath, outputConfigFilePath, ppid)
+		},
+		PostRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+			return nil
+		},
+		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+			return nil
+		},
+	}
+
+	cmd.AddCommand(version.CMD, license.CMD, report.CMD, check.CMD)
+
+	cmd.PersistentFlags().StringVarP(&name, "name", "n", global.Name, "the program display name")
 
-	cmd := root.GetRootCMD("Multi-tasking background system",
-		"A multi-task background system controlled by a controller to run multiple tasks concurrently",
-		&lionv1.AutoReload,
-		true,
-		lionv1.MainV1)
+	cmd.Flags().BoolVar(&reload, "auto-reload", false, "auto reload config file when the file changed")
+	cmd.Flags().IntVar(&ppid, restart.RestartFlag, 0, "restart mode, note: DO NOT SET THIS FLAG unless you know your purpose clearly.")
 
-	subcmd.AddSubCMDOfRoot(cmd)
-	cmd.Flags().StringVarP(&lionv1.InputConfigFilePath, "config", "c", lionv1.InputConfigFilePath, "the file path of the configuration file")
-	cmd.Flags().StringVarP(&lionv1.OutputConfigFilePath, "output-config", "o", lionv1.OutputConfigFilePath, "the file path of the output configuration file")
+	cmd.Flags().StringVarP(&inputConfigFilePath, "config", "c", inputConfigFilePath, "the file path of the configuration file")
+	cmd.Flags().StringVarP(&outputConfigFilePath, "output-config", "o", outputConfigFilePath, "the file path of the output configuration file")
 
 	exitutils.ExitQuite(cmd.Execute())
 }

+ 0 - 73
src/cmd/restart/posix.go

@@ -1,73 +0,0 @@
-// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-//go:build !windows
-
-package restart
-
-import (
-	"github.com/SongZihuan/BackendServerTemplate/src/logger"
-	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
-	"github.com/SongZihuan/BackendServerTemplate/src/utils/sliceutils"
-	"os"
-	"os/exec"
-	"time"
-)
-
-var RestartChan = make(chan bool)
-
-const RestartReadyTime = 5 * time.Second
-const RestartExitTime = 5 * time.Second
-const RestartWaitTime = RestartReadyTime + RestartExitTime + (3 * time.Second)
-
-func RestartProgram(restartFlag string) error {
-	select {
-	case _, ok := <-RestartChan:
-		if ok == false {
-			return nil
-		}
-	default:
-		// pass
-	}
-
-	var args []string
-
-	if len(os.Args) > 1 {
-		args = sliceutils.CopySlice(os.Args[1:])
-		if !sliceutils.SliceHasItem(args, restartFlag) {
-			args = append([]string{restartFlag}, args...)
-		}
-	} else {
-		args = []string{restartFlag}
-	}
-
-	cmd := exec.Command(osutils.GetArgs0(), args...)
-	cmd.Stdin = os.Stdin
-	cmd.Stdout = os.Stdout
-	cmd.Stderr = os.Stderr
-
-	// 保留默认设置,父进程结束后子进程将有init接管
-	//cmd.SysProcAttr = &syscall.SysProcAttr{
-	//	Setpgid: true, // 确保新的进程组独立
-	//}
-
-	if err := cmd.Start(); err != nil {
-		return err
-	}
-
-	logger.Warnf("the program restart...")
-	logger.Warnf("restart ready")
-
-	close(RestartChan)
-	return nil
-}
-
-func FromRestart() error {
-	<-time.After(RestartWaitTime)
-	return nil
-}
-
-func FirstRun() error {
-	return nil
-}

+ 0 - 81
src/cmd/restart/win32.go

@@ -1,81 +0,0 @@
-// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-//go:build windows
-
-package restart
-
-import (
-	"github.com/SongZihuan/BackendServerTemplate/src/logger"
-	"github.com/SongZihuan/BackendServerTemplate/src/utils/consoleutils"
-	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
-	"github.com/SongZihuan/BackendServerTemplate/src/utils/sliceutils"
-	"golang.org/x/sys/windows"
-	"os"
-	"os/exec"
-	"syscall"
-	"time"
-)
-
-var RestartChan = make(chan bool)
-
-const RestartWaitTime = 5 * time.Second
-
-func RestartProgram(restartFlag string) error {
-	select {
-	case _, ok := <-RestartChan:
-		if ok == false {
-			return nil // 已经关闭
-		}
-	default:
-		// pass
-	}
-
-	var args []string
-
-	if len(os.Args) > 1 {
-		args = sliceutils.CopySlice(os.Args[1:])
-		if !sliceutils.SliceHasItem(args, restartFlag) {
-			args = append([]string{restartFlag}, args...)
-		}
-	} else {
-		args = []string{restartFlag}
-	}
-
-	cmd := exec.Command(osutils.GetArgs0(), args...)
-
-	cmd.SysProcAttr = &syscall.SysProcAttr{
-		CreationFlags: windows.CREATE_NEW_PROCESS_GROUP | windows.CREATE_UNICODE_ENVIRONMENT | windows.CREATE_NEW_CONSOLE,
-	}
-
-	if err := cmd.Start(); err != nil {
-		return err
-	}
-
-	logger.Warnf("the program restart...")
-
-	if err := consoleutils.FreeConsole(); err != nil {
-		return err
-	}
-
-	logger.Warnf("restart ready")
-
-	close(RestartChan)
-	return nil
-}
-
-func FromRestart() error {
-	logger.Infof("Wait ready...")
-	<-time.After(RestartWaitTime)
-	logger.Infof("Restart ready...")
-	return nil
-}
-
-func FirstRun() error {
-	err := consoleutils.MakeNewConsole(consoleutils.CodePageUTF8)
-	if err != nil {
-		return err
-	}
-	return nil
-}

+ 84 - 13
src/cmd/tigerv1/main.go

@@ -5,26 +5,97 @@
 package main
 
 import (
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/root"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/subcmd"
-	_ "github.com/SongZihuan/BackendServerTemplate/src/global"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmd/globalmain"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/check"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/license"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/report"
+	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/version"
+	"github.com/SongZihuan/BackendServerTemplate/src/global"
+	restartv1 "github.com/SongZihuan/BackendServerTemplate/src/mainfunc/restart/v1"
 	tigerv1 "github.com/SongZihuan/BackendServerTemplate/src/mainfunc/tiger/v1"
+	"github.com/SongZihuan/BackendServerTemplate/src/restart"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/cleanstringutils"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
+	"github.com/spf13/cobra"
 )
 
+var inputConfigFilePath string = "config.yaml"
+var outputConfigFilePath string = ""
+var name string = global.Name
+var reload bool = false
+var ppid int = 0
+
 func main() {
-	defer logger.Recover()
+	err := globalmain.PreRun()
+	if err != nil {
+		exitutils.Exit(err)
+	}
+	defer globalmain.PostRun()
+
+	cmd := &cobra.Command{
+		Use:           global.Name,
+		Short:         "Single-tasking background system",
+		Long:          "A single-task background system that runs a single task directly without using a controller",
+		SilenceUsage:  false,
+		SilenceErrors: false,
+		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+
+			if name = cleanstringutils.GetStringOneLine(name); cmd.Flags().Changed("name") && name != "" {
+				global.Name = name
+				global.NameFlagChanged = true
+			} else {
+				global.NameFlagChanged = false
+			}
+
+			return nil
+		},
+		PreRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+
+			if reload && cmd.Flags().Changed(restart.RestartFlag) {
+				if ppid == 0 {
+					return fmt.Errorf("`restart` cannot be specified as 0")
+				}
+				reload = false
+			}
+
+			return nil
+		},
+		RunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = true
+			cmd.SilenceErrors = true
+
+			if reload {
+				return restartv1.MainV1(cmd, args, inputConfigFilePath, outputConfigFilePath)
+			}
+
+			return tigerv1.MainV1(cmd, args, inputConfigFilePath, outputConfigFilePath, ppid)
+		},
+		PostRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+			return nil
+		},
+		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
+			cmd.SilenceUsage = false
+			cmd.SilenceErrors = false
+			return nil
+		},
+	}
+
+	cmd.AddCommand(version.CMD, license.CMD, report.CMD, check.CMD)
+
+	cmd.PersistentFlags().StringVarP(&name, "name", "n", global.Name, "the program display name")
 
-	cmd := root.GetRootCMD("Single-tasking background system",
-		"A single-task background system that runs a single task directly without using a controller",
-		&tigerv1.AutoReload,
-		true,
-		tigerv1.MainV1)
+	cmd.Flags().BoolVar(&reload, "auto-reload", false, "auto reload config file when the file changed")
+	cmd.Flags().IntVar(&ppid, restart.RestartFlag, 0, "restart mode, note: DO NOT SET THIS FLAG unless you know your purpose clearly.")
 
-	subcmd.AddSubCMDOfRoot(cmd)
-	cmd.Flags().StringVarP(&tigerv1.InputConfigFilePath, "config", "c", tigerv1.InputConfigFilePath, "the file path of the configuration file")
-	cmd.Flags().StringVarP(&tigerv1.OutputConfigFilePath, "output-config", "o", tigerv1.OutputConfigFilePath, "the file path of the output configuration file")
+	cmd.Flags().StringVarP(&inputConfigFilePath, "config", "c", inputConfigFilePath, "the file path of the configuration file")
+	cmd.Flags().StringVarP(&outputConfigFilePath, "output-config", "o", outputConfigFilePath, "the file path of the output configuration file")
 
 	exitutils.ExitQuite(cmd.Execute())
 }

+ 7 - 4
src/cmdparser/check/cmd.go

@@ -10,6 +10,9 @@ import (
 	"github.com/spf13/cobra"
 )
 
+var inputConfigFilePath string = "config.yaml"
+var outputConfigFilePath string = ""
+
 var CMD = &cobra.Command{
 	Use:     "check",
 	Aliases: []string{"config"},
@@ -18,16 +21,16 @@ var CMD = &cobra.Command{
 		cmd.SilenceUsage = true
 		cmd.SilenceErrors = true
 
-		err := globalmain.PreRun(false)
+		err := globalmain.PreRun()
 		if err != nil {
 			return err
 		}
 
-		return checkv1.MainV1(cmd, args)
+		return checkv1.MainV1(cmd, args, inputConfigFilePath, outputConfigFilePath)
 	},
 }
 
 func init() {
-	CMD.Flags().StringVarP(&checkv1.InputConfigFilePath, "config", "c", checkv1.InputConfigFilePath, "the file path of the configuration file")
-	CMD.Flags().StringVarP(&checkv1.OutputConfigFilePath, "output-config", "o", checkv1.OutputConfigFilePath, "the file path of the output configuration file")
+	CMD.Flags().StringVarP(&inputConfigFilePath, "config", "c", inputConfigFilePath, "the file path of the configuration file")
+	CMD.Flags().StringVarP(&outputConfigFilePath, "output-config", "o", outputConfigFilePath, "the file path of the output configuration file")
 }

+ 0 - 151
src/cmdparser/root/root.go

@@ -1,151 +0,0 @@
-// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package root
-
-import (
-	"fmt"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmd/globalmain"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmd/restart"
-	"github.com/SongZihuan/BackendServerTemplate/src/global"
-	"github.com/SongZihuan/BackendServerTemplate/src/utils/cleanstringutils"
-	"github.com/SongZihuan/BackendServerTemplate/src/utils/consoleutils"
-	"github.com/spf13/cobra"
-	"runtime"
-)
-
-const (
-	ConsoleModeNormal = "normal"
-	ConsoleModeNO     = "no"
-)
-
-var name string = global.Name
-var nameChanged bool = false
-var isRestart bool = false
-var consoleMode string = ConsoleModeNormal
-var hasConsole bool = false
-
-func GetRootCMD(shortDescribe string, longDescribe string, reload *bool, _hasConsole bool, action func(cmd *cobra.Command, args []string) error) *cobra.Command {
-	if runtime.GOOS == "windows" {
-		hasConsole = _hasConsole && consoleutils.HasConsoleWindow()
-	} else {
-		hasConsole = true
-	}
-
-	cmd := &cobra.Command{
-		Use:           global.Name,
-		Short:         shortDescribe,
-		Long:          longDescribe,
-		SilenceUsage:  false,
-		SilenceErrors: false,
-		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
-			cmd.SilenceUsage = false
-			cmd.SilenceErrors = false
-
-			if name = cleanstringutils.GetStringOneLine(name); cmd.Flags().Changed("name") && name != "" {
-				global.Name = name
-				nameChanged = true
-			} else {
-				nameChanged = false
-			}
-
-			return nil
-		},
-		PreRunE: func(cmd *cobra.Command, args []string) error {
-			cmd.SilenceUsage = false
-			cmd.SilenceErrors = false
-
-			if hasConsole {
-				if consoleMode != ConsoleModeNormal && consoleMode != ConsoleModeNO {
-					return fmt.Errorf("console-mode must be %s or %s", ConsoleModeNormal, ConsoleModeNO)
-				}
-			} else if _hasConsole && consoleMode != ConsoleModeNO {
-				return fmt.Errorf("console-mode must be %s, there is not console be found", ConsoleModeNO)
-			}
-
-			if reload != nil && *reload {
-				if consoleMode == ConsoleModeNO {
-					return fmt.Errorf("`auto-reload` can only be enabled when `console-mode` is `normal`")
-				}
-
-				if cmd.Flags().Changed(restart.RestartFlag) {
-					if isRestart {
-						err := restart.FromRestart()
-						if err != nil {
-							return fmt.Errorf("restart failed to attach console: %s", err)
-						}
-					} else {
-						return fmt.Errorf("`restart` cannot be specified as false")
-					}
-				} else {
-					return restart.FirstRun()
-				}
-			}
-
-			return nil
-		},
-		RunE: func(cmd *cobra.Command, args []string) error {
-			cmd.SilenceUsage = true
-			cmd.SilenceErrors = true
-
-			err := globalmain.PreRun(HasConsole())
-			if err != nil {
-				return err
-			}
-
-			return action(cmd, args)
-		},
-		PostRunE: func(cmd *cobra.Command, args []string) error {
-			cmd.SilenceUsage = false
-			cmd.SilenceErrors = false
-			globalmain.PostRun()
-			return nil
-		},
-		PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
-			cmd.SilenceUsage = false
-			cmd.SilenceErrors = false
-			return nil
-		},
-	}
-
-	cmd.PersistentFlags().StringVarP(&name, "name", "n", global.Name, "the program display name")
-
-	if _hasConsole && runtime.GOOS == "windows" {
-		cmd.Flags().StringVar(&consoleMode, "console-mode", ConsoleModeNormal, "the console mode. normally, select `normal`. If you are not using a terminal (e.g. redirection, etc.) please select `no`.")
-	}
-
-	if reload != nil {
-		cmd.Flags().BoolVar(reload, "auto-reload", false, "auto reload config file when the file changed")
-		cmd.Flags().BoolVar(&isRestart, restart.RestartFlag, false, "restart mode, note: DO NOT SET THIS FLAG unless you know your purpose clearly.")
-	}
-
-	return cmd
-}
-
-func Name() string {
-	return name
-}
-
-func NameChanged() bool {
-	return nameChanged
-}
-
-func IsRestart() bool {
-	return isRestart
-}
-
-func ConsoleMode() string {
-	switch consoleMode {
-	case ConsoleModeNormal:
-		return ConsoleModeNormal
-	case ConsoleModeNO:
-		return ConsoleModeNO
-	default:
-		return ConsoleModeNormal
-	}
-}
-
-func HasConsole() bool {
-	return hasConsole && ConsoleMode() == ConsoleModeNormal
-}

+ 0 - 17
src/cmdparser/subcmd/subcmd.go

@@ -1,17 +0,0 @@
-// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package subcmd
-
-import (
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/check"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/license"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/report"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/version"
-	"github.com/spf13/cobra"
-)
-
-func AddSubCMDOfRoot(cmd *cobra.Command) {
-	cmd.AddCommand(version.CMD, license.CMD, report.CMD, check.CMD)
-}

+ 18 - 13
src/config/configparser/json.go

@@ -7,20 +7,23 @@ package configparser
 import (
 	"encoding/json"
 	"errors"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmd/restart"
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"github.com/SongZihuan/BackendServerTemplate/src/restart"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/envutils"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
 	"github.com/fsnotify/fsnotify"
 	"github.com/spf13/viper"
 	"os"
 	"reflect"
+	"sync"
 )
 
 type JsonProvider struct {
-	viper   *viper.Viper
-	hasRead bool
+	viper      *viper.Viper
+	hasRead    bool
+	autoReload bool
+	restart    sync.Once
 }
 
 func NewJsonProvider(opt *NewProviderOption) *JsonProvider {
@@ -33,8 +36,9 @@ func NewJsonProvider(opt *NewProviderOption) *JsonProvider {
 	}
 
 	p := &JsonProvider{
-		viper:   viper.New(),
-		hasRead: false,
+		viper:      viper.New(),
+		autoReload: opt.AutoReload,
+		hasRead:    false,
 	}
 
 	// 环境变量
@@ -42,19 +46,15 @@ func NewJsonProvider(opt *NewProviderOption) *JsonProvider {
 	p.viper.SetEnvKeyReplacer(envutils.GetEnvReplaced())
 	p.viper.AutomaticEnv()
 
-	if opt.AutoReload {
+	if p.autoReload {
 		logger.Infof("start auto reload")
 
 		p.viper.OnConfigChange(func(e fsnotify.Event) {
 			logger.Infof("config change")
-
-			err := restart.RestartProgram(restart.RestartFlagComplete)
-			if err != nil {
-				logger.Errorf("restart program error: %s", err.Error())
-			}
+			p.restart.Do(func() {
+				restart.SetRestart()
+			})
 		})
-
-		p.viper.WatchConfig()
 	}
 
 	return p
@@ -79,6 +79,11 @@ func (j *JsonProvider) ReadFile(filepath string) configerror.Error {
 		return configerror.NewErrorf("read config file error: %s", err.Error())
 	}
 
+	if j.autoReload {
+		logger.Infof("auto reload: watch file: %s", j.viper.ConfigFileUsed())
+		j.viper.WatchConfig()
+	}
+
 	j.hasRead = true
 
 	return nil

+ 13 - 25
src/config/configparser/yaml.go

@@ -6,9 +6,9 @@ package configparser
 
 import (
 	"errors"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmd/restart"
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"github.com/SongZihuan/BackendServerTemplate/src/restart"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/envutils"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
 	"github.com/fsnotify/fsnotify"
@@ -22,8 +22,8 @@ import (
 type YamlProvider struct {
 	viper      *viper.Viper
 	autoReload bool
-	reloadLock sync.Mutex
 	hasRead    bool
+	restart    sync.Once
 }
 
 func NewYamlProvider(opt *NewProviderOption) *YamlProvider {
@@ -36,8 +36,9 @@ func NewYamlProvider(opt *NewProviderOption) *YamlProvider {
 	}
 
 	p := &YamlProvider{
-		viper:   viper.New(),
-		hasRead: false,
+		viper:      viper.New(),
+		autoReload: opt.AutoReload,
+		hasRead:    false,
 	}
 
 	// 环境变量
@@ -45,31 +46,18 @@ func NewYamlProvider(opt *NewProviderOption) *YamlProvider {
 	p.viper.SetEnvKeyReplacer(envutils.GetEnvReplaced())
 	p.viper.AutomaticEnv()
 
-	if opt.AutoReload {
+	if p.autoReload {
 		logger.Infof("start auto reload")
-		p.viper.OnConfigChange(p.reloadEvent)
-		p.autoReload = true
-	} else {
-		p.autoReload = false
-	}
-
-	return p
-}
 
-func (y *YamlProvider) reloadEvent(e fsnotify.Event) {
-	if ok := y.reloadLock.TryLock(); !ok {
-		return
+		p.viper.OnConfigChange(func(e fsnotify.Event) {
+			logger.Infof("config change")
+			p.restart.Do(func() {
+				restart.SetRestart()
+			})
+		})
 	}
 
-	logger.Infof("config change")
-	err := restart.RestartProgram(restart.RestartFlagComplete)
-	if err != nil {
-		logger.Errorf("restart program error: %s", err.Error())
-		y.reloadLock.Unlock()
-		return
-	}
-
-	// 不需要释放 y.reloadLock 锁
+	return p
 }
 
 func (y *YamlProvider) CanUTF8() bool {

+ 1 - 2
src/config/global_config.go

@@ -5,7 +5,6 @@
 package config
 
 import (
-	"github.com/SongZihuan/BackendServerTemplate/src/cmdparser/root"
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
 	"github.com/SongZihuan/BackendServerTemplate/src/global"
@@ -67,7 +66,7 @@ func (d *GlobalConfig) check(c *configInfo) configerror.Error {
 
 func (d *GlobalConfig) process(c *configInfo) (cfgErr configerror.Error) {
 	name := cleanstringutils.GetStringOneLine(d.Name)
-	if (!root.NameChanged() || global.Name == "") && name != "" {
+	if (!global.NameFlagChanged || global.Name == "") && name != "" {
 		global.Name = name
 	}
 

+ 2 - 1
src/global/variabl.go

@@ -22,7 +22,8 @@ var (
 
 	// Name 继承自resource
 	// 注意:命令行参数或配置文件加载时可能会被更改
-	Name = resource.Name
+	Name            = resource.Name
+	NameFlagChanged = false
 )
 
 // Location 以下变量需要在配置文件加载完毕后才可调用

+ 8 - 0
src/mainfunc/cat/v1/config.go

@@ -62,11 +62,19 @@ func initInstallServiceConfig(args []string) error {
 			serviceConfig.ArgumentList = nil
 		}
 	case FromConfig:
+		if len(args) > 0 {
+			return fmt.Errorf("no parameters are allowed: %v", args)
+		}
+
 		if len(serviceConfig.ArgumentList) == 0 {
 			serviceConfig.ArgumentFrom = FromNo
 			serviceConfig.ArgumentList = nil
 		}
 	default:
+		if len(args) > 0 {
+			return fmt.Errorf("no parameters are allowed: %v", args)
+		}
+
 		serviceConfig.ArgumentFrom = FromNo
 		serviceConfig.ArgumentList = nil
 	}

+ 2 - 2
src/mainfunc/cat/v1/main.go

@@ -11,7 +11,7 @@ import (
 	"os"
 )
 
-func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
+func MainV1(cmd *cobra.Command, args []string, inputConfigFilePath string) (exitCode error) {
 	var err error
 
 	logfile, err := os.OpenFile("C:\\Users\\songz\\Code\\GoProject\\BackendServerTemplate\\test_self\\tmpcat.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
@@ -36,7 +36,7 @@ func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
 		EnvVars:     serviceConfig.EnvSetList,
 	}
 
-	prg := NewProgram()
+	prg := NewRunProgram(inputConfigFilePath)
 	s, err := service.New(prg, svcConfig)
 	if err != nil {
 		return exitutils.InitFailedError("Service New", err.Error())

+ 33 - 11
src/mainfunc/cat/v1/service.go

@@ -18,32 +18,50 @@ import (
 	"os"
 )
 
-var InputConfigFilePath string = "config.yaml"
-var OutputConfigFilePath string = ""
-
 type Program struct {
-	sigchan  chan os.Signal
-	stopErr  error
-	ser      serverinterface.Server
-	exitCode exitutils.ExitCode
+	sigchan    chan os.Signal
+	stopErr    error
+	ser        serverinterface.Server
+	exitCode   exitutils.ExitCode
+	configPath string
 }
 
 func NewProgram() *Program {
-	return &Program{}
+	return &Program{
+		sigchan:    make(chan os.Signal), // 临时顶替(后续会重新复制)
+		stopErr:    nil,
+		ser:        nil,
+		exitCode:   exitutils.ExitCode(0),
+		configPath: "",
+	}
+}
+
+func NewRunProgram(configPath string) *Program {
+	return &Program{
+		sigchan:    make(chan os.Signal), // 临时顶替(后续会重新复制)
+		stopErr:    nil,
+		ser:        nil,
+		exitCode:   exitutils.ExitCode(0),
+		configPath: configPath,
+	}
 }
 
 func (p *Program) Start(s service.Service) error {
 	var err error
 
-	configProvider, err := configparser.NewProvider(InputConfigFilePath, nil)
+	if p.configPath == "" {
+		panic("The main process should not be called.")
+	}
+
+	configProvider, err := configparser.NewProvider(p.configPath, nil)
 	if err != nil {
 		p.exitCode = exitutils.InitFailedError("Get config file provider", err.Error())
 		return err
 	}
 
 	err = config.InitConfig(&config.ConfigOption{
-		ConfigFilePath: InputConfigFilePath,
-		OutputFilePath: OutputConfigFilePath,
+		ConfigFilePath: p.configPath,
+		OutputFilePath: "",
 		Provider:       configProvider,
 	})
 	if err != nil {
@@ -90,6 +108,10 @@ func (p *Program) Start(s service.Service) error {
 }
 
 func (p *Program) Stop(s service.Service) error {
+	if p.configPath == "" {
+		panic("The main process should not be called.")
+	}
+
 	p.ser.Stop()
 	if p.stopErr != nil {
 		p.exitCode = exitutils.RunError(p.stopErr.Error())

+ 4 - 7
src/mainfunc/check/v1/main.go

@@ -12,13 +12,10 @@ import (
 	"github.com/spf13/cobra"
 )
 
-var InputConfigFilePath string = "config.yaml"
-var OutputConfigFilePath string = ""
-
-func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
+func MainV1(cmd *cobra.Command, args []string, inputConfigFilePath string, outputConfigFilePath string) (exitCode error) {
 	var err error
 
-	configProvider, err := configparser.NewProvider(InputConfigFilePath, &configparser.NewProviderOption{
+	configProvider, err := configparser.NewProvider(inputConfigFilePath, &configparser.NewProviderOption{
 		AutoReload: false,
 	})
 	if err != nil {
@@ -26,8 +23,8 @@ func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
 	}
 
 	err = config.InitConfig(&config.ConfigOption{
-		ConfigFilePath: InputConfigFilePath,
-		OutputFilePath: OutputConfigFilePath,
+		ConfigFilePath: inputConfigFilePath,
+		OutputFilePath: outputConfigFilePath,
 		Provider:       configProvider,
 	})
 	if err != nil {

+ 30 - 15
src/mainfunc/lion/v1/main.go

@@ -6,11 +6,12 @@ package v1
 
 import (
 	"errors"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmd/restart"
+	"fmt"
 	"github.com/SongZihuan/BackendServerTemplate/src/config"
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
 	"github.com/SongZihuan/BackendServerTemplate/src/consolewatcher"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"github.com/SongZihuan/BackendServerTemplate/src/restart"
 	"github.com/SongZihuan/BackendServerTemplate/src/server/controller"
 	"github.com/SongZihuan/BackendServerTemplate/src/server/example1"
 	"github.com/SongZihuan/BackendServerTemplate/src/server/example2"
@@ -20,23 +21,19 @@ import (
 	"github.com/spf13/cobra"
 )
 
-var InputConfigFilePath string = "config.yaml"
-var OutputConfigFilePath string = ""
-var AutoReload bool = false
-
-func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
+func MainV1(cmd *cobra.Command, args []string, inputConfigFilePath string, outputConfigFilePath string, ppid int) (exitCode error) {
 	var err error
 
-	configProvider, err := configparser.NewProvider(InputConfigFilePath, &configparser.NewProviderOption{
-		AutoReload: AutoReload,
+	configProvider, err := configparser.NewProvider(inputConfigFilePath, &configparser.NewProviderOption{
+		AutoReload: ppid != 0,
 	})
 	if err != nil {
 		return exitutils.InitFailedError("Get config file provider", err.Error())
 	}
 
 	err = config.InitConfig(&config.ConfigOption{
-		ConfigFilePath: InputConfigFilePath,
-		OutputFilePath: OutputConfigFilePath,
+		ConfigFilePath: inputConfigFilePath,
+		OutputFilePath: outputConfigFilePath,
 		Provider:       configProvider,
 	})
 	if err != nil {
@@ -50,6 +47,8 @@ func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
 		return exitutils.InitFailedError("Win32 console channel", err.Error())
 	}
 
+	ppidchan := restart.PpidWatcher(ppid)
+
 	ctrl, err := controller.NewController(&controller.ControllerOption{
 		StopWaitTime: config.Data().Server.StopWaitTimeDuration,
 	})
@@ -82,15 +81,26 @@ func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
 
 	var stopErr error
 
-SELECT:
 	select {
 	case <-restart.RestartChan:
-		if AutoReload {
-			logger.Warnf("stop/restart by config file change")
+		if ppid != 0 {
+			logger.Warnf("stop to restart")
 			err = nil
 			stopErr = nil
 		} else {
-			goto SELECT
+			logger.Warnf("stop to restart (error: restart not set)")
+			err = fmt.Errorf("stop by restart, but restart not set")
+			stopErr = err
+		}
+	case <-ppidchan:
+		if ppid != 0 {
+			logger.Warnf("stop by parent process")
+			err = nil
+			stopErr = nil
+		} else {
+			logger.Warnf("stop by parent process (error: ppid not set)")
+			err = fmt.Errorf("stop by parent process, but pppid not set")
+			stopErr = err
 		}
 	case sig := <-sigchan:
 		logger.Warnf("stop by signal (%s)", sig.String())
@@ -119,5 +129,10 @@ SELECT:
 		return exitutils.RunError(stopErr.Error())
 	}
 
-	return exitutils.SuccessExit("all tasks are completed and the main go routine exits")
+	select {
+	case <-restart.RestartChan:
+		return exitutils.SuccessExit("all tasks are completed and the main go routine exits", exitutils.ExitCodeReload)
+	default:
+		return exitutils.SuccessExit("all tasks are completed and the main go routine exits")
+	}
 }

+ 70 - 0
src/mainfunc/restart/v1/main.go

@@ -0,0 +1,70 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package v1
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/config"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+	"github.com/SongZihuan/BackendServerTemplate/src/consolewatcher"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"github.com/SongZihuan/BackendServerTemplate/src/restart"
+	"github.com/SongZihuan/BackendServerTemplate/src/signalwatcher"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
+	"github.com/spf13/cobra"
+)
+
+func MainV1(cmd *cobra.Command, args []string, inputConfigFilePath string, outputConfigFilePath string) (exitCode error) {
+	var err error
+
+	configProvider, err := configparser.NewProvider(inputConfigFilePath, &configparser.NewProviderOption{
+		AutoReload: false,
+	})
+	if err != nil {
+		return exitutils.InitFailedError("Get config file provider", err.Error())
+	}
+
+	err = config.InitConfig(&config.ConfigOption{
+		ConfigFilePath: inputConfigFilePath,
+		OutputFilePath: outputConfigFilePath,
+		Provider:       configProvider,
+	})
+	if err != nil {
+		return exitutils.InitFailedError("Config file read and parser", err.Error())
+	}
+
+	sigchan := signalwatcher.NewSignalExitChannel()
+
+	consolechan, consolewaitexitchan, err := consolewatcher.NewWin32ConsoleExitChannel()
+	if err != nil {
+		return exitutils.InitFailedError("Win32 console channel", err.Error())
+	}
+
+	stopchan := restart.RunRestart()
+
+	var stopErr error
+
+	select {
+	case <-stopchan:
+		logger.Warnf("stop by sub process")
+		err = nil
+		stopErr = nil
+	case sig := <-sigchan:
+		logger.Warnf("stop by signal (%s)", sig.String())
+		err = nil
+		stopErr = nil
+	case event := <-consolechan:
+		logger.Infof("stop by console event (%s)", event.String())
+		err = nil
+		stopErr = nil
+	}
+
+	close(consolewaitexitchan)
+
+	if stopErr != nil {
+		return exitutils.RunError(stopErr.Error())
+	}
+
+	return exitutils.SuccessExit("all tasks are completed and the main go routine exits")
+}

+ 31 - 14
src/mainfunc/tiger/v1/main.go

@@ -6,11 +6,12 @@ package v1
 
 import (
 	"errors"
-	"github.com/SongZihuan/BackendServerTemplate/src/cmd/restart"
+	"fmt"
 	"github.com/SongZihuan/BackendServerTemplate/src/config"
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
 	"github.com/SongZihuan/BackendServerTemplate/src/consolewatcher"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"github.com/SongZihuan/BackendServerTemplate/src/restart"
 	"github.com/SongZihuan/BackendServerTemplate/src/server/example1"
 	"github.com/SongZihuan/BackendServerTemplate/src/server/servercontext"
 	"github.com/SongZihuan/BackendServerTemplate/src/signalwatcher"
@@ -18,21 +19,19 @@ import (
 	"github.com/spf13/cobra"
 )
 
-var InputConfigFilePath string = "config.yaml"
-var OutputConfigFilePath string = ""
-var AutoReload bool = false
-
-func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
+func MainV1(cmd *cobra.Command, args []string, inputConfigFilePath string, outputConfigFilePath string, ppid int) (exitCode error) {
 	var err error
 
-	configProvider, err := configparser.NewProvider(InputConfigFilePath, nil)
+	configProvider, err := configparser.NewProvider(inputConfigFilePath, &configparser.NewProviderOption{
+		AutoReload: ppid != 0,
+	})
 	if err != nil {
 		return exitutils.InitFailedError("Get config file provider", err.Error())
 	}
 
 	err = config.InitConfig(&config.ConfigOption{
-		ConfigFilePath: InputConfigFilePath,
-		OutputFilePath: OutputConfigFilePath,
+		ConfigFilePath: inputConfigFilePath,
+		OutputFilePath: outputConfigFilePath,
 		Provider:       configProvider,
 	})
 	if err != nil {
@@ -46,6 +45,8 @@ func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
 		return exitutils.InitFailedError("Win32 console channel", err.Error())
 	}
 
+	ppidchan := restart.PpidWatcher(ppid)
+
 	ser, _, err := example1.NewServerExample1(&example1.ServerExample1Option{
 		StopWaitTime: config.Data().Server.StopWaitTimeDuration,
 	})
@@ -58,15 +59,26 @@ func MainV1(cmd *cobra.Command, args []string) (exitCode error) {
 
 	var stopErr error
 
-SELECT:
 	select {
 	case <-restart.RestartChan:
-		if AutoReload {
-			logger.Warnf("stop/restart by config file change")
+		if ppid != 0 {
+			logger.Warnf("stop to restart")
+			err = nil
+			stopErr = nil
+		} else {
+			logger.Warnf("stop to restart (error: restart not set)")
+			err = fmt.Errorf("stop by restart, but restart not set")
+			stopErr = err
+		}
+	case <-ppidchan:
+		if ppid != 0 {
+			logger.Warnf("stop by parent process")
 			err = nil
 			stopErr = nil
 		} else {
-			goto SELECT
+			logger.Warnf("stop by parent process (error: ppid not set)")
+			err = fmt.Errorf("stop by parent process, but pppid not set")
+			stopErr = err
 		}
 	case sig := <-sigchan:
 		logger.Warnf("stop by signal (%s)", sig.String())
@@ -95,5 +107,10 @@ SELECT:
 		return exitutils.RunError(stopErr.Error())
 	}
 
-	return exitutils.SuccessExit("all tasks are completed and the main go routine exits")
+	select {
+	case <-restart.RestartChan:
+		return exitutils.SuccessExit("all tasks are completed and the main go routine exits", exitutils.ExitCodeReload)
+	default:
+		return exitutils.SuccessExit("all tasks are completed and the main go routine exits")
+	}
 }

+ 1 - 1
src/cmd/restart/flags.go → src/restart/flags.go

@@ -5,4 +5,4 @@
 package restart
 
 const RestartFlag = "restart"
-const RestartFlagComplete = "--" + RestartFlag
+const restartFlag = "--" + RestartFlag

+ 30 - 0
src/restart/ppid.go

@@ -0,0 +1,30 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package restart
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/processutils"
+	"time"
+)
+
+func PpidWatcher(ppid int) chan any {
+	ppidchan := make(chan any)
+
+	if ppid == 0 {
+		return ppidchan
+	}
+
+	go func() {
+	PpidCycle:
+		for range time.Tick(3 * time.Second) {
+			if !processutils.PidExists(ppid) {
+				close(ppidchan)
+				break PpidCycle
+			}
+		}
+	}()
+
+	return ppidchan
+}

+ 82 - 0
src/restart/restart.go

@@ -0,0 +1,82 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package restart
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
+	"os"
+	"os/exec"
+)
+
+// 以下为 Sub 函数
+
+var RestartChan = make(chan any)
+
+func SetRestart() {
+	select {
+	case <-RestartChan:
+		return
+	default:
+		close(RestartChan)
+	}
+}
+
+// 以下为 Parent 函数
+
+func RunRestart() chan any {
+	stopchan := make(chan any)
+	go Restart(stopchan)
+	return stopchan
+}
+
+func Restart(stopchan chan any) {
+	var args []string
+	if len(os.Args) > 1 {
+		args = append([]string{fmt.Sprintf("%s=%d", restartFlag, os.Getpid())}, os.Args[1:]...)
+	} else {
+		args = []string{fmt.Sprintf("%s=%d", restartFlag, os.Getpid())}
+	}
+
+RestartCycle:
+	for {
+		stop := restart(args)
+		if stop {
+			break RestartCycle
+		}
+	}
+
+	close(stopchan)
+}
+
+func restart(args []string) (stop bool) {
+	defer func() {
+		if err := recover(); err != nil {
+			logger.Errorf("restart program error: %v", err)
+			stop = false
+		}
+	}()
+
+	cmd := exec.Command(osutils.GetArgs0(), args...)
+
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+
+	if err := cmd.Start(); err != nil {
+		logger.Errorf("restart program start failed: %s", err.Error())
+		return false
+	}
+
+	_ = cmd.Wait()
+	ec := cmd.ProcessState.ExitCode()
+
+	if ec == exitutils.ExitCodeReload {
+		return false
+	}
+	return true
+}

+ 14 - 5
src/utils/consoleutils/win32.go

@@ -20,11 +20,12 @@ var (
 	// 获取 FreeConsole 和 AllocConsole 函数
 	freeConsole           = kernel32.NewProc("FreeConsole")
 	allocConsole          = kernel32.NewProc("AllocConsole")
-	setConsoleCtrlHandler = kernel32.NewProc("SetConsoleCtrlHandler")
-	getConsoleWindow      = kernel32.NewProc("GetConsoleWindow")
-	setConsoleCP          = kernel32.NewProc("SetConsoleCP")
-	setConsoleOutputCP    = kernel32.NewProc("SetConsoleOutputCP")
-	attachConsole         = kernel32.NewProc("AttachConsole")
+	setConsoleCtrlHandler = kernel32.NewProc(
+		"SetConsoleCtrlHandler")
+	getConsoleWindow   = kernel32.NewProc("GetConsoleWindow")
+	setConsoleCP       = kernel32.NewProc("SetConsoleCP")
+	setConsoleOutputCP = kernel32.NewProc("SetConsoleOutputCP")
+	attachConsole      = kernel32.NewProc("AttachConsole")
 )
 
 func FreeConsole() error {
@@ -49,6 +50,14 @@ func AllocConsole() error {
 	return nil
 }
 
+func BindStdToConsoleSafe() error {
+	if !HasConsoleWindow() {
+		return nil
+	}
+
+	return BindStdToConsole()
+}
+
 func BindStdToConsole() error {
 	conin, err := os.OpenFile("CONIN$", os.O_RDWR, 0)
 	if err != nil {

+ 4 - 1
src/utils/exitutils/exit.go

@@ -17,10 +17,13 @@ const (
 	exitCodeMax                 = 255
 	exitCodeDefaultSuccess      = 0
 	exitCodeDefaultError        = 1
-	exitWithUnknownError        = 253
+	exitCodeReload              = 252
+	exitCodeWithUnknownError    = 253
 	exitCodeErrorLogMustBeReady = 254
 )
 
+const ExitCodeReload = exitCodeReload
+
 type ExitCode int
 
 func (e ExitCode) Error() string {

+ 17 - 0
src/utils/processutils/process.go

@@ -0,0 +1,17 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package processutils
+
+import (
+	"github.com/shirou/gopsutil/v4/process"
+)
+
+func PidExists(pid int) bool {
+	ret, err := process.PidExists(int32(pid))
+	if err != nil {
+		return false
+	}
+	return ret
+}