背景分析
书籍是人类进步的阶梯,在信息高度发达而浮躁的当下,静下心来读书成为都市人群的新趋势, 很多读书社团应运而生,那么通过设计一款小程序,把读书会搬到手机上,通过小程序了解最热门的书单和作家,可以每天组织阅读打卡和评比;书友会时常组织书友进行线下活动,那么通过小程序可以方便的搞定报名、签到、活动信息收集,活动结束后还可以进行评价。
功能规划
数据库设计
EnrollModel.DB_STRUCTURE = {
_pid: 'string|true',
ENROLL_ID: 'string|true',
ENROLL_TITLE: 'string|true|comment=标题',
ENROLL_STATUS: 'int|true|default=1|comment=状态 0=未启用,1=使用中',
ENROLL_CATE_ID: 'string|true|default=0|comment=分类',
ENROLL_CATE_NAME: 'string|false|comment=分类冗余',
ENROLL_START: 'int|false|comment=开始时间',
ENROLL_END: 'int|false|comment=结束时间',
ENROLL_DAY_CNT: 'int|false|comment=持续天数',
ENROLL_ORDER: 'int|true|default=9999',
ENROLL_VOUCH: 'int|true|default=0',
ENROLL_FORMS: 'array|true|default=[]',
ENROLL_OBJ: 'object|true|default={}',
ENROLL_JOIN_FORMS: 'array|true|default=[]',
ENROLL_DAYS: 'array|true|default=[]',
ENROLL_QR: 'string|false',
ENROLL_VIEW_CNT: 'int|true|default=0',
ENROLL_JOIN_CNT: 'int|true|default=0',
ENROLL_USER_CNT: 'int|true|default=0',
ENROLL_USER_LIST: 'array|true|default=[]|comment={name,id,pic}',
ENROLL_ADD_TIME: 'int|true',
ENROLL_EDIT_TIME: 'int|true',
ENROLL_ADD_IP: 'string|false',
ENROLL_EDIT_IP: 'string|false',
};
EnrollJoinModel.DB_STRUCTURE = {
_pid: 'string|true',
ENROLL_JOIN_ID: 'string|true',
ENROLL_JOIN_ENROLL_ID: 'string|true|comment=打卡PK',
ENROLL_JOIN_USER_ID: 'string|true|comment=用户ID',
ENROLL_JOIN_DAY: 'string|true|comment=日期',
ENROLL_JOIN_FORMS: 'array|true|default=[]|comment=表单',
ENROLL_JOIN_STATUS: 'int|true|default=1|comment=状态 1=成功',
ENROLL_JOIN_ADD_TIME: 'int|true',
ENROLL_JOIN_EDIT_TIME: 'int|true',
ENROLL_JOIN_ADD_IP: 'string|false',
ENROLL_JOIN_EDIT_IP: 'string|false',
};
核心实现
class EnrollService extends BaseProjectService {
// 获取当前打卡状态
getJoinStatusDesc(enroll) {
let timestamp = this._timestamp;
if (enroll.ENROLL_STATUS == 0)
return '已停止';
else if (enroll.ENROLL_START > timestamp)
return '未开始';
else if (enroll.ENROLL_END <= timestamp)
return '已结束';
else
return '进行中';
}
// 获取某日动态
async getEnrollJoinByDay(enrollId, day = '') {
if (!day) day = timeUtil.time('Y-M-D');
let where = {
ENROLL_JOIN_ENROLL_ID: enrollId,
ENROLL_JOIN_DAY: day,
ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC
}
let joinParams = {
from: UserModel.CL,
localField: 'ENROLL_JOIN_USER_ID',
foreignField: 'USER_MINI_OPENID',
as: 'user',
};
let orderBy = {
ENROLL_JOIN_ADD_TIME: 'desc'
}
let list = await EnrollJoinModel.getListJoin(joinParams, where, 'ENROLL_JOIN_ADD_TIME,user.USER_NAME,user.USER_PIC', orderBy, 1, 100, false, 0);
return list.list;
}
// 获取某活动排行
async getEnrollUserRank(enrollId) {
let where = {
ENROLL_USER_ENROLL_ID: enrollId
}
let joinParams = {
from: UserModel.CL,
localField: 'ENROLL_USER_MINI_OPENID',
foreignField: 'USER_MINI_OPENID',
as: 'user',
};
let orderBy = {
ENROLL_USER_JOIN_CNT: 'desc'
}
let fields = 'ENROLL_USER_JOIN_CNT,ENROLL_USER_LAST_DAY,user.USER_NAME,user.USER_PIC';
let list = await EnrollUserModel.getListJoin(joinParams, where, fields, orderBy, 1, 100, false, 0);
return list.list;
}
/** 浏览信息 */
async viewEnroll(userId, id) {
let fields = '*';
let where = {
_id: id,
ENROLL_STATUS: EnrollModel.STATUS.COMM
}
let enroll = await EnrollModel.getOne(where, fields);
if (!enroll) return null;
EnrollModel.inc(id, 'ENROLL_VIEW_CNT', 1);
// 判断用户今日是否有打卡
let whereJoin = {
ENROLL_JOIN_USER_ID: userId,
ENROLL_JOIN_ENROLL_ID: id,
ENROLL_JOIN_DAY: timeUtil.time('Y-M-D'),
ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC
}
let enrollJoin = await EnrollJoinModel.getOne(whereJoin);
if (enrollJoin) {
enroll.myEnrollJoinId = enrollJoin._id;
}
else {
enroll.myEnrollJoinId = '';
}
// 某日打卡列表
enroll.activity = await this.getEnrollJoinByDay(id);
// 打卡日期数组
let dayList = [];
let start = timeUtil.timestamp2Time(enroll.ENROLL_START, 'Y-M-D');
start = timeUtil.time2Timestamp(start);
let today = timeUtil.time2Timestamp(timeUtil.time('Y-M-D'));
for (let k = start; k <= today;) {
let month = timeUtil.timestamp2Time(k, 'M月');
if (month.startsWith('0')) month = month.substring(1);
let date = timeUtil.timestamp2Time(k, 'D');
let day = timeUtil.timestamp2Time(k, 'Y-M-D');
dayList.push({ month, date, day });
k = k + 86400 * 1000;
}
enroll.dayList = dayList;
// 排行榜
let rankList = await this.getEnrollUserRank(id);
enroll.rankList = rankList;
return enroll;
}
/** 取得分页列表 */
async getEnrollList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'ENROLL_ORDER': 'asc',
'ENROLL_ADD_TIME': 'desc'
};
let fields = 'ENROLL_USER_LIST,ENROLL_JOIN_CNT,ENROLL_OBJ,ENROLL_USER_CNT,ENROLL_TITLE,ENROLL_START,ENROLL_END,ENROLL_ORDER,ENROLL_STATUS,ENROLL_CATE_NAME,ENROLL_OBJ';
let where = {};
where.and = {
_pid: this.getProjectId() //复杂的查询在此处标注PID
};
where.and.ENROLL_STATUS = EnrollModel.STATUS.COMM; // 状态
if (util.isDefined(search) && search) {
where.or = [{
ENROLL_TITLE: ['like', search]
},];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'cateId': {
if (sortVal) where.and.ENROLL_CATE_ID = String(sortVal);
break;
}
case 'sort': {
orderBy = this.fmtOrderBySort(sortVal, 'ENROLL_ADD_TIME');
break;
}
case 'today': { //今天
let day = timeUtil.time('Y-M-D');
where.and.ENROLL_DAYS = day;
break;
}
case 'tomorrow': { //明日
let day = timeUtil.time('Y-M-D', 86400);
where.and.ENROLL_DAYS = day;
break;
}
case 'yesterday': { //昨天
let day = timeUtil.time('Y-M-D', -86400);
where.and.ENROLL_DAYS = day;
break;
}
case 'month': { //本月
let day = timeUtil.time('Y-M-D');
let start = timeUtil.getMonthFirstTimestamp(day);
let end = timeUtil.getMonthLastTimestamp(day);
start = timeUtil.timestamp2Time(start, 'Y-M-D');
end = timeUtil.timestamp2Time(end, 'Y-M-D');
where.and.ENROLL_DAYS = ['between', start, end];
break;
}
}
}
return await EnrollModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
/** 取得我的打卡分页列表 */
async getMyEnrollUserList(userId, {
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'ENROLL_USER_ADD_TIME': 'asc'
};
let fields = 'ENROLL_USER_LAST_DAY,ENROLL_USER_ENROLL_ID,ENROLL_USER_JOIN_CNT,enroll.ENROLL_TITLE,enroll.ENROLL_OBJ.cover,enroll.ENROLL_USER_CNT,enroll.ENROLL_CATE_NAME,enroll.ENROLL_DAY_CNT,enroll.ENROLL_START,enroll.ENROLL_END,enroll.ENROLL_STATUS';
let where = {
ENROLL_USER_MINI_OPENID: userId
};
if (util.isDefined(search) && search) {
where['enroll.ENROLL_TITLE'] = {
$regex: '.*' + search,
$options: 'i'
};
} else if (sortType) {
// 搜索菜单
let timestamp = this._timestamp;
switch (sortType) {
case 'stop': {
where['enroll.ENROLL_STATUS'] = 0;
break;
}
case 'un': {
where['enroll.ENROLL_START'] = ['>', timestamp];
break;
}
case 'over': {
where['enroll.ENROLL_END'] = ['<=', timestamp];
break;
}
case 'run': {
where['enroll.ENROLL_STATUS'] = 1;
where['enroll.ENROLL_START'] = ['<=', timestamp];
where['enroll.ENROLL_END'] = ['>', timestamp];
break;
}
}
}
let joinParams = {
from: EnrollModel.CL,
localField: 'ENROLL_USER_ENROLL_ID',
foreignField: '_id',
as: 'enroll',
};
let result = await EnrollUserModel.getListJoin(joinParams, where, fields, orderBy, page, size, isTotal, oldTotal);
let list = result.list;
for (let k = 0; k < list.length; k++) {
let enroll = {
ENROLL_START: list[k].enroll.ENROLL_START,
ENROLL_END: list[k].enroll.ENROLL_END,
ENROLL_STATUS: list[k].enroll.ENROLL_STATUS,
}
let status = this.getJoinStatusDesc(enroll);
if (status == '进行中') {
if (list[k].ENROLL_USER_LAST_DAY == timeUtil.time('Y-M-D'))
status = '已打卡';
}
list[k].status = status;
list[k].last = list[k].ENROLL_USER_LAST_DAY.split('-')[1] + '-' + list[k].ENROLL_USER_LAST_DAY.split('-')[2];
}
return result;
}
/** 取得我的打卡清单列表 */
async getMyEnrollJoinList(userId, {
enrollId,
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'ENROLL_JOIN_ADD_TIME': 'desc'
};
let fields = '*';
let where = {
ENROLL_JOIN_USER_ID: userId,
ENROLL_JOIN_ENROLL_ID: enrollId
};
return await EnrollJoinModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
//################## 打卡
addEnrollUserList(userList, user) {
//查询是否存在 并删除
for (let k = 0; k < userList.length; k++) {
if (userList[k].id == user.id)
userList.splice(k, 1);
}
userList.unshift(user);
// 判断个数, 多的删除
if (userList.length > 3)
userList.splice(userList.length - 1, 1);
return userList;
}
// 打卡
async enrollJoin(userId, enrollId, forms) {
let user = await UserModel.getOne({ USER_MINI_OPENID: userId, USER_STATUS: UserModel.STATUS.COMM });
if (!user) this.AppError('用户不存在');
// 打卡是否结束
let whereEnroll = {
_id: enrollId,
ENROLL_STATUS: EnrollModel.STATUS.COMM
}
let enroll = await EnrollModel.getOne(whereEnroll);
if (!enroll)
this.AppError('该打卡活动不存在或者已经停止');
// 是否打卡开始
if (enroll.ENROLL_START > this._timestamp)
this.AppError('该打卡活动尚未开始');
// 是否过了打卡结束期
if (enroll.ENROLL_END < this._timestamp)
this.AppError('该打卡活动已经结束');
let day = timeUtil.time('Y-M-D');
// 自己今日是否已经有打卡
let whereMy = {
ENROLL_JOIN_USER_ID: userId,
ENROLL_JOIN_ENROLL_ID: enrollId,
ENROLL_JOIN_DAY: day,
ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC
}
let my = await EnrollJoinModel.getOne(whereMy);
if (my) {
this.AppError('您今日已打卡,无须重复打卡');
}
// 入库
let data = {
ENROLL_JOIN_USER_ID: userId,
ENROLL_JOIN_ENROLL_ID: enrollId,
ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC,
ENROLL_JOIN_DAY: day,
ENROLL_JOIN_FORMS: forms
}
let enrollJoinId = await EnrollJoinModel.insert(data);
// 统计数量
this.statEnrollJoin(enrollId, userId);
// 更新用户头像
let userList = enroll.ENROLL_USER_LIST;
userList = this.addEnrollUserList(userList, { pic: user.USER_PIC, id: userId, name: user.USER_NAME });
EnrollModel.edit(enrollId, { ENROLL_USER_LIST: userList });
return { enrollJoinId }
}
// 统计
async statEnrollJoin(enrollId, userId = '', del = false) {
// 总体统计
let where = {
ENROLL_JOIN_ENROLL_ID: enrollId,
ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC
}
let joinCnt = await EnrollJoinModel.count(where);
let userCnt = await EnrollJoinModel.distinctCnt(where, 'ENROLL_JOIN_USER_ID');
let data = {
ENROLL_JOIN_CNT: joinCnt,
ENROLL_USER_CNT: userCnt,
}
await EnrollModel.edit(enrollId, data);
// 用户统计
if (!userId) return;
where = {
ENROLL_JOIN_USER_ID: userId,
ENROLL_JOIN_ENROLL_ID: enrollId,
ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC
}
let userJoinCnt = await EnrollJoinModel.count(where);
let enrollUserData = {};
enrollUserData.ENROLL_USER_LAST_DAY = timeUtil.time('Y-M-D');
enrollUserData.ENROLL_USER_JOIN_CNT = userJoinCnt;
enrollUserData.ENROLL_USER_DAY_CNT = userJoinCnt;
where = {
ENROLL_USER_MINI_OPENID: userId,
ENROLL_USER_ENROLL_ID: enrollId
};
await EnrollUserModel.insertOrUpdate(where, enrollUserData);
if (del) {
//删除打卡记录,则更新最近打卡时间
let last = '';
where = {
ENROLL_JOIN_USER_ID: userId,
ENROLL_JOIN_ENROLL_ID: enrollId,
ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC
}
let lastModel = await EnrollJoinModel.getOne(where, 'ENROLL_JOIN_DAY', { 'ENROLL_JOIN_ADD_TIME': 'desc' });
if (lastModel) last = lastModel.ENROLL_JOIN_DAY;
where = {
ENROLL_USER_MINI_OPENID: userId,
ENROLL_USER_ENROLL_ID: enrollId
};
await EnrollUserModel.edit(where, { ENROLL_USER_LAST_DAY: last });
}
}
}