Loading

结对作业二

这个作业属于哪个课程 2021春软件工程实践 W班 (福州大学)
这个作业要求在哪里 结对作业二
结对学号 081800306&041802224
这个作业的目标 顶会热词统计

PSP表格和效能分析

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
• Estimate • 估计这个任务需要多少时间 30 30
Development 开发
• Analysis • 需求分析 30 60
• Design Review • 设计复审 10 20
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 20 30
• Design • 具体设计 60 60
• Coding • 具体编码 3000 3500
• Code Review • 代码复审 60 90
• Test • 测试(自我测试,修改代码,提交修改) 10 30
Reporting 报告 50 60
• Test Report • 测试报告 30 30
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 10 10
合计 3310 3920

Github地址&代码规范

项目地址

访问地址

地址

成果展示

项目的首页,由导航栏、搜索框、词云构成

导航栏由首页、论文检索、热度分析构成,支持三个页面的跳转

词云由echarts-wordcloud引入,从数据库中获取关键词简易排列

论文检索页面,在下拉框中可选择关键词、编号、内容、标题的方式检索论文

按照关键词检索论文

按照编号检索论文

按照标题检索论文

点击论文列表的修改图标跳转到详情修改界面

修改标题为TEST

查询标题为TEST的论文

热词分析页面,展示近三年三个会议的关键词统计

切换显示近三年各个会议的热词走势

结对过程

题目刚发出来的那天,由于我们宿舍比较近,我们就开始讨论,这次作业肯定是在结对一基础上完成,因此页面设计要符合原型设计。然后我们就开始讨论需求分析,设计数据库。技术选型的话,我们刚开始想采用前后端分离,后端提供api,前端调用,后来由于作业命名规范,存在特殊字符,前端框架不能使用。解决办法是要么改写前端框架的底层或者另选技术,我们选择后者,半分离开发,最终确定是koa2框架+ejs模板渲染的技术。资料查询是去官网学习,或者网上的博客。时不时还会进行代码合并检查有没有重大的错误什么的。

实现过程

  • 前端
    页面的标题栏复用,
    首页的词云使用了echarts-wordcloud
    论文列表页
    分析页面使用echarts的饼图和折线图
    html、css设计页面,jquery ajax请求接口返回数据给图表

  • 后端
    使用基于nodejs的koa框架
    sequelize创建数据库表对应的模型,生成增删改查的数据库方法
    路由接口提供数据

  • 数据库设计

  • 功能结构图

代码说明

sequelize根据数据库表article创建模型,编写所需的数据库语句
getArticles方法用来显示查询时新增显示5条论文记录,getArticleByXX分别对应了按照关键词等查询方法

    static async getArticles(list, page) {
        let ids = [];
        for (let i = page * 5; i < page * 5 + 5; i++) {
            ids.push(list[i].arcid);
        }
        return await Article.findAll({
            where: {
                id: {
                    [Op.in]: ids,
                },
            },
        });
    }
static async getArticleByid(id) {
        let article = await Article.findOne({
            where: {
                id,
            },
        });
        return article;
    }
    static async getArticleByContent(content) {
        let article = await Article.findAll({
            where: {
                content,
            },
        });
        return article;
    }
    static async getArticleByTitle(id) {
        let article = await Article.findAll({
            where: {
                id,
            },
        });
        return article;
    }

    static async getIdsByKey(key) {
        let ids = await keywords.getIdByKey(key);
        return ids;
    }

    static async updateArticle({ id, title, conclude, link, magazine }) { // eslint-disable-line
        await Article.update({ title, conclude, link, magazine }, { // eslint-disable-line
            where: { id },
        });
    }

    static async deleteArticle({ id }) {
        await Article.destroy({
            where: {
                id,
            },
        });
    }
  }
  Article.init({
    id: {
        type: DataTypes.STRING,
        primaryKey: true,
    },
    title: DataTypes.STRING,
    year: DataTypes.INTEGER,
    conclude: DataTypes.STRING,
    link: DataTypes.STRING,
    magazine: DataTypes.STRING,
}, {
    sequelize,
    tableName: 'article',
});

module.exports = {
    Article,
};

论文查询页面的路由请求,从页面中获取用户输入的key和选择的查询类型kind,返回给数据库model类的方法返回相应的数据:ctx.body = articles

    let key = ctx.query.key; // eslint-disable-line
    let page = ctx.query.page || 0;
    let kind = ctx.query.kind; // eslint-disable-line
    let articles;
    let list;
    if (key && (kind == 1)) { // eslint-disable-line
        console.log(key);
        list = await Article.getIdsByKey(key);
        articles = await Article.getArticles(list, page);
    }
    if (key && (kind == 0)) { // eslint-disable-line
        articles = await Article.getArticleByid(key);
    }
    if (key && (kind == 2)) { // 内容
        list = await Article.getIdsByKey(key);
        articles = await Article.getArticles(list, page);
    }
    if (key && (kind == 3)) { // 标题
        list = await Article.getIdsByKey(key);
        articles = await Article.getArticles(list, page);
    }
    ctx.body = articles;
});
module.exports = router;

前端复用的标题栏,设置链接再由window.location跳转相应的界面

    <div class="logo">
    </div>
    <div class="items">
        <a class="item" index="1">首页</a>
        <a class="item">论文检索</a>
        <a class="item">热度分析</a>
    </div>
</div>
<script>
    let items = document.querySelector('.items')
    items.addEventListener('click', (e) => {
        let str = e.target.innerHTML
        if (str === '首页') {
            window.location.href = '/'
        } else if (str === '论文检索') {
            window.location.href = '/list'
        } else if (str === '热度分析') {
            window.location.href = '/hot'
        }
        console.log(str === '首页');
    })
</script>

使用ejs引擎将js嵌入html中,用来展示查询到的论文信息,foreach中填充所有的信息到item

    <% if (articles) {%>
    <% articles.forEach(function(article){ %>
      <%- include('./item.html', {article}); %>
    <% }); %>
    <%}%>
  </div>

上面代码中list中的item,显示请求得到的论文的标题和简介信息

  <p class="piece-title"><%= article.title %></p>
  <p class="piece-content"><%= article.conclude%></p>
</div>

列表数据通过jquery的ajax方法获取,url设置对应的请求链接,success方法获取得到的res即论文数据,再循环嵌入到展示论文列表的html中

          type: 'GET',
          url: `http://localhost:3000/getArticles?key=${key}&page=${page}&kind=${kind}`,
          success(res) {
            console.log(res);
            if (!Array.isArray(res)) {
              res = [res]
            }
            let $lis = ''
            $('.list').empty()
            for(let i = 0; i < res.length; i++) {
              $lis += `<div class="piece"><a href="/detail?id=${res[i].id}"><i><svg t="1616326953943" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4559" width="32" height="32"><path d="......" fill="#e6e6e6" p-id="4561"></path><path d="......" fill="#e6e6e6" p-id="4562"></path><path d="......" fill="#e6e6e6" p-id="4563"></path></svg></i></a>
                <p class="piece-title">${res[i].title}</p>
                <p class="piece-content">
                  ${res[i].conclude}
                </p>
                <a class="link" href="${res[i].link}" target="_blank">原文链接</a>
              </div>`
                        }
                        $('.list').append($lis)
                    }
                })

在成功跳转链接后展示echart饼图并设置相应的数据,使用了导入的echart.js模板

        type: 'POST',
        url: 'http://localhost:3000/hot/gettop',
        //http://150.158.180.107:3000
        success(res) {
            var data1 = []
            data1 = res
            var chartDom1 = document.getElementById('container1');
            var myChart1 = echarts.init(chartDom1);
            var option1;

            option1 = {
                title: {
                    text: '关键词TOP10',
                    left: 'center',
                    textStyle: {
                        color: '#fff'
                    }
                },
                tooltip: {
                    trigger: 'item'
                },

                series: [{
                    name: '关键词',
                    type: 'pie',
                    radius: '50%',
                    data: data1,
                    emphasis: {
                        itemStyle: {
                            shadowBlur: 10,
                            shadowOffsetX: 0,
                            shadowColor: 'rgba(0, 0, 0, 0.5)'
                        }
                    }
                }]
            };

            option1 && myChart1.setOption(option1);
        }
    })

项目入口app.js,设置注册信息

const app = new Koa();
ejs.clearCache();
app.use(bodyparser());
app.use(serve(path.join(__dirname, '/public')));
app.use(views('view', {
    root: path.join(__dirname, '/view'),
    map: { html: 'ejs' },
}));

function registerRouters(item) {
    if (item instanceof Router) {
        app.use(item.routes());
    }
}
const modules = new RequireDirectory(module, './routes', { visit: registerRouters });

项目用到的依赖包

    "name": "work",
    "version": "1.0.0",
    "description": "",
    "main": "app.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "lint": "eslint src",
        "lint:create": "eslint --init"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "ejs": "^3.1.6",
        "eslint-config-airbnb-base": "^14.2.1",
        "eslint-plugin-import": "^2.22.1",
        "koa": "^2.13.1",
        "koa-bodyparser": "^4.3.0",
        "koa-router": "^10.0.0",
        "koa-static": "^5.0.0",
        "koa-views": "^7.0.1",
        "mysql2": "^2.2.5",
        "require-directory": "^2.1.1",
        "sequelize": "^6.6.2"
    },
    "devDependencies": {
        "eslint": "^7.22.0"
    }
}

心路历程

hanmajack(0418022224):

这次的作业用到的技术对我来说很陌生,web的基础掌握的也不够好,花费了大量的时间去看API文档、博客等。一路上遇到了很多困难,也都一一与队友讨论解决了。总的来说这次结对的体验很好,让我学到了很多新知识。

陈志君(081800306):

这次的作业的技术选型本来想采用前后端分离,前端采用vue框架来完成,但是由于作业文件夹命名必须有&字符,Vue框架就不能采用了,所以我们决定用web半分离的方式来开发,前端通过模板语法来渲染。学到了挺多新知识,包括数据库的高级查询,服务器的部署等等。收获蛮大的

队友评价

hanmajack -> 从零开始的代码生活:

队友很给力,执行力很强,自己的部分总是很快就完成实现了。而且队友在我遇到困难时给了我很多帮助,在编程方面指导我,很耐心的给我讲解,很庆幸这次结对作业遇到了这么好的队友。

陈志君 -> hanmajack

队友学习能力很强,很勤奋,刚开始对web可能不太熟悉,但是他课后自学,b站看视频,csdn上看博客,一步一步提升自己,给队友一个大大的好评

posted @ 2021-03-31 15:38  hanmajack  阅读(115)  评论(11编辑  收藏  举报