6.5 KiB
6.5 KiB
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
namespace app\controller\api\v1;
use app\BaseController;
use app\services\wechat\WechatUserServices;
use think\Response;
class InternalController extends BaseController
{
/**
* 内部接口:根据 openid 获取用户信息
* 用于 app_03 H5 登录时自动补齐用户数据
*
* @param WechatUserServices $wechatUserServices
* @return Response
*/
public function userInfo(WechatUserServices $wechatUserServices): Response
{
// 1. 验证内部 API Key
$apiKey = $this->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 中增加:
// 内部接口(无需用户登录验证)
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 中直接添加:
INTERNAL_API_KEY=sk_crmeb_app03_xxxxxxxx
然后在代码中读取:
$configKey = env('INTERNAL_API_KEY', '');
3.4 验证 WechatUserServices 是否有 getOne 方法
// 如果没有,WechatUserServices 中需添加
public function getOne(array $where): array
{
return $this->dao->getOne($where);
}
四、app_03 后端改动
4.1 .env 新增配置
CRMEB_BASE_URL=https://your-crmeb-domain.com
CRMEB_API_KEY=sk_crmeb_app03_xxxxxxxx
4.2 config.py 新增字段
class Settings(BaseSettings):
# ... 原有字段
crmeb_base_url: str = ""
crmeb_api_key: str = ""
4.3 新增 routers/auth.py 接口
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
// 检测 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 简化为一行:
// 之前(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);