开源一站式全域流量变现 SaaS。多租户多站点(
site_id为业务隔离键),uni-app 三端(小程序 / H5 / 后台)+ PostgreSQL + 腾讯云 CloudBase。
海量优质平台,强强联合:
| 分类 | 品牌 |
|---|---|
| 🍔 餐饮美食 | 麦当劳 · 肯德基 · 星巴克 · 瑞幸咖啡 · 奈雪の茶 · 喜茶 · 茶百道 · 古茗 · 库迪咖啡 · 德克士 · 汉堡王 · 塔斯汀 · 华莱士 · 必胜客 · 百果园 · 茉莉奶白 |
| 🛒 电商平台 | 拼多多 · 淘宝 · 天猫超市 · 京东 · 唯品会 · 淘宝闪购 |
| 🍜 本地生活 | 美团 · 美团外卖 · 饿了么 · 名创优品 · 屈臣氏 |
| 🚗 出行住宿 | 滴滴 · 滴滴代驾 · 花小猪 · 哈啰出行 · 青桔单车 · 美团单车 · 高德打车 · 飞猪 |
| 🎬 影视会员 | 腾讯视频 · 爱奇艺视频 · 优酷视频 · 芒果视频 · 哔哩哔哩 · 埋堆堆 · 央视频 · 华为视频 · 1905电影网 · 乐视 · 南瓜电影 · 咪咕视频 · 搜狐视频 · 腾讯体育 · 腾讯动漫 |
| 🎵 音频娱乐 | QQ音乐 · 网易云音乐 · 酷狗音乐 · 酷我音乐 · 喜马拉雅 · 全民K歌 · 汽水音乐 · 蜻蜓FM · QQ会员 · 华为音乐 · 新浪微博 · 抖音 |
| 📚 读书学习 | 百度文库 · WPS · QQ阅读 · 书旗小说 · 咪咕阅读 · 得到 · 知乎 · 豆瓣阅读 · 掌阅 · 懒人听书 · 快看漫画 · 凯叔讲故事 · 作业帮 · 稿定设计 |
| 🛠️ 工具效率 | 百度网盘 · 夸克 · 360AI云盘 · 迅雷 · 剪映 · 醒图 · 美图秀秀 · KEEP · 每日瑜伽 · 咕咚 · e袋洗 · 即梦 |
| 🎮 游戏加速 | 奇游加速器 · 迅游加速器 · 雷神加速器 · 腾讯手游加速器 · 游帮帮加速器 |
| 📟 智能硬件 | 特来电 · 浩辰CAD看图王 · 乐刻LITTA健身课 · 京东PLUS |
| 🎫 生活服务 | 电影票 · 鲜花配送 · 会员卡券 · 比价寄(圆通 · 申通 · 京东 · 韵达 · 中通 · 顺丰 · 德邦 · 菜鸟 · EMS · 壹米滴答 · 顺心捷达 · 极兔) |
# 1. 装依赖
npm install
# 2. 配环境变量(复制模板后填自己的值)
cp .env.example .env
# 3. 一键部署(迁移 → seed → 构建三端 → 静态托管 → 云托管容器 → 定时器)
node deploy/scripts/deploy.mjsdeploy.mjs 六个阶段全部幂等,重复执行安全:
| 阶段 | 内容 | 脚本 |
|---|---|---|
| 1/6 | 环境校验 | — |
| 2/6 | schema迁移 | migrate.mjs |
| 3/6 | 初始数据 | seed.mjs |
| 4/6 | 构建 + 静态托管 + 云托管容器 | hosting-deploy.mjs / container-deploy.mjs |
| 5/6 | 定时任务自动部署 | function-deploy.mjs --job=all |
| 6/6 | 交付输出 | — |
不需要在 CloudBase 控制台点任何东西。 定时任务走云函数(SCF timer)实现,部署脚本用 .env 里的 TCB_SECRET_ID/KEY 通过 OpenAPI 幂等创建函数与触发器,并把 HMAC token 注入函数环境变量。
SCF timer(每 5 分钟)
→ ordersweep-timer(云函数)
→ HTTPS POST https://<域名>/api/jobs/ordersweep/cron?token=<HMAC>
→ 云托管容器:关单 + 退券 + 回补库存
已注册的任务:
| 任务 | 函数名 | 周期 | 端点 |
|---|---|---|---|
| 订单同步 | ordersync-timer |
每 30 分钟 | /api/jobs/ordersync/cron |
| 签到提醒 | checkin-remind-timer |
每日 09:00 | /api/jobs/checkin-remind/cron |
| 未支付单关单 | ordersweep-timer |
每 5 分钟 | /api/jobs/ordersweep/cron |
新增一个定时任务(三步,无需碰控制台):
-
deploy/scripts/function-deploy.mjs的JOBS注册表加一行:'my-job': { funcName: 'my-job-timer', funcDir: 'my-job-timer', // 目录名必须=函数名(SCF packer 约定) triggerName: 'my-job-hourly', cron: '0 0 * * * * *', // 7 字段:秒 分 时 日 月 周 年(北京时间) url: 'https://<域名>/api/jobs/my-job/cron', hmac: 'my-job', // 与 server/src/routes/jobs.ts 的 cronToken(name) 同名 envPrefix: 'JOB', desc: '...', envExtra: {}, },
-
复制
deploy/functions/checkin-remind-timer/index.js→deploy/functions/my-job-timer/index.js(通用单 POST 桥,零依赖) -
node deploy/scripts/function-deploy.mjs --job=my-job --invoke
服务端对应加一个 POST /api/jobs/my-job/cron 端点,用 cronToken('my-job') 校验即可。
单独重部 / 改周期:
node deploy/scripts/function-deploy.mjs --job=ordersweep # 单个
node deploy/scripts/function-deploy.mjs --job=all --invoke # 全量 + 立即各调一次
node deploy/scripts/print-cron-token.mjs # 打印各任务回调 URL(排查用)为什么不用云托管原生定时触发器:CloudBase 云托管 API 只开放
TimerScale(定时扩缩容),没有「定时发 HTTP 请求」的触发器类型。云函数 timer 全程可 API 配置,才是开源可复用的正确姿势。
apps/
mini/ uni-app 小程序(C 端,25 屏)
h5/ uni-app H5(首页 + 淘口令中转)
admin/ Vue3 + Element Plus 后台(19 屏)
server/ Express + TypeScript 服务端(云托管容器)
packages/
renderer/ Schema 装修渲染器(与 admin 预览双实现须同步)
deploy/
migrations/ SQL 迁移(幂等,现至 031)
functions/ SCF 定时器桥函数
scripts/ 部署 / 迁移 / 验证脚本
seed/ 初始数据
docs/ 设计文档
deploy/scripts/verify-*.mjs 均为打线上真实接口的 E2E,改完必跑:
| 脚本 | 覆盖 |
|---|---|
verify-coupon-loop.mjs |
优惠券全闭环 32 项 |
verify-order-lifecycle.mjs |
订单状态机 + 券释放 36 项 |
verify-members.mjs |
会员管理 19 项 |
verify-theme-coverage.mjs |
全站换肤 10 项 |
verify-cron-autotrigger.mjs |
定时器真实触发(造超时单后纯等SCF 自己跑) |
三端三层,逐项实锤:源码 → 构建产物 → 线上接口。禁止口头交付。
# 1. 源码
git diff --stat
# 2. 构建产物
ls -la apps/admin/dist/assets/ apps/h5/dist/build/h5/
# 3. 线上接口
curl -s https://<域名>/healthz可以去https://www.51skill.com/portal/api/wechat-sub-merchant-managed 调用接口申请,几分钟即可搞定。
第三方 API 调用
- 所有外部
fetch必须带AbortSignal超时。项目里已有hjkCall()(12s 超时 + 重试 + 限频),但曾有 4 处绕过它裸调fasttype,上游冷启动 10.5s 把 C 端遮罩拖成假死。 自查:grep -rn "api-gw.haojingke.com" server/src应只出现在lib/haojingke.ts内。 - 全量静态目录类上游(如 fasttype 81 条权益档位)统一走
fetchFasttype(apikey, uid, force?): 8s 超时 + 5min 进程内 TTL 缓存 + 在途请求去重。分钟级不变的数据不要每次重拉。 - 前端 loading 遮罩必须有硬超时上限(
Promise.race)。任一数据域挂死 = 整页假死。utils/request.js已设默认 20s 超时并把平台 timeout 报错转成可读文案。 - 上游返回必须校验「完整度」,不能只判类型。实测蚂蚁 fasttype 会瞬时返回残缺目录
(同一 apikey 连续请求出现 47 条 vs 完整 81 条,肯德基等整组消失)。只判
Array.isArray就写缓存,会把残缺响应固化 5 分钟,叠加/service-search的 60s 结果级缓存后放大到数分钟—— 症状是「搜某品牌查不到权益,但直连上游完全正常」,极难定位。 现已加两道闸:fetchFasttype低于FASTTYPE_MIN_ITEMS(20)判残缺不写缓存;searchRightsTrack返回degraded标记,权益轨降级时/service-search不写结果缓存。 SELECT col::text后ORDER BY col(无别名)会按字典序排。PG 会把无别名的ORDER BY解析到 SELECT 里的 text 输出列,'1000' < '600'→ 权重最高的排在末尾。 修法:排序用原始数值列(ORDER BY t.weight或带表别名ORDER BY h.weight), 数值转字符串只放在 SELECT 投影里。这是网关「参数须全字符串」铁律的隐蔽副作用。- 多层缓存会放大残缺数据。进程内缓存(5min)+ 端点结果级缓存(60s)+ 容器多副本各自冷启动, 任一层拿到残缺响应都会放大成数分钟的用户可见故障。降级路径必须显式标记并跳过所有写缓存。
图标
- 上游返回的图片 URL 先实测状态码再决定用不用。已知问题域:
某第三方权益图床(图片 URL 含中文未编码,实测 403)、
img14.360buyimg.com(不在小程序 downloadFile 白名单)。 - 商标/品牌类图标一律用「首字 + emoji 背景装饰」:首字为识别主体(玫红 900 字重),分类 emoji 降为右下角低透明度装饰。
零网络请求、零白名单风险、不会白板。参考实现
search-result.vue的firstChar()+.bc-icon-letter。 - 自有商品图(
self_goods.main_imgs)走自家CDN,可正常用<image>。
Apache-2.0 —— 含显式专利授权,商用请保留版权与 NOTICE 声明。