| | | 1 | | using Intentum.Core.Behavior; |
| | | 2 | | using Intentum.Core.Models; |
| | | 3 | | |
| | | 4 | | namespace Intentum.Core.Commerce; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// Pre-built e-commerce intent rules for customer behavior classification. |
| | | 8 | | /// </summary> |
| | | 9 | | public static class CommerceRules |
| | | 10 | | { |
| | | 11 | | /// <summary> |
| | | 12 | | /// Detects purchase intent: product views, cart adds, checkout page visits. |
| | | 13 | | /// </summary> |
| | | 14 | | public static Func<BehaviorSpace, RuleMatch?> PurchaseIntent( |
| | 4 | 15 | | double confidence = 0.85) => space => |
| | 3 | 16 | | { |
| | 3 | 17 | | var productViews = space.Events.Count(e => |
| | 9 | 18 | | e.Action.Contains("product.view", StringComparison.OrdinalIgnoreCase) || |
| | 9 | 19 | | e.Action.Contains("ViewProduct", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 20 | | var cartAdds = space.Events.Count(e => |
| | 9 | 21 | | e.Action.Contains("cart.add", StringComparison.OrdinalIgnoreCase) || |
| | 9 | 22 | | e.Action.Contains("AddToCart", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 23 | | var checkoutVisit = space.Events.Any(e => |
| | 9 | 24 | | e.Action.Contains("checkout", StringComparison.OrdinalIgnoreCase)); |
| | 4 | 25 | | |
| | 3 | 26 | | if (cartAdds >= 1 && checkoutVisit) |
| | 1 | 27 | | return new RuleMatch("PurchaseIntent", confidence, |
| | 1 | 28 | | $"Views: {productViews}, cart adds: {cartAdds}, checkout: true"); |
| | 2 | 29 | | if (cartAdds >= 2) |
| | 1 | 30 | | return new RuleMatch("PurchaseIntent", confidence * 0.7, |
| | 1 | 31 | | $"Multiple cart adds: {cartAdds}"); |
| | 1 | 32 | | return null; |
| | 7 | 33 | | }; |
| | | 34 | | |
| | | 35 | | /// <summary> |
| | | 36 | | /// Detects cart abandonment: cart activity followed by browsing away or leaving. |
| | | 37 | | /// </summary> |
| | | 38 | | public static Func<BehaviorSpace, RuleMatch?> CartAbandonment( |
| | 3 | 39 | | double confidence = 0.75) => space => |
| | 2 | 40 | | { |
| | 2 | 41 | | var cartAdds = space.Events.Count(e => |
| | 6 | 42 | | e.Action.Contains("cart.add", StringComparison.OrdinalIgnoreCase) || |
| | 6 | 43 | | e.Action.Contains("AddToCart", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 44 | | var cartRemoves = space.Events.Count(e => |
| | 6 | 45 | | e.Action.Contains("cart.remove", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 46 | | var hasCheckout = space.Events.Any(e => |
| | 6 | 47 | | e.Action.Contains("checkout", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 48 | | var hasPurchase = space.Events.Any(e => |
| | 6 | 49 | | e.Action.Contains("purchase", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 50 | | var hasBrowseAway = space.Events.Any(e => |
| | 6 | 51 | | e.Action.Contains("navigate.away", StringComparison.OrdinalIgnoreCase) || |
| | 6 | 52 | | e.Action.Contains("session.end", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 53 | | |
| | 2 | 54 | | if (cartAdds >= 1 && !hasPurchase && (cartRemoves >= 1 || hasBrowseAway || !hasCheckout)) |
| | 1 | 55 | | return new RuleMatch("CartAbandonment", confidence, |
| | 1 | 56 | | $"Cart adds: {cartAdds}, removes: {cartRemoves}, no purchase, browse away: {hasBrowseAway}"); |
| | 1 | 57 | | return null; |
| | 5 | 58 | | }; |
| | | 59 | | |
| | | 60 | | /// <summary> |
| | | 61 | | /// Detects browsing/research intent: extensive product views without cart activity. |
| | | 62 | | /// </summary> |
| | | 63 | | public static Func<BehaviorSpace, RuleMatch?> BrowsingIntent( |
| | | 64 | | int minViews = 5, |
| | 3 | 65 | | double confidence = 0.7) => space => |
| | 2 | 66 | | { |
| | 2 | 67 | | var views = space.Events.Count(e => |
| | 12 | 68 | | e.Action.Contains("view", StringComparison.OrdinalIgnoreCase) || |
| | 12 | 69 | | e.Action.Contains("browse", StringComparison.OrdinalIgnoreCase) || |
| | 12 | 70 | | e.Action.Contains("search", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 71 | | var cartAdds = space.Events.Count(e => |
| | 12 | 72 | | e.Action.Contains("cart", StringComparison.OrdinalIgnoreCase)); |
| | 3 | 73 | | |
| | 2 | 74 | | if (views >= minViews && cartAdds == 0) |
| | 1 | 75 | | return new RuleMatch("BrowsingIntent", confidence, |
| | 1 | 76 | | $"Views/searches: {views}, no cart activity"); |
| | 1 | 77 | | return null; |
| | 5 | 78 | | }; |
| | | 79 | | |
| | | 80 | | /// <summary> |
| | | 81 | | /// Detects support/help-seeking intent. |
| | | 82 | | /// </summary> |
| | | 83 | | public static Func<BehaviorSpace, RuleMatch?> SupportIntent( |
| | 2 | 84 | | double confidence = 0.8) => space => |
| | 1 | 85 | | { |
| | 1 | 86 | | var supportActions = space.Events.Count(e => |
| | 3 | 87 | | e.Action.Contains("support", StringComparison.OrdinalIgnoreCase) || |
| | 3 | 88 | | e.Action.Contains("help", StringComparison.OrdinalIgnoreCase) || |
| | 3 | 89 | | e.Action.Contains("faq", StringComparison.OrdinalIgnoreCase) || |
| | 3 | 90 | | e.Action.Contains("contact", StringComparison.OrdinalIgnoreCase) || |
| | 3 | 91 | | e.Action.Contains("return", StringComparison.OrdinalIgnoreCase)); |
| | 2 | 92 | | |
| | 1 | 93 | | if (supportActions >= 2) |
| | 1 | 94 | | return new RuleMatch("SupportIntent", confidence, |
| | 1 | 95 | | $"Support-related actions: {supportActions}"); |
| | 0 | 96 | | return null; |
| | 3 | 97 | | }; |
| | | 98 | | |
| | | 99 | | /// <summary> |
| | | 100 | | /// Returns all standard e-commerce rules in recommended evaluation order. |
| | | 101 | | /// </summary> |
| | | 102 | | public static IReadOnlyList<Func<BehaviorSpace, RuleMatch?>> AllRules() => |
| | 1 | 103 | | [ |
| | 1 | 104 | | PurchaseIntent(), |
| | 1 | 105 | | CartAbandonment(), |
| | 1 | 106 | | SupportIntent(), |
| | 1 | 107 | | BrowsingIntent() |
| | 1 | 108 | | ]; |
| | | 109 | | } |