|
1 | 1 | package beans |
2 | 2 |
|
| 3 | +import ( |
| 4 | + "encoding/json" |
| 5 | +) |
| 6 | + |
3 | 7 | type Experiment struct { |
4 | 8 | // distinct_id 标识 |
5 | 9 | DistinctId string |
6 | 10 | // 是否是登录 id |
7 | 11 | IsLoginId bool |
| 12 | + // 自定义主体 ID |
| 13 | + CustomIDs map[string]string |
8 | 14 | // 试验变量值 |
9 | 15 | Result interface{} |
10 | 16 | InternalExperiment InnerExperiment |
11 | 17 | } |
12 | 18 |
|
| 19 | +// 在代码中有一些浅拷贝操作,新增字段时需要注意 |
13 | 20 | type InnerExperiment struct { |
14 | 21 | // 试验 ID |
15 | 22 | AbtestExperimentId string `json:"abtest_experiment_id"` |
@@ -79,3 +86,192 @@ type Variables struct { |
79 | 86 | // 变量类型 |
80 | 87 | Type string `json:"type"` |
81 | 88 | } |
| 89 | + |
| 90 | +// AllExperimentsResult represents the result of fetching all experiments. |
| 91 | +// Its fields are unexported to ensure immutability and it should be created via the builder. |
| 92 | +type AllExperimentsResult struct { |
| 93 | + // distinct_id 标识 |
| 94 | + distinctId string |
| 95 | + |
| 96 | + // 是否是登录 id |
| 97 | + isLoginId bool |
| 98 | + |
| 99 | + // 自定义主体 ID |
| 100 | + customIDs map[string]string |
| 101 | + |
| 102 | + // 埋点回调函数,在fetchAll的时候自动生成,GetValue 时会自动调用 |
| 103 | + trackCallback func(paramName string, experiment InnerExperiment) |
| 104 | + |
| 105 | + // 全部的试验结果,用于支持 GetValue 方法 |
| 106 | + experiments map[string]InnerExperiment |
| 107 | + |
| 108 | + // 原始网络响应 body,用于跨服务传递 |
| 109 | + responseBody string |
| 110 | + |
| 111 | + // 请求时间 |
| 112 | + timestamp int64 |
| 113 | +} |
| 114 | + |
| 115 | +// DistinctId returns the distinct_id. |
| 116 | +func (result *AllExperimentsResult) DistinctId() string { |
| 117 | + return result.distinctId |
| 118 | +} |
| 119 | + |
| 120 | +// IsLoginId returns whether the id is a login id. |
| 121 | +func (result *AllExperimentsResult) IsLoginId() bool { |
| 122 | + return result.isLoginId |
| 123 | +} |
| 124 | + |
| 125 | +// CustomIDs returns the custom IDs. |
| 126 | +func (result *AllExperimentsResult) CustomIDs() map[string]string { |
| 127 | + // 返回一个副本以防止外部修改 map |
| 128 | + if result.customIDs == nil { |
| 129 | + return nil |
| 130 | + } |
| 131 | + copiedCustomIDs := make(map[string]string, len(result.customIDs)) |
| 132 | + for k, v := range result.customIDs { |
| 133 | + copiedCustomIDs[k] = v |
| 134 | + } |
| 135 | + return copiedCustomIDs |
| 136 | +} |
| 137 | + |
| 138 | +// Timestamp returns the timestamp. |
| 139 | +func (result *AllExperimentsResult) Timestamp() int64 { |
| 140 | + return result.timestamp |
| 141 | +} |
| 142 | + |
| 143 | +func (result *AllExperimentsResult) SetTrackCallback(callback func(paramName string, experiment InnerExperiment)) { |
| 144 | + result.trackCallback = callback |
| 145 | +} |
| 146 | + |
| 147 | +// 安全的取值方法,自动埋点 |
| 148 | +func (result *AllExperimentsResult) GetValue(paramName string, defaultValue interface{}) interface{} { |
| 149 | + var res interface{} |
| 150 | + var hitExperiment InnerExperiment |
| 151 | + if experiment, exists := result.experiments[paramName]; exists { |
| 152 | + res = experiment.Result |
| 153 | + hitExperiment = experiment |
| 154 | + } else { |
| 155 | + res = defaultValue |
| 156 | + } |
| 157 | + // 如果开启了埋点,则进行埋点 |
| 158 | + if result.trackCallback != nil { |
| 159 | + result.trackCallback(paramName, hitExperiment) |
| 160 | + } |
| 161 | + return res |
| 162 | +} |
| 163 | + |
| 164 | +// 获取 experiment对象,不会自动埋点,主要用于获取手动埋点的参数 |
| 165 | +func (result *AllExperimentsResult) GetExperiment(paramName string, defaultValue interface{}) Experiment { |
| 166 | + if experiment, exists := result.experiments[paramName]; exists { |
| 167 | + return Experiment{ |
| 168 | + DistinctId: result.DistinctId(), |
| 169 | + IsLoginId: result.IsLoginId(), |
| 170 | + CustomIDs: result.CustomIDs(), |
| 171 | + Result: experiment.Result, |
| 172 | + InternalExperiment: experiment, |
| 173 | + } |
| 174 | + |
| 175 | + } |
| 176 | + return Experiment{ |
| 177 | + Result: defaultValue, |
| 178 | + } |
| 179 | +} |
| 180 | + |
| 181 | +// 检查是否包含指定参数 |
| 182 | +func (result *AllExperimentsResult) HasParam(paramName string) bool { |
| 183 | + _, exists := result.experiments[paramName] |
| 184 | + return exists |
| 185 | +} |
| 186 | + |
| 187 | +// DumpData 用于跨服务传递的序列化数据结构 |
| 188 | +type DumpData struct { |
| 189 | + DistinctId string `json:"distinct_id"` |
| 190 | + IsLoginId bool `json:"is_login_id"` |
| 191 | + CustomIDs map[string]string `json:"custom_ids,omitempty"` |
| 192 | + ResponseBody string `json:"response_body"` |
| 193 | + Timestamp int64 `json:"timestamp"` |
| 194 | +} |
| 195 | + |
| 196 | +// Dump 序列化完整的上下文信息(包括 distinct_id、is_login_id、custom_ids 和响应体) |
| 197 | +// 返回 JSON 字符串,用于跨服务传递 |
| 198 | +func (result *AllExperimentsResult) Dump() (string, error) { |
| 199 | + data := DumpData{ |
| 200 | + DistinctId: result.distinctId, |
| 201 | + IsLoginId: result.isLoginId, |
| 202 | + CustomIDs: result.customIDs, |
| 203 | + ResponseBody: result.responseBody, |
| 204 | + Timestamp: result.timestamp, |
| 205 | + } |
| 206 | + |
| 207 | + jsonBytes, err := json.Marshal(data) |
| 208 | + if err != nil { |
| 209 | + return "", err |
| 210 | + } |
| 211 | + |
| 212 | + return string(jsonBytes), nil |
| 213 | +} |
| 214 | + |
| 215 | +// AllExperimentsResultBuilder is used to construct an AllExperimentsResult object. |
| 216 | +type AllExperimentsResultBuilder struct { |
| 217 | + distinctId string |
| 218 | + isLoginId bool |
| 219 | + customIDs map[string]string |
| 220 | + trackCallback func(paramName string, experiment InnerExperiment) |
| 221 | + experiments map[string]InnerExperiment |
| 222 | + responseBody string |
| 223 | + timestamp int64 |
| 224 | +} |
| 225 | + |
| 226 | +// NewAllExperimentsResultBuilder creates a new builder. |
| 227 | +func NewAllExperimentsResultBuilder() *AllExperimentsResultBuilder { |
| 228 | + return &AllExperimentsResultBuilder{} |
| 229 | +} |
| 230 | + |
| 231 | +func (b *AllExperimentsResultBuilder) DistinctId(distinctId string) *AllExperimentsResultBuilder { |
| 232 | + b.distinctId = distinctId |
| 233 | + return b |
| 234 | +} |
| 235 | + |
| 236 | +func (b *AllExperimentsResultBuilder) IsLoginId(isLoginId bool) *AllExperimentsResultBuilder { |
| 237 | + b.isLoginId = isLoginId |
| 238 | + return b |
| 239 | +} |
| 240 | + |
| 241 | +func (b *AllExperimentsResultBuilder) CustomIDs(customIDs map[string]string) *AllExperimentsResultBuilder { |
| 242 | + b.customIDs = customIDs |
| 243 | + return b |
| 244 | +} |
| 245 | + |
| 246 | +func (b *AllExperimentsResultBuilder) TrackCallback(callback func(paramName string, experiment InnerExperiment)) *AllExperimentsResultBuilder { |
| 247 | + b.trackCallback = callback |
| 248 | + return b |
| 249 | +} |
| 250 | + |
| 251 | +func (b *AllExperimentsResultBuilder) Experiments(experiments map[string]InnerExperiment) *AllExperimentsResultBuilder { |
| 252 | + b.experiments = experiments |
| 253 | + return b |
| 254 | +} |
| 255 | + |
| 256 | +func (b *AllExperimentsResultBuilder) ResponseBody(responseBody string) *AllExperimentsResultBuilder { |
| 257 | + b.responseBody = responseBody |
| 258 | + return b |
| 259 | +} |
| 260 | + |
| 261 | +func (b *AllExperimentsResultBuilder) Timestamp(timestamp int64) *AllExperimentsResultBuilder { |
| 262 | + b.timestamp = timestamp |
| 263 | + return b |
| 264 | +} |
| 265 | + |
| 266 | +// Build creates and returns the immutable AllExperimentsResult. |
| 267 | +func (b *AllExperimentsResultBuilder) Build() AllExperimentsResult { |
| 268 | + return AllExperimentsResult{ |
| 269 | + distinctId: b.distinctId, |
| 270 | + isLoginId: b.isLoginId, |
| 271 | + customIDs: b.customIDs, |
| 272 | + trackCallback: b.trackCallback, |
| 273 | + experiments: b.experiments, |
| 274 | + responseBody: b.responseBody, |
| 275 | + timestamp: b.timestamp, |
| 276 | + } |
| 277 | +} |
0 commit comments