基于 Aliexpress API 的小程序 : 批量 Copy 产品到不同的店铺
第一个基于 Aliexpress API 的小程序 : 批量 Copy 产品到不同的店铺
还没来得及用 API 重写软件, 先写个小程序来缓解一下手工压力: 批量Copy 产品到不同的店铺.
开网店 ,无论是在阿里上,还是在eBay 上, 大部分小卖家都是一人操作好几个店, 七姑八姨的身份证都找来开店,只为了让订单多点. 相比那些靠拍马屁拍的厚颜无耻而上位的, 这些人更值得成为我的榜样: 虽然辛苦,但都是血汗钱. 拍马屁来的轻松, 但终究是个屁, 保不准哪天马拍你一手”史”!
不扯了,扯多了森森的蛋疼.
由于这个小工具具有商业价值,拿出去肯定有人愿意买,所以不提供下载,本文只聊聊一些其它的.
先看看丑陋的界面:

本打算用WPF 写界面(要现学) , 用Prism 搞了半天(一下午), 搞的心烦意乱(关键是不会), 干脆用 WinForm 写算了, 虽然界面丑点,但是写着顺手.
因为是 Copy 产品到其它店铺,所以第一步是获取到源产品的数据, 在原封不到的发送其它店铺里就行了.
这里的原封不动,其实还是要动一动的,
A图片, 如果原封不动的话,会被认为是盗图.所以要先下载, 在上传,然后改为最终的图片地址. 产品主图/详细说明里的图片用 api.uploadImage 方法, 但是SKU属性里的图片只能用 api.uploadTempImage 方法.
B 产品组和运费模板要改一改.
说到图片, 顺便说一下 File.ReadAllBytes 这个方法. 如果多个线程同时读取同一文件的话, 会报 IOException
原因是 ReadAllBytes 的实现里:
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)
用的是 FileShare.Read , 改为 FileShare.ReadWrite 就可以了.
具体可以参考下这个:
Newtonsoft.Json 序列化的问题.
api.postAeProduct 方法的参数 aeopAeProductPropertys , aeopAeProductSKUs 需要序列化为 Json 数据.
aeopAeProductSKUs 内的 price , 一开始我定义为 double, 序列化结果形式是这样的:
“skuPrice”:xxx
即值没有被引号括起来, 执行后, 直接返回 500 服务器错误, 换为 decimal, float ,结果一样, 只有用:
“skuPrice”:”xxx” 才没有问题, 也就是说要定义为字符串才行.
搜索了一下, 没有找到序列化时, 强制加引号的方法.
API 内一些奇形怪状的错误, Ali 提供的简单的 API 说明, 根本就不足以找出问题在哪里,基本靠猜测来解决.
1,
{"error_code":"07004013","error_message":"Yes(有):Id=350216 :Please select the Retail Package from the drop-down menu","exception":"Yes(有):Id=350216 :Please select the Retail Package from the drop-down menu"}
不懂为什么, 有时会出现.
2, 获取到的产品信息里 bulkOrder 和 bulkDiscount 都为0, 原封不动在的传过去, 居然提示:
{"error_code":"07001014","error_message":"bulkOrder:Please enter numbers between 1 and 99 (no decimal points)","exception":"bulkOrder:Please enter numbers between 1 and 99 (no decimal points)"}
而且是偶尔发生, 解决办法是当 bulkOrder 为0时,将其值置为 null , bulkDiscount 同理.
还有一些莫名其妙的错误,没有记录下来.
统一处理 DisplayName , Description
类的属性太多, 遂一写 [DisplayName(“xxx”)] 都觉得可怕, 想想都手疼.
在我的博文: MVC3 项目总结 里提到过DisplayNameMetadataProvider 这个东西, 它的作用是集中管理 DisplayName , 好处很显然易见.
这里我在提供一个, 用 TypeDescriptorProvider 来注册属性的 DisplayName 和 Description

public class DisplayPropertyTypeDescriptionProvider : TypeDescriptionProvider {
private ICustomTypeDescriptor td;
private ResourceManager resManager;
public DisplayPropertyTypeDescriptionProvider(Type type, ResourceManager resManager)
: this(TypeDescriptor.GetProvider(type) , resManager) {
}
public DisplayPropertyTypeDescriptionProvider(TypeDescriptionProvider parent, ResourceManager resManager)
: base(parent) {
this.resManager = resManager;
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType , object instance) {
if(td == null) {
td = base.GetTypeDescriptor(objectType , instance);
td = new DisplayPropertyCustomTypeDescriptor(td, resManager);
}
return td;
}
}


1 public class DisplayPropertyCustomTypeDescriptor : CustomTypeDescriptor {
2
3 public ResourceManager ResManager { get; set; }
4
5 public DisplayPropertyCustomTypeDescriptor(ICustomTypeDescriptor parent, ResourceManager resManager)
6 : base(parent) {
7
8 this.ResManager = resManager;
9 }
10
11
12 public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) {
13 var props = base.GetProperties().Cast<PropertyDescriptor>();
14 List<PropertyDescriptor> dpps = new List<PropertyDescriptor>();
15 var ns = base.GetClassName().Replace(".", "");
16 foreach(var prop in props) {
17 var attrs = prop.Attributes.Cast<Attribute>().ToList();
18 var dKey = string.Format("{0}_{1}_DisplayName", ns, prop.Name);
19 var displayName = this.ResManager.GetString(dKey);
20 if(!string.IsNullOrWhiteSpace(displayName)) {
21 attrs.Add(new DisplayNameAttribute(displayName));
22 }
23 var descKey = string.Format("{0}_{1}_Description", ns, prop.Name);
24 var desc = this.ResManager.GetString(descKey);
25 if(!string.IsNullOrWhiteSpace(descKey)) {
26 attrs.Add(new DescriptionAttribute(desc));
27 }
28
29 dpps.Add(TypeDescriptor.CreateProperty(prop.ComponentType, prop, attrs.ToArray()));
30 }
31 return new PropertyDescriptorCollection(dpps.ToArray());
32 }
33 }


1 public class TypeDescriptorHelper {
2
3 public static void SetDisplayAttributFromResource<T>(ResourceManager resManager) where T : class {
4 TypeDescriptor.AddProvider(new DisplayPropertyTypeDescriptionProvider(typeof(T), resManager), typeof(T));
5 }
6
7 public static void AutoSetDisplayAttributeFromResource(string assemblyName, ResourceManager resManager) {
8 var asms = Assembly.GetCallingAssembly()
9 .GetReferencedAssemblies()
10 .Where(a => a.Name.Equals(assemblyName));
11
12 foreach(var a in asms) {
13 var asm = Assembly.Load(a);
14 var types = asm.GetTypes().Where(t=>t.IsClass && t.IsPublic && !t.IsAbstract);
15 foreach(var t in types) {
16 TypeDescriptor.AddProvider(new DisplayPropertyTypeDescriptionProvider(t, resManager), t);
17 }
18 }
19 }
20 …

用法:
1 //TypeDescriptorHelper.SetDisplayAttributFromResource<OrderQueryList>(AsNum.Aliexpress.API.Res.Resource.ResourceManager);
2
3 TypeDescriptorHelper.AutoSetDisplayAttributeFromResource("AsNum.Aliexpress.API", AsNum.Aliexpress.API.Res.Resource.ResourceManager);
另外提供一个工具, 编辑 ResX 文件的, 注意是设计时,不是运行时:

下载:
http://files.cnblogs.com/xling/DisplayNameEditor.7z
顺便提一下 Asembly 的方法
ReflectionOnlyLoadFrom
LoadFile
ReflectionOnlyLoadFrom
都会去加载依赖项, 如果找不到依赖项就会报错, 虽然可以通过
AppDomain.CurrentDomain.AssemblyResolve
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve
等对应的事件来处理依赖项的加载, 但是只为了读取,而不是为了执行, 完全没有必要加载依赖项, 遂个试后, 发现 Asembly.LoadFrom 是不会去管依赖项的.
PS : 搞了这个工具之后, 我家”老板”都不怎么疯狂传产品了, 这几天没白天没黑夜的看起电视来了!
基于 Aliexpress API 的小程序 : 批量 Copy 产品到不同的店铺的更多相关文章
- 基于C语言libvirt API简单小程序
libvirt API简单小程序 1.程序代码如下 #include<stdio.h> #include<libvirt/libvirt.h> int getDomainInf ...
- 微信小程序批量上传图片 All In One
微信小程序批量上传图片 All In One open-data https://developers.weixin.qq.com/miniprogram/dev/component/open-dat ...
- 教你轻松构建基于 Serverless 架构的小程序
前言 自 2017 年第一批小程序上线以来,越来越多的移动端应用以小程序的形式呈现.小程序触手可及.用完即走的优点,大大降低了用户的使用负担,也使小程序得到了广泛的传播.在阿里巴巴,小程序也被广泛地应 ...
- 你也可以玩转Skype -- 基于Skype API开发外壳程序入门
原文:你也可以玩转Skype -- 基于Skype API开发外壳程序入门 Skype是目前这个星球上最厉害的IM+VOIP软件,Skype现在已经改变了全球2.8亿人的生活方式.你,值得拥有! :) ...
- 基于AliOS的车载小程序
4月16日上海国际车展首日,阿里巴巴表示正在研发基于AliOS的车载小程序.同时还展出AI HUD.AI驾驶舱等最新技术,AliOS表示正在构建一个可持续发展的整合平台. 阿里方面表示,作为小程序在车 ...
- windows下使用pycharm开发基于ansible api的python程序
Window下python安装ansible,基于ansible api开发python程序 在windows下使用pycharm开发基于ansible api的python程序时,发现ansible ...
- 基于mpvue搭建微信小程序
mpvue是美团开源的一套语法,语法与vue.js一致,快速开发小程序的前端框架.框架基于vue.js核心,修改了vue.js的runtime和compiler实现,使用此框架,开发者可以完全使用vu ...
- 封装简单的API——微信小程序
前几天自己琢磨微信小程序的基本开发,里边用到的技术包括WebAPI,也就是方法的封装. 当然也可以用ASP.NET MVC WCF来写接口.更简单应该就是 WinForm 简单易部署. 这里用的是 2 ...
- Django基于JWT实现微信小程序的登录和鉴权
什么是JWT? JWT,全称Json Web Token,用于作为JSON对象在各方之间安全地传输信息.该信息可以被验证和信任,因为它是数字签名的. 与Session的区别 一.Session是在服务 ...
随机推荐
- java_model_dao_自动生成_generator-mybatis-generator-1.3.2 基于maven插件
用mybatis原因很简单,易用,性能.是介于jdbc和hibernate之间的一个完美方案. 很简单: 1:配置pom <project xmlns="http://maven.ap ...
- 使用WebBrowser控件时在网页元素上绘制文本或其他自定义内容
原文:使用WebBrowser控件时在网页元素上绘制文本或其他自定义内容 第一次在CNBlogs上发Post是提出一个有关使用WebBrowser控件时对SELECT网页元素操作的疑惑,这个问题至今也 ...
- WebBrowser一点心得,如果在Javascript和Winform代码之间实现双向通信
原文:WebBrowser一点心得,如果在Javascript和Winform代码之间实现双向通信 最近工作需要,学习了一下winform内嵌webbrowser控件,然后与htm页面中的javasc ...
- 微信公众平台接口,asp.net实现
原文:微信公众平台接口,asp.net实现 我为自己的笑话网开发了一个微信公众平台的接口,在这里分享给大家,希望能对朋友们有帮助,如果有什么地方写的不好,好请大家指点! 首先是要进行认证,认证的时候, ...
- c++ Constructor FAQ 继续
这一章的时候,才明白什么是编译器的声明只会是一个默认的构造.这也解释了为什么同一似乎没有意义的界定,如果不还声明默认构造函数的意义. Q:当编译器隐含定义了一个默认的构造函数. 答: 一个隐式声明的默 ...
- 设计模式---订阅发布模式(Subscribe/Publish)
设计模式---订阅发布模式(Subscribe/Publish) 订阅发布模式定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象.这个主题对象在自身状态变化时,会通知所有订阅者对象,使 ...
- Dotfuscator自定义规则中的元素选择
Dotfuscator是专业的.NET程序代码保护软件.是支持规则自定义的,你可以对重命名.程序控制流.字符串加密等等功能自定义规则.在进行规则自定义过程中,可以通过元素的不同选择,满足自己的程序需要 ...
- jQuery小例
jQuery小例子 使用前,请先引用jquery 1,map遍历数组 2,jQuery对象与DOM对象才做元素和互转 3,prevall与nextall 4,jquery版的星星评分控件 5,jq ...
- IIS 5.x/6.0/7.0 和 ASP.NET
原文:IIS 5.x/6.0/7.0 和 ASP.NET 本文主要介绍 3 个主要的 IIS 版本各自对 Web 请求的不同处理方式. 本文内容 IIS 5.x 和 ASP.NET IIS 6.0 和 ...
- Andorid类似Fragment更换布置方法
public void replaceRightView(View v) { int f = LinearLayout.LayoutParams.MATCH_PARENT; LinearLayout. ...