feat: 高考志愿与定价分销链路更新(管理端/API/小程序)
- 新增高考志愿核心服务、模型、控制器与迁移脚本 - 同步管理端定价/分销/设置与小程序入口、历史、测试选择和支付逻辑 - 补充相关开发文档 Made-with: Cursor
This commit is contained in:
@@ -172,6 +172,8 @@ class AppUser extends BaseController
|
||||
$enterpriseName = $ent['name'] ?? ('企业' . $enterpriseId);
|
||||
}
|
||||
$coopMap = [];
|
||||
$gaokaoMap = [];
|
||||
$gaokaoReportMap = [];
|
||||
if (!empty($ids)) {
|
||||
// 测试统计严格按 test_results.enterpriseId 归属企业过滤
|
||||
$trBase = Db::name('test_results')->where('userId', 'in', $ids);
|
||||
@@ -313,6 +315,47 @@ class AppUser extends BaseController
|
||||
$coopMap = [];
|
||||
}
|
||||
}
|
||||
|
||||
// 高考任务状态与最近报告摘要
|
||||
try {
|
||||
$gqRows = Db::name('gaokao_user_profile')
|
||||
->whereIn('userId', $ids)
|
||||
->where('tenantId', (int) $enterpriseId)
|
||||
->field('id,userId,entryStatus,mbtiStatus,pdpStatus,discStatus,formStatus,analyzeStatus,lastAnalyzeAt,latestReportId,tagsJson')
|
||||
->select()
|
||||
->toArray();
|
||||
$reportIds = [];
|
||||
foreach ($gqRows as $gr) {
|
||||
$uid = (int) ($gr['userId'] ?? 0);
|
||||
if ($uid > 0) {
|
||||
$gaokaoMap[$uid] = $gr;
|
||||
if (!empty($gr['latestReportId'])) {
|
||||
$reportIds[] = (int) $gr['latestReportId'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$reportIds = array_values(array_unique(array_filter($reportIds)));
|
||||
if ($reportIds) {
|
||||
$rRows = Db::name('test_results')
|
||||
->whereIn('id', $reportIds)
|
||||
->where('testType', 'gaokao')
|
||||
->field('id,resultData')
|
||||
->select()
|
||||
->toArray();
|
||||
foreach ($rRows as $rr) {
|
||||
$raw = $rr['resultData'] ?? '';
|
||||
$rd = is_string($raw) ? (json_decode($raw, true) ?: []) : (is_array($raw) ? $raw : []);
|
||||
$ov = (string) ($rd['overview'] ?? '');
|
||||
if ($ov === '' && isset($rd['report']['overview'])) {
|
||||
$ov = (string) $rd['report']['overview'];
|
||||
}
|
||||
$gaokaoReportMap[(int) $rr['id']] = $ov;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$gaokaoMap = [];
|
||||
$gaokaoReportMap = [];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($list as &$row) {
|
||||
@@ -358,6 +401,19 @@ class AppUser extends BaseController
|
||||
$row['cooperationModeTitle'] = null;
|
||||
$row['cooperationChosenAt'] = null;
|
||||
}
|
||||
|
||||
$gq = $gaokaoMap[$id] ?? null;
|
||||
$row['gaokaoEntryStatus'] = $gq ? (int) ($gq['entryStatus'] ?? 0) : 0;
|
||||
$row['gaokaoAnalyzeStatus'] = $gq ? (int) ($gq['analyzeStatus'] ?? 0) : 0;
|
||||
$row['gaokaoFormStatus'] = $gq ? (int) ($gq['formStatus'] ?? 0) : 0;
|
||||
$row['gaokaoTaskStatus'] = [
|
||||
'mbti' => $gq ? (int) ($gq['mbtiStatus'] ?? 0) : 0,
|
||||
'pdp' => $gq ? (int) ($gq['pdpStatus'] ?? 0) : 0,
|
||||
'disc' => $gq ? (int) ($gq['discStatus'] ?? 0) : 0,
|
||||
];
|
||||
$row['gaokaoLastAnalyzeAt'] = $gq ? (int) ($gq['lastAnalyzeAt'] ?? 0) : 0;
|
||||
$rid = $gq ? (int) ($gq['latestReportId'] ?? 0) : 0;
|
||||
$row['gaokaoOverview'] = $rid > 0 ? (string) ($gaokaoReportMap[$rid] ?? '') : '';
|
||||
}
|
||||
|
||||
return paginate_response($list, $total, $page, $pageSize);
|
||||
@@ -501,6 +557,28 @@ class AppUser extends BaseController
|
||||
? ResumeUploadsAdminService::listForWechatUser((int) $id, (int) $enterpriseId)
|
||||
: [];
|
||||
|
||||
// 高考结果信息
|
||||
try {
|
||||
$gq = Db::name('gaokao_user_profile')
|
||||
->where('userId', (int) $id)
|
||||
->where('tenantId', (int) $enterpriseId)
|
||||
->find();
|
||||
if ($gq) {
|
||||
$data['gaokaoProfile'] = $gq;
|
||||
$rid = (int) ($gq['latestReportId'] ?? 0);
|
||||
if ($rid > 0) {
|
||||
$data['gaokaoLatestReport'] = Db::name('test_results')
|
||||
->where('id', $rid)
|
||||
->where('testType', 'gaokao')
|
||||
->find();
|
||||
}
|
||||
} else {
|
||||
$data['gaokaoProfile'] = null;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$data['gaokaoProfile'] = null;
|
||||
}
|
||||
|
||||
return success($data);
|
||||
}
|
||||
|
||||
|
||||
@@ -753,7 +753,7 @@ class Distribution extends BaseController
|
||||
private static function defaultTestSettings(): array
|
||||
{
|
||||
$item = ['enabled' => true, 'commissionType' => 'ratio', 'commissionRate' => 90, 'commissionAmountFen' => 0, 'noPayment' => false];
|
||||
return ['face' => $item, 'mbti' => $item, 'sbti' => $item, 'disc' => $item, 'pdp' => $item];
|
||||
return ['face' => $item, 'mbti' => $item, 'sbti' => $item, 'disc' => $item, 'pdp' => $item, 'gaokao' => $item];
|
||||
}
|
||||
|
||||
private static function sanitizeTestSettings($raw): array
|
||||
@@ -832,12 +832,13 @@ class Distribution extends BaseController
|
||||
}
|
||||
|
||||
$totals = [
|
||||
'face' => 0,
|
||||
'mbti' => 0,
|
||||
'sbti' => 0,
|
||||
'disc' => 0,
|
||||
'pdp' => 0,
|
||||
'other' => 0,
|
||||
'face' => 0,
|
||||
'mbti' => 0,
|
||||
'sbti' => 0,
|
||||
'disc' => 0,
|
||||
'pdp' => 0,
|
||||
'gaokao' => 0,
|
||||
'other' => 0,
|
||||
];
|
||||
|
||||
foreach ($records as $record) {
|
||||
@@ -860,6 +861,7 @@ class Distribution extends BaseController
|
||||
['label' => 'SBTI', 'value' => round($totals['sbti'] / 100, 2)],
|
||||
['label' => 'DISC', 'value' => round($totals['disc'] / 100, 2)],
|
||||
['label' => 'PDP', 'value' => round($totals['pdp'] / 100, 2)],
|
||||
['label' => '高考志愿', 'value' => round($totals['gaokao'] / 100, 2)],
|
||||
['label' => '其他', 'value' => round($totals['other'] / 100, 2)],
|
||||
];
|
||||
}
|
||||
@@ -870,7 +872,7 @@ class Distribution extends BaseController
|
||||
if ($normalized === 'ai') {
|
||||
return 'face';
|
||||
}
|
||||
if (in_array($normalized, ['face', 'mbti', 'sbti', 'disc', 'pdp'], true)) {
|
||||
if (in_array($normalized, ['face', 'mbti', 'sbti', 'disc', 'pdp', 'gaokao'], true)) {
|
||||
return $normalized;
|
||||
}
|
||||
return 'other';
|
||||
@@ -879,12 +881,13 @@ class Distribution extends BaseController
|
||||
private static function getTestTypeLabel(string $testType): string
|
||||
{
|
||||
$map = [
|
||||
'face' => '人脸',
|
||||
'mbti' => 'MBTI',
|
||||
'sbti' => 'SBTI',
|
||||
'disc' => 'DISC',
|
||||
'pdp' => 'PDP',
|
||||
'other' => '其他',
|
||||
'face' => '人脸',
|
||||
'mbti' => 'MBTI',
|
||||
'sbti' => 'SBTI',
|
||||
'disc' => 'DISC',
|
||||
'pdp' => 'PDP',
|
||||
'gaokao' => '高考志愿',
|
||||
'other' => '其他',
|
||||
];
|
||||
|
||||
return $map[$testType] ?? strtoupper($testType ?: '其他');
|
||||
|
||||
96
api/app/controller/admin/GaokaoUser.php
Normal file
96
api/app/controller/admin/GaokaoUser.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
namespace app\controller\admin;
|
||||
|
||||
use app\BaseController;
|
||||
use think\facade\Db;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* 企业后台:高考用户管理
|
||||
*/
|
||||
class GaokaoUser extends BaseController
|
||||
{
|
||||
private function currentEnterpriseId(): int
|
||||
{
|
||||
$user = $this->request->user ?? null;
|
||||
if (!$user || !in_array(($user['role'] ?? ''), ['admin', 'enterprise_admin'], true)) {
|
||||
return 0;
|
||||
}
|
||||
$eid = (int) ($user['enterpriseId'] ?? 0);
|
||||
if ($eid > 0) {
|
||||
return $eid;
|
||||
}
|
||||
$adminRow = Db::name('users')->where('id', (int) ($user['userId'] ?? 0))->find();
|
||||
return (int) ($adminRow['enterpriseId'] ?? 0);
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$eid = $this->currentEnterpriseId();
|
||||
if ($eid <= 0) {
|
||||
return error('无权限访问', 403);
|
||||
}
|
||||
$page = max(1, (int) Request::param('page', 1));
|
||||
$pageSize = min(100, max(1, (int) Request::param('pageSize', 20)));
|
||||
$keyword = trim((string) Request::param('keyword', ''));
|
||||
$analyzeStatus = Request::param('analyzeStatus', '');
|
||||
|
||||
$q = Db::name('gaokao_user_profile')->alias('g')
|
||||
->join('wechat_users w', 'w.id = g.userId')
|
||||
->where('g.tenantId', $eid);
|
||||
if ($keyword !== '') {
|
||||
$q->whereRaw('(w.nickname LIKE ? OR w.phone LIKE ? OR g.name LIKE ?)', ['%' . $keyword . '%', '%' . $keyword . '%', '%' . $keyword . '%']);
|
||||
}
|
||||
if ($analyzeStatus !== '' && $analyzeStatus !== null) {
|
||||
$q->where('g.analyzeStatus', (int) $analyzeStatus);
|
||||
}
|
||||
|
||||
$total = (int) (clone $q)->count();
|
||||
$rows = (clone $q)->field('g.*,w.nickname,w.phone,w.avatar')
|
||||
->order('g.id', 'desc')
|
||||
->page($page, $pageSize)
|
||||
->select()
|
||||
->toArray();
|
||||
return paginate_response($rows, $total, $page, $pageSize);
|
||||
}
|
||||
|
||||
public function detail($id)
|
||||
{
|
||||
$eid = $this->currentEnterpriseId();
|
||||
if ($eid <= 0) {
|
||||
return error('无权限访问', 403);
|
||||
}
|
||||
$row = Db::name('gaokao_user_profile')->alias('g')
|
||||
->join('wechat_users w', 'w.id = g.userId')
|
||||
->where('g.id', (int) $id)
|
||||
->where('g.tenantId', $eid)
|
||||
->field('g.*,w.nickname,w.phone,w.avatar')
|
||||
->find();
|
||||
if (!$row) {
|
||||
return error('记录不存在', 404);
|
||||
}
|
||||
$report = null;
|
||||
if (!empty($row['latestReportId'])) {
|
||||
$tr = Db::name('test_results')
|
||||
->where('id', (int) $row['latestReportId'])
|
||||
->where('testType', 'gaokao')
|
||||
->find();
|
||||
if ($tr) {
|
||||
$report = $tr;
|
||||
}
|
||||
}
|
||||
$orders = Db::name('orders')
|
||||
->where('userId', (int) $row['userId'])
|
||||
->where('productType', 'gaokao')
|
||||
->order('id', 'desc')
|
||||
->limit(20)
|
||||
->select()
|
||||
->toArray();
|
||||
return success([
|
||||
'profile' => $row,
|
||||
'latestReport' => $report,
|
||||
'orders' => $orders,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class Pricing extends BaseController
|
||||
if (!is_array($personalConfig)) {
|
||||
return error('个人版定价格式错误', 400);
|
||||
}
|
||||
foreach (['face', 'mbti', 'disc', 'pdp', 'sbti'] as $field) {
|
||||
foreach (['face', 'mbti', 'disc', 'pdp', 'sbti', 'gaokao'] as $field) {
|
||||
if (!array_key_exists($field, $personalConfig)) {
|
||||
return error("个人版定价缺少字段:{$field}", 400);
|
||||
}
|
||||
@@ -131,7 +131,7 @@ class Pricing extends BaseController
|
||||
if (!is_array($enterpriseConfig)) {
|
||||
return error('企业版定价格式错误', 400);
|
||||
}
|
||||
foreach (['face', 'mbti', 'disc', 'pdp', 'sbti'] as $field) {
|
||||
foreach (['face', 'mbti', 'disc', 'pdp', 'sbti', 'gaokao'] as $field) {
|
||||
if (!array_key_exists($field, $enterpriseConfig)) {
|
||||
return error("企业版定价缺少字段:{$field}", 400);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user