Initial commit: TaskPool React panel
- React frontend with route-level code splitting - Backend rebranded from Baihu to TaskPool - DB brand migration script and local compatibility
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package constant
|
||||
|
||||
// CommandInfo 定义了终端可用命令的说明信息
|
||||
type CommandInfo struct {
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
// Commands 是系统的可用业务命令说明列表
|
||||
var Commands = []CommandInfo{
|
||||
// {
|
||||
// Name: "server",
|
||||
// Description: "启动后台服务进程",
|
||||
// },
|
||||
{
|
||||
Name: "reposync",
|
||||
Description: "同步远程 Git 仓库或文件到本地",
|
||||
},
|
||||
{
|
||||
Name: "resetpwd",
|
||||
Description: "重置 admin 用户密码(需要二次确认)",
|
||||
},
|
||||
{
|
||||
Name: "restore",
|
||||
Description: "从本地 zip 文件中全量恢复系统级备份数据",
|
||||
},
|
||||
{
|
||||
Name: "builtininstall",
|
||||
Description: "为所有 mise 管理的 Node.js 和 Python 环境安装内建助手库",
|
||||
},
|
||||
{
|
||||
Name: "task",
|
||||
Description: "系统级任务的列表查询、触发运行、启停控制及状态查看",
|
||||
},
|
||||
{
|
||||
Name: "depinstall",
|
||||
Description: "一键补全指定任务执行日志中的缺失依赖包",
|
||||
},
|
||||
{
|
||||
Name: "version",
|
||||
Description: "查看当前系统版本号 (同 -v, -V)",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package constant
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
|
||||
// DefaultRole 默认用户角色
|
||||
DefaultRole = "user"
|
||||
|
||||
// AdminRole 管理员角色
|
||||
AdminRole = "admin"
|
||||
|
||||
// DefaultTaskTimeout 默认任务超时时间(分钟)
|
||||
DefaultTaskTimeout = 30
|
||||
|
||||
// Settings Section 常量
|
||||
SectionSite = "site"
|
||||
SectionSystem = "system"
|
||||
SectionScheduler = "scheduler"
|
||||
SectionSecurity = "security"
|
||||
SectionNotify = "notify"
|
||||
|
||||
// Site Settings Key 常量
|
||||
KeyTitle = "title"
|
||||
KeySubtitle = "subtitle"
|
||||
KeyIcon = "icon"
|
||||
KeyPageSize = "page_size"
|
||||
KeyCookieDays = "cookie_days"
|
||||
KeyOpenapiToken = "openapi_token"
|
||||
KeyActiveWebUI = "active_webui"
|
||||
|
||||
// Security Settings Key 常量
|
||||
KeySecret = "secret"
|
||||
|
||||
// System Settings Key 常量
|
||||
KeyInitialized = "initialized"
|
||||
// KeyLogRetention = "log_retention" // Deprecated
|
||||
|
||||
// Log Retention Keys
|
||||
KeySystemNoticeDays = "system_notice_days"
|
||||
KeySystemNoticeMaxCount = "system_notice_max_count"
|
||||
KeyPushLogDays = "push_log_days"
|
||||
KeyPushLogMaxCount = "push_log_max_count"
|
||||
KeyLoginLogDays = "login_log_days"
|
||||
KeyLoginLogMaxCount = "login_log_max_count"
|
||||
KeySchedulerLogDays = "scheduler_log_days"
|
||||
KeySchedulerLogMaxCount = "scheduler_log_max_count"
|
||||
|
||||
// Scheduler Settings Key 常量
|
||||
KeyWorkerCount = "worker_count"
|
||||
KeyQueueSize = "queue_size"
|
||||
KeyRateInterval = "rate_interval"
|
||||
|
||||
// Notify Settings Key 常量
|
||||
KeyNotifyChannels = "channels"
|
||||
KeyNotifyEvents = "events"
|
||||
KeyNotifyToken = "notify_token"
|
||||
KeyNotifyPrefix = "notify_prefix"
|
||||
|
||||
// Notify Templates Keys
|
||||
KeyNotifyTemplateUserLoginTitle = "notify_template_user_login_title"
|
||||
KeyNotifyTemplateUserLoginText = "notify_template_user_login_text"
|
||||
KeyNotifyTemplateBruteForceLoginTitle = "notify_template_brute_force_login_title"
|
||||
KeyNotifyTemplateBruteForceLoginText = "notify_template_brute_force_login_text"
|
||||
KeyNotifyTemplatePasswordChangedTitle = "notify_template_password_changed_title"
|
||||
KeyNotifyTemplatePasswordChangedText = "notify_template_password_changed_text"
|
||||
KeyNotifyTemplateTaskSuccessTitle = "notify_template_task_success_title"
|
||||
KeyNotifyTemplateTaskSuccessText = "notify_template_task_success_text"
|
||||
KeyNotifyTemplateTaskFailedTitle = "notify_template_task_failed_title"
|
||||
KeyNotifyTemplateTaskFailedText = "notify_template_task_failed_text"
|
||||
KeyNotifyTemplateTaskTimeoutTitle = "notify_template_task_timeout_title"
|
||||
KeyNotifyTemplateTaskTimeoutText = "notify_template_task_timeout_text"
|
||||
|
||||
// 事件绑定类型
|
||||
BindingTypeSystem = "system"
|
||||
BindingTypeTask = "task"
|
||||
|
||||
// 系统事件类型
|
||||
EventUserLogin = "user_login"
|
||||
EventBruteForceLogin = "brute_force_login"
|
||||
EventPasswordChanged = "password_changed"
|
||||
|
||||
// 任务事件类型
|
||||
EventTaskSuccess = "task_success"
|
||||
EventTaskFailed = "task_failed"
|
||||
EventTaskTimeout = "task_timeout"
|
||||
EventTaskRunning = "task_running"
|
||||
EventTaskQueued = "task_queued"
|
||||
EventTaskCancelled = "task_cancelled"
|
||||
|
||||
// 其他事件类型
|
||||
EventSystemNotice = "system_notice"
|
||||
EventSchedulerLog = "scheduler_log"
|
||||
EventNotifySent = "notify_sent"
|
||||
EventAppLogAdded = "app_log_added"
|
||||
|
||||
// WebSocket 消息类型
|
||||
WSTypeHeartbeat = "heartbeat"
|
||||
WSTypeHeartbeatAck = "heartbeat_ack"
|
||||
WSTypeTasks = "tasks"
|
||||
WSTypeTaskResult = "task_result"
|
||||
WSTypeTaskLog = "task_log"
|
||||
WSTypeExecute = "execute"
|
||||
WSTypeUpdate = "update"
|
||||
WSTypeDisconnect = "disconnect"
|
||||
WSTypeConnected = "connected"
|
||||
WSTypeDisabled = "disabled"
|
||||
WSTypeEnabled = "enabled"
|
||||
WSTypeFetchTasks = "fetch_tasks"
|
||||
WSTypeTaskHeartbeat = "task_heartbeat"
|
||||
WSTypeStop = "stop"
|
||||
|
||||
// 任务状态
|
||||
TaskStatusSuccess = "success"
|
||||
TaskStatusFailed = "failed"
|
||||
TaskStatusRunning = "running"
|
||||
TaskStatusPending = "pending"
|
||||
TaskStatusTimeout = "timeout"
|
||||
TaskStatusCancelled = "cancelled"
|
||||
TaskStatusQueued = "queued"
|
||||
|
||||
// 任务类型
|
||||
TaskTypeNormal = "task"
|
||||
TaskTypeRepo = "repo"
|
||||
|
||||
// 任务置顶类型
|
||||
PinTypeNone = "none"
|
||||
PinTypeTop = "top"
|
||||
|
||||
// 触发类型(新值 taskpool_startup;读取时兼容旧值 baihu_startup)
|
||||
TriggerTypeCron = "cron"
|
||||
TriggerTypeTaskPoolStartup = "taskpool_startup"
|
||||
|
||||
// Agent 状态
|
||||
AgentStatusOnline = "online"
|
||||
AgentStatusOffline = "offline"
|
||||
|
||||
// AppLog 分类
|
||||
LogCategoryDefault = "default"
|
||||
LogCategorySystemNotice = "system_notice"
|
||||
LogCategoryPushLog = "push_log"
|
||||
LogCategoryLoginLog = "login_log"
|
||||
LogCategorySchedulerLog = "scheduler_log"
|
||||
|
||||
// AppLog 级别
|
||||
LogLevelInfo = "info"
|
||||
LogLevelWarning = "warning"
|
||||
LogLevelError = "error"
|
||||
|
||||
// AppLog 状态
|
||||
LogStatusUnread = "unread"
|
||||
LogStatusRead = "read"
|
||||
LogStatusSuccess = "success"
|
||||
LogStatusFailed = "failed"
|
||||
|
||||
// Env Type
|
||||
EnvTypeNormal = "normal"
|
||||
EnvTypeSecret = "secret"
|
||||
|
||||
// Relation Types
|
||||
RelationTypeTaskTag = "task_tag"
|
||||
RelationTypeTaskEnv = "task_env"
|
||||
RelationTypeEnvTag = "env_tag"
|
||||
|
||||
// WebSocket 安全常量
|
||||
// PongWait 收到 pong 的超时时间
|
||||
PongWait = 60 * time.Second
|
||||
// PingPeriod 发送 ping 的周期
|
||||
PingPeriod = (PongWait * 9) / 10
|
||||
// MaxMessageSize 允许的最大消息大小
|
||||
MaxMessageSize = 1024 * 1024 // 1MB
|
||||
// MaxLogSize 允许的最大日志大小 (保留末尾 10MB)
|
||||
MaxLogSize = 10 * 1024 * 1024 // 10MB
|
||||
|
||||
// ScriptsDirPlaceholder 脚本目录占位符
|
||||
ScriptsDirPlaceholder = "$SCRIPTS_DIR$"
|
||||
)
|
||||
|
||||
// CookieName Cookie 名称
|
||||
var CookieName = "BHToken"
|
||||
|
||||
// TablePrefix 表前缀,从配置文件读取
|
||||
var TablePrefix string
|
||||
|
||||
// Runtime 数据库配置快照,用于需要单独启动内部子进程(如 reposync)时显式透传数据库连接信息,
|
||||
// 避免主进程启动阶段清理环境变量后,子进程意外回退到默认 sqlite 配置。
|
||||
var (
|
||||
RuntimeDBType string
|
||||
RuntimeDBHost string
|
||||
RuntimeDBPort int
|
||||
RuntimeDBUser string
|
||||
RuntimeDBPassword string
|
||||
RuntimeDBName string
|
||||
RuntimeDBPath string
|
||||
RuntimeDBDSN string
|
||||
RuntimeDBTablePrefix string
|
||||
RuntimeDBSSLMode string
|
||||
)
|
||||
|
||||
// Secret JWT和密码salt密钥,运行中自动从数据库加载
|
||||
var Secret string
|
||||
|
||||
// DemoMode 演示模式,从环境变量读取
|
||||
var DemoMode bool
|
||||
|
||||
// DefaultIcon 默认站点图标
|
||||
var DefaultIcon = `<svg t="1766107903919" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1942" width="200" height="200"><path d="M884.992 273.05984c4.10624 0 5.0688-2.36544 2.10944-5.25312 0 0-64.28672-65.55648-111.7696-75.55072-47.48288-9.984-45.47584-59.37152-80.56832-75.02848-72.0896-32.16384-158.6176-34.3552-158.6176-34.3552s-91.37152-4.92544-138.752-9.89184c-19.46624-2.03776-54.46656-9.58464-54.46656-9.58464-4.0448-0.84992-10.63936-1.19808-14.66368-0.44032 0 0-30.63808-0.07168-44.30848 43.35616-8.8576 28.11904 1.792 104.79616 1.792 104.79616 1.46432 12.27776-3.42016 30.21824-10.72128 40.20224 0 0-36.77184 46.03904-58.9312 100.34176-22.15936 54.30272 118.15936 145.05984 208.98816 205.27104C507.82208 613.89824 502.03648 743.424 502.03648 743.424s-74.19904-96.75776-194.00704-156.9792C188.2112 526.22336 150.30272 442.23488 150.30272 442.23488c-2.89792-5.45792-5.94944-4.94592-6.71744 1.21856 0 0-15.1552 91.61728 16.25088 147.8144 70.92224 126.88384 141.74208 112.88576 197.03808 183.27552s54.272 164.9152 54.272 164.9152-97.62816-141.29152-235.66336-205.55776c-91.53536-61.27616-74.7008-125.91104-85.49376-101.85728-10.79296 24.05376 26.73664 192.41984 65.40288 222.2592 80.57856 62.18752 94.16704 101.98016 94.16704 101.98016h175.53408S544.512 814.85824 572.928 725.73952c44.41088-139.30496 40.20224-191.26272 40.20224-191.26272 0.08192-8.22272 6.81984-14.19264 14.98112-13.29152 0 0 46.45888 4.64896 66.64192 9.68704 23.53152 5.86752 55.35744 26.20416 55.35744 26.20416 3.49184 2.14016 7.39328 0.68608 8.69376-3.1744l34.4576-102.77888c1.30048-3.8912-0.63488-5.51936-4.352-3.75808 0 0-45.37344 25.09824-88.17664 12.1856-20.15232-6.08256-59.60704-14.82752-74.69056-32.6656-16.95744-20.03968-15.59552-71.3728 26.66496-79.21664 48.0256-8.9088 33.13664 15.14496 65.91488 24.64768 27.42272 7.95648 22.29248-1.69984 26.69568 5.21216 1.67936 2.63168 0.38912 32.65536 0.38912 32.65536-0.21504 6.144 3.39968 7.84384 8.0384 3.79904l41.89184-36.46464c17.37728-11.24352 30.86336-0.57344 48.24064-11.81696 14.73536-9.53344 29.58336-43.66336 29.58336-43.66336 4.48512-9.24672 0.60416-20.41856-8.63232-24.92416l-42.58816-20.80768c-3.69664-1.80224-3.38944-3.26656 0.74752-3.26656h62.0032zM422.54336 123.87328s-49.88928 50.7392-74.5472 50.66752c-24.65792-0.07168-33.24928-56.12544-3.92192-56.12544 18.00192 0 76.32896 0.21504 76.32896 0.21504 4.13696 0.02048 5.0688 2.36544 2.14016 5.24288z m123.09504 249.64096s-3.31776-25.53856-33.16736-40.05888c-29.8496-14.52032-52.5312-41.13408-59.648-68.95616-12.1856-54.8864 48.29184-104.192 48.29184-104.192s-30.1056 73.6768 3.5328 106.60864c54.272 53.12512 40.99072 106.5984 40.99072 106.5984z m155.56608-164.46464c-7.94624 10.32192-9.60512 37.92896-59.2384 20.736-49.63328-17.2032-71.00416-54.5792-71.00416-54.5792-2.27328-3.40992-0.79872-6.49216 3.328-6.8096 0 0 43.55072-5.03808 80.06656 6.44096 24.91392 7.82336 54.79424 23.88992 46.848 34.21184z" fill="#272636" p-id="1943"></path><path d="M366.30528 259.26656c1.05472-1.76128 1.51552-1.57696 1.19808 0.43008 0 0-7.55712 19.0464 22.8864 87.63392 27.0848 61.02016 87.49056 68.7104 118.66112 98.23232 55.808 52.82816 53.52448 123.45344 53.52448 123.45344s-25.82528-49.85856-85.98528-78.83776-92.6208-50.52416-127.7952-85.77024c-45.02528-45.12768 17.5104-145.14176 17.5104-145.14176zM500.0704 961.44384h134.49216s95.8464-138.07616 46.68416-291.25632c-16.19968-50.46272-43.45856-80.00512-43.45856-80.00512-5.18144-6.38976-8.89856-4.88448-8.448 3.34848 0 0 9.15456 99.80928-19.44576 194.00704-28.60032 94.208-109.824 173.90592-109.824 173.90592zM681.61536 956.8768h105.24672s22.38464-73.5232 16.61952-130.21184c-9.30816-91.57632-48.31232-132.72064-48.31232-132.72064-3.80928-4.77184-6.0928-3.67616-5.2224 2.38592 0 0 16.75264 89.1904-14.4896 150.1184-30.9248 60.30336-53.84192 110.42816-53.84192 110.42816zM869.30432 811.35616c-2.88768-2.9184-4.80256-1.95584-4.38272 2.10944 0 0 6.79936 44.99456-7.76192 81.22368-10.12736 25.1904-28.91776 58.60352-28.91776 58.60352h107.17184s5.34528-50.31936-19.89632-83.97824c-11.56096-23.99232-46.21312-57.9584-46.21312-57.9584z" fill="#272636" p-id="1944"></path></svg>`
|
||||
|
||||
// DefaultSettings 默认系统设置
|
||||
var DefaultSettings = map[string]map[string]string{
|
||||
SectionSite: {
|
||||
KeyTitle: "任务池",
|
||||
KeySubtitle: "极致轻量、高性能的自动化任务调度平台",
|
||||
KeyIcon: DefaultIcon,
|
||||
KeyPageSize: "10",
|
||||
KeyCookieDays: "7",
|
||||
KeyActiveWebUI: "default",
|
||||
},
|
||||
SectionScheduler: {
|
||||
KeyWorkerCount: "4",
|
||||
KeyQueueSize: "100",
|
||||
KeyRateInterval: "200",
|
||||
},
|
||||
SectionNotify: {
|
||||
KeyNotifyPrefix: "[任务池]",
|
||||
// Login
|
||||
KeyNotifyTemplateUserLoginTitle: "用户登录(成功/失败)",
|
||||
KeyNotifyTemplateUserLoginText: "用户 {{username}} 在 IP {{ip}} 登录{{status_label}}\n{{message}}",
|
||||
KeyNotifyTemplateBruteForceLoginTitle: "系统安全警告",
|
||||
KeyNotifyTemplateBruteForceLoginText: "检测到 IP {{ip}} 正在尝试暴力破解用户 {{username}}",
|
||||
KeyNotifyTemplatePasswordChangedTitle: "账户安全通知",
|
||||
KeyNotifyTemplatePasswordChangedText: "用户 {{username}} 刚刚修改了密码",
|
||||
// Task
|
||||
KeyNotifyTemplateTaskSuccessTitle: "任务[{{task_name}}] 成功",
|
||||
KeyNotifyTemplateTaskSuccessText: "任务 #{{task_id}} {{task_name}}\n状态: 成功\n耗时: {{duration}}ms\n执行结果: {{output}}",
|
||||
KeyNotifyTemplateTaskFailedTitle: "任务[{{task_name}}] 失败",
|
||||
KeyNotifyTemplateTaskFailedText: "任务 #{{task_id}} {{task_name}}\n状态: 失败\n执行时间: {{start_time}}\n原因: {{error}}\n最后输出: {{output}}",
|
||||
KeyNotifyTemplateTaskTimeoutTitle: "任务[{{task_name}}] 超时",
|
||||
KeyNotifyTemplateTaskTimeoutText: "任务 #{{task_id}} {{task_name}}\n状态: 超时\n耗时: {{duration}}ms\n最后输出: {{output}}",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
// CookieActiveInterconnectNodeID 穿越状态下标识目标子节点 ID 的 Cookie 键名
|
||||
CookieActiveInterconnectNodeID = "active_interconnect_node_id"
|
||||
|
||||
// SectionInterconnect 互联设置分组
|
||||
SectionInterconnect = "interconnect"
|
||||
|
||||
// 互联设置相关 Key
|
||||
KeyInterconnectToken = "interconnect_token"
|
||||
KeyInterconnectParentURL = "interconnect_parent_url"
|
||||
KeyInterconnectParentToken = "interconnect_parent_token"
|
||||
KeyInterconnectRole = "interconnect_role"
|
||||
|
||||
// 互联角色
|
||||
InterconnectRoleMaster = "master"
|
||||
InterconnectRoleChild = "child"
|
||||
|
||||
// 互联系统事件
|
||||
EventInterconnectChildStatus = "interconnect_child_status"
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
// IDSchemaSignature 数据库表结构指纹的固定 ID
|
||||
IDSchemaSignature = "sys_schema_sig"
|
||||
|
||||
// KeySchemaSignature 数据库表结构指纹,用于判断是否需要全量自动建表
|
||||
KeySchemaSignature = "schema_signature"
|
||||
|
||||
// KeyTaskEnvsMigrated 任务环境变量迁移标记 (v2版本强制重跑过)
|
||||
KeyTaskEnvsMigrated = "task_envs_migrated_v2"
|
||||
|
||||
// KeyTaskTagsMigrated 任务标签迁移标记 (v2版本强制重跑过)
|
||||
KeyTaskTagsMigrated = "task_tags_migrated_v2"
|
||||
|
||||
// 以下是旧表或字段名,用于前置结构迁移时重命名或删除
|
||||
TableMigrateQlTokens = "ql_tokens"
|
||||
ColumnMigrateQlTokenCode = "code"
|
||||
ColumnMigrateQlTokenToken = "token"
|
||||
ColumnMigrateDependencyType = "type"
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package constant
|
||||
|
||||
// MainstreamMisePlugins 主流的 mise 插件列表
|
||||
var MainstreamMisePlugins = []string{
|
||||
"python", "node", "go", "rust", "ruby", "java", "php",
|
||||
"deno", "bun", "zig", "dotnet", "elixir", "erlang",
|
||||
"crystal", "lua", "julia", "nim", "perl", "scala",
|
||||
"kotlin", "clojure", "dart", "flutter", "terraform",
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package constant
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var (
|
||||
// ConfigPath 配置文件路径
|
||||
ConfigPath string
|
||||
|
||||
// DataDir 数据目录
|
||||
DataDir string
|
||||
|
||||
// DefaultDBPath 默认数据库路径
|
||||
DefaultDBPath string
|
||||
|
||||
// WebDistDir 前端构建目录
|
||||
WebDistDir string
|
||||
|
||||
// ScriptsWorkDir 脚本工作目录
|
||||
ScriptsWorkDir string
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootDir := ResolveAppRootDir()
|
||||
ConfigPath = filepath.Clean(filepath.Join(rootDir, "configs", "config.ini"))
|
||||
DataDir = filepath.Clean(filepath.Join(rootDir, "data"))
|
||||
DefaultDBPath = filepath.Clean(filepath.Join(rootDir, "data", "taskpool.db"))
|
||||
WebDistDir = filepath.Clean(filepath.Join(rootDir, "web", "dist"))
|
||||
ScriptsWorkDir = filepath.Clean(filepath.Join(rootDir, "data", "scripts"))
|
||||
}
|
||||
|
||||
// ResolveAppRootDir 获取应用程序的绝对根目录路径。
|
||||
func ResolveAppRootDir() string {
|
||||
// 1. 检查当前工作目录(CWD)及其上级目录
|
||||
if cwd, err := os.Getwd(); err == nil {
|
||||
dir := cwd
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(dir, "configs", "config.ini")); err == nil {
|
||||
return dir
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
|
||||
return dir
|
||||
}
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 检查当前可执行文件路径及其上级目录
|
||||
if exe, err := os.Executable(); err == nil {
|
||||
dir := filepath.Dir(exe)
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(dir, "configs", "config.ini")); err == nil {
|
||||
return dir
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
|
||||
return dir
|
||||
}
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 兜底回退到当前工作目录
|
||||
if cwd, err := os.Getwd(); err == nil {
|
||||
return cwd
|
||||
}
|
||||
return "."
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package constant
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:embed sentence1-10000.json
|
||||
var sentenceData []byte
|
||||
|
||||
type Sentence struct {
|
||||
Name string `json:"name"`
|
||||
From string `json:"from"`
|
||||
}
|
||||
|
||||
var (
|
||||
lineCount int
|
||||
lineOffsets []int64
|
||||
sentenceOnce sync.Once
|
||||
)
|
||||
|
||||
// initSentences 初始化:统计行数和记录每行偏移
|
||||
func initSentences() {
|
||||
sentenceOnce.Do(func() {
|
||||
data := sentenceData
|
||||
var offset int64 = 0
|
||||
for {
|
||||
// 记录当前行的起始位置
|
||||
lineOffsets = append(lineOffsets, offset)
|
||||
lineCount++
|
||||
|
||||
// 寻找下一个换行符
|
||||
idx := bytes.IndexByte(data[offset:], '\n')
|
||||
if idx == -1 {
|
||||
// 最后一行没有换行符
|
||||
break
|
||||
}
|
||||
|
||||
// 移动到下一行的起始位置
|
||||
offset += int64(idx) + 1
|
||||
if offset >= int64(len(data)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetRandomSentence 随机获取一条古诗词
|
||||
func GetRandomSentence() string {
|
||||
initSentences()
|
||||
if lineCount <= 0 {
|
||||
return "欢迎使用任务池"
|
||||
}
|
||||
|
||||
targetIndex := rand.Intn(lineCount)
|
||||
start := lineOffsets[targetIndex]
|
||||
|
||||
// 确定当前行的结束位置
|
||||
var end int64
|
||||
if targetIndex < lineCount-1 {
|
||||
end = lineOffsets[targetIndex+1]
|
||||
} else {
|
||||
end = int64(len(sentenceData))
|
||||
}
|
||||
|
||||
// 提取行并清理两端的空白字符(包括 \r, \n)
|
||||
line := bytes.TrimSpace(sentenceData[start:end])
|
||||
if len(line) == 0 {
|
||||
return "欢迎使用任务池"
|
||||
}
|
||||
|
||||
var sData []string
|
||||
if err := json.Unmarshal(line, &sData); err == nil && len(sData) >= 1 {
|
||||
name := sData[0]
|
||||
from := ""
|
||||
if len(sData) >= 2 {
|
||||
from = sData[1]
|
||||
}
|
||||
|
||||
if from != "" {
|
||||
return "\"" + name + "\"—— " + from
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
return "欢迎使用任务池"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
package constant
|
||||
|
||||
import "time"
|
||||
|
||||
// 构建时注入的变量
|
||||
var (
|
||||
Version = "dev"
|
||||
BuildTime = "unknown"
|
||||
)
|
||||
|
||||
// 程序启动时间
|
||||
var StartTime = time.Now()
|
||||
Reference in New Issue
Block a user