店铺装修与 Liquid 前台渲染

基于源码梳理:ThemesServiceHomeBaseControllerliquidExtend 自定义 Tag、主题文件与 DB 资产。


一、总体流程:从皮肤安装到前台展示

每个皮肤(theme)在磁盘或主题资产库中都有 config/ 目录,至少包含两个 JSON 文件:

文件作用
settings_schema.json配置项清单(表单结构、可装修页面列表、可添加积木库)
settings_data.json默认配置数据(全局默认值、各页面积木默认 params)

路径示例:public/theme/{name}/config/file_system=database 时等价内容在 o_theme_asset/config 目录。

这两个文件 不会 在运行时整文件读给前台用户,而是:安装时initThemeData() 把默认值抽取写入店铺 DB浏览时 前台读 DB 中的店铺装修数据 + Liquid 模板渲染 HTML。

1.1 流程总览

flowchart TB
    subgraph ThemePkg["皮肤包 config/"]
        SS[settings_schema.json]
        SD[settings_data.json]
        LQ[layout / templates / sections *.liquid]
    end

    subgraph Install["店铺安装皮肤"]
        INS[ThemesService::install]
        ROW[o_store_theme 插入一行]
        INIT[initThemeData]
    end

    subgraph DB["店铺装修数据(MySQL)"]
        ST[(o_store_theme<br/>params / preview_params)]
        TB[(o_theme_block<br/>各页面积木实例)]
    end

    subgraph Publish["商家编辑 / 发布(可选)"]
        EDIT[改 preview_*]
        PUB[publish → params / position 同步]
    end

    subgraph Front["前台访客浏览"]
        SEL[HomePublicVarsService 选默认皮肤]
        GL[读 o_store_theme.params]
        BL[TagGetBlocks → o_theme_block]
        REN[HomeBaseController::fetch + Liquid]
    end

    SS --> INIT
    SD --> INIT
    INS --> ROW --> INIT
    INIT --> ST
    INIT --> TB

    EDIT --> ST & TB
    PUB --> ST & TB

    SEL --> ST
    SEL --> BL
    GL --> REN
    BL --> REN
    LQ --> REN
    REN --> HTML[HTML 展示给用户]

1.2 阶段 A:安装皮肤 → initThemeData

触发入口

场景调用链
商家安装 file 皮肤POST themes/ThemesService::install() → 事务内插入 o_store_theme立即 initThemeData($name, 'file', $themeId)
新店铺默认皮肤initDefaultTheme() → 同上,name=default,且 default=1
商家安装 database 皮肤install() 只插入 o_store_theme;ThemeTool 同步 asset 后 → POST themes/{id}/assetsyncfinishthemeAssetSyncFinish() → 再调 initThemeData()
修改 settings_data.json 且店铺尚未有 paramsinitThemeDataBySettingData()(themeasset 更新回调等)

initThemeData() 读取 config(ThemesService.php

  1. settings_schema.json + settings_data.json(file 读磁盘,database 读 o_theme_asset
  2. 各 section 的 {% schema %}getThemeSectionsConfig*,用于 is_global 判断与缺省 default

写入哪些表

Model写入内容来源
o_store_themeStoreThemeModelparamspreview_params ← JSON 字符串settings_data.jsonglobal 整段(含 sections.header/footer、全局间距/颜色等)
o_theme_blockThemeBlockModel多行积木块记录遍历 settings_schema.pages_setting 每页每个 section;跳过 is_global=true(header/footer 不进此表)

o_theme_block 单行字段来源

字段取值
store_id / theme_id当前店铺与刚安装的主题 ID
route / route_handle来自 pages_setting 该项
typesection 类型,如 block_slidesproduct_detail
fixedpages_settingfixed → 1 top / 2 drag / 3 bottom / 4 require
paramspreview_params优先 settings_data.pages_settings[route_routeHandle][type] 第一条;否则 section liquid schema 的 default
positionpreview_position安装顺序递增
group_id默认 0(默认组)
published_at安装时设为 time()(初始数据即视为已发布态)

安装前会先 DELETEtheme_id 下已有 o_theme_block,再 saveAll 批量插入。

不写入 DB 的部分

  • settings_schema.jsonsettings_data.json 原文件仍留在皮肤目录 / o_theme_asset(供后台 GET themes/{id}/configs、主题升级 merge 用)
  • Liquid 模板(layout、sections 等)仍在磁盘或 o_theme_asset复制进 o_theme_block

1.3 阶段 B:商家装修与发布(简述)

安装完成后,商家可在后台改装修;改动先写 预览字段,发布后才对访客生效:

数据编辑时发布后(访客可见)
全局o_store_theme.preview_paramso_store_theme.params
页面积木o_theme_block.preview_paramspreview_positionparamsposition

POST themes/{id}/publishmentsThemesService::publish(),并清 Liquid/Redis 缓存。

1.4 阶段 C:前台访客浏览 → 渲染

sequenceDiagram
    participant U as 访客
    participant HPV as HomePublicVarsService
    participant ST as o_store_theme
    participant TB as o_theme_block
    participant HBC as HomeBaseController
    participant LQ as Liquid

    U->>HPV: HTTP 请求进入 app/home
    HPV->>ST: setThemeDir() 选 theme_id<br/>(默认 default=1 的皮肤)
    HPV->>ST: setThemeGlobalData() 读 params → theme_config.global
    U->>HBC: Controller → fetch()
    HBC->>LQ: 渲染 layout/theme.liquid
    LQ->>ST: header/footer 用 theme_config.global.sections
    LQ->>TB: TagGetBlocks → themeData → list(preview=0)
    LQ->>LQ: block_main → 各 section liquid
    LQ->>U: HTML

选哪套皮肤setThemeDir() 优先级):

  1. URL ?theme_id=
  2. Cookie 预览主题
  3. 域名绑定主题
  4. o_store_themedefault=1 且已发布的主题ThemesService::default()

读哪些装修数据

用途数据来源说明
全局 header/footer、整站样式o_store_theme.params(正常访客 theme_preview=0注入 Context->theme_config.global
当前页中间积木列表o_theme_blockpublished_at>0group_id=0 或组装修匹配)TagGetBlocksThemesService::themeData()
页面 HTML 结构皮肤 layout/templates/sections/theme.nametheme_dir)或 database asset 加载

与 config 文件的关系:访客请求 不直接读 settings_schema.json / settings_data.json;仅当 DB 中缺字段时,部分逻辑会用 settings_data深合并补全(如 getThemeGlobalData() 在 API/内部链路中)。

1.5 一张表对照:config 字段 → DB → 前台

config / 皮肤文件安装后进 DB前台谁读
settings_data.globalo_store_theme.paramstheme_config.global、layout 中 header/footer section
settings_data.pages_settings + pages_setting 结构o_theme_block 多行TagGetBlocksblocksblock_main.liquid
settings_schema.global_setting不单独存表(值已在 global JSON 里)仅后台表单定义;前台用已保存的 global 值
settings_schema.pages_setting / blocks_setting不存表后台编辑器;pages_setting 参与 init 与 sectionsFormat
sections/*.liquid不存表Liquid 渲染引擎按 type 加载模板

更细的 schema 子类型说明见 settings-schema-reference.md


二、架构总览

装修模块采用 「Liquid 模板 + JSON 配置 + DB 积木块实例」 三层结构:

flowchart TB
    subgraph Backend["后台(app/api)"]
        Editor[装修编辑器]
        API[Themes / ThemeGroup / ThemeView API]
        TS[ThemesService]
        TBS[ThemeBlockService]
        TAS[ThemeAssetService]
    end

    subgraph Storage["存储"]
        ST[(o_store_theme)]
        TB[(o_theme_block)]
        TA[(o_theme_asset)]
        TRG[(o_theme_route_group)]
        Disk["public/theme/{name}/"]
    end

    subgraph Frontend["前台(app/home)"]
        HPV[HomePublicVarsService]
        Fetch[HomeBaseController::fetch]
        LQ[Liquid Template + Tags]
    end

    Editor --> API --> TS
    TS --> ST & TB & TA & TRG
    TS --> Disk

    HTTP[用户请求] --> HPV --> Fetch --> LQ
    LQ --> TB
    LQ --> ST
    LQ --> Disk
    LQ --> TA
层次职责典型位置
主题皮(Liquid 文件)定义 section/template/snippet 的 HTML 结构与 {% schema %} 表单public/theme/default/o_theme_asset
主题配置(JSON)装修后台 UI schema、页面默认 section 列表、全局默认值config/settings_*.json
店铺装修数据(DB)商家拖拽后的积木块实例、全局样式、分组/视角绑定o_store_themeo_theme_block

三、核心数据表

3.1 o_store_theme(店铺已安装主题)

字段说明
store_id租户隔离
sys_theme_id系统主题市场 ID(可选)
typepublic(共享皮)/ private(店铺私有)
name主题目录名,如 defaultbrooklyndbtheme_123
file_systemfile:读磁盘;database:读 o_theme_asset + OSS
default是否店铺默认主题(前台无指定时用它)
params / preview_params全局装修 JSON(header/footer 等 global sections
publish_at最近发布时间
theme_admin_id域名管理员隔离(可选)

Model:common/models/StoreThemeModel.php

3.2 o_theme_block(页面积木块实例)

字段说明
theme_id所属主题
route / route_handle页面路由,如 index/indexproduct/detail + 商品 handle
type对应 sections/{type}.liquid 文件名
params / preview_params积木块配置 JSON(settings、blocks、display 等)
position / preview_position排序
fixed1=top / 2=drag / 3=bottom / 4=require
group_id0 = 默认组;>0 = 组装修专属
status1 正常 / 0 预览态软删
published_at>0 表示曾发布过

注意is_global=true 的 section(如 header/footer)不写入 o_theme_block,运行时从 o_store_theme.paramssettings_data.json 合并注入。

Model:common/models/ThemeBlockModel.php

3.3 o_theme_asset(database 主题文件)

字段说明
theme_id主题 ID
folder/config/sections/layout/templates/assets
file_nameheader.liquidsettings_schema.json
content小文件 inline 内容
content_oss_bucket / public_url大资源走 OSS

Service:common/services/ThemeAssetService.php

3.4 组装修与视角

作用
o_theme_route_group分组定义(名称、obj_type
o_theme_route_group_item对象与分组绑定(同一 obj 在同一 theme 下只属一组)
o_theme_view视角定义 + 受众条件(设备/国家/UTM)

支持的 obj_type(8 种):PRODUCTCOLLECTIONTOPICBLOGCOUPONPROMOTIONNEWSACCOUNT

专题文档:


四、主题文件目录与 Liquid 配置

基准路径:public/theme/default/(其他主题通过 theme_dir 覆盖同名路径)。

public/theme/default/
├── config/                 # 主题级 JSON 配置
│   ├── settings_schema.json
│   ├── settings_data.json
│   └── settings_cblock_schema.json
├── layout/                 # 整页外壳
│   ├── theme.liquid        # 默认 layout
│   ├── template.liquid     # 仅 {% template %},用于 quick view 等
│   └── checkout.liquid     # 结算(强制 theme_dir=system)
├── templates/              # 页面主体模板
│   ├── index.liquid
│   ├── product_detail.liquid
│   └── block_main.liquid   # 循环渲染 blocks
├── sections/               # 可配置积木块(含 {% schema %})
│   ├── header.liquid
│   ├── footer.liquid
│   └── ...
├── snippets/               # {% include %} 片段
├── assets/                 # CSS/JS/图片(asset_url filter)
├── locales/                # 多语言 JSON
└── lib/                    # 前端脚本与样式模块

4.1 settings_schema.json

装修后台编辑器 UI 的元数据,主要块:

⚡⚡⚡ 一句话概括:schema = 装修编辑器的配置项清单(能改什么)

|| 键 | 作用 | ||----|------| || global_setting | 全局样式表单项(间距、宽度、颜色、开关等),对应「主题设置 → 常规」 | || pages_setting | 各 route 可安装的 section 列表及 fixed(top/drag/bottom/require);安装主题时用于初始化 o_theme_block | || blocks_setting | 可拖拽「内容积木库」分类(sections[].id = block type) | || default_blocks_setting | 与 default 主题 merge 策略(mode=none 时不 merge) |

读取:ThemesService::getThemeConfig()getThemeConfigFromFile() / getThemeConfigFromDatabase()

详细说明(各子类型结构、后台页面对应、merge 逻辑):settings-schema-reference.md

4.2 settings_data.json

主题出厂默认装修数据

⚡⚡⚡ 一句话概括:data = 主题自带的默认装修效果(默认长什么样)

|| 键 | 作用 | ||----|------| || global | 全局 section 默认值(如 global.sections.header.settings) | || pages_settings | 各页面 route 下各 section 的默认 params |

与店铺 o_store_theme.params 深合并getThemeGlobalData()),得到前台 theme_config.global

4.3 settings_cblock_schema.json

内容积木(cblock) schema:轮播、单商品等可复用内容块,供编辑器「内容库」选用。

4.4 Section 内 {% schema %}...{% endschema %}

每个 sections/{type}.liquid 末尾的 JSON 定义:

  • nametagclass
  • is_global:true 时不进 o_theme_block,走全局 params
  • settings[]:该 section 的表单项定义
  • blocks[]:section 内子块定义
  • default:默认 params(安装与缺省合并用)

解析:ThemesService::getThemeSectionJsonStr()(正则提取)→ getThemeSectionsConfig()


五、两种主题存储:file vs database

维度file_system=filefile_system=database
物理位置public/theme/{name}/o_theme_asset + OSS
安装后立即 initThemeData()等 ThemeTool 同步 asset → themeAssetSyncFinish
类型通常 type=public通常 type=private(复制/二开)
前台 LiquidOemsaasLocal 读磁盘OemsaasDatabase 读 DB,缺失回退 default 磁盘
静态资源 URL/theme/{dir}/assets/...OemsaasFilter::asset_url 走 OSS public_url
复制主题copy() 扫磁盘写入 DBcopyThemeContent 复制行 + OSS

Fallback 规则(database 模式):config、sections、layout 等若 DB 无记录,回退 public/theme/default/ 同名文件。

完整 database 流程(ThemeTool 同步、Redis、OemsaasDatabase、排障):database-theme-flow.md


六、后台 API 与业务流程

路由定义:app/api/route/route.php(组名 themesthemes-groupthemes-viewthemeasset)。

6.1 主题生命周期

安装 POST themes/ThemesService::install()

1. 插入 o_store_theme(file→public,database→private)
2. file_system=file  → initThemeData(name, file, themeId)
3. file_system=database → 等 asset 同步后 POST themes/{id}/assetsyncfinish
4. initThemeData 流程:
   a. 读 settings_schema + settings_data + 各 section schema
   b. 写 store_theme.params / preview_params ← setting_data.global
   c. 清空旧 blocks,按 pages_setting 逐页插入 o_theme_block(跳过 is_global section)

编辑(预览态)

操作API写入字段
改全局设置PUT themes/{id}/globaldatapreview_params
增/改 sectionPOST/PUT themes/{id}/sections*preview_params / preview_position
删 sectionDELETE themes/{id}/sections*status = PREVIEW_DELETE(软删,发布时物理删除)
批量排序POST themes/{id}/sections/batchsavepreview_position
拉编辑器数据GET themes/{id}/data固定 preview=1(只看草稿)

发布 POST themes/{id}/publishmentsThemesService::publish()

事务内:
  store_theme.params ← preview_params
  theme_block.position ← preview_position, params ← preview_params, published_at=now
  物理删除 status=PREVIEW_DELETE 的 block
  可选 default=1 取消其他主题 default
清缓存:themeData('default')、themeSections('default')、LiquidCacheService::clearLiquidRenderCache()

重置 POST themes/{id}/reset

撤销未发布编辑:preview_* ← 已发布字段;删除 published_at=0 的新增 block。

复制

  • copy():private/database 副本,复制 blocks、route groups、assets、languages
  • copyThemeForPublic():public/file 副本,复制 blocks/groups,不复制磁盘 asset

6.2 预览 vs 已发布(双轨字段)

实体预览(编辑器 / 带 Cookie 的前台)已发布(正常访客)
全局preview_paramsparams
积木块参数preview_paramsparams
积木块排序preview_positionposition
删除status=PREVIEW_DELETE(发布时物理删)

前台 preview 开关:HomePublicVarsService::setThemeDir() 读取
COOKIE_FIREWALL_IS_THEME_EDIT / ?is_theme_edit=Context->theme_preview

6.3 组装修 API(themes-group

详见 route-group-decoration.md

能力说明
创建组复制默认组或指定组的 blocks 到新 group_id
绑定对象theme_route_group_item;同一 obj 会先解绑旧组
查组getGroupByObjIdAndType / getGroupByHandlerAndRoute

6.4 视角 API(themes-view

详见 theme-view-ab-display.md

每主题最多 20 个视角。视角 paramsAbPlanService::checkParams() 匹配;积木 params.display 绑定可见视角 ID。


七、前台渲染完整管线

7.1 请求 → Context 预热

HTTP 请求
  → middleware: RateLimit / DomainAnalysis(写入 storeId、storeInfo)
  → HomeBaseController::initialize()
       → HomePublicVarsService::handle()
            setRoutes()      → Context->routes(current_route、current_route_handle)
            setThemeDir()    → theme_id、theme_dir、theme_preview
            setThemeGlobalData() → theme_config.global
       → langHandle / customerHandle / gateWay ...
  → 业务 Controller::action() → fetch($file, $data)

说明:注释中的「middleware HomePublicVariable」实际对应 HomePublicVarsService,在 initialize() 调用,非独立中间件。

7.2 主题选取优先级(setThemeDir

  1. URL ?theme_id=
  2. Cookie COOKIE_FIREWALL_THEME_ID
  3. 域名绑定主题 getDomianThemeId()
  4. 店铺 default=1 主题

写入 Contextthemetheme_dir(= theme.name)、theme_idtheme_previewis_theme_edit

7.3 HomeBaseController::fetch() 步骤

public function fetch(string $file = '', array $data = [], bool $display = true, string $content = '', $log = false)
步骤逻辑
1Liquid 全局:INCLUDE_SUFFIX=liquidESCAPE_BY_DEFAULT=false
2选 layout:''theme.liquid'template'template.liquid'checkout'checkout.liquid
3theme_dir 覆盖路径:theme/defaulttheme/{theme_dir}
4file_system=database:预载 ThemeAssetService::getThemeAssetsByDatabase();layout 可从 DB 读 /layout/*.liquid
5结算页强制 theme_dir=system
6handlerTemplate($data) → 设置 Context->template
7mergeData($data) → 合并整棵 Context + 控制器数据
8variableEncrypt($data) → 前端追踪变量包
9AST 文件缓存 liquidExtend\file\File($themeName)?liquid_cache= 可关)
10注册 OemsaasFilter + extend/liquidExtend/tags/*.php
11parse + render
12注入 Context->stylesheet / javascript</head> / </body>

7.4 两层 Liquid:Layout + Template

Layoutlayout/theme.liquid):

<header>
  {% section 'header', section:theme_config.global.sections.header, cached:true %}
</header>
<main>
  {% template %}
</main>
<footer>
  {% section 'footer', section:theme_config.global.sections.footer, cached:true %}
</footer>

Template(如 templates/index.liquid):

{% get_blocks route={routes.current_route} route_handle={routes.current_route_handle} limit=80 %}
{% template block_main, { blocks:blocks } %}

Context->template 来源handlerTemplate):

  • 控制器 $data['template'] 优先(如 Product::detailproduct_detail
  • 否则 controller_actionindex_index 映射为 index

7.5 积木块渲染链

TagGetBlocks
  → ThemesService::themeData(theme_id, route, route_handle, theme_preview, ..., themeGroupExtData)
  → ThemeBlockService::list / listByGroupId
  → sectionsFormat()(注入 fixed/global section、补全缺失 fixed 块)
  → TagGetBlocks::view()(视角 display 过滤)
  → context.blocks

block_main.liquid 循环 blocks
  → {% section section_name, { section: block.params, block_id, ... } %}
  → TagSection 读 sections/{type}.liquid 渲染

7.6 组装修前台匹配(ThemesService::themeData

完整流程见 route-group-decoration.md § 六

route 不在 supportGroupRouteList → themeDataByHandler(group_id=0)

group_id === 0(显式默认组)→ themeDataByHandler

group_id 为空:
  obj_id ← 请求上下文 / getObjIdByHandler(objType, routeHandle)
  group_id ← ThemeRouteGroupCacheService::getGroupIdByObjIdAndType()

group_id 仍空 → themeDataByHandler(fallback 默认组 blocks)

有 group_id → themeDataByGroup → 读该 group 下 blocks

TagGetBlocks 可通过 obj_idgroup_id 参数影响匹配(装修编辑器左侧预览商品区场景)。

7.7 视角(ThemeView)前台逻辑

完整说明见 theme-view-ab-display.md

  1. view_id ← URL ?view_id=getPatchViewId()AbPlanService::checkParams 遍历视角)
  2. block 的 params.display 为视角 id 列表时:当前视角在列表内 → 显示;不在 → 隐藏(display=true
  3. 装修模式(is_theme_edit=1)额外校验 device 参数

7.8 Block 预览接口

GET/POST /block/:id/block/type/:typeapp/home/controller/Block.php::rendering()

  • idgetThemeBlockParamsById()preview_params
  • 渲染:display('{% section {type}, { section, block_render: 1 } %}')
  • block_render=1TagStylesheet/TagJavascript 内联输出(AJAX 片段)

八、Liquid 文件加载机制

8.1 FileSystem 实现

路径用途
OemsaasLocalextend/liquidExtend/fileSystem/OemsaasLocal.php磁盘主题
OemsaasDatabaseextend/liquidExtend/fileSystem/OemsaasDatabase.phpDB 主题

OemsaasLocal::fullPath()

  1. 先查 public/theme/{theme_dir}/{type}/{name}.liquid
  2. 不存在则回退 public/theme/default/...

OemsaasDatabase::readTemplateFile()

  1. Context->themeAssets/sections/header.liquid 等路径取 content
  2. 无内容则 fullPath() 回退磁盘

8.2 各类型由谁加载

类型目录加载入口
Layoutlayout/fetch() 直接 file_get_contents 或 DB content
Templatetemplates/TagTemplate
Sectionsections/TagSection
Snippetsnippets/TagInclude(Template 构造时传入 include 根路径)

8.3 自定义 Tag 注册

fetch() 扫描 extend/liquidExtend/tags/Tag*.php,文件名 snake_case 即为 tag 名:

Tag作用
template渲染 templates;集成整页 Redis 片段缓存
section渲染 sections;支持 cached / cache_key
include渲染 snippets
get_blocks从 DB 拉 blocks 列表
schema / endschema解析期块,render 输出空
stylesheet / javascriptCSS/JS 收集到 Context 或内联
get_productsget_productget_collections40+ 数据拉取 tag

Filter:extend/liquidExtend/filters/OemsaasFilter.phpasset_urlformat_price、图片域名等)。


九、缓存层次

层级实现说明
Liquid ASTliquidExtend\file\Fileruntime/liquid/cache/{themeName}/,md5(source),约 90 天
整页/片段 HTMLLiquidCacheServiceindex、collection_detail、header 等 Redis 缓存;编辑模式/B 站跳过
theme sectionsCacheKeyHelper::themeSections('default')默认主题非 preview 时 1 天
theme assetsThemeAssetServicethemeFilesthemeAssets Redis hash
route groupThemeRouteGroupCacheServiceobj_id → group_id

发布主题时:LiquidCacheService::clearLiquidRenderCache() + 相关 Redis key 删除。


十、关键类与方法索引

组件路径关键方法
主题编排common/services/ThemesService.phpinstall, publish, reset, initThemeData, themeData, getThemeConfig, sectionsFormat
积木块common/services/ThemeBlockService.phpadd, update, list, listFormat, saveAll
主题资产common/services/ThemeAssetService.phpgetThemeAssetsByDatabase, getContentsByFolder, copyThemeContent
组匹配common/services/ThemeRouteGroupHandlerService.phpgetGroupByObjIdAndType, getObjIdByHandler, objTypeToRoute
视角common/services/ThemeViewService.phpgetPatchViewId, add, del
前台变量common/services/HomePublicVarsService.phphandle, setThemeDir, setThemeGlobalData
渲染入口app/home/HomeBaseController.phpfetch, handlerTemplate, mergeData, variableEncrypt
积木 tagextend/liquidExtend/tags/TagGetBlocks.phprender, view
模板 tagextend/liquidExtend/tags/TagTemplate.phprender + 页面缓存
后台 APIapp/api/controller/Themes.phpinstall, publish, themeData, blockAdd

十一、端到端时序(访客访问首页)

sequenceDiagram
    participant U as 访客
    participant C as Index Controller
    participant H as HomeBaseController
    participant G as TagGetBlocks
    participant T as TagTemplate
    participant S as TagSection

    U->>C: GET /
    C->>H: fetch('theme', data)
    H->>H: parse theme.liquid
    H->>S: section header(global params)
    H->>T: template → index.liquid
    T->>G: get_blocks index/index
    G->>G: themeData → o_theme_block
    T->>T: block_main.liquid
    T->>S: 各 drag section
    H->>S: section footer
    H->>U: HTML

十二、扩展:新增组装修对象类型

最小改动:

  1. ThemeRouteGroupModel::getAllObjTypeList() — 增加常量
  2. ThemeRouteGroupHandlerService::objTypeToRoute() — 路由映射
  3. getObjIdByHandler() / getObjIdByRawHandler() — handle → obj_id
  4. defaultGroupFirstItem() — 默认组预览项
  5. 按需新增业务 Service/Model

十三、排障 checklist

现象排查点
前台仍是旧装修是否发布;theme_preview;Redis/Liquid 缓存
某商品页装修不对theme_route_group_item 绑定;TagGetBlocks 的 obj_id/group_id
database 主题缺模板o_theme_asset 是否有对应 folder;default 磁盘 fallback
编辑器与线上一致编辑器 themeData 固定 preview=1;访客读 params
组装修不生效route 是否在 supportGroupRouteList;group 是否已发布 blocks

维护:主题/Liquid 行为变更时,同步更新本文与 README.mdREADME.md