设计背景
小区物业费用缴纳,公寓租金收取,出租屋租金收取等场景,大部分都是靠人工收取,而宽广业主和住户可能工夫或者工作忙碌,不能到现场付款,那么设计一个这样的小程序,不便物业公司进行账务收款,账务统计,节俭人力物力老本,不管在家中或者当地,都能够通过小程序间接缴费,不受工夫、地点束缚,操作简略,方便快捷
概要设计
数据字典
SheetModel.DB_STRUCTURE = {
_pid: 'string|true',
SHEET_ID: 'string|true',
SHEET_TITLE: 'string|true|comment= 题目',
SHEET_STATUS: 'int|true|default=1|comment= 状态 0= 未启用,1= 应用中',
SHEET_CATE_ID: 'string|true|default=0|comment= 分类',
SHEET_CATE_NAME: 'string|false|comment= 分类冗余',
SHEET_ORDER: 'int|true|default=9999',
SHEET_VOUCH: 'int|true|default=0',
SHEET_FORMS: 'array|true|default=[]',
SHEET_OBJ: 'object|true|default={}',
SHEET_QR: 'string|false',
SHEET_VIEW_CNT: 'int|true|default=0',
SHEET_CNT: 'int|true|default=0',
SHEET_PAY_CNT: 'int|true|default=0',
SHEET_WAIT_CNT: 'int|true|default=0',
SHEET_NO_CNT: 'int|true|default=0',
SHEET_FEE: 'int|true|default=0',
SHEET_PAY_FEE: 'int|true|default=0',
SHEET_WAIT_FEE: 'int|true|default=0',
SHEET_ADD_TIME: 'int|true',
SHEET_EDIT_TIME: 'int|true',
SHEET_ADD_IP: 'string|false',
SHEET_EDIT_IP: 'string|false',
};
SheetDataModel.DB_STRUCTURE = {
_pid: 'string|true',
SHEET_DATA_ID: 'string|true',
SHEET_DATA_SHEET_ID: 'string|true|comment=FK',
SHEET_DATA_SHEET_TITLE: 'string|false',
SHEET_DATA_PAY_TRADE_NO: 'string|false|comment= 商家订单号 32 位',
SHEET_DATA_PAY_STATUS: 'int|true|default=0|comment= 领取状态 0= 未领取 1= 已领取 99= 无需领取',
SHEET_DATA_PAY_FEE: 'int|true|default=0|comment= 已领取费用 分',
SHEET_DATA_PAY_TIME: 'int|true|default=0|comment= 领取工夫',
SHEET_DATA_NAME: 'string|false| 姓名',
SHEET_DATA_MOBILE: 'string|false| 手机号',
SHEET_DATA_FEE: 'int|true|default=0|comment= 需领取费用 分',
SHEET_DATA_FORMS: 'array|true|default=[]',
SHEET_DATA_OBJ: 'object|true|default={}',
SHEET_DATA_ADD_TIME: 'int|true',
SHEET_DATA_EDIT_TIME: 'int|true',
SHEET_DATA_ADD_IP: 'string|false',
SHEET_DATA_EDIT_IP: 'string|false',
};
要害实现
async getMySheetChartList(userId, time = 180) {let where = {};
if (!config.IS_DEMO) {let user = await UserModel.getOne({ USER_MINI_OPENID: userId});
if (!user) return null;
where = {
SHEET_DATA_PAY_STATUS: 1,
SHEET_DATA_NAME: user.USER_NAME,
SHEET_DATA_MOBILE: user.USER_MOBILE,
'sheet.SHEET_STATUS': SheetModel.STATUS.COMM
};
}
else {
where = {
SHEET_DATA_PAY_STATUS: 1,
SHEET_DATA_NAME: 'Tom',
SHEET_DATA_MOBILE: '14600000000',
'sheet.SHEET_STATUS': SheetModel.STATUS.COMM
};
}
time = Number(time);
time = this._timestamp - time * 86400 * 1000;
where.SHEET_DATA_PAY_TIME = ['>=', time];
let fields = 'SHEET_DATA_PAY_TIME,SHEET_DATA_FORMS,SHEET_DATA_FEE,sheet.SHEET_TITLE';
let orderBy = {
'SHEET_DATA_PAY_TIME': 'desc',
'SHEET_DATA_ADD_TIME': 'desc'
};
let joinParams = {
from: SheetModel.CL,
localField: 'SHEET_DATA_SHEET_ID',
foreignField: '_id',
as: 'sheet',
};
let list = await SheetDataModel.getListJoin(joinParams, where, fields, orderBy, 1, 200, false);
list = list.list;
if (list.length == 0) return null;
let categories = [];
let data = [];
for (let k = 0; k < list.length; k++) {data.push(Number(list[k].SHEET_DATA_FEE / 100));
categories.push(list[k].sheet.SHEET_TITLE.substr(0, 15));
list[k].SHEET_DATA_PAY_TIME = timeUtil.timestamp2Time(list[k].SHEET_DATA_PAY_TIME);
}
return {categories, data, list: list.reverse() };
}
// 获得账单详情
async getMySheetDataDetail(userId, sheetDataId) {let where = {};
if (!config.IS_DEMO) {let user = await UserModel.getOne({ USER_MINI_OPENID: userId, USER_STATUS: UserModel.STATUS.COMM});
if (!user) this.AppError('用户不存在或者状态异样');
where = {
_id: sheetDataId,
SHEET_DATA_NAME: user.USER_NAME,
SHEET_DATA_MOBILE: user.USER_MOBILE,
}
}
else {
where = {
_id: sheetDataId,
SHEET_DATA_NAME: 'Tom',
SHEET_DATA_MOBILE: '14600000000',
}
}
let sheetData = await SheetDataModel.getOne(where);
if (!sheetData) return null;
let sheet = await SheetModel.getOne({_id: sheetData.SHEET_DATA_SHEET_ID, SHEET_STATUS: SheetModel.STATUS.COMM});
if (!sheet) this.AppError('领取我的项目不存在');
sheetData.sheet = sheet;
return sheetData;
}
/** 获得我的分页列表 */
async getMySheetDataList(userId, {
search, // 搜寻条件
sortType, // 搜寻菜单
sortVal, // 搜寻菜单
orderBy, // 排序
page,
size = 30,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {'SHEET_DATA_ADD_TIME': 'desc'};
let fields = 'SHEET_DATA_PAY_TIME,SHEET_DATA_ADD_TIME,SHEET_DATA_FEE,SHEET_DATA_FORMS,SHEET_DATA_PAY_FEE,SHEET_DATA_PAY_STATUS,sheet.SHEET_TITLE,sheet.SHEET_OBJ';
let where = {};
if (!config.IS_DEMO) {let user = await UserModel.getOne({ USER_MINI_OPENID: userId});
if (!user) return null;
where = {
SHEET_DATA_NAME: user.USER_NAME,
SHEET_DATA_MOBILE: user.USER_MOBILE,
'sheet.SHEET_STATUS': SheetModel.STATUS.COMM
};
}
else {
where = {
SHEET_DATA_NAME: 'Tom',
SHEET_DATA_MOBILE: '14600000000',
'sheet.SHEET_STATUS': SheetModel.STATUS.COMM
};
}
if (util.isDefined(search) && search) {where['SHEET_DATA_SHEET_TITLE'] = {
$regex: '.*' + search,
$options: 'i'
};
} else if (sortType) {
// 搜寻菜单
switch (sortType) {
case 'status': {where['SHEET_DATA_PAY_STATUS'] = Number(sortVal);
break;
}
case 'sort': { // 按工夫倒序
orderBy = this.fmtOrderBySort(sortVal, 'SHEET_DATA_ADD_TIME');
break;
}
}
}
let joinParams = {
from: SheetModel.CL,
localField: 'SHEET_DATA_SHEET_ID',
foreignField: '_id',
as: 'sheet',
};
let result = await SheetDataModel.getListJoin(joinParams, where, fields, orderBy, page, size, isTotal, oldTotal);
return result;
}
async statSheetData(sheetId) {
let where = {SHEET_DATA_SHEET_ID: sheetId}
// 总数
let cnt = await SheetDataModel.count(where);
// 总费用
let fee = await SheetDataModel.sum(where, 'SHEET_DATA_FEE');
// 已领取记录
let wherePayCnt = {
SHEET_DATA_SHEET_ID: sheetId,
SHEET_DATA_PAY_STATUS: 1,
}
let payCnt = await SheetDataModel.count(wherePayCnt);
// 毋庸领取
let whereNoCnt = {
SHEET_DATA_SHEET_ID: sheetId,
SHEET_DATA_PAY_STATUS: 99,
}
let noCnt = await SheetDataModel.count(whereNoCnt);
// 已领取金额
let wherePayFee = {
SHEET_DATA_SHEET_ID: sheetId,
SHEET_DATA_PAY_STATUS: 1,
}
let payFee = await SheetDataModel.sum(wherePayFee, 'SHEET_DATA_PAY_FEE');
let waitCnt = cnt - payCnt - noCnt;
let waitFee = fee - payFee;
let data = {
SHEET_CNT: cnt,
SHEET_PAY_CNT: payCnt,
SHEET_WAIT_CNT: waitCnt,
SHEET_NO_CNT: noCnt,
SHEET_FEE: fee,
SHEET_PAY_FEE: payFee,
SHEET_WAIT_FEE: waitFee
}
await SheetModel.edit(sheetId, data);
return {cnt, payCnt, waitCnt, noCnt, fee, payFee, waitFee};
}
// 修改本地订单状态
async fixSheetDataPay(tradeNo, sheetId) {if (!tradeNo) {
// 无领取号空单
let data = {
SHEET_DATA_PAY_STATUS: 0,
SHEET_DATA_PAY_TRADE_NO: '',
SHEET_DATA_PAY_FEE: 0,
SHEET_DATA_PAY_TIME: 0,
}
await SheetDataModel.edit({SHEET_DATA_PAY_TRADE_NO: tradeNo}, data);
// 从新统计
this.statSheetData(sheetId);
return false;
}
let payService = new PayService();
if (!await payService.fixPayResult(tradeNo)) {
// 敞开未领取单
payService.closePay(tradeNo);
// 未领取
let data = {
SHEET_DATA_PAY_STATUS: 0,
SHEET_DATA_PAY_TRADE_NO: '',
SHEET_DATA_PAY_FEE: 0,
SHEET_DATA_PAY_TIME: 0,
}
await SheetDataModel.edit({SHEET_DATA_PAY_TRADE_NO: tradeNo}, data);
// 从新统计
this.statSheetData(sheetId);
return false;
}
// 已领取
let pay = await PayModel.getOne({PAY_TRADE_NO: tradeNo});
if (!pay) this.AppError('领取流水异样,请核查');
// 更新领取信息
let data = {
SHEET_DATA_PAY_STATUS: 1,
SHEET_DATA_PAY_TRADE_NO: tradeNo,
SHEET_DATA_PAY_FEE: pay.PAY_TOTAL_FEE,
SHEET_DATA_PAY_TIME: pay.PAY_END_TIME,
}
await SheetDataModel.edit({SHEET_DATA_PAY_TRADE_NO: tradeNo}, data);
// 从新统计
this.statSheetData(sheetId);
return true;
}
// 预领取
async prepay(userId, sheetDataId) {this.AppError('[ 物业缴费] 该性能暂不凋谢,如有须要请加作者微信:cclinux0730');
}
// 查问领取后果
async queryPayResult(sheetDataId) {let sheetData = await SheetDataModel.getOne(sheetDataId);
if (!sheetData) return {status: 0};
return {status: sheetData.SHEET_DATA_PAY_STATUS}
}
住户端 UI 设计
治理端 UI 设计
源码
github 源码下载