同福

做个用户管理系统(30)——设置安全问题的实现【20201219】

介绍

介绍

福哥今天带着大家完成会员中心的最后一个表单——设置安全问题的开发。这个安全问题一般情况下是不允许修改的,所以我们暂时也不提供修改的功能。

密码安全问题一共有三组,每一组一个问题配着一个答案。问题都是一样的,所以实际上我们是在一组问题里面挑出三个问题。既然问题是固定的,就可以用编号来表示,而答案是根据用户自己的想法写的,只能是原封不动地保存起来了。

这里有个难题,就是三个问题下拉框,一个下拉框选中的选项其他下拉框就不能再选了。也就是说,不能出现重复的问题,第一个问题选择:我居住的城市名称是?,第二个问题就不能选这个了。

模型user

setSecurityQuestions

public function setSecurityQuestions(int $userID, int $q1, string $a1, int $q2, string $a2, int $q3, string $a3):int {
    $tfdo = $this->tfphp->getDatabase()->getTFDO();

    $userInfo = $this->getByTable("user", array($userID));
    if($userInfo == null){

        return 1;
    }

    $sqInfo = $this->getByTable("user_security_questions", array($userID));
    if($sqInfo == null){
        $ret = $tfdo->insert("user_security_questions", array(
            'userID'=>$userID,
            'q1'=>$q1,
            'a1'=>$a1,
            'q2'=>$q2,
            'a2'=>$a2,
            'q3'=>$q3,
            'a3'=>$a3,
        ), array(
            'userID'=>"int",
            'q1'=>"int",
            'q2'=>"int",
            'q3'=>"int",
        ));
    }
    else{
        $ret = $tfdo->update("user_security_questions", array(
            'q1'=>$q1,
            'a1'=>$a1,
            'q2'=>$q2,
            'a2'=>$a2,
            'q3'=>$q3,
            'a3'=>$a3,
        ), array(
            'q1'=>"int",
            'q2'=>"int",
            'q3'=>"int",
        ), "userID = @int", array($userID));
    }
    if(!$ret){

        return 2;
    }

    return 0;
}

接口控制器

doSave

private function doSave(){
    $req = $this->tfphp->getRequest();
    $post = $req->post;
    $user = new user($this->tfphp);
    $q1 = $post->get("q1");
    $a1 = $post->get("a1");
    $q2 = $post->get("q2");
    $a2 = $post->get("a2");
    $q3 = $post->get("q3");
    $a3 = $post->get("a3");

    try{
        // request test
        if($q1 == "" || $a1 == "" || $q2 == "" || $a2 == "" || $q3 == "" || $a3 == ""){

            return $this->tfphp->getResponse()->responseJSON_CM(200, 1001061, "错误请求");
        }

        // create user
        $ret = $user->setSecurityQuestions($this->permission->getLoginStatus()->userID,
            $q1,
            $a1,
            $q2,
            $a2,
            $q3,
            $a3);
        switch ($ret){
            case 1:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001062, "用户名不存在");
                break;
            case 2:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001063, "邮箱地址已经存在");
                break;
            case 3:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001064, "发送邮件失败");
                break;
        }
    }
    catch(\TypeError $e){

        return $this->tfphp->getResponse()->responseJSON_CM(200, 1001061, "错误请求".$e->getMessage());
    }

    // output
    return $this->tfphp->getResponse()->responseJSON_CM(200, 0, "OK");
}

视图

HTML代码

<!-- security question form begin -->
<div class="row login-form">
    <div class="col-sm-12">
        <h3 class="text-center">安全问题</h3>
        <p>设置三个安全问题以及对应的答案,在忘记密码的时候可以通过安全问题重置密码</p>
        <form>
            <div class="form-group">
                <label>第一个问题</label>
                <select class="form-control" name="q1">
                    <option>选择一个问题</option>
                    <% foreach item=cItem key=cKey from=$sqArr %>
                    <option value="<% $cKey %>"><% $cItem %></option>
                    <% /foreach %>
                </select>
            </div>
            <div class="form-group">
                <label>第一个问题的答案</label>
                <input class="form-control" type="text" name="a1" />
            </div>
            <div class="form-group">
                <label>第二个问题</label>
                <select class="form-control" name="q2">
                    <option>选择一个问题</option>
                    <% foreach item=cItem key=cKey from=$sqArr %>
                    <option value="<% $cKey %>"><% $cItem %></option>
                    <% /foreach %>
                </select>
            </div>
            <div class="form-group">
                <label>第二个问题的答案</label>
                <input class="form-control" type="text" name="a2" />
            </div>
            <div class="form-group">
                <label>第三个问题</label>
                <select class="form-control" name="q3">
                    <option>选择一个问题</option>
                    <% foreach item=cItem key=cKey from=$sqArr %>
                    <option value="<% $cKey %>"><% $cItem %></option>
                    <% /foreach %>
                </select>
            </div>
            <div class="form-group">
                <label>第三个问题的答案</label>
                <input class="form-control" type="text" name="a3" />
            </div>
            <div class="form-group">
                <button class="btn btn-primary btn-sm form-control">设置安全问题</button>
            </div>
        </form>
    </div>
</div>
<!-- security question form end -->

JS代码

$('form').form({
    url: "<% $TFReq->server->BASE_URI %>api/member/securityQuestions/_save",
    method: "post",
    validations: [
        {type:"empty", name:"q1", msg:"请选择第一个问题"},
        {type:"empty", name:"a1", msg:"请填写第一个问题的答案"},
        {type:"empty", name:"q2", msg:"请选择第二个问题"},
        {type:"empty", name:"a2", msg:"请填写第二个问题的答案"},
        {type:"empty", name:"q3", msg:"请选择第三个问题"},
        {type:"empty", name:"a3", msg:"请填写第三个问题的答案"}
    ],
    onSuccess: function (d) {
        if(d.errcode == 0){
            document.location = '<% $TFReq->server->BASE_URI %>member/securityQuestionsOK.htm';
        }
        else{
            $('form').tips({
                text:d.errmsg
            });
        }
    },
    onError: function (d) {
        $('form').tips({
            text:"服务器响应错误"
        });
    },
    onValidationError: function (form, name, msg) {
        $('form').tips({
            text:msg
        });
        $('form').find('[name="'+ name +'"]').focus();
    }
});

var onQuestionChanged = function(){
    var qs = [$('[name="q1"]'), $('[name="q2"]'), $('[name="q3"]')], qi, oi, si, selecteds = [], opts;

    for(qi=0;qi<qs.length;qi++){
        selecteds.push(qs[qi].val());
    }
    for(qi=0;qi<qs.length;qi++){
        opts = qs[qi].find('option');
        opts.show();
        for(oi=0;oi<opts.length;oi++){
            if(!$(opts[oi]).prop('selected')){
                for(si=0;si<selecteds.length;si++){
                    if(selecteds[si] == $(opts[oi]).val()){
                        $(opts[oi]).hide();
                    }
                }
            }
        }
    }
};
$('[name="q1"]').bind('change', onQuestionChanged);
$('[name="q2"]').bind('change', onQuestionChanged);
$('[name="q3"]').bind('change', onQuestionChanged);

讲解

模型user

setSecurityQuestions

首先检查用户是否存在,不存在就报错。

接着尝试写入user_security_questions表,不存在就写入,存在就更新。

接口控制器

doSave

使用setSecurityQuestions方法处理设置安全问题表单数据。

视图模板

HTML代码

这里使用PHP传入的问题数据渲染出三个问题下拉框的选项。

JS代码

前面是标准的表单处理JS程序,没什么可说的。

后面是难点,我们要通过JS实现三个问题下拉框不能重复选择相同问题的控制。

思路是这样的,首先将三个问题下拉框放到一个数组qs里面。接着遍历所有问题下拉框找出已经选中的选项,把它们存进一个数组selecteds里面。然后就是重点了,这是一个三层嵌套的循环语句,第一层遍历所有下拉框,将所有选项置为显示;第二层遍历每个下拉框的所有选项,找出不是选中项的选项进行遍历;第三次遍历selecteds数组,如果当前选项属于某一个下拉框的选项中项就隐藏起来。这样就可以避免多个问题下拉框的问题选择重复的问题了!

效果

设置表单界面

3afec3c773755d05.jpg

设置完成界面

081e9f01cc690410.jpg

总结

今天福哥带着童鞋们完成了安全问题设置表单的功能。这里面的一个避免选择重复问题的逻辑稍微有点难度,童鞋们可以发现这个东西实现起来需要很多技巧的,最重要的是要有很好的逻辑思维才能实现。

下一课我们将使用绑定邮箱和安全问题实现密码重置的功能了~~