邮箱修复
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m13s
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m13s
This commit is contained in:
parent
a3cc23bbd4
commit
7d5b4fcb84
@ -47,6 +47,25 @@ func (l *DeleteAccountLogic) DeleteAccountAll() (resp *types.DeleteAccountRespon
|
||||
// 事务前先查出 AuthMethods,用于事务后精确清缓存
|
||||
authMethods, _ := l.svcCtx.UserModel.FindUserAuthMethods(l.ctx, currentUser.Id)
|
||||
|
||||
// 事务前查出家庭关系,判断当前用户是否为 member 且 owner 是邮箱用户
|
||||
var familyOwnerUserID int64
|
||||
if relation, relErr := l.findActiveFamilyRelation(currentUser.Id); relErr == nil && relation.Role == user.FamilyRoleMember {
|
||||
// 当前用户是 member,查 owner
|
||||
var ownerMember user.UserFamilyMember
|
||||
if ownerErr := l.svcCtx.DB.WithContext(l.ctx).
|
||||
Model(&user.UserFamilyMember{}).
|
||||
Where("family_id = ? AND role = ? AND status = ?", relation.FamilyId, user.FamilyRoleOwner, user.FamilyMemberActive).
|
||||
First(&ownerMember).Error; ownerErr == nil {
|
||||
familyOwnerUserID = ownerMember.UserId
|
||||
}
|
||||
}
|
||||
|
||||
// 事务前查出 owner 的 AuthMethods(用于事务后清缓存)
|
||||
var ownerAuthMethods []*user.AuthMethods
|
||||
if familyOwnerUserID > 0 {
|
||||
ownerAuthMethods, _ = l.svcCtx.UserModel.FindUserAuthMethods(l.ctx, familyOwnerUserID)
|
||||
}
|
||||
|
||||
affectedUserIDs := []int64{currentUser.Id}
|
||||
err = l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
|
||||
familyUserIDs, collectErr := l.collectAffectedFamilyUserIDs(tx, currentUser.Id)
|
||||
@ -70,6 +89,36 @@ func (l *DeleteAccountLogic) DeleteAccountAll() (resp *types.DeleteAccountRespon
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "delete user subscribes failed")
|
||||
}
|
||||
|
||||
// 如果 owner(邮箱用户)没有其他活跃成员了,也清理 owner 的数据
|
||||
if familyOwnerUserID > 0 {
|
||||
var remainingMembers int64
|
||||
if err := tx.Model(&user.UserFamilyMember{}).
|
||||
Where("family_id IN (SELECT id FROM user_family WHERE owner_user_id = ? AND deleted_at IS NULL) AND status = ? AND deleted_at IS NULL",
|
||||
familyOwnerUserID, user.FamilyMemberActive).
|
||||
Count(&remainingMembers).Error; err == nil && remainingMembers <= 1 {
|
||||
// 只剩 owner 自己(或没人了),清理 owner 数据
|
||||
// 删除 owner 的 auth_methods
|
||||
if err := tx.Where("user_id = ?", familyOwnerUserID).Delete(&user.AuthMethods{}).Error; err != nil {
|
||||
l.Errorw("delete owner auth methods failed", logger.Field("owner_id", familyOwnerUserID), logger.Field("error", err.Error()))
|
||||
}
|
||||
// 删除 owner 的订阅
|
||||
if err := tx.Where("user_id = ?", familyOwnerUserID).Delete(&user.Subscribe{}).Error; err != nil {
|
||||
l.Errorw("delete owner subscribes failed", logger.Field("owner_id", familyOwnerUserID), logger.Field("error", err.Error()))
|
||||
}
|
||||
// 解散 owner 的家庭
|
||||
exitHelper2 := newFamilyExitHelper(l.ctx, l.svcCtx)
|
||||
if removeErr := exitHelper2.removeUserFromActiveFamily(tx, familyOwnerUserID, true); removeErr != nil {
|
||||
l.Errorw("remove owner from family failed", logger.Field("owner_id", familyOwnerUserID), logger.Field("error", removeErr.Error()))
|
||||
}
|
||||
// 将 owner 加入 affectedUserIDs 以便清缓存
|
||||
affectedUserIDs = append(affectedUserIDs, familyOwnerUserID)
|
||||
l.Infow("cleaned up family owner (email user) on device account deletion",
|
||||
logger.Field("device_user_id", currentUser.Id),
|
||||
logger.Field("owner_user_id", familyOwnerUserID),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@ -96,9 +145,12 @@ func (l *DeleteAccountLogic) DeleteAccountAll() (resp *types.DeleteAccountRespon
|
||||
}
|
||||
|
||||
// 主动清 auth method 相关缓存(含 email/mobile 等 key),避免缓存未命中时无法生成正确 key
|
||||
if len(authMethods) > 0 {
|
||||
allAuthMethods := make([]*user.AuthMethods, 0)
|
||||
allAuthMethods = append(allAuthMethods, authMethods...)
|
||||
allAuthMethods = append(allAuthMethods, ownerAuthMethods...)
|
||||
if len(allAuthMethods) > 0 {
|
||||
var authCacheKeys []string
|
||||
for _, am := range authMethods {
|
||||
for _, am := range allAuthMethods {
|
||||
if am.AuthType == "email" && am.AuthIdentifier != "" {
|
||||
authCacheKeys = append(authCacheKeys, fmt.Sprintf("cache:user:email:%s", am.AuthIdentifier))
|
||||
}
|
||||
@ -334,3 +386,16 @@ func (l *DeleteAccountLogic) clearAllSessions(userId int64) {
|
||||
logger.Field("count", len(sessionSet)),
|
||||
)
|
||||
}
|
||||
|
||||
// findActiveFamilyRelation 查找用户的活跃家庭成员记录
|
||||
func (l *DeleteAccountLogic) findActiveFamilyRelation(userID int64) (*user.UserFamilyMember, error) {
|
||||
var relation user.UserFamilyMember
|
||||
err := l.svcCtx.DB.WithContext(l.ctx).
|
||||
Model(&user.UserFamilyMember{}).
|
||||
Where("user_id = ? AND status = ?", userID, user.FamilyMemberActive).
|
||||
First(&relation).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &relation, nil
|
||||
}
|
||||
|
||||
109
scripts/fix_orphan_devices_family.sql
Normal file
109
scripts/fix_orphan_devices_family.sql
Normal file
@ -0,0 +1,109 @@
|
||||
-- ============================================================
|
||||
-- 修复脚本:为有多设备但无家庭组的用户补建家庭组
|
||||
-- 影响用户数:30
|
||||
-- 每个用户:2 个设备,0 个家庭组
|
||||
-- 执行前请先备份!
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- Step 0: 确认受影响数据(只读,不做任何修改)
|
||||
-- ============================================================
|
||||
SELECT
|
||||
d.user_id,
|
||||
COUNT(*) as device_count,
|
||||
GROUP_CONCAT(d.id ORDER BY d.id) as device_ids
|
||||
FROM user_device d
|
||||
LEFT JOIN user_family_member fm ON fm.user_id = d.user_id AND fm.status = 1
|
||||
WHERE d.enabled = 1 AND fm.id IS NULL
|
||||
GROUP BY d.user_id
|
||||
HAVING device_count > 1
|
||||
ORDER BY d.user_id;
|
||||
|
||||
-- 预期结果:30 行
|
||||
|
||||
-- ============================================================
|
||||
-- Step 1: 为每个用户创建 user_family(家庭组)
|
||||
-- owner_user_id = user_id, max_members = 2, status = 1(active)
|
||||
-- ============================================================
|
||||
INSERT INTO user_family (owner_user_id, max_members, status, created_at, updated_at)
|
||||
SELECT
|
||||
d.user_id,
|
||||
2, -- max_members = 2(当前都是 2 设备)
|
||||
1, -- status = active
|
||||
MIN(d.created_at), -- 用最早设备的创建时间
|
||||
NOW()
|
||||
FROM user_device d
|
||||
LEFT JOIN user_family_member fm ON fm.user_id = d.user_id AND fm.status = 1
|
||||
LEFT JOIN user_family f ON f.owner_user_id = d.user_id AND f.deleted_at IS NULL
|
||||
WHERE d.enabled = 1
|
||||
AND fm.id IS NULL -- 没有 active 家庭成员记录
|
||||
AND f.id IS NULL -- 没有已存在的家庭
|
||||
GROUP BY d.user_id
|
||||
HAVING COUNT(*) > 1;
|
||||
|
||||
-- 预期影响:30 行
|
||||
|
||||
-- ============================================================
|
||||
-- Step 2: 为每个用户创建 user_family_member(owner 身份)
|
||||
-- role = 1(owner), status = 1(active), join_source = 'data_fix'
|
||||
-- ============================================================
|
||||
INSERT INTO user_family_member (family_id, user_id, role, status, join_source, joined_at, created_at, updated_at)
|
||||
SELECT
|
||||
f.id, -- 刚创建的 family_id
|
||||
f.owner_user_id, -- user_id
|
||||
1, -- role = owner
|
||||
1, -- status = active
|
||||
'data_fix', -- 标记来源,方便追溯
|
||||
f.created_at, -- joined_at = family 创建时间
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM user_family f
|
||||
LEFT JOIN user_family_member fm ON fm.user_id = f.owner_user_id AND fm.status = 1
|
||||
WHERE fm.id IS NULL -- 还没有 active 家庭成员记录
|
||||
AND f.deleted_at IS NULL
|
||||
AND f.owner_user_id IN (
|
||||
-- 只处理我们目标用户
|
||||
SELECT d.user_id
|
||||
FROM user_device d
|
||||
WHERE d.enabled = 1
|
||||
GROUP BY d.user_id
|
||||
HAVING COUNT(*) > 1
|
||||
);
|
||||
|
||||
-- 预期影响:30 行
|
||||
|
||||
-- ============================================================
|
||||
-- Step 3: 验证修复结果
|
||||
-- ============================================================
|
||||
|
||||
-- 3a. 确认所有多设备用户都有了家庭组
|
||||
SELECT
|
||||
d.user_id,
|
||||
COUNT(DISTINCT d.id) as device_count,
|
||||
f.id as family_id,
|
||||
f.max_members,
|
||||
fm.role,
|
||||
fm.status as member_status,
|
||||
fm.join_source
|
||||
FROM user_device d
|
||||
JOIN user_family f ON f.owner_user_id = d.user_id AND f.deleted_at IS NULL
|
||||
JOIN user_family_member fm ON fm.user_id = d.user_id AND fm.status = 1
|
||||
WHERE d.enabled = 1
|
||||
AND fm.join_source = 'data_fix'
|
||||
GROUP BY d.user_id, f.id, f.max_members, fm.role, fm.status, fm.join_source
|
||||
ORDER BY d.user_id;
|
||||
|
||||
-- 预期结果:30 行,每行 device_count=2, role=1, member_status=1
|
||||
|
||||
-- 3b. 确认没有遗漏(多设备无家庭组的用户应该为 0)
|
||||
SELECT COUNT(*) as remaining_orphans
|
||||
FROM (
|
||||
SELECT d.user_id
|
||||
FROM user_device d
|
||||
LEFT JOIN user_family_member fm ON fm.user_id = d.user_id AND fm.status = 1
|
||||
WHERE d.enabled = 1 AND fm.id IS NULL
|
||||
GROUP BY d.user_id
|
||||
HAVING COUNT(*) > 1
|
||||
) orphans;
|
||||
|
||||
-- 预期结果:0
|
||||
283
scripts/fix_split_multi_device_users.sql
Normal file
283
scripts/fix_split_multi_device_users.sql
Normal file
@ -0,0 +1,283 @@
|
||||
-- ============================================================
|
||||
-- 修复脚本:将同一 user_id 下的多设备拆分为独立用户 + 家庭组
|
||||
--
|
||||
-- 问题:代码模型要求 每个设备 = 独立用户,多设备通过家庭组关联
|
||||
-- 但旧数据中同一 user_id 下挂了多个 user_device
|
||||
--
|
||||
-- 修复策略:
|
||||
-- 1. 每个用户保留第一个设备(最早创建的),作为 family owner
|
||||
-- 2. 其余设备各创建一个新 user,作为 family member
|
||||
-- 3. 建立家庭组关系
|
||||
--
|
||||
-- ⚠️ 执行前请先备份!
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- Step 0: 诊断 - 查看受影响的用户和设备
|
||||
-- ============================================================
|
||||
SELECT
|
||||
d.user_id,
|
||||
COUNT(*) as device_count,
|
||||
GROUP_CONCAT(d.id ORDER BY d.created_at ASC) as device_ids,
|
||||
GROUP_CONCAT(d.identifier ORDER BY d.created_at ASC SEPARATOR ' | ') as identifiers
|
||||
FROM user_device d
|
||||
WHERE d.enabled = 1
|
||||
GROUP BY d.user_id
|
||||
HAVING device_count > 1
|
||||
ORDER BY d.user_id;
|
||||
|
||||
-- ============================================================
|
||||
-- Step 1: 创建临时表,标记需要拆分的设备
|
||||
-- 每个用户保留最早的设备,其余标记为需要拆分
|
||||
-- ============================================================
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_devices_to_split;
|
||||
|
||||
CREATE TEMPORARY TABLE tmp_devices_to_split AS
|
||||
SELECT
|
||||
d.id as device_id,
|
||||
d.user_id as original_user_id,
|
||||
d.identifier,
|
||||
d.ip,
|
||||
d.user_agent,
|
||||
d.created_at as device_created_at,
|
||||
ROW_NUMBER() OVER (PARTITION BY d.user_id ORDER BY d.created_at ASC) as rn
|
||||
FROM user_device d
|
||||
WHERE d.enabled = 1
|
||||
AND d.user_id IN (
|
||||
SELECT user_id
|
||||
FROM user_device
|
||||
WHERE enabled = 1
|
||||
GROUP BY user_id
|
||||
HAVING COUNT(*) > 1
|
||||
);
|
||||
|
||||
-- 确认:rn=1 的保留在原用户,rn>1 的需要创建新用户
|
||||
SELECT * FROM tmp_devices_to_split ORDER BY original_user_id, rn;
|
||||
|
||||
-- ============================================================
|
||||
-- Step 2: 为 rn>1 的设备创建新用户
|
||||
-- 复制原用户的基本配置,生成新的 refer_code
|
||||
-- ============================================================
|
||||
|
||||
-- 先看需要创建多少个新用户
|
||||
SELECT COUNT(*) as new_users_needed FROM tmp_devices_to_split WHERE rn > 1;
|
||||
|
||||
-- 创建新用户(从原用户复制基本信息)
|
||||
INSERT INTO `user` (
|
||||
password, algo, salt, avatar, balance,
|
||||
refer_code, referer_id, commission,
|
||||
referral_percentage, only_first_purchase, gift_amount,
|
||||
enable, is_admin,
|
||||
enable_balance_notify, enable_login_notify,
|
||||
enable_subscribe_notify, enable_trade_notify,
|
||||
rules, member_status, remark,
|
||||
created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
u.password, u.algo, u.salt, '', 0,
|
||||
'', -- refer_code 后面更新
|
||||
u.referer_id, 0,
|
||||
u.referral_percentage, u.only_first_purchase, 0,
|
||||
u.enable, 0, -- is_admin = false
|
||||
0, 0, 0, 0, -- 通知全关
|
||||
'', '', CONCAT('split_from_user_', u.id),
|
||||
NOW(), NOW()
|
||||
FROM tmp_devices_to_split t
|
||||
JOIN `user` u ON u.id = t.original_user_id
|
||||
WHERE t.rn > 1;
|
||||
|
||||
-- ============================================================
|
||||
-- Step 3: 映射新用户 ID 到设备
|
||||
-- 因为 MySQL 不支持 INSERT ... RETURNING,需要通过 remark 字段找到新创建的用户
|
||||
-- ============================================================
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_new_user_mapping;
|
||||
|
||||
CREATE TEMPORARY TABLE tmp_new_user_mapping AS
|
||||
SELECT
|
||||
u.id as new_user_id,
|
||||
CAST(SUBSTRING(u.remark, LENGTH('split_from_user_') + 1) AS UNSIGNED) as original_user_id,
|
||||
u.created_at
|
||||
FROM `user` u
|
||||
WHERE u.remark LIKE 'split_from_user_%'
|
||||
AND u.deleted_at IS NULL
|
||||
ORDER BY u.id ASC;
|
||||
|
||||
-- 验证映射关系
|
||||
SELECT * FROM tmp_new_user_mapping;
|
||||
|
||||
-- 将新用户与待拆分设备匹配(按原用户分组内的顺序)
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_device_user_mapping;
|
||||
|
||||
CREATE TEMPORARY TABLE tmp_device_user_mapping AS
|
||||
SELECT
|
||||
t.device_id,
|
||||
t.original_user_id,
|
||||
t.identifier,
|
||||
m.new_user_id
|
||||
FROM (
|
||||
SELECT *, ROW_NUMBER() OVER (PARTITION BY original_user_id ORDER BY device_id ASC) as split_seq
|
||||
FROM tmp_devices_to_split
|
||||
WHERE rn > 1
|
||||
) t
|
||||
JOIN (
|
||||
SELECT *, ROW_NUMBER() OVER (PARTITION BY original_user_id ORDER BY new_user_id ASC) as split_seq
|
||||
FROM tmp_new_user_mapping
|
||||
) m ON t.original_user_id = m.original_user_id AND t.split_seq = m.split_seq;
|
||||
|
||||
-- 确认映射
|
||||
SELECT * FROM tmp_device_user_mapping;
|
||||
|
||||
-- ============================================================
|
||||
-- Step 4: 更新设备的 user_id 指向新用户
|
||||
-- ============================================================
|
||||
UPDATE user_device d
|
||||
JOIN tmp_device_user_mapping m ON d.id = m.device_id
|
||||
SET d.user_id = m.new_user_id;
|
||||
|
||||
-- ============================================================
|
||||
-- Step 5: 为新用户创建 device auth_method
|
||||
-- ============================================================
|
||||
INSERT INTO user_auth_methods (user_id, auth_type, auth_identifier, verified, created_at, updated_at)
|
||||
SELECT
|
||||
m.new_user_id,
|
||||
'device',
|
||||
m.identifier,
|
||||
1,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM tmp_device_user_mapping m;
|
||||
|
||||
-- ============================================================
|
||||
-- Step 6: 更新新用户的 refer_code
|
||||
-- 用 CONCAT('u', CONV(new_user_id + UNIX_TIMESTAMP(), 10, 36)) 生成简单唯一码
|
||||
-- (Go 代码用 Base62,SQL 里用 Base36 近似,长度足够唯一)
|
||||
-- ============================================================
|
||||
UPDATE `user` u
|
||||
JOIN tmp_new_user_mapping m ON u.id = m.new_user_id
|
||||
SET u.refer_code = CONCAT('u', LOWER(CONV(u.id + UNIX_TIMESTAMP(NOW()), 10, 36)));
|
||||
|
||||
-- 清理 remark 标记
|
||||
UPDATE `user` u
|
||||
JOIN tmp_new_user_mapping m ON u.id = m.new_user_id
|
||||
SET u.remark = '';
|
||||
|
||||
-- ============================================================
|
||||
-- Step 7: 创建/确保家庭组(原用户为 owner)
|
||||
-- 先处理已有家庭组的情况,再处理没有的
|
||||
-- ============================================================
|
||||
|
||||
-- 7a. 为没有 active 家庭组的原用户创建家庭组
|
||||
INSERT INTO user_family (owner_user_id, max_members, status, created_at, updated_at)
|
||||
SELECT DISTINCT
|
||||
t.original_user_id,
|
||||
2,
|
||||
1, -- active
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM tmp_device_user_mapping t
|
||||
LEFT JOIN user_family f ON f.owner_user_id = t.original_user_id
|
||||
AND f.status = 1 AND f.deleted_at IS NULL
|
||||
WHERE f.id IS NULL;
|
||||
|
||||
-- 7b. 确保原用户在家庭组中有 owner 成员记录
|
||||
INSERT INTO user_family_member (family_id, user_id, role, status, join_source, joined_at, created_at, updated_at)
|
||||
SELECT
|
||||
f.id,
|
||||
f.owner_user_id,
|
||||
1, -- role = owner
|
||||
1, -- status = active
|
||||
'data_fix_split',
|
||||
NOW(),
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM user_family f
|
||||
WHERE f.owner_user_id IN (SELECT DISTINCT original_user_id FROM tmp_device_user_mapping)
|
||||
AND f.status = 1
|
||||
AND f.deleted_at IS NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM user_family_member fm
|
||||
WHERE fm.family_id = f.id
|
||||
AND fm.user_id = f.owner_user_id
|
||||
AND fm.status = 1
|
||||
AND fm.deleted_at IS NULL
|
||||
);
|
||||
|
||||
-- 7c. 将新用户加入家庭组作为 member
|
||||
INSERT INTO user_family_member (family_id, user_id, role, status, join_source, joined_at, created_at, updated_at)
|
||||
SELECT
|
||||
f.id,
|
||||
m.new_user_id,
|
||||
2, -- role = member
|
||||
1, -- status = active
|
||||
'data_fix_split',
|
||||
NOW(),
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM tmp_device_user_mapping m
|
||||
JOIN user_family f ON f.owner_user_id = m.original_user_id
|
||||
AND f.status = 1 AND f.deleted_at IS NULL;
|
||||
|
||||
-- 7d. 更新 max_members(如果原用户有 >2 个设备)
|
||||
UPDATE user_family f
|
||||
SET f.max_members = (
|
||||
SELECT COUNT(*)
|
||||
FROM user_family_member fm
|
||||
WHERE fm.family_id = f.id AND fm.status = 1 AND fm.deleted_at IS NULL
|
||||
)
|
||||
WHERE f.owner_user_id IN (SELECT DISTINCT original_user_id FROM tmp_device_user_mapping)
|
||||
AND f.status = 1 AND f.deleted_at IS NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- Step 8: 转移订阅(原用户的订阅保留,新用户不需要订阅)
|
||||
-- 家庭成员共享 owner 的订阅,所以新用户不需要自己的订阅
|
||||
-- 如果原用户已有订阅,新用户通过家庭组共享
|
||||
-- ============================================================
|
||||
-- (无需操作,代码中 familyBindingHelper.clearMemberSubscribes 会在 joinFamily 时清理)
|
||||
-- 新用户是刚创建的,没有任何订阅记录,无需清理
|
||||
|
||||
-- ============================================================
|
||||
-- Step 9: 验证修复结果
|
||||
-- ============================================================
|
||||
|
||||
-- 9a. 确认没有用户拥有多个设备了
|
||||
SELECT
|
||||
d.user_id,
|
||||
COUNT(*) as device_count,
|
||||
GROUP_CONCAT(d.id ORDER BY d.id) as device_ids
|
||||
FROM user_device d
|
||||
WHERE d.enabled = 1
|
||||
GROUP BY d.user_id
|
||||
HAVING device_count > 1;
|
||||
-- 预期结果:0 行
|
||||
|
||||
-- 9b. 确认家庭组关系正确
|
||||
SELECT
|
||||
f.id as family_id,
|
||||
f.owner_user_id,
|
||||
f.max_members,
|
||||
f.status as family_status,
|
||||
GROUP_CONCAT(CONCAT(fm.user_id, '(role=', fm.role, ')') ORDER BY fm.role) as members
|
||||
FROM user_family f
|
||||
JOIN user_family_member fm ON fm.family_id = f.id AND fm.status = 1 AND fm.deleted_at IS NULL
|
||||
WHERE f.owner_user_id IN (SELECT DISTINCT original_user_id FROM tmp_device_user_mapping)
|
||||
AND f.status = 1 AND f.deleted_at IS NULL
|
||||
GROUP BY f.id, f.owner_user_id, f.max_members, f.status;
|
||||
|
||||
-- 9c. 确认新用户都有 device auth_method
|
||||
SELECT
|
||||
m.new_user_id,
|
||||
m.original_user_id,
|
||||
m.identifier,
|
||||
am.id as auth_method_id,
|
||||
d.id as device_id,
|
||||
d.user_id as device_user_id
|
||||
FROM tmp_device_user_mapping m
|
||||
JOIN user_auth_methods am ON am.user_id = m.new_user_id AND am.auth_type = 'device'
|
||||
JOIN user_device d ON d.id = m.device_id;
|
||||
|
||||
-- ============================================================
|
||||
-- 清理临时表
|
||||
-- ============================================================
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_devices_to_split;
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_new_user_mapping;
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_device_user_mapping;
|
||||
17
说明文档.md
Normal file
17
说明文档.md
Normal file
@ -0,0 +1,17 @@
|
||||
# 说明文档.md
|
||||
|
||||
## 项目规划
|
||||
本任务旨在帮助用户在云端 Docker 日志中查找 "database insert error" 错误,特别是以 "Database" 开头的报错信息。
|
||||
|
||||
## 实施方案
|
||||
1. **环境确认**:确认如何访问云端容器(SSH 或远程 Docker 上下文)。
|
||||
2. **日志检索**:使用 `docker logs` 结合 `grep` 进行过滤。
|
||||
3. **结果分析**:提取具体的报错信息,分析可能的原因。
|
||||
4. **反馈**:将查找到的错误信息整理反馈给用户。
|
||||
|
||||
## 进度记录
|
||||
| 时间节点 | 任务说明 | 进度 | 结果说明 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| 2026-03-11 | 初始化文档并提供指令 | [x] 已完成 | 已提供查找日志的命令并记录在文档中 |
|
||||
| 2026-03-11 | 提供今天所有 ERROR 报错指令 | [x] 已完成 | 已提供根据日期过滤 ERROR 的命令 |
|
||||
| 2026-03-12 | 分析并确认 Unknown column 错误 | [x] 已完成 | 确认为 `user_device` 缺少 `short_code` 字段,已提供 SQL |
|
||||
Loading…
x
Reference in New Issue
Block a user