Files
query-payslip-default/scripts/util/runtime_paths.py
skill-dev 3db196e49d feat: 工资查询助手首版 v1.0.105
基于 skill-template 脚手架开发,包含:
- payroll_config(费率配置,可编辑)+ payslip_snapshots(解析历史,只读)2 张业务表
- 全国默认 + 北上广 4 套费率(社保/公积金/个税起征点/7 级税率表)
- 5 个业务 Action:calculate-payslip-simple / calculate-payslip-full / explain-term / view-config / edit-config
- 个税累计预扣法计算,支持多月历史累加
- 20 个薪资名词库(社保/公积金/个税/全勤/加班费等)
- 完整市场文档(README/TUTORIAL/DEMO/CHANGELOG)
- 296 测试全部通过
2026-09-07 17:02:05 +08:00

156 lines
5.2 KiB
Python
Raw 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.
"""数据根、技能目录、兄弟技能根路径与用户数据子目录解析。"""
from __future__ import annotations
import os
from jiangchang_skill_core.runtime_env import get_data_root, get_sibling_skills_root, get_user_id
from util.constants import SKILL_SLUG
_SCRIPTS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 用户可见的上行/下行目录(数据管理「输入/输出」文件浏览器)
STANDARD_SUBDIR_INPUT = "input"
STANDARD_SUBDIR_OUTPUT = "output"
# 宿主扫描的 HTML 报告页(数据管理侧栏 htmlPages,非 input/output 文件浏览器)
STANDARD_SUBDIR_HTML = "html"
# 内部/可选目录(不上侧栏)
STANDARD_SUBDIR_UPLOADS = "uploads"
STANDARD_SUBDIR_CACHE = "cache"
STANDARD_SUBDIR_TEMP = "temp"
STANDARD_SUBDIR_RPA_ARTIFACTS = "rpa-artifacts"
STANDARD_SUBDIR_VIDEOS = "videos"
CONFIG_KEY_INPUT_DIR = "SKILL_INPUT_DIR"
CONFIG_KEY_OUTPUT_DIR = "SKILL_OUTPUT_DIR"
CONFIG_KEY_UPLOAD_DIR = "SKILL_UPLOAD_DIR"
CONFIG_KEY_CACHE_DIR = "SKILL_CACHE_DIR"
CONFIG_KEY_TEMP_DIR = "SKILL_TEMP_DIR"
def get_skill_root() -> str:
return os.path.dirname(_SCRIPTS_DIR)
def get_openclaw_root() -> str:
return get_sibling_skills_root(_SCRIPTS_DIR)
def get_skills_root() -> str:
return get_sibling_skills_root(_SCRIPTS_DIR)
def get_skill_data_dir() -> str:
path = os.path.join(get_data_root(), get_user_id(), SKILL_SLUG)
os.makedirs(path, exist_ok=True)
return path
def get_db_path(filename: str | None = None) -> str:
name = filename or f"{SKILL_SLUG}.db"
return os.path.join(get_skill_data_dir(), name)
def _normalize_subdir(value: str) -> str:
cleaned = value.strip().strip("/\\")
if not cleaned or cleaned.startswith(".."):
raise ValueError(f"invalid skill data subdir: {value!r}")
return cleaned.replace("/", os.sep).replace("\\", os.sep)
def resolve_data_path(
config_key: str | None,
default_subdir: str,
*,
create: bool = True,
) -> str:
"""解析技能拥有路径:空配置 → 数据目录下 default_subdir;相对 → 相对数据目录;绝对 → 原样。"""
from jiangchang_skill_core import config
base = get_skill_data_dir()
raw = (config.get(config_key) or "").strip() if config_key else ""
if not raw:
path = os.path.join(base, _normalize_subdir(default_subdir))
elif os.path.isabs(raw):
path = os.path.abspath(os.path.expanduser(raw))
else:
path = os.path.join(base, _normalize_subdir(raw))
if create:
os.makedirs(path, exist_ok=True)
return path
def resolve_input_path(raw_path: str, *, create_parent: bool = False) -> str:
"""解析 CLI 输入路径:绝对路径原样;相对路径相对 {skill_data_dir}/input/。"""
text = (raw_path or "").strip()
if not text:
return text
expanded = os.path.expanduser(text)
if os.path.isabs(expanded):
return os.path.abspath(expanded)
input_dir = get_input_dir(create=create_parent)
return os.path.abspath(os.path.join(input_dir, os.path.normpath(expanded)))
def get_input_dir(*, create: bool = True) -> str:
"""上行:用户/Agent 放入的待处理文件(清单、CSV、产品表等)。"""
return resolve_data_path(CONFIG_KEY_INPUT_DIR, STANDARD_SUBDIR_INPUT, create=create)
def get_output_dir(*, create: bool = True) -> str:
"""下行:技能/宿主交给用户拿走的文件(视频、报表、导出 CSV 等)。"""
return resolve_data_path(CONFIG_KEY_OUTPUT_DIR, STANDARD_SUBDIR_OUTPUT, create=create)
def get_uploads_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_UPLOAD_DIR, STANDARD_SUBDIR_UPLOADS, create=create)
def get_cache_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_CACHE_DIR, STANDARD_SUBDIR_CACHE, create=create)
def get_temp_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_TEMP_DIR, STANDARD_SUBDIR_TEMP, create=create)
def get_rpa_artifacts_dir(batch_id: str, *, create: bool = True) -> str:
batch = (batch_id or "").strip() or "default"
path = os.path.join(get_skill_data_dir(), STANDARD_SUBDIR_RPA_ARTIFACTS, batch)
if create:
os.makedirs(path, exist_ok=True)
return path
def get_videos_dir(*, create: bool = True) -> str:
path = os.path.join(get_skill_data_dir(), STANDARD_SUBDIR_VIDEOS)
if create:
os.makedirs(path, exist_ok=True)
return path
def get_html_dir(*, create: bool = True) -> str:
"""HTML 报告页目录:宿主扫描 `{skill_data}/html/*.html`(一层、非递归)。"""
path = os.path.join(get_skill_data_dir(), STANDARD_SUBDIR_HTML)
if create:
os.makedirs(path, exist_ok=True)
return path
def list_resolved_data_paths() -> dict[str, str]:
"""供 health / config-path 输出已解析的数据子目录(不创建目录)。"""
return {
"skill_data_dir": get_skill_data_dir(),
"input_dir": get_input_dir(create=False),
"output_dir": get_output_dir(create=False),
"html_dir": get_html_dir(create=False),
"uploads_dir": get_uploads_dir(create=False),
"cache_dir": get_cache_dir(create=False),
"temp_dir": get_temp_dir(create=False),
"videos_dir": get_videos_dir(create=False),
}