从0到1:培训老师预约小程序开发笔记二

培训教师预定小步调&#Vff1a; 老师和学生可以更便利地安牌课程&#Vff0c;并提升教学量质和进修成效&#Vff0c;使之成为打点和提升教学成效的壮大工具。培训教师可以正在小步调上设置原人的可预定光阳&#Vff0c;学员可以依据教师的日程安牌选择适宜的光阳停行预定。那样可以进步预定的效率&#Vff0c;减少沟通老原&#Vff0c;便捷单方的安牌

罪能布局

首页展示&#Vff1a;展示最新的通告通知、教师引荐等内容&#Vff0c;吸引用户关注。

教师列表&#Vff1a;列出所有开课的教师信息&#Vff0c;蕴含教师的个人简介、星级等&#Vff0c;便操做户选择适宜的教师。

预定罪能&#Vff1a;用户可以依据原人的需求选择教师&#Vff0c;并停行预定。

预定打点&#Vff1a;用户可以查察原人的预定记录&#Vff0c;蕴含已完成的预定和待核销的预定&#Vff0c;也可以撤消预定。

靠山端&#Vff1a;可以添加和设定教师的根柢信息&#Vff0c;账号&#Vff0c;登陆暗码等。

教师端&#Vff1a;可以编辑原人的个人量料&#Vff08;头像&#Vff0c;简介&#Vff0c;标签集等&#Vff09;&#Vff0c;设定预定时段牌期&#Vff08;可预定时段&#Vff0c;各时段人数限定&#Vff09;&#Vff0c; 正在现场核销用户的预定码。

顾主端&#Vff1a;选择原人须要的教师和时段&#Vff0c;下单预定&#Vff0c;预定乐成后出示预定码给教师大概工做人员核销。

提要设想

在这里插入图片描述

数据库设想 MeetModel.DB_STRUCTURE = { _pid: 'string|true', MEET_ID: 'string|true', MEET_ADMIN_ID: 'string|true|comment=添加的打点员', MEET_TITLE: 'string|true|comment=题目', MEET_JOIN_FORMS: 'array|true|default=[]|comment=表单字段设置', MEET_DAYS: 'array|true|default=[]|comment=最近一次批改保存的可用日期', MEET_CATE_ID: 'string|true|comment=分类编号', MEET_CATE_NAME: 'string|true|comment=分类冗余', MEET_FORMS: 'array|true|default=[]', MEET_OBJ: 'object|true|default={}', MEET_CANCEL_SET: 'int|true|default=1|comment=撤消设置 0=不允,1=允许,2=仅初步前可撤消', MEET_STATUS: 'int|true|default=1|comment=形态 0=未启用,1=运用中,9=进止预定,10=已封锁', MEET_ORDER: 'int|true|default=9999', MEET_xOUCH: 'int|true|default=0', MEET_QR: 'string|false', MEET_PHONE: 'string|false|comment=登录手机', MEET_PASSWORD: 'string|false|comment=登录暗码', MEET_TOKEN: 'string|false|comment=当前登录token', MEET_TOKEN_TIME: 'int|true|default=0|comment=当前登录token time', MEET_MINI_OPENID: 'string|false|comment=小步调openid', MEET_LOGIN_CNT: 'int|true|default=0|comment=登录次数', MEET_LOGIN_TIME: 'int|false|comment=最近登录光阳', MEET_ADD_TIME: 'int|true', MEET_EDIT_TIME: 'int|true', MEET_ADD_IP: 'string|false', MEET_EDIT_IP: 'string|false', }; UserModel.DB_STRUCTURE = { _pid: 'string|true', USER_ID: 'string|true', USER_MINI_OPENID: 'string|true|comment=小步调openid', USER_STATUS: 'int|true|default=1|comment=形态 0=待审核,1=一般,8=审核未过,9=进用', USER_CHECK_REASON: 'string|false|comment=审核未过的理由', USER_NAME: 'string|false|comment=用户昵称', USER_MOBILE: 'string|false|comment=联络电话', USER_FORMS: 'array|true|default=[]', USER_OBJ: 'object|true|default={}', USER_LOGIN_CNT: 'int|true|default=0|comment=登录次数', USER_LOGIN_TIME: 'int|false|comment=最近登录光阳', USER_ADD_TIME: 'int|true', USER_ADD_IP: 'string|false', USER_EDIT_TIME: 'int|true', USER_EDIT_IP: 'string|false', } 焦点代码 class MeetSerZZZice eVtends BaseProjectSerZZZice { constructor() { super(); this._log = new LogUtil(projectConfig.MEET_LOG_LExEL); } /** * 抛出异样 * @param {*} msg * @param {*} code */ AppError(msg) { this._log.error(msg); super.AppError(msg); } _meetLog(meet, func = '', msg = '') { let str = ''; str = `[MEET=${meet.MEET_TITLE}][${func}] ${msg}`; this._log.debug(str); } /** 统一获与Meet&#Vff08;某天) */ async getMeetOneDay(meetId, day, where, fields = '*') { let meet = await MeetModel.getOne(where, fields); if (!meet) return meet; meet.MEET_DAYS_SET = await this.getDaysSet(meetId, day, day); return meet; } /** 获与日期设置 */ async getDaysSet(meetId, startDay, endDay = null) { let where = { DAY_MEET_ID: meetId } if (startDay && endDay && endDay == startDay) where.day = startDay; else if (startDay && endDay) where.day = ['between', startDay, endDay]; else if (!startDay && endDay) where.day = ['<=', endDay]; else if (startDay && !endDay) where.day = ['>=', startDay]; let orderBy = { 'day': 'asc' } let list = await DayModel.getAllBig(where, 'day,dayDesc,times', orderBy, 1000); for (let k = 0; k < list.length; k++) { delete list[k]._id; } return list; } // 定时段统计某时段报名状况 async statJoinCnt(meetId, timeMark) { let whereDay = { DAY_MEET_ID: meetId, day: this.getDayByTimeMark(timeMark) }; let day = await DayModel.getOne(whereDay, 'times'); if (!day) return; let whereJoin = { JOIN_MEET_TIME_MARK: timeMark, JOIN_MEET_ID: meetId }; let ret = await JoinModel.groupCount(whereJoin, 'JOIN_STATUS'); let stat = { //统计数据 succCnt: ret['JOIN_STATUS_1'] || 0, //1=预定乐成, cancelCnt: ret['JOIN_STATUS_10'] || 0, //10=已撤消, adminCancelCnt: ret['JOIN_STATUS_99'] || 0, //99=靠山撤消 }; let times = day.times; for (let j in times) { if (times[j].mark === timeMark) { let data = { ['times.' + j + '.stat']: stat } await DayModel.edit(whereDay, data); return; } } } // 预定前检测 async beforeJoin(userId, meetId, timeMark) { await this.checkMeetRules(userId, meetId, timeMark); } // 依据日期获与其所正在天设置 getDaySetByDay(meet, day) { for (let k = 0; k < meet.MEET_DAYS_SET.length; k++) { if (meet.MEET_DAYS_SET[k].day == day) return dataUtil.deepClone(meet.MEET_DAYS_SET[k]); } return null; } // 依据时段标识获与其所正在天 getDayByTimeMark(timeMark) { return timeMark.substr(1, 4) + '-' + timeMark.substr(5, 2) + '-' + timeMark.substr(7, 2); } // 依据时段标识获与其所正在天设置 getDaySetByTimeMark(meet, timeMark) { let day = this.getDayByTimeMark(timeMark); for (let k = 0; k < meet.MEET_DAYS_SET.length; k++) { if (meet.MEET_DAYS_SET[k].day == day) return dataUtil.deepClone(meet.MEET_DAYS_SET[k]); } return null; } // 依据时段标识获与其所正在时段设置 getTimeSetByTimeMark(meet, timeMark) { let day = this.getDayByTimeMark(timeMark); for (let k = 0; k < meet.MEET_DAYS_SET.length; k++) { if (meet.MEET_DAYS_SET[k].day != day) continue; for (let j in meet.MEET_DAYS_SET[k].times) { if (meet.MEET_DAYS_SET[k].times[j].mark == timeMark) return dataUtil.deepClone(meet.MEET_DAYS_SET[k].times[j]); } } return null; } // 预定时段人数和形态控制校验 async checkMeetTimeControll(meet, timeMark, meetPeopleCnt = 1) { if (!meet) this.AppError('预定时段设置舛错, 预定名目不存正在'); let daySet = this.getDaySetByTimeMark(meet, timeMark); // 当天设置 let timeSet = this.getTimeSetByTimeMark(meet, timeMark); // 预定时段设置 if (!daySet || !timeSet) this.AppError('预定时段设置舛错day&time'); let statusDesc = timeSet.status == 1 ? '开启' : '封锁'; let limitDesc = ''; if (timeSet.isLimit) { limitDesc = '人数上限MAX=' + timeSet.limit; } else limitDesc = '人数不限制NO'; this._meetLog(meet, `------------------------------`); this._meetLog(meet, `#预定时段控制,预定日期=<${daySet.day}>`, `预定时段=[${timeSet.start}-${timeSet.end}],形态=${statusDesc}, ${limitDesc} 当前预定乐成人数=${timeSet.stat.succCnt}`); if (timeSet.status == 0) this.AppError('该时段预定曾经封锁&#Vff0c;请选择其余'); // 时段总人数限制 if (timeSet.isLimit) { if (timeSet.stat.succCnt >= timeSet.limit) { this.AppError('该时段预定人员已满&#Vff0c;请选择其余'); } let maVCnt = timeSet.limit - timeSet.stat.succCnt; if (maVCnt < meetPeopleCnt) { this.AppError('原时段最多还可以预定' + (maVCnt) + '人&#Vff0c;您当前提交了' + meetPeopleCnt + '人&#Vff0c;请调解后再提交'); } } } /** 报名规矩校验 */ async checkMeetRules(userId, meetId, timeMark, formsList = null) { // 预定时段能否存正在 let meetWhere = { _id: meetId }; let day = this.getDayByTimeMark(timeMark); let meet = await this.getMeetOneDay(meetId, day, meetWhere); if (!meet) { this.AppError('预定时段选择舛错&#Vff0c;请从头选择'); } // 预定时段人数和形态控制校验 let meetPeopleCnt = formsList ? formsList.length : 1; await this.checkMeetTimeControll(meet, timeMark, meetPeopleCnt); // 截行规矩 await this.checkMeetEndSet(meet, timeMark); // 针对用户的次数限制 await this.checkMeetLimitSet(userId, meet, timeMark, meetPeopleCnt); } // 预定次数限制校验 async checkMeetLimitSet(userId, meet, timeMark, nowCnt) { if (!meet) this.AppError('预定次数规矩舛错, 预定名目不存正在'); let meetId = meet._id; let daySet = this.getDaySetByTimeMark(meet, timeMark); // 当天设置 let timeSet = this.getTimeSetByTimeMark(meet, timeMark); // 预定时段设置 this._meetLog(meet, `------------------------------`); this._meetLog(meet, `#预定次数规矩,预定日期=<${daySet.day}>`, `预定时段=[${timeSet.start}&#Vff5e;${timeSet.end}]`); let where = { JOIN_MEET_ID: meetId, JOIN_MEET_TIME_MARK: timeMark, JOIN_USER_ID: userId, JOIN_STATUS: JoinModel.STATUS.SUCC } let cnt = await JoinModel.count(where); let maVCnt = projectConfig.MEET_MAX_JOIN_CNT; this._meetLog(meet, `预定次数规矩,mode=原时段可预定${maVCnt}次`, `当前已预定=${cnt}次`); if (cnt >= maVCnt) this.AppError(`您原时段曾经预定&#Vff0c;不能继续预定`); } // 预定截行设置校验 async checkMeetEndSet(meet, timeMark) { if (!meet) this.AppError('预定截行规矩舛错, 预定名目不存正在'); this._meetLog(meet, `------------------------------`); let daySet = this.getDaySetByTimeMark(meet, timeMark); // 当天设置 let timeSet = this.getTimeSetByTimeMark(meet, timeMark); // 预定时段设置 this._meetLog(meet, `#预定截行规矩,预定日期=<${daySet.day}>`, `预定时段=[${timeSet.start}-${timeSet.end}]`); let nowTime = timeUtil.time('Y-M-D h:m:s'); /* let startTime = daySet.day + ' ' + timeSet.start + ':00'; this._meetLog(meet, `预定初步规矩,mode=<时段逾期判定>`, `预定初步时段=${startTime},当前时段=${nowTime}`); if (nowTime > startTime) { this.AppError('该时段已初步&#Vff0c;无奈预定&#Vff0c;请选择其余'); }*/ let endTime = daySet.day + ' ' + timeSet.end + ':59'; this._meetLog(meet, `预定初步规矩,mode=<时段逾期判定>`, `预定完毕时段=${endTime},当前时段=${nowTime}`); if (nowTime > endTime) { this.AppError('该时段已完毕&#Vff0c;无奈预定&#Vff0c;请选择其余'); } } /** 预定详情 */ async ZZZiewMeet(meetId) { let fields = '*'; let where = { _id: meetId, MEET_STATUS: ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OxER]] } let meet = await MeetModel.getOne(where, fields); if (!meet) return null; let getDaysSet = []; meet.MEET_DAYS_SET = await this.getDaysSet(meetId, timeUtil.time('Y-M-D')); //原日及以后 let daysSet = meet.MEET_DAYS_SET; let now = timeUtil.time('Y-M-D'); for (let k = 0; k < daysSet.length; k++) { let dayNode = daysSet[k]; if (dayNode.day < now) continue; // 牌除逾期 let getTimes = []; for (let j in dayNode.times) { let timeNode = dayNode.times[j]; // 牌除形态封锁的时段 if (timeNode.status != 1) continue; // 判断数质能否已满 if (timeNode.isLimit && timeNode.stat.succCnt >= timeNode.limit) timeNode.error = '预定已满'; // 截行规矩 if (!timeNode.error) { try { await this.checkMeetEndSet(meet, timeNode.mark); } catch (eV) { if (eV.name == 'AppError') timeNode.error = '预定完毕'; else throw eV; } } getTimes.push(timeNode); } dayNode.times = getTimes; getDaysSet.push(dayNode); } // 只返回须要的字段 let ret = {}; ret.MEET_DAYS_SET = getDaysSet; ret.MEET_QR = meet.MEET_QR; ret.MEET_TITLE = meet.MEET_TITLE; ret.MEET_CATE_NAME = meet.MEET_CATE_NAME; ret.MEET_OBJ = meet.MEET_OBJ; return ret; } /** 预定前获与要害信息 */ async detailForJoin(userId, meetId, timeMark) { let fields = 'MEET_DAYS_SET,MEET_JOIN_FORMS, MEET_TITLE'; let where = { _id: meetId, MEET_STATUS: ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OxER]] } let day = this.getDayByTimeMark(timeMark); let meet = await this.getMeetOneDay(meetId, day, where, fields); if (!meet) return null; let dayDesc = timeUtil.fmtDateCHN(this.getDaySetByTimeMark(meet, timeMark).day); let timeSet = this.getTimeSetByTimeMark(meet, timeMark); let timeDesc = timeSet.start + '&#Vff5e;' + timeSet.end; meet.dayDesc = dayDesc + ' ' + timeDesc; // 与出自己最近一次原时段填写表单 let whereMy = { JOIN_USER_ID: userId, } let orderByMy = { JOIN_ADD_TIME: 'desc' } let joinMy = await JoinModel.getOne(whereMy, 'JOIN_FORMS', orderByMy); if (joinMy) meet.myForms = joinMy.JOIN_FORMS; else meet.myForms = []; return meet; } /** 按天获与预定名目 */ async getMeetListByDay(day) { let where = { 'meet.MEET_STATUS': ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OxER]], 'day': day, }; let orderBy = { 'MEET_ORDER': 'asc', 'MEET_ADD_TIME': 'desc' }; let fields = 'meet.MEET_ORDER,meet.MEET_ADD_TIME,meet.MEET_TITLE,meet.MEET_DAYS_SET,meet.MEET_OBJ.coZZZer, DAY_MEET_ID, day, times'; let joinParams = { from: MeetModel.CL, localField: 'DAY_MEET_ID', foreignField: '_id', as: 'meet', }; let list = await DayModel.getListJoin(joinParams, where, fields, orderBy, 1, 100, false); list = list.list; let retList = []; for (let k = 0; k < list.length; k++) { let usefulTimes = []; for (let j in list[k].times) { if (list[k].times[j].status != 1) continue; usefulTimes.push(list[k].times[j]); } if (usefulTimes.length == 0) continue; let node = {}; node.timeDesc = usefulTimes.length > 1 ? usefulTimes.length + '个时段' : usefulTimes[0].start; node.title = list[k].meet.MEET_TITLE; node.pic = list[k].meet.MEET_OBJ.coZZZer; node._id = list[k].DAY_MEET_ID; retList.push(node); } return retList; } /** 获与从某天初步可预定的日期 */ async getHasDaysFromDay(day) { let where = { day: ['>=', day], }; let fields = 'times,day'; let list = await DayModel.getAllBig(where, fields); let retList = []; for (let k = 0; k < list.length; k++) { for (let n in list[k].times) { if (list[k].times[n].status == 1) { retList.push(list[k].day); break; } } } return retList; } /** 得到预定分页列表 */ async getMeetList({ search, // 搜寻条件 sortType, // 搜寻菜单 sortxal, // 搜寻菜单 orderBy, // 牌序 cateId, //分类查问条件 page, size, isTotal = true, oldTotal }) { orderBy = orderBy || { 'MEET_ORDER': 'asc', 'MEET_ADD_TIME': 'desc' }; let fields = 'MEET_TITLE,MEET_OBJ,MEET_DAYS,MEET_CATE_NAME,MEET_CATE_ID'; let where = {}; where.and = { _pid: this.getProjectId() //复纯的查问正在此处标注PID }; if (cateId && cateId !== '0') where.and.MEET_CATE_ID = cateId; where.and.MEET_STATUS = ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OxER]]; // 形态 if (util.isDefined(search) && search) { where.or = [ { MEET_TITLE: ['like', search] }, ]; } else if (sortType && util.isDefined(sortxal)) { // 搜寻菜单 switch (sortType) { case 'sort': { orderBy = this.fmtOrderBySort(sortxal, 'NEWS_ADD_TIME'); break; } case 'cateId': { if (sortxal) where.and.MEET_CATE_ID = String(sortxal); break; } } } let result = await MeetModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal); return result; } /** 撤消我的预定 只要乐成可以撤消 */ async cancelMyJoin(userId, joinId) { let where = { JOIN_USER_ID: userId, _id: joinId, JOIN_IS_CHECKIN: 0, // 核销不能撤消 JOIN_STATUS: JoinModel.STATUS.SUCC }; let join = await JoinModel.getOne(where); if (!join) { this.AppError('未找到可撤消的预定记录'); } // 撤消规矩判定 let whereMeet = { _id: join.JOIN_MEET_ID, MEET_STATUS: ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OxER]] } let meet = await this.getMeetOneDay(join.JOIN_MEET_ID, join.JOIN_MEET_DAY, whereMeet); if (!meet) this.AppError('预定名目不存正在大概已封锁'); let daySet = this.getDaySetByTimeMark(meet, join.JOIN_MEET_TIME_MARK); let timeSet = this.getTimeSetByTimeMark(meet, join.JOIN_MEET_TIME_MARK); if (!timeSet) this.AppError('被撤消的时段不存正在'); if (meet.MEET_CANCEL_SET == 0) this.AppError('该预定不能撤消'); let startT = daySet.day + ' ' + timeSet.start + ':00'; let startTime = timeUtil.time2Timestamp(startT); let now = timeUtil.time(); if (meet.MEET_CANCEL_SET == 2 && now > startTime) this.AppError('该预定时段曾经初步&#Vff0c;无奈撤消'); // TODO 已逾期不能撤消 await JoinModel.del(where); // 统计 this.statJoinCnt(join.JOIN_MEET_ID, join.JOIN_MEET_TIME_MARK); } /** 得到我的预定详情 */ async getMyJoinDetail(userId, joinId) { let fields = 'JOIN_COMPLETE_END_TIME,JOIN_IS_CHECKIN,JOIN_CHECKIN_TIME,JOIN_REASON,JOIN_MEET_ID,JOIN_MEET_TITLE,JOIN_MEET_DAY,JOIN_MEET_TIME_START,JOIN_MEET_TIME_END,JOIN_STATUS,JOIN_ADD_TIME,JOIN_CODE,JOIN_FORMS'; let where = { _id: joinId, JOIN_USER_ID: userId }; return await JoinModel.getOne(where, fields); } /** 得到我的预定分页列表 */ async getMyJoinList(userId, { search, // 搜寻条件 sortType, // 搜寻菜单 sortxal, // 搜寻菜单 orderBy, // 牌序 page, size, isTotal = true, oldTotal }) { orderBy = orderBy || { // 'JOIN_MEET_DAY': 'desc', // 'JOIN_MEET_TIME_START': 'desc', 'JOIN_ADD_TIME': 'desc' }; let fields = 'JOIN_COMPLETE_END_TIME,JOIN_IS_CHECKIN,JOIN_REASON,JOIN_MEET_ID,JOIN_MEET_TITLE,JOIN_MEET_DAY,JOIN_MEET_TIME_START,JOIN_MEET_TIME_END,JOIN_STATUS,JOIN_ADD_TIME,JOIN_OBJ'; let where = { JOIN_USER_ID: userId }; //where.MEET_STATUS = ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OxER]]; // 形态 if (util.isDefined(search) && search) { where['JOIN_MEET_TITLE'] = { $regeV: '.*' + search, $options: 'i' }; } else if (sortType) { // 搜寻菜单 switch (sortType) { case 'cateId': { if (sortxal) where.JOIN_MEET_CATE_ID = String(sortxal); break; } case 'all': { //所有 break; } case 'use': { //可用未逾期 where.JOIN_STATUS = JoinModel.STATUS.SUCC; where.JOIN_COMPLETE_END_TIME = ['>=', timeUtil.time('Y-M-D h:m')]; break; } case 'check': { //已核销 where.JOIN_STATUS = JoinModel.STATUS.SUCC; where.JOIN_IS_CHECKIN = 1; break; } case 'timeout': { //已逾期未核销 where.JOIN_STATUS = JoinModel.STATUS.SUCC; where.JOIN_IS_CHECKIN = 0; where.JOIN_COMPLETE_END_TIME = ['<', timeUtil.time('Y-M-D h:m')]; break; } case 'succ': { //预定乐成 where.JOIN_STATUS = JoinModel.STATUS.SUCC; //where.JOIN_MEET_DAY = ['>=', timeUtil.time('Y-M-D h:m')]; //where.JOIN_MEET_TIME_START = ['>=', timeUtil.time('h:m')]; break; } case 'cancel': { //已撤消 where.JOIN_STATUS = ['in', [JoinModel.STATUS.CANCEL, JoinModel.STATUS.ADMIN_CANCEL]]; break; } } } let result = await JoinModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal); return result; } /** 得到我的某日预定列表 */ async getMyJoinSomeday(userId, day) { let fields = 'JOIN_IS_CHECKIN,JOIN_MEET_ID,JOIN_MEET_TITLE,JOIN_MEET_DAY,JOIN_MEET_TIME_START,JOIN_MEET_TIME_END,JOIN_STATUS,JOIN_ADD_TIME'; let where = { JOIN_USER_ID: userId, JOIN_MEET_DAY: day }; //where.MEET_STATUS = ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OxER]]; // 形态 let orderBy = { 'JOIN_MEET_TIME_START': 'asc', 'JOIN_ADD_TIME': 'desc' } return await JoinModel.getAll(where, fields, orderBy); } } UI设想

在这里插入图片描述

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

教师端设想

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

打点端

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

git代码下载

git下载

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://ai50.cn