浏览代码

wesmiler 更新第5期更新代码合并

wesmiler 4 年之前
父节点
当前提交
67b2a4f7b6

+ 17 - 0
app/api/controller/MemberController.php

@@ -8,6 +8,7 @@ namespace app\api\controller;
 
 use app\weixin\model\Advice;
 use app\weixin\model\Complain;
+use app\weixin\service\FaceAuth;
 use app\weixin\service\IdnAuth;
 use function AlibabaCloud\Client\value;
 use app\weixin\model\AccountLog;
@@ -1086,6 +1087,22 @@ class MemberController extends BaseController
         showJson(1004, 2009);
     }
 
+    public function authCheck(){
+
+        // 验证
+        $this->checkUser();
+
+        $params = input();
+        $validate = new MemberValidate();
+        if (!$validate->scene('idcard')->check($params)) {
+            showJson(1004, $validate->getError());
+        }
+
+        $result = FaceAuth::getToken($params);
+        var_dump($result);
+
+    }
+
     /**
      * 加入怦然心动
      */

+ 7 - 0
app/api/controller/NotifyController.php

@@ -167,4 +167,11 @@ class NotifyController extends Controller
 
         return 'fail';
     }
+
+    /**
+     * 人脸识别回调
+     */
+    public function face(){
+        PRedis::set("caches:face:notify:post", \request()->all(), 3600);
+    }
 }

+ 3 - 0
app/weixin/model/Member.php

@@ -218,6 +218,9 @@ class Member extends Model
                     $info['age'] = date('Y') - $year;
                     $info['year'] = $year;
                 }
+
+                $month = date('m-d', $birthday);
+                $info['start'] = getStart($month);
             }
 
             $info['height_txt'] = '';

+ 177 - 0
app/weixin/service/FaceAuth.php

@@ -0,0 +1,177 @@
+<?php
+/**
+ * 身份认证服务
+ * @author wesmiler
+ */
+
+namespace app\weixin\service;
+
+class FaceAuth
+{
+    // 文档 https://market.aliyun.com/products/57124001/cmapi030146.html?spm=5176.2020520132.101.3.588172184iFKo0
+    protected static $apiUrl = 'https://fidlite.market.alicloudapi.com';
+    protected static $appKey = '203953968';
+    protected static $appSecret = 'nbP0eTttVbGFjXSI8GbY04tE89Mnw9ow';
+    protected static $appCode = '689f5d471a2140ea8091c8f4e979f8dd';
+    protected static $apiUrls = [
+        'getToken'=>'/lite/v1/get_biz_token',
+        'getResult'=>'/lite/v1/get_result?biz_token=%s',
+        'checkUrl'=>'/lite/v1/do/%s',
+    ];
+    protected static $errors = [
+        'MISSING_ARGUMENTS'=> '缺少某个必选参数',
+        'BAD_ARGUMENTS'=> '参数解析错误',
+        'AUTHORIZATION_ERROR'=> '鉴权失败或签名错误',
+        'CONCURRENCY_LIMIT_EXCEEDED'=> '请求超过并发',
+        'INTERNAL_ERROR'=> '内部错误',
+        'Request Entity Too Large'=> '请求内容大小超出限制',
+    ];
+    protected static $resultErrors = [
+        'SUCCESS'=> '待比对照片与权威数据照片或参考照片对比是同一个人',
+        'PASS_LIVING_NOT_THE_SAME'=> '待比对照片与权威数据照片或参考照片对比不是同一个人',
+        'NO_ID_CARD_NUMBER'=> '无此身份证号',
+        'ID_NUMBER_NAME_NOT_MATCH'=> '身份证号与姓名不相符',
+        'IDCARD_PHOTO_FRONTSIDE'=> '身份证人像面识别错误或信息不匹配',
+        'IDCARD_BACKSIDE_BLURRED'=> '身份证国徽面识别错误',
+        'NO_FACE_FOUND_IDCARD'=> '身份证人像面找不到人脸',
+        'IDCARD_PHOTO_NOTFRONTSIDE'=> '非身份证人像面',
+        'IDCARD_PHOTO_NOTBACKSIDE'=> '非身份证国徽面',
+        'FAIL_OCR_FAKE_IDCARD'=> '假证',
+        'FAIL_LIVING_FACE_ATTACK'=> '云端活体验证失败',
+        'CHANGE_FACE_ATTACK'=> '活体验证视频中发生了换脸攻击',
+        'NO_FACE_FOUND'=> '活体验证视频中没有检测到人脸',
+        'FACE_QUALITY_TOO_LOW'=> '活体验证视频中质量太差',
+        'INVALID_VIDEO_DURATION'=> '活体验证视频中长度不符合要求(2s~20s)',
+        'VIDEO_TOO_LARGE'=> '活体验证视频过大',
+        'SR_ERROR'=> '活体验证视频中,用户读数语音不符合要求',
+        'NOT_SYNCHRONIZED'=> '活体验证视频中,用户读数唇语不符合要求',
+        'NO_AUDIO'=> '活体验证视频无声音',
+        'VIDEO_FORMAT_UNSUPPORTED'=> '活体验证视频格式无法识别',
+        'LIP_VOICE_NOT_SYNC'=> '活体验证视频中语音唇语不同步',
+        'VIDEO_OK'=> '活体验证视频可用',
+        'VIDEO_MANY_TIMES'=> '活体验证视频上传超过阈值',
+        'VIDEO_INTERNAL_ERROR'=> '活体验证内部错误',
+        'NON_ENTERPRISE_CERTIFICATION'=> '客户未进行企业认证',
+        'BALANCE_NOT_ENOUGH'=> '余额不足',
+        'ACCOUNT_DISABLED'=> '账户已停用',
+        'USER_CANCEL'=> '用户主动退出流程',
+        'LIVING_NOT_START'=> '验证流程尚未开始',
+    ];
+
+    /**
+     * 获取验证参数Token
+     * @param $params
+     * @return array|int
+     */
+    public static function getToken($params){
+        $realname = isset($params['realname'])? trim($params['realname']) : '';
+        $idcard = isset($params['idcard'])? trim($params['idcard']) : '';
+        if(empty($realname) || empty($idcard)){
+            return 2012;
+        }
+
+        $data = [
+            'biz_no'=> makeTradeNo('F'),
+            'idcard_name'=> $realname,
+            'idcard_number'=> $idcard,
+            'idcard_threshold'=> 0.8,  // 身份证识别阈值,0-1,group=1有效
+            'idcard_side'=> 0,  // 拍摄身份证但双面,0-双面,1-单面
+            'idcard_retry_time'=> 5, // 人脸重拍次数,1-5
+            'liveness_type'=> 'video_number',  // 活体检测类型,可选:video_number
+            'notify_url'=> request()->domain().'/api/notify/face',  // 异步回调地址
+            'return_url'=> request()->domain().'/weixin/auth/idcard?type=back',  // 回调跳转页面
+            'security_level'=> 2,  // 检测严格程度,1-宽松,2-常规,3-严格
+        ];
+
+        // 参数配置
+        $smsConfig = cmf_get_option('idenauth_config');
+        $appKey = isset($params['appKey']) && $params['appKey']?trim($params['appKey']) : self::$appKey;
+        $appCode = isset($params['appCode'])&&$params['appCode']?trim($params['appCode']) : self::$appCode;
+        $appSecret = isset($params['appSecret'])&&$params['appSecret']?trim($params['appSecret']) : self::$appSecret;
+
+
+        // 构建请求参数
+        $headers = [
+            "Authorization:APPCODE {$appCode}",
+            "Content-Type:application/x-www-form-urlencoded; charset=UTF-8",
+        ];
+
+        $url = self::$apiUrl.self::$apiUrls['getToken'];
+        $query = FaceAuth::getParams($data);
+        PRedis::set("caches:faceAuth:{$idcard}:request", ['url'=> $url,'query'=> $query,'headers'=> $headers], 600);
+        $result = httpHeaderRequest($url,$query,'post',$headers);
+        $respError = isset($result['error'])? $result['error'] : '';
+        $codeData = $respError? explode(':',$respError) : [];
+        $code = isset($codeData[0])? $codeData[0] : '';
+        $bizToken = isset($result['biz_token'])? $result['biz_token'] : '';
+        PRedis::set("caches:faceAuth:{$idcard}:result", $result, 600);
+        if($respError || empty($bizToken)){
+            $msg = isset(self::$errors[$code])? self::$errors[$code] : '获取在线验证参数失败,请刷新重试';
+            return ['code'=> 'error', 'msg'=> $msg, 'result'=> $result];
+        }
+
+        $url = sprintf(self::$apiUrl.self::$apiUrls['checkUrl'],$bizToken);
+        return ['code'=> 'success', 'msg'=> '获取验证参数成功', 'result'=> ['token'=> $bizToken, 'url'=> $url]];
+
+    }
+
+    public static function getResult($token){
+        if(empty($token)){
+            return false;
+        }
+
+        // 参数配置
+        $smsConfig = cmf_get_option('idenauth_config');
+        $appKey = isset($params['appKey']) && $params['appKey']?trim($params['appKey']) : self::$appKey;
+        $appCode = isset($params['appCode'])&&$params['appCode']?trim($params['appCode']) : self::$appCode;
+        $appSecret = isset($params['appSecret'])&&$params['appSecret']?trim($params['appSecret']) : self::$appSecret;
+
+        // 构建请求参数
+        $headers = [
+            "Authorization:APPCODE {$appCode}",
+            "Content-Type:application/x-www-form-urlencoded; charset=UTF-8",
+        ];
+
+        $url = sprintf(self::$apiUrl.self::$apiUrls['getResult'], $token);
+        PRedis::set("caches:faceAuth:result:request_{$token}", ['url'=> $url,'headers'=> $headers], 600);
+        $result = httpHeaderRequest($url, '','get',$headers);
+        $resultCode = isset($result['result_code'])? $result['result_code'] : '';
+        $resultMsg = isset($result['result_message'])? $result['result_message'] : '';
+        $bizToken = isset($result['biz_token'])? $result['biz_token'] : '';
+        $images = isset($result['images'])? $result['images'] : [];
+        PRedis::set("caches:faceAuth:result:result_{$token}", $result, 600);
+        if($resultCode != 'SUCCESS'){
+            $msg = isset(self::$resultErrors[$resultMsg])? self::$resultErrors[$resultMsg] : '获取验证结果失败';
+            return ['code'=> 'error', 'msg'=> $msg, 'result'=> $result];
+        }
+
+        return ['code'=> 'success', 'msg'=> '获取验证结果', 'result'=> $result];
+
+    }
+
+    /**
+     * 格式化请求参数
+     * @param array $params
+     * @return false|string
+     */
+    public static function getParams(array $params){
+        $data = [];
+        if(empty($params)){
+            return false;
+        }
+
+        foreach ($params as $key => $val){
+            $data[] = $key.'='.$val;
+        }
+        
+        return $data? implode('&', $data) : '';
+    }
+
+    /**
+     * 处理回调
+     * @param $params
+     */
+    public static function catchNotify($params){
+
+    }
+}

+ 2 - 2
public/themes/default/weixin/auth/education.html

@@ -10,9 +10,9 @@
             <p>认证信息仅用于学历审核,所有信息都会自动打上码,拾光承诺绝对不会将用户资料用于任何第三方场景。</p></div>
         <div class="gerxix_box">
             <ul>
-                <li><h1>认证方式:</h1><input class="tianx select-value4" id="education_type" type="text" placeholder="请选择" readonly :data-code="authInfo.education_type" :disabled="authInfo.education_check==2? true: false"></li>
+                <li><h1>认证方式:</h1><img src="/themes/default/weixin/public/assets/img/tubiao10.png" class="minzu"><input class="tianx select-value4" id="education_type" type="text" placeholder="请选择" readonly :data-code="authInfo.education_type" :disabled="authInfo.education_check==2? true: false"></li>
                 <li v-if="education_type!=2"><h1>毕业院校:</h1><input class="tianx" id="graduate" type="text" placeholder="请填写" :value="authInfo.graduate" :readonly="authInfo.education_check==2? true : false"></li>
-                <li v-if="education_type!=2"><h1>认证学历:</h1> <input class="tianx select-value7" id="education" type="text" placeholder="请选择" readonly :data-code="authInfo.education" :disabled="authInfo.education_check==2? true : false"></li>
+                <li v-if="education_type!=2"><h1>认证学历:</h1><img src="/themes/default/weixin/public/assets/img/tubiao10.png" class="minzu"> <input class="tianx select-value7" id="education" type="text" placeholder="请选择" readonly :data-code="authInfo.education" :disabled="authInfo.education_check==2? true : false"></li>
                 <li v-if="education_type==2"><h1>在线验证码:</h1> <input class="tianx select-value7" id="education_code" type="text" :value="authInfo.education_code" placeholder="请填入验证码" :disabled="authInfo.education_check==2? true : false"></li>
                 <li v-if="education_type==2" class="online" @click="showHelp()"><a target="_blank" ><h1>获取学信网在线验证码:</h1><img
                         src="__TMPL__/weixin/public/assets/img/tubiao10.png" alt=""></a></li>

+ 3 - 3
public/themes/default/weixin/auth/idcard.html

@@ -14,16 +14,16 @@
                 <li><h1>身份证号:</h1><input v-model="authInfo.idcard" class="tianx" type="text" placeholder="请填写" :readonly="authInfo.idcard_check==2? true : false"></li>
             </ul>
         </div>
-        <div class="xiangce_tj">
+        <!--<div class="xiangce_tj">
             <h1>请上传两张正反面身份证照片(点击图片上传)生日、身高、婚姻状态通过认证后无法修改(可选)</h1>
             <p class="error" v-if="authInfo.idcard_check==3">失败原因:<span v-text="authInfo.idcard_fail? authInfo.idcard_fail : '无'"></span></p>
             <ul>
                 <li><img id="front_idcard" :src="authInfo.front_idcard_preview? authInfo.front_idcard_preview : '__TMPL__/weixin/public/assets/img/tubiao20.png'" onerror="this.src='__TMPL__/weixin/public/assets/img/tubiao20.png'"><input type="file" accept="image/*" @change="selectImg($event,'front_idcard')" :disabled="authInfo.idcard_check==2? true : false"></li>
                 <li><img id="back_idcard" :src="authInfo.back_idcard_preview? authInfo.back_idcard_preview : '__TMPL__/weixin/public/assets/img/tubiao20.png'" onerror="this.src='__TMPL__/weixin/public/assets/img/tubiao20.png'"><input type="file" accept="image/*" @change="selectImg($event,'back_idcard')" :disabled="authInfo.idcard_check==2? true : false"></li>
             </ul>
-        </div>
+        </div>-->
         <div class="baoc_ann" >
-            <h1 v-if="authInfo.idcard_check != 2" @click="saveData()">提交</h1>
+            <h1 v-if="authInfo.idcard_check != 2" @click="authCheck()">提交</h1>
             <h1 v-else >已认证</h1>
         </div>
     </div>

+ 2 - 2
public/themes/default/weixin/auth/position.html

@@ -10,9 +10,9 @@
             <p>认证信息仅用于工作审核,所有信息都会自动打上码,拾光承诺绝对不会将用户资料用于任何第三方场景。</p></div>
         <div class="gerxix_box">
             <ul>
-                <li><h1>认证方式:</h1><input class="tianx select-value4" id="position_type" type="text" placeholder="请选择" readonly :data-code="authInfo.position_type" :disabled="authInfo.position_check==2? true: false"></li>
+                <li><h1>认证方式:</h1><img src="/themes/default/weixin/public/assets/img/tubiao10.png" class="minzu"><input class="tianx select-value4" id="position_type" type="text" placeholder="请选择" readonly :data-code="authInfo.position_type" :disabled="authInfo.position_check==2? true: false"></li>
                 <li><h1>公司名称:</h1><input class="tianx" type="text" placeholder="请填写" v-model="authInfo.company"></li>
-                <li><h1>个人职位:</h1><input type="text" id="position" v-model="authInfo.occupation" class="tianx" placeholder="请填写" :readonly="authInfo.position_check==2? true : false"></li>
+                <li><h1>个人职位:</h1><img src="/themes/default/weixin/public/assets/img/tubiao10.png" class="minzu"><input type="text" id="position" v-model="authInfo.occupation" class="tianx" placeholder="请填写" :readonly="authInfo.position_check==2? true : false"></li>
             </ul>
         </div>
         <div class="xiangce_tj">

+ 3 - 7
public/themes/default/weixin/member/home.html

@@ -18,13 +18,6 @@
         <div class="ger_xix">
             <h1 class="nickname">
                 <span v-text="homeInfo.user_nickname"></span>
-                <p class="auth">
-                    <i v-if="homeInfo.idcard_check==2 || homeInfo.education_check==2 || homeInfo.position_check==2"
-                       class="info-auth" @click="showAuth(homeInfo)"><img
-                            src="/themes/default/weixin/public/assets/img/auth.png" width="28" height="28"></i>
-
-                    <!--                    <i v-if="homeInfo.vip_auth" class="vip-auth"><img  src="/themes/default/weixin/public/assets/img/vip-auth.png" width="24" height="24"><span>已认证</span></i>-->
-                </p>
                 <span v-if="homeInfo.id != memberInfo.id" class="complain" @click="showPopup()">举报用户</span>
                 <!--<span v-if="homeInfo.idcard_check==2"><img @click="showAuth('idcard')" src="__TMPL__/weixin/public/assets/img/idcard.png" alt="" width="24" height="24"><em v-show="showStatus.idcard">身份已认证</em></span>
                 <span v-if="homeInfo.education_check==2"><img @click="showAuth('education')" src="__TMPL__/weixin/public/assets/img/education.png" alt="" width="24" height="24"><em v-show="showStatus.education">学历已认证</em></span>
@@ -40,10 +33,13 @@
                         src="/themes/default/weixin/public/assets/img/info/height.png?v=2" class="height-icon"><span
                         class="height ext" v-text="homeInfo.height_txt?homeInfo.height_txt+' - ':''"></span><span
                         class="weight ext" v-text="homeInfo.weight_txt"></span></p>
+                <p><img src="__TMPL__/weixin/public/assets/img/icon/icon-start.png" alt=""><span
+                        class="start ext" v-text="homeInfo.start"></span></p>
                 <p v-if="(homeInfo.show_graduate && homeInfo.graduate) || homeInfo.education_txt"><img
                         src="__TMPL__/weixin/public/assets/img/info/education.png" alt=""><span
                         v-if="homeInfo.show_graduate" v-text="homeInfo.graduate?homeInfo.graduate+' · ':''"></span>
                     <span v-text="homeInfo.education_txt"></span></p>
+
                 <p v-if="homeInfo.married_txt"><img src="__TMPL__/weixin/public/assets/img/info/married.png"
                                                     alt=""><span v-text="homeInfo.married_txt"></span></p>
                 <p v-if="(homeInfo.show_company && homeInfo.company) || homeInfo.occupation"><img

+ 6 - 1
public/themes/default/weixin/public/assets/css/auth-education.css

@@ -117,4 +117,9 @@ body{background: #fff;}
     margin-top: 10px;
     color: #666;
     font-size: 14px;
-}
+}
+.minzu {
+    float: right;
+    width: 12px;
+    margin-top: 18px;
+}

+ 6 - 0
public/themes/default/weixin/public/assets/css/auth-position.css

@@ -199,4 +199,10 @@ body {
     margin-top: 10px;
     color: #666;
     font-size: 14px;
+}
+
+.minzu {
+    float: right;
+    width: 12px;
+    margin-top: 18px;
 }

二进制
public/themes/default/weixin/public/assets/img/icon/icon-start.png


+ 66 - 0
public/themes/default/weixin/public/assets/js/auth-idcard.js

@@ -104,6 +104,72 @@ var app = new Vue({
                 }
             }
         },
+        authCheck: function(){
+            var _this = this;
+            var params = _this.authInfo;
+            console.log(params)
+            if(_this.submitting.submit){
+                return false;
+            }
+
+            if(params.realname == '' || params.realname == null){
+                $.toast('请填写真实姓名', 'text');
+                return false;
+            }
+
+            if(params.idcard == '' || params.idcard == null){
+                $.toast('请填写身份证号', 'text');
+                return false;
+            }
+
+            $.modal({
+                title: "确定提交身份认证信息?",
+                text: "",
+                buttons: [
+                    {
+                        text: "取消", className: "default", onClick: function () {
+                            return false;
+                        }
+                    },
+                    {
+                        text: "确定", onClick: function () {
+                            _this.submitting.submit = true;
+                            $.showLoading('数据提交中...');
+                            $.ajax({
+                                url: '/api/member/authCheck',
+                                data: params,
+                                type: "post",
+                                dataType: 'json',
+                                success: function(res) {
+                                    $.hideLoading();
+                                    _this.submitting.submit = false;
+                                    if(res.code == 'success'){
+                                        $.showLoading(res.message);
+                                    }else if (res.code == 'login'){
+                                        var url = res.data.url;
+                                        $.toast(res.message,'text');
+                                        if(url){
+                                            setTimeout(function(){
+                                                $.hideLoading();
+                                                location.href = url;
+                                            }, 500)
+                                        }
+                                    }else{
+                                        $.toast(res.message,'text');
+                                    }
+                                },
+                                error:function (res) {
+                                    _this.submitting.submit = false;
+                                    $.toast(res.message,'text');
+                                }
+                            });
+                        }
+                    }
+                ]
+            });
+
+
+        },
         // 保存数据
         saveData: function(){
             var _this = this;