|
|
@@ -0,0 +1,660 @@
|
|
|
+package com.tzld.rta.codec;
|
|
|
+
|
|
|
+import com.google.protobuf.CodedInputStream;
|
|
|
+import com.google.protobuf.CodedOutputStream;
|
|
|
+import com.google.protobuf.WireFormat;
|
|
|
+import com.tzld.rta.model.DynamicContentInfoModel;
|
|
|
+import com.tzld.rta.model.DynamicProductInfoModel;
|
|
|
+import com.tzld.rta.model.RtaRequestModel;
|
|
|
+import com.tzld.rta.model.RtaResponseModel;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.util.ArrayList;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 腾讯RTA Protobuf 手工编解码器
|
|
|
+ * <p>
|
|
|
+ * 按照 rta.proto 的 field number 手动读写,避免依赖 protoc 生成代码
|
|
|
+ * 协议参考: https://wiki.algo.com.cn/docs/rta/dev/proto
|
|
|
+ * <p>
|
|
|
+ * 完整覆盖 proto 所有字段:
|
|
|
+ * - RtaRequest: id/is_ping/is_test/device/request_info/exps/ad_infos/unified_request_id/fl_models/crt_template_id/rta_trace_id_list/ad_request_time
|
|
|
+ * - RtaResponse: request_id/code/processing_time_ms/response_cache_time/dsp_tag/dynamic_product_infos/out_target_id/target_infos/aid_whitelist/ad_results/fl_model_res_infos/dsp_tag_str/sdpa_dynamic_product_infos
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+public class RtaProtobufCodec {
|
|
|
+
|
|
|
+ // ====================== 解码 RtaRequest ======================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 RtaRequest protobuf 二进制数据
|
|
|
+ *
|
|
|
+ * @param bytes 请求字节
|
|
|
+ * @return 解析后的模型
|
|
|
+ */
|
|
|
+ public static RtaRequestModel decodeRequest(byte[] bytes) throws IOException {
|
|
|
+ RtaRequestModel req = new RtaRequestModel();
|
|
|
+ CodedInputStream input = CodedInputStream.newInstance(bytes);
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ int fieldNumber = tag >>> 3;
|
|
|
+ switch (fieldNumber) {
|
|
|
+ case 0:
|
|
|
+ done = true;
|
|
|
+ break;
|
|
|
+ case 1: // id (string)
|
|
|
+ req.setId(input.readString());
|
|
|
+ break;
|
|
|
+ case 2: // is_ping (bool)
|
|
|
+ req.setPing(input.readBool());
|
|
|
+ break;
|
|
|
+ case 3: // is_test (bool)
|
|
|
+ req.setTest(input.readBool());
|
|
|
+ break;
|
|
|
+ case 5: // device (message)
|
|
|
+ int deviceLength = input.readRawVarint32();
|
|
|
+ int deviceLimit = input.pushLimit(deviceLength);
|
|
|
+ req.setDevice(decodeDevice(input));
|
|
|
+ input.popLimit(deviceLimit);
|
|
|
+ break;
|
|
|
+ case 6: // siteset_id (uint64, 已禁用)
|
|
|
+ req.setSitesetId(input.readUInt64());
|
|
|
+ break;
|
|
|
+ case 8: // request_info (message)
|
|
|
+ int reqInfoLen = input.readRawVarint32();
|
|
|
+ int reqInfoLimit = input.pushLimit(reqInfoLen);
|
|
|
+ req.setRequestInfo(decodeRequestInfo(input));
|
|
|
+ input.popLimit(reqInfoLimit);
|
|
|
+ break;
|
|
|
+ case 9: // exps (repeated message)
|
|
|
+ int expLen = input.readRawVarint32();
|
|
|
+ int expLimit = input.pushLimit(expLen);
|
|
|
+ RtaRequestModel.ExperimentModel exp = decodeExperiment(input);
|
|
|
+ input.popLimit(expLimit);
|
|
|
+ if (req.getExps() == null) req.setExps(new ArrayList<>());
|
|
|
+ req.getExps().add(exp);
|
|
|
+ break;
|
|
|
+ case 11: // ad_infos (repeated message, 二次请求)
|
|
|
+ int adInfoLen = input.readRawVarint32();
|
|
|
+ int adInfoLimit = input.pushLimit(adInfoLen);
|
|
|
+ RtaRequestModel.AdInfoModel adInfo = decodeAdInfo(input);
|
|
|
+ input.popLimit(adInfoLimit);
|
|
|
+ if (req.getAdInfos() == null) req.setAdInfos(new ArrayList<>());
|
|
|
+ req.getAdInfos().add(adInfo);
|
|
|
+ break;
|
|
|
+ case 12: // unified_request_id (string)
|
|
|
+ req.setUnifiedRequestId(input.readString());
|
|
|
+ break;
|
|
|
+ case 14: // fl_models (repeated message)
|
|
|
+ int flLen = input.readRawVarint32();
|
|
|
+ int flLimit = input.pushLimit(flLen);
|
|
|
+ RtaRequestModel.FLModelModel flModel = decodeFLModel(input);
|
|
|
+ input.popLimit(flLimit);
|
|
|
+ if (req.getFlModels() == null) req.setFlModels(new ArrayList<>());
|
|
|
+ req.getFlModels().add(flModel);
|
|
|
+ break;
|
|
|
+ case 15: // crt_template_id (repeated uint32, packed)
|
|
|
+ decodePacked(input, tag, (val) -> {
|
|
|
+ if (req.getCrtTemplateIds() == null) req.setCrtTemplateIds(new ArrayList<>());
|
|
|
+ req.getCrtTemplateIds().add((int) val);
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 18: // rta_trace_id_list (repeated string)
|
|
|
+ if (req.getRtaTraceIdList() == null) req.setRtaTraceIdList(new ArrayList<>());
|
|
|
+ req.getRtaTraceIdList().add(input.readString());
|
|
|
+ break;
|
|
|
+ case 19: // ad_request_time (uint64)
|
|
|
+ req.setAdRequestTime(input.readUInt64());
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (!input.skipField(tag)) {
|
|
|
+ done = true;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return req;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static RtaRequestModel.RequestInfoModel decodeRequestInfo(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.RequestInfoModel info = new RtaRequestModel.RequestInfoModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ switch (tag >>> 3) {
|
|
|
+ case 0: done = true; break;
|
|
|
+ case 1: info.setRequestType(input.readEnum()); break;
|
|
|
+ default: if (!input.skipField(tag)) { done = true; } break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return info;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static RtaRequestModel.ExperimentModel decodeExperiment(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.ExperimentModel exp = new RtaRequestModel.ExperimentModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ switch (tag >>> 3) {
|
|
|
+ case 0: done = true; break;
|
|
|
+ case 1: exp.setExpId((int) input.readUInt32()); break;
|
|
|
+ default: if (!input.skipField(tag)) { done = true; } break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return exp;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static RtaRequestModel.AdInfoModel decodeAdInfo(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.AdInfoModel adInfo = new RtaRequestModel.AdInfoModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ switch (tag >>> 3) {
|
|
|
+ case 0: done = true; break;
|
|
|
+ case 1: adInfo.setAdgroupId(input.readUInt64()); break;
|
|
|
+ case 2: // creative_infos (repeated message)
|
|
|
+ int crtLen = input.readRawVarint32();
|
|
|
+ int crtLimit = input.pushLimit(crtLen);
|
|
|
+ RtaRequestModel.CreativeInfoModel crtInfo = decodeCreativeInfo(input);
|
|
|
+ input.popLimit(crtLimit);
|
|
|
+ if (adInfo.getCreativeInfos() == null) adInfo.setCreativeInfos(new ArrayList<>());
|
|
|
+ adInfo.getCreativeInfos().add(crtInfo);
|
|
|
+ break;
|
|
|
+ case 3: // product_infos (repeated message)
|
|
|
+ int prodLen = input.readRawVarint32();
|
|
|
+ int prodLimit = input.pushLimit(prodLen);
|
|
|
+ RtaRequestModel.ProductInfoModel prodInfo = decodeProductInfo(input);
|
|
|
+ input.popLimit(prodLimit);
|
|
|
+ if (adInfo.getProductInfos() == null) adInfo.setProductInfos(new ArrayList<>());
|
|
|
+ adInfo.getProductInfos().add(prodInfo);
|
|
|
+ break;
|
|
|
+ case 4: adInfo.setAdvertiserId(input.readUInt32()); break;
|
|
|
+ case 5: adInfo.setOutTargetId(input.readString()); break;
|
|
|
+ default: if (!input.skipField(tag)) { done = true; } break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return adInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static RtaRequestModel.CreativeInfoModel decodeCreativeInfo(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.CreativeInfoModel info = new RtaRequestModel.CreativeInfoModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ switch (tag >>> 3) {
|
|
|
+ case 0: done = true; break;
|
|
|
+ case 1: info.setCreativeId(input.readUInt64()); break;
|
|
|
+ case 2: info.setCrtTemplateId((int) input.readUInt32()); break;
|
|
|
+ default: if (!input.skipField(tag)) { done = true; } break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return info;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static RtaRequestModel.ProductInfoModel decodeProductInfo(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.ProductInfoModel info = new RtaRequestModel.ProductInfoModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ switch (tag >>> 3) {
|
|
|
+ case 0: done = true; break;
|
|
|
+ case 1: info.setProductLib(input.readUInt64()); break;
|
|
|
+ case 2: info.setOutProductId(input.readString()); break;
|
|
|
+ default: if (!input.skipField(tag)) { done = true; } break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return info;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static RtaRequestModel.FLModelModel decodeFLModel(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.FLModelModel model = new RtaRequestModel.FLModelModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ switch (tag >>> 3) {
|
|
|
+ case 0: done = true; break;
|
|
|
+ case 1: model.setModelId(input.readUInt64()); break;
|
|
|
+ case 2: model.setModelName(input.readString()); break;
|
|
|
+ case 3: model.setModelVersion(input.readString()); break;
|
|
|
+ case 4: model.setModelType(input.readEnum()); break;
|
|
|
+ case 5: // embedding (repeated float, packed)
|
|
|
+ decodePacked(input, tag, (val) -> {
|
|
|
+ // packed float 需特殊处理,此处作为原始bits读取
|
|
|
+ if (model.getEmbedding() == null) model.setEmbedding(new ArrayList<>());
|
|
|
+ model.getEmbedding().add(Float.intBitsToFloat((int) val));
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ default: if (!input.skipField(tag)) { done = true; } break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return model;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 Device 子消息
|
|
|
+ */
|
|
|
+ private static RtaRequestModel.DeviceModel decodeDevice(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.DeviceModel device = new RtaRequestModel.DeviceModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ int fieldNumber = tag >>> 3;
|
|
|
+ switch (fieldNumber) {
|
|
|
+ case 0:
|
|
|
+ done = true;
|
|
|
+ break;
|
|
|
+ case 1: // os (enum)
|
|
|
+ device.setOs(input.readEnum());
|
|
|
+ break;
|
|
|
+ case 2: // idfa_md5sum (string)
|
|
|
+ device.setIdfaMd5sum(input.readString());
|
|
|
+ break;
|
|
|
+ case 3: // imei_md5sum (string)
|
|
|
+ device.setImeiMd5sum(input.readString());
|
|
|
+ break;
|
|
|
+ case 4: // android_id_md5sum (string)
|
|
|
+ device.setAndroidIdMd5sum(input.readString());
|
|
|
+ break;
|
|
|
+ case 5: // mac_md5sum (string)
|
|
|
+ device.setMacMd5sum(input.readString());
|
|
|
+ break;
|
|
|
+ case 6: // oaid_md5sum (string)
|
|
|
+ device.setOaidMd5sum(input.readString());
|
|
|
+ break;
|
|
|
+ case 7: // ip (string)
|
|
|
+ device.setIp(input.readString());
|
|
|
+ break;
|
|
|
+ case 9: // doubtful_ids_list (repeated enum)
|
|
|
+ if (device.getDoubtfulIdsList() == null) device.setDoubtfulIdsList(new ArrayList<>());
|
|
|
+ device.getDoubtfulIdsList().add(input.readEnum());
|
|
|
+ break;
|
|
|
+ case 10: // cached_deviceid_type (enum)
|
|
|
+ device.setCachedDeviceidType(input.readEnum());
|
|
|
+ break;
|
|
|
+ case 13: // qaid_infos (repeated message)
|
|
|
+ int qaidLen = input.readRawVarint32();
|
|
|
+ int qaidLimit = input.pushLimit(qaidLen);
|
|
|
+ RtaRequestModel.QaidInfoModel qaid = decodeQaidInfo(input);
|
|
|
+ input.popLimit(qaidLimit);
|
|
|
+ if (device.getQaidInfos() == null) device.setQaidInfos(new ArrayList<>());
|
|
|
+ device.getQaidInfos().add(qaid);
|
|
|
+ break;
|
|
|
+ case 14: // wx_openid (repeated message)
|
|
|
+ int wxLen = input.readRawVarint32();
|
|
|
+ int wxLimit = input.pushLimit(wxLen);
|
|
|
+ RtaRequestModel.WxOpenidModel wx = decodeWxOpenid(input);
|
|
|
+ input.popLimit(wxLimit);
|
|
|
+ if (device.getWxOpenids() == null) device.setWxOpenids(new ArrayList<>());
|
|
|
+ device.getWxOpenids().add(wx);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (!input.skipField(tag)) {
|
|
|
+ done = true;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return device;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static RtaRequestModel.QaidInfoModel decodeQaidInfo(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.QaidInfoModel qaid = new RtaRequestModel.QaidInfoModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ switch (tag >>> 3) {
|
|
|
+ case 0: done = true; break;
|
|
|
+ case 1: qaid.setVersion((int) input.readUInt32()); break;
|
|
|
+ case 2: qaid.setQaid(input.readString()); break;
|
|
|
+ case 3: qaid.setOriginVersion(input.readString()); break;
|
|
|
+ case 4: qaid.setQaidMd5sum(input.readString()); break;
|
|
|
+ default: if (!input.skipField(tag)) { done = true; } break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return qaid;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static RtaRequestModel.WxOpenidModel decodeWxOpenid(CodedInputStream input) throws IOException {
|
|
|
+ RtaRequestModel.WxOpenidModel wx = new RtaRequestModel.WxOpenidModel();
|
|
|
+ boolean done = false;
|
|
|
+ while (!done) {
|
|
|
+ int tag = input.readTag();
|
|
|
+ switch (tag >>> 3) {
|
|
|
+ case 0: done = true; break;
|
|
|
+ case 1: wx.setAppid(input.readString()); break;
|
|
|
+ case 2: wx.setOpenid(input.readString()); break;
|
|
|
+ default: if (!input.skipField(tag)) { done = true; } break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return wx;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====================== 编码 RtaResponse ======================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将 RtaResponseModel 序列化为 protobuf 二进制
|
|
|
+ *
|
|
|
+ * @param response 响应模型
|
|
|
+ * @return protobuf 字节
|
|
|
+ */
|
|
|
+ public static byte[] encodeResponse(RtaResponseModel response) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
|
|
|
+ CodedOutputStream output = CodedOutputStream.newInstance(baos);
|
|
|
+
|
|
|
+ // field 1: request_id (string)
|
|
|
+ if (response.getRequestId() != null && !response.getRequestId().isEmpty()) {
|
|
|
+ output.writeString(1, response.getRequestId());
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 2: code (uint32)
|
|
|
+ output.writeUInt32(2, response.getCode());
|
|
|
+
|
|
|
+ // field 4: processing_time_ms (int32)
|
|
|
+ if (response.getProcessingTimeMs() > 0) {
|
|
|
+ output.writeInt32(4, response.getProcessingTimeMs());
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 7: response_cache_time (uint32)
|
|
|
+ if (response.getResponseCacheTime() > 0) {
|
|
|
+ output.writeUInt32(7, response.getResponseCacheTime());
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 8: dsp_tag (uint64)
|
|
|
+ if (response.getDspTag() > 0) {
|
|
|
+ output.writeUInt64(8, response.getDspTag());
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 9: dynamic_product_infos (repeated message, mDPA 全局级)
|
|
|
+ if (response.getDynamicProductInfos() != null) {
|
|
|
+ for (DynamicProductInfoModel dpi : response.getDynamicProductInfos()) {
|
|
|
+ writeMessage(output, 9, encodeDynamicProductInfo(dpi));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 10: out_target_id (repeated string)
|
|
|
+ if (response.getOutTargetIds() != null) {
|
|
|
+ for (String targetId : response.getOutTargetIds()) {
|
|
|
+ output.writeString(10, targetId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 12: target_infos (repeated message)
|
|
|
+ if (response.getTargetInfos() != null) {
|
|
|
+ for (RtaResponseModel.TargetInfoModel ti : response.getTargetInfos()) {
|
|
|
+ writeMessage(output, 12, encodeTargetInfo(ti));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 13: aid_whitelist (repeated uint64, packed)
|
|
|
+ if (response.getAidWhitelist() != null && !response.getAidWhitelist().isEmpty()) {
|
|
|
+ ByteArrayOutputStream packedBaos = new ByteArrayOutputStream();
|
|
|
+ CodedOutputStream packedOut = CodedOutputStream.newInstance(packedBaos);
|
|
|
+ for (Long aid : response.getAidWhitelist()) {
|
|
|
+ packedOut.writeUInt64NoTag(aid);
|
|
|
+ }
|
|
|
+ packedOut.flush();
|
|
|
+ byte[] packedBytes = packedBaos.toByteArray();
|
|
|
+ output.writeTag(13, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
|
|
+ output.writeRawVarint32(packedBytes.length);
|
|
|
+ output.writeRawBytes(packedBytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 14: ad_results (repeated message, 二次请求改价)
|
|
|
+ if (response.getAdResults() != null) {
|
|
|
+ for (RtaResponseModel.AdResultModel ar : response.getAdResults()) {
|
|
|
+ writeMessage(output, 14, encodeAdResult(ar));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 16: fl_model_res_infos (repeated message)
|
|
|
+ if (response.getFlModelResInfos() != null) {
|
|
|
+ for (RtaResponseModel.FLModelResInfoModel fl : response.getFlModelResInfos()) {
|
|
|
+ writeMessage(output, 16, encodeFLModelResInfo(fl));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 17: dsp_tag_str (string)
|
|
|
+ if (response.getDspTagStr() != null && !response.getDspTagStr().isEmpty()) {
|
|
|
+ output.writeString(17, response.getDspTagStr());
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 18: sdpa_dynamic_product_infos (repeated message)
|
|
|
+ if (response.getSdpaDynamicProductInfos() != null) {
|
|
|
+ for (DynamicProductInfoModel sdpa : response.getSdpaDynamicProductInfos()) {
|
|
|
+ writeMessage(output, 18, encodeDynamicProductInfo(sdpa));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ output.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeTargetInfo(RtaResponseModel.TargetInfoModel ti) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+
|
|
|
+ // field 1: out_target_id (string)
|
|
|
+ if (ti.getOutTargetId() != null && !ti.getOutTargetId().isEmpty()) {
|
|
|
+ out.writeString(1, ti.getOutTargetId());
|
|
|
+ }
|
|
|
+ // field 3: cpc_price (uint32)
|
|
|
+ if (ti.getCpcPrice() > 0) {
|
|
|
+ out.writeUInt32(3, ti.getCpcPrice());
|
|
|
+ }
|
|
|
+ // field 4: dynamic_product_infos (repeated message, mDPA 策略级)
|
|
|
+ if (ti.getDynamicProductInfos() != null) {
|
|
|
+ for (DynamicProductInfoModel dpi : ti.getDynamicProductInfos()) {
|
|
|
+ writeMessage(out, 4, encodeDynamicProductInfo(dpi));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // field 5: cpa_price (uint32)
|
|
|
+ if (ti.getCpaPrice() > 0) {
|
|
|
+ out.writeUInt32(5, ti.getCpaPrice());
|
|
|
+ }
|
|
|
+ // field 6: user_weight_factor (float)
|
|
|
+ if (ti.getUserWeightFactor() != 0f) {
|
|
|
+ out.writeFloat(6, ti.getUserWeightFactor());
|
|
|
+ }
|
|
|
+ // field 7: cpc_factor (float)
|
|
|
+ if (ti.getCpcFactor() != 0f) {
|
|
|
+ out.writeFloat(7, ti.getCpcFactor());
|
|
|
+ }
|
|
|
+ // field 8: aid (uint64)
|
|
|
+ if (ti.getAid() > 0) {
|
|
|
+ out.writeUInt64(8, ti.getAid());
|
|
|
+ }
|
|
|
+ // field 9: dsp_tag (uint64)
|
|
|
+ if (ti.getDspTag() > 0) {
|
|
|
+ out.writeUInt64(9, ti.getDspTag());
|
|
|
+ }
|
|
|
+ // field 10: dsp_tag_str (string)
|
|
|
+ if (ti.getDspTagStr() != null && !ti.getDspTagStr().isEmpty()) {
|
|
|
+ out.writeString(10, ti.getDspTagStr());
|
|
|
+ }
|
|
|
+ // field 11: pcvr (float)
|
|
|
+ if (ti.getPcvr() != 0f) {
|
|
|
+ out.writeFloat(11, ti.getPcvr());
|
|
|
+ }
|
|
|
+ // field 12: advertiser_id (uint64)
|
|
|
+ if (ti.getAdvertiserId() > 0) {
|
|
|
+ out.writeUInt64(12, ti.getAdvertiserId());
|
|
|
+ }
|
|
|
+ // field 14: sdpa_product_id (string)
|
|
|
+ if (ti.getSdpaProductId() != null && !ti.getSdpaProductId().isEmpty()) {
|
|
|
+ out.writeString(14, ti.getSdpaProductId());
|
|
|
+ }
|
|
|
+ // field 15: sdpa_product_lib (uint64)
|
|
|
+ if (ti.getSdpaProductLib() > 0) {
|
|
|
+ out.writeUInt64(15, ti.getSdpaProductLib());
|
|
|
+ }
|
|
|
+ // field 17: dynamic_content_infos (repeated message, DCA)
|
|
|
+ if (ti.getDynamicContentInfos() != null) {
|
|
|
+ for (DynamicContentInfoModel dci : ti.getDynamicContentInfos()) {
|
|
|
+ writeMessage(out, 17, encodeDynamicContentInfo(dci));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // field 18: sp_action (message)
|
|
|
+ if (ti.getSpAction() != null) {
|
|
|
+ writeMessage(out, 18, encodeSpAssist(ti.getSpAction()));
|
|
|
+ }
|
|
|
+
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeDynamicProductInfo(DynamicProductInfoModel dpi) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ // field 1: product_lib (uint64)
|
|
|
+ if (dpi.getProductLib() > 0) {
|
|
|
+ out.writeUInt64(1, dpi.getProductLib());
|
|
|
+ }
|
|
|
+ // field 2: products (repeated message)
|
|
|
+ if (dpi.getProducts() != null) {
|
|
|
+ for (DynamicProductInfoModel.Product p : dpi.getProducts()) {
|
|
|
+ writeMessage(out, 2, encodeProduct(p));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeProduct(DynamicProductInfoModel.Product p) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ if (p.getId() > 0) out.writeUInt64(1, p.getId());
|
|
|
+ if (p.getPriority() > 0) out.writeUInt32(2, p.getPriority());
|
|
|
+ if (p.getTimestamp() > 0) out.writeUInt64(3, p.getTimestamp());
|
|
|
+ if (p.getSdpaProductId() != null && !p.getSdpaProductId().isEmpty()) out.writeString(4, p.getSdpaProductId());
|
|
|
+ if (p.getIdStr() != null && !p.getIdStr().isEmpty()) out.writeString(5, p.getIdStr());
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeDynamicContentInfo(DynamicContentInfoModel dci) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ if (dci.getTags() != null) {
|
|
|
+ for (DynamicContentInfoModel.Tag tag : dci.getTags()) {
|
|
|
+ writeMessage(out, 1, encodeTag(tag));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeTag(DynamicContentInfoModel.Tag tag) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(16);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ if (tag.getName() != null && !tag.getName().isEmpty()) out.writeString(1, tag.getName());
|
|
|
+ if (tag.getValue() != null && !tag.getValue().isEmpty()) out.writeString(2, tag.getValue());
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeSpAssist(RtaResponseModel.SpAssistModel sp) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(8);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ if (sp.getType() != 0) out.writeEnum(1, sp.getType());
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeAdResult(RtaResponseModel.AdResultModel ar) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ // field 1: adgroup_id (uint64)
|
|
|
+ if (ar.getAdgroupId() > 0) out.writeUInt64(1, ar.getAdgroupId());
|
|
|
+ // field 2: creative_results (repeated message)
|
|
|
+ if (ar.getCreativeResults() != null) {
|
|
|
+ for (RtaResponseModel.CreativeResultModel cr : ar.getCreativeResults()) {
|
|
|
+ writeMessage(out, 2, encodeCreativeResult(cr));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // field 3: cpc_price (uint32, 广告级)
|
|
|
+ if (ar.getCpcPrice() > 0) out.writeUInt32(3, ar.getCpcPrice());
|
|
|
+ // field 4: ignore (bool)
|
|
|
+ if (ar.isIgnore()) out.writeBool(4, true);
|
|
|
+ // field 5: product_results (repeated message)
|
|
|
+ if (ar.getProductResults() != null) {
|
|
|
+ for (RtaResponseModel.ProductResultModel pr : ar.getProductResults()) {
|
|
|
+ writeMessage(out, 5, encodeProductResult(pr));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // field 6: cpa_price (uint32, 广告级)
|
|
|
+ if (ar.getCpaPrice() > 0) out.writeUInt32(6, ar.getCpaPrice());
|
|
|
+ // field 12: raise_factor_to_tencent (float)
|
|
|
+ if (ar.getRaiseFactorToTencent() > 1.0f) out.writeFloat(12, ar.getRaiseFactorToTencent());
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeCreativeResult(RtaResponseModel.CreativeResultModel cr) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ if (cr.getCreativeId() > 0) out.writeUInt64(1, cr.getCreativeId());
|
|
|
+ if (cr.getCpcPrice() > 0) out.writeUInt32(2, cr.getCpcPrice());
|
|
|
+ if (cr.getOutRaiseFactor() != 0f) out.writeFloat(3, cr.getOutRaiseFactor());
|
|
|
+ if (cr.getCpaPrice() > 0) out.writeUInt32(4, cr.getCpaPrice());
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeProductResult(RtaResponseModel.ProductResultModel pr) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ if (pr.getProductLib() > 0) out.writeUInt64(1, pr.getProductLib());
|
|
|
+ if (pr.getOutProductId() != null && !pr.getOutProductId().isEmpty()) out.writeString(2, pr.getOutProductId());
|
|
|
+ if (pr.getCpcPrice() > 0) out.writeUInt32(3, pr.getCpcPrice());
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] encodeFLModelResInfo(RtaResponseModel.FLModelResInfoModel fl) throws IOException {
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(16);
|
|
|
+ CodedOutputStream out = CodedOutputStream.newInstance(baos);
|
|
|
+ if (fl.getModelId() > 0) out.writeUInt64(1, fl.getModelId());
|
|
|
+ if (fl.getStatus() != 0) out.writeEnum(2, fl.getStatus());
|
|
|
+ out.flush();
|
|
|
+ return baos.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====================== 工具方法 ======================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 写入嵌套 message 字段
|
|
|
+ */
|
|
|
+ private static void writeMessage(CodedOutputStream output, int fieldNumber, byte[] bytes) throws IOException {
|
|
|
+ output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
|
|
+ output.writeRawVarint32(bytes.length);
|
|
|
+ output.writeRawBytes(bytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理 packed repeated 字段(int/uint32/enum/float 等)
|
|
|
+ * 同时兼容 packed 和 non-packed 两种编码方式
|
|
|
+ */
|
|
|
+ private static void decodePacked(CodedInputStream input, int tag, LongConsumer consumer) throws IOException {
|
|
|
+ int wireType = tag & 0x7;
|
|
|
+ if (wireType == WireFormat.WIRETYPE_LENGTH_DELIMITED) {
|
|
|
+ // packed 编码
|
|
|
+ int length = input.readRawVarint32();
|
|
|
+ int limit = input.pushLimit(length);
|
|
|
+ while (input.getBytesUntilLimit() > 0) {
|
|
|
+ consumer.accept(input.readRawVarint32());
|
|
|
+ }
|
|
|
+ input.popLimit(limit);
|
|
|
+ } else {
|
|
|
+ // non-packed 编码
|
|
|
+ consumer.accept(input.readRawVarint32());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @FunctionalInterface
|
|
|
+ private interface LongConsumer {
|
|
|
+ void accept(long value) throws IOException;
|
|
|
+ }
|
|
|
+}
|