可研剖析

大学生流动社交小程序是一种基于挪动互联网的社交平台,旨在为大学生提供一个不便、快捷、平安的社交和流动交流平台

性能布局

  • 流动公布:平台能够公布将要举办的流动,包含工夫、地点、费用等信息,并邀请其余用户加入。
  • 流动搜寻:用户能够依据本人的兴趣爱好搜寻流动,并加入感兴趣的流动。
  • 流动评估:用户能够对加入过的流动进行评估和反馈,帮忙其余用户更好地抉择流动。
  • 打卡模块:能够帮忙大学生记录和评比每天的学习、浏览、文艺、体育、体育等流动。通过实现一个个小指标帮忙大学生更好地治理本人的工夫和指标。
  • 实现目标

    大学生流动社交小程序能够帮忙大学生更好地组织和加入流动,扩大社交圈子,丰盛大学生存。 本我的项目前后端残缺代码包含布告告诉,校园风采,流动分类与列表,流动报名与评估,打卡我的项目列表,打卡排行与每日动静,我的流动报名,我的每日打卡;后盾打卡项目管理,打卡记录治理与导出,后盾流动与报名治理,报名审核与数据导出等性能,流动组织方能够自定义要填写的内容

数据库设计

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设计











后盾UI






git地址

git代码地址