business background
The University for the Elderly is a classroom for "empty-nest" seniors to update their knowledge, a place to exercise and nourish their minds, a playground for fun and entertainment, a platform for making friends, and a base for intellectual development! Today, with the popularization of mobile phone networks, the majority of the elderly can learn about training classes and courses, register information, and check the registration results through small programs without leaving their homes. The background staff can also collect registration materials through electronic means, saving a lot of money. The tired time of the elderly rushing around also improves the work efficiency of the elderly university.
functional planning
The main functions include announcement notification, classroom style, training class list, training course introduction, my registration, background training project management, registration list management, background notification management, background administrator management and other functions
Registration module
- Registration project management: start / deadline / whether to review / whether to cancel / whether to modify / number of people and other parameters can be flexibly set, and can customize the data items filled in by the elderly
- Registration review: You can set whether to review the registration list. If the review fails, you can prompt the user to improve the information
- My registration record: can modify or cancel my registration record
- Detailed registration data: support the export of registration list data to Excel, printing
Technical selection
- This project is developed using the WeChat applet platform.
- Using Tencent's specialized small program cloud development technology, cloud resources include cloud functions, databases, bandwidth, storage space, timers, etc. The resource quota is cheap and can be built without domain names and servers.
- The applet itself is ready-to-go, suitable for the use of small tools, and also suitable for rapid development and iteration.
- The cloud development technology adopts Tencent's internal link, there is no risk of being hacked, no DDOS attack, saving firewall costs, high security and maintenance-free.
- Resource carrying capacity can be flexibly expanded at any time according to business development needs.
Database Design
EnrollJoinModel.DB_STRUCTURE = { _pid: 'string|true', ENROLL_JOIN_ID: 'string|true', ENROLL_JOIN_ENROLL_ID: 'string|true|comment=Sign up PK', ENROLL_JOIN_IS_ADMIN: 'int|true|default=0|comment=Whether the administrator added 0/1', ENROLL_JOIN_USER_ID: 'string|true|comment=user ID', ENROLL_JOIN_FORMS: 'array|true|default=[]|comment=form', ENROLL_JOIN_STATUS: 'int|true|default=1|comment=Status 0=pending review 1=Sign up successfully, 99=Not reviewed', ENROLL_JOIN_REASON: 'string|false|comment=Review the reasons for rejection or cancellation', ENROLL_JOIN_LAST_TIME: 'int|true|default=0', ENROLL_JOIN_ADD_TIME: 'int|true', ENROLL_JOIN_EDIT_TIME: 'int|true', ENROLL_JOIN_ADD_IP: 'string|false', ENROLL_JOIN_EDIT_IP: 'string|false', }; UserModel.DB_STRUCTURE = { _pid: 'string|true', USER_ID: 'string|true', USER_MINI_OPENID: 'string|true|comment=Applets openid', USER_STATUS: 'int|true|default=1|comment=Status 0=pending review,1=normal,8=Not reviewed,9=disabled', USER_CHECK_REASON: 'string|false|comment=Reasons not reviewed', USER_NAME: 'string|false|comment=User's Nickname', USER_MOBILE: 'string|false|comment=contact number', USER_FORMS: 'array|true|default=[]', USER_OBJ: 'object|true|default={}', USER_LOGIN_CNT: 'int|true|default=0|comment=number of logins', USER_LOGIN_TIME: 'int|false|comment=Last login time', USER_ADD_TIME: 'int|true', USER_ADD_IP: 'string|false', USER_EDIT_TIME: 'int|true', USER_EDIT_IP: 'string|false', }
core implementation
class EnrollService extends BaseProjectService { // Get current registration status getJoinStatusDesc(enroll) { let timestamp = this._timestamp; if (enroll.ENROLL_STATUS == 0) return 'stopped'; else if (enroll.ENROLL_START > timestamp) return 'has not started'; else if (enroll.ENROLL_END <= timestamp) return 'has expired'; else if (enroll.ENROLL_MAX_CNT > 0 && enroll.ENROLL_JOIN_CNT >= enroll.ENROLL_MAX_CNT) return 'full'; else return 'processing'; } /** Browsing information */ 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); // Determine if there is a registration let whereJoin = { ENROLL_JOIN_USER_ID: userId, ENROLL_JOIN_ENROLL_ID: id, ENROLL_JOIN_STATUS: ['in', [EnrollJoinModel.STATUS.WAIT, EnrollJoinModel.STATUS.SUCC]] } let enrollJoin = await EnrollJoinModel.getOne(whereJoin); if (enrollJoin) { enroll.myEnrollJoinId = enrollJoin._id; enroll.myEnrollJoinTag = (enrollJoin.ENROLL_JOIN_STATUS == EnrollJoinModel.STATUS.WAIT) ? 'pending review' : 'filled in'; } else { enroll.myEnrollJoinId = ''; enroll.myEnrollJoinTag = ''; } return enroll; } /** Get paginated list */ async getEnrollList({ search, // search condition sortType, // search menu sortVal, // search menu orderBy, // sort page, size, isTotal = true, oldTotal }) { orderBy = orderBy || { 'ENROLL_ORDER': 'asc', 'ENROLL_ADD_TIME': 'desc' }; let fields = 'ENROLL_STOP,ENROLL_JOIN_CNT,ENROLL_OBJ,ENROLL_VIEW_CNT,ENROLL_TITLE,ENROLL_MAX_CNT,ENROLL_START,ENROLL_END,ENROLL_ORDER,ENROLL_STATUS,ENROLL_CATE_NAME,ENROLL_OBJ'; let where = {}; where.and = { _pid: this.getProjectId() //Complex queries are marked with PID s here }; where.and.ENROLL_STATUS = EnrollModel.STATUS.COMM; // state if (util.isDefined(search) && search) { where.or = [{ ENROLL_TITLE: ['like', search] },]; } else if (sortType && util.isDefined(sortVal)) { // search menu 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); } /** Get my registration tab list */ async getMyEnrollJoinList(userId, { search, // search condition sortType, // search menu sortVal, // search menu orderBy, // sort page, size, isTotal = true, oldTotal }) { orderBy = orderBy || { 'ENROLL_JOIN_ADD_TIME': 'desc' }; let fields = 'ENROLL_JOIN_LAST_TIME,ENROLL_JOIN_REASON,ENROLL_JOIN_ENROLL_ID,ENROLL_JOIN_STATUS,ENROLL_JOIN_ADD_TIME,enroll.ENROLL_TITLE,enroll.ENROLL_EDIT_SET,enroll.ENROLL_CANCEL_SET'; let where = { ENROLL_JOIN_USER_ID: userId }; if (util.isDefined(search) && search) { where['enroll.ENROLL_TITLE'] = { $regex: '.*' + search, $options: 'i' }; } else if (sortType) { // search menu switch (sortType) { case 'timedesc': { //in reverse chronological order orderBy = { 'ENROLL_JOIN_ADD_TIME': 'desc' }; break; } case 'timeasc': { //in chronological order orderBy = { 'ENROLL_JOIN_ADD_TIME': 'asc' }; break; } case 'succ': { where.ENROLL_JOIN_STATUS = EnrollJoinModel.STATUS.SUCC; break; } case 'wait': { where.ENROLL_JOIN_STATUS = EnrollJoinModel.STATUS.WAIT; break; } case 'cancel': { where.ENROLL_JOIN_STATUS = EnrollJoinModel.STATUS.ADMIN_CANCEL; break; } } } let joinParams = { from: EnrollModel.CL, localField: 'ENROLL_JOIN_ENROLL_ID', foreignField: '_id', as: 'enroll', }; let result = await EnrollJoinModel.getListJoin(joinParams, where, fields, orderBy, page, size, isTotal, oldTotal); return result; } /** Get my registration details */ async getMyEnrollJoinDetail(userId, enrollJoinId) { let fields = '*'; let where = { _id: enrollJoinId, ENROLL_JOIN_USER_ID: userId }; let enrollJoin = await EnrollJoinModel.getOne(where, fields); if (enrollJoin) { enrollJoin.enroll = await EnrollModel.getOne(enrollJoin.ENROLL_JOIN_ENROLL_ID, 'ENROLL_TITLE'); } return enrollJoin; } //################## Registration // register async enrollJoin(userId, enrollId, forms) { // Whether the registration is over let whereEnroll = { _id: enrollId, ENROLL_STATUS: EnrollModel.STATUS.COMM } let enroll = await EnrollModel.getOne(whereEnroll); if (!enroll) this.AppError('Should' + ENROLL_NAME + 'does not exist or has stopped'); // whether to start registration if (enroll.ENROLL_START > this._timestamp) this.AppError('Should' + ENROLL_NAME + 'not yet started'); // Has the registration deadline passed? if (enroll.ENROLL_END < this._timestamp) this.AppError('Should' + ENROLL_NAME + 'has expired'); // Is the number full if (enroll.ENROLL_MAX_CNT > 0) { let whereCnt = { ENROLL_JOIN_ENROLL_ID: enrollId, ENROLL_JOIN_STATUS: ['in', [EnrollJoinModel.STATUS.WAIT, EnrollJoinModel.STATUS.SUCC]] } let cntJoin = await EnrollJoinModel.count(whereCnt); if (cntJoin >= enroll.ENROLL_MAX_CNT) this.AppError('Should' + ENROLL_NAME + 'full'); } // have you registered let whereMy = { ENROLL_JOIN_USER_ID: userId, ENROLL_JOIN_ENROLL_ID: enrollId, ENROLL_JOIN_STATUS: ['in', [EnrollJoinModel.STATUS.WAIT, EnrollJoinModel.STATUS.SUCC]] } let my = await EnrollJoinModel.getOne(whereMy); if (my) { if (my.ENROLL_JOIN_STATUS == EnrollJoinModel.STATUS.WAIT) this.AppError('You have already filled in the form and are waiting for review, so there is no need to repeat the form'); else this.AppError('You have already filled in the form successfully, no need to repeat the form'); } // warehousing let data = { ENROLL_JOIN_USER_ID: userId, ENROLL_JOIN_ENROLL_ID: enrollId, ENROLL_JOIN_STATUS: (enroll.ENROLL_CHECK_SET == 0) ? EnrollJoinModel.STATUS.SUCC : EnrollJoinModel.STATUS.WAIT, ENROLL_JOIN_FORMS: forms } let enrollJoinId = await EnrollJoinModel.insert(data); // total quantity this.statEnrollJoin(enrollId); let check = enroll.ENROLL_CHECK_SET; return { enrollJoinId, check } } // Amend the registration async enrollJoinEdit(userId, enrollId, enrollJoinId, forms) { let whereJoin = { _id: enrollJoinId, ENROLL_JOIN_USER_ID: userId, ENROLL_JOIN_ENROLL_ID: enrollId, ENROLL_JOIN_STATUS: ['in', [EnrollJoinModel.STATUS.WAIT, EnrollJoinModel.STATUS.SUCC]], } let enrollJoin = await EnrollJoinModel.getOne(whereJoin); if (!enrollJoin) this.AppError('Should' + ENROLL_NAME + 'The record does not exist or has been cancelled by the system'); // Whether the registration is over let whereEnroll = { _id: enrollId, ENROLL_STATUS: EnrollModel.STATUS.COMM } let enroll = await EnrollModel.getOne(whereEnroll); if (!enroll) this.AppError('Should' + ENROLL_NAME + 'does not exist or has stopped'); if (enroll.ENROLL_EDIT_SET == 0) this.AppError('Should' + ENROLL_NAME + 'Modification of data is not allowed'); if (enroll.ENROLL_EDIT_SET == 2 && enroll.ENROLL_END < this._timestamp) this.AppError('Should' + ENROLL_NAME + 'Expired, cannot modify the information'); if (enroll.ENROLL_EDIT_SET == 3 && enroll.ENROLL_CHECK_SET == 1 && enrollJoin.ENROLL_JOIN_STATUS == EnrollJoinModel.STATUS.SUCC ) this.AppError('Should' + ENROLL_NAME + 'It has passed the review and cannot be modified'); let data = { ENROLL_JOIN_FORMS: forms, ENROLL_JOIN_LAST_TIME: this._timestamp, } await EnrollJoinModel.edit(whereJoin, data); } async statEnrollJoin(id) { let where = { ENROLL_JOIN_ENROLL_ID: id, ENROLL_JOIN_STATUS: ['in', [EnrollJoinModel.STATUS.WAIT, EnrollJoinModel.STATUS.SUCC]] } let cnt = await EnrollJoinModel.count(where); await EnrollModel.edit(id, { ENROLL_JOIN_CNT: cnt }); } /** Get key information before registering */ async detailForEnrollJoin(userId, enrollId, enrollJoinId = '') { let fields = 'ENROLL_JOIN_FORMS, ENROLL_TITLE'; let where = { _id: enrollId, ENROLL_STATUS: EnrollModel.STATUS.COMM } let enroll = await EnrollModel.getOne(where, fields); if (!enroll) this.AppError('Should' + ENROLL_NAME + 'does not exist'); let joinMy = null; if (enrollJoinId) { // edit let whereMy = { ENROLL_JOIN_USER_ID: userId, _id: enrollJoinId } joinMy = await EnrollJoinModel.getOne(whereMy); } else { // Take out my last filled form /* let whereMy = { ENROLL_JOIN_USER_ID: userId, } let orderByMy = { ENROLL_JOIN_ADD_TIME: 'desc' } joinMy = await EnrollJoinModel.getOne(whereMy, 'ENROLL_JOIN_FORMS', orderByMy);*/ } let myForms = joinMy ? joinMy.ENROLL_JOIN_FORMS : []; enroll.myForms = myForms; return enroll; } /** Cancel my registration Only successful and pending approval can be cancelled Cancellation is to delete the record */ async cancelMyEnrollJoin(userId, enrollJoinId) { let where = { ENROLL_JOIN_USER_ID: userId, _id: enrollJoinId, ENROLL_JOIN_STATUS: ['in', [EnrollJoinModel.STATUS.WAIT, EnrollJoinModel.STATUS.SUCC]] }; let enrollJoin = await EnrollJoinModel.getOne(where); if (!enrollJoin) { this.AppError('No records to cancel found'); } let enroll = await EnrollModel.getOne(enrollJoin.ENROLL_JOIN_ENROLL_ID); if (!enroll) this.AppError('Should' + ENROLL_NAME + 'does not exist'); if (enroll.ENROLL_CANCEL_SET == 0) this.AppError('Should' + ENROLL_NAME + 'Can't cancel'); if (enroll.ENROLL_CANCEL_SET == 2 && enroll.ENROLL_END < this._timestamp) this.AppError('Should' + ENROLL_NAME + 'It has expired and cannot be cancelled'); if (enroll.ENROLL_CANCEL_SET == 3 && enroll.ENROLL_CHECK_SET == 1 && enrollJoin.ENROLL_JOIN_STATUS == EnrollJoinModel.STATUS.SUCC ) this.AppError('Should' + ENROLL_NAME + 'Approved and cannot be cancelled'); await EnrollJoinModel.del(where); this.statEnrollJoin(enrollJoin.ENROLL_JOIN_ENROLL_ID); } }