• 基于 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 就可以了.

    具体可以参考下这个:

    http://blog.somecreativity.com/2008/04/04/ioexception-when-trying-to-read-a-file-using-filereadallbytes-function/

    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 : 搞了这个工具之后, 我家”老板”都不怎么疯狂传产品了, 这几天没白天没黑夜的看起电视来了!

     
     
     
  • 相关阅读:
    SAP 锁对象
    smartforms取消word为默认编辑器
    abap 配置 zconfig
    Ant步步为营(1)解压本地的zip包
    点击页面出现文字动画
    js简单实现累加
    github发布线上项目
    jsonp的实现
    js操作class
    js开发实用技巧
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3305002.html
Copyright © 2020-2023  润新知