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 测试全部通过
This commit is contained in:
52
examples/simulator_browser_rpa/scripts/util/constants.py
Normal file
52
examples/simulator_browser_rpa/scripts/util/constants.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""仿真浏览器 RPA 示例常量。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from jiangchang_skill_core import config
|
||||
|
||||
SKILL_SLUG = "example-simulator-browser-rpa"
|
||||
LOG_LOGGER_NAME = "openclaw.skill.example_simulator_browser_rpa"
|
||||
|
||||
TARGET_PLATFORM = "sim_batch"
|
||||
LEASE_HOLDER = "example-simulator-browser-rpa"
|
||||
LEASE_TTL_SEC = "1800"
|
||||
|
||||
DEFAULT_TIMEOUT_MS = 15_000
|
||||
LOGIN_NAVIGATE_TIMEOUT_MS = 30_000
|
||||
SUBMIT_NAVIGATE_TIMEOUT_MS = 60_000
|
||||
DEFAULT_PORTAL_LOGIN_WAIT_SEC = 120
|
||||
|
||||
DEFAULT_DEMO_LOGIN_ID = "demo001"
|
||||
DEFAULT_DEMO_PASSWORD = "demo-password"
|
||||
DEFAULT_DEMO_CAPTCHA = "0000"
|
||||
DEFAULT_DEMO_TOKEN_PIN = "123456"
|
||||
|
||||
ENV_SIMULATOR_BASE_URL = "SIMULATOR_BASE_URL"
|
||||
ENV_SIMULATOR_PASSWORD = "SIMULATOR_PASSWORD"
|
||||
ENV_SIMULATOR_TOKEN_PIN = "SIMULATOR_TOKEN_PIN"
|
||||
ENV_PORTAL_LOGIN_WAIT_SEC = "PORTAL_LOGIN_WAIT_SEC"
|
||||
|
||||
_EXAMPLE_ROOT = Path(__file__).resolve().parents[2]
|
||||
DEFAULT_DEMO_APP_PATH = _EXAMPLE_ROOT / "sandbox" / "demo_app.html"
|
||||
|
||||
|
||||
def portal_login_wait_sec() -> int:
|
||||
raw = (
|
||||
os.getenv(ENV_PORTAL_LOGIN_WAIT_SEC)
|
||||
or config.get(ENV_PORTAL_LOGIN_WAIT_SEC)
|
||||
or str(DEFAULT_PORTAL_LOGIN_WAIT_SEC)
|
||||
)
|
||||
try:
|
||||
return max(1, int(str(raw).strip()))
|
||||
except (TypeError, ValueError):
|
||||
return DEFAULT_PORTAL_LOGIN_WAIT_SEC
|
||||
|
||||
|
||||
def resolve_simulator_base_url() -> str:
|
||||
env = (os.getenv(ENV_SIMULATOR_BASE_URL) or "").strip()
|
||||
if env:
|
||||
return env.rstrip("/")
|
||||
return DEFAULT_DEMO_APP_PATH.as_uri()
|
||||
38
examples/simulator_browser_rpa/scripts/util/logging.py
Normal file
38
examples/simulator_browser_rpa/scripts/util/logging.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""示例日志工具。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from util.constants import LOG_LOGGER_NAME
|
||||
|
||||
|
||||
def get_logger(name: str | None = None) -> logging.Logger:
|
||||
return logging.getLogger(name or LOG_LOGGER_NAME)
|
||||
|
||||
|
||||
def configure_logging(level: int = logging.INFO) -> None:
|
||||
logging.basicConfig(level=level, format="%(levelname)s %(name)s %(message)s")
|
||||
|
||||
|
||||
def mask_text(value: str, *, keep_start: int = 2, keep_end: int = 2) -> str:
|
||||
s = (value or "").strip()
|
||||
if len(s) >= 11 and s.isdigit():
|
||||
return s[:3] + "****" + s[-4:]
|
||||
if len(s) > keep_start + keep_end:
|
||||
return s[:keep_start] + "****" + s[-keep_end:]
|
||||
return "****"
|
||||
|
||||
|
||||
def safe_log_value(value: Any) -> str:
|
||||
if value is None:
|
||||
return ""
|
||||
text = str(value)
|
||||
if re.fullmatch(r"\d{7,}", text):
|
||||
return mask_text(text)
|
||||
if "@" in text and len(text) > 6:
|
||||
local, _, domain = text.partition("@")
|
||||
return f"{mask_text(local, keep_start=1, keep_end=1)}@{domain}"
|
||||
return text
|
||||
Reference in New Issue
Block a user