hn-readcard-go/config.go
2026-08-12 13:40:06 +08:00

169 lines
4.6 KiB
Go
Raw Permalink 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 main
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)
type Config struct {
Host string `json:"host"`
Port int `json:"port"`
DLLDir string `json:"dll_dir"`
CORSOrigin string `json:"cors_origin"`
LogDir string `json:"log_dir"`
LogRetentionDays int `json:"log_retention_days"`
LogMaxFileMB int `json:"log_max_file_mb"`
LogMaxTotalMB int `json:"log_max_total_mb"`
RestartDelayMS int `json:"restart_delay_ms"`
MaxRapidRestarts int `json:"max_rapid_restarts"`
GUIReaderType string `json:"gui_reader_type"`
AppRoot string `json:"-"`
}
func defaultConfig(appRoot string) Config {
return Config{
Host: "127.0.0.1",
Port: 17880,
DLLDir: "package/DWCardReaderDLL",
CORSOrigin: "*",
LogDir: "logs",
LogRetentionDays: 30,
LogMaxFileMB: 20,
LogMaxTotalMB: 200,
RestartDelayMS: 1500,
MaxRapidRestarts: 5,
GUIReaderType: "ZJ_HZ_310000",
AppRoot: appRoot,
}
}
func resolveAppRoot() (string, error) {
if root := strings.TrimSpace(os.Getenv("CARD_APP_ROOT")); root != "" {
return filepath.Abs(root)
}
cwd, cwdErr := os.Getwd()
if cwdErr == nil && fileExists(filepath.Join(cwd, "config.json")) {
return filepath.Abs(cwd)
}
exe, exeErr := os.Executable()
if exeErr == nil {
return filepath.Abs(filepath.Dir(exe))
}
if cwdErr != nil {
return "", fmt.Errorf("无法确定程序目录: cwd=%v, executable=%v", cwdErr, exeErr)
}
return filepath.Abs(cwd)
}
func loadConfig() (Config, error) {
root, err := resolveAppRoot()
if err != nil {
return Config{}, err
}
return loadConfigAt(root)
}
func loadConfigAt(root string) (Config, error) {
root, err := filepath.Abs(root)
if err != nil {
return Config{}, fmt.Errorf("解析程序目录失败: %w", err)
}
cfg := defaultConfig(root)
path := filepath.Join(root, "config.json")
if data, readErr := os.ReadFile(path); readErr == nil {
if err = json.Unmarshal(data, &cfg); err != nil {
return Config{}, fmt.Errorf("配置文件格式错误 %s: %w", path, err)
}
} else if !errors.Is(readErr, os.ErrNotExist) {
return Config{}, fmt.Errorf("读取配置文件失败 %s: %w", path, readErr)
}
cfg.AppRoot = root
if value := strings.TrimSpace(os.Getenv("CARD_HOST")); value != "" {
cfg.Host = value
}
if value := strings.TrimSpace(os.Getenv("CARD_PORT")); value != "" {
cfg.Port, err = strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("CARD_PORT 不是有效端口: %q", value)
}
}
if value := strings.TrimSpace(os.Getenv("CARD_DLL_DIR")); value != "" {
cfg.DLLDir = value
}
if value := strings.TrimSpace(os.Getenv("CARD_GUI_READER_TYPE")); value != "" {
cfg.GUIReaderType = value
}
if value := os.Getenv("CARD_CORS_ORIGIN"); value != "" {
cfg.CORSOrigin = value
}
if value := strings.TrimSpace(os.Getenv("CARD_LOG_DIR")); value != "" {
cfg.LogDir = value
}
if value := strings.TrimSpace(os.Getenv("CARD_LOG_MAX_FILE_MB")); value != "" {
cfg.LogMaxFileMB, err = strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("CARD_LOG_MAX_FILE_MB 不是有效整数: %q", value)
}
}
if value := strings.TrimSpace(os.Getenv("CARD_LOG_MAX_TOTAL_MB")); value != "" {
cfg.LogMaxTotalMB, err = strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("CARD_LOG_MAX_TOTAL_MB 不是有效整数: %q", value)
}
}
if strings.TrimSpace(cfg.Host) == "" {
return Config{}, errors.New("host 不能为空")
}
if cfg.Port < 1 || cfg.Port > 65535 {
return Config{}, fmt.Errorf("port 超出范围: %d", cfg.Port)
}
if strings.TrimSpace(cfg.DLLDir) == "" {
return Config{}, errors.New("dll_dir 不能为空")
}
if strings.TrimSpace(cfg.GUIReaderType) == "" {
return Config{}, errors.New("gui_reader_type 不能为空")
}
if strings.TrimSpace(cfg.CORSOrigin) == "" {
cfg.CORSOrigin = "*"
}
if strings.TrimSpace(cfg.LogDir) == "" {
cfg.LogDir = "logs"
}
if cfg.LogRetentionDays <= 0 {
cfg.LogRetentionDays = 30
}
if cfg.LogMaxFileMB <= 0 {
cfg.LogMaxFileMB = 20
}
if cfg.LogMaxTotalMB <= 0 {
cfg.LogMaxTotalMB = 200
}
if cfg.LogMaxTotalMB < cfg.LogMaxFileMB*3 {
return Config{}, fmt.Errorf("log_max_total_mb 至少应为 log_max_file_mb 的 3 倍(当前 %d < %d", cfg.LogMaxTotalMB, cfg.LogMaxFileMB*3)
}
if cfg.RestartDelayMS < 100 {
cfg.RestartDelayMS = 1500
}
if cfg.MaxRapidRestarts <= 0 {
cfg.MaxRapidRestarts = 5
}
if !filepath.IsAbs(cfg.LogDir) {
cfg.LogDir = filepath.Join(cfg.AppRoot, cfg.LogDir)
}
return cfg, nil
}
func fileExists(path string) bool {
info, err := os.Stat(path)
return err == nil && !info.IsDir()
}