定稿日期:2026-06-27
与桌面端共用后端 · Base URL 示例:https://api.proprice.kailin.com.cn/v1
关联 PRD:PRD-小程序-V1.0.md
| Header | 说明 |
|---|---|
Authorization |
Bearer {access_token},除登录/回调外必填 |
X-Client |
固定 mini_program |
X-Client-Version |
小程序版本号,如 1.0.0 |
Content-Type |
application/json |
{
"code": 0,
"message": "ok",
"data": {}
}
| code | 含义 |
|---|---|
| 0 | 成功 |
| 40001 | 参数错误 |
| 40101 | 未登录 / Token 过期 |
| 40301 | 无权限(如体验用户访问超限历史) |
| 40401 | 资源不存在 |
| 42901 | 请求过于频繁 |
| 50001 | 服务器错误 |
列表接口统一参数:
| 参数 | 类型 | 说明 |
|---|---|---|
page |
int | 页码,从 1 开始,默认 1 |
page_size |
int | 每页条数,默认 20,最大 50 |
响应 data 结构:
{
"items": [],
"total": 100,
"page": 1,
"page_size": 20,
"has_more": true
}
在 PRD §10 基础上扩展:
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 用户 ID |
phone |
string | 手机号(脱敏返回) |
pharmacy_name |
string | 药店名称 |
region |
object | { province, city, district } |
license_status |
enum | pending / approved / rejected |
license_reject_reason |
string? | 驳回原因 |
member_type |
enum | trial / paid / expired |
trial_end_at |
datetime? | 体验到期时间 |
member_expire_at |
datetime? | 会员到期时间 |
invite_code |
string | 个人邀请码 |
wx_openid |
string | 微信 openid(内部) |
wx_unionid |
string? | 微信 unionid(内部) |
created_at |
datetime | 注册时间 |
/users/me 返回的门禁摘要(与主 PRD §3.4 一致,各端共用):
| 字段 | 类型 | 说明 |
|---|---|---|
can_query_on_desktop |
bool | 执照已通过且绑平台 ≥1 时为 true |
license_status |
enum | 执照状态 |
platform_bound_count |
int | 已绑定平台数 |
gate_messages |
string[] | 待完成事项(引导完成注册/执照/绑平台) |
上述门禁仅影响桌面查价等等价能力;不用于裁剪小程序历史/采购接口返回。
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 执照记录 ID |
user_id |
string | 用户 ID |
image_url |
string | 执照图片 URL(私有存储,需签名访问) |
status |
enum | pending / approved / rejected |
reject_reason |
string? | 驳回原因 |
submitted_at |
datetime | 提交时间 |
reviewed_at |
datetime? | 审核时间 |
source_client |
enum | mini_program / windows / android |
reviewer_id |
string? | 运营审核人(Admin) |
审核通过/驳回由 Admin 操作台调用内部接口,与桌面端上传共用队列。
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | query_id |
user_id |
string | 用户 ID |
keyword |
string | 搜索关键词 |
queried_at |
datetime | 查询时间 |
source_client |
enum | windows / android / mini_program |
result_count |
int | 命中 SKU 数 |
lowest_platform |
string | 最低价平台 |
lowest_price |
decimal | 参考最低价 |
snapshot |
QuerySnapshot | 完整快照 JSON |
related_intent_ids |
string[] | 关联跳转购买 ID |
QuerySnapshot 结构:
{
"items": [
{
"platform": "药帮忙",
"platform_id": "yaobangmang",
"price": 8.50,
"spec": "0.25g×24粒",
"manufacturer": "石药集团",
"approval_number": "国药准字H13020961",
"expiry": "2027-06",
"moq": "10盒",
"stock_status": "in_stock",
"is_lowest": true,
"external_url": "https://..."
}
]
}
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | intent_id |
user_id |
string | 用户 ID |
query_id |
string? | 关联 QueryLog |
drug_name |
string | 药品名称 |
spec |
string | 规格 |
manufacturer |
string? | 厂家 |
platform |
string | 药师帮 / 药帮忙 / 1药城 |
platform_id |
string | 平台标识 |
reference_price |
decimal | 跳转时参考价 |
clicked_at |
datetime | 跳转时间 |
source_client |
enum | 产生端 |
external_url |
string? | 跳转 URL(内部存储,小程序不返回) |
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | ledger_id |
user_id |
string | 用户 ID |
intent_id |
string? | 关联 PurchaseIntent |
query_id |
string? | 关联 QueryLog |
drug_name |
string | 药品名称 |
spec |
string | 规格 |
qty |
decimal | 数量 |
unit_price |
decimal | 单价 |
total |
decimal | 总价 |
platform |
string? | 平台 |
order_note |
string? | 备注 |
status |
enum | ordered / received |
created_at |
datetime | 录入时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 订单 ID |
order_no |
string | 业务订单号 |
user_id |
string | 用户 ID |
package_type |
enum | monthly / quarterly / yearly |
amount |
decimal | 应付金额(元) |
pay_channel |
enum | wechat_mp / wechat_qr / alipay |
source_client |
enum | mini_program / windows / android |
status |
enum | pending / paid / failed / closed |
paid_at |
datetime? | 支付时间 |
member_expire_at |
datetime? | 开通后会员到期 |
created_at |
datetime | 创建时间 |
/users/me 返回配额摘要:
| 字段 | 类型 | 说明 |
|---|---|---|
daily_query_used |
int | 桌面今日已用查询次数 |
daily_query_limit |
int | 桌面每日上限(体验 20,会员 -1 表示不限) |
watchlist_used |
int | 本月已关注品种数 |
watchlist_limit |
int | 关注上限 |
小程序历史/采购参考无
history_visible_*配额字段;列表接口不对体验用户做裁剪。
POST /auth/mp/login
请求:
{
"code": "wx.login 返回的 code",
"phone_code": "getPhoneNumber 返回的 code"
}
响应 data:
{
"access_token": "eyJ...",
"expires_in": 7200,
"refresh_token": "rf_...",
"is_registered": true,
"user": { /* User 摘要,is_registered=true 时返回 */ }
}
| 场景 | 行为 |
|---|---|
| 手机号已注册 | 绑定/更新 wx_openid,返回 token + user |
| 手机号未注册 | is_registered: false,前端跳转 mp-register |
POST /auth/register
{
"invite_code": "PX8K2M",
"pharmacy_name": "康宁大药房",
"region": {
"province": "广东省",
"city": "广州市",
"district": "天河区"
}
}
需已登录(微信授权 + 手机号已绑定)。成功后创建 User,license_status: null(未上传),返回更新后的 user。
POST /auth/refresh
{ "refresh_token": "rf_..." }
POST /auth/logout
清除服务端 session(可选);小程序端清除本地 token。
GET /users/me
响应 data:
{
"user": { /* User */ },
"gate": { /* UserGateStatus */ },
"quota": { /* MembershipQuota */ },
"platform_bindings": [
{
"platform_id": "yaobangmang",
"platform_name": "药帮忙",
"status": "bound",
"last_validated_at": "2026-06-27T10:00:00Z"
}
]
}
不返回平台账号密码。
GET /users/me/dashboard
响应 data:
{
"today_query_count": 8,
"today_purchase_intent_count": 3,
"total_query_count": 156,
"recent_queries": [ /* QueryLog 摘要,最多 3 条 */ ]
}
GET /licenses/current
响应 data:
{
"status": "pending",
"reject_reason": null,
"submitted_at": "2026-06-27T15:30:00Z",
"reviewed_at": null,
"image_url": "https://...signed...",
"source_client": "mini_program"
}
| status | 说明 |
|---|---|
null / 404 |
未上传,前端跳转 mp-license-upload |
pending |
审核中 |
approved |
已通过 |
rejected |
已驳回,含 reject_reason |
POST /licenses/upload
Content-Type: multipart/form-data
| 字段 | 类型 | 说明 |
|---|---|---|
file |
file | JPG/PNG,≤ 5MB |
响应 data:
{
"id": "lic_abc",
"status": "pending",
"submitted_at": "2026-06-27T15:30:00Z"
}
行为:
status → pendingUser.license_status → pending40903)GET /licenses/current/image-url
返回短期有效的 signed URL,供小程序预览已提交执照。
与桌面端共用 PlatformBinding 实体;任一端绑定/解绑,各端同步。
GET /platform-bindings
响应 data.items[]:
{
"platform_id": "yaobangmang",
"platform_name": "药帮忙",
"status": "bound",
"account_masked": "138****5678",
"last_validated_at": "2026-06-27T10:00:00Z"
}
未绑定的平台也返回,status: "unbound"。
POST /platform-bindings
{
"platform_id": "yaobangmang",
"account": "user@example.com",
"password": "******",
"source_client": "mini_program"
}
密码加密传输,服务端加密存储;响应不含明文密码。
DELETE /platform-bindings/{platform_id}
至少保留 0 个绑定;若解绑后导致 can_query_on_desktop=false,与主 PRD 一致。
POST /platform-bindings/{platform_id}/validate
触发凭证有效性校验,更新 last_validated_at。
GET /queries
| 参数 | 类型 | 说明 |
|---|---|---|
keyword |
string? | 关键词搜索 |
days |
int? | 可选时间筛选:7 / 30 / 90(用户主动筛选,非会员限制) |
source_client |
string? | 来源端筛选 |
响应 data.items[] 摘要字段:
{
"id": "q_abc123",
"keyword": "阿莫西林胶囊",
"queried_at": "2026-06-27T14:32:00Z",
"source_client": "windows",
"result_count": 3,
"lowest_platform": "药帮忙",
"lowest_price": 8.50
}
不做会员级裁剪:体验用户与付费会员返回相同全量数据(分页仍按 page / page_size)。
GET /queries/{id}
响应 data:完整 QueryLog + snapshot + related_intents[]
GET /purchase-intents
| 参数 | 类型 | 说明 |
|---|---|---|
keyword |
string? | 药品名搜索 |
platform_id |
string? | 平台筛选 |
days |
int? | 可选时间筛选(用户主动筛选,非会员限制) |
不做会员级裁剪:体验用户与付费会员返回相同全量数据。
响应 data.items[]:
{
"id": "pi_xyz789",
"query_id": "q_abc123",
"drug_name": "阿莫西林胶囊",
"spec": "0.25g×24粒",
"manufacturer": "石药集团",
"platform": "药帮忙",
"platform_id": "yaobangmang",
"reference_price": 8.50,
"clicked_at": "2026-06-27T14:35:00Z",
"source_client": "windows"
}
GET /purchase-intents/{id}
含关联 query_summary(若有 query_id)。
POST /purchase-intents
小程序 MVP 不暴露此接口;由 Windows/Android 在点击「去购买」时调用。
{
"query_id": "q_abc123",
"drug_name": "阿莫西林胶囊",
"spec": "0.25g×24粒",
"manufacturer": "石药集团",
"platform_id": "yaobangmang",
"reference_price": 8.50,
"source_client": "windows",
"external_url": "https://..."
}
POST /queries
小程序 MVP 不暴露写接口。
{
"keyword": "阿莫西林胶囊",
"source_client": "windows",
"result_count": 3,
"lowest_platform": "药帮忙",
"lowest_price": 8.50,
"snapshot": { "items": [ /* ... */ ] }
}
GET /procurement-ledgers
仅付费会员可用;体验用户返回 40301。
POST /procurement-ledgers
{
"intent_id": "pi_xyz789",
"query_id": "q_abc123",
"drug_name": "阿莫西林胶囊",
"spec": "0.25g×24粒",
"qty": 10,
"unit_price": 8.50,
"total": 85.00,
"platform": "药帮忙",
"order_note": "6月补货",
"status": "ordered"
}
PATCH /procurement-ledgers/{id}
DELETE /procurement-ledgers/{id}
GET /membership/plans
响应 data.plans[]:
{
"package_type": "yearly",
"name": "年卡会员",
"price": 899.00,
"original_price": 999.00,
"duration_days": 365,
"badge": "最划算",
"features": ["不限查询", "1999关注/月"]
}
POST /membership/orders
{
"package_type": "yearly",
"source_client": "mini_program"
}
响应 data:
{
"order_id": "ord_123",
"order_no": "202606271430001",
"amount": 899.00,
"status": "pending",
"wechat_pay": {
"timeStamp": "1719492600",
"nonceStr": "abc123",
"package": "prepay_id=wx...",
"signType": "RSA",
"paySign": "..."
}
}
小程序调用 wx.requestPayment(wechat_pay)。
GET /membership/orders/{order_no}
用于支付后轮询:
{
"order_no": "202606271430001",
"status": "paid",
"paid_at": "2026-06-27T14:30:05Z",
"member_expire_at": "2027-06-27T14:30:05Z"
}
POST /payments/wechat/notify
微信服务器回调,验签后更新 Order 状态及 User 会员字段。不对小程序暴露。
GET /invites/stats
{
"invite_code": "PX8K2M",
"invited_count": 5,
"activated_count": 3,
"total_reward_days": 21,
"records": [
{
"invitee_phone_masked": "138****1234",
"status": "activated",
"reward_days": 7,
"activated_at": "2026-06-20T10:00:00Z"
}
]
}
POST /coupons/redeem
{ "code": "TRIAL-ABCD-1234" }
成功响应:
{
"days_added": 15,
"trial_end_at": "2026-07-12T00:00:00Z",
"member_type": "trial"
}
GET /content/help
返回 FAQ 列表、合规文案、桌面下载链接。
GET /content/download
{
"windows_url": "https://proprice.kailin.com.cn/download/windows",
"official_site_url": "https://proprice.kailin.com.cn",
"mini_program_appid": "wx...",
"version": "1.0.0",
"release_notes": "..."
}
| code | message 示例 | 场景 |
|---|---|---|
| 40302 | 已废弃:历史不做会员裁剪 | |
| 40303 | 请升级会员后使用采购台账 | V1.1 台账 |
| 40304 | 已废弃:小程序历史/采购不因执照状态拦截 | |
| 40402 | 用户未注册 | 登录后业务接口 |
| 40901 | 体验券已使用或无效 | 核销失败 |
| 40902 | 订单已支付或已关闭 | 重复支付 |
| 40903 | 营业执照审核中,请勿重复提交 | 重复上传 |
| 41301 | 图片超过 5MB 或格式不支持 | 执照上传 |
| 方法 | 路径 | 调用端 | MVP |
|---|---|---|---|
| POST | /auth/mp/login |
小程序 | ✓ |
| POST | /auth/register |
小程序 | ✓ |
| POST | /auth/refresh |
小程序 | ✓ |
| GET | /users/me |
小程序 | ✓ |
| GET | /users/me/dashboard |
小程序 | ✓ |
| GET | /licenses/current |
小程序 | ✓ |
| POST | /licenses/upload |
小程序 | ✓ |
| GET | /licenses/current/image-url |
小程序 | ✓ |
| GET | /platform-bindings |
小程序 | ✓ |
| POST | /platform-bindings |
小程序 | ✓ |
| DELETE | /platform-bindings/{platform_id} |
小程序 | ✓ |
| POST | /platform-bindings/{platform_id}/validate |
小程序 | ✓ |
| GET | /queries |
小程序 | ✓ |
| GET | /queries/{id} |
小程序 | ✓ |
| POST | /queries |
桌面/Android | ✓ |
| GET | /purchase-intents |
小程序 | ✓ |
| GET | /purchase-intents/{id} |
小程序 | ✓ |
| POST | /purchase-intents |
桌面/Android | ✓ |
| GET | /membership/plans |
小程序 | ✓ |
| POST | /membership/orders |
小程序 | ✓ |
| GET | /membership/orders/{order_no} |
小程序 | ✓ |
| POST | /payments/wechat/notify |
微信服务器 | ✓ |
| GET | /invites/stats |
小程序 | ✓ |
| POST | /coupons/redeem |
小程序 | ✓ |
| GET | /content/help |
小程序 | ✓ |
| GET | /content/download |
小程序 | ✓ |
| GET | /procurement-ledgers |
小程序 | V1.1 |
| POST | /procurement-ledgers |
小程序 | V1.1 |
-- User 扩展
ALTER TABLE users ADD COLUMN wx_openid VARCHAR(64) NULL;
ALTER TABLE users ADD COLUMN wx_unionid VARCHAR(64) NULL;
CREATE UNIQUE INDEX idx_users_wx_openid ON users(wx_openid);
-- BusinessLicense 新表
CREATE TABLE business_licenses (
id VARCHAR(32) PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
image_url TEXT NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'pending',
reject_reason TEXT NULL,
submitted_at TIMESTAMP NOT NULL,
reviewed_at TIMESTAMP NULL,
source_client VARCHAR(32) NOT NULL,
reviewer_id VARCHAR(32) NULL,
INDEX idx_bl_user_status (user_id, status),
INDEX idx_bl_pending (status, submitted_at)
);
-- QueryLog 扩展
ALTER TABLE query_logs ADD COLUMN source_client VARCHAR(32) NOT NULL DEFAULT 'windows';
ALTER TABLE query_logs ADD COLUMN result_count INT NOT NULL DEFAULT 0;
ALTER TABLE query_logs ADD COLUMN lowest_platform VARCHAR(32) NULL;
ALTER TABLE query_logs ADD COLUMN lowest_price DECIMAL(10,2) NULL;
ALTER TABLE query_logs ADD COLUMN snapshot JSON NOT NULL;
-- PurchaseIntent 新表
CREATE TABLE purchase_intents (
id VARCHAR(32) PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
query_id VARCHAR(32) NULL,
drug_name VARCHAR(128) NOT NULL,
spec VARCHAR(64) NOT NULL,
manufacturer VARCHAR(128) NULL,
platform_id VARCHAR(32) NOT NULL,
reference_price DECIMAL(10,2) NOT NULL,
clicked_at TIMESTAMP NOT NULL,
source_client VARCHAR(32) NOT NULL,
external_url TEXT NULL,
INDEX idx_pi_user_clicked (user_id, clicked_at DESC),
INDEX idx_pi_query (query_id)
);
-- Order 扩展
ALTER TABLE orders ADD COLUMN source_client VARCHAR(32) NOT NULL DEFAULT 'windows';