fix(shangwutong): 完善访客资料同步

This commit is contained in:
Rogee
2026-08-06 15:14:32 +08:00
parent 3bceb2d0b2
commit 5c47d1d5fe
24 changed files with 1800 additions and 952 deletions
@@ -0,0 +1,27 @@
UPDATE custom_attribute_definitions
SET
attribute_display_name = 'Custom Priority',
description = 'Custom ticket priority',
attribute_values = '["Low", "Medium", "High"]'::jsonb,
updated_at = CURRENT_TIMESTAMP
WHERE attribute_name = 'custom_priority'
AND attribute_model = 'conversation_attribute'
AND attribute_display_name = '自定义优先级'
AND description = '自定义工单优先级'
AND attribute_values = '["低", "中", "高"]'::jsonb;
UPDATE conversations
SET
custom_attributes = jsonb_set(
custom_attributes,
'{custom_priority}',
to_jsonb(
CASE custom_attributes->>'custom_priority'
WHEN '' THEN 'Low'
WHEN '' THEN 'Medium'
WHEN '' THEN 'High'
END
)
),
updated_at = CURRENT_TIMESTAMP
WHERE custom_attributes->>'custom_priority' IN ('', '', '');
@@ -0,0 +1,27 @@
UPDATE custom_attribute_definitions
SET
attribute_display_name = '自定义优先级',
description = '自定义工单优先级',
attribute_values = '["低", "中", "高"]'::jsonb,
updated_at = CURRENT_TIMESTAMP
WHERE attribute_name = 'custom_priority'
AND attribute_model = 'conversation_attribute'
AND attribute_display_name = 'Custom Priority'
AND description = 'Custom ticket priority'
AND attribute_values = '["Low", "Medium", "High"]'::jsonb;
UPDATE conversations
SET
custom_attributes = jsonb_set(
custom_attributes,
'{custom_priority}',
to_jsonb(
CASE custom_attributes->>'custom_priority'
WHEN 'Low' THEN ''
WHEN 'Medium' THEN ''
WHEN 'High' THEN ''
END
)
),
updated_at = CURRENT_TIMESTAMP
WHERE custom_attributes->>'custom_priority' IN ('Low', 'Medium', 'High');
@@ -0,0 +1,12 @@
DELETE FROM custom_attribute_definitions
WHERE attribute_model = 'contact_attribute'
AND attribute_name IN (
'swt_ip', 'swt_ip_location', 'swt_isp', 'swt_resolution',
'swt_language', 'swt_timezone', 'swt_os', 'swt_browser',
'swt_browser_version', 'swt_user_agent', 'swt_device',
'swt_query_title', 'swt_query_word', 'swt_traffic_source',
'swt_profile_channel', 'swt_site_id'
);
-- Contact values are intentionally retained in custom_attributes. Removing
-- them would discard visitor diagnostics that may have arrived after upgrade.
@@ -0,0 +1,83 @@
INSERT INTO custom_attribute_definitions (
account_id,
attribute_name,
attribute_display_name,
attribute_type,
attribute_model,
description,
created_at,
updated_at
)
SELECT
accounts.id,
definitions.attribute_name,
definitions.attribute_display_name,
'text',
'contact_attribute',
definitions.description,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
FROM accounts
CROSS JOIN (VALUES
('swt_ip', '访客 IP', '商务通访客 IP 地址'),
('swt_ip_location', 'IP 归属地', '商务通解析的访客 IP 归属地'),
('swt_isp', '网络运营商', '商务通访客网络运营商'),
('swt_resolution', '屏幕分辨率', '商务通访客设备屏幕分辨率'),
('swt_language', '浏览器语言', '商务通访客浏览器语言'),
('swt_timezone', '访客时区', '商务通访客设备时区'),
('swt_os', '操作系统', '商务通访客设备操作系统'),
('swt_browser', '浏览器', '商务通访客浏览器名称'),
('swt_browser_version', '浏览器版本', '商务通访客浏览器版本'),
('swt_user_agent', 'User Agent', '商务通访客浏览器 User Agent'),
('swt_device', '访客设备', '商务通访客设备信息'),
('swt_query_title', '搜索主题', '商务通访客进入页面时的搜索主题'),
('swt_query_word', '搜索关键词', '商务通访客进入页面时的搜索关键词'),
('swt_traffic_source', '流量来源', '商务通访客流量来源'),
('swt_profile_channel', '来源渠道', '商务通访客来源渠道'),
('swt_site_id', '商务通站点 ID', '商务通站点标识')
) AS definitions(attribute_name, attribute_display_name, description)
WHERE NOT EXISTS (
SELECT 1
FROM custom_attribute_definitions existing
WHERE existing.account_id = accounts.id
AND existing.attribute_name = definitions.attribute_name
AND existing.attribute_model = 'contact_attribute'
AND existing.deleted_at IS NULL
);
UPDATE contacts
SET
custom_attributes = COALESCE(custom_attributes, '{}'::jsonb)
|| jsonb_strip_nulls(jsonb_build_object(
'swt_ip', additional_attributes->'swt_ip',
'swt_ip_location', additional_attributes->'swt_ip_location',
'swt_isp', additional_attributes->'swt_isp',
'swt_resolution', additional_attributes->'swt_resolution',
'swt_language', additional_attributes->'swt_language',
'swt_timezone', additional_attributes->'swt_timezone',
'swt_os', additional_attributes->'swt_os',
'swt_browser', additional_attributes->'swt_browser',
'swt_browser_version', additional_attributes->'swt_browser_version',
'swt_user_agent', additional_attributes->'swt_user_agent',
'swt_device', additional_attributes->'swt_device',
'swt_query_title', additional_attributes->'swt_query_title',
'swt_query_word', additional_attributes->'swt_query_word',
'swt_traffic_source', additional_attributes->'swt_traffic_source',
'swt_profile_channel', additional_attributes->'swt_profile_channel',
'swt_site_id', additional_attributes->'swt_site_id'
)),
updated_at = CURRENT_TIMESTAMP
WHERE EXISTS (
SELECT 1 FROM contact_inboxes
WHERE contact_inboxes.contact_id = contacts.id
AND contact_inboxes.inbox_id IN (
SELECT id FROM inboxes WHERE channel_type = 'shangwutong'
)
)
AND COALESCE(additional_attributes, '{}'::jsonb) ?| ARRAY[
'swt_ip', 'swt_ip_location', 'swt_isp', 'swt_resolution',
'swt_language', 'swt_timezone', 'swt_os', 'swt_browser',
'swt_browser_version', 'swt_user_agent', 'swt_device',
'swt_query_title', 'swt_query_word', 'swt_traffic_source',
'swt_profile_channel', 'swt_site_id'
];
@@ -0,0 +1,3 @@
-- Message sender names are denormalized presentation metadata. The original
-- hard-coded values cannot be restored reliably after contacts are renamed.
SELECT 1;
@@ -0,0 +1,37 @@
WITH latest_names AS (
SELECT DISTINCT ON (conversation_id)
conversation_id,
NULLIF(BTRIM(additional_attributes->>'senderName'), '') AS visitor_name
FROM messages
WHERE message_type = 'incoming'
AND NULLIF(BTRIM(additional_attributes->>'senderName'), '') IS NOT NULL
AND BTRIM(additional_attributes->>'senderName') <> '商务通访客'
ORDER BY conversation_id, created_at DESC, id DESC
)
UPDATE contacts
SET
name = latest_names.visitor_name,
updated_at = CURRENT_TIMESTAMP
FROM conversations
JOIN latest_names ON latest_names.conversation_id = conversations.id
JOIN inboxes ON inboxes.id = conversations.inbox_id
WHERE contacts.id = conversations.contact_id
AND inboxes.channel_type = 'shangwutong'
AND contacts.name <> latest_names.visitor_name;
-- Existing imported messages used a hard-coded senderName. Contact identity is
-- the authoritative sender for incoming messages after this repair.
UPDATE messages
SET additional_attributes = jsonb_set(
COALESCE(messages.additional_attributes, '{}'::jsonb),
'{senderName}',
to_jsonb(contacts.name)
)
FROM conversations
JOIN contacts ON contacts.id = conversations.contact_id
JOIN inboxes ON inboxes.id = conversations.inbox_id
WHERE messages.conversation_id = conversations.id
AND messages.message_type = 'incoming'
AND inboxes.channel_type = 'shangwutong'
AND BTRIM(contacts.name) <> ''
AND COALESCE(messages.additional_attributes->>'senderName', '') <> contacts.name;
@@ -0,0 +1,3 @@
-- Percent-decoded visitor metadata cannot be losslessly restored to the
-- original hex casing and plus-sign representation.
SELECT 1;
@@ -0,0 +1,74 @@
CREATE OR REPLACE FUNCTION gochat_percent_decode(value TEXT)
RETURNS TEXT
LANGUAGE plpgsql
IMMUTABLE
STRICT
AS $$
DECLARE
position INTEGER := 1;
encoded_bytes TEXT := '';
token TEXT;
BEGIN
WHILE position <= char_length(value) LOOP
token := substr(value, position, 1);
IF token = '%' AND substr(value, position + 1, 2) ~ '^[0-9A-Fa-f]{2}$' THEN
encoded_bytes := encoded_bytes || substr(value, position + 1, 2);
position := position + 3;
ELSIF token = '+' THEN
encoded_bytes := encoded_bytes || '20';
position := position + 1;
ELSE
encoded_bytes := encoded_bytes || encode(convert_to(token, 'UTF8'), 'hex');
position := position + 1;
END IF;
END LOOP;
RETURN convert_from(decode(encoded_bytes, 'hex'), 'UTF8');
EXCEPTION WHEN OTHERS THEN
RETURN value;
END;
$$;
UPDATE contacts
SET
custom_attributes = COALESCE(custom_attributes, '{}'::jsonb)
|| jsonb_strip_nulls(jsonb_build_object(
'swt_ip_location', gochat_percent_decode(custom_attributes->>'swt_ip_location'),
'swt_color_depth', gochat_percent_decode(custom_attributes->>'swt_color_depth'),
'swt_resolution', gochat_percent_decode(custom_attributes->>'swt_resolution'),
'swt_os', gochat_percent_decode(custom_attributes->>'swt_os'),
'city', CASE
WHEN COALESCE(custom_attributes->>'city', '') = ''
THEN gochat_percent_decode(custom_attributes->>'swt_ip_location')
END
)),
additional_attributes = COALESCE(additional_attributes, '{}'::jsonb)
|| jsonb_strip_nulls(jsonb_build_object(
'swt_ip_location', gochat_percent_decode(additional_attributes->>'swt_ip_location'),
'swt_color_depth', gochat_percent_decode(additional_attributes->>'swt_color_depth'),
'swt_resolution', gochat_percent_decode(additional_attributes->>'swt_resolution'),
'swt_os', gochat_percent_decode(additional_attributes->>'swt_os'),
'city', CASE
WHEN COALESCE(additional_attributes->>'city', '') = ''
THEN gochat_percent_decode(additional_attributes->>'swt_ip_location')
END
)),
updated_at = CURRENT_TIMESTAMP
WHERE EXISTS (
SELECT 1
FROM contact_inboxes
JOIN inboxes ON inboxes.id = contact_inboxes.inbox_id
WHERE contact_inboxes.contact_id = contacts.id
AND inboxes.channel_type = 'shangwutong'
)
AND (
COALESCE(custom_attributes->>'swt_ip_location', '') ~ '%[0-9A-Fa-f]{2}'
OR COALESCE(custom_attributes->>'swt_color_depth', '') ~ '%[0-9A-Fa-f]{2}'
OR COALESCE(custom_attributes->>'swt_resolution', '') ~ '%[0-9A-Fa-f]{2}'
OR COALESCE(custom_attributes->>'swt_os', '') ~ '%[0-9A-Fa-f]{2}'
OR COALESCE(additional_attributes->>'swt_ip_location', '') ~ '%[0-9A-Fa-f]{2}'
OR COALESCE(additional_attributes->>'swt_color_depth', '') ~ '%[0-9A-Fa-f]{2}'
OR COALESCE(additional_attributes->>'swt_resolution', '') ~ '%[0-9A-Fa-f]{2}'
OR COALESCE(additional_attributes->>'swt_os', '') ~ '%[0-9A-Fa-f]{2}'
);
DROP FUNCTION gochat_percent_decode(TEXT);
@@ -0,0 +1,3 @@
-- Repaired environment fields cannot be reconstructed to the previous
-- misaligned values without reintroducing corrupt contact metadata.
SELECT 1;
@@ -0,0 +1,91 @@
CREATE OR REPLACE FUNCTION gochat_percent_decode(value TEXT)
RETURNS TEXT
LANGUAGE plpgsql
IMMUTABLE
STRICT
AS $$
DECLARE
position INTEGER := 1;
encoded_bytes TEXT := '';
token TEXT;
BEGIN
WHILE position <= char_length(value) LOOP
token := substr(value, position, 1);
IF token = '%' AND substr(value, position + 1, 2) ~ '^[0-9A-Fa-f]{2}$' THEN
encoded_bytes := encoded_bytes || substr(value, position + 1, 2);
position := position + 3;
ELSIF token = '+' THEN
encoded_bytes := encoded_bytes || '20';
position := position + 1;
ELSE
encoded_bytes := encoded_bytes || encode(convert_to(token, 'UTF8'), 'hex');
position := position + 1;
END IF;
END LOOP;
RETURN convert_from(decode(encoded_bytes, 'hex'), 'UTF8');
EXCEPTION WHEN OTHERS THEN
RETURN value;
END;
$$;
WITH profiles AS (
SELECT DISTINCT ON (contacts.id)
contacts.id,
split_part(
COALESCE(
NULLIF(contacts.custom_attributes->>'swt_xst_profile', ''),
contacts.additional_attributes->>'swt_xst_profile'
),
'|||||',
1
) AS profile
FROM contacts
JOIN contact_inboxes ON contact_inboxes.contact_id = contacts.id
JOIN inboxes ON inboxes.id = contact_inboxes.inbox_id
WHERE inboxes.channel_type = 'shangwutong'
AND COALESCE(
NULLIF(contacts.custom_attributes->>'swt_xst_profile', ''),
contacts.additional_attributes->>'swt_xst_profile'
) IS NOT NULL
), decoded AS (
SELECT
id,
NULLIF(gochat_percent_decode(split_part(profile, chr(26), 5)), '') AS location,
NULLIF(gochat_percent_decode(split_part(profile, chr(26), 11)), '') AS resolution,
NULLIF(gochat_percent_decode(split_part(profile, chr(26), 12)), '') AS user_agent,
NULLIF(gochat_percent_decode(split_part(profile, chr(26), 13)), '') AS ip_address
FROM profiles
)
UPDATE contacts
SET
custom_attributes = COALESCE(contacts.custom_attributes, '{}'::jsonb)
|| jsonb_strip_nulls(jsonb_build_object(
'swt_ip_location', decoded.location,
'city', decoded.location,
'swt_resolution', decoded.resolution,
'swt_user_agent', decoded.user_agent,
'swt_device', decoded.user_agent,
'swt_os', decoded.user_agent,
'swt_ip', decoded.ip_address
)),
additional_attributes = COALESCE(contacts.additional_attributes, '{}'::jsonb)
|| jsonb_strip_nulls(jsonb_build_object(
'swt_ip_location', decoded.location,
'city', decoded.location,
'swt_resolution', decoded.resolution,
'swt_user_agent', decoded.user_agent,
'swt_device', decoded.user_agent,
'swt_os', decoded.user_agent,
'swt_ip', decoded.ip_address
)),
updated_at = CURRENT_TIMESTAMP
FROM decoded
WHERE contacts.id = decoded.id
AND (
decoded.location IS NOT NULL
OR decoded.resolution IS NOT NULL
OR decoded.user_agent IS NOT NULL
OR decoded.ip_address IS NOT NULL
);
DROP FUNCTION gochat_percent_decode(TEXT);
@@ -0,0 +1,6 @@
DELETE FROM custom_attribute_definitions
WHERE attribute_name = 'swt_environment_version'
AND attribute_model = 'contact_attribute';
-- The previous swt_isp and duplicated UA-derived values were semantically
-- incorrect and are intentionally not restored.
@@ -0,0 +1,116 @@
INSERT INTO custom_attribute_definitions (
account_id,
attribute_name,
attribute_display_name,
attribute_type,
attribute_model,
description,
created_at,
updated_at
)
SELECT
accounts.id,
'swt_environment_version',
'环境协议版本',
'text',
'contact_attribute',
'商务通 kind=7 环境信息格式版本;不是网络运营商',
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
FROM accounts
WHERE NOT EXISTS (
SELECT 1
FROM custom_attribute_definitions existing
WHERE existing.account_id = accounts.id
AND existing.attribute_name = 'swt_environment_version'
AND existing.attribute_model = 'contact_attribute'
AND existing.deleted_at IS NULL
);
DELETE FROM custom_attribute_definitions
WHERE attribute_name = 'swt_isp'
AND attribute_model = 'contact_attribute';
WITH swt_contacts AS (
SELECT DISTINCT contacts.id
FROM contacts
JOIN contact_inboxes ON contact_inboxes.contact_id = contacts.id
JOIN inboxes ON inboxes.id = contact_inboxes.inbox_id
WHERE inboxes.channel_type = 'shangwutong'
), source AS (
SELECT
contacts.id,
COALESCE(
NULLIF(contacts.custom_attributes->>'swt_user_agent', ''),
NULLIF(contacts.additional_attributes->>'swt_user_agent', ''),
NULLIF(contacts.custom_attributes->>'swt_os', ''),
NULLIF(contacts.additional_attributes->>'swt_os', '')
) AS user_agent,
COALESCE(
NULLIF(contacts.custom_attributes->>'swt_environment_version', ''),
NULLIF(contacts.custom_attributes->>'swt_isp', ''),
NULLIF(contacts.additional_attributes->>'swt_environment_version', ''),
NULLIF(contacts.additional_attributes->>'swt_isp', '')
) AS environment_version
FROM contacts
JOIN swt_contacts ON swt_contacts.id = contacts.id
), parsed AS (
SELECT
id,
user_agent,
environment_version,
CASE
WHEN user_agent ~ 'OpenHarmony[[:space:]]+[0-9.]+'
THEN 'OpenHarmony ' || substring(user_agent FROM 'OpenHarmony[[:space:]]+([0-9.]+)')
WHEN user_agent ~ 'Android[[:space:]]+[^;]+'
THEN 'Android ' || BTRIM(substring(user_agent FROM 'Android[[:space:]]+([^;]+)'))
WHEN user_agent ~ 'iPhone OS[[:space:]]+[0-9_]+'
THEN 'iOS ' || replace(substring(user_agent FROM 'iPhone OS[[:space:]]+([0-9_]+)'), '_', '.')
WHEN user_agent LIKE '%Windows NT 10.0%'
THEN 'Windows 10/11'
WHEN user_agent LIKE '%Windows%'
THEN 'Windows'
WHEN user_agent ~ 'Mac OS X[[:space:]]+[0-9_]+'
THEN 'macOS ' || replace(substring(user_agent FROM 'Mac OS X[[:space:]]+([0-9_]+)'), '_', '.')
WHEN user_agent LIKE '%Linux%'
THEN 'Linux'
END AS operating_system,
CASE
WHEN user_agent LIKE '%OpenHarmony%'
THEN BTRIM(split_part(user_agent, ';', 1))
WHEN user_agent ~ 'Android[[:space:]]+[^;]+;[[:space:]]*[^;]+ Build/'
THEN BTRIM(substring(user_agent FROM 'Android[[:space:]]+[^;]+;[[:space:]]*([^;]+) Build/'))
WHEN user_agent LIKE '%Android%'
THEN 'Android 设备'
WHEN user_agent LIKE '%iPhone OS%'
THEN 'iPhone'
WHEN user_agent LIKE '%Windows%'
THEN 'Windows PC'
WHEN user_agent LIKE '%Mac OS X%'
THEN 'Mac'
WHEN user_agent LIKE '%Linux%'
THEN 'Linux 设备'
END AS device
FROM source
)
UPDATE contacts
SET
custom_attributes = (
COALESCE(contacts.custom_attributes, '{}'::jsonb) - 'swt_isp' - 'swt_color_depth'
) || jsonb_strip_nulls(jsonb_build_object(
'swt_environment_version', parsed.environment_version,
'swt_user_agent', parsed.user_agent,
'swt_os', parsed.operating_system,
'swt_device', parsed.device
)),
additional_attributes = (
COALESCE(contacts.additional_attributes, '{}'::jsonb) - 'swt_isp' - 'swt_color_depth'
) || jsonb_strip_nulls(jsonb_build_object(
'swt_environment_version', parsed.environment_version,
'swt_user_agent', parsed.user_agent,
'swt_os', parsed.operating_system,
'swt_device', parsed.device
)),
updated_at = CURRENT_TIMESTAMP
FROM parsed
WHERE contacts.id = parsed.id;
@@ -0,0 +1,2 @@
-- The previous device values duplicated the full User Agent and were invalid.
SELECT 1;
@@ -0,0 +1,75 @@
WITH swt_contacts AS (
SELECT DISTINCT contacts.id
FROM contacts
JOIN contact_inboxes ON contact_inboxes.contact_id = contacts.id
JOIN inboxes ON inboxes.id = contact_inboxes.inbox_id
WHERE inboxes.channel_type = 'shangwutong'
), source AS (
SELECT
contacts.id,
COALESCE(
NULLIF(contacts.custom_attributes->>'swt_user_agent', ''),
NULLIF(contacts.additional_attributes->>'swt_user_agent', ''),
NULLIF(contacts.custom_attributes->>'swt_device', ''),
NULLIF(contacts.additional_attributes->>'swt_device', '')
) AS user_agent
FROM contacts
JOIN swt_contacts ON swt_contacts.id = contacts.id
), parsed AS (
SELECT
id,
user_agent,
CASE
WHEN user_agent ~ 'OpenHarmony[[:space:]]+[0-9.]+'
THEN 'OpenHarmony ' || substring(user_agent FROM 'OpenHarmony[[:space:]]+([0-9.]+)')
WHEN user_agent ~ 'Android[[:space:]]+[^;]+'
THEN 'Android ' || BTRIM(substring(user_agent FROM 'Android[[:space:]]+([^;]+)'))
WHEN user_agent ~ 'iPhone OS[[:space:]]+[0-9_]+'
THEN 'iOS ' || replace(substring(user_agent FROM 'iPhone OS[[:space:]]+([0-9_]+)'), '_', '.')
WHEN user_agent LIKE '%Windows NT 10.0%'
THEN 'Windows 10/11'
WHEN user_agent LIKE '%Windows%'
THEN 'Windows'
WHEN user_agent ~ 'Mac OS X[[:space:]]+[0-9_]+'
THEN 'macOS ' || replace(substring(user_agent FROM 'Mac OS X[[:space:]]+([0-9_]+)'), '_', '.')
WHEN user_agent LIKE '%Linux%'
THEN 'Linux'
END AS operating_system,
CASE
WHEN user_agent LIKE '%OpenHarmony%'
THEN BTRIM(split_part(user_agent, ';', 1))
WHEN user_agent ~ 'Android[[:space:]]+[^;]+;[[:space:]]*[^;]+ Build/'
THEN BTRIM(substring(user_agent FROM 'Android[[:space:]]+[^;]+;[[:space:]]*([^;]+) Build/'))
WHEN user_agent ~ 'Android[[:space:]]+[^;]+;[[:space:]]*[^;]+;[[:space:]]*[^;]+ Build/'
THEN BTRIM(substring(user_agent FROM 'Android[[:space:]]+[^;]+;[[:space:]]*[^;]+;[[:space:]]*([^;]+) Build/'))
WHEN user_agent LIKE '%Android%'
THEN 'Android 设备'
WHEN user_agent LIKE '%iPhone OS%'
THEN 'iPhone'
WHEN user_agent LIKE '%Windows%'
THEN 'Windows PC'
WHEN user_agent LIKE '%Mac OS X%'
THEN 'Mac'
WHEN user_agent LIKE '%Linux%'
THEN 'Linux 设备'
END AS device
FROM source
WHERE user_agent IS NOT NULL
)
UPDATE contacts
SET
custom_attributes = COALESCE(contacts.custom_attributes, '{}'::jsonb)
|| jsonb_strip_nulls(jsonb_build_object(
'swt_user_agent', parsed.user_agent,
'swt_device', parsed.device,
'swt_os', parsed.operating_system
)),
additional_attributes = COALESCE(contacts.additional_attributes, '{}'::jsonb)
|| jsonb_strip_nulls(jsonb_build_object(
'swt_user_agent', parsed.user_agent,
'swt_device', parsed.device,
'swt_os', parsed.operating_system
)),
updated_at = CURRENT_TIMESTAMP
FROM parsed
WHERE contacts.id = parsed.id;
@@ -0,0 +1,3 @@
-- Country metadata inferred from IP location cannot be safely distinguished from
-- user-entered values, so this data repair is intentionally irreversible.
SELECT 1;
@@ -0,0 +1,22 @@
UPDATE contacts
SET
country_code = 'CN',
location = CASE
WHEN COALESCE(location, '') = '' THEN custom_attributes->>'swt_ip_location'
ELSE location
END,
additional_attributes = COALESCE(additional_attributes, '{}'::jsonb)
|| jsonb_build_object(
'country_code', 'CN',
'country', 'China',
'city', custom_attributes->>'swt_ip_location'
),
updated_at = CURRENT_TIMESTAMP
WHERE EXISTS (
SELECT 1
FROM contact_inboxes
JOIN inboxes ON inboxes.id = contact_inboxes.inbox_id
WHERE contact_inboxes.contact_id = contacts.id
AND inboxes.channel_type = 'shangwutong'
)
AND COALESCE(custom_attributes->>'swt_ip_location', '') ~ '^(北京|天津|上海|重庆|河北|山西|辽宁|吉林|黑龙江|江苏|浙江|安徽|福建|江西|山东|河南|湖北|湖南|广东|海南|四川|贵州|云南|陕西|甘肃|青海|台湾|内蒙古|广西|西藏|宁夏|新疆|香港|澳门)';