更新
This commit is contained in:
@@ -5,16 +5,14 @@ declare(strict_types=1);
|
||||
namespace app\adminapi\controller\firstvisit;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\logic\auth\AuthLogic;
|
||||
use app\adminapi\logic\firstvisit\WecomAcquisitionCustomerLogic;
|
||||
use app\adminapi\logic\firstvisit\WecomPromotionLogic;
|
||||
use app\common\service\qywx\QywxPromotionContactApiService;
|
||||
use app\common\service\qywx\QywxPromotionMediaService;
|
||||
use app\common\service\qywx\QywxPromotionOperatorAccess;
|
||||
|
||||
class WecomPromotionController extends BaseAdminController
|
||||
{
|
||||
private const PAGE_PERMISSION = 'firstvisit.wecomPromotion/overview';
|
||||
|
||||
public function tagOptions()
|
||||
{
|
||||
if (!$this->hasPagePermission()) {
|
||||
@@ -25,7 +23,7 @@ class WecomPromotionController extends BaseAdminController
|
||||
|
||||
public function createTag()
|
||||
{
|
||||
if (!$this->hasPagePermission()) {
|
||||
if (!$this->hasBasePagePermission()) {
|
||||
return $this->fail('权限不足');
|
||||
}
|
||||
if (!$this->request->isPost()) {
|
||||
@@ -68,9 +66,13 @@ class WecomPromotionController extends BaseAdminController
|
||||
if (!$this->hasPagePermission()) {
|
||||
return $this->fail('权限不足');
|
||||
}
|
||||
$params = $this->request->post();
|
||||
if ((int) ($params['id'] ?? 0) <= 0 && !$this->hasBasePagePermission()) {
|
||||
return $this->fail('共享操作人只能编辑已授权方案,不能新建分流方案');
|
||||
}
|
||||
|
||||
return $this->run(fn () => $this->success('分流方案已保存', WecomPromotionLogic::savePool(
|
||||
$this->request->post(),
|
||||
$params,
|
||||
$this->adminId,
|
||||
$this->adminInfo
|
||||
)));
|
||||
@@ -91,7 +93,7 @@ class WecomPromotionController extends BaseAdminController
|
||||
|
||||
public function batchSetOperators()
|
||||
{
|
||||
if (!$this->hasPagePermission()) {
|
||||
if (!$this->hasBasePagePermission()) {
|
||||
return $this->fail('权限不足');
|
||||
}
|
||||
|
||||
@@ -104,7 +106,7 @@ class WecomPromotionController extends BaseAdminController
|
||||
|
||||
public function deletePool()
|
||||
{
|
||||
if (!$this->hasPagePermission()) {
|
||||
if (!$this->hasBasePagePermission()) {
|
||||
return $this->fail('权限不足');
|
||||
}
|
||||
$id = (int) $this->request->post('id', 0);
|
||||
@@ -158,7 +160,7 @@ class WecomPromotionController extends BaseAdminController
|
||||
|
||||
public function checkApiPermission()
|
||||
{
|
||||
if (!$this->hasPagePermission()) {
|
||||
if (!$this->hasBasePagePermission()) {
|
||||
return $this->fail('权限不足');
|
||||
}
|
||||
|
||||
@@ -167,7 +169,7 @@ class WecomPromotionController extends BaseAdminController
|
||||
|
||||
public function syncRemoteLinks()
|
||||
{
|
||||
if (!$this->hasPagePermission()) {
|
||||
if (!$this->hasBasePagePermission()) {
|
||||
return $this->fail('权限不足');
|
||||
}
|
||||
$poolId = (int) $this->request->post('pool_id', 0);
|
||||
@@ -195,7 +197,7 @@ class WecomPromotionController extends BaseAdminController
|
||||
|
||||
public function deleteRemoteLink()
|
||||
{
|
||||
if (!$this->hasPagePermission()) {
|
||||
if (!$this->hasBasePagePermission()) {
|
||||
return $this->fail('权限不足');
|
||||
}
|
||||
$id = (int) $this->request->post('id', 0);
|
||||
@@ -250,7 +252,7 @@ class WecomPromotionController extends BaseAdminController
|
||||
|
||||
public function deleteLink()
|
||||
{
|
||||
if (!$this->hasPagePermission()) {
|
||||
if (!$this->hasBasePagePermission()) {
|
||||
return $this->fail('权限不足');
|
||||
}
|
||||
$id = (int) $this->request->post('id', 0);
|
||||
@@ -273,10 +275,11 @@ class WecomPromotionController extends BaseAdminController
|
||||
|
||||
private function hasPagePermission(): bool
|
||||
{
|
||||
if ((int) ($this->adminInfo['root'] ?? 0) === 1) {
|
||||
return true;
|
||||
}
|
||||
return QywxPromotionOperatorAccess::hasPagePermission($this->adminId, $this->adminInfo);
|
||||
}
|
||||
|
||||
return in_array(self::PAGE_PERMISSION, AuthLogic::getAuthByAdminId($this->adminId), true);
|
||||
private function hasBasePagePermission(): bool
|
||||
{
|
||||
return QywxPromotionOperatorAccess::hasBasePagePermission($this->adminId, $this->adminInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@ declare (strict_types=1);
|
||||
|
||||
namespace app\adminapi\http\middleware;
|
||||
|
||||
use app\adminapi\logic\LoginLogic;
|
||||
use app\common\service\pharmacy\PharmacyUploadPermissionAlias;
|
||||
use app\adminapi\logic\LoginLogic;
|
||||
use app\common\service\pharmacy\PharmacyUploadPermissionAlias;
|
||||
use app\common\service\qywx\QywxPromotionOperatorAccess;
|
||||
use app\common\{
|
||||
cache\AdminAuthCache,
|
||||
service\JsonService
|
||||
@@ -69,10 +70,23 @@ class AuthMiddleware
|
||||
|
||||
$adminAuthCache = new AdminAuthCache($request->adminInfo['admin_id']);
|
||||
|
||||
// 当前访问路径
|
||||
$accessUri = strtolower($request->controller() . '/' . $request->action());
|
||||
// 全部路由
|
||||
$allUri = $this->formatUrl($adminAuthCache->getAllUri());
|
||||
// 当前访问路径
|
||||
$accessUri = strtolower($request->controller() . '/' . $request->action());
|
||||
|
||||
// 获客助手的子接口多数不是独立菜单权限。整组动作统一绑定页面权限,
|
||||
// 共享操作人的动态页面权限也必须先经过这一层,再由业务层校验具体方案。
|
||||
if (str_starts_with($accessUri, 'firstvisit.wecompromotion/')) {
|
||||
$adminUris = $this->formatUrl($adminAuthCache->getAdminUri() ?? []);
|
||||
if ($this->isKnownWecomPromotionAction($accessUri)
|
||||
&& in_array(strtolower(QywxPromotionOperatorAccess::PAGE_PERMISSION), $adminUris, true)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return JsonService::fail('权限不足,无法访问或操作');
|
||||
}
|
||||
|
||||
// 全部路由
|
||||
$allUri = $this->formatUrl($adminAuthCache->getAllUri());
|
||||
|
||||
// 判断该当前访问的uri是否存在,不存在无需验证
|
||||
if (!in_array($accessUri, $allUri, true)
|
||||
@@ -204,6 +218,31 @@ class AuthMiddleware
|
||||
'tcm.prescription/audit',
|
||||
], true);
|
||||
}
|
||||
|
||||
private function isKnownWecomPromotionAction(string $accessUri): bool
|
||||
{
|
||||
return in_array($accessUri, [
|
||||
'firstvisit.wecompromotion/tagoptions',
|
||||
'firstvisit.wecompromotion/createtag',
|
||||
'firstvisit.wecompromotion/uploadwelcomemedia',
|
||||
'firstvisit.wecompromotion/overview',
|
||||
'firstvisit.wecompromotion/savepool',
|
||||
'firstvisit.wecompromotion/savewidget',
|
||||
'firstvisit.wecompromotion/batchsetoperators',
|
||||
'firstvisit.wecompromotion/deletepool',
|
||||
'firstvisit.wecompromotion/savelink',
|
||||
'firstvisit.wecompromotion/savemember',
|
||||
'firstvisit.wecompromotion/togglemember',
|
||||
'firstvisit.wecompromotion/checkapipermission',
|
||||
'firstvisit.wecompromotion/syncremotelinks',
|
||||
'firstvisit.wecompromotion/remotelinkdetail',
|
||||
'firstvisit.wecompromotion/deleteremotelink',
|
||||
'firstvisit.wecompromotion/synccustomers',
|
||||
'firstvisit.wecompromotion/customerstatistics',
|
||||
'firstvisit.wecompromotion/togglelink',
|
||||
'firstvisit.wecompromotion/deletelink',
|
||||
], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处方库 lists:与开方、处方库维护菜单权限互通(避免开方页「从处方库导入」403)
|
||||
|
||||
@@ -18,6 +18,7 @@ use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\AdminRole;
|
||||
use app\common\model\auth\SystemMenu;
|
||||
use app\common\model\auth\SystemRoleMenu;
|
||||
use app\common\service\qywx\QywxPromotionOperatorAccess;
|
||||
|
||||
|
||||
/**
|
||||
@@ -74,11 +75,9 @@ class AuthLogic
|
||||
->column('perms');
|
||||
|
||||
$hasAllAuth = array_diff($allAuth, $roleAuth);
|
||||
if (empty($hasAllAuth)) {
|
||||
return ['*'];
|
||||
}
|
||||
$permissions = empty($hasAllAuth) ? ['*'] : $roleAuth;
|
||||
|
||||
return $roleAuth;
|
||||
return self::appendSharedPromotionPermission($permissions, (int) ($admin['id'] ?? 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -94,12 +93,28 @@ class AuthLogic
|
||||
$roleIds = AdminRole::where('admin_id', $adminId)->column('role_id');
|
||||
$menuId = SystemRoleMenu::whereIn('role_id', $roleIds)->column('menu_id');
|
||||
|
||||
return SystemMenu::distinct(true)
|
||||
$permissions = SystemMenu::distinct(true)
|
||||
->where([
|
||||
['is_disable', '=', 0],
|
||||
['perms', '<>', ''],
|
||||
['id', 'in', array_unique($menuId)],
|
||||
])
|
||||
->column('perms');
|
||||
|
||||
return self::appendSharedPromotionPermission($permissions, $adminId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static function appendSharedPromotionPermission(array $permissions, int $adminId): array
|
||||
{
|
||||
if (in_array('*', $permissions, true)
|
||||
|| in_array(QywxPromotionOperatorAccess::PAGE_PERMISSION, $permissions, true)
|
||||
|| !QywxPromotionOperatorAccess::hasSharedPagePermission($adminId)) {
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
$permissions[] = QywxPromotionOperatorAccess::PAGE_PERMISSION;
|
||||
|
||||
return array_values(array_unique($permissions));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use app\common\logic\BaseLogic;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\SystemMenu;
|
||||
use app\common\model\auth\SystemRoleMenu;
|
||||
use app\common\service\qywx\QywxPromotionOperatorAccess;
|
||||
|
||||
|
||||
/**
|
||||
@@ -51,6 +52,12 @@ class MenuLogic extends BaseLogic
|
||||
|
||||
if ($admin['root'] != 1) {
|
||||
$roleMenu = SystemRoleMenu::whereIn('role_id', $admin['role_id'])->column('menu_id');
|
||||
if (QywxPromotionOperatorAccess::hasSharedPagePermission((int) $adminId)) {
|
||||
$roleMenu = array_values(array_unique(array_merge(
|
||||
array_map('intval', $roleMenu),
|
||||
self::sharedPromotionMenuIds()
|
||||
)));
|
||||
}
|
||||
$where[] = ['id', 'in', $roleMenu];
|
||||
}
|
||||
|
||||
@@ -62,6 +69,45 @@ class MenuLogic extends BaseLogic
|
||||
}
|
||||
|
||||
|
||||
/** @return list<int> */
|
||||
private static function sharedPromotionMenuIds(): array
|
||||
{
|
||||
$menus = SystemMenu::where('is_disable', 0)
|
||||
->field('id,pid,perms')
|
||||
->select()
|
||||
->toArray();
|
||||
$byId = [];
|
||||
$pageIds = [];
|
||||
foreach ($menus as $menu) {
|
||||
$id = (int) ($menu['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
}
|
||||
$byId[$id] = $menu;
|
||||
if ((string) ($menu['perms'] ?? '') === QywxPromotionOperatorAccess::PAGE_PERMISSION) {
|
||||
$pageIds[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($pageIds as $pageId) {
|
||||
$chain = [];
|
||||
$currentId = $pageId;
|
||||
while ($currentId > 0) {
|
||||
if (!isset($byId[$currentId])) {
|
||||
$chain = [];
|
||||
break;
|
||||
}
|
||||
$chain[] = $currentId;
|
||||
$currentId = (int) ($byId[$currentId]['pid'] ?? 0);
|
||||
}
|
||||
$result = array_merge($result, $chain);
|
||||
}
|
||||
|
||||
return array_values(array_unique($result));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @notes 添加菜单
|
||||
* @param array $params
|
||||
@@ -181,4 +227,4 @@ class MenuLogic extends BaseLogic
|
||||
return linear_to_tree($data, 'children');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace app\adminapi\logic\firstvisit;
|
||||
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use app\common\service\qywx\QywxCustomerAcquisitionCustomerService;
|
||||
use app\common\service\qywx\QywxPromotionOperatorAccess;
|
||||
use RuntimeException;
|
||||
use think\facade\Db;
|
||||
|
||||
@@ -23,7 +24,7 @@ class WecomAcquisitionCustomerLogic
|
||||
self::applyScope(
|
||||
$query,
|
||||
'l',
|
||||
DataScopeService::getVisibleAdminIds($adminId, $adminInfo),
|
||||
QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo),
|
||||
self::operatorPoolIds($adminId)
|
||||
);
|
||||
if ($localLinkId > 0) {
|
||||
@@ -60,7 +61,8 @@ class WecomAcquisitionCustomerLogic
|
||||
{
|
||||
$page = max(1, (int) ($params['page_no'] ?? $params['page'] ?? 1));
|
||||
$pageSize = min(100, max(1, (int) ($params['page_size'] ?? 20)));
|
||||
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
$hasBasePagePermission = QywxPromotionOperatorAccess::hasBasePagePermission($adminId, $adminInfo);
|
||||
$visibleIds = QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo);
|
||||
$operatorPoolIds = self::operatorPoolIds($adminId);
|
||||
$base = self::customerQuery($params, $visibleIds, $operatorPoolIds);
|
||||
$total = (int) (clone $base)->count();
|
||||
@@ -87,7 +89,9 @@ class WecomAcquisitionCustomerLogic
|
||||
|
||||
return [
|
||||
'meta' => [
|
||||
'scope_label' => DataScopeService::scopeLabel(DataScopeService::getEffectiveScope($adminInfo)),
|
||||
'scope_label' => $hasBasePagePermission
|
||||
? DataScopeService::scopeLabel(DataScopeService::getEffectiveScope($adminInfo))
|
||||
: '仅共享方案',
|
||||
'generated_at' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
'summary' => [
|
||||
@@ -188,18 +192,7 @@ class WecomAcquisitionCustomerLogic
|
||||
/** @return list<int> */
|
||||
private static function operatorPoolIds(int $adminId): array
|
||||
{
|
||||
if ($adminId <= 0) {
|
||||
return [];
|
||||
}
|
||||
$ids = Db::name('qywx_promotion_pool_operator')
|
||||
->where('admin_id', $adminId)
|
||||
->whereNull('delete_time')
|
||||
->column('pool_id');
|
||||
|
||||
return array_values(array_unique(array_filter(array_map(
|
||||
static fn ($value): int => (int) $value,
|
||||
$ids
|
||||
), static fn (int $value): bool => $value > 0)));
|
||||
return QywxPromotionOperatorAccess::activePoolIds($adminId);
|
||||
}
|
||||
|
||||
private static function maskIdentifier(string $value): string
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace app\adminapi\logic\firstvisit;
|
||||
|
||||
use app\adminapi\logic\dept\DeptLogic;
|
||||
use app\common\cache\AdminAuthCache;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use app\common\service\qywx\QywxCustomerAcquisitionApiService;
|
||||
use app\common\service\qywx\QywxCustomerAcquisitionLinkService;
|
||||
@@ -13,10 +14,12 @@ use app\common\service\qywx\QywxPromotionConfig;
|
||||
use app\common\service\qywx\QywxPromotionContactApiService;
|
||||
use app\common\service\qywx\QywxPromotionMediaService;
|
||||
use app\common\service\qywx\QywxPromotionMemberSchedulerService;
|
||||
use app\common\service\qywx\QywxPromotionOperatorAccess;
|
||||
use app\common\service\qywx\QywxPromotionRangeSyncService;
|
||||
use app\common\service\qywx\QywxPromotionWidgetService;
|
||||
use RuntimeException;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
/** 一诊 / 企业微信获客助手管理逻辑。 */
|
||||
class WecomPromotionLogic
|
||||
@@ -25,7 +28,8 @@ class WecomPromotionLogic
|
||||
{
|
||||
self::assertMemberDispatchSchema();
|
||||
self::assertPoolOperatorSchema();
|
||||
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
$hasBasePagePermission = QywxPromotionOperatorAccess::hasBasePagePermission($adminId, $adminInfo);
|
||||
$visibleIds = QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo);
|
||||
$operatorPoolIds = self::operatorPoolIds($adminId);
|
||||
$poolsQuery = Db::name('qywx_promotion_pool')->alias('p')
|
||||
->leftJoin('admin u', 'u.id = p.owner_admin_id')
|
||||
@@ -174,7 +178,8 @@ class WecomPromotionLogic
|
||||
));
|
||||
$pool['is_shared_with_me'] = in_array($adminId, $pool['operator_admin_ids'], true);
|
||||
$pool['can_operate'] = true;
|
||||
$pool['can_manage_access'] = self::ownerInScope((int) ($pool['owner_admin_id'] ?? 0), $visibleIds);
|
||||
$pool['can_manage_access'] = $hasBasePagePermission
|
||||
&& self::ownerInScope((int) ($pool['owner_admin_id'] ?? 0), $visibleIds);
|
||||
$pool['can_delete'] = $pool['can_manage_access'];
|
||||
$pool['dispatch_sync'] = $sync;
|
||||
$pool['using_backup'] = $reception['using_backup'];
|
||||
@@ -196,7 +201,9 @@ class WecomPromotionLogic
|
||||
return [
|
||||
'meta' => [
|
||||
'admin_id' => $adminId,
|
||||
'scope_label' => DataScopeService::scopeLabel(DataScopeService::getEffectiveScope($adminInfo)),
|
||||
'scope_label' => $hasBasePagePermission
|
||||
? DataScopeService::scopeLabel(DataScopeService::getEffectiveScope($adminInfo))
|
||||
: '仅共享方案',
|
||||
'generated_at' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
'config' => $config,
|
||||
@@ -210,7 +217,9 @@ class WecomPromotionLogic
|
||||
'links' => $links,
|
||||
'member_options' => $memberOptions,
|
||||
'operator_options' => $operatorOptions,
|
||||
'department_options' => DeptLogic::getAllDataScoped($adminId, $adminInfo),
|
||||
'department_options' => $hasBasePagePermission
|
||||
? DeptLogic::getAllDataScoped($adminId, $adminInfo)
|
||||
: [],
|
||||
'automation_installed' => QywxPromotionConfig::installed(),
|
||||
'customer_acquisition_link_example' => QywxCustomerAcquisitionLinkService::example(),
|
||||
];
|
||||
@@ -220,6 +229,9 @@ class WecomPromotionLogic
|
||||
{
|
||||
self::assertMemberDispatchSchema();
|
||||
$id = max(0, (int) ($params['id'] ?? 0));
|
||||
if ($id <= 0 && !QywxPromotionOperatorAccess::hasBasePagePermission($adminId, $adminInfo)) {
|
||||
throw new RuntimeException('共享操作人只能编辑已授权方案,不能新建分流方案');
|
||||
}
|
||||
$existingPool = $id > 0
|
||||
? self::assertScopedRow('qywx_promotion_pool', $id, $adminId, $adminInfo)
|
||||
: null;
|
||||
@@ -459,7 +471,12 @@ class WecomPromotionLogic
|
||||
?QywxCustomerAcquisitionApiService $api = null
|
||||
): void
|
||||
{
|
||||
self::assertBasePagePermission($adminId, $adminInfo);
|
||||
self::assertScopedRow('qywx_promotion_pool', $id, $adminId, $adminInfo, false);
|
||||
$operatorAdminIds = self::normalizePositiveIds(Db::name('qywx_promotion_pool_operator')
|
||||
->where('pool_id', $id)
|
||||
->whereNull('delete_time')
|
||||
->column('admin_id'));
|
||||
$links = Db::name('qywx_promotion_link')
|
||||
->where('pool_id', $id)
|
||||
->order('id', 'asc')
|
||||
@@ -563,6 +580,7 @@ class WecomPromotionLogic
|
||||
'update_time' => $now,
|
||||
]);
|
||||
});
|
||||
self::clearOperatorAuthCaches($operatorAdminIds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,6 +592,7 @@ class WecomPromotionLogic
|
||||
public static function batchSetOperators(array $params, int $adminId, array $adminInfo): array
|
||||
{
|
||||
self::assertPoolOperatorSchema();
|
||||
self::assertBasePagePermission($adminId, $adminInfo);
|
||||
$poolIds = self::normalizePositiveIds((array) ($params['pool_ids'] ?? []));
|
||||
$operatorAdminIds = self::normalizePositiveIds((array) (
|
||||
$params['operator_admin_ids'] ?? $params['admin_ids'] ?? []
|
||||
@@ -598,9 +617,8 @@ class WecomPromotionLogic
|
||||
throw new RuntimeException('不能将当前账号设置为自己的共享操作人');
|
||||
}
|
||||
|
||||
$pools = [];
|
||||
foreach ($poolIds as $poolId) {
|
||||
$pools[$poolId] = self::assertScopedRow(
|
||||
self::assertScopedRow(
|
||||
'qywx_promotion_pool',
|
||||
$poolId,
|
||||
$adminId,
|
||||
@@ -609,7 +627,7 @@ class WecomPromotionLogic
|
||||
);
|
||||
}
|
||||
|
||||
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
$visibleIds = QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo);
|
||||
if ($visibleIds !== null) {
|
||||
foreach ($operatorAdminIds as $operatorAdminId) {
|
||||
if (!in_array($operatorAdminId, $visibleIds, true)) {
|
||||
@@ -621,40 +639,24 @@ class WecomPromotionLogic
|
||||
if ($action === 'grant') {
|
||||
$adminQuery->where('disable', 0);
|
||||
}
|
||||
$operatorAdmins = $adminQuery->field('id,root')->select()->toArray();
|
||||
$operatorAdmins = $adminQuery->field('id')->select()->toArray();
|
||||
$existingAdminIds = self::normalizePositiveIds(array_column($operatorAdmins, 'id'));
|
||||
if (count($existingAdminIds) !== count($operatorAdminIds)) {
|
||||
throw new RuntimeException($action === 'grant'
|
||||
? '选择的操作人不存在或账号已被禁用'
|
||||
: '选择的操作人不存在');
|
||||
}
|
||||
if ($action === 'grant') {
|
||||
$pagePermissionAdminIds = self::promotionPagePermissionAdminIdSet($operatorAdminIds);
|
||||
foreach ($operatorAdmins as $operatorAdmin) {
|
||||
if ((int) ($operatorAdmin['root'] ?? 0) !== 1
|
||||
&& !isset($pagePermissionAdminIds[(int) ($operatorAdmin['id'] ?? 0)])) {
|
||||
throw new RuntimeException('选择的操作人尚未获得企业微信获客助手页面权限');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$affected = Db::transaction(function () use (
|
||||
$action,
|
||||
$poolIds,
|
||||
$pools,
|
||||
$operatorAdminIds,
|
||||
$adminId,
|
||||
$now
|
||||
): int {
|
||||
$changed = 0;
|
||||
foreach ($poolIds as $poolId) {
|
||||
$ownerAdminId = (int) ($pools[$poolId]['owner_admin_id'] ?? 0);
|
||||
foreach ($operatorAdminIds as $operatorAdminId) {
|
||||
// 方案归属人天然拥有权限,无需写入共享关系。
|
||||
if ($operatorAdminId === $ownerAdminId) {
|
||||
continue;
|
||||
}
|
||||
$query = Db::name('qywx_promotion_pool_operator')
|
||||
->where('pool_id', $poolId)
|
||||
->where('admin_id', $operatorAdminId);
|
||||
@@ -689,6 +691,7 @@ class WecomPromotionLogic
|
||||
|
||||
return $changed;
|
||||
});
|
||||
self::clearOperatorAuthCaches($operatorAdminIds);
|
||||
|
||||
return [
|
||||
'action' => $action,
|
||||
@@ -836,13 +839,14 @@ class WecomPromotionLogic
|
||||
*/
|
||||
public static function syncRemoteLinks(int $poolId, int $adminId, array $adminInfo): array
|
||||
{
|
||||
$pool = self::assertScopedRow('qywx_promotion_pool', $poolId, $adminId, $adminInfo);
|
||||
self::assertBasePagePermission($adminId, $adminInfo);
|
||||
$pool = self::assertScopedRow('qywx_promotion_pool', $poolId, $adminId, $adminInfo, false);
|
||||
$legacyCount = (int) Db::name('qywx_promotion_link')
|
||||
->where('pool_id', $poolId)
|
||||
->whereNull('delete_time')
|
||||
->whereRaw("(remote_link_id IS NULL OR remote_link_id = '')")
|
||||
->count();
|
||||
$visibleAdminIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
$visibleAdminIds = QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo);
|
||||
$visibleUserIds = null;
|
||||
if ($visibleAdminIds !== null) {
|
||||
$visibleUserIds = array_fill_keys(array_column(self::memberOptions(
|
||||
@@ -914,7 +918,7 @@ class WecomPromotionLogic
|
||||
}
|
||||
$api = new QywxCustomerAcquisitionApiService();
|
||||
$remote = self::normaliseRemoteLink($api->getLink($remoteLinkId), $remoteLinkId);
|
||||
$visibleUserIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo) === null
|
||||
$visibleUserIds = QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo) === null
|
||||
? null
|
||||
: array_fill_keys(array_column(self::memberOptions(
|
||||
$adminId,
|
||||
@@ -932,7 +936,8 @@ class WecomPromotionLogic
|
||||
/** 官方链接由方案统一删除,避免绕过方案删除租约和同步状态机。 */
|
||||
public static function deleteRemoteLink(int $id, int $adminId, array $adminInfo): void
|
||||
{
|
||||
$row = self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo);
|
||||
self::assertBasePagePermission($adminId, $adminInfo);
|
||||
$row = self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo, false);
|
||||
$remoteLinkId = trim((string) ($row['remote_link_id'] ?? ''));
|
||||
if ($remoteLinkId === '') {
|
||||
throw new RuntimeException('历史手工链接只能从本地移除');
|
||||
@@ -955,7 +960,8 @@ class WecomPromotionLogic
|
||||
|
||||
public static function deleteLink(int $id, int $adminId, array $adminInfo): void
|
||||
{
|
||||
$row = self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo);
|
||||
self::assertBasePagePermission($adminId, $adminInfo);
|
||||
$row = self::assertScopedRow('qywx_promotion_link', $id, $adminId, $adminInfo, false);
|
||||
if (trim((string) ($row['remote_link_id'] ?? '')) !== '') {
|
||||
throw new RuntimeException('官方获客链接不能仅从本地移除,请使用“删除分流方案”同步删除企业微信链接');
|
||||
}
|
||||
@@ -968,7 +974,7 @@ class WecomPromotionLogic
|
||||
/** @return list<array{id:int,name:string,disable:int,can_grant:bool,display_dept_id:int,dept_ids:list<int>,dept_names:list<string>}> */
|
||||
private static function operatorOptions(int $adminId, array $adminInfo): array
|
||||
{
|
||||
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
$visibleIds = QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo);
|
||||
if ($visibleIds === []) {
|
||||
return [];
|
||||
}
|
||||
@@ -978,24 +984,21 @@ class WecomPromotionLogic
|
||||
if ($visibleIds !== null) {
|
||||
$query->whereIn('a.id', $visibleIds);
|
||||
}
|
||||
$admins = $query->field('a.id,a.name,a.root,a.disable')->order('a.disable', 'asc')->order('a.name', 'asc')->order('a.id', 'asc')->select()->toArray();
|
||||
$admins = $query->field('a.id,a.name,a.disable')->order('a.disable', 'asc')->order('a.name', 'asc')->order('a.id', 'asc')->select()->toArray();
|
||||
if ($admins === []) {
|
||||
return [];
|
||||
}
|
||||
$adminIds = self::normalizePositiveIds(array_column($admins, 'id'));
|
||||
$departments = self::adminDepartmentMaps($adminIds);
|
||||
$pagePermissionAdminIds = self::promotionPagePermissionAdminIdSet($adminIds);
|
||||
$result = [];
|
||||
foreach ($admins as $admin) {
|
||||
$aid = (int) $admin['id'];
|
||||
$deptIds = array_values(array_unique(array_filter($departments[$aid]['ids'] ?? [])));
|
||||
$hasPagePermission = (int) ($admin['root'] ?? 0) === 1
|
||||
|| isset($pagePermissionAdminIds[$aid]);
|
||||
$result[] = [
|
||||
'id' => $aid,
|
||||
'name' => (string) ($admin['name'] ?? ('账号 ' . $aid)),
|
||||
'disable' => (int) ($admin['disable'] ?? 0),
|
||||
'can_grant' => (int) ($admin['disable'] ?? 0) === 0 && $hasPagePermission,
|
||||
'can_grant' => (int) ($admin['disable'] ?? 0) === 0,
|
||||
'display_dept_id' => (int) ($deptIds[0] ?? 0),
|
||||
'dept_ids' => $deptIds,
|
||||
'dept_names' => array_values(array_unique($departments[$aid]['names'] ?? [])),
|
||||
@@ -1038,7 +1041,7 @@ class WecomPromotionLogic
|
||||
/** @return list<array{id:int,name:string,userid:string,display_dept_id:int,dept_ids:list<int>,dept_names:list<string>}> */
|
||||
private static function memberOptions(int $adminId, array $adminInfo, array $extraAdminIds = []): array
|
||||
{
|
||||
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
$visibleIds = QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo);
|
||||
$allowedIds = $visibleIds === null
|
||||
? null
|
||||
: self::normalizePositiveIds(array_merge($visibleIds, $extraAdminIds));
|
||||
@@ -1439,7 +1442,7 @@ class WecomPromotionLogic
|
||||
throw new RuntimeException('数据不存在或已删除');
|
||||
}
|
||||
|
||||
$visibleIds = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
$visibleIds = QywxPromotionOperatorAccess::visibleAdminIds($adminId, $adminInfo);
|
||||
$pool = $table === 'qywx_promotion_pool'
|
||||
? $row
|
||||
: ($table === 'qywx_promotion_link'
|
||||
@@ -1506,14 +1509,7 @@ class WecomPromotionLogic
|
||||
/** @return list<int> */
|
||||
private static function operatorPoolIds(int $adminId): array
|
||||
{
|
||||
if ($adminId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return self::normalizePositiveIds(Db::name('qywx_promotion_pool_operator')
|
||||
->where('admin_id', $adminId)
|
||||
->whereNull('delete_time')
|
||||
->column('pool_id'));
|
||||
return QywxPromotionOperatorAccess::activePoolIds($adminId);
|
||||
}
|
||||
|
||||
private static function isPoolOperator(int $poolId, int $adminId): bool
|
||||
@@ -1566,33 +1562,6 @@ class WecomPromotionLogic
|
||||
return $departments;
|
||||
}
|
||||
|
||||
/** @param list<int> $adminIds @return array<int,true> */
|
||||
private static function promotionPagePermissionAdminIdSet(array $adminIds): array
|
||||
{
|
||||
if ($adminIds === []) {
|
||||
return [];
|
||||
}
|
||||
$menuIds = self::normalizePositiveIds(Db::name('system_menu')
|
||||
->where('perms', 'firstvisit.wecomPromotion/overview')
|
||||
->where('is_disable', 0)
|
||||
->column('id'));
|
||||
if ($menuIds === []) {
|
||||
return [];
|
||||
}
|
||||
$roleIds = self::normalizePositiveIds(Db::name('system_role_menu')
|
||||
->whereIn('menu_id', $menuIds)
|
||||
->column('role_id'));
|
||||
if ($roleIds === []) {
|
||||
return [];
|
||||
}
|
||||
$permittedAdminIds = self::normalizePositiveIds(Db::name('admin_role')
|
||||
->whereIn('role_id', $roleIds)
|
||||
->whereIn('admin_id', $adminIds)
|
||||
->column('admin_id'));
|
||||
|
||||
return array_fill_keys($permittedAdminIds, true);
|
||||
}
|
||||
|
||||
/** @return list<int> */
|
||||
private static function normalizePositiveIds(array $ids): array
|
||||
{
|
||||
@@ -1620,6 +1589,30 @@ class WecomPromotionLogic
|
||||
return $time === false ? 0 : $time;
|
||||
}
|
||||
|
||||
private static function assertBasePagePermission(int $adminId, array $adminInfo): void
|
||||
{
|
||||
if (!QywxPromotionOperatorAccess::hasBasePagePermission($adminId, $adminInfo)) {
|
||||
throw new RuntimeException('共享操作人无权执行新建、删除、转授权或全局同步操作');
|
||||
}
|
||||
}
|
||||
|
||||
/** @param list<int> $adminIds */
|
||||
private static function clearOperatorAuthCaches(array $adminIds): void
|
||||
{
|
||||
foreach (self::normalizePositiveIds($adminIds) as $operatorAdminId) {
|
||||
try {
|
||||
(new AdminAuthCache($operatorAdminId))->clearAuthCache();
|
||||
} catch (\Throwable $error) {
|
||||
// 授权关系已提交,缓存清理失败不应回滚数据;业务接口仍有实时权限校验。
|
||||
Log::warning(sprintf(
|
||||
'清理获客助手共享操作人权限缓存失败 admin_id=%d: %s',
|
||||
$operatorAdminId,
|
||||
$error->getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertMemberDispatchSchema(): void
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\service\qywx;
|
||||
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use think\facade\Db;
|
||||
|
||||
/** 分流方案共享操作人产生的页面入口与专用数据范围。 */
|
||||
final class QywxPromotionOperatorAccess
|
||||
{
|
||||
public const PAGE_PERMISSION = 'firstvisit.wecomPromotion/overview';
|
||||
|
||||
public static function hasBasePagePermission(int $adminId, array $adminInfo = []): bool
|
||||
{
|
||||
if ((int) ($adminInfo['root'] ?? 0) === 1) {
|
||||
return true;
|
||||
}
|
||||
if ($adminId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Db::name('admin_role')->alias('ar')
|
||||
->join('system_role_menu rm', 'rm.role_id = ar.role_id')
|
||||
->join('system_menu m', 'm.id = rm.menu_id')
|
||||
->where('ar.admin_id', $adminId)
|
||||
->where('m.perms', self::PAGE_PERMISSION)
|
||||
->where('m.is_disable', 0)
|
||||
->count() > 0;
|
||||
}
|
||||
|
||||
public static function hasSharedPagePermission(int $adminId): bool
|
||||
{
|
||||
if ($adminId <= 0 || !self::pageMenuEnabled()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return Db::name('qywx_promotion_pool_operator')->alias('po')
|
||||
->join('qywx_promotion_pool p', 'p.id = po.pool_id')
|
||||
->where('po.admin_id', $adminId)
|
||||
->whereNull('po.delete_time')
|
||||
->whereNull('p.delete_time')
|
||||
->count() > 0;
|
||||
} catch (\Throwable $error) {
|
||||
if (self::isMissingTable($error)) {
|
||||
return false;
|
||||
}
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
public static function hasPagePermission(int $adminId, array $adminInfo = []): bool
|
||||
{
|
||||
return self::hasBasePagePermission($adminId, $adminInfo)
|
||||
|| self::hasSharedPagePermission($adminId);
|
||||
}
|
||||
|
||||
/** 基础页面权限沿用角色数据范围;纯共享账号只能通过 operator pool 范围访问。 */
|
||||
public static function visibleAdminIds(int $adminId, array $adminInfo): ?array
|
||||
{
|
||||
return self::hasBasePagePermission($adminId, $adminInfo)
|
||||
? DataScopeService::getVisibleAdminIds($adminId, $adminInfo)
|
||||
: [];
|
||||
}
|
||||
|
||||
/** @return list<int> */
|
||||
public static function activePoolIds(int $adminId): array
|
||||
{
|
||||
if ($adminId <= 0) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
$ids = Db::name('qywx_promotion_pool_operator')->alias('po')
|
||||
->join('qywx_promotion_pool p', 'p.id = po.pool_id')
|
||||
->where('po.admin_id', $adminId)
|
||||
->whereNull('po.delete_time')
|
||||
->whereNull('p.delete_time')
|
||||
->column('po.pool_id');
|
||||
} catch (\Throwable $error) {
|
||||
if (self::isMissingTable($error)) {
|
||||
return [];
|
||||
}
|
||||
throw $error;
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_filter(array_map(
|
||||
static fn ($value): int => (int) $value,
|
||||
$ids
|
||||
), static fn (int $value): bool => $value > 0)));
|
||||
}
|
||||
|
||||
private static function pageMenuEnabled(): bool
|
||||
{
|
||||
return Db::name('system_menu')
|
||||
->where('perms', self::PAGE_PERMISSION)
|
||||
->where('is_disable', 0)
|
||||
->count() > 0;
|
||||
}
|
||||
|
||||
private static function isMissingTable(\Throwable $error): bool
|
||||
{
|
||||
$message = strtolower($error->getMessage());
|
||||
|
||||
return str_contains($message, '42s02')
|
||||
|| str_contains($message, '1146')
|
||||
|| str_contains($message, 'no such table');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user