You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js使用类与建造者模式构建MySQL模型的参数校验及交互问题

问题1:入参校验与isAdmin默认值实现

首先在User类构造函数中初始化isAdmin默认值为false,参数校验分两层实现:单个setter中做参数合法性校验,build方法中做必填项的最终校验,非必填参数仅在传入有效值时才校验赋值。改造后的实现代码如下:

// Basic
class User {
    constructor(fname) {
        this.fname = fname;
        // 初始化isAdmin默认值为false
        this.isAdmin = false;
    };    
}

// Builder for the user class
module.exports = class UserBuilder {
    constructor(fname) {
        // 构造函数先校验必填的fname
        if(!fname || typeof fname !== 'string' || fname.trim().length === 0) {
            throw new Error('用户姓氏不能为空')
        }
        this.user = new User(fname.trim());
    }

    setLname(lname) {
        // 非必填参数,传值才校验赋值
        if(lname) {
            if(typeof lname !== 'string' || lname.trim().length === 0) {
                throw new Error('用户名字格式非法')
            }
            this.user.lname = lname.trim();
        }
        return this;
    }

    setEmail(email) {
        if(email) {
            const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            if(!emailReg.test(email)) {
                throw new Error('邮箱格式非法')
            }
            this.user.email = email.trim();
        }
        return this;
    }

    setPassword(password) {
        if(password) {
            if(typeof password !== 'string' || password.length < 6) {
                throw new Error('密码长度不能少于6位')
            }
            this.user.password = password;
        }
        return this;
    }

    setAge(age) {
        if(age !== undefined && age !== null) {
            const ageNum = Number(age);
            if(isNaN(ageNum) || ageNum < 0 || ageNum > 120 || !Number.isInteger(ageNum)) {
                throw new Error('年龄必须是0-120之间的整数')
            }
            this.user.age = ageNum;
        }
        return this;
    }

    setGender(gender) {
        if(gender) {
            if(!['男','女','保密'].includes(gender)) {
                throw new Error('性别参数非法')
            }
            this.user.gender = gender;
        }
        return this;
    }

    setAvatar(avatar) {
        if(avatar) {
            if(typeof avatar !== 'string' || !avatar.startsWith('http')) {
                throw new Error('头像地址必须为合法URL')
            }
            this.user.avatar = avatar.trim();
        }
        return this;
    }

    setPhoneNumber(number) {
        if(number) {
            const phoneReg = /^1[3-9]\d{9}$/;
            if(!phoneReg.test(number)) {
                throw new Error('手机号格式非法')
            }
            this.user.number = number.trim();
        }
        return this;
    }

    setIsAdmin(flag) {
        this.user.isAdmin = Boolean(flag);
        return this;
    }

    build() {
        // 可根据业务需求补充必填项校验,例如注册场景要求邮箱必填则放开下方注释
        // if(!this.user.email) {
        //     throw new Error('用户邮箱为必填项')
        // }
        return this.user;
    }
}

测试时如果传入非法参数会直接抛出明确的错误,便于问题定位,你提供的测试代码可以正常运行,打印出的User实例会默认携带isAdmin: false属性。

问题2:业务逻辑与数据库交互的分层归属

按照单一职责的分层原则,数据库交互、基础的用户对象增删改操作应当封装在模型类内部,不要放在控制器中:

  • 模型层负责数据定义、校验、持久化,你可以在User类中新增save()实例方法实现用户数据的写入/更新,新增static delete(userId)静态方法实现用户删除,所有SQL操作都收拢在模型层,后续表结构调整只需修改模型代码即可,不会影响上层调用
  • 控制器仅负责处理请求参数、调用模型方法、处理业务侧逻辑(比如用户注册后发送欢迎邮件)、组装返回响应,不要直接在控制器中写SQL,避免代码冗余、维护成本升高

如果后续业务复杂度提升,可以在控制器和模型之间新增Service层封装复用性高的业务逻辑,进一步拆分职责。

内容的提问来源于stack exchange,提问作者daniel sas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 01:54:04