Skip to content

Commit 5a70f5a

Browse files
committed
Added Goal model and related apis
1 parent 24155ea commit 5a70f5a

1 file changed

Lines changed: 171 additions & 17 deletions

File tree

backend/main.go

Lines changed: 171 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,31 @@ type ComplexInput struct {
4141
Category string `json:"category" validate:"required,min=1,max=100"`
4242
}
4343

44-
// type Goal struct { ... }
44+
// Goal represents the goal entity.
45+
type Goal struct {
46+
ID uint `gorm:"primarykey" json:"id"`
47+
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index"`
48+
ComplexID uint `json:"complex_id" gorm:"not null;index"` // Foreign key to Complex
49+
SurfaceGoal string `json:"surface_goal" gorm:"not null"`
50+
UnderlyingGoal string `json:"underlying_goal" gorm:"not null"`
51+
CreatedAt time.Time `json:"created_at"`
52+
UpdatedAt time.Time `json:"updated_at"`
53+
Complex Complex `gorm:"foreignKey:ComplexID"` // Belongs to Complex
54+
}
55+
56+
// GoalInput defines the expected input for creating or updating a goal.
57+
type GoalInput struct {
58+
ComplexID uint `json:"complex_id" validate:"required"`
59+
SurfaceGoal string `json:"surface_goal" validate:"required,min=1,max=255"`
60+
UnderlyingGoal string `json:"underlying_goal" validate:"required,min=1,max=255"`
61+
}
62+
4563
// type Action struct { ... }
4664

65+
// --- GORM AutoMigrate Helper ---
66+
// (This can be moved to a more appropriate place like a db setup function later)
67+
var modelsToMigrate = []interface{}{&Complex{}, &Goal{}} // Add &Action{} later
68+
4769
// ErrorResponse Helper
4870
type ErrorResponse struct {
4971
Code int `json:"code"`
@@ -212,12 +234,144 @@ func DeleteComplexHandler(c *gin.Context) {
212234
c.Status(http.StatusNoContent)
213235
}
214236

215-
// Goal Handlers (スタブ)
216-
// func CreateGoalHandler(c *gin.Context) { /* ... */ }
217-
// func GetGoalsHandler(c *gin.Context) { /* ... */ }
218-
// func GetGoalHandler(c *gin.Context) { /* ... */ }
219-
// func UpdateGoalHandler(c *gin.Context) { /* ... */ }
220-
// func DeleteGoalHandler(c *gin.Context) { /* ... */ }
237+
// CreateGoalHandler handles the creation of a new goal.
238+
func CreateGoalHandler(c *gin.Context) {
239+
userID, exists := c.Get("userID")
240+
if !exists {
241+
c.JSON(http.StatusUnauthorized, NewErrorResponse(http.StatusUnauthorized, "User ID not found in context"))
242+
return
243+
}
244+
245+
var input GoalInput
246+
if err := c.ShouldBindJSON(&input); err != nil {
247+
c.JSON(http.StatusBadRequest, NewErrorResponse(http.StatusBadRequest, "Invalid request body: "+err.Error()))
248+
return
249+
}
250+
251+
if err := validate.Struct(input); err != nil {
252+
c.JSON(http.StatusBadRequest, NewErrorResponse(http.StatusBadRequest, "Validation failed: "+err.Error()))
253+
return
254+
}
255+
256+
// Check if the referenced complex exists and belongs to the user
257+
var complex Complex
258+
if err := db.Where("id = ? AND user_id = ?", input.ComplexID, userID.(string)).First(&complex).Error; err != nil {
259+
if err == gorm.ErrRecordNotFound {
260+
c.JSON(http.StatusBadRequest, NewErrorResponse(http.StatusBadRequest, "Referenced complex not found or does not belong to user"))
261+
return
262+
}
263+
c.JSON(http.StatusInternalServerError, NewErrorResponse(http.StatusInternalServerError, "Error checking complex: "+err.Error()))
264+
return
265+
}
266+
267+
goal := Goal{
268+
UserID: userID.(string),
269+
ComplexID: input.ComplexID,
270+
SurfaceGoal: input.SurfaceGoal,
271+
UnderlyingGoal: input.UnderlyingGoal,
272+
}
273+
274+
if result := db.Create(&goal); result.Error != nil {
275+
c.JSON(http.StatusInternalServerError, NewErrorResponse(http.StatusInternalServerError, "Failed to create goal: "+result.Error.Error()))
276+
return
277+
}
278+
279+
c.JSON(http.StatusCreated, goal)
280+
}
281+
282+
// GetGoalsHandler handles fetching all goals for the authenticated user.
283+
func GetGoalsHandler(c *gin.Context) {
284+
userID, exists := c.Get("userID")
285+
if !exists {
286+
c.JSON(http.StatusUnauthorized, NewErrorResponse(http.StatusUnauthorized, "User ID not found in context"))
287+
return
288+
}
289+
290+
var goals []Goal
291+
if result := db.Where("user_id = ?", userID.(string)).Find(&goals); result.Error != nil {
292+
c.JSON(http.StatusInternalServerError, NewErrorResponse(http.StatusInternalServerError, "Failed to fetch goals: "+result.Error.Error()))
293+
return
294+
}
295+
296+
if goals == nil {
297+
goals = []Goal{}
298+
}
299+
c.JSON(http.StatusOK, goals)
300+
}
301+
302+
// GetGoalHandler handles fetching a specific goal by its ID.
303+
func GetGoalHandler(c *gin.Context) {
304+
userID, exists := c.Get("userID")
305+
if !exists {
306+
c.JSON(http.StatusUnauthorized, NewErrorResponse(http.StatusUnauthorized, "User ID not found in context"))
307+
return
308+
}
309+
goalID := c.Param("goalId")
310+
311+
var goal Goal
312+
if err := db.Where("id = ? AND user_id = ?", goalID, userID.(string)).First(&goal).Error; err != nil {
313+
if err == gorm.ErrRecordNotFound {
314+
c.JSON(http.StatusNotFound, NewErrorResponse(http.StatusNotFound, "Goal not found"))
315+
return
316+
}
317+
c.JSON(http.StatusInternalServerError, NewErrorResponse(http.StatusInternalServerError, "Failed to fetch goal: "+err.Error()))
318+
return
319+
}
320+
c.JSON(http.StatusOK, goal)
321+
}
322+
323+
// UpdateGoalHandler handles updating an existing goal.
324+
func UpdateGoalHandler(c *gin.Context) {
325+
userID, exists := c.Get("userID")
326+
if !exists {
327+
c.JSON(http.StatusUnauthorized, NewErrorResponse(http.StatusUnauthorized, "User ID not found in context"))
328+
return
329+
}
330+
goalID := c.Param("goalId")
331+
332+
var input GoalInput
333+
if err := c.ShouldBindJSON(&input); err != nil {
334+
c.JSON(http.StatusBadRequest, NewErrorResponse(http.StatusBadRequest, "Invalid request body: "+err.Error()))
335+
return
336+
}
337+
if err := validate.Struct(input); err != nil {
338+
c.JSON(http.StatusBadRequest, NewErrorResponse(http.StatusBadRequest, "Validation failed: "+err.Error()))
339+
return
340+
}
341+
342+
var goal Goal
343+
if err := db.Where("id = ? AND user_id = ?", goalID, userID.(string)).First(&goal).Error; err != nil {
344+
c.JSON(http.StatusNotFound, NewErrorResponse(http.StatusNotFound, "Goal not found to update"))
345+
return
346+
}
347+
348+
// Note: ComplexID update is not allowed here for simplicity, but can be added if needed.
349+
// If ComplexID is updated, ensure the new ComplexID also belongs to the user.
350+
goal.SurfaceGoal = input.SurfaceGoal
351+
goal.UnderlyingGoal = input.UnderlyingGoal
352+
353+
if err := db.Save(&goal).Error; err != nil {
354+
c.JSON(http.StatusInternalServerError, NewErrorResponse(http.StatusInternalServerError, "Failed to update goal: "+err.Error()))
355+
return
356+
}
357+
c.JSON(http.StatusOK, goal)
358+
}
359+
360+
// DeleteGoalHandler handles deleting a goal by its ID.
361+
func DeleteGoalHandler(c *gin.Context) {
362+
userID, exists := c.Get("userID")
363+
if !exists {
364+
c.JSON(http.StatusUnauthorized, NewErrorResponse(http.StatusUnauthorized, "User ID not found in context"))
365+
return
366+
}
367+
goalID := c.Param("goalId")
368+
369+
if result := db.Where("id = ? AND user_id = ?", goalID, userID.(string)).Delete(&Goal{}); result.Error != nil || result.RowsAffected == 0 {
370+
c.JSON(http.StatusNotFound, NewErrorResponse(http.StatusNotFound, "Goal not found or already deleted"))
371+
return
372+
}
373+
c.Status(http.StatusNoContent)
374+
}
221375

222376
// Action Handlers (スタブ)
223377
// func CreateActionHandler(c *gin.Context) { /* ... */ }
@@ -299,8 +453,8 @@ func main() {
299453
} else {
300454
log.Println("ℹ️ MIGRATION_PATH not set, skipping automated migrations. Ensure DB schema is up to date.")
301455
// 開発初期段階ではGORMのAutoMigrateも便利です
302-
// log.Println("Running GORM AutoMigrate for initial schema setup...")
303-
// db.AutoMigrate(&Complex{}, &Goal{}, &Action{}) // ここにモデルを追加
456+
log.Println("Running GORM AutoMigrate for initial schema setup...")
457+
db.AutoMigrate(modelsToMigrate...) // Use the slice for AutoMigrate
304458
}
305459

306460
// --- Ginルーターの初期化 ---
@@ -331,14 +485,14 @@ func main() {
331485
}
332486

333487
// Goals
334-
// goalsGroup := apiV1.Group("/goals")
335-
// {
336-
// // goalsGroup.POST("", CreateGoalHandler)
337-
// // goalsGroup.GET("", GetGoalsHandler)
338-
// // goalsGroup.GET("/:goalId", GetGoalHandler)
339-
// // goalsGroup.PUT("/:goalId", UpdateGoalHandler)
340-
// // goalsGroup.DELETE("/:goalId", DeleteGoalHandler)
341-
// }
488+
goalsGroup := apiV1.Group("/goals")
489+
{
490+
goalsGroup.POST("", CreateGoalHandler)
491+
goalsGroup.GET("", GetGoalsHandler)
492+
goalsGroup.GET("/:goalId", GetGoalHandler)
493+
goalsGroup.PUT("/:goalId", UpdateGoalHandler)
494+
goalsGroup.DELETE("/:goalId", DeleteGoalHandler)
495+
}
342496

343497
// Actions
344498
// actionsGroup := apiV1.Group("/actions")

0 commit comments

Comments
 (0)