# 智价云小程序 API 接口设计 V1.0 > 定稿日期:2026-06-27 > 与桌面端共用后端 · Base URL 示例:`https://api.proprice.kailin.com.cn/v1` > 关联 PRD:[PRD-小程序-V1.0.md](../prd/PRD-小程序-V1.0.md) --- ## 1. 通用约定 ### 1.1 请求头 | Header | 说明 | |--------|------| | `Authorization` | `Bearer {access_token}`,除登录/回调外必填 | | `X-Client` | 固定 `mini_program` | | `X-Client-Version` | 小程序版本号,如 `1.0.0` | | `Content-Type` | `application/json` | ### 1.2 响应格式 ```json { "code": 0, "message": "ok", "data": {} } ``` | code | 含义 | |------|------| | 0 | 成功 | | 40001 | 参数错误 | | 40101 | 未登录 / Token 过期 | | 40301 | 无权限(如体验用户访问超限历史) | | 40401 | 资源不存在 | | 42901 | 请求过于频繁 | | 50001 | 服务器错误 | ### 1.3 分页 列表接口统一参数: | 参数 | 类型 | 说明 | |------|------|------| | `page` | int | 页码,从 1 开始,默认 1 | | `page_size` | int | 每页条数,默认 20,最大 50 | 响应 `data` 结构: ```json { "items": [], "total": 100, "page": 1, "page_size": 20, "has_more": true } ``` --- ## 2. 数据实体 ### 2.1 User(扩展) 在 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 | 注册时间 | ### 2.2 UserGateStatus(聚合,非表) `/users/me` 返回的门禁摘要(**与主 PRD §3.4 一致,各端共用**): | 字段 | 类型 | 说明 | |------|------|------| | `can_query_on_desktop` | bool | 执照已通过且绑平台 ≥1 时为 true | | `license_status` | enum | 执照状态 | | `platform_bound_count` | int | 已绑定平台数 | | `gate_messages` | string[] | 待完成事项(引导完成注册/执照/绑平台) | > 上述门禁**仅影响桌面查价**等等价能力;**不用于裁剪**小程序历史/采购接口返回。 ### 2.3 BusinessLicense(新增) | 字段 | 类型 | 说明 | |------|------|------| | `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 操作台调用内部接口,与桌面端上传共用队列。 ### 2.4 QueryLog(扩展) | 字段 | 类型 | 说明 | |------|------|------| | `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 结构**: ```json { "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://..." } ] } ``` ### 2.5 PurchaseIntent(新增) | 字段 | 类型 | 说明 | |------|------|------| | `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(内部存储,小程序不返回) | ### 2.6 ProcurementLedger(V1.1 新增) | 字段 | 类型 | 说明 | |------|------|------| | `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 | 录入时间 | ### 2.7 Order(扩展) | 字段 | 类型 | 说明 | |------|------|------| | `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 | 创建时间 | ### 2.8 MembershipQuota(聚合) `/users/me` 返回配额摘要: | 字段 | 类型 | 说明 | |------|------|------| | `daily_query_used` | int | 桌面今日已用查询次数 | | `daily_query_limit` | int | 桌面每日上限(体验 20,会员 -1 表示不限) | | `watchlist_used` | int | 本月已关注品种数 | | `watchlist_limit` | int | 关注上限 | > 小程序历史/采购参考**无** `history_visible_*` 配额字段;列表接口不对体验用户做裁剪。 --- ## 3. 认证接口 ### 3.1 小程序登录 **POST** `/auth/mp/login` 请求: ```json { "code": "wx.login 返回的 code", "phone_code": "getPhoneNumber 返回的 code" } ``` 响应 `data`: ```json { "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` | ### 3.2 新用户注册 **POST** `/auth/register` ```json { "invite_code": "PX8K2M", "pharmacy_name": "康宁大药房", "region": { "province": "广东省", "city": "广州市", "district": "天河区" } } ``` 需已登录(微信授权 + 手机号已绑定)。成功后创建 User,`license_status: null`(未上传),返回更新后的 user。 ### 3.3 刷新 Token **POST** `/auth/refresh` ```json { "refresh_token": "rf_..." } ``` ### 3.4 退出登录 **POST** `/auth/logout` 清除服务端 session(可选);小程序端清除本地 token。 --- ## 4. 用户接口 ### 4.1 获取当前用户 **GET** `/users/me` 响应 `data`: ```json { "user": { /* User */ }, "gate": { /* UserGateStatus */ }, "quota": { /* MembershipQuota */ }, "platform_bindings": [ { "platform_id": "yaobangmang", "platform_name": "药帮忙", "status": "bound", "last_validated_at": "2026-06-27T10:00:00Z" } ] } ``` > 不返回平台账号密码。 ### 4.2 首页概览统计 **GET** `/users/me/dashboard` 响应 `data`: ```json { "today_query_count": 8, "today_purchase_intent_count": 3, "total_query_count": 156, "recent_queries": [ /* QueryLog 摘要,最多 3 条 */ ] } ``` --- ## 5. 营业执照接口 ### 5.1 获取执照状态 **GET** `/licenses/current` 响应 `data`: ```json { "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` | ### 5.2 上传营业执照 **POST** `/licenses/upload` `Content-Type: multipart/form-data` | 字段 | 类型 | 说明 | |------|------|------| | `file` | file | JPG/PNG,≤ 5MB | 响应 `data`: ```json { "id": "lic_abc", "status": "pending", "submitted_at": "2026-06-27T15:30:00Z" } ``` 行为: - 新上传或驳回后重传:创建新记录或更新,`status` → `pending` - 同步更新 `User.license_status` → `pending` - 审核中不可重复提交(返回 `40903`) ### 5.3 获取执照图片(签名 URL) **GET** `/licenses/current/image-url` 返回短期有效的 signed URL,供小程序预览已提交执照。 --- ## 6. 平台绑定接口 与桌面端共用 `PlatformBinding` 实体;任一端绑定/解绑,各端同步。 ### 6.1 平台列表 **GET** `/platform-bindings` 响应 `data.items[]`: ```json { "platform_id": "yaobangmang", "platform_name": "药帮忙", "status": "bound", "account_masked": "138****5678", "last_validated_at": "2026-06-27T10:00:00Z" } ``` 未绑定的平台也返回,`status: "unbound"`。 ### 6.2 绑定平台 **POST** `/platform-bindings` ```json { "platform_id": "yaobangmang", "account": "user@example.com", "password": "******", "source_client": "mini_program" } ``` 密码加密传输,服务端加密存储;响应不含明文密码。 ### 6.3 解绑平台 **DELETE** `/platform-bindings/{platform_id}` 至少保留 0 个绑定;若解绑后导致 `can_query_on_desktop=false`,与主 PRD 一致。 ### 6.4 验证绑定 **POST** `/platform-bindings/{platform_id}/validate` 触发凭证有效性校验,更新 `last_validated_at`。 --- ## 7. 查询历史接口 ### 7.1 查询历史列表 **GET** `/queries` | 参数 | 类型 | 说明 | |------|------|------| | `keyword` | string? | 关键词搜索 | | `days` | int? | 可选时间筛选:7 / 30 / 90(用户主动筛选,非会员限制) | | `source_client` | string? | 来源端筛选 | 响应 `data.items[]` 摘要字段: ```json { "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`)。 ### 7.2 查询快照详情 **GET** `/queries/{id}` 响应 `data`:完整 QueryLog + `snapshot` + `related_intents[]` --- ## 8. 采购参考接口 ### 8.1 跳转购买记录列表 **GET** `/purchase-intents` | 参数 | 类型 | 说明 | |------|------|------| | `keyword` | string? | 药品名搜索 | | `platform_id` | string? | 平台筛选 | | `days` | int? | 可选时间筛选(用户主动筛选,非会员限制) | **不做会员级裁剪**:体验用户与付费会员返回相同全量数据。 响应 `data.items[]`: ```json { "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" } ``` ### 8.2 跳转购买详情 **GET** `/purchase-intents/{id}` 含关联 `query_summary`(若有 query_id)。 ### 8.3 写入跳转记录(桌面/Android 调用,小程序只读) **POST** `/purchase-intents` > 小程序 MVP **不暴露**此接口;由 Windows/Android 在点击「去购买」时调用。 ```json { "query_id": "q_abc123", "drug_name": "阿莫西林胶囊", "spec": "0.25g×24粒", "manufacturer": "石药集团", "platform_id": "yaobangmang", "reference_price": 8.50, "source_client": "windows", "external_url": "https://..." } ``` ### 8.4 写入查询快照(桌面/Android 调用) **POST** `/queries` > 小程序 MVP **不暴露**写接口。 ```json { "keyword": "阿莫西林胶囊", "source_client": "windows", "result_count": 3, "lowest_platform": "药帮忙", "lowest_price": 8.50, "snapshot": { "items": [ /* ... */ ] } } ``` --- ## 8. 手动采购台账(V1.1) ### 8.1 列表 **GET** `/procurement-ledgers` 仅付费会员可用;体验用户返回 `40301`。 ### 8.2 创建 **POST** `/procurement-ledgers` ```json { "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" } ``` ### 8.3 更新 / 删除 **PATCH** `/procurement-ledgers/{id}` **DELETE** `/procurement-ledgers/{id}` --- ## 9. 会员与支付接口 ### 9.1 获取套餐列表 **GET** `/membership/plans` 响应 `data.plans[]`: ```json { "package_type": "yearly", "name": "年卡会员", "price": 899.00, "original_price": 999.00, "duration_days": 365, "badge": "最划算", "features": ["不限查询", "1999关注/月"] } ``` ### 9.2 创建会员订单 **POST** `/membership/orders` ```json { "package_type": "yearly", "source_client": "mini_program" } ``` 响应 `data`: ```json { "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)`。 ### 9.3 查询订单状态 **GET** `/membership/orders/{order_no}` 用于支付后轮询: ```json { "order_no": "202606271430001", "status": "paid", "paid_at": "2026-06-27T14:30:05Z", "member_expire_at": "2027-06-27T14:30:05Z" } ``` ### 9.4 微信支付回调 **POST** `/payments/wechat/notify` 微信服务器回调,验签后更新 Order 状态及 User 会员字段。不对小程序暴露。 --- ## 10. 邀请与体验券 ### 10.1 邀请统计 **GET** `/invites/stats` ```json { "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" } ] } ``` ### 10.2 体验券核销 **POST** `/coupons/redeem` ```json { "code": "TRIAL-ABCD-1234" } ``` 成功响应: ```json { "days_added": 15, "trial_end_at": "2026-07-12T00:00:00Z", "member_type": "trial" } ``` --- ## 11. 内容与配置 ### 11.1 帮助与 FAQ **GET** `/content/help` 返回 FAQ 列表、合规文案、桌面下载链接。 ### 11.2 桌面下载链接 **GET** `/content/download` ```json { "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": "..." } ``` --- ## 12. 错误码补充 | code | message 示例 | 场景 | |------|--------------|------| | 40302 | ~~体验用户历史记录超出可见范围~~ | 已废弃:历史不做会员裁剪 | | 40303 | 请升级会员后使用采购台账 | V1.1 台账 | | 40304 | ~~请先上传营业执照~~ | 已废弃:小程序历史/采购不因执照状态拦截 | | 40402 | 用户未注册 | 登录后业务接口 | | 40901 | 体验券已使用或无效 | 核销失败 | | 40902 | 订单已支付或已关闭 | 重复支付 | | 40903 | 营业执照审核中,请勿重复提交 | 重复上传 | | 41301 | 图片超过 5MB 或格式不支持 | 执照上传 | --- ## 13. 接口清单汇总 | 方法 | 路径 | 调用端 | 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 | --- ## 14. 数据库变更建议 ```sql -- 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'; ```