更新启动逻辑
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
|||||||
dist/
|
dist/
|
||||||
pdf/
|
pdf/
|
||||||
../log/
|
../log/
|
||||||
|
/log/
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ cp .env.example .env
|
|||||||
|
|
||||||
## 运行
|
## 运行
|
||||||
|
|
||||||
**运营推荐**:直接双击或运行 `transit.exe`(无参数),按中文菜单选择「单次执行」或「定时执行」。
|
**运营推荐**:直接双击或运行 `transit.exe`(无参数)。交互菜单 **10 秒内无操作** 将自动进入「定时执行(常驻)」;也可用菜单手动选「单次执行」或「定时执行」。超时秒数:`MENU_AUTO_SERVE_TIMEOUT_SEC`(默认 10)。
|
||||||
|
|
||||||
**完整命令说明**(单次 / 定时 / 计划任务 / 排错):见 **[docs/COMMANDS.md](docs/COMMANDS.md)**。
|
**完整命令说明**(单次 / 定时 / 计划任务 / 排错):见 **[docs/COMMANDS.md](docs/COMMANDS.md)**。
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ func isInteractiveTerminal() bool {
|
|||||||
// runInteractiveMenu 无参数时展示中文菜单,供运营选择单次或定时。
|
// runInteractiveMenu 无参数时展示中文菜单,供运营选择单次或定时。
|
||||||
func runInteractiveMenu() {
|
func runInteractiveMenu() {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
timeout := menuAutoServeTimeout()
|
||||||
for {
|
for {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("======== 互联网医院监管中转 ========")
|
fmt.Println("======== 互联网医院监管中转 ========")
|
||||||
@@ -86,9 +87,13 @@ func runInteractiveMenu() {
|
|||||||
fmt.Println(" 2) 定时执行(常驻,按 .env 中 CRON 每天跑)")
|
fmt.Println(" 2) 定时执行(常驻,按 .env 中 CRON 每天跑)")
|
||||||
fmt.Println(" 3) 查看日志(Web 控制台,不跑定时)")
|
fmt.Println(" 3) 查看日志(Web 控制台,不跑定时)")
|
||||||
fmt.Println(" 0) 退出")
|
fmt.Println(" 0) 退出")
|
||||||
|
fmt.Printf("%v 内未选择将自动启动「2) 定时执行(常驻)」\n", timeout.Round(time.Second))
|
||||||
fmt.Print("请选择 [0-3]: ")
|
fmt.Print("请选择 [0-3]: ")
|
||||||
choice, _ := reader.ReadString('\n')
|
choice, timedOut := readLineTimeout(reader, timeout)
|
||||||
choice = strings.TrimSpace(choice)
|
if timedOut {
|
||||||
|
choice = "2"
|
||||||
|
fmt.Println("已超时,自动选择: 2) 定时执行(常驻)")
|
||||||
|
}
|
||||||
switch choice {
|
switch choice {
|
||||||
case "1":
|
case "1":
|
||||||
runInteractiveSync(reader)
|
runInteractiveSync(reader)
|
||||||
@@ -145,10 +150,14 @@ func runInteractiveServe(reader *bufio.Reader) {
|
|||||||
fmt.Printf(" 默认 cron: %s(每天 22:00)\n", expr)
|
fmt.Printf(" 默认 cron: %s(每天 22:00)\n", expr)
|
||||||
}
|
}
|
||||||
fmt.Println("启动后进程将常驻,到点自动执行 step=all。")
|
fmt.Println("启动后进程将常驻,到点自动执行 step=all。")
|
||||||
|
timeout := menuAutoServeTimeout()
|
||||||
|
fmt.Printf("%v 内未确认将自动启动定时服务\n", timeout.Round(time.Second))
|
||||||
fmt.Print("确认启动定时服务? [Y/n]: ")
|
fmt.Print("确认启动定时服务? [Y/n]: ")
|
||||||
confirm, _ := reader.ReadString('\n')
|
confirm, timedOut := readLineTimeout(reader, timeout)
|
||||||
confirm = strings.TrimSpace(strings.ToLower(confirm))
|
if timedOut {
|
||||||
if confirm == "n" || confirm == "no" {
|
fmt.Println("已超时,自动确认启动。")
|
||||||
|
}
|
||||||
|
if !serveConfirmYes(confirm, timedOut) {
|
||||||
fmt.Println("已取消。")
|
fmt.Println("已取消。")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
49
cmd/transit/menu_io.go
Normal file
49
cmd/transit/menu_io.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultMenuAutoServeTimeout = 10 * time.Second
|
||||||
|
|
||||||
|
// menuAutoServeTimeout 交互菜单自动进入常驻的超时;可由 MENU_AUTO_SERVE_TIMEOUT_SEC 覆盖。
|
||||||
|
func menuAutoServeTimeout() time.Duration {
|
||||||
|
v := strings.TrimSpace(os.Getenv("MENU_AUTO_SERVE_TIMEOUT_SEC"))
|
||||||
|
if v == "" {
|
||||||
|
return defaultMenuAutoServeTimeout
|
||||||
|
}
|
||||||
|
sec, err := strconv.Atoi(v)
|
||||||
|
if err != nil || sec <= 0 {
|
||||||
|
return defaultMenuAutoServeTimeout
|
||||||
|
}
|
||||||
|
return time.Duration(sec) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
// readLineTimeout 在 timeout 内等待一行;超时返回 timedOut=true。
|
||||||
|
// 注意:Windows 控制台无法在超时后取消阻塞读,后台 goroutine 仍会等到用户按 Enter。
|
||||||
|
func readLineTimeout(r *bufio.Reader, timeout time.Duration) (line string, timedOut bool) {
|
||||||
|
ch := make(chan string, 1)
|
||||||
|
go func() {
|
||||||
|
s, _ := r.ReadString('\n')
|
||||||
|
ch <- s
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case s := <-ch:
|
||||||
|
return strings.TrimSpace(s), false
|
||||||
|
case <-time.After(timeout):
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveConfirmYes 是否启动定时常驻(超时视为确认)。
|
||||||
|
func serveConfirmYes(confirm string, timedOut bool) bool {
|
||||||
|
if timedOut {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
c := strings.TrimSpace(strings.ToLower(confirm))
|
||||||
|
return c != "n" && c != "no"
|
||||||
|
}
|
||||||
61
cmd/transit/menu_io_test.go
Normal file
61
cmd/transit/menu_io_test.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadLineTimeout_returnsBeforeDeadline(t *testing.T) {
|
||||||
|
r := bufio.NewReader(bytes.NewBufferString("hello\n"))
|
||||||
|
line, timedOut := readLineTimeout(r, 2*time.Second)
|
||||||
|
if timedOut {
|
||||||
|
t.Fatal("expected input, got timeout")
|
||||||
|
}
|
||||||
|
if line != "hello" {
|
||||||
|
t.Fatalf("line=%q", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadLineTimeout_timesOut(t *testing.T) {
|
||||||
|
pr, _ := io.Pipe()
|
||||||
|
r := bufio.NewReader(pr)
|
||||||
|
_, timedOut := readLineTimeout(r, 50*time.Millisecond)
|
||||||
|
if !timedOut {
|
||||||
|
t.Fatal("expected timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServeConfirmYes(t *testing.T) {
|
||||||
|
if !serveConfirmYes("", false) {
|
||||||
|
t.Fatal("empty should confirm")
|
||||||
|
}
|
||||||
|
if !serveConfirmYes("y", false) {
|
||||||
|
t.Fatal("y should confirm")
|
||||||
|
}
|
||||||
|
if !serveConfirmYes("", true) {
|
||||||
|
t.Fatal("timeout should confirm")
|
||||||
|
}
|
||||||
|
if serveConfirmYes("n", false) {
|
||||||
|
t.Fatal("n should cancel")
|
||||||
|
}
|
||||||
|
if serveConfirmYes("no", false) {
|
||||||
|
t.Fatal("no should cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMenuAutoServeTimeout_default(t *testing.T) {
|
||||||
|
t.Setenv("MENU_AUTO_SERVE_TIMEOUT_SEC", "")
|
||||||
|
if menuAutoServeTimeout() != defaultMenuAutoServeTimeout {
|
||||||
|
t.Fatalf("got %v", menuAutoServeTimeout())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMenuAutoServeTimeout_env(t *testing.T) {
|
||||||
|
t.Setenv("MENU_AUTO_SERVE_TIMEOUT_SEC", "5")
|
||||||
|
if menuAutoServeTimeout() != 5*time.Second {
|
||||||
|
t.Fatalf("got %v", menuAutoServeTimeout())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,10 +31,12 @@ go run ./cmd/transit
|
|||||||
2) 定时执行(常驻,按 .env 中 CRON 每天跑)
|
2) 定时执行(常驻,按 .env 中 CRON 每天跑)
|
||||||
3) 查看日志(Web 控制台,不跑定时)
|
3) 查看日志(Web 控制台,不跑定时)
|
||||||
0) 退出
|
0) 退出
|
||||||
|
10s 内未选择将自动启动「2) 定时执行(常驻)」
|
||||||
请选择 [0-3]:
|
请选择 [0-3]:
|
||||||
```
|
```
|
||||||
|
|
||||||
- 选 **1**:再输入 `step`(默认 all)、`date`(回车=按 ANCHOR_OFFSET_DAYS 推算,默认昨日)。
|
- **10 秒无输入**:自动选择 **2** 并进入确认步骤;确认步骤同样 **10 秒无输入** 则自动启动常驻(适合双击 `transit.exe` 无人值守)。超时秒数可用环境变量 `MENU_AUTO_SERVE_TIMEOUT_SEC`(正整数,默认 10)调整。
|
||||||
|
- 选 **1**:再输入 `step`(默认 all)、`date`(回车=按 ANCHOR_OFFSET_DAYS 推算,默认昨日);该路径仍须手动输入,无自动超时。
|
||||||
- 选 **2**:显示当前 cron 配置,确认后进程常驻,到点自动 `step=all`,并启动 Web 控制台(默认 `http://127.0.0.1:8765/`)。
|
- 选 **2**:显示当前 cron 配置,确认后进程常驻,到点自动 `step=all`,并启动 Web 控制台(默认 `http://127.0.0.1:8765/`)。
|
||||||
- 选 **3**:仅启动 Web,可浏览文件日志与本机同步流水(测试同步需选 2 用 serve)。
|
- 选 **3**:仅启动 Web,可浏览文件日志与本机同步流水(测试同步需选 2 用 serve)。
|
||||||
- 选 **0**:退出。
|
- 选 **0**:退出。
|
||||||
|
|||||||
Reference in New Issue
Block a user