refactor: update development standards and enhance ProfilePage with emergency contact editing

This commit is contained in:
2026-05-28 16:08:26 +08:00
parent 96a938e11b
commit 201f80811a
4 changed files with 724 additions and 412 deletions

View 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);
```