可研剖析大学生流动社交小程序是一种基于挪动互联网的社交平台,旨在为大学生提供一个不便、快捷、平安的社交和流动交流平台
性能布局流动公布:平台能够公布将要举办的流动,包含工夫、地点、费用等信息,并邀请其余用户加入。流动搜寻:用户能够依据本人的兴趣爱好搜寻流动,并加入感兴趣的流动。流动评估:用户能够对加入过的流动进行评估和反馈,帮忙其余用户更好地抉择流动。打卡模块:能够帮忙大学生记录和评比每天的学习、浏览、文艺、体育、体育等流动。通过实现一个个小指标帮忙大学生更好地治理本人的工夫和指标。
实现目标大学生流动社交小程序能够帮忙大学生更好地组织和加入流动,扩大社交圈子,丰盛大学生存。 本我的项目前后端残缺代码包含布告告诉,校园风采,流动分类与列表,流动报名与评估,打卡我的项目列表,打卡排行与每日动静,我的流动报名,我的每日打卡;后盾打卡项目管理,打卡记录治理与导出,后盾流动与报名治理,报名审核与数据导出等性能,流动组织方能够自定义要填写的内容
数据库设计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_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_CHECK_SET: 'int|true|default=0|comment=审核 0=不须要审核,1=须要审核', ACTIVITY_IS_MENU: 'int|true|default=1|comment=是否公开展现名单', ACTIVITY_MAX_CNT: 'int|true|default=20|comment=人数下限 0=不限', ACTIVITY_START: 'int|false|comment=流动开始工夫', ACTIVITY_END: 'int|false|comment=流动截止工夫', ACTIVITY_STOP: 'int|true|default=0|comment=报名截止工夫 0=永不过期', 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_USER_LIST: 'array|true|default=[]|comment={name,id,pic}', 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_IS_ADMIN: 'int|true|default=0|comment=是否管理员增加 0/1', ACTIVITY_JOIN_CODE: 'string|true|comment=核验码15位', ACTIVITY_JOIN_IS_CHECKIN: 'int|true|default=0|comment=是否签到 0/1 ', ACTIVITY_JOIN_CHECKIN_TIME: 'int|false|default=0|签到工夫', ACTIVITY_JOIN_USER_ID: 'string|true|comment=用户ID', ACTIVITY_JOIN_FORMS: 'array|true|default=[]|comment=表单', ACTIVITY_JOIN_OBJ: 'object|true|default={}', ACTIVITY_JOIN_STATUS: 'int|true|default=1|comment=状态 0=待审核 1=报名胜利, 99=审核未过', ACTIVITY_JOIN_REASON: 'string|false|comment=审核回绝或者勾销理由', 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 == 0) return '流动进行'; else if (activity.ACTIVITY_END <= timestamp) return '流动完结'; else if (activity.ACTIVITY_STOP <= timestamp) return '报名完结'; else if (activity.ACTIVITY_MAX_CNT > 0 && activity.ACTIVITY_JOIN_CNT >= activity.ACTIVITY_MAX_CNT) 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); // 判断是否有报名 let whereJoin = { ACTIVITY_JOIN_USER_ID: userId, ACTIVITY_JOIN_ACTIVITY_ID: id, ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]] } let activityJoin = await ActivityJoinModel.getOne(whereJoin); if (activityJoin) { activity.myActivityJoinId = activityJoin._id; activity.myActivityJoinTag = (activityJoin.ACTIVITY_JOIN_STATUS == ActivityJoinModel.STATUS.WAIT) ? '待审核' : '已报名'; } else { activity.myActivityJoinId = ''; activity.myActivityJoinTag = ''; } return activity; } /** 获得分页列表 */ async getActivityList({ cateId, //分类查问条件 search, // 搜寻条件 sortType, // 搜寻菜单 sortVal, // 搜寻菜单 orderBy, // 排序 page, size, isTotal = true, oldTotal }) { orderBy = orderBy || { 'ACTIVITY_ORDER': 'asc', 'ACTIVITY_ADD_TIME': 'desc' }; let fields = 'ACTIVITY_USER_LIST,ACTIVITY_STOP,ACTIVITY_JOIN_CNT,ACTIVITY_OBJ,ACTIVITY_VIEW_CNT,ACTIVITY_TITLE,ACTIVITY_MAX_CNT,ACTIVITY_START,ACTIVITY_END,ACTIVITY_ORDER,ACTIVITY_STATUS,ACTIVITY_CATE_NAME,ACTIVITY_OBJ'; let where = {}; where.and = { _pid: this.getProjectId() //简单的查问在此处标注PID }; if (cateId && cateId !== '0') where.and.ACTIVITY_CATE_ID = cateId; where.and.ACTIVITY_STATUS = ActivityModel.STATUS.COMM; // 状态 if (util.isDefined(search) && search) { where.or = [{ ACTIVITY_TITLE: ['like', search] },]; } else if (sortType && util.isDefined(sortVal)) { // 搜寻菜单 switch (sortType) { case 'cateId': { if (sortVal) where.and.ACTIVITY_CATE_ID = String(sortVal); break; } case 'sort': { // 排序 orderBy = this.fmtOrderBySort(sortVal, 'ACTIVITY_ADD_TIME'); break; } case 'today': { //明天 let start = timeUtil.getDayFirstTimestamp(); let end = start + 86400 * 1000 - 1; where.and.ACTIVITY_START = ['between', start, end]; break; } case 'tomorrow': { //明日 let start = timeUtil.getDayFirstTimestamp() + 86400 * 1000; let end = start + 86400 * 1000 - 1; where.and.ACTIVITY_START = ['between', start, end]; break; } case 'month': { //本月 let day = timeUtil.time('Y-M-D'); let start = timeUtil.getMonthFirstTimestamp(day); let end = timeUtil.getMonthLastTimestamp(day); where.and.ACTIVITY_START = ['between', start, end]; break; } } } return await ActivityModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal); } /** 获得某一个报名分页列表 */ async getActivityJoinList(activityId, { search, // 搜寻条件 sortType, // 搜寻菜单 sortVal, // 搜寻菜单 orderBy, // 排序 page, size, isTotal = true, oldTotal }) { orderBy = orderBy || { 'ACTIVITY_JOIN_ADD_TIME': 'desc' }; let fields = 'ACTIVITY_JOIN_OBJ,ACTIVITY_JOIN_IS_CHECKIN,ACTIVITY_JOIN_REASON,ACTIVITY_JOIN_ACTIVITY_ID,ACTIVITY_JOIN_STATUS,ACTIVITY_JOIN_ADD_TIME,user.USER_PIC,user.USER_NAME,user.USER_OBJ'; let where = { ACTIVITY_JOIN_ACTIVITY_ID: activityId, ACTIVITY_JOIN_STATUS: ActivityModel.STATUS.COMM }; let joinParams = { from: UserModel.CL, localField: 'ACTIVITY_JOIN_USER_ID', foreignField: 'USER_MINI_OPENID', as: 'user', }; let result = await ActivityJoinModel.getListJoin(joinParams, where, fields, orderBy, page, size, isTotal, oldTotal); return result; } /** 获得我的报名分页列表 */ async getMyActivityJoinList(userId, { search, // 搜寻条件 sortType, // 搜寻菜单 sortVal, // 搜寻菜单 orderBy, // 排序 page, size, isTotal = true, oldTotal }) { orderBy = orderBy || { 'ACTIVITY_JOIN_ADD_TIME': 'desc' }; let fields = 'ACTIVITY_JOIN_IS_CHECKIN,ACTIVITY_JOIN_REASON,ACTIVITY_JOIN_ACTIVITY_ID,ACTIVITY_JOIN_STATUS,ACTIVITY_JOIN_ADD_TIME,activity.ACTIVITY_END,activity.ACTIVITY_START,activity.ACTIVITY_TITLE'; 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 'succ': { where.ACTIVITY_JOIN_STATUS = ActivityJoinModel.STATUS.SUCC; break; } case 'wait': { where.ACTIVITY_JOIN_STATUS = ActivityJoinModel.STATUS.WAIT; break; } case 'cancel': { where.ACTIVITY_JOIN_STATUS = ActivityJoinModel.STATUS.ADMIN_CANCEL; break; } } } let joinParams = { from: ActivityModel.CL, localField: 'ACTIVITY_JOIN_ACTIVITY_ID', foreignField: '_id', as: 'activity', }; let result = await ActivityJoinModel.getListJoin(joinParams, 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); if (activityJoin) { activityJoin.activity = await ActivityModel.getOne(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID, 'ACTIVITY_TITLE,ACTIVITY_START,ACTIVITY_END'); } return activityJoin; } //################## 报名 // 报名 async activityJoin(userId, activityId, forms) { // 报名是否完结 let whereActivity = { _id: activityId, ACTIVITY_STATUS: ActivityModel.STATUS.COMM } let activity = await ActivityModel.getOne(whereActivity); if (!activity) this.AppError('该流动不存在或者曾经进行'); // 是否流动完结 if (activity.ACTIVITY_END < this._timestamp) this.AppError('该流动曾经完结,请抉择其余流动'); // 是否过了报名截止期 if (activity.ACTIVITY_STOP < this._timestamp) this.AppError('该流动报名曾经截止,请抉择其余流动'); // 人数是否满 if (activity.ACTIVITY_MAX_CNT > 0) { let whereCnt = { ACTIVITY_JOIN_ACTIVITY_ID: activityId, ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]] } let cntJoin = await ActivityJoinModel.count(whereCnt); if (cntJoin >= activity.ACTIVITY_MAX_CNT) this.AppError('该流动报名已满,请抉择其余流动'); } // 本人是否曾经有报名 let whereMy = { ACTIVITY_JOIN_USER_ID: userId, ACTIVITY_JOIN_ACTIVITY_ID: activityId, ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]] } let my = await ActivityJoinModel.getOne(whereMy); if (my) { if (my.ACTIVITY_JOIN_STATUS == ActivityJoinModel.STATUS.WAIT) this.AppError('您曾经报名,正在期待审核,毋庸反复报名'); else this.AppError('您曾经报名胜利,毋庸反复报名'); } // 入库 let data = { ACTIVITY_JOIN_USER_ID: userId, ACTIVITY_JOIN_ACTIVITY_ID: activityId, ACTIVITY_JOIN_STATUS: (activity.ACTIVITY_CHECK_SET == 0) ? ActivityJoinModel.STATUS.SUCC : ActivityJoinModel.STATUS.WAIT, ACTIVITY_JOIN_FORMS: forms, ACTIVITY_JOIN_OBJ: dataUtil.dbForms2Obj(forms), ACTIVITY_JOIN_CODE: dataUtil.genRandomIntString(15), } let activityJoinId = await ActivityJoinModel.insert(data); // 统计数量 await this.statActivityJoin(activityId); let check = activity.ACTIVITY_CHECK_SET; return { activityJoinId, check } } async statActivityJoin(id) { // 报名数 let where = { ACTIVITY_JOIN_ACTIVITY_ID: id, ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]] } let cnt = await ActivityJoinModel.count(where); // 用户列表 where = { ACTIVITY_JOIN_ACTIVITY_ID: id, ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC } let joinParams = { from: UserModel.CL, localField: 'ACTIVITY_JOIN_USER_ID', foreignField: 'USER_MINI_OPENID', as: 'user', }; let orderBy = { ACTIVITY_JOIN_ADD_TIME: 'desc' } let list = await ActivityJoinModel.getListJoin(joinParams, where, 'ACTIVITY_JOIN_ADD_TIME,user.USER_MINI_OPENID,user.USER_NAME,user.USER_PIC', orderBy, 1, 6, false, 0); list = list.list; for (let k = 0; k < list.length; k++) { list[k] = list[k].user; } await ActivityModel.edit(id, { ACTIVITY_JOIN_CNT: cnt, ACTIVITY_USER_LIST: list }); } /** 报名前获取要害信息 */ 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('该流动不存在'); // 取出自己最近一次的填写表单 let whereMy = { ACTIVITY_JOIN_USER_ID: userId, } let orderByMy = { ACTIVITY_JOIN_ADD_TIME: 'desc' } let joinMy = await ActivityJoinModel.getOne(whereMy, 'ACTIVITY_JOIN_FORMS', orderByMy); let myForms = joinMy ? joinMy.ACTIVITY_JOIN_FORMS : []; activity.myForms = myForms; return activity; } /** 勾销我的报名 只有胜利和待审核能够勾销 勾销即为删除记录 */ async cancelMyActivityJoin(userId, activityJoinId) { let where = { ACTIVITY_JOIN_USER_ID: userId, _id: activityJoinId, ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]] }; let activityJoin = await ActivityJoinModel.getOne(where); if (!activityJoin) { this.AppError('未找到可勾销的报名记录'); } if (activityJoin.ACTIVITY_JOIN_IS_CHECKIN == 1) this.AppError('该流动曾经签到,无奈勾销'); let activity = await ActivityModel.getOne(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID); if (!activity) this.AppError('该流动不存在'); if (activity.ACTIVITY_END <= this._timestamp) 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 myJoinSelf(userId, activityId) { let activity = await ActivityModel.getOne(activityId); if (!activity) this.AppError('流动不存在或者曾经敞开'); let day = timeUtil.timestamp2Time(activity.ACTIVITY_START, 'Y-M-D'); let today = timeUtil.time('Y-M-D'); if (day != today) this.AppError('仅在流动当天能够签到,以后签到码的日期是' + day); let whereSucc = { ACTIVITY_JOIN_USER_ID: userId, ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC } let cntSucc = await ActivityJoinModel.count(whereSucc); let whereCheckin = { ACTIVITY_JOIN_USER_ID: userId, ACTIVITY_JOIN_IS_CHECKIN: 1, ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC } let cntCheckin = await ActivityJoinModel.count(whereCheckin); let ret = ''; if (cntSucc == 0) { ret = '您没有本次流动报名胜利的记录,请在「集体核心 - 我的流动报名」查看详情~'; } else if (cntSucc == cntCheckin) { // 同一流动屡次报名的状况 ret = '您已签到,毋庸反复签到,请在「集体核心 - 我的流动报名」查看详情~'; } else { let where = { ACTIVITY_JOIN_USER_ID: userId, ACTIVITY_JOIN_IS_CHECKIN: 0, ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC } let data = { ACTIVITY_JOIN_IS_CHECKIN: 1, ACTIVITY_JOIN_CHECKIN_TIME: this._timestamp, } await ActivityJoinModel.edit(where, data); ret = '签到胜利,请在「集体核心 - 我的流动报名」查看详情~' } return { ret }; } /** 按天获取报名我的项目 */ 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设计
...