基于Flink的个人装扮商城群体用户画像与数据实时统计系统(六)-需求集C实现

一、需求集C有什么?

所有需求link:基于Flink的个人装扮商城群体用户画像与数据实时统计系统(二)-项目介绍与需求介绍

  需求集C是针对模拟生成的用户购买商品的信息提出的,包括:

  • 群体用户画像之当日支付类型偏好
  • 各类产品每日销售额实时Top5
  • 群体用户画像之每月实时消费水平&用户每月实时消费标签

附:模拟生成的用户购买商品的信息字段
在这里插入图片描述

二、模拟生成用户购买商品的信息

  • 用户购买商品的信息实体类编写:ConsumeInfo
package cn.edu.neu.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 32098
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ConsumeInfo {
    /**
     * 订单ID
     */
    private String orderId;
    /**
     * 用户ID
     */
    private String userId;
    /**
     * 商品ID
     */
    private String productId;
    /**
     * 商品类型
     */
    private String productType;
    /**
     * 商品数量
     */
    private int count;
    /**
     * 商品单价
     */
    private double unitPrice;
    /**
     * 支付类型
     */
    private String payType;
    /**
     * 支付时间
     */
    private long payTime;
    /**
     * 优惠券金额
     */
    private double couponAmount;
}

  • 用户购买商品的信息模拟生成:ConsumeInfoSource
package cn.edu.neu.source;

import cn.edu.neu.bean.ConsumeInfo;
import jdk.nashorn.internal.ir.annotations.Ignore;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.source.RichParallelSourceFunction;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * @author 32098
 */
public class ConsumeInfoSource extends RichParallelSourceFunction<ConsumeInfo> {
    private boolean keepMockData;

    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        keepMockData = true;
    }

    @Override
    public void run(SourceContext<ConsumeInfo> sourceContext) throws Exception {
        // 订单ID
        String orderId;
        // 用户ID
        String userId;
        // 商品ID
        String productId;
        // 商品类型
        List<String> productTypeList = Arrays.asList("休闲服类", "运动服类", "西服类", "运动鞋类", "休闲鞋类", "包类", "皮鞋类", "拖鞋类", "靴类");
        String productType;
        // 商品数量
        int count;
        // 商品单价
        double unitPrice;
        // 支付类型
        List<String> payTypeList = Arrays.asList("微信", "微信", "微信", "支付宝", "支付宝", "掌上银行");
        String payType;
        // 支付时间
        long payTime;
        // 优惠券金额
        double couponAmount;

        Random r = new Random();
        productType = productTypeList.get(r.nextInt(productTypeList.size()));
        payType = payTypeList.get(r.nextInt(payTypeList.size()));
        while (keepMockData) {
            for(int i=0; i<r.nextInt(20); i++){
                orderId = RandomStringUtils.randomAlphanumeric(12);
                userId = RandomStringUtils.randomNumeric(3);
                productId = RandomStringUtils.randomAlphabetic(8);
                if(r.nextDouble()>0.5){
                    productType = productTypeList.get(r.nextInt(productTypeList.size()));
                }
                count = r.nextInt(10)+1;
                unitPrice = Math.abs(r.nextGaussian()*30+300);
                if(r.nextDouble()>0.5){
                    payType = payTypeList.get(r.nextInt(payTypeList.size()));
                }
                payTime = System.currentTimeMillis() + 2000;
                couponAmount = r.nextInt(10);
                sourceContext.collect(new ConsumeInfo(orderId, userId, productId, productType, count, unitPrice, payType, payTime, couponAmount));
            }
            Thread.sleep(2000);
        }
    }

    @Override
    public void cancel() {
        keepMockData = false;
    }
}

三、需求集C实现

  • 群体用户画像之当日支付类型偏好
package cn.edu.neu.task.windowTask;

import cn.edu.neu.bean.ConsumeInfo;
import cn.edu.neu.bean.Statics;
import cn.edu.neu.sink.StaticsSink;
import cn.edu.neu.source.ConsumeInfoSource;
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.triggers.ContinuousProcessingTimeTrigger;

/**
 * @author 32098
 *
 * 群体用户画像之支付类型偏好
 */
public class PayTypeTask {
    public static void main(String[] args) {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.STREAMING);

        DataStreamSource<ConsumeInfo> consumeInfoDs = env.addSource(new ConsumeInfoSource());

        SingleOutputStreamOperator<Statics> resultDs = consumeInfoDs.map(new MapFunction<ConsumeInfo, Tuple2<String, Long>>() {
            @Override
            public Tuple2<String, Long> map(ConsumeInfo consumeInfo) throws Exception {
                String payType = consumeInfo.getPayType();
                return Tuple2.of(payType, 1L);
            }
        }).keyBy(e -> e.f0).window(TumblingProcessingTimeWindows.of(Time.days(1))).trigger(ContinuousProcessingTimeTrigger.of(Time.seconds(10))).reduce(new ReduceFunction<Tuple2<String, Long>>() {
            @Override
            public Tuple2<String, Long> reduce(Tuple2<String, Long> tA, Tuple2<String, Long> tB) throws Exception {
                return Tuple2.of(tA.f0, tA.f1+tB.f1);
            }
        }).map(new MapFunction<Tuple2<String, Long>, Statics>() {
            @Override
            public Statics map(Tuple2<String, Long> tupleIn) throws Exception {
                return new Statics("payType", tupleIn.f0, tupleIn.f1);
            }
        });

        resultDs.addSink(new StaticsSink());

        try {
            env.execute("payType analysis");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  • 各类产品当日实时销售额Top5(前台页面展示Top5)
package cn.edu.neu.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 32098
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ProductTypeSaleStatic {
    private String productType;
    private Double totalSale;
}
package cn.edu.neu.task.windowTask;

import cn.edu.neu.bean.ConsumeInfo;
import cn.edu.neu.bean.ProductTypeSaleStatic;
import cn.edu.neu.sink.ProductTypeSaleSink;
import cn.edu.neu.source.ConsumeInfoSource;
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.triggers.ContinuousProcessingTimeTrigger;

/**
 * @author 32098
 *
 * 各类产品每天销售额实时统计:10触发一次计算
 */
public class ProductTypeSaleTask {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.STREAMING);

        DataStreamSource<ConsumeInfo> consumeInfoDs = env.addSource(new ConsumeInfoSource());

        SingleOutputStreamOperator<ProductTypeSaleStatic> productTypeTotalSaleDs = consumeInfoDs.map(new MapFunction<ConsumeInfo, Tuple2<String, Double>>() {
            @Override
            public Tuple2<String, Double> map(ConsumeInfo consumeInfo) throws Exception {
                String productType = consumeInfo.getProductType();
                double totalAmount = consumeInfo.getUnitPrice() * consumeInfo.getCount() - consumeInfo.getCouponAmount();
                return Tuple2.of(productType, totalAmount);
            }
        }).keyBy(e -> e.f0).window(TumblingProcessingTimeWindows.of(Time.days(1))).trigger(ContinuousProcessingTimeTrigger.of(Time.seconds(10))
        ).reduce(new ReduceFunction<Tuple2<String, Double>>() {
            @Override
            public Tuple2<String, Double> reduce(Tuple2<String, Double> inA, Tuple2<String, Double> inB) throws Exception {
                return Tuple2.of(inA.f0, inA.f1 + inB.f1);
            }
        }).map(new MapFunction<Tuple2<String, Double>, ProductTypeSaleStatic>() {
            @Override
            public ProductTypeSaleStatic map(Tuple2<String, Double> in) throws Exception {
                return new ProductTypeSaleStatic(in.f0, in.f1);
            }
        });

        productTypeTotalSaleDs.addSink(new ProductTypeSaleSink());

        env.execute();
    }
}
  • 群体用户画像之每月实时消费水平&用户每月实时消费标签
package cn.edu.neu.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 32098
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserConsumptionLevelStatics {
    private String uid;
    private double avgAmount;
    private String level;
}
package cn.edu.neu.task.windowTask;

import cn.edu.neu.bean.ConsumeInfo;
import cn.edu.neu.bean.Statics;
import cn.edu.neu.bean.UserConsumptionLevelStatics;
import cn.edu.neu.sink.ConsumptionLevelStaticsSink;
import cn.edu.neu.sink.StaticsSink;
import cn.edu.neu.source.ConsumeInfoSource;
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.triggers.ContinuousProcessingTimeTrigger;
import scala.tools.nsc.Global;

import java.math.BigDecimal;
import java.text.NumberFormat;

/**
 *
 * @author 32098
 *
 * 1.群体用户画像之每月实时消费水平
 * 2.用户每月实时消费标签
 */
public class ConsumptionLevelTask {
    public static void main(String[] args) {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.STREAMING);

        DataStreamSource<ConsumeInfo> consumeInfoDs = env.addSource(new ConsumeInfoSource());

        SingleOutputStreamOperator<Tuple2<String, Double>> userAvgConsumeDs = consumeInfoDs.map(new MapFunction<ConsumeInfo, Tuple3<String, Double, Integer>>() {
            @Override
            public Tuple3<String, Double, Integer> map(ConsumeInfo consumeInfo) throws Exception {
                String userId = consumeInfo.getUserId();
                double totalAmount = consumeInfo.getUnitPrice() * consumeInfo.getCount() - consumeInfo.getCouponAmount();
                return Tuple3.of(userId, totalAmount, 1);
            }
        }).keyBy(e -> e.f0).window(TumblingProcessingTimeWindows.of(Time.days(30))).trigger(ContinuousProcessingTimeTrigger.of(Time.seconds(10))
        ).reduce(new ReduceFunction<Tuple3<String, Double, Integer>>() {
            @Override
            public Tuple3<String, Double, Integer> reduce(Tuple3<String, Double, Integer> inA, Tuple3<String, Double, Integer> inB) throws Exception {
                return Tuple3.of(inA.f0, inA.f1 + inB.f1, inA.f2 + inA.f2);
            }
        }).map(new MapFunction<Tuple3<String, Double, Integer>, Tuple2<String, Double>>() {
            @Override
            public Tuple2<String, Double> map(Tuple3<String, Double, Integer> in) throws Exception {
                return Tuple2.of(in.f0, in.f1 / in.f2);
            }
        });
        NumberFormat nbf= NumberFormat.getInstance();
        SingleOutputStreamOperator<UserConsumptionLevelStatics> resultDs = userAvgConsumeDs.map(new MapFunction<Tuple2<String, Double>, UserConsumptionLevelStatics>() {
            @Override
            public UserConsumptionLevelStatics map(Tuple2<String, Double> tupleIn) throws Exception {
                String uid = tupleIn.f0;

                double avgAmount = tupleIn.f1;

                String level = "低消费水平";
                if (avgAmount >= 800 && avgAmount < 1800) {
                    level = "中消费水平";
                } else if (avgAmount >= 1800) {
                    level = "高消费水平";
                }

                BigDecimal bigDecimal = new BigDecimal(avgAmount);

                return new UserConsumptionLevelStatics(uid, bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(), level);
            }
        });

        resultDs.addSink(new ConsumptionLevelStaticsSink());

        try {
            env.execute();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

附:

  • 上述需求实现涉及的Sink类:ProductTypeSaleSink
package cn.edu.neu.sink;

import cn.edu.neu.bean.ProductTypeSaleStatic;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

/**
 * @author 32098
 */
public class ProductTypeSaleSink extends RichSinkFunction<ProductTypeSaleStatic> {
    private Connection conn = null;
    private PreparedStatement ps = null;

    @Override
    public void open(Configuration parameters) throws Exception {
        conn = DriverManager.getConnection("jdbc:mysql://master:3306/user_portrait", "root", "Hive@2020");
        String sql = "";

        sql = "insert into product_type_total_sale(product_type, total_sale) values (?,?) on duplicate key update total_sale=?";

        ps = conn.prepareStatement(sql);
    }

    @Override
    public void invoke(ProductTypeSaleStatic value, Context context) throws Exception {
        ps.setString(1, value.getProductType());
        ps.setDouble(2, value.getTotalSale());
        ps.setDouble(3, value.getTotalSale());
        ps.executeUpdate();
    }

    @Override
    public void close() throws Exception {
        if (conn != null) {
            conn.close();
        }
        if (ps != null) {
            ps.close();
        }
    }
}

  • 上述需求实现涉及的Sink类:ConsumptionLevelStaticsSink
package cn.edu.neu.sink;

import cn.edu.neu.bean.Statics;
import cn.edu.neu.bean.UserConsumptionLevelStatics;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

/**
 * @author 32098
 */
public class ConsumptionLevelStaticsSink extends RichSinkFunction<UserConsumptionLevelStatics> {
    private Connection conn = null;
    private PreparedStatement ps = null;

    @Override
    public void open(Configuration parameters) throws Exception {
        conn = DriverManager.getConnection("jdbc:mysql://master:3306/user_portrait", "root", "Hive@2020");
        String sql = "";

        sql = "insert into user_consumption_level(userId, avg_amount, level) values (?,?,?) on duplicate key update avg_amount=?, level=?";

        ps = conn.prepareStatement(sql);
    }

    @Override
    public void invoke(UserConsumptionLevelStatics statics, Context context) throws Exception {
        ps.setString(1, statics.getUid());
        ps.setDouble(2, statics.getAvgAmount());
        ps.setString(3, statics.getLevel());
        ps.setDouble(4, statics.getAvgAmount());
        ps.setString(5, statics.getLevel());
        ps.executeUpdate();
    }

    @Override
    public void close() throws Exception {
        if (conn != null) {
            conn.close();
        }
        if (ps != null) {
            ps.close();
        }
    }
}

  • Mysql数据库表:
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
用户画像,作为一种勾画目标用户、联系用户诉求与设计方向的有效工具,用户画像在各领域得到了广泛的应用。用户画像最初是在电商领域得到应用的,在大数据时代背景下,用户信息充斥在网络中,将用户的每个具体信息抽象成标签,利用这些标签将用户形象具体化,从而为用户提供有针对性的服务。还记得年底收到的支付宝年度消费账单吗?帮助客户回顾一年的消费细节,包括消费能力、消费去向、信用额度等等,再根据每位客户的消费习惯,量身定制商品推荐列表……这一活动,将数据这个量化的词以形象生动的表现手法推到了大众面前。这就是用户画像在电商领域的一个应用,随着我国电子商务的高速发展,越来越多的人注意到数据信息对于电商市场的推动作用。基于数据分析的精准营销方式,可以最大限度的挖掘并留住潜在客户,数据统计与分析为电商市场带来的突破不可估量。在大数据时代,一切皆可“量化”,看似普通的小小数字背后,蕴藏着无限商机,也正在被越来越多的企业所洞悉。如何从大数据中挖掘商机?建立用户画像和精准化分析是关键。什么是用户画像呢?用户画像是根据市场研究和数据,创建的理想中客户虚构的表示。创建用户画像,这将有助于理解现实生活中的目标受众。企业创建的人物角色画像,具体到针对他们的目标和需求,并解决他们的问题,同时,这将帮助企业更加直观的转化客户。用户画像最重要的一个步骤就是对用户标签化,我们要明确要分析用户的各种维度,才能确定如何对用户进行画像用户画像建立步骤首先,基础数据收集,电商领域大致分为行为数据、内容偏好数据、交易数据,如浏览量、访问时长、家具偏好、回头率等等。而金融领域又有贷款信息,信用卡,各种征信信息等等。然后,当我们对用户画像所需要的基础数据收集完毕后,需要对这些资料进行分析和加工,提炼关键要素,构建可视化模型。对收集到的数据进行行为建模,抽象出用户的标签。电商领域可能是把用户的基本属性、购买能力、行为特征、兴趣爱好、心理特征、社交网络大致的标签化,而金融风控领域则是更关注用户的基本信息,风险信息,财务信息等等。随后,要利用大数据的整体架构对标签化的过程进行开发实现,对数据进行加工,将标签管理化。同时将标签计算的结果进行计算。这个过程中需要依靠Hive,Hbase等大数据技术,为了提高数据实时性,还要用到Flink,Kafka等实时计算技术。最后,也是最关键的一步,要将我们的计算结果,数据,接口等等,形成服务。比如,图表展示,可视化展示。基于Flink+Alink构建全端亿级实时用户画像系统课程,将带领大家一步一步实现一个强大的实时用户画像系统,该系统以热门的互联网电商实际业务应用场景为案例讲解,具体包含:标签管理(支持动态标签扩展,动态标签指标)、用户预测、用户群体画像用户行为画像用户中心、几大内容。本课程采用全新的大数据技术栈:Flink+Alink,让你体验到全新技术栈的强大,感受时代变化的气息,通过学习完本课程可以节省你摸索的时间,节省企业成本,提高企业开发效率。本课程包含的技术: 开发工具为:IDEA、WebStorm Flink1.13.0Alink1.5.0 ClickHouseDolphinSchedulerHadoopHbaseKafkaZookeeper SpringBoot2.0.8.RELEASE SpringCloud Finchley.SR2BinlogCanal MySQL MybatisVue.js、Nodejs、ElementUI 课程亮点: 1.与企业接轨、真实工业界产品2.标签化管理模块功能,支持动态标签扩展3.动态标签指标分析和维护4.Alink算法技术框架 5.大数据热门技术Flink新版本 6.主流微服务后端系统 7.数据实时同步解决方案 8.涵盖主流前端技术VUE+NodeJS+ElementUI 9.集成SpringCloud实现统一整合方案 10.互联网大数据企业热门技术栈 11.支持海量数据实时画像 12.支持全端实时画像 13.全程代码实操,提供全部代码和资料 14.提供答疑和提供企业技术方案咨询 
用户画像作为大数据的根基,它抽象出一个用户的信息全貌,为进一步精准、快速地分析用户行为习惯、消费习惯等重要信息,提供了足够的数据基础,奠定了大数据时代的基石。 用户画像,即用户信息标签化,就是企业通过收集与分析消费者社会属性、生活习惯、消费行为等主要信息的数据之后,抽象出一个用户的商业全貌作是企业应用大数据技术的基本方式。用户画像为企业提供了足够的信息基础,能够帮助企业快速找到精准用户群体以及用户需求等更为广泛的反馈信息。 用户画像系统能很好地帮助企业分析用户的行为与消费习惯,可以预测商品的发展的趋势,提高产品质量,同时提高用户满意度。构建一个用户画像,包括数据源端数据收集、数据预处理、行为建模、构建用户画像。有些标签是可以直接获取到的,有些标签需要通过数据挖掘分析到!本套课程会带着你一步一步的实现用户画像案例,掌握了本套课程内容,可以让你感受到Flink+ClickHouse技术架构的强大和大数据应用的广泛性。 在这个数据爆发的时代,像大型电商的数据量达到百亿级别,我们往往无法对海量的明细数据做进一步层次的预聚合,大量的业务数据都是好几亿数据关联,并且我们需要聚合结果能在秒级返回。 包括我们的画像数据,也是有这方便的需求,那怎么才能达到秒级返回呢?ClickHouse正好满足我们的需求,它是非常的强大的。 本课程采用Flink+ClickHouse技术架构实现我们的画像系统,通过学习完本课程可以节省你摸索的时间,节省企业成本,提高企业开发效率。希望本课程对一些企业开发人员和对新技术栈有兴趣的伙伴有所帮助,如对我录制的教程内容有建议请及时交流。项目中采用到的算法包含Logistic Regression、Kmeans、TF-IDF等,Flink暂时支持的算法比较少,对于以上算法,本课程将带大家用Flink实现,并且结合真实场景,学完即用。系统包含所有终端的数据(移动端、PC端、小程序端),支持亿级数据量的分析和查询,并且是实时和近实时的对用户进行画像计算。本课程包含的画像指标包含:概况趋势,基础属性,行为特征,兴趣爱好,风险特征,消费特征,营销敏感度,用户标签信息,用户群里,商品关键字等几大指标模块,每个指标都会带大家实现。课程所涵盖的知识点包括:开发工具为:IDEA FlinkClickhouseHadoopHbaseKafkaCanalbinlogSpringBootSpringCloudHDFSVue.jsNode.jsElemntUIEcharts等等 课程亮点: 1.企业级实战、真实工业界产品 2.ClickHouse高性能列式存储数据库 3.提供原始日志数据进行效果检测 4.Flink join企业级实战演练 5.第四代计算引擎Flink+ClickHouse技术架构6.微服务架构技术SpringBoot+SpringCloud技术架构7.算法处理包含Logistic Regression、Kmeans、TF-IDF等8.数据实时同步落地方案实操9.统计终端的数据(移动端、PC端、小程序端) 10.支撑亿级海量数据用户画像平台11.实时和近实时的对用户进行画像计算12.后端+大数据技术栈+前端可视化13.提供技术落地指导支持 14.课程凝聚讲师多年实战经验,经验直接复制15.掌握全部内容能独立进行大数据用户平台的设计和实操企业一线架构师讲授,代码在老师的指导下企业可以复用,提供企业解决方案。  版权归作者所有,盗版将进行法律维权。 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值