Исправление отображения проектов в условиях
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m21s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m21s
This commit is contained in:
@@ -334,6 +334,7 @@ type WishlistRequest struct {
|
||||
}
|
||||
|
||||
type UnlockConditionRequest struct {
|
||||
ID *int `json:"id,omitempty"` // ID существующего условия (для сохранения чужих условий)
|
||||
Type string `json:"type"`
|
||||
TaskID *int `json:"task_id,omitempty"`
|
||||
ProjectID *int `json:"project_id,omitempty"`
|
||||
@@ -2865,6 +2866,12 @@ func (a *App) initAuthDB() error {
|
||||
// Не возвращаем ошибку, чтобы приложение могло запуститься
|
||||
}
|
||||
|
||||
// Apply migration 025: Remove wishlist conditions without user_id
|
||||
if err := a.applyMigration025(); err != nil {
|
||||
log.Printf("Warning: Failed to apply migration 025: %v", err)
|
||||
// Не возвращаем ошибку, чтобы приложение могло запуститься
|
||||
}
|
||||
|
||||
// Clean up expired refresh tokens (only those with expiration date set)
|
||||
a.DB.Exec("DELETE FROM refresh_tokens WHERE expires_at IS NOT NULL AND expires_at < NOW()")
|
||||
|
||||
@@ -3268,6 +3275,50 @@ func (a *App) applyMigration024() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyMigration025 применяет миграцию 025_remove_conditions_without_user_id.sql
|
||||
func (a *App) applyMigration025() error {
|
||||
log.Printf("Applying migration 025: Remove wishlist conditions without user_id")
|
||||
|
||||
// Проверяем, есть ли условия без user_id
|
||||
var count int
|
||||
err := a.DB.QueryRow(`
|
||||
SELECT COUNT(*)
|
||||
FROM wishlist_conditions
|
||||
WHERE user_id IS NULL
|
||||
`).Scan(&count)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check conditions without user_id: %w", err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
log.Printf("Migration 025 already applied (no conditions without user_id), skipping")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Found %d conditions without user_id, removing them", count)
|
||||
|
||||
// Читаем SQL из файла миграции
|
||||
migrationPath := "migrations/025_remove_conditions_without_user_id.sql"
|
||||
if _, err := os.Stat(migrationPath); os.IsNotExist(err) {
|
||||
// Пробуем альтернативный путь (в Docker)
|
||||
migrationPath = "/migrations/025_remove_conditions_without_user_id.sql"
|
||||
}
|
||||
|
||||
migrationSQL, err := os.ReadFile(migrationPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read migration file %s: %w", migrationPath, err)
|
||||
}
|
||||
|
||||
// Выполняем миграцию
|
||||
if _, err := a.DB.Exec(string(migrationSQL)); err != nil {
|
||||
return fmt.Errorf("failed to execute migration 025: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Migration 025 applied successfully, removed %d conditions without user_id", count)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) initPlayLifeDB() error {
|
||||
// Создаем таблицу projects
|
||||
createProjectsTable := `
|
||||
@@ -9372,7 +9423,7 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
||||
LEFT JOIN task_conditions tc ON wc.task_condition_id = tc.id
|
||||
LEFT JOIN tasks t ON tc.task_id = t.id
|
||||
LEFT JOIN score_conditions sc ON wc.score_condition_id = sc.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id AND p.deleted = FALSE
|
||||
WHERE wi.user_id = $1
|
||||
AND wi.deleted = FALSE
|
||||
AND ($2 = TRUE OR wi.completed = FALSE)
|
||||
@@ -9457,6 +9508,10 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
||||
if projectName.Valid {
|
||||
condition.ProjectName = &projectName.String
|
||||
}
|
||||
if projectID.Valid {
|
||||
projectIDVal := int(projectID.Int64)
|
||||
condition.ProjectID = &projectIDVal
|
||||
}
|
||||
if requiredPoints.Valid {
|
||||
condition.RequiredPoints = &requiredPoints.Float64
|
||||
}
|
||||
@@ -9633,18 +9688,43 @@ func (a *App) getWishlistItemsWithConditions(userID int, includeCompleted bool)
|
||||
}
|
||||
|
||||
// saveWishlistConditions сохраняет условия для желания
|
||||
// userID - автор условий (пользователь, который создает/обновляет условия)
|
||||
func (a *App) saveWishlistConditions(
|
||||
tx *sql.Tx,
|
||||
wishlistItemID int,
|
||||
userID int,
|
||||
conditions []UnlockConditionRequest,
|
||||
) error {
|
||||
// Удаляем старые условия
|
||||
_, err := tx.Exec(`
|
||||
DELETE FROM wishlist_conditions
|
||||
// Получаем все существующие условия с их user_id перед удалением
|
||||
existingConditions := make(map[int]int) // map[conditionID]userID
|
||||
rows, err := tx.Query(`
|
||||
SELECT id, user_id
|
||||
FROM wishlist_conditions
|
||||
WHERE wishlist_item_id = $1
|
||||
`, wishlistItemID)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting existing conditions: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var condID int
|
||||
var condUserID sql.NullInt64
|
||||
if err := rows.Scan(&condID, &condUserID); err != nil {
|
||||
return fmt.Errorf("error scanning existing condition: %w", err)
|
||||
}
|
||||
if condUserID.Valid {
|
||||
existingConditions[condID] = int(condUserID.Int64)
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем только условия текущего пользователя
|
||||
_, err = tx.Exec(`
|
||||
DELETE FROM wishlist_conditions
|
||||
WHERE wishlist_item_id = $1 AND user_id = $2
|
||||
`, wishlistItemID, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting user conditions: %w", err)
|
||||
}
|
||||
|
||||
if len(conditions) == 0 {
|
||||
@@ -9654,8 +9734,8 @@ func (a *App) saveWishlistConditions(
|
||||
// Подготавливаем statement для вставки условий
|
||||
stmt, err := tx.Prepare(`
|
||||
INSERT INTO wishlist_conditions
|
||||
(wishlist_item_id, task_condition_id, score_condition_id, display_order)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
(wishlist_item_id, user_id, task_condition_id, score_condition_id, display_order)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -9743,9 +9823,20 @@ func (a *App) saveWishlistConditions(
|
||||
scoreConditionID = scID
|
||||
}
|
||||
|
||||
// Определяем user_id для условия:
|
||||
// - Если условие имеет id и это условие существовало - используем его оригинальный user_id
|
||||
// - Иначе - используем userID текущего пользователя
|
||||
conditionUserID := userID
|
||||
if condition.ID != nil {
|
||||
if originalUserID, exists := existingConditions[*condition.ID]; exists {
|
||||
conditionUserID = originalUserID
|
||||
}
|
||||
}
|
||||
|
||||
// Создаём связь
|
||||
_, err = stmt.Exec(
|
||||
wishlistItemID,
|
||||
conditionUserID,
|
||||
taskConditionID,
|
||||
scoreConditionID,
|
||||
displayOrder,
|
||||
@@ -9937,7 +10028,7 @@ func (a *App) createWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Сохраняем условия
|
||||
if len(req.UnlockConditions) > 0 {
|
||||
err = a.saveWishlistConditions(tx, wishlistID, req.UnlockConditions)
|
||||
err = a.saveWishlistConditions(tx, wishlistID, userID, req.UnlockConditions)
|
||||
if err != nil {
|
||||
log.Printf("Error saving wishlist conditions: %v", err)
|
||||
sendErrorWithCORS(w, fmt.Sprintf("Error saving wishlist conditions: %v", err), http.StatusInternalServerError)
|
||||
@@ -10088,7 +10179,7 @@ func (a *App) getWishlistItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||
LEFT JOIN task_conditions tc ON wc.task_condition_id = tc.id
|
||||
LEFT JOIN tasks t ON tc.task_id = t.id
|
||||
LEFT JOIN score_conditions sc ON wc.score_condition_id = sc.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id AND p.deleted = FALSE
|
||||
WHERE wi.id = $1
|
||||
AND wi.deleted = FALSE
|
||||
ORDER BY wc.display_order, wc.id
|
||||
@@ -10185,6 +10276,12 @@ func (a *App) getWishlistItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if projectName.Valid {
|
||||
condition.ProjectName = &projectName.String
|
||||
}
|
||||
if projectID.Valid {
|
||||
projectIDVal := int(projectID.Int64)
|
||||
condition.ProjectID = &projectIDVal
|
||||
points, _ := a.calculateProjectPointsFromDate(int(projectID.Int64), startDate, conditionOwnerID)
|
||||
condition.CurrentPoints = &points
|
||||
}
|
||||
if requiredPoints.Valid {
|
||||
condition.RequiredPoints = &requiredPoints.Float64
|
||||
}
|
||||
@@ -10192,10 +10289,6 @@ func (a *App) getWishlistItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||
dateStr := startDate.Time.Format("2006-01-02")
|
||||
condition.StartDate = &dateStr
|
||||
}
|
||||
if projectID.Valid {
|
||||
points, _ := a.calculateProjectPointsFromDate(int(projectID.Int64), startDate, conditionOwnerID)
|
||||
condition.CurrentPoints = &points
|
||||
}
|
||||
}
|
||||
|
||||
item.UnlockConditions = append(item.UnlockConditions, condition)
|
||||
@@ -10358,7 +10451,7 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Сохраняем условия
|
||||
err = a.saveWishlistConditions(tx, itemID, req.UnlockConditions)
|
||||
err = a.saveWishlistConditions(tx, itemID, userID, req.UnlockConditions)
|
||||
if err != nil {
|
||||
log.Printf("Error saving wishlist conditions: %v", err)
|
||||
sendErrorWithCORS(w, fmt.Sprintf("Error saving wishlist conditions: %v", err), http.StatusInternalServerError)
|
||||
@@ -10397,7 +10490,7 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
LEFT JOIN task_conditions tc ON wc.task_condition_id = tc.id
|
||||
LEFT JOIN tasks t ON tc.task_id = t.id
|
||||
LEFT JOIN score_conditions sc ON wc.score_condition_id = sc.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id AND p.deleted = FALSE
|
||||
WHERE wi.id = $1
|
||||
AND wi.deleted = FALSE
|
||||
ORDER BY wc.display_order, wc.id
|
||||
@@ -10500,6 +10593,12 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if projectName.Valid {
|
||||
condition.ProjectName = &projectName.String
|
||||
}
|
||||
if projectID.Valid {
|
||||
projectIDVal := int(projectID.Int64)
|
||||
condition.ProjectID = &projectIDVal
|
||||
points, _ := a.calculateProjectPointsFromDate(int(projectID.Int64), startDate, conditionOwnerID)
|
||||
condition.CurrentPoints = &points
|
||||
}
|
||||
if requiredPoints.Valid {
|
||||
condition.RequiredPoints = &requiredPoints.Float64
|
||||
}
|
||||
@@ -10507,10 +10606,6 @@ func (a *App) updateWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
dateStr := startDate.Time.Format("2006-01-02")
|
||||
condition.StartDate = &dateStr
|
||||
}
|
||||
if projectID.Valid {
|
||||
points, _ := a.calculateProjectPointsFromDate(int(projectID.Int64), startDate, conditionOwnerID)
|
||||
condition.CurrentPoints = &points
|
||||
}
|
||||
}
|
||||
|
||||
item.UnlockConditions = append(item.UnlockConditions, condition)
|
||||
@@ -10944,11 +11039,13 @@ func (a *App) copyWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var link sql.NullString
|
||||
var imagePath sql.NullString
|
||||
var ownerID int
|
||||
var boardID sql.NullInt64
|
||||
var authorID sql.NullInt64
|
||||
err = a.DB.QueryRow(`
|
||||
SELECT user_id, name, price, link, image_path
|
||||
SELECT user_id, name, price, link, image_path, board_id, author_id
|
||||
FROM wishlist_items
|
||||
WHERE id = $1 AND deleted = FALSE
|
||||
`, itemID).Scan(&ownerID, &name, &price, &link, &imagePath)
|
||||
`, itemID).Scan(&ownerID, &name, &price, &link, &imagePath, &boardID, &authorID)
|
||||
|
||||
if err == sql.ErrNoRows || ownerID != userID {
|
||||
sendErrorWithCORS(w, "Wishlist item not found", http.StatusNotFound)
|
||||
@@ -11039,11 +11136,23 @@ func (a *App) copyWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
linkVal = link.String
|
||||
}
|
||||
|
||||
// Определяем значения для board_id и author_id
|
||||
var boardIDVal, authorIDVal interface{}
|
||||
if boardID.Valid {
|
||||
boardIDVal = int(boardID.Int64)
|
||||
}
|
||||
if authorID.Valid {
|
||||
authorIDVal = int(authorID.Int64)
|
||||
} else {
|
||||
// Если author_id не был установлен, используем текущего пользователя
|
||||
authorIDVal = userID
|
||||
}
|
||||
|
||||
err = tx.QueryRow(`
|
||||
INSERT INTO wishlist_items (user_id, name, price, link, completed, deleted)
|
||||
VALUES ($1, $2, $3, $4, FALSE, FALSE)
|
||||
INSERT INTO wishlist_items (user_id, board_id, author_id, name, price, link, completed, deleted)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, FALSE, FALSE)
|
||||
RETURNING id
|
||||
`, userID, name+" (копия)", priceVal, linkVal).Scan(&newWishlistID)
|
||||
`, ownerID, boardIDVal, authorIDVal, name+" (копия)", priceVal, linkVal).Scan(&newWishlistID)
|
||||
if err != nil {
|
||||
log.Printf("Error creating wishlist copy: %v", err)
|
||||
sendErrorWithCORS(w, fmt.Sprintf("Error creating wishlist copy: %v", err), http.StatusInternalServerError)
|
||||
@@ -11052,7 +11161,7 @@ func (a *App) copyWishlistHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Сохраняем условия
|
||||
if len(conditions) > 0 {
|
||||
err = a.saveWishlistConditions(tx, newWishlistID, conditions)
|
||||
err = a.saveWishlistConditions(tx, newWishlistID, userID, conditions)
|
||||
if err != nil {
|
||||
log.Printf("Error saving wishlist conditions: %v", err)
|
||||
sendErrorWithCORS(w, fmt.Sprintf("Error saving wishlist conditions: %v", err), http.StatusInternalServerError)
|
||||
@@ -12022,7 +12131,7 @@ func (a *App) getBoardCompletedHandler(w http.ResponseWriter, r *http.Request) {
|
||||
LEFT JOIN task_conditions tc ON wc.task_condition_id = tc.id
|
||||
LEFT JOIN tasks t ON tc.task_id = t.id
|
||||
LEFT JOIN score_conditions sc ON wc.score_condition_id = sc.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id AND p.deleted = FALSE
|
||||
LEFT JOIN users u ON wc.user_id = u.id
|
||||
WHERE wi.board_id = $1
|
||||
AND wi.deleted = FALSE
|
||||
@@ -12178,6 +12287,7 @@ func (a *App) getWishlistItemsByBoard(boardID int, userID int) ([]WishlistItem,
|
||||
wi.image_path,
|
||||
wi.link,
|
||||
wi.completed,
|
||||
COALESCE(wi.author_id, wi.user_id) AS item_owner_id,
|
||||
wc.id AS condition_id,
|
||||
wc.display_order,
|
||||
wc.task_condition_id,
|
||||
@@ -12194,7 +12304,7 @@ func (a *App) getWishlistItemsByBoard(boardID int, userID int) ([]WishlistItem,
|
||||
LEFT JOIN task_conditions tc ON wc.task_condition_id = tc.id
|
||||
LEFT JOIN tasks t ON tc.task_id = t.id
|
||||
LEFT JOIN score_conditions sc ON wc.score_condition_id = sc.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id
|
||||
LEFT JOIN projects p ON sc.project_id = p.id AND p.deleted = FALSE
|
||||
WHERE wi.board_id = $1
|
||||
AND wi.deleted = FALSE
|
||||
AND wi.completed = FALSE
|
||||
@@ -12216,6 +12326,7 @@ func (a *App) getWishlistItemsByBoard(boardID int, userID int) ([]WishlistItem,
|
||||
var imagePath sql.NullString
|
||||
var link sql.NullString
|
||||
var completed bool
|
||||
var itemOwnerID sql.NullInt64
|
||||
var conditionID sql.NullInt64
|
||||
var displayOrder sql.NullInt64
|
||||
var taskConditionID sql.NullInt64
|
||||
@@ -12229,7 +12340,7 @@ func (a *App) getWishlistItemsByBoard(boardID int, userID int) ([]WishlistItem,
|
||||
var startDate sql.NullTime
|
||||
|
||||
err := rows.Scan(
|
||||
&itemID, &name, &price, &imagePath, &link, &completed,
|
||||
&itemID, &name, &price, &imagePath, &link, &completed, &itemOwnerID,
|
||||
&conditionID, &displayOrder, &taskConditionID, &scoreConditionID, &conditionUserID,
|
||||
&taskID, &taskName, &projectID, &projectName, &requiredPoints, &startDate,
|
||||
)
|
||||
@@ -12268,8 +12379,12 @@ func (a *App) getWishlistItemsByBoard(boardID int, userID int) ([]WishlistItem,
|
||||
DisplayOrder: int(displayOrder.Int64),
|
||||
}
|
||||
|
||||
// Используем user_id из условия, если он есть, иначе используем текущего пользователя
|
||||
conditionOwnerID := userID
|
||||
// Используем user_id из условия, если он есть, иначе используем владельца желания
|
||||
if !itemOwnerID.Valid {
|
||||
log.Printf("Warning: item_owner_id is NULL for wishlist item %d, skipping condition", itemID)
|
||||
continue
|
||||
}
|
||||
conditionOwnerID := int(itemOwnerID.Int64)
|
||||
if conditionUserID.Valid {
|
||||
conditionOwnerID = int(conditionUserID.Int64)
|
||||
}
|
||||
@@ -12291,6 +12406,13 @@ func (a *App) getWishlistItemsByBoard(boardID int, userID int) ([]WishlistItem,
|
||||
if projectName.Valid {
|
||||
condition.ProjectName = &projectName.String
|
||||
}
|
||||
if projectID.Valid {
|
||||
projectIDVal := int(projectID.Int64)
|
||||
condition.ProjectID = &projectIDVal
|
||||
// Считаем текущие баллы для владельца условия
|
||||
points, _ := a.calculateProjectPointsFromDate(int(projectID.Int64), startDate, conditionOwnerID)
|
||||
condition.CurrentPoints = &points
|
||||
}
|
||||
if requiredPoints.Valid {
|
||||
condition.RequiredPoints = &requiredPoints.Float64
|
||||
}
|
||||
@@ -12298,11 +12420,6 @@ func (a *App) getWishlistItemsByBoard(boardID int, userID int) ([]WishlistItem,
|
||||
dateStr := startDate.Time.Format("2006-01-02")
|
||||
condition.StartDate = &dateStr
|
||||
}
|
||||
// Считаем текущие баллы для владельца условия
|
||||
if projectID.Valid {
|
||||
points, _ := a.calculateProjectPointsFromDate(int(projectID.Int64), startDate, conditionOwnerID)
|
||||
condition.CurrentPoints = &points
|
||||
}
|
||||
}
|
||||
|
||||
item.UnlockConditions = append(item.UnlockConditions, condition)
|
||||
|
||||
Reference in New Issue
Block a user