【delphi】微信公众号控件开发(二)

  • 三、架构说明
  • 1. 使用的控件
  • 2. 网页授权
  • 3. 网页中有Ajax请求的约定
  • 3.1 公众号JSSDK授权:1001
  • 3.2 身份证识别:1002
  • 3.3 通用JSON请求命令:999
  • 4. 重要函数说明
  • 5. SparkleGenericServerProcessRequest函数处理流程图

)

三、架构说明

本章说明微信公众号控件内部使用的控件以及整体架构。

1. 使用的控件

序号

控件

描述

1

TNetHTTPClient

用来进行HTTP请求,可以支持HTTPS。以前都是用的是TidHTTP,现在新版的delphi已经原生的支持HTTPS了,所以就不需要使用Indy控件了,也就不需要附带额外的DLL了。

2

TTimer

用来做刷新Token的定时器

3

TSparkleGenericServer

用来作为微信公众号的Web服务器,这个是TMS的控件。需要购买!

4

TSparkleHttpSysDispatcher

用来实现Web分发的中间件,需要和TSparkleGenericServer 配合使用,都是TMS的控件。

5

TXDataServer

能够实现和数据库直接连接的提供Restful API的控件。

6

TXDataConnectionPool

XData的数据库连接池

7

TAureliusConnection

XData需要使用的控件

8

TSparkleGenericMiddleware

中间件,可以直接修改XData的Web请求底层数据,其中6、7、8控件都是配合TXDataServer 的。

具体的程序定义如下:

//创建Web服务
  FSparkleGenericServer     :=  TSparkleGenericServer.Create(nil);
  FSparkleHttpSysDispatcher :=  TSparkleHttpSysDispatcher.Create(nil);
  FSparkleGenericServer.Dispatcher := FSparkleHttpSysDispatcher;
  FSparkleGenericServer.OnProcessRequest := SparkleGenericServerProcessRequest;
  //创建Xdata相关
  FAureliusConnection       :=  TAureliusConnection.Create(nil);      //定义Xdata
  FXDataConnectionPool      :=  TXDataConnectionPool.Create(nil);
  FXDataServer              :=  TXDataServer.Create(nil);
  FXDataServerGeneric       :=  TSparkleGenericMiddleware.Create(nil);
  FXDataServerGeneric.OnRequest := FXDataServerGenericRequest;
  FXDataServer.MiddlewareList.Add(FXDataServerGeneric);
  //设置相关属性
  FXDataServer.Dispatcher   :=  FSparkleHttpSysDispatcher;
  FXDataServer.Pool         :=  FXDataConnectionPool;
  FXDataConnectionPool.Connection := FAureliusConnection;

程序通过 TSparkleGenericServer 提供微信公众号的Web基本服务器,如果需要和数据库打交道,此时是通过TXDataServer 控件提供Web服务。

2. 网页授权

对于微信公众号网页授权,需要授权的网址要求附带 state=A01参数。否则将作为一般的请求,不进行授权请求处理。

spark shuffle的区别_json

3. 网页中有Ajax请求的约定

不同的请求,功能不一样,所以需要使用what参数来区别,请求的格式为:http://sensorwx.a3650.com/wxh?what=1002,如果还有其他个性参数没有问题,但是必须有what=nnn这个参数,后台会根据nnn的不同进行处理。

3.1 公众号JSSDK授权:1001
  1. 请求格式:http://sensorwx.a3650.com/wxh?what=1001
  2. 入口参数:{“url" : “需要授权的网址”}
  3. 请求方法:post
  4. 返回参数:’{“timestamp”:"%s",“noncestr”:"%s",“signature”:"%s",“appid”:"%s"}’

前端 javascript 程序:请注意wx.config,这个是微信JSSDK授权的函数调用。

//生成入口参数
function myBuild_JSSDKData(){
  var json = { 
       "url":window.location.href
       };
       return json;
 };
 
 function myGet_JSSDK_Post(){
  //ajax注入权限验证
  $.ajax({ 
   type: "post",
   url:'http://sensorwx.a3650.com/wxh?what=1001', 
   dataType : "json",   //此处必须是json
   contentType: 'application/json;charset="UTF-8"',  //此处必须有这一句
   data:  JSON.stringify(myBuild_JSSDKData()) , 
   complete: function(XMLHttpRequest, textStatus){ },
   error: function(XMLHttpRequest, textStatus, errorThrown){
    alert("发生错误:"+errorThrown);
   },
   success: function(res){
    var appid = res.appid;
    var noncestr = res.noncestr;
    var timestamp = res.timestamp;
    var signature = res.signature;
    alert(JSON.stringify(res));
    alert(res.appid + '.' + res.noncestr + '.' + res.timestamp + '.' + res.signature);
    wx.config({
     debug: true, //开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
     appId: appid, //必填,公众号的唯一标识
     timestamp: timestamp, // 必填,生成签名的时间戳
     nonceStr: noncestr, //必填,生成签名的随机串
     signature: signature,// 必填,签名,见附录1
     jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ',
        'onMenuShareWeibo','onMenuShareQZone','chooseImage',
        'uploadImage','downloadImage','startRecord','stopRecord',
         'onVoiceRecordEnd','playVoice','pauseVoice','stopVoice',
         'translateVoice','openLocation','getLocation','hideOptionMenu',
         'showOptionMenu','closeWindow','hideMenuItems','showMenuItems',
         'showAllNonBaseMenuItem','hideAllNonBaseMenuItem'] //必填,需要使用的JS接口列表,
    });
   },
  });
 };

delphi 后端程序函数:

function TWeiXinComponent.Get_jssdk_signature({noncestr, jsapi_ticket, timestamp,}  url: string): string;
const
  JSON = '{"timestamp":"%s","noncestr":"%s","signature":"%s","appid":"%s"}';
var
   strs: TStringList;
   tmpStr: string;
   noncestr, jsapi_ticket, timestamp : string;
begin
   Result := '';
   noncestr     := 'sensor' + FormatDateTime('hhmmsszzz',Now);
   timestamp    := IntToStr(DateTimeToUnix(Now));
   jsapi_ticket := Fjsapi_ticket;
   strs := TStringList.Create;
   try
     strs.Add('noncestr=' + noncestr);
     strs.Add('jsapi_ticket=' + jsapi_ticket);
     strs.Add('timestamp=' + timestamp);
     strs.Add('url=' + url);
     strs.Sort; //这个排序不严谨,但是这里使用的参数没有问题
     tmpStr := strs[0] + '&' + strs[1] + '&' + strs[2] + '&' + strs[3];
     //进行数据签名
     tmpStr := THashSHA1.GetHashString(tmpStr);
     Result := Format(JSON,[timestamp,nonceStr,tmpStr,FAppID]);
   finally
     FreeAndNil(strs);
   end;
 end;
3.2 身份证识别:1002
  1. 请求格式:http://sensorwx.a3650.com/wxh?what=1002
  2. 请求方法:post
  3. 入口参数:{“media_id”:"%s" ,“user_id”:"%s"},参数格式JSON
  4. 出口参数:{“name”:"%s",“id”:"%s",“addr”:"%s",“gender”:"%s",“nationality”:"%s"}

前端请求流程:

  1. 通过 wx.chooseImage 选择一张身份证图片,返回选定照片的本地ID列表;
  2. 通过 wx.uploadImage 上传选定的照片到腾讯后台,使用第一步得到的本地照片ID上传,上传的照片会保存到公众号的临时素材中,返回一个临时素材的media_id;
  3. 调用 http://sensorwx.a3650.com/wxh?what=1002 接口,post数据为 {“media_id”:"%s" ,“user_id”:"%s"},其中media_id是第2步返回的serverId(实际是media_id),user_id 是签写调用者自己是谁,可以为空。
  4. 后台通过 media_id 从腾讯后台下载临时素材命令抓取到照片,将照片保存成本地一个文件,然后再将这个文件传送给腾讯后台进行识别,识别结果以JSON数据的格式返回。

前端 javascript 程序:

$('#my_TestJSSDK').on('click', function(){
  wx.chooseImage({
    count: 1, // 默认9
    sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
    success: function (res) {
    var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
    //上传到服务器
    wx.uploadImage({
      localId: localIds[0], // 需要上传的图片的本地ID,由chooseImage接口获得
      isShowProgressTips: 1, // 默认为1,显示进度提示
      success: function (result) {
       var serverId = result.serverId; // 返回图片的服务器端ID
       //alert(serverId);
       $('#loadingToast').fadeIn('fast');
       //$.showLoading("数据加载中");  //只有小程序才可以这样使用
       $.ajax({ 
      type: "post",
      url:'http://sensorwx.a3650.com/wxh?what=1002', 
      dataType : "json",   //此处必须是json
      contentType: 'application/json;charset="UTF-8"',  //此处必须有这一句
      data:  '{"media_id":"' + serverId +'","user_id":"AAAA"}',  //   serverId,  //发送的是一个 Media_id
      //data: {"url" : url},  
      complete: function(XMLHttpRequest, textStatus){ },
      error: function(XMLHttpRequest, textStatus, errorThrown){
       alert("发生错误:"+errorThrown);
       },
      success: function(res){
         if (res.errcode) {alert(res.errcode + '  ' + res.errmsg)};      
         if (res.name) { alert(res.name + ' / ' + res.id + ' / ' + res.addr + ' /' + res.gender + ' / ' + res.nationality)};
         $('#loadingToast').fadeOut('fast');
         //$.hideLoading();
         },
      });           
      }
     });     
    }    
  });  
 });

后端delphi 程序

procedure TWeiXinComponent.Process_REC_IDCARD(const JSON_Str: string;  context: THttpServerContext);
const
  JSON = '{"name":"%s","id":"%s","addr":"%s","gender":"%s","nationality":"%s"}';
var
  media_id, user_id : string;
  Pic_IDCard_path : string;
  FileName        : string;
  Media_Type      : integer;
  Filestream      : TStringStream;
  IDCard_info     : TIDCard_Front;
  jo              : TJSONObject;
  jv              : TJSONString;
  S               : string;
  B               : TBytes;
begin
  PutLogFile('收到: ' + JSON_Str);
  jo := TjsonObject.ParseJSONValue(JSON_Str) as TjsonObject;
  if not Assigned(jo) then
    begin
      Return_ERRORMSG('-99','上报的JOSN字符串不合法:' + JSON_Str,context);
      Exit;
    end;
  if jo.TryGetValue('media_id',jv) then media_id := jv.Value;
  if jo.TryGetValue('user_id',jv) then user_id := jv.Value;
  if media_id = '' then
    begin
      Return_ERRORMSG('-99','上报的JOSN字符串media_id不能为空:' + JSON_Str,context);
      Exit;
    end;
  PutLogFile('收到A: ' + JSON_Str);
  //1. 首先判断本地存放图片的文件目录是否存在
  Pic_IDCard_path := FBaseRootDir + '\Pic_IDCard';
  if not DirectoryExists(Pic_IDCard_path) then ForceDirectories(Pic_IDCard_path);
  //2. 构建文件名称
  FileName := Pic_IDCard_path + '\' + User_id + '__' + media_id + '.jpg';
  //3. 下载到本地
  Filestream := TStringStream.Create;
  try
     if not Temporary_material_Get(Filestream, media_id, Media_Type) then
       begin
         Return_ERRORMSG('-99','获取图片失败 media_id:' + media_id,context);
         Exit;
       end;
     Filestream.SaveToFile(FileName);
  finally
    Filestream.Free;
  end;
  PutLogFile('收到B: ' + FileName);
  //4. 准备识别这张招照片
  if not Distinguish_IDCard_Front_File(FileName, IDCard_info) then
    begin
      PutLogFile('收到c: 失败!');
      Return_ERRORMSG('-99','识别图片失败!',context);
      Exit;
    end;

  S := Format(JSON,[IDCard_info.name,IDCard_info.id,IDCard_info.addr,IDCard_info.gender,IDCard_info.nationality]);
  B := TEncoding.UTF8.GetBytes(S);
  context.Response.StatusCode := 200;
  context.Response.Content.Write(B[0],Length(B));
  context.Response.Close;
end;
3.3 通用JSON请求命令:999

如果前端发送这个命令,微信控件将会触发 TOn001_JSONRequest 这个事件。

procedure(const Request_JSON : string; var Response_JSON : string) of object;

参数说明:

序号

参数

说明

1

Request_JSON

前端请求的JSON字符串

2

Response_JSON

后端经过处理后返回给前端的JSON字符串

  1. 请求格式:http://sensorwx.a3650.com/wxh?what=999
  2. 请求方法:post
  3. 请求参数:Request_JSON,是一个JSON字符串
  4. 返回参数:Response_JSON,返回一个JSON字符串

4. 重要函数说明

序号

函数名称

说明

1

SparkleGenericServerProcessRequest

负责监听所有微信Web服务器的请求并分发回复,是控件TSparkleGenericServer的事件处理函数,这个是一个核心函数。

2

FXDataServerGenericRequest

XData的底层监听处理函数

5. SparkleGenericServerProcessRequest函数处理流程图

spark shuffle的区别_控件_02