概要设计

打卡是一种习惯的养成,是当下最流行、最受欢迎的工具,应用塌景广泛多样;各团体为了促活会定期开展一些闯关打卡活动;找一群志同道合的人,一起去打卡

功能定位:集学习、兴趣、生活、运动于一身的打卡小程序
用户群:面向学校学生、学生家长、老师、各种兴趣团体(跑步,运动,体育,艺术等)
主要模块:打卡项目列表,打卡排行,每日动态,后台打卡项目管理,后台打卡记录管理,后台打卡记录导出等功能

功能规划

image.png

数据字典


EnrollModel.DB_STRUCTURE = {
    _pid: 'string|true',
    ENROLL_ID: 'string|true',

    ENROLL_TITLE: 'string|true|comment=标题',
    ENROLL_STATUS: 'int|true|default=1|comment=状态 0=未启用,1=使用中',

    ENROLL_CATE_ID: 'string|true|default=0|comment=分类',
    ENROLL_CATE_NAME: 'string|false|comment=分类冗余',

    ENROLL_START: 'int|false|comment=开始时间',
    ENROLL_END: 'int|false|comment=结束时间',
    ENROLL_DAY_CNT: 'int|false|comment=持续天数',

    ENROLL_ORDER: 'int|true|default=9999',
    ENROLL_VOUCH: 'int|true|default=0',

    ENROLL_FORMS: 'array|true|default=[]',
    ENROLL_OBJ: 'object|true|default={}',

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

    ENROLL_QR: 'string|false',
    ENROLL_VIEW_CNT: 'int|true|default=0',
    ENROLL_JOIN_CNT: 'int|true|default=0',
    ENROLL_USER_CNT: 'int|true|default=0',

    ENROLL_USER_LIST: 'array|true|default=[]|comment={name,id,pic}',

    ENROLL_ADD_TIME: 'int|true',
    ENROLL_EDIT_TIME: 'int|true',
    ENROLL_ADD_IP: 'string|false',
    ENROLL_EDIT_IP: 'string|false',
};


EnrollJoinModel.DB_STRUCTURE = {
    _pid: 'string|true',
    ENROLL_JOIN_ID: 'string|true',
    ENROLL_JOIN_ENROLL_ID: 'string|true|comment=打卡PK',

    ENROLL_JOIN_USER_ID: 'string|true|comment=用户ID',
    ENROLL_JOIN_DAY: 'string|true|comment=日期',
    ENROLL_JOIN_FORMS: 'array|true|default=[]|comment=表单',

    ENROLL_JOIN_STATUS: 'int|true|default=1|comment=状态 1=成功', 

    ENROLL_JOIN_ADD_TIME: 'int|true',
    ENROLL_JOIN_EDIT_TIME: 'int|true',
    ENROLL_JOIN_ADD_IP: 'string|false',
    ENROLL_JOIN_EDIT_IP: 'string|false',
};

核心难点


    // 获取当前打卡状态
    getJoinStatusDesc(enroll) {
        let timestamp = this._timestamp;

        if (enroll.ENROLL_STATUS == 0)
            return '已停止';
        else if (enroll.ENROLL_START > timestamp)
            return '未开始';
        else if (enroll.ENROLL_END <= timestamp)
            return '已结束';
        else
            return '进行中';
    }

    // 获取某日动态
    async getEnrollJoinByDay(enrollId, day = '') {
        if (!day) day = timeUtil.time('Y-M-D');

        let where = {
            ENROLL_JOIN_ENROLL_ID: enrollId,
            ENROLL_JOIN_DAY: day,
            ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC
        }
        let joinParams = {
            from: UserModel.CL,
            localField: 'ENROLL_JOIN_USER_ID',
            foreignField: 'USER_MINI_OPENID',
            as: 'user',
        };
        let orderBy = {
            ENROLL_JOIN_ADD_TIME: 'desc'
        }
        let list = await EnrollJoinModel.getListJoin(joinParams, where, 'user.USER_NAME,user.USER_PIC', orderBy, 1, 100, false, 0);
        return list.list;
    }

    // 获取某活动排行
    async getEnrollUserRank(enrollId) {

        let where = {
            ENROLL_USER_ENROLL_ID: enrollId
        }
        let joinParams = {
            from: UserModel.CL,
            localField: 'ENROLL_USER_MINI_OPENID',
            foreignField: 'USER_MINI_OPENID',
            as: 'user',
        };
        let orderBy = {
            ENROLL_USER_JOIN_CNT: 'desc'
        }
        let fields = 'ENROLL_USER_JOIN_CNT,ENROLL_USER_LAST_DAY,user.USER_NAME,user.USER_PIC';
        let list = await EnrollUserModel.getListJoin(joinParams, where, fields, orderBy, 1, 100, false, 0);
        return list.list;
    }

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

        let fields = '*';

        let where = {
            _id: id,
            ENROLL_STATUS: EnrollModel.STATUS.COMM
        }
        let enroll = await EnrollModel.getOne(where, fields);
        if (!enroll) return null;

        EnrollModel.inc(id, 'ENROLL_VIEW_CNT', 1);

        // 判断用户今日是否有打卡
        let whereJoin = {
            ENROLL_JOIN_USER_ID: userId,
            ENROLL_JOIN_ENROLL_ID: id,
            ENROLL_JOIN_DAY: timeUtil.time('Y-M-D'),
            ENROLL_JOIN_STATUS: EnrollJoinModel.STATUS.SUCC
        }
        let enrollJoin = await EnrollJoinModel.getOne(whereJoin);
        if (enrollJoin) {
            enroll.myEnrollJoinId = enrollJoin._id;
        }
        else {
            enroll.myEnrollJoinId = '';
        }

        // 某日打卡列表
        enroll.activity = await this.getEnrollJoinByDay(id);

        // 打卡日期数组
        let dayList = [];
        let start = timeUtil.timestamp2Time(enroll.ENROLL_START, 'Y-M-D');
        start = timeUtil.time2Timestamp(start);
        let today = timeUtil.time2Timestamp(timeUtil.time('Y-M-D'));

        for (let k = start; k <= today;) {
            let month = timeUtil.timestamp2Time(k, 'M月');
            if (month.startsWith('0')) month = month.substring(1);

            let date = timeUtil.timestamp2Time(k, 'D');
            let day = timeUtil.timestamp2Time(k, 'Y-M-D');

            dayList.push({ month, date, day });
            k = k + 86400 * 1000;
        }
        enroll.dayList = dayList;

        // 排行榜 
        let rankList = await this.getEnrollUserRank(id);
        enroll.rankList = rankList;

        return enroll;
    }


    /** 取得分页列表 */
    async getEnrollList({
        search, // 搜索条件
        sortType, // 搜索菜单
        sortVal, // 搜索菜单
        orderBy, // 排序 
        page,
        size,
        isTotal = true,
        oldTotal
    }) {

        orderBy = orderBy || {
            'ENROLL_ORDER': 'asc',
            'ENROLL_ADD_TIME': 'desc'
        };
        let fields = 'ENROLL_USER_LIST,ENROLL_JOIN_CNT,ENROLL_OBJ,ENROLL_USER_CNT,ENROLL_TITLE,ENROLL_START,ENROLL_END,ENROLL_ORDER,ENROLL_STATUS,ENROLL_CATE_NAME,ENROLL_OBJ';

        let where = {};
        where.and = {
            _pid: this.getProjectId() //复杂的查询在此处标注PID
        };

        where.and.ENROLL_STATUS = EnrollModel.STATUS.COMM; // 状态  

        if (util.isDefined(search) && search) {
            where.or = [{
                ENROLL_TITLE: ['like', search]
            },];
        } else if (sortType && util.isDefined(sortVal)) {
            // 搜索菜单
            switch (sortType) {
                case 'cateId': {
                    if (sortVal) where.and.ENROLL_CATE_ID = String(sortVal);
                    break;
                }
                case 'sort': {
                    orderBy = this.fmtOrderBySort(sortVal, 'ENROLL_ADD_TIME');
                    break;
                }

            }
        }

        return await EnrollModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
    }

用户打卡 UI设计

image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

后台系统UI设计

image.png
image.png
image.png
image.png
image.png
image.png
image.png

源码

gitee源码地址


CC同学呀
27 声望13 粉丝

鹅厂程序猿一枚,交流v: cclinux0730