题注: 
      发表这篇解决方案,属于非盈利目的。主要是为了让大家了解一种接口技术解决方案文档的编写格式以及让大家评审在我的这个技术解决方案中的不足之处,以便大家指出并加以改进。 
      转载,下载或与各种形式使用这篇文章,必须注明文章的作者,出处。 
      其他未尽事宜,以国家法律规定的为准! 
                                                         作者:南疯

 

8       数据格式

       Web Service的调用过程中,无论是外协系统还是施工系统,都有发送数据和接收数据的要求,也即双方系统同时作为客户端又作为服务端。我们统称这些传递的数据为报文。

    客户端发送报文,属于调用;服务端接收报文,属于被调用。

    客户端和服务端互相之间通讯的请求报文和结果报文遵循XML格式。客户端发送请求报文,服务器解析调用报文,执行报文中所在接口对应的服务功能。生成结果报文,以xml格式页返回给请求者。请求者拿到结果报文,进行解析,然后再进行相应的处理。

8.1    约定

u       报文中所有的字典信息(比如性别1-男,2-女),都以代码的值(1或者2)来传递。施工单位向外协系统发送的报文中的代码都需要转换成外协的代码,外协系统向施工系统发送的报文中的代码无需转换。

u       报文中的其他数据类型,比如货币、RowID,自定义对象类型等,根据需要转换成文本、数值或二进制(最终转换成Base64字符)的数据类型。

u       报文中数值信息,转换成文本,如:

<ItemCount>50</ItemCount>

<ValueSum>12368.36</ValueSum>

u       报文中数值不支持科学计数的方式。报文中数值的单位使用国标的单位,比如货币使用“元”,长度使用“米”等。无国标的单位以约定为准。

u       报文中的日期信息,转换成YYYYMMDD HHmmss文本格式(24小时制)。如果是空日期,则转换成空文本。

u       报文中的truefalse数据类型,转换成 0(表示false)1(表示true

u       报文中的二进制数据,转换成Base64字符方式发送。

u       报文中的记录集,放在< Records>标签中;报文中一条记录,放在< Records>标签中。

u       报文中如果存在多条记录,则在<Records>标签中就包含多个的<Record>标签。如果报文中仅有一条记录,则<Records>标签中只包含一个<Record>标签.如果没有记录,则仅仅包含一个<Records>标签,没有<Record>标签。

u       如果返回结果数据集非常多,在性能考虑和数据量冲突的情况下,可以使用分页返回数据集的方式分批返回数据(每次返回最多100条记录)。服务端提供分批结果返回的功能。至于如何使用分页查询的功能,参见下面的XML框架说明。

8.2    施工系统向外协系统发送请求

施工系统向外协系统发送请求的数据目前有几点需要考虑:

u       如何请求查询一个业务数据

u       如何新增一条记录,新增之后如何点到记录的键值

u       如何修改一条记录

u       如何删除一条记录

u       文档如何上传

u       一条记录中一个FileID的字段如何上传多个文件

u       如何在一条记录中补充上传文档

u       如何在一条记录中删除一个文档

u       如何获得文档的基本信息

u       如何获得文档的所有兄弟信息

u       如何获得文档的所有父亲信息

u       如何下载一个文档

针对这些问题,接口方案的解决方法如下:

8.2.1   请求查询一个业务数据

施工系统针对外协系统发送的业务数据查询请求根据业务类型有很多种。为了简化接口,而不是在接口上进行业务操作,所以,外协系统将施工系统发起的数据查询请求看作是数据下载的一种方式,而不为了复杂的业务查询请求提供复杂的条件解析。

外协系统提供的数据查询接口是从数据下载和数据延期性来考虑的。为了满足数据的下载,外协系统提供了按照某一个表的主键来下载数据和按照记录的变更时间范围来下载数据的两种方式查询请求。

请求报文:
<?xml version="1.0" encoding="utf-8" ?>
<XmlData>
<UserInfo>
    
<User>ZhangSan</User>
    
<PassWord>123456</PassWord>
  
</UserInfo>
  
<Description>
    
<RowID>0</RowID>
    
<KeyValue>123</KeyValue>
    
<QueryBeginTime>20061018 153000</QueryBeginTime>
    
<QueryEndTime>20061019 153000</QueryEndTime>
  
</Description>
</XmlData>

响应报文:

<?xml version="1.0" encoding="utf-8"?>
<XmlData>
  
<Description>
    
<RowID>100</RowID>
  
</Description>
  
<Records>
    
<Record>
      
<Field1>Value1</Field1>
      
<Field2>Value2</Field2>
      
<Field3>Value3</Field3>
      
<Field4>Value4</Field4>
    
</Record>
    
<Record>
      
<Field1>Value1</Field1>
      
<Field2>Value2</Field2>
      
<Field3>Value3</Field3>
      
<Field4>Value4</Field4>
    
</Record>
    
<Record>
      
<Field1>Value1</Field1>
      
<Field2>Value2</Field2>
      
<Field3>Value3</Field3>
      
<Field4>Value4</Field4>
    
</Record>
  
</Records>
</XmlData>

报文说明:

标签名

说明

<XmlData>

报文数据主体

<Description>

报文头部信息

<Records>

记录集合

<Record>

一行记录

<UserInfo>

业务认证的用户信息

<User>

业务用户登录名

<PassWord>

业务用户验证口令

<RowID>

第一次请求的时候,客户端RowID发送0,表示从第0条记录开发返回。服务端根据条件查询,发现结果超过100条记录,则在返回的结果中,RowID的值为99,表示服务端当前的记录位置处在第100条的位置上,后面还会有剩余的记录。客户端检查返回的结果,如果发现RowID大于0,则继续发送请求进行查询。但是,客户端第二次发送请求继续查询的时候,RowID的值要赋值为第一次返回的值,即RowID=99。服务端第二次收到请求的时候,发现RowID99,则从第100条返回结果。以此类推循环调用,直到服务端达到记录的末尾,这时候,服务端在返回的结果中RowID=0.客户端发现服务端RowID=0,终止循环调用。

字典、用户信息、单位信息,因为返回的字段比较少,不受100条记录返回的限制。一次调用,就返回全部的结果。

<KeyValue>

查询时主键的值。这个<KeyValue>和下面的<QueryBeginTime><QueryEndTime>是互斥的。如果在请求的时候提供了主键的值,表示客户端要求服务器按照主键的值查询一条记录。如果客户端提供了主键的值,则服务端将忽略<QueryBeginTime><QueryEndTime>中的值。

字典、用户信息、单位信息,因为没有查询时间范围,所以<KeyValue>即表示字典类型。

<QueryBeginTime>

<QueryEndTime>

查询时时间段范围。<QueryBeginTime>是起始时间,<QueryEndTime>是结束时间。表示客户端要求服务端查询在这个时间范围之内所有变更过的记录(包括新增、修改、删除)。

在外协中,一条记录新增的时候,它的创建时间和最后修改时间是一样的,以后修改记录的时候,创建时间不变,改变的仅仅是最后修改时间。同时,外协系统中删除记录仅仅在“记录是否删除”字段中标记“1”,并不是真正的物理删除记录。

这里的时间指的是记录变更的时间,不是记录中的某个业务时间。如果用户需要按照业务时间来查询数据,则施工系统把外协系统的数据获取到本地进行保存,在施工系统中提供按照业务时间查询的功能。

<QueryBeginTime><QueryEndTime><KeyValue>是互斥的。如果客户端需要按照时间范围来查询,则必须<KeyValue>空。

<Field1>

<Field2>

<Field3>

<Field4>

一行记录中的英文字段名称。实际中,这些标签都是字典的英文名。字段的标签全部是大写。

具体的字段名称请参见提供的数据模型


 

8.2.1   新增一条记录,得到记录的键值

为了简化数据模型的处理,本方案不考虑主从表的并发处理情况。如果存在主从表的数据需要上传,那么,在一个事务中,施工单位首先先上传主表的记录,从反馈信息中获得主表的主键值。然后,把刚刚获得的主表的主键值赋值给从表的对应外键,再上传从表数据,得到从表的主键值。

如果不是主从表,而是单表,则直接上传数据,从反馈信息中得到主键值。

这种情况的优点是:数据和表相关,施工单位可以灵活的控制表之间的关系;同时,数据包中的报文比较简单,容易解析;接口上面比较清晰,与业务的耦合比较低。

缺点是:一个业务涉及的主从表不能在同一个报文中(这个缺点可以通过施工系统灵活的控制表之间关系来解决);一个业务中可能会使用到两个或两个以上的接口,造成业务完整性上面的分离(这种缺点可以通过把业务放在一个事务中来解决);

键值的返回:在调用新增接口之后,外协会按照记录的顺序返回外外协中所生成的键值。   施工单位获得键值之后,可以在本地表中更新记录的主键值。

请求报文:

 

<?xml version="1.0" encoding="utf-8"?>
<XmlData>
  
<UserInfo>
    
<User>ZhangSan</User>
    
<PassWord>123456</PassWord>
  
</UserInfo>
  
<Description>
    
<Note>开工报告</Note>
 
</Description>
  
<Records>
    
<Record>
      
<KeyField></KeyField>
      
<NormalField1>Value1</NormalField1>
      
<NormalField2>Value2</NormalField2>
      
<NormalField3>Value3</NormalField3>
      
<NormalField4>Value4</NormalField4>
    
</Record>
    
<Record>
      
<KeyField></KeyField>
      
<NormalField1>Value1</NormalField1>
      
<NormalField2>Value2</NormalField2>
      
<NormalField3>Value3</NormalField3>
      
<NormalField4>Value4</NormalField4>
    
</Record>
  
</Records>
</XmlData>

响应报文:

<?xml version="1.0" encoding="utf-8"?>
<XmlData>
  
<Description>
    
<Result>成功</Result>  <!--如果失败,则<Result>里面内容是:失败:(错误原因)-->
  
</Description>
  
<Records>
    
<Record>
      
<KeyField>Value1</KeyField>      
    
</Record>
    
<Record>
      
<KeyField>Value2</KeyField>      
    
</Record>
  
</Records>
</XmlData>

报文说明:

标签名

说明

<XmlData>

报文数据主体

<Description>

报文头部信息

<Records>

记录集合

<Record>

一行记录

<UserInfo>

业务认证的用户信息

<User>

业务用户登录名

<PassWord>

业务用户验证口令

<Note>

业务的简单描述。比如:开工报告、施工组织方案 等

请求中的<KeyField>

一行记录中的主键字段。在新增的时候,施工系统所给的主键字段内容为空。外协系统中根据主键字段内容为空,认为这是一条新增的记录

响应中的<KeyField>

一行记录中的主键字段。外协系统返回的主键值。这里的主键值和施工系统发送的记录的顺序是一一对应的。

<Result>

反馈报文中的保存成功与否信息。

如果保存成功,则信息是“成功”

如果保存失败,则信息是“失败:(后面是错误的详细信息)