前期調研
不文明現象隨手拍小程序:在城市的快速發展進程中,不文明現象時有發生,為了有效解決這一問題,提升城市文明程度, 市民若發現不文明行為,如亂扔垃圾、隨地吐痰、破壞公共設施、違規停車等,只需點擊“上報不文明現象”按鈕,即可將這些不文明行為記錄下來,并附上簡短的文字描述,如事件發生的具體地點、時間以及對周圍環境或他人造成的影響等。市民可以在小程序的“我的上報記錄”頁面中隨時查看自己的上報; 為了進一步調動市民參與的積極性,小程序設置了積分激勵機制。每當市民成功上報一條不文明現象并被后臺審核通過后,即可獲得相應的積分獎勵。 同時提供積分商城模塊,可以讓市民使用通過參與任務所獲得的積分來兌換各種獎勵或福利。
功能規劃
數據設計
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 '項目結束';elsereturn '進行中';}/** 瀏覽信息 */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代碼下載
點擊下載