diff --git a/admin/src/components/UserDetailDialog.vue b/admin/src/components/UserDetailDialog.vue
index 688f78d..78bea9d 100644
--- a/admin/src/components/UserDetailDialog.vue
+++ b/admin/src/components/UserDetailDialog.vue
@@ -14,12 +14,7 @@
@@ -198,8 +189,7 @@ const faceSubtypeHints = ref<{
const loading = ref(false)
const inviteLoading = ref(false)
-const inviteQrcodeEnterprise = ref('')
-const inviteQrcodePersonal = ref('')
+const inviteQrcode = ref('')
/** 侧栏表格最大高度:单屏内滚动,不撑开整页 */
const tableMaxH = 220
@@ -398,13 +388,9 @@ const loadInviteQrcode = async () => {
inviteLoading.value = true
try {
const res: any = await request.get('/admin/invite/qrcode')
- const d = res?.data
- const ent = d?.enterprise?.qrcode ?? d?.qrcode
- const per = d?.personal?.qrcode
- if (typeof ent === 'string' && ent) inviteQrcodeEnterprise.value = ent
- if (typeof per === 'string' && per) inviteQrcodePersonal.value = per
- if (inviteQrcodeEnterprise.value || inviteQrcodePersonal.value) {
- ElMessage.success('已生成企业版与个人版小程序码')
+ const qrcode = res?.data?.qrcode
+ if (qrcode && typeof qrcode === 'string') {
+ inviteQrcode.value = qrcode
} else {
ElMessage.error(res?.message || res?.msg || '生成失败,请确认企业绑定')
}
@@ -821,24 +807,9 @@ const loadInviteQrcode = async () => {
.invite-body {
display: flex;
- flex-wrap: wrap;
- align-items: flex-start;
- justify-content: center;
- gap: 16px;
- min-height: 112px;
-}
-
-.invite-card {
- display: flex;
- flex-direction: column;
align-items: center;
- gap: 6px;
-}
-
-.invite-label {
- font-size: 12px;
- font-weight: 600;
- color: #374151;
+ justify-content: center;
+ min-height: 112px;
}
.invite-img {
diff --git a/admin/src/views/admin/Users.vue b/admin/src/views/admin/Users.vue
index 9a10f59..c5505ee 100644
--- a/admin/src/views/admin/Users.vue
+++ b/admin/src/views/admin/Users.vue
@@ -1,1649 +1,1626 @@
-
-
-
-
-
-
-
-
-
-
-
- 搜索
-
-
-
-
-
-
-
-
-
- {{ avatarLetter(row) }}
-
-
-
{{ row.username || '未设置昵称' }}
-
{{ row.openid || '—' }}
-
{{ row.phone}}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ row.faceMbtiType }}
-
-
- {{ row.faceDiscType }}
-
-
- {{ row.facePdpType }}
-
-
- —
-
-
-
-
-
-
-
-
-
- {{ row.mbtiType }}
-
-
- —
-
-
-
-
-
-
-
-
-
- {{ row.pdpType }}
-
-
- —
-
-
-
-
-
-
-
-
-
- {{ row.discType }}
-
-
- —
-
-
-
-
-
-
- {{ formatDate(row.createdAt) }}
-
-
-
-
-
-
-
- 查看
-
-
-
-
-
-
- 暂无用户数据
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ mbtiDetail.type }}
-
{{ mbtiDetail.name }}
-
{{ mbtiDetail.description }}
-
-
-
-
-
-
-
-
-
-
{{ discDetail.type }}
-
{{ discDetail.title }}
-
{{ discDetail.description }}
-
-
-
四维占比
-
-
-
{{ key }}
-
-
{{ discDetail.percentages[key] || 0 }}%
-
-
-
-
-
-
-
-
-
-
-
-
{{ pdpDetail.emoji }}
-
- {{ pdpDetail.type }}
- · {{ pdpDetail.title }}
-
-
{{ pdpDetail.description }}
-
-
-
-
-
团队角色定位
-
{{ pdpDetail.teamRole }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
AI 识别 MBTI
-
- {{ faceDetail.mbti.type }}
- · {{ faceDetail.mbti.title }}
-
-
-
-
AI 识别 DISC
-
- 主类型:{{ faceDetail.disc.primary }}
- ,次:{{ faceDetail.disc.secondary }}
-
-
-
-
AI 识别 PDP
-
- 主类型:{{ faceDetail.pdp.primary }}
- ,次:{{ faceDetail.pdp.secondary }}
-
-
-
-
-
-
-
-
-
盖洛普前三大优势
-
- - {{ g }}
-
-
-
-
-
-
-
-
整体气质概览
-
{{ faceDetail.overview }}
-
-
-
性格总结
-
{{ faceDetail.personalitySummary }}
-
-
-
-
-
-
-
面部特征分析
-
- -
- {{ f.label }}:
- {{ f.description }}
-
-
-
-
-
面相分析
-
{{ faceDetail.faceAnalysisText }}
-
-
-
-
骨相分析(《冰鉴》)
-
- 五行形相:{{ faceDetail.boneIceSummary.elementType }}
-
-
- 骨肉关系:{{ faceDetail.boneIceSummary.boneFleshRelation }}
-
-
-
-
骨相特征
-
{{ faceDetail.boneAnalysisText }}
-
-
-
-
-
-
-
人际关系与团队合作
-
{{ faceDetail.relationship }}
-
-
-
-
-
-
-
-
职业发展方向
-
{{ faceDetail.careerDevelopment }}
-
-
-
家庭亲子关系
-
{{ faceDetail.familyParenting }}
-
-
-
寻找合伙人
-
{{ faceDetail.partnerCofounder }}
-
-
-
-
-
-
-
-
-
-
深度·工作风格
-
{{ faceDetail.portrait.workStyle }}
-
-
-
-
简历要点
-
{{ faceDetail.resumeHighlights }}
-
-
-
-
老板视角·一句话
-
{{ faceDetail.bossView.headline }}
-
-
-
老板视角·成本洞察
-
{{ faceDetail.bossView.costInsight }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ resumeDetail.bossView.headline }}
-
-
-
{{ m.value }}
-
{{ m.label }}
-
-
-
- {{ resumeDetail.bossView.costInsight }}
-
-
-
-
-
-
-
-
-
-
工作风格
-
{{ resumeDetail.portrait.workStyle }}
-
-
-
-
-
-
-
-
-
员工全生命周期预测
-
-
- 入职
- {{ resumeDetail.hrView.lifecycle.onboarding }}
-
-
- 试用期
- {{ resumeDetail.hrView.lifecycle.probation }}
-
-
- 成长期
- {{ resumeDetail.hrView.lifecycle.growth }}
-
-
- 留存
- {{ resumeDetail.hrView.lifecycle.retention }}
-
-
-
-
-
-
绩效潜力
-
{{ resumeDetail.hrView.performance.potential }}
-
-
-
-
-
团队匹配 & 管理建议
-
- 最佳团队:{{ resumeDetail.hrView.teamFit.bestTeam }}
-
-
- 管理建议:{{ resumeDetail.hrView.teamFit.manageAdvice }}
-
-
-
-
合规风险
-
- 风险等级:{{ resumeDetail.hrView.complianceRisk.level }}
-
-
- {{ resumeDetail.hrView.complianceRisk.notes }}
-
-
-
-
-
-
-
-
简历要点
-
{{ resumeDetail.resumeHighlights }}
-
-
-
-
-
-
分析报告
-
{{ currentTestDescription }}
-
-
-
-
补充说明
-
{{ currentTestDescription }}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+
+
+
+
+
+
+
+
{{ (row.username || '?')[0] }}
+
+
{{ row.username || '未设置昵称' }}
+
{{ row.openid || '—' }}
+
{{ row.phone}}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.faceMbtiType }}
+
+
+ {{ row.faceDiscType }}
+
+
+ {{ row.facePdpType }}
+
+
+ —
+
+
+
+
+
+
+
+
+
+ {{ row.mbtiType }}
+
+
+ —
+
+
+
+
+
+
+
+
+
+ {{ row.pdpType }}
+
+
+ —
+
+
+
+
+
+
+
+
+
+ {{ row.discType }}
+
+
+ —
+
+
+
+
+
+
+ {{ formatDate(row.createdAt) }}
+
+
+
+
+
+
+
+ 查看
+
+
+
+
+
+
+ 暂无用户数据
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ mbtiDetail.type }}
+
{{ mbtiDetail.name }}
+
{{ mbtiDetail.description }}
+
+
+
+
+
+
+
+
+
+
{{ discDetail.type }}
+
{{ discDetail.title }}
+
{{ discDetail.description }}
+
+
+
四维占比
+
+
+
{{ key }}
+
+
{{ discDetail.percentages[key] || 0 }}%
+
+
+
+
+
+
+
+
+
+
+
+
{{ pdpDetail.emoji }}
+
+ {{ pdpDetail.type }}
+ · {{ pdpDetail.title }}
+
+
{{ pdpDetail.description }}
+
+
+
+
+
团队角色定位
+
{{ pdpDetail.teamRole }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
AI 识别 MBTI
+
+ {{ faceDetail.mbti.type }}
+ · {{ faceDetail.mbti.title }}
+
+
+
+
AI 识别 DISC
+
+ 主类型:{{ faceDetail.disc.primary }}
+ ,次:{{ faceDetail.disc.secondary }}
+
+
+
+
AI 识别 PDP
+
+ 主类型:{{ faceDetail.pdp.primary }}
+ ,次:{{ faceDetail.pdp.secondary }}
+
+
+
+
+
+
+
+
+
盖洛普前三大优势
+
+ - {{ g }}
+
+
+
+
+
+
+
+
整体气质概览
+
{{ faceDetail.overview }}
+
+
+
性格总结
+
{{ faceDetail.personalitySummary }}
+
+
+
+
+
+
+
面部特征分析
+
+ -
+ {{ f.label }}:
+ {{ f.description }}
+
+
+
+
+
面相分析
+
{{ faceDetail.faceAnalysisText }}
+
+
+
+
骨相分析(《冰鉴》)
+
+ 五行形相:{{ faceDetail.boneIceSummary.elementType }}
+
+
+ 骨肉关系:{{ faceDetail.boneIceSummary.boneFleshRelation }}
+
+
+
+
骨相特征
+
{{ faceDetail.boneAnalysisText }}
+
+
+
+
+
+
+
人际关系与团队合作
+
{{ faceDetail.relationship }}
+
+
+
+
+
+
+
+
职业发展方向
+
{{ faceDetail.careerDevelopment }}
+
+
+
家庭亲子关系
+
{{ faceDetail.familyParenting }}
+
+
+
寻找合伙人
+
{{ faceDetail.partnerCofounder }}
+
+
+
+
+
+
+
+
+
+
深度·工作风格
+
{{ faceDetail.portrait.workStyle }}
+
+
+
+
简历要点
+
{{ faceDetail.resumeHighlights }}
+
+
+
+
老板视角·一句话
+
{{ faceDetail.bossView.headline }}
+
+
+
老板视角·成本洞察
+
{{ faceDetail.bossView.costInsight }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ resumeDetail.bossView.headline }}
+
+
+
{{ m.value }}
+
{{ m.label }}
+
+
+
+ {{ resumeDetail.bossView.costInsight }}
+
+
+
+
+
+
+
+
+
+
工作风格
+
{{ resumeDetail.portrait.workStyle }}
+
+
+
+
+
+
+
+
+
员工全生命周期预测
+
+
+ 入职
+ {{ resumeDetail.hrView.lifecycle.onboarding }}
+
+
+ 试用期
+ {{ resumeDetail.hrView.lifecycle.probation }}
+
+
+ 成长期
+ {{ resumeDetail.hrView.lifecycle.growth }}
+
+
+ 留存
+ {{ resumeDetail.hrView.lifecycle.retention }}
+
+
+
+
+
+
绩效潜力
+
{{ resumeDetail.hrView.performance.potential }}
+
+
+
+
+
团队匹配 & 管理建议
+
+ 最佳团队:{{ resumeDetail.hrView.teamFit.bestTeam }}
+
+
+ 管理建议:{{ resumeDetail.hrView.teamFit.manageAdvice }}
+
+
+
+
合规风险
+
+ 风险等级:{{ resumeDetail.hrView.complianceRisk.level }}
+
+
+ {{ resumeDetail.hrView.complianceRisk.notes }}
+
+
+
+
+
+
+
+
简历要点
+
{{ resumeDetail.resumeHighlights }}
+
+
+
+
+
+
分析报告
+
{{ currentTestDescription }}
+
+
+
+
补充说明
+
{{ currentTestDescription }}
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/views/superadmin/Settings.vue b/admin/src/views/superadmin/Settings.vue
index fe0a684..a002fe2 100644
--- a/admin/src/views/superadmin/Settings.vue
+++ b/admin/src/views/superadmin/Settings.vue
@@ -53,12 +53,12 @@
-
+
-
- {{ reviewMode.enabled ? '审核模式已开启' : '审核模式已关闭' }}
+
+ {{ systemConfig.maintenanceMode ? '审核模式已开启' : '审核模式已关闭' }}
-
{{ reviewMode.enabled ? '当前小程序处于审核状态,AI面相分析功能已隐藏' : '当前小程序正常运行,所有功能可用' }}
+
{{ systemConfig.maintenanceMode ? '当前小程序处于审核状态,AI面相分析功能已隐藏' : '当前小程序正常运行,所有功能可用' }}
@@ -68,7 +68,7 @@
开启后小程序将执行以下变更:
@@ -116,12 +116,12 @@
- {{ reviewMode.enabled ? '保存并开启审核模式' : '保存并关闭审核模式' }}
+ {{ systemConfig.maintenanceMode ? '保存并开启审核模式' : '保存并关闭审核模式' }}
@@ -560,11 +560,6 @@ watch(
() => applyRouteTab()
)
-// 审核模式
-const reviewMode = reactive({
- enabled: false
-})
-
// 系统配置
const systemConfig = reactive({
siteName: '神仙团队AI性格测试',
@@ -616,13 +611,10 @@ const loadSettings = async () => {
const response: any = await request.get('/superadmin/settings')
if (response.code === 200 && response.data) {
- // 加载审核模式
- if (response.data.reviewMode) {
- Object.assign(reviewMode, response.data.reviewMode)
- }
- // 加载系统配置
+ // 系统配置(含 maintenanceMode:审核模式唯一存储字段)
if (response.data.system) {
Object.assign(systemConfig, response.data.system)
+ systemConfig.maintenanceMode = !!systemConfig.maintenanceMode
const de = response.data.system.defaultEnterpriseId
systemConfig.defaultEnterpriseId =
de != null && de !== '' && Number(de) > 0 ? Number(de) : null
@@ -687,11 +679,11 @@ const handleSave = async (section: string) => {
switch (section) {
case 'review':
- response = await request.put('/superadmin/settings/review-mode', {
- enabled: reviewMode.enabled
+ response = await request.put('/superadmin/settings/system', {
+ maintenanceMode: !!systemConfig.maintenanceMode
})
if (response.code === 200) {
- ElMessage.success(reviewMode.enabled ? '审核模式已开启,小程序将隐藏AI功能' : '审核模式已关闭,AI功能已恢复')
+ ElMessage.success(systemConfig.maintenanceMode ? '审核模式已开启,小程序将隐藏AI功能' : '审核模式已关闭,AI功能已恢复')
saveSuccess.value = section
setTimeout(() => { saveSuccess.value = null }, 3000)
}
diff --git a/admin/src/views/superadmin/Users.vue b/admin/src/views/superadmin/Users.vue
index c3b3088..8dffb2b 100644
--- a/admin/src/views/superadmin/Users.vue
+++ b/admin/src/views/superadmin/Users.vue
@@ -102,16 +102,19 @@
-
+
- {{ avatarLetter(row) }}
-
+ {{ avatarLetter(row) }}
+
{{ row.username || '未设置昵称' }}
{{ row.openid || '—' }}
@@ -838,12 +841,6 @@ function formatPhone(phone: string) {
return phone
}
-/** 列表头像 URL(兼容 avatar / avatarUrl,避免字段名或空白异常导致整列不显示) */
-function displayAvatar(row: Record
) {
- const a = row?.avatar ?? row?.avatarUrl ?? ''
- return typeof a === 'string' ? a.trim() : ''
-}
-
/** 文字头像:根据昵称取首字 */
function avatarLetter(row: { username?: string; nickname?: string }) {
const name = (row?.username || row?.nickname || '?').trim()
@@ -1494,16 +1491,6 @@ onMounted(() => {
color: #fff;
}
- .user-el-avatar {
- flex-shrink: 0;
- font-size: 16px;
- font-weight: 600;
- }
-
- .user-avatar-letter-fallback {
- color: #fff;
- }
-
.user-details {
.username {
font-weight: 600;
diff --git a/api/app/controller/admin/AppUser.php b/api/app/controller/admin/AppUser.php
index f3f54e0..560fd46 100644
--- a/api/app/controller/admin/AppUser.php
+++ b/api/app/controller/admin/AppUser.php
@@ -78,28 +78,9 @@ class AppUser extends BaseController
$baseQuery->where($where);
}
- // 默认不展示「从未有过测试记录」的用户(企业后台按本企业 test_results 判定);?includeZeroTests=1 显示全部
- $includeZeroTests = Request::param('includeZeroTests', '');
- $showUntested = ($includeZeroTests === '1' || $includeZeroTests === 'true' || $includeZeroTests === true);
- if (!$showUntested) {
- $trQ = Db::name('test_results');
- if ($enterpriseId) {
- $trQ->where('enterpriseId', (int) $enterpriseId);
- }
- $testedUserIds = $trQ->distinct(true)->column('userId');
- $testedUserIds = array_values(array_unique(array_filter(array_map('intval', $testedUserIds))));
- if (empty($testedUserIds)) {
- return paginate_response([], 0, $page, $pageSize);
- }
- $baseQuery->whereIn('id', $testedUserIds);
- }
-
$total = (int) $baseQuery->count();
$list = (clone $baseQuery)
- ->field([
- 'id', 'nickname', 'openid', 'avatar', 'phone', 'gender',
- 'country', 'province', 'city', 'status', 'lastLoginAt', 'createdAt',
- ])
+ ->field('id,nickname,openid,avatar,phone,gender,country,province,city,status,lastLoginAt,createdAt')
->order('createdAt', 'desc')
->page($page, $pageSize)
->select()
@@ -174,9 +155,6 @@ class AppUser extends BaseController
foreach ($list as &$row) {
$id = $row['id'];
- $av = $row['avatar'] ?? '';
- $row['avatar'] = is_scalar($av) ? trim((string) $av) : '';
- $row['avatarUrl'] = $row['avatar'];
$testsForUser = $testTypes[$id] ?? [];
$row['username'] = $row['nickname'] ?? ('用户' . $id);
$row['testCount'] = (int) ($testCounts[$id] ?? 0);
@@ -194,7 +172,6 @@ class AppUser extends BaseController
$row['paidOrders'] = $pay ? (int) ($pay['paidOrders'] ?? 0) : 0;
$row['totalPaidAmount'] = $pay ? (int) ($pay['totalPaidAmount'] ?? 0) : 0;
}
- unset($row);
return paginate_response($list, $total, $page, $pageSize);
}
diff --git a/api/app/controller/admin/Invite.php b/api/app/controller/admin/Invite.php
index 2077757..cebb80a 100644
--- a/api/app/controller/admin/Invite.php
+++ b/api/app/controller/admin/Invite.php
@@ -1,86 +1,64 @@
-request->user ?? null;
- if (!$admin || !in_array($admin['role'] ?? '', ['admin', 'enterprise_admin'])) {
- return error('无权限访问', 403);
- }
-
- $enterpriseId = null;
- if (($admin['role'] ?? '') === 'enterprise_admin') {
- $row = Db::name('users')->where('id', (int) ($admin['userId'] ?? 0))->find();
- $enterpriseId = isset($row['enterpriseId']) ? (int) $row['enterpriseId'] : null;
- } else {
- $enterpriseId = (int) $this->request->param('enterpriseId', 0);
- if ($enterpriseId <= 0) {
- return error('请指定企业(企业管理员无需传参,使用所属企业)', 400);
- }
- }
- if ($enterpriseId <= 0) {
- return error('无法确定企业,仅企业管理员或指定 enterpriseId 可生成邀请码', 400);
- }
-
- $sceneEnterprise = 'e_' . $enterpriseId;
- $pageEnterprise = 'pages/enterprise/index';
-
- $resultEnt = WechatService::getWxacodeUnlimited($sceneEnterprise, $pageEnterprise, 430);
- if (isset($resultEnt['errcode'])) {
- return error('企业版小程序码失败:' . ($resultEnt['errmsg'] ?? ''), 500);
- }
- $binEnt = $resultEnt['binary'] ?? '';
- if ($binEnt === '') {
- return error('企业版小程序码生成失败', 500);
- }
- $b64Ent = 'data:image/png;base64,' . base64_encode($binEnt);
-
- // 个人版:首页,scene 使用短串 p(index 无 eid 则留在个人版流程)
- $scenePersonal = 'p';
- $pagePersonal = 'pages/index/index';
- $resultPer = WechatService::getWxacodeUnlimited($scenePersonal, $pagePersonal, 430);
- if (isset($resultPer['errcode'])) {
- return error('个人版小程序码失败:' . ($resultPer['errmsg'] ?? ''), 500);
- }
- $binPer = $resultPer['binary'] ?? '';
- if ($binPer === '') {
- return error('个人版小程序码生成失败', 500);
- }
- $b64Per = 'data:image/png;base64,' . base64_encode($binPer);
-
- return success([
- 'qrcode' => $b64Ent,
- 'scene' => $sceneEnterprise,
- 'page' => $pageEnterprise,
- 'enterprise' => [
- 'qrcode' => $b64Ent,
- 'scene' => $sceneEnterprise,
- 'page' => $pageEnterprise,
- 'label' => '企业版',
- ],
- 'personal' => [
- 'qrcode' => $b64Per,
- 'scene' => $scenePersonal,
- 'page' => $pagePersonal,
- 'label' => '个人版',
- ],
- ]);
- }
-}
-
+request->user ?? null;
+ if (!$admin || !in_array($admin['role'] ?? '', ['admin', 'enterprise_admin'])) {
+ return error('无权限访问', 403);
+ }
+
+ $enterpriseId = null;
+ if (($admin['role'] ?? '') === 'enterprise_admin') {
+ $row = Db::name('users')->where('id', (int) ($admin['userId'] ?? 0))->find();
+ $enterpriseId = isset($row['enterpriseId']) ? (int) $row['enterpriseId'] : null;
+ } else {
+ $enterpriseId = (int) $this->request->param('enterpriseId', 0);
+ if ($enterpriseId <= 0) {
+ return error('请指定企业(企业管理员无需传参,使用所属企业)', 400);
+ }
+ }
+ if ($enterpriseId <= 0) {
+ return error('无法确定企业,仅企业管理员或指定 enterpriseId 可生成邀请码', 400);
+ }
+
+ // 场景值:e_企业ID,小程序 onLoad(options.scene) 可解析
+ $scene = 'e_' . $enterpriseId;
+ $page = 'pages/enterprise/index';
+
+ $result = WechatService::getWxacodeUnlimited($scene, $page, 430);
+ if (isset($result['errcode'])) {
+ return error('获取小程序码失败:' . ($result['errmsg'] ?? ''), 500);
+ }
+
+ $binary = $result['binary'] ?? '';
+ if ($binary === '') {
+ return error('小程序码生成失败', 500);
+ }
+
+ $base64 = 'data:image/png;base64,' . base64_encode($binary);
+
+ return success([
+ 'qrcode' => $base64,
+ 'scene' => $scene,
+ 'page' => $page,
+ ]);
+ }
+}
+
diff --git a/api/app/controller/api/AppConfig.php b/api/app/controller/api/AppConfig.php
index 159f6e8..4267df6 100644
--- a/api/app/controller/api/AppConfig.php
+++ b/api/app/controller/api/AppConfig.php
@@ -158,6 +158,9 @@ class AppConfig extends BaseController
}
}
+ // 与 maintenanceMode 同源:小程序旧逻辑读 reviewMode
+ $reviewMode = $maintenanceMode;
+
return success([
'pricingType' => $pricingType,
'pricing' => $pricing,
@@ -169,6 +172,7 @@ class AppConfig extends BaseController
'siteTitle' => $siteTitle,
'textConfig' => $textConfig,
'maintenanceMode' => $maintenanceMode,
+ 'reviewMode' => $reviewMode,
'defaultEnterpriseId' => $defaultEnterpriseId,
]);
}
diff --git a/api/app/controller/superadmin/AppUser.php b/api/app/controller/superadmin/AppUser.php
index 0ff1c8a..9bc2d53 100644
--- a/api/app/controller/superadmin/AppUser.php
+++ b/api/app/controller/superadmin/AppUser.php
@@ -167,7 +167,7 @@ class AppUser extends BaseController
/**
* 测试用户列表:分页、关键词、池筛选、MBTI 筛选
- * GET /api/v1/superadmin/app-users?page=1&pageSize=20&keyword=&pool=all|individual|enterprise&enterpriseId=&mbti=&includeZeroTests=
+ * GET /api/v1/superadmin/app-users?page=1&pageSize=20&keyword=&pool=all|individual|enterprise&enterpriseId=&mbti=
*/
public function index()
{
@@ -215,18 +215,6 @@ class AppUser extends BaseController
$baseQuery->where('enterpriseId', (int) $enterpriseId);
}
- // 默认不展示「从未有过测试记录」的用户;?includeZeroTests=1 可显示全部(排查用)
- $includeZeroTests = Request::param('includeZeroTests', '');
- $showUntested = ($includeZeroTests === '1' || $includeZeroTests === 'true' || $includeZeroTests === true);
- if (!$showUntested) {
- $testedUserIds = Db::name('test_results')->distinct(true)->column('userId');
- $testedUserIds = array_values(array_unique(array_filter(array_map('intval', $testedUserIds))));
- if (empty($testedUserIds)) {
- return paginate_response([], 0, $page, $pageSize);
- }
- $baseQuery->whereIn('id', $testedUserIds);
- }
-
// MBTI 筛选:保留旧逻辑,从 test_results 取有 mbti 测试的用户
if ($mbti !== '') {
$mbtiUserIds = Db::name('test_results')->where('testType', 'mbti')->distinct(true)->column('userId');
@@ -240,10 +228,7 @@ class AppUser extends BaseController
$total = $baseQuery->count();
$list = (clone $baseQuery)
- ->field([
- 'id', 'openid', 'nickname', 'avatar', 'phone', 'gender',
- 'country', 'province', 'city', 'status', 'lastLoginAt', 'createdAt',
- ])
+ ->field('id,openid,nickname,avatar,phone,gender,country,province,city,status,lastLoginAt,createdAt')
->order('createdAt', 'desc')
->page($page, $pageSize)
->select()
@@ -319,9 +304,6 @@ class AppUser extends BaseController
foreach ($list as &$row) {
$id = $row['id'];
- $av = $row['avatar'] ?? '';
- $row['avatar'] = is_scalar($av) ? trim((string) $av) : '';
- $row['avatarUrl'] = $row['avatar'];
$testsForUser = $testTypes[$id] ?? [];
$row['username'] = $row['nickname'] ?? ('用户' . $id);
$row['testCount'] = (int) ($testCounts[$id] ?? 0);
@@ -342,7 +324,6 @@ class AppUser extends BaseController
$row['totalPaidAmount'] = $totalPaidFen;
$row['totalPaidAmountYuan'] = $totalPaidFen > 0 ? round($totalPaidFen / 100, 2) : 0;
}
- unset($row);
return paginate_response($list, $total, $page, $pageSize);
}
diff --git a/api/app/controller/superadmin/Settings.php b/api/app/controller/superadmin/Settings.php
index 8c6759e..1becf85 100644
--- a/api/app/controller/superadmin/Settings.php
+++ b/api/app/controller/superadmin/Settings.php
@@ -51,17 +51,42 @@ class Settings extends BaseController
}
}
+ $systemDefault = [
+ 'siteName' => '神仙团队AI性格测试',
+ 'siteDescription' => '专业的AI性格测试平台',
+ 'miniprogramName' => '神仙团队AI性格测试',
+ 'maintenanceMode' => false,
+ 'maxTestsPerDay' => 100,
+ 'trialTestCount' => 10,
+ 'defaultEnterpriseId' => null,
+ ];
+ $systemOut = $systemDefault;
+ if ($systemConfig && !empty($systemConfig->value)) {
+ $raw = $systemConfig->value;
+ $decoded = is_string($raw) ? json_decode($raw, true) : $raw;
+ if (is_array($decoded)) {
+ $systemOut = array_merge($systemDefault, $decoded);
+ }
+ }
+ // 审核模式唯一口径:system.maintenanceMode(布尔)。兼容旧版 review_mode.enabled
+ $maint = false;
+ if (isset($systemOut['maintenanceMode'])) {
+ $maint = (bool) $systemOut['maintenanceMode'];
+ }
+ if (!$maint && $reviewModeConfig && !empty($reviewModeConfig->value)) {
+ $rv = $reviewModeConfig->value;
+ if (is_string($rv)) {
+ $rv = json_decode($rv, true);
+ }
+ if (is_array($rv) && !empty($rv['enabled'])) {
+ $maint = true;
+ }
+ }
+ $systemOut['maintenanceMode'] = $maint;
+
return success([
- 'system' => $systemConfig ? $systemConfig->value : [
- 'siteName' => '神仙团队AI性格测试',
- 'siteDescription' => '专业的AI性格测试平台',
- 'miniprogramName' => '神仙团队AI性格测试',
- 'maintenanceMode' => false,
- 'maxTestsPerDay' => 100,
- 'trialTestCount' => 10,
- 'defaultEnterpriseId' => null,
- ],
- 'reviewMode' => $reviewModeConfig && !empty($reviewModeConfig->value) ? $reviewModeConfig->value : ['enabled' => false],
+ 'system' => $systemOut,
+ 'reviewMode' => ['enabled' => $maint],
'notification' => $notificationConfig ? $notificationConfig->value : [
'emailNotification' => true,
'lowBalanceAlert' => true,
@@ -111,6 +136,9 @@ class Settings extends BaseController
if (empty($data)) {
$data = Request::only($allowedKeys);
}
+ if (array_key_exists('maintenanceMode', $data)) {
+ $data['maintenanceMode'] = filter_var($data['maintenanceMode'], FILTER_VALIDATE_BOOLEAN);
+ }
// 默认企业:0 或空视为不启用
if (array_key_exists('defaultEnterpriseId', $data)) {
$de = $data['defaultEnterpriseId'];
@@ -452,9 +480,8 @@ class Settings extends BaseController
}
/**
- * 更新审核模式配置
- * PUT body: { "enabled": true/false }
- * 开启后小程序隐藏AI面相分析功能,仅展示问卷测试,用于通过微信审核
+ * 更新审核模式:仅写入 system 顶层 maintenanceMode(true/false),与 runtime 一致。
+ * PUT body: { "enabled": true } 或 { "maintenanceMode": true }(二者等价,任一即可)
*/
public function updateReviewMode()
{
@@ -467,19 +494,34 @@ class Settings extends BaseController
if (!is_array($input)) {
$input = Request::param();
}
- $enabled = !empty($input['enabled']);
+ $on = false;
+ if (array_key_exists('maintenanceMode', $input)) {
+ $on = !empty($input['maintenanceMode']);
+ } elseif (array_key_exists('enabled', $input)) {
+ $on = !empty($input['enabled']);
+ }
try {
- $config = SystemConfigModel::where('key', 'review_mode')->where('enterprise_id', 0)->find();
+ $config = SystemConfigModel::where('key', 'system')->where('enterprise_id', 0)->find();
if (!$config) {
$config = new SystemConfigModel();
- $config->key = 'review_mode';
+ $config->key = 'system';
$config->enterprise_id = 0;
- $config->description = '审核模式:开启后隐藏AI功能以通过微信审核';
+ $config->description = '系统基础配置';
}
- $config->value = ['enabled' => $enabled];
+ $oldVal = $config->value;
+ $oldArr = is_array($oldVal) ? $oldVal : (is_string($oldVal) ? (json_decode($oldVal, true) ?: []) : []);
+ if (!is_array($oldArr)) {
+ $oldArr = [];
+ }
+ $oldArr['maintenanceMode'] = (bool) $on;
+ $config->value = $oldArr;
$config->save();
- return success($config->value, '审核模式已' . ($enabled ? '开启' : '关闭'));
+
+ return success([
+ 'maintenanceMode' => (bool) $on,
+ 'enabled' => (bool) $on,
+ ], '审核模式已' . ($on ? '开启' : '关闭'));
} catch (\Exception $e) {
return error('保存失败:' . $e->getMessage(), 500);
}
diff --git a/douyin-miniprogram/app.js b/douyin-miniprogram/app.js
index 594ac4f..3a7c8ec 100644
--- a/douyin-miniprogram/app.js
+++ b/douyin-miniprogram/app.js
@@ -41,7 +41,8 @@ App({
discResult: null,
pdpResult: null,
aiResult: null,
- reviewMode: true
+ maintenanceMode: undefined,
+ reviewMode: undefined
},
onLaunch(launchOptions) {
@@ -57,8 +58,11 @@ App({
this.getRuntimeConfig().then((cfg) => {
if (cfg) {
if (cfg.siteTitle) this.globalData.siteTitle = cfg.siteTitle
- if (typeof cfg.reviewMode === 'boolean') {
- this.globalData.reviewMode = cfg.reviewMode
+ if (cfg.maintenanceMode !== undefined) this.globalData.maintenanceMode = !!cfg.maintenanceMode
+ if (cfg.reviewMode !== undefined) {
+ this.globalData.reviewMode = !!cfg.reviewMode
+ } else if (cfg.maintenanceMode !== undefined) {
+ this.globalData.reviewMode = !!cfg.maintenanceMode
}
}
}).catch(() => {})
@@ -352,6 +356,12 @@ App({
const data = res.data.data || {}
if (data.siteTitle) this.globalData.siteTitle = data.siteTitle
if (data.textConfig) this.globalData.textConfig = data.textConfig
+ if (data.maintenanceMode !== undefined) this.globalData.maintenanceMode = !!data.maintenanceMode
+ if (data.reviewMode !== undefined) {
+ this.globalData.reviewMode = !!data.reviewMode
+ } else if (data.maintenanceMode !== undefined) {
+ this.globalData.reviewMode = !!data.maintenanceMode
+ }
resolve(data)
} else {
reject(new Error(res.data && res.data.message ? res.data.message : '获取配置失败'))
diff --git a/douyin-miniprogram/custom-tab-bar/index.js b/douyin-miniprogram/custom-tab-bar/index.js
index 1e49d17..c697da0 100644
--- a/douyin-miniprogram/custom-tab-bar/index.js
+++ b/douyin-miniprogram/custom-tab-bar/index.js
@@ -2,6 +2,7 @@
Component({
data: {
selected: 0,
+ reviewMode: false,
list: [
{ pagePath: '/pages/index/index', text: '首页', textKey: 'home', icon: 'home' },
{ pagePath: '/pages/index/camera', text: '拍摄', textKey: 'camera', icon: 'camera' },
@@ -26,6 +27,8 @@ Component({
const currentPage = pages[pages.length - 1]
if (!currentPage || !currentPage.route) return
const url = currentPage.route
+ const gd = getApp().globalData || {}
+ const reviewMode = !!(gd.reviewMode || gd.maintenanceMode)
let selected = 0
if (url === 'pages/index/index' || url === 'pages/enterprise/index') {
selected = 0
@@ -34,16 +37,21 @@ Component({
} else if (url === 'pages/profile/index') {
selected = 2
}
- this.setData({ selected })
+ this.setData({ selected, reviewMode })
} catch (error) {
console.error('updateSelected error:', error)
- this.setData({ selected: 0 })
+ this.setData({ selected: 0, reviewMode: false })
}
},
switchTab(e) {
const index = parseInt(e.currentTarget.dataset.index, 10)
let url = e.currentTarget.dataset.path
+ const gd = getApp().globalData || {}
+ if (index === 1 && !!(gd.reviewMode || gd.maintenanceMode)) {
+ return
+ }
+
if (index === 0) {
try {
const app = getApp()
diff --git a/douyin-miniprogram/custom-tab-bar/index.ttml b/douyin-miniprogram/custom-tab-bar/index.ttml
index c07927a..5b68beb 100644
--- a/douyin-miniprogram/custom-tab-bar/index.ttml
+++ b/douyin-miniprogram/custom-tab-bar/index.ttml
@@ -1,7 +1,7 @@
-
+
首页
-
+
{
if (cfg) {
- if (typeof cfg.reviewMode === 'boolean') {
- app.globalData.reviewMode = cfg.reviewMode
- }
+ if (cfg.reviewMode !== undefined) app.globalData.reviewMode = !!cfg.reviewMode
+ else if (cfg.maintenanceMode !== undefined) app.globalData.reviewMode = !!cfg.maintenanceMode
+ if (cfg.maintenanceMode !== undefined) app.globalData.maintenanceMode = !!cfg.maintenanceMode
if (cfg.textConfig) {
app.globalData.textConfig = cfg.textConfig
this.setData({
aiAnalysisText: cfg.textConfig.aiAnalysisText || '分析',
- reviewMode: !!app.globalData.reviewMode
+ reviewMode: _auditFlag()
})
+ try {
+ const tb = typeof this.getTabBar === 'function' ? this.getTabBar() : null
+ if (tb && typeof tb.updateSelected === 'function') tb.updateSelected()
+ } catch (e) {}
return
}
}
- this.setData({ reviewMode: !!app.globalData.reviewMode })
+ this.setData({ reviewMode: _auditFlag() })
}).catch(() => {
- this.setData({ reviewMode: !!app.globalData.reviewMode })
+ this.setData({ reviewMode: _auditFlag() })
})
},
onReady() {
- if (!app.globalData.reviewMode) {
+ if (!_auditFlag()) {
this.initCameraContext()
}
},
@@ -62,11 +70,11 @@ Page({
},
onShow() {
- const rm = !!app.globalData.reviewMode
+ const rm = _auditFlag()
this.setData({ reviewMode: rm })
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- this.getTabBar().setData({ selected: 1 })
+ this.getTabBar().setData({ selected: 1, reviewMode: rm })
}
if (rm) {
diff --git a/douyin-miniprogram/pages/index/index.js b/douyin-miniprogram/pages/index/index.js
index e54d6af..29669dc 100644
--- a/douyin-miniprogram/pages/index/index.js
+++ b/douyin-miniprogram/pages/index/index.js
@@ -24,20 +24,24 @@ Page({
const userInfo = getApp().globalData.userInfo || tt.getStorageSync('userInfo') || {}
const gd = getApp().globalData
+ const rm0 = !!(gd.reviewMode || gd.maintenanceMode)
this.setData({
statusBarHeight: statusBarHeightRpx,
navbarHeight: navbarHeightRpx,
showEnterpriseEntry: userInfo.hasEnterprise === true,
- siteTitle: gd.reviewMode ? (gd.siteTitle || '神仙团队性格测试').replace(/AI/gi, '') : (gd.siteTitle || '神仙团队性格测试'),
- startButtonText: gd.reviewMode ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonText) || '拍摄'),
- aiAnalysisText: gd.reviewMode ? '分析' : ((gd.textConfig && gd.textConfig.aiAnalysisText) || '分析'),
- reviewMode: !!gd.reviewMode
+ siteTitle: rm0 ? (gd.siteTitle || '神仙团队性格测试').replace(/AI/gi, '') : (gd.siteTitle || '神仙团队性格测试'),
+ startButtonText: rm0 ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonText) || '拍摄'),
+ aiAnalysisText: rm0 ? '分析' : ((gd.textConfig && gd.textConfig.aiAnalysisText) || '分析'),
+ reviewMode: rm0
})
// 预加载站点名称与文案配置
app.getRuntimeConfig().then((cfg) => {
if (cfg) {
- const rm = !!cfg.reviewMode
+ const rm = !!(cfg.reviewMode || cfg.maintenanceMode)
getApp().globalData.reviewMode = rm
+ if (cfg.maintenanceMode !== undefined) {
+ getApp().globalData.maintenanceMode = !!cfg.maintenanceMode
+ }
if (cfg.siteTitle) {
getApp().globalData.siteTitle = cfg.siteTitle
this.setData({ siteTitle: rm ? cfg.siteTitle.replace(/AI/gi, '') : cfg.siteTitle })
@@ -50,6 +54,10 @@ Page({
})
}
this.setData({ reviewMode: rm })
+ try {
+ const tb = typeof this.getTabBar === 'function' ? this.getTabBar() : null
+ if (tb && typeof tb.updateSelected === 'function') tb.updateSelected()
+ } catch (e) {}
}
}).catch(() => {})
// ── 解析入参(兼容扫码 scene / 分享链接 options)──
@@ -96,12 +104,12 @@ Page({
getApp().globalData.appScope = 'personal'
getApp().globalData.enterpriseIdFromScene = null
} catch (e) {}
- const gd = getApp().globalData
- const rm = !!gd.reviewMode
+ const g = getApp().globalData
+ const rm = !!(g.reviewMode || g.maintenanceMode)
this.setData({
- siteTitle: rm ? (gd.siteTitle || '神仙团队性格测试').replace(/AI/gi, '') : (gd.siteTitle || '神仙团队性格测试'),
- startButtonText: rm ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonText) || '拍摄'),
- aiAnalysisText: rm ? '分析' : ((gd.textConfig && gd.textConfig.aiAnalysisText) || '分析'),
+ siteTitle: rm ? (g.siteTitle || '神仙团队性格测试').replace(/AI/gi, '') : (g.siteTitle || '神仙团队性格测试'),
+ startButtonText: rm ? '开始性格测试' : ((g.textConfig && g.textConfig.startButtonText) || '拍摄'),
+ aiAnalysisText: rm ? '分析' : ((g.textConfig && g.textConfig.aiAnalysisText) || '分析'),
reviewMode: rm
})
const userInfo = getApp().globalData.userInfo || tt.getStorageSync('userInfo') || {}
@@ -124,9 +132,13 @@ Page({
}
},
- // 与底栏中间一致:始终进入拍摄 Tab(审核态在 camera 页内展示问卷引导)
startCamera() {
try { getApp().globalData.appScope = 'personal' } catch (e) {}
+ const gd = getApp().globalData
+ if (gd.reviewMode || gd.maintenanceMode) {
+ tt.navigateTo({ url: '/pages/test-select/index' })
+ return
+ }
tt.switchTab({ url: '/pages/index/camera' })
},
diff --git a/douyin-miniprogram/pages/index/upload.js b/douyin-miniprogram/pages/index/upload.js
index 8d341b7..171a41a 100644
--- a/douyin-miniprogram/pages/index/upload.js
+++ b/douyin-miniprogram/pages/index/upload.js
@@ -34,14 +34,14 @@ Page({
},
onShow() {
- // 审核模式下重定向到测试选择页
- if (app.globalData.reviewMode) {
+ const audit = !!(app.globalData.reviewMode || app.globalData.maintenanceMode)
+ if (audit) {
tt.navigateTo({ url: '/pages/test-select/index' })
return
}
if (!ensureProfileCompleteAndRedirect()) return
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- this.getTabBar().setData({ selected: 1 })
+ this.getTabBar().setData({ selected: 1, reviewMode: audit })
}
this.setData({ needPhoneAuth: !hasPhone() })
const tc = app.globalData.textConfig
diff --git a/douyin-miniprogram/pages/profile/index.js b/douyin-miniprogram/pages/profile/index.js
index d53727d..1102521 100644
--- a/douyin-miniprogram/pages/profile/index.js
+++ b/douyin-miniprogram/pages/profile/index.js
@@ -47,15 +47,16 @@ Page({
this.runLoginThenLoad()
},
onShow() {
- // 同步审核模式
- this.setData({ reviewMode: !!app.globalData.reviewMode })
+ const gd = app.globalData
+ this.setData({ reviewMode: !!(gd.reviewMode || gd.maintenanceMode) })
// 如果是从其他页面返回,且已经登录,则只刷新数据而不重新执行登录流程
if (this.data.hasLogin) {
this.loadData()
}
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- this.getTabBar().setData({ selected: 2 })
+ const audit = !!(gd.reviewMode || gd.maintenanceMode)
+ this.getTabBar().setData({ selected: 2, reviewMode: audit })
}
},
@@ -142,9 +143,8 @@ Page({
/** 从 /api/test/recent 拉取各类型最新记录 */
_loadRecentFromAPI() {
- const scope = app.globalData.appScope || 'personal'
request({
- url: `/api/test/recent?scope=${scope}`,
+ url: '/api/test/recent?scope=all',
method: 'GET',
success: (res) => {
// res 是 wx.request 原始响应:{ statusCode, data: { code, data, message } }
diff --git a/douyin-miniprogram/pages/profile/index.ttml b/douyin-miniprogram/pages/profile/index.ttml
index acf3886..ebb6d3f 100644
--- a/douyin-miniprogram/pages/profile/index.ttml
+++ b/douyin-miniprogram/pages/profile/index.ttml
@@ -178,7 +178,7 @@
-
+