简介
本文件面向 DouPHP 小程序“门店查询”功能,聚焦门店列表、门店详情、分类筛选、分页加载等能力,并给出地图导航集成的扩展方案。文档覆盖前端页面实现、后端控制器与服务层逻辑、数据模型与路由、以及 LBS(高德/百度地图)集成建议与常见问题处理。
项目结构
- 小程序端
- 门店列表页:pages/store/store.*(wxml/ts/wxss/json)
- 门店详情页:pages/store/show.*(wxml/ts/wxss/json)
- 后端 API
- 门店列表/详情:'module/store/api/controller/store/StoreController.php
- 业务服务:'module/store/front/service/store/StoreService.php
- 数据模型:'module/store/front/model/store/Store.php
- 前端展示(可选)
- 前台控制器:'module/store/front/controller/store/StoreController.php(用于 Web 模板渲染)
graph TB
subgraph "小程序"
A["store.ts"] --> B["store.wxml"]
C["show.ts"] --> D["show.wxml"]
end
subgraph "后端API"
E["StoreController.php(API)"]
F["StoreService.php"]
G["Store.php(模型)"]
end
A --> E
C --> E
E --> F
F --> G
核心组件
- 小程序门店列表页
- 分类切换、滚动导航、列表渲染、分页加载、错误提示
- 小程序门店详情页
- 获取门店详情、标题与分享信息设置
- 后端 API 控制器
- 列表接口:支持 class 过滤、分页
- 详情接口:按 id 获取已发布门店,返回 store 与自定义字段
- 业务服务 StoreService
- 列表构建:分页、排序、摘要截取、分类列表
- 详情构建:Markdown 内容渲染、点击量自增
- 数据模型 Store
- 已发布筛选、按 class 过滤、默认排序、多语言与附件 URL 格式化
架构总览
小程序通过 http.get 调用后端 store 与 store.show 两个接口;后端控制器将请求参数交给 StoreService,服务层基于 Store 模型进行查询与格式化,最终返回统一响应。
sequenceDiagram
participant U as "用户"
participant P as "小程序(store.ts)"
participant API as "StoreController(API)"
participant S as "StoreService"
participant M as "Store(模型)"
U->>P : 打开门店列表
P->>API : GET /store?page&class
API->>S : buildStoreListData(page,class,pageSize)
S->>M : filterByClass + applyDefaultOrder + paginate
M-->>S : 列表数据
S-->>API : 组装后的列表+分页+分类
API-->>P : 成功响应
P->>P : 渲染列表/分类/分页
U->>P : 点击某门店
P->>API : GET /store.show?id
API->>S : buildStoreShowData(id)
S->>M : findPublishedById
M-->>S : 门店详情
S-->>API : store + defined
API-->>P : 成功响应
P->>P : 渲染详情
详细组件分析
小程序门店列表页(store.ts / store.wxml)
- 功能要点
- 顶部分类导航:全部与各分类项,点击切换 class 并重置分页
- 列表渲染:图片、名称、地址、电话
- 分页加载:触底延迟加载下一页,避免频繁请求
- 错误处理:网络异常时提示
- 关键流程
- onLoad 初始化标题与共享配置,绑定全局状态
- loadData 发起 HTTP 请求,合并或替换列表数据
- onReachBottom 触发下一页加载
- scrollLeft 计算横向滚动位置以高亮当前分类
flowchart TD
Start(["进入门店列表"]) --> Init["初始化标题/共享配置"]
Init --> LoadData["loadData() 请求列表"]
LoadData --> Render{"是否拼接下一页?"}
Render --> |是| Concat["合并到现有列表"]
Render --> |否| Replace["替换为最新列表"]
Concat --> Done["渲染完成"]
Replace --> Done
Done --> Bottom{"触底?"}
Bottom --> |是| NextPage["page+1 延迟加载"]
Bottom --> |否| End(["结束"])
NextPage --> LoadData
小程序门店详情页(show.ts / show.wxml)
- 功能要点
- 根据 id 拉取详情,设置分享标题
- 展示名称、图片、分类、地址、电话、富文本内容
- 关键流程
- onLoad 中调用 store.show 接口
- 成功后更新 store 数据并设置分享标题
sequenceDiagram
participant P as "小程序(show.ts)"
participant API as "StoreController(API)"
participant S as "StoreService"
participant M as "Store(模型)"
P->>API : GET /store.show?id
API->>S : buildStoreShowData(id)
S->>M : findPublishedById(id)
M-->>S : 门店详情
S-->>API : store + defined
API-->>P : 成功响应
P->>P : 渲染详情/设置分享标题
后端 API 控制器(StoreController.php)
- 列表接口 index
- 接收 page、pageSize、class,校验 class 非法字符
- 调用 StoreService.buildStoreListData 返回统一响应
- 详情接口 show
- 解析 route id,校验有效性
- 调用 StoreService.buildStoreShowData 返回 store 与定义字段
业务服务(StoreService.php)
- 列表构建
- 读取分页配置,构造 get 参数
- 使用 Store::filterByClass + applyDefaultOrder + paginate
- 对 description 做摘要截取,返回 store_list、pager、class_list
- 详情构建
- 校验 id,查询已发布门店
- Markdown 渲染 content,自增 click,返回 store 与 defined
数据模型(Store.php)
- 已发布筛选 scopePublished
- 按 class 过滤 scopeFilterByClass
- 默认排序 scopeApplyDefaultOrder
- 单条查询 findPublishedById
- 列表字段格式化:image 附件 URL、defined 键值对、created_at 日期格式
- 多语言字段:name/class/content/keywords/description
依赖关系分析
- 小程序 store.ts 依赖 http 服务与 route 工具,调用后端 store 与 store.show
- 小程序 show.ts 依赖 http 服务与 route 工具,调用 store.show
- 后端 StoreController 依赖 StoreService
- StoreService 依赖 Store 模型与 MarkdownRenderer
- Store 模型依赖框架 ORM、多语言、附件、URL 生成等基础能力
graph LR
ST["store.ts"] --> API["StoreController(API)"]
SH["show.ts"] --> API
API --> SV["StoreService"]
SV --> MD["Store(模型)"]
性能与体验优化
- 列表分页与节流
- 使用 pageSize 控制每页数量,默认由配置决定
- 触底加载增加短暂延迟,降低并发请求压力
- 分类导航滚动定位
- 通过计算累计宽度设置 scroll-left,提升交互流畅度
- 详情内容渲染
- 服务端 Markdown 转 HTML,减少客户端渲染成本
- 图片与资源
- 列表图片使用固定高度模式,利于布局稳定与预加载
- 缓存策略(建议)
- 对分类列表与热门门店可做短期本地缓存,减少重复请求
常见问题与排错
- 分类切换后未刷新列表
- 检查 douMenu 是否正确重置 page 与 nomore,并重新调用 loadData
- 确认传入的 class 参数未被过滤为空
- 分页无效或重复加载
- 检查 onReachBottom 中的 nomore 判断与 page 递增逻辑
- 确保后端返回的 store_list 非空且分页正确
- 详情页面无法显示
- 检查 store.show 接口是否返回有效 id
- 确认后端 findPublishedById 能查到已发布门店
- 富文本内容空白
- 确认后端 MarkdownRenderer 正常执行,content 字段存在
- 分享标题不正确
- 在 onLoad 中设置 shareTitle,并在 onShareAppMessage 中返回
结论
当前门店查询功能已具备完整的列表与详情能力,支持分类筛选与分页加载。若需增强 LBS 能力(地图、导航、距离计算),可在现有 store.show 数据结构基础上扩展经纬度字段,并在小程序端接入地图 SDK 与导航跳转能力。
附录:接口与开发示例
接口定义
- 门店列表
- 路径:/store
- 方法:GET
- 参数:page(默认1)、pageSize(可选,0表示使用配置)、class(可选,字符串)
- 返回:store_list、pager、class_list、title、class
- 门店详情
- 路径:/store.show
- 方法:GET
- 参数:id(必填)
- 返回:store(含 name、address、mobile、image、class、content 等)、title、defined(可选)
小程序调用示例
- 列表加载
- 使用 http.get(route('store'), { class, page }) 获取数据
- 首次加载替换 store_list,加载更多时 concat 追加
- 详情加载
- 使用 http.get(route('store.show', { id })) 获取详情
- 成功后设置 store 与分享标题
地图与导航集成(扩展方案)
- 数据准备
- 在 store 表中增加 latitude、longitude 字段(数值类型)
- 在服务层返回 store 时附带经纬度
- 小程序侧
- 使用 wx.openLocation 或 wx.createMapContext 展示地图标记
- 使用 wx.openLocation 或 wx.navigateToMiniProgram 调用系统地图导航
- 如需路线规划,可调用地图 SDK 的 Directions API(高德/百度)
- 地理编码
- 若仅有地址无坐标,可通过后端调用地图服务商的地理编码接口转换
- 将结果缓存至 store 表,减少重复调用