招生报名缴费小程序开发笔记(上)

news2024/10/6 8:35:14

前期调研

  • 1.数字化趋势: 随着社会的数字化转型,越来越多的教育机构倾向于采用数字工具来简化和优化他们的招生和报名过程。招生报名缴费小程序是应对这一趋势的一种解决方案,可以提供高效、方便、快速的在线招生渠道。
  • 2.用户需求: 学生和家长希望能够通过手机或电脑完成入学申请和缴费,而不必亲自前往学校或教育机构,填写纸质表格,或排队等候。这种便捷性对于吸引更多申请者和提高报名率非常重要。
  • 3.减少错误和欺诈: 使用在线报名和缴费系统可以减少手动处理和数据输入错误的风险。此外,数字支付方法可以提高资金管理的透明度,减少现金管理问题和欺诈风险。
  • 4.提高效率: 传统的报名流程通常需要大量的人力资源和时间,而自动化的招生报名缴费小程序可以大大提高效率,减少了繁琐的手工工作。
  • 5.竞争优势: 对于教育机构而言,提供一种现代化、便捷的招生报名流程可以成为他们在竞争激烈的市场中脱颖而出的重要优势。
  • 6.数据分析和管理: 数字化报名系统可以生成大量数据,使教育机构能够更好地了解他们的申请者,优化招生流程,并进行更精确的市场营销。

综上所述,招生报名缴费小程序的开发背景是满足数字化趋势、提供用户需求、提高效率、降低错误和欺诈风险,并在教育市场中获得竞争优势。这种小程序为学校、大学和培训机构提供了一个现代化、便捷的方式来管理招生流程,同时也为申请者提供更好的用户体验。

功能设计

招生报名缴费小程序是一个用于学校、大学、培训机构或其他教育机构的应用程序,旨在帮助学生完成入学申请和支付报名费用。以下是招生报名缴费小程序可能包括的功能和组成部分:

  • 1.用户注册与登录: 学生和家长需要能够创建个人账户并登录,以便访问相关功能。
  • 2.招生信息浏览: 提供学校或机构的招生信息,包括不同课程、入学要求、截止日期等。
  • 3.在线报名表格: 学生可以填写入学申请表格,包括个人信息、学历背景、联系信息等。
  • 4.文件上传: 允许学生上传必要的文件,如身份证、学历证明、推荐信等。
  • 5.报名费计算: 根据所选课程和选项自动计算报名费用,并显示给用户。
  • 6.在线支付: 提供各种支付选项,如信用卡、支付宝、微信支付等,以便用户能够方便地支付报名费用。
  • 7.报名进度追踪: 学生可以查看他们的报名进度,了解是否需要提供额外的信息,审核状态等。
  • 8.通知和提醒: 发送通知和提醒,以提醒学生关于重要的报名截止日期、文件提交等事项。
  • 9.电子收据和确认信: 学生完成缴费后,能够获得电子收据和入学确认信。
    招生报名缴费小程序的设计和开发需要充分考虑用户友好性、安全性和数据保护,以确保顺畅的用户体验和数据的安全性。这样的小程序可以极大地简化招生报名过程,提高效率,同时提供更好的用户体验。
    在这里插入图片描述

数据库设计


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_MAX_CNT: 'int|true|default=20|comment=人数上限 0=不限',
    ACTIVITY_START: 'int|false|comment=开始时间戳',
	ACTIVITY_END: 'int|false|comment=截止时间戳',
	ACTIVITY_START_DAY: 'string|false|comment=开始时间',
    ACTIVITY_END_DAY: 'string|false|comment=截止时间', 

	ACTIVITY_START_MONTH: 'string|false|comment=开始月份',
	ACTIVITY_END_MONTH: 'string|false|comment=截止月份',

    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_QR: 'string|false',
    ACTIVITY_VIEW_CNT: 'int|true|default=0',
    ACTIVITY_COMMENT_CNT: 'int|true|default=0',

    ACTIVITY_METHOD: 'int|true|default=0|comment=支付方式 0=线下,1=线上',
    ACTIVITY_FEE: 'int|false|comment=支付金额 分',

    ACTIVITY_JOIN_CNT: 'int|true|default=0',
    ACTIVITY_PAY_CNT: 'int|true|default=0|comment=支付数',
    ACTIVITY_PAY_FEE: 'int|true|default=0|comment=支付额', 

    ACTIVITY_ADD_TIME: 'int|true',
    ACTIVITY_EDIT_TIME: 'int|true',
    ACTIVITY_ADD_IP: 'string|false',
    ACTIVITY_EDIT_IP: 'string|false',
};

// 字段前缀
ActivityModel.FIELD_PREFIX = "ACTIVITY_";

/**
 * 状态 0=未启用,1=使用中 
 */
ActivityModel.STATUS = {
    UNUSE: 0,
    COMM: 1
};

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_CANCEL_TIME: 'int|true|default=0|comment=取消时间',

    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=报名成功, 98=自己取消,99=审核未过/取消',
    ACTIVITY_JOIN_REASON: 'string|false|comment=审核拒绝或者取消理由',


    ACTIVITY_JOIN_FEE: 'int|true|default=0|comment=需支付费用 分',

    ACTIVITY_JOIN_PAY_TRADE_NO: 'string|false|comment=商家订单号 32位',
    ACTIVITY_JOIN_PAY_STATUS: 'int|true|default=0|comment=支付状态 0=未支付 1=已支付 8=已退款 99=无需支付',
    ACTIVITY_JOIN_PAY_FEE: 'int|true|default=0|comment=已支付费用 分',
    ACTIVITY_JOIN_PAY_TIME: 'int|true|default=0|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',
};

// 字段前缀
ActivityJoinModel.FIELD_PREFIX = "ACTIVITY_JOIN_";

/**
 * 状态 0=待审核 1=报名成功, 99=审核未过
 */
ActivityJoinModel.STATUS = {
    WAIT: 0,
    SUCC: 1,
    CANCEL: 98,
    ADMIN_CANCEL: 99
};

ActivityJoinModel.STATUS_DESC = {
    WAIT: '待审核',
    SUCC: '成功',
    CANCEL: '取消',
    ADMIN_CANCEL: '审核未过'
};

代码核心

async minuteJob() {
		console.log('### minuteJob >>>>>');


		// 未支付的成功订单取消  
		let time = this._timestamp - 6 * 60 * 1000;
		console.log('###### Begin>>> 未支付订单6分钟后取消, time<=' + time + ', ' + timeUtil.timestamp2Time(time));


		let where = {
			ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]],
			ACTIVITY_JOIN_PAY_STATUS: 0,
			ACTIVITY_JOIN_ADD_TIME: ['<=', time],
		}
		let rows = await ActivityJoinModel.getAll(where, '*', {}, 3000, false);
		console.log('未支付订单6分钟后取消, count=', rows.length);

		for (let k in rows) {
			let activityJoin = rows[k];

			let tradeNo = activityJoin.ACTIVITY_JOIN_PAY_TRADE_NO;

			if (!await this.fixActivityJoinPay(tradeNo, activityJoin.ACTIVITY_JOIN_ACTIVITY_ID)) {
				console.log('该报名记录未支付,已取消并删除!', activityJoin);
			}

		}

		console.log('###### END. 未支付订单6分钟后取消');

	}

	// 获取当前报名状态
	getJoinStatusDesc(activity) {
		let timestamp = this._timestamp;

		if (activity.ACTIVITY_STATUS == 0)
			return '报名停止';
		else if (activity.ACTIVITY_START > timestamp)
			return '报名未开始';
		else if (activity.ACTIVITY_END <= timestamp)
			return '报名截止';
		else if (activity.ACTIVITY_MAX_CNT > 0
			&& activity.ACTIVITY_JOIN_CNT >= activity.ACTIVITY_MAX_CNT)
			return '报名已满';
		else
			return '报名中';
	}

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

		await this.fixUserActivityJoinPayRecord(userId);

		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) ? '待审核' : '已报名';
			if (activity.myActivityJoinTag == '已报名' && activityJoin.ACTIVITY_JOIN_PAY_STATUS == 1) {
				activity.myActivityJoinTag = '已报名缴费';
			}
		}
		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_START_MONTH,ACTIVITY_END_MONTH,ACTIVITY_START_DAY,ACTIVITY_END_DAY,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 time = timeUtil.time('Y-M-D');
					where.and.ACTIVITY_START_DAY = ['<=', time];
					where.and.ACTIVITY_END_DAY = ['>=', time];
					break;
				}
				case 'tomorrow': { //明日
					let time = timeUtil.time('Y-M-D', 86400);
					where.and.ACTIVITY_START_DAY = ['<=', time];
					where.and.ACTIVITY_END_DAY = ['>=', time];
					break;
				}
				case 'month': { //本月
					let month = timeUtil.time('Y-M');
					where.and.ACTIVITY_START_MONTH = ['<=', month];
					where.and.ACTIVITY_END_MONTH = ['>=', month];
					break;
				}
			}
		}

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


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

		await this.fixUserActivityJoinPayRecord(userId);

		orderBy = orderBy || {
			'ACTIVITY_JOIN_ADD_TIME': 'desc'
		};
		let fields = 'ACTIVITY_JOIN_PAY_STATUS,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 'usercancel': {
					where.ACTIVITY_JOIN_STATUS = ActivityJoinModel.STATUS.CANCEL;
					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 fixUserActivityJoinPayRecord(userId) {
		let where = {
			ACTIVITY_JOIN_USER_ID: userId,
			ACTIVITY_JOIN_PAY_STATUS: 0,
			ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]],
		}
		let list = await ActivityJoinModel.getAll(where);

		for (let k = 0; k < list.length; k++) {
			await this.fixActivityJoinPay(list[k].ACTIVITY_JOIN_PAY_TRADE_NO, list[k].ACTIVITY_JOIN_ACTIVITY_ID);
		}
	}

	// 修正某订单状态 (仅需支付订单)
	async fixActivityJoinPay(tradeNo, activityId) {

		if (!tradeNo) {
			// 无支付号空单 删除
			await ActivityJoinModel.del({ ACTIVITY_JOIN_PAY_TRADE_NO: tradeNo });

			// 重新统计
			this.statActivityJoin(activityId);

			return false;
		}

		let payService = new PayService();
		if (!await payService.fixPayResult(tradeNo)) {
			// 关闭未支付单
			payService.closePay(tradeNo);

			// 未支付 
			await ActivityJoinModel.del({ ACTIVITY_JOIN_PAY_TRADE_NO: tradeNo });

			// 重新统计
			this.statActivityJoin(activityId);

			return false;
		}

		// 已支付
		let pay = await PayModel.getOne({ PAY_TRADE_NO: tradeNo });
		if (!pay) this.AppError('支付流水异常,请核查');

		// 更新支付信息
		let data = {
			ACTIVITY_JOIN_PAY_STATUS: 1,
			ACTIVITY_JOIN_PAY_TRADE_NO: tradeNo,
			ACTIVITY_JOIN_PAY_FEE: pay.PAY_TOTAL_FEE,
			ACTIVITY_JOIN_PAY_TIME: pay.PAY_END_TIME,
		}
		await ActivityJoinModel.edit({ ACTIVITY_JOIN_PAY_TRADE_NO: tradeNo }, data);


		// 重新统计
		this.statActivityJoin(activityId);
		return true;
	}

	//################## 报名 
	// 报名 
	async prepay(userId, activityId, forms) {
		this.AppError('[招生报名缴费]该功能暂不开放,如有需要请加作者微信:cclinux0730');

	}


	async statActivityJoin(activityId) {
		// 报名数
		let where = {
			ACTIVITY_JOIN_ACTIVITY_ID: activityId,
			ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]]
		}
		let cnt = await ActivityJoinModel.count(where);


		// 已支付记录
		let wherePayCnt = {
			ACTIVITY_JOIN_ACTIVITY_ID: activityId,
			ACTIVITY_JOIN_PAY_STATUS: 1,
			ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]]
		}
		let payCnt = await ActivityJoinModel.count(wherePayCnt);


		// 已支付金额
		let wherePayFee = {
			ACTIVITY_JOIN_ACTIVITY_ID: activityId,
			ACTIVITY_JOIN_PAY_STATUS: 1,
			ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]]
		}
		let payFee = await ActivityJoinModel.sum(wherePayFee, 'ACTIVITY_JOIN_PAY_FEE');

		let data = {
			ACTIVITY_JOIN_CNT: cnt,
			ACTIVITY_PAY_CNT: payCnt,
			ACTIVITY_PAY_FEE: payFee,

		}
		await ActivityModel.edit(activityId, data);
	}

	/**  报名前获取关键信息 */
	async detailForActivityJoin(userId, activityId) {

		await this.fixUserActivityJoinPayRecord(userId);

		let fields = 'ACTIVITY_JOIN_FORMS, ACTIVITY_TITLE, ACTIVITY_FEE, ACTIVITY_METHOD';

		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 joinList = await ActivityJoinModel.getAll(whereMy, 'ACTIVITY_JOIN_OBJ,ACTIVITY_JOIN_FORMS', orderByMy);
		let addressList = [];
		let addressList2 = [];
		for (let k = 0; k < joinList.length; k++) {
			let exist = false;
			for (let j = 0; j < addressList.length; j++) {
				if (addressList[j].name === joinList[k].ACTIVITY_JOIN_OBJ.name) {
					exist = true;
					break;
				}
			}

			if (!exist) {
				addressList.push(joinList[k].ACTIVITY_JOIN_OBJ);
				addressList2.push(joinList[k].ACTIVITY_JOIN_FORMS);
			}
		}

		// 取出本人最近一次的填写表单
		let joinMy = await ActivityJoinModel.getOne(whereMy, 'ACTIVITY_JOIN_FORMS', orderByMy);
		joinMy = null;

		let myForms = joinMy ? joinMy.ACTIVITY_JOIN_FORMS : [];
		activity.myForms = myForms;

		activity.addressList = addressList;
		activity.addressList2 = addressList2;

		activity.ACTIVITY_FEE = Number(dataUtil.fmtMoney(activity.ACTIVITY_FEE / 100));

		return activity;
	}

	/** 取消我的报名 只有成功和待审核可以取消   */
	async cancelMyActivityJoin(userId, activityJoinId) {
		let where = {
			_id: activityJoinId,
			ACTIVITY_JOIN_USER_ID: userId,
			ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]] //只有成功和待审核可以取消
		};
		let activityJoin = await ActivityJoinModel.getOne(where);

		if (!activityJoin) {
			this.AppError('未找到可取消的报名记录');
		}


		let activity = await ActivityModel.getOne(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID);
		if (!activity)
			this.AppError('该报名不存在');

		if (activity.ACTIVITY_CANCEL_SET == 0)
			this.AppError('该报名不能取消报名');

		if (activity.ACTIVITY_CANCEL_SET == 2 && activity.ACTIVITY_END < this._timestamp)
			this.AppError('该报名已经截止,无法取消');

		if (activityJoin.ACTIVITY_JOIN_PAY_STATUS == 99) {
			// 无须支付
			// 更新记录 
			let data = {
				ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.CANCEL,
				ACTIVITY_JOIN_CANCEL_TIME: this._timestamp,
			}
			await ActivityJoinModel.edit(activityJoinId, data);
		}
		else {
			let tradeNo = activityJoin.ACTIVITY_JOIN_PAY_TRADE_NO;
			if (!await this.fixActivityJoinPay(tradeNo, activityJoin.ACTIVITY_JOIN_ACTIVITY_ID)) {
				this.AppError('该报名记录未支付,已取消并删除!');
			}
			let payService = new PayService();
			await payService.refundPay(tradeNo, '用户取消报名');

			// 更新记录 
			let data = {
				ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.CANCEL,
				ACTIVITY_JOIN_CANCEL_TIME: this._timestamp,
				ACTIVITY_JOIN_PAY_STATUS: 8,
			}
			await ActivityJoinModel.edit(activityJoinId, data);
		}


		// 统计
		await this.statActivityJoin(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID);
	}


	/** 按天获取报名项目 */
	async getActivityListByDay(day) {

		let where = {
			ACTIVITY_START_DAY: ['<=', day],
			ACTIVITY_END_DAY: ['>=', day]
		};

		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_STATUS: 1,
			//ACTIVITY_START_DAY: ['<=', fromDay],
			ACTIVITY_END_DAY: ['>=', fromDay]
		};

		let fields = 'ACTIVITY_START_DAY,ACTIVITY_END_DAY';
		let list = await ActivityModel.getAllBig(where, fields);

		if (list.length == 0) return;

		let min = await ActivityModel.min(where, 'ACTIVITY_START_DAY');
		let max = await ActivityModel.max(where, 'ACTIVITY_END_DAY');

		let minTimestamp = timeUtil.time2Timestamp(min);
		let maxTimestamp = timeUtil.time2Timestamp(max);

		let retList = [];
		for (let n = minTimestamp; n <= maxTimestamp; n += 86400 * 1000) {
			let day = timeUtil.timestamp2Time(n, 'Y-M-D');

			if (day < fromDay) continue;

			for (let k = 0; k < list.length; k++) {
				if (day >= list[k].ACTIVITY_START_DAY && day <= list[k].ACTIVITY_END_DAY) {
					if (!retList.includes(day)) retList.push(day);
					break;
				}
			}
		}

		return retList;
	}

管理系统

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

用户系统设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码下载git

下载地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1148789.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

强化学习中值函数应用示例

一、Gridworld Gridworld是一个用于教授强化学习概念的简化的电子游戏环境。它具有一个简单的二维网格&#xff0c;智能体可以在其中执行动作并获得奖励。这个环境是有限的&#xff0c;因为它有一个明确的开始和结束状态&#xff0c;以及一组确定的动作和奖励。 在Gridworld中&…

C++二分查找算法的应用:最长递增子序列

涉及知识点 二分查找 单调映射 源码下载 点击下载源码 题目 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xf…

Rust 语言介绍及安装

目录 1、简介 1.1 为什么选择Rust 高性能 可靠性 生产力 1.2 用 Rust 构建应用 命令行 WebAssembly 网络 嵌入式 2、安装 Rust Windows 的 Linux 子系统&#xff08;WSL&#xff09; 检查Rust 是最新的 卸载Rust版本&#xff1a; Cargo&#xff1a;Rust 的构建工…

读图数据库实战笔记03_遍历

1. Gremlin Server只将数据存储在内存中 1.1. 如果停止Gremlin Server&#xff0c;将丢失数据库里的所有数据 2. 概念 2.1. 遍历&#xff08;动词&#xff09; 2.1.1. 当在图数据库中导航时&#xff0c;从顶点到边或从边到顶点的移动过程 2.1.2. 类似于在关系数据库中的查…

我在Vscode学OpenCV 初步接触

OpenCV是一个开源的计算机视觉库&#xff0c;可以处理图像和视频数据。它包含了超过2500个优化过的算法&#xff0c;用于对图像和视频进行处理&#xff0c;包括目标识别、面部识别、运动跟踪、立体视觉等。OpenCV支持多种编程语言&#xff0c;包括C、Python、Java等&#xff0c…

光谱图像论文浅读

文章目录 Hyperspectral Image Super-Resolution via Deep Spatiospectral Attention Convolutional Neural Networks Hyperspectral Image Super-Resolution via Deep Spatiospectral Attention Convolutional Neural Networks 通过上采样高光谱保留其光谱特征&#xff0c;采用…

在R中安装CmdStanR的步骤-R4.3.1-CmdStanR-0.6.1.900

报错未安装cmdstanr 安装包官网详细介绍&#xff1a; R Interface to CmdStan • cmdstanrhttps://mc-stan.org/cmdstanr/ 以下是在R中安装CmdStanR的步骤&#xff1a; 1. 首先&#xff0c;需要下载和安装C编译器 例如gcc。如果您已经安装了C编译器&#xff0c;则可以跳过此…

【数据结构--C语言】有序表算法及其应用

有序表是指其中的所有元素以递增或递减方式有序排列。为了简单&#xff0c;假设有序表以递增排列。 有序表的基本运算 InitLIst(&L)&#xff1a;初始化有序表LDestoryList(&L)&#xff1a;销毁有序表LListEmpty(L)&#xff1a;判断空表ListLength(L)&#xff1a;求有…

Mysql进阶-索引篇(上)

目录 索引概述 索引结构 数据结构 二叉树 红黑树 B-Tree BTree Hash 索引分类 聚集索引&二级索引 聚集索引选取规则: 具体结构 索引基础语法 SQL性能分析 SQL执行频率 慢查询日志 profile详情 explain 索引概述 介绍&#xff1a; 索引&#xff08; index &…

基于哈里斯鹰算法的无人机航迹规划-附代码

基于哈里斯鹰算法的无人机航迹规划 文章目录 基于哈里斯鹰算法的无人机航迹规划1.哈里斯鹰搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用哈里斯鹰算法来优化无人机航迹规划。 …

IOC课程整理-18 Spring注解

1. Spring 注解驱动编程发展历程 2. Spring 核心注解场景分类 3. Spring 注解编程模型 https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model 4. Spring 元注解&#xff08;Meta-Annotations&#xff09; 元注解&#xff08;Meta-A…

PostGreSQL:JSON|JSONB数据类型

JSON JSON 指的是 JavaScript 对象表示法&#xff08;JavaScript Object Notation&#xff09;JSON 是轻量级的文本数据交换格式JSON 独立于语言&#xff1a;JSON 使用 Javascript语法来描述数据对象&#xff0c;但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许…

什么是 CNN? 卷积神经网络? 怎么用 CNN 进行分类?(1)

先看卷积是啥&#xff0c;url: https://www.bilibili.com/video/BV1JX4y1K7Dr/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 下面这个式子就是卷积 看完了&#xff0c;感觉似懂非懂 下一个参考视频&#xff1a;https://www.y…

linux PELT算法中的load计算

滑窗平均的累加计算 权重y按滑窗距离衰减&#xff0c;y^32 0.5&#xff0c;也就是经历32个周期将衰减一半的权重&#xff0c;假设本周期值是&#xff36;&#xff0c;本周期的加权累加值为&#xff36;x&#xff0c;则&#xff1a; 一个完整周期是T&#xff0c;给一个基础值1…

【技能树笔记】网络篇——练习题解析(十)

【技能树笔记】网络篇系列前九篇 【技能树笔记】网络篇——练习题解析&#xff08;一&#xff09;-CSDN博客 【技能树笔记】网络篇——练习题解析&#xff08;二&#xff09;-CSDN博客 【技能树笔记】网络篇——练习题解析&#xff08;三&#xff09;-CSDN博客 【技能树笔记】网…

大数据Flink(一百零五):SQL性能调优

文章目录 SQL性能调优 一、 ​​​​​​​MiniBatch 聚合

2023 年值得关注的国外网络安全初创公司

网络安全初创公司试图解决的问题往往有点超前于主流。他们可以比大多数老牌公司更快地填补空白或新兴需求。初创公司通常可以更快地创新&#xff0c;因为它们不受安装基础的限制。 当然&#xff0c;缺点是初创公司往往缺乏资源和成熟度。公司致力于初创公司的产品或平台是有风…

JavaWeb 怎么在servlet向页面输出Html元素?

service()方法里面的方法体&#xff1a; resp.setContentType("text/html;charsetutf-8");//获得输出流PrintWriter对象PrintWriter outresp.getWriter();out.println("<html>");out.println("<head><title>a servlet</title>…

SDL事件处理以及线程使用(2)

事件使用 #include <stdio.h> #include <SDL.h>#define FF_QUIT_EVENT (SDL_USEREVENT 1) // 定义自定义事件#undef main int main() {SDL_Window* pWindow NULL;SDL_Init(SDL_INIT_VIDEO);// 创建窗口pWindow SDL_CreateWindow("Event Test Title&…

DAY38 动态规划 + 509. 斐波那契数 + 70. 爬楼梯 + 746. 使用最小花费爬楼梯

动态规划理论 动态规划&#xff0c;Dynamic Programming&#xff0c; DP&#xff0c; 如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 所以动态规划中每一个状态一定是由上一个状态推导出来的&#xff0c;这一点就区分于贪心&#xff0c;贪心没有状态推导…