# xk-hy-forward-go 内网双通道转发:外网 `xk-hy-transit-go` 只访问本服务,由本机转发至浙江省政务云(`59.202.52.129`)。 ## 架构 | 监听路径 | 转发目标 | 用途 | |----------|----------|------| | `POST /province/supervise/data` | `SUPERVISE_TARGET_URL`(28212) | 加密后的监管业务 JSON | | `POST /mng/file/auth/upload` | `FILE_TARGET_URL`(28211) | 处方 PDF multipart 上传 | | `GET /health` | 本地 | 健康检查 | | `GET /t` | 本地 | 政务云测试页(读 file api-logs、连通测试、回放、导出 ApiPost) | | `GET /t/prescription` | 本地 | 处方业务记录(PDF / 在线处方 / 处方核销,支持详情与单条重试) | 转发规则: - **URL**:由环境变量固定(Scheme/Host/Path),不使用客户端 URL。 - **Body**:原样透传。 - **Header**:仅白名单内的业务头透传到政务云;`User-Agent`、`Cookie`、`X-Forwarded-*`、`X-Forward-Token` 等不会带上游。 - 监管白名单含 `requestBody`、`X-Ca-Signature` 等(与 transit `hy.BuildUpload` 一致)。 ## 环境配置 ```bash cp .env.example .env # 编辑 .env 后,建议在项目根目录启动(见下方「启动与 .env 加载」) ``` ### 启动与 .env 加载 程序启动时会按以下顺序查找并加载 **第一个存在的** `.env` 文件: 1. 当前工作目录(`os.Getwd()`) 2. 可执行文件所在目录 3. 可执行文件上级目录 加载成功时控制台会输出:`[forward] 已加载 /path/to/.env`。若未找到任何 `.env`,会提示使用环境变量或代码默认值。 **注意:** - 请使用 **`#` 作为注释**(不要用 `;`,godotenv 不会把 `;` 行当作注释)。 - 从 IDE / 服务方式启动时,请把 **Working Directory** 设为 `xk-hy-forward-go` 项目根,或把 `.env` 放在 **exe 同目录**。 - 核对启动日志中的 `forward_enabled=`、`supervise=`、`file=` 是否为你在 `.env` 里配置的项。 | 变量 | 默认 | 说明 | |------|------|------| | `LISTEN_ADDR` | `:16001` | 监听地址(建议绑内网 IP) | | `SUPERVISE_TARGET_URL` | 政务云 28212 | 监管业务上报 | | `FILE_TARGET_URL` | 政务云 28211 | 处方 PDF 上传 | | `TARGET_URL` | (兼容) | 未设 `SUPERVISE_TARGET_URL` 时生效 | | `ALLOW_IPS` | 空 | 逗号分隔来源 IP;**生产建议填 transit 出口 IP** | | `FORWARD_SHARED_SECRET` | 空 | 非空时要求请求头 `X-Forward-Token` 一致(与 transit `.env` 同值) | | `ENABLE_FORWARD` | `true` | 是否真正转发到政务云;`false`/`0`/`no` 时进入回显模式(见下) | | `LOG_DIR` | 空 | 日志根目录;默认 `{程序目录}/../log/forward/` | ### 回显模式(`ENABLE_FORWARD=false`) 不请求政务云上游,按与真实转发**相同规则**(固定目标 URL + Header 白名单 + 原样 Body)组装出站内容,以 JSON 返回给 `xk-hy-transit-go`,便于联调核对将要发出的报文。 响应示例: ```json { "dry_run": true, "kind": "supervise", "target_url": "https://59.202.52.129:28212/province/supervise/data", "method": "POST", "headers": { "Content-Type": ["application/json"], "X-Ca-Signature": ["..."] }, "body": "明文(监管 JSON 且 UTF-8 有效时)", "body_base64": "始终提供,可还原 multipart/二进制" } ``` - `headers`:过滤后将要转发的请求头。 - `body`:明文;`kind=file` 或 `multipart` 时为空,请用 `body_base64`。 - 启动日志含 `forward_enabled=false`;应用日志行为 `dry_run | target=... | body_len=N`。 本地排查示例: ```env ENABLE_FORWARD=false ALLOW_IPS= ``` ## 运行 ```bash cd xk-hy-forward-go go run main.go go test ./... ``` `main.go` 仅作入口,业务逻辑在 `internal/forward` 包;`go run main.go` 会编译入口及其依赖,启动全部路由(`/health`、监管、文件上传)。等价方式:`go run .`、`go build -o forward.exe .`。 Windows 也可双击或执行项目根目录下的 [`run.bat`](d:/worker/code/xk-hy-forward-go/run.bat)(内部为 `go run main.go`)。 IDE 调试:Working Directory 设为项目根,运行文件选 `main.go` 即可。 ## 政务云测试页 `/t` 启动后浏览器访问:`http://:16001/t`(随 forward 进程开放,**仅建议内网使用**)。 功能: - **连通测试**:对当前 `.env` 中 28212 / 28211 目标 URL 发起探测。 - **file 日志**:左侧列出 `api-logs/**/_file_*.log`,点击解析 **入站(块1)** / **出站(块2)** 的 Headers 与 Body(multipart 为 `body_base64`)。 - **发送**:按出站头/体向政务云回放 POST(不含 `X-Forward-Token`)。 - **导出 ApiPost**:下载 Postman Collection v2.1 JSON,在 ApiPost 中选择 **导入 → Postman 集合**。 日志含 `X-Authorization` 等敏感信息,请勿将导出的集合或 `/t` 暴露到公网。 ## 处方业务记录 `/t/prescription` 访问:`http://:16001/t/prescription` 每次写 api-log 后,若为处方相关业务,会追加一行到 `{api-logs}/prescription-audit.jsonl`: | bizType | 含义 | |---------|------| | `pdf` | 28211 处方 PDF 上传 | | `recipe` | 28212 `uploadRecipeIndicators` 在线处方 | | `verification` | 28212 `uploadRecipeVerificationIndicators` 处方核销 | 页面三栏切换查看记录,支持 **详情**(api-log 三段 JSON)与 **重试**(出站块 2 回放政务云,不更新 transit/xk-api 状态)。 历史日志可点 **重建索引**(`POST /t/api/prescription/rebuild`)全量扫描 `*_file_*.log` 与 `*_supervise_*.log`。 `/t/api/logs/detail` 与 `/t/api/test/send` 已支持 supervise 日志 `rel` 路径。 本地联调 mock 上游时,在 `.env` 中设置: ```env SUPERVISE_TARGET_URL=http://127.0.0.1:18001/api/ FILE_TARGET_URL=http://127.0.0.1:18001/api/u ``` 未配置 `SUPERVISE_TARGET_URL` / `FILE_TARGET_URL` 时,代码默认指向政务云 `59.202.52.129`(与 `.env.example` 一致)。 ## 应用日志 每条转发写入 `app-YYYY-MM-DD.log`(同时输出控制台),格式: ```text 2026-05-19T22:00:01+08:00 | 192.168.1.20 | supervise | http=200 | 120ms | https://59.202.52.129:28212/... 2026-05-19T22:00:02+08:00 | 192.168.1.20 | supervise | dry_run | target=https://... | body_len=1024 | 3ms ``` 摘要行不含完整 body;明细见下方 **API 请求日志**。 ## API 请求日志(api-logs) 每次 HTTP 请求(含 `/health`、监管、文件上传、403 本地拒绝、dry-run)在**程序同级目录**写入独立文件: ```text {程序目录}/api-logs/2026-05-27 14/20260527143015_supervise_a1b2c3d4.log ``` 单文件固定 **三段**(完整原文,不脱敏): ```text === META === Time / ClientIP / Path / Kind(supervise|file) / DurationMs / Status === 1. 入站请求(transit → forward)=== URL、原始 Headers(含 X-Forward-Token)、Body(multipart/PDF 为 body_len + body_base64) === 2. 出站转发(forward → 政务云)=== TargetURL、白名单过滤后 Headers、Body(与入站相同字节) === 3. 回复(政务云 → forward → transit)=== UpstreamStatus、UpstreamHeaders、Body;JSON 响应额外输出 BodyJSON(缩进格式化) ``` 排查超时:若 **块1+块2** 完整、**块3** 为空且 `DurationMs` 很大,说明已发往政务云但上游无响应(常见为 28211 网络不通)。 **注意**:日志含 `X-Authorization`、`X-Forward-Token`、业务密文及 PDF,已加入 `.gitignore`,请勿提交仓库或暴露到外网。