Springboot+Java推荐算法,开发商品推荐系统+商品管理系统

面对海量的商品信息如何实现针对不同用户维度开展个性化商品推荐,实现用户线上选购商品,下订单,支付,物流配送等?本次毕设程序基于前后端分离开发模式,搭建系统网络商品推荐系统前台与系统后台商品管理系统,通过可以配置的方式一体化管理商品信息,推送商品内容,生成丰富的可视化统计分析。

一、程序设计

本次商品推荐及管理系统主要内容涉及:

主要功能模块:商品推荐网站前台,商品管理系统后台
主要包含技术:springboot,mybatisplus,mysql,javascript,vue.js,html,css
主要包含算法:基于用户协同过滤推荐算法

系统采用前后端分离的开发模式完成,商品推荐网站前台要采用Vue.js,javascript,html,CSS等技术实现。系统前后端数据交互,采用Ajax异步调用传输JSON实现。
商品推荐网站前台主要包括以下功能清单:

用户登录注册
商品轮播图
商品分类展示
商品推荐展示
用户购物车
订单管理
订单配送管理
个人中心
修改密码

商品管理系统后台主要包括以下功能清单:

管理员登录
商品管理
轮播图配置
热销商品配置
新品上线配置
为您推荐配置
商品分类管理
会员管理
订单管理

二、效果实现

网站登录

image.png

系统主页

image.png

商品详情

image.png

购物车

image.png

我的订单

image.png

后台商品管理

image.png

轮播图管理

image.png

订单管理

image.png

热销商品管理

image.png

其他效果省略

三、商品推荐设计

本次毕设系统在商品推荐算法设计中,主要采用基于用户协同过滤算法+商品内容关键词统计分析计算两种方式,其中基于用户协同过滤推荐算法主要利用用户历史购买商品的情况,开展相似用户计算,商品关键词统计则是按照商品特征开展计算,两种计算方式结合优化商品推荐精准度。系统推荐流程,如下图所示:


image.png

基于用户协同过滤推荐算法实现

java实现协同过滤推荐算法代码

 class UserBaseCF{
    public static final int USERSIZE=943;
    public static final int ITEMSIZE=1682;
    public static final int UN=10;//某一user的最近邻居数
    //public static final int IN=10;//某一item的最近邻居数
    public int [] num=new int[USERSIZE+1];//每个用户为几部评了分
    public double[] average=new double[USERSIZE+1];//每个user的平均打分
    public double[][] rate=new double[USERSIZE+1][ITEMSIZE+1];//评分矩阵
    public double[][] DealedOfRate=new double[USERSIZE+1][ITEMSIZE+1];//针对稀疏问题处理后的评分矩阵
    Neighbor[][] NofUser =new Neighbor[USERSIZE+1][UN+1];//每个用户的最近的UN个邻居
    List<Double> x=new LinkedList<Double>();//LinkedList按照对象加入的顺序存储
    List<Double> y=new LinkedList<Double>();
    public static void main(String args[]) throws Exception{
        UserBaseCF cf=new UserBaseCF();
        if(cf.readFile("bin/ml-data_0/u1.base")){
            System.out.println("请等待,正在分析");
            cf.getAvr();//得到average[]
            cf.dealRate();//得到DealedOfRate
            cf.getNofUser();//得到NofUser
            for(int i=1;i<=UN;i++){
                System.out.println(cf.NofUser[1][i].getID()+":"+cf.NofUser[1][i].getValue());
            }
            //读文件
            File inputFile=new File("bin/ml-data_0/u1.test");
            BufferedReader reader=null;
            if(!inputFile.exists()||inputFile.isDirectory())
                    throw new FileNotFoundException();
            reader=new BufferedReader(new FileReader(inputFile));           
            //写文件
            File outputFile=new File("bin/testResult.txt");
            FileWriter writer=null;
            if(!outputFile.exists())
                if(!outputFile.createNewFile())
                    System.out.println("输出文件创建失败");
            writer=new FileWriter(outputFile);
            String title ="UserID"+"\t"+"ItemID"+"\t"+"OriginalRate"+"\t"+"PredictRate"+"\r\n";
            writer.write(title);
            writer.flush();
            String[] part=new String[3];
            String tmpToRead="";
            String tmpToWrite="";
            while((tmpToRead=reader.readLine())!=null){
                part=tmpToRead.split("\t");
                int userID=Integer.parseInt(part[0]);
                int itemID=Integer.parseInt(part[1]);
                double originalRate=Double.parseDouble(part[2]);
                double predictRate=cf.predict(userID, itemID);
                cf.x.add(originalRate);
                cf.y.add(predictRate);
                tmpToWrite=userID+"\t"+itemID+"\t"+originalRate+"\t"+predictRate+"\r\n";
                writer.write(tmpToWrite);
                writer.flush();
            }
            System.out.println("分析完成,请打开工程目录下bin文件夹中的testResult.txt");
            System.out.println("利用RMSE分析结果为"+cf.analyse(cf.x, cf.y));           
        }
        else            
            System.out.println("失败");       
    }   
    //Chapter1:准备工作
    //1-1:读取文件内容,得到评分矩阵     1:读取成功       -1:读取失败
    public boolean readFile(String filePath){
        File inputFile=new File(filePath);
        BufferedReader reader=null;
        try {
            reader=new BufferedReader(new FileReader(inputFile));
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在"+e.getMessage());
            return false;
        }       
        String sentence="";
        String[] part=new String[3];
        try {
            while((sentence=reader.readLine())!=null){
                part=sentence.split("\t");
                int userID=Integer.parseInt(part[0]);
                int itemID=Integer.parseInt(part[1]);
                double Rate=Double.parseDouble(part[2]);
                //构造矩阵
                rate[userID][itemID]=Rate;
            }
        } catch (NumberFormatException|IOException e) {
            System.out.println("读文件发生错误"+e.getMessage());
            return false;
        }
        return true;    
    }
        //1-2计算每个用户的平均分
    public void getLen(){//计算每个用户为几部电影打分
        for(int i=1;i<=USERSIZE;i++){
            int n=0;
            for(int j=1;j<=ITEMSIZE;j++){
                if(rate[i][j]!=0)
                    n++;
            }
            num[i]=n;
        }   
    }
    public void getAvr(){
        getLen();
        int i,j;
        for(i=1;i<=USERSIZE;i++){
            double sum=0.0;
            for(j=1;j<rate[i].length;j++){//每个length都是ITEMSIZE=1682
                sum+=rate[i][j];
            }
            average[i]=sum/num[i];
        }
    }
        //1-3处理评分矩阵的稀疏问题(重要事项!!!)
        //重点处理该user对没有被评分的item,会打几分
        //暂时用1-2中计算出的平均分    
    public void dealRate(){
        int  i,j;
        for(i=1;i<=USERSIZE;i++){
            for(j=1;j<=ITEMSIZE;j++){
                if(rate[i][j]==0)
                    DealedOfRate[i][j]=average[i];
                else
                    DealedOfRate[i][j]=rate[i][j];
            }
        }
    }
    //Chapter2:聚类,找和某一用户有相同喜好的一类用户
        //2-1::Pearson计算向量的相似度
    public double Sum(double[] arr){
        double total=(double)0.0;
        for(double ele:arr)
            total+=ele;
        return total;
    }
    public double Mutipl(double[] arr1,double[] arr2,int len){
        double total=(double)0.0;
        for(int i=0;i<len;i++)
            total+=arr1[i]*arr2[i];
        return total;
    }
    public double Pearson(double[] x,double[] y){
        int lenx=x.length;
        int leny=y.length;
        int len=lenx;//小容错
        if(lenx<leny) len=lenx;
        else len=leny;  
        double sumX=Sum(x);
        double sumY=Sum(y);
        double sumXX=Mutipl(x,x,len);
        double sumYY=Mutipl(y,y,len);
        double sumXY=Mutipl(x,y,len);
        double upside=sumXY-sumX*sumY/len;
        //double downside=(double) Math.sqrt((sumXX-(Math.pow(sumX, 2))/len)*(sumYY-(Math.pow(sumY, 2))/len));
        double downside=(double) Math.sqrt((sumXX-Math.pow(sumX, 2)/len)*(sumYY-Math.pow(sumY, 2)/len));        
        //System.out.println(len+" "+sumX+" "+sumY+" "+sumXX+" "+sumYY+" "+sumXY);
        return upside/downside;
    }   
}

商品关键词统计分析计算

针对商品信息简历关键词库,采用TF-IDF对商品关键词进行加权分析处理,按照用户检索关键词匹配最佳商品推荐。
java实现关键词加权计算代码

//查询所有商品关键词库
 List<String> skeys = keywordsList.stream().map(item -> item.getSkeys()).collect(Collectors.toList());
        List skeyList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(skeys)) {
            Map<String, Integer> sMap = new HashMap<>();
            for (String item : skeys) {
                List<String> strings = Arrays.asList(item.split(","));
                if (CollectionUtils.isNotEmpty(strings)) {
                    strings.forEach(sItem -> {
                        if (StringUtils.isNotBlank(sItem)) {
                            if (sMap.containsKey(sItem)) {
                                sMap.put(sItem, sMap.get(sItem) + 1);
                            } else {
                                sMap.put(sItem, 1);
                            }
                        }
                    });
                }
            }
            for (Map.Entry<String, Integer> entry : sMap.entrySet()) {
                skeyList.add(new HashMap() {{
                    put("name", entry.getKey());
                    put("value", entry.getValue());
                }});
            }
        }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容