New warehouse

This commit is contained in:
2025-10-24 00:09:45 +08:00
commit ac07e27908
75 changed files with 26814 additions and 0 deletions

111
cmd/root.go Normal file
View File

@@ -0,0 +1,111 @@
package cmd
import (
"io"
"networkDev/config"
"networkDev/utils/logger"
"os"
"path/filepath"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
// rootCmd 代表没有调用子命令时的基础命令
var rootCmd = &cobra.Command{
Use: "networkDev",
Short: "一个基于Cobra的网络验证服务器应用",
Long: `networkDev是一个使用Cobra CLI框架构建的网络验证服务器应用
集成了Viper配置管理、Logrus日志记录和embed静态资源嵌入功能。`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// 在加载配置前配置logrus用于非HTTP日志
setupLogrusForNonHTTP()
},
}
// Execute 添加所有子命令到根命令并设置适当的标志
// 这由main.main()调用。只需要对rootCmd执行一次。
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
// 在这里定义标志和配置设置
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件路径 (默认为 config.json)")
}
// setupLogrusForNonHTTP 配置logrus用于非HTTP日志
// 在加载配置文件之前进行基本的logrus设置
func setupLogrusForNonHTTP() {
// 设置日志格式
logrus.SetFormatter(&logrus.TextFormatter{
TimestampFormat: "2006-01-02 15:04:05",
FullTimestamp: true,
ForceColors: false,
DisableColors: true,
})
// 设置默认日志级别
logrus.SetLevel(logrus.InfoLevel)
// 设置输出目标(稍后会根据配置文件调整)
logrus.SetOutput(os.Stdout)
if cfgFile != "" {
// 使用命令行指定的配置文件
config.Init(cfgFile)
} else {
// 使用默认配置文件路径
config.Init("./config.json")
}
// 根据配置文件进一步配置logrus
setupLogrusFromConfig()
// 初始化HTTP日志处理器
logger.InitLogger()
// 记录配置加载完成
logrus.WithField("config_file", viper.ConfigFileUsed()).Info("配置文件加载完成")
}
// initConfig 读取配置文件和环境变量
func initConfig() {
}
// setupLogrusFromConfig 根据配置文件进一步配置logrus
// 设置日志级别和输出目标
func setupLogrusFromConfig() {
// 设置日志级别
if level := viper.GetString("log.level"); level != "" {
if logLevel, err := logrus.ParseLevel(level); err == nil {
logrus.SetLevel(logLevel)
}
}
// 设置日志输出目标
logFile := viper.GetString("log.file")
if logFile != "" {
// 确保日志目录存在
logDir := filepath.Dir(logFile)
if err := os.MkdirAll(logDir, 0755); err == nil {
// 打开日志文件
if file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil {
// 同时输出到控制台和文件
multiWriter := io.MultiWriter(os.Stdout, file)
logrus.SetOutput(multiWriter)
}
}
}
// 当日志文件路径为空时,保持默认输出到控制台,不创建任何目录
}

149
cmd/server.go Normal file
View File

@@ -0,0 +1,149 @@
package cmd
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"networkDev/database"
"networkDev/middleware"
"networkDev/server"
"networkDev/utils"
"networkDev/utils/logger"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// serverCmd 代表服务器命令
var serverCmd = &cobra.Command{
Use: "server",
Short: "启动HTTP服务器",
Long: `启动一个简单的HTTP服务器监听配置文件中指定的端口。`,
Run: runServer,
}
func init() {
// 将服务器命令添加到根命令
rootCmd.AddCommand(serverCmd)
// 添加服务器特定的标志
serverCmd.Flags().StringP("host", "H", "", "服务器监听地址 (覆盖配置文件)")
serverCmd.Flags().IntP("port", "p", 0, "服务器监听端口 (覆盖配置文件)")
}
// runServer 运行HTTP服务器
func runServer(cmd *cobra.Command, args []string) {
// 获取配置
host := getServerHost(cmd)
port := getServerPort(cmd)
addr := fmt.Sprintf("%s:%d", host, port)
// 获取全局日志实例
logger := logger.GetLogger()
logger.LogServerStart(host, port)
// 初始化Redis如果配置存在失败不致命
utils.InitRedis()
// 初始化数据库(根据 viper 配置选择 SQLite 或 MySQL
// 如果初始化失败则回退并退出
if _, err := database.Init(); err != nil {
logrus.WithError(err).Fatal("数据库初始化失败")
}
// 执行自动迁移(确保表结构存在)
if err := database.AutoMigrate(); err != nil {
logrus.WithError(err).Fatal("数据库自动迁移失败")
}
// 初始化默认管理员账号admin/admin123
if err := database.SeedDefaultAdmin(); err != nil {
logrus.WithError(err).Fatal("默认管理员初始化失败")
}
// 初始化默认系统设置
if err := database.SeedDefaultSettings(); err != nil {
logrus.WithError(err).Fatal("默认系统设置初始化失败")
}
// 创建HTTP服务器
server := createHTTPServer(addr)
// 启动服务器
startServer(server)
}
// getServerHost 获取服务器监听地址
func getServerHost(cmd *cobra.Command) string {
if host, _ := cmd.Flags().GetString("host"); host != "" {
return host
}
return viper.GetString("server.host")
}
// getServerPort 获取服务器监听端口
func getServerPort(cmd *cobra.Command) int {
if port, _ := cmd.Flags().GetInt("port"); port != 0 {
return port
}
return viper.GetInt("server.port")
}
// createHTTPServer 创建HTTP服务器
func createHTTPServer(addr string) *http.Server {
mux := http.NewServeMux()
// 注册路由
registerRoutes(mux)
// 使用日志中间件包装处理器
handler := middleware.WrapHandler(mux)
return &http.Server{
Addr: addr,
Handler: handler,
}
}
// registerRoutes 注册HTTP路由
func registerRoutes(mux *http.ServeMux) {
// 使用server包中的路由注册函数
server.RegisterRoutes(mux)
}
// startServer 启动服务器并处理优雅关闭
func startServer(server *http.Server) {
// 获取全局日志实例
logger := logger.GetLogger()
// 创建一个通道来接收操作系统信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 在goroutine中启动服务器
go func() {
logger.WithField("addr", server.Addr).Info("HTTP服务器已启动")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.LogError(err, "服务器启动失败")
os.Exit(1)
}
}()
// 等待中断信号
<-sigChan
logger.Info("收到关闭信号,正在优雅关闭服务器...")
// 创建一个带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 优雅关闭服务器
if err := server.Shutdown(ctx); err != nil {
logger.LogError(err, "服务器关闭时出错")
} else {
logger.LogServerStop()
}
}