从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代码下载

点击下载

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值