2023年2月

继续前面一篇随笔《
淘宝API开发系列---淘宝API的测试及使用
》,来继续介绍淘宝API的具体代码开发部分,上篇主要是介绍淘宝SDK开发的一些流程及必备的信息,以及掌握如何学会利用API文档、淘宝API测试工具来获取我们所需的数据,其中我一般倾向于获取Json数据,然后进行分析,其中Json数据可以通过JsonView工具(
http://jsonviewer.codeplex.com/
)进行格式化显示,方便我们了解和区分各个属性的信息。另外淘宝的SDK里面,封装了很多对象,我们通过数据就可以获取到相关的信息了,不过注意的就是,我们每个接口调用,都要传入Fields的属性,如果我们没有指定相应的属性字段,那么接口返回的数据,就没有这项的,淘宝SDK的对象属性就会为空。

通过以上的工具,我们就能可视化属性的信息了,对接口数据的理解就更加清晰,首先我们来看看我测试例子的运行效果如下所示。

其中上面例子的代码如下所示。

private
void
TestUserGet()
{
//
单独用户的信息


Console.WriteLine(
"
单独用户的信息
"
);
UserGetRequest req =
new
UserGetRequest();
//
实例化具体API对应的Request类


req.Fields =
"
user_id,nick,created,buyer_credit,type,sex
"
;
req.Nick =
"
wuhuacong
"
;
UserGetResponse rsp = myclient.Execute(req);
//
执行API请求并将该类转换为response对象


Console.WriteLine(rsp.Body);
if
(rsp.User !=
null
)
{
//
Console.WriteLine(rsp.User.Nick);

//
Console.WriteLine(ObjectToString(rsp.User));


List<User> list =
new
List<User>();
list.Add(rsp.User);
this
.winGridView1.DisplayColumns = req.Fields.Replace(
"
_
"
,
""
);
//
对应字段的属性没有“_”字符的


this
.winGridView1.DataSource = list;
}
}

private
void
TestItemGet()
{
//
单独商品的信息


Console.WriteLine(
"
单独商品的信息
"
);
ItemGetRequest req =
new
ItemGetRequest();
req.Fields =
"
num_iid,title,nick,pic_path,cid,price,type,location.city,delist_time,post_fee
"
;
req.NumIid =
3838293428L
;
ItemGetResponse itemRsp = myclient.Execute(req);
if
(itemRsp !=
null
&& itemRsp.Item !=
null
)
{
//
Console.WriteLine(itemRsp.Item.Nick);

//
Console.WriteLine(ObjectToString(itemRsp.Item));


List<Item> list =
new
List<Item>();
list.Add(itemRsp.Item);
this
.winGridView1.DisplayColumns = req.Fields.Replace(
"
_
"
,
""
);
//
对应字段的属性没有“_”字符的


this
.winGridView1.DataSource = list;
}
}

private
void
TestItemSearch()
{
//
查询商品信息(不含类别)


Console.WriteLine(
"
查询商品信息(不含类别)
"
);
ItemsGetRequest req =
new
ItemsGetRequest();
req.Fields =
"
num_iid,title,nick,pic_url,cid,price,type,delist_time,post_fee,score,volume
"
;
//
,location.city,location.state";


req.Q =
"
笔记本
"
;
//
itemReq.Cid = "14";


req.OrderBy =
"
volume:desc
"
;
req.PageNo =
1
;
req.PageSize =
40
;

//
显示列表信息


ItemsGetResponse itemRsp = myclient.Execute(req);
if
(itemRsp !=
null
)
{
//
Console.WriteLine(itemRsp.TotalResults);

//
foreach (Item item in itemRsp.Items)

//
{

//
Console.WriteLine(ObjectToString(item));

//
}


this
.winGridView1.DisplayColumns = req.Fields.Replace(
"
_
"
,
""
);
//
对应字段的属性没有“_”字符的


this
.winGridView1.DataSource = itemRsp.Items;
}

}

对于需要获取用户私密信息,如买入卖出等重要信息,还需要获取用户的SessionKey的,我们可以通过下面接口函数,弹出登录窗口,然后登录后,定位到对应的App应用页面,然后页面加载的时候,获取到对应的SessionKey、

///

<summary>


///
判断是否顺利获取SessionKey

///

</summary>


///

<returns></returns>


private
string
GetAuthorizeCode(
string
appKey)
{
string
authorizeCode =
""
;
FrmAuthorized dlg =
new
FrmAuthorized();
dlg.AppKey = appkey;
if
(dlg.ShowDialog() == DialogResult.OK)
{
authorizeCode = dlg.AuthrizeCode;
}
if
(
string
.IsNullOrEmpty(authorizeCode))
return
null
;

string
sessionKeyUrl =
string
.Format(TOP_AUTH_URL, authorizeCode);
HttpHelper helper =
new
HttpHelper();
string
html = helper.GetHtml(sessionKeyUrl);
//
格式

//
top_appkey=1142&top_parameters=xxx&top_session=xxx&top_sign=xxx&encode=utf-8


string
reg =
"
.*?&top_session=(?<session>.*?)&top_sign
"
;
string
sessionKey = CRegex.GetText(html, reg,
1
);
return
sessionKey;

}

最后我们看看其中获取已买记录的接口实现如下所示。

private
void
TestBuyInfo()
{
if
(
string
.IsNullOrEmpty(sessionKey))
{
sessionKey = GetAuthorizeCode(
this
.appkey);
}

//
买入交易


Console.WriteLine(
"
买入交易
"
);
TradesBoughtGetRequest req =
new
TradesBoughtGetRequest();
//
req.Fields = "tid,title,price,type,num_iid,seller_nick,buyer_nick,status";


req.Fields =
"
tid,title,price,type,num_iid,seller_nick,buyer_nick,status,receiver_state,receiver_city,receiver_district,receiver_address
"
;
req.PageNo =
1L
;
req.PageSize =
40L
;
TradesBoughtGetResponse rsp = myclient.Execute(req, sessionKey);
if
(rsp !=
null
)
{
//
Console.WriteLine(rsp.Trades.Count);

//
if (rsp.Trades.Count > 0)

//
{

//
foreach (Trade item in rsp.Trades)

//
{

//
Console.WriteLine(ObjectToString(item));

//
}

//
}


this
.winGridView1.DisplayColumns = req.Fields.Replace(
"
_
"
,
""
);
//
对应字段的属性没有“_”字符的


this
.winGridView1.DataSource = rsp.Trades;
}

}

以上利用了我的Winform分页控件进行数据展示,因此代码要简化一些,当然,也可以用DataGridView来进行数据显示,不过代码方面可能要多一些。

在我的Winform开发框架或者WCF开发框架中,均提供多种界面样式选择,其中有一种就是界面效果比较好的DevExpress控件界面样式,但是我们一般在开发环境中使用,安装包的大小都很大,100多M,那么我们在部署到客户端应用的时候,该如何操作呢?

我们分析DevExpress的特殊补丁发现,他们是一个DOS批处理文件,主要是创建相应的目录,然后把DevExpress的三个特殊文件复制到对应的目录中,就可以实现DevExpress的正常使用了。那个DOS批处理文件内容如下所示。

一般我们部署客户端应用的时候,都会制作一个安装包,我们只需要自定义安装操作中正确执行这些DOS命令即可。

但是,在实际安装过程中,由于执行Register.bat文件的时候,gacutil.exe会以System目录作为起始目录,那么就会导致以上操作失败,找不到对应的目录,因为默认System目录下面没有gacUtil的文件,更没有要拷贝的DevExpress.Data.v11.1.dll等三个dll文件。那么我们应该如何操作才能正确执行DOS命令,完成顺利部署DevExpress的几个特殊的dll文件呢?

1、首先我们创建一个安装工程,并添加以上的特殊注册文件到安装包中,然后修改Register.bat文件的内容,增加一个路径变量"{INSTALL_PATH}",该路径变量会在安装的时候被替换为具体的路径名称。修改后的文件如下所示。

@echo off

set path="{INSTALL_PATH}"

gacutil -u "{INSTALL_PATH}\DevExpress.Data.v11.1"
mkdir %windir%\assembly\GAC_MSIL\DevExpress.Data.v11.1\11.1.6.0__b88d1754d700e49a
copy "{INSTALL_PATH}\DevExpress.Data.v11.1.dll" %windir%\assembly\GAC_MSIL\DevExpress.Data.v11.1\11.1.6.0__b88d1754d700e49a

gacutil -u "{INSTALL_PATH}\DevExpress.Utils.v11.1"
mkdir %windir%\assembly\GAC_MSIL\DevExpress.Utils.v11.1\11.1.6.0__b88d1754d700e49a
copy "{INSTALL_PATH}\DevExpress.Utils.v11.1.dll" %windir%\assembly\GAC_MSIL\DevExpress.Utils.v11.1\11.1.6.0__b88d1754d700e49a

gacutil -u "{INSTALL_PATH}\DevExpress.CodeRush.Common"
mkdir %windir%\assembly\GAC_MSIL\DevExpress.CodeRush.Common\11.1.6.0__35c9f04b7764aa3d
copy "{INSTALL_PATH}\DevExpress.CodeRush.Common.dll" %windir%\assembly\GAC_MSIL\DevExpress.CodeRush.Common\11.1.6.0__35c9f04b7764aa3d
REMcopy "{INSTALL_PATH}\DevExpress.CodeRush.Common.dll" "C:\Program Files\DevExpress 2011.1\IDETools\System\DXCore\BIN\DevExpress.CodeRush.Common.dll"
if "[%ProgramFiles(x86)%]" == "[]" (copy "{INSTALL_PATH}\DevExpress.CodeRush.Common.dll" "%ProgramFiles%\DevExpress 2011.1\IDETools\System\DXCore\BIN\DevExpress.CodeRush.Common.dll") else (copy "{INSTALL_PATH}\DevExpress.CodeRush.Common.dll" "%ProgramFiles(x86)%\DevExpress 2011.1\IDETools\System\DXCore\BIN\DevExpress.CodeRush.Common.dll")

echo 'OK'
pause

2、创建一个安装包后,并增加一个自定义安装项目,命名为InstallerAction项目,然后创建一个“安装程序类”,如下所示

项目工程如下:

3)然后我们修改“安装程序类”的代码,覆盖Install函数,主要是用来替换Register.bat批处理文件的路径变量"{INSTALL_PATH}",如下代码所示

    [RunInstaller(true)]
public partial class InstallAction : Installer
{
public InstallAction()
{
InitializeComponent();
}

public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);

try
{
string physicalRoot = this.Context.Parameters["targetdir"]; //安装物理路径

try
{
//由于执行Register.bat文件的时候,gacutil.exe会以System目录作为起始目录,
//因此在Bat文件中设置一个参数,安装的时候替换为具体的目录
string Register = Path.Combine(physicalRoot, "Register.bat");
ReplaceName(Register, "{INSTALL_PATH}", physicalRoot);
}
catch { }

string registerFile = Path.Combine(physicalRoot, "Register.bat");
RunDos(registerFile, "", false);
}
catch (Exception ex)
{
WriteLog(ex.ToString());
}
}

/// <summary>
///在运行脚本之前把脚本中的指定名称替换成新的名词
/// </summary>
/// <param name="filePath">脚本文件名</param>
/// <param name="oldDBName">原有的名称</param>
/// <param name="newDBName">新的名称</param>
private void ReplaceName(string filePath, string oldName, string newName)
{
if (newName.CompareTo(oldName) != 0)
{
string fileText = string.Empty;
using (StreamReader streamReader = new StreamReader(filePath, Encoding.Default))
{
fileText = streamReader.ReadToEnd();
fileText = fileText.Replace(oldName, newName);
}

using (StreamWriter streamWriter = new StreamWriter(filePath, false, Encoding.Default))
{
streamWriter.Write(fileText);
}
}
}

/// <summary>
///后台执行DOS文件
/// </summary>
/// <param name="fileName">文件名(包含路径)</param>
/// <param name="argument">运行参数</param>
/// <param name="hidden">是否隐藏窗口</param>
private void RunDos(string fileName, string argument, bool hidden)
{
Process process = new Process();
process.EnableRaisingEvents = false;
process.StartInfo.FileName = string.Format("\"{0}\"", fileName);
process.StartInfo.Arguments = argument;
if (hidden)
{
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
}
else
{
process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
}
process.Start();
process.WaitForExit();
}

4、接着在安装包的自定义安装视图中,把刚才自定义安装项目关联到安装包中,如下所示。

并修改主输出来自InstallAction的属性项目如下所示

5、编译运行程序,我们发现DOS运行的效果如下所示,说明DOS命令正常运行,并复制了文件。

6、运行程序,DevExpress界面正常出来,并没有弹出额外的窗体,这真是我们需要的,示例界面如下所示。



Visio二次开发开发过程中,模具制作也是其中一项有比较细致,技术含量高的工作来的,要做好一个不错的模具,需要了解不少Visio的开发资料以及制作技巧,由于Visio二次开发的资料比较少,一般人很少能够系统了解整个模具绘制的开发过程,本人做Visio二次开发多年,自己也亲自制作过不少模具,因此沉下来,分享一下制作模具的整个过程。由于模具制作可能涉及的内容比较多,因此由简入繁,一步步来进行介绍。

我们知道,Visio安装后,本身带有很多类型的模具,以供我们对各种领域,各种场景的图形进行快速绘制,其中有建筑设计方面、室内设计、软件流程图、UML图、电气方面等等,从中我们可以知道Visio其实很强大,凭借其易用方便、以及丰富的图形、和Office一脉相承的特点,在应用上也是有很大的市场比例的。


言归正传,我们来介绍一下,如何创建各种符合我们需要的模具,首先我把它分为几个模块来介绍,先介绍简单Visio模具的绘制,然后介绍复杂一点的操作,如连接点、事件、数据、文本变形、组合图形等等,后面的序列继续介绍。

本序列操作以Visio2010版本进行介绍,其他版本操作请找到对应的功能模块,一样可以进行相关的操作。

1)简单的Visio模具绘制

首先我们打开一个空白的Visio文件,可选公制或者美制均可,然后创建一个空白模具文档,用来放置我们下一步创建的模具形状的。一个模具可以包含一组模具(又叫Master),它是类似我们的图形模板,我们拖动到Visio图纸文档后,它就会创建一个个图形实例了,这样是为了实现快速绘图的操作的。

然后我们在空白的Visio文档上绘制一个矩形,使用Visio的绘图工具,并设置矩形的填充透明度为100%,如下图所示。

然后把整个图形拖动到刚才新建的模具文档里面,就创建了一个简单的模具了,我们把它命名为透明矩形,并把模具文档保存为简单模具,那么就会如下所示。

拖动过去模具后,我们发现,Visio图纸上的图形也不见了,但我们已经完成了一个简单模具的创建了。以上就是我们简单模具的创建工作流程。

2)为模具图形添加连接点

我们如果有经常使用Visio来绘制图形的经历的话,就对连接点不会陌生,Visio图形一般都有一些连接点,以供连接,一旦连线靠近,两个连接点就会出现红色,标识粘附连接一起的,如下图所示。

那么我们应该如何添加上去呢,有两种方式,一种方式是通过在ShapeSheet的对应表格中添加连接点的经纬度,如下所示

另外一种方式是通过图形方式,可视化放置控制点,实现图形连接点的绘制,虽然这种方式最终会生成上图的数据,不过这种方式推荐使用,方便直观!

图形可视化放置连接点的操作如下,先把图形放大到合适的比例,以便我们准确方便绘制连接点,并选择连接点的操作,然后按住Ctrl键,单击把连接点放到图形的合适位置,如下图所示。

以上就是我在先前绘制的图形基础上增加连接点(圈中的四个点),可以看到有四个小×那样的符号,就是连接点了,然后我们再次把图形拖到模具里面,重新命名为新的模具图形即可。

以上即是模具的简单入门操作,具体复杂的模具创建,下一篇随笔继续介绍

Visio二次开发开发过程中,模具制作也是其中一项有比较细致,技术含量高的工作来的,要做好一个不错的模具,需要了解不少Visio的开发资料以及制作技巧。

在前面一篇《
C#进行Visio二次开发之Visio模具制作(1)
》中介绍了简单模具的制作,那个模具绘制过程主要是利用Visio本身的绘制图形进行操作,下面我们来继续介绍模具的其他制作操作。例如如何利用铅笔工具绘制不规则形状、如何组合形状、如何创建容器模具等操作,组合形状是作为一个整体进行操作的,如果不需要选定子模具,需要设置更多的行为特点,容器也一样,之所以称为容器,就是图形放入的时候,他们为一个整体,一起拖动位置,并且图形拖入容器的时候,容器有明显的提示的特点,这些在本文中介绍。

1)利用铅笔工具绘制不规则图形

我们知道,Visio本身只是提供很少的图形用来绘制,如下所示只包含矩形、椭圆、折线等,我们要实现更多的图形绘制,则可以利用线条、铅笔等工具来绘制各种不规则图形。

要利用铅笔绘制图形,先选定铅笔,然后在图纸上绘制图形,按住Ctrl键可以绘制直线,如下所示绘制两个不规则形状。

这两个图形,每个图形都是一个闭合的不规则图形,他们各条边是一个整体,是图形不可分割的一部分,我们进一步在ShapeSheet分析可以看到,他是一个Geometry的,通过MoveTo/LineTo进行绘制出来的。

这个说明它(以上任一图形)本身是一个图形,不是组合的,这个非常重要。

2)组合图形

一般为了绘出丰富表现显示的图形,多数都是组合起来的,组合图形,处理的好,其实和单独的形状看起来很显示,可以一起拖动,一起缩放,甚至一般情况下你还不知道他是组合的,因为它设置了不可选择子图形。

下面我们把上图几个图形组合起来,先把矩形、两个不规则图形放置好位置后,选定这几个图形,然后选择组合,那么就可以看到图形组合起来了。

然后我们拖动组合图形,发现整个一起拖动了,这个已经作为一个组合整体了。不过细心的你可能发现,如果选择组合形状的子形状,还是可以脱离出来的,这个就有点不太好了(一般来说,我们不希望给拆开的)。

不过不要着急,我们可以通过设置组合形状的行为特点,可以不让操作者选定子模型即可,如下所示。

设置选定为“仅限组合”这样,那么整个图形就只有组合形状可以选到,子图形不会选中,也就不会发生刚才所说的脱离组合形状的问题了。

设置后,再次拖动图形,真的不会发生了,这个真是我们所希望组合形状的特点。

3)容器图形的设置

先以矩形作为容器,第一步要先设置矩形的形状手柄等不可选择,如下所示。注意,去掉显示形状手柄、控制手柄是为了,形状选择的时候,选择不到该组合框,但是能够选择组合后的组合框(是不是很拗口)。

反正目的就是只操作该组合后的形状。

然后选定图形,进行组合(一个图形也可以组合的哦),然后设置图形的组合设置如下所示。其中接收放下的形状及放下时将形状添加到组合这些设置必选,否则起不到一起拖动组合,作为容器必备的特性了。

这样设置后,作为组合方面的容器,已经设置好了,不过,我们的其他要添加到这个容器里面的形状,也还需要修改形状组合特性为,放下时将形状添加到组合才起作用的哦。

这样两方面设置好后,拖动图形到组合容器里面,就会发现,组合容器的边界明显加粗并增加边框提示,很直观哦,如下所示。

本文主要介绍这么多,主要是为了告诉大家,如何实现更加复杂图形的绘制及设置,以期达到我们设置图形的目的。其中组合形状的这些特性很隐秘,官方没有很清晰的操作指引,即使是英文的,也很少系统介绍模具的具体开发过程,需要大家在实际操作过程中逐步掌握,或者寻求有经验的人士进行指引,这样才不会出现,为了实现一个想要的功能,找了N多资料,试了很多门道,一直上手不了。

下一篇继续结合ShapeSheet进行介绍一些复杂的操作。在模具的开发过程中,ShapeSheet里面的各种内容,是必须要理解掌握的,有时候我们需要调整或者增加一些特殊的东西,就需要直接操作里面的属性数据,如果不熟悉,可能很难实现一些特殊的操作。

Oracle中对数据对象和数据的管理,无疑都是使用PL/SQL Developer来进行管理,该工具也提供给我们很多方便、快捷的操作,使得我们不再为Oracle本身丑陋、难用的UI而抱怨。由于我们一般都是建建表、查查数据的操作居多,较少会考虑系统的整个Oracle的完整备份操作。但是在我们一些发布操作中,我们必须考虑如何把Oracle的对象、表数据导出到Sql脚本中,并且把创建Oracle表空间、创建Oracle数据库的操作也放到脚本中,这样我们就方便快速还原或者部署Oracle数据库到新的机器上了。

本文主要介绍如何结合Sql脚本和PL/SQL Developer工具来实现创建表空间、创建数据库、备份数据库、数据导出等操作,然后实现Oracle对象创建、导入数据等操作,方便我们快速了解、创建所需要的部署Sql脚本和数据库操作。

1、 准备数据库创建脚本

create tablespace whc_tbs datafile 'E:\oracle\oradata\whcdb\whc.dbf' size 100M;
--DROP TABLESPACE whc_tbs INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS;
create user whc identified by whc default tablespace whc_tbs;

grant connect,resource to whc; 
grant dba to whc;
--Revoke dba from whc;

其中--的注释语句是删除命名空间和移除DBA用户的权限的。

以上这些脚本是为了我们在新建一个表空间和Oracle数据库的时候使用的。

其中E:\oracle\oradata\whcdb\whc.dbf路径是我们要存放数据库数据的位置,因此要保证该路径有足够的空间和足够的访问权限,否则会失败的。

2、 导出数据库对象

在PL/SQL Developer的菜单Tools=>Export User Objects中出来一个对话框界面,然后框选要到处的Oracle 数据库对象,包括表,序列,存储过程,视图,函数等等,并指定导出的文件名称,如下操作。

3)导出表数据

导出表结构和其他对象后,我们下一步就是把数据库的表数据进行导出,PL/SQL Developer工具支持导出数据到PL/SQL数据库脚本中,如下所示。这样导出的脚本,我们可以在PL/SQL Developer工具导入或者自己通过Sql plus 工具进行导入。

至此,我们就已经完成了三种脚本了,包括创建数据库空间和数据库脚本、创建Oracle数据库对象(表、存储过程、视图、序列等)、创建导入数据的脚本,这样三种合一,就是一个完整的数据库了。最后一步就是我们如何导入数据库对象和数据的问题了。

4)导入数据库对象及数据

导入数据库对象和数据的操作一样,都可以通过Import Tables操作实现,我们指定刚才上面两步创建的数据库脚本,执行即可再新的环境中创建数据库对象和数据库数据了。如下所示。

执行的数据界面如下所示。

完成上面几个步骤,我们在新的数据库环境中,就具备了所有的东西了,顺利完成整个Oracle数据库对象及数据的迁移工作。

以上值得注意的是,我们导出Oracle对象和数据的时候,默认还是原来Oracle数据库的表空间和数据库用户名称的,如果我们想要在新的数据库服务器中指定不同的表空间和数据库用户对象,那么我们就需要替换生成的sql脚本,并在第一步指定合理的表空间和数据库用户。

如果是Linux平台下的数据库服务器,第一步操作也是差不多的,就是指定表空间的路径名稍微不同,其他操作并无差异。