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);
}
This diff is collapsed.
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;
}
This diff is collapsed.
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