关于我的项目-智慧图书馆(金蝶云·苍穹)

本文介绍了基于金蝶云·苍穹平台完成的智慧图书馆项目,实现了书籍检索、借还等功能,并荣获国家三等奖。项目中运用协同过滤算法实现图书智能推荐,包括页面建模、自动填充单据代码和推荐算法的具体实现。作者计划将此项目作为毕业设计,进一步完善图书推荐功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

### 0.欢迎参加23年软件杯a6/b4,申请环境时,推荐码填7068,凭截图可后台私戳我一对一答疑+送资料~
(宣传一波嘿嘿)

1.前言

  • 项目来自2022年的中国软件杯A6赛题—智慧图书馆
  • 基于“金蝶云·苍穹”平台完成图书馆功能的设计
  • 项目基本是独立完成,最终有幸获得国家三等奖

2.项目展示

图书馆0802演示视频

3.项目描述

基于金蝶云·苍穹平台,通过社区自主学习并独立完成智慧图书馆项目的设计与实现
-> 功能:书籍检索,图书借还及预约委托,图书采购申请,借书单打印,个人中心,新增书籍,基础信息维护等
-> 涉及平台技术:页面开发,单据流转换,插件开发(Java)打印功能,到期预警,轻分析等
-> 项目设计:从基础资料(书籍-ISBN、书本-索书号、图书馆、中图法)到单据流转换(图书的采购申请单映射为图书采购订单);从书籍检索、借阅、申购到自动审批、书籍到期预警、轻分析卡片;

4.下一步(2022.10计划)

.

5.ItemCF实现(2023.3实现… 摆烂哈哈哈)

5.1 页面建模

其实就是单据啦
在这里插入图片描述

5.2 自动填充单据的相关代码(java插件,需要在页面注册)
package kd.lpqj.sa.plugins;

import akka.dispatch.Foreach;
import kd.bos.bill.AbstractBillPlugIn;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.entity.datamodel.IDataModel;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.servicehelper.user.UserServiceHelper;
import kd.lpqj.sa.plugins.utils.Recommend;
import org.apache.commons.collections.map.HashedMap;

import java.util.ArrayList;
import java.util.EventObject;
import java.util.HashMap;
import java.util.List;

public class BookRecommendPlugin extends AbstractBillPlugIn {

    @Override
    public void afterCreateNewData(EventObject e) {
        super.afterCreateNewData(e);
        //用户设置为:当前用户
//        DynamicObject dataEntity = this.getModel().getDataEntity();
        long currentUserId = UserServiceHelper.getCurrentUserId();//获取当前用户id
        QFilter qFilter = new QFilter("lpqj_borrowinfo.lpqj_borrow_person", QCP.equals, currentUserId);
        QFilter qFilter2 = new QFilter("lpqj_borrowinfo.enable", QCP.equals, 1);
        QFilter[] qFilters = {qFilter,qFilter2};

        DynamicObject[] query_record = BusinessDataServiceHelper.load("lpqj_borrowinfo", "id,number,lpqj_borror_book",qFilters);
//        lpqj_book_isbn   lpqj_borror_book
        QFilter[] qFilter3 = {new QFilter("lpqj_borrowinfo.lpqj_borrow_person", QCP.equals, currentUserId)};
        //查出 书籍isbn表的数据,填充到单据体
        int all = query_record.length;
        HashMap<String,DynamicObject> recommendMap = new HashMap<String,DynamicObject>();
        if(all > 0){
            for (int i = 0; i < all; i++) {
                QFilter[] qFilter_isbn = {new QFilter("number", QCP.equals, query_record[i].get("lpqj_borror_book.lpqj_textfield4"))};
                DynamicObject[] queryIsbn = BusinessDataServiceHelper.load("lpqj_book_isbn", "id,number",qFilter_isbn);
                System.out.println("queryIsbn[0]: "+queryIsbn[0]);//lpqj_book_isbn[id,masterid,name,number]
//              //查出一条书籍isbn,如果已经存在,则跳过;不存在就加上
                String isbn = query_record[i].get("lpqj_borror_book.lpqj_textfield4").toString();//978-7-111-67031-5
                if(recommendMap.get(isbn)==null){
                    recommendMap.put(isbn,queryIsbn[0]);
                }
            }
        }

        //在 seqMap<id,isbn> 表中遍历,取出values(isbn)
        //   根据isbn从recommendMap取出book
        int index = 0;
        List<DynamicObject> recommend = new Recommend().builderRecommend(currentUserId);
        int size = recommend.size();
        System.out.println(size);

        if (size > 0) {
            // 待新增分录的行数
            IDataModel dataModel = this.getModel();
            dataModel.batchCreateNewEntryRow("entryentity", size);
            for (DynamicObject book : recommend) {
                // 第 1 个参数(propName): 单据体列字段的字段标识
                // 第 2 个参数(value):对应列字段待填充的值
                // 第 3 个参数(rowIndex):行号, 从0开始计
                dataModel.setValue("lpqj_isbn", book.get("id"), index++);
            }
        }
    }
}
5.3 推荐算法代码(推荐算法函数)
package kd.lpqj.sa.plugins.utils;

import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.BusinessDataServiceHelper;

import java.util.*;
import java.util.stream.Collectors;

public class Recommend {
/*
在电影推荐系统的基础上修改 (可能存在用户数少于书籍数的情况)
int len = arr1.length>arr2.length?arr2.length:arr1.length;  //原来是arr1.length
 */
    public List<DynamicObject> builderRecommend(long UserId) {
        // 查询所有订单记录
        QFilter[] qFilter1 = {new QFilter("lpqj_borrowinfo.enable", QCP.equals, 1)};
        DynamicObject[] borrowList = BusinessDataServiceHelper.load("lpqj_borrowinfo", "id,number,lpqj_borror_book,lpqj_borrow_person", qFilter1);

        QFilter[] qFilter2 = {new QFilter("lpqj_book_isbn.enable", QCP.equals, 1)};
        DynamicObject[] bookList = BusinessDataServiceHelper.load("lpqj_book_isbn", "id,number", qFilter2);

        QFilter[] qFilter3 = {new QFilter("lpqj_bos_user_ext.enable", QCP.equals, 1)};
        DynamicObject[] userList = BusinessDataServiceHelper.load("bos_user", "id,number", qFilter3);
        //java.lang.RuntimeException: CAUSE: java.lang.NullPointerException

        // 定义一个分数集合用户去存储用户行为,根据购买记录赋分,购买则算1分
        List<ScoreDto> scoreList = new ArrayList<>();
        for (DynamicObject borrow : borrowList) {
            String isbn = (String) borrow.get("lpqj_borror_book.lpqj_textfield4");
            Long user = (Long) borrow.get("lpqj_borrow_person.id");
            ScoreDto scoreDto = new ScoreDto(user, isbn, 1d);
            scoreList.add(scoreDto);
            System.out.println("************借书记录:"+scoreDto.toString());
        }

        // 构建评分矩阵,行数是书籍数,列数是用户数
        double[][] scoreArr = new double[bookList.length][userList.length];
        for (ScoreDto scoreDto : scoreList) {
            Long currentUserId = scoreDto.getUserId();
            String currentBookIsbn = scoreDto.getbookId();
            for (int i = 0; i < bookList.length; i++) {
                DynamicObject book = bookList[i];
                if (currentBookIsbn.equals(String.valueOf(book.get("number")))) {
                    for (int j = 0; j < userList.length; j++) {
                        DynamicObject user = userList[j];
                        if (currentUserId.equals(user.get("id"))) {
                            scoreArr[i][j] += scoreDto.getScore();
                            break;
                        }
                    }
                    break;
                }
            }
        }
        System.out.println("************打印评分矩阵************");
        for (int i = 0; i < scoreArr.length; i++) {
            for (int j = 0; j < scoreArr[i].length; j++) {
                System.out.print(scoreArr[i][j] + "\t");
            }
            System.out.println();
        }

        // 根据评分矩阵构建相似度矩阵,基于物品协同,就是要求物品之间的相似度,所以二维数组的长和宽都是物品数
        double[][] similarScoreArr = new double[bookList.length][userList.length];
        for (int i = 0; i < bookList.length; i++) {
            for (int j = i; j < bookList.length; j++) {
                if (i == j) {
                    similarScoreArr[i][j] = 1;
                } else {
                    // 应用余弦相似度计算
                    double[] arr1 = scoreArr[i];
                    double[] arr2 = scoreArr[j];
                    double molecule = 0;
                    double denominator1 = 0;
                    double denominator2 = 0;
                    for (int k = 0; k < arr1.length; k++) {
                        denominator1 += Math.pow(arr1[k], 2);
                        denominator2 += Math.pow(arr2[k], 2);
                        molecule += arr1[k] * arr2[k];
                    }
                    // 分母
                    double denominator = Math.sqrt(denominator1) * Math.sqrt(denominator2);
                    if (denominator != 0) {
                        double resultScore = molecule / denominator;
                        similarScoreArr[i][j] = resultScore;
                        similarScoreArr[j][i] = resultScore;
                    }
                }
            }
        }
        System.out.println("************打印相似度矩阵************");
        for (int i = 0; i < similarScoreArr.length; i++) {
            for (int j = 0; j < similarScoreArr[i].length; j++) {
                System.out.print(similarScoreArr[i][j] + "\t");
            }
            System.out.println();
        }
        /*
            接下来就是 相似度矩阵 * 评分矩阵 = 推荐矩阵,
            这个思路就是一个用户对项目的评分是来源于其他那些与他相似的物品的评分
            相似度越高,分数的影响则越大,这个时候回顾一下以前学的矩阵相乘,就是前一个矩阵的每一行 * 后一个矩阵的每一列
         */
        // 定义推荐矩阵
        double[][] recommendScoreArr = new double[bookList.length][userList.length];
        /*
            由于前一个矩阵每一行 * 后一个矩阵每一列算起来比较麻烦,所以把后一个矩阵进行倒置一下,行变列,列变行,
            这样就变成前一个矩阵的每一行 * 后一个矩阵的每一行,比较容易计算和理解
         */
        double[][] invertScoreArr = new double[userList.length][bookList.length];
        for (int i = 0; i < scoreArr.length; i++) {
            for (int j = 0; j < scoreArr[i].length; j++) {
                invertScoreArr[j][i] = scoreArr[i][j];
            }
        }
        System.out.println("************打印倒置后的评分矩阵************");
        for (int i = 0; i < invertScoreArr.length; i++) {
            for (int j = 0; j < invertScoreArr[i].length; j++) {
                System.out.print(invertScoreArr[i][j] + "\t");
            }
            System.out.println();
        }
        for (int i = 0; i < recommendScoreArr.length; i++) {   
            for (int j = 0; j < recommendScoreArr[i].length; j++) {
                double[] arr1 = similarScoreArr[i]; 
                double[] arr2 = invertScoreArr[j];  
                double recommendScore = 0d;
                int len = arr1.length>arr2.length?arr2.length:arr1.length;  //原来是arr1.length
                for (int k = 0; k < len; k++) { //arr1.length
                    recommendScore += arr1[k] * arr2[k];
                }
                recommendScoreArr[i][j] = recommendScore;
            }
        }
        // 这里的推荐矩阵行数是书籍数量,列数是用户数,矩阵中的数据就是用户对书籍的预测喜好程度
        System.out.println("************打印推荐矩阵************");
        for (int i = 0; i < recommendScoreArr.length; i++) {
            for (int j = 0; j < recommendScoreArr[i].length; j++) {
                System.out.print(recommendScoreArr[i][j] + "\t");
            }
            System.out.println();
        }
        // 这里得到的矩阵实际上就是每个用户对每本书的预测评分,接下来看看要推荐的那个用户是哪一列数据,就把那一列数据取出来
        // 然后再按预测的分数从高到低进行排序
        int index = 0;
        for (int i = 0; i < userList.length; i++) {
            if ((Long) userList[i].get("id") == UserId) {
                index = i;
                break;
            }
        }

        List<ItemScoreDto> itemScoreDtoList = new ArrayList<>();
        for (int i = 0; i < recommendScoreArr.length; i++) {
            itemScoreDtoList.add(new ItemScoreDto(recommendScoreArr[i][index], bookList[i]));
        }

        // 按预测分数从高到低排序
        itemScoreDtoList.sort((o1, o2) -> o2.getItemScore().compareTo(o1.getItemScore()));
        // 打印最终推荐
        System.out.println("当前用户推荐的影片id为:");
        itemScoreDtoList.forEach(book -> System.out.println(book.getItem().get("name")));
        List<DynamicObject> collect = itemScoreDtoList.stream().map(ItemScoreDto::getItem).collect(Collectors.toList());

        QFilter[] qFilter4 = {new QFilter("lpqj_borrowinfo.enable", QCP.equals, 1),
                new QFilter("lpqj_borrowinfo.lpqj_borrow_person", QCP.equals, UserId)};
        DynamicObject[] list = BusinessDataServiceHelper.load("lpqj_borrowinfo", "id,number,lpqj_borror_book,lpqj_borrow_person", qFilter4);


        // 表示这个用户没有借过书
        if (list.length == 0) {
            return null;
        }
        // 去掉用户借过的书
        List<String> borrow_isbn = new ArrayList<>();
        List<DynamicObject> res = new ArrayList<DynamicObject>();

        List<DynamicObject> collect_temp = new ArrayList<>();
        for(DynamicObject  recommend_book: collect){
            collect_temp.add(recommend_book);
        }
        for(DynamicObject  recommend_book: collect_temp){
            for(DynamicObject borrow_record : list){
                String isbn1 = recommend_book.get("number").toString();
                String isbn2 = borrow_record.get("lpqj_borror_book.lpqj_textfield4").toString();
                System.out.println("isbn:"+isbn1);
                if(isbn1.equals(isbn2)&&collect.contains(recommend_book)){
                    collect.remove(recommend_book);
                }
            }
        }
        System.out.println("去重后推荐书籍数目:"+ collect.size());
        if (collect.size() > 5) {
            collect = collect.subList(0, 5);
        }
        return collect;
    }


    class ScoreDto {
        private Long userId;
        private String bookId;
        private Double score;

        public ScoreDto() {
        }

        public ScoreDto(Long userId, String bookId, Double score) {
            this.userId = userId;
            this.bookId = bookId;
            this.score = score;
        }

        public Long getUserId() {
            return userId;
        }

        public void setUserId(Long userId) {
            this.userId = userId;
        }

        public String getbookId() {
            return bookId;
        }

        public void setbookId(String bookId) {
            this.bookId = bookId;
        }

        public Double getScore() {
            return score;
        }

        public void setScore(Double score) {
            this.score = score;
        }

        @Override
        public String toString() {
            return "ScoreDto{" +
                    "userId=" + userId +
                    ", bookId='" + bookId + '\'' +
                    ", score=" + score +
                    '}';
        }
    }

    class ItemScoreDto {
        private Double itemScore;
        private DynamicObject book;

        public ItemScoreDto() {
        }

        public ItemScoreDto(Double itemScore, DynamicObject book) {
            this.itemScore = itemScore;
            this.book = book;
        }

        public DynamicObject getItem() {
            return book;
        }

        public void setItem(DynamicObject book) {
            this.book = book;
        }

        public Double getItemScore() {
            return itemScore;
        }

        public void setItemScore(Double itemScore) {
            this.itemScore = itemScore;
        }
    }
}

5.4 推荐结果

在这里插入图片描述

6.最后

抱歉各位uu,本文源码不公开喔🤗
(其实2022年的苍穹搭建还是有点复杂的,导出来的项目不是纯代码,建议多了解一下哇🤗)

这个项目在参赛期间有配套课程辅助学习(如下图,但是泥萌好像看不了了555)
智慧图书馆管理系统及开发
在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卅拓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值