阅读视图

发现新文章,点击刷新页面。

从0到1:不文明现象随手拍小程序开发日记(一)

前期调研

不文明现象随手拍小程序:在城市的快速发展进程中,不文明现象时有发生,为了有效解决这一问题,提升城市文明程度, 市民若发现不文明行为,如乱扔垃圾、随地吐痰、破坏公共设施、违规停车等,只需点击“上报不文明现象”按钮,即可将这些不文明行为记录下来,并附上简短的文字描述,如事件发生的具体地点、时间以及对周围环境或他人造成的影响等。市民可以在小程序的“我的上报记录”页面中随时查看自己的上报; 为了进一步调动市民参与的积极性,小程序设置了积分激励机制。每当市民成功上报一条不文明现象并被后台审核通过后,即可获得相应的积分奖励。 同时提供积分商城模块,可以让市民使用通过参与任务所获得的积分来兑换各种奖励或福利。

功能规划

在这里插入图片描述

数据设计

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_CHECK_REASON: 'string|false|comment=审核理由',

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_MAX_CNT: 'int|true|default=20|comment=每人次数上限 0=不限',
ACTIVITY_START: 'int|false|comment=项目时间',
ACTIVITY_START_DAY: 'string|false',

ACTIVITY_BEGIN: 'int|true|default=0|comment=开始时间',
ACTIVITY_STOP: 'int|true|default=0|comment=截止时间',


ACTIVITY_ADD_MONTH: 'string|false',

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_ADDRESS: 'string|false|comment=详细地址',
ACTIVITY_ADDRESS_GEO: 'object|false|comment=详细地址坐标参数',

ACTIVITY_QR: 'string|false',
ACTIVITY_VIEW_CNT: 'int|true|default=0',
ACTIVITY_JOIN_CNT: 'int|true|default=0',
ACTIVITY_COMMENT_CNT: 'int|true|default=0',

ACTIVITY_ADD_TIME: 'int|true',
ACTIVITY_EDIT_TIME: 'int|true',
ACTIVITY_ADD_IP: 'string|false',
ACTIVITY_EDIT_IP: 'string|false',
};
ActivityJoinModel.DB_STRUCTURE = {
_pid: 'string|true',
ACTIVITY_JOIN_ID: 'string|true',
ACTIVITY_JOIN_ACTIVITY_ID: 'string|true|comment=上报PK',
ACTIVITY_JOIN_ACTIVITY_TITLE: 'string|true',

ACTIVITY_JOIN_IS_ADMIN: 'int|true|default=0|comment=是否管理员添加 0/1',
 

ACTIVITY_JOIN_USER_ID: 'string|true|comment=用户ID',
ACTIVITY_JOIN_SCORE: 'int|true|default=0|comment=获取积分',

ACTIVITY_JOIN_FORMS: 'array|true|default=[]|comment=表单',
ACTIVITY_JOIN_OBJ: 'object|true|default={}',

ACTIVITY_JOIN_STATUS: 'int|true|default=0|comment=状态 1=成功, 99=系统撤销',
ACTIVITY_JOIN_REASON: 'string|false|comment=撤销理由',

ACTIVITY_JOIN_ADD_MONTH: 'string|false',

ACTIVITY_JOIN_ADD_TIME: 'int|true',
ACTIVITY_JOIN_EDIT_TIME: 'int|true',
ACTIVITY_JOIN_ADD_IP: 'string|false',
ACTIVITY_JOIN_EDIT_IP: 'string|false',
};

核心实现

class ActivityService extends BaseProjectService {

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

if (activity.ACTIVITY_STATUS == ActivityModel.STATUS.UNUSE)
return '项目停止';
else if (activity.ACTIVITY_START > timestamp)
return '项目未开始';
else if (activity.ACTIVITY_STOP <= timestamp)
return '项目结束';
else
return '进行中';
}

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

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);


return activity;
}

/** 取得分页列表 */
async getActivityList(type = 'run', {
cateId, //分类查询条件
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序 
page,
size,
isTotal = true,
oldTotal
}) {

orderBy = orderBy || {
'ACTIVITY_ORDER': 'asc',
'ACTIVITY_START': 'asc',
'ACTIVITY_ADD_TIME': 'desc'
};
let fields = 'ACTIVITY_ADDRESS,ACTIVITY_STOP,ACTIVITY_JOIN_CNT,ACTIVITY_OBJ,ACTIVITY_VIEW_CNT,ACTIVITY_TITLE,ACTIVITY_MAX_CNT,ACTIVITY_START_DAY,ACTIVITY_START,ACTIVITY_ORDER,ACTIVITY_STATUS,ACTIVITY_CATE_NAME,ACTIVITY_OBJ.cover,ACTIVITY_OBJ.score';

let where = {};

if (cateId && cateId !== '0') where.ACTIVITY_CATE_ID = cateId;

where.ACTIVITY_STATUS = ActivityModel.STATUS.COMM; // 状态  

// 进行状态
let day = timeUtil.time('Y-M-D');
if (type == 'run') {
where.ACTIVITY_STOP = ['>=', this._timestamp];
}
else {
where.ACTIVITY_STOP = ['<=', this._timestamp];
orderBy = {
'ACTIVITY_ORDER': 'asc',
'ACTIVITY_START': 'desc',
'ACTIVITY_ADD_TIME': 'desc'
};
}

if (util.isDefined(search) && search) {
where['ACTIVITY_TITLE'] = ['like', search];

} else if (sortType && util.isDefined(sortVal)) {

// 搜索菜单
switch (sortType) {
case 'cateId': {
if (sortVal) where.ACTIVITY_CATE_ID = String(sortVal);
break;
}
case 'sort': {
// 排序
orderBy = this.fmtOrderBySort(sortVal, 'ACTIVITY_ADD_TIME');
break;
}
}
}

let ret = await ActivityModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
if (ret) ret.type = type;
return ret;
}


/** 取得我的上报分页列表 */
async getMyActivityJoinList(userId, {
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序 
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'ACTIVITY_JOIN_ADD_TIME': 'desc'
};
let fields = 'ACTIVITY_JOIN_REASON,ACTIVITY_JOIN_ACTIVITY_ID,ACTIVITY_JOIN_ACTIVITY_TITLE,ACTIVITY_JOIN_STATUS,ACTIVITY_JOIN_ADD_TIME';

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 'status': {
where.ACTIVITY_JOIN_STATUS = Number(sortVal)
break;
}
}
}


let result = await ActivityJoinModel.getList(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);

return activityJoin;
}
 


async statActivityJoin(id) {
// 上报数
let where = {
ACTIVITY_JOIN_ACTIVITY_ID: id,
ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC
}
let cnt = await ActivityJoinModel.count(where);


await ActivityModel.edit(id, { ACTIVITY_JOIN_CNT: cnt });
}

/**  上报前获取关键信息 */
async detailForActivityJoin(userId, activityId) {
let fields = 'ACTIVITY_JOIN_FORMS, ACTIVITY_TITLE';

let where = {
_id: activityId,
ACTIVITY_STATUS: ActivityModel.STATUS.COMM,
}
let activity = await ActivityModel.getOne(where, fields);
if (!activity)
this.AppError('该项目不存在');

if (activity.ACTIVITY_MAX_CNT > 0) {
let cnt = await ActivityJoinModel.count({ ACTIVITY_JOIN_USER_ID: userId });
if (cnt >= activity.ACTIVITY_MAX_CNT)
this.AppError('该项目您已经上报' + cnt + '次,已超过可提交上限~');
}


let myForms = [];

if (myForms.length == 0) {

let user = await UserModel.getOne({ USER_MINI_OPENID: userId, USER_STATUS: UserModel.STATUS.COMM });
if (!user) this.AppError('用户异常');

// 取得我的上报信息
myForms = [
{ mark: 'name', type: 'text', title: '姓名', val: user.USER_NAME },
{ mark: 'phone', type: 'mobile', title: '手机', val: user.USER_MOBILE },
]

}

activity.myForms = myForms;

return activity;
}

/** 撤销我的上报 只有成功可以撤销 取消即为删除记录 */
async cancelMyActivityJoin(userId, activityJoinId) {
let where = {
ACTIVITY_JOIN_USER_ID: userId,
_id: activityJoinId,
ACTIVITY_JOIN_STATUS: 0
};
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_STATUS == ActivityModel.STATUS.UNUSE)
this.AppError('该项目已停止,不能撤销');

if (activity.ACTIVITY_CANCEL_SET == 0)
this.AppError('该项目设置了不能撤销');

if (activity.ACTIVITY_CANCEL_SET == 2 && activity.ACTIVITY_STOP < this._timestamp)
this.AppError('该项目已经截止上报,不能撤销');

await ActivityJoinModel.del(where);

// 上报数量统计
await this.statActivityJoin(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID);

}


/** 按天获取上报项目 */
async getActivityListByDay(day) {
let start = timeUtil.time2Timestamp(day);
let end = start + 86400 * 1000 - 1;
let where = {
ACTIVITY_STATUS: ActivityModel.STATUS.COMM,
ACTIVITY_START: ['between', start, end],
};

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_START: ['>=', timeUtil.time2Timestamp(fromDay)],
};

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

let retList = [];
for (let k = 0; k < list.length; k++) {
let day = timeUtil.timestamp2Time(list[k].ACTIVITY_START, 'Y-M-D');
if (!retList.includes(day)) retList.push(day);
}
return retList;
}


}

UI设计

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

后台管理

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

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

git代码下载

点击下载

从0到1:文旅小程序开发笔记(上)

可行性调查

涵盖活动报名、景点介绍、旅行攻略以及游记分享等功能,满足用户在旅游过程中的多种需求,提升旅游体验,同时助力文旅发展。

  • 活动报名:分类呈现各类文旅活动,如文化节庆、户外探险、亲子研学、美食体验等,每项活动展示活动名称、时间、地点、活动亮点)、参与人数上限以及报名截止日期等关键信息,并配以精美的活动海报图片,方便用户快速了解活动概况;点击后进入报名表单填写页面,用户填写完成后可提交报名,系统自动生成报名订单。
  • 景点攻略:将景点按照不同类型进行分类,用户可通过分类导航快速进入相应类别页面浏览景点。
  • 我的游记:展示用户发布的精彩的游记案例,用户也可以自行发布自己的游记。

概要设计

在这里插入图片描述

数据库设计


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_IS_MENU: 'int|true|default=1|comment=是否公开展示名单',

ACTIVITY_MAX_CNT: 'int|true|default=20|comment=人数上限 0=不限',
ACTIVITY_START: 'int|false|comment=活动开始时间',
ACTIVITY_END: 'int|false|comment=活动截止时间',
ACTIVITY_STOP: 'int|true|default=0|comment=报名截止时间 0=永不过期',

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_ADDRESS: 'string|false|comment=详细地址',
ACTIVITY_ADDRESS_GEO: 'object|false|comment=详细地址坐标参数',

ACTIVITY_QR: 'string|false',
ACTIVITY_VIEW_CNT: 'int|true|default=0',
ACTIVITY_JOIN_CNT: 'int|true|default=0',
ACTIVITY_COMMENT_CNT: 'int|true|default=0',

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

ACTIVITY_ADD_TIME: 'int|true',
ACTIVITY_EDIT_TIME: 'int|true',
ACTIVITY_ADD_IP: 'string|false',
ACTIVITY_EDIT_IP: 'string|false',
};
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_CODE: 'string|true|comment=核验码15',
ACTIVITY_JOIN_IS_CHECKIN: 'int|true|default=0|comment=是否签到 0/1 ',
ACTIVITY_JOIN_CHECKIN_TIME: 'int|false|default=0|签到时间',

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=报名成功, 99=审核未过',
ACTIVITY_JOIN_REASON: 'string|false|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',
};

核心实现

class ActivityService extends BaseProjectService {

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

if (activity.ACTIVITY_STATUS == 0)
return '活动停止';
else if (activity.ACTIVITY_END <= timestamp)
return '活动结束';
else if (activity.ACTIVITY_STOP <= timestamp)
return '报名结束';
else if (activity.ACTIVITY_MAX_CNT > 0
&& activity.ACTIVITY_JOIN_CNT >= activity.ACTIVITY_MAX_CNT)
return '报名已满';
else
return '报名中';
}

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

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) ? '待审核' : '已报名';
}

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_CATE_NAME,ACTIVITY_USER_LIST,ACTIVITY_STOP,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 start = timeUtil.getDayFirstTimestamp();
let end = start + 86400 * 1000 - 1;
where.and.ACTIVITY_START = ['between', start, end];
break;
}
case 'tomorrow': { //明日
let start = timeUtil.getDayFirstTimestamp() + 86400 * 1000;
let end = start + 86400 * 1000 - 1;
where.and.ACTIVITY_START = ['between', start, end];
break;
}
case 'month': { //本月
let day = timeUtil.time('Y-M-D');
let start = timeUtil.getMonthFirstTimestamp(day);
let end = timeUtil.getMonthLastTimestamp(day);

where.and.ACTIVITY_START = ['between', start, end];
break;
}
}
}

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


/** 取得某一个报名分页列表 */
async getActivityJoinList(activityId, {
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序 
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'ACTIVITY_JOIN_ADD_TIME': 'desc'
};
let fields = 'ACTIVITY_JOIN_OBJ,ACTIVITY_JOIN_IS_CHECKIN,ACTIVITY_JOIN_REASON,ACTIVITY_JOIN_ACTIVITY_ID,ACTIVITY_JOIN_STATUS,ACTIVITY_JOIN_ADD_TIME,user.USER_PIC,user.USER_NAME,user.USER_OBJ';

let where = {
ACTIVITY_JOIN_ACTIVITY_ID: activityId,
ACTIVITY_JOIN_STATUS: ActivityModel.STATUS.COMM
};

let joinParams = {
from: UserModel.CL,
localField: 'ACTIVITY_JOIN_USER_ID',
foreignField: 'USER_MINI_OPENID',
as: 'user',
};

let result = await ActivityJoinModel.getListJoin(joinParams, where, fields, orderBy, page, size, isTotal, oldTotal);

return result;
}


/** 取得我的报名分页列表 */
async getMyActivityJoinList(userId, {
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序 
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'ACTIVITY_JOIN_ADD_TIME': 'desc'
};
let fields = 'ACTIVITY_JOIN_IS_CHECKIN,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 '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 statActivityJoin(id) {
// 报名数
let where = {
ACTIVITY_JOIN_ACTIVITY_ID: id,
ACTIVITY_JOIN_STATUS: ['in', [ActivityJoinModel.STATUS.WAIT, ActivityJoinModel.STATUS.SUCC]]
}
let cnt = await ActivityJoinModel.count(where);


// 用户列表
where = {
ACTIVITY_JOIN_ACTIVITY_ID: id,
ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC
}
let joinParams = {
from: UserModel.CL,
localField: 'ACTIVITY_JOIN_USER_ID',
foreignField: 'USER_MINI_OPENID',
as: 'user',
};
let orderBy = {
ACTIVITY_JOIN_ADD_TIME: 'desc'
}
let list = await ActivityJoinModel.getListJoin(joinParams, where, 'ACTIVITY_JOIN_ADD_TIME,user.USER_MINI_OPENID,user.USER_NAME,user.USER_PIC', orderBy, 1, 6, false, 0);
list = list.list;

for (let k = 0; k < list.length; k++) {
list[k] = list[k].user;
}

await ActivityModel.edit(id, { ACTIVITY_JOIN_CNT: cnt, ACTIVITY_USER_LIST: list });
}

/**  报名前获取关键信息 */
async detailForActivityJoin(userId, activityId) {
let fields = 'ACTIVITY_JOIN_FORMS, ACTIVITY_TITLE';

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 joinMy = await ActivityJoinModel.getOne(whereMy, 'ACTIVITY_JOIN_FORMS', orderByMy);


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

return activity;
}

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

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

if (activityJoin.ACTIVITY_JOIN_IS_CHECKIN == 1)
this.AppError('该活动已经签到,无法取消');

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

if (activity.ACTIVITY_END <= this._timestamp)
this.AppError('该活动已经结束,无法取消');

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

if (activity.ACTIVITY_CANCEL_SET == 2 && activity.ACTIVITY_STOP < this._timestamp)
this.AppError('该活动已经截止报名,不能取消');

await ActivityJoinModel.del(where);

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


/** 用户自助签到 */
async myJoinSelf(userId, activityId) {
let activity = await ActivityModel.getOne(activityId);
if (!activity)
this.AppError('活动不存在或者已经关闭');

let day = timeUtil.timestamp2Time(activity.ACTIVITY_START, 'Y-M-D');

let today = timeUtil.time('Y-M-D');
if (day != today)
this.AppError('仅在活动当天可以签到,当前签到码的日期是' + day);

let whereSucc = {
ACTIVITY_JOIN_USER_ID: userId,
ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC
}
let cntSucc = await ActivityJoinModel.count(whereSucc);

let whereCheckin = {
ACTIVITY_JOIN_USER_ID: userId,
ACTIVITY_JOIN_IS_CHECKIN: 1,
ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC
}
let cntCheckin = await ActivityJoinModel.count(whereCheckin);

let ret = '';
if (cntSucc == 0) {
ret = '您没有本次活动报名成功的记录,请在「个人中心 - 我的活动报名」查看详情~';
} else if (cntSucc == cntCheckin) {
// 同一活动多次报名的情况
ret = '您已签到,无须重复签到,请在「个人中心 - 我的活动报名」查看详情~';
} else {
let where = {
ACTIVITY_JOIN_USER_ID: userId,
ACTIVITY_JOIN_IS_CHECKIN: 0,
ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC
}
let data = {
ACTIVITY_JOIN_IS_CHECKIN: 1,
ACTIVITY_JOIN_CHECKIN_TIME: this._timestamp,
}
await ActivityJoinModel.edit(where, data);
ret = '签到成功,请在「个人中心 - 我的活动报名」查看详情~'
}
return {
ret
};
}

/** 按天获取报名项目 */
async getActivityListByDay(day) {
let start = timeUtil.time2Timestamp(day);
let end = start + 86400 * 1000 - 1;
let where = {
ACTIVITY_STATUS: ActivityModel.STATUS.COMM,
//ACTIVITY_START: ['between', start, end], //for demo
};

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_START: ['>=', timeUtil.time2Timestamp(fromDay)],
};

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

let retList = [];
for (let k = 0; k < list.length; k++) {
let day = timeUtil.timestamp2Time(list[k].ACTIVITY_START, 'Y-M-D');
if (!retList.includes(day)) retList.push(day);
}

return [timeUtil.time('Y-M-D'), timeUtil.time('Y-M-D', 86400), timeUtil.time('Y-M-D', 86400 * 2), timeUtil.time('Y-M-D', 86400 * 3), timeUtil.time('Y-M-D', 86400 * 4), timeUtil.time('Y-M-D', 86400 * 5), timeUtil.time('Y-M-D', 86400 * 6)]; //for demo
return retList;
}


}

UI设计

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

后台管理系统设计

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

git代码下载

点击下载

❌