diff --git a/etc/ppanel.yaml b/etc/ppanel.yaml index d19d1f7..0ecc9bd 100644 --- a/etc/ppanel.yaml +++ b/etc/ppanel.yaml @@ -15,10 +15,10 @@ Logger: # 日志配置 Level: debug # 日志级别: debug, info, warn, error, panic, fatal MySQL: - Addr: 154.12.35.103:3306 # host 网络模式; bridge 模式改为 mysql:3306 + Addr: 103.150.215.44:3306 # host 网络模式; bridge 模式改为 mysql:3306 Username: root # MySQL用户名 Password: jpcV41ppanel # MySQL密码,与 .env MYSQL_ROOT_PASSWORD 一致 - Dbname: ppanel # MySQL数据库名 + Dbname: hifast # MySQL数据库名 Config: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai MaxIdleConns: 10 MaxOpenConns: 100 diff --git a/internal/svc/serviceContext.go b/internal/svc/serviceContext.go index b358f31..bd26660 100644 --- a/internal/svc/serviceContext.go +++ b/internal/svc/serviceContext.go @@ -107,8 +107,6 @@ func NewServiceContext(c config.Config) *ServiceContext { err = rds.Ping(context.Background()).Err() if err != nil { panic(err.Error()) - } else { - _ = rds.FlushAll(context.Background()).Err() } authLimiter := limit.NewPeriodLimit(86400, 15, rds, config.SendCountLimitKeyPrefix, limit.Align()) nonceStore := signature.NewRedisNonceStore(rds) diff --git a/scripts/migrate_paid_users.go b/scripts/migrate_paid_users.go index ecc62aa..cc69f56 100644 --- a/scripts/migrate_paid_users.go +++ b/scripts/migrate_paid_users.go @@ -276,8 +276,13 @@ func main() { SELECT user_id AS uid FROM ` + "`order`" + ` WHERE status = ? AND user_id > 0 UNION SELECT user_id AS uid FROM apple_iap_transactions WHERE user_id > 0 + UNION + SELECT user_id AS uid FROM user_subscribe WHERE user_id > 0 ) t INNER JOIN user u ON u.id = t.uid + WHERE u.id NOT IN ( + SELECT user_id FROM user_auth_methods WHERE auth_type = 'email' AND auth_identifier = 'devneeds52@gmail.com' + ) ORDER BY t.uid `, orderStatusCompleted).Scan(&paidIDs).Error if err != nil { @@ -674,10 +679,22 @@ func main() { now := time.Now() familyCount := 0 + // ── 为了配合基于新 UID 的条件检查,预先构建映射 ── + deviceCountByNewUID := make(map[int64]int) + for i := range devices { + deviceCountByNewUID[devices[i].UserId]++ + } + hasEmailByNewUID := make(map[int64]bool) + for i := range auths { + if auths[i].AuthType == "email" { + hasEmailByNewUID[auths[i].UserId] = true + } + } + err = dstDB.Transaction(func(tx *gorm.DB) error { for _, uid := range paidIDs { - // 只为多设备用户创建家庭组 - if len(deviceByUser[uid]) <= 1 { + // 只为多设备且有邮箱的用户创建家庭组 + if deviceCountByNewUID[uid] <= 1 || !hasEmailByNewUID[uid] { continue } @@ -753,55 +770,58 @@ func main() { } } - // 4. 查找原用户的家庭组(如果不存在则创建,虽然理论上 Step 8 已经为多设备用户创建了) - var family UserFamily - if err := tx.Where("owner_user_id = ?", s.OwnerUID).First(&family).Error; err != nil { - if err == gorm.ErrRecordNotFound { - // 补救措施:为该用户创建一个家庭组 - family = UserFamily{ - OwnerUserId: s.OwnerUID, - MaxMembers: defaultFamilyMaxSize, - Status: 1, - CreatedAt: now, - UpdatedAt: now, + // 仅仅当原用户有邮箱时,才尝试将其加入家庭组(无邮箱的仅拆分为独立用户) + if hasEmailByNewUID[s.OwnerUID] { + // 4. 查找原用户的家庭组(如果不存在则创建,虽然理论上 Step 8 已经为多设备用户创建了) + var family UserFamily + if err := tx.Where("owner_user_id = ?", s.OwnerUID).First(&family).Error; err != nil { + if err == gorm.ErrRecordNotFound { + // 补救措施:为该用户创建一个家庭组 + family = UserFamily{ + OwnerUserId: s.OwnerUID, + MaxMembers: defaultFamilyMaxSize, + Status: 1, + CreatedAt: now, + UpdatedAt: now, + } + if err := tx.Create(&family).Error; err != nil { + return fmt.Errorf("创建家庭组补救失败(owner=%d): %w", s.OwnerUID, err) + } + + // 创建家主成员 + ownerMember := UserFamilyMember{ + FamilyId: family.Id, + UserId: s.OwnerUID, + Role: familyRoleOwner, + Status: 1, + JoinSource: "migration_split_recovery", + JoinedAt: now, + CreatedAt: now, + UpdatedAt: now, + } + if err := tx.Create(&ownerMember).Error; err != nil { + return fmt.Errorf("创建家主成员补救失败(owner=%d): %w", s.OwnerUID, err) + } + familyCount++ // 更新计数器 + } else { + return fmt.Errorf("查找家庭组失败(owner=%d): %w", s.OwnerUID, err) } - if err := tx.Create(&family).Error; err != nil { - return fmt.Errorf("创建家庭组补救失败(owner=%d): %w", s.OwnerUID, err) - } - - // 创建家主成员 - ownerMember := UserFamilyMember{ - FamilyId: family.Id, - UserId: s.OwnerUID, - Role: familyRoleOwner, - Status: 1, - JoinSource: "migration_split_recovery", - JoinedAt: now, - CreatedAt: now, - UpdatedAt: now, - } - if err := tx.Create(&ownerMember).Error; err != nil { - return fmt.Errorf("创建家主成员补救失败(owner=%d): %w", s.OwnerUID, err) - } - familyCount++ // 更新计数器 - } else { - return fmt.Errorf("查找家庭组失败(owner=%d): %w", s.OwnerUID, err) } - } - // 5. 加入家庭组 - member := UserFamilyMember{ - FamilyId: family.Id, - UserId: newUser.Id, - Role: familyRoleMember, - Status: 1, - JoinSource: "migration_split", - JoinedAt: s.Device.CreatedAt, - CreatedAt: now, - UpdatedAt: now, - } - if err := tx.Create(&member).Error; err != nil { - return fmt.Errorf("添加家庭成员失败: %w", err) + // 5. 加入家庭组 + member := UserFamilyMember{ + FamilyId: family.Id, + UserId: newUser.Id, + Role: familyRoleMember, + Status: 1, + JoinSource: "migration_split", + JoinedAt: s.Device.CreatedAt, + CreatedAt: now, + UpdatedAt: now, + } + if err := tx.Create(&member).Error; err != nil { + return fmt.Errorf("添加家庭成员失败: %w", err) + } } splitCount++ diff --git a/说明文档.md b/说明文档.md index 3b82543..4b2f60b 100644 --- a/说明文档.md +++ b/说明文档.md @@ -15,16 +15,17 @@ | 2026-03-11 | 提供今天所有 ERROR 报错指令 | [x] 已完成 | 已提供根据日期过滤 ERROR 的命令 | | 2026-03-12 | 分析并确认 Unknown column 错误 | [x] 已完成 | 确认为 `user_device` 缺少 `short_code` 字段,已提供 SQL | | 2026-03-12 | 提供 SSL 证书替换指令 | [x] 已完成 | 已提供备份与替换证书的组合指令 | +| 2026-03-17 | 合并 internal 到 internal/main | [x] 已完成 | 已查验均为fast-forward,受限网络/权限,需手动push完成合并 | certbot certonly --manual --preferred-challenges dns -d airoport.win -d "*.airoport.win" -d hifastapp.com - gunzip -c mysql_dump_20260317_145137.sql.gz \ + gunzip -c mysql_dump_20260318_052811.sql.gz \ | docker exec -i ppanel-mysql mysql -uroot -pjpcV41ppanel -go run scripts/migrate_paid_users.go -src 'root:rootpassword@tcp(127.0.0.1:3306)/ppanel?charset=utf8mb4&parseTime=True&loc=Local' -dst 'root:jpcV41ppanel@tcp(154.12.35.103:3306)/ppanel?charset=utf8mb4&parseTime=True&loc=Local' -clean +go run scripts/migrate_paid_users.go -src 'root:rootpassword@tcp(127.0.0.1:3306)/ppanel?charset=utf8mb4&parseTime=True&loc=Local' -dst 'root:jpcV41ppanel@tcp(103.150.215.44:3306)/hifast?charset=utf8mb4&parseTime=True&loc=Local' -clean