Files
slogan/docs/superpowers/specs/2026-07-31-commerce-monetization-design.md
T

16 KiB
Raw Blame History

商业化四支柱设计(后端)· slogan-agent

目标: 以「个人形象设计」为主题业务,落地四支柱收入:VIP 会员充值、穿山甲广告、线下门店引流(OTA 联盟)、线上商品(电商联盟 CPS)。 核心原则: 商业化从「方案/单品」长出,不做泛化场景广场。所有推荐由方案已有字段驱动,零新增 LLM 调用

1. 总体架构

Appslogan-app)
  │  会员中心/方案详情商业化入口/衣橱升级款/广告位
  ▼
slogan-agent 新增模块
  ├─ 会员模块    member_plan / payment_order / user_member / pay_notify_log
  ├─ 广告激励    ad_reward_log + 发放权益
  ├─ CPS 统一引擎 cps_category / cps_product / cps_click_log / scene_category_map
  │     └─ 适配器:美团联盟(OTA 到店) / 京东联盟(电商) / 淘宝客(美妆配饰)
  └─ 配置        config.ymlcps.payment.ad 配置段,Key 默认空 → 模块自动降级)
        │
        ├─▶ 虎皮椒聚合支付(微信/支付宝收银台,iOS WebView)
        ├─▶ 美团联盟 API(选品 + 转链,pid 归因)
        ├─▶ 京东联盟 API(选品 + 转链)
        └─▶ 淘宝客 API(选品 + 淘口令)

模块降级原则:与现有 llm/weather/geo 配置段同模式 —— 支付/CPS 相关 key 未配置时,接口返回明确错误信息(如"支付未开通,请在 config.yml 配置"),App 端隐藏对应入口,不影响主功能闭环。

2. 支柱 AVIP 会员与聚合支付

2.1 支付服务商:虎皮棋(xunhupay)

  • 个人可开通、无营业执照门槛、微信+支付宝双通道、收银台 URL 模式(App WebView 打开)
  • 下单:POST /v1/paymentRSA 签名请求);回调:POST notify_url(验签后解析)
  • 签名/验签细节以官方最新文档为准,实现时封装在 payment/gateway.go 适配器内,与业务解耦
  • 金额一律以「分」为单位存库,避免浮点误差

2.2 数据模型(dao init 自动建表,沿用 SQLite 规范)

CREATE TABLE IF NOT EXISTS member_plan (
  id            INTEGER PRIMARY KEY AUTOINCREMENT,
  name          TEXT NOT NULL DEFAULT '',
  price_fen     INTEGER NOT NULL DEFAULT 0,       -- 金额(分)
  duration_days INTEGER NOT NULL DEFAULT 30,      -- 时长(天)
  features      TEXT NOT NULL DEFAULT '[]',        -- 权益 JSON["effect_unlimited","ai_priority","cps_commission_x15","store_discount"]
  sort          INTEGER NOT NULL DEFAULT 0,
  status        INTEGER NOT NULL DEFAULT 1,
  created_at    DATETIME DEFAULT (datetime('now','localtime'))
);

CREATE TABLE IF NOT EXISTS payment_order (
  id           INTEGER PRIMARY KEY AUTOINCREMENT,
  order_no     TEXT NOT NULL UNIQUE,               -- 业务订单号
  user_id      INTEGER NOT NULL DEFAULT 0,
  plan_id      INTEGER NOT NULL DEFAULT 0,
  amount_fen   INTEGER NOT NULL DEFAULT 0,
  channel      TEXT NOT NULL DEFAULT '',           -- alipay | wechat
  status       TEXT NOT NULL DEFAULT 'pending',    -- pending | paid | closed
  trade_no     TEXT NOT NULL DEFAULT '',           -- 第三方交易号
  notify_raw   TEXT NOT NULL DEFAULT '',           -- 回调原文(审计)
  paid_at      DATETIME,
  created_at   DATETIME DEFAULT (datetime('now','localtime'))
);
CREATE INDEX IF NOT EXISTS idx_payment_order_user ON payment_order(user_id, created_at);

CREATE TABLE IF NOT EXISTS user_member (
  id         INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id    INTEGER NOT NULL UNIQUE,
  plan_id    INTEGER NOT NULL DEFAULT 0,
  expire_at  DATETIME,
  source     TEXT NOT NULL DEFAULT 'vip_pay',      -- vip_pay | ad_trial | gift
  created_at DATETIME DEFAULT (datetime('now','localtime')),
  updated_at DATETIME
);

CREATE TABLE IF NOT EXISTS pay_notify_log (
  id        INTEGER PRIMARY KEY AUTOINCREMENT,
  order_no  TEXT NOT NULL DEFAULT '',
  body      TEXT NOT NULL DEFAULT '',
  sign      TEXT NOT NULL DEFAULT '',
  remote_ip TEXT NOT NULL DEFAULT '',
  status    TEXT NOT NULL DEFAULT 'ok',            -- ok | bad_sign | duplicate | no_order
  created_at DATETIME DEFAULT (datetime('now','localtime'))
);

2.3 接口(RouteRegister 2 参 handlercommon.GetUserId(g.RequestFromCtx(ctx)) 取用户)

路径 方法 请求 响应 说明
/member/plan/list GET - {list: [member_plan]} 上架套餐
/member/status GET - {member: {...}, is_vip, expire_at} 我的会员状态
/member/order/create POST {plan_id} {order_no, pay_url} 下单 → 虎皮棋收银台 URL
/member/order/notify POST 表单回调 "success" publicPaths 放行;验签 → 幂等 → 订单 paid → 开通/续期会员
/member/order/status GET {order_no} {status} App 轮询

支付时序

App → POST /member/order/create → 后端生成订单 + 调虎皮棋下单 → 返回 pay_url
App → WebView 打开 pay_url(用户完成支付)
虎皮棋 → POST /member/order/notifyRSA 验签)
后端 → 幂等校验(order_no 状态机 pending→paid,重复回调忽略并记 pay_notify_log
后端 → 更新 user_member(续费:expire_at 在原有效期上叠加,min 逻辑;过期则从现在起算)
App → GET /member/order/status 轮询(间隔 2s,超时 60s)→ 展示开通成功

幂等与安全:回调必须验签(失败记 bad_sign 并返回非 success);order_no 唯一 + 状态机保证只开通一次;回调日志全量入库审计;退款 MVP 阶段客服手动处理(标记 order closed + 人工延退会员)。

3. 支柱 B:广告激励

3.1 数据模型

CREATE TABLE IF NOT EXISTS ad_reward_log (
  id         INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id    INTEGER NOT NULL DEFAULT 0,
  ad_type    TEXT NOT NULL DEFAULT '',             -- effect_extra(效果图+1) | vip_trial(体验会员1天)
  reward_key TEXT NOT NULL DEFAULT '',             -- "2026-07-31:effect_extra" 自然日去重粒度
  status     TEXT NOT NULL DEFAULT 'ok',
  created_at DATETIME DEFAULT (datetime('now','localtime'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_ad_reward_unique ON ad_reward_log(user_id, reward_key);

3.2 接口

路径 方法 请求 响应 说明
/ad/reward/claim POST {ad_type} {reward: {...}} 发放权益(限频见下)

风控(防刷,纯服务端计数,不信任客户端):

  • ad_type=effect_extra:每日每用户限 2 次reward_key 唯一索引 + 计数),发放后效果图当日额外 +1 次
  • ad_type=vip_trial:每日每用户限 1 次,发放 1 天体验会员(写 user_membersource=ad_trial,到期自动失效)
  • 效果图限额判定逻辑改造:EffectImageService.GenerateForPlanCountByUserToday 判断改为 当日已用 ≤ 基础额度(3) + 额外次数(ad_reward_log 当日 count);额外次数次日归零(不落独立表,按日查询即可)

4. 支柱 C/D:统一 CPS 引擎

4.1 核心抽象

// cps/provider.go —— 数据源适配器接口(包级单例:cps.Providers 注册表)
type Provider interface {
  Source() string                                  // meituan_ota | jd_ecom | tb_ecom
  SyncProducts(ctx, city string, catCode string) ([]CpsProduct, error)  // 定时选品池同步
  Search(ctx, keyword string, catCode string, page int) ([]CpsProduct, error) // 实时搜索兜底
  GetLink(ctx, outerId string) (string, error)     // 转链(带 pid),结果按 outerId 缓存 24h
}
  • 统一 cps_product 选品池:联盟商品定时同步入库,列表读库(不实时调联盟);搜索接口实时兜底
  • 转链结果缓存(与 imagegen cache 同模式),点击时写 cps_click_log
  • 未配置某联盟 key → 该 source 降级(列表为空 + App 隐藏入口)

4.2 数据模型

CREATE TABLE IF NOT EXISTS cps_category (
  id            INTEGER PRIMARY KEY AUTOINCREMENT,
  code          TEXT NOT NULL UNIQUE,             -- haircut / clothing / beauty / food / hotel / ticket / transport / digital ...
  name          TEXT NOT NULL DEFAULT '',
  parent_code   TEXT NOT NULL DEFAULT '',
  source        TEXT NOT NULL DEFAULT '',          -- meituan_ota / jd_ecom / tb_ecom
  source_cat_id TEXT NOT NULL DEFAULT '',          -- 联盟侧类目 ID
  sort          INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS cps_product (
  id              INTEGER PRIMARY KEY AUTOINCREMENT,
  source          TEXT NOT NULL DEFAULT '',
  outer_id        TEXT NOT NULL DEFAULT '',        -- 联盟商品 ID
  category_code   TEXT NOT NULL DEFAULT '',
  name            TEXT NOT NULL DEFAULT '',
  cover_url       TEXT NOT NULL DEFAULT '',
  price_fen       INTEGER NOT NULL DEFAULT 0,
  shop_name       TEXT NOT NULL DEFAULT '',
  commission_rate INTEGER NOT NULL DEFAULT 0,      -- 万分比
  city            TEXT NOT NULL DEFAULT '',        -- OTA 到店类目按城市
  scene_tags      TEXT NOT NULL DEFAULT '[]',      -- 场合标签 ["通勤","约会","旅行"]
  raw             TEXT NOT NULL DEFAULT '',        -- 联盟原始数据 JSON
  status          INTEGER NOT NULL DEFAULT 1,
  sync_at         DATETIME,
  created_at      DATETIME DEFAULT (datetime('now','localtime'))
);
CREATE INDEX IF NOT EXISTS idx_cps_product_cat ON cps_product(source, category_code, status);

CREATE TABLE IF NOT EXISTS cps_click_log (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id     INTEGER NOT NULL DEFAULT 0,
  source      TEXT NOT NULL DEFAULT '',
  outer_id    TEXT NOT NULL DEFAULT '',
  scene       TEXT NOT NULL DEFAULT '',            -- plan_haircut / plan_item / plan_occasion / wardrobe_upgrade / member_benefit
  plan_id     INTEGER NOT NULL DEFAULT 0,
  category_code TEXT NOT NULL DEFAULT '',
  deeplink    TEXT NOT NULL DEFAULT '',
  ip          TEXT NOT NULL DEFAULT '',
  created_at  DATETIME DEFAULT (datetime('now','localtime'))
);
CREATE INDEX IF NOT EXISTS idx_cps_click_user ON cps_click_log(user_id, created_at);

CREATE TABLE IF NOT EXISTS scene_category_map (   -- 方案字段 → 联盟类目映射(零 LLM 推荐核心)
  id           INTEGER PRIMARY KEY AUTOINCREMENT,
  scene_type   TEXT NOT NULL DEFAULT '',           -- haircut / item_buy / item_upgrade / occasion
  occasion     TEXT NOT NULL DEFAULT '',           -- 通勤/约会/旅行/运动/商务(occasion 场景)
  source       TEXT NOT NULL DEFAULT '',
  category_code TEXT NOT NULL DEFAULT '',
  priority     INTEGER NOT NULL DEFAULT 0
);

4.3 方案驱动推荐(核心:用已有方案字段,零新增 LLM 调用)

入口 方案字段 映射 推荐内容
发型卡「做同款发型」 发型名 + 城市 scene_type=haircut → 丽人类目 理发/造型店联盟券(美团)
穿衣清单「买同款」 单品 name/Desc 京东联盟搜索关键词 电商同款卡片
穿衣清单「到店试穿」 单品风格 tags + 城市 scene_type=item_upgrade → 服装类目 服装店联盟券(美团)
场合卡「延伸优惠」 occasion + 地点 scene_type=occasion 映射表 约会→餐厅+丽人;旅行→酒店/车票/当地丽人
衣橱「找升级款」 旧款 category + style_tags 京东搜索相似款 电商升级款

4.4 接口

路径 方法 请求 响应 说明
/cps/category/list GET - {list: [cps_category]} 统一分类树
/cps/product/list GET {source, category_code, city, page} {list, has_more} 选品池分页
/cps/product/link POST {product_id, scene, plan_id} {deeplink} 转链(缓存 24h+ 记点击日志
/cps/plan/recommend GET {plan_id, scene} {list: [推荐项]} 方案驱动推荐(发型卡/单品/场合)
/cps/wardrobe/upgrade GET {item_id} {list} 衣橱旧款升级款
/cps/my/recent GET - {list: [点击记录]} 我的优惠记录(含返现状态占位)

归因:转链 URL 内嵌联盟 pid(下单时由适配器生成),联盟侧自动归因;cps_click_log 用于转化分析,结算数据以联盟后台为准。

5. 会员权益实现

  • effect_unlimited:效果图限额判定跳过(EffectImageServiceIsVip(userId) 查询)
  • ai_priorityoutfit_service.Generate 任务插入优先级字段(MVP 可用简单 FIFO + vip 优先标记,或仅权益展示占位)
  • cps_commission_x15VIP 购买 CPS 佣金 ×1.5 —— 结算在联盟后台,MVP 仅权益展示(文案"返现加成 1.5x"),真实返现二期(需联盟侧对账)
  • store_discount:品牌合作门店(partner_store)展示"会员价"标识,到店出示会员状态(App 会员码页),自营核销二期

6. 配置(config.yml 新增段,Key 默认空)

payment:
  xunhu_appid: ""
  xunhu_appsecret: ""
  notify_url: "http://<公网>/member/order/notify"   # 回调需公网可达
  channel: "alipay,wechat"

ad:
  limit_effect_extra: 2     # 每日激励视频次数(效果图)
  limit_vip_trial: 1

cps:
  meituan_appkey: ""
  meituan_pid: ""
  meituan_shop_id: ""
  jd_appkey: ""
  jd_secret: ""
  jd_pid: ""
  tb_appkey: ""
  tb_secret: ""
  tb_pid: ""
  sync_cron: "0 4 * * *"     # 选品池定时同步

7. 合规与风控

  • 支付:金额单位分;回调幂等 + 验签;pay_notify_log 全量审计;退款人工处理(记录到订单)
  • iOS 合规iOS 端 WebView 支付为国内惯例做法,需在 App Store 审核时注意(虚拟商品 IAP 政策风险,上线策略:iOS 端主推激励广告+门店引流,充值入口弱化或按要求接 IAP)
  • 广告:隐私政策披露第三方 SDK 收集信息;提供个性化广告关闭入口(穿山甲 SDK 提供)
  • CPS:各联盟 API 需个人/企业账号申请(美团联盟、京东联盟、淘宝客均可个人申请);跳转链接遵守联盟推广规范(不得截流/改链接);禁用敏感类目(医疗、成人等)
  • 激励防刷ad_reward_log 唯一索引 + 自然日限频;异常用户(同设备多账号)风控日志记录

8. 分期实施与成本

分期 内容 后端工作量 依赖
P0 会员全链路(4 表 + 5 接口 + 虎皮棋适配器 + 回调验签)+ 广告激励(1 表 + 1 接口 + 效果图限额改造) ~2 人日 虎皮棋账号
P1 CPS 引擎(4 表 + 6 接口 + 美团适配器 + 方案驱动推荐)+ 转链缓存 + 点击日志 ~2.5 人日 美团联盟账号
P2 京东/淘宝适配器 + 会员返现加成 + 收益看板 + 风控报表 ~2 人日 京东/淘宝联盟账号
  • 服务器成本:零新增基础设施(SQLite 表均小体量,选品池定时同步 + 转链缓存)
  • 模型成本:零新增 LLM 调用(类目映射 + 关键词匹配)
  • 维护成本:联盟 API 变更由适配器隔离;第三方故障 → 接口降级返回错误,App 隐藏入口

9. 开发规范约束(沿用 video-factory 规范)

  • Controller→Service→DAO 三层,包级单例(var MemberService = new(memberService)
  • RouteRegister 反射路由,handler 必须 2 参 func(ctx context.Context, req *BizReq) (*BizRes, error)struct 名 kebab-casemember_plan/member/plan
  • 用户 ID 一律 common.GetUserId(g.RequestFromCtx(ctx))
  • 每表一 DAOdao/member_plan_dao.go 等),init()CREATE TABLE IF NOT EXISTS + seed
  • 统一响应 {"code":0,"message":"OK","data":...}/member/order/notify 加入 publicPaths
  • 外部服务(支付/联盟)全部走包内适配器(payment/cps/),业务层不直接感知
  • 配置默认空 → 降级不 panic(与 llm/weather 同模式)