feat: 双端题库统一与 PDP/DISC 结果文案、测评 API 与企测上下文
1、修复了测评/结果页与个人中心展示不一致问题;废弃独立 questions.js,统一使用 questionBank;结果格式与详情解析相关问题。 2、新增了 PdpDiscResultText(PDP+DISC 双类型摘要文案)、抖音 questionBank 与 enterpriseContext;扩展 api/Test 与路由、管理端 Order 与 ExtractsTestResults。 3、优化了 resultFormat 与 questionBank 组织方式、UserDetailDialog;移除不再使用的 mbti_test_results 恢复 SQL/脚本。 Made-with: Cursor
This commit is contained in:
250
api/app/common/PdpDiscResultText.php
Normal file
250
api/app/common/PdpDiscResultText.php
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
namespace app\common;
|
||||
|
||||
/**
|
||||
* PDP / DISC 结果摘要:权重最高的 2 项。
|
||||
* 展示格式:DISC 为「D+I型」(仅最后一项带「型」);PDP 为「孔雀+老虎型」。
|
||||
*/
|
||||
class PdpDiscResultText
|
||||
{
|
||||
private const PDP_EN_TO_CN = [
|
||||
'Tiger' => '老虎型',
|
||||
'Peacock' => '孔雀型',
|
||||
'Koala' => '考拉型',
|
||||
'Owl' => '猫头鹰型',
|
||||
'Chameleon' => '变色龙型',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $data
|
||||
*/
|
||||
public static function discTopTwo(array $data): string
|
||||
{
|
||||
[$fL, $sL] = self::discResolveTwoLetters($data);
|
||||
if ($fL !== '' || $sL !== '') {
|
||||
if ($fL === '') {
|
||||
return $sL !== '' ? ($sL . '型') : '';
|
||||
}
|
||||
if ($sL === '' || $sL === $fL) {
|
||||
return $fL . '型';
|
||||
}
|
||||
|
||||
return $fL . '+' . $sL . '型';
|
||||
}
|
||||
|
||||
return self::discNormalizeLegacyDualDescription($data['description']['type'] ?? null) ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $data
|
||||
*/
|
||||
public static function pdpTopTwo(array $data): string
|
||||
{
|
||||
[$firstFull, $secondFull] = self::pdpResolveTwoFull($data);
|
||||
if ($firstFull === '') {
|
||||
return $secondFull;
|
||||
}
|
||||
if ($secondFull === '' || $secondFull === $firstFull) {
|
||||
return $firstFull;
|
||||
}
|
||||
$firstShort = preg_replace('/型$/u', '', $firstFull);
|
||||
|
||||
return $firstShort . '+' . $secondFull;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0:string,1:string} DISC 字母 D/I/S/C
|
||||
*/
|
||||
private static function discResolveTwoLetters(array $data): array
|
||||
{
|
||||
$f = self::discPrimaryLetter($data);
|
||||
$s = '';
|
||||
$sk = $data['secondaryType'] ?? null;
|
||||
if (is_string($sk) && $sk !== '') {
|
||||
$u = strtoupper(substr(trim($sk), 0, 1));
|
||||
if (in_array($u, ['D', 'I', 'S', 'C'], true)) {
|
||||
$s = $u;
|
||||
}
|
||||
}
|
||||
$a = '';
|
||||
$b = '';
|
||||
if (isset($data['scores']) && is_array($data['scores'])) {
|
||||
[$a, $b] = self::discOrderedLetters($data['scores']);
|
||||
}
|
||||
if ($a === '' && $b === '' && isset($data['percentages']) && is_array($data['percentages'])) {
|
||||
[$a, $b] = self::discOrderedLetters($data['percentages']);
|
||||
}
|
||||
if ($f === '' && $a !== '') {
|
||||
$f = $a;
|
||||
}
|
||||
if ($s === '' || $s === $f) {
|
||||
if ($b !== '' && $b !== $f) {
|
||||
$s = $b;
|
||||
} else {
|
||||
$s = '';
|
||||
}
|
||||
}
|
||||
|
||||
return [$f, $s];
|
||||
}
|
||||
|
||||
/**
|
||||
* 旧数据:「S型 + I型」等 → 「S+I型」
|
||||
*/
|
||||
private static function discNormalizeLegacyDualDescription(?string $desc): ?string
|
||||
{
|
||||
if ($desc === null || trim($desc) === '') {
|
||||
return null;
|
||||
}
|
||||
$t = preg_replace('/\s+/u', '', trim($desc));
|
||||
$t = str_replace('+', '+', $t);
|
||||
if (preg_match('/^([DISC])型\+([DISC])型$/iu', $t, $m)) {
|
||||
return strtoupper($m[1]) . '+' . strtoupper($m[2]) . '型';
|
||||
}
|
||||
if (preg_match('/^([DISC])型$/iu', $t, $m)) {
|
||||
return strtoupper($m[1]) . '型';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0:string,1:string} 完整 PDP 类型文案(含「型」)
|
||||
*/
|
||||
private static function pdpResolveTwoFull(array $data): array
|
||||
{
|
||||
$f = self::pdpPrimaryFull($data);
|
||||
$s = '';
|
||||
$sk = $data['secondaryType'] ?? null;
|
||||
if (is_string($sk) && $sk !== '') {
|
||||
$s = self::PDP_EN_TO_CN[$sk] ?? $sk;
|
||||
}
|
||||
$aEn = '';
|
||||
$bEn = '';
|
||||
if (isset($data['scores']) && is_array($data['scores'])) {
|
||||
[$aEn, $bEn] = self::pdpOrderedKeys($data['scores']);
|
||||
}
|
||||
if ($aEn === '' && $bEn === '' && isset($data['percentages']) && is_array($data['percentages'])) {
|
||||
[$aEn, $bEn] = self::pdpOrderedKeys($data['percentages']);
|
||||
}
|
||||
if ($f === '' && $aEn !== '') {
|
||||
$f = self::PDP_EN_TO_CN[$aEn] ?? $aEn;
|
||||
}
|
||||
if ($s === '' || $s === $f) {
|
||||
if ($bEn !== '') {
|
||||
$cand = self::PDP_EN_TO_CN[$bEn] ?? $bEn;
|
||||
if ($cand !== $f) {
|
||||
$s = $cand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$f, $s];
|
||||
}
|
||||
|
||||
private static function discPrimaryLetter(array $data): string
|
||||
{
|
||||
$desc = $data['description']['type'] ?? null;
|
||||
if (is_string($desc) && $desc !== '') {
|
||||
$t = trim($desc);
|
||||
$noXing = preg_replace('/型$/u', '', $t);
|
||||
if (mb_strlen($noXing) === 1) {
|
||||
$u = strtoupper($noXing);
|
||||
if (in_array($u, ['D', 'I', 'S', 'C'], true)) {
|
||||
return $u;
|
||||
}
|
||||
}
|
||||
}
|
||||
$dom = $data['dominantType'] ?? null;
|
||||
if (is_string($dom) && $dom !== '') {
|
||||
$u = strtoupper(substr(trim($dom), 0, 1));
|
||||
if (in_array($u, ['D', 'I', 'S', 'C'], true)) {
|
||||
return $u;
|
||||
}
|
||||
}
|
||||
$disc = $data['disc'] ?? null;
|
||||
if (is_string($disc) && $disc !== '') {
|
||||
$t = trim($disc);
|
||||
$noXing = preg_replace('/型$/u', '', $t);
|
||||
if (mb_strlen($noXing) === 1) {
|
||||
$u = strtoupper($noXing);
|
||||
if (in_array($u, ['D', 'I', 'S', 'C'], true)) {
|
||||
return $u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private static function pdpPrimaryFull(array $data): string
|
||||
{
|
||||
$desc = $data['description']['type'] ?? null;
|
||||
if (is_string($desc) && $desc !== '') {
|
||||
return trim($desc);
|
||||
}
|
||||
$dom = $data['dominantType'] ?? null;
|
||||
if (is_string($dom) && $dom !== '') {
|
||||
return self::PDP_EN_TO_CN[$dom] ?? trim($dom);
|
||||
}
|
||||
$pdp = $data['pdp'] ?? null;
|
||||
if (is_string($pdp) && $pdp !== '') {
|
||||
return trim($pdp);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,int|float> $scores
|
||||
* @return array{0:string,1:string}
|
||||
*/
|
||||
private static function discOrderedLetters(array $scores): array
|
||||
{
|
||||
$allowed = ['D' => true, 'I' => true, 'S' => true, 'C' => true];
|
||||
$pairs = [];
|
||||
foreach ($scores as $k => $v) {
|
||||
if (!is_string($k) && !is_numeric($k)) {
|
||||
continue;
|
||||
}
|
||||
$ku = strtoupper(substr(trim((string) $k), 0, 1));
|
||||
if (!isset($allowed[$ku])) {
|
||||
continue;
|
||||
}
|
||||
$pairs[] = [$ku, (int) $v];
|
||||
}
|
||||
usort($pairs, static function ($a, $b) {
|
||||
return ($b[1] <=> $a[1]) ?: strcmp($a[0], $b[0]);
|
||||
});
|
||||
$f = $pairs[0][0] ?? '';
|
||||
$s = $pairs[1][0] ?? '';
|
||||
|
||||
return [$f, $s];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,int|float> $scores
|
||||
* @return array{0:string,1:string}
|
||||
*/
|
||||
private static function pdpOrderedKeys(array $scores): array
|
||||
{
|
||||
$pairs = [];
|
||||
foreach ($scores as $k => $v) {
|
||||
if (!is_string($k)) {
|
||||
continue;
|
||||
}
|
||||
$key = trim($k);
|
||||
if ($key === '' || !isset(self::PDP_EN_TO_CN[$key])) {
|
||||
continue;
|
||||
}
|
||||
$pairs[] = [$key, (int) $v];
|
||||
}
|
||||
usort($pairs, static function ($a, $b) {
|
||||
return ($b[1] <=> $a[1]) ?: strcmp($a[0], $b[0]);
|
||||
});
|
||||
$f = $pairs[0][0] ?? '';
|
||||
$s = $pairs[1][0] ?? '';
|
||||
|
||||
return [$f, $s];
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace app\controller\admin;
|
||||
|
||||
use app\BaseController;
|
||||
use app\common\PdpDiscResultText;
|
||||
use think\facade\Db;
|
||||
use think\facade\Request;
|
||||
|
||||
@@ -156,6 +157,10 @@ class Order extends BaseController
|
||||
return (string) ($data['mbtiType'] ?? $data['type'] ?? $data['result'] ?? '');
|
||||
}
|
||||
if ($type === 'disc') {
|
||||
$two = PdpDiscResultText::discTopTwo($data);
|
||||
if ($two !== '') {
|
||||
return $two;
|
||||
}
|
||||
$desc = $data['description']['type'] ?? null;
|
||||
if (is_string($desc) && $desc !== '') {
|
||||
return $desc;
|
||||
@@ -166,6 +171,10 @@ class Order extends BaseController
|
||||
return (string) ($data['disc'] ?? '');
|
||||
}
|
||||
if ($type === 'pdp') {
|
||||
$two = PdpDiscResultText::pdpTopTwo($data);
|
||||
if ($two !== '') {
|
||||
return $two;
|
||||
}
|
||||
$desc = $data['description']['type'] ?? null;
|
||||
if (is_string($desc) && $desc !== '') {
|
||||
return $desc;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace app\controller\admin\concern;
|
||||
|
||||
use app\common\PdpDiscResultText;
|
||||
|
||||
/**
|
||||
* 从测试记录数组中解析 MBTI / DISC / PDP / 人脸子类型(与 AppUser 逻辑一致)
|
||||
*/
|
||||
@@ -69,6 +71,10 @@ trait ExtractsTestResults
|
||||
}
|
||||
|
||||
if ($targetType === 'disc') {
|
||||
$two = PdpDiscResultText::discTopTwo($dec);
|
||||
if ($two !== '') {
|
||||
return $two;
|
||||
}
|
||||
$desc = $dec['description']['type'] ?? null;
|
||||
if (is_string($desc) && $desc !== '') {
|
||||
return $desc;
|
||||
@@ -81,6 +87,10 @@ trait ExtractsTestResults
|
||||
}
|
||||
|
||||
if ($targetType === 'pdp') {
|
||||
$two = PdpDiscResultText::pdpTopTwo($dec);
|
||||
if ($two !== '') {
|
||||
return $two;
|
||||
}
|
||||
$desc = $dec['description']['type'] ?? null;
|
||||
if (is_string($desc) && $desc !== '') {
|
||||
return $desc;
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\BaseController;
|
||||
use app\common\PdpDiscResultText;
|
||||
use app\model\Enterprise as EnterpriseModel;
|
||||
use app\model\PricingConfig as PricingConfigModel;
|
||||
use app\model\Question as QuestionModel;
|
||||
use app\model\UserProfile as UserProfileModel;
|
||||
use think\facade\Db;
|
||||
use think\facade\Request;
|
||||
@@ -144,20 +146,33 @@ class Test extends BaseController
|
||||
], $paymentFields);
|
||||
break;
|
||||
case 'disc':
|
||||
$discType = is_array($data) ? ($data['dominantType'] ?? $data['disc'] ?? '未知') : '未知';
|
||||
$discTxt = '未知';
|
||||
if (is_array($data)) {
|
||||
$discTxt = PdpDiscResultText::discTopTwo($data);
|
||||
if ($discTxt === '') {
|
||||
$fallback = $data['dominantType'] ?? $data['disc'] ?? '未知';
|
||||
$discTxt = (is_string($fallback) || is_numeric($fallback) ? (string) $fallback : '未知') . '型';
|
||||
}
|
||||
}
|
||||
$list[] = array_merge([
|
||||
'id' => $id,
|
||||
'type' => 'disc',
|
||||
'key' => 'disc_' . $id,
|
||||
'emoji' => '📊',
|
||||
'typeName' => 'DISC性格测试',
|
||||
'resultText'=> (is_string($discType) || is_numeric($discType) ? (string) $discType : '未知') . '型',
|
||||
'resultText'=> $discTxt,
|
||||
'testTime' => $timeLabel,
|
||||
'data' => null,
|
||||
], $paymentFields);
|
||||
break;
|
||||
case 'pdp':
|
||||
$primary = is_array($data) ? ($data['description']['type'] ?? $data['pdp'] ?? '未知') : '未知';
|
||||
$primary = '未知';
|
||||
if (is_array($data)) {
|
||||
$primary = PdpDiscResultText::pdpTopTwo($data);
|
||||
if ($primary === '') {
|
||||
$primary = (string) ($data['description']['type'] ?? $data['pdp'] ?? '未知');
|
||||
}
|
||||
}
|
||||
$emoji = (is_array($data) && isset($data['description']['emoji'])) ? $data['description']['emoji'] : '🦁';
|
||||
$list[] = array_merge([
|
||||
'id' => $id,
|
||||
@@ -337,6 +352,36 @@ class Test extends BaseController
|
||||
return array_values(array_unique($allowed));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 test_results.resultData:支持 JSON 字符串、已解码的 array、包在 result 键里的结构
|
||||
*
|
||||
* @param mixed $raw
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
protected function decodeResultDataPayload($raw): array
|
||||
{
|
||||
if ($raw === null || $raw === '') {
|
||||
return [];
|
||||
}
|
||||
if (is_array($raw)) {
|
||||
$data = $raw;
|
||||
} elseif (is_string($raw)) {
|
||||
$decoded = json_decode(trim($raw), true);
|
||||
$data = is_array($decoded) ? $decoded : [];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
if (isset($data['result']) && is_array($data['result'])) {
|
||||
$inner = $data['result'];
|
||||
if (isset($inner['percentages']) || isset($inner['scores']) || isset($inner['dominantType'])
|
||||
|| isset($inner['description']) || isset($inner['mbtiType'])) {
|
||||
$data = array_merge($data, $inner);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化单条记录为 recent 接口返回结构
|
||||
*/
|
||||
@@ -345,11 +390,7 @@ class Test extends BaseController
|
||||
$testType = $row['testType'] ?? '';
|
||||
$createdAt = $row['createdAt'] ?? null;
|
||||
$raw = $row['resultData'] ?? ($row['result'] ?? null);
|
||||
$data = [];
|
||||
if ($raw !== null && $raw !== '') {
|
||||
$decoded = json_decode($raw, true);
|
||||
$data = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
$data = $this->decodeResultDataPayload($raw);
|
||||
|
||||
$resultText = '';
|
||||
$emoji = '';
|
||||
@@ -363,13 +404,19 @@ class Test extends BaseController
|
||||
$typeName = 'MBTI性格';
|
||||
break;
|
||||
case 'disc':
|
||||
$dominantType = $data['dominantType'] ?? $data['disc'] ?? '未知';
|
||||
$resultText = $dominantType . '型';
|
||||
$emoji = '📊';
|
||||
$typeName = 'DISC测评';
|
||||
$resultText = PdpDiscResultText::discTopTwo($data);
|
||||
if ($resultText === '') {
|
||||
$dominantType = $data['dominantType'] ?? $data['disc'] ?? '未知';
|
||||
$resultText = (is_string($dominantType) || is_numeric($dominantType) ? (string) $dominantType : '未知') . '型';
|
||||
}
|
||||
$emoji = '📊';
|
||||
$typeName = 'DISC测评';
|
||||
break;
|
||||
case 'pdp':
|
||||
$resultText = $data['description']['type'] ?? $data['pdp'] ?? '未知';
|
||||
$resultText = PdpDiscResultText::pdpTopTwo($data);
|
||||
if ($resultText === '') {
|
||||
$resultText = $data['description']['type'] ?? $data['pdp'] ?? '未知';
|
||||
}
|
||||
$emoji = $data['description']['emoji'] ?? '🦁';
|
||||
$typeName = 'PDP行为';
|
||||
break;
|
||||
@@ -397,6 +444,28 @@ class Test extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
// 小程序「最新测试」:始终附带 resultMeta(便于前端 getTypeOnly;避免仅 resultText 旧格式)
|
||||
$resultMeta = null;
|
||||
if ($testType === 'disc') {
|
||||
$resultMeta = [
|
||||
'scores' => $data['scores'] ?? null,
|
||||
'percentages' => $data['percentages'] ?? null,
|
||||
'dominantType' => $data['dominantType'] ?? null,
|
||||
'secondaryType' => $data['secondaryType'] ?? null,
|
||||
'description' => $data['description'] ?? null,
|
||||
'disc' => $data['disc'] ?? null,
|
||||
];
|
||||
} elseif ($testType === 'pdp') {
|
||||
$resultMeta = [
|
||||
'scores' => $data['scores'] ?? null,
|
||||
'percentages' => $data['percentages'] ?? null,
|
||||
'dominantType' => $data['dominantType'] ?? null,
|
||||
'secondaryType' => $data['secondaryType'] ?? null,
|
||||
'description' => $data['description'] ?? null,
|
||||
'pdp' => $data['pdp'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
$out = [
|
||||
'id' => (int) $row['id'],
|
||||
'testType' => ($testType === 'face') ? 'ai' : $testType,
|
||||
@@ -410,6 +479,9 @@ class Test extends BaseController
|
||||
if ($gallupPreview !== '') {
|
||||
$out['gallupPreview'] = $gallupPreview;
|
||||
}
|
||||
if ($resultMeta !== null) {
|
||||
$out['resultMeta'] = $resultMeta;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
@@ -802,5 +874,90 @@ class Test extends BaseController
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序拉取做题题库(仅启用题):企业本题库有题则用企业,否则用超管 enterpriseId 为空
|
||||
* GET /api/test/questions?type=mbti|disc|pdp&enterpriseId=可选
|
||||
*/
|
||||
public function questions()
|
||||
{
|
||||
$user = $this->request->user ?? null;
|
||||
if (!$user || ($user['source'] ?? '') !== 'wechat') {
|
||||
return error('未登录', 401);
|
||||
}
|
||||
|
||||
$type = (string) Request::param('type', '');
|
||||
if (!in_array($type, ['mbti', 'disc', 'pdp'], true)) {
|
||||
return error('type 须为 mbti、disc 或 pdp', 400);
|
||||
}
|
||||
|
||||
$rawEid = Request::param('enterpriseId', null);
|
||||
$enterpriseId = null;
|
||||
if ($rawEid !== null && $rawEid !== '') {
|
||||
$enterpriseId = (int) $rawEid;
|
||||
if ($enterpriseId <= 0) {
|
||||
$enterpriseId = null;
|
||||
}
|
||||
}
|
||||
|
||||
$resolvedEnterpriseId = null;
|
||||
if ($enterpriseId !== null) {
|
||||
$enterpriseQuestionCount = QuestionModel::where('enterpriseId', $enterpriseId)
|
||||
->where('type', $type)
|
||||
->where('status', 1)
|
||||
->count();
|
||||
if ($enterpriseQuestionCount > 0) {
|
||||
$resolvedEnterpriseId = $enterpriseId;
|
||||
}
|
||||
}
|
||||
|
||||
$query = QuestionModel::where('type', $type)->where('status', 1);
|
||||
if ($resolvedEnterpriseId !== null) {
|
||||
$query->where('enterpriseId', $resolvedEnterpriseId);
|
||||
} else {
|
||||
$query->whereNull('enterpriseId');
|
||||
}
|
||||
|
||||
$list = $query->order('sort', 'asc')
|
||||
->order('id', 'asc')
|
||||
->field('id,question,options,dimension')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
foreach ($list as &$item) {
|
||||
if (isset($item['options']) && is_object($item['options'])) {
|
||||
$item['options'] = json_decode(json_encode($item['options']), true);
|
||||
}
|
||||
if (isset($item['options']) && is_array($item['options']) && !empty($item['options']) && !isset($item['options'][0])) {
|
||||
$item['options'] = array_values($item['options']);
|
||||
}
|
||||
if (!isset($item['options']) || !is_array($item['options'])) {
|
||||
$item['options'] = [];
|
||||
}
|
||||
$item['id'] = (int) ($item['id'] ?? 0);
|
||||
foreach ($item['options'] as &$opt) {
|
||||
if (!is_array($opt)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($opt['value']) && isset($opt['label'])) {
|
||||
$opt['value'] = $opt['label'];
|
||||
}
|
||||
if (!isset($opt['text']) && isset($opt['label'])) {
|
||||
$opt['text'] = $opt['label'];
|
||||
}
|
||||
}
|
||||
unset($opt);
|
||||
if ($type !== 'mbti') {
|
||||
unset($item['dimension']);
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return success([
|
||||
'list' => $list,
|
||||
'resolvedEnterpriseId' => $resolvedEnterpriseId,
|
||||
'usingSuperAdminBank' => $resolvedEnterpriseId === null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Generate restore_mbti_test_results_1_145.sql from api/mbti_data.sql."""
|
||||
import pathlib
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[2]
|
||||
src = ROOT / "mbti_data.sql"
|
||||
out = pathlib.Path(__file__).resolve().parent / "restore_mbti_test_results_1_145.sql"
|
||||
|
||||
lines = src.read_text(encoding="utf-8").splitlines()
|
||||
starts = None
|
||||
ends = None
|
||||
for i, L in enumerate(lines):
|
||||
if starts is None and L.startswith("INSERT INTO `mbti_test_results`") and "VALUES (1," in L:
|
||||
starts = i
|
||||
if L.startswith("INSERT INTO `mbti_test_results`") and "VALUES (145," in L:
|
||||
ends = i + 1
|
||||
break
|
||||
|
||||
if starts is None or ends is None:
|
||||
raise SystemExit(f"range not found: starts={starts} ends={ends}")
|
||||
|
||||
chunk = lines[starts:ends]
|
||||
head = """-- 恢复 mbti_test_results 表 id 1~145(与 api/mbti_data.sql 中导出一致)
|
||||
-- 用法:mysql -u... -p... 数据库名 < restore_mbti_test_results_1_145.sql
|
||||
-- REPLACE 会按主键删除旧行再插入,执行前请备份。
|
||||
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
START TRANSACTION;
|
||||
|
||||
"""
|
||||
body = "\n".join(L.replace("INSERT INTO", "REPLACE INTO", 1) for L in chunk)
|
||||
tail = """
|
||||
|
||||
COMMIT;
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
"""
|
||||
out.write_text(head + body + tail, encoding="utf-8")
|
||||
print("wrote", out, "statements", len(chunk))
|
||||
File diff suppressed because one or more lines are too long
@@ -49,6 +49,8 @@ Route::group('api', function () {
|
||||
Route::get('test/recent', 'api.Test/recent');
|
||||
// 单条测试详情
|
||||
Route::get('test/detail', 'api.Test/detail');
|
||||
// 启用题库(企业有则用企业,否则超管)
|
||||
Route::get('test/questions', 'api.Test/questions');
|
||||
// 提交测试结果(MBTI/DISC/PDP 等)
|
||||
Route::post('test/submit', 'api.Test/submit');
|
||||
// 简历综合分析(基于人脸/MBTI/PDP/DISC 最近一次结果)
|
||||
|
||||
Reference in New Issue
Block a user