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: activity functions #2

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

raiki02
Copy link
Collaborator

@raiki02 raiki02 commented Feb 7, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced activity management with refined search and filtering options for events.
    • New post features enabling users to create, view, search, and delete posts.
    • Expanded user functionality with additional search capabilities for activities and posts.
    • Introduced an improved image uploading service that supports multiple file uploads.
    • Added functionality for managing user activities and posts through the user service.
    • New comment management features, including creation, deletion, and response handling.
    • Added support for handling likes and comments through a new number controller.
  • Improvements

    • Updated the default profile avatar, providing a refreshed look.
    • Updated MySQL Data Source Name (DSN) configuration for improved security.
  • Bug Fixes

    • Simplified user login and logout processes with improved error handling.
    • Resolved issues related to activity and post management functionalities.
  • Refactor

    • Significant restructuring of routing and controller functionalities for activities and posts.
    • Updated data models for activities, posts, comments, and users to enhance data management and interaction.
    • Transitioned to a more integrated routing mechanism using Gin for user and post management.
    • Refactored comment handling to utilize a service layer for improved functionality.
    • Restructured Kafka handling for asynchronous operations and improved message consumption.

Copy link

coderabbitai bot commented Feb 7, 2025

Walkthrough

The pull request introduces widespread refactoring and new features across the project. It updates configuration files and dependency management, refines caching and database migration logic, and reorganizes the controller, DAO, router, and model layers for activities, users, and posts. Key interface and method signature changes improve consistency, with renamed types and streamlined parameters. Kafka integration is completely removed. New services for image uploading, post management, and user operations (including external login integration) are added, further shaping the control flow and HTTP routing for improved modularity and clarity.

Changes

File(s) Change Summary
.gitignore, config/conf-example.yaml, go.mod Updated ignored files; added a new imgbed configuration section; reorganized dependency management with new direct and indirect dependencies.
internal/cache/redis.go Refactored cache interface and implementation: changed method signatures to use []interface{}, removed CommentDAO dependency, and renamed serialization functions.
internal/controller/activity_controller.go, internal/controller/user_controller.go, internal/controller/post_controller.go Renamed controller interfaces/structs; removed outdated endpoints; updated method signatures (e.g., removed context parameters) and added new search/find methods; introduced a new PostController with CRUD endpoints.
internal/dao/activity_dao.go, internal/dao/user_dao.go, internal/dao/post_dao.go Refactored DAOs: Activity DAO renamed to ActDao with gin.Context usage; User DAO’s Insert changed to Create and removed existence checks; Post DAO reworked with methods for retrieving, creating, finding, and deleting posts.
internal/router/activity_router.go, internal/router/user_router.go, internal/router/post_router.go Modified routers: Renamed ActivityRouter to ActRouter; updated endpoints and error handling; UserRouter now includes search routes; PostRouter now accepts a gin.Engine and maps HTTP methods appropriately.
internal/model/activity.go, internal/model/post.go, internal/model/user.go Expanded Activity model with new fields and methods; Post model restructured using *gorm.Model and additional fields; User model updated JSON tags and DefaultAvatar constant.
internal/ioc/db.go, internal/middleware/jwt.go Removed ActivityDraft from auto-migration; updated JWT comments to broaden token validation scope.
test/main.go, tools/tool.go Removed Kafka implementation and related functions; updated ReturnMSG to return a map structure instead of a custom response type.
internal/service/* Introduced new service packages: ActivityService, ImgUploader (for image processing/upload), PostService, and UserService (with external login integration via ccnuService).

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant PR as PostRouter
    participant PC as PostController
    participant PS as PostService
    participant PDAO as PostDao
    participant DB as Database
    participant IU as ImgUploader

    U->>PR: HTTP POST /post
    PR->>PC: Invoke CreatePost()
    PC->>PS: Process request
    PS->>IU: ProcessImg(ctx)
    IU-->>PS: Return image URL(s)
    PS->>PDAO: CreatePost(ctx, post)
    PDAO->>DB: Insert post record
    DB-->>PDAO: Acknowledge insertion
    PDAO-->>PS: Return success
    PS-->>PC: Return result
    PC-->>PR: HTTP Response with status
    PR-->>U: Final response delivered
Loading

Poem

I hop through code with whiskered cheer,
Refactored routes and models appear.
New services bloom like carrots bright,
Clean interfaces guide me right.
With every commit my joy takes flight,
A bunny’s code song in pure delight!

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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: 12

🔭 Outside diff range comments (1)
internal/dao/user_dao.go (1)

34-41: Add input validation for student ID.

The Insert method accepts a student ID without validation. Consider adding validation to prevent potential SQL injection or invalid data.

Here's a suggested implementation with validation:

 func (dao *UserDAO) Insert(ctx context.Context, ssid string) error {
+    // Validate student ID format
+    if !isValidStudentID(ssid) {
+        return fmt.Errorf("invalid student ID format: %s", ssid)
+    }
     u := model.User{
         Name:      ssid,
         StudentId: ssid,
         Avatar:    model.DefaultAvatar,
     }
     return dao.db.Create(&u).Error
 }

+// isValidStudentID validates the format of a student ID
+func isValidStudentID(id string) bool {
+    // Adjust the pattern based on your student ID format
+    pattern := `^\d{8,12}$`
+    match, _ := regexp.MatchString(pattern, id)
+    return match
+}
🧹 Nitpick comments (26)
internal/controller/user_controller.go (2)

20-20: Consider documenting the rationale.

The comment about infrequent database operations could be more descriptive. Consider explaining why this is important and any implications for the design.

-// user这边操作数据库不频繁
+// User-related database operations are infrequent, which influences our design choices:
+// - No need for caching layer
+// - Direct database access is acceptable
+// - Performance optimization is not critical

65-67: Review the user creation flow.

The user creation happens automatically during login if the user doesn't exist. This approach:

  1. Simplifies the registration process
  2. Ensures user existence before authentication
  3. Uses default values for new users

However, consider adding logging for audit purposes.

 if !uc.udh.CheckUserExist(c.Request.Context(), tools.StrToInt(studentid)) {
+    log.Info("Creating new user", "studentid", studentid)
     uc.udh.Insert(c, studentid)
+    log.Info("User created successfully", "studentid", studentid)
 }
tools/tool.go (1)

31-39: Consider using or returning a strongly typed response structure.
Currently, the function builds a resp.Resp but then wraps it in a gin.H map. This approach may reduce clarity and type safety if different parts of the code expect resp.Resp directly.

internal/router/activity_router.go (5)

8-9: Validate necessity of returning an error from RegisterActRouter.
The interface method returns an error, yet usage doesn’t seem to require it. If no real error is ever returned, consider removing the error return value.


14-14: Parameter naming nitpick.
“ech” is a bit abstract. Consider a more descriptive name like “actCtrl” for readability.


24-29: Router group established.
The “act” group is clearly named. Request validation or authentication middleware might be considered if needed.


38-41: Descriptive route parameters.
Using “/time” and “/name” is straightforward. For “/:date,” consider clarifying if date is in YYYY-MM-DD or some other format, or use a more explicit parameter name to improve readability.


43-43: Always returning nil.
Since this method always returns nil, consider removing the error return type from RegisterActRouter, or handle potential routing errors.

internal/cache/redis.go (2)

11-11: Pending TODO.
The comment indicates planned scope expansions (活动, 帖子, JWT). Let us know if you’d like help addressing them or tracking them in a new ticket.


37-39: 48-hour TTL review.
48 hours is set as a hard-coded expiry. Confirm that this policy aligns with your business requirements.

test/main.go (4)

12-44: Consider adding error handling to r.Run().

While creating a Gin server with default settings and routes is fine, calling r.Run() without capturing its returned error can mask potential port-binding failures.

You might update:

-r.Run()
+if err := r.Run(); err != nil {
+    // handle or log error
+    panic(err)
+}

58-79: Validate upload key construction and handle potential long upload times.

The logic to generate the upload token and perform the upload is correct in principle; however, for larger files, consider streaming or a more robust approach that won’t hold the entire file in memory. Also ensure the “EventGlide/” prefix is intended for all uploads.


81-83: Return descriptive error codes.

You return a generic code 501 upon Qiniu upload failure. Clarify if 501 is your standard approach or if it’s more appropriate to return a domain-specific status.


86-87: Provide a more descriptive return type instead of (int, string).

Currently, the function returns (0, url) for success and (errorCode, errorMessage) for failure. Consider returning (string, error) or a structured object that includes both fields for clarity.

internal/model/activity.go (3)

3-7: Confirm usage of gin.Context in model-related methods.

These imports indicate that your model is tightly coupled with the “gin.Context”. Typically, models are kept framework-agnostic. Consider passing simpler parameters to model methods and using gin in the controller or DAO layer instead.


51-53: Implement GetImgUrl functionality or remove it.

Currently, it returns nil and does nothing. Either implement fetching the URL or remove it to avoid confusion.


55-75: Align ActivityDraft with Activity regarding time fields and curly quotes.

Like Activity, consider using time.Time over string for StartTime/EndTime, and also fix the mismatched quote in line 57.

internal/dao/activity_dao.go (2)

10-25: Evaluate necessity of so many specialized find methods.

You’ve introduced many “FindActByX” methods, which can lead to code duplication. Consider a more generic or dynamic query builder, or a single “FindActivities” method with optional filtering.


49-50: Add pagination support.

The TODO about restricted page size is beneficial for large data sets. Consider implementing offset/limit or cursor-based pagination sooner rather than later.

internal/controller/activity/activity_controller.go (3)

12-12: Consider implementing the filtering logic.
The TODO at line 12 (“find函数写成过滤器模式”) suggests refactoring the find methods into a coherent filtering approach. This could be beneficial in reducing repetitive code and simplifying the addition of new filters down the line.


84-106: Return a success message for NewDraft.
Currently, upon successful creation of the draft, no response is returned to the client. Convey success explicitly by sending a final success message (e.g., “Draft created successfully” along with draft data) so that clients receive confirmation.

 func (ac ActController) NewDraft() gin.HandlerFunc {
   return func(ctx *gin.Context) {
     ...
     err = ac.ad.CreateDraft(ctx, d)
     if err != nil {
       tools.ReturnMSG(ctx, err.Error(), nil)
       return
     }
+    tools.ReturnMSG(ctx, "Draft created successfully", d)
   }
 }

208-218: Check for empty name/date parameters to maintain consistency.
Unlike other retrieval methods that return an error when the query is empty, “FindActByName” and “FindActByDate” do not. Consider enforcing a consistent pattern across all find methods.

 func (ac ActController) FindActByName() gin.HandlerFunc {
   return func(ctx *gin.Context) {
     n := ctx.Query("name")
+    if n == "" {
+      tools.ReturnMSG(ctx, "query cannot be nil", nil)
+      return
+    }
     ...
   }
 }

 func (ac ActController) FindActByDate() gin.HandlerFunc {
   return func(ctx *gin.Context) {
     date := ctx.Param("date")
+    if date == "" {
+      tools.ReturnMSG(ctx, "param cannot be nil", nil)
+      return
+    }
     ...
   }
 }

Also applies to: 220-231

internal/controller/activity/imgUploader.go (2)

52-65: Handle missing file gracefully or proceed with partial uploads.
The current loop breaks if any of “file0” through “file8” is missing. If partial uploads are acceptable, consider skipping missing files instead of returning nil outright. Otherwise, return an explicit error to inform the client.


93-95: Prevent filename collisions.
Using “EventGlide/<original_filename>” may cause collisions if multiple clients upload files with the same name. Consider incorporating a unique key prefix (like a UUID) or time-based stamp to ensure distinct paths in the bucket.

key := "EventGlide/" + file.Filename
+uniqueID := uuid.New().String()
+key := fmt.Sprintf("EventGlide/%s_%s", uniqueID, file.Filename)
config/conf-example.yaml (2)

13-14: Consider storing the JWT key securely.
The “secret-key-for-jwt” should not be hard-coded in the config file in production. Provide an environment variable or secrets manager reference to mitigate security risks.


15-19: Add a newline at the end of the file and protect sensitive keys.
Static analysis indicates there’s no newline at EOF. Also, consider placing your image-bed credentials in environment variables for better security.

 imgbed:
   accessKey: your-access-key
   secretKey: your-secret-key
   bucket: bucket-name
   imgUrl: img-store-url
+ 
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 19-19: no new line character at the end of file

(new-line-at-end-of-file)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ddc8d8f and 721d689.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (16)
  • .gitignore (1 hunks)
  • config/conf-example.yaml (1 hunks)
  • go.mod (6 hunks)
  • internal/cache/redis.go (1 hunks)
  • internal/controller/activity/activity_controller.go (1 hunks)
  • internal/controller/activity/imgUploader.go (1 hunks)
  • internal/controller/activity_controller.go (0 hunks)
  • internal/controller/user_controller.go (3 hunks)
  • internal/dao/activity_dao.go (1 hunks)
  • internal/dao/user_dao.go (2 hunks)
  • internal/ioc/db.go (0 hunks)
  • internal/middleware/jwt.go (1 hunks)
  • internal/model/activity.go (1 hunks)
  • internal/router/activity_router.go (1 hunks)
  • test/main.go (1 hunks)
  • tools/tool.go (1 hunks)
💤 Files with no reviewable changes (2)
  • internal/ioc/db.go
  • internal/controller/activity_controller.go
✅ Files skipped from review due to trivial changes (2)
  • .gitignore
  • internal/middleware/jwt.go
🧰 Additional context used
🪛 YAMLlint (1.35.1)
config/conf-example.yaml

[error] 19-19: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (27)
internal/dao/user_dao.go (1)

12-12: Verify the security implications of removing password parameter.

The removal of the password parameter from the interface suggests that user authentication is handled elsewhere. Please ensure this aligns with the application's security requirements and authentication flow.

Let's verify the authentication flow in the codebase:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for authentication-related code
rg -A 5 "password|auth|login|credential" --type go

Length of output: 4789


Authentication Flow and Password Parameter Removal Verification

The removal of the password parameter from the Insert method in the user DAO appears intentional. Our search confirms that:

  • The Login-related functions in internal/controller/user_controller.go and within the ccnuService still receive and use the password parameter to communicate with the external authentication service.
  • The Insert method is now focused solely on creating user records (e.g., assigning a default avatar and storing non-sensitive user data) without involving authentication logic.

This separation ensures that the authentication flow is not compromised by removing the password parameter from the DAO. However, please double-check that all user identifiers (such as the student ID) are properly validated and sanitized elsewhere in the codebase to avoid injection risks.

internal/controller/user_controller.go (1)

137-138: LGTM! Clear middleware documentation.

The comment clearly explains that this function is used as middleware for token validation.

internal/router/activity_router.go (4)

5-5: Import statement confirmed.
The import of "github.com/raiki02/EG/internal/controller/activity" is consistent with the new controller structure.


12-12: Renaming to ActRouter is consistent with the new naming scheme.
No issues found.


17-21: Satisfactory constructor pattern.
The constructor properly returns the ActRouterHdl interface. This is consistent with other constructor patterns in the codebase.


31-35: Grouped GET endpoints.
Multiple GET endpoints are introduced. This is clear, but ensure the hosting logic is tested and not duplicating routes.

internal/cache/redis.go (5)

13-15: Generic slice usage.
Switching to []interface{} broadens type flexibility but may complicate deserialization logic elsewhere. Ensure callers handle dynamic types properly.


23-26: Simplified constructor confirmed.
Removing the extra DAO parameter keeps the CacheHdl focused purely on cache operations.


29-29: Cache Get method clarity.
Storing raw bytes in Redis is typical, but ensure that downstream code properly handles the untyped []interface{} after retrieval.


34-34: Ensure proper error checking.
If key is not found, the Redis GET call can yield nil or an error. Confirm callers handle nil slices appropriately.


46-47: Serialization logic is acceptable.
Using tools.Marshal ensures consistent JSON encoding. No issues found here.

test/main.go (1)

4-9: Use consistent naming and ensure minimal imports.

These newly added imports (context, gin, qbox, storage, multipart, http) align well with the file upload functionality. However, please confirm that all imports are needed. Unused imports can lead to clutter and maintenance overhead.

internal/dao/activity_dao.go (2)

4-5: Centralize error handling.

Adding "errors" and "github.com/gin-gonic/gin" is logical. Still, ensure that custom error creation with “errors.New” is consistent with the rest of your project’s error handling strategy.
[approve]


127-140: CheckExist only compares partial fields.

It only checks existence on Type, Host, Location, IfRegister, Capacity, ignoring other fields like Name, StartTime, etc. This can lead to false negatives. Consider adding all relevant fields or using a unique key constraint.

internal/controller/activity/activity_controller.go (3)

108-123: Host-based query appears correct.
The query handling for "host" looks straightforward and consistent with the rest of the codebase.


125-140: Type-based query appears correct.
Fetching by “type” parameter is consistent here, and the error handling looks aligned with your pattern.


193-206: Add validation or error handling for time query parameters.
The code appends “:00” to the provided times. Consider validating the format beforehand to avoid unexpected runtime errors or partial data retrieval.

internal/controller/activity/imgUploader.go (1)

3-19: Interface and struct setup looks good.
Defining the ImgUploaderHdl interface and its methods is straightforward. The “ImgUploader” struct fields map logically to the needed credentials.

go.mod (9)

6-6: Direct Dependency Addition: sarama v1.45.0
This addition promotes the Sarama library to a direct dependency. Please ensure that your new activity functions or any related Kafka integrations are fully compatible with v1.45.0 and that you have reviewed any potential breaking changes between versions.


19-19: Indirect Dependency Addition: BurntSushi/toml v1.4.0
Confirm that this dependency is indeed required—typically for configuration or TOML parsing needs triggered by one of your direct dependencies. If it plays a key role, consider whether it should be documented or even promoted to a direct dependency.


23-23: Indirect Dependency Addition: alex-ant/gomath
Review the necessity of including 'github.com/alex-ant/gomath'. If it is pulled in solely via another dependency, you might not need to pin its version explicitly unless your new features (like activity functions) depend on specific math utilities.


36-36: Indirect Dependency Addition: gammazero/toposort v0.1.1
This dependency suggests an ordering or dependency resolution utility is in use. Verify that its usage within your refactored components (especially controller and DAO interfaces) aligns with its API, and that there are no unintentional side effects from the update.


47-47: Indirect Dependency Addition: gofrs/flock v0.8.1
'gofrs/flock' is useful for file locking. Please check if it is being leveraged in new or refactored features (for example, in handling concurrency for image uploads or caching mechanisms) and that its integration meets your concurrency requirements.


73-73: Indirect Dependency Addition: qiniu/go-sdk/v7 v7.25.2
This addition is likely tied to the new image uploader feature. Ensure that your imgUploader implementation follows the SDK’s best practices for handling errors and authentication, and that you have tested its integration with Qiniu’s API.


90-90: Indirect Dependency Addition: golang.org/x/mod v0.22.0
This dependency supports Go module operations. Verify that its fixed version is fully compatible with your build tools and that there is no conflict with the Go version specified (go 1.23.2).


92-92: Indirect Dependency Addition: golang.org/x/sync v0.10.0
This package provides useful concurrency primitives. Ensure that its version meets your project's concurrency demands, particularly within any new or refactored features.


100-100: Indirect Dependency Addition: modernc.org/fileutil v1.0.0
Confirm that the inclusion of 'modernc.org/fileutil' is necessary for your use cases, such as low-level file operations or utilities in your build process, and that it integrates well with your overall dependency graph.

Comment on lines +50 to +51
func jsonTOstrus(b []byte) (out []interface{}) {
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

Deserialization function is incomplete.
Returning nil breaks retrieval functionality. Implement JSON unmarshaling to restore the original data:

Example fix:

 func jsonTOstrus(b []byte) (out []interface{}) {
-   return nil
+   err := json.Unmarshal(b, &out)
+   if err != nil {
+       return nil
+   }
+   return out
 }
📝 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 jsonTOstrus(b []byte) (out []interface{}) {
return nil
func jsonTOstrus(b []byte) (out []interface{}) {
err := json.Unmarshal(b, &out)
if err != nil {
return nil
}
return out
}

test/main.go Outdated
Comment on lines 46 to 50
func UploadToQiNiu(file *multipart.FileHeader) (int, string) {
var AccessKey = ""
var SerectKey = ""
var Bucket = ""
var ImgUrl = ""
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid hardcoding credentials and fix the typo in “SerectKey”.

Currently, AccessKey, SerectKey, Bucket, and ImgUrl are all empty strings, but if you intend to store real credentials here, it’s insecure. Pull them from configuration or environment variables instead. Also, rename “SerectKey” to “SecretKey”.

-var SerectKey = ""
+var SecretKey = ""

Also applies to: 52-57

Comment on lines +41 to +49
func (act Activity) SetBid(ctx *gin.Context) error {
bid, err := uuid.NewUUID()
if err != nil {
act.Bid = ""
return err
}
act.Bid = bid.String()
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

Use pointer receivers when mutating struct fields.

This method sets “act.Bid” under a value receiver (func (act Activity)), so it won’t persist changes back in the caller. Convert to a pointer receiver:

-func (act Activity) SetBid(ctx *gin.Context) error {
+func (act *Activity) SetBid(ctx *gin.Context) error {

Comment on lines 12 to 39
Bid string `json:"bid" gorm:"not null;type: varchar(255);comment:绑定id;column:bid"`
//divided by function
//basic
Type string `json:"type" gorm:"not null;type: varchar(255);comment:活动类型;column:type"`
Host string `json:"host" gorm:"not null;type: varchar(255);comment:活动主办方;column:host"`
Location string `json:"location" gorm:"not null;type: varchar(255);comment:活动地点;column:location"`
IfRegister string `json:"if_register" gorm:"null;type: enum('yes','no');comment:是否需要报名;column:if_register"`
RegisterMethod string `json:"register_method" gorm:"null;type: varchar(255);comment:报名方式;column:register_method"`

//complex
StartTime string `json:"start_time" gorm:"not null;type: datetime;comment:活动开始时间;column:start_time"`
EndTime string `json:"end_time" gorm:"not null;type: datetime;comment:活动结束时间;column:end_time"`
Name string `json:"name" gorm:"not null;type: varchar(255);comment:活动名称;column:name"`

//descriptive
Capacity int `json:"capacity" gorm:"not null;type: int;comment:活动容量;column:capacity"`
Images string `json:"images" gorm:"null; type: text; comment:图片描述; column:images“`
Details string `json:"details" gorm:"not null;type: text;comment:活动详情;column:details"`

//interactive
Likes int `json:"likes" gorm:"not null;type: int;comment:活动点赞数;column:likes"`
Comments int `json:"comments" gorm:"not null;type: int;comment:活动评论数;column:comments"`

//audit
Identification string `json:"identification"`
Audition string `json:"audition"`
Pending string `json:"pending"`
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use standard time.Time fields and fix curly quotes.

“StartTime” and “EndTime” are declared as string. For better type safety and date/time validation, prefer time.Time. Also check line 28 for a typographical issue in gorm tag (mismatched quote character “).

-Images   string `json:"images" gorm:"null; type: text; comment:图片描述; column:images“`
+Images   string `json:"images" gorm:"null; type: text; comment:图片描述; column:images"`
📝 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
Bid string `json:"bid" gorm:"not null;type: varchar(255);comment:绑定id;column:bid"`
//divided by function
//basic
Type string `json:"type" gorm:"not null;type: varchar(255);comment:活动类型;column:type"`
Host string `json:"host" gorm:"not null;type: varchar(255);comment:活动主办方;column:host"`
Location string `json:"location" gorm:"not null;type: varchar(255);comment:活动地点;column:location"`
IfRegister string `json:"if_register" gorm:"null;type: enum('yes','no');comment:是否需要报名;column:if_register"`
RegisterMethod string `json:"register_method" gorm:"null;type: varchar(255);comment:报名方式;column:register_method"`
//complex
StartTime string `json:"start_time" gorm:"not null;type: datetime;comment:活动开始时间;column:start_time"`
EndTime string `json:"end_time" gorm:"not null;type: datetime;comment:活动结束时间;column:end_time"`
Name string `json:"name" gorm:"not null;type: varchar(255);comment:活动名称;column:name"`
//descriptive
Capacity int `json:"capacity" gorm:"not null;type: int;comment:活动容量;column:capacity"`
Images string `json:"images" gorm:"null; type: text; comment:图片描述; column:images`
Details string `json:"details" gorm:"not null;type: text;comment:活动详情;column:details"`
//interactive
Likes int `json:"likes" gorm:"not null;type: int;comment:活动点赞数;column:likes"`
Comments int `json:"comments" gorm:"not null;type: int;comment:活动评论数;column:comments"`
//audit
Identification string `json:"identification"`
Audition string `json:"audition"`
Pending string `json:"pending"`
}
Bid string `json:"bid" gorm:"not null;type: varchar(255);comment:绑定id;column:bid"`
//divided by function
//basic
Type string `json:"type" gorm:"not null;type: varchar(255);comment:活动类型;column:type"`
Host string `json:"host" gorm:"not null;type: varchar(255);comment:活动主办方;column:host"`
Location string `json:"location" gorm:"not null;type: varchar(255);comment:活动地点;column:location"`
IfRegister string `json:"if_register" gorm:"null;type: enum('yes','no');comment:是否需要报名;column:if_register"`
RegisterMethod string `json:"register_method" gorm:"null;type: varchar(255);comment:报名方式;column:register_method"`
//complex
StartTime string `json:"start_time" gorm:"not null;type: datetime;comment:活动开始时间;column:start_time"`
EndTime string `json:"end_time" gorm:"not null;type: datetime;comment:活动结束时间;column:end_time"`
Name string `json:"name" gorm:"not null;type: varchar(255);comment:活动名称;column:name"`
//descriptive
Capacity int `json:"capacity" gorm:"not null;type: int;comment:活动容量;column:capacity"`
Images string `json:"images" gorm:"null; type: text; comment:图片描述; column:images"`
Details string `json:"details" gorm:"not null;type: text;comment:活动详情;column:details"`
//interactive
Likes int `json:"likes" gorm:"not null;type: int;comment:活动点赞数;column:likes"`
Comments int `json:"comments" gorm:"not null;type: int;comment:活动评论数;column:comments"`
//audit
Identification string `json:"identification"`
Audition string `json:"audition"`
Pending string `json:"pending"`
}

Comment on lines 37 to 43
func (ad ActDao) CreateAct(c *gin.Context, a model.Activity) error {
if ad.CheckExist(c, a) {
return errors.New("activity exist")
} else {
return ad.db.Create(&a).Error
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check for concurrency or race conditions on existence check.

“CheckExist” → “CreateAct” logic can be susceptible to race conditions if two processes attempt to create the same activity concurrently. Consider using unique DB constraints for robust safety.

Comment on lines +100 to 107
func (ad ActDao) FindActByTime(c *gin.Context, start string, end string) ([]model.Activity, error) {
var as []model.Activity
err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Error
if err != nil {
return nil, err
}
return as, 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

Fix missing .Find(&as).

The query uses “err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Error” but never fetches the results into the slice. Usually you’d do:

-err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Error
+err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Find(&as).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 (ad ActDao) FindActByTime(c *gin.Context, start string, end string) ([]model.Activity, error) {
var as []model.Activity
err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Error
if err != nil {
return nil, err
}
return as, nil
}
func (ad ActDao) FindActByTime(c *gin.Context, start string, end string) ([]model.Activity, error) {
var as []model.Activity
err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Find(&as).Error
if err != nil {
return nil, err
}
return as, nil
}

Comment on lines 44 to 82
func (ac ActController) NewAct() gin.HandlerFunc {
return func(ctx *gin.Context) {
var act model.Activity
//获取用户填写信息
err := ctx.ShouldBindJSON(&act)
if err != nil {
tools.ReturnMSG(ctx, err.Error(), nil)
return
}

//处理用户上传图像给图床,返回储存url
urls, err := ac.iu.ProcessImg(ctx)
if err != nil {
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
act.Images = urls

//创建首帖关联id
err = act.SetBid(ctx)
if err != nil {
tools.ReturnMSG(ctx, err.Error(), nil)
return
}

//防止重复创建活动
if ac.ad.CheckExist(ctx, act) {
tools.ReturnMSG(ctx, "error exist", nil)
return
} else {
err := ac.ad.CreateAct(ctx, act)
if err != nil {
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
tools.ReturnMSG(ctx, "success", act)
}
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a database constraint for concurrency safety and improved error messages.
While you verify an activity’s existence before creation, this check can still fail in concurrent scenarios. A unique constraint in the database or an equivalent safeguard will ensure that duplicate activities can’t be created simultaneously. Additionally, “error exist” is not very user-friendly; consider providing a clearer message to end users.

Comment on lines 142 to 147
func (ac ActController) FindActByLocation() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("type")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use matching query param names in location, ifSignup, and isForeign lookups.
All three methods use ctx.Query("type") instead of a more descriptive query param (e.g., "location", "signup", "foreign"). This looks like a copy-paste slip. Adjust them to match the requested field.

 func (ac ActController) FindActByLocation() gin.HandlerFunc {
   return func(ctx *gin.Context) {
-    target := ctx.Query("type")
+    target := ctx.Query("location")
     if target == "" {
       ...
     }
     ...
   }
 }

 ... similarly for FindActByIfSignup() and FindActByIsForeign() ...

Also applies to: 159-165, 176-182

Comment on lines 37 to 49
func (iu ImgUploader) ProcessImg(ctx *gin.Context) (string, error) {
fhs := iu.GetFile(ctx)
var urls string
for _, f := range fhs {
url, err := iu.Upload(ctx, f)
if err != nil {
return "", nil
}
//eg."abc.com; qwe.com; "
urls = url + "; "
}
return urls, 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

Return the actual error when image uploads fail.
At line 43, “return "", nil” discards the original error, which hinders debugging. Forward the error instead:

url, err := iu.Upload(ctx, f)
if err != nil {
-  return "", nil
+  return "", err
}
📝 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 (iu ImgUploader) ProcessImg(ctx *gin.Context) (string, error) {
fhs := iu.GetFile(ctx)
var urls string
for _, f := range fhs {
url, err := iu.Upload(ctx, f)
if err != nil {
return "", nil
}
//eg."abc.com; qwe.com; "
urls = url + "; "
}
return urls, nil
}
func (iu ImgUploader) ProcessImg(ctx *gin.Context) (string, error) {
fhs := iu.GetFile(ctx)
var urls string
for _, f := range fhs {
url, err := iu.Upload(ctx, f)
if err != nil {
return "", err
}
//eg."abc.com; qwe.com; "
urls = url + "; "
}
return urls, nil
}

go.mod Outdated
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Indirect Dependency Addition: satori/go.uuid v1.2.0
Notably, your mod file already includes 'github.com/google/uuid v1.4.0' as a direct dependency. Having both UUID libraries may lead to inconsistencies in UUID generation. Consider consolidating to one library or clearly documenting the intended usage for each.

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.

问题不大,一点点,回头改下

Copy link
Contributor

Choose a reason for hiding this comment

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

从go语言的接口规范来看一般接口在调用方定义,因为不管底层实现如何,调用方需要的方法大多数情况只是实现的方法,这种在被调用方定义的方式耦合度比较高。可以看看去年陈诚分享的文章

iu ImgUploaderHdl
}

func NewActController(ad dao.ActDaoHdl, jwth middleware.ClaimsHdl, ch cache.CacheHdl, iu ImgUploaderHdl) ActControllerHdl {
Copy link
Contributor

Choose a reason for hiding this comment

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

这种直接在Controller里面注入dao的方式不是不行,但是对于复杂项目来说,可能抽象程度不高耦合会比较严重,有时候也不太方便,一般多一个service层,Controller层只负责解析请求,处理响应和调用service这些职责

FindActByDate() gin.HandlerFunc
}

type ActController struct {
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.

后端传图片你就等着测试的时候服务器爆了吧

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: 18

🔭 Outside diff range comments (1)
internal/dao/user_dao.go (1)

24-26: Implement missing method logic and error handling.

Several methods have empty or incomplete implementations:

  • UpdateAvatar
  • UpdateUsername
  • Create
  • CheckUserExist

This could lead to runtime issues if deployed. Each method should:

  1. Implement the required business logic
  2. Include proper error handling
  3. Add logging for debugging

For example, the Create method should:

func (dao *UserDAO) Create(ctx context.Context, ssid string) error {
    user := &model.User{
        StudentID: ssid,
        Username:  ssid, // As per comment: default username is studentid
        // Set default avatar
    }
    
    if err := dao.db.WithContext(ctx).Create(user).Error; err != nil {
        return fmt.Errorf("failed to create user: %w", err)
    }
    return nil
}

Also applies to: 28-30, 32-35, 37-39

🧹 Nitpick comments (10)
internal/service/user_service.go (2)

31-32: Unused fields "adh" and "pdh".
The fields “adh dao.ActDaoHdl” and “pdh dao.PostDaoHdl” are declared but never assigned or used in your constructor. If you need them, update your constructor to initialize them. Otherwise, remove them to prevent confusion.


129-129: Use request = request.WithContext(ctx) if you want to propagate the context.
The call to request.WithContext(ctx) returns a new request object. Currently, it’s not assigned back, so you lose the context reference.

- request.WithContext(ctx)
+ request = request.WithContext(ctx)
internal/dao/user_dao.go (1)

9-14: Consider adding method documentation.

The interface methods lack documentation explaining their purpose, parameters, and return values. This makes it harder for other developers to understand and use the interface correctly.

Example documentation:

type UserDAOHdl interface {
    // UpdateAvatar updates the user's avatar.
    // Returns an error if the update fails.
    UpdateAvatar(context.Context) error

    // UpdateUsername updates the user's username.
    // Returns an error if the update fails.
    UpdateUsername(context.Context) error

    // Create creates a new user with the given student ID.
    // The username defaults to the student ID.
    // Returns an error if the creation fails.
    Create(context.Context, string) error

    // CheckUserExist checks if a user with the given ID exists.
    // Returns true if the user exists, false otherwise.
    CheckUserExist(context.Context, int) bool
}
internal/controller/user_controller.go (2)

8-17: Add method documentation and consider adding request/response types.

The interface methods lack documentation explaining their purpose, parameters, and return values. Additionally, consider defining request/response types for better type safety and documentation.

Example:

// UserControllerHdl handles HTTP requests for user-related operations
type UserControllerHdl interface {
    // Login handles user authentication
    // POST /login
    // Request: {"student_id": string, "password": string}
    // Response: {"token": string}
    Login() gin.HandlerFunc
    
    // Additional method documentation...
}

// Define request/response types
type LoginRequest struct {
    StudentID string `json:"student_id" binding:"required"`
    Password  string `json:"password" binding:"required"`
}

type LoginResponse struct {
    Token string `json:"token"`
}

19-22: Consider adding configuration options.

The UserController struct could benefit from additional configuration options such as:

  • Rate limiting settings
  • Session timeout duration
  • Password policy settings

Example:

type UserController struct {
    e      *gin.Engine
    ush    service.UserServiceHdl
    config UserControllerConfig
}

type UserControllerConfig struct {
    RateLimit         int           `json:"rate_limit"`
    SessionTimeout    time.Duration `json:"session_timeout"`
    MinPasswordLength int           `json:"min_password_length"`
}
internal/router/activity_router.go (1)

27-42: Enhance route documentation.

Consider adding more descriptive comments for each route group to explain their purpose and expected parameters.

Replace the current numeric comments with descriptive ones:

-		//1
+		// Create new activities
 		act.POST("/new", ar.ech.NewAct())
 		act.POST("/draft", ar.ech.NewDraft())

-		//0 or 1
+		// Filter activities by various criteria
 		act.GET("/host", ar.ech.FindActByHost())
 		act.GET("/type", ar.ech.FindActByType())
 		act.GET("/location", ar.ech.FindActByLocation())
 		act.GET("/signup", ar.ech.FindActByIfSignup())
 		act.GET("/foreign", ar.ech.FindActByIsForeign())

-		//more complex
+		// Search activities by temporal and textual criteria
 		act.GET("/time", ar.ech.FindActByTime())
 		act.GET("/name", ar.ech.FindActByName())
 		act.GET("/:date", ar.ech.FindActByDate())
internal/service/post_service.go (1)

57-63: Simplify error handling in DeletePost.

The function can be simplified by directly returning the error from DeletePost.

Apply this diff to simplify the code:

 func (ps *PostService) DeletePost(c *gin.Context, post *model.Post) error {
-	err := ps.pdh.DeletePost(c, post)
-	if err != nil {
-		return err
-	}
-	return nil
+	return ps.pdh.DeletePost(c, post)
 }
internal/controller/activity_controller.go (3)

45-83: Improve error messages and flatten the control flow.

While the error handling is thorough, consider these improvements:

  1. Make error messages more descriptive (e.g., "error exist" -> "activity already exists")
  2. Flatten the control flow by removing the else block

Here's the suggested refactoring:

 if ac.ad.CheckExist(ctx, act) {
-			tools.ReturnMSG(ctx, "error exist", nil)
+			tools.ReturnMSG(ctx, "activity already exists", nil)
 			return
-		} else {
-			err := ac.ad.CreateAct(ctx, act)
-			if err != nil {
-				tools.ReturnMSG(ctx, err.Error(), nil)
-				return
-			}
-			tools.ReturnMSG(ctx, "success", act)
-		}
+		}
+		
+		err := ac.ad.CreateAct(ctx, act)
+		if err != nil {
+			tools.ReturnMSG(ctx, err.Error(), nil)
+			return
+		}
+		tools.ReturnMSG(ctx, "success", act)

194-207: Improve time handling robustness.

The time handling could be improved:

  1. Validate time format before appending ":00"
  2. Handle potential time parsing errors

Here's a suggested improvement:

-		start := ctx.Query("start_time") + ":00"
-		end := ctx.Query("end_time") + ":00"
+		start := ctx.Query("start_time")
+		end := ctx.Query("end_time")
+		
+		if start == "" || end == "" {
+			tools.ReturnMSG(ctx, "start_time and end_time cannot be empty", nil)
+			return
+		}
+		
+		// Validate time format (HH:mm)
+		if !tools.IsValidTimeFormat(start) || !tools.IsValidTimeFormat(end) {
+			tools.ReturnMSG(ctx, "invalid time format, expected HH:mm", nil)
+			return
+		}
+		
+		start = start + ":00"
+		end = end + ":00"

109-232: Consider implementing a generic finder to reduce duplication.

The finder methods share similar patterns and could benefit from a generic implementation to reduce code duplication.

Here's a suggested approach using a generic finder:

// Generic finder function type
type FinderFunc func(ctx *gin.Context, target string) ([]model.Activity, error)

// Generic handler generator
func (ac ActController) genericFinder(paramName string, finder FinderFunc) gin.HandlerFunc {
    return func(ctx *gin.Context) {
        target := ctx.Query(paramName)
        if target == "" {
            tools.ReturnMSG(ctx, "query cannot be nil", nil)
            return
        }

        as, err := finder(ctx, target)
        if err != nil {
            tools.ReturnMSG(ctx, err.Error(), nil)
            return
        }
        tools.ReturnMSG(ctx, "success", as)
    }
}

// Example usage:
func (ac ActController) FindActByHost() gin.HandlerFunc {
    return ac.genericFinder("host", ac.ad.FindActByHost)
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 721d689 and 99975b4.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (17)
  • .gitignore (1 hunks)
  • go.mod (5 hunks)
  • internal/controller/activity_controller.go (1 hunks)
  • internal/controller/post_controller.go (1 hunks)
  • internal/controller/user_controller.go (1 hunks)
  • internal/dao/post_dao.go (1 hunks)
  • internal/dao/user_dao.go (2 hunks)
  • internal/model/post.go (1 hunks)
  • internal/model/user.go (1 hunks)
  • internal/router/activity_router.go (1 hunks)
  • internal/router/post_router.go (1 hunks)
  • internal/router/user_router.go (1 hunks)
  • internal/service/activity_service.go (1 hunks)
  • internal/service/imgUploader.go (1 hunks)
  • internal/service/post_service.go (1 hunks)
  • internal/service/user_service.go (1 hunks)
  • test/main.go (0 hunks)
💤 Files with no reviewable changes (1)
  • test/main.go
✅ Files skipped from review due to trivial changes (1)
  • internal/service/activity_service.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • .gitignore
🧰 Additional context used
🪛 golangci-lint (1.62.2)
internal/service/post_service.go

6-6: could not import github.com/raiki02/EG/internal/dao (-: # github.com/raiki02/EG/internal/dao
internal/dao/user_dao.go:5:2: "github.com/raiki02/EG/internal/model" imported and not used
internal/dao/user_dao.go:35:1: missing return
internal/dao/user_dao.go:39:1: missing return)

(typecheck)

internal/service/user_service.go

91-91: undefined: context

(typecheck)


112-112: undefined: context

(typecheck)


49-49: missing return

(typecheck)


53-53: missing return

(typecheck)


57-57: missing return

(typecheck)


135-135: undefined: errors

(typecheck)


136-136: undefined: errors

(typecheck)


141-141: undefined: errors

(typecheck)

🔇 Additional comments (28)
go.mod (9)

6-6: Direct Dependency Addition: github.com/IBM/sarama v1.45.0
This change converts sarama from an indirect to a direct dependency. Please verify that this update is intentional for supporting the new activity functions and that any previous Kafka-related logic has been fully refactored or removed.


11-11: New Dependency Addition: github.com/qiniu/go-sdk/v7 v7.25.2
Ensure that this dependency is indeed required—potentially for features like image uploading—and that its version aligns with your overall dependency strategy.


20-20: New Indirect Dependency: github.com/BurntSushi/toml v1.4.0 // indirect
This package, commonly used for TOML configuration parsing, is now referenced indirectly. Confirm that its inclusion is necessary for the new features and consider promoting it to a direct dependency if it becomes a core part of configuration handling.


24-24: New Indirect Dependency: github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
Note that the version in use dates back to 2016. Please verify if this is still the best-maintained option or if there’s an updated alternative that you might prefer to adopt.


37-37: New Indirect Dependency: github.com/gammazero/toposort v0.1.1 // indirect
If topological sorting is required in your new modules, this dependency appears to fulfill that need. Confirm its usage meets current performance and functionality expectations.


48-48: New Indirect Dependency: github.com/gofrs/flock v0.8.1 // indirect
This file locking library is now included indirectly. Verify that its functionality is leveraged in your code and that no more common or updated alternatives are available for this purpose.


89-89: New Indirect Dependency: golang.org/x/mod v0.22.0 // indirect
Ensure that this version of the module handling library fulfills your requirements and remains consistent with other x/* packages in your project.


91-91: New Indirect Dependency: golang.org/x/sync v0.10.0 // indirect
This addition supports synchronization utilities; please confirm that its version is compatible with your other dependencies and that its functionalities are needed by your new features.


99-99: New Indirect Dependency: modernc.org/fileutil v1.0.0 // indirect
Check whether this file utility package is essential for your expanded functionality and if its inclusion positively impacts your build performance or file management routines.

internal/model/user.go (2)

5-5: JSON tag addition is appropriate.
Adding “json:"id"” ensures the ID is serialized in JSON.


12-12: Enhanced default avatar URL.
Switching from a placeholder string to this URL is fine. Just be sure the link remains accessible and relevant.

internal/router/user_router.go (1)

24-30: Routes updated to match new controller signatures.
The new GET/POST endpoints (including /search/act and /search/post) align with the expanded interface methods. This routing setup appears consistent and correct.

internal/router/post_router.go (4)

3-6: No issues with imports.
These imported packages appear valid and consistent with your usage.


13-15: Struct fields look good.
Storing the gin.Engine pointer and the PostController handler in the PostRouter struct is a straightforward approach to ensure the router can register the necessary routes without extraneous global context.


17-21: Clear and concise constructor.
Initializes the router fields correctly and returns the interface as expected.


24-31: Verify the handler function references vs. calls.
If the methods (GetAllPost, CreatePost, etc.) return gin.HandlerFunc, it's correct to invoke them here. Otherwise, the parentheses might be unintended and should be removed. Please confirm the method signatures.

internal/dao/post_dao.go (7)

3-7: Imports are correctly declared.
No concerns with these dependencies for GORM and model usage.


10-14: Interface definition aligned with post-related data operations.
The methods offer clear responsibilities.


16-18: Struct definition ties GORM DB to PostDao.
Encapsulating the *gorm.DB field is a common pattern for data access objects.


20-24: Factory function pattern is well-structured.
Returning an interface instance allows flexible testing and mocking.


26-33: Fetching all posts.
Logic is straightforward, with a direct call to db.Find.


35-41: Post creation logic looks correct.
Create is properly invoked on the passed-in model, error is handled.


52-57: Deletion logic is straightforward.
Deletes the given post instance if found.

internal/router/activity_router.go (1)

24-26: LGTM! Well-organized route grouping.

The routes are logically grouped under the "act" prefix, following RESTful conventions.

internal/controller/activity_controller.go (4)

13-13: Consider implementing the filter pattern for find functions.

The TODO comment suggests implementing a filter pattern for find functions. This would improve code maintainability and reduce duplication across similar find methods.

Would you like me to provide an example implementation of the filter pattern for these find functions?


14-27: LGTM! Well-organized interface with clear method grouping.

The interface is well-structured with logically grouped methods for activity creation and finding activities by various criteria.


29-43: LGTM! Clean dependency injection pattern.

The struct and constructor follow good practices:

  • Clear field names
  • Proper dependency injection
  • Complete initialization

85-107: LGTM! Well-documented draft functionality.

The comments clearly explain the draft logic, its purpose, and limitations (e.g., not saving images to conserve space).

Comment on lines 47 to 49
func (s *UserService) CreateUser(ctx *gin.Context, user model.User) error {

}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Function “CreateUser” is incomplete and missing a return.
This function promises to return an error but never does. Either implement correct logic or return a placeholder value.

 func (s *UserService) CreateUser(ctx *gin.Context, user model.User) error {
-}
+    // TODO: Implement user creation logic.
+    return nil
 }
📝 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 (s *UserService) CreateUser(ctx *gin.Context, user model.User) error {
}
func (s *UserService) CreateUser(ctx *gin.Context, user model.User) error {
// TODO: Implement user creation logic.
return nil
}
🧰 Tools
🪛 golangci-lint (1.62.2)

49-49: missing return

(typecheck)

Comment on lines 59 to 61
func (s *UserService) GetUserInfo(ctx *gin.Context, studentId string) (model.User, error) {

}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Function “GetUserInfo” has no return statement.
The return type is (model.User, error) but nothing is returned.

 func (s *UserService) GetUserInfo(ctx *gin.Context, studentId string) (model.User, error) {
-}
+    // TODO: Fetch user information from DAO, or handle user not found, etc.
+    return model.User{}, nil
 }
📝 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 (s *UserService) GetUserInfo(ctx *gin.Context, studentId string) (model.User, error) {
}
func (s *UserService) GetUserInfo(ctx *gin.Context, studentId string) (model.User, error) {
// TODO: Fetch user information from DAO, or handle user not found, etc.
return model.User{}, nil
}

Comment on lines 63 to 65
func (s *UserService) UpdateAvatar(ctx *gin.Context) (string, error) {

}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Function “UpdateAvatar” is incomplete.
You must return (string, error) based on your function signature.

 func (s *UserService) UpdateAvatar(ctx *gin.Context) (string, error) {
-}
+    // TODO: Implement avatar update logic.
+    return "", nil
 }
📝 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 (s *UserService) UpdateAvatar(ctx *gin.Context) (string, error) {
}
func (s *UserService) UpdateAvatar(ctx *gin.Context) (string, error) {
// TODO: Implement avatar update logic.
return "", nil
}

Comment on lines 51 to 53
func (s *UserService) Login(ctx *gin.Context, studentId string, password string) error {

}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Function “Login” is incomplete and missing a return.
You must return an error (or nil) to satisfy the function signature.

 func (s *UserService) Login(ctx *gin.Context, studentId string, password string) error {
-}
+    // TODO: Implement login logic.
+    return nil
 }
📝 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 (s *UserService) Login(ctx *gin.Context, studentId string, password string) error {
}
func (s *UserService) Login(ctx *gin.Context, studentId string, password string) error {
// TODO: Implement login logic.
return nil
}
🧰 Tools
🪛 golangci-lint (1.62.2)

53-53: missing return

(typecheck)

Comment on lines 55 to 57
func (s *UserService) Logout(ctx *gin.Context) error {

}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Function “Logout” is incomplete and missing a return.
Please ensure the method returns an error or nil.

 func (s *UserService) Logout(ctx *gin.Context) error {
-}
+    // TODO: Implement logout logic.
+    return nil
 }
📝 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 (s *UserService) Logout(ctx *gin.Context) error {
}
func (s *UserService) Logout(ctx *gin.Context) error {
// TODO: Implement logout logic.
return nil
}
🧰 Tools
🪛 golangci-lint (1.62.2)

57-57: missing return

(typecheck)

Comment on lines +38 to +53
func (pc *PostController) CreatePost() gin.HandlerFunc {
return func(c *gin.Context) {
var post model.Post
err := c.ShouldBindJSON(&post)
if err != nil {
tools.ReturnMSG(c, err.Error(), nil)
return
}
err = pc.ps.CreatePost(c, &post)
if err != nil {
tools.ReturnMSG(c, err.Error(), nil)
return
}
tools.ReturnMSG(c, "success", post)
}
}
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 input validation and response messages in CreatePost.

The current implementation lacks input validation and uses generic success messages.

Apply this diff to add validation and improve messages:

 func (pc *PostController) CreatePost() gin.HandlerFunc {
 	return func(c *gin.Context) {
 		var post model.Post
 		err := c.ShouldBindJSON(&post)
 		if err != nil {
-			tools.ReturnMSG(c, err.Error(), nil)
+			tools.ReturnMSG(c, "Invalid post data: "+err.Error(), nil)
 			return
 		}
+		if post.Title == "" {
+			tools.ReturnMSG(c, "Post title is required", nil)
+			return
+		}
 		err = pc.ps.CreatePost(c, &post)
 		if err != nil {
 			tools.ReturnMSG(c, err.Error(), nil)
 			return
 		}
-		tools.ReturnMSG(c, "success", post)
+		tools.ReturnMSG(c, "Post created successfully", post)
 	}
 }
📝 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 (pc *PostController) CreatePost() gin.HandlerFunc {
return func(c *gin.Context) {
var post model.Post
err := c.ShouldBindJSON(&post)
if err != nil {
tools.ReturnMSG(c, err.Error(), nil)
return
}
err = pc.ps.CreatePost(c, &post)
if err != nil {
tools.ReturnMSG(c, err.Error(), nil)
return
}
tools.ReturnMSG(c, "success", post)
}
}
func (pc *PostController) CreatePost() gin.HandlerFunc {
return func(c *gin.Context) {
var post model.Post
err := c.ShouldBindJSON(&post)
if err != nil {
tools.ReturnMSG(c, "Invalid post data: "+err.Error(), nil)
return
}
if post.Title == "" {
tools.ReturnMSG(c, "Post title is required", nil)
return
}
err = pc.ps.CreatePost(c, &post)
if err != nil {
tools.ReturnMSG(c, err.Error(), nil)
return
}
tools.ReturnMSG(c, "Post created successfully", post)
}
}

Comment on lines +37 to +49
func (iu *ImgUploader) ProcessImg(ctx *gin.Context) (string, error) {
fhs := iu.GetFile(ctx)
var urls string
for _, f := range fhs {
url, err := iu.Upload(ctx, f)
if err != nil {
return "", nil
}
//eg."abc.com; qwe.com; "
urls = url + "; "
}
return urls, 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

Fix error handling in ProcessImg.

The function returns nil error when upload fails, which is incorrect.

Apply this diff to fix the error handling:

 func (iu *ImgUploader) ProcessImg(ctx *gin.Context) (string, error) {
 	fhs := iu.GetFile(ctx)
+	if len(fhs) == 0 {
+		return "", errors.New("no valid image files found")
+	}
 	var urls string
 	for _, f := range fhs {
 		url, err := iu.Upload(ctx, f)
 		if err != nil {
-			return "", nil
+			return "", fmt.Errorf("failed to upload %s: %w", f.Filename, err)
 		}
-		//eg."abc.com; qwe.com; "
 		urls = url + "; "
 	}
 	return urls, nil
 }
📝 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 (iu *ImgUploader) ProcessImg(ctx *gin.Context) (string, error) {
fhs := iu.GetFile(ctx)
var urls string
for _, f := range fhs {
url, err := iu.Upload(ctx, f)
if err != nil {
return "", nil
}
//eg."abc.com; qwe.com; "
urls = url + "; "
}
return urls, nil
}
func (iu *ImgUploader) ProcessImg(ctx *gin.Context) (string, error) {
fhs := iu.GetFile(ctx)
if len(fhs) == 0 {
return "", errors.New("no valid image files found")
}
var urls string
for _, f := range fhs {
url, err := iu.Upload(ctx, f)
if err != nil {
return "", fmt.Errorf("failed to upload %s: %w", f.Filename, err)
}
urls = url + "; "
}
return urls, nil
}

Comment on lines +94 to +95
key := "EventGlide/" + file.Filename // 上传路径,如果当前目录中已存在相同文件,则返回上传失败错误
err = formUploader.Put(context.Background(), &ret, upToken, key, src, file.Size, &putExtra)
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 file size and type validation before upload.

The current implementation lacks file size limits and proper type validation, which could lead to security issues.

Apply this diff to add validation:

+const maxFileSize = 5 * 1024 * 1024 // 5MB

 func (iu *ImgUploader) Upload(ctx *gin.Context, file *multipart.FileHeader) (string, error) {
+	if file.Size > maxFileSize {
+		return "", fmt.Errorf("file size %d exceeds maximum allowed size of %d", file.Size, maxFileSize)
+	}
+	contentType := file.Header.Get("Content-Type")
+	if !strings.Contains(allowedTypes, contentType) {
+		return "", fmt.Errorf("file type %s is not allowed", contentType)
+	}
 	src, err := file.Open()

Comment on lines +51 to +65
// TODO: 不优雅。感觉跑不起来
func (iu *ImgUploader) GetFile(ctx *gin.Context) []*multipart.FileHeader {
base := "file"
var fhs []*multipart.FileHeader
//获取file0 - file8 的用户传入图片(如果存在)
for i := 0; i < 9; i++ {
filename := base + strconv.Itoa(i)
file, err := ctx.FormFile(filename)
if err != nil {
return nil
}
fhs = append(fhs, file)
}
return fhs
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Refactor GetFile implementation for better reliability.

The current implementation has several issues:

  1. Returns nil on first error, potentially missing valid files
  2. Hard-coded limit of 9 files
  3. No file type validation

Apply this diff to improve the implementation:

-// TODO: 不优雅。感觉跑不起来
+const maxFiles = 9
+const allowedTypes = "image/jpeg,image/png,image/gif"

 func (iu *ImgUploader) GetFile(ctx *gin.Context) []*multipart.FileHeader {
-	base := "file"
 	var fhs []*multipart.FileHeader
-	//获取file0 - file8 的用户传入图片(如果存在)
-	for i := 0; i < 9; i++ {
-		filename := base + strconv.Itoa(i)
-		file, err := ctx.FormFile(filename)
-		if err != nil {
-			return nil
+	form, err := ctx.MultipartForm()
+	if err != nil {
+		return nil
+	}
+	for _, files := range form.File {
+		for _, file := range files {
+			if len(fhs) >= maxFiles {
+				return fhs
+			}
+			contentType := file.Header.Get("Content-Type")
+			if !strings.Contains(allowedTypes, contentType) {
+				continue
+			}
+			fhs = append(fhs, file)
 		}
-		fhs = append(fhs, file)
 	}
 	return fhs
 }

Comment on lines 143 to 158
func (ac ActController) FindActByLocation() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("type")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
//不能因为缓存as为空而去数据库查找,因为可能数据库中没有这个数据

//去数据库
activities, err = a.adh.ListActivityByTime(c, time)
as, err := ac.ad.FindActByLocation(ctx, target)
if err != nil {
tools.ReturnMSG(c, "error", err)
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
err = a.c.Set(c, time, activities)
if err != nil {
tools.ReturnMSG(c, "error", err)
return
}
tools.ReturnMSG(c, "success", activities)
tools.ReturnMSG(ctx, "success", as)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect query parameter name in FindActByLocation.

The method uses "type" instead of "location" for the query parameter.

Apply this fix:

-		target := ctx.Query("type")
+		target := ctx.Query("location")
📝 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 (ac ActController) FindActByLocation() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("type")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
//不能因为缓存as为空而去数据库查找,因为可能数据库中没有这个数据
//去数据库
activities, err = a.adh.ListActivityByTime(c, time)
as, err := ac.ad.FindActByLocation(ctx, target)
if err != nil {
tools.ReturnMSG(c, "error", err)
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
err = a.c.Set(c, time, activities)
if err != nil {
tools.ReturnMSG(c, "error", err)
return
}
tools.ReturnMSG(c, "success", activities)
tools.ReturnMSG(ctx, "success", as)
}
}
func (ac ActController) FindActByLocation() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("location")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
as, err := ac.ad.FindActByLocation(ctx, target)
if err != nil {
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
tools.ReturnMSG(ctx, "success", as)
}
}

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: 5

♻️ Duplicate comments (5)
internal/dao/post_dao.go (1)

44-51: ⚠️ Potential issue

Fix the query condition to match the intended search.

The query uses a literal "%name%" instead of the parameter value.

-	err := pd.db.Where("name = ?", "%name%").Find(&posts).Error
+	err := pd.db.Where("name LIKE ?", "%"+name+"%").Find(&posts).Error
internal/cache/redis.go (1)

50-51: ⚠️ Potential issue

Deserialization function is incomplete.

The jsonTOstrus function returns nil which breaks retrieval functionality.

Implement proper JSON unmarshaling:

-func jsonTOstrus(b []byte) (out []interface{}) {
-   return nil
+func jsonTOstrus(b []byte) (out []interface{}) {
+   err := json.Unmarshal(b, &out)
+   if err != nil {
+       return nil
+   }
+   return out
}
internal/model/activity.go (1)

42-50: ⚠️ Potential issue

Use pointer receivers when mutating struct fields.

This method sets "act.Bid" under a value receiver (func (act Activity)), so it won't persist changes back in the caller.

Apply this diff:

-func (act Activity) SetBid(ctx *gin.Context) error {
+func (act *Activity) SetBid(ctx *gin.Context) error {
internal/dao/activity_dao.go (2)

120-127: ⚠️ Potential issue

Fix missing .Find(&as) in FindActByTime.

The query never fetches the results into the slice.

Apply this diff:

-err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Error
+err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Find(&as).Error

129-145: ⚠️ Potential issue

Fix the LIKE clauses in FindActByName and FindActByDate.

Both methods use hardcoded strings instead of the actual variables.

Apply these diffs:

-err := ad.db.Where("name like ?", "%n%").Find(&as).Error
+err := ad.db.Where("name like ?", "%" + n + "%").Find(&as).Error

-err := ad.db.Where("start_time like ?", "%d%").Find(&as).Error
+err := ad.db.Where("start_time like ?", "%" + d + "%").Find(&as).Error
🧹 Nitpick comments (13)
tools/tool.go (4)

10-13: Consider handling conversion errors.

The function silently ignores potential conversion errors, which could lead to unexpected behavior when invalid strings are provided.

Consider this improvement:

-func StrToInt(str string) int {
-	res, _ := strconv.Atoi(str)
-	return res
+func StrToInt(str string) (int, error) {
+	return strconv.Atoi(str)
}

15-21: Consider propagating JSON marshaling errors.

Returning an empty string on marshaling errors could mask important issues. Consider propagating the error to the caller for better error handling.

Consider this improvement:

-func Marshal(v interface{}) string {
+func Marshal(v interface{}) (string, error) {
 	res, err := json.Marshal(v)
 	if err != nil {
-		return ""
+		return "", err
 	}
-	return string(res)
+	return string(res), nil
}

23-29: Consider propagating JSON unmarshaling errors.

Returning nil on unmarshaling errors could mask important issues. Consider propagating the error to the caller for better error handling.

Consider this improvement:

-func Unmarshal(data []byte, v interface{}) interface{} {
+func Unmarshal(data []byte, v interface{}) (interface{}, error) {
 	err := json.Unmarshal(data, v)
 	if err != nil {
-		return nil
+		return nil, err
 	}
-	return v
+	return v, nil
}

31-40: LGTM! Consider adding documentation.

The changes to the function signature and return type are well-thought-out improvements that provide more flexibility and better align with gin's response handling. The nested response structure under the "response" key provides a consistent format.

Consider adding documentation to explain the function's purpose and response structure:

+// ReturnMSG creates a standardized response format for API endpoints.
+// It takes a gin context for status code, a message string, and optional data arguments.
+// Returns a map with a nested response object containing code, message, and data.
 func ReturnMSG(c *gin.Context, msg string, res ...interface{}) map[string]interface{} {
internal/dao/post_dao.go (2)

36-42: Simplify error handling.

The error handling can be more concise by directly returning the error.

 func (pd *PostDao) CreatePost(ctx context.Context, post *model.Post) error {
-	err := pd.db.Create(post).Error
-	if err != nil {
-		return err
-	}
-	return nil
+	return pd.db.Create(post).Error
 }

53-59: Simplify error handling.

Similar to CreatePost, the error handling can be more concise.

 func (pd *PostDao) DeletePost(ctx context.Context, post *model.Post) error {
-	err := pd.db.Delete(post).Error
-	if err != nil {
-		return err
-	}
-	return nil
+	return pd.db.Delete(post).Error
 }
internal/cache/redis.go (1)

11-11: Fix typo in TODO comment.

The word "scpoe" should be "scope".

-// TODO: scpoe: 活动,帖子,jwt
+// TODO: scope: 活动,帖子,jwt
internal/dao/user_dao.go (1)

9-15: Avoid framework-specific dependencies in the DAO layer.

Using gin.Context in the DAO layer tightly couples it to the Gin framework, breaking separation of concerns.

Consider using context.Context instead and passing required values through it:

type UserDAOHdl interface {
-   UpdateAvatar(*gin.Context, string, string) error
+   UpdateAvatar(context.Context, string, string) error
    // Apply similar changes to other methods
}
internal/middleware/jwt.go (1)

24-24: Function name doesn't match type name.

The constructor is still named NewClaimsHdl but returns JwtHdl.

-func NewClaimsHdl(rdb *redis.Client) JwtHdl {
+func NewJwtHdl(rdb *redis.Client) JwtHdl {
internal/controller/user_controller.go (2)

9-9: Translate comment to English for better maintainability.

The Chinese comment "user这边操作数据库不频繁" should be translated to English.

-// user这边操作数据库不频繁
+// Database operations for users are infrequent

32-138: Improve error messages with more details.

While the handlers are well-implemented with proper validation and error handling, the error messages could be more descriptive by including the actual error details. This would help with debugging and troubleshooting.

Example improvement for the Login handler:

 func (uc *UserController) Login() gin.HandlerFunc {
     return func(c *gin.Context) {
         studentid := c.PostForm("studentid")
         password := c.PostForm("password")
         if studentid == "" || password == "" {
-            tools.ReturnMSG(c, "studentid or password is empty", nil)
+            tools.ReturnMSG(c, "login validation failed: studentid or password is empty", nil)
             return
         }
         user, token, err := uc.ush.Login(c, studentid, password)
         if err != nil {
-            tools.ReturnMSG(c, "login fail", nil)
+            tools.ReturnMSG(c, "login failed: "+err.Error(), nil)
             return
         }
         tools.ReturnMSG(c, "login success", user, token)
     }
 }

Apply similar improvements to other handlers for consistent error handling across the controller.

internal/model/activity.go (1)

19-19: Use boolean for IfRegister field.

Instead of using an enum with 'yes'/'no' strings, consider using a boolean type for better type safety and clearer semantics:

-IfRegister     string `json:"if_register" gorm:"null;type: enum('yes','no');comment:是否需要报名;column:if_register"`
+IfRegister     bool   `json:"if_register" gorm:"null;type: boolean;comment:是否需要报名;column:if_register"`
internal/dao/activity_dao.go (1)

147-175: Refactor duplicate WHERE conditions and optimize DeleteAct.

  1. Extract duplicate WHERE conditions into a helper method
  2. Optimize DeleteAct to use direct Delete operation

Apply these changes:

+func (ad ActDao) getActivityFilter(a model.Activity) model.Activity {
+  return model.Activity{
+    Type:       a.Type,
+    Host:       a.Host,
+    Location:   a.Location,
+    IfRegister: a.IfRegister,
+    Capacity:   a.Capacity,
+  }
+}

 func (ad ActDao) CheckExist(c *gin.Context, a model.Activity) bool {
-  ret := ad.db.Where(&model.Activity{
-    Type:       a.Type,
-    Host:       a.Host,
-    Location:   a.Location,
-    IfRegister: a.IfRegister,
-    Capacity:   a.Capacity,
-  }).Find(&model.Activity{}).RowsAffected
+  ret := ad.db.Where(&ad.getActivityFilter(a)).Find(&model.Activity{}).RowsAffected
   return ret != 0
 }

 func (ad ActDao) DeleteAct(c *gin.Context, a model.Activity) error {
-  ret := ad.db.Where(&model.Activity{
-    Type:       a.Type,
-    Host:       a.Host,
-    Location:   a.Location,
-    IfRegister: a.IfRegister,
-    Capacity:   a.Capacity,
-  }).Find(&model.Activity{}).Delete(&model.Activity{}).RowsAffected
+  ret := ad.db.Where(&ad.getActivityFilter(a)).Delete(&model.Activity{}).RowsAffected
   if ret == 0 {
     return errors.New("activity not exist")
   }
   return nil
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 99975b4 and 96b4a99.

📒 Files selected for processing (10)
  • internal/cache/redis.go (1 hunks)
  • internal/controller/user_controller.go (1 hunks)
  • internal/dao/activity_dao.go (1 hunks)
  • internal/dao/post_dao.go (1 hunks)
  • internal/dao/user_dao.go (2 hunks)
  • internal/middleware/jwt.go (1 hunks)
  • internal/model/activity.go (1 hunks)
  • internal/model/user.go (1 hunks)
  • internal/service/user_service.go (1 hunks)
  • tools/tool.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/service/user_service.go
🔇 Additional comments (9)
internal/dao/post_dao.go (2)

3-15: LGTM! Well-structured interface definition.

The interface is well-designed with clear method signatures and appropriate use of context.


17-25: LGTM! Clean dependency injection pattern.

The struct and constructor follow best practices for dependency injection.

internal/model/user.go (1)

9-9: Add validation for the School field.

The School field is marked as not null in the database schema, but there's no validation to ensure it's not empty when creating/updating users.

Consider adding validation or providing a default value.

internal/dao/user_dao.go (1)

34-36: Add validation before creating user.

The Create method directly passes the user model to the database without validation.

Consider adding validation for required fields and proper error handling:

func (ud *UserDAO) Create(ctx *gin.Context, user *model.User) error {
+   if user == nil {
+       return errors.New("user cannot be nil")
+   }
+   if user.StudentId == "" || user.School == "" {
+       return errors.New("required fields cannot be empty")
+   }
    return ud.db.Create(user).Error
}
internal/controller/user_controller.go (3)

10-23: LGTM! Clean interface design with improved architecture.

The changes reflect a good architectural decision to:

  1. Move from DAO to Service layer dependency
  2. Remove redundant context parameters
  3. Add new search capabilities

25-30: LGTM! Clean constructor with proper dependency injection.


32-138: Previous review comment about missing handler logic has been addressed.

The handlers now have proper implementations with input validation, error handling, and appropriate HTTP responses.

internal/model/activity.go (2)

52-54: Implement GetImgUrl method.

The method is currently a stub that returns nil. Please implement the image URL retrieval logic or add a TODO comment explaining the intended functionality.


23-24: 🛠️ Refactor suggestion

Use time.Time for date fields.

The StartTime and EndTime fields are declared as string but store datetime values. For better type safety and date/time validation:

-StartTime string `json:"start_time" gorm:"not null;type: datetime;comment:活动开始时间;column:start_time"`
-EndTime   string `json:"end_time" gorm:"not null;type: datetime;comment:活动结束时间;column:end_time"`
+StartTime time.Time `json:"start_time" gorm:"not null;type: datetime;comment:活动开始时间;column:start_time"`
+EndTime   time.Time `json:"end_time" gorm:"not null;type: datetime;comment:活动结束时间;column:end_time"`

Likely invalid or redundant comment.

Comment on lines +27 to +34
func (pd *PostDao) GetAllPost(ctx context.Context) ([]model.Post, error) {
var posts []model.Post
err := pd.db.Find(&posts).Error
if err != nil {
return nil, err
}
return posts, 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

Consider implementing pagination for better performance.

Fetching all posts without pagination could lead to performance issues with large datasets. Consider adding limit and offset parameters.

-func (pd *PostDao) GetAllPost(ctx context.Context) ([]model.Post, error) {
+func (pd *PostDao) GetAllPost(ctx context.Context, limit, offset int) ([]model.Post, error) {
 	var posts []model.Post
-	err := pd.db.Find(&posts).Error
+	err := pd.db.Limit(limit).Offset(offset).Find(&posts).Error
 	if err != nil {
 		return nil, err
 	}
 	return posts, nil
}
📝 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 (pd *PostDao) GetAllPost(ctx context.Context) ([]model.Post, error) {
var posts []model.Post
err := pd.db.Find(&posts).Error
if err != nil {
return nil, err
}
return posts, nil
}
func (pd *PostDao) GetAllPost(ctx context.Context, limit, offset int) ([]model.Post, error) {
var posts []model.Post
err := pd.db.Limit(limit).Offset(offset).Find(&posts).Error
if err != nil {
return nil, err
}
return posts, nil
}

Comment on lines +61 to 77
func (pd *PostDao) FindPostByUser(ctx context.Context, sid string, keyword string) ([]model.Post, error) {
if keyword == "" {
var posts []model.Post
err := pd.db.Where("user_id = ?", sid).Find(&posts).Error
if err != nil {
return nil, err
}
return posts, nil
} else {
var posts []model.Post
err := pd.db.Where("user_id = ? and name like ?", sid, "%keyword%").Find(&posts).Error
if err != nil {
return nil, err
}
return posts, 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

Simplify the method and fix the keyword search.

The method has several issues:

  1. Duplicate code blocks can be combined
  2. Incorrect usage of the keyword parameter in the LIKE clause
  3. Error handling can be simplified
 func (pd *PostDao) FindPostByUser(ctx context.Context, sid string, keyword string) ([]model.Post, error) {
-	if keyword == "" {
-		var posts []model.Post
-		err := pd.db.Where("user_id = ?", sid).Find(&posts).Error
-		if err != nil {
-			return nil, err
-		}
-		return posts, nil
-	} else {
-		var posts []model.Post
-		err := pd.db.Where("user_id = ? and name like ?", sid, "%keyword%").Find(&posts).Error
-		if err != nil {
-			return nil, err
-		}
-		return posts, nil
-	}
+	var posts []model.Post
+	query := pd.db.Where("user_id = ?", sid)
+	if keyword != "" {
+		query = query.Where("name LIKE ?", "%"+keyword+"%")
+	}
+	return posts, query.Find(&posts).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 (pd *PostDao) FindPostByUser(ctx context.Context, sid string, keyword string) ([]model.Post, error) {
if keyword == "" {
var posts []model.Post
err := pd.db.Where("user_id = ?", sid).Find(&posts).Error
if err != nil {
return nil, err
}
return posts, nil
} else {
var posts []model.Post
err := pd.db.Where("user_id = ? and name like ?", sid, "%keyword%").Find(&posts).Error
if err != nil {
return nil, err
}
return posts, nil
}
}
func (pd *PostDao) FindPostByUser(ctx context.Context, sid string, keyword string) ([]model.Post, error) {
var posts []model.Post
query := pd.db.Where("user_id = ?", sid)
if keyword != "" {
query = query.Where("name LIKE ?", "%"+keyword+"%")
}
return posts, query.Find(&posts).Error
}

}

const (
DefaultAvatar string = "default_avatar_url"
DefaultAvatar string = "http://img.inside-me.top/EventGlide/OIP-C.jpg"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid hardcoding external URLs in constants.

The DefaultAvatar URL should be configurable through environment variables or configuration files to allow for easier changes and environment-specific values.

Consider moving this to a configuration file:

-DefaultAvatar string = "http://img.inside-me.top/EventGlide/OIP-C.jpg"
+DefaultAvatar string = config.GetString("defaults.avatar_url")

if err != nil {
return err
}
return nil
}

func (c Claims) parseTokenId(token string) string {
func (c *Jwt) parseTokenId(token string) string {
token = token[7:] //去掉bearer
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace hardcoded string slicing with safer token parsing.

Using hardcoded slice index to remove "Bearer " prefix is fragile and could panic if token is malformed.

Consider using strings.TrimPrefix:

-token = token[7:] //去掉bearer
+token = strings.TrimPrefix(token, "Bearer ")
+if token == "" {
+    return ""
+}
📝 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
token = token[7:] //去掉bearer
token = strings.TrimPrefix(token, "Bearer ")
if token == "" {
return ""
}

Comment on lines +83 to +98
func (ad ActDao) FindActByUser(c *gin.Context, s string, keyword string) ([]model.Activity, error) {
var as []model.Activity
if keyword == "" {
err := ad.db.Where("host = ? ", s).Find(&as).Error
if err != nil {
return nil, err
}
return as, nil
} else {
err := ad.db.Where("host = ? and name like ?", s, "%keyword%").Find(&as).Error
if err != nil {
return nil, err
}
return as, 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

Fix the LIKE clause in FindActByUser.

The query uses a hardcoded "%keyword%" instead of the actual keyword variable.

Apply this diff:

-err := ad.db.Where("host = ? and name like ?", s, "%keyword%").Find(&as).Error
+err := ad.db.Where("host = ? and name like ?", s, "%" + keyword + "%").Find(&as).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 (ad ActDao) FindActByUser(c *gin.Context, s string, keyword string) ([]model.Activity, error) {
var as []model.Activity
if keyword == "" {
err := ad.db.Where("host = ? ", s).Find(&as).Error
if err != nil {
return nil, err
}
return as, nil
} else {
err := ad.db.Where("host = ? and name like ?", s, "%keyword%").Find(&as).Error
if err != nil {
return nil, err
}
return as, nil
}
}
func (ad ActDao) FindActByUser(c *gin.Context, s string, keyword string) ([]model.Activity, error) {
var as []model.Activity
if keyword == "" {
err := ad.db.Where("host = ? ", s).Find(&as).Error
if err != nil {
return nil, err
}
return as, nil
} else {
err := ad.db.Where("host = ? and name like ?", s, "%" + keyword + "%").Find(&as).Error
if err != nil {
return nil, err
}
return as, nil
}
}

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: 18

🔭 Outside diff range comments (2)
internal/controller/comment_controller.go (1)

22-35:

🧩 Analysis chain

Add input validation and authentication checks.

The CreateComment handler needs additional security measures:

  1. Authentication check to ensure the request is from an authorized user
  2. Input validation for the comment fields (e.g., length limits, content sanitization)
  3. Rate limiting to prevent spam

Consider adding these security measures:

 func (cc *CommentController) CreateComment() gin.HandlerFunc {
 	return func(c *gin.Context) {
+		// Authentication check
+		userID, exists := c.Get("user_id")
+		if !exists {
+			tools.ReturnMSG(c, "unauthorized", "authentication required")
+			return
+		}
+
 		var comment model.Comment
 		if err := c.ShouldBindJSON(&comment); err != nil {
 			tools.ReturnMSG(c, "bind error", err.Error())
 			return
 		}
+
+		// Input validation
+		if err := comment.Validate(); err != nil {
+			tools.ReturnMSG(c, "validation error", err.Error())
+			return
+		}
+
+		// Set authenticated user as the comment author
+		comment.UserID = userID.(uint)
+
 		if err := cc.cd.Create(c, comment); err != nil {
 			tools.ReturnMSG(c, "create error", err.Error())
 			return
 		}
+
 		c.JSON(200, gin.H{"message": "comment created"})
 	}
 }

Let's verify if authentication middleware is properly set up:


🏁 Script executed:

#!/bin/bash
# Check for authentication middleware in the router setup
rg -A 5 "middleware.*auth" .

Length of output: 30


I'll generate additional shell scripts to search for common patterns related to authentication middleware (e.g., setting "user_id" in the context, definitions of an authentication middleware function, and JWT usage) to get a better picture of whether authentication is properly set up.


🏁 Script executed:

#!/bin/bash
# Searching for lines where "user_id" is set in the context
echo "Checking for context setting of 'user_id':"
rg -n 'c\.Set\("user_id"' .

# Searching for potential authentication middleware definitions (e.g., AuthMiddleware)
echo "Searching for authentication middleware definitions:"
rg -n 'func AuthMiddleware' .

# Searching for JWT usage (a common authentication mechanism)
echo "Searching for JWT usage (case insensitive):"
rg -i 'jwt' .

Length of output: 3462


Authentication and Input Validation Missing in CreateComment Handler

  • The search did not reveal any evidence that a middleware is applied to set the "user_id" context key (e.g., via c.Set("user_id", ...)), nor any authentication middleware (e.g., AuthMiddleware) being used with the comment routes.
  • While the codebase contains JWT-related functionality used by other controllers, the CreateComment handler in internal/controller/comment_controller.go lacks any authentication or input validation checks.

This confirms that the review comment is valid, and the suggested security measures should be incorporated.

internal/middleware/cors.go (1)

21-26: Restrict CORS origins for security.

Using "*" for AllowOrigins is not secure. Consider restricting it to specific domains.

Apply this diff:

 corsConf := cors.Config{
-    AllowOrigins:     []string{"*"},
+    AllowOrigins:     []string{"https://your-domain.com", "http://localhost:3000"},
     AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
     AllowHeaders:     []string{"Origin", "Content-Length", "Content-Type", "Authorization"},
     AllowCredentials: true,
 }
♻️ Duplicate comments (8)
internal/model/post.go (1)

6-7: *Refactor the embedded gorm.Model and primary keys.

Embedding *gorm.Model as a pointer plus adding a BId primary key can create conflicts if gorm.Model's ID field is also treated as a primary key. Additionally, BId should be marked as not null.

Apply this diff:

-	*gorm.Model
-	BId        int    `json:"bid" gorm:"column:bid; type:int; comment:绑定id"`
+	gorm.Model
+	BId        int    `json:"bid" gorm:"column:bid; type:int; comment:绑定id; not null"`
internal/cache/redis.go (1)

50-51: Deserialization function is incomplete.

The jsonTOstrus function returns nil, which breaks retrieval functionality.

-func jsonTOstrus(b []byte) (out []interface{}) {
-   return nil
+func jsonTOstrus(b []byte) (out []interface{}) {
+   err := tools.Unmarshal(b, &out)
+   if err != nil {
+       return nil
+   }
+   return out
}
internal/dao/post_dao.go (3)

27-34: Consider implementing pagination for better performance.

Fetching all posts without pagination could lead to performance issues with large datasets. Consider adding limit and offset parameters.


44-51: Fix the query condition to match the intended search.

Currently, "name = ?", "%name%" is a string literal. If you want to search by partial name, use LIKE syntax.


61-77: Simplify the method and fix the keyword search.

The method has several issues:

  1. Duplicate code blocks can be combined
  2. Incorrect usage of the keyword parameter in the LIKE clause
  3. Error handling can be simplified
internal/controller/post_controller.go (1)

38-53: Enhance input validation and response messages in CreatePost.

The current implementation lacks input validation and uses generic success messages.

internal/service/imgUploader.go (2)

37-49: Fix error handling in ProcessImg.

The function returns nil error when upload fails, which is incorrect.


51-65: Refactor GetFile implementation for better reliability.

The current implementation has several issues:

  1. Returns nil on first error, potentially missing valid files
  2. Hard-coded limit of 9 files
  3. No file type validation
🧹 Nitpick comments (13)
internal/router/comment_router.go (1)

13-13: Consider maintaining interface-based design.

The change from controller.CommentControllerHdl to *controller.CommentController reduces flexibility and makes testing harder. Interface-based design allows for better:

  • Dependency injection
  • Mock implementations in tests
  • Future extensibility

Consider reverting to the interface type:

-	cch *controller.CommentController
+	cch controller.CommentControllerHdl

-func NewCommentRouter(cch *controller.CommentController, e *gin.Engine) *CommentRouter {
+func NewCommentRouter(cch controller.CommentControllerHdl, e *gin.Engine) *CommentRouter {

Also applies to: 17-21

internal/controller/comment_controller.go (1)

15-15: Consider maintaining interface-based design.

Similar to the router changes, switching from dao.CommentDAOHdl to *dao.CommentDao reduces flexibility and makes testing harder. Interface-based design allows for better:

  • Dependency injection
  • Mock implementations in tests
  • Future extensibility

Consider reverting to the interface type:

-	cd *dao.CommentDao
+	cd dao.CommentDAOHdl

-func NewCommentController(cd *dao.CommentDao) *CommentController {
+func NewCommentController(cd dao.CommentDAOHdl) *CommentController {

Also applies to: 18-20

internal/service/post_service.go (1)

10-15: Use more descriptive and consistent method names.

Currently, the interface methods are named inconsistently (e.g., GetAllPost instead of GetAllPosts). Adopting a cohesive naming scheme would enhance code readability and align with Go naming conventions.

test/main.go (1)

5-9: Consider using an interface for Message type.

The Message type could benefit from an interface to allow different message implementations and improve testability.

Apply this diff:

-type Message string
+type Message interface {
+    Content() string
+}

+type SimpleMessage string

+func (m SimpleMessage) Content() string {
+    return string(m)
+}

 func NewMessage() Message {
-    return Message("Hi there!")
+    return SimpleMessage("Hi there!")
 }
internal/middleware/cors.go (1)

16-18: Consider returning the interface type.

Returning a concrete type instead of the interface reduces flexibility and makes testing more difficult.

Apply this diff:

-func NewCors(e *gin.Engine) *Cors {
+func NewCors(e *gin.Engine) CorsHdl {
     return &Cors{e: e}
 }
internal/model/post.go (1)

11-13: Improve field constraints and default values.

Consider the following improvements:

  1. ImgUrls might need to be nullable for posts without images
  2. CommentNum and LikeNum should have default values of 0

Apply this diff:

-	ImgUrls    string `json:"img_urls" gorm:"column:img_urls; type:text; comment:图片链接; not null"`
-	CommentNum int    `json:"comment_num" gorm:"column:comment_num; type:int; comment:评论数; not null"`
-	LikeNum    int    `json:"like_num" gorm:"column:like_num; type:int; comment:点赞数; not null"`
+	ImgUrls    string `json:"img_urls" gorm:"column:img_urls; type:text; comment:图片链接"`
+	CommentNum int    `json:"comment_num" gorm:"column:comment_num; type:int; comment:评论数; not null; default:0"`
+	LikeNum    int    `json:"like_num" gorm:"column:like_num; type:int; comment:点赞数; not null; default:0"`
internal/router/user_router.go (1)

17-19: Consider using interfaces for better testability.

Using concrete types instead of interfaces makes it harder to mock dependencies in tests. Consider using the interface type for better testability and flexibility.

Apply this diff:

-func NewUserRouter(e *gin.Engine, uc *controller.UserController) *UserRouter {
+func NewUserRouter(e *gin.Engine, uc controller.UserControllerHdl) *UserRouter {
internal/router/router.go (1)

13-20: Consider using interfaces for better testability.

Using concrete types instead of interfaces makes it harder to mock dependencies in tests. Consider using interface types for better testability and flexibility.

Apply this diff:

 type Router struct {
 	e    *gin.Engine
-	ur   *UserRouter
-	ar   *ActRouter
-	pr   *PostRouter
-	cr   *CommentRouter
+	ur   UserRouterHdl
+	ar   ActRouterHdl
+	pr   PostRouterHdl
+	cr   CommentRouterHdl
 	cors *middleware.Cors
 }
internal/router/activity_router.go (1)

17-22: Consider using interfaces for better testability.

Using concrete types instead of interfaces makes it harder to mock dependencies in tests. Consider using the interface type for better testability and flexibility.

Apply this diff:

-func NewActRouter(e *gin.Engine, ach *controller.ActController) *ActRouter {
+func NewActRouter(e *gin.Engine, ach controller.ActControllerHdl) *ActRouter {
internal/cache/redis.go (1)

11-11: Make the TODO comment more specific.

The current TODO comment is too vague. Consider updating it to clearly specify the scope and requirements.

-// TODO: scpoe: 活动,帖子,jwt
+// TODO: Implement caching for:
+// - Activities (list, details)
+// - Posts (list, details)
+// - JWT tokens (blacklist)
internal/dao/user_dao.go (1)

25-46: Optimize GORM operations for better performance.

Consider the following optimizations:

  1. Use Select to specify columns
  2. Use First instead of Find for single record lookups
  3. Add appropriate indexes
func (ud *UserDao) UpdateAvatar(ctx *gin.Context, sid string, imgurl string) error {
-   return ud.db.Model(&model.User{}).Where("sid = ?", sid).Update("avatar", imgurl).Error
+   return ud.db.Model(&model.User{}).Select("avatar").Where("sid = ?", sid).Update("avatar", imgurl).Error
}

func (ud *UserDao) CheckUserExist(ctx *gin.Context, sid string) bool {
-   res := ud.db.Where("sid = ?", sid).Find(&model.User{}).RowsAffected
+   var exists bool
+   ud.db.Select("1").Model(&model.User{}).Where("sid = ?", sid).First(&exists)
+   return exists
}
internal/dao/post_dao.go (2)

36-42: Simplify error handling.

The error handling can be simplified by directly returning the error from the Create operation.

Apply this diff to simplify the error handling:

 func (pd *PostDao) CreatePost(ctx context.Context, post *model.Post) error {
-	err := pd.db.Create(post).Error
-	if err != nil {
-		return err
-	}
-	return nil
+	return pd.db.Create(post).Error
 }

53-59: Simplify error handling.

The error handling can be simplified by directly returning the error from the Delete operation.

Apply this diff to simplify the error handling:

 func (pd *PostDao) DeletePost(ctx context.Context, post *model.Post) error {
-	err := pd.db.Delete(post).Error
-	if err != nil {
-		return err
-	}
-	return nil
+	return pd.db.Delete(post).Error
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 96b4a99 and 9c0b187.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (28)
  • config/conf.go (1 hunks)
  • go.mod (6 hunks)
  • internal/cache/redis.go (1 hunks)
  • internal/controller/activity_controller.go (1 hunks)
  • internal/controller/comment_controller.go (1 hunks)
  • internal/controller/post_controller.go (1 hunks)
  • internal/controller/user_controller.go (1 hunks)
  • internal/dao/activity_dao.go (1 hunks)
  • internal/dao/comment_dao.go (1 hunks)
  • internal/dao/post_dao.go (1 hunks)
  • internal/dao/user_dao.go (1 hunks)
  • internal/ioc/db.go (2 hunks)
  • internal/middleware/cors.go (1 hunks)
  • internal/middleware/jwt.go (1 hunks)
  • internal/model/post.go (1 hunks)
  • internal/router/activity_router.go (1 hunks)
  • internal/router/comment_router.go (1 hunks)
  • internal/router/post_router.go (1 hunks)
  • internal/router/router.go (1 hunks)
  • internal/router/user_router.go (1 hunks)
  • internal/server/server.go (1 hunks)
  • internal/service/imgUploader.go (1 hunks)
  • internal/service/post_service.go (1 hunks)
  • internal/service/user_service.go (1 hunks)
  • main.go (1 hunks)
  • test/main.go (1 hunks)
  • wire.go (1 hunks)
  • wire_gen.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • internal/dao/comment_dao.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/ioc/db.go
  • go.mod
  • internal/middleware/jwt.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
main.go

10-10: Error return value of config.Init is not checked

(errcheck)

internal/router/router.go

35-35: Error return value of r.ar.RegisterActRouters is not checked

(errcheck)

internal/service/user_service.go

84-84: ineffectual assignment to err

(ineffassign)


106-106: ineffectual assignment to err

(ineffassign)


185-185: ineffectual assignment to err

(ineffassign)


188-188: SA4017: WithContext doesn't have side effects and its return value is ignored

(staticcheck)

🔇 Additional comments (16)
internal/service/post_service.go (1)

39-40: Preserve context in error handling.

Use the underlying error details instead of a generic message. This aids debugging and troubleshooting. The same suggestion was provided in a past review comment:

-		return errors.New("img upload failed")
+		return fmt.Errorf("failed to process images: %w", err)
internal/server/server.go (1)

15-18: LGTM!

The Run method straightforwardly integrates router registration and startup. No immediate issues found.

internal/router/user_router.go (1)

21-32: LGTM!

The new routes are well-organized, follow RESTful conventions, and are logically grouped under the /user prefix.

internal/router/router.go (2)

8-11: LGTM!

The new Run() error method follows Go conventions and enhances the interface's functionality.


40-42: LGTM!

The function properly delegates to the underlying gin.Engine's Run method and handles error propagation correctly.

internal/router/activity_router.go (1)

24-42: LGTM!

The routes are well-organized, follow RESTful conventions, and have clear comments indicating complexity levels.

wire.go (2)

1-16: LGTM! Build tag and imports are properly configured.

The build tag wireinject and imports are correctly set up for Google Wire dependency injection.


18-45: Verify the completeness of dependency injection setup.

The wire.Build call includes all necessary components, but please ensure that:

  1. All components have their dependencies satisfied
  2. There are no circular dependencies
  3. The order of initialization is correct (e.g., database before DAOs)
✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Verify wire dependencies and check for potential issues

# Check if all referenced packages exist
echo "Checking for missing packages..."
for pkg in cache controller dao ioc middleware router server service; do
  if ! fd -t d "internal/$pkg" >/dev/null; then
    echo "Warning: Package internal/$pkg not found"
  fi
done

# Look for potential circular dependencies in imports
echo "Checking for circular dependencies..."
for pkg in cache controller dao ioc middleware router server service; do
  if fd -t f . -e go | xargs rg "github\.com/raiki02/EG/internal/$pkg" | rg -v "^internal/$pkg"; then
    echo "Note: Package internal/$pkg is imported by other packages"
  fi
done

Length of output: 9151


Dependency Injection Setup Verified

The dependency injection appears complete. Based on the inspection:

  • Dependencies Satisfied: All components referenced in the wire.Build call (e.g., initialization functions for DB and Redis, cache, DAOs, services, middleware, controllers, routers, and server) are correctly included.
  • Initialization Order: The order (e.g., ioc.InitDB and ioc.InitRedis preceding DAO initializations) is appropriate.
  • Circular Dependencies: No circular dependency issues were detected in the reference search.
internal/cache/redis.go (1)

12-16: LGTM! Interface changes improve flexibility.

The change from []model.Activity to []interface{} makes the cache more generic and reusable across different types.

wire_gen.go (1)

1-49: LGTM! Generated Wire implementation looks correct.

This is an auto-generated file by Wire. The initialization order follows dependencies correctly, and all components are properly wired together.

internal/dao/activity_dao.go (5)

40-46: Add database constraints to prevent race conditions.

The "CheckExist" → "CreateAct" logic can be susceptible to race conditions if two processes attempt to create the same activity concurrently.

Consider using unique database constraints for robust safety.


120-127: Fix missing .Find(&as).

The query uses "err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Error" but never fetches the results into the slice.

Apply this diff:

-err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Error
+err := ad.db.Where("start_time >= ? and end_time <= ?", start, end).Find(&as).Error

129-136: Use the variable "n" instead of literal "%n%" in LIKE clause.

In line 131, it queries "name like ?" with "%n%", which should be "%"+n+"%".

Apply this diff:

-err := ad.db.Where("name like ?", "%n%").Find(&as).Error
+err := ad.db.Where("name like ?", "%" + n + "%").Find(&as).Error

138-145: Fix the variable usage in "FindActByDate".

Same issue as "FindActByName": it uses "%d%" instead of the actual variable.

Apply this diff:

-err := ad.db.Where("start_time like ?", "%d%").Find(&as).Error
+err := ad.db.Where("start_time like ?", "%" + d + "%").Find(&as).Error

83-98: Fix the LIKE clause in FindActByUser.

The query uses a hardcoded "%keyword%" instead of the actual keyword variable.

Apply this diff:

-err := ad.db.Where("host = ? and name like ?", s, "%keyword%").Find(&as).Error
+err := ad.db.Where("host = ? and name like ?", s, "%" + keyword + "%").Find(&as).Error
internal/controller/activity_controller.go (1)

143-158: Fix incorrect query parameter name in FindActByLocation.

The method uses "type" instead of "location" for the query parameter.

Apply this fix:

-		target := ctx.Query("type")
+		target := ctx.Query("location")


func main() {
config.Init()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check the error from config.Init().

Ignoring potential initialization failures can lead to undefined behavior. Capture and handle the error:

-	config.Init()
+	if err := config.Init(); err != nil {
+		log.Fatalf("failed to initialize config: %v", err)
+	}
📝 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
config.Init()
if err := config.Init(); err != nil {
log.Fatalf("failed to initialize config: %v", err)
}
🧰 Tools
🪛 golangci-lint (1.62.2)

10-10: Error return value of config.Init is not checked

(errcheck)

@@ -14,7 +14,7 @@ func Init() error {
flag.Parse()
viper.SetConfigFile(ConfPath)

viper.SetDefault("mysql.dsn", "root:114514@tcp(127.0.0.1:3306)/EventGlide?charset=utf8mb4&parseTime=True&loc=Local")
viper.SetDefault("mysql.dsn", "root:123456@tcp(127.0.0.1:3306)/EventGlide?charset=utf8mb4&parseTime=True&loc=Local")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid hardcoding database credentials.

The MySQL password should not be hardcoded in the source code. Instead, load it from environment variables or a secure configuration file.

Apply this diff to use environment variables:

-viper.SetDefault("mysql.dsn", "root:123456@tcp(127.0.0.1:3306)/EventGlide?charset=utf8mb4&parseTime=True&loc=Local")
+viper.SetDefault("mysql.dsn", fmt.Sprintf("root:%s@tcp(127.0.0.1:3306)/EventGlide?charset=utf8mb4&parseTime=True&loc=Local", os.Getenv("MYSQL_PASSWORD")))

Add these imports:

import (
    "fmt"
    "os"
)

Comment on lines 36 to 38
func main() {
kafka := NewKafka()
likes_contents := []string{"like1", "like2", "like3", "like4", "like5"}

go kafka.ConsumeLikes()

time.Sleep(7 * time.Second)
log.Println("start send likes after 7s")
for i := 0; i < 5; i++ {
wg.Add(1)
time.Sleep(2 * time.Second)
kafka.SendLikes(likes_contents[rand.Intn(5)])
}

wg.Wait()

}
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 example usage in main function.

The main function is empty, making it difficult to understand how to use the implemented types.

Apply this diff:

 func main() {
+    message := NewMessage()
+    greeter := NewGreeter(message)
+    event := NewEvent(greeter)
+    event.Start()
 }
📝 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 main() {
kafka := NewKafka()
likes_contents := []string{"like1", "like2", "like3", "like4", "like5"}
go kafka.ConsumeLikes()
time.Sleep(7 * time.Second)
log.Println("start send likes after 7s")
for i := 0; i < 5; i++ {
wg.Add(1)
time.Sleep(2 * time.Second)
kafka.SendLikes(likes_contents[rand.Intn(5)])
}
wg.Wait()
}
func main() {
message := NewMessage()
greeter := NewGreeter(message)
event := NewEvent(greeter)
event.Start()
}

Comment on lines +24 to 32
func (pr *PostRouter) RegisterPostRouters() {
post := pr.e.Group("/post")
{
post.GET("/list", pr.pch.GetAllPost())
post.POST("/create", pr.pch.CreatePost())
post.GET("/find", pr.pch.FindPostByName())
post.DELETE("/delete", pr.pch.DeletePost())
}
}
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 authentication middleware and API versioning.

The post routes need authentication for create and delete operations. Also, consider adding API versioning.

Apply this diff:

 func (pr *PostRouter) RegisterPostRouters() {
-    post := pr.e.Group("/post")
+    post := pr.e.Group("/api/v1/post")
     {
         post.GET("/list", pr.pch.GetAllPost())
         post.GET("/find", pr.pch.FindPostByName())
-        post.POST("/create", pr.pch.CreatePost())
-        post.DELETE("/delete", pr.pch.DeletePost())
+        post.POST("/create", middleware.AuthRequired(), pr.pch.CreatePost())
+        post.DELETE("/delete", middleware.AuthRequired(), pr.pch.DeletePost())
     }
 }

Add this import:

"github.com/raiki02/EG/internal/middleware"

Comment on lines 33 to 38
func (r *Router) RegisterRouters() {
r.ur.RegisterUserRouters()
r.ar.RegisterActivityRouters()
r.ar.RegisterActRouters()
r.pr.RegisterPostRouters()
r.cr.RegisterCommentRouter()
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle error from RegisterActRouters.

The error returned by RegisterActRouters is not being checked. This could hide important issues.

Apply this diff:

-func (r *Router) RegisterRouters() {
+func (r *Router) RegisterRouters() error {
 	r.ur.RegisterUserRouters()
-	r.ar.RegisterActRouters()
+	if err := r.ar.RegisterActRouters(); err != nil {
+		return err
+	}
 	r.pr.RegisterPostRouters()
 	r.cr.RegisterCommentRouter()
+	return nil
 }
📝 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 (r *Router) RegisterRouters() {
r.ur.RegisterUserRouters()
r.ar.RegisterActivityRouters()
r.ar.RegisterActRouters()
r.pr.RegisterPostRouters()
r.cr.RegisterCommentRouter()
}
func (r *Router) RegisterRouters() error {
r.ur.RegisterUserRouters()
if err := r.ar.RegisterActRouters(); err != nil {
return err
}
r.pr.RegisterPostRouters()
r.cr.RegisterCommentRouter()
return nil
}
🧰 Tools
🪛 golangci-lint (1.62.2)

35-35: Error return value of r.ar.RegisterActRouters is not checked

(errcheck)

Comment on lines +32 to 47
func (uc *UserController) Login() gin.HandlerFunc {
return func(c *gin.Context) {
// TODO 更新头像
}
}

func (uc *UserController) UpdateUsername(ctx context.Context) gin.HandlerFunc {
return func(c *gin.Context) {
// TODO 更新用户名
}
}

func (uc *UserController) Login(ctx context.Context) gin.HandlerFunc {
return func(c *gin.Context) {
//根据一站式账号登录,登陆后返回token
studentid := c.PostForm("studentid")
password := c.PostForm("password")

//首次登录要初始化信息,全部使用默认
if !uc.udh.CheckUserExist(c.Request.Context(), tools.StrToInt(studentid)) {
uc.udh.Insert(c, studentid, password)
}

success, err := uc.cSvc.Login(context.Background(), studentid, password)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(),
})
if studentid == "" || password == "" {
tools.ReturnMSG(c, "studentid or password is empty", nil)
return
}
if !success {
c.JSON(http.StatusBadRequest, gin.H{
"message": "登录失败",
})
return
}
//token操作
token := uc.jwtHdl.GenToken(c.Request.Context(), studentid)
err = uc.jwtHdl.StoreInRedis(c.Request.Context(), studentid, token)
user, token, err := uc.ush.Login(c, studentid, password)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": "token存储失败",
})
tools.ReturnMSG(c, "login fail", nil)
return
}

c.JSON(http.StatusOK, gin.H{
"message": "登录成功: " + studentid,
})

tools.ReturnMSG(c, "login success", user, token)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance login security and error handling.

The current implementation has several security concerns:

  1. No rate limiting for login attempts
  2. Generic error messages that could aid attackers
  3. Using form data instead of JSON for credentials

Apply this diff to improve the implementation:

+const maxLoginAttempts = 5
+const loginLockoutDuration = 15 * time.Minute

 func (uc *UserController) Login() gin.HandlerFunc {
 	return func(c *gin.Context) {
-		studentid := c.PostForm("studentid")
-		password := c.PostForm("password")
+		var loginReq struct {
+			StudentID string `json:"student_id" binding:"required"`
+			Password  string `json:"password" binding:"required"`
+		}
+		if err := c.ShouldBindJSON(&loginReq); err != nil {
+			tools.ReturnMSG(c, "Invalid request format", nil)
+			return
+		}

-		if studentid == "" || password == "" {
-			tools.ReturnMSG(c, "studentid or password is empty", nil)
+		// Check rate limiting
+		key := fmt.Sprintf("login:%s", loginReq.StudentID)
+		attempts, _ := cache.Get(key)
+		if attempts >= maxLoginAttempts {
+			tools.ReturnMSG(c, "Too many login attempts. Please try again later", nil)
+			return
+		}

-		user, token, err := uc.ush.Login(c, studentid, password)
+		user, token, err := uc.ush.Login(c, loginReq.StudentID, loginReq.Password)
 		if err != nil {
-			tools.ReturnMSG(c, "login fail", nil)
+			// Increment failed attempts
+			cache.Incr(key)
+			cache.Expire(key, loginLockoutDuration)
+			tools.ReturnMSG(c, "Invalid credentials", nil)
 			return
 		}
-		tools.ReturnMSG(c, "login success", user, token)
+		// Clear failed attempts on successful login
+		cache.Del(key)
+		tools.ReturnMSG(c, "Login successful", map[string]interface{}{
+			"user":  user,
+			"token": token,
+		})
 	}
 }
📝 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 (uc *UserController) Login() gin.HandlerFunc {
return func(c *gin.Context) {
// TODO 更新头像
}
}
func (uc *UserController) UpdateUsername(ctx context.Context) gin.HandlerFunc {
return func(c *gin.Context) {
// TODO 更新用户名
}
}
func (uc *UserController) Login(ctx context.Context) gin.HandlerFunc {
return func(c *gin.Context) {
//根据一站式账号登录,登陆后返回token
studentid := c.PostForm("studentid")
password := c.PostForm("password")
//首次登录要初始化信息,全部使用默认
if !uc.udh.CheckUserExist(c.Request.Context(), tools.StrToInt(studentid)) {
uc.udh.Insert(c, studentid, password)
}
success, err := uc.cSvc.Login(context.Background(), studentid, password)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(),
})
if studentid == "" || password == "" {
tools.ReturnMSG(c, "studentid or password is empty", nil)
return
}
if !success {
c.JSON(http.StatusBadRequest, gin.H{
"message": "登录失败",
})
return
}
//token操作
token := uc.jwtHdl.GenToken(c.Request.Context(), studentid)
err = uc.jwtHdl.StoreInRedis(c.Request.Context(), studentid, token)
user, token, err := uc.ush.Login(c, studentid, password)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": "token存储失败",
})
tools.ReturnMSG(c, "login fail", nil)
return
}
c.JSON(http.StatusOK, gin.H{
"message": "登录成功: " + studentid,
})
tools.ReturnMSG(c, "login success", user, token)
}
}
const maxLoginAttempts = 5
const loginLockoutDuration = 15 * time.Minute
func (uc *UserController) Login() gin.HandlerFunc {
return func(c *gin.Context) {
var loginReq struct {
StudentID string `json:"student_id" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindJSON(&loginReq); err != nil {
tools.ReturnMSG(c, "Invalid request format", nil)
return
}
// Check rate limiting
key := fmt.Sprintf("login:%s", loginReq.StudentID)
attempts, _ := cache.Get(key)
if attempts >= maxLoginAttempts {
tools.ReturnMSG(c, "Too many login attempts. Please try again later", nil)
return
}
user, token, err := uc.ush.Login(c, loginReq.StudentID, loginReq.Password)
if err != nil {
// Increment failed attempts
cache.Incr(key)
cache.Expire(key, loginLockoutDuration)
tools.ReturnMSG(c, "Invalid credentials", nil)
return
}
// Clear failed attempts on successful login
cache.Del(key)
tools.ReturnMSG(c, "Login successful", map[string]interface{}{
"user": user,
"token": token,
})
}
}

Comment on lines +177 to +192
func (ac ActController) FindActByIsForeign() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("type")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
activities, err = a.adh.ListActivityByLocation(c, location)

as, err := ac.ad.FindActByIsForeign(ctx, target)
if err != nil {
tools.ReturnMSG(c, "get dao error", err)
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
err = a.c.Set(c, location, activities)
tools.ReturnMSG(ctx, "success", as)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect query parameter name in FindActByIsForeign.

The method uses "type" instead of "visibility" for the query parameter.

Apply this fix:

-		target := ctx.Query("type")
+		target := ctx.Query("visibility")
📝 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 (ac ActController) FindActByIsForeign() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("type")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
activities, err = a.adh.ListActivityByLocation(c, location)
as, err := ac.ad.FindActByIsForeign(ctx, target)
if err != nil {
tools.ReturnMSG(c, "get dao error", err)
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
err = a.c.Set(c, location, activities)
tools.ReturnMSG(ctx, "success", as)
}
}
func (ac ActController) FindActByIsForeign() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("visibility")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
as, err := ac.ad.FindActByIsForeign(ctx, target)
if err != nil {
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
tools.ReturnMSG(ctx, "success", as)
}
}

Comment on lines 160 to 175
func (ac ActController) FindActByIfSignup() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("type")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
activities, err = a.adh.ListActivityByHost(c, host)
if err != nil {
tools.ReturnMSG(c, "get dao error", err)
return
}
err = a.c.Set(c, host, activities)

as, err := ac.ad.FindActByIfSignup(ctx, target)
if err != nil {
tools.ReturnMSG(c, "set cache error", err)
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
tools.ReturnMSG(c, "get dao success", activities)
tools.ReturnMSG(ctx, "success", as)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect query parameter name in FindActByIfSignup.

The method uses "type" instead of "if_register" for the query parameter.

Apply this fix:

-		target := ctx.Query("type")
+		target := ctx.Query("if_register")
📝 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 (ac ActController) FindActByIfSignup() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("type")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
activities, err = a.adh.ListActivityByHost(c, host)
if err != nil {
tools.ReturnMSG(c, "get dao error", err)
return
}
err = a.c.Set(c, host, activities)
as, err := ac.ad.FindActByIfSignup(ctx, target)
if err != nil {
tools.ReturnMSG(c, "set cache error", err)
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
tools.ReturnMSG(c, "get dao success", activities)
tools.ReturnMSG(ctx, "success", as)
}
}
func (ac ActController) FindActByIfSignup() gin.HandlerFunc {
return func(ctx *gin.Context) {
target := ctx.Query("if_register")
if target == "" {
tools.ReturnMSG(ctx, "query cannot be nil", nil)
return
}
as, err := ac.ad.FindActByIfSignup(ctx, target)
if err != nil {
tools.ReturnMSG(ctx, err.Error(), nil)
return
}
tools.ReturnMSG(ctx, "success", as)
}
}

Comment on lines +105 to +112
func (s *UserService) UpdateAvatar(ctx *gin.Context, sid string) error {
imgurl, err := s.iuh.ProcessImg(ctx)
err = s.udh.UpdateAvatar(ctx, sid, imgurl)
if err != nil {
return err
}
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

Fix error handling in UpdateAvatar.

The error from ProcessImg is ignored.

Apply this diff:

-	imgurl, err := s.iuh.ProcessImg(ctx)
-	err = s.udh.UpdateAvatar(ctx, sid, imgurl)
+	imgurl, err := s.iuh.ProcessImg(ctx)
+	if err != nil {
+		return err
+	}
+	err = s.udh.UpdateAvatar(ctx, sid, imgurl)
📝 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 (s *UserService) UpdateAvatar(ctx *gin.Context, sid string) error {
imgurl, err := s.iuh.ProcessImg(ctx)
err = s.udh.UpdateAvatar(ctx, sid, imgurl)
if err != nil {
return err
}
return nil
}
func (s *UserService) UpdateAvatar(ctx *gin.Context, sid string) error {
imgurl, err := s.iuh.ProcessImg(ctx)
if err != nil {
return err
}
err = s.udh.UpdateAvatar(ctx, sid, imgurl)
if err != nil {
return err
}
return nil
}
🧰 Tools
🪛 golangci-lint (1.62.2)

106-106: ineffectual assignment to err

(ineffassign)

Comment on lines +171 to +203
func (c *ccnuService) loginUndergraduateClient(ctx context.Context, studentId string, password string) (*http.Client, error) {
params, err := c.makeAccountPreflightRequest()
if err != nil {
return nil, err
}

v := url.Values{}
v.Set("username", studentId)
v.Set("password", password)
v.Set("lt", params.lt)
v.Set("execution", params.execution)
v.Set("_eventId", params._eventId)
v.Set("submit", params.submit)

request, err := http.NewRequest("POST", "https://account.ccnu.edu.cn/cas/login;jsessionid="+params.JSESSIONID, strings.NewReader(v.Encode()))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36")
request.WithContext(ctx)

client := c.client()
resp, err := client.Do(request)
if err != nil {
var opErr *net.OpError
if errors.As(err, &opErr) {
return nil, errors.New("网络异常")
}
return nil, err
}
if len(resp.Header.Get("Set-Cookie")) == 0 {
return nil, errors.New("学号或密码错误")
}
return client, 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

Fix error handling and context usage in loginUndergraduateClient.

The function has two issues:

  1. The error from NewRequest is ignored.
  2. The return value from WithContext is ignored.

Apply this diff:

-	request, err := http.NewRequest("POST", "https://account.ccnu.edu.cn/cas/login;jsessionid="+params.JSESSIONID, strings.NewReader(v.Encode()))
+	request, err := http.NewRequest("POST", "https://account.ccnu.edu.cn/cas/login;jsessionid="+params.JSESSIONID, strings.NewReader(v.Encode()))
+	if err != nil {
+		return nil, err
+	}
 	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 	request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36")
-	request.WithContext(ctx)
+	request = request.WithContext(ctx)
📝 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 (c *ccnuService) loginUndergraduateClient(ctx context.Context, studentId string, password string) (*http.Client, error) {
params, err := c.makeAccountPreflightRequest()
if err != nil {
return nil, err
}
v := url.Values{}
v.Set("username", studentId)
v.Set("password", password)
v.Set("lt", params.lt)
v.Set("execution", params.execution)
v.Set("_eventId", params._eventId)
v.Set("submit", params.submit)
request, err := http.NewRequest("POST", "https://account.ccnu.edu.cn/cas/login;jsessionid="+params.JSESSIONID, strings.NewReader(v.Encode()))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36")
request.WithContext(ctx)
client := c.client()
resp, err := client.Do(request)
if err != nil {
var opErr *net.OpError
if errors.As(err, &opErr) {
return nil, errors.New("网络异常")
}
return nil, err
}
if len(resp.Header.Get("Set-Cookie")) == 0 {
return nil, errors.New("学号或密码错误")
}
return client, nil
}
func (c *ccnuService) loginUndergraduateClient(ctx context.Context, studentId string, password string) (*http.Client, error) {
params, err := c.makeAccountPreflightRequest()
if err != nil {
return nil, err
}
v := url.Values{}
v.Set("username", studentId)
v.Set("password", password)
v.Set("lt", params.lt)
v.Set("execution", params.execution)
v.Set("_eventId", params._eventId)
v.Set("submit", params.submit)
request, err := http.NewRequest("POST", "https://account.ccnu.edu.cn/cas/login;jsessionid="+params.JSESSIONID, strings.NewReader(v.Encode()))
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36")
request = request.WithContext(ctx)
client := c.client()
resp, err := client.Do(request)
if err != nil {
var opErr *net.OpError
if errors.As(err, &opErr) {
return nil, errors.New("网络异常")
}
return nil, err
}
if len(resp.Header.Get("Set-Cookie")) == 0 {
return nil, errors.New("学号或密码错误")
}
return client, nil
}
🧰 Tools
🪛 golangci-lint (1.62.2)

185-185: ineffectual assignment to err

(ineffassign)


188-188: SA4017: WithContext doesn't have side effects and its return value is ignored

(staticcheck)

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: 6

♻️ Duplicate comments (2)
internal/model/post.go (1)

12-12: Add primaryKey tag to BId field.

Since BId is the binding ID field, it should be marked as the primary key:

-	BId      int    `json:"bid" gorm:"column:bid; type:int; comment:绑定id"`
+	BId      int    `json:"bid" gorm:"column:bid; type:int; primaryKey; comment:绑定id"`
📝 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.

	BId      int    `json:"bid" gorm:"column:bid; type:int; primaryKey; comment:绑定id"`
internal/model/activity.go (1)

23-24: Use time.Time for date fields.

Using string for date/time fields bypasses Go's type safety and validation capabilities.

Apply this fix:

-       StartTime string `json:"start_time" gorm:"not null;type: datetime;comment:活动开始时间;column:start_time"`
-       EndTime   string `json:"end_time" gorm:"not null;type: datetime;comment:活动结束时间;column:end_time"`
+       StartTime time.Time `json:"start_time" gorm:"not null;type: datetime;comment:活动开始时间;column:start_time"`
+       EndTime   time.Time `json:"end_time" gorm:"not null;type: datetime;comment:活动结束时间;column:end_time"`

Don't forget to add "time" to your imports.

📝 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.

	StartTime time.Time `json:"start_time" gorm:"not null;type: datetime;comment:活动开始时间;column:start_time"`
	EndTime   time.Time `json:"end_time" gorm:"not null;type: datetime;comment:活动结束时间;column:end_time"`
🧹 Nitpick comments (12)
internal/model/post.go (1)

8-11: Consider adding UpdatedAt field for complete timestamp tracking.

The struct includes CreatedAt and DeletedAt but is missing UpdatedAt. For consistency with GORM's standard timestamp tracking, consider adding:

 type Post struct {
 	CreatedAt time.Time `json:"created_at" gorm:"column:created_at; type:datetime; comment:创建时间"`
+	UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at; type:datetime; comment:更新时间"`
 	DeletedAt gorm.DeletedAt
tools/tool.go (1)

31-40: Consider standardizing the response structure.

The function now returns a map with a nested response object, which could be simplified for consistency.

Consider this alternative:

-func ReturnMSG(c *gin.Context, msg string, res ...interface{}) map[string]interface{} {
+func ReturnMSG(c *gin.Context, msg string, res ...interface{}) resp.Resp {
        re := resp.Resp{
                Code: c.Writer.Status(),
                Msg:  msg,
                Data: res,
        }
-       return gin.H{
-               "response": re,
-       }
+       return re
}
internal/model/activity.go (1)

51-53: Implement GetImgUrl method.

The method is currently a stub that always returns nil.

Would you like me to help implement this method to properly handle image URL retrieval?

internal/model/comment.go (2)

8-17: Consider consolidating or internationalizing the docstring.
The doc comments are informative but partially in Chinese. If the codebase or teams primarily use English, consider standardizing or providing both English and Chinese comments to improve maintainability for all contributors.


19-20: Validate column definitions and consider indexing frequently queried fields.

  • The DeletedAt field suggests soft deletes in GORM. Confirm that downstream operations (e.g., .Delete()) effectively perform soft deletes rather than permanently removing records.
  • Changing IDs from int to string (e.g., PosterID, CommentID) may require updating foreign key constraints or migrations.
  • For Content, consider sanitizing or escaping user input to avoid potential XSS vulnerabilities.
  • Storing counters such as Likes and Answers directly in the table may lead to concurrency concerns under high load. Consider separate tables for more robust concurrency handling or an atomic increment approach.

Also applies to: 22-25, 27-30

internal/router/comment_router.go (2)

13-13: Reevaluate transitioning from an interface to a concrete controller.
Switching to a pointer reference of the concrete CommentController (*controller.CommentController) reduces abstraction. If you anticipate multiple implementations or mock testing, maintaining an interface-based injection may be more flexible.


28-29: Use dedicated HTTP methods to align with REST conventions.
Using POST for deletion is not typically RESTful. Consider using DELETE /comment/:id for deletions or adopting more descriptive endpoints for “citing.”

internal/dao/comment_dao.go (3)

4-4: Prefer standard library context over Gin context in DAO layer.
Using *gin.Context directly in the DAO can tightly couple your data operations with the web framework. Adopting context.Context from the standard library improves portability and testability.


25-27: Handle database errors more gracefully.
Currently, only the raw DB error is returned. Consider adding error context or logging specific failure details for debugging.


33-34: Reduce duplication in “AnswerComment.”
The AnswerComment method is nearly identical to CreateComment. Factor out common code to avoid duplication and promote maintainability.

internal/service/comment_service.go (1)

16-24: Consider adding validation in the constructor.

While the constructor is simple and follows good practices, consider adding validation to ensure the CommentDao dependency is not nil.

 func NewCommentService(cd *dao.CommentDao) *CommentService {
+	if cd == nil {
+		panic("CommentDao cannot be nil")
+	}
 	return &CommentService{
 		cd: cd,
 	}
 }
internal/controller/comment_controller.go (1)

42-55: Use appropriate HTTP method for DeleteComment.

The DeleteComment handler should use the DELETE HTTP method and URL parameters instead of form data.

Consider updating the router configuration to use:

router.DELETE("/comments/:comment_id", commentController.DeleteComment())
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9c0b187 and 1d4c51e.

📒 Files selected for processing (12)
  • internal/controller/activity_controller.go (1 hunks)
  • internal/controller/comment_controller.go (1 hunks)
  • internal/dao/comment_dao.go (1 hunks)
  • internal/middleware/cors.go (1 hunks)
  • internal/model/activity.go (1 hunks)
  • internal/model/comment.go (1 hunks)
  • internal/model/post.go (1 hunks)
  • internal/model/user.go (1 hunks)
  • internal/router/comment_router.go (1 hunks)
  • internal/service/comment_service.go (1 hunks)
  • internal/service/user_service.go (1 hunks)
  • tools/tool.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/model/user.go
  • internal/service/user_service.go
🔇 Additional comments (12)
internal/model/post.go (2)

3-6: LGTM! Imports are appropriate.

The imports are correctly defined and all are being used in the code.


18-19: LGTM! Good use of default values.

The default values of 0 for Comments and Likes fields are appropriate.

internal/middleware/cors.go (1)

8-10: LGTM! Good improvements to the CORS middleware.

The changes improve the code by:

  • Removing unnecessary error handling from HandleCors since CORS middleware setup rarely fails
  • Using concrete type returns which is preferred in Go when there's only one implementation
  • Maintaining secure CORS configuration with appropriate headers and methods

Also applies to: 16-18, 20-28

internal/model/activity.go (1)

41-49: Use pointer receivers when mutating struct fields.

The SetBid method won't persist changes to the Bid field since it uses a value receiver.

Apply this fix:

-func (act Activity) SetBid(ctx *gin.Context) error {
+func (act *Activity) SetBid(ctx *gin.Context) error {
internal/controller/activity_controller.go (3)

144-145: Fix incorrect query parameter name in FindActByLocation.

The method uses "type" instead of "location" for the query parameter.

Apply this fix:

-       target := ctx.Query("type")
+       target := ctx.Query("location")

161-162: Fix incorrect query parameter name in FindActByIfSignup.

The method uses "type" instead of "if_register" for the query parameter.

Apply this fix:

-       target := ctx.Query("type")
+       target := ctx.Query("if_register")

178-179: Fix incorrect query parameter name in FindActByIsForeign.

The method uses "type" instead of "visibility" for the query parameter.

Apply this fix:

-       target := ctx.Query("type")
+       target := ctx.Query("visibility")
internal/router/comment_router.go (1)

17-21: Check for nil references in constructor.
Consider adding a simple check to avoid nil dereference issues for cch or e.

internal/dao/comment_dao.go (2)

19-22: Instantiating CommentDao looks good.
No concerns; the constructor pattern is straightforward.


29-31: Verify whether the delete operation is intended to be soft or hard.
Since your model includes DeletedAt, GORM’s default behavior is soft delete. Double-check that this meets your business requirements.

internal/service/comment_service.go (1)

10-14: LGTM! Well-defined interface.

The CommentServiceHdl interface clearly defines the contract for comment operations with appropriate method signatures.

internal/controller/comment_controller.go (1)

10-14: LGTM! Clean interface and struct updates.

The interface and struct changes properly reflect the new service layer architecture.

Also applies to: 16-24

Title string `json:"title" gorm:"column:title; type:varchar(255); comment:标题; not null"`
Content string `json:"content" gorm:"column:content; type:text; comment:详细内容; not null"`
PosterId int `json:"poster_id" gorm:"column:poster_id; type:int; comment:发布者id; not null"`
PosterId string `json:"poster_id" gorm:"column:poster_id; type:varchar(255); comment:发布者id; not null"`
ImgUrls string `json:"img_urls" gorm:"column:img_urls; type:text; comment:图片链接; not null"`
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove not null constraint from ImgUrls.

The ImgUrls field should allow null values for posts without images:

-	ImgUrls  string `json:"img_urls" gorm:"column:img_urls; type:text; comment:图片链接; not null"`
+	ImgUrls  string `json:"img_urls" gorm:"column:img_urls; type:text; comment:图片链接"`
📝 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
ImgUrls string `json:"img_urls" gorm:"column:img_urls; type:text; comment:图片链接; not null"`
ImgUrls string `json:"img_urls" gorm:"column:img_urls; type:text; comment:图片链接"`

Comment on lines +10 to 13
func GenUUID(c *gin.Context) string {
u, _ := uuid.NewUUID()
return u.String()
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle UUID generation errors properly.

The function ignores potential UUID generation errors by using the blank identifier _. This could mask issues and make debugging harder.

Apply this fix:

func GenUUID(c *gin.Context) string {
-       u, _ := uuid.NewUUID()
+       u, err := uuid.NewUUID()
+       if err != nil {
+               return ""
+       }
        return u.String()
}
📝 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 GenUUID(c *gin.Context) string {
u, _ := uuid.NewUUID()
return u.String()
}
func GenUUID(c *gin.Context) string {
u, err := uuid.NewUUID()
if err != nil {
return ""
}
return u.String()
}

Comment on lines +195 to +198
//format: yyyy-mm-dd hh:mm:ss in db
start := ctx.Query("start_time") + ":00"
end := ctx.Query("end_time") + ":00"

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve time handling in FindActByTime.

The current implementation assumes a specific time format and manually appends seconds.

Consider using time.Parse for proper time handling:

-       start := ctx.Query("start_time") + ":00"
-       end := ctx.Query("end_time") + ":00"
+       start, err := time.Parse("2006-01-02 15:04:05", ctx.Query("start_time"))
+       if err != nil {
+               tools.ReturnMSG(ctx, "invalid start time format", nil)
+               return
+       }
+       end, err := time.Parse("2006-01-02 15:04:05", ctx.Query("end_time"))
+       if err != nil {
+               tools.ReturnMSG(ctx, "invalid end time format", nil)
+               return
+       }
📝 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
//format: yyyy-mm-dd hh:mm:ss in db
start := ctx.Query("start_time") + ":00"
end := ctx.Query("end_time") + ":00"
//format: yyyy-mm-dd hh:mm:ss in db
- start := ctx.Query("start_time") + ":00"
- end := ctx.Query("end_time") + ":00"
+ start, err := time.Parse("2006-01-02 15:04:05", ctx.Query("start_time"))
+ if err != nil {
+ tools.ReturnMSG(ctx, "invalid start time format", nil)
+ return
+ }
+ end, err := time.Parse("2006-01-02 15:04:05", ctx.Query("end_time"))
+ if err != nil {
+ tools.ReturnMSG(ctx, "invalid end time format", nil)
+ return
+ }

Comment on lines +9 to +12
type CommentDaoHdl interface {
CreateComment(*gin.Context, *model.Comment) error
DeleteComment(*gin.Context, string) error
AnswerComment(*gin.Context, *model.Comment) error
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider decoupling from Gin context in the interface definition.
The CommentDaoHdl interface could be more flexible if it took a standard context.Context parameter. This avoids locking the DAO to a specific web framework.

Comment on lines +26 to +29
func (cs *CommentService) CreateComment(c *gin.Context, cmt *model.Comment) error {
cmt.CommentID = tools.GenUUID(c)
return cs.cd.CreateComment(c, cmt)
}
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 and error wrapping.

The service methods should validate input parameters and wrap errors with context for better debugging.

 func (cs *CommentService) CreateComment(c *gin.Context, cmt *model.Comment) error {
+	if cmt == nil {
+		return fmt.Errorf("comment cannot be nil")
+	}
 	cmt.CommentID = tools.GenUUID(c)
-	return cs.cd.CreateComment(c, cmt)
+	if err := cs.cd.CreateComment(c, cmt); err != nil {
+		return fmt.Errorf("failed to create comment: %w", err)
+	}
+	return nil
 }

 func (cs *CommentService) DeleteComment(c *gin.Context, commentID string) error {
+	if commentID == "" {
+		return fmt.Errorf("comment ID cannot be empty")
+	}
-	return cs.cd.DeleteComment(c, commentID)
+	if err := cs.cd.DeleteComment(c, commentID); err != nil {
+		return fmt.Errorf("failed to delete comment: %w", err)
+	}
+	return nil
 }

 func (cs *CommentService) AnswerComment(c *gin.Context, cmt *model.Comment) error {
+	if cmt == nil {
+		return fmt.Errorf("comment cannot be nil")
+	}
+	if cmt.ParentID == "" {
+		return fmt.Errorf("parent comment ID cannot be empty")
+	}
 	cmt.CommentID = tools.GenUUID(c)
-	return cs.cd.AnswerComment(c, cmt)
+	if err := cs.cd.AnswerComment(c, cmt); err != nil {
+		return fmt.Errorf("failed to answer comment: %w", err)
+	}
+	return nil
 }

Don't forget to add the import:

+import "fmt"

Also applies to: 31-33, 35-38

Comment on lines 26 to +40
func (cc *CommentController) CreateComment() gin.HandlerFunc {
return func(c *gin.Context) {
var comment model.Comment
if err := c.ShouldBindJSON(&comment); err != nil {
tools.ReturnMSG(c, "bind error", err.Error())
var cmt model.Comment
//content, posterID, TargetID
if err := c.ShouldBindJSON(&cmt); err != nil {
tools.ReturnMSG(c, err.Error(), nil)
return
}
if err := cc.cs.CreateComment(c, &cmt); err != nil {
tools.ReturnMSG(c, err.Error(), nil)
return
}
tools.ReturnMSG(c, "comment success", 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

Enhance error handling and response structure.

The handlers should use consistent error handling and response structures.

 func (cc *CommentController) CreateComment() gin.HandlerFunc {
 	return func(c *gin.Context) {
 		var cmt model.Comment
-		//content, posterID, TargetID
 		if err := c.ShouldBindJSON(&cmt); err != nil {
-			tools.ReturnMSG(c, err.Error(), nil)
+			tools.ReturnMSG(c, "invalid request body", nil)
 			return
 		}
+		// Validate required fields
+		if cmt.Content == "" || cmt.PosterID == "" || cmt.TargetID == "" {
+			tools.ReturnMSG(c, "missing required fields", nil)
+			return
+		}
 		if err := cc.cs.CreateComment(c, &cmt); err != nil {
 			tools.ReturnMSG(c, err.Error(), nil)
 			return
 		}
-		tools.ReturnMSG(c, "comment success", nil)
+		tools.ReturnMSG(c, "comment created successfully", cmt)
 	}
 }

 func (cc *CommentController) DeleteComment() gin.HandlerFunc {
 	return func(c *gin.Context) {
-		cid := c.PostForm("comment_id")
+		cid := c.Param("comment_id") // Use URL parameter instead of form data for DELETE
 		if cid == "" {
-			tools.ReturnMSG(c, "comment not exist", nil)
+			tools.ReturnMSG(c, "comment ID is required", nil)
 			return
 		}
 		if err := cc.cs.DeleteComment(c, cid); err != nil {
 			tools.ReturnMSG(c, err.Error(), nil)
 			return
 		}
-		tools.ReturnMSG(c, "delete success", nil)
+		tools.ReturnMSG(c, "comment deleted successfully", nil)
 	}
 }

 func (cc *CommentController) AnswerComment() gin.HandlerFunc {
 	return func(c *gin.Context) {
 		var cmt model.Comment
-		//content, posterID, TargetID, ParentID
 		if err := c.ShouldBindJSON(&cmt); err != nil {
-			tools.ReturnMSG(c, err.Error(), nil)
+			tools.ReturnMSG(c, "invalid request body", nil)
 			return
 		}
+		// Validate required fields for answer
+		if cmt.Content == "" || cmt.PosterID == "" || cmt.TargetID == "" || cmt.ParentID == "" {
+			tools.ReturnMSG(c, "missing required fields", nil)
+			return
+		}
 		if err := cc.cs.AnswerComment(c, &cmt); err != nil {
 			tools.ReturnMSG(c, err.Error(), nil)
 			return
 		}
-		tools.ReturnMSG(c, "answer success", nil)
+		tools.ReturnMSG(c, "comment answered successfully", cmt)
 	}
 }

Also applies to: 42-55, 57-71

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: 10

🧹 Nitpick comments (10)
internal/model/comment.go (4)

8-17: Enhance documentation readability and accessibility.

Consider improving the documentation by:

  1. Adding English translations for better international collaboration
  2. Using proper Go doc formatting with complete sentences

Example improvement:

/*
- post -> comment -> subcomment 独立
-
- 在一个帖子下评论,获取帖子id,制作评论发布:评论id,发布者id,评论内容
- 回复评论,获取帖子id,评论id,制作回复:回复id,回复者id,回复内容
-
- act-post -> comment -> subcomment 共享
- 获取bid,制作评论发布:评论id,发布者id,评论内容
- 回复评论,获取bid,评论id,制作回复:回复id,回复者id,回复内容
+ Comment System Architecture:
+
+ 1. Independent Structure (Post -> Comment -> Subcomment):
+    - When commenting on a post: Uses post_id to create comment with {comment_id, poster_id, content}
+    - When replying to comment: Uses {post_id, comment_id} to create reply with {reply_id, replier_id, content}
+
+ 2. Shared Structure (Activity-Post -> Comment -> Subcomment):
+    - When commenting: Uses bid to create comment with {comment_id, poster_id, content}
+    - When replying: Uses {bid, comment_id} to create reply with {reply_id, replier_id, content}
*/

25-25: Use consistent naming in GORM tags.

The ParentID field uses an abbreviated column name "pnt_id" which is inconsistent with other field names.

-	ParentID  string `json:"parent_id" gorm:"column: pnt_id; type: varchar(255); comment: 父评论id,专用于回复"`
+	ParentID  string `json:"parent_id" gorm:"column: parent_id; type: varchar(255); comment: 父评论id,专用于回复"`

20-20: Add JSON tag for DeletedAt field.

The DeletedAt field is missing a JSON tag which might affect API responses.

-	DeletedAt gorm.DeletedAt
+	DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty"`

18-31: Consider reordering struct fields for better readability.

The current field ordering mixes metadata, identifiers, content, and metrics. Consider grouping related fields together.

 type Comment struct {
-	CreatedAt time.Time `json:"created_at" gorm:"column: created_at; not null; type: datetime; comment: 创建时间"`
-	DeletedAt gorm.DeletedAt
-
 	CommentID string `json:"comment_id" gorm:"column: comment_id; not null; type: varchar(255); comment: 评论ID;primary_key"`
 	PosterID  string `json:"poster_id" gorm:"column: poster_id; not null; type: varchar(255); comment: 发布者ID"`
 	TargetID  string `json:"target_id" gorm:"column: target_id; not null; type: varchar(255); comment: 帖子id或者绑定id"`
 	ParentID  string `json:"parent_id" gorm:"column: parent_id; type: varchar(255); comment: 父评论id,专用于回复"`
-
 	Content string `json:"content" gorm:"column: content; not null; type: text; comment: 评论内容"`
-
 	Likes   int `json:"likes" gorm:"column: likes; default:0; type: int; comment: 点赞数"`
 	Answers int `json:"answers" gorm:"column: answers; default:0; type: int; comment: 回复数"`
+
+	CreatedAt time.Time      `json:"created_at" gorm:"column: created_at; not null; type: datetime; comment: 创建时间"`
+	DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty"`
 }
docker-compose.yml (2)

2-20: Enhance Kafka service reliability and maintainability.

Consider the following improvements:

  1. Add a restart policy for automatic recovery
  2. Add healthcheck for better orchestration
  3. Use named volumes instead of relative paths
  4. Define networks explicitly
 kafka:
   image: docker.io/bitnami/kafka:3.9
   container_name: kafka_EG
+  restart: unless-stopped
   ports:
     - "9092:9092"
   volumes:
-    - ./volumes/kafka:/bitnami
+    - kafka_data:/bitnami
   environment:
     # KRaft settings
     - KAFKA_CFG_NODE_ID=0
     - KAFKA_CFG_PROCESS_ROLES=controller,broker
     - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
     # Listeners
     - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
     - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://:9092
     - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
     - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
     - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT
+  healthcheck:
+    test: ["CMD-SHELL", "kafka-topics.sh --bootstrap-server localhost:9092 --list"]
+    interval: 30s
+    timeout: 10s
+    retries: 3

Add at the end of the file:

volumes:
  kafka_data:
    driver: local

21-30: Improve Zookeeper service security and reliability.

Consider the following improvements:

  1. Add a restart policy
  2. Add healthcheck
  3. Use named volumes
  4. Review anonymous login for production use
 zookeeper:
   container_name: zookeeper_EG
   image: docker.io/bitnami/zookeeper:3.9
+  restart: unless-stopped
   ports:
     - '2181:2181'
   volumes:
-    - ./volumes/zookeeper:/bitnami/zookeeper
+    - zookeeper_data:/bitnami/zookeeper
   environment:
     - ALLOW_ANONYMOUS_LOGIN=yes
+  healthcheck:
+    test: ["CMD-SHELL", "echo ruok | nc localhost 2181 | grep imok"]
+    interval: 30s
+    timeout: 10s
+    retries: 3

Add at the end of the file:

volumes:
  zookeeper_data:
    driver: local
internal/controller/number_controller.go (1)

1-10: Consider architectural implications and integration points.

As part of the broader system restructuring:

  1. Consider how this controller integrates with PostController and ActivityController
  2. Ensure consistent error handling and response formats across controllers
  3. Consider implementing caching strategies for performance
  4. Think about concurrent access patterns and thread safety

Consider implementing a middleware or service layer to handle common concerns across controllers:

  • Caching strategy
  • Error handling
  • Metrics/monitoring
  • Rate limiting

Would you like me to provide example implementations for these architectural components?

test/docker-compose.yaml (3)

2-20: Consider enhancing Kafka security configuration.

While the KRaft configuration is correct, consider implementing these security measures:

  1. Enable authentication
  2. Restrict network access
  3. Use secrets for sensitive configurations

Apply this diff to enhance security:

   kafka:
     image: docker.io/bitnami/kafka:3.9
     container_name: kafka_EG
     ports:
-      - "9092:9092"
+      - "127.0.0.1:9092:9092"
     volumes:
       - ./volumes/kafka:/bitnami
     environment:
       # KRaft settings
       - KAFKA_CFG_NODE_ID=0
       - KAFKA_CFG_PROCESS_ROLES=controller,broker
       - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
       # Listeners
       - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
       - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://:9092
       - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
       - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
       - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT
+      # Security settings
+      - KAFKA_CFG_SASL_ENABLED_MECHANISMS=PLAIN
+      - KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL=PLAIN

21-30: Enhance Zookeeper security configuration.

The current configuration allows anonymous login and unrestricted network access, which could pose security risks.

Apply this diff to enhance security:

   zookeeper:
     container_name: zookeeper_EG
     image: docker.io/bitnami/zookeeper:3.9
     ports:
-      - '2181:2181'
+      - '127.0.0.1:2181:2181'
     volumes:
       - ./volumes/zookeeper:/bitnami/zookeeper
     environment:
-      - ALLOW_ANONYMOUS_LOGIN=yes
+      - ALLOW_ANONYMOUS_LOGIN=no
+      - ZOO_ENABLE_AUTH=yes
+      - ZOO_SERVER_USERS=kafka
+      - ZOO_SERVER_PASSWORDS=kafka_password

1-51: Add version and network configuration.

The file would benefit from:

  1. Specified docker-compose version
  2. Defined networks for service isolation

Add these configurations at the beginning of the file:

+version: '3.8'
+
 services:
   kafka:
     # ... existing kafka config ...
+    networks:
+      - backend
   
   zookeeper:
     # ... existing zookeeper config ...
+    networks:
+      - backend
   
   db:
     # ... existing db config ...
+    networks:
+      - backend
   
   redis:
     # ... existing redis config ...
+    networks:
+      - backend
+
+networks:
+  backend:
+    driver: bridge
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1d4c51e and c6e3356.

📒 Files selected for processing (9)
  • .gitignore (1 hunks)
  • docker-compose.yml (1 hunks)
  • internal/controller/number_controller.go (1 hunks)
  • internal/ioc/kafka.go (1 hunks)
  • internal/model/comment.go (1 hunks)
  • internal/model/user.go (1 hunks)
  • test/docker-compose.yaml (1 hunks)
  • wire.go (1 hunks)
  • wire_gen.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • .gitignore
  • wire.go
  • wire_gen.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
internal/ioc/kafka.go

11-11: field producer is unused

(unused)


12-12: field consumer is unused

(unused)

internal/model/user.go

10-10: SA5008: invalid JSON field name "likes; comment:赞过的帖子id"

(staticcheck)

🔇 Additional comments (5)
docker-compose.yml (1)

1-51: Inconsistency with PR summary.

The AI-generated summary states that "Kafka integration is completely removed", but this file adds Kafka and Zookeeper services. Please clarify if Kafka is being removed or added to the project.

Likely an incorrect or invalid review comment.

internal/model/user.go (1)

9-9: Consider migration impact of non-null School field.

Adding a non-null column without a default value could cause migration issues for existing users in the database.

Consider either:

  1. Adding a default value in the migration
  2. Making the field nullable
  3. Ensuring all existing users have a school value set during migration

Run this script to check for existing users that might be affected:

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Description: Check for existing users in the database that might be affected by the new non-null school field

# Search for database migration files
fd -e sql -e go migration | grep -i user

Length of output: 42


Review Migration Impact of Non-null School Field

The recent script did not reveal any migration scripts that update the user table to handle the new non-null "School" column. This means manual verification is required to ensure that:

  • Either a default value is set,
  • The field is made nullable, or
  • Existing user records are updated during migration to avoid issues.
internal/ioc/kafka.go (3)

3-3: No concerns about importing "github.com/IBM/sarama".

This import aligns well with the creation of an asynchronous Kafka producer and consumer group.


6-7: Flexible method signatures for producing and consuming.

Adding the key parameter to Produce and the group and handler parameters to Consume increases flexibility. Ensure the call sites supply these properly and handle possible errors from the handler function.


11-12: Unused fields detected by static analysis.

According to static analysis, producer and consumer are unused. Confirm if these fields will be initialized and used in future methods; otherwise, consider removing them to avoid clutter.

🧰 Tools
🪛 golangci-lint (1.62.2)

11-11: field producer is unused

(unused)


12-12: field consumer is unused

(unused)


CommentID string `json:"comment_id" gorm:"column: comment_id; not null; type: varchar(255); comment: 评论ID;primary_key"`
PosterID string `json:"poster_id" gorm:"column: poster_id; not null; type: varchar(255); comment: 发布者ID"`
TargetID string `json:"target_id" gorm:"column: targareet_id; not null; type: varchar(255); comment: 帖子id或者绑定id"`
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in TargetID field's GORM tag.

There's a typo in the column name: "targareet_id" should be "target_id".

-	TargetID  string `json:"target_id" gorm:"column: targareet_id; not null; type: varchar(255); comment: 帖子id或者绑定id"`
+	TargetID  string `json:"target_id" gorm:"column: target_id; not null; type: varchar(255); comment: 帖子id或者绑定id"`
📝 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
TargetID string `json:"target_id" gorm:"column: targareet_id; not null; type: varchar(255); comment: 帖子id或者绑定id"`
TargetID string `json:"target_id" gorm:"column: target_id; not null; type: varchar(255); comment: 帖子id或者绑定id"`

Comment on lines +31 to +41
db:
image: mysql:8.0
container_name: mysql_EG
restart: always
environment:
MYSQL_ROOT_PASSWORD: 114514
ports:
- "3306:3306"
volumes:
- ./volumes/mysql:/var/lib/mysql

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Address MySQL security concerns and configuration.

  1. Move sensitive data to environment variables
  2. Create specific database and user instead of using root
  3. Configure character set and collation
  4. Use named volumes
 db:
   image: mysql:8.0
   container_name: mysql_EG
   restart: always
   environment:
-    MYSQL_ROOT_PASSWORD: 114514
+    MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
+    MYSQL_DATABASE: ${MYSQL_DATABASE}
+    MYSQL_USER: ${MYSQL_USER}
+    MYSQL_PASSWORD: ${MYSQL_PASSWORD}
+    MYSQL_CHARACTER_SET_SERVER: utf8mb4
+    MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
   ports:
     - "3306:3306"
   volumes:
-    - ./volumes/mysql:/var/lib/mysql
+    - mysql_data:/var/lib/mysql
+  healthcheck:
+    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
+    interval: 30s
+    timeout: 10s
+    retries: 3

Create a .env file (add to .gitignore):

MYSQL_ROOT_PASSWORD=your_secure_password
MYSQL_DATABASE=app_db
MYSQL_USER=app_user
MYSQL_PASSWORD=app_password

Add at the end of the file:

volumes:
  mysql_data:
    driver: local

Comment on lines +42 to +50
redis:
image: redis:7.0
container_name: redis_EG
restart: always
ports:
- "6379:6379"
volumes:
- ./volumes/redis:/data

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 Redis security and performance configuration.

Consider the following improvements:

  1. Add password protection
  2. Set memory limits
  3. Configure persistence properly
  4. Add healthcheck
 redis:
   image: redis:7.0
   container_name: redis_EG
   restart: always
+  command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 512mb --maxmemory-policy allkeys-lru
   ports:
     - "6379:6379"
   volumes:
-    - ./volumes/redis:/data
+    - redis_data:/data
+  healthcheck:
+    test: ["CMD", "redis-cli", "ping"]
+    interval: 30s
+    timeout: 10s
+    retries: 3

Add to .env file:

REDIS_PASSWORD=your_secure_password

Add at the end of the file:

volumes:
  redis_data:
    driver: local

Comment on lines +8 to +9
type NumberController struct {
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Empty struct needs implementation and dependencies.

The NumberController struct is empty and missing:

  1. Implementation of the NumberControllerHdl interface
  2. Required dependencies (e.g., database connection or caching layer)
  3. Documentation explaining its purpose

Consider adding necessary dependencies and implementing the interface:

+// NumberController implements NumberControllerHdl interface for managing post engagement metrics
 type NumberController struct {
+    db    *gorm.DB
+    cache cache.Store
 }
+
+// NewNumberController creates a new instance of NumberController
+func NewNumberController(db *gorm.DB, cache cache.Store) *NumberController {
+    return &NumberController{
+        db:    db,
+        cache: cache,
+    }
+}
📝 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
type NumberController struct {
}
// NumberController implements NumberControllerHdl interface for managing post engagement metrics
type NumberController struct {
db *gorm.DB
cache cache.Store
}
// NewNumberController creates a new instance of NumberController
func NewNumberController(db *gorm.DB, cache cache.Store) *NumberController {
return &NumberController{
db: db,
cache: cache,
}
}

Comment on lines +3 to +6
type NumberControllerHdl interface {
HandleLikesNum()
HandleCommentsNum()
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Interface methods need parameters, return types, and documentation.

The interface methods are currently incomplete:

  1. Methods should have parameters to identify what numbers to handle
  2. Methods should have return types to provide results
  3. Method names should clarify their purpose (get/set/update?)
  4. Interface and methods need documentation

Consider this improved interface definition:

+// NumberControllerHdl handles operations related to counting likes and comments
 type NumberControllerHdl interface {
-    HandleLikesNum()
-    HandleCommentsNum()
+    // GetLikesCount returns the number of likes for a given post
+    GetLikesCount(postID string) (int64, error)
+    // GetCommentsCount returns the number of comments for a given post
+    GetCommentsCount(postID string) (int64, error)
+    // UpdateLikesCount updates the likes count for a given post
+    UpdateLikesCount(postID string, delta int) error
+    // UpdateCommentsCount updates the comments count for a given post
+    UpdateCommentsCount(postID string, delta int) 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
type NumberControllerHdl interface {
HandleLikesNum()
HandleCommentsNum()
}
// NumberControllerHdl handles operations related to counting likes and comments
type NumberControllerHdl interface {
// GetLikesCount returns the number of likes for a given post
GetLikesCount(postID string) (int64, error)
// GetCommentsCount returns the number of comments for a given post
GetCommentsCount(postID string) (int64, error)
// UpdateLikesCount updates the likes count for a given post
UpdateLikesCount(postID string, delta int) error
// UpdateCommentsCount updates the comments count for a given post
UpdateCommentsCount(postID string, delta int) error
}

StudentId string `gorm:"column:sid;type:varchar(255);not null" json:"sid"`
Name string `gorm:"column:name;type:varchar(255);not null" json:"name"`
Avatar string `gorm:"column:avatar;type:varchar(255);not null" json:"avatar"`
School string `gorm:"column:school;type:varchar(255);not null" json:"school"`
Likes string `gorm:"column:likes;type:varchar(255);not null" json:"likes; comment:赞过的帖子id"`
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Fix invalid JSON tag and improve likes storage design.

  1. The JSON tag contains invalid syntax by including a comment.
  2. Storing post IDs as a string field suggests a suboptimal design.

First, fix the JSON tag:

-	Likes     string `gorm:"column:likes;type:varchar(255);not null" json:"likes; comment:赞过的帖子id"`
+	Likes     string `gorm:"column:likes;type:varchar(255);not null" json:"likes"`

Then, consider refactoring to use a many-to-many relationship:

type UserLike struct {
    UserId    int       `gorm:"column:user_id;type:int;not null" json:"user_id"`
    PostId    int       `gorm:"column:post_id;type:int;not null" json:"post_id"`
    CreatedAt time.Time `gorm:"column:created_at;type:timestamp;not null" json:"created_at"`
}

This design would:

  • Make it easier to query liked posts
  • Improve performance
  • Allow tracking when each post was liked
  • Enable better indexing
🧰 Tools
🪛 golangci-lint (1.62.2)

10-10: SA5008: invalid JSON field name "likes; comment:赞过的帖子id"

(staticcheck)

@@ -2,13 +2,10 @@ package model

// 每个首次使用的用户根据插入数据库,用于活动草稿,头像,昵称操作
type User struct {
Id int `gorm:"column:id; type: int; not null; primary_key; autoIncrement"
:"id"`
Id int `gorm:"column:id; type: int; not null; primary_key; autoIncrement"`
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 missing JSON tag for the Id field.

The JSON tag for the Id field has been removed, which means it won't be included in JSON responses. This could break client applications expecting this field.

Apply this diff to restore the JSON tag:

-	Id        int    `gorm:"column:id; type: int; not null; primary_key; autoIncrement"`
+	Id        int    `gorm:"column:id; type: int; not null; primary_key; autoIncrement" json:"id"`
📝 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
Id int `gorm:"column:id; type: int; not null; primary_key; autoIncrement"`
Id int `gorm:"column:id; type: int; not null; primary_key; autoIncrement" json:"id"`

Comment on lines +15 to +16
func NewKafkaHdl() *Kafka {
return &Kafka{}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Initialize the producer and consumer or remove them if not used.

Currently, NewKafkaHdl returns a *Kafka with nil fields. This will cause runtime errors if Produce or Consume methods assume non-nil producer and consumer. If initialization is planned, consider something like the following:

 func NewKafkaHdl() *Kafka {
-	return &Kafka{}
+	producer, _ := sarama.NewAsyncProducer([]string{"localhost:9092"}, nil)
+	consumer, _ := sarama.NewConsumerGroup([]string{"localhost:9092"}, "groupID", nil)
+	return &Kafka{
+		producer: &producer,
+		consumer: &consumer,
+	}
 }

Note: Replace "localhost:9092" and "groupID" with your actual broker address(es) and consumer group.

📝 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 NewKafkaHdl() *Kafka {
return &Kafka{}
func NewKafkaHdl() *Kafka {
producer, _ := sarama.NewAsyncProducer([]string{"localhost:9092"}, nil)
consumer, _ := sarama.NewConsumerGroup([]string{"localhost:9092"}, "groupID", nil)
return &Kafka{
producer: &producer,
consumer: &consumer,
}
}

Comment on lines 30 to 41
db:
image: mysql:8.0
container_name: mysql_EG
restart: always
image: docker.io/bitnami/kafka:latest
network_mode: "bridge"
container_name: kafka_1
environment:
MYSQL_ROOT_PASSWORD: 114514
ports:
- "9004:9004"
- "9092:9092"
- "3306:3306"
volumes:
- /d/docker_temp/kafka/kafka_1:/bitnami/kafka #持久化数据
environment:
- TZ=Asia/Shanghai - KAFKA_BROKER_ID=1
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://8.148.22.219:9092 #替换成你自己的IP
- KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
- ALLOW_PLAINTEXT_LISTENER=yes
depends_on:
- zookeeper No newline at end of file
- ./volumes/mysql:/var/lib/mysql

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve MySQL security and configuration.

Current configuration has several security concerns:

  1. Hardcoded root password
  2. Unrestricted network access
  3. Missing specific database/user setup

Apply this diff to enhance security and follow best practices:

   db:
     image: mysql:8.0
     container_name: mysql_EG
     restart: always
     environment:
-      MYSQL_ROOT_PASSWORD: 114514
+      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
+      MYSQL_DATABASE: myapp
+      MYSQL_USER: myapp_user
+      MYSQL_PASSWORD_FILE: /run/secrets/db_password
     ports:
-        - "3306:3306"
+        - "127.0.0.1:3306:3306"
     volumes:
         - ./volumes/mysql:/var/lib/mysql
+    secrets:
+      - db_root_password
+      - db_password
+
+secrets:
+  db_root_password:
+    file: ./secrets/db_root_password.txt
+  db_password:
+    file: ./secrets/db_password.txt
📝 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
db:
image: mysql:8.0
container_name: mysql_EG
restart: always
image: docker.io/bitnami/kafka:latest
network_mode: "bridge"
container_name: kafka_1
environment:
MYSQL_ROOT_PASSWORD: 114514
ports:
- "9004:9004"
- "9092:9092"
- "3306:3306"
volumes:
- /d/docker_temp/kafka/kafka_1:/bitnami/kafka #持久化数据
environment:
- TZ=Asia/Shanghai - KAFKA_BROKER_ID=1
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://8.148.22.219:9092 #替换成你自己的IP
- KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
- ALLOW_PLAINTEXT_LISTENER=yes
depends_on:
- zookeeper
\ No newline at end of file
- ./volumes/mysql:/var/lib/mysql
db:
image: mysql:8.0
container_name: mysql_EG
restart: always
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
MYSQL_DATABASE: myapp
MYSQL_USER: myapp_user
MYSQL_PASSWORD_FILE: /run/secrets/db_password
ports:
- "127.0.0.1:3306:3306"
volumes:
- ./volumes/mysql:/var/lib/mysql
secrets:
- db_root_password
- db_password
secrets:
db_root_password:
file: ./secrets/db_root_password.txt
db_password:
file: ./secrets/db_password.txt

Comment on lines +42 to +50
image: redis:7.0
container_name: redis_EG
restart: always
ports:
- "6379:6379"
volumes:
- ./volumes/redis:/data

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 Redis security and add resource constraints.

Current configuration lacks:

  1. Password protection
  2. Network access restrictions
  3. Memory limits

Apply this diff to enhance security and add resource management:

   redis:
     image: redis:7.0
     container_name: redis_EG
     restart: always
+    command: redis-server --requirepass ${REDIS_PASSWORD}
     ports:
-        - "6379:6379"
+        - "127.0.0.1:6379:6379"
     volumes:
         - ./volumes/redis:/data
+    environment:
+      - REDIS_PASSWORD_FILE=/run/secrets/redis_password
+    deploy:
+      resources:
+        limits:
+          memory: 512M
+    secrets:
+      - redis_password
+
+secrets:
+  redis_password:
+    file: ./secrets/redis_password.txt
📝 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
redis:
image: redis:7.0
container_name: redis_EG
restart: always
ports:
- "6379:6379"
volumes:
- ./volumes/redis:/data
redis:
image: redis:7.0
container_name: redis_EG
restart: always
command: redis-server --requirepass ${REDIS_PASSWORD}
ports:
- "127.0.0.1:6379:6379"
volumes:
- ./volumes/redis:/data
environment:
- REDIS_PASSWORD_FILE=/run/secrets/redis_password
deploy:
resources:
limits:
memory: 512M
secrets:
- redis_password
secrets:
redis_password:
file: ./secrets/redis_password.txt

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