今日油价:公众号与小程序怎么加油价查询?省份选择与卡片展示
约 14 分钟 进阶今日油价小程序公众号云函数
# 今日油价:公众号与小程序怎么加油价查询?省份选择与卡片展示
接口/接入点:今日油价(apiCode=138)· 查询油价(138-46) · 免费 · 适用人群:公众号运营、小程序开发者 · 阅读时间:约 6 分钟 · 最后实测核对:2026-09-18
公众号菜单点进去选省份看油价,是小程序里最容易做起来的一个功能。链路只有三段:前端选省、云函数代理、卡片渲染。省份列表直接从接口返回生成,省掉自己维护枚举表的功夫。
## 核心要点
- 后端一次不传 `prov` 的调用就能取回 31 个省级行政区的名称与价格,2026-09-18 实测返回 31 条、`showapi_fee_num=1`。
- AppKey 放云函数环境变量,小程序前端只调云函数,不接触密钥。
- 省份名按精确匹配处理,下拉列表里的值与接口返回的 `prov` 保持一致即可。
## 为什么适合放在公众号与小程序里
车主类公众号、养车小程序天然有查油价的需求:用户主动查、顺手就能分享,比推一篇图文更容易形成使用习惯。今日油价(apiCode=138)覆盖 31 个省级行政区,返回结构化 JSON,注册后即可调用,做起步功能的成本很低。
## 云函数:代理与取数
小程序云函数持有 AppKey,一次全量调用填充缓存,之后按省从缓存里取。
```javascript
// 云函数入口(Node.js)
const cloud = require("wx-server-sdk");
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
const API = "https://route.showapi.com/138-46";
let snapshot = { prices: null, ct: null, expireAt: 0 };
async function refreshSnapshot(ttlMs = 3600_000) {
if (snapshot.prices && Date.now() < snapshot.expireAt) return snapshot.prices;
const resp = await fetch(API, {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
// 不传 prov 取全量;AppKey 从云函数环境变量注入
body: new URLSearchParams({ appKey: process.env.OIL_APPKEY }),
signal: AbortSignal.timeout(5000),
});
const data = await resp.json();
if (data.showapi_res_code !== 0) throw new Error(data.showapi_res_error);
const body = data.showapi_res_body;
if (body.ret_code !== 0) throw new Error(body.remark);
snapshot = {
prices: Object.fromEntries(body.list.map((x) => [x.prov, x])),
ct: body.list[0].ct,
expireAt: Date.now() + ttlMs,
};
return snapshot.prices;
}
const pick = (raw) => (raw && Number(raw) > 0 ? Number(Number(raw).toFixed(2)) : null);
exports.main = async (event) => {
const action = event.action || "price";
if (action === "provinces") {
const prices = await refreshSnapshot();
return { provinces: Object.keys(prices).sort() };
}
const prov = event.prov || "北京";
const prices = await refreshSnapshot();
const item = prices[prov];
if (!item) return { ok: false, msg: "请选择标准省名" };
return {
ok: true,
prov: item.prov,
gas: { p89: pick(item.p89), p92: pick(item.p92), p95: pick(item.p95), p98: pick(item.p98) },
diesel: pick(item.p0),
ct: item.ct,
};
};
```
Python 侧的后端实现(自建服务器时用):
```python
import time
import requests
from flask import Flask, jsonify, request
app = Flask(__name__)
API = "https://route.showapi.com/138-46"
_cache = {"prices": None, "expire_at": 0}
def snapshot(ttl=3600):
if _cache["prices"] and time.time() < _cache["expire_at"]:
return _cache["prices"]
resp = requests.post(API, data={"appKey": "YOUR_APPKEY"}, timeout=5).json()
body = resp["showapi_res_body"]
if body.get("ret_code") != 0:
raise RuntimeError(body.get("remark"))
_cache["prices"] = {x["prov"]: x for x in body["list"]}
_cache["expire_at"] = time.time() + ttl
return _cache["prices"]
@app.route("/api/oil/price")
def price():
item = snapshot().get(request.args.get("prov", "北京"))
if not item:
return jsonify({"ok": False, "msg": "请选择标准省名"}), 404
return jsonify({"ok": True, "prov": item["prov"], "p92": item["p92"],
"p95": item["p95"], "diesel": item["p0"], "ct": item["ct"]})
```
cURL(本地验证云函数或后端接口):
```bash
curl -X POST "https://route.showapi.com/138-46?appKey=YOUR_APPKEY" \
-H "content-type: application/x-www-form-urlencoded" -d "prov=%E5%B9%BF%E8%A5%BF"
```
## 小程序端:picker 与卡片
```javascript
Page({
data: { provinces: [], idx: 0, price: null },
async onLoad() {
// 省份选项来自云函数,不用在前端写死
const res = await wx.cloud.callFunction({ name: "oilPrice", data: { action: "provinces" } });
this.setData({ provinces: res.result.provinces });
this.load("北京");
},
async load(prov) {
const res = await wx.cloud.callFunction({ name: "oilPrice", data: { prov } });
this.setData({ price: res.result });
},
onProv(e) {
const prov = this.data.provinces[e.detail.value];
this.setData({ idx: e.detail.value });
this.load(prov);
},
});
```
```html
<picker mode="selector" range="{{provinces}}" value="{{idx}}" bindchange="onProv">
<view>当前省份:{{provinces[idx]}}</view>
</picker>
<view class="oil-card" wx:if="{{price.ok}}">
<view>{{price.prov}} 今日油价</view>
<view>92号:{{price.gas.p92 === null ? '—' : price.gas.p92}} 元/升</view>
<view>95号:{{price.gas.p95 === null ? '—' : price.gas.p95}} 元/升</view>
<view>0号柴油:{{price.diesel === null ? '—' : price.diesel}} 元/升</view>
<view class="ts">数据时间 {{price.ct}}</view>
</view>
```
公众号侧的做法更简单:在自定义菜单里挂一个 H5 页面地址,页面调你的后端接口,省份选择用一个 `<select>` 即可。菜单跳转用的链接需要走你自己的域名,AppKey 依然留在后端。
## 适用条件与注意事项
- 省份名用接口返回的 `prov` 值,不要出现带「省」「市」后缀的写法。2026-09-18 实测 `prov=北京市` 返回 `ret_code=-1`。
- `p89` 有 7 个省份返回 `"0.00"`(2026-09-18 实测),在后端统一转成 `null`,前端显示「—」。
- 云函数冷启动后缓存会重建,属正常现象;回源次数按实例数计,量级仍然很低。
- AppKey 放云函数环境变量,不进代码仓库。
- 小程序需要在小程序后台配置合法域名,H5 页面需要在公众号后台配置业务域名。
## FAQ
**Q1:AppKey 能直接写在小程序里吗?**
不能。放云函数环境变量,或放在你自己的后端服务上,由服务端代理请求今日油价。
**Q2:省份下拉的 31 个选项要自己写吗?**
不用。2026-09-18 实测不传 `prov` 的调用会返回 31 个省级行政区的 `prov` 值,直接作为选项即可。
**Q3:免费档位够公众号用吗?**
注册默认是 L0(100 次/天,1 QPS)。后端按全量快照缓存后,回源次数按小时计,通常够用。需要更高额度时可按积分规则升级档位,见 https://www.showapi.com/free-api
**Q4:想让用户看到涨跌,怎么加?**
另外调用查询行情(138-49),它返回 8 个标号的涨跌对象与调价日,`prov` 必填。用法见《今日油价涨跌幅怎么查?查询行情接入点的调价日与涨跌率用法》。
**Q5:卡片时间显示哪个字段?**
`ct`,并说明它是数据生成时间。
## 相关能力 / 下一步阅读
- [在车主服务 APP 里接入今日油价的完整流程](https://www.showapi.com/guides/oilprice-app-integration-138)
- [今日油价调用量规划:缓存粒度与 TTL 的确定方式](https://www.showapi.com/guides/oilprice-cache-138)
- [今日油价返回字段对照:p89/p92/p0 与 ct 的含义与空值处理](https://www.showapi.com/guides/oilprice-fields-138)
- **本系列共 13 篇**:查看[今日油价指南总目录](https://www.showapi.com/guides/oilprice-guides-138)





