Skip to content

用户行为-需求文档

1.什么是行为

用户行为数据的记录包括了关注、点赞、不喜欢、收藏、阅读等行为

黑马头条项目整个项目开发涉及web展示和大数据分析来给用户推荐文章,如何找出哪些文章是热点文章进行针对性的推荐呢?这个时候需要进行大数据分析的准备工作,埋点。

所谓“埋点”,是数据采集领域(尤其是用户行为数据采集领域)的术语,指的是针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。比如用户某个icon点击次数、阅读文章的时长,观看视频的时长等等。

2.关注

image-20210727162600274

如上效果:

当前登录后的用户可以关注作者,也可以取消关注作者

一个用户关注了作者,作者是由用户实名认证以后开通的作者权限,才有了作者信息,作者肯定是app中的一个用户。

从用户的角度出发:一个用户可以关注其他多个作者 —— 我的关注

从作者的角度出发:一个用户(同是作者)也可以拥有很多个粉丝 —— 我的粉丝

image-20210727163038634

3 点赞或取消点赞

image-20210727161838807

  • 当前登录的用户点击了”赞“,就要保存当前行为数据
  • 可以取消点赞

4 阅读

当用户查看了某一篇文章,需要记录当前用户查看的次数,阅读时长(非必要),阅读文章的比例(非必要),加载的时长(非必要)

5 不喜欢

为什么会有不喜欢?

一旦用户点击了不喜欢,不再给当前用户推荐这一类型的文章信息

image-20210727162221437

6 收藏

image-20210727162332931

记录当前登录人收藏的文章

7 文章详情-行为数据回显

主要是用来展示文章的关系,app端用户必须登录,判断当前用户是否已经关注该文章的作者、是否收藏了此文章、是否点赞了文章、是否不喜欢该文章等

例:如果当前用户点赞了该文章,点赞按钮进行高亮,其他功能类似。

image-20210727162449406

8 注意:

  • 所有的行为数据,都存储到redis中
  • 点赞、阅读、不喜欢需要专门创建一个微服务来处理数据,新建模块:heima-leadnews-behavior
  • 关注需要在heima-leadnews-user微服务中实现
  • 收藏与文章详情数据回显在heima-leadnews-article微服务中实现

用户行为-接口文档

1 点赞

接口地址:/api/v1/likes_behavior

请求方式:POST

请求数据类型:application/json

响应数据类型:*/*

接口描述:

请求示例:

javascript
{
	"articleId": 0,
	"operation": 0,
	"type": 0
}

请求参数:

参数名称参数说明in是否必须数据类型schema
dtodtobodytrueLikesBehaviorDtoLikesBehaviorDto
  articleId文章idfalselong
  operation0 点赞 1 取消点赞falseshort
  type0文章 1动态 2评论falseshort

响应状态:

状态码说明schema
200OKResponseResult
201Created
401Unauthorized
403Forbidden
404Not Found

响应参数:

参数名称参数说明类型schema
codeinteger(int32)integer(int32)
dataobject
errorMessagestring
hoststring

响应示例:

javascript
{
	"code": 0,
	"data": {},
	"errorMessage": "",
	"host": ""
}

2 阅读

接口地址:/api/v1/read_behavior

请求方式:POST

请求数据类型:application/json

响应数据类型:*/*

接口描述:

请求示例:

javascript
{
	"articleId": 0,
	"count": 0
}

请求参数:

参数名称参数说明in是否必须
dtodtobodytrue
  articleId文章idlongfalse
  count阅读次数intfalse

响应状态:

状态码说明schema
200OKResponseResult
201Created
401Unauthorized
403Forbidden
404Not Found

响应参数:

参数名称参数说明类型schema
codeinteger(int32)integer(int32)
dataobject
errorMessagestring
hoststring

响应示例:

javascript
{
	"code": 0,
	"data": {},
	"errorMessage": "",
	"host": ""
}

3 不喜欢

接口地址:/api/v1/un_likes_behavior

请求方式:POST

请求数据类型:application/json

响应数据类型:*/*

接口描述:

请求示例:

javascript
{
	"articleId": 0,
	"type": 0
}

请求参数:

参数名称参数说明in是否必须数据类型schema
dtodtobodytrueUnLikesBehaviorDtoUnLikesBehaviorDto
  articleId文章idfalselong
  type0 不喜欢 1 取消不喜欢falseshort

响应状态:

状态码说明schema
200OKResponseResult
201Created
401Unauthorized
403Forbidden
404Not Found

响应参数:

参数名称参数说明类型schema
codeinteger(int32)integer(int32)
dataobject
errorMessagestring
hoststring

响应示例:

javascript
{
	"code": 0,
	"data": {},
	"errorMessage": "",
	"host": ""
}

4 关注与取消关注

接口地址:/api/v1/user/user_follow

请求方式:POST

请求数据类型:application/json

响应数据类型:*/*

接口描述:

请求示例:

javascript
{
	"articleId": 0,
	"authorId": 0,
	"operation": 0
}

请求参数:

参数名称参数说明in是否必须数据类型schema
dtodtobodytrueUserRelationDtoUserRelationDto
  articleId文章idfalselong
  authorId作者idfalseint
  operation0 关注 1 取消falseshort

响应状态:

状态码说明schema
200OKResponseResult
201Created
401Unauthorized
403Forbidden
404Not Found

响应参数:

参数名称参数说明类型schema
codeinteger(int32)integer(int32)
dataobject
errorMessagestring
hoststring

响应示例:

javascript
{
	"code": 0,+
	"data": {},
	"errorMessage": "",
	"host": ""
}

5 文章收藏

接口地址:/api/v1/collection_behavior

请求方式:POST

请求数据类型:application/json

响应数据类型:*/*

接口描述:

请求示例:

javascript
{
	"entryId": 0,
	"operation": 0,
	"publishedTime": "",
	"type": 0
}

请求参数:

参数名称参数说明in是否必须数据类型schema
dtodtobodytrueCollectionBehaviorDtoCollectionBehaviorDto
  entryId文章idfalselong
  operation0收藏 1取消收藏falseshort
  publishedTime发布时间falsedate
  type0文章 1动态falseshort

响应状态:

状态码说明schema
200OKResponseResult
201Created
401Unauthorized
403Forbidden
404Not Found

响应参数:

参数名称参数说明类型schema
codeinteger(int32)integer(int32)
dataobject
errorMessagestring
hoststring

响应示例:

javascript
{
	"code": 0,
	"data": {},
	"errorMessage": "",
	"host": ""
}

6 加载文章行为-数据回显

接口地址:/api/v1/article/load_article_behavior

请求方式:POST

请求数据类型:application/json

响应数据类型:*/*

接口描述:

请求示例:

javascript
{
	"articleId": 0,
	"authorId": 0
}

请求参数:

参数名称参数说明in是否必须数据类型schema
dtodtobodytrueArticleInfoDtoArticleInfoDto
  articleId文章idfalselong
  authorId作者idfalseint

响应状态:

状态码说明schema
200OKResponseResult
201Created
401Unauthorized
403Forbidden
404Not Found

响应参数:

参数名称参数说明类型schema
codeinteger(int32)integer(int32)
dataobject
errorMessagestring
hoststring

响应示例:

javascript
{
    "host":null,
    "code":200,
    "errorMessage":"操作成功",
    "data":{
        "islike":false,
        "isunlike":false,
        "iscollection":false,
        "isfollow":false
    }
}

jackson进行序列化和反序列化解决

image-20210727184750641

  • 当后端响应给前端的数据中包含了id或者特殊标识(可自定义)的时候,把当前数据进行转换为String类型
  • 当前端传递后后端的dto中有id或者特殊标识(可自定义)的时候,把当前数据转为Integer或Long类型。

特殊标识类说明:

IdEncrypt 自定义注解 作用在需要转换类型的字段属性上,用于非id的属性上 在model包下

java
package com.heima.model.common.annotation;

import com.fasterxml.jackson.annotation.JacksonAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@JacksonAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface IdEncrypt {
}

序列化和反序列化类说明:以下类理解为主,可直接在资料文件夹下拷贝到leadnews-common模块中使用。

  • ConfusionSerializer 用于序列化自增数字的混淆

    java
    public class ConfusionSerializer extends JsonSerializer<Object> {
    
        @Override
        public  void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {
            try {
                if (value != null) {
                    jsonGenerator.writeString(value.toString());
                    return;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            serializers.defaultSerializeValue(value, jsonGenerator);
        }
    }
  • ConfusionDeserializer 用于反序列化自增数字的混淆解密

    java
    public class ConfusionDeserializer extends JsonDeserializer<Object> {
    
        JsonDeserializer<Object>  deserializer = null;
        JavaType type =null;
    
        public  ConfusionDeserializer(JsonDeserializer<Object> deserializer, JavaType type){
            this.deserializer = deserializer;
            this.type = type;
        }
    
        @Override
        public  Object deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException{
            try {
                if(type!=null){
                    if(type.getTypeName().contains("Long")){
                        return Long.valueOf(p.getValueAsString());
                    }
                    if(type.getTypeName().contains("Integer")){
                        return Integer.valueOf(p.getValueAsString());
                    }
                }
                return IdsUtils.decryptLong(p.getValueAsString());
            }catch (Exception e){
                if(deserializer!=null){
                    return deserializer.deserialize(p,ctxt);
                }else {
                    return p.getCurrentValue();
                }
            }
        }
    }
  • ConfusionSerializerModifier 用于过滤序列化时处理的字段

    java
    public class ConfusionSerializerModifier extends BeanSerializerModifier {
    
        @Override
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
                                                         BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
            List<BeanPropertyWriter> newWriter = new ArrayList<>();
            for(BeanPropertyWriter writer : beanProperties){
                String name = writer.getType().getTypeName();
                if(null == writer.getAnnotation(IdEncrypt.class) && !writer.getName().equalsIgnoreCase("id")){
                    newWriter.add(writer);
                } else {
                    writer.assignSerializer(new ConfusionSerializer());
                    newWriter.add(writer);
                }
            }
            return newWriter;
        }
    }
  • ConfusionDeserializerModifier 用于过滤反序列化时处理的字段

    java
    public class ConfusionDeserializerModifier extends BeanDeserializerModifier {
    
        @Override
        public BeanDeserializerBuilder updateBuilder(final DeserializationConfig config, final BeanDescription beanDescription, final BeanDeserializerBuilder builder) {
            Iterator it = builder.getProperties();
    
            while (it.hasNext()) {
                SettableBeanProperty p = (SettableBeanProperty) it.next();
                if ((null != p.getAnnotation(IdEncrypt.class)||p.getName().equalsIgnoreCase("id"))) {
                    JsonDeserializer<Object> current = p.getValueDeserializer();
                    builder.addOrReplaceProperty(p.withValueDeserializer(new ConfusionDeserializer(p.getValueDeserializer(),p.getType())), true);
                }
            }
            return builder;
        }
    }
  • ConfusionModule 用于注册模块和修改器

    java
    public class ConfusionModule extends Module {
    
        public final static String MODULE_NAME = "jackson-confusion-encryption";
        public final static Version VERSION = new Version(1,0,0,null,"heima",MODULE_NAME);
    
        @Override
        public String getModuleName() {
            return MODULE_NAME;
        }
    
        @Override
        public Version version() {
            return VERSION;
        }
    
        @Override
        public void setupModule(SetupContext context) {
            context.addBeanSerializerModifier(new ConfusionSerializerModifier());
            context.addBeanDeserializerModifier(new ConfusionDeserializerModifier());
        }
    
        /**
         * 注册当前模块
         * @return
         */
        public static ObjectMapper registerModule(ObjectMapper objectMapper){
            //CamelCase策略,Java对象属性:personId,序列化后属性:persionId
            //PascalCase策略,Java对象属性:personId,序列化后属性:PersonId
            //SnakeCase策略,Java对象属性:personId,序列化后属性:person_id
            //KebabCase策略,Java对象属性:personId,序列化后属性:person-id
            // 忽略多余字段,抛错
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    //        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
            return objectMapper.registerModule(new ConfusionModule());
        }
    
    }
  • InitJacksonConfig 提供自动化配置默认ObjectMapper,让整个框架自动处理id混淆

    java
    @Configuration
    public class InitJacksonConfig {
    
        @Bean
        public ObjectMapper objectMapper() {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper = ConfusionModule.registerModule(objectMapper);
            return objectMapper;
        }
    
    }

在common模块中的自动配置的spring.factories中添加如下内容

java
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.heima.common.swagger.SwaggerConfiguration,\
  com.heima.common.swagger.Swagger2Configuration,\
  com.heima.common.exception.ExceptionCatch,\
  com.heima.common.aliyun.GreenTextScan,\
  com.heima.common.aliyun.GreenImageScan,\
  com.heima.common.jackson.InitJacksonConfig

在dto中传递参数的时候如果想要把数值类型转为json,可以使用@IdEncrypt标识字段进行转换,如下:

java
@Data
public class ArticleInfoDto {
    
    // 文章ID
    @IdEncrypt
    Long articleId;
}