deploy: auto commit repo-root changes 2026-07-05 12:33:57

This commit is contained in:
hajimi
2026-07-05 12:33:57 +08:00
parent 1954948445
commit f641276b9d
123 changed files with 1182 additions and 572 deletions
@@ -0,0 +1,759 @@
<?php
namespace app\common\service\ai;
use app\common\enum\YesNoEnum;
use app\common\model\article\Article;
use app\common\model\community\CommunityPost;
use app\common\model\community\CommunityTag;
use app\common\model\lottery\LotteryCategory;
use app\common\model\lottery\LotteryDraw;
use app\common\model\lottery\LotteryDrawResult;
use app\common\model\lottery\LotteryGame;
use app\common\model\lottery\LotteryNumberMapping;
use app\common\model\match\MatchEvent;
use app\common\model\match\MatchHistory;
use app\common\model\match\MatchRecent;
use app\common\service\FileService;
use think\facade\Db;
class AiAnalysisRequestService
{
private const LIUHE_TAGS = ['旧澳六合', '新澳六合'];
private const MATCH_SECTIONS = ['prediction', 'factors', 'odds', 'summary'];
private const LOTTERY_MODULES = ['hot_cold', 'trend', 'stats', 'color_zodiac'];
public static function analyze(string $type, array $params, int $userId): array
{
$type = strtolower(str_replace('-', '_', trim($type)));
return match ($type) {
'match', 'match_section' => self::analyzeMatchSection(
(int) ($params['match_id'] ?? 0),
(string) ($params['section'] ?? 'prediction'),
$userId
),
'match_full', 'match_predict' => self::analyzeMatchFull((int) ($params['match_id'] ?? 0), $userId),
'article' => self::analyzeArticle((int) ($params['article_id'] ?? 0), $userId),
'post' => self::analyzePost((int) ($params['post_id'] ?? 0), $userId, (int) ($params['force'] ?? 0) === 1),
'lottery' => self::analyzeLotteryOverview($params, $userId),
'lottery_module' => self::analyzeLotteryModule(
(string) ($params['code'] ?? ''),
(string) ($params['module'] ?? ''),
$userId
),
'credibility', 'user_credibility' => self::analyzeUserCredibility((int) ($params['user_id'] ?? 0), $userId),
default => self::fail('不支持的AI分析类型'),
};
}
public static function analyzeMatchFull(int $matchId, int $userId): array
{
if ($matchId <= 0) {
return self::fail('参数缺失');
}
$matchData = self::buildMatchData($matchId, false);
if (empty($matchData['success'])) {
return $matchData;
}
$quota = self::ensureQuota($userId, AiQuotaService::TYPE_MATCH, $matchId, $matchId);
if (empty($quota['success'])) {
return $quota;
}
$result = AiService::predictMatch($matchId, $matchData['data'], $userId);
if (!$result['success']) {
return self::fail($result['error'] ?? 'AI分析失败', 0, $quota['quota']);
}
return self::withQuota([
'analysis' => $result['data'],
'from_cache' => $result['from_cache'] ?? false,
'match_info' => $matchData['data']['match_info'],
], $quota['quota']);
}
public static function analyzeMatchSection(int $matchId, string $section, int $userId): array
{
$section = trim($section);
if ($matchId <= 0 || $section === '') {
return self::fail('参数缺失');
}
if (!in_array($section, self::MATCH_SECTIONS, true)) {
return self::fail('无效的分析模块');
}
$matchData = self::buildMatchData($matchId, true);
if (empty($matchData['success'])) {
return $matchData;
}
$quota = self::ensureQuota($userId, AiQuotaService::TYPE_MATCH, $matchId, $matchId);
if (empty($quota['success'])) {
return $quota;
}
$result = AiService::predictMatchSection($matchId, $section, $matchData['data'], $userId);
if (!$result['success']) {
return self::fail($result['error'] ?? 'AI分析失败', 0, $quota['quota']);
}
return self::withQuota([
'section' => $section,
'data' => $result['data'],
'from_cache' => $result['from_cache'] ?? false,
'match_info' => $matchData['data']['match_info'],
], $quota['quota']);
}
public static function analyzeArticle(int $articleId, int $userId): array
{
if ($articleId <= 0) {
return self::fail('参数缺失');
}
$article = Article::where(['id' => $articleId, 'is_show' => YesNoEnum::YES])->findOrEmpty();
if ($article->isEmpty()) {
return self::fail('文章不存在');
}
$quota = self::ensureQuota($userId, AiQuotaService::TYPE_ARTICLE, $articleId, $articleId);
if (empty($quota['success'])) {
return $quota;
}
$result = AiService::analyzeArticle($articleId, $article->toArray(), $userId);
if (!$result['success']) {
return self::fail($result['error'] ?? 'AI分析失败', 0, $quota['quota']);
}
return self::withQuota([
'analysis' => $result['data'],
'from_cache' => $result['from_cache'] ?? false,
], $quota['quota']);
}
public static function analyzeUserCredibility(int $targetUserId, int $userId): array
{
if ($targetUserId <= 0) {
return self::fail('参数缺失');
}
$quota = self::ensureQuota($userId, AiQuotaService::TYPE_CREDIBILITY, $targetUserId, $targetUserId);
if (empty($quota['success'])) {
return $quota;
}
$userData = [
'user_id' => $targetUserId,
'register_days' => 30,
'post_count' => 0,
'comment_count' => 0,
'like_received' => 0,
];
$result = AiService::analyzeCredibility($targetUserId, $userData, $userId);
if (!$result['success']) {
return self::fail($result['error'] ?? 'AI分析失败', 0, $quota['quota']);
}
return self::withQuota([
'analysis' => $result['data'],
'from_cache' => $result['from_cache'] ?? false,
], $quota['quota']);
}
public static function analyzePost(int $postId, int $userId, bool $force = false): array
{
if ($postId <= 0) {
return self::fail('参数缺失');
}
try {
$post = CommunityPost::where('id', $postId)->where('status', 1)->findOrEmpty();
if ($post->isEmpty()) {
return self::fail('帖子不存在');
}
$data = $post->toArray();
$ext = is_array($data['ext'] ?? null) ? $data['ext'] : (json_decode((string) ($data['ext'] ?? ''), true) ?: []);
$tags = self::getPostTags($postId);
if (self::isTrumpPost($tags)) {
return self::fail('特朗普帖子仅支持翻译');
}
if (!self::isLiuhePost($tags, $ext)) {
return self::fail('该帖子类型无需AI分析');
}
$quota = self::ensureQuota($userId, AiQuotaService::TYPE_POST, $postId, $postId);
if (empty($quota['success'])) {
return $quota;
}
$analysis = self::ensureLiuheAnalysisContent($post, $ext);
if (!$analysis['success']) {
return self::fail($analysis['error'] ?? '图片分析失败', 0, $quota['quota']);
}
$ext = $analysis['ext'];
$data['tags'] = array_values($tags);
$data['ext'] = $ext;
if (!empty($ext['translated_content'])) {
$data['translated_content'] = (string) $ext['translated_content'];
}
if (!empty($ext['lottery_analysis_content'])) {
$data['lottery_analysis_content'] = (string) $ext['lottery_analysis_content'];
}
$result = AiService::analyzePost($postId, $data, $userId, true, $force);
if (!$result['success']) {
return self::fail($result['error'] ?? 'AI分析失败', 0, $quota['quota']);
}
return self::withQuota([
'analysis' => $result['data'],
'from_cache' => $result['from_cache'] ?? false,
], $quota['quota']);
} catch (\Throwable $e) {
return self::fail('AI分析异常: ' . $e->getMessage());
}
}
public static function analyzeLotteryOverview(array $params, int $userId): array
{
$prepared = self::buildLotteryOverviewData($params);
if (empty($prepared['success'])) {
return $prepared;
}
$quota = self::ensureQuota(
$userId,
AiQuotaService::TYPE_LOTTERY,
self::lotteryQuotaRef($prepared['code'], $prepared['latest_issue']),
self::crcRefId($prepared['code'] . ':' . $prepared['latest_issue'])
);
if (empty($quota['success'])) {
return $quota;
}
$result = AiService::analyzeLottery($prepared['lottery_data'], $userId);
if (!$result['success']) {
return self::fail($result['error'] ?? 'AI分析失败', 0, $quota['quota']);
}
return self::withQuota([
'analysis' => $result['data'],
'from_cache' => $result['from_cache'] ?? false,
'category_name' => $prepared['category_name'],
], $quota['quota']);
}
public static function analyzeLotteryModule(string $code, string $module, int $userId): array
{
$code = trim($code);
$module = trim($module);
if ($code === '' || $module === '') {
return self::fail('参数缺失');
}
if (!in_array($module, self::LOTTERY_MODULES, true)) {
return self::fail('无效的分析模块');
}
$prepared = self::buildLotteryModuleData($code, $module);
if (empty($prepared['success'])) {
return $prepared;
}
$quota = self::ensureQuota(
$userId,
AiQuotaService::TYPE_LOTTERY,
self::lotteryQuotaRef($code, $prepared['latest_issue']),
self::crcRefId($code . ':' . $prepared['latest_issue'])
);
if (empty($quota['success'])) {
return $quota;
}
$result = AiService::analyzeLotteryModule($prepared['lottery_data'], $module, $userId);
if (!$result['success']) {
return self::fail($result['error'] ?? 'AI分析失败', 0, $quota['quota']);
}
return self::withQuota([
'module' => $module,
'data' => $result['data'],
'from_cache' => $result['from_cache'] ?? false,
], $quota['quota']);
}
private static function buildMatchData(int $matchId, bool $includeOdds): array
{
$match = MatchEvent::where('id', $matchId)->where('is_show', 1)->findOrEmpty();
if ($match->isEmpty()) {
return self::fail('赛事不存在');
}
$data = $match->toArray();
$homeTeam = $data['home_team'];
$awayTeam = $data['away_team'];
$matchInfo = [
'home_team' => $homeTeam,
'away_team' => $awayTeam,
'league' => $data['league_name'] ?? '',
'match_time' => date('Y-m-d H:i', $data['match_time'] ?? time()),
'status' => $data['status'] ?? 0,
];
if ($includeOdds) {
$matchInfo += [
'home_score' => $data['home_score'] ?? 0,
'away_score' => $data['away_score'] ?? 0,
'home_odds' => $data['home_odds'] ?? 0,
'draw_odds' => $data['draw_odds'] ?? 0,
'away_odds' => $data['away_odds'] ?? 0,
];
}
return [
'success' => true,
'data' => [
'match_info' => $matchInfo,
'head_to_head' => MatchHistory::where(function ($query) use ($homeTeam, $awayTeam) {
$query->where([['home_team', '=', $homeTeam], ['away_team', '=', $awayTeam]])
->whereOr([['home_team', '=', $awayTeam], ['away_team', '=', $homeTeam]]);
})->order('match_time desc')->limit(5)->select()->toArray(),
'home_recent' => MatchRecent::where('team_name', $homeTeam)
->order('match_time desc')->limit(5)->select()->toArray(),
'away_recent' => MatchRecent::where('team_name', $awayTeam)
->order('match_time desc')->limit(5)->select()->toArray(),
],
];
}
private static function buildLotteryOverviewData(array $params): array
{
$categoryId = (int) ($params['category_id'] ?? 0);
$code = trim((string) ($params['code'] ?? ''));
if ($code !== '') {
$game = LotteryGame::where('code', $code)->findOrEmpty();
if ($game->isEmpty()) {
return self::fail('彩种不存在');
}
$recentDraws = LotteryDrawResult::where('code', $code)
->where('draw_code', '<>', '')
->order('draw_time desc, issue desc')
->limit(30)
->field('issue,draw_time,draw_code')
->select()
->toArray();
foreach ($recentDraws as &$draw) {
$draw['numbers'] = $draw['draw_code'] ? explode(',', $draw['draw_code']) : [];
}
return [
'success' => true,
'code' => $code,
'latest_issue' => (string) ($recentDraws[0]['issue'] ?? ''),
'category_name' => $game['name'],
'lottery_data' => [
'lottery_type' => $code,
'lottery_name' => $game['name'],
'draw_rule' => $game['template'] ?? '',
'recent_draws' => $recentDraws,
],
];
}
if ($categoryId <= 0) {
return self::fail('参数缺失');
}
$category = LotteryCategory::where('id', $categoryId)->findOrEmpty();
if ($category->isEmpty()) {
return self::fail('彩种不存在');
}
$recentDraws = LotteryDraw::where('category_id', $categoryId)
->where('status', 1)
->where('is_show', 1)
->order('draw_date desc, period desc')
->limit(30)
->field('period,draw_date,numbers,special_number,zodiac,color')
->select()
->toArray();
foreach ($recentDraws as &$draw) {
$draw['numbers'] = is_string($draw['numbers']) ? json_decode($draw['numbers'], true) : $draw['numbers'];
$draw['zodiac'] = is_string($draw['zodiac']) ? json_decode($draw['zodiac'], true) : $draw['zodiac'];
$draw['color'] = is_string($draw['color']) ? json_decode($draw['color'], true) : $draw['color'];
}
return [
'success' => true,
'code' => (string) $category['code'],
'latest_issue' => (string) ($recentDraws[0]['period'] ?? ''),
'category_name' => $category['name'],
'lottery_data' => [
'lottery_type' => $category['code'],
'lottery_name' => $category['name'],
'draw_rule' => $category['draw_rule'],
'recent_draws' => $recentDraws,
],
];
}
private static function buildLotteryModuleData(string $code, string $module): array
{
$game = LotteryGame::where('code', $code)->findOrEmpty();
if ($game->isEmpty()) {
return self::fail('彩种不存在');
}
if ($module === 'color_zodiac' && ($game['template'] ?? '') !== 'HK6') {
return self::fail('该彩种不支持波色生肖分析');
}
$recentDraws = self::getRecentDrawCodes($code, 30);
$lotteryData = [
'lottery_type' => $code,
'lottery_name' => $game['name'],
'draw_rule' => $game['template'] ?? '',
'recent_draws' => $recentDraws,
];
if ($module === 'color_zodiac') {
$year = (int) date('Y');
$lotteryData['number_mapping'] = [
'color' => LotteryNumberMapping::getMapByType($year, LotteryNumberMapping::TYPE_COLOR),
'zodiac' => LotteryNumberMapping::getMapByType($year, LotteryNumberMapping::TYPE_ZODIAC),
];
}
return [
'success' => true,
'latest_issue' => (string) ($recentDraws[0]['issue'] ?? ''),
'lottery_data' => $lotteryData,
];
}
private static function getRecentDrawCodes(string $code, int $limit = 30): array
{
$rows = LotteryDrawResult::where('code', $code)
->where('draw_code', '<>', '')
->order('draw_time desc, issue desc')
->limit($limit)
->field('issue,draw_time,draw_code')
->select()
->toArray();
foreach ($rows as &$row) {
$row['numbers'] = $row['draw_code'] ? array_map('intval', explode(',', $row['draw_code'])) : [];
unset($row['draw_code']);
}
return $rows;
}
private static function ensureQuota(int $userId, string $analysisType, $refId, int $dbRefId): array
{
$analysisKey = AiQuotaService::makeKey($analysisType, $refId);
$quota = AiQuotaService::unlock($userId, $analysisType, $analysisKey, $dbRefId);
if (empty($quota['ok'])) {
return self::fail((string) ($quota['msg'] ?? '今日免费次数已用完'), (int) ($quota['code'] ?? 0), $quota);
}
return ['success' => true, 'quota' => $quota];
}
private static function withQuota(array $payload, array $quota): array
{
return array_merge(['success' => true], $payload, [
'remain_count' => $quota['remain'] ?? 0,
'unlocked' => $quota['unlocked'] ?? true,
'charged' => $quota['charged'] ?? false,
]);
}
private static function fail(string $error, int $code = 0, array $quota = []): array
{
$result = ['success' => false, 'error' => $error, 'code' => $code];
if (array_key_exists('remain', $quota)) {
$result['remain_count'] = $quota['remain'];
}
return $result;
}
private static function lotteryQuotaRef(string $code, string $latestIssue): string
{
return $latestIssue !== '' ? $code . ':' . $latestIssue : $code;
}
private static function crcRefId(string $value): int
{
return (int) sprintf('%u', crc32($value));
}
private static function getPostTags(int $postId): array
{
$tagIds = Db::name('community_post_tag')->where('post_id', $postId)->column('tag_id');
if (empty($tagIds)) {
return [];
}
return array_values(CommunityTag::whereIn('id', $tagIds)->column('name'));
}
private static function isTrumpPost(array $tags): bool
{
return in_array('特朗普', $tags, true);
}
private static function isLiuhePost(array $tags, array $ext = []): bool
{
foreach (self::LIUHE_TAGS as $tag) {
if (in_array($tag, $tags, true)) {
return true;
}
}
$lotteryKey = (string) ($ext['lottery_key'] ?? '');
return in_array($lotteryKey, ['a6', 'xa6'], true);
}
private static function localizeLiuhePostImagesForAi(CommunityPost $post, array $images): array
{
$localizedImages = [];
$changed = false;
foreach ($images as $image) {
$image = trim((string) $image);
if ($image === '') {
continue;
}
if (self::isLocalUploadImage($image)) {
$localUrl = self::normalizeLocalUploadImageUrl($image);
$localizedImages[] = $localUrl;
$changed = $changed || $localUrl !== $image;
continue;
}
if (!preg_match('/^https?:\/\//i', $image)) {
$localUrl = FileService::getFileUrl(ltrim($image, '/'));
$localizedImages[] = $localUrl;
$changed = $changed || $localUrl !== $image;
continue;
}
$localUrl = self::saveRemoteImageForAiAnalysis($image);
if ($localUrl === '') {
return ['success' => false, 'error' => '图片下载到本地失败,请稍后重试', 'images' => []];
}
$localizedImages[] = $localUrl;
$changed = true;
}
if (empty($localizedImages)) {
return ['success' => false, 'error' => '帖子图片为空,无法分析', 'images' => []];
}
if ($changed || $localizedImages !== array_values($images)) {
$now = time();
Db::name('community_post')->where('id', (int) $post->id)->update([
'images' => json_encode($localizedImages, JSON_UNESCAPED_UNICODE),
'update_time' => $now,
]);
Db::name('community_post_image')->where('post_id', (int) $post->id)->delete();
foreach ($localizedImages as $index => $imageUrl) {
Db::name('community_post_image')->insert([
'post_id' => (int) $post->id,
'image_url' => $imageUrl,
'sort' => $index,
'create_time' => $now,
]);
}
}
return ['success' => true, 'images' => $localizedImages, 'changed' => $changed];
}
private static function isLocalUploadImage(string $image): bool
{
if (!preg_match('/^https?:\/\//i', $image)) {
return str_starts_with(ltrim($image, '/'), 'uploads/');
}
$path = (string) (parse_url($image, PHP_URL_PATH) ?: '');
if (!str_starts_with($path, '/uploads/')) {
return false;
}
$imageHost = strtolower((string) (parse_url($image, PHP_URL_HOST) ?: ''));
$currentHost = strtolower((string) (parse_url(request()->domain(), PHP_URL_HOST) ?: ''));
return $imageHost !== '' && $currentHost !== '' && $imageHost === $currentHost;
}
private static function normalizeLocalUploadImageUrl(string $image): string
{
if (preg_match('/^https?:\/\//i', $image)) {
return $image;
}
return FileService::getFileUrl(ltrim($image, '/'));
}
private static function saveRemoteImageForAiAnalysis(string $imageUrl): string
{
$ch = curl_init($imageUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 25,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => 'Mozilla/5.0 SportEraBot/1.0',
]);
$body = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = (string) curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
if ($httpCode !== 200 || !is_string($body) || $body === '' || strlen($body) > 8 * 1024 * 1024) {
return '';
}
$mime = strtolower(trim(explode(';', $contentType)[0] ?? ''));
$imageInfo = @getimagesizefromstring($body);
if (is_array($imageInfo) && !empty($imageInfo['mime'])) {
$mime = strtolower((string) $imageInfo['mime']);
}
$extension = match ($mime) {
'image/jpeg', 'image/jpg' => 'jpg',
'image/png' => 'png',
'image/webp' => 'webp',
'image/gif' => 'gif',
default => '',
};
if ($extension === '') {
$path = parse_url($imageUrl, PHP_URL_PATH) ?: '';
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (!in_array($extension, ['jpg', 'jpeg', 'png', 'webp', 'gif'], true)) {
return '';
}
$extension = $extension === 'jpeg' ? 'jpg' : $extension;
}
$relativeDir = 'uploads/ai_lottery_posts/' . date('Ymd');
$publicRoot = rtrim(public_path(), DIRECTORY_SEPARATOR . '/');
$targetDir = $publicRoot . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relativeDir);
if (!is_dir($targetDir) && !@mkdir($targetDir, 0755, true) && !is_dir($targetDir)) {
return '';
}
if (!is_writable($targetDir)) {
return '';
}
$filename = sha1($imageUrl . "\n" . $body) . '.' . $extension;
$relativeUri = $relativeDir . '/' . $filename;
$targetPath = $targetDir . DIRECTORY_SEPARATOR . $filename;
if (!is_file($targetPath) && @file_put_contents($targetPath, $body) === false) {
return '';
}
return FileService::getFileUrl($relativeUri);
}
private static function ensureLiuheAnalysisContent(CommunityPost $post, array $ext): array
{
$images = is_array($post->images) ? array_values($post->images) : [];
$originalImageHash = md5(json_encode($images, JSON_UNESCAPED_UNICODE));
$localizeResult = self::localizeLiuhePostImagesForAi($post, $images);
if (empty($localizeResult['success'])) {
return [
'success' => false,
'error' => (string) ($localizeResult['error'] ?? '图片下载到本地失败'),
];
}
$images = array_values($localizeResult['images']);
$imageHash = md5(json_encode($images, JSON_UNESCAPED_UNICODE));
$cacheSource = (string) ($ext['lottery_analysis_source'] ?? '');
$cacheHash = (string) ($ext['lottery_analysis_images_hash'] ?? '');
if (
!empty($ext['lottery_analysis_content'])
&& $cacheSource === 'image_only'
&& in_array($cacheHash, [$imageHash, $originalImageHash], true)
) {
if ($cacheHash !== $imageHash) {
$ext['lottery_analysis_images_hash'] = $imageHash;
CommunityPost::where('id', (int) $post->id)->update([
'ext' => json_encode($ext, JSON_UNESCAPED_UNICODE),
'update_time' => time(),
]);
KbSyncService::enqueue('post', 'post', (int) $post->id, 'upsert', 45);
}
return ['success' => true, 'ext' => $ext, 'from_cache' => true];
}
$result = AiService::analyzeLotteryPostImages($post->content ?: '', $images);
if (empty($result['success']) || empty($result['content'])) {
$fallbackContent = self::buildLiuheImageAnalysisFallback($images, (string) ($result['error'] ?? ''));
if ($fallbackContent === '') {
return [
'success' => false,
'error' => (string) ($result['error'] ?? '帖子图片分析失败'),
];
}
$result = [
'success' => true,
'content' => $fallbackContent,
'tokens' => 0,
'cost_ms' => (int) ($result['cost_ms'] ?? 0),
];
}
$ext['lottery_analysis_content'] = trim((string) $result['content']);
$ext['lottery_analysis_source'] = 'image_only';
$ext['lottery_analysis_images_hash'] = $imageHash;
$ext['lottery_analysis_generated_at'] = time();
CommunityPost::where('id', (int) $post->id)->update([
'ext' => json_encode($ext, JSON_UNESCAPED_UNICODE),
'update_time' => time(),
]);
KbSyncService::enqueue('post', 'post', (int) $post->id, 'upsert', 45);
return ['success' => true, 'ext' => $ext, 'from_cache' => false];
}
private static function buildLiuheImageAnalysisFallback(array $images, string $error): string
{
$error = trim($error);
$isTimeout = $error !== '' && (stripos($error, 'timed out') !== false
|| stripos($error, 'timeout') !== false
|| stripos($error, 'cURL错误') !== false);
if (!$isTimeout) {
return '';
}
return implode("\n", [
'图片已下载并替换为站内链接,但视觉模型在限定时间内未返回完整识别结果。',
'当前可确认信息:帖子包含 ' . count($images) . ' 张本地化图片,可在原帖图片区域查看。',
'识别状态:图片文字、号码、生肖、波色等细项暂未可靠识别。',
'处理建议:以下分析只能结合帖子文案和图片展示场景进行保守解读,不能视为开奖结果或中奖承诺。',
]);
}
}