--- name: nffund-holdings description: 查询南方基金持仓、盈亏或交易记录时使用。通过网页 API 和本地账号密码配置登录,复用私有 Cookie 会话;短信验证码优先通过可用的 Messages 插件读取,无法读取时询问用户。 --- # 南方基金 API 查询 仅使用附带脚本的独立 HTTP 会话完成「本地配置 → 复用会话或加密登录 → 必要验证 → API 查询」。目标站点是 `https://trade.southernfund.com`,只进行登录、必要验证和只读查询,不交易或修改账户。 用户请求查询南方基金账户时,查询流程包含必要的身份验证:优先复用已保存会话;没有可用会话或会话明确失效时,使用已指定配置中的 `account`(手机号或证件号)和 `password`,按官网协议向 `https://trade.southernfund.com` 提交一次登录请求,再完成必要的短信验证和只读查询。这些步骤已包含在查询请求中,不再单独询问是否允许向官网提交账号密码、发送本次验证码或更新本地会话与结果。 “优先复用会话”是执行顺序,不表示“仅允许复用、禁止密码登录”。只有用户明确提出后者时才按该限制执行;不要自行添加这一限制,也不要因脚本包含正常登录回退而在启动前等待额外授权。沿用会话中已确定的配置路径和验证码获取方式,只询问尚缺的信息。 仅安装、维护或离线测试 Skill 时,不登录真实账户或收发真实短信;会话中已授权的实测无需再次确认。 ## 范围与输入 - 依赖 macOS/Linux、Python 3.9+、Node.js 和 `/usr/bin/curl`,保持 HTTPS 证书校验。 - 从用户本次指定或会话中已确定的本地 JSON 读取 `account`、`password`;`certificatetype` 默认 `"0"`(手机号码/身份证号)。仅在配置路径缺失或无法确定时询问路径,不在聊天中索要密码或搜索其他文件寻找凭据。 - 需要模板时创建空 JSON,由用户填写。私有工作目录权限 `700`、配置和验证码文件权限 `600`;凭据、会话信息和账户数据不进入 Skill、插件缓存、版本控制或测试夹具。 ## 启动查询 使用 [scripts/query.py](scripts/query.py),将下列路径替换为实际绝对路径,日期使用用户时区的当前日期或其指定截止日。账号配置和可复用的 `session.json` 放在任务的私有 `work/nffund-api/` 下;每次运行另建私有子目录,`verification.json` 初始内容为 `{"sms_code":""}`,`result.json` 必须是新文件。旧验证码不能复用。 ```bash python3 /absolute/skill/scripts/query.py \ --config /absolute/task/work/nffund-api/config.json \ --session-file /absolute/task/work/nffund-api/session.json \ --sms-code-file /absolute/task/work/nffund-api/run/verification.json \ --out /absolute/task/work/nffund-api/run/result.json \ --as-of YYYY-MM-DD ``` 脚本输出 JSON 行作为进度,凭据通过子进程标准输入传递。`--session-file` 会保存 Cookie 和恢复会话所需的 `SECURE_TOKEN`,不保存密码或短信验证码;文件权限为 `600`、父目录为 `700`,使用原子替换并通过同目录的 `.lock` 文件拒绝并发使用。文件按账号及证件类型绑定,切换账号时用独立文件。不要展示、上传或提交其内容;省略该参数时仅在内存中保留状态。 加载状态后先访问受保护页面并验证持仓响应。会话有效时不提交密码、不发送短信;仅当官网返回登录页、`/new/timeout` 或明确的登录超时错误码时,保留服务端下发的可信 Cookie,回退一次正常登录。网络错误、未知业务错误、损坏文件或账号不匹配均停止,不能通过删除状态后反复登录来处理。状态文件仅在持仓业务成功且结构有效后保存。 记录命令工具返回的进程/会话标识,等待用户时保留原进程;单次状态轮询不超过 60 秒。`start.session_loaded` 表示读到了状态,只有 `session_reuse.success=true` 才表示本次成功复用。 ## 短信二次验证 Messages 插件可用且具有读取权限时,默认直接按 [references/messages-sms.md](references/messages-sms.md) 搜索本次登录的新短信,将唯一匹配的验证码写入同一验证文件,无需用户另行选择或确认。插件不可用、权限不足或无法可靠匹配时,请用户提供验证码。用户明确要求手动输入或不读取 Messages 时优先遵从;无需修改账号密码配置。 1. 脚本根据登录响应的 `need2FA`、`verifyToken` 和短信入口决定是否发送一次验证码。仅在 `sms_send.success=true` 且进入 `waiting_for_sms_code` 后读取或询问验证码;事件中的 `requested_at` 和 `deadline_at` 是本次请求与输入截止时间。 2. 用户要求手动输入,或 Messages 无法取得可靠验证码时,使用 `request_user_input_async` 的自由文本问题(无该工具时使用客户端可用的询问方式),替换下面的实际文件路径。用户已提供本次验证码、已填入验证文件,或 Messages 已取得唯一匹配时直接继续,不再提问: ```json { "questions": [{ "title": "南方基金已发送本次登录的短信验证码,请回复 6 位数字。我会用它完成当前登录并继续查询。若不想在聊天中提供,请将验证码填入验证文件的 sms_code 字段并保存:。" }] } ``` 3. 用户回复或 Messages 读取结果必须恰好为 6 位 ASCII 数字,按字符串保留前导零;回复可去掉首尾空白,格式错误只请用户修正。用文件工具或结构化脚本输入写入本次 `verification.json` 的 `sms_code`,不放入 shell 参数或输出。用户自行填文件时直接等待读取;聊天输入和插件返回可能留在会话或工具记录中,不能承诺不留痕。 4. 原进程最多等待 300 秒,沿用本次 Cookie、`SECURE_TOKEN` 和 `verifyToken` 提交一次验证码;该等待上限不代表服务端有效期。只有 `sms_verification.success=true` 后才继续查询。脚本清空已消费的验证码,清理失败时处理临时文件,不在答复中复述验证码。 登录失败、短信等待超时、取消或验证中会话丢失时停止,报告失败阶段,不自动重试、重发或复用旧码。用户要求重试且原流程已结束时,使用新进程和空验证文件;仍可加载同账号的已保存状态。无法保留后台进程,或遇未实现的图形/人脸验证时报告阻塞,不转为浏览器或云电脑查询;传输错误不等于密码错误。 ## 结果与完成条件 - 只有 `complete.success=true` 且所需查询返回 `success=true` 才报告完成;HTTP 200 或短信发送成功不够。 - 从 `--out` 的私有 JSON 读取相关账户字段,避免输出无关身份或银行卡信息。持仓是结构化 JSON,盈亏和交易记录的 `data` 是 HTML 片段,只解析、不执行脚本。未传 `--out` 时只检查链路并输出摘要。 - `mergedByFund` 与 `mergedByBank` 是同一资产的不同视图,不相加;分组数量不等于基金数量。按同一币种、账户范围和日期核对总额与明细,确认待确认金额是否已计入,缺失字段不填零。报告观察时间、净值日期、覆盖范围和未解释差额。 - 默认交易查询为截止日前 365 天、第 1 页、每页 10 条,`total` 不代表已读取全部记录。保存状态可减少重复验证,但 Cookie 到期、服务端撤销或风控仍可能要求短信;不能承诺长期免短信或无人值守登录。删除本地会话文件只清除本地副本,不撤销服务端会话。 核对资产口径或排查接口变化时,阅读 [references/web-api.md](references/web-api.md) 的字段、来源和实测边界。网站内部接口变化时检查当前官方实现,不猜测参数或反复试密码。