# gold_api **Repository Path**: anglers/gold_api ## Basic Information - **Project Name**: gold_api - **Description**: 金价api - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-12 - **Last Updated**: 2026-09-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # gold_api 贵金属行情聚合接口服务(Go + Gin)。对接**天眼、融通金、金投网**三个上游数据源, 对外提供统一的 HTTP JSON 接口,价格每 **3 秒**刷新一次。 --- ## 一、环境与启动 ### 环境要求 | 项 | 要求 | |---|---| | Go | 1.25 或更高(本地运行需要;Docker 方式无需本机装 Go) | | 依赖 | 仅 [gin](https://github.com/gin-gonic/gin),`go mod tidy` 会自动拉取 | | 网络 | 需能访问三个上游接口(见「三、数据来源」) | | 端口 | 默认 `8080`,可用环境变量 `PORT` 修改 | | Docker | 可选。容器部署需 Docker 20.10+ | ### 方式一:本地运行 先拉依赖: ```bash go mod tidy ``` #### Windows(PowerShell / CMD) ```powershell # 直接运行(开发常用,Ctrl+C 即退出) go run . # 打包成 .exe 后运行(产物在当前目录的 gold_api.exe) go build -o gold_api.exe . .\gold_api.exe ``` > 提示:Windows 上若用 Git Bash 运行 `./gold_api.exe`,`Ctrl+C` 偶尔会先被 bash 吃掉, > 需要在 PowerShell / CMD 里跑才能保证优雅退出。 #### Linux / macOS(bash) ```bash # 直接运行 go run . # 打包后运行(产物是当前目录的 gold_api,无扩展名) go build -o gold_api . ./gold_api ``` 跨平台交叉编译示例(在 Windows 上编出 Linux 二进制,Docker 镜像里用的就是这个开关): ```bash CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o gold_api . ``` - `CGO_ENABLED=0` 关闭 cgo,得到纯静态二进制,alpine 镜像里无需 glibc。 - `-trimpath -ldflags="-s -w"` 去掉绝对路径与符号表,二进制更小(实测 ~8.5MB)。 ### 方式二:Docker 运行(服务器部署推荐) 仓库内置 `Dockerfile`(多阶段构建,最终镜像基于 Alpine、非 root 运行)与一键脚本 `deploy.sh`。 #### 一条命令部署(服务器上无需 clone) 从 gitee 取最新脚本直接执行 —— 下载代码 → 构建镜像 → 重建容器 → 等健康检查通过: ```bash curl -fsSL https://raw.giteeusercontent.com/anglers/gold_api/raw/master/deploy.sh | bash -s deploy ``` 子命令与 `./deploy.sh` 完全一致,把末尾的 `deploy` 换成 `status` / `logs` / `restart` 等即可: ```bash curl -fsSL https://raw.giteeusercontent.com/anglers/gold_api/raw/master/deploy.sh | bash -s status ``` 代码下载到**当前目录**下的 `gold_api_src/`(先 `cd` 到专门的部署目录再执行更清爽), 跑完后同一份脚本就在 `gold_api_src/deploy.sh`,之后直接用磁盘上这份即可,不必再 curl: ```bash cd gold_api_src && ./deploy.sh logs ``` #### 已 clone 到服务器(用仓库里的脚本) ```bash # 构建镜像 + 重启容器,并等待健康检查通过 ./deploy.sh # 脚本若没有执行权限(Windows 下 clone 常见),先加权限或用 bash 调用 chmod +x deploy.sh # 或者: bash deploy.sh ``` `deploy.sh` 子命令: | 命令 | 说明 | |---|---| | `./deploy.sh`(默认,等价 `up`) | 下载代码 + 构建镜像 + 重启容器 + 等健康检查通过 | | `./deploy.sh download` | 只下载最新代码到 `src/`(不构建、不重启) | | `./deploy.sh build` | 只构建镜像(不重启容器) | | `./deploy.sh start` | 只启动容器(不下载、不构建) | | `./deploy.sh stop` | 停止并删除容器 | | `./deploy.sh restart` | 重启容器 | | `./deploy.sh logs` | 跟踪容器日志 | | `./deploy.sh status` | 查看容器状态与 `/api/health` 输出 | | `./deploy.sh sh` | 进容器起 shell(排查用) | | `./deploy.sh clean` | 停止容器并删除镜像 | 脚本默认从 gitee 仓库的 master 分支 zip 归档(`https://gitee.com/anglers/gold_api/repository/archive/master.zip`)下载最新代码再构建,**无需在服务器上配置 git 凭据**。下载会解压到 `SRC_DIR`(默认值见下表),下次再跑自动覆盖;若 `SRC_DIR` 已存在且里面没有 `go.mod`(说明不是本项目,例如撞上了你自己的 `~/src`),脚本会中止而不是删除它。 可用环境变量覆盖默认配置: | 变量 | 默认值 | 说明 | |---|---|---| | `HOST_PORT` | `8080` | 映射到宿主机的端口 | | `IMAGE` | `gold_api:latest` | 镜像名 | | `CONTAINER` | `gold_api` | 容器名 | | `GOPROXY` | `https://goproxy.cn,direct` | 构建时的 Go 模块代理 | | `SRC_DIR` | `<脚本目录>/src` | 代码解压目录 | | `ZIP_URL` | `https://gitee.com/anglers/gold_api/repository/archive/master.zip` | 下载地址,可换成别的分支或仓库 | | `SKIP_DOWNLOAD` | `0` | 设为 `1` 时跳过下载,用 `SRC_DIR` 里已有的代码构建 | 构造 `/api/health` HTTP 200 即视为启动成功;容器若已存在会先 `docker rm -f` 再重建。 ### 验证是否启动成功 启动后会先拉一次上游行情(约 1~2 秒),随后即可访问: ```bash curl http://127.0.0.1:8080/api/price curl http://127.0.0.1:8080/api/price/hj_ty curl http://127.0.0.1:8080/api/health ``` ### 环境变量 | 变量 | 默认值 | 说明 | |---|---|---| | `PORT` | `8080` | 监听端口(容器内固定 8080,对外端口用 `HOST_PORT` 调) | | `GIN_MODE` | `release` | Gin 运行模式,调试时设为 `debug` | ## 二、接口文档 | 方法 | 路径 | 说明 | |---|---|---| | GET | `/api/price` | 全部品种价格(共 18 条) | | GET | `/api/price/:tvalue` | 单个品种,如 `/api/price/hj_ty` | | GET | `/api/health` | 健康检查 | ### GET /api/price **直接返回数组**(无 `code` / `msg` 外层包装): ```json [ { "name": "黄金(天眼)", "tvalue": "hj_ty", "buyPrice": 940.54, "sellPrice": 940.54, "updateTime": "2026-09-13 12:10:05" }, { "name": "黄金(融通金)", "tvalue": "hj_rtj", "buyPrice": 939, "sellPrice": 936, "updateTime": "2026-09-13 12:10:06" } ] ``` | 字段 | 类型 | 说明 | |---|---|---| | `name` | string | 品种名称 | | `tvalue` | string | 业务标识,前端按此取值 | | `buyPrice` | number \| null | 买入价,保留 2 位小数;无数据为 `null` | | `sellPrice` | number \| null | 卖出价,保留 2 位小数;无数据为 `null` | | `updateTime` | string | 行情时间,格式 `2006-01-02 15:04:05`(东八区)。上游未给时间时为空串 | **品种清单**(返回顺序即下表顺序,共 18 条): | name | tvalue | 上游代码 | 单位 | |---|---|---|---| | 黄金(天眼) | `hj_ty` | `AUS` | 元/克 | | 国际黄金(天眼) | `hj_ty_intl` | `XAUUSD` | 美元/盎司 | | 白银(天眼) | `by_ty` | `AGS` | 元/克 | | 国际白银(天眼) | `by_ty_intl` | `XAGUSD` | 美元/盎司 | | 铂金(天眼) | `bj_ty` | `PTS` | 元/克 | | 国际铂金(天眼) | `bj_ty_intl` | `XPTUSD` | 美元/盎司 | | 黄金(暗) | `hj_a` | `AU24` | 元/克 | | 国际黄金(暗) | `hj_a_intl` | `XAU24` | 美元/盎司 | | 白银(暗) | `by_a` | `AG24` | 元/克 | | 国际白银(暗) | `by_a_intl` | `XAG24` | 美元/盎司 | | 铂金(暗) | `bj_a` | `PT24` | 元/克 | | 国际铂金(暗) | `bj_a_intl` | `XPT24` | 美元/盎司 | | 黄金(融通金) | `hj_rtj` | — | 元/克 | | 白银(融通金) | `by_rtj` | — | 元/克 | | 铂金(融通金) | `bj_rtj` | — | 元/克 | | 国际黄金(金投网) | `hj_jtw` | `JO_92233` | 美元/盎司 | | 国际白银(金投网) | `by_jtw` | `JO_92232` | 美元/盎司 | | 美元人民币(金投网) | `usdcny_jtw` | `JO_57285` | 汇率 | > **单位不统一**:国内品种为元/克,国际品种为美元/盎司,接口按各上游原值输出,不做换算。 > `usdcny_jtw` 是美元对人民币汇率(非贵金属),可用于换算:`金价(美元/盎司) × 美元人民币 ÷ 31.1035 ≈ 元/克`。 **买卖价取值规则**(按数据源区分): | 数据源 | 规则 | 买卖价是否相同 | |---|---|---| | 天眼 / 暗 | 均取上游 `ask` | 相同 | | 融通金 | `sellPrice` 取每对第一个值,`buyPrice` 取第二个 | 通常不同 | | 金投网 | 均取上游 `q63` | 相同 | **价格精度**:所有价格输出前统一四舍五入到 **2 位小数**,前端可直接 `toFixed(2)` 渲染。 **异常情况**: - 单个品种取不到价格 → 该条目的 `buyPrice` / `sellPrice` 为 `null`,不影响其他品种。 - 所有数据源都取不到价格且均有报错 → 返回 `503`: ```json { "code": 503, "msg": "行情数据暂不可用", "errors": "天眼: ...; 融通金: ...; 金投网: ..." } ``` **缓存协商**:响应带 `ETag` / `Last-Modified` 与 `Cache-Control: public, max-age=1`。 客户端轮询时带 `If-None-Match`,数据未变化时返回 `304`(响应体 0 字节)。 ### GET /api/price/:tvalue 按业务标识查询单个品种,返回单个对象: ```bash curl http://127.0.0.1:8080/api/price/hj_ty ``` ```json { "name": "黄金(天眼)", "tvalue": "hj_ty", "buyPrice": 940.54, "sellPrice": 940.54, "updateTime": "2026-09-13 12:10:05" } ``` 不存在时返回 `404`: ```json { "code": 404, "msg": "未找到该品种: nope" } ``` ### GET /api/health ```json { "code": 0, "msg": "ok", "ready": true, "count": 44, "products": 18, "rtj_count": 3, "jtw_count": 3, "interval": 3, "server_time": 1789272615000, "updated_at": "2026-09-13T12:10:15+08:00", "age_ms": 1200, "list_updatetime": 1789272613000, "token_expires_at": "2026-09-13T14:05:35+08:00", "token_ttl_sec": 6896 } ``` | 字段 | 说明 | |---|---| | `ready` | 是否已有可用行情(以天眼为准) | | `count` | 天眼缓存的 symbol 数量(44) | | `products` | `/api/price` 输出的品种数量(18) | | `rtj_count` / `jtw_count` | 融通金 / 金投网缓存的品种数量 | | `age_ms` | 距上次成功刷新的毫秒数,正常应 `< 3000` | | `token_ttl_sec` | 天眼 access_token 剩余有效秒数 | 某个数据源出错时会额外出现对应的错误字段(无错误则整个字段不出现): ```json { "jtw_count": 0, "jtw_last_error": "请求金投网失败: dial tcp: connect: connection refused" } ``` 三个数据源的错误分别记录,任一源故障都不影响其余两源的数据展示。 --- ## 三、数据来源 | 数据源 | 上游地址 | 鉴权 | 品种数 | |---|---|---|---| | 天眼 | `fusiongoldnew.baoangold.com` | OAuth2 三步调用链(取 code → 换 token → 拉行情) | 12(天眼 6 + 暗盘 6) | | 融通金 | `bl.rtj029.com` | 无 | 3 | | 金投网 | `api.jijinhao.com` | 无 | 3 | 各上游的报文形态差异较大,解析细节都写在各数据源自己的代码里(见「四、项目结构」): - **天眼**:JSON POST。同时提供「天眼」与「暗盘」两套体系,报价单位有元/克与美元/盎司两种。 - **融通金**:一行逗号分隔的纯文本;每对价格顺序为「卖出价, 买入价」。 - **金投网**:JS 赋值语句(`var quot_str = [...]`,非纯 JSON);数值均为字符串。 **tvalue 命名规则**:天眼国内 `_ty`、天眼国际 `_ty_intl`、暗盘国内 `_a`、暗盘国际 `_a_intl`、 融通金 `_rtj`、金投网 `_jtw`。 **刷新与容错**:后台协程每 3 秒并行拉取三个上游,结果映射成业务品种后写入内存快照, 接口只读内存(不实时访问上游,避免被高并发放大请求量)。任一数据源失败时, 用上一次的好数据补位,做到「单源故障只损失该源」。 --- ## 四、项目结构 代码分为「**公共框架**」与「**数据源实现**」两层:每个上游数据源自成一个文件, 公共框架不认识任何具体数据源,只依赖 `source.go` 里的 `Source` 接口。 **新增数据源只需新增一个 `source_xxx.go` 并在 `newSources()` 登记一行。** ``` gold_api/ │ ── 公共框架 ──────────────────────────────────────────────── ├── main.go 程序入口:组装数据源、启动刷新与 HTTP 服务、优雅退出 ├── config.go 全局可调参数(刷新间隔、请求超时) ├── source.go 数据源契约:Source 接口、统一的 Price 结构、 │ 数据源注册表 newSources()、公共工具 ├── product.go 业务品种:Product / ProductPrice 结构、品种映射 ├── store.go 内存快照 + 3 秒并行刷新调度 ├── handler.go HTTP 路由、缓存协商、写预序列化响应体 │ ── 数据源实现(每个上游一个文件,自包含)───────────────────── ├── source_fusion.go ① 天眼:OAuth 三步链、44 个订阅 symbol、12 个品种(含暗盘 6 个) ├── source_rtj.go ② 融通金:逗号分隔文本解析、3 个品种 ├── source_jtw.go ③ 金投网:JSONP 前缀剥离、字符串数值解析、3 个品种 │ ── 部署 ──────────────────────────────────────────────────── ├── Dockerfile 多阶段构建:Go 编译 + Alpine 运行 ├── .dockerignore 构建上下文排除项 ├── deploy.sh 一键构建 / 启动 / 查看容器 │ ── 其它 ──────────────────────────────────────────────────── ├── go.mod / go.sum 依赖定义(仅 gin) └── README.md ```