前期调研
- 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 '报名已满';elsereturn '报名中';}/** 浏览信息 */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
下载地址