# CRMEB 后端新增内部接口方案 ## 一、背景 当前 H5 登录需要小程序端传多个 URL 参数: ``` ?uid=7&openid=oauWC...&phone=13693687880&name=王小明&avatar=https://... ``` 改为只传 `openid`,由 app_03 后端主动向 CRMEB 查询用户信息。 --- ## 二、方案架构 ``` H5: ?openid=xxx ↓ app_03 后端 → GET /api/internal/user-info?openid=xxx → CRMEB 后端 ← { nickname, phone, avatar, uid } ↓ 创建/更新 User → 返回 token → 登录完成 ``` --- ## 三、CRMEB 端改动 ### 3.1 新增控制器 `app/controller/api/v1/InternalController.php`: ```php request->header('X-Internal-Key'); $configKey = sys_config('internal_api_key', ''); if (!$apiKey || $apiKey !== $configKey) { return json(['code' => 403, 'msg' => 'Forbidden']); } // 2. 获取 openid 参数 $openid = $this->request->param('openid', ''); if (!$openid) { return json(['code' => 400, 'msg' => 'openid is required']); } // 3. 查询微信用户信息 $wechatUser = $wechatUserServices->getOne(['openid' => $openid]); if (!$wechatUser) { return json(['code' => 404, 'msg' => 'user not found']); } // 4. 获取用户主表信息(nickname, phone, avatar 可能在用户表或微信表) $uid = $wechatUser['uid']; $userServices = app()->make(\app\services\user\UserServices::class); $user = $userServices->getUserInfo($uid); return json([ 'code' => 200, 'data' => [ 'uid' => $uid, 'nickname' => $user['nickname'] ?? $wechatUser['nickname'] ?? '', 'phone' => $user['phone'] ?? $wechatUser['phone'] ?? '', 'avatar' => $user['avatar'] ?? $wechatUser['headimgurl'] ?? '', ], ]); } } ``` ### 3.2 新增路由 `config/route.php` 或 `route/app.php` 中增加: ```php // 内部接口(无需用户登录验证) Route::get('api/internal/user-info', 'api/v1.Internal/userInfo'); ``` > CRMEB 的路由文件通常在 `route/app.php`、`route/api.php` 或 `route/v1.php` 中,根据实际项目结构添加。 ### 3.3 配置内部分享密钥 在 CRMEB 后台 → 系统设置 → 系统配置 中添加配置项 `internal_api_key`: ``` 键名: internal_api_key 键值: 生成一个随机字符串,如:sk_crmeb_app03_xxxxxxxx 说明: 内部服务间通信密钥 ``` 或者在 `.env` 中直接添加: ```ini INTERNAL_API_KEY=sk_crmeb_app03_xxxxxxxx ``` 然后在代码中读取: ```php $configKey = env('INTERNAL_API_KEY', ''); ``` ### 3.4 验证 WechatUserServices 是否有 getOne 方法 ```php // 如果没有,WechatUserServices 中需添加 public function getOne(array $where): array { return $this->dao->getOne($where); } ``` --- ## 四、app_03 后端改动 ### 4.1 `.env` 新增配置 ```ini CRMEB_BASE_URL=https://your-crmeb-domain.com CRMEB_API_KEY=sk_crmeb_app03_xxxxxxxx ``` ### 4.2 `config.py` 新增字段 ```python class Settings(BaseSettings): # ... 原有字段 crmeb_base_url: str = "" crmeb_api_key: str = "" ``` ### 4.3 新增 `routers/auth.py` 接口 ```python class OpenidRequest(BaseModel): openid: str @router.post("/openid-login") async def openid_login(req: OpenidRequest) -> TokenResponse: """仅传 openid → 调 CRMEB 获取用户信息 → 创建/更新 User → 返回 token""" # 1. 调 CRMEB 内部接口 async with httpx.AsyncClient() as client: resp = await client.get( f"{settings.crmeb_base_url}/api/internal/user-info", params={"openid": req.openid}, headers={"X-Internal-Key": settings.crmeb_api_key}, ) data = resp.json() if data.get("code") != 200: raise HTTPException(status_code=400, detail="获取用户信息失败") info = data["data"] # 2. 查找或创建用户 user = await User.find_one(User.openid == req.openid) if not user: user = User( openid=req.openid, name=info.get("nickname", "") or "", phone=info.get("phone") or None, avatar_url=info.get("avatar") or None, ) await user.insert() else: user.name = info.get("nickname", "") or user.name if info.get("phone"): user.phone = info["phone"] if info.get("avatar"): user.avatar_url = info["avatar"] await user.save() # 3. 返回 token token = _build_token(user) return TokenResponse( token=token, user_id=str(user.id), openid=user.openid, mobile=user.phone or "", ) ``` ### 4.4 H5 前端 LoginPage ```typescript // 检测 openid 参数 → 调后端 openid-login const openidParam = params.get('openid'); if (openidParam && openidParam.trim()) { const res = await fetchApi("/auth/openid-login", { method: "POST", body: JSON.stringify({ openid: openidParam }), }); setTokenAndUser(res.token, { id: res.user_id, openid: res.openid, phone: res.mobile }); // fetchProfile 自动拿到 name/avatar_url } ``` --- ## 五、部署步骤 | 步骤 | 操作 | 负责 | |------|------|------| | 1 | CRMEB 加 `InternalController` | 小程序端 | | 2 | CRMEB 加路由 | 小程序端 | | 3 | CRMEB 配置 `internal_api_key` | 小程序端 | | 4 | app_03 `.env` 加 `CRMEB_BASE_URL` + `CRMEB_API_KEY` | H5 端 | | 5 | app_03 `config.py` 加字段 | H5 端 | | 6 | app_03 加 `openid-login` 接口 | H5 端 | | 7 | H5 前端 LoginPage 改为调 `openid-login` | H5 端 | --- ## 六、最终效果 小程序端 web-view URL 简化为一行: ```javascript // 之前(5个参数) const url = 'https://mp.yizhezhe.com/app_03/?uid=' + uid + '&openid=' + encodeURIComponent(openid) + '&phone=' + phone + '&name=' + encodeURIComponent(name) + '&avatar=' + encodeURIComponent(avatar); // 之后(1个参数) const url = 'https://mp.yizhezhe.com/app_03/?openid=' + encodeURIComponent(openid); ```