Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat add follow, counts-and-status- related functions #2

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

becky200605
Copy link
Collaborator

@becky200605 becky200605 commented Feb 7, 2025

Summary by CodeRabbit

  • New Features

    • Added endpoints for user logout, profile updates (background, picture, password, email, username), and follow/unfollow actions.
    • Introduced token retrieval and status-check endpoints for posts, comments, and replies.
    • Enhanced management of unread messages (like, comment, follow) and updated user statistics (like, follower, followee counts).
  • Documentation

    • Updated API documentation to reflect new endpoints and revised response schemas.

Copy link

coderabbitai bot commented Feb 7, 2025

Walkthrough

This update revises file exclusions in .gitignore, refactors controller methods to streamline API endpoints (for likes, follows, comments, tokens, and user profile updates), and modernizes DAO and service layers by replacing concrete types with interfaces. In addition, new API endpoints and documentation updates have been introduced, data models have been extended or adjusted, and several files now support dependency injection via Wire. Other changes include improvements to JWT token management and overall initialization processes.

Changes

File(s) Change Summary
.gitignore Added entries “KnowEase1_linux_amd64” and “config.yaml”; removed “.env”.
controllers/like.go, controllers/post.go, controllers/user.go Refactored service calls and message formatting; updated endpoints; added methods for like, follow/unfollow, token retrieval, and user profile management.
controllers/wire.go Added Wire ProviderSet for controller dependency injection.
dao/dao.go, dao/interface.go, dao/likes.go, dao/post.go, dao/user.go, dao/wire.go Updated migration (using FollowMessage), introduced DAO interfaces and provider functions, enhanced syncing/query methods, added user update operations, and provided a Wire ProviderSet for DAOs.
docs/docs.go, docs/swagger.json, docs/swagger.yaml Added/modified API endpoints for token, logout, like, follow, and comment messages; revised response schemas and expanded user model definitions.
go.mod Added new indirect dependencies (e.g., toml, jwt-go, google/wire, difflib, qiniu, golang.org/x/mod, x/sync).
main.go Introduced ProvideDBConnectionString and refactored initialization via InitializeApp (removing direct DAO instantiation and count updates).
middleware/middleware.go Removed a trailing blank line; no functional changes.
models/likes.go, models/post.go, models/user.go Removed the UserLikes struct; updated GORM tags and corrected typos; added new fields to Message and User; introduced QiNiuYunConfig and FollowMessage structs.
routes/post.go, routes/userpage.go, routes/wire.go Added new GET and POST endpoints for token, status, user profile, and follow actions; updated service injection for user controllers; defined a Wire ProviderSet for routes.
services/email.go, services/like.go, services/post.go, services/token.go, services/user.go, services/wire.go Refactored services to use DAO interfaces; added methods for token invalidation, follow/unfollow, like and status checks, post retrieval, configuration reading, and user profile updates; included a Wire ProviderSet for service instantiation.
wire.go, wire_gen.go Introduced Wire dependency injection setup; defined and generated InitializeApp to aggregate DAOs, services, controllers, and middleware into the application instance.

Sequence Diagram(s)

sequenceDiagram
  participant Client as C
  participant Router as R
  participant PostCtrl as PC
  participant PostSvc as PS
  participant Config as CF

  C->>R: GET /api/getToken
  R->>PC: Handle GetToken request
  PC->>PS: Invoke GetToken()
  PS->>CF: Read configuration file
  CF-->>PS: Return configuration data
  PS->>PS: Generate upload token
  PS-->>PC: Return token
  PC-->>R: Send token response
  R-->>C: Deliver token
Loading
sequenceDiagram
  participant Client as C
  participant Router as R
  participant UserCtrl as UC
  participant TokenSvc as TS

  C->>R: POST /api/logout (with token)
  R->>UC: Process Logout request
  UC->>TS: Call InvalidateToken(token)
  TS-->>UC: Return invalidated token/info
  UC-->>R: Send logout response
  R-->>C: Confirm logout success
Loading

Poem

I'm a little rabbit with ears so keen,
Hopping through code changes so neat and clean.
New endpoints bloom like carrots in spring,
With tokens and follows that make our app sing.
I nibble on bugs and munch on refactored code,
Celebrating progress on this adventurous road!
🥕 Keep on hopping through every update!

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🔭 Outside diff range comments (2)
models/post.go (1)

14-14: Fix typo in GORM tag.

There's a typo in the GORM tag for ImageURL field (grom instead of gorm).

-	ImageURL   string    `grom:"type:varchar(255)" json:"urls"`
+	ImageURL   string    `gorm:"type:varchar(255)" json:"urls"`
services/email.go (1)

72-91: Enhance email security and configuration.

The email configuration should be more secure and configurable.

 func (es *EmailService) SendEmail(email, code string) error {
+    sender := os.Getenv("EMAIL_SENDER")
+    if sender == "" {
+        return fmt.Errorf("email sender not configured")
+    }
     message := gomail.NewMessage()
-    message.SetHeader("From", "[email protected]")
+    message.SetHeader("From", sender)
     message.SetHeader("To", email)
-    message.SetHeader("Subject", "小知账号注册")
+    message.SetHeader("Subject", os.Getenv("EMAIL_SUBJECT"))
     password := os.Getenv("EMAIL_PASSWORD")
     if password == "" {
         return fmt.Errorf("未设置邮件授权码,请检查环境变量")
     }
-    message.SetBody("text/plain", "【小知创想家】您的验证码"+code+"该验证码5分钟内有效,请勿泄露于他人!")
-    dialer := gomail.NewDialer("smtp.qq.com", 587, "[email protected]", password)
+    template := os.Getenv("EMAIL_TEMPLATE")
+    message.SetBody("text/plain", fmt.Sprintf(template, code))
+    dialer := gomail.NewDialer(
+        os.Getenv("SMTP_HOST"),
+        587,
+        sender,
+        password,
+    )
🧹 Nitpick comments (26)
services/post.go (2)

180-185: Error handling improvement suggestion.
Currently, errors are only printed. Consider returning or logging them more thoroughly higher up to ensure issues aren't silently swallowed.


201-215: Token generation correctness.
Generates an upload token for Qiniu Cloud storage. Error handling is correct, but the nil return on line 213 might be a small oversight; could return the error for consistency.

dao/likes.go (2)

217-225: SearchCommentByID for commenter info.
The triple return (commenterID, body, error) is consistent, though consider returning a struct for clarity.


227-235: SearchReplyByID for replyer info.
Same pattern as SearchCommentByID. Returning a struct could improve maintainability.

services/like.go (5)

32-32: Debug print statement.
Using fmt.Print for debugging is okay short-term. Consider structured logging if you need more permanent logs.


415-419: GetUserFolower method name.
Small naming nit: consider “GetUserFollower” for consistency. Currently it fetches the followees, so be sure it's intended.


460-476: GetUserFoloweeList logic.
Retrieves the followees by ID, then fetches user data. Consider concurrency or batch fetching to optimize performance at scale.


478-494: GetUserFolowerList logic.
Same pattern as GetUserFoloweeList. Also good to confirm naming consistency: “Folower” vs “Follower.”


568-573: Bulk user iteration for data sync.
Calls multiple sync methods for each user. Consider chunking or concurrency if the user set is large.

controllers/post.go (2)

495-508: Consistent logic for retrieving and updating comment messages
Consider logging errors from the goroutine if any occur.


522-532: Ensure concurrency error handling
When spawning a goroutine for UpdateMessageStatus, consider capturing or logging any returned errors.

controllers/like.go (5)

95-102: Check combined error approach
Using a single fallback mask for different errors might hamper debugging.


335-336: Potential naming mismatch
Using "Postid" in c.Param might be inconsistent with "PostID" usage.

- PostID := c.Param("Postid")
+ PostID := c.Param("postid")

522-533: Ensure error context
If GetUserFoloweeList fails, it returns only a generic error. Logging the details could help.


572-579: Check for retrieval errors
Methods like GetPostLikeStatus could fail silently. Consider improved error reporting.


633-642: GetFollowStatus
Optionally verify the existence of both users.

services/wire.go (1)

5-10: Consider consistent formatting with other wire.go files.

For consistency with other wire.go files in the codebase, consider moving the closing parenthesis to a new line.

 var ProviderSet = wire.NewSet(
 	NewEmailService,
 	NewLikeService,
 	NewPostService,
 	NewTokenService,
-	NewUserService)
+	NewUserService,
+)
main.go (1)

16-23: Consider improving error handling and message consistency.

  1. The error message should be in English for consistency.
  2. Consider adding validation for malformed DSN strings.
 func ProvideDBConnectionString() string {
 	dsn := os.Getenv("DSN")
 	if dsn == "" {
-		panic("未设置数据库连接,请检查环境变量")
+		panic("Database connection string not set. Please check environment variables")
 	}
+	// Validate DSN format
+	if !strings.Contains(dsn, "@") || !strings.Contains(dsn, "/") {
+		panic("Invalid DSN format. Expected format: [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]")
+	}
 
 	return dsn
 }
models/user.go (1)

13-17: Consider improving configuration management and field validation.

  1. Default image URLs should be moved to configuration.
  2. Consider adding validation for count fields.
+const (
+	DefaultImageURL = "https://mini-project.muxixyz.com/lmAp5ighezmJ-vJ7SMnSmCW9Mfau"
+)

type User struct {
	// ...
-	LikeCount         int    `gorm:"default:0"`
-	FollowerCount     int    `gorm:"default:0"`
-	FolloweeCount     int    `gorm:"default:0"`
+	LikeCount         int    `gorm:"default:0;check:like_count >= 0"`
+	FollowerCount     int    `gorm:"default:0;check:follower_count >= 0"`
+	FolloweeCount     int    `gorm:"default:0;check:followee_count >= 0"`
-	ImageURL          string `gorm:"default:'https://mini-project.muxixyz.com/lmAp5ighezmJ-vJ7SMnSmCW9Mfau'" json:"url"`
-	PageBackgroundURL string `gorm:"default:'https://mini-project.muxixyz.com/lmAp5ighezmJ-vJ7SMnSmCW9Mfau'" json:"backgroundURL"`
+	ImageURL          string `gorm:"default:DefaultImageURL" json:"url"`
+	PageBackgroundURL string `gorm:"default:DefaultImageURL" json:"backgroundURL"`
}
wire_gen.go (1)

19-40: Consider reducing service coupling and improving modularity.

The dependency graph shows high coupling between services. Consider:

  1. Breaking down large services into smaller, focused ones
  2. Using interfaces to reduce direct dependencies
  3. Implementing facade pattern for complex service interactions

Example approach:

// Define clear service boundaries
type PostService interface {
    CreatePost(ctx context.Context, post *models.Post) error
    // ... other post-related methods
}

type UserService interface {
    GetUser(ctx context.Context, id string) (*models.User, error)
    // ... other user-related methods
}

// Use facade pattern for complex interactions
type UserPostFacade struct {
    posts PostService
    users UserService
}

func NewUserPostFacade(posts PostService, users UserService) *UserPostFacade {
    return &UserPostFacade{posts: posts, users: users}
}
dao/user.go (1)

59-62: Enhance SearchUserid function to return user data.

The function currently only checks existence but could be more useful by returning the found user data.

-func (ud *UserDao) SearchUserid(UserID string) error {
+func (ud *UserDao) SearchUserid(UserID string) (*models.User, error) {
     var User models.User
-    return ud.db.Where("id = ?", UserID).First(&User).Error
+    if err := ud.db.Where("id = ?", UserID).First(&User).Error; err != nil {
+        return nil, err
+    }
+    return &User, nil
 }
dao/interface.go (1)

77-89: Consider adding methods for user deletion and suspension.

The UserDaoInterface could benefit from additional methods for user account management.

Add these methods to the interface:

 type UserDaoInterface interface {
     // ... existing methods ...
+    DeleteUser(UserID string) error
+    SuspendUser(UserID string, until time.Time) error
+    ReactivateUser(UserID string) error
 }
docs/swagger.yaml (1)

1155-1194: Alter Email Endpoint – Summary Mismatch
The endpoint /api/{userid}/userpage/alteremail (lines 1155–1194) appears to validate new email addresses and update the user’s email. However, the summary text currently reads “验证验证码” even though the description suggests the endpoint modifies the email address. Consider updating the summary (e.g. “修改邮箱地址”) to more accurately reflect its function.

controllers/user.go (1)

470-487: Add username validation and rate limiting.

The current implementation needs additional validation:

  1. Username format validation
  2. Profanity check
  3. Rate limiting for username changes

Apply this diff to add validation:

 func (uc *UserControllers) ChangeUsername(c *gin.Context) {
     var User models.User
     if err := c.BindJSON(&User); err != nil {
         c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
         return
     }
     User.ID = c.Param("userid")
     if User.ID == "" {
         c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
         return
     }
+    // Validate username format
+    if !isValidUsernameFormat(User.Username) {
+        c.JSON(http.StatusBadRequest, models.Write("Invalid username format"))
+        return
+    }
+    // Check for profanity
+    if containsProfanity(User.Username) {
+        c.JSON(http.StatusBadRequest, models.Write("Username contains inappropriate content"))
+        return
+    }
+    // Check rate limiting
+    if isRateLimited(User.ID, "username_change") {
+        c.JSON(http.StatusTooManyRequests, models.Write("Too many username change attempts"))
+        return
+    }
     if err := uc.UserService.ChangeUsername(User.ID, User.Username); err != nil {
         c.JSON(http.StatusInternalServerError, models.Write("更新用户名失败"))
         return
     }
     c.JSON(http.StatusCreated, gin.H{"Message": "更新用户名成功!", "NewUsername": User.Username})
 }
.gitignore (1)

1-5: Review configuration management approach.

The shift from .env to config.yaml suggests a change in configuration management:

  1. Ensure sensitive data is not committed to version control
  2. Document the configuration structure in README
  3. Provide a sample config file (e.g., config.yaml.example)
docs/swagger.json (1)

150-179: Standardize HTTP status codes in API responses.

Inconsistent use of status codes:

  1. /api/getToken uses 200 for success while similar endpoints use 201
  2. /api/logout uses 201 for success but should use 200 as no resource is created

Standardize status codes according to HTTP specifications:

  • Use 200 for successful GET requests
  • Use 201 for successful resource creation
  • Use 204 for successful requests with no content

Also applies to: 332-375

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0e0ecb and 86bd0b1.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (31)
  • .gitignore (1 hunks)
  • controllers/like.go (7 hunks)
  • controllers/post.go (6 hunks)
  • controllers/user.go (1 hunks)
  • controllers/wire.go (1 hunks)
  • dao/dao.go (1 hunks)
  • dao/interface.go (1 hunks)
  • dao/likes.go (2 hunks)
  • dao/post.go (5 hunks)
  • dao/user.go (2 hunks)
  • dao/wire.go (1 hunks)
  • docs/docs.go (35 hunks)
  • docs/swagger.json (35 hunks)
  • docs/swagger.yaml (14 hunks)
  • go.mod (5 hunks)
  • main.go (1 hunks)
  • middleware/middleware.go (0 hunks)
  • models/likes.go (0 hunks)
  • models/post.go (3 hunks)
  • models/user.go (2 hunks)
  • routes/post.go (1 hunks)
  • routes/userpage.go (2 hunks)
  • routes/wire.go (1 hunks)
  • services/email.go (1 hunks)
  • services/like.go (7 hunks)
  • services/post.go (4 hunks)
  • services/token.go (1 hunks)
  • services/user.go (2 hunks)
  • services/wire.go (1 hunks)
  • wire.go (1 hunks)
  • wire_gen.go (1 hunks)
💤 Files with no reviewable changes (2)
  • middleware/middleware.go
  • models/likes.go
✅ Files skipped from review due to trivial changes (1)
  • controllers/wire.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
dao/dao.go

18-18: Error return value of db.AutoMigrate is not checked

(errcheck)

services/like.go

436-436: Error return value of ls.ld.SyncFollowMessageToDB is not checked

(errcheck)


454-454: Error return value of ls.ld.DeleteFollowMessage is not checked

(errcheck)


504-504: ineffectual assignment to count1

(ineffassign)


506-506: ineffectual assignment to count2

(ineffassign)

🔇 Additional comments (159)
services/post.go (6)

6-6: Imports look good.
No immediate issues with these newly added imports. They appear necessary for the new functionality (reading files, working with context, time, etc.).

Also applies to: 9-10, 12-14


19-21: Great switch to interfaces for better testability and flexibility.
Replacing pointers to concrete DAOs with interface types is a commendable design choice, allowing seamless mocking and improved modularity.

Also applies to: 24-24


114-114: Swap logic acknowledged.
Using tuple swapping for sorting is straightforward and valid. This improves readability compared to manual temp variables.


155-160: New GetPostByID method clarity.
Returning the post by ID is straightforward. Ensure input validation handles empty or invalid PostID.


168-169: Tag-based filtering for unread messages.
This parameter enhancement is consistent with the new requirement to categorize messages by tag.


186-200: File reading and YAML decoding.
The logic looks sound, with proper file closure and YAML unmarshalling. Watch out for partial reads if the file is corrupted, but otherwise good.

dao/likes.go (9)

78-78: User like count sync.
This single-field update is fine, though consider concurrency if multiple services update the same field concurrently.


168-169: New tag support for searching unread messages.
Adds flexibility to filter user messages by tag. Looks correct.


176-179: UpdateMessageStatus with tag.
Effectively sets all messages matching (UserID, Tag) to "read." Make sure partial updates are intended, or consider more granular filters if needed.


181-185: SyncUserFollowersToDB.
Updates the follower count in the User table. Ensure no race conditions with other writes to the same field.


187-191: SyncUserFolloweesToDB.
Similar to SyncUserFollowersToDB, concurrency handling is needed if this field is updated elsewhere.


193-196: Insert follow message.
Creating a new follow record in the database. Straightforward approach.


198-201: Delete follow message.
Deletes follow records by user ID. Confirm whether a more specific filter is needed if multiple follow relationships share the same user ID.


203-208: SearchUserFollowee logic.
Retrieves IDs of users that the given user follows. Consider validating the returned slice for cases of no results.


210-215: SearchUserFollower logic.
Retrieves user IDs of those who follow the given user. Consistent with naming and usage.

services/like.go (11)

16-19: Interface-based fields for LikeService.
Good step to allow mocking and more flexible testing.


21-23: Constructor revised for interfaces.
Aligns seamlessly with the new interface-based design.


90-90: Key format changed to "Poster:%s:likes".
Matches the rename for storing like counts. Ensure references reflect this new naming.


303-313: InitMessage signature changes.
Added Tag and PostID usage. This method is straightforward, but ensure message scoping is correct for the new fields.


515-529: SyncFollowCount method.
Captures the up-to-date follow/follower info from Redis. Looks reasonable. Remember to handle partial errors or concurrency edge cases.


531-541: SyncLikeCount method.
Synchronizes user’s total likes from Redis to DB. Implementation is consistent with other syncs.


575-579: GetPostLikeStatus addition.
Utility function checks membership in the “like_users” set. Straightforward approach.


581-585: GetPostSaveStatus addition.
Consistent with the approach in GetPostLikeStatus.


587-591: GetCommentLikeStatus addition.
Follows the same set-membership approach.


593-597: GetReplyLikeStatus addition.
Same pattern for reply-likes membership.


599-603: GetFollowStatus addition.
Checks if the user is a member of the followee’s “followers” set. Good for quick lookups.

controllers/post.go (14)

6-6: Use of "encoding/json" is appropriate


190-190: No functional changes


362-362: Good consolidated parameter retrieval


456-466: Docstring updated to reflect 'GetUserUnreadLikeMessage'
This improved clarity is helpful.


467-467: Renaming method accurately represents its scope


473-481: Asynchronous update of message status
Launching a goroutine to update the message status in the background can improve responsiveness, but ensure potential errors from UpdateMessageStatus are at least logged.


482-482: No functional changes


483-485: Doc update for 'GetUserUnreadCommentMessage'


509-509: No functional changes


510-520: Doc updates for 'GetUserUnreadFollowMessage'


536-536: No functional changes


537-537: Doc added for 'GetToken'


545-545: New method 'GetToken'


546-556: Robust error handling for config read & token generation
Implementation is clear and covers failure points.

controllers/like.go (23)

47-48: Notification message is well-structured
Consider whether more detailed error logs or user-facing messages should be returned.


64-64: Endpoint name update is consistent


79-80: Docstring clarifies broader user data scope


85-88: Enhanced doc for response codes


266-266: Docs updated to include 'postid'


274-275: Validating post ID


284-286: Partial error handling
At least the service call error is caught here.


289-289: Async message initialization


349-350: Meaningful notification
Implementation is consistent with the pattern used elsewhere.


455-466: New doc annotations for follow user


467-485: Follow user logic
The code is straightforward; consider more detailed logging on failures.


486-497: Documentation for unfollow route


498-509: Unfollow logic
Implementation mirrored from follow.


511-521: Docs for retrieving followee list


535-545: Documentation for 'GetFollowerList'


546-557: Consistent approach for fetching followers


559-569: Docs for retrieving post status


581-581: Doc annotation


592-600: Retrieving comment like status
No immediate concerns if error cases are handled internally.


602-602: Doc update


613-621: Similar logic to 'GetCommentStatus'
No issues if errors are handled inside the service layer.


623-623: Doc addition


624-632: Doc changes

docs/docs.go (45)

156-185: New doc block: /api/getToken
The documentation accurately reflects the "GetToken" endpoint.


578-629: Renamed endpoint for comment messages
Documentation clarifies the message update logic.


630-681: New endpoint for follow messages
Matches the changes for follow notifications.


682-733: New endpoint for like messages
Documentation matches the updated logic.


734-777: Campus endpoint doc
Reflects new route details.


779-823: Food endpoint doc
Accurately updated.


824-868: Life endpoint doc
Implementation consistent with code changes.


869-913: Paint endpoint doc
No issues found.


914-967: Publish post doc
Clear explanation of request and response details.


968-1012: Recommend endpoint doc
Well detailed success/failure states.


1013-1057: Get post detail doc
Consistent with the updated controller logic.


1058-1108: Cancel like doc
Implementation is consistent with the revised route.


1109-1159: Cancel save doc
Endpoint documentation matches the controller changes.


1160-1203: Delete comment doc
Ensure doc summary precisely aligns with actual route usage.


1204-1242: Get post counts doc
No further concerns.


1243-1288: Get post status doc
Implementation is easy to understand.


1289-1339: Like post doc
Route documented thoroughly.


1340-1391: Publish comment doc
Description is accurate and up to date.


1392-1442: Cancel comment like doc
No issues found.


1482-1527: Get comment status doc
Matches newly added route references.


1528-1585: Like comment doc
Documentation parallels the controller changes.


1586-1643: Publish reply doc
Consistent with new reply publishing logic.


1644-1687: Delete reply doc
No issues found.


1688-1738: Cancel reply like doc
Implementation reflected in controllers.


1739-1777: Get reply counts doc
Documentation accurate for new functionality.


1778-1823: Get reply status doc
No concerns identified.


1824-1881: Like reply doc
Matches the newly introduced method.


1882-1935: Alter background doc
Implementation references user background updates.


1936-1995: Alter email doc
Ensure correct email verification.


1996-2049: Alter user image doc
No issues detected.


2050-2102: Alter password doc
Implementation references user security.


2103-2156: Alter username doc
No concerns.


2157-2200: Get user data doc
Reflects retrieving user stats properly.


2201-2245: Followee list doc
Aligns with code changes.


2246-2290: Follower list doc
No issues found.


2291-2335: Like record doc
Implementation is consistent with code.


2336-2379: Delete single post doc
Matches the 'DeletePost' logic.


2380-2423: Delete multiple posts doc
Reflects 'DeletePosts' approach.


2424-2468: Save record doc
Route doc is consistent with controllers.


2469-2514: Bind new email doc
Covers sending and verifying email.


2515-2559: View record doc
No issues found.


2560-2610: Cancel follow doc
Matches the controller route.


2611-2667: Follow user doc
Implementation is consistent.


2668-2713: Get follow status doc
No issues identified.


2888-2905: New user fields for backgroundURL, followeeCount, followerCount, and likeCount
These added attributes expand the user model meaningfully.

routes/wire.go (1)

1-9: LGTM! Clean dependency injection setup.

The ProviderSet correctly aggregates the service constructors using Wire's dependency injection pattern.

dao/wire.go (1)

1-11: LGTM! Clean dependency injection setup.

The ProviderSet correctly aggregates the DAO providers and database initialization using Wire's dependency injection pattern.

wire.go (1)

1-26: LGTM! Clean dependency injection setup.

The InitializeApp function correctly aggregates all provider sets and follows Wire's dependency injection pattern. The build tags and imports are properly configured.

main.go (1)

30-33: LGTM! Good use of dependency injection.

The main function is now cleaner and more maintainable, with dependencies properly managed through Wire.

routes/userpage.go (3)

14-14: LGTM! Good addition of UserControllers dependency.

The addition of UserControllers as a dependency follows good dependency injection practices.


17-23: LGTM! Constructor properly updated.

The constructor is correctly updated to handle the new UserControllers dependency.


36-51: Verify error handling in new routes.

The new routes look good and align with the PR objectives. However, ensure proper error handling is implemented in the controller methods, especially for critical operations like password changes and email updates.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for error handling patterns in the controller methods
echo "Checking error handling in UserControllers methods..."
ast-grep --pattern 'func (uc *UserControllers) $_($$) {
  $$$
  if err != nil {
    $$$
  }
  $$$
}'

Length of output: 219


I'll run another script to inspect the definitions of the critical controller functions, particularly ChangeUserPassword and ChangeUserEmail, and see if error handling (e.g. checking for an error using "if err !=") is implemented in these functions.


🏁 Script executed:

#!/bin/bash
echo "Searching for ChangeUserPassword with context..."
rg -A 10 "func.*ChangeUserPassword" -n

echo "Searching for ChangeUserEmail with context..."
rg -A 10 "func.*ChangeUserEmail" -n

Length of output: 3755


Subject: Error Handling in Critical Routes Confirmed

The inspection confirms that both ChangeUserPassword and ChangeUserEmail methods include proper error handling:

  • In ChangeUserPassword (controllers/user.go:379-389), JSON binding errors and missing user ID are correctly caught, and the corresponding service function checks encryption and DAO update errors.
  • Similarly, in ChangeUserEmail (controllers/user.go:435-445), invalid JSON input and absent user ID trigger error responses, with service and DAO layers propagating any update errors.

Overall, error handling for these critical routes is consistent and thorough.

routes/post.go (1)

51-54: LGTM! Good addition of status endpoints.

The new endpoints for retrieving token and status information are well-structured and follow consistent naming patterns.

Ensure these endpoints are properly documented in the API documentation.

models/post.go (2)

28-29: LGTM! Good addition of fields for message tracking.

The addition of PostID and Tag fields to the Message struct improves message tracking capabilities.


86-91: Consider security implications of QiNiuYun configuration.

The QiNiuYunConfig struct contains sensitive credentials. Ensure these values are properly secured and not committed to version control.

Check if sensitive credentials are accidentally committed:

Consider using environment variables or a secure secrets management system for these credentials.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for potential QiNiuYun credentials in the codebase
echo "Checking for hardcoded QiNiuYun credentials..."
rg -i "access_key|secret_key" --type yaml

Length of output: 146


I'll now run an additional script that searches the entire repository (not limited to YAML files) for any hardcoded QiNiuYun credential elements, including the struct usage.


🏁 Script executed:

#!/bin/bash
# Comprehensive search for hardcoded QiNiuYun credentials in the repository

echo "Searching for QiNiuYun credential keywords across all files..."
# Use \b to match complete words where possible.
rg -i "AccessKey\\b|SecretKey\\b|Bucket\\b|Domain\\b" .

echo "Searching for references to QiNiuYunConfig struct..."
rg -i "QiNiuYunConfig" .

Length of output: 1176


Security Verification: QiNiuYunConfig Usage is Consistent with Runtime Configuration

The search results revealed only the definition and usage of the QiNiuYunConfig struct without any actual hardcoded credentials in the repository. The sensitive fields (AccessKey, SecretKey, etc.) are read from a configuration file (evidenced by the ReadConfig function) rather than being embedded as literals in the source code.

Nevertheless, remain cautious with the configuration file itself—ensure it's excluded from version control (e.g., via .gitignore) and consider using environment variables or a secrets management solution to further protect these credentials.

services/email.go (1)

13-16: LGTM! Good use of interface-based design.

The change from concrete types to interfaces improves testability and flexibility.

dao/interface.go (1)

9-20: LGTM! Well-structured provider pattern implementation.

The provider functions are well-organized and follow the dependency injection pattern.

dao/post.go (4)

58-61: LGTM! Improved error handling.

The changes to use First instead of direct Where queries improve error handling by properly returning gorm.ErrRecordNotFound when no records are found. This aligns better with GORM's best practices.

Also applies to: 64-67, 77-80


167-169: LGTM! Improved handling of empty record sets.

The changes prevent SQL errors when using NOT IN with an empty slice by appending an empty string to RecordID when it's nil. This is a good defensive programming practice.

Also applies to: 190-192


30-30: Verify soft delete behavior.

The change in deletion method from Delete(&models.PostMessage{}, ...) to Model(&models.PostMessage{}).Delete(...) might affect how GORM handles soft deletes. Please ensure that soft deletes work as expected if the PostMessage model has the gorm.DeletedAt field.

Run the following script to check if the model has soft delete support:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check if PostMessage model has soft delete support.

ast-grep --pattern $'type PostMessage struct {
  $$$
  DeletedAt $_ `gorm:"$$$"`
  $$$
}'

Length of output: 90


I'll generate a new shell script to search for the PostMessage struct definition and directly check for any soft delete related field (specifically, the presence of a DeletedAt field with a gorm tag) within it.


🏁 Script executed:

#!/bin/bash
# Step 1: Find the definition of the PostMessage struct
echo "Searching for 'PostMessage' struct definition with context:"
rg "type PostMessage struct {" -A 40

# Step 2: Check if there's a DeletedAt field with a gorm tag (indicating soft delete support)
echo -e "\nSearching for a 'DeletedAt' field with a gorm tag:"
rg "DeletedAt.*gorm:" -n

Length of output: 437


I'll generate a new shell script using fixed string searches (to avoid regex errors) and also attempt to locate the file where the PostMessage model is defined. This will help ensure we can determine if the model has soft delete support via a DeletedAt field.


🏁 Script executed:

#!/bin/bash
# Step 1: Search for the file that likely defines PostMessage (e.g., post_message.go)
echo "Searching for file(s) containing 'PostMessage':"
fd -HI -e go -x grep -H "type PostMessage struct" {}

# Step 2: Use fixed-string search to show the PostMessage struct definition context.
echo -e "\nDisplaying context around 'type PostMessage struct {' using fixed string search:"
rg -F "type PostMessage struct {" -A 40

# Step 3: Search within the repository for any line containing 'DeletedAt' with indication of a gorm tag.
echo -e "\nSearching for 'DeletedAt' with a gorm tag using fixed string search:"
rg -F "DeletedAt" -n

Length of output: 2322


Confirmed soft delete behavior is not affected.

The PostMessage model (located in models/post.go) does not include a DeletedAt field, meaning soft deletes aren’t enabled for this entity. As a result, the change in deletion method does not alter soft delete behavior.


86-86: Verify soft delete behavior.

Similar to DeletePostBody, the change in deletion method might affect how GORM handles soft deletes. Please ensure that soft deletes work as expected if the Comment model has the gorm.DeletedAt field.

Run the following script to check if the model has soft delete support:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Check if Comment model has soft delete support.

ast-grep --pattern $'type Comment struct {
  $$$
  DeletedAt $_ `gorm:"$$$"`
  $$$
}'

Length of output: 86


I'm not seeing the expected output from the initial ast-grep command. Let's run additional commands to check for the Comment model definition and look explicitly for any soft delete field configuration (i.e. any use of "DeletedAt" with a gorm tag).


🏁 Script executed:

#!/bin/bash
echo "Searching for Comment model definition with context..."
rg -C 5 "type Comment struct" .

echo ""
echo "Searching for DeletedAt field with gorm tag in the repository..."
rg -C 2 'DeletedAt.*gorm:"' .

Length of output: 895


Soft delete functionality not enabled.
The Comment model (in models/post.go) does not define a DeletedAt field, so soft deletes are not activated. The new deletion method, therefore, is performing a hard delete as intended. If soft deletes are later required, the model should be updated to include a DeletedAt field.

docs/swagger.yaml (33)

115-126: User Model Enhancements
New properties have been introduced in the models.User definition – namely, backgroundURL (lines 115–116), followeeCount and followerCount (lines 119–122), and likeCount (lines 125–126). These additions align with the new user profile features. Please verify that all related controller code, database fields, and UI components consistently use the same names and types.


143-178: Comment Message Endpoint Addition
A new GET endpoint is added at /api/{userid}/commentmessage (lines 143–178) to retrieve unread comment notifications and update their status to read. The overall structure (consumes, produces, response codes such as 200, 207, 400, and 500) looks sound. One point to verify: the use of status code “207” for “状态更新错误” is unusual – please confirm that this custom code is intentional and documented.


178-212: Follow Message Endpoint Addition
The changes introduce a GET endpoint at /api/{userid}/followmessage (lines 178–212) that retrieves unread “follow” notifications. The descriptions, parameters, and responses are consistent with similar endpoints.


213-247: Like Message Endpoint Addition
A new GET endpoint at /api/{userid}/likemessage (lines 213–247) is included to fetch unread like messages. The endpoint’s configuration and response schemas match the pattern seen in the other message endpoints.


367-397: Reply Like Status Endpoint
At /api/{userid}/post/{postid}/{commentid}/{replyid}/getstatus (lines 367–397) the API now supports fetching the like status for a reply. The definition (consumes, produces, parameters, and response schema) is clear. Verify that the client side expects the “additionalProperties” style response.


497-527: Comment Like Status Endpoint
The GET endpoint /api/{userid}/post/{postid}/{commentid}/getstatus (lines 497–527) has been added to obtain the like status for a comment. The configuration is analogous to the reply status endpoint.


1016-1049: Cancel Follow Endpoint
The new POST endpoint /api/{userid}/userpage/{followid}/cancelfollow (lines 1016–1049) for unfollowing a user appears well defined, with clear path parameters and a 201 response code on success.


1050-1087: Follow User Endpoint
At /api/{userid}/userpage/{followid}/follow (lines 1050–1087), the API allows following another user while also uploading a follow-related message. In addition to the expected 201, a 207 response is provided for cases where message upload fails but focus action succeeds; please double-check that this pattern is documented for clients.


1088-1116: Get Follow Status Endpoint
The GET endpoint /api/{userid}/userpage/{followid}/getstatus (lines 1088–1116) returns the follow status between users. The structure is straightforward; ensure that the “additionalProperties” response scheme is fully supported on the client side.


1119-1154: Alter Background Endpoint
The POST endpoint /api/{userid}/userpage/alterbackground (lines 1119–1154) for updating the user’s personal homepage background image is clear and consistent.


1195-1227: Alter Image Endpoint
The POST endpoint /api/{userid}/userpage/alterimage (lines 1195–1227) for updating the user’s avatar is well configured with the expected request/response structure.


1231-1263: Alter Password Endpoint
The password change endpoint /api/{userid}/userpage/alterpassword (lines 1231–1263) is clear and follows the same design pattern as the other personal profile modifications.


1266-1299: Alter Username Endpoint
The POST endpoint /api/{userid}/userpage/alterusername (lines 1266–1299) for changing the username is straightforward and consistent with the other profile endpoints.


1302-1328: User Count Endpoint
The GET endpoint /api/{userid}/userpage/count (lines 1302–1328) returns various user statistics such as like, follow, and fan counts. Verify that the returned data directly corresponds to the new properties (e.g. followeeCount, followerCount, likeCount) added in the User model.


1331-1360: Followee List Endpoint
The GET endpoint /api/{userid}/userpage/followeelist (lines 1331–1360) that retrieves the list of users the current user is following is defined clearly.


1361-1390: Follower List Endpoint
Likewise, the GET endpoint /api/{userid}/userpage/followerlist (lines 1361–1390) returns the list of a user’s followers accurately.


1391-1418: Like Record Endpoint
The new GET endpoint /api/{userid}/userpage/likerecord (lines 1391–1418) provides historical like records over one month. Its request and response structures appear consistent with similar endpoints.


1421-1449: Delete Post Endpoint
The DELETE endpoint /api/{userid}/userpage/mypost/delete/{postid} (lines 1421–1449) for deleting an individual post is clear and uses proper response codes.


1450-1476: Bulk Delete Posts Endpoint
The bulk delete endpoint /api/{userid}/userpage/mypost/deleteposts (lines 1450–1476) is designed similarly to the single delete endpoint. Its request/response format meets the expected structure.


1479-1508: Save Record Endpoint
The GET endpoint /api/{userid}/userpage/saverecord (lines 1479–1508) that retrieves a user’s historical collection records is well defined.


1509-1538: Send Email Verification for User Page
The endpoint /api/{userid}/userpage/sendemail (lines 1509–1538) to bind a new email address is added with suitable request and response schemas.


1539-1568: View Record Endpoint
The GET endpoint /api/{userid}/userpage/viewrecord (lines 1539–1568) retrieves a user’s historical browsing records. The definition is clear and consistent.


1659-1678: Get Token Endpoint
A new GET endpoint /api/getToken (lines 1659–1678) is introduced for token retrieval (targeted at ‘图床’). Its response (object with additionalProperties) should be verified against the client’s expectations.


1679-1713: Password Login Endpoint
The POST endpoint /api/login/bypassword (lines 1679–1713) for logging in with email and password is standard and appears correct.


1714-1744: Login Send Email Endpoint
The POST endpoint /api/login/sendemail (lines 1714–1744) sends a verification code for login. Its setup compares well with similar endpoints.


1748-1776: Login Verify Code Endpoint
The endpoint /api/login/sendemail/verifycode (lines 1748–1776) verifies the user’s code as part of the login process and returns the token if successful. The structure and response format are as expected.


1779-1807: Logout Endpoint
The POST endpoint /api/logout (lines 1779–1807) forces token expiration to log the user out. This change is clear and mirrors the corresponding YAML documentation.


1808-1838: Post Save Endpoint
The POST endpoint /api/posts/{postid}/{userid}/save (lines 1808–1838) lets users save a post (adding to favorites) by incrementing the save count. The parameters and response schema are consistent.


1841-1870: Register Send Email Endpoint
The endpoint /api/register/sendemail (lines 1841–1870) determines if the user is already registered and sends a verification code accordingly. The response structure is well defined.


1876-1905: Register Verify Code Endpoint
The POST endpoint at /api/register/sendemail/verifycode (lines 1876–1905) validates the code and, upon success, returns the token and user information. It is in line with the new registration flow.


2554-2604: Cancel Follow Endpoint (JSON)
The POST endpoint /api/{userid}/userpage/{followid}/cancelfollow (lines 2554–2604) in the JSON version cleanly mirrors its YAML counterpart. The structure and status responses are consistent.


2605-2661: Follow User Endpoint (JSON)
The POST endpoint /api/{userid}/userpage/{followid}/follow (lines 2605–2661) is well defined; it even accounts for potential conflicts (409) when the user is already followed.


2662-2707: Get Follow Status (JSON)
The GET endpoint /api/{userid}/userpage/{followid}/getstatus (lines 2662–2707) returns the follow status data. The response schema using an object with additionalProperties appears consistent.

controllers/user.go (1)

338-366: Apply similar validation for profile picture URL.

This method has similar validation needs as ChangeUserBackground.

Please apply the same validation improvements suggested for the background image URL:

  1. URL format validation
  2. Image size/dimension limits
  3. Input sanitization
go.mod (1)

6-48: Verify dependency versions and security.

Please verify the following:

  1. Check for known vulnerabilities in jwt-go v3.2.0
  2. Consider upgrading to golang-jwt/jwt for better security
  3. Ensure wire v0.6.0 is compatible with the project's Go version

Comment on lines +496 to +513
// 获取用户关注数和粉丝数
func (ls *LikeService) GetFollowCount(UserID string) (int, int, error) {
var count1, count2 int
key1 := "User:followers"
key2 := "User:followees"
followerscount, err1 := utils.Client.HGet(utils.Ctx, key1, UserID).Result()
followeescount, err2 := utils.Client.HGet(utils.Ctx, key2, UserID).Result()
if err1 == redis.Nil {
count1 = 0
} else if err2 == redis.Nil {
count2 = 0
} else if err1 != nil || err2 != nil {
return 0, 0, fmt.Errorf("get follow counts error:%v\t%v", err1, err2)
}
count1, _ = strconv.Atoi(followerscount)
count2, _ = strconv.Atoi(followeescount)
return count1, count2, nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

GetFollowCount has ineffectual assignments on lines 504 and 506.
The conditions set count1 = 0 and count2 = 0, but they are overwritten by the final conversion at lines 510 and 511. Remove or rethink the assignments to avoid confusion.

🧰 Tools
🪛 golangci-lint (1.62.2)

504-504: ineffectual assignment to count1

(ineffassign)


506-506: ineffectual assignment to count2

(ineffassign)

Comment on lines +420 to +439
// 用户关注操作
func (ls *LikeService) Follow(UserID, FolloweeID string) error {
key := fmt.Sprintf("User:%s:followers", FolloweeID)
//查找关注记录以避免重复关注
if utils.Client.SIsMember(utils.Ctx, key, UserID).Val() {
return fmt.Errorf("the user %s is already followed user %s", UserID, FolloweeID)
}
//添加用户到粉丝合集
utils.Client.SAdd(utils.Ctx, key, UserID)
//更新用户关注数
key = "User:followees"
utils.Client.HIncrBy(utils.Ctx, key, UserID, 1)
Record := models.FollowMessage{
FollowerID: UserID,
FolloweeID: FolloweeID,
}
go ls.ld.SyncFollowMessageToDB(&Record)
key = "User:followers"
utils.Client.HIncrBy(utils.Ctx, key, FolloweeID, 1)
return nil
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Follow method missing error handling on SyncFollowMessageToDB.
The static analysis warns the returned error from line 436 is unhandled. This can silently swallow DB insertion failures.

Apply the fix by capturing and handling the error:

-go ls.ld.SyncFollowMessageToDB(&Record)
+if err := ls.ld.SyncFollowMessageToDB(&Record); err != nil {
+    // handle or log error
+}
🧰 Tools
🪛 golangci-lint (1.62.2)

436-436: Error return value of ls.ld.SyncFollowMessageToDB is not checked

(errcheck)

Comment on lines +442 to +458
// 用户取消关注操作
func (ls *LikeService) CancelFollow(UserID, FolloweeID string) error {
key := fmt.Sprintf("User:%s:followers", FolloweeID)
//查找关注记录以避免重复关注
if !utils.Client.SIsMember(utils.Ctx, key, UserID).Val() {
return fmt.Errorf("the user %s is not followed user %s", UserID, FolloweeID)
}
//用户移除粉丝合集
utils.Client.SRem(utils.Ctx, key, UserID)
//更新用户关注数
key = "User:followees"
utils.Client.HIncrBy(utils.Ctx, key, UserID, -1)
go ls.ld.DeleteFollowMessage(UserID)
key = "User:followers"
utils.Client.HIncrBy(utils.Ctx, key, FolloweeID, -1)
return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

CancelFollow method missing error handling on DeleteFollowMessage.
Similarly, the returned error from line 454 is unhandled, creating potential silent failures.

-go ls.ld.DeleteFollowMessage(UserID)
+if err := ls.ld.DeleteFollowMessage(UserID); err != nil {
+    // handle or log error
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 用户取消关注操作
func (ls *LikeService) CancelFollow(UserID, FolloweeID string) error {
key := fmt.Sprintf("User:%s:followers", FolloweeID)
//查找关注记录以避免重复关注
if !utils.Client.SIsMember(utils.Ctx, key, UserID).Val() {
return fmt.Errorf("the user %s is not followed user %s", UserID, FolloweeID)
}
//用户移除粉丝合集
utils.Client.SRem(utils.Ctx, key, UserID)
//更新用户关注数
key = "User:followees"
utils.Client.HIncrBy(utils.Ctx, key, UserID, -1)
go ls.ld.DeleteFollowMessage(UserID)
key = "User:followers"
utils.Client.HIncrBy(utils.Ctx, key, FolloweeID, -1)
return nil
}
// 用户取消关注操作
func (ls *LikeService) CancelFollow(UserID, FolloweeID string) error {
key := fmt.Sprintf("User:%s:followers", FolloweeID)
//查找关注记录以避免重复关注
if !utils.Client.SIsMember(utils.Ctx, key, UserID).Val() {
return fmt.Errorf("the user %s is not followed user %s", UserID, FolloweeID)
}
//用户移除粉丝合集
utils.Client.SRem(utils.Ctx, key, UserID)
//更新用户关注数
key = "User:followees"
utils.Client.HIncrBy(utils.Ctx, key, UserID, -1)
if err := ls.ld.DeleteFollowMessage(UserID); err != nil {
// handle or log error
}
key = "User:followers"
utils.Client.HIncrBy(utils.Ctx, key, FolloweeID, -1)
return nil
}
🧰 Tools
🪛 golangci-lint (1.62.2)

454-454: Error return value of ls.ld.DeleteFollowMessage is not checked

(errcheck)

Comment on lines +392 to +396
PostMessage, _ := pc.PostService.GetPostByID(PostID)
CommenterID, CommentBody, _ := pc.LikeService.SearchCommentByID(CommentID)
User, _ := pc.UserService.UserDao.GetUserFromID(UserID)
message := fmt.Sprintf("用户%s回复了你在帖子%s的评论!", User.Username, PostMessage.Title)
if err := pc.LikeService.InitMessage(CommentMessage.CommenterID, message, User.ImageURL); err != nil {
message := fmt.Sprintf("用户%s回复了你在帖子%s的评论:\n%s", User.Username, PostMessage.Title, CommentBody)
if err := pc.LikeService.InitMessage(CommenterID, message, User.ImageURL, "评论", PostID); err != nil {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Check returned errors for robust error handling
In the event these calls fail, the subsequent logic may cause runtime issues or produce incomplete notifications.

Comment on lines +311 to +315
PostMessage, _ := pc.PostService.GetPostByID(PostID)
User, _ := pc.UserService.GetUserFromID(UserID)
message := fmt.Sprintf("用户%s评论了帖子:\n%s", User.Username, PostMessage.Title)
if err := pc.LikeService.InitMessage(PostMessage.PosterID, message, User.ImageURL, "评论", PostID); err != nil {
c.JSON(http.StatusMultiStatus, models.Write("评论处理成功,消息上传失败"))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle potential errors from GetPostByID and GetUserFromID
If these calls fail, it could lead to nil pointer references or inconsistent data. It's recommended to check for errors.

Example suggestion:

 PostMessage, err := pc.PostService.GetPostByID(PostID)
-if err != nil {
-   // handle error
-}
 User, err1 := pc.UserService.GetUserFromID(UserID)
-if err1 != nil {
-   // handle error
-}
 message := fmt.Sprintf("用户%s评论了帖子:\n%s", User.Username, PostMessage.Title)
 ...

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +606 to +639
/api/{userid}/post/{postid}/cancellike:
post:
consumes:
- application/json
description: 用户取消点赞,点赞数减一
parameters:
- description: 帖子ID
in: path
name: postid
required: true
type: string
- description: 用户D
in: path
name: userid
required: true
type: string
produces:
- application/json
responses:
"201":
description: 用户取消点赞成功
schema:
$ref: '#/definitions/models.Response'
"400":
description: 输入无效
schema:
$ref: '#/definitions/models.Response'
"500":
description: 取消点赞记录上传失败
schema:
$ref: '#/definitions/models.Response'
summary: 用户取消点赞帖子
tags:
- 帖子-点赞
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Cancel Post Like Endpoint – Minor Typo
The POST endpoint /api/{userid}/post/{postid}/cancellike (lines 606–639) correctly defines the cancellation action; however, in the parameters the description for the user ID on line 617 is written “用户D” instead of “用户ID.” Please fix this typo to prevent confusion in the API documentation.

Comment on lines +309 to +336
// @Summary 修改个人主页背景
// @Description 修改用户个人主页的背景图片
// @Tags 个人主页
// @Accept json
// @Produce json
// @Param userbackground body models.User true "新背景"
// @Param userid path string true "用户ID"
// @Success 201 {object} map[string]interface{} "响应成功信息以及背景图片的url"
// @Failure 400 {object} models.Response "输入无效"
// @Failure 500 {object} models.Response "修改背景失败"
// @Router /api/{userid}/userpage/alterbackground [post]
func (uc *UserControllers) ChangeUserBackground(c *gin.Context) {
var UserBackground models.User
if err := c.BindJSON(&UserBackground); err != nil {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
UserBackground.ID = c.Param("userid")
if UserBackground.ID == "" {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
if err := uc.UserService.ChangeUserBackground(UserBackground.ID, UserBackground.PageBackgroundURL); err != nil {
c.JSON(http.StatusInternalServerError, models.Write("修改背景失败!"))
return
}
c.JSON(http.StatusCreated, gin.H{"message": "修改成功!", "newbackground": UserBackground.PageBackgroundURL})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for background image URL.

The current implementation lacks validation for the background image URL. Consider:

  1. URL format validation
  2. Image size/dimension limits
  3. Input sanitization

Apply this diff to add validation:

 func (uc *UserControllers) ChangeUserBackground(c *gin.Context) {
     var UserBackground models.User
     if err := c.BindJSON(&UserBackground); err != nil {
         c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
         return
     }
     UserBackground.ID = c.Param("userid")
     if UserBackground.ID == "" {
         c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
         return
     }
+    // Validate URL format
+    if !isValidImageURL(UserBackground.PageBackgroundURL) {
+        c.JSON(http.StatusBadRequest, models.Write("Invalid image URL format"))
+        return
+    }
+    // Check image size/dimensions
+    if err := validateImageDimensions(UserBackground.PageBackgroundURL); err != nil {
+        c.JSON(http.StatusBadRequest, models.Write(err.Error()))
+        return
+    }
+    // Sanitize URL
+    UserBackground.PageBackgroundURL = sanitizeURL(UserBackground.PageBackgroundURL)
     if err := uc.UserService.ChangeUserBackground(UserBackground.ID, UserBackground.PageBackgroundURL); err != nil {
         c.JSON(http.StatusInternalServerError, models.Write("修改背景失败!"))
         return
     }
     c.JSON(http.StatusCreated, gin.H{"message": "修改成功!", "newbackground": UserBackground.PageBackgroundURL})
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// @Summary 修改个人主页背景
// @Description 修改用户个人主页的背景图片
// @Tags 个人主页
// @Accept json
// @Produce json
// @Param userbackground body models.User true "新背景"
// @Param userid path string true "用户ID"
// @Success 201 {object} map[string]interface{} "响应成功信息以及背景图片的url"
// @Failure 400 {object} models.Response "输入无效"
// @Failure 500 {object} models.Response "修改背景失败"
// @Router /api/{userid}/userpage/alterbackground [post]
func (uc *UserControllers) ChangeUserBackground(c *gin.Context) {
var UserBackground models.User
if err := c.BindJSON(&UserBackground); err != nil {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
UserBackground.ID = c.Param("userid")
if UserBackground.ID == "" {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
if err := uc.UserService.ChangeUserBackground(UserBackground.ID, UserBackground.PageBackgroundURL); err != nil {
c.JSON(http.StatusInternalServerError, models.Write("修改背景失败!"))
return
}
c.JSON(http.StatusCreated, gin.H{"message": "修改成功!", "newbackground": UserBackground.PageBackgroundURL})
}
// @Summary 修改个人主页背景
// @Description 修改用户个人主页的背景图片
// @Tags 个人主页
// @Accept json
// @Produce json
// @Param userbackground body models.User true "新背景"
// @Param userid path string true "用户ID"
// @Success 201 {object} map[string]interface{} "响应成功信息以及背景图片的url"
// @Failure 400 {object} models.Response "输入无效"
// @Failure 500 {object} models.Response "修改背景失败"
// @Router /api/{userid}/userpage/alterbackground [post]
func (uc *UserControllers) ChangeUserBackground(c *gin.Context) {
var UserBackground models.User
if err := c.BindJSON(&UserBackground); err != nil {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
UserBackground.ID = c.Param("userid")
if UserBackground.ID == "" {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
// Validate URL format
if !isValidImageURL(UserBackground.PageBackgroundURL) {
c.JSON(http.StatusBadRequest, models.Write("Invalid image URL format"))
return
}
// Check image size/dimensions
if err := validateImageDimensions(UserBackground.PageBackgroundURL); err != nil {
c.JSON(http.StatusBadRequest, models.Write(err.Error()))
return
}
// Sanitize URL
UserBackground.PageBackgroundURL = sanitizeURL(UserBackground.PageBackgroundURL)
if err := uc.UserService.ChangeUserBackground(UserBackground.ID, UserBackground.PageBackgroundURL); err != nil {
c.JSON(http.StatusInternalServerError, models.Write("修改背景失败!"))
return
}
c.JSON(http.StatusCreated, gin.H{"message": "修改成功!", "newbackground": UserBackground.PageBackgroundURL})
}

Comment on lines +368 to +395
// @Summary 修改个人主页密码
// @Description 修改用户个人主页的密码
// @Tags 个人主页
// @Accept json
// @Produce json
// @Param user body models.User true "新密码"
// @Param userid path string true "用户ID"
// @Success 201 {object} models.Response "修改密码成功"
// @Failure 400 {object} models.Response "输入无效"
// @Failure 500 {object} models.Response "修改密码成功"
// @Router /api/{userid}/userpage/alterpassword [post]
func (uc *UserControllers) ChangeUserPassword(c *gin.Context) {
var User models.User
if err := c.BindJSON(&User); err != nil {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
User.ID = c.Param("userid")
if User.ID == "" {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
if err := uc.UserService.ChangeUserPassword(User.ID, User.Password); err != nil {
c.JSON(http.StatusInternalServerError, models.Write(err.Error()))
return
}
c.JSON(http.StatusCreated, models.Write("修改密码成功"))
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add critical security measures for password change.

The current implementation lacks essential security measures:

  1. Password strength validation
  2. Old password verification
  3. Rate limiting for password changes

Apply this diff to enhance security:

 func (uc *UserControllers) ChangeUserPassword(c *gin.Context) {
     var User models.User
     if err := c.BindJSON(&User); err != nil {
         c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
         return
     }
     User.ID = c.Param("userid")
     if User.ID == "" {
         c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
         return
     }
+    // Validate password strength
+    if err := validatePasswordStrength(User.Password); err != nil {
+        c.JSON(http.StatusBadRequest, models.Write(err.Error()))
+        return
+    }
+    // Verify old password
+    if err := uc.UserService.VerifyOldPassword(User.ID, User.OldPassword); err != nil {
+        c.JSON(http.StatusUnauthorized, models.Write("Old password is incorrect"))
+        return
+    }
+    // Check rate limiting
+    if isRateLimited(User.ID, "password_change") {
+        c.JSON(http.StatusTooManyRequests, models.Write("Too many password change attempts"))
+        return
+    }
     if err := uc.UserService.ChangeUserPassword(User.ID, User.Password); err != nil {
         c.JSON(http.StatusInternalServerError, models.Write(err.Error()))
         return
     }
+    // Invalidate all existing sessions
+    if err := uc.TokenService.InvalidateAllUserTokens(User.ID); err != nil {
+        log.Printf("Failed to invalidate user tokens: %v", err)
+    }
     c.JSON(http.StatusCreated, models.Write("修改密码成功"))
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +435 to +457
func (u *UserControllers) ChangeUserEmail(c *gin.Context) {
var code models.Emailverify
if err := c.BindJSON(&code); err != nil {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
UserID := c.Param("userid")
if UserID == "" {
c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
return
}
if err := u.EmailService.VerifyCode(code.Code); err != nil {
response := err.Error()
c.JSON(http.StatusConflict, response)
return
}
if err := u.UserService.ChangeUserEmail(UserID, code.Email); err != nil {
c.JSON(http.StatusInternalServerError, models.Write("更新邮箱地址失败"))
return
}
c.JSON(http.StatusCreated, gin.H{"message": "修改密码成功!", "newEmail": code.Email})

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance email change security measures.

The current implementation needs additional security measures:

  1. Email format validation
  2. Check for existing email usage
  3. Notification to old email address

Apply this diff to improve security:

 func (u *UserControllers) ChangeUserEmail(c *gin.Context) {
     var code models.Emailverify
     if err := c.BindJSON(&code); err != nil {
         c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
         return
     }
     UserID := c.Param("userid")
     if UserID == "" {
         c.JSON(http.StatusBadRequest, models.Write("输入无效,请重试!"))
         return
     }
+    // Validate email format
+    if !isValidEmailFormat(code.Email) {
+        c.JSON(http.StatusBadRequest, models.Write("Invalid email format"))
+        return
+    }
+    // Check if email is already in use
+    if u.UserService.IsEmailInUse(code.Email) {
+        c.JSON(http.StatusConflict, models.Write("Email already in use"))
+        return
+    }
     if err := u.EmailService.VerifyCode(code.Code); err != nil {
         response := err.Error()
         c.JSON(http.StatusConflict, response)
         return
     }
+    // Get old email for notification
+    oldEmail, _ := u.UserService.GetUserEmail(UserID)
     if err := u.UserService.ChangeUserEmail(UserID, code.Email); err != nil {
         c.JSON(http.StatusInternalServerError, models.Write("更新邮箱地址失败"))
         return
     }
+    // Notify old email about the change
+    go u.EmailService.SendEmailChangeNotification(oldEmail)
     c.JSON(http.StatusCreated, gin.H{"message": "修改密码成功!", "newEmail": code.Email})
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +281 to +307
// @Summary 退出登录
// @Description 退出登录并将token强制过期
// @Tags 个人主页-退出登录
// @Accept application/json
// @Produce application/json
// @Param Authorization header string true "Bearer token"
// @Success 201 {object} models.Response "登出成功"
// @Failure 400 {object} models.Response "token获取失败"
// @Failure 500 {object} models.Response "服务器错误"
// @Router /api/logout [post]
func (uc *UserControllers) Logout(c *gin.Context) {
// 从请求头中获取token
tokenString := c.GetHeader("Authorization")
if tokenString == "" {
c.JSON(http.StatusBadRequest, models.Write("token获取失败!"))
return
}
if len(tokenString) > 7 && tokenString[:7] == "Bearer " {
tokenString = tokenString[7:]
}
_, err := uc.TokenService.InvalidateToken(tokenString)
if err != nil {
c.JSON(http.StatusInternalServerError, models.Write(err.Error()))
return
}
c.JSON(http.StatusCreated, models.Write("登出成功!"))
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance security in logout implementation.

Consider the following security improvements:

  1. Add token format validation before invalidation
  2. Add logging for security audit trail
  3. Consider implementing rate limiting for the logout endpoint

Apply this diff to enhance security:

 func (uc *UserControllers) Logout(c *gin.Context) {
+    // Get token from Authorization header
     tokenString := c.GetHeader("Authorization")
     if tokenString == "" {
         c.JSON(http.StatusBadRequest, models.Write("token获取失败!"))
         return
     }
+    // Validate token format
+    if !strings.HasPrefix(tokenString, "Bearer ") {
+        c.JSON(http.StatusBadRequest, models.Write("Invalid token format"))
+        return
+    }
     if len(tokenString) > 7 && tokenString[:7] == "Bearer " {
         tokenString = tokenString[7:]
     }
+    // Validate token string format (e.g., JWT format)
+    if !isValidTokenFormat(tokenString) {
+        c.JSON(http.StatusBadRequest, models.Write("Invalid token format"))
+        return
+    }
+    // Log logout attempt
+    log.Printf("Logout attempt for token: %s", tokenString[:10]) // Log only first 10 chars for security
     _, err := uc.TokenService.InvalidateToken(tokenString)
     if err != nil {
+        log.Printf("Failed to invalidate token: %v", err)
         c.JSON(http.StatusInternalServerError, models.Write(err.Error()))
         return
     }
+    // Log successful logout
+    log.Printf("Successful logout for token: %s", tokenString[:10])
     c.JSON(http.StatusCreated, models.Write("登出成功!"))
 }

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@sunjunnan79 sunjunnan79 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

很好捏,dao层还可以优化一下,其实多数情况下一个model只要crud四个方法就能完成大部分任务了

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好神秘的做法,跨层级了,为什么不抽象一个like服务?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我去这么多方法,劳模!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么不直接读取一个models.User,然后在service再去聚合呢?这样有很多重复逻辑都可以避免吧

@@ -18,34 +13,21 @@ func init() {
}
}

func ProvideDBConnectionString() string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

要不试试viper来读取配置?OS的方式有点费劲啊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants