機能概要
- いいね機能の実装
- ユーザー共通設定機能
- ブラックリスト機能
- 電話番号変更機能
1. いいね統計表示
1.1 基本概念
- いいね:他のユーザーに対する評価(例:AがBにいいねしても、BがAにいいねしたとは限らない)
- フォロワー:自分をいいねしたユーザー
- 相互フォロー:お互いにいいねした状態
1.2 Dubboサービス
//UserLikeApi.java
public interface UserLikeApi {
/**
* 相互いいね数の取得
*/
Long getMutualFollowCount(Long userId);
/**
* いいね数の取得
*/
Long getFollowCount(Long userId);
/**
* フォロワー数の取得
*/
Long getFollowerCount(Long userId);
}
//UserLikeApiImpl.java
@Override
public Long getMutualFollowCount(Long userId) {
List<Long> followingList = queryFollowingList(userId);
Long mutualCount = 0L;
for (Long targetId : followingList) {
String redisKey = buildFollowKey(targetId);
if (redisTemplate.opsForHash().hasKey(redisKey, String.valueOf(userId))) {
mutualCount++;
}
}
return mutualCount;
}
@Override
public Long getFollowCount(Long userId) {
String redisKey = buildFollowKey(userId);
return redisTemplate.opsForHash().size(redisKey);
}
@Override
public Long getFollowerCount(Long userId) {
Query query = Query.query(Criteria.where("targetUserId").is(userId));
return mongoTemplate.count(query, FollowRelation.class);
}
1.3 API実装
//FollowStatsVo.java
@Data
public class FollowStatsVo {
private Long mutualCount;
private Long followingCount;
private Long followerCount;
}
//UserController.java
@GetMapping("/stats")
public ResponseEntity<FollowStatsVo> getFollowStats() {
try {
FollowStatsVo stats = userService.getFollowStats();
return ResponseEntity.ok(stats);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}
2. フォローリスト表示
2.1 フォロー機能のDubbo実装
//UserLikeApi.java
public interface UserLikeApi {
/**
* 相互フォローリスト取得
*/
PageInfo<FollowRelation> getMutualFollows(Long userId, Integer page, Integer size);
/**
* フォローリスト取得
*/
PageInfo<FollowRelation> getFollowingList(Long userId, Integer page, Integer size);
/**
* フォロワーリスト取得
*/
PageInfo<FollowRelation> getFollowerList(Long userId, Integer page, Integer size);
}
//UserLikeApiImpl.java
@Override
public PageInfo<FollowRelation> getMutualFollows(Long userId, Integer page, Integer size) {
List<Long> followingIds = queryFollowingList(userId);
Query query = Query.query(
Criteria.where("userId").in(followingIds)
.and("targetUserId").is(userId)
);
return executePaginatedQuery(query, page, size);
}
@Override
public PageInfo<FollowRelation> getFollowingList(Long userId, Integer page, Integer size) {
Query query = Query.query(Criteria.where("userId").is(userId));
return executePaginatedQuery(query, page, size);
}
@Override
public PageInfo<FollowRelation> getFollowerList(Long userId, Integer page, Integer size) {
Query query = Query.query(Criteria.where("targetUserId").is(userId));
return executePaginatedQuery(query, page, size);
}
2.2 訪問者リスト機能
//VisitorApi.java
public interface VisitorApi {
/**
* 最新訪問者リスト取得
*/
PageInfo<Visitor> getRecentVisitors(Long userId, Integer page, Integer size);
}
//VisitorApiImpl.java
@Override
public PageInfo<Visitor> getRecentVisitors(Long userId, Integer page, Integer size) {
PageRequest pagination = PageRequest.of(page - 1, size,
Sort.by(Sort.Direction.DESC, "visitTime"));
Query query = Query.query(Criteria.where("profileOwnerId").is(userId))
.with(pagination);
List<Visitor> visitors = mongoTemplate.find(query, Visitor.class);
enrichWithMatchScore(visitors, userId);
PageInfo<Visitor> result = new PageInfo<>();
result.setRecords(visitors);
result.setPageNum(page);
result.setPageSize(size);
updateLastViewTime(userId);
return result;
}
2.3 APIエンドポイント実装
//FollowListVo.java
@Data
public class FollowListVo {
private Long userId;
private String avatar;
private String nickname;
private String gender;
private Integer age;
private String location;
private String education;
private String maritalStatus;
private Integer matchScore;
private Boolean isFollowing;
}
//FollowController.java
@GetMapping("/list/{type}")
public ResponseEntity<PageResult> getFollowList(
@PathVariable Integer type,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) String keyword) {
try {
PageResult result = followService.getFollowList(type, page, size, keyword);
return ResponseEntity.ok(result);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}
2.4 フォロー解除機能
//FollowController.java
@DeleteMapping("/unfollow/{targetId}")
public ResponseEntity<Void> unfollowUser(@PathVariable Long targetId) {
try {
followService.unfollowUser(targetId);
return ResponseEntity.ok().build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}
//FollowService.java
public void unfollowUser(Long targetId) {
Long currentUserId = getCurrentUserId();
boolean wasMutual = followApi.isMutualFollow(currentUserId, targetId);
followApi.removeFollow(currentUserId, targetId);
if (wasMutual) {
chatService.removeFriend(targetId);
}
}
3. ユーザー設定機能
3.1 データベース構造
CREATE TABLE `user_preferences` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`user_id` BIGINT NOT NULL,
`like_notifications` TINYINT(1) DEFAULT 1,
`comment_notifications` TINYINT(1) DEFAULT 1,
`announcement_notifications` TINYINT(1) DEFAULT 1,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_user_id` (`user_id`)
);
3.2 エンティティクラス
//UserPreferences.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserPreferences {
private Long id;
private Long userId;
private Boolean likeNotifications = true;
private Boolean commentNotifications = true;
private Boolean announcementNotifications = true;
}
3.3 設定取得機能
//SettingsController.java
@GetMapping("/preferences")
public ResponseEntity<UserSettingsVo> getUserSettings() {
try {
UserSettingsVo settings = settingsService.getUserSettings();
return ResponseEntity.ok(settings);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}
//UserSettingsVo.java
@Data
public class UserSettingsVo {
private Long userId;
private String phoneNumber;
private String questionForStrangers = "";
private Boolean likeNotification = true;
private Boolean commentNotification = true;
private Boolean announcementNotification = true;
}
4. ブラックリスト機能
4.1 テーブル設計
CREATE TABLE `blocked_users` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`user_id` BIGINT NOT NULL,
`blocked_user_id` BIGINT NOT NULL,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_user_id` (`user_id`),
INDEX `idx_blocked_user_id` (`blocked_user_id`)
);
4.2 ブラックリスト管理
//BlockListController.java
@GetMapping("/blocked")
public ResponseEntity<PageResult> getBlockedList(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
try {
PageResult result = blockService.getBlockedList(page, size);
return ResponseEntity.ok(result);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}
@DeleteMapping("/blocked/{userId}")
public ResponseEntity<Void> unblockUser(@PathVariable Long userId) {
try {
blockService.unblockUser(userId);
return ResponseEntity.ok().build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}
5. 電話番号変更機能
5.1 認証コード送信
//PhoneUpdateController.java
@PostMapping("/verify-code/send")
public ResponseEntity<Void> sendVerifyCode(
@RequestHeader("Authorization") String token) {
try {
phoneUpdateService.sendVerificationCode(token);
return ResponseEntity.ok().build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}
5.2 認証コード検証
//PhoneUpdateController.java
@PostMapping("/verify-code/validate")
public ResponseEntity<Map<String, Object>> validateCode(
@RequestBody Map<String, String> request,
@RequestHeader("Authorization") String token) {
try {
String code = request.get("code");
boolean isValid = phoneUpdateService.validateVerificationCode(code, token);
Map<String, Object> response = Map.of("valid", isValid);
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}
5.3 電話番号更新
//PhoneUpdateController.java
@PutMapping("/phone-number")
public ResponseEntity<Void> updatePhoneNumber(
@RequestBody Map<String, String> request,
@RequestHeader("Authorization") String token) {
try {
String newPhone = request.get("phoneNumber");
phoneUpdateService.updatePhoneNumber(newPhone, token);
return ResponseEntity.ok().build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_ERROR).build();
}
}