refactor: update development standards and enhance ProfilePage with emergency contact editing
This commit is contained in:
247
docs/crmeb-internal-api-plan.md
Normal file
247
docs/crmeb-internal-api-plan.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# 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
|
||||
<?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` 中增加:
|
||||
|
||||
```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);
|
||||
```
|
||||
Reference in New Issue
Block a user