Commit d397040c authored by dingjy's avatar dingjy

FS

parents
/.idea/
/target/
FROM harbor.ibreader.com/base/openjdk:8u342-jdk
WORKDIR /app
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
ADD target/dmp-engine.jar app.jar
ENV JAVA_OPTS="-server -Xms2G -Xmx2G -Xmn1G -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:MaxDirectMemorySize=128m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:MaxTenuringThreshold=5 -XX:+ExplicitGCInvokesConcurrent -XX:SurvivorRatio=7 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logservice/dump"
ENTRYPOINT [ "/bin/sh", "-c", "java $JAVA_OPTS -Dspring.profiles.active=$APP_ENV -Djava.security.egd=file:/dev/./urandom -jar app.jar" ]
\ No newline at end of file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lwby</groupId>
<artifactId>aas</artifactId>
<version>0.01</version>
<packaging>jar</packaging>
<name>advertising attribution system</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.19</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.rocksdb</groupId>
<artifactId>rocksdbjni</artifactId>
<version>8.9.1</version>
</dependency>
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.7</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>javax.persistence</groupId>-->
<!-- <artifactId>persistence-api</artifactId>-->
<!-- <version>1.0.2</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.oceanbase</groupId>-->
<!-- <artifactId>oceanbase-client</artifactId>-->
<!-- <version>2.4.7.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.8.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.1</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>aas</finalName>
</build>
<distributionManagement>
<repository>
<id>lwby-releases</id>
<name>releases repository</name>
<url>http://maven.bayread.com/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>lwby-snapshots</id>
<name>snapshots repository</name>
<url>http://maven.bayread.com/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>
package com.lwby.aas;
import com.lwby.aas.vo.ClientInfo;
import org.apache.commons.lang3.StringUtils;
import java.util.function.Function;
public enum DeviceType {
IMEI("imei",(c) -> isNotEmptyAndSNull(c.getImei())?c.getImei():null),
OAID("oaid", (c) -> isNotEmptyAndSNull(c.getOaid())?c.getOaid():null),
IDFA("idfa",(c) -> isNotEmptyAndSNull(c.getIdfa())?c.getIdfa():null),
IP_MODEL("ipmodel",(c) -> isNotEmptyAndSNull(c.getClientIp()) && isNotEmptyAndSNull(c.getPhoneModel())?c.getClientIp().concat(c.getPhoneModel()):null),
IP_UA("ipua",(c) -> isNotEmptyAndSNull(c.getClientIp()) && isNotEmptyAndSNull(c.getUa())?c.getClientIp().concat(StringUtils.substringBefore(c.getUa(), " Chrome/")):null),
IP("ip",(c) -> isNotEmptyAndSNull(c.getClientIp())?c.getClientIp():null);
private String value;
private Function<ClientInfo,String> fun;
DeviceType(String value,Function<ClientInfo,String> fun) {
this.value = value;
this.fun = fun;
}
public String getValue() {
return this.value;
}
public String getDeviceId(ClientInfo clientInfo){
return fun.apply(clientInfo);
}
private static boolean isNotEmptyAndSNull(String str) {
return StringUtils.isNotEmpty(str) && !"null".equals(str);
}
}
\ No newline at end of file
package com.lwby.aas;
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
@EnableCreateCacheAnnotation
@EnableMethodCache(basePackages = "com.lwby.aas")
@SpringBootApplication
@RestController
@EnableAsync
@Slf4j
@Component
public class Main {
@Resource
JdbcTemplate lwbyJdbcTemplate;
// @Resource
// private KafkaTemplate<String, String> kafkaTemplate;
//
// @Resource
// private AttributionTestHandle attributionHandle;
//
// @KafkaListener(topics = "registerMediaEvent1", groupId = "test")
// public void listen(String message) {
// System.out.println("Received Message: " + message);
//
// attributionHandle.handle(message);
//
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// }
@PostConstruct
private void init(){
}
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
SpringApplication.run(Main.class, args);
}
}
\ No newline at end of file
package com.lwby.aas.conf;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class AsyncConfig {
@Value("${core.thread.pool.size:10}")
private Integer corePoolSize;
@Bean
public TaskExecutor commonTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize); // 设置核心线程数
executor.setMaxPoolSize(10); // 设置最大线程数
executor.setQueueCapacity(100); // 设置队列容量
executor.setKeepAliveSeconds(600); // 设置线程活跃时间(秒)
executor.setThreadNamePrefix("AAS-TASK-"); // 设置默认线程名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 设置拒绝策略
executor.setWaitForTasksToCompleteOnShutdown(true); // 等待所有任务结束后再关闭线程池
return executor;
}
}
package com.lwby.aas.conf;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean(name = "lwbyDataSource")
@Primary
@ConfigurationProperties("spring.datasource.lwby")
DataSource lwbyDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "marketingDataSource")
@ConfigurationProperties("spring.datasource.marketing")
DataSource marketingDataSource() {
return DataSourceBuilder.create().build();
}
}
package com.lwby.aas.conf;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class JdbcTemplateConfig {
@Bean(name = "lwbyJdbcTemplate")
JdbcTemplate lwbyJdbc(@Qualifier("lwbyDataSource") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean(name = "marketingJdbcTemplate")
JdbcTemplate marketingJdbc(@Qualifier("marketingDataSource") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}
package com.lwby.aas.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
//@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(stringRedisSerializer);
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
\ No newline at end of file
package com.lwby.aas.handle;
import com.lwby.aas.vo.Action;
public interface AttributionHandle {
public int order();
public Flow handle(Action action);
}
//package com.lwby.aas.handle;
//
//import com.alibaba.fastjson2.JSON;
//import com.alibaba.fastjson2.JSONObject;
//import com.lwby.aas.DeviceType;
//import com.lwby.aas.handle.impl.PrizeSevenUserHandle;
//import com.lwby.aas.vo.BookStoreEvent;
//import com.lwby.aas.vo.ClientInfo;
//import com.lwby.aas.vo.DeliveryDeviceInfo;
//import com.lwby.aas.vo.UserProfile;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.apache.commons.lang3.math.NumberUtils;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.stereotype.Component;
//
//import javax.annotation.Resource;
//import java.util.*;
//
//@Slf4j
//@Component
//public class AttributionTestHandle extends BaseHelper{
// /**
// * 归因商店窗口时间 单位为:毫秒
// */
// @Value("${ascribe.to.store.time:600000}")
// private Long ascribeToStoreTime;
// /**
// * 开启归因商店窗口时间的平台
// */
// @Value("${ascribe.to.store.platformId:51}")
// private String ascribeToStorePlatformIdStr;
//
// @Value("${media.channel.prefix:18,21,22,23}")
// private String mediaChannelPrefixStr;
//
// /**
// * 老用户活跃设备不回传指定平台的账号配置对应的天数,格式:{"平台Id_账号":"天数"},key只有平台Id表示平台默认的天数
// */
// @Value("${daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccount:{}}")
// private String daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccount;
//
// @Resource
// private PrizeSevenUserHandle prizeHandle;
//
// public void handle(String msg) {
// //String str = "{\"bookStoreEvent\":1,\"clientInfo\":{\"channel\":220605094,\"clientIp\":\"171.14.28.126\",\"dID\":\"aad1da5913749ee5\",\"ddid\":\"null\",\"firm\":\"HUAWEI\",\"fixVersion\":12,\"mainVersion\":2,\"oaid\":\"f243f86a-8887-44df-92f3-7efed504b6f7\",\"os\":\"0\",\"phoneModel\":\"NOH-AN00\",\"pkv\":1,\"platformGroupId\":6,\"platformId\":6,\"pm\":\"NOH-AN00\",\"screenSize\":\"1152*2256\",\"sessionid\":\"wnL6tSDnvCjwnLtNJRDK299Z_jQEkUqSj\",\"signVersion\":2,\"subVersion\":27,\"systemVersion\":\"12\",\"ua\":\"Mozilla/5.0 (Linux; Android 12; NOH-AN00 Build/HUAWEINOH-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36\",\"user\":{\"career\":\"\",\"channel\":220605094,\"deviceId\":\"aad1da5913749ee5\",\"experience\":1,\"gender\":\"M\",\"headImage\":\"http://cdn.ibreader.com/group1/M00/56/07/rBH0olunjiKAJCYCAAAQJEqQEcM308.png\",\"id\":133842748,\"isKaiqiUser\":false,\"isMajia\":false,\"isPrivate\":false,\"lastLogin\":1705980630000,\"level\":0,\"mainversion\":2,\"nickname\":\"书友742606\",\"platformId\":6,\"registrationDate\":1705980630000,\"subversion\":27,\"userStatus\":0,\"username\":\"bduXujqDcJbCZD\"},\"version\":\"6.2.27.12.220605094\",\"visitor\":\"wnL6tSDnvCjwnLtNJRDK299Z_jQEkUqSj\",\"xClient\":\"dID=aad1da5913749ee5;os=0;firm=HUAWEI;webVersion=new;version=6.2.27.12.220605094;username=wnL6tSDnvCjwnLtNJRDK299Z_jQEkUqSj;ddid=null;sv=12;pm=NOH-AN00;ss=1152*2256;signVersion=2;androidosv=31;oaid=f243f86a-8887-44df-92f3-7efed504b6f7;pkv=1;\"},\"createTime\":1705980632408,\"extraData\":{},\"id\":\"fd17b7d0-2ec2-4392-8980-170dbdf36e69\"}";
//
//
// BookStoreEvent event = JSONObject.parseObject(msg, BookStoreEvent.class);
// ClientInfo clientInfo = event.getClientInfo();
//
// //ANDROID deviceId
// String deviceId = clientInfo.getDID();
// //平台ID
// Integer platformId = clientInfo.getPlatformId();
//
// //获取用户注册时间
// UserProfile userProfile = clientInfo.getUser();
// if (userProfile == null || userProfile.getRegistrationDate() == null) {
// log.error("ClientInfo.userProfile 对像为[{}],BookStoreEvent报文[{}]、对像[{}]",userProfile, msg, event);
// return;
// }
//
// String encryptType = "original";
// DeviceType deviceType = null;
// DeliveryDeviceInfo deliveryDeviceInfo = null;
// String deviceIdKey = null;
//
// for(DeviceType type:DeviceType.values()){
// deviceIdKey = type.getDeviceId(clientInfo);
// if(null != deviceIdKey && null != (deliveryDeviceInfo = getForRedis(assembleKey(deviceIdKey,platformId)))){
// deviceType = type;
// clientInfo.setOaid(deliveryDeviceInfo.getOaid()); //回补参数
// break;
// }
// }
//
// if (Objects.isNull(deliveryDeviceInfo)) {
// log.error("DeviceId未能匹配到DeliveryDeviceInfo对像, BookStoreEvent报文[{}]", msg);
// return;
// }
//
// //IP归因直接扣量?
// if (DeviceType.IP == deviceType) {
// //summaryByUserChannel(clientInfo, null, 9,deliveryDeviceInfo,matchDeviceId,deviceIdType,encryptType);
// return;
// }
//
// //商业化处理(如果大于7天,算拉新老用户)//TODO;补充说明
//// prizeHandle.handle(userProfile,platformId);
//
// // 投放账户
// String advertiserId = deliveryDeviceInfo.getAdvertiser_id();
// // 获取媒体
// String media = deliveryDeviceInfo.getMedia();
// // 用户ID
// String userId = String.valueOf(userProfile.getId());
// //是否纯新用户(true 是,false 不是.定义:如果是今天注册算纯新用户)
// boolean isRealUser = isCurrentDayTime(userProfile.getRegistrationDate());
// //设置注册时间
// deliveryDeviceInfo.setRegisterTime(DATE_TIME_FORMAT.format(userProfile.getRegistrationDate()));
//
// //TODO;GC
// //false 新用户 true 老用户
// boolean isCheckDateSevenBefore = false;
// //广点通新增150天老用户,走扣量回传逻辑,分子+分母
// boolean isOneFifityUser = false;
// //广点通新增纯新用户,全回传,算到扣量分母
// boolean isRealNewUser = false;
// //广点通两者之间的老用户
// boolean isBetweenUser = false;
//
//
// // 判断是否来自商店下载的包,是则需判断是否在窗口期内,不是的归因到商店 前置,不参与扣量!!!
// //TODO;建议在前置任务报警
// if (ascribeToStore(deliveryDeviceInfo, clientInfo)) {
// //summaryByUserChannel(clientInfo, source, 3,null,matchDeviceId,deviceIdType,encryptType);
// return;
// }
//
//
// //点击渠道
// Integer djChannel = StringUtils.isNotBlank(deliveryDeviceInfo.getDj_channel()) ? Integer.parseInt(deliveryDeviceInfo.getDj_channel()) : null;
//
// boolean isOldUserByalive = false;
//
// //非纯新用户逻辑,判断指定天数内,是否活跃,如果有直接扣量。
// if (!isRealUser) {
// //分平台,分广告主取 活跃天数据,判断老用户是否在活跃天数内
// int daysOfOldUserNoReturnAliveDevice = this.getDaysOfOldUserNoReturnAliveDevice(platformId, advertiserId);
//
// if (daysOfOldUserNoReturnAliveDevice == 0) {
// //TODO;不部不回传?
// return;
// }
//
// // 指定天数相关时间
// long lastestAliveTime = System.currentTimeMillis() - (daysOfOldUserNoReturnAliveDevice * 24 * 60 * 60 * 1000);
// Date lastestAliveDate = new Date(lastestAliveTime);
//
// //查询指定天数内,是否活跃
// Integer aliveCount = getOldUserCountByUserIdAndlatestDate(userId, lastestAliveDate);
// if (aliveCount != null && aliveCount >= 1) {
// //在配置的活跃天数内,不回传,参与扣量
// //如果在指定活跃天数内,不回传,直接参与扣量。
// isOldUserByalive = true;
// }
// }
//
// //是否当天已经激活过一次,没有,则走原来逻辑(扣量+回传),如果有,则表示当天已经激活过一次了,直接跳过
// String _date = DATE_BOTTOM_FORMAT.format(new Date());
// //回传
// String isActiveOneByTodayKey = String.format("v_dec_%s_%d_%s_%s",deviceId,platformId,media,_date);
// //扣量
// String isDeducOneByTodayKey = String.format("v_deduc_%s_%d_%s_%s",deviceId,platformId,media,_date);
//
//
// long expireTime = (getTodayStartTime() - System.currentTimeMillis())/1000;
//
// if (exists(isActiveOneByTodayKey) || exists(isDeducOneByTodayKey)) {
// //如果存在回传或扣量 设置今天过期
//// expire(oaidKey, expireTime);
//// expire(ipAndUaKey, expireTime);
//// expire(ipAndModelKey, expireTime);
//// expire(imeiKey,expireTime);
//
// //直接return,不走扣量
// return;
// } else {
// //计划扣量
//// String acPlanList = accountPlanCallbackService.getAccountPlanCallback();
// String acPlanList = "";
// //扣量回传媒体
// //1 扣量 0 不扣量
// if (deduction(platformId, djChannel, deliveryDeviceInfo.getAd_plan_id(), isRealUser, deviceId, deliveryDeviceInfo.getAdvertiser_id(), isOldUserByalive, isRealNewUser, isBetweenUser, media, acPlanList)) {
// if (isOldUserByalive) {
// //summaryByUserChannel(clientInfo, source, 7, deliveryDeviceInfo, matchDeviceId, deviceIdType, encryptType);
// } else {
// //summaryByUserChannel(clientInfo, source, 4, deliveryDeviceInfo, matchDeviceId, deviceIdType, encryptType);
// }
// set(isDeducOneByTodayKey, 60 * 60 * 24, 1);
//// expire(oaidKey, expireTime);
//// expire(ipAndUaKey, expireTime);
//// expire(ipAndModelKey, expireTime);
//// expire(imeiKey, expireTime);
// //扣量设备存入数据库
// //deductionDeviceFacade.saveDeductionDevice(clientInfo, deliveryDeviceInfo);
// } else {
// //不扣量回传媒体
// //正常上报的删除点击缓存
// //if,deliveryDeviceInfo is not null,deleteredis
// if (Objects.nonNull(deliveryDeviceInfo)) {
//// del(imeiKey);
//// del(oaidKey);
//// del(ipAndUaKey);
//// del(ipAndModelKey);
// }
// //正常上报逻辑
// //registerCallBackFacade.registerCallBack(clientInfo, source, deliveryDeviceInfo, matchDeviceId, deviceIdType, encryptType);
// }
// }
// }
//
//
// /**
// * 校验是否归因到商店 有点击(播放)行为,并且在窗口期内的归信息流,否则归商店
// *
// * @param deliveryDeviceInfo 点击数据
// * @param clientInfo 用户信息
// * @return 是否
// */
// private boolean ascribeToStore(DeliveryDeviceInfo deliveryDeviceInfo, ClientInfo clientInfo) {
// String channelStr = String.valueOf(clientInfo.getChannel());
// // 目前只开放51平台
// String[] ascribeToStorePlatformIdArray = ascribeToStorePlatformIdStr.split(",");
// List<String> ascribeToStorePlatformIdList = Arrays.asList(ascribeToStorePlatformIdArray);
// if (StringUtils.isBlank(channelStr) || !ascribeToStorePlatformIdList.contains(deliveryDeviceInfo.getPlatform_id())) {
// return false;
// }
// // 不符合规则的老渠道号不参与判断
// if (channelStr.length() < 9) {
// log.info("ascribeToStore_false_old_channel: channel:{}", channelStr);
// return false;
// }
// String channelPrefix = channelStr.substring(0, 2);
// String[] mediaChannelPrefixArray = mediaChannelPrefixStr.split(",");
// List<String> mediaChannelPrefixList = Arrays.asList(mediaChannelPrefixArray);
// long timeDifference = System.currentTimeMillis() - deliveryDeviceInfo.getClick_time();
// if (timeDifference > ascribeToStoreTime && !mediaChannelPrefixList.contains(channelPrefix)) {
// return true;
// }
// return false;
// }
//
//
// /**
// * 获取不回传活跃的天数
// *
// * @param platformId 平台Id
// * @param advertiserId 广告账户
// * @return 对应的不回传活跃的天数
// */
// private int getDaysOfOldUserNoReturnAliveDevice(Integer platformId, String advertiserId) {
// JSONObject daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccountJson = JSON.parseObject(
// this.daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccount);
// // 优先匹配平台Id_账户
// String platformIdAndAccountkey = platformId + "_" + advertiserId;
// String daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccount = daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccountJson.getString(
// platformIdAndAccountkey);
// if (NumberUtils.isDigits(daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccount)) {
// return NumberUtils.toInt(daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccount);
// } else {
// // 再次匹配平台Id
// String platformKey = String.valueOf(platformId);
// String daysOfNoReturnAliveDeviceByPlatformId = daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccountJson.getString(platformKey);
// if (NumberUtils.isDigits(daysOfNoReturnAliveDeviceByPlatformId)) {
// return NumberUtils.toInt(daysOfNoReturnAliveDeviceByPlatformId);
// } else {
// log.warn(
// "olduser can`t find the days of no return active device config, platformId={}, advertiserId={}, "
// + "daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccountJson={}",
// platformId, advertiserId, daysOfOldUserNoReturnAliveDeviceByPlatformIdAndAccountJson.toJSONString());
// return 0;
// }
// }
// }
//
// public DeliveryDeviceInfo getForRedis(String key) {
// return null;
// }
//}
package com.lwby.aas.handle;
public enum AttributionType{
CHANNEL("channel"),PLAN("plan"),CROSS_PLATFORM("cross_platform");
private String name;
private AttributionType(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public int select(int channelId,int planId){
switch (this){
case PLAN:
return planId;
case CHANNEL:
return channelId;
}
return -1;
}
}
\ No newline at end of file
package com.lwby.aas.handle;
import cn.hutool.crypto.SecureUtil;
import com.lwby.aas.po.AppChannel;
import com.lwby.aas.vo.DeliveryDeviceInfo;
import org.apache.commons.lang3.time.FastDateFormat;
import org.springframework.jdbc.core.JdbcTemplate;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
public class BaseHelper {
public static final FastDateFormat DATE_TIME_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss", TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
public static final FastDateFormat DATE_BOTTOM_FORMAT = FastDateFormat.getInstance("yyyy_MM_dd", TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
public String assembleKey(String deviceId, int platformId) {
return String.format("getClickByIdfaAndPlatformId:%s:%d", SecureUtil.md5(deviceId), platformId);
}
/**
* 是否为当天时间
*
* @param date1
* @return
*/
public boolean isCurrentDayTime(Date date1) {
Date date2 = new Date();
return date1.getYear() == date2.getYear() && date1.getMonth() == date2.getMonth() && date1.getDate() == date2.getDate();
}
/**
* 时间是否大于7天
*
* @param date
* @return
*/
public boolean isTimeGreaterThanSevenDays(Date date) {
return isTimeGreaterThanDays(date,7);
}
/**
* 时间是否大于指定天数
*
* @param date
* @return
*/
public boolean isTimeGreaterThanDays(Date date,long day) {
return date.before(new Date(System.currentTimeMillis() - 60 * 60 * 24 * day * 1000));
}
/**
* 计算注册时间
* @return
*/
public long calculateDaysBetweenDates(Date startDate, Date endDate) {
return TimeUnit.DAYS.convert(Math.abs(endDate.getTime() - startDate.getTime()), TimeUnit.MILLISECONDS);
}
/**
* 根据渠道ID和平台ID查询渠道配置信息
* @param platformId
* @param channelId
* @return
*/
public AppChannel getAppChannel(JdbcTemplate lwbyJdbcTemplate,int platformId,int channelId){
return lwbyJdbcTemplate.queryForObject(String.format("select * from app_channel where platform_id=%d and channel_id=%d ORDER BY update_time DESC LIMIT 1",platformId,channelId),AppChannel.class);
}
/**
* 获取明天零点时间戳
*
* @return 当天的零点时间戳
*/
public long getTodayStartTime() {
LocalDate tomorrow = LocalDate.now().plusDays(1);
LocalDateTime tomorrowMidnight = LocalDateTime.of(tomorrow, LocalTime.MIDNIGHT);
return tomorrowMidnight.atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli();
}
/**
* REDIS set
*
* @param key
* @param expires 过期时间
* @param o
*/
public void set(String key, int expires, Object o) {
}
/**
* 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
*
* @param key KEY值
* @param seconds 过期时间,单位:秒
*/
public void expire(String key, int seconds) {
}
public boolean exists(String key) {
return false;
}
public <T> T get(Class<T> clazz, String key) {
return null;
}
public Long incrby(String key, int increment, int expireSecond) {
return -1l;
}
public static Long incrby(String key, int increment) {
return -1l;
}
/**
* 根据指定的userId和相对的激活日期来获取对应的激活次数
*
* @param userId userId
* @param lastestAliveDate 最后一次活跃时间
* @return 对应的激活次数
*/
public Integer getOldUserCountByUserIdAndlatestDate(String userId, Date lastestAliveDate) {
return -1;
}
public DeliveryDeviceInfo getForRedis(String key) {
return null;
}
}
package com.lwby.aas.handle;
public enum Flow {
NEXT,END,ERROR
}
\ No newline at end of file
package com.lwby.aas.handle;
import com.alibaba.fastjson2.JSONObject;
import com.lwby.aas.DeviceType;
import com.lwby.aas.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
public class TestHandle extends BaseHelper{
List<AttributionHandle> handleList = new ArrayList<>();
@Resource
ApplicationContext applicationContext;
@PostConstruct
public void load(){
handleList = applicationContext.getBeansOfType(AttributionHandle.class).values().stream()
.sorted(Comparator.comparingInt(AttributionHandle::order).reversed())
.collect(Collectors.toList());
}
public void init(){
String msg = "{\"bookStoreEvent\":1,\"clientInfo\":{\"channel\":220605094,\"clientIp\":\"171.14.28.126\",\"dID\":\"aad1da5913749ee5\",\"ddid\":\"null\",\"firm\":\"HUAWEI\",\"fixVersion\":12,\"mainVersion\":2,\"oaid\":\"f243f86a-8887-44df-92f3-7efed504b6f7\",\"os\":\"0\",\"phoneModel\":\"NOH-AN00\",\"pkv\":1,\"platformGroupId\":6,\"platformId\":6,\"pm\":\"NOH-AN00\",\"screenSize\":\"1152*2256\",\"sessionid\":\"wnL6tSDnvCjwnLtNJRDK299Z_jQEkUqSj\",\"signVersion\":2,\"subVersion\":27,\"systemVersion\":\"12\",\"ua\":\"Mozilla/5.0 (Linux; Android 12; NOH-AN00 Build/HUAWEINOH-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36\",\"user\":{\"career\":\"\",\"channel\":220605094,\"deviceId\":\"aad1da5913749ee5\",\"experience\":1,\"gender\":\"M\",\"headImage\":\"http://cdn.ibreader.com/group1/M00/56/07/rBH0olunjiKAJCYCAAAQJEqQEcM308.png\",\"id\":133842748,\"isKaiqiUser\":false,\"isMajia\":false,\"isPrivate\":false,\"lastLogin\":1705980630000,\"level\":0,\"mainversion\":2,\"nickname\":\"书友742606\",\"platformId\":6,\"registrationDate\":1705980630000,\"subversion\":27,\"userStatus\":0,\"username\":\"bduXujqDcJbCZD\"},\"version\":\"6.2.27.12.220605094\",\"visitor\":\"wnL6tSDnvCjwnLtNJRDK299Z_jQEkUqSj\",\"xClient\":\"dID=aad1da5913749ee5;os=0;firm=HUAWEI;webVersion=new;version=6.2.27.12.220605094;username=wnL6tSDnvCjwnLtNJRDK299Z_jQEkUqSj;ddid=null;sv=12;pm=NOH-AN00;ss=1152*2256;signVersion=2;androidosv=31;oaid=f243f86a-8887-44df-92f3-7efed504b6f7;pkv=1;\"},\"createTime\":1705980632408,\"extraData\":{},\"id\":\"fd17b7d0-2ec2-4392-8980-170dbdf36e69\"}";
BookStoreEvent event = JSONObject.parseObject(msg, BookStoreEvent.class);
ClientInfo clientInfo = event.getClientInfo();
//获取用户注册时间
UserProfile userProfile = clientInfo.getUser();
if (userProfile == null || userProfile.getRegistrationDate() == null) {
log.error("ClientInfo.userProfile 对像为[{}],BookStoreEvent报文[{}]、对像[{}]",userProfile, msg, event);
return;
}
//ANDROID deviceId
String deviceId = clientInfo.getDID();
//平台ID
Integer platformId = clientInfo.getPlatformId();
//设备ID类型
DeviceType deviceType = null;
//设备ID
String deviceIdKey = null;
//VO对像
DeliveryDeviceInfo deliveryDeviceInfo = null;
//匹配设备ID
for(DeviceType type:DeviceType.values()){
deviceIdKey = type.getDeviceId(clientInfo);
if(null != deviceIdKey && null != (deliveryDeviceInfo = getForRedis(assembleKey(deviceIdKey,platformId)))){
deviceType = type;
clientInfo.setOaid(deliveryDeviceInfo.getOaid()); //回补OAID
break;
}
}
if(Objects.isNull(deliveryDeviceInfo)){
//TODO;如果为空归商店
}
if(DeviceType.IP == deviceType){
//TODO;只发BI 不上报逻辑
}
Action action = new Action(clientInfo,deliveryDeviceInfo);
for(AttributionHandle handle:handleList){
if(handle.handle(action) != Flow.NEXT){
break;
}
}
}
public static void main(String[] args) {
new TestHandle().init();
}
}
package com.lwby.aas.handle.impl;
import com.lwby.aas.handle.AttributionHandle;
import com.lwby.aas.handle.BaseHelper;
import com.lwby.aas.handle.Flow;
import com.lwby.aas.vo.Action;
import com.lwby.aas.vo.ClientInfo;
import com.lwby.aas.vo.DeliveryDeviceInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@Component
public class BeforeHandle extends BaseHelper implements AttributionHandle {
@Override
public int order() {
return 8;
}
@Override
public Flow handle(Action action) {
DeliveryDeviceInfo dd = action.getDeliveryDeviceInfo();
ClientInfo ci = action.getClientInfo();
action.setPlatformId(ci.getPlatformId());
action.setUserId(ci.getUser().getId());
action.setChannelId(StringUtils.isNotBlank(dd.getDj_channel()) ? Integer.parseInt(dd.getDj_channel()) : -1);
action.setPlanId(StringUtils.isNotBlank(dd.getAd_plan_id()) ? Integer.parseInt(dd.getAd_plan_id()) : -1);
action.setAdvertiserId(dd.getAdvertiser_id());
action.setDeviceId(ci.getDID());
action.setMedia(dd.getMedia());
action.setNewUser(isCurrentDayTime(ci.getUser().getRegistrationDate()));
action.setRegistrationDate(ci.getUser().getRegistrationDate());
return Flow.NEXT;
}
}
package com.lwby.aas.handle.impl;
import com.lwby.aas.handle.AttributionHandle;
import com.lwby.aas.handle.AttributionType;
import com.lwby.aas.handle.BaseHelper;
import com.lwby.aas.handle.Flow;
import com.lwby.aas.po.AppChannel;
import com.lwby.aas.vo.Action;
import com.lwby.aas.vo.ClientInfo;
import com.lwby.aas.vo.DeliveryDeviceInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.concurrent.ThreadLocalRandom;
@Component
public class ChannelAttributionHandle extends BaseHelper implements AttributionHandle {
@Resource
JdbcTemplate marketingJdbcTemplate;
@Resource
JdbcTemplate lwbyJdbcTemplate;
@Override
public int order() {
return 0;
}
@Override
public Flow handle(Action action) {
return handle0(action, AttributionType.CHANNEL);
}
public Flow handle0(Action action,AttributionType type) {
//是否是当天注册用户
boolean isNewUser = isCurrentDayTime(action.getRegistrationDate());
//获取满意配置
AppChannel appChannel = getAppChannel(lwbyJdbcTemplate,action.getPlatformId(), action.getChannelId());
//TODO;应该是等于空 或 = 100,如果空怎么办
if (appChannel != null && appChannel.getSprDedu() == 100) {
//TODO;扣量
return Flow.END;
}
//如果是20 就是回传 20
Integer sprDedu = appChannel.getSprDedu();
String dateStr = DATE_BOTTOM_FORMAT.format(new Date());
//总数
String channelTotal = String.format("%s_total_%d_%d_%d_%s",type.getName(), action.getPlatformId(), type.select(action.getChannelId(),action.getPlanId()), sprDedu, dateStr);
//回传
String channelCallback = String.format("%s_callback_%d_%d_%d_%s",type.getName(), action.getPlatformId(), type.select(action.getChannelId(),action.getPlanId()), sprDedu, dateStr);
double percent = sprDedu.doubleValue();
long channelTotalCount = incrby(channelTotal, 0, 60 * 60 * 24);
long channelCallbackCount = incrby(channelCallback, 0, 60 * 60 * 24);
//TODO;判断1-30天内是否活跃老用户,如果是则 100% 扣量;
if(!isNewUser && isAliveByDay(action.getUserId(),30)){
incrby(channelTotal, 1);
//TODO;扣量
return Flow.END;
}
//首次随机
if(0 == channelTotalCount){
incrby(channelTotal, 1);
if (ThreadLocalRandom.current().nextInt(1, 3) == 1) {
//回传个数 ++
incrby(channelCallback, 1);
//TODO;回传事件
return Flow.END;
}
}else{
//计算%
BigDecimal divide = new BigDecimal(channelCallbackCount).divide(new BigDecimal(channelTotalCount == 0 ? 1 : channelTotalCount), 4, RoundingMode.HALF_UP);
//总数 ++
incrby(channelTotal, 1);
// 扣量
if (divide.compareTo(new BigDecimal(percent).setScale(4, RoundingMode.HALF_UP)) <= 0) {
//TODO;回传事件
incrby(channelCallback, 1);
return Flow.END;
}
}
//TODO;扣量
return Flow.END;
}
/**
* 用户在指定天数内是否有活跃
* @param userId
* @param day
* @return
*/
public boolean isAliveByDay(long userId, int day){
return marketingJdbcTemplate.queryForMap(String.format("select 1 from lwby_marketing_growth.alive_olduser where user_id ='%d' and last_alive_date >= CURDATE() - INTERVAL %d DAY",userId,day)).size() > 0;
}
}
\ No newline at end of file
package com.lwby.aas.handle.impl;
import com.alicp.jetcache.anno.CacheRefresh;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.lwby.aas.handle.AttributionHandle;
import com.lwby.aas.handle.AttributionType;
import com.lwby.aas.handle.BaseHelper;
import com.lwby.aas.handle.Flow;
import com.lwby.aas.po.AppChannel;
import com.lwby.aas.po.CrossCallback;
import com.lwby.aas.vo.Action;
import com.lwby.aas.vo.ClientInfo;
import com.lwby.aas.vo.DeliveryDeviceInfo;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
@Component
public class CrossPlatformAttributionHandle extends BaseHelper implements AttributionHandle {
@Resource
JdbcTemplate lwbyJdbcTemplate;
@Override
public int order() {
return 4;
}
@Override
public Flow handle(Action action) {
//获取跨平台帐号配置
CrossPlatformAccount cpa = getCrossPlatformMedia().get(action.getMedia());
//判断是否配置跨平台归因,如果没有则继续执行下一个计划归因处理器
if(Objects.isNull(cpa) || !cpa.getAccount().contains(action.getAdvertiserId())){
return Flow.NEXT;
}
//返回注册时间
long daysSinceRegistration = 0;
// 如果是当天注册新用户,则检查跨平台是否注册过,如果注册过,取最早注册时间做判断。
if (isCurrentDayTime(action.getRegistrationDate())) {
daysSinceRegistration = calculateDaysBetweenDates(getEarliestRegistrationTimeByDeviceId(action.getDeviceId()), new Date());
}
if(daysSinceRegistration <= cpa.spanCheckMaxDay){
// 纯新用户逻辑
//TODO;回传
return Flow.END;
} else if (daysSinceRegistration <= 150) {
// 中间用户逻辑 (注册时间大于 cpa.spanCheckMaxDay 且小于 150 天)
//TODO;直接扣量
} else {
// 老用户逻辑 (注册时间大于等于 150 天)
AppChannel appChannel = getAppChannel(lwbyJdbcTemplate,action.getPlatformId(), action.getChannelId());
//TODO;应该是等于空 或 = 100,如果空怎么办
if (appChannel != null && appChannel.getSprDedu() == 100) {
//TODO;回传
return Flow.END;
}
//如果是20 就是回传 20
Integer sprDedu = appChannel.getSprDedu();
String dateStr = DATE_BOTTOM_FORMAT.format(new Date());
//总数
String channelTotal = String.format("%s_total_%d_%d_%d_%s",AttributionType.CROSS_PLATFORM, action.getPlatformId(), action.getChannelId(), sprDedu, dateStr);
//回传
String channelCallback = String.format("%s_callback_%d_%d_%d_%s", AttributionType.CROSS_PLATFORM, action.getPlatformId(), action.getChannelId(), sprDedu, dateStr);
double percent = sprDedu.doubleValue();
long channelTotalCount = incrby(channelTotal, 0, 60 * 60 * 24);
long channelCallbackCount = incrby(channelCallback, 0, 60 * 60 * 24);
//首次随机
if(0 == channelTotalCount){
incrby(channelTotal, 1);
if (ThreadLocalRandom.current().nextInt(1, 3) == 1) {
//回传个数 ++
incrby(channelCallback, 1);
//TODO;回传事件
return Flow.END;
}
}else{
//计算%
BigDecimal divide = new BigDecimal(channelCallbackCount).divide(new BigDecimal(channelTotalCount == 0 ? 1 : channelTotalCount), 4, RoundingMode.HALF_UP);
//总数 ++
incrby(channelTotal, 1);
// 扣量
if (divide.compareTo(new BigDecimal(percent).setScale(4, RoundingMode.HALF_UP)) <= 0) {
//TODO;回传事件
incrby(channelCallback, 1);
return Flow.END;
}
}
//TODO;扣量
return Flow.END;
}
return Flow.END;
}
/**
* 获取跨包配置
* @return
*/
@Cached(name="cross_platform_account", cacheType = CacheType.LOCAL)
@CacheRefresh(refresh = 300)
public Map<String,CrossPlatformAccount> getCrossPlatformMedia(){
List<CrossCallback> ls = lwbyJdbcTemplate.queryForList("select id,span_check_max_day,new_account,old_account,media_name from cross_callback", CrossCallback.class);
return ls.stream()
.collect(Collectors.toMap(CrossCallback::getMediaName, CrossPlatformAccount::new));
}
/**
* 获取设备ID的最早注册时间
* @return
*/
public Date getEarliestRegistrationTimeByDeviceId(String deviceId){
Date date = lwbyJdbcTemplate.queryForObject(String.format("select min(registration_date) from user_profiles where device_id = '%s'",deviceId),Date.class);
return Objects.isNull(date)?new Date():date;
}
@Data
static class CrossPlatformAccount {
/**
* 媒体名称
*/
private String mediaName;
/**
* 跨包纯新用户最大配置天数
*/
private Integer spanCheckMaxDay;
/**
* 跨平台帐号
*/
private Set<String> account;
public CrossPlatformAccount(CrossCallback ccb){
setMediaName(ccb.getMediaName());
setSpanCheckMaxDay(ccb.getSpanCheckMaxDay());
setAccount(Arrays.stream(ccb.getNewAccount().split(","))
.map(String::trim)
.filter(_s -> _s.matches("\\d+"))
.collect(Collectors.toSet()));
}
}
}
\ No newline at end of file
package com.lwby.aas.handle.impl;
import com.alicp.jetcache.anno.CacheRefresh;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.lwby.aas.handle.AttributionType;
import com.lwby.aas.handle.Flow;
import com.lwby.aas.vo.Action;
import com.lwby.aas.vo.ClientInfo;
import com.lwby.aas.vo.DeliveryDeviceInfo;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class PlanAttributionHandle extends ChannelAttributionHandle{
@Override
public int order() {
return 2;
}
@Override
public Flow handle(Action action) {
//如果投放帐号没有配置计划归因,继续执行下一个渠道归因处理器
if(!getPlanAccount().contains(action.getAdvertiserId())){
return Flow.NEXT;
}
return this.handle0(action, AttributionType.PLAN);
}
@Cached(name="plan_account", cacheType = CacheType.LOCAL)
@CacheRefresh(refresh = 300)
public Set<String> getPlanAccount(){
List<String> ls = lwbyJdbcTemplate.queryForList("select account from account_plan_callback",String.class);
return ls.stream()
.flatMap(e -> Arrays.stream(e.split(",")))
.map(String::trim)
.filter(_s -> _s.matches("\\d+"))
.collect(Collectors.toSet());
}
}
\ No newline at end of file
package com.lwby.aas.handle.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.lwby.aas.handle.AttributionHandle;
import com.lwby.aas.handle.BaseHelper;
import com.lwby.aas.handle.Flow;
import com.lwby.aas.vo.Action;
import com.lwby.aas.vo.ClientInfo;
import com.lwby.aas.vo.DeliveryDeviceInfo;
import com.lwby.aas.vo.UserProfile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Objects;
@Component
public class PrizeSevenUserHandle extends BaseHelper implements AttributionHandle {
@Value("${platformPrizeExpire:{}}")
private String platformPrizeExpire;
@Override
public int order() {
return 4;
}
@Override
public Flow handle(Action action) {
if(isTimeGreaterThanSevenDays(action.getRegistrationDate())){
//代码有问题
JSONObject platformPrizeExpireJson = JSON.parseObject(platformPrizeExpire);
Integer platformPrizeExpireStr = platformPrizeExpireJson.getInteger(String.valueOf(action.getPlatformId()));
String prizeAssembleKey = "c:ouser:";
if (Objects.nonNull(platformPrizeExpireStr)) {
//商业化老用户大奖缓存
set(prizeAssembleKey.concat(String.valueOf(action.getUserId())), platformPrizeExpireStr, 1);
} else {
//商业化老用户大奖缓存3天
set(prizeAssembleKey.concat(String.valueOf(action.getUserId())), 60 * 60 * 24 * 3, 1);
}
}
return Flow.NEXT;
}
}
package com.lwby.aas.po;
import lombok.Data;
@Data
public class AppChannel {
private Integer id;
/**
* 合作方
*/
private String cpname;
/**
* 渠道名
*/
private String channel;
/**
* 渠道号
*/
private String channelId;
/**
* 合作方式
*/
private String type;
/**
* 平台id
*/
private Integer platformId;
/**
* 包:1=必看小说标准版 2=广告版
*/
private Integer shadowPackage;
/**
* 1 = s 安全 2 = g 普通 3 = h 大尺度
*/
private Integer level;
/**
* 当前渠道收费章节,不填从默认章节收费
*/
private Integer chargeChapterNum;
/**
* 0=显示原书名 1=显示多媒体书名
*/
private Integer isSubTitle;
/**
* 0按渠道收费章节 1按书籍收费章节
*/
private Integer newUserSwitch;
/**
* 是否直接广告订阅
*/
private Integer autoAd;
/**
* 推广扣量默认不扣量
*/
private Integer sprDedu;
/**
*
*/
private String productLine;
/**
* apk链接
*/
private String apkUrl;
/**
* 书号
*/
private Integer bookId;
/**
* 章节数
*/
private Integer chapterNum;
/**
* 授权模式(1.授权模式,2非授权模式)
*/
private Integer authType;
/**
* 链接类型(1 http 2.hap)
*/
private Integer linkType;
private static final long serialVersionUID = 1L;
/**
* 媒体
*/
private String mediaName;
/**
* 代理
*/
private String proxyName;
}
package com.lwby.aas.po;
import lombok.Data;
@Data
public class CrossCallback {
private Integer id;
/**
* 跨包纯新用户最大配置天数
*/
private Integer spanCheckMaxDay;
/**
* 灰度渠道(走新的逻辑)
*/
private String newAccount;
/**
* 灰度渠道(走老的逻辑)
*/
private String oldAccount;
/**
* 媒体
*/
private String mediaName;
}
package com.lwby.aas.util;
import java.nio.charset.Charset;
/**
* 字节数组转换工具类
*/
public class BytesUtils {
public static final String GBK = "GBK";
public static final String UTF8 = "utf-8";
public static final char[] ascii = "0123456789ABCDEF".toCharArray();
private static char[] HEX_VOCABLE = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
* 将short整型数值转换为字节数组
*
* @param data
* @return
*/
public static byte[] getBytes(short data) {
byte[] bytes = new byte[2];
bytes[0] = (byte) ((data & 0xff00) >> 8);
bytes[1] = (byte) (data & 0xff);
return bytes;
}
/**
* 将字符转换为字节数组
*
* @param data
* @return
*/
public static byte[] getBytes(char data) {
byte[] bytes = new byte[2];
bytes[0] = (byte) (data >> 8);
bytes[1] = (byte) (data);
return bytes;
}
/**
* 将布尔值转换为字节数组
*
* @param data
* @return
*/
public static byte[] getBytes(boolean data) {
byte[] bytes = new byte[1];
bytes[0] = (byte) (data ? 1 : 0);
return bytes;
}
/**
* 将整型数值转换为字节数组
*
* @param data
* @return
*/
public static byte[] getBytes(int data) {
byte[] bytes = new byte[4];
bytes[0] = (byte) ((data & 0xff000000) >> 24);
bytes[1] = (byte) ((data & 0xff0000) >> 16);
bytes[2] = (byte) ((data & 0xff00) >> 8);
bytes[3] = (byte) (data & 0xff);
return bytes;
}
/**
* 将long整型数值转换为字节数组
*
* @param data
* @return
*/
public static byte[] getBytes(long data) {
byte[] bytes = new byte[8];
bytes[0] = (byte) ((data >> 56) & 0xff);
bytes[1] = (byte) ((data >> 48) & 0xff);
bytes[2] = (byte) ((data >> 40) & 0xff);
bytes[3] = (byte) ((data >> 32) & 0xff);
bytes[4] = (byte) ((data >> 24) & 0xff);
bytes[5] = (byte) ((data >> 16) & 0xff);
bytes[6] = (byte) ((data >> 8) & 0xff);
bytes[7] = (byte) (data & 0xff);
return bytes;
}
/**
* 将float型数值转换为字节数组
*
* @param data
* @return
*/
public static byte[] getBytes(float data) {
int intBits = Float.floatToIntBits(data);
return getBytes(intBits);
}
/**
* 将double型数值转换为字节数组
*
* @param data
* @return
*/
public static byte[] getBytes(double data) {
long intBits = Double.doubleToLongBits(data);
return getBytes(intBits);
}
/**
* 将字符串按照charsetName编码格式的字节数组
*
* @param data
* 字符串
* @param charsetName
* 编码格式
* @return
*/
public static byte[] getBytes(String data, String charsetName) {
Charset charset = Charset.forName(charsetName);
return data.getBytes(charset);
}
/**
* 将字符串按照GBK编码格式的字节数组
*
* @param data
* @return
*/
public static byte[] getBytes(String data) {
return getBytes(data, GBK);
}
/**
* 将字节数组第0字节转换为布尔值
*
* @param bytes
* @return
*/
public static boolean getBoolean(byte[] bytes) {
return bytes[0] == 1;
}
/**
* 将字节数组的第index字节转换为布尔值
*
* @param bytes
* @param index
* @return
*/
public static boolean getBoolean(byte[] bytes, int index) {
return bytes[index] == 1;
}
/**
* 将字节数组前2字节转换为short整型数值
*
* @param bytes
* @return
*/
public static short getShort(byte[] bytes) {
return (short) ((0xff00 & (bytes[0] << 8)) | (0xff & bytes[1]));
}
/**
* 将字节数组从startIndex开始的2个字节转换为short整型数值
*
* @param bytes
* @param startIndex
* @return
*/
public static short getShort(byte[] bytes, int startIndex) {
return (short) ((0xff00 & (bytes[startIndex] << 8)) | (0xff & bytes[startIndex + 1]));
}
/**
* 将字节数组前2字节转换为字符
*
* @param bytes
* @return
*/
public static char getChar(byte[] bytes) {
return (char) ((0xff00 & (bytes[0] << 8)) | (0xff & bytes[1]));
}
/**
* 将字节数组从startIndex开始的2个字节转换为字符
*
* @param bytes
* @param startIndex
* @return
*/
public static char getChar(byte[] bytes, int startIndex) {
return (char) ((0xff00 & (bytes[startIndex] << 8)) | (0xff & bytes[startIndex + 1]));
}
/**
* 将字节数组前4字节转换为整型数值
*
* @param bytes
* @return
*/
public static int getInt(byte[] bytes) {
return (0xff000000 & (bytes[0] << 24) | (0xff0000 & (bytes[1] << 16))
| (0xff00 & (bytes[2] << 8)) | (0xff & bytes[3]));
}
/**
* 将字节数组从startIndex开始的4个字节转换为整型数值
*
* @param bytes
* @param startIndex
* @return
*/
public static int getInt(byte[] bytes, int startIndex) {
return (0xff000000 & (bytes[startIndex] << 24)
| (0xff0000 & (bytes[startIndex + 1] << 16))
| (0xff00 & (bytes[startIndex + 2] << 8)) | (0xff & bytes[startIndex + 3]));
}
/**
* 将字节数组前8字节转换为long整型数值
*
* @param bytes
* @return
*/
public static long getLong(byte[] bytes) {
return (0xff00000000000000L & ((long) bytes[0] << 56)
| (0xff000000000000L & ((long) bytes[1] << 48))
| (0xff0000000000L & ((long) bytes[2] << 40))
| (0xff00000000L & ((long) bytes[3] << 32))
| (0xff000000L & ((long) bytes[4] << 24))
| (0xff0000L & ((long) bytes[5] << 16))
| (0xff00L & ((long) bytes[6] << 8)) | (0xffL & (long) bytes[7]));
}
/**
* 将字节数组从startIndex开始的8个字节转换为long整型数值
*
* @param bytes
* @param startIndex
* @return
*/
public static long getLong(byte[] bytes, int startIndex) {
return (0xff00000000000000L & ((long) bytes[startIndex] << 56)
| (0xff000000000000L & ((long) bytes[startIndex + 1] << 48))
| (0xff0000000000L & ((long) bytes[startIndex + 2] << 40))
| (0xff00000000L & ((long) bytes[startIndex + 3] << 32))
| (0xff000000L & ((long) bytes[startIndex + 4] << 24))
| (0xff0000L & ((long) bytes[startIndex + 5] << 16))
| (0xff00L & ((long) bytes[startIndex + 6] << 8)) | (0xffL & (long) bytes[startIndex + 7]));
}
/**
* 将字节数组前4字节转换为float型数值
*
* @param bytes
* @return
*/
public static float getFloat(byte[] bytes) {
return Float.intBitsToFloat(getInt(bytes));
}
/**
* 将字节数组从startIndex开始的4个字节转换为float型数值
*
* @param bytes
* @param startIndex
* @return
*/
public static float getFloat(byte[] bytes, int startIndex) {
byte[] result = new byte[4];
System.arraycopy(bytes, startIndex, result, 0, 4);
return Float.intBitsToFloat(getInt(result));
}
/**
* 将字节数组前8字节转换为double型数值
*
* @param bytes
* @return
*/
public static double getDouble(byte[] bytes) {
long l = getLong(bytes);
return Double.longBitsToDouble(l);
}
/**
* 将字节数组从startIndex开始的8个字节转换为double型数值
*
* @param bytes
* @param startIndex
* @return
*/
public static double getDouble(byte[] bytes, int startIndex) {
byte[] result = new byte[8];
System.arraycopy(bytes, startIndex, result, 0, 8);
long l = getLong(result);
return Double.longBitsToDouble(l);
}
/**
* 将charsetName编码格式的字节数组转换为字符串
*
* @param bytes
* @param charsetName
* @return
*/
public static String getString(byte[] bytes, String charsetName) {
return new String(bytes, Charset.forName(charsetName));
}
/**
* 将GBK编码格式的字节数组转换为字符串
*
* @param bytes
* @return
*/
public static String getString(byte[] bytes) {
return getString(bytes, GBK);
}
/**
* 将16进制字符串转换为字节数组
*
* @param hex
* @return
*/
public static byte[] hexStringToBytes(String hex) {
if (hex == null || "".equals(hex)) {
return null;
}
int len = hex.length() / 2;
byte[] result = new byte[len];
char[] chArr = hex.toCharArray();
for (int i = 0; i < len; i++) {
int pos = i * 2;
result[i] = (byte) (toByte(chArr[pos]) << 4 | toByte(chArr[pos + 1]));
}
return result;
}
/**
* 将BCD编码的字节数组转换为字符串
*
* @param bcds
* @return
*/
public static String bcdToString(byte[] bcds) {
if (bcds == null || bcds.length == 0) {
return null;
}
byte[] temp = new byte[2 * bcds.length];
for (int i = 0; i < bcds.length; i++) {
temp[i * 2] = (byte) ((bcds[i] >> 4) & 0x0f);
temp[i * 2 + 1] = (byte) (bcds[i] & 0x0f);
}
StringBuffer res = new StringBuffer();
for (int i = 0; i < temp.length; i++) {
res.append(ascii[temp[i]]);
}
return res.toString();
}
/**
* 字节转整形
* @param value
* @return
*/
public static int bcdToInt(byte value){
return ((value>>4) * 10) + (value&0x0F);
}
/**
* 字节数组转16进制字符串
* @param bs
* @return
*/
public static String bytesToHex(byte[] bs) {
StringBuilder sb = new StringBuilder();
for (byte b : bs) {
int high = (b >> 4) & 0x0f;
int low = b & 0x0f;
sb.append(HEX_VOCABLE[high]);
sb.append(HEX_VOCABLE[low]);
}
return sb.toString();
}
/**
* 字节数组取前len个字节转16进制字符串
* @param bs
* @param len
* @return
*/
public static String bytesToHex(byte[] bs, int len) {
StringBuilder sb = new StringBuilder();
for (int i=0; i<len; i++ ) {
byte b = bs[i];
int high = (b >> 4) & 0x0f;
int low = b & 0x0f;
sb.append(HEX_VOCABLE[high]);
sb.append(HEX_VOCABLE[low]);
}
return sb.toString();
}
/**
* 字节数组偏移offset长度之后的取len个字节转16进制字符串
* @param bs
* @param offset
* @param len
* @return
*/
public static String bytesToHex(byte[] bs, int offset, int len) {
StringBuilder sb = new StringBuilder();
for (int i=0; i<len; i++ ) {
byte b = bs[offset + i];
int high = (b >> 4) & 0x0f;
int low = b & 0x0f;
sb.append(HEX_VOCABLE[high]);
sb.append(HEX_VOCABLE[low]);
}
return sb.toString();
}
/**
* 字节数组转16进制字符串
* @return
*/
public static String byteToHex(byte b) {
StringBuilder sb = new StringBuilder();
int high = (b >> 4) & 0x0f;
int low = b & 0x0f;
sb.append(HEX_VOCABLE[high]);
sb.append(HEX_VOCABLE[low]);
return sb.toString();
}
/**
* 将字节数组取反
*
* @param src
* @return
*/
public static String negate(byte[] src) {
if (src == null || src.length == 0) {
return null;
}
byte[] temp = new byte[2 * src.length];
for (int i = 0; i < src.length; i++) {
byte tmp = (byte) (0xFF ^ src[i]);
temp[i * 2] = (byte) ((tmp >> 4) & 0x0f);
temp[i * 2 + 1] = (byte) (tmp & 0x0f);
}
StringBuffer res = new StringBuffer();
for (int i = 0; i < temp.length; i++) {
res.append(ascii[temp[i]]);
}
return res.toString();
}
/**
* 比较字节数组是否相同
*
* @param a
* @param b
* @return
*/
public static boolean compareBytes(byte[] a, byte[] b) {
if (a == null || a.length == 0 || b == null || b.length == 0
|| a.length != b.length) {
return false;
}
if (a.length == b.length) {
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
} else {
return false;
}
return true;
}
/**
* 只比对指定长度byte
* @param a
* @param b
* @param len
* @return
*/
public static boolean compareBytes(byte[] a, byte[] b, int len) {
if (a == null || a.length == 0 || b == null || b.length == 0
|| a.length < len || b.length < len) {
return false;
}
for (int i = 0; i < len; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
/**
* 将字节数组转换为二进制字符串
*
* @param items
* @return
*/
public static String bytesToBinaryString(byte[] items) {
if (items == null || items.length == 0) {
return null;
}
StringBuffer buf = new StringBuffer();
for (byte item : items) {
buf.append(byteToBinaryString(item));
}
return buf.toString();
}
/**
* 将字节转换为二进制字符串
*
* @return
*/
public static String byteToBinaryString(byte item) {
byte a = item;
StringBuffer buf = new StringBuffer();
for (int i = 0; i < 8; i++) {
buf.insert(0, a % 2);
a = (byte) (a >> 1);
}
return buf.toString();
}
/**
* 对数组a,b进行异或运算
*
* @param a
* @param b
* @return
*/
public static byte[] xor(byte[] a, byte[] b) {
if (a == null || a.length == 0 || b == null || b.length == 0
|| a.length != b.length) {
return null;
}
byte[] result = new byte[a.length];
for (int i = 0; i < a.length; i++) {
result[i] = (byte) (a[i] ^ b[i]);
}
return result;
}
/**
* 对数组a,b进行异或运算 运算长度len
* @param a
* @param b
* @param len
* @return
*/
public static byte[] xor(byte[] a, byte[] b, int len) {
if (a == null || a.length == 0 || b == null || b.length == 0) {
return null;
}
if (a.length < len || b.length < len){
return null;
}
byte[] result = new byte[len];
for (int i = 0; i < len; i++) {
result[i] = (byte) (a[i] ^ b[i]);
}
return result;
}
/**
* 将short整型数值转换为字节数组
*
* @param num
* @return
*/
public static byte[] shortToBytes(int num) {
byte[] temp = new byte[2];
for (int i = 0; i < 2; i++) {
temp[i] = (byte) ((num >>> (8 - i * 8)) & 0xFF);
}
return temp;
}
/**
* 将字节数组转为整型
*
* @return
*/
public static int bytesToShort(byte[] arr) {
int mask = 0xFF;
int temp = 0;
int result = 0;
for (int i = 0; i < 2; i++) {
result <<= 8;
temp = arr[i] & mask;
result |= temp;
}
return result;
}
/**
* 将整型数值转换为指定长度的字节数组
*
* @param num
* @return
*/
public static byte[] intToBytes(int num) {
byte[] temp = new byte[4];
for (int i = 0; i < 4; i++) {
temp[i] = (byte) ((num >>> (24 - i * 8)) & 0xFF);
}
return temp;
}
/**
* 将整型数值转换为指定长度的字节数组
*
* @param src
* @param len
* @return
*/
public static byte[] intToBytes(int src, int len) {
if (len < 1 || len > 4) {
return null;
}
byte[] temp = new byte[len];
for (int i = 0; i < len; i++) {
temp[len - 1 - i] = (byte) ((src >>> (8 * i)) & 0xFF);
}
return temp;
}
/**
* 将字节数组转换为整型数值
*
* @param arr
* @return
*/
public static int bytesToInt(byte[] arr) {
int mask = 0xFF;
int temp = 0;
int result = 0;
for (int i = 0; i < 4; i++) {
result <<= 8;
temp = arr[i] & mask;
result |= temp;
}
return result;
}
/**
* 将long整型数值转换为字节数组
*
* @param num
* @return
*/
public static byte[] longToBytes(long num) {
byte[] temp = new byte[8];
for (int i = 0; i < 8; i++) {
temp[i] = (byte) ((num >>> (56 - i * 8)) & 0xFF);
}
return temp;
}
/**
* 将字节数组转换为long整型数值
*
* @param arr
* @return
*/
public static long bytesToLong(byte[] arr) {
int mask = 0xFF;
int temp = 0;
long result = 0;
int len = Math.min(8, arr.length);
for (int i = 0; i < len; i++) {
result <<= 8;
temp = arr[i] & mask;
result |= temp;
}
return result;
}
/**
* 将16进制字符转换为字节
*
* @param c
* @return
*/
public static byte toByte(char c) {
byte b = (byte) "0123456789ABCDEF".indexOf(c);
return b;
}
/**
* 功能描述:把两个字节的字节数组转化为整型数据,高位补零,例如:<br/>
* 有字节数组byte[] data = new byte[]{1,2};转换后int数据的字节分布如下:<br/>
* 00000000 00000000 00000001 00000010,函数返回258
* @param lenData 需要进行转换的字节数组
* @return 字节数组所表示整型值的大小
*/
public static int bytesToIntWhereByteLengthEquals2(byte lenData[]) {
if(lenData.length != 2){
return -1;
}
byte fill[] = new byte[]{0,0};
byte real[] = new byte[4];
System.arraycopy(fill, 0, real, 0, 2);
System.arraycopy(lenData, 0, real, 2, 2);
int len = byteToInt(real);
return len;
}
/**
* 功能描述:将byte数组转化为int类型的数据
* @param byteVal 需要转化的字节数组
* @return 字节数组所表示的整型数据
*/
public static int byteToInt(byte[] byteVal) {
int result = 0;
for(int i = 0;i < byteVal.length;i++) {
int tmpVal = (byteVal[i]<<(8*(3-i)));
switch(i) {
case 0:
tmpVal = tmpVal & 0xFF000000;
break;
case 1:
tmpVal = tmpVal & 0x00FF0000;
break;
case 2:
tmpVal = tmpVal & 0x0000FF00;
break;
case 3:
tmpVal = tmpVal & 0x000000FF;
break;
}
result = result | tmpVal;
}
return result;
}
public static byte CheckXORSum(byte[] bData){
byte sum = 0x00;
for (int i = 0; i < bData.length; i++) {
sum ^= bData[i];
}
return sum;
}
/**
* 从offset开始 将后续长度为len的byte字节转为int
* @param data
* @param offset
* @param len
* @return
*/
public static int bytesToInt(byte[] data, int offset, int len){
int mask = 0xFF;
int temp = 0;
int result = 0;
len = Math.min(len, 4);
for (int i = 0; i < len; i++) {
result <<= 8;
temp = data[offset + i] & mask;
result |= temp;
}
return result;
}
/**
* byte字节数组中的字符串的长度
* @param data
* @return
*/
public static int getBytesStringLen(byte[] data)
{
int count = 0;
for (byte b : data) {
if(b == 0x00)
break;
count++;
}
return count;
}
}
\ No newline at end of file
package com.lwby.aas.util;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
/**
* @author martin.ad
* @Description: 非线程安全工具类,且把buf曝露在外不安全,只为内存复用和高性能,慎用。
* @date 创建时间: 2022/11/17 18:21
*/
public
class CharStream extends Writer {
protected char buf[];
protected int count;
public CharStream() {
this(32);
}
public CharStream(String initStr){
this(initStr.length());
this.buf = initStr.toCharArray();
this.count = initStr.length();
}
public char[] getChars(){
return this.buf;
}
public int getCount(){
return this.count;
}
public void copy(CharStream cs){
reset();
write(cs.getChars(),0,cs.getCount());
}
public void copy(String str){
reset();
write(str,0,str.length());
}
public CharStream clone(){
CharStream _cs = new CharStream(this.count);
_cs.write(buf,0,this.count);
return _cs;
}
public void group(CharStream[] csa,char separtor,CharStream defalut){
reset();
for(CharStream _cs:csa){
write(_cs.getCount() == 0?defalut:_cs);
write(separtor);
}
count--;
}
public boolean equals(char[] chars){
int n = chars.length;
if (count == n) {
for(int i=0;i<n;i++){
if (buf[i] != chars[i])
return false;
}
return true;
}
return false;
}
public CharStream(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ initialSize);
}
buf = new char[initialSize];
}
public void write(int c) {
int newcount = count + 1;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
buf[count] = (char)c;
count = newcount;
}
public void write(CharStream cs) {
write(cs.getChars(),0,cs.getCount());
}
public void write(char c[], int off, int len) {
if ((off < 0) || (off > c.length) || (len < 0) ||
((off + len) > c.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
int newcount = count + len;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
System.arraycopy(c, off, buf, count, len);
count = newcount;
}
public void write(String str, int off, int len) {
int newcount = count + len;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
str.getChars(off, off + len, buf, count);
count = newcount;
}
public void writeTo(Writer out) throws IOException {
out.write(buf, 0, count);
}
public CharStream append(CharSequence csq) {
String s = (csq == null ? "null" : csq.toString());
write(s, 0, s.length());
return this;
}
public CharStream append(CharSequence csq, int start, int end) {
String s = (csq == null ? "null" : csq).subSequence(start, end).toString();
write(s, 0, s.length());
return this;
}
public CharStream append(char c) {
write(c);
return this;
}
public void reset() {
count = 0;
}
public char toCharArray()[] {
return Arrays.copyOf(buf, count);
}
public int size() {
return count;
}
public String toString() {
return new String(buf, 0, count);
}
public void flush() { }
public void close() { }
}
\ No newline at end of file
package com.lwby.aas.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
/**
* @author martin.ad
* @Description:
* @date 创建时间: 2022/10/31 14:21
*/
public class CookieUtils {
/**
* 根据Cookie名称得到Cookie的值,没有返回Null
* <p/>
* 2006-7-28
*
* @param request
* @param name
* @return
*/
public static String getCookieValue(HttpServletRequest request, String name) {
Cookie cookie = getCookie(request, name);
if (cookie != null) {
return cookie.getValue();
} else {
return null;
}
}
/**
* 根据Cookie名称得到Cookie对象,不存在该对象则返回Null
* <p/>
* 2006-7-28
*
* @param request
* @param name
* @return
*/
public static Cookie getCookie(HttpServletRequest request, String name) {
Cookie cookies[] = request.getCookies();
if (cookies == null || name == null || name.length() == 0) {
return null;
}
Cookie cookie = null;
for (int i = 0; i < cookies.length; i++) {
if (!cookies[i].getName().equals(name)) {
continue;
}
cookie = cookies[i];
if (request.getServerName().equals(cookie.getDomain())) {
break;
}
}
return cookie;
}
}
package com.lwby.aas.util;
import lombok.extern.slf4j.Slf4j;
import org.rocksdb.Options;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
import java.io.File;
import java.util.Objects;
@Slf4j
public final class DataOperate {
private RocksDB rocksDB;
static {
RocksDB.loadLibrary();
}
public DataOperate(String path) throws RocksDBException {
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
Options options = new Options();
options.setCreateIfMissing(true);
rocksDB = RocksDB.open(options,path);
}
public boolean exist(String key) {
try {
return !Objects.isNull(rocksDB.get(key.getBytes()));
} catch (RocksDBException e) {
throw new RuntimeException(e);
}
}
public void set(String key,byte[] value){
try {
rocksDB.put(key.getBytes(), value);
} catch (RocksDBException e) {
throw new RuntimeException(e);
}
}
public byte[] get(String key){
try {
return rocksDB.get(key.getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void setLong(String key,long value) {
try {
rocksDB.put(key.getBytes(), BytesUtils.getBytes(value));
} catch (RocksDBException e) {
throw new RuntimeException(e);
}
}
public long getLong(String key) {
try {
byte[] b = rocksDB.get(key.getBytes());
return b == null?0:BytesUtils.bytesToLong(b);
} catch (RocksDBException e) {
throw new RuntimeException(e);
}
}
public void close(){
rocksDB.close();
}
}
package com.lwby.aas.util;
import org.apache.commons.lang3.time.FastDateFormat;
import java.time.ZoneId;
import java.util.TimeZone;
public class DateTimUtils {
static final FastDateFormat UNSIGNED_DATE_FORMAT = FastDateFormat.getInstance("yyyyMMdd", TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd", TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
static final FastDateFormat UNSIGNED_DATE_TIME_FORMAT = FastDateFormat.getInstance("yyyyMMddHHmmss", TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
public static String getUnsignedDate(){
return UNSIGNED_DATE_FORMAT.format(System.currentTimeMillis());
}
public static String getUnsignedDateTime(){
return UNSIGNED_DATE_TIME_FORMAT.format(System.currentTimeMillis());
}
public static String getDate(){
return DATE_FORMAT.format(System.currentTimeMillis());
}
}
package com.lwby.aas.util;
import javax.servlet.http.HttpServletRequest;
public class IPUtils {
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (check(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (check(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (check(ip)) {
try {
ip = request.getRemoteAddr();
}catch (Exception e){
}
}
return ip;
}
public static boolean check(String ip){
return (ip == null || ip.length() == 0 || ip.charAt(0)=='u' || ip.charAt(0)=='U');
}
public static String getRealIpAddr(HttpServletRequest request) {
String ip = getIpAddr(request);
if(ip != null && ip.indexOf(",") >= 7) {
String sa[] = ip.split(",");
for (int i = 0; i < sa.length; i++) {
String _t = sa[i];
if (_t.length()>0 && _t.charAt(0)!='u' && _t.charAt(0)!='U') {
ip = _t;
break;
}
}
}
return ip;
}
}
package com.lwby.aas.util;
/**
* SESSION ID 生成器
* @author martin.ad
* @Description:
* @date 创建时间: 2022/9/20 16:09
*/
import java.util.Arrays;
import java.util.Objects;
public class SessionIdGenerator {
private final static char[] ENCODE_DIC = "wnL6tS1obdOmYV9rMGBkgI8RJvUp3H2xya-u5DENseP_zKAihlqQTZc4X0j7CfFW".toCharArray();
private final static byte[] DECODE_DIC = new byte[256];
private final static String UTF_8 = "UTF-8";
private final static int SESSION_LENGTH = 33;
private final static int LONG_ENCODE_LENGTH=11;
static {
Arrays.fill(DECODE_DIC, (byte) -1);
for (int i = 0; i < ENCODE_DIC.length; i++) {
DECODE_DIC[ENCODE_DIC[i]] = (byte) i;
}
}
public static String encode(byte[] bytes) {
if (bytes == null) {
return "";
}
if (bytes.length == 0) {
return new String(new char[] { ENCODE_DIC[0] });
}
int fullGroups = bytes.length / 3;
int remainBytes = bytes.length % 3;
char[] buf = new char[(bytes.length * 4 - 1) / 3 + 1];
int i = 0;
for (; i < fullGroups; i++) {
int b0 = 255 & bytes[i * 3];
int b1 = 255 & bytes[i * 3 + 1];
int b2 = 255 & bytes[i * 3 + 2];
int c0 = i * 4;
int c1 = i * 4 + 1;
int c2 = i * 4 + 2;
int c3 = i * 4 + 3;
buf[c0] = ENCODE_DIC[encrypt(c0, b0 >>> 2)];
buf[c1] = ENCODE_DIC[encrypt(c1, ((3 & b0) << 4) | (b1 >>> 4))];
buf[c2] = ENCODE_DIC[encrypt(c2, ((15 & b1) << 2) | (b2 >>> 6))];
buf[c3] = ENCODE_DIC[encrypt(c3, 63 & b2)];
}
switch (remainBytes) {
case 2:
int b0 = 255 & bytes[i * 3];
int b1 = 255 & bytes[i * 3 + 1];
int c0 = i * 4;
int c1 = i * 4 + 1;
int c2 = i * 4 + 2;
buf[c0] = ENCODE_DIC[encrypt(c0, b0 >>> 2)];
buf[c1] = ENCODE_DIC[encrypt(c1, ((3 & b0) << 4) | (b1 >>> 4))];
buf[c2] = ENCODE_DIC[encrypt(c2, ((15 & b1) << 2))];
break;
case 1:
b0 = 255 & bytes[i * 3];
c0 = i * 4;
c1 = i * 4 + 1;
buf[c0] = ENCODE_DIC[encrypt(c0, b0 >>> 2)];
buf[c1] = ENCODE_DIC[encrypt(c1, ((3 & b0) << 4))];
break;
}
return new String(buf);
}
public static byte[] decode(String str) {
if (str == null || str.length() == 0) {
return null;
}
if (str.length() == 1) {
return new byte[0];
}
char[] chars = str.toCharArray();
int fullGroups = chars.length / 4;
int remainChars = chars.length % 4;
byte[] ret = new byte[(chars.length - 1) * 3 / 4 + 1];
int i = 0;
for (; i < fullGroups; i++) {
int c0 = i * 4;
int c1 = i * 4 + 1;
int c2 = i * 4 + 2;
int c3 = i * 4 + 3;
int b0 = decrypt(c0, DECODE_DIC[chars[c0]]);
int b1 = decrypt(c1, DECODE_DIC[chars[c1]]);
int b2 = decrypt(c2, DECODE_DIC[chars[c2]]);
int b3 = decrypt(c3, DECODE_DIC[chars[c3]]);
ret[i * 3] = (byte) ((b0 << 2) | (b1 >>> 4));
ret[i * 3 + 1] = (byte) ((b1 << 4) | (b2 >>> 2));
ret[i * 3 + 2] = (byte) ((b2 << 6) | b3);
}
switch (remainChars) {
case 3:
int c0 = i * 4;
int c1 = i * 4 + 1;
int c2 = i * 4 + 2;
int b0 = decrypt(c0, DECODE_DIC[chars[c0]]);
int b1 = decrypt(c1, DECODE_DIC[chars[c1]]);
int b2 = decrypt(c2, DECODE_DIC[chars[c2]]);
ret[i * 3] = (byte) ((b0 << 2) | (b1 >>> 4));
ret[i * 3 + 1] = (byte) ((b1 << 4) | (b2 >>> 2));
break;
case 2:
c0 = i * 4;
c1 = i * 4 + 1;
b0 = decrypt(c0, DECODE_DIC[chars[c0]]);
b1 = decrypt(c1, DECODE_DIC[chars[c1]]);
ret[i * 3] = (byte) ((b0 << 2) | (b1 >>> 4));
break;
}
return ret;
}
private static int encrypt(int charIndex, int tableIndex) {
return (charIndex + tableIndex) % 64;
}
private static int decrypt(int charIndex, int crptyIndex) {
int r = crptyIndex - charIndex % 64;
if (r < 0) {
r += 64;
}
return r;
}
public static byte[] getBytes(long data) {
byte[] bytes = new byte[8];
bytes[0] = (byte) ((data >> 56) & 0xff);
bytes[1] = (byte) ((data >> 48) & 0xff);
bytes[2] = (byte) ((data >> 40) & 0xff);
bytes[3] = (byte) ((data >> 32) & 0xff);
bytes[4] = (byte) ((data >> 24) & 0xff);
bytes[5] = (byte) ((data >> 16) & 0xff);
bytes[6] = (byte) ((data >> 8) & 0xff);
bytes[7] = (byte) (data & 0xff);
return bytes;
}
public static long getLong(byte[] bytes) {
return (0xff00000000000000L & ((long) bytes[0] << 56)
| (0xff000000000000L & ((long) bytes[1] << 48))
| (0xff0000000000L & ((long) bytes[2] << 40))
| (0xff00000000L & ((long) bytes[3] << 32))
| (0xff000000L & ((long) bytes[4] << 24))
| (0xff0000L & ((long) bytes[5] << 16))
| (0xff00L & ((long) bytes[6] << 8)) | (0xffL & (long) bytes[7]));
}
public static String generateId(long args0,long args1,long args2){
return new StringBuffer(encode(getBytes(args0))).
append(encode(getBytes(args1))).
append(encode(getBytes(args2))).
toString();
}
public static Long[] decryptParams(String sessoinId){
Objects.requireNonNull("sessoinId");
if(sessoinId.length() == SESSION_LENGTH){
Long[] params = new Long[3];
for(int i=0;i<params.length;i++){
params[i] = getLong(decode(sessoinId.substring(i * LONG_ENCODE_LENGTH, (i+1)*LONG_ENCODE_LENGTH)));
}
return params;
}
return null;
}
public static long decryptParam(String sessoinId,int index){
Objects.requireNonNull("sessoinId");
if(sessoinId.length() == 33){
return getLong(decode(sessoinId.substring(index*11, (index + 1) * 11)));
}
return 0;
}
}
package com.lwby.aas.vo;
import com.lwby.aas.po.AppChannel;
import lombok.Data;
import java.util.Date;
@Data
public class Action {
ClientInfo clientInfo;
DeliveryDeviceInfo deliveryDeviceInfo;
int platformId;
long userId;
int channelId;
int planId;
String advertiserId;
String deviceId;
String media;
boolean isNewUser;
Date RegistrationDate;
public Action(ClientInfo clientInfo,DeliveryDeviceInfo deliveryDeviceInfo){
this.clientInfo = clientInfo;
this.deliveryDeviceInfo = deliveryDeviceInfo;
}
}
\ No newline at end of file
package com.lwby.aas.vo;
import lombok.Data;
import java.util.Date;
import java.util.Map;
@Data
public class BookStoreEvent {
public static final String ADD_USER_READ_TIME = "addUserReadTime";
public static final String USER_ADD_BOOK_SHELF = "userAddBookShelf";
public static final String USER_DELETE_BOOK_SHELF = "userDeleteBookShelf";
public static final String PART_ID = "partId";
public static final String PART_NUM = "partNum";
public static final String BOOK_ID = "bookId";
private String id;
private Integer bookStoreEvent;
private ClientInfo clientInfo;
private String pushProvider;
private String pushToken;
private Map<String, Object> extraData;
private Date createTime;
}
\ No newline at end of file
package com.lwby.aas.vo;
import lombok.Data;
@Data
public class ClientInfo {
private String sessionid;
private UserProfile user;
private String xClient;
private String imei;
private String imsi;
private String mac;
private String phoneModel;
private String systemVersion;
private String screenSize;
private String cellId;
private String version;
private String rootPath;
private String skin;
private String rn;
private String tdcn;
private Integer platformId;
private Integer mainVersion;
private Integer subVersion;
private Integer channel;
private Integer platformGroupId;
private Integer apiType;
private String visitor;
private String sid;
private Integer signVersion;
private String dID;
private Integer pkv = 1;
private boolean isVisitor = false;
private Integer fixVersion;
private String pm;
private String firm;
private String idfa;
private String os;
private String androidid;
private String h5UserId;
private String oaid;
private String ddid;
private String clientIp;
private String ua;
}
package com.lwby.aas.vo;
import lombok.Data;
@Data
public class DeliveryDeviceInfo {
private String device_id; //设备id,和其他业务表一致
private String ocpc_device_id; //ocpc业务原始设备id,可能是明文或者密文
private String ocpc_device_id_type; // 设备id类型(idfa,imei,oaid...)
private String encryption_type; // 加密类型,MD5,明文等
private String platform_id; // (监测链接)
private String channel; // 渠道id
private String dj_channel; //点击渠道
private String book_id; // 书籍id
private String media; // 媒体标识(如'jrtt','gdt')
private String os; // 系统
private String advertiser_id; // 账号
private String ad_group_id; //广告组id
private String ad_plan_id; //广告计划id
private String ad_creative_id; // 广告创意id
private String ip;
private String ua;
private String callback_url;
private String parameter; //(所有参数以json的形式存在这里)
private long click_time = 0; // 点击时间
private Long active_time; //激活时间
private Integer is_call; // 是否回调 2 回调 3 不回调 4 扣量 5 老用户xx天活跃不回传 6短剧注册发消息 7 老用户在配置活跃天数内不回传 8 关键行为 9 使用ip只发bi消息
private String click_id; //点击id
private String uuid;// 唯一id
private String ad_platform_type;
private String target_audience;
//通投智选使用字段-点击来自穿山甲的广告位编码
private String union_site;
//客户在小米渠道投放的渠道包的 id 值
private String appId;
//头条回传上报途径 巨量引擎的关键广告信息
private String callback_param;
//巨量广告体验版中特有的宏参,代表巨量广告体验版的项目ID
private String promotion_id;
//巨量广告体验版中的广告名称
private String project_id;
//巨量广告体验版中的广告名称
private String promotion_name;
//巨量广告体验版中的项目名称
private String project_name;
//请求id vivo 商店投放 带监测链接
private String request_id;
//存oaid
private String oaid;
//章节id
private String partId;
/**
* 百度专用账户key
*/
private String aKey;
/**
* 用户id
*/
private Long userId;
/**
* 投放来源
*/
private Integer source;
//落地页url
private String link;
private String ctype;
/**
* 存媒体下发的imeiMd5给广告使用
*/
private String imeiMd5;
/**
* 微信小程序appid
*/
private String wechatAppId;
/**
* 微信小程序openid
*/
private String wechatOpenId;
/**
* 短剧剧名id
*/
private String productId;
/**
* 短剧剧名
*/
private String productName;
/**
* 短剧付费金额
*/
private Double value;
/**
* 小程序短剧广点通 下单上报媒体字段
*/
private String orderObject;
private Integer device_status;
//手机型号
private String model;
private String videoResourceId;
private String videoId;
private String registerTime;
private String ecpmAvgCount;
private String motivateCount;
private String arpuCount;
//每次ecpm达标
private String pecpmCount;
//达标次数,与激励视频阈值比较
private String perecpmSize;
//素材
private String mid1;
private String mid2;
private String mid3;
private String mid4;
private String mid5;
private String mid6;
}
package com.lwby.aas.vo;
import lombok.Data;
import java.util.Date;
@Data
public class UserProfile {
private Long id;
private String username;
private String password;
private Boolean isKaiqiUser;
private String signature;
private String gender;
private Integer level;
private Integer experience;
private Integer tadou;
private String nickname;
private String city;
private String contact;
private String hobbies;
private String phoneNumber;
private String career;
private Date birthday;
private Date lastLogin;
private Boolean isMajia;
private Date registrationDate;
private String deviceId;
private String bindingNum;
private String bloodType;
private String email;
private Boolean isPrivate;
private String cellPhone;
private String myBackup;
private Integer numOfMedals;
private Integer medalId;
private Integer authorId;
private String penName;
private String headImage;
private String cellEmail;
private String viewPassword;
private Integer platformId;
private Integer mainversion;
private Integer subversion;
private Integer channel;
private Integer userStatus;
}
package com.lwby.aas.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Slf4j
@Controller
@RequestMapping(value = "/api")
public class ApiService {
@CrossOrigin(originPatterns = "*",allowCredentials = "true")
@ResponseBody
@RequestMapping(value = "query")
public void query(int pid,String expr) {
}
}
\ No newline at end of file
spring:
datasource:
lwby:
jdbc-url: jdbc:mysql://rm-2zem654n5267sl284.mysql.rds.aliyuncs.com/lwby_novel?zeroDateTimeBehavior=convertToNull&characterEncoding=utf8&autoReconnect=true
username: lwby_read
password: TXlEjAy0zO2S
driver-class-name: com.mysql.cj.jdbc.Driver
initialSize: 1
minIdle: 1
marketing:
jdbc-url: jdbc:mysql://rm-2ze1e2ykb87kj2592.mysql.rds.aliyuncs.com:3306/lwby_marketing_growth?zeroDateTimeBehavior=convertToNull&characterEncoding=utf8&autoReconnect=true
username: read01
password: read01!@#
driver-class-name: com.mysql.cj.jdbc.Driver
initialSize: 1
minIdle: 1
# redis:
# host: 127.0.0.1
# port: 6379
kafka:
bootstrap-servers: 172.17.243.58:9092,172.17.243.59:9092,172.17.243.60:9092,172.17.243.61:9092,172.17.243.62:9092
producer:
retries: 3
acks: 1
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
enable-auto-commit: true
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
max-poll-records: 100
listener:
ack-mode: RECORD
\ No newline at end of file
spring:
# datasource:
# dmp:
# jdbc-url: jdbc:oceanbase://t4k45ch7fuiw0.oceanbase.aliyuncs.com:3306/dmp?zeroDateTimeBehavior=round&characterEncoding=utf8&serverTimezone=Asia/Shanghai&autoReconnect=true
# username: dmp
# password: Lwby@123!@#
# driver-class-name: com.alipay.oceanbase.jdbc.Driver
# initialSize: 2
# minIdle: 2
# dmpadmin:
# jdbc-url: jdbc:mysql://rm-2zeo09186ukqa8zh1.mysql.rds.aliyuncs.com:3306/lwby-admin
# username: lwby
# password: VjxYfmY8J77ISChp
# driver-class-name: com.mysql.cj.jdbc.Driver
# initialSize: 2
# minIdle: 2
# redis:
# host: 127.0.0.1
# port: 6379
kafka:
bootstrap-servers: 172.17.243.58:9092,172.17.243.59:9092,172.17.243.60:9092,172.17.243.61:9092,172.17.243.62:9092
producer:
retries: 3
acks: 1
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
enable-auto-commit: true
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
max-poll-records: 500
listener:
ack-mode: RECORD
\ No newline at end of file
server:
port: 8081
tomcat:
accept-count: 10000
max-threads: 100
min-spare-threads: 100
max-connections: 2000
connection-timeout: 1000
max-http-form-post-size: 10MB
spring:
application:
name: ssa
profiles:
active: prod
#prometheus监控平台配置
management:
endpoints:
web:
exposure:
include: "*"
exclude: configprops
endpoint:
health:
show-details: ALWAYS
metrics:
tags:
application: ${spring.application.name}
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: linkedhashmap
keyConvertor: fastjson
limit: 10000
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<timestamp key="DATETIME" datePattern="yyyy-MM-dd HH:mm:ss" />
<!-- 控制台打印 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%-5level] %d{${DATETIME}} [%thread] %logger{36} - %m%n</pattern>
</encoder>
</appender>
<!-- Logger 根目录 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment