从0开始:活动打卡小程序开发笔记
发布于 1 年前 作者 gang40 4134 次浏览 来自 分享

概要设计

打卡是一种习惯的养成,是当下最流行、最受欢迎的工具,应用塌景广泛多样;各团体为了促活会定期开展一些闯关打卡活动;找一群志同道合的人,一起去打卡

功能定位:集学习、兴趣、生活、运动于一身的打卡小程序
用户群:面向学校学生、学生家长、老师、各种兴趣团体(跑步,运动,体育,艺术等)
主要模块:打卡项目列表,打卡排行,每日动态,后台打卡项目管理,后台打卡记录管理,后台打卡记录导出等功能

功能规划

数据字典


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_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',
};

核心难点


	// 获取当前打卡状态
	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, '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;
				}

			}
		}

		return await EnrollModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
	}

用户打卡 UI设计








# 后台系统UI设计







回到顶部