- 后端
- 电商
- 前端
【免费下载链接】CRMEB
🔥CRMEB开源商城免费开源多语言商城系统,Tp6框架商城,系统可商用;包含小程序商城、H5商城、公众号商城、PC商城、App,支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面DIY,前后端分离,方便二开,更有详细使用文档、接口文档、数据字典、二开文档/视频教程,欢迎大家提出宝贵意见和建议
本文聚焦 CRMEB 开源商城(ThinkPHP 6 架构)中的 crmeb/app/outapi 应用目录,系统讲解项目如何通过独立的应用分层,为合作第三方开放不依赖登录态的 RESTful 接口,覆盖目录设计、JWT 鉴权、路由组织、账号权限、接口能力清单与异常处理。读完本文,你将掌握在 CRMEB 项目中接入第三方小程序/APP、同步订单、下发商品与库存的完整实现方案,并能够对照源码定位关键调用链。
outapi 是什么:对外部开放的 API 定义目录
在 CRMEB 的多应用结构中,outapi是一个独立的应用目录,专门用于定义项目对外开放的接口。它承担的角色可以概括为三点:
- 是项目对合作第三方开放的 API 接口定义目录;
- 这里的接口可以被第三方直接调用来获取数据或完成相关业务;
- 这类接口与项目内部使用的 API(如
adminapi、api、kefuapi)在定位上有明确区隔。
目录入口说明位于 crmeb/app/outapi/README.md,它明确给出了这类 API 与内部 API 的三个关键区别:
- 对外开放,不需要登录授权:第三方无需注册平台用户、无需携带用户会话,而是通过专门的账号体系(appid/appsecret)换取访问令牌;
- 安全限制较严,只提供必要接口:不会把内部全部能力暴露出去,只开放合作方业务所需的必要端点;
- 接口规范遵循 RESTful 原则:以资源为中心组织 URL,使用 GET/POST/PUT/DELETE 表达查询、创建、修改、删除语义。
常见接入场景
README 中列举了三个最典型的场景,也是本文后续接口能力章节的对应落点:
- 第三方小程序/APP 直接获取商品数据——对应
product/list、product/:id、category/list等商品与分类接口; - 第三方商户后台系统同步订单信息——对应
order/list、order/:order_id、order/delivery/:order_id、refund/list等订单与售后接口; - 小程序支付回调通知类接口——对应账号配置中可自定义的推送接口(订单创建、支付、售后等事件推送)。
为什么要单独用这个目录
将外部接口独立成目录,可以带来三方面收益(对应 README 的表述):
- 实现与其他系统的深度集成:通过标准 HTTP API 对接 ERP、分销系统、外部商城或自研 APP;
- 让更多场景能够使用 CRMEB 提供的能力:把商城沉淀的商品、订单、用户、优惠券能力复用给更多端;
- 降低对第三方的侵入性,仅开放必要接口:通过账号 + 接口权限(rules)按需授权,避免把整个后台能力暴露出去。
目录结构与代码分层
outapi遵循 CRMEB 一贯的 Controller / Middleware / Validate / Route 分层风格,同时复用app/services中的业务服务层:
crmeb/app/outapi/ ├── config/route.php # 本应用路由配置 ├── controller/ │ ├── AuthController.php # 对外接口控制器基类(解析 outId / outInfo / auth) │ ├── Login.php # 登录:获取 token / 刷新 token │ ├── StoreCategory.php # 商品分类 │ ├── StoreProduct.php # 商品 │ ├── StoreOrder.php # 订单 │ ├── RefundOrder.php # 售后/退款 │ ├── StoreCoupon.php # 优惠券 │ ├── User.php # 用户 │ └── UserLevel.php # 用户等级 ├── middleware/ │ └── AuthTokenMiddleware.php # 对外接口鉴权中间件 ├── route/route.php # 对外路由定义 ├── validate/ │ ├── LoginValidate.php # 登录参数校验 │ ├── StoreCategoryValidate.php # 分类参数校验 │ └── StoreOutAccountValidate.php # 后台账号参数校验 ├── OutApiExceptionHandle.php # 本应用异常处理 └── provider.php # 覆盖全局异常 Handle业务逻辑并不直接写在控制器里,而是委托给app/services/out与各业务域的Out*Services:
- crmeb/app/services/out/OutAccountServices.php:对外账号的登录、token 解析、刷新、推送配置;
- crmeb/app/services/out/OutInterfaceServices.php:对外接口权限校验与接口文档管理;
- 业务服务如 OutStoreOrderServices.php、OutStoreProductServices.php、
OutStoreOrderRefundServices、OutUserServices等,负责订单、商品、售后、用户的查询与写操作。
数据访问层则复用 crmeb/app/dao/out/OutAccountDao.php 与OutInterfaceDao,最终落到eb_out_account、eb_out_interface两张表(建表语句见 crmeb/public/install/crmeb.sql)。
认证体系:appid/appsecret 换取 JWT
对外接口虽然“不需要登录授权”,但并不意味着裸奔——它使用一套独立的账号 + 令牌体系。
获取 access_token
第三方接入的第一步是通过POST /access_token用appid与appsecret换取令牌,路由定义在 crmeb/app/outapi/route/route.php:
//获取token Route::post('access_token', 'Login/getToken')->name('getToken')->option(['real_name' => '登录获取token']); //刷新token Route::post('refresh_token', 'Login/refreshToken')->name('refreshToken')->option(['real_name' => '刷新token']);控制器实现见 crmeb/app/outapi/controller/Login.php,它先通过LoginValidate校验参数(appid必须为 4~30 位字母数字,规则定义在 LoginValidate.php),再调用OutAccountServices::authLogin()。
底层authLogin的逻辑(见 OutAccountServices.php)依次完成:
- 按
appid查询未删除的对外账号,不存在则抛AuthException(410141); - 使用
password_verify校验appsecret(后台创建账号时用password_hash($data['appsecret'], PASSWORD_DEFAULT)加密存储,见 SystemOutAccount.php); - 检查账号状态
status == 0时禁止登录; - 通过
createToken($autInfo->id, 'out')生成 JWT(type 固定为out); - 记录
last_time与ip; - 返回
access_token、exp_time以及隐藏敏感字段后的auth_info。
请求与返回示例(来源为 crmeb.sql 中预置的接口文档数据eb_out_interfaceid=2):
{ "appid": "demo", "appsecret": "XXWaCnCZi8b6jcSWzkBcbCKKpkpNPrKy" }{ "status": 200, "msg": "获取成功", "data": { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", "exp_time": 1655954673, "auth_info": { "id": 7, "appid": "demo", "title": "测试账号" } } }token 刷新
令牌过期前可调用POST /refresh_token,传入旧的access_token换新。OutAccountServices::refresh()(见 OutAccountServices.php)会先完成verifyToken校验,然后删除缓存中的旧 token 并签发新 token,实现“平滑续期”。
中间件统一鉴权
除access_token、refresh_token之外的对外接口,路由组统一挂在AuthTokenMiddleware之下(见 route.php)。中间件核心逻辑见 AuthTokenMiddleware.php:
- 从请求头
Authori-zation中取出Bearer前缀后的 token(header 名称来自Config::get('cookie.token_name', 'Authori-zation')); - 调用
OutAccountServices::parseToken($token)解析并校验; - 通过
$request->macro('outId')、$request->macro('outInfo')将账号信息注入请求对象,供控制器基类使用; - 调用
OutInterfaceServices::verifyAuth($request)做接口权限校验(详见下文)。
parseToken的校验链路(见 OutAccountServices.php)包括:token 是否为空/为undefined、缓存中是否存在该 token(md5Token维度)、JWT 能否解析出id且type == 'out'、JWT 签名与有效期是否合法(通过jwtAuth->verifyToken()),以及账号是否存在、是否被禁用(status == 2时拒绝,见checkAuth)。任何一环失败都会抛出对应错误码的AuthException。
控制器基类 AuthController.php 在initialize()中统一读取outId、outInfo与账号授权规则auth,后续业务控制器可据此感知“当前是哪个第三方账号在调用”。
路由设计:RESTful 接口能力清单
对外路由全部定义在 crmeb/app/outapi/route/route.php,分为公共接口(无需鉴权)与业务接口(需鉴权)两组,业务接口按资源划分为以下能力域:
商品分类(mark: category)
| 方法 | 路径 | 控制器动作 | 说明 |
|---|---|---|---|
| GET | category/list | StoreCategory/index | 分类列表 |
| GET | category/:id | StoreCategory/read | 获取分类 |
| POST | category | StoreCategory/save | 新增分类 |
| PUT | category/:id | StoreCategory/update | 修改分类 |
| DELETE | category/:id | StoreCategory/delete | 删除分类 |
| PUT | category/set_show/:id/:is_show | StoreCategory/set_show | 修改分类状态 |
分类新增/修改支持pid、cate_name、pic、big_pic、sort、is_show字段,并经由 StoreCategoryValidate.php 校验,实现在 StoreCategory.php。
商品(mark: product)
| 方法 | 路径 | 控制器动作 | 说明 |
|---|---|---|---|
| GET | product/list | StoreProduct/index | 商品列表 |
| POST | product | StoreProduct/save | 新增商品 |
| PUT | product/:id | StoreProduct/update | 修改商品 |
| GET | product/:id | StoreProduct/read | 获取商品 |
| PUT | product/set_show/:id/:is_show | StoreProduct/set_show | 修改商品状态 |
| PUT | product/stock/upload | StoreProduct/uploadStock | 同步商品库存 |
商品列表支持cate_id、store_name、type、is_live、is_new、is_virtual、is_presale等筛选参数,控制器会依据cate_id是否为顶级分类自动映射为sid或cid查询条件(见 StoreProduct.php),底层列表查询由 OutStoreProductServices::searchList 完成,并补充cate_name分类名。
新增商品save参数与 CRMEB 商品模型完全对齐,包括store_name、keyword、unit_name、store_info、slider_image、video_open/link、spec_type、items/attrs(单多规格)、description、freight/postage/temp_id(运费)、give_integral、presale(预售)、vip_product(会员价)、activity(活动优先级)、recommend(推荐位)、is_limit/limit_type/limit_num(限购)等 30 余个字段,完整定义见 StoreProduct.php。
库存同步是 ERP 类合作方高频使用的接口:PUT /product/stock/upload接收items数组(每项含bar_code与qty),控制器限制单次最多 100 条并强制校验两个字段(见 StoreProduct.php)。底层 syncStock 按bar_code批量更新规格库存,并通过ProductStockJob::dispatch('distribute', ...)异步重算商品总库存。
订单(mark: order)
| 方法 | 路径 | 控制器动作 | 说明 |
|---|---|---|---|
| GET | order/list | StoreOrder/lst | 订单列表 |
| GET | order/:order_id | StoreOrder/read | 订单详情 |
| PUT | order/remark/:order_id | StoreOrder/remark | 修改备注信息 |
| PUT | order/receive/:order_id | StoreOrder/receive | 确认收货 |
| GET | order/express_list | StoreOrder/express | 获取物流公司 |
| PUT | order/delivery/:order_id | StoreOrder/delivery | 订单发货 |
| PUT | order/distribution/:order_id | StoreOrder/updateDistribution | 修改配送信息 |
| GET | order/split_cart_info/:order_id | StoreOrder/splitCartInfo | 获取订单可拆分商品列表 |
| PUT | order/split_delivery/:order_id | StoreOrder/splitDelivery | 拆单发货 |
| PUT | order/invoice/:order_id | StoreOrder/setInvoice | 修改订单发票 |
| PUT | order/invoice_status/:order_id | StoreOrder/setInvoiceStatus | 修改订单发票状态 |
订单列表支持status(0 未发货 / 1 已发货 / 2 已收货 / 3 已完成 / -2 已退款)、real_name、pay_type、data(下单时间区间)、paid、type、order、field_key等筛选,查询实现在 OutStoreOrderServices::getOrderList。返回数据会补充pay_type_name、items(含cart_id、store_name、suk、image、price、cart_num、surplus_num、refund_num)等字段。
订单发货PUT /order/delivery/:order_id接收delivery_name、delivery_id、delivery_code三个字段(见 StoreOrder.php),并固定写入express_record_type = 1、type = 1表示“后台/外部发货”。
拆单发货先通过split_cart_info获取可拆分商品列表,再调用split_delivery提交cart_ids(每项含cart_id、cart_num),控制器会逐项强校验并转整型(见 StoreOrder.php)。
发票相关接口对抬头类型(个人/企业)、税号格式、手机号、银行卡号等做了正则校验(见 StoreOrder.php),开票/回传票号等操作可通过setInvoiceStatus完成。
售后/退款(mark: refund)
| 方法 | 路径 | 控制器动作 | 说明 |
|---|---|---|---|
| GET | refund/list | RefundOrder/lst | 售后订单列表 |
| PUT | refund/remark/:order_id | RefundOrder/remark | 售后订单备注 |
| PUT | refund/:order_id | RefundOrder/refundPrice | 售后订单退款 |
| PUT | refund/agree/:order_id | RefundOrder/agree | 商家同意退款 |
| PUT | refund/refuse/:order_id | RefundOrder/refuse | 商家拒绝退款 |
| GET | refund/:order_id | RefundOrder/read | 售后订单详情 |
售后列表支持order_id、time(退款时间区间)、refund_type(1 仅退款 / 2 退货退款 / 3 拒绝退款 / 4 商品待退货 / 5 退货待收货 / 6 已退款)筛选(见 RefundOrder.php),底层由OutStoreOrderRefundServices提供服务。退款操作refundPrice通过refund_price指定退款金额,refuse需携带refund_reason拒绝原因。
优惠券(mark: coupon)
| 方法 | 路径 | 控制器动作 | 说明 |
|---|---|---|---|
| GET | coupon/list | StoreCoupon/lst | 优惠券列表 |
| POST | coupon | StoreCoupon/save | 新增优惠券 |
| PUT | coupon/status/:id/:status | StoreCoupon/status | 修改优惠券状态 |
| DELETE | coupon/:id | StoreCoupon/delete | 删除优惠券 |
新增优惠券支持coupon_title、coupon_price、use_min_price、coupon_time、start_use_time、end_use_time、start_time、end_time、receive_type、total_count、status等字段;当total_count = 0时自动标记为不限量(is_permanent = 1,见 StoreCoupon.php)。删除优惠券会同步清理商品与券的关联数据。
用户与用户等级(mark: user)
| 方法 | 路径 | 控制器动作 | 说明 |
|---|---|---|---|
| GET | user_level/list | UserLevel/lst | 用户等级列表 |
| GET | user/list | User/lst | 用户列表 |
| GET | user/info/:uid | User/info | 用户详情 |
| POST | user | User/save | 新增用户 |
| PUT | user/:uid | User/update | 修改用户 |
| PUT | user/give_balance/:uid | User/giveBalance | 赠送余额 |
| PUT | user/give_point/:uid | User/givePoint | 赠送积分 |
| PUT | user/change_balance/:uid | User/changeBalance | 修改余额 |
| PUT | user/change_point/:uid | User/changePoint | 修改积分 |
用户列表支持nickname、status、field_key筛选;新增/修改用户支持real_name、phone、mark、pwd、level、spread_open、is_promoter、status字段(见 User.php)。余额/积分赠送接口通过money_status/integration_status(1 增加 / 2 减少)与money/integration完成增减操作,changeBalance/changePoint则直接设置目标值。
接口权限:rules 与 verifyAuth
对外接口做到了“按账号授权”,而不是“拿到 token 全放行”。账号表eb_out_account中的rules字段以逗号分隔保存了该账号可访问的系统路由 ID 列表(见 crmeb.sql),后台创建账号时通过implode(',', $data['rules'])写入(见 SystemOutAccount.php)。
每次请求经过AuthTokenMiddleware时,OutInterfaceServices::verifyAuth($request)会执行如下校验(见 OutInterfaceServices.php):
- 取当前请求路由规则
request->rule()->getRule()与 HTTP 方法,统一转小写; - 从系统路由表
SystemRouteServices查询该账号rules内且app_name == 'outapi'的路由,按 method 分组为rolesAuth[method][]; - 先做规则精确匹配;不匹配时,再把
<>路由占位符替换为{}后二次匹配(兼容不同路由语法); - 仍未匹配则抛
AuthException(110000)拒绝访问。
也就是说,第三方账号的开放范围完全由后台分配的rules决定——给某个账号勾选哪些接口,它就只能调用哪些接口,实现“仅开放必要接口”的安全目标。
账号与接口文档的后台管理
对外账号的完整生命周期由管理后台「设置 → 对外接口账号」管理,控制器为 SystemOutAccount.php,路由见 crmeb/app/adminapi/route/setting.php:
system_out_account/index:对外接口账号信息列表;system_out_account/save:新增账号(appid、appsecret、title、rules);system_out_account/update/:id:修改账号(可留空 appsecret 表示不修改);system_out_account/set_status/:id/:status:启用/禁用账号;system_out_account/set_up/:id:设置账号推送接口;system_out_account/text_out_url:测试获取推送 token 接口;system_out_interface/list:对外接口列表(按分类树形返回,见 OutInterfaceServices::outInterfaceList);system_out_interface/save/:id、/info/:id、/edit_name、/del/:id:对外接口文档的增删改查。
新增账号时appsecret使用password_hash(..., PASSWORD_DEFAULT)加密存储、登录时password_verify比对,账号与密钥校验规则(appid 4~30 位字母数字、appsecret 6~32 位)定义在 StoreOutAccountValidate.php。
账号还可配置事件推送(outSetUpSave,见 SystemOutAccount.php):push_open(是否推送)、push_account/push_password(第三方推送账号)、push_token_url(第三方获取 token 接口)、user_update_push(用户更新推送)、order_create_push(订单创建推送)、order_pay_push(订单支付推送)、refund_create_push(售后创建推送)、refund_cancel_push(售后取消推送)。eb_out_account表中的对应字段定义见 crmeb.sql,OutAccountServices::textOutUrl()则实现了后台“测试获取 token 接口”的连通性验证(见 OutAccountServices.php)。
异常处理与全局配置
对外应用通过 provider.php 将全局异常处理器覆盖为OutApiExceptionHandle,实现“对外表现可控”:
report()只对非忽略类异常写错误日志,且日志以|分隔记录用户 ID、IP、耗时、请求方法、路由、参数与报错数据(见 OutApiExceptionHandle.php);ValidateException、AuthException、AdminException、ApiException列入ignoreReport不落日志;render()对上述业务异常返回标准 JSON 结构(status+msg),调试模式下才附带message/file/line/trace等细节,避免把内部堆栈暴露给第三方(见 OutApiExceptionHandle.php)。
本应用的路由配置在 crmeb/app/outapi/config/route.php:强制使用路由、合并路由规则、路由完全匹配、关闭控制器/操作名自动转换,保证对外 URL 的稳定与 RESTful 语义不被改写。路由组最外层挂载AllowOriginMiddleware(见 route.php)以处理跨域,同时Route::miss对 OPTIONS 预检请求返回 200 与允许跨域头、对未知路径统一返回 404。
数据结构:两张核心表
对外能力依赖两张数据表(完整建表语句见 crmeb.sql):
eb_out_account(对外接口账号):id、appid(账号)、appsecret(密钥,bcrypt 密文)、title(描述)、status(状态)、rules(可访问接口权限,逗号分隔的 SystemRoute ID)、add_time、last_time、ip、is_del,以及push_open等 7 个推送配置字段。
eb_out_interface(对外接口):id、pid(父级,用于组织分类)、type(0 菜单 / 1 接口)、name、describe、method、url、request_params、return_params、request_example、return_example、error_code、is_del。安装包中预置了数十条接口文档数据(如“获取 access_token”“用户列表”“商品详情”“订单列表”“同意退款”等),字段中 JSON 化的请求/返回参数结构,为第三方联调提供了可直接参考的请求示例与返回示例,这也是后台“对外接口列表”的数据来源。
小结
CRMEB 的outapi应用目录通过“独立应用 + JWT 令牌 + 账号级接口权限 + RESTful 路由 + 独立异常处理”的组合,在不引入登录态的前提下把商城核心能力安全地开放给了合作第三方。若你需要对接 ERP、自研 APP 或第三方分销系统,可以依照本文的目录结构、认证流程与接口清单,在 crmeb/app/outapi/route/route.php 中扩展新路由、在controller/下新增资源控制器,并复用app/services/out与各Out*Services完成业务实现;需要新增对外账号与分配接口权限时,则到管理后台的「对外接口账号」中配置rules即可。
- 后端
- 电商
- 前端
【免费下载链接】CRMEB
🔥CRMEB开源商城免费开源多语言商城系统,Tp6框架商城,系统可商用;包含小程序商城、H5商城、公众号商城、PC商城、App,支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面DIY,前后端分离,方便二开,更有详细使用文档、接口文档、数据字典、二开文档/视频教程,欢迎大家提出宝贵意见和建议
相关推荐
超强lx-music-desktop API设计:开放接口实现第三方集成
超强lx music desktop API设计:开放接口实现第三方集成 你是否还在为音乐软件无法与智能家居联动而烦恼?是否想过让你的音乐播放状态实时同步到自定
桌面应用音视频前端CRMEB 前端 API 接口层解析:基于 ThinkPHP6 的 Controller 路由设计与前后端分离实践
CRMEB 前端 API 接口层解析:基于 ThinkPHP6 的 Controller 路由设计与前后端分离实践 crmeb/app/api 目录是 CRME
后端电商前端ZyPlayer视频播放控制API:第三方集成接口详解
ZyPlayer视频播放控制API:第三方集成接口详解 ZyPlayer作为跨平台桌面端视频资源播放器,提供了灵活的视频播放控制API,支持第三方应用通过接口实
桌面应用音视频即时通讯
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考