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;