Compare commits

...

1 Commits

Author SHA1 Message Date
Tim
512e5623e1 Add bot flag to users and surface in comments 2025-10-28 19:49:33 +08:00
12 changed files with 55 additions and 4 deletions

View File

@@ -13,4 +13,5 @@ public class AuthorDto {
private String username; private String username;
private String avatar; private String avatar;
private MedalType displayMedal; private MedalType displayMedal;
private boolean bot;
} }

View File

@@ -28,4 +28,5 @@ public class UserDto {
private int point; private int point;
private int currentLevel; private int currentLevel;
private int nextLevelExp; private int nextLevelExp;
private boolean bot;
} }

View File

@@ -8,4 +8,5 @@ public class UserSummaryDto {
private Long id; private Long id;
private String username; private String username;
private String avatar; private String avatar;
private boolean bot;
} }

View File

@@ -37,6 +37,7 @@ public class UserMapper {
dto.setUsername(user.getUsername()); dto.setUsername(user.getUsername());
dto.setAvatar(user.getAvatar()); dto.setAvatar(user.getAvatar());
dto.setDisplayMedal(user.getDisplayMedal()); dto.setDisplayMedal(user.getDisplayMedal());
dto.setBot(user.isBot());
return dto; return dto;
} }
@@ -63,6 +64,7 @@ public class UserMapper {
dto.setPoint(user.getPoint()); dto.setPoint(user.getPoint());
dto.setCurrentLevel(levelService.getLevel(user.getExperience())); dto.setCurrentLevel(levelService.getLevel(user.getExperience()));
dto.setNextLevelExp(levelService.nextLevelExp(user.getExperience())); dto.setNextLevelExp(levelService.nextLevelExp(user.getExperience()));
dto.setBot(user.isBot());
if (viewer != null) { if (viewer != null) {
dto.setSubscribed(subscriptionService.isSubscribed(viewer.getName(), user.getUsername())); dto.setSubscribed(subscriptionService.isSubscribed(viewer.getName(), user.getUsername()));
} else { } else {

View File

@@ -62,6 +62,9 @@ public class User {
@Column(nullable = false) @Column(nullable = false)
private Role role = Role.USER; private Role role = Role.USER;
@Column(name = "is_bot", nullable = false)
private boolean bot = false;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private MedalType displayMedal; private MedalType displayMedal;

View File

@@ -105,6 +105,7 @@ public class ChannelService {
userDto.setId(message.getSender().getId()); userDto.setId(message.getSender().getId());
userDto.setUsername(message.getSender().getUsername()); userDto.setUsername(message.getSender().getUsername());
userDto.setAvatar(message.getSender().getAvatar()); userDto.setAvatar(message.getSender().getAvatar());
userDto.setBot(message.getSender().isBot());
dto.setSender(userDto); dto.setSender(userDto);
return dto; return dto;

View File

@@ -211,6 +211,7 @@ public class MessageService {
userSummaryDto.setId(message.getSender().getId()); userSummaryDto.setId(message.getSender().getId());
userSummaryDto.setUsername(message.getSender().getUsername()); userSummaryDto.setUsername(message.getSender().getUsername());
userSummaryDto.setAvatar(message.getSender().getAvatar()); userSummaryDto.setAvatar(message.getSender().getAvatar());
userSummaryDto.setBot(message.getSender().isBot());
dto.setSender(userSummaryDto); dto.setSender(userSummaryDto);
if (message.getReplyTo() != null) { if (message.getReplyTo() != null) {
@@ -222,6 +223,7 @@ public class MessageService {
replySender.setId(reply.getSender().getId()); replySender.setId(reply.getSender().getId());
replySender.setUsername(reply.getSender().getUsername()); replySender.setUsername(reply.getSender().getUsername());
replySender.setAvatar(reply.getSender().getAvatar()); replySender.setAvatar(reply.getSender().getAvatar());
replySender.setBot(reply.getSender().isBot());
replyDto.setSender(replySender); replyDto.setSender(replySender);
dto.setReplyTo(replyDto); dto.setReplyTo(replyDto);
} }
@@ -316,6 +318,7 @@ public class MessageService {
userDto.setId(p.getUser().getId()); userDto.setId(p.getUser().getId());
userDto.setUsername(p.getUser().getUsername()); userDto.setUsername(p.getUser().getUsername());
userDto.setAvatar(p.getUser().getAvatar()); userDto.setAvatar(p.getUser().getAvatar());
userDto.setBot(p.getUser().isBot());
return userDto; return userDto;
}) })
.collect(Collectors.toList()) .collect(Collectors.toList())
@@ -365,6 +368,7 @@ public class MessageService {
userDto.setId(p.getUser().getId()); userDto.setId(p.getUser().getId());
userDto.setUsername(p.getUser().getUsername()); userDto.setUsername(p.getUser().getUsername());
userDto.setAvatar(p.getUser().getAvatar()); userDto.setAvatar(p.getUser().getAvatar());
userDto.setBot(p.getUser().isBot());
return userDto; return userDto;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@@ -20,6 +20,7 @@ CREATE TABLE IF NOT EXISTS `users` (
`username` varchar(50) NOT NULL, `username` varchar(50) NOT NULL,
`verification_code` varchar(255) DEFAULT NULL, `verification_code` varchar(255) DEFAULT NULL,
`verified` bit(1) DEFAULT NULL, `verified` bit(1) DEFAULT NULL,
`is_bot` bit(1) NOT NULL DEFAULT b'0',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `UK_users_email` (`email`), UNIQUE KEY `UK_users_email` (`email`),
UNIQUE KEY `UK_users_username` (`username`) UNIQUE KEY `UK_users_username` (`username`)

View File

@@ -8,10 +8,28 @@ DELETE FROM `users`;
-- 插入用户,两个普通用户,一个管理员 -- 插入用户,两个普通用户,一个管理员
-- username:admin/user1/user2 password:123456 -- 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 INSERT INTO `users` (
(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'), `id`,
(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'), `approved`,
(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'); `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 INSERT INTO `categories` (`id`,`description`,`icon`,`name`,`small_icon`) VALUES
(1,'测试用分类1','star','测试用分类1',NULL), (1,'测试用分类1','star','测试用分类1',NULL),

View File

@@ -0,0 +1,2 @@
ALTER TABLE users
ADD COLUMN is_bot BIT(1) NOT NULL DEFAULT b'0';

View File

@@ -16,6 +16,7 @@
<div class="info-content-header-left"> <div class="info-content-header-left">
<span class="user-name">{{ comment.userName }}</span> <span class="user-name">{{ comment.userName }}</span>
<span v-if="isCommentFromPostAuthor" class="op-badge" title="楼主">OP</span> <span v-if="isCommentFromPostAuthor" class="op-badge" title="楼主">OP</span>
<span v-if="comment.isBot" class="bot-badge" title="Bot">Bot</span>
<medal-one class="medal-icon" /> <medal-one class="medal-icon" />
<NuxtLink <NuxtLink
v-if="comment.medal" v-if="comment.medal"
@@ -522,6 +523,21 @@ const handleContentClick = (e) => {
line-height: 1; 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 { .medal-icon {
font-size: 12px; font-size: 12px;
opacity: 0.6; opacity: 0.6;

View File

@@ -377,6 +377,7 @@ const mapComment = (
text: c.content, text: c.content,
reactions: c.reactions || [], reactions: c.reactions || [],
pinned: Boolean(c.pinned ?? c.pinnedAt ?? c.pinned_at), pinned: Boolean(c.pinned ?? c.pinnedAt ?? c.pinned_at),
isBot: Boolean(c.author?.bot),
reply: (c.replies || []).map((r) => reply: (c.replies || []).map((r) =>
mapComment(r, c.author.username, c.author.avatar, c.author.id, level + 1), mapComment(r, c.author.username, c.author.avatar, c.author.id, level + 1),
), ),