从0到1:不文明现象随手拍小程序开发日记(一)

前期调研

不文明现象随手拍小程序:在城市的快速发展进程中,不文明现象时有发生,为了有效解决这一问题,提升城市文明程度, 市民若发现不文明行为,如乱扔垃圾、随地吐痰、破坏公共设施、违规停车等,只需点击“上报不文明现象”按钮,即可将这些不文明行为记录下来,并附上简短的文字描述,如事件发生的具体地点、时间以及对周围环境或他人造成的影响等。市民可以在小程序的“我的上报记录”页面中随时查看自己的上报; 为了进一步调动市民参与的积极性,小程序设置了积分激励机制。每当市民成功上报一条不文明现象并被后台审核通过后,即可获得相应的积分奖励。 同时提供积分商城模块,可以让市民使用通过参与任务所获得的积分来兑换各种奖励或福利。

功能规划

在这里插入图片描述

数据设计

ActivityModel.DB_STRUCTURE = {
	_pid: 'string|true',
	ACTIVITY_ID: 'string|true',

	ACTIVITY_TITLE: 'string|true|comment=标题',
	ACTIVITY_STATUS: 'int|true|default=1|comment=状态 0=未启用,1=使用中',
	ACTIVITY_CHECK_REASON: 'string|false|comment=审核理由',

	ACTIVITY_CATE_ID: 'string|true|default=0|comment=分类',
	ACTIVITY_CATE_NAME: 'string|false|comment=分类冗余',

	ACTIVITY_CANCEL_SET: 'int|true|default=1|comment=撤销设置 0=不允,1=允许,2=仅上报截止前可撤销',  

	ACTIVITY_MAX_CNT: 'int|true|default=20|comment=每人次数上限 0=不限',
	ACTIVITY_START: 'int|false|comment=项目时间',
	ACTIVITY_START_DAY: 'string|false',

	ACTIVITY_BEGIN: 'int|true|default=0|comment=开始时间',
	ACTIVITY_STOP: 'int|true|default=0|comment=截止时间',


	ACTIVITY_ADD_MONTH: 'string|false',

	ACTIVITY_ORDER: 'int|true|default=9999',
	ACTIVITY_VOUCH: 'int|true|default=0',

	ACTIVITY_FORMS: 'array|true|default=[]',
	ACTIVITY_OBJ: 'object|true|default={}',

	ACTIVITY_JOIN_FORMS: 'array|true|default=[]',

	ACTIVITY_ADDRESS: 'string|false|comment=详细地址',
	ACTIVITY_ADDRESS_GEO: 'object|false|comment=详细地址坐标参数',

	ACTIVITY_QR: 'string|false',
	ACTIVITY_VIEW_CNT: 'int|true|default=0',
	ACTIVITY_JOIN_CNT: 'int|true|default=0',
	ACTIVITY_COMMENT_CNT: 'int|true|default=0',

	ACTIVITY_ADD_TIME: 'int|true',
	ACTIVITY_EDIT_TIME: 'int|true',
	ACTIVITY_ADD_IP: 'string|false',
	ACTIVITY_EDIT_IP: 'string|false',
};
ActivityJoinModel.DB_STRUCTURE = {
	_pid: 'string|true',
	ACTIVITY_JOIN_ID: 'string|true',
	ACTIVITY_JOIN_ACTIVITY_ID: 'string|true|comment=上报PK',
	ACTIVITY_JOIN_ACTIVITY_TITLE: 'string|true',

	ACTIVITY_JOIN_IS_ADMIN: 'int|true|default=0|comment=是否管理员添加 0/1',
 

	ACTIVITY_JOIN_USER_ID: 'string|true|comment=用户ID',
	ACTIVITY_JOIN_SCORE: 'int|true|default=0|comment=获取积分',

	ACTIVITY_JOIN_FORMS: 'array|true|default=[]|comment=表单',
	ACTIVITY_JOIN_OBJ: 'object|true|default={}',

	ACTIVITY_JOIN_STATUS: 'int|true|default=0|comment=状态 1=成功, 99=系统撤销',
	ACTIVITY_JOIN_REASON: 'string|false|comment=撤销理由',

	ACTIVITY_JOIN_ADD_MONTH: 'string|false',

	ACTIVITY_JOIN_ADD_TIME: 'int|true',
	ACTIVITY_JOIN_EDIT_TIME: 'int|true',
	ACTIVITY_JOIN_ADD_IP: 'string|false',
	ACTIVITY_JOIN_EDIT_IP: 'string|false',
};

核心实现

class ActivityService extends BaseProjectService {

	// 获取当前项目状态
	getJoinStatusDesc(activity) {
		let timestamp = this._timestamp;

		if (activity.ACTIVITY_STATUS == ActivityModel.STATUS.UNUSE)
			return '项目停止';
		else if (activity.ACTIVITY_START > timestamp)
			return '项目未开始';
		else if (activity.ACTIVITY_STOP <= timestamp)
			return '项目结束';
		else
			return '进行中';
	}

	/** 浏览信息 */
	async viewActivity(userId, id) {

		let fields = '*';

		let where = {
			_id: id,
			ACTIVITY_STATUS: ActivityModel.STATUS.COMM,
		}
		let activity = await ActivityModel.getOne(where, fields);
		if (!activity) return null;

		ActivityModel.inc(id, 'ACTIVITY_VIEW_CNT', 1);


		return activity;
	}

	/** 取得分页列表 */
	async getActivityList(type = 'run', {
		cateId, //分类查询条件
		search, // 搜索条件
		sortType, // 搜索菜单
		sortVal, // 搜索菜单
		orderBy, // 排序 
		page,
		size,
		isTotal = true,
		oldTotal
	}) {

		orderBy = orderBy || {
			'ACTIVITY_ORDER': 'asc',
			'ACTIVITY_START': 'asc',
			'ACTIVITY_ADD_TIME': 'desc'
		};
		let fields = 'ACTIVITY_ADDRESS,ACTIVITY_STOP,ACTIVITY_JOIN_CNT,ACTIVITY_OBJ,ACTIVITY_VIEW_CNT,ACTIVITY_TITLE,ACTIVITY_MAX_CNT,ACTIVITY_START_DAY,ACTIVITY_START,ACTIVITY_ORDER,ACTIVITY_STATUS,ACTIVITY_CATE_NAME,ACTIVITY_OBJ.cover,ACTIVITY_OBJ.score';

		let where = {};

		if (cateId && cateId !== '0') where.ACTIVITY_CATE_ID = cateId;

		where.ACTIVITY_STATUS = ActivityModel.STATUS.COMM; // 状态  

		// 进行状态
		let day = timeUtil.time('Y-M-D');
		if (type == 'run') {
			where.ACTIVITY_STOP = ['>=', this._timestamp];
		}
		else {
			where.ACTIVITY_STOP = ['<=', this._timestamp];
			orderBy = {
				'ACTIVITY_ORDER': 'asc',
				'ACTIVITY_START': 'desc',
				'ACTIVITY_ADD_TIME': 'desc'
			};
		}

		if (util.isDefined(search) && search) {
			where['ACTIVITY_TITLE'] = ['like', search];

		} else if (sortType && util.isDefined(sortVal)) {

			// 搜索菜单
			switch (sortType) {
				case 'cateId': {
					if (sortVal) where.ACTIVITY_CATE_ID = String(sortVal);
					break;
				}
				case 'sort': {
					// 排序
					orderBy = this.fmtOrderBySort(sortVal, 'ACTIVITY_ADD_TIME');
					break;
				}
			}
		}

		let ret = await ActivityModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
		if (ret) ret.type = type;
		return ret;
	}


	/** 取得我的上报分页列表 */
	async getMyActivityJoinList(userId, {
		search, // 搜索条件
		sortType, // 搜索菜单
		sortVal, // 搜索菜单
		orderBy, // 排序 
		page,
		size,
		isTotal = true,
		oldTotal
	}) {
		orderBy = orderBy || {
			'ACTIVITY_JOIN_ADD_TIME': 'desc'
		};
		let fields = 'ACTIVITY_JOIN_REASON,ACTIVITY_JOIN_ACTIVITY_ID,ACTIVITY_JOIN_ACTIVITY_TITLE,ACTIVITY_JOIN_STATUS,ACTIVITY_JOIN_ADD_TIME';

		let where = {
			ACTIVITY_JOIN_USER_ID: userId
		};

		if (util.isDefined(search) && search) {
			where['activity.ACTIVITY_TITLE'] = {
				$regex: '.*' + search,
				$options: 'i'
			};
		} else if (sortType) {
			// 搜索菜单
			switch (sortType) {
				case 'timedesc': { //按时间倒序
					orderBy = {
						'activity.ACTIVITY_START': 'desc',
						'ACTIVITY_JOIN_ADD_TIME': 'desc'
					};
					break;
				}
				case 'timeasc': { //按时间正序
					orderBy = {
						'activity.ACTIVITY_START': 'asc',
						'ACTIVITY_JOIN_ADD_TIME': 'asc'
					};
					break;
				}
				case 'status': {
					where.ACTIVITY_JOIN_STATUS = Number(sortVal)
					break;
				}
			}
		}


		let result = await ActivityJoinModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);

		return result;
	}

	/** 取得我的上报详情 */
	async getMyActivityJoinDetail(userId, activityJoinId) {

		let fields = '*';

		let where = {
			_id: activityJoinId,
			ACTIVITY_JOIN_USER_ID: userId
		};
		let activityJoin = await ActivityJoinModel.getOne(where, fields);

		return activityJoin;
	}
 


	async statActivityJoin(id) {
		// 上报数
		let where = {
			ACTIVITY_JOIN_ACTIVITY_ID: id,
			ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC
		}
		let cnt = await ActivityJoinModel.count(where);


		await ActivityModel.edit(id, { ACTIVITY_JOIN_CNT: cnt });
	}

	/**  上报前获取关键信息 */
	async detailForActivityJoin(userId, activityId) {
		let fields = 'ACTIVITY_JOIN_FORMS, ACTIVITY_TITLE';

		let where = {
			_id: activityId,
			ACTIVITY_STATUS: ActivityModel.STATUS.COMM,
		}
		let activity = await ActivityModel.getOne(where, fields);
		if (!activity)
			this.AppError('该项目不存在');

		if (activity.ACTIVITY_MAX_CNT > 0) {
			let cnt = await ActivityJoinModel.count({ ACTIVITY_JOIN_USER_ID: userId });
			if (cnt >= activity.ACTIVITY_MAX_CNT)
				this.AppError('该项目您已经上报' + cnt + '次,已超过可提交上限~');
		}


		let myForms = [];

		if (myForms.length == 0) {

			let user = await UserModel.getOne({ USER_MINI_OPENID: userId, USER_STATUS: UserModel.STATUS.COMM });
			if (!user) this.AppError('用户异常');

			// 取得我的上报信息
			myForms = [
				{ mark: 'name', type: 'text', title: '姓名', val: user.USER_NAME },
				{ mark: 'phone', type: 'mobile', title: '手机', val: user.USER_MOBILE },
			]

		}

		activity.myForms = myForms;

		return activity;
	}

	/** 撤销我的上报 只有成功可以撤销 取消即为删除记录 */
	async cancelMyActivityJoin(userId, activityJoinId) {
		let where = {
			ACTIVITY_JOIN_USER_ID: userId,
			_id: activityJoinId,
			ACTIVITY_JOIN_STATUS: 0
		};
		let activityJoin = await ActivityJoinModel.getOne(where);

		if (!activityJoin) {
			this.AppError('未找到可撤销的记录');
		}

		let activity = await ActivityModel.getOne(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID);
		if (!activity)
			this.AppError('该项目不存在');

		if (activity.ACTIVITY_STATUS == ActivityModel.STATUS.UNUSE)
			this.AppError('该项目已停止,不能撤销');

		if (activity.ACTIVITY_CANCEL_SET == 0)
			this.AppError('该项目设置了不能撤销');

		if (activity.ACTIVITY_CANCEL_SET == 2 && activity.ACTIVITY_STOP < this._timestamp)
			this.AppError('该项目已经截止上报,不能撤销');

		await ActivityJoinModel.del(where);

		// 上报数量统计
		await this.statActivityJoin(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID);

	}


	/** 按天获取上报项目 */
	async getActivityListByDay(day) {
		let start = timeUtil.time2Timestamp(day);
		let end = start + 86400 * 1000 - 1;
		let where = {
			ACTIVITY_STATUS: ActivityModel.STATUS.COMM,
			ACTIVITY_START: ['between', start, end],
		};

		let orderBy = {
			'ACTIVITY_ORDER': 'asc',
			'ACTIVITY_ADD_TIME': 'desc'
		};

		let fields = 'ACTIVITY_TITLE,ACTIVITY_START,ACTIVITY_OBJ.cover';

		let list = await ActivityModel.getAll(where, fields, orderBy);

		let retList = [];

		for (let k = 0; k < list.length; k++) {

			let node = {};
			node.timeDesc = timeUtil.timestamp2Time(list[k].ACTIVITY_START, 'h:m');
			node.title = list[k].ACTIVITY_TITLE;
			node.pic = list[k].ACTIVITY_OBJ.cover[0];
			node._id = list[k]._id;
			retList.push(node);

		}
		return retList;
	}

	/**
	 * 获取从某天开始可报名的日期
	 * @param {*} fromDay  日期 Y-M-D
	 */
	async getActivityHasDaysFromDay(fromDay) {
		let where = {
			ACTIVITY_START: ['>=', timeUtil.time2Timestamp(fromDay)],
		};

		let fields = 'ACTIVITY_START';
		let list = await ActivityModel.getAllBig(where, fields);

		let retList = [];
		for (let k = 0; k < list.length; k++) {
			let day = timeUtil.timestamp2Time(list[k].ACTIVITY_START, 'Y-M-D');
			if (!retList.includes(day)) retList.push(day);
		}
		return retList;
	}


}

UI设计

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

后台管理

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

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

git代码下载

点击下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值