ModelMapRouterFacade 使用手册
2026年3月6日大约 9 分钟
ModelMapRouterFacade 使用手册
版本: v1
作者: youhujun & xueer
更新日期: 2026-03-05
📖 概述
ModelMapRouterFacade 是游鹄生态中用于兼容单体和微服务架构的映射表查询门面服务。它是业务代码重构时查询映射表的主要入口。
核心特点
- ✅ 单体/微服务自动适配:根据配置自动选择查询方式
- ✅ 智能路由选择:单体时直接查询数据库,微服务时调用API
- ✅ 映射表模型映射:通过
modelFlagMap管理映射表模型 - ✅ 统一接口:业务层无需关心底层架构,使用统一方法调用
- ✅ 支持所有映射表:通用方法,适配所有映射表(UserMap、OrderMap等)
与其他门面的关系
ModelMapRouterFacade (单体/微服务适配层)
├── 单体模式: 调用 ShardMapHelperFacade (直接查询映射表)
└── 微服务模式: 调用 ApiRequestFacade (跨服务API调用)🔧 配置说明
1. 环境配置
.env 文件中配置微服务模式:
# 微服务模式开关(true=微服务,false=单体)
YOUHUJUN_IS_MS=false
YOUHU_IS_MS=false
YOUHUSHOP_IS_MS=false
XUEHU_IS_MS=false2. 微服务API配置
微服务模式下,需要在配置文件中配置API地址:
// config/shardmap_api_url.php (示例)
return [
'queryOneData' => 'http://youhu-base/api/v1/shardmap/query-one',
'queryManyData' => 'http://youhu-base/api/v1/shardmap/query-many',
];3. 映射表模型映射
ModelMapRouterFacadeService 中维护映射表模型映射:
private $modelFlagMap = [
'UserMap' => \App\Models\LaravelShardMap\V1\Map\UserMap::class,
// 'OrderMap' => \App\Models\LaravelShardMap\V1\Map\OrderMap::class,
// 'ProductMap' => \App\Models\LaravelShardMap\V1\Map\ProductMap::class,
];📚 方法详解
1. getShardInfoByNonBizId
通过非业务ID字段查询映射表,获取主库分片信息。
方法签名
public static function getShardInfoByNonBizId(
string $mapModelClass,
string $searchField,
mixed $searchValue
): ?array参数说明
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
$mapModelClass | string | 是 | 映射表模型标识(如 'UserMap', 'OrderMap') |
$searchField | string | 是 | 查询字段(如 'phone', 'email', 'order_no') |
$searchValue | mixed | 是 | 查询值(如 '13800138000', 'user@example.com') |
返回值
成功时返回分片信息数组:
[
'biz_id' => '276406781286953', // 业务ID(如 user_uid)
'shard_db' => 'ds_0', // 分片数据库名
'shard_table' => 'users_0', // 分片表名
'shard_key' => 0, // 分片键值
'base_table' => 'users', // 基础表名
]未找到数据时返回 null。
工作流程
调用 getShardInfoByNonBizId
↓
判断是否微服务模式
↓
┌─────────────────┬─────────────────┐
│ 单体模式 │ 微服务模式 │
├─────────────────┼─────────────────┤
│ 1. 根据 │ 1. 构建 API │
│ modelFlagMap │ 请求参数 │
│ 获取模型类 │ │
│ 2. 调用 │ 2. 通过 │
│ ShardMap │ ApiRequest │
│ HelperFacade │ 跨服务调用 │
│ 查询数据库 │ 3. 解析 API │
│ │ 返回数据 │
└─────────────────┴─────────────────┘
↓
返回分片信息使用示例
示例1:用户登录 - 通过手机号查询分片信息
// 场景:用户使用手机号登录
public function loginByPhone(Request $request)
{
$phone = $request->input('phone');
// 查询映射表,获取用户分片信息
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId(
'UserMap', // 映射表模型标识
'phone', // 查询字段
$phone // 查询值
);
if (!$shardInfo) {
return $this->error('用户不存在');
}
// 使用分片信息查询用户主表
$user = User::on($shardInfo['shard_db'])
->from($shardInfo['shard_table'])
->where('user_uid', $shardInfo['biz_id'])
->first();
// ... 后续登录逻辑
}示例2:订单查询 - 通过订单号查询分片信息
// 场景:用户查询订单详情
public function getOrderDetail(Request $request)
{
$orderNo = $request->input('order_no');
// 查询映射表,获取订单分片信息
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId(
'OrderMap', // 映射表模型标识
'order_no', // 查询字段
$orderNo // 订单号
);
if (!$shardInfo) {
return $this->error('订单不存在');
}
// 使用分片信息查询订单主表
$order = Order::on($shardInfo['shard_db'])
->from($shardInfo['shard_table'])
->where('order_uid', $shardInfo['biz_id'])
->first();
return $this->success($order);
}示例3:用户信息查询 - 通过邮箱查询分片信息
// 场景:通过邮箱获取用户信息
public function getUserByEmail(string $email)
{
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId(
'UserMap',
'email',
$email
);
if (!$shardInfo) {
return null;
}
return User::on($shardInfo['shard_db'])
->from($shardInfo['shard_table'])
->where('user_uid', $shardInfo['biz_id'])
->first();
}2. getBizIdsByFuzzySearch
映射表模糊查询,获取符合条件的业务ID列表。
方法签名
public static function getBizIdsByFuzzySearch(
string $mapModelClass,
string $searchField,
mixed $searchValue,
array $extraConditions = []
): array参数说明
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
$mapModelClass | string | 是 | 映射表模型标识(如 'UserMap', 'OrderMap') |
$searchField | string | 是 | 模糊查询字段(如 'phone', 'nick_name') |
$searchValue | mixed | 是 | 模糊查询值(支持 % 占位符) |
$extraConditions | array | 否 | 额外精准条件(如 ['status' => 1]) |
返回值
成功时返回业务ID数组:
[
'276406781286953',
'276406781286954',
'276406781286955',
]未找到数据时返回空数组 []。
工作流程
调用 getBizIdsByFuzzySearch
↓
判断是否微服务模式
↓
┌─────────────────┬─────────────────┐
│ 单体模式 │ 微服务模式 │
├─────────────────┼─────────────────┤
│ 1. 根据 │ 1. 构建 API │
│ modelFlagMap │ 请求参数 │
│ 获取模型类 │ │
│ 2. 调用 │ 2. 通过 │
│ ShardMap │ ApiRequest │
│ HelperFacade │ 跨服务调用 │
│ 模糊查询 │ 3. 解析 API │
│ │ 返回数据 │
└─────────────────┴─────────────────┘
↓
返回业务ID数组使用示例
示例1:用户搜索 - 按手机号前缀查询
// 场景:查询所有手机号以 138 开头的用户
public function searchUsersByPhonePrefix(Request $request)
{
$phonePrefix = $request->input('phone_prefix');
// 模糊查询获取符合条件的用户ID列表
$userUids = ModelMapRouterFacade::getBizIdsByFuzzySearch(
'UserMap', // 映射表模型标识
'phone', // 查询字段
$phonePrefix . '%' // 模糊查询值(138%)
);
// 批量查询用户信息
$users = User::whereIn('user_uid', $userUids)->get();
return $this->success($users);
}示例2:用户搜索 - 按昵称模糊查询
// 场景:查询昵称包含"游鹄"的所有用户
public function searchUsersByNickname(Request $request)
{
$keyword = $request->input('keyword');
// 模糊查询获取用户ID列表
$userUids = ModelMapRouterFacade::getBizIdsByFuzzySearch(
'UserMap',
'nick_name',
'%' . $keyword . '%' // %游鹄%
);
// 批量查询用户信息
$users = User::whereIn('user_uid', $userUids)->get();
return $this->success($users);
}示例3:订单查询 - 按订单号前缀 + 状态筛选
// 场景:查询某个订单号前缀且状态为"待支付"的订单
public function searchOrders(Request $request)
{
$orderNoPrefix = $request->input('order_no_prefix');
// 模糊查询 + 额外条件
$orderUids = ModelMapRouterFacade::getBizIdsByFuzzySearch(
'OrderMap',
'order_no',
$orderNoPrefix . '%', // 订单号前缀
['status' => Order::STATUS_PENDING_PAYMENT] // 额外条件
);
// 批量查询订单信息
$orders = Order::whereIn('order_uid', $orderUids)->get();
return $this->success($orders);
}示例4:用户统计 - 按邮箱域名查询
// 场景:统计所有 qq.com 邮箱的用户
public function getQQEmailUsers()
{
$userUids = ModelMapRouterFacade::getBizIdsByFuzzySearch(
'UserMap',
'email',
'%@qq.com' // 所有 qq.com 邮箱
);
$users = User::whereIn('user_uid', $userUids)->get();
return $this->success([
'count' => $users->count(),
'users' => $users
]);
}💡 完整业务场景示例
场景1:用户登录(单体模式)
namespace App\Services\Facade\User\V1;
use App\Facades\Common\V1\Shard\ModelMapRouterFacade;
use App\Models\YouHu\V1\User\User;
class UserLoginService
{
public function loginByPhone(string $phone, string $password)
{
// 1. 通过手机号查询映射表,获取分片信息
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId(
'UserMap',
'phone',
$phone
);
if (!$shardInfo) {
throw new BusinessException('用户不存在');
}
// 2. 使用分片信息查询用户主表
$user = User::on($shardInfo['shard_db'])
->from($shardInfo['shard_table'])
->where('user_uid', $shardInfo['biz_id'])
->first();
if (!$user) {
throw new BusinessException('用户数据异常');
}
// 3. 验证密码
if (!Hash::check($password, $user->password)) {
throw new BusinessException('密码错误');
}
// 4. 生成Token
$token = $this->generateToken($user);
return [
'token' => $token,
'user' => $user
];
}
}场景2:批量用户搜索(微服务模式)
namespace App\Services\Facade\User\V1;
use App\Facades\Common\V1\Shard\ModelMapRouterFacade;
use App\Models\YouHu\V1\User\User;
class UserSearchService
{
public function searchUsers(string $keyword, int $limit = 10)
{
// 1. 通过昵称模糊查询,获取符合条件的用户ID列表
$userUids = ModelMapRouterFacade::getBizIdsByFuzzySearch(
'UserMap',
'nick_name',
'%' . $keyword . '%'
);
if (empty($userUids)) {
return [];
}
// 2. 限制查询数量
$userUids = array_slice($userUids, 0, $limit);
// 3. 批量查询用户信息
$users = User::whereIn('user_uid', $userUids)
->get()
->keyBy('user_uid');
// 4. 按原始顺序返回结果
$result = [];
foreach ($userUids as $uid) {
if (isset($users[$uid])) {
$result[] = $users[$uid];
}
}
return $result;
}
}场景3:订单管理(完整流程)
namespace App\Services\Facade\Order\V1;
use App\Facades\Common\V1\Shard\ModelMapRouterFacade;
use App\Models\YouHuShop\V1\Order\Order;
class OrderService
{
// 查询订单详情
public function getOrderDetail(string $orderNo)
{
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId(
'OrderMap',
'order_no',
$orderNo
);
if (!$shardInfo) {
throw new BusinessException('订单不存在');
}
$order = Order::on($shardInfo['shard_db'])
->from($shardInfo['shard_table'])
->where('order_uid', $shardInfo['biz_id'])
->first();
return $order;
}
// 查询用户的订单列表
public function getUserOrders(string $userUid, int $page = 1, int $pageSize = 20)
{
// 通过 user_uid 计算分片信息
$shardInfo = ShardHelperFacade::calc($userUid, 'order');
$orders = Order::on($shardInfo['db'])
->from($shardInfo['table'])
->where('user_uid', $userUid)
->orderBy('created_time', 'desc')
->offset(($page - 1) * $pageSize)
->limit($pageSize)
->get();
return $orders;
}
// 按订单号前缀查询订单
public function searchOrdersByPrefix(string $prefix, string $status = null)
{
$extraConditions = [];
if ($status) {
$extraConditions['status'] = $status;
}
$orderUids = ModelMapRouterFacade::getBizIdsByFuzzySearch(
'OrderMap',
'order_no',
$prefix . '%',
$extraConditions
);
if (empty($orderUids)) {
return [];
}
return Order::whereIn('order_uid', $orderUids)->get();
}
}🎯 最佳实践
1. 优先使用 ModelMapRouterFacade
在业务代码中,优先使用 ModelMapRouterFacade 而不是直接使用 ShardMapHelperFacade:
// ✅ 推荐:使用 ModelMapRouterFacade(自动适配单体/微服务)
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId('UserMap', 'phone', $phone);
// ❌ 不推荐:直接使用 ShardMapHelperFacade(单体专用)
$shardInfo = ShardMapHelperFacade::getShardInfoByNonBizId(UserMap::class, 'phone', $phone);2. 正确配置 modelFlagMap
在 ModelMapRouterFacadeService 中维护映射表模型映射:
private $modelFlagMap = [
'UserMap' => \App\Models\LaravelShardMap\V1\Map\UserMap::class,
'OrderMap' => \App\Models\LaravelShardMap\V1\Map\OrderMap::class,
'ProductMap' => \App\Models\LaravelShardMap\V1\Map\ProductMap::class,
];3. 统一异常处理
建议在业务层统一处理 CommonException:
try {
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId('UserMap', 'phone', $phone);
// ... 后续逻辑
} catch (CommonException $e) {
// 记录日志
Log::error('查询分片信息失败', [
'error' => $e->getMessage(),
'mapModelClass' => 'UserMap',
'searchField' => 'phone',
'searchValue' => $phone
]);
// 返回友好提示
return $this->error('系统繁忙,请稍后重试');
}4. 缓存查询结果
对于频繁查询的数据,建议缓存查询结果:
$cacheKey = "user:shard_info:{$phone}";
$shardInfo = Cache::remember($cacheKey, 3600, function () use ($phone) {
return ModelMapRouterFacade::getShardInfoByNonBizId('UserMap', 'phone', $phone);
});⚠️ 注意事项
1. 模型标识必须配置
使用前必须确保 $modelFlagMap 中配置了对应的映射表模型标识,否则会抛出异常:
// ❌ 错误:OrderMap 未配置
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId('OrderMap', 'order_no', 'ORD001');
// 抛出异常: QueryModelMapFlagEmptyError
// ✅ 正确:先在 $modelFlagMap 中配置
private $modelFlagMap = [
'UserMap' => \App\Models\LaravelShardMap\V1\Map\UserMap::class,
'OrderMap' => \App\Models\LaravelShardMap\V1\Map\OrderMap::class, // 新增
];2. 微服务模式下的API配置
微服务模式下必须配置正确的API地址:
// config/shardmap_api_url.php
return [
'queryOneData' => env('SHARDMAP_API_QUERY_ONE_URL', 'http://youhu-base/api/v1/shardmap/query-one'),
'queryManyData' => env('SHARDMAP_API_QUERY_MANY_URL', 'http://youhu-base/api/v1/shardmap/query-many'),
];3. 模糊查询性能
模糊查询性能较低,建议:
- 使用前缀查询(
value%)而不是包含查询(%value%) - 限制返回结果数量
- 结合缓存使用
4. 返回值检查
调用方法后必须检查返回值:
$shardInfo = ModelMapRouterFacade::getShardInfoByNonBizId('UserMap', 'phone', $phone);
// ✅ 检查返回值
if (!$shardInfo) {
return $this->error('用户不存在');
}
// ❌ 不检查返回值可能导致后续错误
$user = User::on($shardInfo['shard_db'])... // $shardInfo 为 null 时报错📊 与 ShardMapHelperFacade 对比
| 特性 | ModelMapRouterFacade | ShardMapHelperFacade |
|---|---|---|
| 使用场景 | 业务代码重构(推荐) | 映射表专用操作 |
| 架构支持 | 单体 + 微服务自动适配 | 仅单体模式 |
| 调用方式 | 使用模型标识('UserMap') | 使用模型类(UserMap::class) |
| 底层实现 | 单体调用 ShardMapHelperFacade 微服务调用 ApiRequestFacade | 直接查询数据库 |
| 适用范围 | 所有业务层代码 | 映射表门面层内部 |
💬 总结
ModelMapRouterFacade 是游鹄生态中业务代码重构时查询映射表的主要入口,其核心价值在于:
- ✅ 自动适配架构:根据配置自动选择单体/微服务模式
- ✅ 统一调用接口:业务层无需关心底层架构
- ✅ 简化开发复杂度:让开发者专注于业务逻辑
- ✅ 支持平滑迁移:从单体到微服务无需修改业务代码
在实际开发中,强烈建议业务层优先使用 ModelMapRouterFacade,以获得最大的架构灵活性和可维护性。
© 2026 youhujun & xueer. All rights reserved.
