diff --git a/docker/crawler/ai_comment_dispatch.py b/docker/crawler/ai_comment_dispatch.py
index 56ffb9d..2301bd2 100644
--- a/docker/crawler/ai_comment_dispatch.py
+++ b/docker/crawler/ai_comment_dispatch.py
@@ -25,7 +25,40 @@ DEFAULT_REQUEST_TIMEOUT = 180
DEFAULT_RETRY_TIMES = 2
DEFAULT_RETRY_SLEEP_MS = 800
BOT_ACCOUNT_PREFIX = "ai_comment_"
-BOT_NICKNAME_PREFIX = "AI评论员"
+BOT_HANDLE_PREFIXES = [
+ "看台",
+ "深夜",
+ "边线",
+ "北区",
+ "南看台",
+ "东门",
+ "西街",
+ "慢热",
+ "小酒馆",
+ "补时",
+ "角旗",
+ "传中",
+ "临场",
+ "半场",
+ "球门后",
+ "旧球鞋",
+ "赛点",
+ "风向",
+ "追球",
+ "回防",
+]
+BOT_HANDLE_SUFFIXES = [
+ "阿泽",
+ "小顾",
+ "老韩",
+ "阿凯",
+ "小唐",
+ "阿林",
+ "阿松",
+ "阿北",
+ "阿岳",
+ "阿诚",
+]
DEFAULT_ARTICLE_PROMPT = (
"你是一名专业、克制、真实的体育资讯评论员。请根据给定资讯内容生成1条中文评论,"
@@ -592,7 +625,7 @@ class AiCommentRepository:
for index in range(1, target_count + 1):
persona = self._persona_for_index(index)
account = f"{BOT_ACCOUNT_PREFIX}{index:04d}"
- nickname = self._build_bot_nickname(index, persona["label"])
+ nickname = self._build_bot_nickname(index)
row = indexed.get(index)
if row:
if row["nickname"] != nickname:
@@ -618,7 +651,7 @@ class AiCommentRepository:
for row in rows:
index = self._parse_account_index(row["account"])
persona = self._persona_for_index(index)
- users.append(VirtualUser(int(row["id"]), row["account"], self._build_bot_nickname(index, persona["label"]), persona["key"]))
+ users.append(VirtualUser(int(row["id"]), row["account"], self._build_bot_nickname(index), persona["key"]))
self._virtual_users_cache = list(users)
return users[:target_count]
@@ -991,9 +1024,11 @@ class AiCommentRepository:
return hashlib.md5(str(random.random()).encode("utf-8")).hexdigest()[:12]
@staticmethod
- def _build_bot_nickname(index: int, persona_label: str) -> str:
- number = f"{index:02d}" if index < 100 else str(index)
- return f"{BOT_NICKNAME_PREFIX}{number}·{persona_label}"
+ def _build_bot_nickname(index: int) -> str:
+ zero_based = max(0, index - 1)
+ prefix = BOT_HANDLE_PREFIXES[zero_based % len(BOT_HANDLE_PREFIXES)]
+ suffix = BOT_HANDLE_SUFFIXES[(zero_based // len(BOT_HANDLE_PREFIXES)) % len(BOT_HANDLE_SUFFIXES)]
+ return f"{prefix}{suffix}"
@staticmethod
def _persona_for_index(index: int) -> Dict[str, str]:
diff --git a/docker/crawler/tests/test_ai_comment_dispatch.py b/docker/crawler/tests/test_ai_comment_dispatch.py
index 0314929..33e19fc 100644
--- a/docker/crawler/tests/test_ai_comment_dispatch.py
+++ b/docker/crawler/tests/test_ai_comment_dispatch.py
@@ -10,6 +10,7 @@ if str(ROOT) not in sys.path:
from ai_comment_dispatch import (
AiCommentDispatcher,
AiCommentSettings,
+ AiCommentRepository,
CommentCandidate,
DispatchTask,
VirtualUser,
@@ -209,6 +210,13 @@ class AiCommentDispatchTest(unittest.TestCase):
self.assertEqual(settings.max_tokens, 300)
self.assertAlmostEqual(settings.temperature, 0.65)
+ def test_generated_bot_nickname_uses_normal_handle(self):
+ nickname = AiCommentRepository._build_bot_nickname(17)
+
+ self.assertNotIn("AI评论员", nickname)
+ self.assertNotIn("·", nickname)
+ self.assertGreaterEqual(len(nickname), 3)
+
if __name__ == "__main__":
unittest.main()
diff --git a/server/app/adminapi/lists/community/CommentAccountLists.php b/server/app/adminapi/lists/community/CommentAccountLists.php
index 1e01848..85afad0 100644
--- a/server/app/adminapi/lists/community/CommentAccountLists.php
+++ b/server/app/adminapi/lists/community/CommentAccountLists.php
@@ -9,6 +9,21 @@ use think\facade\Db;
class CommentAccountLists extends BaseAdminDataLists implements ListsSearchInterface
{
+ protected const PERSONA_LABELS = [
+ '数据派',
+ '老球迷',
+ '教练视角',
+ '夜班编辑',
+ '伤停控',
+ '赔率派',
+ '更衣室派',
+ '球探派',
+ '解说腔',
+ '主队党',
+ '模型党',
+ '资讯控',
+ ];
+
public function setSearch(): array
{
return [];
@@ -61,7 +76,7 @@ class CommentAccountLists extends BaseAdminDataLists implements ListsSearchInter
}
foreach ($lists as &$item) {
- $item['persona_label'] = $this->resolvePersonaLabel((string) $item['nickname']);
+ $item['persona_label'] = $this->resolvePersonaLabel((string) $item['account'], (string) $item['nickname']);
$item['article_comment_count'] = $articleCounts[(int) $item['id']] ?? 0;
$item['community_comment_count'] = $communityCounts[(int) $item['id']] ?? 0;
$item['total_comment_count'] = $item['article_comment_count'] + $item['community_comment_count'];
@@ -85,12 +100,16 @@ class CommentAccountLists extends BaseAdminDataLists implements ListsSearchInter
return $query->count();
}
- protected function resolvePersonaLabel(string $nickname): string
+ protected function resolvePersonaLabel(string $account, string $nickname): string
{
if (str_contains($nickname, '·')) {
$parts = explode('·', $nickname, 2);
return $parts[1] ?: '-';
}
+ if (preg_match('/(\d+)$/', $account, $matches)) {
+ $index = max(1, (int) $matches[1]);
+ return self::PERSONA_LABELS[($index - 1) % count(self::PERSONA_LABELS)] ?? '-';
+ }
return '-';
}
}
diff --git a/server/app/common/service/article/ArticleAiCommentService.php b/server/app/common/service/article/ArticleAiCommentService.php
index 316f04c..985646e 100644
--- a/server/app/common/service/article/ArticleAiCommentService.php
+++ b/server/app/common/service/article/ArticleAiCommentService.php
@@ -13,8 +13,41 @@ use think\facade\Db;
class ArticleAiCommentService
{
private const VIRTUAL_ACCOUNT_PREFIX = 'ai_comment_';
- private const VIRTUAL_NICKNAME_PREFIX = 'AI评论员';
private const VIRTUAL_POOL_SIZE = 20;
+ private const VIRTUAL_NICKNAME_PREFIXES = [
+ '看台',
+ '深夜',
+ '边线',
+ '北区',
+ '南看台',
+ '东门',
+ '西街',
+ '慢热',
+ '小酒馆',
+ '补时',
+ '角旗',
+ '传中',
+ '临场',
+ '半场',
+ '球门后',
+ '旧球鞋',
+ '赛点',
+ '风向',
+ '追球',
+ '回防',
+ ];
+ private const VIRTUAL_NICKNAME_SUFFIXES = [
+ '阿泽',
+ '小顾',
+ '老韩',
+ '阿凯',
+ '小唐',
+ '阿林',
+ '阿松',
+ '阿北',
+ '阿岳',
+ '阿诚',
+ ];
public static function tick(int $seedLimit = 10, int $processLimit = 20): array
{
@@ -158,12 +191,32 @@ class ArticleAiCommentService
protected static function ensureVirtualUsers(int $targetCount = self::VIRTUAL_POOL_SIZE): array
{
- $existing = User::where('account', 'like', self::VIRTUAL_ACCOUNT_PREFIX . '%')
+ $existingRows = User::where('account', 'like', self::VIRTUAL_ACCOUNT_PREFIX . '%')
->order('id', 'asc')
- ->column('id');
+ ->field('id,account,nickname')
+ ->select()
+ ->toArray();
- $existing = array_values(array_map('intval', $existing));
+ $existing = [];
+ foreach ($existingRows as $row) {
+ $existing[] = (int) $row['id'];
+ }
$missing = max(0, $targetCount - count($existing));
+ $now = time();
+ foreach ($existingRows as $row) {
+ $index = self::parseAccountIndex((string) ($row['account'] ?? ''));
+ if ($index <= 0) {
+ continue;
+ }
+ $expectedNickname = self::buildVirtualNickname($index);
+ if (($row['nickname'] ?? '') !== $expectedNickname) {
+ User::update([
+ 'id' => (int) $row['id'],
+ 'nickname' => $expectedNickname,
+ 'update_time' => $now,
+ ]);
+ }
+ }
if ($missing > 0) {
$avatar = (string) Config::get('project.default_image.user_avatar', '');
$passwordSalt = (string) Config::get('project.unique_identification', 'sport-era');
@@ -177,7 +230,7 @@ class ArticleAiCommentService
'sn' => $sn,
'avatar' => $avatar,
'real_name' => '',
- 'nickname' => self::VIRTUAL_NICKNAME_PREFIX . str_pad((string) $accountIndex, 2, '0', STR_PAD_LEFT),
+ 'nickname' => self::buildVirtualNickname($accountIndex),
'account' => self::VIRTUAL_ACCOUNT_PREFIX . str_pad((string) $accountIndex, 4, '0', STR_PAD_LEFT),
'password' => $password,
'mobile' => '',
@@ -196,6 +249,22 @@ class ArticleAiCommentService
return $existing;
}
+ protected static function buildVirtualNickname(int $index): string
+ {
+ $zeroBased = max(0, $index - 1);
+ $prefix = self::VIRTUAL_NICKNAME_PREFIXES[$zeroBased % count(self::VIRTUAL_NICKNAME_PREFIXES)];
+ $suffix = self::VIRTUAL_NICKNAME_SUFFIXES[intdiv($zeroBased, count(self::VIRTUAL_NICKNAME_PREFIXES)) % count(self::VIRTUAL_NICKNAME_SUFFIXES)];
+ return $prefix . $suffix;
+ }
+
+ protected static function parseAccountIndex(string $account): int
+ {
+ if (!preg_match('/(\d+)$/', $account, $matches)) {
+ return 0;
+ }
+ return (int) $matches[1];
+ }
+
protected static function resolveTaskCount(array $article): int
{
$commentCount = (int) ($article['comment_count'] ?? 0);
diff --git a/uniapp/src/packages_lottery/pages/ai_predict.vue b/uniapp/src/packages_lottery/pages/ai_predict.vue
index f1d8fc5..491eee4 100644
--- a/uniapp/src/packages_lottery/pages/ai_predict.vue
+++ b/uniapp/src/packages_lottery/pages/ai_predict.vue
@@ -166,102 +166,6 @@
-
-
-
-
-
-
- 🔴🔵🟢 波色统计
-
-
- 红波
- {{
- modules.colorZodiac.data.color_analysis.red.count }}次
- {{
- modules.colorZodiac.data.color_analysis.red.percentage }}
-
-
- 蓝波
- {{
- modules.colorZodiac.data.color_analysis.blue.count }}次
- {{
- modules.colorZodiac.data.color_analysis.blue.percentage }}
-
-
- 绿波
- {{
- modules.colorZodiac.data.color_analysis.green.count }}次
- {{
- modules.colorZodiac.data.color_analysis.green.percentage }}
-
-
-
- {{ colorZodiacTyped.colorText }}
- |
-
-
-
-
- 🐲 生肖分析
-
- 热门生肖
-
- {{ z }}
-
-
-
- 冷门生肖
-
- {{ z }}
-
-
-
- {{ colorZodiacTyped.zodiacText }}
- |
-
-
-
-
- 💡 推荐
-
- 推荐波色
-
- {{ c }}
-
-
-
- 推荐生肖
-
- {{ z }}
-
-
-
- {{ colorZodiacTyped.reasonText }}
- |
-
-
-
-
-