广告系统开发-投放模块

投放模块

数据表设计

  • 用户账户
  • 推广计划(与推广单元是一对多)
  • 推广单元 (与创意是多对多)
  • 创意

层级设计

  • 最高层级: 用户账户

  • 第二层级: 推广计划

  • 第三层级: 推广单元

    • 关键词推广单元
    • 地域推广单元
    • 兴趣推广单元
    • 人群推广单元
  • 创意: 文字创意,图片创意等等

用户表

用户账户(ad_user) 含义
username 账户名称
token 账户token
user_status 账号状态
create_time 创建时间
update_time 更新时间

推广计划表

推广计划(ad_plan) 含义
user_id 标记当前记录所属用户
plan_name 推广计划名称
plan_status 推广计划状态
start_date 推广计划开始时间
end_date 推广计划结束时间
create_time 创建时间
update_time 更新时间

推广单元表

推广单元(ad_unit) 含义
plan_id 关联推广计划id
unit_name 推广单元名称
unit_status 推广单元状态
position_type 广告位类型
budget 预算
create_time 创建时间
update_time 更新时间

关键词推广单元

关键词限制(ad_unit_keyword) 含义
unit_id 关联推广单元id
keyword 关键词

地域推广单元

关键词限制(ad_unit_district) 含义
unit_id 关联推广单元id
province
city

兴趣推广单元

关键词限制(ad_unit_it) 含义
unit_id 关联推广单元id
it_tag 兴趣标签

创意表

创意(ad_creative) 含义
name 创意名称
type 物料类型
material_type 物料子类型
height 高度
width 宽度
size 尺寸
duration 持续时长
audit_status 审核状态
user_id 标记当前所属用户
url 物料地址
create_time 创建时间
update_time 更新时间

由于创意与推广单元是多对多关系,因此还要有一个创意与推广单元关联表。

创意与推广单元关联表

创意与推广单元关联(creative_unit) 含义
creative_id 关联创意id
unit_id 关联推广单元id

代码编写

编写配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
server:
port: 7000
servlet:
context-path: /ad-sponsor

spring:
application:
name: eureka-client-ad-sponsor
jpa:
show-sql: true
hibernate:
ddl-auto: none
properties:
hibernate.format_sql: true
open-in-view: false
datasource:
url: jdbc:mysql://127.0.0.1:3306/ad_data?autoReconnect=true
username: root
password: admin
tomcat:
#最大连接数
max-active: 4
#最小空闲连接
min-idle: 2
#初始化连接
initial-size: 2

eureka:
client:
service-url:
defaultZone: http://server1:8000/eureka/

定义数据表实体类(ad_user)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
//可以调用其他的微服务模块,不过在广告投放系统中不存在调用其他的微服务,只是可以令dashboard监控
@EnableFeignClients
//断路器
@EnableCircuitBreaker
//能够从服务注册中心中拿到其他微服务的信息,相当于客户端
@EnableEurekaClient
@SpringBootApplication
public class SponsorApplication {

public static void main(String[] args) {

SpringApplication.run(SponsorApplication.class, args);
}
}

对应创建的数据库表

1
2
3
4
5
6
7
8
9
10
11
-- 用户表
CREATE TABLE `ad_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`username` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名',
`token` varchar(256) NOT NULL DEFAULT '' COMMENT '给用户生成的 token',
`user_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '用户状态',
`create_time` datetime NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COMMENT='用户信息表';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.Date;


@Data
@NoArgsConstructor
@AllArgsConstructor
//标记实体类
@Entity
//标记对应的数据库中的表
@Table(name = "ad_user")
public class AdUser {

//表明该字段为主键,@Id
@Id
//表明主键的生成策略
@GeneratedValue(strategy = GenerationType.IDENTITY)
//表明字段名字
@Column(name = "id", nullable = false)
private Long id;

//表示数据库表的基本属性,也可以不写
@Basic
@Column(name = "username", nullable = false)
private String username;

@Basic
@Column(name = "token", nullable = false)
private String token;

@Basic
@Column(name = "user_status", nullable = false)
private Integer userStatus;

@Basic
@Column(name = "create_time", nullable = false)
private Date createTime;

@Basic
@Column(name = "update_time", nullable = false)
private Date updateTime;

public AdUser(String username, String token) {
this.username = username;
this.token = token;
//使用枚举类进行初始化
this.userStatus = CommonStatus.VALID.getStatus();
//创建时间和更新时间这两个字段使用new Date()进行初始化
this.createTime = new Date();
this.updateTime = this.createTime;
}
}


/*
定义枚举类型
*/
@Getter
public enum CommonStatus {
VALID(1, "有效状态"),
INVALID(0, "无效状态");
private Integer status;
private String desc;
CommonStatus(Integer status, String desc) {
this.status = status;
this.desc = desc;
}
}

接下来编写ad_planad_unit以及创意表,创意推广单元关联表对应的实体类,方式与上述代码基本一致。

定义数据表Dao接口(以AdUser实体类为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import org.springframework.data.jpa.repository.JpaRepository;

//继承spring-data jpa的接口,泛型中有两个参数,第一个是相关联的实体类,第二个参数是主键类型
public interface AdUserRepository extends JpaRepository<AdUser, Long> {

/**
* 根据用户名查询用户记录
* */
AdUser findByUsername(String username);
}
public interface AdPlanRepository extends JpaRepository<AdPlan, Long> {
//主键和用户查询
AdPlan findByIdAndUserId(Long id, Long userId);
//主键列表和用户,返回一个计划列表
List findAllByIdInAndUserId(List ids, Long userId);
//用户和计划名称
AdPlan findByUserIdAndPlanName(Long userId, String planName);
//状态查询
List findAllByPlanStatus(Integer status);
}
public interface AdUnitRepository extends JpaRepository<AdUnit, Long> {
//计划和推广单元名称查询
AdUnit findByPlanIdAndUnitName(Long planId, String unitName);
//推广单元状态去查询
List findAllByUnitStatus(Integer unitStatus);
}
//如果不写查询方法,默认主键查询即可。

用户账户服务功能实现

用户创建需要提供创建用户的请求数据,还需要返回给广告主数据。

vo对象定义,即请求和响应

请求对象

1
2
3
4
5
6
7
8
9
10
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CreateUserRequest {

private String username;
public boolean validate() {
return !StringUtils.isEmpty(username);
}
}

响应对象

1
2
3
4
5
6
7
8
9
10
11
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CreateUserResponse {

private Long userId;
private String username;
private String token;
private Date createTime;
private Date updateTime;
}

创建用户的接口实现

对于创建用户接口,创建了相应的请求对象和响应对象,方便管理实体类。

如果有其他功能的接口方法,则创建相对应的请求对象和响应对象。

1
2
3
4
5
6
7
public interface IUserService {

/**
* 创建用户
* */
CreateUserResponse createUser(CreateUserRequest request) throws AdException;
}

实现用户接口

使用内部类定义一些error信息

1
2
3
4
5
6
7
8
9
public class Constants {
public static class ErrorMsg {
public static final String REQUEST_PARAM_ERROR = "请求参数错误";
public static final String SAME_NAME_ERROR = "存在同名的用户";
public static final String CAN_NOT_FIND_RECORD = "找不到数据记录";
public static final String SAME_NAME_PLAN_ERROR = "存在同名的推广计划";
public static final String SAME_NAME_UNIT_ERROR = "存在同名的推广单元";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// service层
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

//lombok中的日志属性
@Slf4j
//标记为java bean实体,spring之后可以获取bean实体
@Service
//实现创建用户接口
public class UserServiceImpl implements IUserService {
//要用到dao接口对象,完成注入
private final AdUserRepository userRepository;

@Autowired
public UserServiceImpl(AdUserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
@Transactional
//涉及到数据库写操作,要开启事务,传入参数request请求对象
public CreateUserResponse createUser(CreateUserRequest request) throws AdException {

if (!request.validate()) {
throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
}
//去数据库查找用户对象
AdUser oldUser = userRepository.findByUsername(request.getUsername());
//当前数据库存在同名用户
if (oldUser != null) {
throw new AdException(Constants.ErrorMsg.SAME_NAME_ERROR);
}
//使用save创建新对象
AdUser newUser = userRepository.save(new AdUser(
request.getUsername(),
//md5实现 DigestUtils.md5Hex(value).toUpperCase();
CommonUtils.md5(request.getUsername())
));
//返回响应对象
return new CreateUserResponse(
newUser.getId(), newUser.getUsername(), newUser.getToken(),
newUser.getCreateTime(), newUser.getUpdateTime()
);
}
}

推广计划服务功能实现

同样的编写对应的vo 请求对象。应用于创建,更新以及删除推广计划的方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdPlanRequest {

private Long id;
private Long userId;
private String planName;
private String startDate;
private String endDate;
//定义一些验证请求正确性的方法
public boolean createValidate() {

return userId != null
&& !StringUtils.isEmpty(planName)
&& !StringUtils.isEmpty(startDate)
&& !StringUtils.isEmpty(endDate);
}
//传递更新时的校验
public boolean updateValidate() {

return id != null && userId != null;
}

public boolean deleteValidate() {

return id != null && userId != null;
}
}

创建获取推广计划的请求对象

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdPlanGetRequest {

private Long userId;
private List ids;

public boolean validate() {

return userId != null && !CollectionUtils.isEmpty(ids);
}
}

响应对象的封装

1
2
3
4
5
6
7
8
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdPlanResponse {

private Long id;
private String planName;
}

创建推广计划服务的增删改查的接口

定义了请求的vo对象,方便序列化和反序列化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.List;
//传入不同的请求对象
public interface IAdPlanService {
/**
* 创建推广计划
* */
AdPlanResponse createAdPlan(AdPlanRequest request) throws AdException;

/**
* 获取推广计划
* */
List getAdPlanByIds(AdPlanGetRequest request) throws AdException;

/**
* 更新推广计划
* */
AdPlanResponse updateAdPlan(AdPlanRequest request) throws AdException;

/**
* 删除推广计划
* */
void deleteAdPlan(AdPlanRequest request) throws AdException;
}

将String属性转化成Date属性

1
2
private static String[] parsePatterns = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyy.MM.dd"};
DateUtils.parseDate(dateString, parsePatterns);

实现推广计划服务接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
@Service
public class AdPlanServiceImpl implements IAdPlanService {
@Autowired
private final AdUserRepository userRepository;
private final AdPlanRepository planRepository;
//创建推广计划,开启事务
@Override
@Transactional
public AdPlanResponse createAdPlan(AdPlanRequest request)
throws AdException {

if (!request.createValidate()) {
throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
}
// 确保关联的 User 是存在的
// findById是jpa实现的
Optional adUser = userRepository.findById(request.getUserId());
if (!adUser.isPresent()) {
throw new AdException(Constants.ErrorMsg.CAN_NOT_FIND_RECORD);
}

AdPlan oldPlan = planRepository.findByUserIdAndPlanName(
request.getUserId(), request.getPlanName()
);
if (oldPlan != null) {
throw new AdException(Constants.ErrorMsg.SAME_NAME_PLAN_ERROR);
}
//都要涉及到数据库操作
AdPlan newAdPlan = planRepository.save(
new AdPlan(request.getUserId(), request.getPlanName(),
CommonUtils.parseStringDate(request.getStartDate()),
CommonUtils.parseStringDate(request.getEndDate())
)
);
return new AdPlanResponse(newAdPlan.getId(), newAdPlan.getPlanName());
}
//获取推广计划,通过
@Override
public List getAdPlanByIds(AdPlanGetRequest request)
throws AdException {
if (!request.validate()) {
throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
}
return planRepository.findAllByIdInAndUserId(request.getIds(),request.getUserId());
}

@Override
@Transactional
public AdPlanResponse updateAdPlan(AdPlanRequest request)
throws AdException {

if (!request.updateValidate()) {
throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
}

AdPlan plan = planRepository.findByIdAndUserId(
request.getId(), request.getUserId()
);
if (plan == null) {
throw new AdException(Constants.ErrorMsg.CAN_NOT_FIND_RECORD);
}
//判断各个字段是否为空
if (request.getPlanName() != null) {
plan.setPlanName(request.getPlanName());
}
if (request.getStartDate() != null) {
plan.setStartDate(
CommonUtils.parseStringDate(request.getStartDate())
);
}
if (request.getEndDate() != null) {
plan.setEndDate(
CommonUtils.parseStringDate(request.getEndDate())
);
}
//其中修改和删除字段需要设置更新时间
plan.setUpdateTime(new Date());
plan = planRepository.save(plan);

return new AdPlanResponse(plan.getId(), plan.getPlanName());
}

@Override
//发生异常之后,数据库会进行回滚操作
@Transactional
public void deleteAdPlan(AdPlanRequest request) throws AdException {

if (!request.deleteValidate()) {
throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
}

AdPlan plan = planRepository.findByIdAndUserId(
request.getId(), request.getUserId()
);
if (plan == null) {
throw new AdException(Constants.ErrorMsg.CAN_NOT_FIND_RECORD);
}

plan.setPlanStatus(CommonStatus.INVALID.getStatus());
//其中修改和删除字段需要设置更新时间
plan.setUpdateTime(new Date());
planRepository.save(plan);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdUnitKeywordRequest {

private List unitKeywords;

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class UnitKeyword {

private Long unitId;
private String keyword;
}
}
1
2
3
4
5
6
7
8
9
private boolean isRelatedUnitExist(List unitIds) {

if (CollectionUtils.isEmpty(unitIds)) {
return false;
}
//判定相关的推广单元是否存在
return unitRepository.findAllById(unitIds).size() ==
new HashSet<>(unitIds).size();
}

java8特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public AdUnitKeywordResponse createUnitKeyword(AdUnitKeywordRequest request) throws AdException {
//先获取推广ID,根据推广的关键词限制
List unitIds = request.getUnitKeywords().stream()
.map(AdUnitKeywordRequest.UnitKeyword::getUnitId)
.collect(Collectors.toList());
//判断相关联的推广单元是否存在
if (!isRelatedUnitExist(unitIds)) {
throw new AdException(Constants.ErrorMsg.REQUEST_PARAM_ERROR);
}

List ids = Collections.emptyList();

List unitKeywords = new ArrayList<>();
if (!CollectionUtils.isEmpty(request.getUnitKeywords())) {
//根据关键词和推广ID进行创建,和之前的创建方法类似,也是先创建推广关键词,然后再通过dao接口去saveall
request.getUnitKeywords().forEach(i -> unitKeywords.add(
new AdUnitKeyword(i.getUnitId(), i.getKeyword())
));
ids = unitKeywordRepository.saveAll(unitKeywords).stream()
.map(AdUnitKeyword::getId)
.collect(Collectors.toList());
}

return new AdUnitKeywordResponse(ids);
}

java8 stream().map().collect()用法

有一个集合:

1
List users = getList(); //从数据库查询的用户集合

现在想获取User的身份证号码;在后续的逻辑处理中要用;

常用的方法我们大家都知道,用for循环,

1
2
3
4
List idcards=new ArrayList();//定义一个集合来装身份证号码
for(int i=0;i
  idcards.add(users.get(i).getIdcard());
}

java8 之后:

1
List idcards= users.stream().map(User::getIdcard).collect(Collectors.toList());

解释下一这行代码:

users:一个实体类的集合,类型为List
User:实体类
getIdcard:实体类中的get方法,为获取User的idcard

stream()优点

无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
stream().map()方法的使用示例:

  • 数组字母小写变大写
1
2
3
4
List list= Arrays.asList("a", "b", "c", "d");

List collect=list.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println(collect); //[A, B, C, D]
  • 数组所有元素,按某种规律计算:

    1
    2
    3
    List num = Arrays.asList(1,2,3,4,5);
    List collect1 = num.stream().map(n -> n * 2).collect(Collectors.toList());
    System.out.println(collect1); //[2, 4, 6, 8, 10]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Slf4j
@RestController
public class UserOPController {

private final IUserService userService;

@Autowired
public UserOPController(IUserService userService) {
this.userService = userService;
}
//post方法,post会改变服务端的数据,不具有幂等性
@PostMapping("/create/user")
//序列化与反序列化
public CreateUserResponse createUser(
@RequestBody CreateUserRequest request) throws AdException {
log.info("ad-sponsor: createUser -> {}",
JSON.toJSONString(request));
return userService.createUser(request);
}
}

配置路由信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
zuul:
prefix: /imooc
routes:
sponsor:
#**支持多级目录
path: /ad-sponsor/**
#微服务名字
serviceId: eureka-client-ad-sponsor
#是否跳过前缀,去掉后会发生404
strip-prefix: false
search:
path: /ad-search/**
serviceId: eureka-client-ad-search
strip-prefix: false

检索模块

1
2
3
4
5
6
7
8
9
10
feign:
hystrix:
enabled: true

management:
endpoints:
web:
exposure:
#暴露全部监控信息
include: "*"
1
2
3
4
5
6
7
8
9
@FeignClient(value = "eureka-client-ad-sponsor",
fallback = SponsorClientHystrix.class)
public interface SponsorClient {
//指出调用的微服务接口,即ad_sponsor,结合spring_mvc
@RequestMapping(value = "/ad-sponsor/get/adPlan",
method = RequestMethod.POST)
CommonResponse> getAdPlans(
@RequestBody AdPlanGetRequest request);
}

断路器

1
2
3
4
5
6
7
8
9
10
11
@Component
public class SponsorClientHystrix implements SponsorClient {

@Override
public CommonResponse> getAdPlans(
AdPlanGetRequest request) {
//服务降级,就会调用该方法,返回一个响应
return new CommonResponse<>(-1,
"eureka-client-ad-sponsor error");
}
}

构建正向索引和倒排索引。

构建索引接口,对索引的增删改查

1
2
3
4
5
6
7
8
9
10
public interface IndexAware<K, V> {

V get(K key);

void add(K key, V value);

void update(K key, V value);

void delete(K key, V value);
}

不需要对数据库全部的字段建立索引,而且也不需要对全部的数据库表建立索引,比如用户表,不需要对广告主建立索引。

建立推广计划的索引属性planname不需要作为索引,定义索引对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdPlanObject {

private Long planId;
private Long userId;
private Integer planStatus;
private Date startDate;
private Date endDate;

public void update(AdPlanObject newObject) {

if (null != newObject.getPlanId()) {
this.planId = newObject.getPlanId();
}
if (null != newObject.getUserId()) {
this.userId = newObject.getUserId();
}
if (null != newObject.getPlanStatus()) {
this.planStatus = newObject.getPlanStatus();
}
if (null != newObject.getStartDate()) {
this.startDate = newObject.getStartDate();
}
if (null != newObject.getEndDate()) {
this.endDate = newObject.getEndDate();
}
}
}

构建正向索引,通过id得到推广计划的索引对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Slf4j
@Component
public class AdPlanIndex implements IndexAware<Long, AdPlanObject> {
//构建map来存储索引对象
private static Map objectMap;
//线程安全,在索引更新时
static {
objectMap = new ConcurrentHashMap<>();
}

@Override
public AdPlanObject get(Long key) {
return objectMap.get(key);
}

@Override
public void add(Long key, AdPlanObject value) {

log.info("before add: {}", objectMap);
objectMap.put(key, value);
log.info("after add: {}", objectMap);
}

@Override
public void update(Long key, AdPlanObject value) {

log.info("before update: {}", objectMap);

AdPlanObject oldObject = objectMap.get(key);
if (null == oldObject) {
objectMap.put(key, value);
} else {
oldObject.update(value);
}

log.info("after update: {}", objectMap);
}

@Override
public void delete(Long key, AdPlanObject value) {

log.info("before delete: {}", objectMap);
objectMap.remove(key);
log.info("after delete: {}", objectMap);
}
}

构建推广单元的索引对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdUnitObject {

private Long unitId;
private Integer unitStatus;
//位置属性
private Integer positionType;
//推广计划id
private Long planId;
//引用推广计划索引对象
private AdPlanObject adPlanObject;

void update(AdUnitObject newObject) {

if (null != newObject.getUnitId()) {
this.unitId = newObject.getUnitId();
}
if (null != newObject.getUnitStatus()) {
this.unitStatus = newObject.getUnitStatus();
}
if (null != newObject.getPositionType()) {
this.positionType = newObject.getPositionType();
}
if (null != planId) {
this.planId = newObject.getPlanId();
}
if (null != newObject.getAdPlanObject()) {
this.adPlanObject = newObject.getAdPlanObject();
}
}

private static boolean isKaiPing(int positionType) {
return (positionType & AdUnitConstants.POSITION_TYPE.KAIPING) > 0;
}

private static boolean isTiePian(int positionType) {
return (positionType & AdUnitConstants.POSITION_TYPE.TIEPIAN) > 0;
}

private static boolean isTiePianMiddle(int positionType) {
return (positionType & AdUnitConstants.POSITION_TYPE.TIEPIAN_MIDDLE) > 0;
}

private static boolean isTiePianPause(int positionType) {
return (positionType & AdUnitConstants.POSITION_TYPE.TIEPIAN_PAUSE) > 0;
}

private static boolean isTiePianPost(int positionType) {
return (positionType & AdUnitConstants.POSITION_TYPE.TIEPIAN_POST) > 0;
}

public static boolean isAdSlotTypeOK(int adSlotType, int positionType) {

switch (adSlotType) {
case AdUnitConstants.POSITION_TYPE.KAIPING:
return isKaiPing(positionType);
case AdUnitConstants.POSITION_TYPE.TIEPIAN:
return isTiePian(positionType);
case AdUnitConstants.POSITION_TYPE.TIEPIAN_MIDDLE:
return isTiePianMiddle(positionType);
case AdUnitConstants.POSITION_TYPE.TIEPIAN_PAUSE:
return isTiePianPause(positionType);
case AdUnitConstants.POSITION_TYPE.TIEPIAN_POST:
return isTiePianPost(positionType);
default:
return false;
}
}
}

关键词推广单元的索引实现

其中,对推广单元的限制这种要使用倒排索引。

1
2
3
4
5
6
7
8
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UnitKeywordObject {
//与推广单元的关联ID
private Long unitId;
private String keyword;
}
0%