读书会小程序开发笔记(一)

背景分析

书籍是人类进步的阶梯,在信息高度发达而浮躁的当下,静下心来读书成为都市人群的新趋势, 很多读书社团应运而生,那么通过设计一款小程序,把读书会搬到手机上,通过小程序了解最热门的书单和作家,可以每天组织阅读打卡和评比;书友会时常组织书友进行线下活动,那么通过小程序可以方便的搞定报名、签到、活动信息收集,活动结束后还可以进行评价。

功能规划

在这里插入图片描述

数据库设计


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设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码

代码地址

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值