更新
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user