diff --git a/backend/src/main/java/com/openisle/dto/AuthorDto.java b/backend/src/main/java/com/openisle/dto/AuthorDto.java index 6a2512fff..db1f31697 100644 --- a/backend/src/main/java/com/openisle/dto/AuthorDto.java +++ b/backend/src/main/java/com/openisle/dto/AuthorDto.java @@ -13,4 +13,5 @@ public class AuthorDto { private String username; private String avatar; private MedalType displayMedal; + private boolean bot; } diff --git a/backend/src/main/java/com/openisle/dto/UserDto.java b/backend/src/main/java/com/openisle/dto/UserDto.java index cf502c546..69547d284 100644 --- a/backend/src/main/java/com/openisle/dto/UserDto.java +++ b/backend/src/main/java/com/openisle/dto/UserDto.java @@ -28,4 +28,5 @@ public class UserDto { private int point; private int currentLevel; private int nextLevelExp; + private boolean bot; } diff --git a/backend/src/main/java/com/openisle/dto/UserSummaryDto.java b/backend/src/main/java/com/openisle/dto/UserSummaryDto.java index 8db045f79..6b3d93648 100644 --- a/backend/src/main/java/com/openisle/dto/UserSummaryDto.java +++ b/backend/src/main/java/com/openisle/dto/UserSummaryDto.java @@ -8,4 +8,5 @@ public class UserSummaryDto { private Long id; private String username; private String avatar; + private boolean bot; } diff --git a/backend/src/main/java/com/openisle/mapper/UserMapper.java b/backend/src/main/java/com/openisle/mapper/UserMapper.java index 99203bf2d..8b17d97e0 100644 --- a/backend/src/main/java/com/openisle/mapper/UserMapper.java +++ b/backend/src/main/java/com/openisle/mapper/UserMapper.java @@ -37,6 +37,7 @@ public class UserMapper { dto.setUsername(user.getUsername()); dto.setAvatar(user.getAvatar()); dto.setDisplayMedal(user.getDisplayMedal()); + dto.setBot(user.isBot()); return dto; } @@ -63,6 +64,7 @@ public class UserMapper { dto.setPoint(user.getPoint()); dto.setCurrentLevel(levelService.getLevel(user.getExperience())); dto.setNextLevelExp(levelService.nextLevelExp(user.getExperience())); + dto.setBot(user.isBot()); if (viewer != null) { dto.setSubscribed(subscriptionService.isSubscribed(viewer.getName(), user.getUsername())); } else { diff --git a/backend/src/main/java/com/openisle/model/User.java b/backend/src/main/java/com/openisle/model/User.java index bf68d5507..95a352d71 100644 --- a/backend/src/main/java/com/openisle/model/User.java +++ b/backend/src/main/java/com/openisle/model/User.java @@ -62,6 +62,9 @@ public class User { @Column(nullable = false) private Role role = Role.USER; + @Column(name = "is_bot", nullable = false) + private boolean bot = false; + @Enumerated(EnumType.STRING) private MedalType displayMedal; diff --git a/backend/src/main/java/com/openisle/service/ChannelService.java b/backend/src/main/java/com/openisle/service/ChannelService.java index 42c872cde..5fd6bd259 100644 --- a/backend/src/main/java/com/openisle/service/ChannelService.java +++ b/backend/src/main/java/com/openisle/service/ChannelService.java @@ -105,6 +105,7 @@ public class ChannelService { userDto.setId(message.getSender().getId()); userDto.setUsername(message.getSender().getUsername()); userDto.setAvatar(message.getSender().getAvatar()); + userDto.setBot(message.getSender().isBot()); dto.setSender(userDto); return dto; diff --git a/backend/src/main/java/com/openisle/service/MessageService.java b/backend/src/main/java/com/openisle/service/MessageService.java index 68ce3c3f9..087219f03 100644 --- a/backend/src/main/java/com/openisle/service/MessageService.java +++ b/backend/src/main/java/com/openisle/service/MessageService.java @@ -211,6 +211,7 @@ public class MessageService { userSummaryDto.setId(message.getSender().getId()); userSummaryDto.setUsername(message.getSender().getUsername()); userSummaryDto.setAvatar(message.getSender().getAvatar()); + userSummaryDto.setBot(message.getSender().isBot()); dto.setSender(userSummaryDto); if (message.getReplyTo() != null) { @@ -222,6 +223,7 @@ public class MessageService { replySender.setId(reply.getSender().getId()); replySender.setUsername(reply.getSender().getUsername()); replySender.setAvatar(reply.getSender().getAvatar()); + replySender.setBot(reply.getSender().isBot()); replyDto.setSender(replySender); dto.setReplyTo(replyDto); } @@ -316,6 +318,7 @@ public class MessageService { userDto.setId(p.getUser().getId()); userDto.setUsername(p.getUser().getUsername()); userDto.setAvatar(p.getUser().getAvatar()); + userDto.setBot(p.getUser().isBot()); return userDto; }) .collect(Collectors.toList()) @@ -365,6 +368,7 @@ public class MessageService { userDto.setId(p.getUser().getId()); userDto.setUsername(p.getUser().getUsername()); userDto.setAvatar(p.getUser().getAvatar()); + userDto.setBot(p.getUser().isBot()); return userDto; }) .collect(Collectors.toList()); diff --git a/backend/src/main/resources/db/init/01_schema.sql b/backend/src/main/resources/db/init/01_schema.sql index 87e1e22da..f42ba790b 100644 --- a/backend/src/main/resources/db/init/01_schema.sql +++ b/backend/src/main/resources/db/init/01_schema.sql @@ -20,6 +20,7 @@ CREATE TABLE IF NOT EXISTS `users` ( `username` varchar(50) NOT NULL, `verification_code` varchar(255) DEFAULT NULL, `verified` bit(1) DEFAULT NULL, + `is_bot` bit(1) NOT NULL DEFAULT b'0', PRIMARY KEY (`id`), UNIQUE KEY `UK_users_email` (`email`), UNIQUE KEY `UK_users_username` (`username`) diff --git a/backend/src/main/resources/db/init/02_seed_data.sql b/backend/src/main/resources/db/init/02_seed_data.sql index f3f375b7a..6d8ccd6b9 100644 --- a/backend/src/main/resources/db/init/02_seed_data.sql +++ b/backend/src/main/resources/db/init/02_seed_data.sql @@ -8,10 +8,28 @@ DELETE FROM `users`; -- 插入用户,两个普通用户,一个管理员 -- username:admin/user1/user2 password:123456 -INSERT INTO `users` (`id`, `approved`, `avatar`, `created_at`, `display_medal`, `email`, `experience`, `introduction`, `password`, `password_reset_code`, `point`, `register_reason`, `role`, `username`, `verification_code`, `verified`) VALUES -(1, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-01 16:08:17.426430', 'PIONEER', 'adminmail@openisle.com', 70, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 110, '测试测试测试……', 'ADMIN', 'admin', NULL, b'1'), -(2, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-03 16:08:17.426430', 'PIONEER', 'usermail2@openisle.com', 70, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 110, '测试测试测试……', 'USER', 'user1', NULL, b'1'), -(3, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-02 17:21:21.617666', 'PIONEER', 'usermail1@openisle.com', 40, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 40, '测试测试测试……', 'USER', 'user2', NULL, b'1'); +INSERT INTO `users` ( + `id`, + `approved`, + `avatar`, + `created_at`, + `display_medal`, + `email`, + `experience`, + `introduction`, + `password`, + `password_reset_code`, + `point`, + `register_reason`, + `role`, + `username`, + `verification_code`, + `verified`, + `is_bot` +) VALUES +(1, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-01 16:08:17.426430', 'PIONEER', 'adminmail@openisle.com', 70, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 110, '测试测试测试……', 'ADMIN', 'admin', NULL, b'1', b'0'), +(2, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-03 16:08:17.426430', 'PIONEER', 'usermail2@openisle.com', 70, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 110, '测试测试测试……', 'USER', 'user1', NULL, b'1', b'0'), +(3, b'1', 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png', '2025-09-02 17:21:21.617666', 'PIONEER', 'usermail1@openisle.com', 40, NULL, '$2a$10$x7HXjUyJTmrvqjnBlBQZH.vmfsC56NzTSWqQ6WqZqRjUO859EhviS', NULL, 40, '测试测试测试……', 'USER', 'user2', NULL, b'1', b'0'); INSERT INTO `categories` (`id`,`description`,`icon`,`name`,`small_icon`) VALUES (1,'测试用分类1','star','测试用分类1',NULL), diff --git a/backend/src/main/resources/db/migration/V8__add_is_bot_to_users.sql b/backend/src/main/resources/db/migration/V8__add_is_bot_to_users.sql new file mode 100644 index 000000000..56b3201aa --- /dev/null +++ b/backend/src/main/resources/db/migration/V8__add_is_bot_to_users.sql @@ -0,0 +1,2 @@ +ALTER TABLE users +ADD COLUMN is_bot BIT(1) NOT NULL DEFAULT b'0'; diff --git a/frontend_nuxt/components/CommentItem.vue b/frontend_nuxt/components/CommentItem.vue index 34a7e703d..8a8adb0aa 100644 --- a/frontend_nuxt/components/CommentItem.vue +++ b/frontend_nuxt/components/CommentItem.vue @@ -16,6 +16,7 @@
{{ comment.userName }} OP + Bot { line-height: 1; } +.bot-badge { + display: inline-flex; + align-items: center; + justify-content: center; + margin-left: 6px; + padding: 0 6px; + height: 18px; + border-radius: 9px; + background-color: rgba(76, 175, 80, 0.16); + color: #2e7d32; + font-size: 12px; + font-weight: 600; + line-height: 1; +} + .medal-icon { font-size: 12px; opacity: 0.6; diff --git a/frontend_nuxt/pages/posts/[id]/index.vue b/frontend_nuxt/pages/posts/[id]/index.vue index 234393c26..85cb3c637 100644 --- a/frontend_nuxt/pages/posts/[id]/index.vue +++ b/frontend_nuxt/pages/posts/[id]/index.vue @@ -377,6 +377,7 @@ const mapComment = ( text: c.content, reactions: c.reactions || [], pinned: Boolean(c.pinned ?? c.pinnedAt ?? c.pinned_at), + isBot: Boolean(c.author?.bot), reply: (c.replies || []).map((r) => mapComment(r, c.author.username, c.author.avatar, c.author.id, level + 1), ),