WeChat JSSDK를 사용하여 체크인 기능을 구현하기 위해 C#을 사용하여 WeChat 포털 및 애플리케이션 개발 소개

高洛峰
풀어 주다: 2017-03-09 15:01:18
원래의
2499명이 탐색했습니다.

이 문서에서는 체크인 기능을 구현하기 위해 WeChat JSSDK를 사용하는 WeChat 포털 및 애플리케이션의 C# 개발 도입에 대해 설명합니다.

WeChat이 점차 더 많은 JSSDK 인터페이스를 개방함에 따라 사용자 정의 웹 페이지에서는 더 많은 WeChat 인터페이스를 호출하여 더 풍부한 인터페이스 기능과 효과를 얻을 수 있습니다. 예를 들어, 페이지에서 다양한 휴대폰 하드웨어를 호출하여 카메라 사진, GPS 정보, QR 코드 스캔 등과 같은 정보를 얻을 수 있습니다. 이 기사에서는 이러한 JSSDK 인터페이스를 사용하여 체크인 기능을 구현하는 방법을 소개합니다. 여기서 체크인하려면 지리적 좌표와 주소를 보고하고 카메라를 호출하여 실시간으로 사진을 찍고 현재 사용자에 대한 관련 정보를 얻는 등이 필요합니다.

1. JSSDK 설명

WeChat JS-SDK는 WeChat 공개 플랫폼에서 웹 개발자를 위해 제공하는 WeChat 기반의 웹 개발 툴킷입니다. WeChat JS-SDK를 사용하면 웹 개발자는 WeChat을 사용하여 사진 촬영, 사진 선택, 음성 선택, 위치 등 휴대폰 시스템의 기능을 효율적으로 사용할 수 있으며 동시에 공유, 공유 등 WeChat 고유의 기능을 직접 사용할 수 있습니다. 스캔, 쿠폰 및 결제를 통해 WeChat 사용자에게 더 나은 웹 경험을 제공합니다.

현재 JSSDK에서 지원하는 인터페이스 카테고리에는 기본 인터페이스, 공유 인터페이스, 이미지 인터페이스, 오디오 인터페이스, 스마트 인터페이스, 장치 정보, 지리적 위치, 주변기기 흔들기, 인터페이스 작동, WeChat 스캔, WeChat 스토어 등이 있습니다. , WeChat 쿠폰, WeChat 결제 등 모든 WeChat 기능이 통합되어 더 많은 인터페이스가 차례로 열릴 것으로 예상됩니다.

WeChat 백엔드에서 [개발자 문서] 모듈에 들어가면 아래와 같이 해당 JSSDK의 기능 분류 및 소개를 볼 수 있습니다.

WeChat JSSDK를 사용하여 체크인 기능을 구현하기 위해 C#을 사용하여 WeChat 포털 및 애플리케이션 개발 소개

오른쪽에서 각 인터페이스의 사용법을 자세히 볼 수 있습니다. 기본적으로 JSSDK의 사용 방법은 유사하므로 디버깅을 통과하고 마스터할 수 있습니다. 그 중 두 개, 나머지는 지침을 따르고 그대로 따르십시오.

1) JSSDK 사용 단계

1단계: 도메인 이름 바인딩

먼저 WeChat 공개 플랫폼에 로그인하고 "공식 계정 설정"의 "기능 설정"으로 들어갑니다. "를 입력하여 "JS 인터페이스" "보안 도메인 이름"을 입력합니다. 아래와 같이 공개 플랫폼에 설정합니다.

WeChat JSSDK를 사용하여 체크인 기능을 구현하기 위해 C#을 사용하여 WeChat 포털 및 애플리케이션 개발 소개

참고: 로그인 후 "개발자 센터"에서 해당 인터페이스 권한을 확인할 수 있습니다.

2단계: JS 파일 소개

JS 인터페이스를 호출해야 하는 페이지에 다음 JS 파일을 소개합니다(https 지원): http://res .wx.qq .com/open/js/jweixin-1.0.0.js

흔들기 주변기기 기능을 사용해야 하는 경우 http://res.wx.qq.com/open/을 가져와주세요. js/jweixin-1.1 .0.js

참고: AMD/CMD 표준 모듈 로딩 방법을 사용하여 로딩을 지원합니다

물론 일반적으로 더 많은 효과를 내기 위해 페이지를 편집합니다. JQuery 클래스 라이브러리 등과 같은 다른 JS를 도입할 수도 있습니다. 또한 WeUI의 jquery-weui 클래스 라이브러리를 기반으로 더욱 풍부한 기능을 구현할 수도 있습니다. 다음은 사례 코드의 JS 참조입니다.

    <script src="~/Content/wechat/jquery-weui/lib/jquery-2.1.4.js"></script>
    <script src="~/Content/wechat/jquery-weui/js/jquery-weui.js"></script>
    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.1.0.js"></script>
로그인 후 복사

3단계: 구성 인터페이스를 통해 권한 확인 구성 삽입

JS-SDK를 사용해야 하는 모든 페이지는 먼저 구성 정보를 삽입해야 합니다. 그렇지 않으면 호출되지 않습니다(동일한 URL만) URL을 변경하는 SPA 웹 앱은 URL이 변경될 때마다 호출할 수 있습니다. 현재 Android WeChat 클라이언트는 pushState의 새로운 H5 기능을 지원하지 않으므로 pushState를 사용하여 웹 앱 페이지를 구현하면 이 문제는 Android 6 .2에서 발생합니다.

wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: &#39;&#39;, // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: &#39;&#39;, // 必填,生成签名的随机串
    signature: &#39;&#39;,// 必填,签名,见附录1
    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2

});
로그인 후 복사

위 구성은 JSSDK의 핵심입니다. 해당 appid, timestamp 및 nonceStr을 구성해야 합니다. 가장 주목할만한 점은 서명 구현 메커니즘입니다. 백그라운드에서 수행할 수 있습니다. 해당 값을 생성하여 JS 페이지에 할당하는 것이 가장 안전한 방법이기도 합니다.

다음 코드는 실제 프로젝트의 Asp.net 보기 페이지에 있는 HTML 코드로 아래와 같습니다.

<script language="javascript">
    var appid = &#39;@ViewBag.appid&#39;;
    var noncestr = &#39;@ViewBag.noncestr&#39;;
    var signature = &#39;@ViewBag.signature&#39;;
    var timestamp = &#39;@ViewBag.timestamp&#39;;

        wx.config({
            debug: false,
            appId: appid, // 必填,公众号的唯一标识
            timestamp: timestamp, // 必填,生成签名的时间戳
            nonceStr: noncestr, // 必填,生成签名的随机串
            signature: signature, // 必填,签名,见附录1
            jsApiList: [
               &#39;checkJsApi&#39;,
               &#39;onMenuShareTimeline&#39;,
               &#39;onMenuShareAppMessage&#39;,
               &#39;onMenuShareQQ&#39;,
               &#39;onMenuShareWeibo&#39;,
               &#39;onMenuShareQZone&#39;,
               &#39;hideMenuItems&#39;,
               &#39;showMenuItems&#39;,
               &#39;hideAllNonBaseMenuItem&#39;,
               &#39;showAllNonBaseMenuItem&#39;,
               &#39;translateVoice&#39;,
               &#39;startRecord&#39;,
               &#39;stopRecord&#39;,
               &#39;onVoiceRecordEnd&#39;,
               &#39;playVoice&#39;,
               &#39;onVoicePlayEnd&#39;,
               &#39;pauseVoice&#39;,
               &#39;stopVoice&#39;,
               &#39;uploadVoice&#39;,
               &#39;downloadVoice&#39;,
               &#39;chooseImage&#39;,
               &#39;previewImage&#39;,
               &#39;uploadImage&#39;,
               &#39;downloadImage&#39;,
               &#39;getNetworkType&#39;,
               &#39;openLocation&#39;,
               &#39;getLocation&#39;,
               &#39;hideOptionMenu&#39;,
               &#39;showOptionMenu&#39;,
               &#39;closeWindow&#39;,
               &#39;scanQRCode&#39;,
               &#39;chooseWXPay&#39;,
               &#39;openProductSpecificView&#39;,
               &#39;addCard&#39;,
               &#39;chooseCard&#39;,
               &#39;openCard&#39;
            ]
        });
로그인 후 복사

4단계: Ready 인터페이스 처리를 통한 성공적인 검증

wx.ready(function(){
    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,
    //则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
로그인 후 복사

Ready 인터페이스는 페이지가 성공적으로 로드된 후의 콘텐츠를 처리하는 것입니다. 일반적으로 Only를 포함한 많은 작업을 수행해야 합니다. 페이지가 로드된 후 할당, 처리 및 기타 작업을 위해 관련 개체를 호출할 수 있습니다.

예를 들어 페이지가 준비된 후 해당 GPS 좌표 및 기타 작업을 얻을 수 있으며 이는 다음 JS 코드로 구현할 수 있습니다.

wx.ready(function () {
            wx.getLocation({
                type: &#39;wgs84&#39;, // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入&#39;gcj02&#39;
                success: function (res) {
                    var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
                    var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
                    var speed = res.speed; // 速度,以米/每秒计
                    var accuracy = res.accuracy; // 位置精度
                    $("#lblLoacation").text(latitude + "," + longitude);
                }
            });
        });
로그인 후 복사

5단계: 오류 인터페이스를 통해 실패한 확인 처리

wx.error(function(res){
    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,
    // 也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
로그인 후 복사

이 오류 인터페이스는 예외 정보를 처리하는 데에도 사용됩니다. 일반적인 상황에서는 여기에 오류 메시지가 표시될 수 있습니다.

2), 서명 알고리즘

签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。

即signature=sha1(string1)。 示例:

noncestr=Wm3WZYTPz0wzccnW

jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg

timestamp=1414587457

url=http://mp.weixin.qq.com?params=value
로그인 후 복사

步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:

jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value
로그인 후 복사

步骤2. 对string1进行sha1签名,得到signature:

0f9de62fce790f9a083d5c99e95740ceb90c27ed
로그인 후 복사

注意事项

1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。

2.签名用的url必须是调用JS接口页面的完整URL。

3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

如出现invalid signature 等错误详见附录5常见错误及解决办法。

以上就是JSSDK总体的使用流程,虽然看起来比较抽象,但是基本上也就是这些步骤了。

上面的过程是具体的参数处理逻辑,我们要对应到C#代码的签名实现,需要对几个变量进行处理,下面是对应的生成noncestr、timestamp、以及签名等操作的代码。

/// <summary>
        /// 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
        /// </summary>
        /// <returns>时间戳</returns>
        private static string GetTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        /// <summary>
        /// 生成随机串,随机串包含字母或数字
        /// </summary>
        /// <returns>随机串</returns>
        private static string GetNonceStr()
        {
            return Guid.NewGuid().ToString().Replace("-", "");
        }
로그인 후 복사

还有我们要实现JSSDK签名的处理,必须先根据几个变量,构建好URL字符串,具体的处理过程,我们可以把它们逐一放在一个Hashtable里面,如下代码所示。

/// <summary>
        /// 获取JSSDK所需要的参数信息,返回Hashtable结合
        /// </summary>
        /// <param name="appId">微信AppID</param>
        /// <param name="jsTicket">根据Token获取到的JSSDK ticket</param>
        /// <param name="url">页面URL</param>
        /// <returns></returns>
        public static Hashtable GetParameters(string appId, string jsTicket, string url)
        {
            string timestamp = GetTimeStamp();
            string nonceStr = GetNonceStr();

            // 这里参数的顺序要按照 key 值 ASCII 码升序排序  
            string rawstring = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url + "";

            string signature = GetSignature(rawstring);
            Hashtable signPackage = new Hashtable();
            signPackage.Add("appid", appId);
            signPackage.Add("noncestr", nonceStr);
            signPackage.Add("timestamp", timestamp);
            signPackage.Add("url", url);
            signPackage.Add("signature", signature);
            signPackage.Add("jsapi_ticket", jsTicket);
            signPackage.Add("rawstring", rawstring);

            return signPackage;
        }
로그인 후 복사

我们注意到URL参数的字符串组合:

string rawstring = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url + "";
로그인 후 복사

这里我们拼接好URL参数后,就需要使用签名的规则来实现签名的处理了,签名的代码如下所示,注释代码和上面代码等同。

/// <summary>
        /// 使用SHA1哈希加密算法生成签名
        /// </summary>
        /// <param name="rawstring">待处理的字符串</param>
        /// <returns></returns>
        private static string GetSignature(string rawstring)
        {
            return FormsAuthentication.HashPasswordForStoringInConfigFile(rawstring, "SHA1").ToLower();

            ////下面和上面代码等价
            //SHA1 sha1 = new SHA1CryptoServiceProvider();
            //byte[] bytes_sha1_in = System.Text.UTF8Encoding.Default.GetBytes(rawstring);
            //byte[] bytes_sha1_out = sha1.ComputeHash(bytes_sha1_in);
            //string signature = BitConverter.ToString(bytes_sha1_out);
            //signature = signature.Replace("-", "").ToLower();
            //return signature;
        }
로그인 후 복사

这样我们有了对应的值后,我们就可以把它们的参数全部放在集合里面了供使用。

/// <summary>
        /// 获取用于JS-SDK的相关参数列表(该方法对accessToken和JSTicket都进行了指定时间的缓存处理,多次调用不会重复生成)
        /// 集合里面包括jsapi_ticket、noncestr、timestamp、url、signature、appid、rawstring
        /// </summary>
        /// <param name="appid">应用ID</param>
        /// <param name="appSecret">开发者凭据</param>
        /// <param name="url">页面URL</param>
        /// <returns></returns>
        public Hashtable GetJSAPI_Parameters(string appid, string appSecret, string url)
        {
            string accessToken = GetAccessToken(appid, appSecret);
            string jsTicket = GetJSAPI_Ticket(accessToken);

            return JSSDKHelper.GetParameters(appid, jsTicket, url);
        }
로그인 후 복사

下面我们通过具体的代码案例来介绍使用的过程。

2、签到功能的实现处理

其实签到,都可以在微信公众号和企业号实现,微信的企业号可能实现更佳一些,不过他们使用JSSDK的接口操作是一样的,我们可以拓展过去就可以了。这里介绍微信公众号JSSDK实现签到的功能处理。

签到的功能,我们希望记录用户的GPS位置信息,还有就是利用拍照功能,拍一个照片同时上传到服务器,这样我们就可以实现整个业务效果了。

首先我们来设计签到的界面,代码及效果如下所示。

WeChat JSSDK를 사용하여 체크인 기능을 구현하기 위해 C#을 사용하여 WeChat 포털 및 애플리케이션 개발 소개

界面预览效果如下所示:

WeChat JSSDK를 사용하여 체크인 기능을 구현하기 위해 C#을 사용하여 WeChat 포털 및 애플리케이션 개발 소개

我们来看看微信JSSDK里面对于【获取地理位置接口】的说明:

wx.getLocation({
    type: &#39;wgs84&#39;, // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入&#39;gcj02&#39;
    success: function (res) {
        var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
        var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
        var speed = res.speed; // 速度,以米/每秒计
        var accuracy = res.accuracy; // 位置精度
    }
});
로그인 후 복사

以及图形接口里面【拍照或从手机相册中选图接口】的说明:

wx.chooseImage({
    count: 1, // 默认9
    sizeType: [&#39;original&#39;, &#39;compressed&#39;], // 可以指定是原图还是压缩图,默认二者都有
    sourceType: [&#39;album&#39;, &#39;camera&#39;], // 可以指定来源是相册还是相机,默认二者都有
    success: function (res) {
        var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
    }
});
로그인 후 복사

上传图片到微信服务器接口如下所示。

wx.uploadImage({
    localId: &#39;&#39;, // 需要上传的图片的本地ID,由chooseImage接口获得
    isShowProgressTips: 1, // 默认为1,显示进度提示
    success: function (res) {
        var serverId = res.serverId; // 返回图片的服务器端ID
    }
});
로그인 후 복사

备注:上传图片有效期3天,可用微信多媒体接口下载图片到自己的服务器,此处获得的 serverId 即 media_id。

根据这几个接口,我们来对它们进行包装,以实现我们的业务需求。根据我们的需要,我们对JSSDK接口进行了调用,如下所示。

<script language="javascript">
    var appid = &#39;@ViewBag.appid&#39;;
    var noncestr = &#39;@ViewBag.noncestr&#39;;
    var signature = &#39;@ViewBag.signature&#39;;
    var timestamp = &#39;@ViewBag.timestamp&#39;;

        wx.config({
            debug: false,
            appId: appid, // 必填,公众号的唯一标识
            timestamp: timestamp, // 必填,生成签名的时间戳
            nonceStr: noncestr, // 必填,生成签名的随机串
            signature: signature, // 必填,签名,见附录1
            jsApiList: [
               &#39;checkJsApi&#39;,
               &#39;chooseImage&#39;,
               &#39;previewImage&#39;,
               &#39;uploadImage&#39;,
               &#39;downloadImage&#39;,
               &#39;getNetworkType&#39;,
               &#39;openLocation&#39;,
               &#39;getLocation&#39;
            ]
        });

        wx.ready(function () {
            wx.getLocation({
                type: &#39;wgs84&#39;, // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入&#39;gcj02&#39;
                success: function (res) {
                    var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
                    var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
                    var speed = res.speed; // 速度,以米/每秒计
                    var accuracy = res.accuracy; // 位置精度
                    $("#lblLoacation").text(latitude + "," + longitude);

                    //解析坐标地址
                    var location = latitude + "," + longitude;
                    $.ajax({
                        type: &#39;GET&#39;,
                        url: &#39;/JSSDKTest/GetAddress?location=&#39; + location,
                        //async: false, //同步
                        //dataType: &#39;json&#39;,
                        success: function (json) {
                            $("#lblAddress").text(json);
                        },
                        error: function (xhr, status, error) {
                            $.messager.alert("提示", "操作失败" + xhr.responseText); //xhr.responseText
                        }
                    });
                }
            });
            wx.getNetworkType({
                success: function (res) {
                    var networkType = res.networkType; // 返回网络类型2g,3g,4g,wifi
                    $("#lblNetwork").text(networkType);
                }
            });
            
            chooseImage();
        });
    </script>
로그인 후 복사

其中的chooseImage()是我们在页面开始的时候,让用户拍照的操作,具体JS代码如下所示。

//拍照显示
        var localIds;
        function chooseImage() {
            wx.chooseImage({
                count: 1, // 默认9
                sizeType: [&#39;original&#39;, &#39;compressed&#39;], // 可以指定是原图还是压缩图,默认二者都有
                sourceType: [&#39;camera&#39;], // 可以指定来源是相册还是相机,默认二者都有
                success: function (res) {
                    localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
                    $("#imgUpload").attr("src", localIds);
                }
            });
        }
로그인 후 복사

但用户使用摄像头拍照后,就会返回一个res.localIds集合,因为我们拍照一个,那么可以把它直接赋值给图片对象,让它显示当前拍照的图片。

拍照完成,我们单击【签到】应该把图片和相关的坐标等信息上传到服务器的,图片首先是保存在微信服务器的,上传图片有效期3天,可用微信多媒体接口下载图片到自己的服务器,此处获得的 serverId 即 media_id。

为了实现我们自己的业务数据,我们需要把图片集相关信息存储在自己的服务器,这样才可以实现信息的保存,最后提示【签到操作成功】,具体过程如下所示。

//上传图片
        var serverId;
        function upload() {
            wx.uploadImage({
                localId: localIds[0],
                success: function (res) {
                    serverId = res.serverId;

                    //提交数据到服务器

                    //提示信息
                    $.toast("签到操作成功");
                },
                fail: function (res) {
                    alert(JSON.stringify(res));
                }
            });
        }
로그인 후 복사

另外,我们为了实现单击图片控件,实现重新拍照的操作,以及签到的事件处理,我们对控件的单击处理进行了绑定,如下代码所示。

document.querySelector(&#39;#imgUpload&#39;).onclick = function () {
            chooseImage();
        };

        $(document).on("click", "#btnSignIn", function () {
            if (localIds == undefined || localIds== null) {
                $.toast(&#39;请先拍照&#39;, "forbidden");
                return;
            }
            //调用上传图片获得媒体ID
            upload();
        });
로그인 후 복사

WeChat JSSDK를 사용하여 체크인 기능을 구현하기 위해 C#을 사용하여 WeChat 포털 및 애플리케이션 개발 소개

위 내용은 WeChat JSSDK를 사용하여 체크인 기능을 구현하기 위해 C#을 사용하여 WeChat 포털 및 애플리케이션 개발 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!