diff --git a/backend/api/container.go b/backend/api/container.go index f7790db..3e757f5 100644 --- a/backend/api/container.go +++ b/backend/api/container.go @@ -2,4 +2,5 @@ package api type ApiHandlers struct { UserHandler *UserHandler + TaskHandler *TaskHandler } diff --git a/backend/api/task.go b/backend/api/task.go new file mode 100644 index 0000000..4cf1c17 --- /dev/null +++ b/backend/api/task.go @@ -0,0 +1,68 @@ +package api + +import ( + "errors" + "fmt" + "net/http" + + "github.com/LoveLosita/smartflow/backend/model" + "github.com/LoveLosita/smartflow/backend/respond" + "github.com/LoveLosita/smartflow/backend/service" + "github.com/gin-gonic/gin" +) + +type TaskHandler struct { + // 伸出手:准备接住 Service + svc *service.TaskService +} + +// NewTaskHandler 创建 TaskHandler 实例 +func NewTaskHandler(svc *service.TaskService) *TaskHandler { + return &TaskHandler{ + svc: svc, + } +} + +func (th *TaskHandler) AddTask(c *gin.Context) { + //1. 绑定请求参数 + var req model.UserAddTaskRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, respond.WrongParamType) + fmt.Println(err) + return + } + // 用户ID从上下文中获取 + userID := c.GetInt("user_id") + //2. 调用 Service 层处理业务逻辑 + resp, err := th.svc.AddTask(&req, userID) + if err != nil { + switch { + case errors.Is(err, respond.InvalidPriority): //如果是无效刷新令牌或者无效claims或者无效签名方法 + c.JSON(http.StatusBadRequest, err) + return + default: + c.JSON(http.StatusInternalServerError, respond.InternalError(err)) + } + } + //3. 返回响应 + c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp)) +} + +func (th *TaskHandler) GetUserTasks(c *gin.Context) { + // 用户ID从上下文中获取 + userID := c.GetInt("user_id") + //2. 调用 Service 层处理业务逻辑 + resp, err := th.svc.GetUserTasks(userID) + if err != nil { + switch { + case errors.Is(err, respond.UserTasksEmpty): //如果任务列表为空 + c.JSON(http.StatusOK, respond.RespWithData(respond.UserTasksEmpty, []model.Task{})) //确实没错误,但是任务列表为空,返回自定义响应 + return + default: + c.JSON(http.StatusInternalServerError, respond.InternalError(err)) + return + } + } + //3. 返回响应 + c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp)) +} diff --git a/backend/api/user.go b/backend/api/user.go index 52f1c2f..b236e78 100644 --- a/backend/api/user.go +++ b/backend/api/user.go @@ -47,7 +47,7 @@ func (api *UserHandler) UserRegister(c *gin.Context) { } } - c.JSON(http.StatusOK, respond.OKWithData(respond.Ok, retUser)) + c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, retUser)) } func (api *UserHandler) UserLogin(c *gin.Context) { @@ -68,7 +68,7 @@ func (api *UserHandler) UserLogin(c *gin.Context) { return } } - c.JSON(http.StatusOK, respond.OKWithData(respond.Ok, tokens)) + c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, tokens)) } func (api *UserHandler) RefreshTokenHandler(c *gin.Context) { @@ -93,7 +93,7 @@ func (api *UserHandler) RefreshTokenHandler(c *gin.Context) { c.JSON(http.StatusInternalServerError, respond.InternalError(err)) } } - c.JSON(http.StatusOK, respond.OKWithData(respond.Ok, tokens)) + c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, tokens)) } func (api *UserHandler) UserLogout(c *gin.Context) { diff --git a/backend/cmd/start.go b/backend/cmd/start.go index 7a0d454..c6ee81d 100644 --- a/backend/cmd/start.go +++ b/backend/cmd/start.go @@ -39,13 +39,20 @@ func Start() { log.Fatalf("Failed to connect to database: %v", err) } rdb := inits.InitRedis() - + //dao 层 userRepo := dao.NewUserDAO(db) cacheRepo := dao.NewCacheDAO(rdb) + taskRepo := dao.NewTaskDAO(db) + //service 层 userService := service.NewUserService(userRepo, cacheRepo) + taskSv := service.NewTaskService(taskRepo) + //api 层 userApi := api.NewUserHandler(userService) + taskApi := api.NewTaskHandler(taskSv) + handlers := &api.ApiHandlers{ UserHandler: userApi, + TaskHandler: taskApi, } r := routers.RegisterRouters(handlers, cacheRepo) routers.StartEngine(r) diff --git a/backend/conv/task.go b/backend/conv/task.go new file mode 100644 index 0000000..a7cbbae --- /dev/null +++ b/backend/conv/task.go @@ -0,0 +1,52 @@ +package conv + +import ( + "time" + + "github.com/LoveLosita/smartflow/backend/model" +) + +func UserAddTaskRequestToModel(request *model.UserAddTaskRequest, userID int) *model.Task { + return &model.Task{ + Title: request.Title, + Priority: request.PriorityGroup, + DeadlineAt: request.DeadlineAt, + UserID: userID, + } +} + +func ModelToUserAddTaskResponse(task *model.Task) *model.UserAddTaskResponse { + status := "incomplete" + if task.IsCompleted { + status = "completed" + } + return &model.UserAddTaskResponse{ + ID: task.ID, + Title: task.Title, + PriorityGroup: task.Priority, + DeadlineAt: task.DeadlineAt, + Status: status, + CreatedAt: time.Now(), // 创建时间为当前时间 + } +} + +func ModelToGetUserTasksResp(tasks []model.Task) []model.GetUserTaskResp { + var resp []model.GetUserTaskResp + for _, task := range tasks { + status := "incomplete" + if task.IsCompleted { + status = "completed" + } + deadline := task.DeadlineAt.Format("2006-01-02 15:04:05") + resp = append(resp, model.GetUserTaskResp{ + ID: task.ID, + UserID: task.UserID, + Title: task.Title, + PriorityGroup: task.Priority, + Status: status, + Deadline: deadline, + IsCompleted: task.IsCompleted, + }) + } + return resp +} diff --git a/backend/dao/task.go b/backend/dao/task.go new file mode 100644 index 0000000..5ce2c9f --- /dev/null +++ b/backend/dao/task.go @@ -0,0 +1,39 @@ +package dao + +import ( + "github.com/LoveLosita/smartflow/backend/model" + "github.com/LoveLosita/smartflow/backend/respond" + "gorm.io/gorm" +) + +type TaskDAO struct { + // 这是一个口袋,用来装数据库连接实例 + db *gorm.DB +} + +// NewTaskDAO 创建TaskDAO实例 +// NewTaskDAO 接收一个 *gorm.DB,并把它塞进结构体的口袋里 +func NewTaskDAO(db *gorm.DB) *TaskDAO { + return &TaskDAO{ + db: db, + } +} + +// AddTask 为指定用户添加任务 +func (dao *TaskDAO) AddTask(req *model.Task) (*model.Task, error) { + if err := dao.db.Create(req).Error; err != nil { + return nil, err + } + return req, nil +} + +func (dao *TaskDAO) GetTasksByUserID(userID int) ([]model.Task, error) { + var tasks []model.Task + if err := dao.db.Where("user_id = ?", userID).Find(&tasks).Error; err != nil { + return nil, err + } + if len(tasks) == 0 { // 如果没有任务,返回自定义错误 + return nil, respond.UserTasksEmpty + } + return tasks, nil +} diff --git a/backend/model/task.go b/backend/model/task.go new file mode 100644 index 0000000..0540f90 --- /dev/null +++ b/backend/model/task.go @@ -0,0 +1,36 @@ +package model + +import "time" + +type Task struct { + ID int `gorm:"primaryKey;autoIncrement"` + UserID int `gorm:"column:user_id;index"` + Title string `gorm:"type:varchar(255)"` + Priority int `gorm:"not null"` + IsCompleted bool `gorm:"column:is_completed;default:false"` + DeadlineAt time.Time `gorm:"column:deadline_at"` +} + +type UserAddTaskResponse struct { + ID int `json:"id"` + Title string `json:"title"` + PriorityGroup int `json:"priority_group"` + DeadlineAt time.Time `json:"deadline_at"` + Status string `json:"status"` + CreatedAt time.Time `json:"created_at"` +} + +type UserAddTaskRequest struct { + Title string `json:"title"` + PriorityGroup int `json:"priority_group"` + DeadlineAt time.Time `json:"deadline_at"` +} +type GetUserTaskResp struct { + ID int `json:"id"` + UserID int `json:"user_id"` + Title string `json:"title"` + PriorityGroup int `json:"priority_group"` + Status string `json:"status"` + Deadline string `json:"deadline"` + IsCompleted bool `json:"is_completed"` +} diff --git a/backend/respond/respond.go b/backend/respond/respond.go index 00d556c..1ae8f95 100644 --- a/backend/respond/respond.go +++ b/backend/respond/respond.go @@ -18,7 +18,7 @@ func (r Response) Error() string { // 实现 error 接口 return r.Info } -func OKWithData(response Response, data interface{}) FinalResponse { //传入一个响应结构体和数据,返回一个最终响应结构体 +func RespWithData(response Response, data interface{}) FinalResponse { //传入一个响应结构体和数据,返回一个最终响应结构体 var finalResponse FinalResponse finalResponse.Status = response.Status finalResponse.Info = response.Info @@ -39,6 +39,11 @@ var ( //请求相关的响应 Info: "success", } + UserTasksEmpty = Response{ //用户任务为空 + Status: "10001", + Info: "user tasks empty", + } + WrongName = Response{ //用户名错误 Status: "40001", Info: "wrong username", @@ -123,4 +128,9 @@ var ( //请求相关的响应 Status: "40017", Info: "user logged out", } + + InvalidPriority = Response{ //无效优先级 + Status: "40018", + Info: "invalid priority", + } ) diff --git a/backend/routers/routers.go b/backend/routers/routers.go index 99a7ae9..ca2cde2 100644 --- a/backend/routers/routers.go +++ b/backend/routers/routers.go @@ -48,6 +48,12 @@ func RegisterRouters(handlers *api.ApiHandlers, cache *dao.CacheDAO) *gin.Engine userGroup.POST("/refresh-token", handlers.UserHandler.RefreshTokenHandler) userGroup.POST("/logout", middleware.JWTTokenAuth(cache), handlers.UserHandler.UserLogout) } + taskGroup := apiGroup.Group("/task") + { + taskGroup.Use(middleware.JWTTokenAuth(cache)) + taskGroup.POST("/create", handlers.TaskHandler.AddTask) + taskGroup.GET("/get", handlers.TaskHandler.GetUserTasks) + } } // 初始化Gin引擎 log.Println("Routes setup completed") diff --git a/backend/service/task.go b/backend/service/task.go new file mode 100644 index 0000000..7512e80 --- /dev/null +++ b/backend/service/task.go @@ -0,0 +1,48 @@ +package service + +import ( + "github.com/LoveLosita/smartflow/backend/conv" + "github.com/LoveLosita/smartflow/backend/dao" + "github.com/LoveLosita/smartflow/backend/model" + "github.com/LoveLosita/smartflow/backend/respond" +) + +type TaskService struct { + // 伸出手:准备接住 DAO + dao *dao.TaskDAO +} + +// NewTaskService 创建 TaskService 实例 +func NewTaskService(dao *dao.TaskDAO) *TaskService { + return &TaskService{ + dao: dao, + } +} + +func (ts *TaskService) AddTask(req *model.UserAddTaskRequest, userID int) (*model.UserAddTaskResponse, error) { + //1. 调用 conv 层进行转换 + taskModel := conv.UserAddTaskRequestToModel(req, userID) + //2.检查优先级是否合法 + if taskModel.Priority < 1 || taskModel.Priority >= 5 { + return nil, respond.InvalidPriority + } + //3. 调用 dao 层进行数据持久化 + createdTask, err := ts.dao.AddTask(taskModel) + if err != nil { + return nil, err + } + //4. 调用 conv 层进行响应转换 + response := conv.ModelToUserAddTaskResponse(createdTask) + return response, nil +} + +func (ts *TaskService) GetUserTasks(userID int) ([]model.GetUserTaskResp, error) { + //1. 调用 dao 层获取数据 + tasks, err := ts.dao.GetTasksByUserID(userID) + if err != nil { + return nil, err + } + //2. 调用 conv 层进行响应转换 + response := conv.ModelToGetUserTasksResp(tasks) + return response, nil +}