说明
该分页列表查询接口我自己分成三步写的,跟小册上的实现方式不太样,我是通过 mysql 的 limit 分页去实现的。
主要分成三个大的部分逻辑:
- 通过查询一个月里的账单所处的是哪一天,全都查出来,形成一个天数列表
- 通过天数列表,去查里面每一天的账单数据
- 最后统计这个月的所有账单
列表原型要求
可以查看原型,看前端账单需要怎样展示:账单是以时间天数作为一条数据,需要显示每天的账单信息,还有当月总支出,当月总收入,全部类型,月份选择。
分析接口需要字段
入参字段有:查询时需要
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 方法,里面的大体逻辑
- 获取查询参数
- 拿到 token 获取用户信息 user_id
- 通过 user_id 获取列表分页数据
- 组装数据:需要通过日获取一天的账单列表数据
- 获取当月总支出、当月总收入、总条数
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 里新建接口
如下图所示:
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
参数里
填入下面的参数,我们测试一下
结果如下:
{ "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 } } }
其他的条件可以自己去测试一下,如有错误也在所难免,还请指正。
注意
在编写分页逻辑的过程中,我们可能会遇到一些问题,这个时候,我需要清楚是 sql 的问题,还是控制层逻辑问题,我们可以在关键的地方打印一些日志,比如查询的 sql 是什么,方便我们快速粘贴到 DBeaver 里去查询。
参考资料
- JavaScript 日期处理类库【Moment.js】:http://momentjs.cn/docs/
- Mysql实现分页
- mysql函数date(),date_format()和str_to_date()
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') 输出年月