chore: 管理端与 API 批量更新(测评恢复、看板、分销与用户)

1、修复了用户详情/测评结果与人脸结果解析展示问题;管理端订单、分销及超管用户相关接口与展示异常。

2、新增了 testResultParse 统一解析工具、mbti_test_results 恢复 SQL 及生成脚本;补充 API 路由与 Dashboard/用户侧能力。

3、优化了企业/超管数据看板与分销结算逻辑;精简超管 Settings 与布局代码;完善 faceResultDetail;小程序 app.js 小幅同步。

Made-with: Cursor
This commit is contained in:
Ghost
2026-03-31 10:56:28 +08:00
parent 3af7bb35dd
commit 3fdf9b6ed2
18 changed files with 1306 additions and 546 deletions

View File

@@ -11,6 +11,65 @@ use think\facade\Request;
*/
class Distribution extends BaseController
{
/**
* 企业管理员 enterpriseIdJWT 可能为空,需与 users 表对齐(与订单等接口一致)
*/
private function resolveEnterpriseIdForAdmin(?array $user): int
{
if (!$user || !in_array($user['role'] ?? '', ['admin', 'enterprise_admin'], true)) {
return 0;
}
$eid = $user['enterpriseId'] ?? null;
if (is_array($eid)) {
$eid = null;
}
$enterpriseId = $eid !== null && $eid !== '' ? (int) $eid : 0;
if ($enterpriseId <= 0) {
$adminRow = Db::name('users')->where('id', (int) ($user['userId'] ?? 0))->find();
$e2 = $adminRow['enterpriseId'] ?? null;
$enterpriseId = ($e2 !== null && $e2 !== '') ? (int) $e2 : 0;
}
return $enterpriseId > 0 ? $enterpriseId : 0;
}
/**
* 本企业维度的「推荐人 id」集合佣金表 + 企业绑定表 + 历史仅填 agentId 的佣金行
*
* @return int[]
*/
private function distributorUserIdsForEnterprise(int $enterpriseId): array
{
if ($enterpriseId <= 0) {
return [];
}
$a = Db::name('commission_records')
->where('enterpriseId', $enterpriseId)
->where('inviterId', '>', 0)
->distinct(true)
->column('inviterId');
$b = Db::name('distribution_bindings')
->where('enterpriseId', $enterpriseId)
->where('inviterId', '>', 0)
->distinct(true)
->column('inviterId');
$c = Db::name('commission_records')
->where('enterpriseId', $enterpriseId)
->where(function ($q) {
$q->whereNull('inviterId')->whereOr('inviterId', 0);
})
->where('agentId', '>', 0)
->distinct(true)
->column('agentId');
$merged = array_merge($a ?: [], $b ?: [], $c ?: []);
return array_values(array_unique(array_filter(array_map('intval', $merged), static function ($v) {
return $v > 0;
})));
}
// ─────────────────────────────────────────────────────────────
// GET distribution/overview
// ─────────────────────────────────────────────────────────────
@@ -21,7 +80,10 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
$todayStart = strtotime(date('Y-m-d 00:00:00'));
$days = 7;
$trendStart = strtotime(date('Y-m-d 00:00:00', strtotime('-' . ($days - 1) . ' days')));
@@ -105,83 +167,102 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
$search = trim((string) Request::param('search', ''));
$page = max(1, (int) Request::param('page', 1));
$pageSize = min(100, (int) Request::param('pageSize', 20));
try {
// 找出与本企业关联的所有有过邀请行为的用户(不限 scopeenterpriseId 筛选)
$query = Db::name('distribution_bindings')
->where('enterpriseId', $enterpriseId)
->distinct(true)
->field('inviterId')
->buildSql();
$poolIds = $this->distributorUserIdsForEnterprise($enterpriseId);
if (empty($poolIds)) {
return success(['list' => [], 'total' => 0, 'page' => $page, 'pageSize' => $pageSize]);
}
$inviterQuery = Db::name('wechat_users')
->alias('u')
->whereRaw("u.id IN {$query}")
->whereIn('u.id', $poolIds)
->field('u.id, u.nickname, u.avatar, u.createdAt');
if ($search !== '') {
$inviterQuery->where(function ($q) use ($search) {
$q->where('u.nickname', 'like', "%{$search}%")
->whereOr('u.id', '=', is_numeric($search) ? (int)$search : -1);
->whereOr('u.id', '=', is_numeric($search) ? (int) $search : -1);
});
}
$total = (clone $inviterQuery)->count();
$inviters = $inviterQuery->page($page, $pageSize)->select()->toArray();
$inviterQuery->order('u.id', 'desc');
$inviterIds = array_column($inviters, 'id');
$total = (int) (clone $inviterQuery)->count();
$inviters = $inviterQuery->page($page, $pageSize)->select()->toArray();
$inviterIds = array_values(array_filter(array_map('intval', array_column($inviters, 'id'))));
$commStats = [];
$withdrawnMap = [];
$teamMap = [];
// 各邀请人的累计佣金与可提现佣金
$commStats = [];
if (!empty($inviterIds)) {
$rows = Db::name('commission_records')
->whereIn('inviterId', $inviterIds)
$inStr = implode(',', $inviterIds);
$rows = Db::name('commission_records')
->where('enterpriseId', $enterpriseId)
->field('inviterId,
SUM(IF(status IN ("paid","frozen"), commissionFen, 0)) as totalFen,
SUM(IF(status = "paid", commissionFen, 0)) as paidFen')
->group('inviterId')
->select()->toArray();
->whereRaw('COALESCE(inviterId, agentId) IN (' . $inStr . ')')
->fieldRaw("COALESCE(inviterId, agentId) AS inviterKey, SUM(IF(status IN ('paid','frozen'), commissionFen, 0)) AS totalFen, SUM(IF(status = 'paid', commissionFen, 0)) AS paidFen")
->group('inviterKey')
->select()
->toArray();
foreach ($rows as $r) {
$commStats[$r['inviterId']] = $r;
$kid = (int) ($r['inviterKey'] ?? 0);
if ($kid > 0) {
$commStats[$kid] = $r;
}
}
// 已提现金额
$withdrawnRows = Db::name('distribution_withdrawals')
->whereIn('userId', $inviterIds)
// 提现金额统计0=审核中,2=待收款,3=已收款
->whereIn('status', [0, 2, 3])
->field('userId, SUM(amountFen) as withdrawnFen')
->group('userId')
->select()->toArray();
$withdrawnMap = [];
->alias('w')
->join('wechat_users u', 'w.userId = u.id')
->whereIn('w.userId', $inviterIds)
->where('u.enterpriseId', $enterpriseId)
->whereIn('w.status', [0, 2, 3])
->field('w.userId, SUM(w.amountFen) AS withdrawnFen')
->group('w.userId')
->select()
->toArray();
foreach ($withdrawnRows as $r) {
$withdrawnMap[$r['userId']] = (int)$r['withdrawnFen'];
$withdrawnMap[(int) ($r['userId'] ?? 0)] = (int) ($r['withdrawnFen'] ?? 0);
}
// 团队人数(绑定人数,不限 scope
$now = time();
$teamRows = Db::name('distribution_bindings')
->whereIn('inviterId', $inviterIds)
->where('enterpriseId', $enterpriseId)
->field('inviterId, COUNT(DISTINCT inviteeId) as teamCount')
->group('inviterId')
->select()->toArray();
$teamMap = [];
->alias('b')
->leftJoin('wechat_users w', 'w.id = b.inviteeId')
->whereIn('b.inviterId', $inviterIds)
->where(function ($q) use ($enterpriseId) {
$q->where('b.enterpriseId', $enterpriseId)
->whereOr(function ($q2) use ($enterpriseId) {
$q2->whereNull('b.enterpriseId')->where('w.enterpriseId', $enterpriseId);
});
})
->where('b.status', 'active')
->where('b.expireAt', '>', $now)
->fieldRaw('b.inviterId, COUNT(DISTINCT b.inviteeId) AS teamCount')
->group('b.inviterId')
->select()
->toArray();
foreach ($teamRows as $r) {
$teamMap[$r['inviterId']] = (int)$r['teamCount'];
$teamMap[(int) ($r['inviterId'] ?? 0)] = (int) ($r['teamCount'] ?? 0);
}
}
$list = [];
foreach ($inviters as $inv) {
$uid = $inv['id'];
$totalFen = (int)($commStats[$uid]['totalFen'] ?? 0);
$paidFen = (int)($commStats[$uid]['paidFen'] ?? 0);
$withdrawn = $withdrawnMap[$uid] ?? 0;
$uid = (int) ($inv['id'] ?? 0);
$totalFen = (int) ($commStats[$uid]['totalFen'] ?? 0);
$paidFen = (int) ($commStats[$uid]['paidFen'] ?? 0);
$withdrawn = (int) ($withdrawnMap[$uid] ?? 0);
$avail = max(0, $paidFen - $withdrawn);
$list[] = [
'id' => $uid,
@@ -189,7 +270,7 @@ class Distribution extends BaseController
'avatar' => $inv['avatar'] ?? '',
'totalCommission' => number_format($totalFen / 100, 2, '.', ''),
'availableCommission'=> number_format($avail / 100, 2, '.', ''),
'teamCount' => $teamMap[$uid] ?? 0,
'teamCount' => (int) ($teamMap[$uid] ?? 0),
'teamPerformance' => '-',
'inviteCode' => '-',
'level' => '-',
@@ -213,7 +294,11 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
$page = max(1, (int) Request::param('page', 1));
$pageSize = min(100, (int) Request::param('pageSize', 20));
$status = Request::param('status', '');
@@ -226,7 +311,16 @@ class Distribution extends BaseController
->leftJoin('wechat_users invt', 'b.inviteeId = invt.id')
->field('b.*, inv.nickname as inviterName, inv.avatar as inviterAvatar,
invt.nickname as inviteeName, invt.avatar as inviteeAvatar')
->where('b.enterpriseId', $enterpriseId);
->where(function ($q) use ($enterpriseId) {
$q->where('b.enterpriseId', $enterpriseId)
->whereOr(function ($q2) use ($enterpriseId) {
$q2->whereNull('b.enterpriseId')
->where(function ($q3) use ($enterpriseId) {
$q3->where('inv.enterpriseId', $enterpriseId)
->whereOr('invt.enterpriseId', $enterpriseId);
});
});
});
if ($inviterId > 0) {
$query->where('b.inviterId', $inviterId);
@@ -264,7 +358,11 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
$page = max(1, (int) Request::param('page', 1));
$pageSize = min(100, (int) Request::param('pageSize', 20));
$status = Request::param('status', '');
@@ -279,7 +377,7 @@ class Distribution extends BaseController
->where('c.enterpriseId', $enterpriseId);
if ($inviterId > 0) {
$query->where('c.inviterId', $inviterId);
$query->whereRaw('COALESCE(c.inviterId, c.agentId) = ?', [$inviterId]);
}
if ($status) {
$query->where('c.status', $status);
@@ -356,7 +454,11 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
$page = max(1, (int) Request::param('page', 1));
$pageSize = min(100, (int) Request::param('pageSize', 20));
$status = Request::param('status', '');
@@ -426,7 +528,11 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
$note = Request::param('note', '');
$now = time();
@@ -497,7 +603,11 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
$note = Request::param('note', '');
$now = time();
@@ -549,7 +659,10 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
try {
$config = Db::name('system_config')
@@ -593,7 +706,11 @@ class Distribution extends BaseController
return error('无权限', 403);
}
$enterpriseId = (int) ($user['enterpriseId'] ?? 0);
$enterpriseId = $this->resolveEnterpriseIdForAdmin($user);
if ($enterpriseId <= 0) {
return error('未绑定企业或企业无效', 400);
}
$settings = Request::only(['enabled', 'promoCenterTitle', 'bindingDays', 'testSettings']);
$promoTitle = trim((string)($settings['promoCenterTitle'] ?? ''));