chore: 同步最新改动(admin/superadmin、api、小程序页面与配置)

Made-with: Cursor
This commit is contained in:
卡若
2026-03-17 13:50:22 +08:00
parent a0aeb844d1
commit b394691c91
19 changed files with 5260 additions and 4925 deletions

View File

@@ -1,201 +1,212 @@
<?php
namespace app\controller\api;
use app\BaseController;
use app\model\PricingConfig as PricingConfigModel;
use app\model\AiProvider as AiProviderModel;
use app\common\service\JwtService;
use think\facade\Db;
/**
* 小程序运行配置(定价 + AI 服务商)
* 个人用户读超管个人定价企业用户读超管企业定价AI 一律读超管配置,默认使用第一个启用的服务商。
*/
class AppConfig extends BaseController
{
/**
* GET api/config/runtime
* 可选 Header Authorization有 token 且用户属于企业则返回企业定价,否则个人定价。
* 返回pricingType(personal|enterprise), pricing(config), aiProviderId, aiProviderName
*/
public function runtime()
{
$pricingType = 'personal';
$enterpriseId = null;
$pricing = [];
$scope = (string) ($this->request->param('scope', '') ?? '');
$user = $this->request->user ?? null;
if (!$user) {
$token = JwtService::getTokenFromRequest($this->request);
if ($token) {
$payload = JwtService::verifyToken($token);
if ($payload) {
$user = [
'source' => $payload['source'] ?? '',
'user_id' => $payload['user_id'] ?? $payload['userId'] ?? null,
'userId' => $payload['user_id'] ?? $payload['userId'] ?? null,
];
}
}
}
if ($scope !== 'personal' && $user && ($user['source'] ?? '') === 'wechat') {
$userId = (int) ($user['user_id'] ?? $user['userId'] ?? 0);
if ($userId > 0) {
$row = Db::name('wechat_users')->where('id', $userId)->field('enterpriseId')->find();
if (!empty($row['enterpriseId'])) {
$enterpriseId = (int) $row['enterpriseId'];
$pricingType = 'enterprise';
}
}
}
$config = PricingConfigModel::getByTypeAndEnterprise($pricingType, $enterpriseId);
if ($config && !empty($config->config)) {
// PricingConfig 模型已对 config 做 JSON 转换,这里直接当数组/对象用即可
$rawConfig = $config->config;
$pricing = is_array($rawConfig) ? $rawConfig : (array) $rawConfig;
}
// 超管 AI 配置:第一个 enabled=1、visible=1显示且 apiKey 非空;隐藏的服务商不参与选用
$firstProvider = AiProviderModel::where('enabled', 1)
->whereRaw('(visible IS NULL OR visible = 1)')
->whereRaw('(apiKey IS NOT NULL AND LENGTH(TRIM(apiKey)) > 0)')
->order('id', 'asc')
->find();
$aiProviderId = null;
$aiProviderName = null;
if ($firstProvider) {
$aiProviderId = $firstProvider->providerId;
$aiProviderName = $firstProvider->name ?? $firstProvider->providerId;
}
// 报告付费开关:完全根据定价配置判断(价格 > 0 视为需要付费)
$reportRequiresPayment = ['face' => 0, 'mbti' => 0, 'disc' => 0, 'pdp' => 0];
foreach (['face', 'mbti', 'disc', 'pdp'] as $k) {
$key = $k === 'team_analysis' ? 'teamAnalysis' : $k;
if (isset($pricing[$key]) && (float) $pricing[$key] > 0) {
$reportRequiresPayment[$k] = 1;
}
}
// 站点信息(网站名称、小程序名称):供小程序导航栏等展示
$siteName = '';
$miniprogramName = '';
$siteInfo = Db::name('system_config')->where('key', 'site_info')->find();
if ($siteInfo && !empty($siteInfo['value'])) {
$val = is_string($siteInfo['value']) ? json_decode($siteInfo['value'], true) : $siteInfo['value'];
if (is_array($val)) {
$siteName = (string) ($val['siteName'] ?? '');
$miniprogramName = (string) ($val['miniprogramName'] ?? '');
}
}
if ($siteName === '' || $miniprogramName === '') {
$system = Db::name('system_config')->where('key', 'system')->find();
if ($system && !empty($system['value'])) {
$val = is_string($system['value']) ? json_decode($system['value'], true) : $system['value'];
if (is_array($val)) {
if ($siteName === '') $siteName = (string) ($val['siteName'] ?? '');
if ($miniprogramName === '') $miniprogramName = (string) ($val['miniprogramName'] ?? $val['siteName'] ?? '');
}
}
}
$siteTitle = $miniprogramName !== '' ? $miniprogramName : ($siteName !== '' ? $siteName : '神仙团队AI性格测试');
// 小程序文案配置(分析中提示、按钮、报告标题等)
$textConfig = [
'analyzingTitle' => '正在分析中',
'startButtonText' => '开始面相测试',
'startButtonEnterprise' => '开始面部测试',
'reportTitle' => '分析报告',
'aiAnalysisText' => '智能分析'
];
// 先读全局 text_configenterprise_id=0
$tcRow = Db::name('system_config')->where('key', 'text_config')->where('enterprise_id', 0)->find();
if ($tcRow && !empty($tcRow['value'])) {
$tcVal = is_string($tcRow['value']) ? json_decode($tcRow['value'], true) : $tcRow['value'];
if (is_array($tcVal)) {
$textConfig = array_merge($textConfig, array_intersect_key($tcVal, $textConfig));
}
}
// 企业专属文案 + 小程序名称 覆盖全局enterprise_id={eid}
if ($enterpriseId > 0) {
$eidTc = Db::name('system_config')->where('key', 'text_config')->where('enterprise_id', $enterpriseId)->find();
if ($eidTc && !empty($eidTc['value'])) {
$eidVal = is_string($eidTc['value']) ? json_decode($eidTc['value'], true) : $eidTc['value'];
if (is_array($eidVal)) {
$textConfig = array_merge($textConfig, array_intersect_key($eidVal, $textConfig));
if (!empty($eidVal['miniprogramName'])) {
$miniprogramName = (string) $eidVal['miniprogramName'];
$siteTitle = $miniprogramName;
}
}
}
}
return success([
'pricingType' => $pricingType,
'pricing' => $pricing,
'aiProviderId' => $aiProviderId,
'aiProviderName' => $aiProviderName,
'reportRequiresPayment' => $reportRequiresPayment,
'siteName' => $siteName,
'miniprogramName' => $miniprogramName,
'siteTitle' => $siteTitle,
'textConfig' => $textConfig,
]);
}
/**
* GET api/config/deep-pricing?scope=personal|enterprise
* 深度服务价格(开通会员页):个人版与企业版分别返回可配置的类目列表,支持后台新增类目
*/
public function deepPricing()
{
$scope = (string) ($this->request->param('scope', 'personal') ?? 'personal');
$type = $scope === 'enterprise' ? 'deep_enterprise' : 'deep_personal';
$config = PricingConfigModel::where('type', $type)->whereNull('enterpriseId')->find();
$categories = [];
if ($config && !empty($config->config)) {
$raw = $config->config;
$data = is_array($raw) ? $raw : (array) $raw;
$categories = isset($data['categories']) && is_array($data['categories']) ? $data['categories'] : [];
// 兼容旧数据:补全可能缺失的字段,确保前端始终能读到完整结构
foreach ($categories as &$cat) {
// features旧数据只存 featuresText动态拆成数组
if (!isset($cat['features']) || !is_array($cat['features'])) {
if (!empty($cat['featuresText']) && is_string($cat['featuresText'])) {
$lines = preg_split('/\r?\n/', $cat['featuresText']);
$cat['features'] = array_values(array_filter(array_map('trim', $lines), static function ($s) {
return $s !== '';
}));
} else {
$cat['features'] = [];
}
}
// serviceWechat客服微信展示给用户的微信号
if (!isset($cat['serviceWechat'])) {
$cat['serviceWechat'] = '';
}
// consultWechat存客宝KEY旧类目可能没有该字段补空字符串
if (!isset($cat['consultWechat'])) {
$cat['consultWechat'] = '';
}
// promptText同样补全
if (!isset($cat['promptText'])) {
$cat['promptText'] = '';
}
// successMessage成功提示词补全
if (!isset($cat['successMessage'])) {
$cat['successMessage'] = '';
}
}
unset($cat);
}
return success(['scope' => $scope, 'categories' => $categories]);
}
}
<?php
namespace app\controller\api;
use app\BaseController;
use app\model\PricingConfig as PricingConfigModel;
use app\model\AiProvider as AiProviderModel;
use app\common\service\JwtService;
use think\facade\Db;
/**
* 小程序运行配置(定价 + AI 服务商)
* 个人用户读超管个人定价企业用户读超管企业定价AI 一律读超管配置,默认使用第一个启用的服务商。
*/
class AppConfig extends BaseController
{
/**
* GET api/config/runtime
* 可选 Header Authorization有 token 且用户属于企业则返回企业定价,否则个人定价。
* 返回pricingType(personal|enterprise), pricing(config), aiProviderId, aiProviderName
*/
public function runtime()
{
$pricingType = 'personal';
$enterpriseId = null;
$pricing = [];
$scope = (string) ($this->request->param('scope', '') ?? '');
$user = $this->request->user ?? null;
if (!$user) {
$token = JwtService::getTokenFromRequest($this->request);
if ($token) {
$payload = JwtService::verifyToken($token);
if ($payload) {
$user = [
'source' => $payload['source'] ?? '',
'user_id' => $payload['user_id'] ?? $payload['userId'] ?? null,
'userId' => $payload['user_id'] ?? $payload['userId'] ?? null,
];
}
}
}
if ($scope !== 'personal' && $user && ($user['source'] ?? '') === 'wechat') {
$userId = (int) ($user['user_id'] ?? $user['userId'] ?? 0);
if ($userId > 0) {
$row = Db::name('wechat_users')->where('id', $userId)->field('enterpriseId')->find();
if (!empty($row['enterpriseId'])) {
$enterpriseId = (int) $row['enterpriseId'];
$pricingType = 'enterprise';
}
}
}
$config = PricingConfigModel::getByTypeAndEnterprise($pricingType, $enterpriseId);
if ($config && !empty($config->config)) {
// PricingConfig 模型已对 config 做 JSON 转换,这里直接当数组/对象用即可
$rawConfig = $config->config;
$pricing = is_array($rawConfig) ? $rawConfig : (array) $rawConfig;
}
// 超管 AI 配置:第一个 enabled=1、visible=1显示且 apiKey 非空;隐藏的服务商不参与选用
$firstProvider = AiProviderModel::where('enabled', 1)
->whereRaw('(visible IS NULL OR visible = 1)')
->whereRaw('(apiKey IS NOT NULL AND LENGTH(TRIM(apiKey)) > 0)')
->order('id', 'asc')
->find();
$aiProviderId = null;
$aiProviderName = null;
if ($firstProvider) {
$aiProviderId = $firstProvider->providerId;
$aiProviderName = $firstProvider->name ?? $firstProvider->providerId;
}
// 报告付费开关:完全根据定价配置判断(价格 > 0 视为需要付费)
$reportRequiresPayment = ['face' => 0, 'mbti' => 0, 'disc' => 0, 'pdp' => 0];
foreach (['face', 'mbti', 'disc', 'pdp'] as $k) {
$key = $k === 'team_analysis' ? 'teamAnalysis' : $k;
if (isset($pricing[$key]) && (float) $pricing[$key] > 0) {
$reportRequiresPayment[$k] = 1;
}
}
// 站点信息(网站名称、小程序名称):供小程序导航栏等展示
$siteName = '';
$miniprogramName = '';
$siteInfo = Db::name('system_config')->where('key', 'site_info')->find();
if ($siteInfo && !empty($siteInfo['value'])) {
$val = is_string($siteInfo['value']) ? json_decode($siteInfo['value'], true) : $siteInfo['value'];
if (is_array($val)) {
$siteName = (string) ($val['siteName'] ?? '');
$miniprogramName = (string) ($val['miniprogramName'] ?? '');
}
}
if ($siteName === '' || $miniprogramName === '') {
$system = Db::name('system_config')->where('key', 'system')->find();
if ($system && !empty($system['value'])) {
$val = is_string($system['value']) ? json_decode($system['value'], true) : $system['value'];
if (is_array($val)) {
if ($siteName === '') $siteName = (string) ($val['siteName'] ?? '');
if ($miniprogramName === '') $miniprogramName = (string) ($val['miniprogramName'] ?? $val['siteName'] ?? '');
}
}
}
$siteTitle = $miniprogramName !== '' ? $miniprogramName : ($siteName !== '' ? $siteName : '神仙团队AI性格测试');
// 小程序文案配置(分析中提示、按钮、报告标题等)
$textConfig = [
'analyzingTitle' => '正在分析中',
'startButtonText' => '开始面相测试',
'startButtonEnterprise' => '开始面部测试',
'reportTitle' => '分析报告',
'aiAnalysisText' => '智能分析'
];
// 先读全局 text_configenterprise_id=0
$tcRow = Db::name('system_config')->where('key', 'text_config')->where('enterprise_id', 0)->find();
if ($tcRow && !empty($tcRow['value'])) {
$tcVal = is_string($tcRow['value']) ? json_decode($tcRow['value'], true) : $tcRow['value'];
if (is_array($tcVal)) {
$textConfig = array_merge($textConfig, array_intersect_key($tcVal, $textConfig));
}
}
// 企业专属文案 + 小程序名称 覆盖全局enterprise_id={eid}
if ($enterpriseId > 0) {
$eidTc = Db::name('system_config')->where('key', 'text_config')->where('enterprise_id', $enterpriseId)->find();
if ($eidTc && !empty($eidTc['value'])) {
$eidVal = is_string($eidTc['value']) ? json_decode($eidTc['value'], true) : $eidTc['value'];
if (is_array($eidVal)) {
$textConfig = array_merge($textConfig, array_intersect_key($eidVal, $textConfig));
if (!empty($eidVal['miniprogramName'])) {
$miniprogramName = (string) $eidVal['miniprogramName'];
$siteTitle = $miniprogramName;
}
}
}
}
// 审核模式开关
$reviewMode = false;
$rmRow = Db::name('system_config')->where('key', 'review_mode')->where('enterprise_id', 0)->find();
if ($rmRow && !empty($rmRow['value'])) {
$rmVal = is_string($rmRow['value']) ? json_decode($rmRow['value'], true) : $rmRow['value'];
if (is_array($rmVal) && !empty($rmVal['enabled'])) {
$reviewMode = true;
}
}
return success([
'pricingType' => $pricingType,
'pricing' => $pricing,
'aiProviderId' => $aiProviderId,
'aiProviderName' => $aiProviderName,
'reportRequiresPayment' => $reportRequiresPayment,
'siteName' => $siteName,
'miniprogramName' => $miniprogramName,
'siteTitle' => $siteTitle,
'textConfig' => $textConfig,
'reviewMode' => $reviewMode,
]);
}
/**
* GET api/config/deep-pricing?scope=personal|enterprise
* 深度服务价格(开通会员页):个人版与企业版分别返回可配置的类目列表,支持后台新增类目
*/
public function deepPricing()
{
$scope = (string) ($this->request->param('scope', 'personal') ?? 'personal');
$type = $scope === 'enterprise' ? 'deep_enterprise' : 'deep_personal';
$config = PricingConfigModel::where('type', $type)->whereNull('enterpriseId')->find();
$categories = [];
if ($config && !empty($config->config)) {
$raw = $config->config;
$data = is_array($raw) ? $raw : (array) $raw;
$categories = isset($data['categories']) && is_array($data['categories']) ? $data['categories'] : [];
// 兼容旧数据:补全可能缺失的字段,确保前端始终能读到完整结构
foreach ($categories as &$cat) {
// features旧数据只存 featuresText动态拆成数组
if (!isset($cat['features']) || !is_array($cat['features'])) {
if (!empty($cat['featuresText']) && is_string($cat['featuresText'])) {
$lines = preg_split('/\r?\n/', $cat['featuresText']);
$cat['features'] = array_values(array_filter(array_map('trim', $lines), static function ($s) {
return $s !== '';
}));
} else {
$cat['features'] = [];
}
}
// serviceWechat客服微信展示给用户的微信号
if (!isset($cat['serviceWechat'])) {
$cat['serviceWechat'] = '';
}
// consultWechat存客宝KEY旧类目可能没有该字段补空字符串
if (!isset($cat['consultWechat'])) {
$cat['consultWechat'] = '';
}
// promptText同样补全
if (!isset($cat['promptText'])) {
$cat['promptText'] = '';
}
// successMessage成功提示词补全
if (!isset($cat['successMessage'])) {
$cat['successMessage'] = '';
}
}
unset($cat);
}
return success(['scope' => $scope, 'categories' => $categories]);
}
}

File diff suppressed because it is too large Load Diff