eggjs 怎么实现获取账单列表接口并且实现列表数据分页查询功能?

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: eggjs 怎么实现获取账单列表接口并且实现列表数据分页查询功能?

说明


该分页列表查询接口我自己分成三步写的,跟小册上的实现方式不太样,我是通过 mysql 的 limit 分页去实现的。


主要分成三个大的部分逻辑:


  1. 通过查询一个月里的账单所处的是哪一天,全都查出来,形成一个天数列表
  2. 通过天数列表,去查里面每一天的账单数据
  3. 最后统计这个月的所有账单



列表原型要求

可以查看原型,看前端账单需要怎样展示:账单是以时间天数作为一条数据,需要显示每天的账单信息,还有当月总支出,当月总收入,全部类型,月份选择。

bd9604a6d5a8488da65ef690265863bb.png



分析接口需要字段


入参字段有:查询时需要

curPage, // 当前页数 (默认为1)
pageSize, // 一页多少条(默认为5)
typeId, // 类型 'all'就是全部类型
billDate, // 账单日期


需要返回的字段有

data: {
  totalExpense, // 当月支出
  totalIncome, // 当月收入
  dataList: [{
    day, // 日期
  bills: {} // bill 数据表中的每一项账单
  }, ...] // 列表数据
  pageObj: {
    curPage, // 当前页数
  pageSize, // 一页多少条
  totalPage, // 总分页数
  totalRow, // 总条数
  }
}


实现获取账单列表接口

1、在控制层添加一个 list 方法

打开 /controller/bill.js,新增一个 list 方法,里面的大体逻辑


  1. 获取查询参数
  2. 拿到 token 获取用户信息 user_id
  3. 通过 user_id 获取列表分页数据
  4. 组装数据:需要通过日获取一天的账单列表数据
  5. 获取当月总支出、当月总收入、总条数



const moment = require('moment'); // JavaScript 日期处理类库
class BillController extends Controller {
  async list() {
    const { ctx, app } = this;
    try {
      /**
       *  curPage, // 当前页数 (默认为1)
       *  pageSize, // 一页多少条(默认为5)
       *  typeId, // 类型 'all'就是全部类型
       *  billDate, // YYYY-MM 账单日期
       * */ 
      // 1、获取查询参数
      const { curPage = 1, pageSize = 5, typeId = 'all', billDate } = ctx.query;
      console.log('1、获取查询参数',curPage,pageSize,typeId,billDate);
      // 2、拿到 token 获取用户信息 user_id
      const token = ctx.request.header.authorization;
      const decode = await app.jwt.verify(token, app.config.jwt.secret);
      if (!decode) return;
      let user_id = decode.id;
      console.log('2、拿到 token 获取用户信息 user_id',user_id);
      // 3、通过 user_id 获取列表分页数据
      const dayResult = await ctx.service.bill.list(user_id, typeId, billDate, curPage, pageSize);
      const dayList = JSON.parse(JSON.stringify(dayResult));
      console.log('3、通过 user_id 获取当前用户的账单列表',dayResult,dayList);
      // 4、组装数据:需要通过日获取一天的账单列表数据
      let dataList = [];
      for(var i = 0; i < dayList.length; i++) {
        const day = moment(dayList[i].day).format("YYYY-MM-DD");
        const month = moment(dayList[i].day).format("YYYY-MM");
        if(month === billDate) {
          const listResult = await ctx.service.bill.listByDay(user_id, typeId, billDate, day);
          const billsList = JSON.parse(JSON.stringify(listResult));
          console.log(day,listResult,billsList);
          dataList.push({
            day: day,
            bills: billsList
          });
        }
      }
      console.log('4、然后将 list 过滤出月份和类型所对应的账单列表',dataList);
      // 5、获取当月总支出、当月总收入、总条数
      // 获取一个月的有账单的所有天数列表 typeId 写死为all
      const allDayResult = await ctx.service.bill.allList(user_id, 'all', billDate);
      const allDayList = JSON.parse(JSON.stringify(allDayResult));
      // 当月总支出:支付类型:1:支出,2:收入
      let totalExpense = allDayList.reduce((curr, next) => {
        if (next.pay_type === 1) {
          curr += Number(next.amount);
          return curr;
        }
        return curr;
      }, 0).toFixed(2); 
      // 当月总收入:支付类型:1:支出,2:收入
      let totalIncome = allDayList.reduce((curr, next) => {
        if (next.pay_type === 2) {
          curr += Number(next.amount);
          return curr;
        }
        return curr;
      }, 0).toFixed(2);
      // 总条数 需要根据 typeId 去过滤
      const allDayTypeIdResult = await ctx.service.bill.allList(user_id, typeId, billDate);
      const allDayTypeIdList = JSON.parse(JSON.stringify(allDayTypeIdResult));
      let obj = {}; // 用年月日作为 key(YYYY-MM-DD)
      // 天数去重
      let newAllDayTypeIdList = allDayTypeIdList.reduce((item, next) => {
        let nextDate = moment(next.date).format("YYYY-MM-DD");
        obj[nextDate] ? '' : obj[nextDate] = true && item.push(next);
        return item;
      }, []);
      let totalRow = newAllDayTypeIdList.length;
      console.log('5、获取一个月的有账单的天数列表', obj, newAllDayTypeIdList);
      console.log('总条数', totalRow, '当月总支出:',totalExpense, '当月总收入:',totalIncome);
      ctx.body = {
        status: 200,
        desc: '请求成功',
        data: {
          totalExpense, // 当月总支出
          totalIncome, // 当月总收入
          dataList: dataList, // 列表数据
          pageObj: {
            curPage, // 当前页数
            pageSize, // 一页多少条
            totalPage: Math.ceil(totalRow / pageSize), // 总分页数
            totalRow, // 总条数
          }
        }
      }
    } catch (error) {
      console.log(error);
      ctx.body = {
        status: 500,
        desc: '系统错误',
        data: null
      }
    }
  }
}


2、在服务层添加几个方法获取sql数据

这里需要会一点 sql 语句,分页的参考资料在文章后面。

获取一个月的列表分页数据:主要就是通过日期去去重找到哪些天有账单的日期列表数据,然后通过降序分页处理。

async list(user_id, type_id, month, curPage, pageSize) {
    const { app } = this;
    try {
      // 去重找到日的数据列表降序分页
      let type_id_str = type_id === "all" || type_id === "" ? "" : " and type_id = " + type_id;
      let date_str = " and DATE_FORMAT(b.date,'%Y-%m') = '" + month + "'";
      let sql = "select distinct STR_TO_DATE(b.date,'%Y-%m-%d') day from `kaimo-cost`.bill b where user_id = "+ user_id+ type_id_str + date_str + " order by day desc limit "+(curPage-1)*pageSize+", "+pageSize;
      const result = await app.mysql.query(sql);
      return result;
    } catch (error) {
      console.log(error);
      return null;
    }
  }


获取某一天的账单列表数据:主要加入一些查询条件

async listByDay(user_id, type_id, month, day) {
  const { app } = this;
  try {
    const QUERY_STR = 'id, pay_type, amount, date, type_id, type_name, remark';
    let type_id_str = type_id === "all" || type_id === "" ? "" : " and type_id = " + type_id;
    let date_str = " and STR_TO_DATE(b.date,'%Y-%m-%d') = '" + day + "'" + " and DATE_FORMAT(b.date,'%Y-%m') = '" + month + "'";
    let sql = "select " + QUERY_STR + " from `kaimo-cost`.bill b where user_id = " + user_id + date_str + type_id_str;
    console.log('通过日获取列表数据', sql);
    const result = await app.mysql.query(sql);
    return result;
  } catch (error) {
    console.log(error);
    return null;
  }
}

获取某个月的所有账单数据

async allList(user_id, type_id, month) {
  const { app } = this;
  try {
    let type_id_str = type_id === "all" || type_id === "" ? "" : " and type_id = " + type_id;
    let sql = "select * from `kaimo-cost`.bill b where user_id = "+ user_id + " and DATE_FORMAT(b.date,'%Y-%m') = '" + month + "'" + type_id_str;
    console.log('获取月的所有账单数据', sql);
    const result = await app.mysql.query(sql);
    return result;
  } catch (error) {
    console.log(error);
    return null;
  }
}


3、配置路由

// 获取账单列表
router.get('/api/bill/list', verify_token, controller.bill.list);




测试获取账单列表接口


1、在 apifox 里新建接口

如下图所示:

c2a5936725c246a2a022cf94e879ec6d.png



2、伪造列表数据

我们可以将下面的 sql 在 DBeaver 执行,插入这些数据。

INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(1, 1, '33.00', '2022-01-01 22:00:00', 2, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(2, 1, '3', '2022-01-01 19:00:00', 2, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(3, 1, '13', '2022-01-02 22:00:00', 2, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(4, 2, '56', '2022-01-03 18:00:00', 2, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(5, 2, '42', '2022-01-05 12:07:00', 1, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(6, 2, '11', '2022-01-06 22:00:00', 2, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(7, 1, '3', '2022-01-07 22:00:00', 5, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(8, 1, '33', '2022-02-01 22:00:00', 14, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(9, 1, '3', '2022-02-02 22:00:00', 14, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(10, 2, '2', '2022-02-03 22:00:00', 2, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(11, 2, '46.9', '2022-02-04 22:00:00', 2, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(12, 2, '27', '2022-02-05 20:00:00', 1, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(13, 2, '4', '2022-02-05 22:00:00', 1, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(14, 2, '7', '2022-02-07 22:00:00', 4, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(15, 1, '11', '2022-02-08 22:00:00', 4, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(16, 2, '0', '2022-02-09 22:00:00', 4, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(17, 2, '16.8', '2022-02-10 22:00:00', 6, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(18, 1, '143', '2022-02-11 22:00:00', 6, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(19, 2, '70', '2022-02-12 22:00:00', 6, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(20, 1, '44', '2022-02-13 22:00:00', 8, '1', 5, '测试');
INSERT INTO `kaimo-cost`.bill
(id, pay_type, amount, `date`, type_id, type_name, user_id, remark)
VALUES(21, 1, '3', '2022-02-11 19:00:00', 5, '1', 5, '测试');


3、开始运行测试

登录之后,把 token 加入 Authorization 参数里

9301f6f072704f549103295666ffebaa.png

填入下面的参数,我们测试一下

12eb256759794b06ad13c29b54ab3ebc.png


结果如下:

{
    "status": 200,
    "desc": "请求成功",
    "data": {
        "totalExpense": "237.00",
        "totalIncome": "173.70",
        "dataList": [
            {
                "day": "2022-02-02",
                "bills": [
                    {
                        "id": 9,
                        "pay_type": 1,
                        "amount": "3",
                        "date": "2022-02-02 22:00:00",
                        "type_id": 14,
                        "type_name": "1",
                        "remark": "测试"
                    }
                ]
            },
            {
                "day": "2022-02-01",
                "bills": [
                    {
                        "id": 8,
                        "pay_type": 1,
                        "amount": "33",
                        "date": "2022-02-01 22:00:00",
                        "type_id": 14,
                        "type_name": "1",
                        "remark": "测试"
                    }
                ]
            }
        ],
        "pageObj": {
            "curPage": "2",
            "pageSize": "10",
            "totalPage": 2,
            "totalRow": 12
        }
    }
}


8885e3cf62bc44049524a2fcfbe6b9bb.png

其他的条件可以自己去测试一下,如有错误也在所难免,还请指正。



注意

在编写分页逻辑的过程中,我们可能会遇到一些问题,这个时候,我需要清楚是 sql 的问题,还是控制层逻辑问题,我们可以在关键的地方打印一些日志,比如查询的 sql 是什么,方便我们快速粘贴到 DBeaver 里去查询。


a8ae7f8c777544b291af38f8e79a5d99.png


c040c09a115948c68b81d9fae838408b.png




参考资料



JavaScript 日期处理类库 Moment.js


下面的接口实现需要用到 moment.js

例子:


> moment("2013-12-24 14:30:00").format("YYYY-MM-DD")
> '2013-12-24'
> moment("2013-12-24 14:30:00").format("YYYY-MM")
> '2013-12'
> moment('2022-03-08 19:15:41').format("x")
> '1646738141000'


limit 分页公式:

(1)limit分页公式:curPage是当前第几页;pageSize是一页多少条记录

limit (curPage-1)*pageSize,pageSize;


(2)用的地方:sql语句中

select * from student limit(curPage-1)*pageSize,pageSize;


日期格式函数:

STR_TO_DATE(b.date,'%Y-%m-%d') 输出年月日
DATE_FORMAT(b.date,'%Y-%m') 输出年月



相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
5月前
|
PHP 数据库
fastadmin框架如何查询数据表指定时间段内的数据
fastadmin框架如何查询数据表指定时间段内的数据
116 0
|
1月前
|
XML Java 数据库连接
【MyBtis】各种查询功能
【MyBtis】各种查询功能
32 0
|
2月前
|
JavaScript Java
若依框架 --- 主表和明细表批量添加
若依框架 --- 主表和明细表批量添加
78 0
|
4月前
|
运维 BI 调度
新增离线集成任务列表,支持快速筛选任务并进行批量操作 【V3.13】
离线集成任务列表提供了一个展示集成任务详细信息的页面,便于查看集成任务的各类信息。同时还支持根据任务的各类属性进行筛选。还支持根据来源与目标数据源或数据表进行任务的查找与筛选。便于在一些业务变更的时候,能快速筛选到所有任务,进行查看或批量修改的操作,提高开发与运维的效率。
|
XML 前端开发 测试技术
【测试开花】三、项目管理-后端-实现列表接口(含分页、模糊查询)
【测试开花】三、项目管理-后端-实现列表接口(含分页、模糊查询)
【测试开花】三、项目管理-后端-实现列表接口(含分页、模糊查询)
|
开发框架 前端开发 Java
java程序设计与j2ee中间件技术/软件开发技术(III)-大作业-采用MVC模式实现商品信息的查询显示(可以模糊查询)、增加和删除功能,商品表自拟,实现简单菜单操作和分页显示(三)
java程序设计与j2ee中间件技术/软件开发技术(III)-大作业-采用MVC模式实现商品信息的查询显示(可以模糊查询)、增加和删除功能,商品表自拟,实现简单菜单操作和分页显示
166 0
|
开发框架 前端开发 Java
java程序设计与j2ee中间件技术/软件开发技术(III)-大作业-采用MVC模式实现商品信息的查询显示(可以模糊查询)、增加和删除功能,商品表自拟,实现简单菜单操作和分页显示(二)
java程序设计与j2ee中间件技术/软件开发技术(III)-大作业-采用MVC模式实现商品信息的查询显示(可以模糊查询)、增加和删除功能,商品表自拟,实现简单菜单操作和分页显示
157 0
|
开发框架 前端开发 JavaScript
java程序设计与j2ee中间件技术/软件开发技术(III)-大作业-采用MVC模式实现商品信息的查询显示(可以模糊查询)、增加和删除功能,商品表自拟,实现简单菜单操作和分页显示(一)
java程序设计与j2ee中间件技术/软件开发技术(III)-大作业-采用MVC模式实现商品信息的查询显示(可以模糊查询)、增加和删除功能,商品表自拟,实现简单菜单操作和分页显示
294 0
java程序设计与j2ee中间件技术/软件开发技术(III)-大作业-采用MVC模式实现商品信息的查询显示(可以模糊查询)、增加和删除功能,商品表自拟,实现简单菜单操作和分页显示(一)
eggjs 怎么实现获取账单类型字典接口?
eggjs 怎么实现获取账单类型字典接口?
66 0
eggjs 怎么实现获取账单类型字典接口?
|
SQL Java 中间件
MyCat - 商品管理 - 根据条件分页查询 SPU 列表 | 学习笔记
快速学习 MyCat - 商品管理 - 根据条件分页查询 SPU 列表
167 0
MyCat - 商品管理 - 根据条件分页查询 SPU 列表 | 学习笔记