手把手教你写项目之“家政到家”小程序全栈开发---第六章:家政员的工作台——服务者端功能实现

阅读前请先下载项目源码,边读边看源码以加深理解和实操,
源码地址已放于文章末尾!

小程序效果预览:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

第六章:家政员的工作台——服务者端功能实现

各位同学,欢迎回到我们的项目实战。在彻底搞定了用户端的预约全流程后,我们现在要换个视角,穿上“工作服”,体验一下家政员(服务提供者)的工作日常。

README.md的规划中,我们知道项目包含三个端:用户端、家政员端和后台管理端。这一章,我们就来揭秘“家政员端”的实现。它不像用户端那样功能繁多,但却至关重要,因为它直接关系到服务的供给侧。家政员需要在这里管理个人资料、设置可预约的时间(也就是“上架库存”),以及核销用户的预约(完成“交易闭环”)。

我们将快速过一下这部分的核心功能,你会发现,很多后端的基础设施(如Service、Model)都是可以复用的,这也是分层架构的巨大优势。

6.1 角色权限的“守门人”:家政员登录与身份验证

与后台管理员类似,家政员端也需要一套独立的登录和身份验证机制,确保只有合法的服务人员才能访问相关功能。

  • 前端登录入口: /miniprogram/projects/workhome/pages/work/login/work_login.js
  • 后端登录接口: cloudfunctions/.../controller/work/work_home_controller.js -> workLogin()
  • 后端服务逻辑: cloudfunctions/.../service/work/work_home_service.js -> login()
  • 身份校验基类: cloudfunctions/.../controller/work/base_project_work_controller.js
后端登录逻辑 work_home_service.js
// 位置: /cloudfunctions/mcloud/project/workhome/service/work/work_home_service.js

const MeetModel = require('../../../model/meet_model.js');
// ...

class WorkHomeService extends BaseProjectWorkService {
	/**
	 * 登录
	 */
	async login(phone, password) {
		// 查询家政员(MEET)是否存在
		let where = {
			MEET_PHONE: phone,
			MEET_PASSWORD: md5Lib.md5(password)
		};
		let fields = 'MEET_ID, MEET_TOKEN, MEET_TOKEN_TIME, MEET_STATUS';
		let meet = await MeetModel.getOne(where, fields);

		if (!meet) this.AppError('账号或密码不正确');
		if (meet.MEET_STATUS != 1) this.AppError('该账号已被禁用');

		// 生成新的token并更新
		let token = dataUtil.genRandomString(32);
		let tokenTime = timeUtil.time();
		let dataUpdate = {
			MEET_TOKEN: token,
			MEET_TOKEN_TIME: tokenTime
		};
		await MeetModel.edit(meet._id, dataUpdate);

		return { token };
	}
}

代码讲解

  • 复用MeetModel:这里有一个非常巧妙的设计。家政员(服务者)的数据,并没有单独创建一张worker表,而是直接复用了meet表。meet表既代表一个“服务”,也代表提供这个服务的“家政员”。通过在meet表中增加MEET_PHONE, MEET_PASSWORD, MEET_TOKEN等字段,实现了账户管理功能。这种“一表多用”的设计在业务实体高度绑定的情况下可以简化数据结构。
  • 登录逻辑:与后台管理员登录如出一辙,通过手机号和MD5加密后的密码查询用户,成功后生成一个新的token并存入数据库,然后将token返回给前端。
身份校验 base_project_work_controller.js

所有家政员端接口的Controller都继承自BaseProjectWorkController,这个基类会在执行具体业务方法前,自动进行身份校验。

// 位置: /cloudfunctions/mcloud/project/workhome/controller/work/base_project_work_controller.js

class BaseProjectWorkController extends BaseController {
	async initSetup() {
		// 调用service进行token校验
		let workService = new BaseProjectWorkService();
		let meet = await workService.isWork(this._token); // this._token是前端传来的
		if (!meet) this.AppError('登录已过期,请重新登录', 401);

		this._meetId = meet.MEET_ID; // 将家政员ID挂载到this上,方便后续方法使用
		this._meet = meet;
	}
}

代码讲解

  • initSetup():我们在第二章讲过,这个方法会在application.js中被自动调用。这里它重写了父类的initSetup,在里面执行了登录校验workService.isWork(this._token)
  • isWork():这个服务方法会拿着前端传来的tokenmeet表里查找,并检查token是否在有效期内。
  • 通过这种方式,所有继承自它的Controller(如work_meet_controller.js)的每个方法,在执行前都自动获得了“身份保护”,无需重复编写校验代码。

6.2 “我的时间我做主”——排班管理

家政员最重要的工作就是设定自己什么时间可以接单。这个功能对应到我们系统里,就是修改day集合的数据。

  • 前端页面: /miniprogram/projects/workhome/pages/work/meet/time/work_meet_time.js
  • 后端控制器: .../controller/work/work_meet_controller.js -> editMeet()
  • 后端服务: .../service/work/work_meet_service.js -> editMeet()
work_meet_service.js - 更新排班的核心逻辑
// 位置: /cloudfunctions/mcloud/project/workhome/service/work/work_meet_service.js

const DayModel = require('../../../model/day_model.js');
const MeetModel = require('../../../model/meet_model.js');
// ...

class WorkMeetService extends BaseProjectWorkService {
	/**
	 * 修改自己的可预约时段
	 * @param {string} meetId 家政员ID
	 * @param {object} daysSet 包含日期和时段的完整排班数据
	 */
	async editMeet(meetId, { daysSet }) {
		// 1. 先删除旧的排班数据
		await DayModel.del({ DAY_MEET_ID: meetId });

		// 2. 遍历新的排班数据,逐条插入
		for (let i = 0; i < daysSet.length; i++) {
			let dayNode = daysSet[i];
			if (!dayNode.day || !dayNode.times) continue;

			let data = {
				DAY_MEET_ID: meetId,
				day: dayNode.day,
				dayDesc: timeUtil.getWeek(dayNode.day),
				times: dayNode.times // [{mark, start, end, limit...}]
			};
			await DayModel.insert(data);
		}
		
		// 3. 更新meet表中的日期缓存
		let days = dataUtil.getArrByKey(daysSet, 'day');
		let dataMeet = {
			MEET_DAYS: days.sort()
		};
		await MeetModel.edit(meetId, dataMeet);

		return;
	}
}

代码讲解

  • 全量更新:这里的排班更新采用了一种简单粗暴但有效的方式——“先删后插”。每次家政员保存排班时,后端会先把该家政员(DAY_MEET_ID)所有旧的day记录全部删除,然后再把前端提交的新的排班数据(daysSet)一条条地重新插入。
    • 优点:逻辑简单,不需要去比对哪些时段是新增的、哪些是修改的、哪些是删除的。
    • 缺点:如果排班数据量巨大,性能可能会有影响。但在当前项目的中小业务规模下,这是一种非常实用的策略。
  • 更新MEET_DAYS缓存:在更新完day表后,会把所有有排期的日期提取出来,更新到meet表本身的MEET_DAYS字段中。这个字段在用户端列表页会用到,用于快速判断某个家政员“近期可约”,而无需去day表里做复杂的查询,是典型的用空间换时间的性能优化。

7.3 核销预约——服务完成的见证

当用户上门时,家政员需要核销用户的预约码,标志着本次服务的正式开始和完成。

  • 后端控制器: .../controller/work/work_meet_controller.js -> scanJoin()
  • 后端服务: .../service/work/work_meet_service.js -> scanJoin()
// 位置: /cloudfunctions/mcloud/project/workhome/service/work/work_meet_service.js
class WorkMeetService extends BaseProjectWorkService {
    /**
     * 核销
     * @param {string} meetId 家政员ID
     * @param {string} code 核销码
     */
	async scanJoin(meetId, code) {
		let where = {
			JOIN_CODE: code,
			JOIN_MEET_ID: meetId // 必须是预约我自己的单
		};
		let join = await JoinModel.getOne(where);

		if (!join) this.AppError('未找到该预约记录');
		if (join.JOIN_IS_CHECKIN) this.AppError('该预约码已被使用');
		if (join.JOIN_STATUS != JoinModel.STATUS.SUCC) this.AppError('该预约状态不可核销');

		// 更新核销状态和时间
		let data = {
			JOIN_IS_CHECKIN: 1,
			JOIN_CHECKIN_TIME: this._timestamp
		};
		await JoinModel.edit(join._id, data);

		return;
	}
}

代码讲解

  • 多重校验:核销是一个关键的业务闭环节点,因此包含了多重安全校验:
    1. JOIN_CODE 是否存在。
    2. JOIN_MEET_ID 是否匹配当前家政员,防止误核销别人的订单。
    3. JOIN_IS_CHECKIN 是否已经被核销过,防止重复使用。
    4. JOIN_STATUS 是否是“预约成功”状态,已取消的订单不能被核销。
  • 状态变更:校验通过后,将 JOIN_IS_CHECKIN 字段置为1,并记录下核销时间。

通过对家政员端这三个核心功能的剖析,我们可以看到,后端代码得到了最大程度的复用。一套设计良好的数据模型和分层架构,可以轻松地支撑起多个不同角色的业务需求。

下一章,我们将戴上“管理员”的帽子,进入拥有最高权限的后台管理系统,看看它是如何“运筹帷幄”,掌控整个平台的。

游戏源码下载地址:
https://thmail.lanzouu.com/iNlWs345lk9i

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

THMAIL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值