news 2026/5/10 18:52:20

适用于LiblibLiblibTV跨项目的商业化体系重构实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
适用于LiblibLiblibTV跨项目的商业化体系重构实践

背景

1. 业务动机:为什么需要跨项目复用

本 monorepo 中有两个面向用户的产品:

  • 主站项目:社区型 Web 应用,提供模型浏览、AI 生图/生视频、训练等核心功能,是最早的产品形态
  • TV 项目:面向大屏/新场景的独立应用,承载新的用户入口和交互体验

两个产品共享同一套会员体系——同一套后端 API、同一套 SKU 定义、同一套支付/订单流程。用户在任一端购买的会员权益全平台通用。

如果各自维护一套商业化实现,会带来严重的一致性风险:SKU 变更、定价策略、促销活动需在两个项目各改一遍,极易产生数据不一致和体验割裂。因此,统一的商业化层是"一个会员身份、多端体验"的技术保障。

2. 体系演进历史

商业化体系经历了五个阶段的渐进式演化:

阶段一 阶段二 阶段三 阶段四 阶段五 内聚于主站 → API 层抽取 → Store/Hooks → Facade 门面 → TV 项目接入 抽取 包创建
  • 阶段一:完全内聚。所有商业化功能(组件、Store、Hooks、API)集中在主站src/下,通过@/相互引用,无跨项目复用能力
  • 阶段二:API 层抽取。将 VIP、支付、订单等 API 请求封装抽取到独立的_services_ts包,实现服务调用的共享
  • 阶段三:Store/Hooks 抽取。将vipInfoStorecommercialStore等状态管理抽取到app-store包;将useUserIdentificationHooksuseVipBenefitHooks等业务钩子抽取到app-hooks
  • 阶段四:Facade 门面包。创建packages/commercial作为统一导出层,采用 Facade 模式对外暴露组件、Hooks、Store 和工具函数。组件实现仍驻留在主站src/components/
  • 阶段五:TV 项目接入。TV 项目通过依赖@org/commercial包复用商业化能力,当前正在进行 TV 端的技术栈适配工作

3. 两个项目的技术栈差异

两个项目在框架版本和构建工具上存在显著差异,这直接影响了商业化组件的复用方式:

维度主站项目TV 项目
Next.js13.x(Pages Router)16.x(App Router)
构建工具WebpackTurbopack
UI 框架Mantine 6Mantine 8
Tailwindv3v4
SCSS 引擎sass 1.69sass-embedded 1.86
路由 APInext/routernext/navigation
CSS Modules支持:global {}block 语法仅支持:global(selector)函数语法
国际化@org/i18n
动画库GSAP / framer-motion

关键影响点:

  • 路由 API 不兼容:共享组件需统一使用next/navigation(App Router 兼容)而非next/router
  • Mantine 版本跨代:v6 与 v8 API 差异大(如sx/createStyles在 v8 中已移除),TV 端通过turbopack.resolveAlias将 Mantine 统一到 v8
  • CSS Modules 语法限制:Turbopack 不支持:global { }block 语法,所有共享样式必须使用:global(selector)函数语法
  • SSR 行为差异:商业化组件在 TV 端需通过dynamic(() => import(...), { ssr: false })禁用服务端渲染

一、体系概览

本项目的商业化体系是一套跨项目复用的会员/付费/权益系统,同时为主站项目(社区主站)和TV 项目(TV 应用)提供服务。核心架构采用Facade 模式,通过packages/commercial包作为统一门面,对外暴露组件、Hooks、Store、工具函数等。

┌─────────────────────┐ ┌─────────────────────┐ │ TV 应用 │ │ 社区主站 │ │ (apps/tv) │ │ (packages/main) │ └──────────┬──────────┘ └──────────┬──────────┘ │ │ ▼ ▼ ┌──────────────────────────────────────────────────┐ │ packages/commercial │ │ (Facade 门面层:统一导出组件/Hooks/Store/Utils) │ └──────────┬───────────┬───────────┬───────────────┘ │ │ │ ▼ ▼ ▼ ┌────────────┐ ┌──────────┐ ┌──────────────────┐ │ app-store │ │ app- │ │ main/src/ │ │ (状态管理) │ │ hooks │ │ components/ │ │ │ │ (业务钩子)│ │ (UI 组件实现) │ └─────┬──────┘ └────┬─────┘ └────────┬─────────┘ │ │ │ └─────────────┴────────────────┘ │ ▼ ┌─────────────────────────────┐ │ _services / _services_ts │ │ (VIP/支付/权益 API 服务) │ └─────────────────────────────┘

二、基础层面支撑

1. UI 组件层

商业化组件的实现体位于packages/main/src/components/,由commercial包 re-export:

组件功能
VipModel个人 VIP 购买弹窗(含升级、续费、Tab 切换、QR 码)
VipSku/VipSkuPopVIP SKU 展示与弹窗
VipSuccess支付成功弹窗
VipEquity/VipPromoteLabelVIP 权益展示与推广标签
TeamVipModel团队 VIP 购买弹窗(含 Header/Content/Footer/PayModal)
TeamSku/TeamVipTable团队 SKU 与明细表
TimelimitDiscount限时折扣组件
PopGuide购买引导(含PurchaseVipModalPopMiniWindow
BuyPower/BuySpeed积分购买 / 加速包购买
OnlyPurchase纯购买模式
vipQrCode扫码支付 QR 码组件
AccountCenter用户账户中心
VipHomeContentVIP 主页内容

2. 状态管理层(Store)

通过packages/app-store提供全局状态:

Store职责
vipInfoStoreVIP 信息、团队信息、用户权益、限速状态、折扣信息、倒计时
commercialStore套餐列表、支付弹窗状态、远程配置、限时折扣、加速卡
useTeamVipPurchaseModalStore团队 VIP 购买弹窗可见性与意图

3. 业务 Hooks 层

通过packages/app-hookspackages/commercial/src/提供:

Hook职责
useUserIdentificationHooks用户身份识别(VIP 等级、团队状态、isVip、isTeamUser)
useVipBenefitHooks权益查询(剩余积分、加速优先级、训练 VIP 等级)
usePurchaseGoodsHooks下单、支付轮询
useCombineOrderHooks个人+团队合并下单入口
useTimelimitHooks限时优惠状态与操作
useTeamInfoHooks团队信息、邀请链接、成员列表
useCommercialInitData商业化数据初始化(加载配置与套餐)
usePointChargeSkuHooks积分充值 SKU
useCanPurchaseCoupons可购买优惠券
syncCommercialConfig加载远程配置并更新 commercialStore

4. API 服务层

职责
_services/src/vip.js微信/支付宝支付参数获取、续费、限时优惠、用户权益、积分、订阅管理
_services_ts/src/services/vip.ts会员套餐、积分、体验卡、团队购买、支付结果查询
_services_ts/src/types/vip.tsVIP 相关 TypeScript 类型
_services_ts/src/types/order.ts订单类型定义

5. 页面层

页面功能
viphome/VIP 中心(仪表盘、套餐列表、留存、折扣弹窗)
vip-model-page/VIP 模型页
wxpay/微信/支付宝支付页
transaction/subscribe/订阅管理
incomeR2//incomeReport/收入报告
_authorIncome/作者收入、账号绑定
membershipInvitationBonus/会员邀请奖励

6. 通信机制层

通过postMsg.ts实现跨组件/跨 iframe 通信:

  • openVipModel()— 发送show_purchase_dialogpostMessage,触发 VipModel 弹窗
  • communityPostMessage()— 通用 postMessage 通信
  • openTeamVipPurchase()— 打开团队 VIP 购买

7. 共享 UI 基础层(common-ui)

packages/common-ui提供两个项目共用的基础 UI 组件:

  • ui-notification.js— 通知(notifyError, notifySuccess, notifyWarn)
  • ui-button.jsx/ui-color.jsx/ui-iconfont.jsx— 基础 UI
  • ui-responsive.jsx— 响应式工具
  • ui-theme.jsx— 主题配置
  • ui-upload.jsx— 上传组件

8. 路径别名系统(#/ alias)

通过#/路径别名实现跨包引用主站src/的模块:

项目#/*映射目标
主站项目./src/*
commercial../main/src/*
TV 项目../../packages/main/src/*

9. 静态资源层

packages/commercial/src/assets.ts提供STATIC_CDN_MAP,统一管理 VIP 相关图标、图片等 CDN 资源。


三、两个项目同时使用带来的挑战

1. 循环依赖风险

commercial包 re-export 主站src/components/的组件,而这些组件又通过#/别名引用主站src/下的模块,甚至部分模块又 import@org/commercial。目前通过主站utils/postMsg.tsmanage/commerical.ts中的中间层 re-export来打断循环,但这种模式非常脆弱,新增 import 时容易不慎引入循环。

2. 组件实现与门面层的耦合

商业化组件的实际代码驻留在packages/main/src/components/中,commercial包只是一个 re-export 门面。这意味着:

  • 组件代码与主站项目的内部模块(如#/manage/callbackAction#/pages/viphome/*)深度绑定
  • TV 项目使用时,这些依赖必须通过#/别名正确解析,增加了配置复杂度
  • 组件难以真正独立于主站存在

3.#/路径别名的脆弱性

#/别名将三个项目绑定到主站src/的目录结构上:

  • 任何主站src/下的文件移动、重命名都可能同时影响主站、TV 项目和 commercial 包
  • 不同项目的 tsconfig 需要手动保持#/映射的一致性
  • IDE 的自动重构(如文件移动)通常不会同时更新所有项目的#/引用

4. 运行时环境差异

两个项目的运行时环境存在差异:

  • 主站:Next.js 13 + Webpack
  • TV 项目:Next.js + Turbopack
  • CSS Modules 语法兼容性要求不同(Turbopack 不支持:global { }block 语法)
  • SSR 行为差异导致商业组件需要dynamic(() => import(...), { ssr: false })特殊处理
  • 路由系统差异(主站用.page.tsx约定,TV 用 App Router)

5. 状态同步与初始化时机

两个项目有不同的应用生命周期:

  • useCommercialInitData的调用时机在两个项目中不同
  • vipInfoStorecommercialStore的初始化依赖于用户登录状态,但两个项目的登录流程不同
  • syncVipInfo等数据同步函数需要在正确的时机被调用,但各项目的布局和路由守卫不同

6. 功能子集与 UI 适配

TV 项目只使用商业化体系的部分功能(如VipModelAccountCenteropenVipModel),但整个 commercial 包及其依赖链会被打入。这带来:

  • 不必要的包体积
  • TV 端的 UI 适配需求(屏幕尺寸、交互方式不同)未在组件层面得到系统性支持
  • 移动端支付组件(MobileComponents/Pay)等与 TV 无关的代码也在依赖树中

7. 服务层的 JS/TS 双轨制

API 服务分散在_services(JS)和_services_ts(TS)两个包中,且命名不一致(目录用下划线_services_ts,包名用services-ts)。两个项目都需要依赖这两套服务,增加了维护成本和理解难度。

8. 代码规范与命名不一致

存在历史遗留问题:

  • commerical拼写错误(应为commercial)出现在多个文件中
  • 混合使用@org/commercial#/两种导入方式,缺乏统一规范
  • 部分组件用.jsx,部分用.tsx,风格不统一

9. 测试覆盖困难

由于商业组件依赖于主站src/的大量模块和 Store,在 TV 项目中进行独立的单元测试非常困难——需要 mock 整个#/别名链路和 Store 初始化过程。

10. 版本与发布耦合

两个项目共享workspace:*依赖,意味着任何商业化组件的变更都会同时影响两个项目。缺乏独立的版本管理机制,无法为不同项目发布不同版本的商业化组件。


四、跨项目复用的收益

尽管存在上述挑战,跨项目复用商业化体系带来的收益是显著的。

业务角度

  • 会员体系一致性:同一套 SKU、定价、促销规则在两端保持同步,用户在任一端购买的权益全平台生效,避免出现"A 端有折扣 B 端没有"的体验割裂
  • 上线效率:新增套餐、调整定价、上线限时活动只需改一处,两端同时生效,显著缩短商业化需求的交付周期
  • 收入归一:统一的支付流程和订单系统,确保收入数据口径一致,财务对账与运营分析无需跨系统聚合
  • 用户体验连贯:会员身份、权益展示、购买流程在两端视觉和交互上保持一致,增强品牌认知与用户信任

技术角度

  • 单一事实来源:VIP 状态、权益计算、SKU 列表等核心逻辑只有一份实现,消除多份代码之间的行为漂移风险
  • Bug 修复效率:支付流程、权益判断等关键路径的 bug 只需修复一次,两端同时受益
  • Store/Hooks 复用vipInfoStorecommercialStoreuseUserIdentificationHooks等状态管理和业务逻辑共享,减少大量重复代码
  • API 服务层共享:两端对接同一套后端 API,不需要各自维护请求封装、类型定义和错误处理
  • 渐进式解耦架构:Facade 模式允许 TV 端按需引入功能子集,未来可通过 tree-shaking 或拆分 entry point 进一步优化包体积
  • 统一技术演进路径:商业化相关的技术升级(如 Mantine 版本迁移、Turbopack 兼容、CSS Modules 语法统一)只需在共享层推进一次,两端同步受益
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/7 2:58:14

Cosmos-Reason1-7B快速部署:Docker镜像免配置启动本地推理服务

Cosmos-Reason1-7B快速部署:Docker镜像免配置启动本地推理服务 一句话总结:无需复杂配置,一条命令启动专业级本地推理服务,让AI帮你解决逻辑推理、数学计算和编程问题。 1. 为什么选择Cosmos-Reason1-7B? 如果你经常需…

作者头像 李华
网站建设 2026/5/5 7:02:36

UI-TARS-desktop入门必看:零基础搭建AI开发环境

UI-TARS-desktop入门必看:零基础搭建AI开发环境 1. UI-TARS-desktop是什么?为什么选择它? 如果你正在寻找一个能在自己电脑上运行的AI助手,既能理解你的指令,又能帮你完成各种实际任务,那么UI-TARS-deskt…

作者头像 李华
网站建设 2026/5/9 14:38:48

校验日期格式:正则表达式

// 不允许空字符串,使用分支(|) Pattern(regexp "^\\d{4}-\\d{2}-\\d{2}$", message "日期格式必须为yyyy-MM-dd") // 允许空字符串,使用分支(|) Pattern(regexp "^\\d{4}-\\d{2}-\\d{2}$|^$", message "日期格式…

作者头像 李华
网站建设 2026/5/3 8:13:07

M2LOrder开源模型管理:option/SDGB/1.51目录结构说明+新模型热加载机制

M2LOrder开源模型管理:option/SDGB/1.51目录结构说明新模型热加载机制 1. 项目概述 M2LOrder是一个基于.opt模型文件的情绪识别与情感分析服务,提供HTTP API和WebUI两种访问方式。这个轻量级WebUI让用户能够快速上手使用情感分析功能,无需复…

作者头像 李华
网站建设 2026/5/6 11:11:00

小白必看:Qwen3-ASR-1.7B语音识别常见问题解答

小白必看:Qwen3-ASR-1.7B语音识别常见问题解答 你是不是也遇到过这种情况:开会录音想整理成文字,结果发现语音转文字工具要么识别不准,要么收费太贵,要么担心隐私泄露?或者想给一段视频配上字幕&#xff0…

作者头像 李华