"""CLI:argparse 与分发模板。 只负责参数解析并转交 service;业务内核在 domain / task_service,勿在此按入口复制逻辑。 """ from __future__ import annotations import argparse import sys from typing import List, Optional from service.task_service import ( cmd_calculate_full, cmd_calculate_simple, cmd_config_path, cmd_edit_config, cmd_explain, cmd_health, cmd_init_db, cmd_run, cmd_version, cmd_view_config, _parse_float_or_none, ) from util.config_bootstrap import bootstrap_skill_config from util.argparse_zh import ZhArgumentParser from util.constants import LOG_LOGGER_NAME, SKILL_SLUG from util.logging_config import get_skill_logger, setup_skill_logging def _cli_str_or_none(raw: Optional[str]) -> Optional[str]: if raw is None: return None v = str(raw).strip() return v or None def _handle_run(args: argparse.Namespace) -> int: tail = [str(x).strip() for x in (args.run_tail or []) if str(x).strip()] if len(tail) > 2: print("参数过多。") print("用法:python main.py run [target [input_id]] | run [-t target] [-i input_id]") return 1 t_target: Optional[str] = None t_input: Optional[str] = None if len(tail) == 2: t_target, t_input = tail[0], tail[1] elif len(tail) == 1: if tail[0].isdigit(): t_input = tail[0] else: t_target = tail[0] pick_t = _cli_str_or_none(getattr(args, "target", None)) pick_i = _cli_str_or_none(getattr(args, "input_id", None)) target = pick_t or t_target input_id = pick_i or t_input return cmd_run(target=target, input_id=input_id) def _handle_calculate_simple(args: argparse.Namespace) -> int: base_salary = float(args.base_salary) period = _cli_str_or_none(args.period) or "" reported_net = _parse_float_or_none(_cli_str_or_none(args.reported_net)) region = _cli_str_or_none(args.region) or "default" return cmd_calculate_simple(base_salary=base_salary, period=period, reported_net=reported_net, region=region) def _handle_calculate_full(args: argparse.Namespace) -> int: region = _cli_str_or_none(args.region) or "default" period = _cli_str_or_none(args.period) or "" raw_payload = _cli_str_or_none(args.raw_payload) or "" return cmd_calculate_full( base_salary=float(args.base_salary or 0), performance=float(args.performance or 0), full_attendance=float(args.full_attendance or 0), meal_allowance=float(args.meal_allowance or 0), transport_allowance=float(args.transport_allowance or 0), overtime_pay=float(args.overtime_pay or 0), other_gross=float(args.other_gross or 0), social_personal=_parse_float_or_none(_cli_str_or_none(args.social_personal)), housing_fund_personal=_parse_float_or_none(_cli_str_or_none(args.housing_fund_personal)), tax=_parse_float_or_none(_cli_str_or_none(args.tax)), leave_deduction=float(args.leave_deduction or 0), other_deduction=float(args.other_deduction or 0), period=period, reported_net=_parse_float_or_none(_cli_str_or_none(args.reported_net)), raw_payload=raw_payload, region=region, ) def _handle_explain(args: argparse.Namespace) -> int: term = _cli_str_or_none(args.term) or "" return cmd_explain(term) def _handle_view_config(args: argparse.Namespace) -> int: region = _cli_str_or_none(args.region) return cmd_view_config(region=region) def _handle_edit_config(args: argparse.Namespace) -> int: return cmd_edit_config( config_key=_cli_str_or_none(args.config_key) or "", region=_cli_str_or_none(args.region) or "default", config_value=float(args.config_value), ) def _print_full_usage() -> None: print("工资查询助手(main.py)可用命令:") print(" python main.py calculate-simple --base-salary N --period YYYY-MM [--reported-net N] [--region default|beijing|shanghai|guangzhou]") print(" python main.py calculate-full [全字段参数] --period YYYY-MM [--region ...]") print(" python main.py explain --term 名词") print(" python main.py view-config [--region ...]") print(" python main.py edit-config --config-key KEY --region R --config-value V") print(" python main.py run [target [input_id]] [-t target] [-i input_id]") print(" python main.py health") print(" python main.py config-path") print(" python main.py version") print(" python main.py init-db") def build_parser() -> ZhArgumentParser: p = ZhArgumentParser( prog="main.py", description="工资查询助手:核算应发/扣除/实发,核对差异,解释名词,估算个税。", formatter_class=argparse.RawDescriptionHelpFormatter, ) sub = p.add_subparsers(dest="cmd", required=True, parser_class=ZhArgumentParser) # calculate-simple sp = sub.add_parser("calculate-simple", help="快速估算(仅基本工资+period)") sp.add_argument("--base-salary", required=True, type=float, metavar="基本工资") sp.add_argument("--period", required=True, metavar="YYYY-MM") sp.add_argument("--reported-net", default=None, metavar="用户自报实发") sp.add_argument("--region", default=None, metavar="地区") sp.set_defaults(handler=_handle_calculate_simple) # calculate-full sp = sub.add_parser("calculate-full", help="精确核算(全字段)") sp.add_argument("--base-salary", type=float, default=0, metavar="基本工资") sp.add_argument("--performance", type=float, default=0, metavar="绩效") sp.add_argument("--full-attendance", type=float, default=0, metavar="全勤", dest="full_attendance") sp.add_argument("--meal-allowance", type=float, default=0, metavar="餐补", dest="meal_allowance") sp.add_argument("--transport-allowance", type=float, default=0, metavar="交通补", dest="transport_allowance") sp.add_argument("--overtime-pay", type=float, default=0, metavar="加班费", dest="overtime_pay") sp.add_argument("--other-gross", type=float, default=0, metavar="其他应发", dest="other_gross") sp.add_argument("--social-personal", default=None, metavar="社保个人", dest="social_personal") sp.add_argument("--housing-fund-personal", default=None, metavar="公积金个人", dest="housing_fund_personal") sp.add_argument("--tax", default=None, metavar="个税") sp.add_argument("--leave-deduction", type=float, default=0, metavar="事假扣款", dest="leave_deduction") sp.add_argument("--other-deduction", type=float, default=0, metavar="其他扣款", dest="other_deduction") sp.add_argument("--period", default="", metavar="YYYY-MM") sp.add_argument("--reported-net", default=None, metavar="用户自报实发", dest="reported_net") sp.add_argument("--raw-payload", default=None, metavar="原始工资条文本", dest="raw_payload") sp.add_argument("--region", default=None, metavar="地区") sp.set_defaults(handler=_handle_calculate_full) # explain sp = sub.add_parser("explain", help="名词解释") sp.add_argument("--term", required=True, metavar="名词") sp.set_defaults(handler=_handle_explain) # view-config sp = sub.add_parser("view-config", help="查看费率配置") sp.add_argument("--region", default=None, metavar="地区") sp.set_defaults(handler=_handle_view_config) # edit-config sp = sub.add_parser("edit-config", help="编辑费率配置") sp.add_argument("--config-key", required=True, metavar="配置键", dest="config_key") sp.add_argument("--region", default="default", metavar="地区") sp.add_argument("--config-value", required=True, type=float, metavar="新值", dest="config_value") sp.set_defaults(handler=_handle_edit_config) sp = sub.add_parser("run", help="任务执行命令骨架") sp.add_argument("--target", "-t", default=None, metavar="目标") sp.add_argument("--input-id", "-i", default=None, metavar="输入id", dest="input_id") sp.add_argument("run_tail", nargs="*", metavar="位置参数") sp.set_defaults(handler=_handle_run) sp = sub.add_parser("health", help="健康检查") sp.set_defaults(handler=lambda _a: cmd_health()) sp = sub.add_parser("config-path", help="输出用户 .env 与模板路径") sp.set_defaults(handler=lambda _a: cmd_config_path()) sp = sub.add_parser("version", help="版本信息(JSON)") sp.set_defaults(handler=lambda _a: cmd_version()) sp = sub.add_parser("init-db", help="幂等初始化本地数据库与数据管理元数据") sp.set_defaults(handler=lambda _a: cmd_init_db()) return p def main(argv: Optional[List[str]] = None) -> int: argv = argv if argv is not None else sys.argv[1:] bootstrap_skill_config() setup_skill_logging(SKILL_SLUG, LOG_LOGGER_NAME) get_skill_logger().info("cli_start argv=%s", sys.argv) if not argv: _print_full_usage() return 1 parser = build_parser() args = parser.parse_args(argv) return int(args.handler(args))