remp / campaign-module
REMP Campaign Laravel 包
Requires
- php: ^8.1
- ext-intl: *
- ext-json: *
- cache/predis-adapter: ^1.0
- cache/redis-adapter: ^1.2
- doctrine/dbal: ^3.1
- fico7489/laravel-pivot: ^3.0
- fideloper/proxy: ^4.4
- fruitcake/laravel-cors: ^2.0
- geoip2/geoip2: ~2.0
- guzzlehttp/guzzle: ^7.0.1
- kouz/laravel-airbrake: ^0.7.0
- laracasts/flash: ^3.2
- laravel/framework: v8.76.2
- laravel/serializable-closure: ^1.2
- laravelcollective/html: ^6.2
- nicolaslopezj/searchable: ^1.13
- php-amqplib/php-amqplib: ^2.7
- piwik/device-detector: ^3.10
- predis/predis: ^1.1
- remp/laravel-helpers: *
- remp/laravel-sso: *
- remp/laravel-widgets: *
- remp/remp-commons: *
- sentry/sentry-laravel: ^2.5
- spatie/laravel-searchable: ^1.9
- symfony/psr-http-message-bridge: ^v2.1.2
- tightenco/ziggy: ^1.2
- ukfast/laravel-health-check: ^1.5
- yajra/laravel-datatables-oracle: ^9.18
Requires (Dev)
- barryvdh/laravel-debugbar: ^3.7
- barryvdh/laravel-ide-helper: ^2.10
- facade/ignition: ^2.5
- fakerphp/faker: ^1.9.1
- friendsofphp/php-cs-fixer: ^3.0
- josiasmontag/laravel-redis-mock: ~1.2
- laravel/tinker: ^2.0
- mockery/mockery: ^1.4.2
- nunomaduro/collision: ^5.0
- phpunit/phpunit: ^9.3.3
- squizlabs/php_codesniffer: ^3.6
Suggests
- ext-redis: The extension required for better performance.
This package is auto-updated.
Last update: 2024-09-26 10:55:45 UTC
README
活动管理器允许您在不了解HTML/CSS/JS的情况下创建横幅活动,对横幅进行A/B测试,并结合REMP Beam(用于提供统计信息)显示和评估活动的性能。
CMS/CRM集成
JavaScript代码片段
将以下片段包含到页面中,以处理活动并显示横幅。根据需要更新rempConfig
对象。
注意:要自动跟踪横幅事件到BEAM Tracker,请向rempConfig
对象中添加tracker
属性。有关详细信息,请参阅BEAM README。这两个片段相互补充,可以合并成一个包含活动和Beam功能的JS代码片段。
(function(win, doc) { function mock(fn) { return function() { this._.push([fn, arguments]) } } function load(url) { var script = doc.createElement("script"); script.type = "text/javascript"; script.async = true; script.src = url; doc.getElementsByTagName("head")[0].appendChild(script); } win.remplib = win.remplib || {}; var mockFuncs = { "campaign": "init", "tracker": "init trackEvent trackPageview trackCommerce", "iota": "init" }; Object.keys(mockFuncs).forEach(function (key) { if (!win.remplib[key]) { var fn, i, funcs = mockFuncs[key].split(" "); win.remplib[key] = {_: []}; for (i = 0; i < funcs.length; i++) { fn = funcs[i]; win.remplib[key][fn] = mock(fn); } } }); // change URL to location of CAMPAIGN remplib.js load("http://campaign.remp.press/assets/lib/js/remplib.js"); })(window, document); var rempConfig = { // UUIDv4 based REMP BEAM token of appropriate property // (see BEAM Admin -> Properties) // required if you're using REMP segments token: String, // optional, identification of logged user userId: String, // optional, flag whether user is currently subscribed to the displayed content userSubscribed: Boolean, // optional, this is by default generated by remplib.js library and you don't need to override it browserId: String, // optional, controls where cookies (RTM - REMP's UTM parameters of visit) are stored cookieDomain: ".remp.press", // optional, controls which type of storage should be used by default (`local_storage` or `cookie`) // default is `local_storage` storage: "local_storage", // optional, specifies how long to store specific keys in storage (in minutes) storageExpiration: { // default value (in minutes) for all storage keys if not overriden in `keys` "default": 15, // specific pairs of key name and life length in minutes "keys": { "browser_id": 1051200, // 2 years in minutes "campaigns": 1051200 } }, // required, Campaign specific options campaign: { // required, URL host of REMP Campaign url: "http://campaign.remp.press", // Additional params that will be appended links within displayed banner // // Key represents variable name, value should be defined as callback returning string response. // Following example will be appended as "&foo=bar&baz=XXX". // If the value is not function, remplib validation will throw an error and won't proceed further. bannerUrlParams: { "foo": function() { return "bar" }, "baz": function() { return "XXX" } }, variables: { // variables replace template placeholders in banners, // e.g. {{ email }} -> foo@example.com // // the callback doesn't pass any parameters, it's required for convenience and just-in-time evaluation // // missing variable is translated to empty string email: { value: function() { return "foo@example.com" } }, }, // Optional. Pageview attributes are used to provide to Campaign additional information about the pageview. // You can configure your campaigns to be displayed based on these attributes - see "Pageview attributes" // section when editing the campaign. // // The value can be: // - string: Campaign is displayed when the string matches configured value for given attribute name. // - array of strings: Campaign is displayed, when one of the provided strings matches the configured value. // // Any other kind of value is ignored and triggers JS console warning. // // All of provided attributes which are not configured in the campaign are ignored during processing. pageviewAttributes: { "attribute_1": "value_1", "attribute_2": ["value_1", "value_2"] } } // if you want to automatically track banner events to BEAM Tracker, // add also rempConfig.tracker property // // see REMP BEAM README.md }; remplib.campaign.init(rempConfig);
单页应用程序
如果您使用单页应用程序并且需要在加载后重新初始化JS库
- 更新
rempConfig
变量以反映导航更改。 - 再次调用
remplib.campaign.init(rempConfig)
以重新初始化JS跟踪状态。所有现有的横幅将隐藏,活动将再次评估。
Segment集成
为了确定向谁显示活动,活动依赖于用户段(实际上是一系列应看到横幅的用户/浏览器ID)。您可以注册尽可能多的段提供者,唯一条件是提供者应与同一用户基础(一个用户ID必须始终指向)相同用户。
实现必须实现App\Contracts\SegmentContract
接口。
所有注册的实现都隐藏在SegmentAggregator
的伪装之下。然后使用该伪装显示配置列表中的可用段,并在活动运行时评估段的实际成员。
如果您想将活动链接到自己的系统,以下是实现的方法
-
provider(): string
:在段提供者中唯一标识段提供者。这在内部分配段名称时需要,以防在多个段源中使用相同的段名称。return "my-provider";
-
list(): Collection
:返回此提供者所有可用段的集合。响应结构为return [ [ 'name' => String, // user friendly label 'provider' => String, // should be same as result of provider() 'code' => String, // machine friendly name, slug 'group' => [ 'id' => Integer, // ID of segment group 'name' => String, // user friendly label of group 'sorting' => Integer // sorting index; lower the number, sooner the group appears in the list ] ], ];
-
users(CampaignSegment $segment): Collection
:返回属于该段的用户ID列表。$segment
:包含用于活动中段引用的CampaignSegment
实例。
响应预期为表示用户ID的整数/字符串集合
return collect([ String, String, // ... ])
-
checkUser(CampaignSegment $campaignSegment, string $userId): bool
:检查给定的userId
是否属于提供的campaignSegment
。这可以通过外部API(如果已准备用于实时使用)或用户ID的缓存列表完成——这是实现内部的部分。如果实现不支持基于用户的段,则返回false
。 -
checkBrowser(CampaignSegment $campaignSegment, string $browserId): bool
:检查给定的browserId
是否属于提供的campaignSegment
。这可以通过外部API(如果已准备用于实时使用)或浏览器ID的缓存列表完成——这是实现内部的部分。如果实现不支持基于浏览器的段,则返回false
。 -
cacheEnabled(): bool
:标志是否应该由应用程序缓存此提供者中用于活动活动的段。如果为true
,则活动每小时将请求每个活动中的users()
并存储用户ID集合到应用程序缓存中。 -
getProviderData()
:提供者可以将任意数据传递回用户的浏览器。这可以用来缓存与浏览器/用户相关的信息,而不影响后端系统。这些数据可以随后由前端应用程序中的JS(如有必要)进行修改。所有提供的数据都在每次campaign/showtime
请求时传递回服务器 - 每次横幅显示尝试时。注意:我们使用这种缓存来直接在浏览器中计算可跟踪事件的数量。然后根据缓存中提供的数据评估基于这些事件的段规则,而不是击中数据库。这种方法大大提高了REMP Beam事件数据库(Elasticsearch)的性能。
-
setProviderData($providerData): void
:用于存储由前端应用程序在传入请求中提供的数据的辅助providerData方法。
实现时,创建一个提供您实现实例的段提供者。提供者应将\App\Contracts\SegmentAggregator::TAG
设置为类,以便您的实现也能注册到SegmentAggregator
。
横幅自定义样式
横幅使用默认系统字体。如果您想使用自己的字体,您可以在自定义CSS字段中添加对.remp-banner .serif
和.remp-banner .sans-serif
的样式,或者直接使用横幅显示功能将它们添加到网页中。
示例
.remp-banner .serif { font-family: 'Lumin Serif', serif; } .remp-banner .sans-serif { font-family: 'Lumin Sans', sans-serif; }
设置和功能
活动位置冲突解决(实验性)
默认情况下,活动将所有活动匹配的横幅显示在其预期显示的位置。如果您想确保始终只有一个横幅在单个位置显示,您可以通过ENV变量启用此功能。
PRIORITIZE_BANNERS_ON_SAME_POSITION=1
当此功能启用且系统在相同位置解析多个横幅时,规则如下
- 显示属于具有最多变体的活动的横幅。
- 如果有更多,显示最近更新的活动的横幅。
可折叠横幅状态存储
可折叠横幅存储其折叠状态,并在下次显示时以相同状态显示(展开、折叠)。如果用户折叠活动横幅,则下一次显示时将折叠显示,反之亦然。
在可折叠横幅设置中有一个切换,可以覆盖此行为并始终以初始状态显示横幅。
片段
活动支持在横幅模板内容、自定义JavaScript和自定义CSS中使用片段。您可以在“片段”主菜单部分使用“添加新片段”创建片段。
您可以通过将{{ snippet_name }}
添加到用于横幅内容或自定义JavaScript & CSS的其中一个字段来使用创建的片段。
活动调试器
为了能够调试实时活动和解决问题,活动提供活动调试器工具。
在活动的.env
文件中设置CAMPAIGN_DEBUG_KEY
。它可以是一个(最好是随机生成的)值 - 当您使用调试器时,您将被要求输入密钥。
要启动调试器,转到显示您想要调试的活动所在的页面,并在URL中附加#campaignDebug
。在调试器窗口中,填写所需的参数Debug key
和Campaign public ID
,然后提交。页面刷新后,调试器应提供显示或未显示特定活动的理由。
API 文档
活动为以下描述的任务提供简单的API。
所有示例都使用http://campaign.remp.press
作为基础域名。请在执行示例之前将主机更改为您使用的域名。
所有示例使用 XXX
作为默认的授权令牌,请将其替换为在 REMP SSO 中获得的真实令牌 API 令牌。
所有请求应包含(并符合)以下 HTTP 头。
Content-Type: application/json
Accept: application/json
Authorization: Bearer REMP_SSO_API_TOKEN
API 响应可以包含以下 HTTP 状态码
如果可能的话,响应将包含带有进一步错误信息的 application/json
编码的有效负载。
POST /api/banners/BANNER_ID/one-time-display
活动支持对特定用户一次性显示横幅。此类横幅不需要关联活动即可显示。这可以用作提醒用户执行所需操作的方式(而不是通过电子邮件通知他们)。
调用此端点时,请将 URL 中的 BANNER_ID
替换为实际的横幅 ID。每个一次性横幅都必须指定其到期日期和要显示给的用户(通过用户 ID 或浏览器 ID 进行定位)。
请求体
{ "user_id": "29953", // String; Required (if browser_id is not present); ID of targeted user "browser_id": "aaaaaa-bbbb-cccc-dddd-1111111", // String; Required (if user_id is not present); ID of targeted browser "expires_at": "2019-12-29T10:05:00" // Date; Required; RFC3339 formatted datetime, specifying when the one-time banner expires (it won't show after the date) }
示例:
curl
curl -X POST \ http://campaign.remp.press/api/banners/[BANNER_ID]/one-time-display \ -H 'Accept: application/json' \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "user_id": "29953", "expires_at": "2019-12-29T10:05:00" }'
原始 PHP
$payload = [ "user_id" => "29953", "expires_at" => "2019-12-29T10:05:00" ]; $jsonPayload = json_encode($payload); $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: type=application/json\r\n" . "Accept: application/json\r\n" . "Content-Length: " . strlen($jsonPayload) . "\r\n" . "Authorization: Bearer XXX", 'content' => $jsonPayload, ] ] ); $bannerId = 1; $response = file_get_contents("http://campaign.remp.press/api/banners/{$bannerId}/one-time-display", false, $context); // process response (raw JSON string)
响应
有效的 202 HTTP 状态码的响应
{ "status": "ok" }
覆盖段缓存中用户的定位
- 添加用户:
POST /api/segment-cache/provider/{segment_provider}/code/{segment_code}/add-user
- 删除用户:
POST /api/segment-cache/provider/{segment_provider}/code/{segment_code}/remove-user
如果段提供者支持,活动将缓存段成员以实现快速访问。如果段是动态的(经常更改),可能会导致活动定位错误(旧数据)。
注意:缓存失效间隔设置为每小时一次(检查
App\Console\Kernel->schedule()
)。
这两个 API 端点允许从活动外部覆盖缓存段中一个用户的成员资格(例如,从 CRM)。
如果段未被活跃或计划中的活动积极使用,则两个端点都会返回响应 404 Not found
。
API 路径参数
请求体
{ "user_id": "29953", // String; Required; ID of targeted user }
示例:
curl
路径参数
- segment_provider -
crm_segment
- segment_code -
example_testing_segment
curl -X POST \ http://campaign.remp.press/api/segment-cache/provider/crm_segment/code/example_testing_segment/add-user \ -H 'Accept: application/json' \ -H 'Authorization: Bearer XXX' \ -H 'Content-Type: application/json' \ -d '{ "user_id": "29953" }'
原始 PHP
$payload = [ "user_id" => "29953", ]; $jsonPayload = json_encode($payload); $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: type=application/json\r\n" . "Accept: application/json\r\n" . "Content-Length: " . strlen($jsonPayload) . "\r\n" . "Authorization: Bearer XXX", 'content' => $jsonPayload, ] ] ); $segmentProvider = "crm_segment"; $segmentCode = "example_testing_segment"; $response = file_get_contents("http://campaign.remp.press/api/segment-cache/provider/{$segmentProvider}/code/{$segmentCode}/add-user", false, $context); // process response (raw JSON string)
响应
有效的 202 HTTP 状态码的响应
{ "status": "ok" }
404 HTTP 状态码的响应
{ "status": "error", "message": "Segment with code [example_testing_segment] from provider [crm_segment] is not actively used in the campaign." }
新闻通讯横幅
此横幅类型允许您直接收集新闻通讯订阅者。
在大多数情况下,您需要自己的 API 代理,因为大多数新闻通讯 API(包括 REMP CRM)都使用不应向客户端公开的私有令牌。如果您的新闻通讯 API 不需要这样的令牌,您可能不需要代理。
设置
所有配置选项都通过 .env
设置,有关可用选项的完整列表,请参阅 .env.example
。
API 要求
新闻通讯 API 必须以正确的 HTTP 响应代码响应。在成功和失败的情况下,都会发出自定义事件。有关详细信息,请参阅下一节。
自定义事件
如果请求配置了 XHR,则可用的自定义事件为 rempNewsletterSubscribeFailure
或 rempNewsletterSubscribeSuccess
。事件在 form
元素上触发。事件在 detail
属性中包含以下项
错误处理示例
window.addEventListener("rempNewsletterSubscribeFailure", function(event){ if (event.detail && event.detail.type === 'response'){ const response = event.detail.response; console.warn('HTTP Status Code:', response.status); // to retrieve body use `json()` or `text()` depending on your API implemetation response.json().then(function(data) { console.warn(data); }); } if (event.detail && event.detail.type === 'exception') { console.warn(event.detail.message); } });
健康检查
路由 http://campaign.remp.press/health
提供数据库、Redis、存储和日志的健康检查。
返回
-
200 OK 和包含服务列表的 JSON(状态为 "OK")。
-
500 Internal Server Error 和包含问题描述的 JSON。例如
{ "status":"PROBLEM", "log":{ "status":"PROBLEM", "message":"Could not write to log file", "context": // error or thrown exception... //... }
MaxMind - GeoIP2 Lite
此产品包含由 MaxMind 创建的 GeoLite2 数据,可在 http://www.maxmind.com 获取。由于许可证变更,您需要手动更新数据库。如果这样做,请通过 MAXMIND_DATABASE
环境变量提供更新数据库的路径。
RTM 跟踪
以下参数添加到每个请求或横幅内的链接中。它们用于活动性能跟踪。
- rtm_source:
"remp_campaign"
=> 始终相同的字符串 - rtm_medium:
displayType
=> 覆盖或内嵌 - rtm_campaign:
campaignUuid
=> 广告活动 ID - rtm_content:
uuid
=> 横幅 ID - banner_variant:
variantUuid
=> 横幅变体 ID(在广告活动中)