加载中…
文档目录
平台特性适配

简介

本技术文档面向 DouPHP 小程序端(miniprogram)的平台特性适配,聚焦以下目标:

  • 统一网络请求封装与信封解析、缓存与去重、调试增强
  • 文件上传/删除的登录态校验与错误处理
  • 权限校验与降级策略(如工作台模块权限)
  • 本地存储持久化与版本失效机制
  • 运行环境探测与全局异常处理
  • 后端管理端的小程序代码包管理与配置同步
  • 通用列表查询能力(商品/文章等)为小程序页面提供数据模型

项目结构

小程序前端位于 miniprogram/default,包含服务层(http/upload/permission)、状态管理(stores)、工具(utils)、入口(app.ts)。后端管理端通过 admin 下的控制器与服务完成小程序代码包的管理与配置同步;API 层提供通用列表查询能力。

graph TB
subgraph "小程序前端"
A["app.ts<br/>应用启动/全局拦截"]
B["services/http.ts<br/>统一HTTP/缓存/去重"]
C["services/upload.ts<br/>选图/上传/删除"]
D["services/permission.ts<br/>权限校验"]
E["stores/auth.ts<br/>登录态/鉴权"]
F["stores/persist.ts<br/>持久化/版本失效"]
G["utils/env.ts<br/>调试环境探测"]
end
subgraph "后端管理端"
H["admin/controller/miniprogram/MiniprogramController.php"]
I["admin/service/miniprogram/MiniprogramService.php"]
end
subgraph "后端API"
J["api/service/miniprogram/MiniprogramCatalogQuery.php"]
end
A --> B
A --> E
A --> G
C --> E
D --> B
B --> J
H --> I

核心组件

  • 统一 HTTP 层:负责信封解析、默认头注入、拦截器、内存缓存与 TTL、GET 去重、调试增强、RESTful 方法伪装(PUT/DELETE)
  • 文件上传/删除:先确保登录,再调用 wx.uploadFile,并解析服务端信封返回图片列表
  • 权限校验:对特定模块(如工作台)进行后端权限校验,失败则降级回首页
  • 登录态 Store:集中管理 api_token/user_id/loginEd,支持恢复、登出、跳转登录页
  • 持久化 Store:带 schema_version 的本地存储读写,字段不兼容时自动失效旧缓存
  • 运行环境探测:区分开发/体验/正式版,控制 vConsole 与调试弹窗
  • 管理端小程序管理:列出/启用/删除小程序代码包,同步配置到云端
  • API 通用列表查询:按模块/分类/排序/分页生成小程序列表 ViewModel

架构总览

小程序前端通过 app.ts 注册全局错误与未捕获拒绝处理,初始化 store,并在 onLaunch 中设置窗口尺寸、导航栏高度、自动更新与调试开关。所有业务请求经 services/http.ts 统一发出,携带 Authorization 头与表单编码类型,服务端以标准信封响应。文件操作通过 services/upload.ts 封装,确保登录后调用 wx.uploadFile。权限相关逻辑在 services/permission.ts 中实现,结合路由表动态生成 URL。

sequenceDiagram
participant U as "用户"
participant APP as "app.ts"
participant HTTP as "services/http.ts"
participant AUTH as "stores/auth.ts"
participant API as "后端API"
U->>APP : 启动小程序
APP->>AUTH : 初始化/恢复登录态
APP->>APP : 计算导航栏高度/开启调试(可选)
U->>HTTP : 发起 GET/POST/PUT/DELETE
HTTP->>HTTP : 注入默认头/缓存/去重
HTTP->>API : 发送请求
API-->>HTTP : 返回信封(code/message/data/errors/request_id)
HTTP-->>APP : 成功返回data或抛出ApiError
alt UNAUTHORIZED
HTTP->>AUTH : 触发onError -> logout()
end

详细组件分析

统一 HTTP 层(services/http.ts)

  • 信封解析:严格读取 code/message/data/errors/request_id,code === 'OK' 视为成功
  • 默认头:Content-Type=application/x-www-form-urlencoded,Authorization: Bearer &lt;api_token>
  • 拦截器:onRequest/onSuccess/onError 可扩展
  • 缓存与去重:内存缓存 + TTL,GET 去重复用 Promise
  • RESTful 伪装:PUT/DELETE 通过 POST + _method 参数传递
  • 调试增强:非正式版开启 vConsole,异常时弹出堆栈并复制
flowchart TD
Start(["进入 request"]) --> BuildCfg["构建请求配置<br/>注入默认头"]
BuildCfg --> CacheCheck{"是否命中缓存?"}
CacheCheck --> |是| ReturnCache["返回缓存数据"]
CacheCheck --> |否| DedupeCheck{"是否重复请求?"}
DedupeCheck --> |是| ReturnInflight["复用飞行中Promise"]
DedupeCheck --> |否| WxReq["调用 wx.request"]
WxReq --> HttpCode{"HTTP 状态码有效?"}
HttpCode --> |否| ParseEnvHttp["解析信封并构造HTTP错误"]
HttpCode --> |是| ParseEnvelope["解析信封"]
ParseEnvHttp --> ErrorInterceptors["执行错误拦截器"]
ParseEnvelope --> Ok{"code === 'OK' ?"}
Ok --> |否| BizErr["构造业务错误"]
Ok --> |是| AttachMeta["附加不可枚举元信息"]
BizErr --> ErrorInterceptors
ErrorInterceptors --> Reject["reject ApiError"]
AttachMeta --> SuccessInterceptors["执行成功拦截器"]
SuccessInterceptors --> Resolve["resolve data"]

文件上传/删除(services/upload.ts)

  • 登录态前置:调用 authStore.ensureLogin(),未登录自动跳转登录页
  • 选图与上传:wx.chooseImage + wx.uploadFile,formData 携带 type/module/item_id/folder/draft_token
  • 信封解析:手动解析 res.data JSON,提取 img_list
  • 删除接口:调用 user/filedel,返回剩余图片列表
sequenceDiagram
participant UI as "页面"
participant UP as "upload.ts"
participant AUTH as "auth.ts"
participant WX as "微信API"
participant API as "后端API"
UI->>UP : filebox({dataset})
UP->>AUTH : ensureLogin()
AUTH-->>UP : true/false
alt 已登录
UP->>WX : chooseImage()
WX-->>UP : tempFilePaths
UP->>WX : uploadFile(url, formData, header)
WX-->>UP : {data : JSON}
UP->>UP : 解析信封/取img_list
UP-->>UI : callback(img_list)
else 未登录
UP-->>UI : 无回调
end

权限校验(services/permission.ts)

  • 动态路由:尝试 route('work.permission'),若路由不存在视为无权限,直接 switchTab 回首页
  • 后端校验:post 模块名,失败同样回首页,保证弱网/模块禁用时的稳定降级
flowchart TD
PStart["checkWorkPermission(module)"] --> TryRoute["尝试获取 work.permission 路由"]
TryRoute --> RouteOk{"路由存在?"}
RouteOk --> |否| SwitchHome["switchTab 首页"]
RouteOk --> |是| PostPerm["POST 模块名校验"]
PostPerm --> PermOk{"校验通过?"}
PermOk --> |否| SwitchHome
PermOk --> |是| Done["继续业务"]

登录态 Store(stores/auth.ts)

  • 持久化键:api_token、user_id、loginEd
  • 恢复流程:优先从 storage 读取,再拉取 user/index 聚合 flags(is_login/is_vip/is_work/is_distribution)
  • 确保登录:ensureLogin 未登录则跳转默认登录页
  • 登出:清除 storage 并重置状态
classDiagram
class AuthStore {
+string api_token
+string user_id
+boolean loginEd
+boolean is_login
+boolean is_vip
+boolean is_work
+boolean is_distribution
+hydrate() void
+applyAuthFlags(flags) void
+restore() Promise~void~
+ensureLogin(redirectUrl?) Promise~boolean~
+login(payload) void
+logout() void
}

持久化 Store(stores/persist.ts)

  • 版本化存储:写入 envelope{__v, data},读取时版本不匹配即丢弃旧缓存
  • 自动写回:autorun 订阅 selector,变化即持久化
  • 安全清理:clearPersisted 移除指定 key
flowchart TD
SStart["选择器变化"] --> ToJS["toJS(selector())"]
ToJS --> Save["savePersisted(key, data)"]
Save --> SetStorage["wx.setStorageSync('store:'+key, JSON.stringify({__v,data}))"]
SetStorage --> End["完成"]

运行环境与调试(utils/env.ts)

  • isDebugEnv:基于 wx.getAccountInfoSync().miniProgram.envVersion 判断是否为 release
  • isServerDebug:读取 site.ts 中的 debug_enable 镜像值
  • 用途:控制 vConsole 开启、调试弹窗展示

应用入口(app.ts)

  • 全局错误钩子:onError/onUnhandledRejection/onPageNotFound
  • 推广解析:onLaunch/onShow 解析 user_sn 并落本地缓存
  • 自动更新:getUpdateManager 检查并提示重启
  • 窗口尺寸:计算 statusBarHeight/navigationBarHeight/menuButtonHeight

管理端小程序管理(admin/controller/miniprogram/MiniprogramController.php & MiniprogramService.php)

  • 列表/安装/启用/删除:读取代码包目录,维护当前激活 slug,同步配置到云端
  • 系统参数:白名单参数更新后同步小程序配置
  • 元数据解析:从 app.wxss 头部注释解析 slug、截图等信息
sequenceDiagram
participant Admin as "管理员"
participant Ctrl as "MiniprogramController"
participant Svc as "MiniprogramService"
participant Cloud as "CloudService"
Admin->>Ctrl : 访问 小程序列表/安装/启用/删除
Ctrl->>Svc : listMiniprogramCodeSlugs()/enablePackage()/deletePackage()
Svc->>Cloud : changeMiniprogramConfigFile()/changeUpdateDate()
Svc-->>Ctrl : 返回结果
Ctrl-->>Admin : 渲染页面/重定向

API 通用列表查询(api/service/miniprogram/MiniprogramCatalogQuery.php)

  • 输入:module、category_id/id、num、sort
  • 过滤:按分类树过滤,product/article 强制 status=1
  • 输出:id/title/name/price/sale_price/click/stock/sales/sales_percentage/created_at/description/image/thumb/url/bor
  • 价格:调用 PricingService 计算优惠价
flowchart TD
QStart["listing(module, catId, num, sort)"] --> BuildQ["构建查询"]
BuildQ --> FilterCat{"catId 有效?"}
FilterCat --> |是| Subtree["subtree(category_table, catId)"]
FilterCat --> |否| SkipCat["跳过分类过滤"]
Subtree --> WhereCat["where category_id IN (...)"]
SkipCat --> StatusFilter
WhereCat --> StatusFilter{"module 为 product/article?"}
StatusFilter --> |是| WhereStatus["where status = 1"]
StatusFilter --> |否| OrderLimit
WhereStatus --> OrderLimit["order by sort,id DESC; limit num"]
OrderLimit --> Rows["查询 rows"]
Rows --> Map["映射为 ViewModel"]
Map --> QEnd["返回列表"]

依赖关系分析

  • 前端依赖:
    • http.ts 依赖 env.ts(调试环境判断)
    • upload.ts 依赖 auth.ts(登录态)与 http.ts(删除接口)
    • permission.ts 依赖 http.ts 与 utils/route.ts(动态路由)
    • app.ts 依赖 stores/auth.ts、services/http.ts、utils/env.ts
  • 后端依赖:
    • MiniprogramController 依赖 MiniprogramService 与 Cloud/CloudService
    • MiniprogramService 依赖 DB、FileHelper、Config、Cloud
    • MiniprogramCatalogQuery 依赖 DB、PricingService、Attachment、Url
graph LR
ENV["utils/env.ts"] --> HTTP["services/http.ts"]
AUTH["stores/auth.ts"] --> UPLOAD["services/upload.ts"]
HTTP --> UPLOAD
PERM["services/permission.ts"] --> HTTP
APP["app.ts"] --> HTTP
APP --> AUTH
CTRL["MiniprogramController.php"] --> SVC["MiniprogramService.php"]
SVC --> DB["DB"]
SVC --> CLOUD["CloudService"]
CAT["MiniprogramCatalogQuery.php"] --> PRICING["PricingService"]

性能考量

  • 网络层
    • GET 去重:相同请求在飞行中复用 Promise,减少重复网络开销
    • 内存缓存+TTL:适合读多写少的列表数据,降低首屏加载时间
    • 方法伪装:避免 PUT/DELETE 在 PHP $_POST 解析上的差异
  • 存储层
    • 版本化持久化:schema_version 变更自动失效旧缓存,避免脏数据
    • autorun 写回:仅当 selector 变化时写 storage,减少频繁 IO
  • 调试与监控
    • 非正式版开启 vConsole,便于定位问题
    • 服务端异常堆栈在调试期弹窗并支持复制,缩短排障路径

故障排查指南

  • 网络请求失败
    • 检查信封 code 是否为 'OK',关注 errors 与 request_id
    • 使用 getLastRequestId 关联服务端日志
    • 非正式版可开启 vConsole 查看完整请求/响应
  • 登录态异常
    • UNAUTHORIZED 会触发全局 onError 并 logout,确认后端 token 是否过期
    • 确保 Authorization 头正确注入
  • 文件上传失败
    • 确认已登录(ensureLogin),检查 wx.uploadFile 返回信封
    • 检查 formData 字段是否与后端期望一致
  • 权限校验失败
    • 若路由不存在,将降级回首页;检查模块是否启用
    • 后端校验失败同样回首页,需检查模块权限配置
  • 存储异常
    • 版本不匹配会导致旧缓存被丢弃,升级后需重新水合
    • 写 storage 失败会被静默忽略,注意关键路径的容错

结论

DouPHP 小程序端通过统一的 HTTP 层、严格的登录态管理、版本化的本地存储以及完善的调试与降级策略,构建了稳健的前端基础设施。配合后端管理端的小程序代码包管理与 API 通用列表查询,能够快速支撑多模块、多场景的小程序业务。建议在实际项目中遵循:

  • 始终通过 http.ts 发起请求,利用缓存与去重提升性能
  • 敏感操作前调用 ensureLogin,避免未登录态导致的抖动
  • 合理使用权限校验与降级,保证弱网/模块禁用时的用户体验
  • 利用调试环境快速定位问题,生产环境保持静默

附录

  • 常见适配要点
    • 网络请求:统一信封解析、方法伪装、缓存与去重
    • 文件上传:先登录再上传,手动解析信封
    • 权限管理:动态路由校验,失败降级回首页
    • 存储限制:版本化持久化,避免脏数据
    • 调试增强:非正式版开启 vConsole,异常弹窗辅助定位
  • 最佳实践
    • 列表数据优先使用 GET + cache/ttl
    • 大文件上传分片与进度反馈(可在 upload.ts 基础上扩展)
    • 权限不足时提供友好提示与引导(当前实现为降级回首页)
    • 升级 schema_version 时做好迁移与兼容性测试
添加日期:2026-10-05