1010import org .springframework .cache .annotation .CacheEvict ;
1111import org .springframework .cache .annotation .Cacheable ;
1212import org .springframework .stereotype .Service ;
13+ import org .springframework .transaction .annotation .Transactional ;
1314
14- import java .net .URLDecoder ;
15- import java .nio .charset .StandardCharsets ;
16- import java .util .List ;
17- import java .util .Optional ;
18- import java .util .UUID ;
15+ import java .util .*;
1916import java .util .stream .Collectors ;
2017
2118@ Service
@@ -35,63 +32,64 @@ public UserInterestService(UserInterestRepository userInterestRepository, IUserR
3532 @ Override
3633 @ Cacheable (value = "userInterests" , key = "#userId" )
3734 public List <String > getUserInterests (UUID userId ) {
38- List <UserInterest > interests = userInterestRepository .findByUserId (userId );
39- return interests .stream ()
35+ return userInterestRepository .findByUserId (userId ).stream ()
4036 .map (UserInterest ::getInterest )
4137 .collect (Collectors .toList ());
4238 }
4339
4440 @ Override
4541 @ CacheEvict (value = "userInterests" , key = "#userId" )
4642 public String addUserInterest (UUID userId , String interestName ) {
43+ String trimmed = interestName .trim ();
44+
45+ Optional <UserInterest > existing = userInterestRepository .findByUserIdAndInterestIgnoreCase (userId , trimmed );
46+ if (existing .isPresent ()) {
47+ return existing .get ().getInterest ();
48+ }
49+
4750 User user = userRepository .findById (userId )
4851 .orElseThrow (() -> new RuntimeException ("User not found with id: " + userId ));
4952
50- UserInterest userInterest = new UserInterest (user , interestName );
53+ UserInterest userInterest = new UserInterest (user , trimmed );
5154 userInterest = userInterestRepository .save (userInterest );
52-
5355 return userInterest .getInterest ();
5456 }
5557
5658 @ Override
5759 @ CacheEvict (value = "userInterests" , key = "#userId" )
58- public boolean removeUserInterest (UUID userId , String encodedInterestName ) {
59- try {
60- // URL decode the interest name to handle spaces and special characters
61- String decodedInterest = URLDecoder .decode (encodedInterestName , StandardCharsets .UTF_8 );
62-
63- logger .info ("Attempting to remove interest '" + decodedInterest + "' (encoded: '" + encodedInterestName + "') for user: " + LoggingUtils .formatUserIdInfo (userId ));
64-
65- // Debug: Log all existing interests for this user
66- List <UserInterest > allUserInterests = userInterestRepository .findByUserId (userId );
67- logger .info ("User " + LoggingUtils .formatUserIdInfo (userId ) + " currently has " + allUserInterests .size () + " interests:" );
68- for (UserInterest existingInterest : allUserInterests ) {
69- logger .info (" - '" + existingInterest .getInterest () + "' (length: " + existingInterest .getInterest ().length () + ")" );
70- }
71-
72- Optional <UserInterest > userInterestOpt = userInterestRepository .findByUserIdAndInterest (userId , decodedInterest );
73-
74- if (userInterestOpt .isPresent ()) {
75- userInterestRepository .delete (userInterestOpt .get ());
76- logger .info ("Successfully removed interest '" + decodedInterest + "' for user: " + LoggingUtils .formatUserIdInfo (userId ));
77- return true ;
78- } else {
79- logger .warn ("Interest '" + decodedInterest + "' not found for user: " + LoggingUtils .formatUserIdInfo (userId ));
80- logger .warn ("Exact search failed. Trying case-insensitive search..." );
81-
82- // Try case-insensitive search for debugging
83- for (UserInterest existingInterest : allUserInterests ) {
84- if (existingInterest .getInterest ().equalsIgnoreCase (decodedInterest )) {
85- logger .warn ("Found case-insensitive match: '" + existingInterest .getInterest () + "' vs '" + decodedInterest + "'" );
86- break ;
87- }
88- }
89-
90- return false ;
60+ public boolean removeUserInterest (UUID userId , String interestName ) {
61+ // Spring already URL-decodes @PathVariable, so no manual decoding needed.
62+ // Use case-insensitive lookup to be resilient to casing mismatches.
63+ Optional <UserInterest > userInterestOpt = userInterestRepository .findByUserIdAndInterestIgnoreCase (userId , interestName );
64+
65+ if (userInterestOpt .isPresent ()) {
66+ userInterestRepository .delete (userInterestOpt .get ());
67+ return true ;
68+ }
69+
70+ logger .warn ("Interest '" + interestName + "' not found for user: " + LoggingUtils .formatUserIdInfo (userId ));
71+ return false ;
72+ }
73+
74+ @ Override
75+ @ Transactional
76+ @ CacheEvict (value = "userInterests" , key = "#userId" )
77+ public List <String > replaceUserInterests (UUID userId , List <String > interests ) {
78+ User user = userRepository .findById (userId )
79+ .orElseThrow (() -> new RuntimeException ("User not found with id: " + userId ));
80+
81+ userInterestRepository .deleteAllByUserId (userId );
82+
83+ List <String > saved = new ArrayList <>();
84+ Set <String > seen = new HashSet <>();
85+ for (String interest : interests ) {
86+ String trimmed = interest .trim ();
87+ if (!trimmed .isEmpty () && seen .add (trimmed .toLowerCase ())) {
88+ UserInterest entity = new UserInterest (user , trimmed );
89+ userInterestRepository .save (entity );
90+ saved .add (trimmed );
9191 }
92- } catch (Exception e ) {
93- logger .error ("Error removing interest '" + encodedInterestName + "' for user: " + LoggingUtils .formatUserIdInfo (userId ) + ": " + e .getMessage ());
94- throw new RuntimeException ("Failed to remove user interest" , e );
9592 }
93+ return saved ;
9694 }
9795}
0 commit comments