提交前自检清单
2026/10/26大约 6 分钟
提交前自检清单
插件从开发到启用,坑集中在命名、路由、注册、缓存、鉴权几处。发布或交付前逐项对照,能省掉大部分"装不上/白屏/404/接管不生效"的问题。
命名与清单
name匹配/^am_[a-z][a-z0-9_]*$/:am_前缀、后缀全小写、只含字母数字下划线。- 后缀至少 5 个字符(
MIN_NAME_SUFFIX_LEN = 5)。am_demo是预置历史示例,新建插件不要拿它当长度参照。 name不等于任何保留名(PluginManifestService::RESERVED_NAMES,含am_system/am_app/am_store/am_member等),且在库中全局唯一。- 目录名 ==
manifest.name== 命名空间末段(plugin\{name}\...)。三者不一致是最常见的装配失败原因。 path写plugin/{name},view_root、hook指向真实的视图目录与Plugin.php类。entry/manage_entry与实际页面路由一致;纯前台插件把manage_entry显式写成空串表示"无管理入口",不要漏字段导致误判。
入口菜单 code 前缀保留
app:entry: 是基座保留前缀,PluginRegistrar 会禁用插件自行声明此前缀的其它 code。顶级入口菜单由 PluginManager::syncEntry 在安装/启用/重载时独占写入,不要在 install.sql 里手写这条 INSERT(会与 syncEntry 冲突重复)。
路由与页面入口
- 页面入口(
/app/{name}、/app/{name}/manage)是无尾斜杠路径,注解生成不出来,必须在config/route.php用Route::get手动注册。 - 业务接口走类级
#[RouteGroup('/app/{name}')]+ 方法级#[Get]/#[Post],前缀自动拼接,不要在方法里重复写前缀。 - 免登录白名单
$noNeedLogin只放行真正需要公开的方法(页面入口、公开数据接口);涉及写操作或用户数据的接口不要图省事放进去。
后端注册(生命周期与 Registry)
- 所有静态注册(事件、服务接管、适配器、中间件、任务、队列消费、身份 provider、通道鉴权)一律写在
Plugin.php::boot(),且每个 worker 进程都会重复执行,注册逻辑必须幂等。 - 每个注册都传
owner(用插件名),卸载时基座按forgetByOwner($owner)反向清理。漏传 owner 会让卸载后残留注册。 ServiceRegistry::bind/extend的优先级:基座默认实现视为优先级 0,插件接管须priority > 0才盖得住。provide()的 Provider 类必须实现ServiceProviderInterface且覆写KEY,否则抛ApiException。- 用到的扩展通道要先确认对应特性开关为
true(config/plugin_extend.php:event/service/task/queue/push/composer默认开,middleware默认关)。用中间件却发现没生效,先查middleware_enabled。
控制器返回与鉴权口径
- 基座统一响应体是
{code, msg, total, data, page},由全局 helpersuccess()/warning()/error()(底层ret())生成,键名是msg不是message,前端拦截器只读msg。手写json([...])时用message是错的(am_demo的info就是这么写的偏离,别照抄)。 - 会员鉴权
MemberAuth的错误体用的是msg不是message:未登录{code:401,msg:'请先登录'}、非会员{code:403,msg:'当前账号无权访问会员内容'}。前端取错误文案时按这个键对齐。 - 队列
dispatch、推送toChannel、任务等"尽力而为"的 API 在依赖不可用时返回false并记日志,不抛异常。调用方要按返回值降级,不要当必然成功。
前端
- qiankun 生命周期挂出的全局名必须
=== manifest.name,否则基座loadMicroApp找不到、直接白屏。 - UMD 单 bundle,
build.lib.name必须等于manifest.name。 - 前台入口(
front)默认无 Bridge、可能无登录态:调用能力前先hasBridge()判空做降级,别假设一定能拿到登录态。 - 前台页面只依赖
sdk/http、sdk/bridge(判空)与通用组件,禁止依赖菜单/worktab 等后台模块。 - 独立运行的默认入口是
front(由resolveEntryMode决定),不是manage。管理入口靠基座 qiankun 免登注入。 - 脚手架装配保留
pnpm-lock.yaml;构建顺序vite build→vue-tsc --noEmit→post-build.ts,产物按VITE_PLUGIN_ROOT回写到插件public/assets。
数据与安装
一条边界
install.sql 只放插件自有业务表 + 这些表的种子数据。配置组、页面菜单、按钮权限、工作台小部件一律由程序声明式写入,不进 SQL。出处是 DatabaseService::cleanInstallSql 的注释。
装配思路:菜单声明、按钮自动、后台可见
- 菜单只设前端路由菜单:页面菜单用
PluginRegistrar::menu()声明路由 + 组件即可,不写install.sql。 - 按钮权限自动扫描:控制器方法上标
#[Permission(title, slug)],安装/启用时syncPermissionsFromControllers自动扫描派生、落库,无需手动维护按钮清单。 - 后台菜单管理同步可见:声明/扫描的结果都在基座
am_menu表,后台「菜单管理」立即可见、可编辑、可授权;漏扫可随时用/sys/menu/syncPermission补一遍。
- 表前缀用
__PREFIX__占位符({{DB_PREFIX}}等价);安装期另有{{APP_ID}}、{{APP_NAME}}、{{APP_ENTRY_PATH}},由DatabaseService::executeInstallSql替换。入口菜单地址用{{APP_ENTRY_PATH}}(不含 id)——迁移到别的实例时 id 会变而标识不变。 - 建表写幂等(
CREATE TABLE IF NOT EXISTS),种子数据按自然键 upsert(先查 id 再INSERT ... WHERE @id IS NULL);升级重跑不能报"已存在"。 - 业务表名以
{name}_前缀;纳入备份的表在Plugin::backupTables()显式声明。 - 不要把菜单 / 配置 / 权限 / 小部件的 INSERT 写进
install.sql。旧版本打包会生成这类幂等块,装到新站点就成了「SQL 导一份 + 程序再声明一份」两份数据;打包前基座调cleanInstallSql剥离残留块,恢复单一写入者。 - 带 Composer 依赖的在
manifest.composer.require声明,安装时基座按此装包、卸载清理(受composer_enabled控制)。
谁写什么、什么时候写
| 数据 | 写入者 | 时机 |
|---|---|---|
| 「管理入口」顶级菜单 | PluginManager::syncEntry(基座独占,插件不得自行声明 app:entry: 前缀) | 安装 / 启用 / 重载 |
| 页面菜单 | PluginRegistrar::menu(),parent_code 填 app:entry:{name} | afterInstall 和 afterEnable |
| 按钮权限 | PluginRegistrar::syncPermissionsFromControllers($name, [控制器目录]) 从 #[Permission] 注解派生;单条补写用 PluginRegistrar::permission() | 同上,afterUpgrade 也要跑 |
| 配置组 | PluginRegistrar::configSchema() | 同上 |
| 工作台小部件 | 插件自行 upsert Widget 模型(暂无 Registrar 方法,参考 am_member 的 registerWidgets()) | 同上 |
afterInstall与afterEnable都要调同一套注册:自定义插件创建时不跑install.sql,启用钩子是开发环境的主要注册时机。syncEntry在afterInstall之前已执行,入口菜单此时已存在,可安全挂子菜单。syncPermissionsFromControllers必须排在页面菜单声明之后——按钮只挂到已存在的页面上,推不出归属的只进报告、不建野节点。configSchema只增不删、已填值不覆盖;改字段名或删字段后要自行清理陈旧配置行。afterUpgrade跑完权限同步记得ReflectionCache::clear(),否则注解里新增的 slug 库里没有对应按钮行,非超管调用直接 403。- 例外:卸载备份生成的
restore.sql仍需还原现场,pluginConfigRowsToMigrationSql等导出函数保留给BackupService使用,插件侧无需关心。
缓存与生效
- 改控制器注解、
$noNeedLogin、路由后,Webman 反射/路由有缓存(app/cache/ReflectionCache.php),需清缓存或重启 worker 才生效。别在"改了没反应"上浪费时间,先清缓存。 - 改
config/plugin_extend.php开关同样要重启。
最小验证路径
装 → 启用 → 打开管理入口(看是否免登注入成功、菜单是否落地)→ 直访前台 /app/{name}(看无登录态能否渲染)→ 触发一次用到的扩展通道(事件/存储/通知…)看日志与 forgetByOwner 清理是否对称。
各机制细节见生命周期、清单 manifest、服务接管 ServiceRegistry、控制器与路由、Vue3 脚手架。
