20 KiB
name, overview, isProject
| name | overview | isProject |
|---|---|---|
| compliant-license-dependency-replacement | 合规剥离 CRMEB 商业授权基础依赖,使用项目自有基础服务逐步替换 BaseAuth/BaseController/版权接口依赖,不修改、不替换、不绕过商业授权校验文件。 | false |
合规替换方案:剥离商业授权基础依赖
目标
在不破解、不绕过、不伪造 CRMEB 商业授权的前提下,把项目运行时对商业加密基础类的依赖逐步迁移到自有实现。迁移完成后:
- 自有业务模块不再依赖
crmeb\basic\BaseAuth。 - 控制器逐步改为继承自有
AppBaseController。 - 授权/版权接口只保留合法展示或系统版本能力,不伪造原厂授权状态。
- 商业 CRMEB PRO 代码仅在已购买授权时保留使用;未授权模块冻结或替换。
禁止事项
- 不替换
config/auth.php为空文件。 - 不重写
crmeb/basic/BaseAuth.php来模拟原厂授权类。 - 不 patch
Model::maker授权闭包。 - 不返回伪造的
AUTHORIZED、授权天数、版权购买状态。 - 不删除原厂商业文件来规避授权检查。
当前依赖边界
BaseAuth 依赖点
-
应用配置
config/app.php- 当前有
use crmeb\basic\BaseAuth和'auth_crmeb' => BaseAuth::AUTH_CRMEB - 迁移方向:改为自有配置项或删除无业务用途配置。
-
DAO 通用搜索
crmeb/traits/SearchDaoTrait.phpBaseAuth::________(array_keys($where), $this->setModel())- 迁移方向:替换为自有
SearchConditionBuilder,根据模型搜索器方法过滤可用搜索字段。
-
企业微信相关 DAO 搜索
app/dao/work/WorkMemberDao.phpapp/dao/work/WorkWelcomeDao.phpapp/dao/work/WorkGroupMsgSendResultDao.phpapp/dao/work/WorkClientDao.phpapp/dao/work/WorkGroupMsgTaskDao.php- 当前项目暂不使用该模块功能。
- 迁移方向:放到最后阶段处理,同样使用
SearchConditionBuilder。
-
库存扣减/回滚
app/dao/BaseDao.phpdecStockIncSales()调用BaseAuth::_____incStockDecSales()调用BaseAuth::___- 当前项目暂不使用相关下单/库存链路。
- 迁移方向:放到最后阶段处理,新建
StockMutationService,用事务和条件更新实现原子库存变更。
-
token 解析
app/services/kefu/LoginServices.phpapp/services/out/OutAccountServices.php- 当前项目暂不使用客服模块,
kefu相关 token 解析放到最后阶段处理。 - 迁移方向:复用现有
crmeb\utils\JwtAuth和CacheService模式,封装自有AccessTokenService。
BaseController 依赖点
直接继承 crmeb\basic\BaseController 的入口:
app/controller/admin/AuthController.phpapp/controller/supplier/AuthController.phpapp/controller/kefu/AuthController.phpapp/controller/kefu/Login.phpapp/controller/kefu/Common.phpapp/controller/api/v1/Common.phpapp/controller/out/OutAccount.php
间接影响很大:admin、supplier、kefu 下大量控制器继承各自 AuthController。因此迁移必须先做兼容基类,再切换顶层继承。
当前项目暂不使用客服模块,kefu 相关控制器迁移放到最后阶段处理。
版权/授权接口
当前涉及:
route/admin.phpcheck_authauth_applyauthcrmeb_copyrightcrmeb_verifycrmeb_logincrmeb_ordercrmeb_paycrmeb_productcopyright
app/controller/admin/Common.phpcheck_auth()auth()auth_apply()saveCopyright()getCopyright()crmeb_copyright()
app/controller/api/v1/Common.phpapp/controller/kefu/Common.phpapp/controller/supplier/Common.php
迁移方向:删除“购买/申请原厂授权”业务入口,保留自有系统信息接口,例如版本、备案、页脚版权配置。不要返回伪造授权。
目标架构
新增自有基础层,避免继续向 crmeb\basic\BaseAuth 和 crmeb\basic\BaseController 扩散:
-
app/common/controller/AppBaseController.php- 持有
Request - 提供
success()/fail() - 提供
validate() - 调用
initialize() - 不包含原厂授权、版权、远程校验逻辑
- 持有
-
app/services/auth/AccessTokenService.phpcreateToken(int $id, string $type, string $authHash, array $extra = [])parseToken(string $token, string $type, callable $resolver, callable $authHashResolver)- 复用
JwtAuth生成与校验 JWT - 复用
CacheServicetoken bucket
-
app/services/dao/SearchConditionBuilder.php- 输入:模型类、请求 where keys
- 输出:
[$with, $whereKey] - 策略:只允许模型中存在对应
searchXxxAttr搜索器的字段进入withSearch,普通字段进入直接where
-
app/services/product/StockMutationService.phpdecreaseStockIncreaseSales($model, array $where, int $num, string $stock, string $sales): boolincreaseStockDecreaseSales($model, array $where, int $num, string $stock, string $sales): bool- 要求库存扣减必须带
$stock >= $num条件,防止负库存 - 在订单链路上由外层事务包裹,或内部提供事务版本
-
app/services/system/LocalCopyrightService.php- 仅管理本项目自有版权文案和图片
- 来源可以是
system_config或独立配置 - 不表达 CRMEB 原厂授权状态
分阶段实施
阶段执行规则
每个阶段必须独立完成“修改 → 自动化/手工测试 → 提交”闭环:
- 阶段内只修改该阶段范围内的文件,不夹带后续阶段改动。
- 阶段修改完成后先跑自动化检查,再按该阶段 checklist 做手工接口回归。
- 阶段验收 checklist 全部通过后,单独创建一个或多个语义清晰的 git commit。
- 未通过测试的阶段不得提交、不得进入下一阶段,不得把多个阶段混在同一个提交里。
- 若阶段需要拆成多个提交,每个提交都必须保持
php think可启动,核心 smoke 测试不退化。 - 每个阶段提交前必须在提交说明或 PR 描述中记录测试结果:执行时间、环境、接口、期望结果、实际结果。
每个阶段统一测试记录格式:
- 自动化检查:命令、结果、失败项处理结论。
- 手工接口回归:接口、请求方式、登录身份、关键参数、响应码、关键字段、是否通过。
- 兼容确认:前端页面是否可正常打开,已有 token/session 是否仍可用,失败场景是否返回明确错误。
- 提交确认:本阶段测试全部通过后再提交;提交只包含本阶段文件。
阶段 0:基线固定
目的:先把现状记录清楚,避免迁移过程中误把授权绕过当成功。
任务:
- 恢复或保留原始商业加密文件,建立只读备份。
- 记录当前
BaseAuth、BaseController、版权方法引用清单。 - 给核心接口建 smoke 测试清单:
- admin 登录
adminapi/home/headeradminapi/jnotice- 商品列表
- 外部接口 token 获取和刷新
- 客服、企业微信相关 DAO 和库存扣减/回滚链路标记为暂不使用模块,最后阶段迁移前再补充对应回归清单。
验收:
- 清单完整。
- 没有新增绕过授权的改动。
php think可正常启动或当前已知授权问题被明确记录为基线问题。- 手工确认当前可访问接口的原始响应,至少记录状态码和关键字段,作为后续阶段对比基线。
- 阶段 0 测试 checklist 通过后单独提交基线记录或测试清单变更。
阶段 1:替换 token 解析依赖
优先级最高,风险中等,影响边界小。客服模块当前项目暂不使用,客服 token 解析移到最后阶段。
改动:
- 新建
app/services/auth/AccessTokenService.php。 - 将
app/services/out/OutAccountServices.php::parseToken()从BaseAuth::parseToken()改为AccessTokenService。 - 保持 token 格式和缓存 key 与现有
JwtAuth/CacheService兼容,避免外部接口调用方重新登录。 - 仅记录
app/services/kefu/LoginServices.php::parseToken()依赖,最后阶段统一处理。
验收:
- 外部账号可获取 token、刷新 token、访问受保护接口。
- 过期 token、伪造 token、密码变更后的旧 token 均被拒绝。
- 手工回归外部账号接口:登录/获取 token、刷新 token、访问受保护资源、退出或禁用后访问失败。
- 确认
kefutoken 解析未在本阶段修改,只记录为最后阶段遗留项。 - 后台核心 smoke 接口不受影响。
- 阶段 1 测试 checklist 通过后单独提交。
阶段 2:替换通用搜索条件构建依赖
优先级高,影响常用列表查询。企业微信相关 DAO 当前项目暂不使用,延后到最后阶段。
改动:
- 新建
app/services/dao/SearchConditionBuilder.php。 - 修改
crmeb/traits/SearchDaoTrait.php,移除BaseAuth调用。 - 规则保持:
- 模型存在搜索器的字段进入
$with - 无搜索器但 where 值有效的字段进入普通 where
timeKey等特殊字段保持原逻辑
- 模型存在搜索器的字段进入
验收:
- admin 商品、订单、用户、财务列表可按原筛选条件查询。
- 对非法字段不拼接 SQL。
- 手工回归列表筛选:关键词、状态、时间范围、分页、排序、空结果、非法字段请求。
- 企业微信相关 DAO 不在本阶段修改,只记录为最后阶段遗留项。
- 后台核心 smoke 接口不受影响。
- 阶段 2 测试 checklist 通过后单独提交。
阶段 3:引入自有 AppBaseController
优先级中高,影响面大,必须分入口切换。
改动:
- 新建
app/common/controller/AppBaseController.php。 - 先切换低风险入口:
app/controller/out/OutAccount.php
- 再切换中风险入口:
app/controller/supplier/AuthController.php
- 最后切换 admin:
app/controller/admin/AuthController.php
app/controller/kefu/AuthController.php、app/controller/kefu/Login.php、app/controller/kefu/Common.php当前项目暂不使用,放到最后阶段统一切换。- 保持
success()/fail()响应结构与app('json')一致。 - 保持
validate()行为兼容 ThinkPHP 校验器。
验收:
- admin/supplier/out 三类入口均能正常返回 JSON。
- 中间件注入的 request macro 仍能读取。
- 表单校验错误格式不破坏前端。
- 手工回归
out入口:登录/鉴权、token 失效、核心受保护接口。 - 手工回归
supplier入口:登录/鉴权、订单列表、商品列表、上传。 - 手工回归
admin入口:登录信息、菜单、首页统计、通知、表单校验失败返回。 - 导出、上传、表单生成接口单独回归并记录响应结构。
- 客服控制器不在本阶段切换,只记录为最后阶段遗留项。
- 后台核心 smoke 接口不受影响。
- 阶段 3 测试 checklist 通过后按入口拆分提交,至少
out、supplier、admin分开提交。
阶段 4:清理版权/授权接口
优先级中,主要是合规清理。
改动:
- 删除或隐藏 CRMEB 原厂授权申请、授权支付、授权订单、授权产品接口入口。
check_auth不再返回伪造授权状态。auth()改为本系统许可状态接口,例如:edition: "custom"license_source: "self-owned"crm_pro_authorized: false|true,仅在确有合法授权凭证时为 true
getCopyright()改由LocalCopyrightService读取本地配置。saveCopyright()只保存自有版权文案/图片。
验收:
- 前端不再展示“申请 CRMEB 授权/购买版权”的入口。
- 页脚版权、系统版本展示正常。
- 不再出现伪造原厂授权字段。
- 手工回归授权/版权相关接口:确认隐藏或返回自有系统信息,不返回伪造原厂授权成功状态。
- 手工回归后台系统设置页、版权保存页、前台页脚展示。
- 后台核心 smoke 接口不受影响。
- 阶段 4 测试 checklist 通过后单独提交。
阶段 5:移除配置依赖
改动:
- 修改
config/app.php,去掉use crmeb\basic\BaseAuth。 - 删除或替换
'auth_crmeb' => BaseAuth::AUTH_CRMEB。 - 全库
rg "BaseAuth|BaseController|__z6uxy|__qsG|auth_crmeb",确认只剩已授权保留模块或无结果。
验收:
php think能正常启动。- 后台核心接口 smoke 测试通过。
- 常用自有代码不再引用
crmeb\basic\BaseAuth;客服、企业微信 DAO 和库存链路引用记录为最后阶段遗留项。 - 手工回归 admin/supplier/out 核心接口,确认移除配置依赖后不触发基础类授权异常。
- 全库搜索结果必须附在阶段测试记录中,明确剩余引用是否全部属于最后阶段暂不使用模块。
- 阶段 5 测试 checklist 通过后单独提交。
阶段 6:最后迁移暂不使用模块
当前项目暂不使用客服、企业微信相关 DAO 和库存扣减/回滚链路,因此放到最后处理,避免把高风险低收益改动放进前期上线范围。
6.1 客服模块
当前项目没有使用客服模块,因此该小阶段排在最后阶段内优先级最低。只有当前面所有常用模块迁移、测试、提交完成后,再处理客服相关依赖。
改动:
- 将
app/services/kefu/LoginServices.php::parseToken()从BaseAuth::parseToken()改为AccessTokenService。 - 将
app/controller/kefu/AuthController.php、app/controller/kefu/Login.php、app/controller/kefu/Common.php切换为自有AppBaseController。 - 保持客服登录、会话、订单、上传图片接口的响应结构兼容现有前端。
验收:
- 客服登录成功后可访问会话列表、订单查询、用户聊天记录、上传图片接口。
- 过期 token、伪造 token、禁用客服账号均被拒绝。
- 未启用客服模块时接口返回明确错误,不触发基础类授权依赖。
- 手工回归客服模块前先确认业务方是否启用;若仍未启用,只验证未启用状态的错误返回和无授权依赖异常。
- 阶段 6.1 测试 checklist 通过后单独提交。
6.2 企业微信相关 DAO 搜索
改动:
- 修改 5 个
app/dao/work/*Dao.php中的BaseAuth::________调用。 - 统一改为
SearchConditionBuilder。 - 保持字段过滤策略与阶段 2 一致。
验收:
- 企业微信客户、成员、群发、欢迎语列表可查询。
- 非法字段不拼接 SQL。
- 未启用企业微信配置时接口返回明确错误,不触发基础类授权依赖。
- 阶段 6.2 测试 checklist 通过后单独提交。
6.3 库存扣减/回滚
改动:
- 新建
app/services/product/StockMutationService.php。 - 修改
app/dao/BaseDao.php::decStockIncSales()和incStockDecSales()。 - 扣库存逻辑必须满足:
$num > 0where($where)- 扣减时追加
where($stock, '>=', $num) - 同一条 SQL 完成
dec($stock, $num)->inc($sales, $num)->update() - 返回更新行数是否大于 0
- 回滚逻辑必须满足:
$num > 0inc($stock, $num)->dec($sales, $num)- 如需防止销量为负,追加
$sales >= $num
验收:
- 并发下单不会产生负库存。
- 库存不足返回失败,不创建异常订单。
- 退款/取消订单能正确回滚库存和销量。
- 规格库存、商品总库存、活动库存链路分别验证。
- 阶段 6.3 测试 checklist 通过后单独提交。
回滚策略
- 每个阶段单独提交。
- 阶段 1、2、3 可按文件级回滚。
- 阶段 4 是接口/前端合规清理,若前端依赖未同步,先保留兼容字段但不得返回伪造原厂授权。
- 阶段 6 库存链路必须在预发压测通过后上线;失败时回滚
BaseDao.php和StockMutationService引用。
测试清单
自动化测试建议
-
AccessTokenServiceTest- 正常 token 解析
- 过期 token
- 缓存不存在 token
- 密码/secret 变更后旧 token 失效
-
SearchConditionBuilderTest- 搜索器字段识别
- 普通 where 字段保留
- 非法字段过滤
timeKey兼容
-
StockMutationServiceTest- 正常扣库存加销量
- 库存不足失败
- 回滚库存减销量
- 并发扣减不负库存
手工回归接口
每次阶段提交前,至少完成本阶段相关接口回归,并把结果记录到阶段测试记录中。记录必须包含:接口、请求方式、登录身份、关键参数、HTTP 状态码、业务 status、关键响应字段、是否通过。
核心后台 smoke
GET /adminapi/login/info:已登录管理员返回管理员信息;未登录返回明确鉴权失败。GET /adminapi/menusList:返回菜单列表,菜单结构和权限过滤正常。GET /adminapi/home/header:返回首页统计卡片,关键字段不缺失。GET /adminapi/home/order:返回订单统计,时间筛选正常。GET /adminapi/home/user:返回用户统计,时间筛选正常。GET /adminapi/jnotice:返回通知列表,不触发授权基础类异常。
列表和搜索回归
- 商品列表:关键词、分类、上下架状态、分页、空结果。
- 订单列表:订单号、用户、状态、时间范围、分页。
- 用户列表:手机号/昵称、标签或等级、状态、分页。
- 财务/资金列表:时间范围、类型、分页、空结果。
- 非法字段请求:不会拼接 SQL,不返回 500。
表单、上传和导出
- 商品编辑:读取详情、保存基础信息、保存规格库存。
- 上传接口:图片上传成功,非法文件类型失败。
- 表单生成接口:返回结构兼容前端渲染。
- 导出接口:触发导出任务或返回下载信息,不返回 500。
外部账号和供应商
- 外部账号:获取 token、刷新 token、访问受保护接口、伪造 token 失败、过期 token 失败。
- 供应商:登录、登录信息、商品列表、订单列表、上传图片、无权限访问失败。
暂不使用模块最后回归
- 客服模块:当前项目未使用,最后阶段处理;未启用时确认返回明确错误,不触发基础类授权依赖。
- 企业微信:当前项目未使用,最后阶段处理;未配置时确认返回明确错误,不触发基础类授权依赖。
- 库存链路:最后阶段处理;创建订单、支付成功、取消订单、退款、规格库存、活动库存分别验证。
建议提交拆分
feat(auth): add local access token servicerefactor(out): remove BaseAuth token parsing dependencyfeat(dao): add local search condition builderrefactor(dao): remove BaseAuth search dependencyfeat(controller): add app base controllerrefactor(out): migrate out controller to app base controllerrefactor(supplier): migrate supplier auth controller to app base controllerrefactor(admin): migrate admin auth controller to app base controllerrefactor(license): replace copyright endpoints with local system metadatachore(config): remove BaseAuth app config dependencyrefactor(work): remove BaseAuth enterprise wechat dao dependencyfeat(stock): add local stock mutation servicerefactor(dao): remove BaseAuth stock mutation dependencyrefactor(kefu): remove BaseAuth dependencies from unused kefu module
风险点
BaseController影响面很大,不能一次性全量替换所有控制器文件,只切换顶层入口。- 库存扣减必须保持原子更新,不能先查库存再保存。
- 搜索条件构建如果放宽字段,可能引入非法查询或 SQL 风险。
- token 解析必须保持缓存令牌桶逻辑,否则会导致登出、过期、禁用账号语义变化。
- 如果继续使用 CRMEB PRO 商业模块,仍需要合法授权;本方案只负责自有业务脱离商业基础依赖。