背景剖析
书籍是人类提高的阶梯,在信息高度发达而塌实的当下,静下心来读书成为都市人群的新趋势, 很多读书社团应运而生,那么通过设计一款小程序,把读书会搬到手机上,通过小程序理解最热门的书单和作家,能够每天组织浏览打卡和评比;书友会时常组织书友进行线下流动,那么通过小程序能够不便的搞定报名、签到、流动信息收集,流动完结后还能够进行评估。
性能布局
数据库设计
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 }); } }}
UI设计
后端UI设计
代码
git代码地址