在星座社区与社交 App 里接入星座运势查询:数据表、刷新时序与分享图
约 14 分钟 进阶星座社区社交App集成场景数据表设计
# 在星座社区与社交 App 里接入星座运势查询:数据表、刷新时序与分享图
接口 872(两接入点)· 免费服务 · POST / GET · 返回 JSON · 适用:产品经理、社区与社交产品全栈 · 阅读时间:约 8 分钟 · 最后实测核对:2026-09-18
## 核心要点
- 星座运势查询接口(apiCode=872)提供多周期运势与生日识别,星座配对接口提供双人互动,两个接入点配合就能覆盖社区产品的日常内容供给。
- 数据每天 1 点、7 点、17 点更新,落库加缓存的组合能把用户请求全部挡在本地,回源只剩定时任务。
- 配对结果是娱乐性质,接口文档标注「数据结果仅供娱乐参考」,结果页与分享图保留这句说明。
## 一个星座社区需要哪些数据
社区产品的星座板块通常有三类页面:今日运势卡片、配对互动页、周月趋势页。
第一类每天开一次,用户看的是综合指数、吉色、幸运数字和一段简评。第二类是社交入口,两人各填星座说一句「配不配」。第三类面向愿意停留更久的用户,看的是本周提醒与本月优势弱势。
这三类页面用到的数据都来自同一个接口的两个接入点。接入点 1 提供多周期运势,接入点 2 提供配对结果。
## 能力盘点
| 能力 | 接入点 | 常用场景 |
|---|---|---|
| 多周期运势 | 872-1 | 今日签、周趋势、月总览 |
| 生日识别星座 | 872-1 的 `date` 参数 | 用户填生日即出运势,少一个下拉控件 |
| 星座配对 | 872-2 | 配对测试、破冰互动、分享图 |
| 免费与定时更新 | 两个接入点 | 低成本的内容底座 |
## 数据表设计
落库的目标是让用户请求不打到接口上。两张表配合一层缓存就够用。
```sql
-- 运势:一个星座一个周期一天一行
CREATE TABLE horoscope_cache (
star VARCHAR(16) NOT NULL, -- baiyang / jinniu ...
period VARCHAR(12) NOT NULL, -- day / tomorrow / week / month / year
bucket VARCHAR(16) NOT NULL, -- 20260918 / 202609 / 2026 / week:20260913
payload JSON NOT NULL,
fetched_at DATETIME NOT NULL,
PRIMARY KEY (star, period, bucket)
);
-- 配对:两个星座一组,顺序无关
CREATE TABLE match_cache (
star_a VARCHAR(16) NOT NULL, -- 排序后的较小值
star_b VARCHAR(16) NOT NULL, -- 排序后的较大值
payload JSON NOT NULL,
fetched_at DATETIME NOT NULL,
PRIMARY KEY (star_a, star_b)
);
```
`bucket` 按周期各自的粒度取值。实测 `day` 的 `time` 是 `20260918`、`week` 是 `20260913-20260920`、`month` 是 `202609`、`year` 是 `2026`,落库时按对应粒度切分,避免周月年的数据跟着每天刷新。
配对表的两个星座列建议写入前排序。2026-09-18 实测 `tianxie` 配 `shuiping` 与 `shuiping` 配 `tianxie` 的 `match` 都是 `50分`、`proportion` 都是 `58:42`,性别也不影响结果,键收敛后缓存命中率更高。
## 刷新时序
```
01:05 / 07:05 / 17:05 定时任务触发
→ 12 个星座各取一次全周期运势(带齐 needX 开关)
→ 按周期拆分写入 horoscope_cache
→ 同步刷新 Redis,TTL 对齐下一个更新窗口
用户请求
→ 先读 Redis
→ 未命中读 horoscope_cache
→ 仍未命中才回源,并写入两层
```
一轮预热是 12 次调用,对比基础版每日 100 次的额度留有余量。预热任务本身建议加令牌桶,把并发控制在档位允许的 QPS 内。
## 代码
Python(取数并落库):
```python
import json
import requests
STARS = ["baiyang", "jinniu", "shuangzi", "juxie", "shizi", "chunv",
"tiancheng", "tianxie", "sheshou", "mojie", "shuiping", "shuangyu"]
BASE = "https://route.showapi.com"
def fetch_one_star(star: str, appkey: str) -> dict:
"""取一个星座的五个周期,返回 {period: payload}。"""
data = requests.get(f"{BASE}/872-1", params={
"appKey": appkey, "star": star,
"needTomorrow": "1", "needWeek": "1", "needMonth": "1", "needYear": "1",
}, timeout=15).json()
if data.get("showapi_res_code") != 0:
raise RuntimeError(f"系统错误: {data.get('showapi_res_error')}")
body = data["showapi_res_body"]
if str(body.get("ret_code")) != "0":
raise RuntimeError(f"业务错误: {body.get('remark')}")
return {p: body[p] for p in ("day", "tomorrow", "week", "month", "year") if body.get(p)}
def fetch_match(star_a: str, star_b: str, appkey: str) -> dict:
"""配对取数;两个星座先排序,与顺序无关。"""
a, b = sorted([star_a, star_b])
data = requests.get(f"{BASE}/872-2", params={
"appKey": appkey, "star1": a, "gender1": "1",
"star2": b, "gender2": "0",
}, timeout=5).json()
if data.get("showapi_res_code") != 0:
raise RuntimeError(f"系统错误: {data.get('showapi_res_error')}")
body = data["showapi_res_body"]
if str(body.get("ret_code")) != "0":
raise RuntimeError(f"业务错误: {body.get('remark')}")
return body
if __name__ == "__main__":
for star in STARS:
periods = fetch_one_star(star, "YOUR_APPKEY")
print(star, {p: periods[p]["time"] for p in periods})
```
cURL 与 Node.js:
```bash
curl -G "https://route.showapi.com/872-2" \
--data-urlencode "appKey=YOUR_APPKEY" \
--data-urlencode "star1=jinniu" \
--data-urlencode "gender1=1" \
--data-urlencode "star2=baiyang" \
--data-urlencode "gender2=0" \
--max-time 5
```
```javascript
// 配对结果先排序再查,与用户填写顺序无关
const [a, b] = ["shuiping", "tianxie"].sort();
const url = new URL("https://route.showapi.com/872-2");
Object.entries({ appKey: "YOUR_APPKEY", star1: a, gender1: "1", star2: b, gender2: "0" })
.forEach(([k, v]) => url.searchParams.set(k, v));
const data = await (await fetch(url, { signal: AbortSignal.timeout(5000) })).json();
if (String(data.showapi_res_body?.ret_code) !== "0") throw new Error("业务失败");
console.log(data.showapi_res_body.match, data.showapi_res_body.proportion);
```
## 前端展示
今日签卡片取 `day` 的 `summary_star`、`lucky_color`、`lucky_num`、`general_txt` 四项就够。`summary_star` 是数字,直接映射星级;`lucky_color` 返回的是中文颜色名(实测出现过 `金色`、`天蓝`、`海蓝色`),需要一层色值映射表。
配对页取 `match` 做大字,`proportion` 做双方比重条,`review` 做标题。`match` 是带「分」字的字符串,渲染前去掉单位。
分享图的配色可以直接跟随 `lucky_color`,不必在代码里写死色板。
## 用户身份与配对结果的对应
返回体里的 `star1`、`star2` 与 `gender1`、`gender2` 不保证按同一位置对应。2026-09-18 实测传入 `star1=baiyang`(`gender1=0`)、`star2=jinniu`(`gender2=1`)时,返回 `star1=jinniu`、`star2=baiyang`,而 `gender1` 仍是「女」。
落库与渲染时按两组字段分别取:星座看 `grxz1` / `grxz2`,性别看 `gender1` / `gender2`,不要按位置拼成同一个人的档案。
配对结果为娱乐性质,接口文档明确标注「数据结果仅供娱乐参考」。结果页、分享图与推送文案都保留这句说明。
## 生日信息的处理
`date` 参数只吃四位 `MMdd`,接口文档也没有要求年份。前端收集生日时只取月和日,用户信息里少存一项,映射区间以接口返回的 `star` 为准。
接口实际使用的星座边界与常见对照表有差异,接口自己判出来的边界见 date 参数那篇的实测表。
## FAQ
**Q1:每天需要调多少次接口?**
预热一轮是 12 个星座各一次,共 12 次调用。用户请求全部走缓存,不额外消耗额度。
**Q2:配对能做成付费功能吗?**
可以,付费点建议放在更长的解读文案、历史对比或去广告上。结果页保留「仅供娱乐参考」的说明。
**Q3:多周期要一次取还是分开取?**
一次带齐四个 `needX` 开关取,比调五次省调用次数。取回后按周期拆开写缓存。
**Q4:用户填的生日要不要存年份?**
不需要。接口只接受 `MMdd`,不收年份也就少一项需要保管的用户信息。
**Q5:接口不可用时页面怎么处理?**
展示兜底文案并保留上一次的缓存数据,不要白屏。缓存策略那篇有 TTL 与预热的具体写法。
**Q6:星座和性别的字段能拼成同一个人的展示元素吗?**
不能。实测返回的 `star1` / `star2` 顺序与 `gender1` / `gender2` 的位置不保证对应,两组字段分别取值。
## 下一步阅读
- [星座运势查询的缓存怎么设](https://www.showapi.com/guides/horoscope-cache-872)
- [星座类产品的三条内容线](https://www.showapi.com/guides/horoscope-product-plan-872)
- [星座运势查询的 date 参数:按生日识别星座](https://www.showapi.com/guides/horoscope-by-date-872)
- [星座配对:用四个必填参数算出配对指数](https://www.showapi.com/guides/constellation-match-quickstart-872)
- **本系列共 13 篇**:查看[星座运势 API 指南总目录](https://www.showapi.com/guides/horoscope-guides-872)





