🧩 班友应用开发标准 v1

面向经销商 / 开发者的插件接入规范 · 适用于六个门店项目 · 更新于 2026-10-24

1. 这是什么

班友门店系统(美业 / 教培 / 零售商超 / 餐饮 / 宠物 / 票务场馆)支持按需安装第三方功能插件。开发者上传符合本规范的 zip 包,经平台审核后上架;租户在「应用中心」购买/安装,插件出现在其左侧菜单。

开发者身份与班友经销商体系合并:经销商即开发者,不需要另开账号。上传前需由平台开通「应用开发者资质」。

当前版本(v1)能力边界:插件前端 + 只读数据网关 + 人工确认到账的付费流程。 写数据能力、在线支付分账、经销商自托管后端健康检查会在后续版本开放;未开放的能力网关会返回 501,不会静默失败。
入口:经销商登录后,在左侧「应用市场」进入;页面顶部和侧栏均提供「开发文档」入口。 未开通应用开发者资质的经销商可以查看文档,但不能上传或提交应用。

2. 交付物:一个 zip 包

zip 根目录(允许套一层文件夹)必须包含:

文件必需说明
manifest.json是应用清单,字段见下一节
index.html是(或 manifest.entry 指定的文件)插件入口页,在 iframe 中打开
其它静态资源否JS/CSS/图片等,总解压后 ≤ 30MB,文件数 ≤ 500

限制:zip ≤ 10MB;禁止 .. 路径、绝对路径、符号链接;入口必须是 .html。

3. manifest.json

{
  "app_key": "retail-birthday",
  "name": "生日营销",
  "version": "1.0.0",
  "api_version": "1",
  "projects": ["retailpro"],
  "entry": "index.html",
  "scopes": ["members:read", "coupons:write"],
  "backend_url": "https://your-server.example.com/banu-app",
  "menu": { "title": "生日营销", "icon": "🎂", "order": 100 },
  "settings_schema": {
    "type": "object",
    "properties": {
      "storeTip": { "type": "string", "title": "门店提示语" }
    }
  }
}
字段必需规则
app_key是小写字母开头,数字和短横线,3~64 位;创建应用时确定,不可修改
name是≤100 字
version是语义化版本,如 1.0.0;同一应用内不可重复
api_version是固定 "1"
projects是salonpro / edupro / retailpro / cateringpro / petpro / ticketpro;必须是创建应用时勾选项目的子集。["*"] 为通用型,暂不开放
entry否相对路径,默认 index.html
scopes否只能从下表选择;不声明就拿不到对应数据
backend_url否经销商自托管后端,必须 https、不能是 localhost
menu否安装后注入租户侧栏的标题和图标;title ≤20 字,icon 1 个 emoji
settings_schema否JSON Schema,预留给租户配置项(v1 仅存档,不渲染)

可用 scope

scope能力v1 状态
store:read读取门店基本信息✅ 已开放
members:read读取会员白名单字段✅ 已开放
orders:read读取订单✅ 已开放
inventory:read读取库存规划中
products:read读取商品/服务规划中
coupons:read / coupons:write优惠券规划中
members:write / orders:write / products:write写数据规划中(网关返回 501)
print:write / message:write打印 / 消息规划中

遵循最小权限:不要为了「以后可能用」把所有 scope 都写上,审核会按实际功能核对。

4. 运行方式

初始化握手

// 插件页 index.html
let gatewayToken = '';

window.addEventListener('message', (e) => {
  // 生产环境校验来源,避免任意页面伪造初始化消息
  if (e.origin !== 'https://salonpro.banutech.com') return;

  if (e.data && e.data.type === 'banu-app-init') {
    gatewayToken = e.data.token;          // 10 分钟有效
    console.log('tenant', e.data.tenant_id, 'project', e.data.project);
  }
  if (e.data && e.data.type === 'banu-app-token') {
    gatewayToken = e.data.token;          // 续期后的新 token
  }
});

// 告诉宿主你已就绪(可选;宿主在 load 事件后就会发 init)
window.parent.postMessage({ type: 'banu-app-ready' }, '*');

// 调网关遇到 401 时,向宿主换一张新票
async function refreshToken() {
  window.parent.postMessage({ type: 'banu-app-need-token' }, '*');
  await new Promise(r => setTimeout(r, 300));
}
安全:token 只代表「这个租户安装的这个应用」,10 分钟过期。 不要把它写进日志、URL、第三方统计;不要转交给你的后端长期保存。

5. 数据网关

基址:https://ai.banutech.com/api/app-gateway/v1。所有请求带 Authorization: Bearer <token>。

方法路径scope说明
GET/me无需当前应用、项目、版本、已授予 scope
GET/storestore:read门店列表(可选 store_id 过滤)
GET/members?page=1&pageSize=20&keyword=&birthday_month=10members:read会员分页;birthday_month 按月筛生日并按日期排序;只返回白名单字段
GET/member-statsmembers:read会员聚合:总数、近 7/30 天新增、积分/储值/累计消费、等级分布
GET/orders?page=1&pageSize=20&from=2026-01-01&to=2026-12-31orders:read订单分页
其它/*—未开放能力返回 501

分页上限 100 条;同一应用 + 租户限流 120 次/分钟。所有查询自动按租户隔离,插件无法指定别的 tenant_id。

会员返回字段(白名单)

{
  "success": true,
  "data": {
    "items": [{
      "id": "…", "name": "张女士", "phone": "138…", "gender": "female",
      "birthday": "1990-05-01", "level": "gold", "points": 1200,
      "recharge_balance": 500.00, "gift_balance": 0.00,
      "total_spent": 3200.00, "status": "active", "created_at": "…"
    }],
    "total": 1, "page": 1, "pageSize": 20
  }
}

错误码

HTTP含义建议处理
401token 缺失/过期/伪造向宿主发 banu-app-need-token
403未安装、授权到期、scope 不足、越权门店提示租户,不要重试
429超出限流退避重试
501能力未开放提示功能暂不可用

6. 禁止事项(审核会直接驳回)

  • 直连 MySQL / Redis / 任何数据库,或索取平台密钥、租户登录 token。
  • 读取宿主 localStorage、Cookie、window.parent 的 DOM。
  • 使用 eval、new Function、远程加载并执行脚本、混淆代码。
  • 页面内跳转/新窗口打开外部站点作为主流程(可申请,但需说明用途)。
  • 收集与功能无关的个人信息;超范围抓取会员/订单。
  • 在插件里做与门店业务无关的广告、导流、挖矿、爬虫。
  • 调用未声明 scope 的接口;把 token 传给第三方。

7. 审核与上架

  1. 创建应用:经销商后台 → 应用市场 → 创建应用(app_key 创建后不可改)。
  2. 上传版本:zip 包;平台自动校验 manifest、路径、入口、大小。
  3. 提交审核:审核期间不能修改或重复提交。
  4. 平台审核:核对功能与 scope 是否匹配、是否有禁止行为、在小范围租户试用。
  5. 上架:通过后出现在对应项目的应用中心;驳回会附原因,可改后重新提交。
  6. 下架:平台或经销商均可下架;已购买租户仍可用到到期。

审核清单

常见驳回原因

版本兼容与热修

8. 定价与结算

9. 数据与合规

10. 最小示例

<!DOCTYPE html>
<html>
<body style="font-family:system-ui;padding:20px">
  <h3 id="title">加载中…</h3>
  <ul id="members"></ul>

  <script>
  let token = '';

  async function loadMembers() {
    const r = await fetch('https://ai.banutech.com/api/app-gateway/v1/members?pageSize=5', {
      headers: { Authorization: 'Bearer ' + token }
    });
    if (r.status === 401) {
      window.parent.postMessage({ type: 'banu-app-need-token' }, '*');
      return;
    }
    const data = await r.json();
    document.getElementById('title').textContent = '最近会员';
    document.getElementById('members').innerHTML =
      (data.data.items || []).map(m =>
        '<li>' + m.name + ' · ' + m.phone + ' · ' + m.points + ' 积分</li>'
      ).join('');
  }

  window.addEventListener('message', (e) => {
    if (e.data && e.data.type === 'banu-app-init') {
      token = e.data.token;
      loadMembers();
    }
    if (e.data && e.data.type === 'banu-app-token') {
      token = e.data.token;
      loadMembers();
    }
  });

  window.parent.postMessage({ type: 'banu-app-ready' }, '*');
  </script>
</body>
</html>

11. 常见问题

可以只做一个项目吗?

可以,而且推荐。创建应用时只勾选你熟悉的项目;通用型插件后续开放。

插件需要自己的服务器吗?

纯前端插件不需要。需要定时任务、外部对接、私密逻辑时,用 backend_url 指向你自己的 https 服务;平台不会代跑你的代码。

能改后台主菜单/页面吗?

不能。你只能提供侧栏里的一个入口和 iframe 页面,主程序 UI 不注入你的 JS。

为什么请求返回 403?

依次检查:租户是否安装、授权是否到期、manifest 是否声明对应 scope、store_id 是否属于该租户。

token 10 分钟就过期怎么办?

收到 401 时发 banu-app-need-token,宿主会换新票并通过 banu-app-token 回传。