diff --git a/AllBase/BaseClass.cs b/AllBase/BaseClass.cs index b581114..b49b4fa 100644 --- a/AllBase/BaseClass.cs +++ b/AllBase/BaseClass.cs @@ -31,7 +31,7 @@ namespace AllBase /// /// 版本ID /// - public static int VersionCount = 1366; + public static int VersionCount = 1369; public static string MainPath = "29wx"; //public static string MainPath = "29wx"; //public static string MainPath = "29pdd"; diff --git a/AllBase/Helper/GlobalConfig.cs b/AllBase/Helper/GlobalConfig.cs index 674a0ab..1e41080 100644 --- a/AllBase/Helper/GlobalConfig.cs +++ b/AllBase/Helper/GlobalConfig.cs @@ -133,5 +133,14 @@ namespace AllBase.Helper /// 微信使用软件设置的默认上货地址 /// public bool IsWxUseSoftwareDefaultAddress { get; set; } = true; - } + + /// 自动上报违规商品 + public bool AutoReportViolationProducts { get; set; } = true; + + /// 上货自动剔除违规商品 + public bool AutoExcludeViolationProductsOnUpload { get; set; } = true; + + /// 自动下架违规商品 + public bool AutoDelistViolationProducts { get; set; } = true; + } } diff --git a/AllBase/HttpHelper.cs b/AllBase/HttpHelper.cs new file mode 100644 index 0000000..f90ca82 --- /dev/null +++ b/AllBase/HttpHelper.cs @@ -0,0 +1,1036 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace CsharpHttpHelper +{ + public class HttpException : Exception + { + /// + /// Cookie是否在线 + /// + public bool IsOnline { get; set; } = true; + public HttpException(string message) : base(message) + { + + } + public HttpException(string message, Exception innerException) : base(message, innerException) + { + + } + } + /// + /// Http连接操作帮助类 + /// + public class HttpHelper + { + public static HttpResult Get(string Url) + { + return Request(new HttpItem + { + URL = Url, + Method = MethodType.Get, + }); + } + + public static HttpResult Post(string Url, string PostData) + { + return Request(new HttpItem + { + URL = Url, + Method = MethodType.Post, + Postdata = PostData, + PostDataType = PostDataType.String + }); + } + + /// + /// 下载文件 + /// + /// + /// + public static byte[] DownloadFile(string address) + { + HttpItem item = new HttpItem(); + item.URL = address; + item.Timeout = 3000; + item.ResultType = ResultType.Byte; + HttpHelper http = new HttpHelper(); + HttpResult result = http.GetHtml(item); + if (result.StatusCode == HttpStatusCode.OK) + { + return result.ResultByte; + } + else + { + throw new HttpListenerException((int)result.StatusCode, result.StatusDescription); + } + } + + public static HttpResult Request(HttpItem item) + { + HttpResult result = new HttpHelper().GetHtml(item); + if (!string.IsNullOrEmpty(result.Html)) + { + if (result.Html[0] == 65279) + { + result.Html = result.Html.Remove(0, 1); + } + } + return result; + } + + + #region Cookie + /// + /// 根据字符生成Cookie和精简串,将排除path,expires,domain以及重复项 + /// + /// Cookie字符串 + /// 精简串 + public static string GetSmallCookie(string strcookie) + { + return HttpCookieHelper.GetSmallCookie(strcookie); + } + /// + /// 将字符串Cookie转为CookieCollection + /// + /// Cookie字符串 + /// List-CookieItem + public static CookieCollection StrCookieToCookieCollection(string strcookie) + { + return HttpCookieHelper.StrCookieToCookieCollection(strcookie); + } + /// + /// 将CookieCollection转为字符串Cookie + /// + /// Cookie字符串 + /// strcookie + public static string CookieCollectionToStrCookie(CookieCollection cookie) + { + return HttpCookieHelper.CookieCollectionToStrCookie(cookie); + } + /// + /// 自动合并两个Cookie的值返回更新后结果 + /// + /// Cookie1 新 + /// Cookie2 旧 + /// 返回更新后的Cookie + public static string GetMergeCookie(string cookie1, string cookie2) + { + return HttpCookieHelper.GetMergeCookie(cookie1, cookie2); + } + #endregion + + + #region 预定义方法或者变更 + //默认的编码 + private Encoding encoding = Encoding.Default; + //Post数据编码 + private Encoding postencoding = Encoding.Default; + //HttpWebRequest对象用来发起请求 + private HttpWebRequest request = null; + //获取影响流的数据对象 + private HttpWebResponse response = null; + /// + /// 根据相传入的数据,得到相应页面数据 + /// + /// 参数类对象 + /// 返回HttpResult类型 + public HttpResult GetHtml(HttpItem item) + { + //返回参数 + HttpResult result = new HttpResult(); + result.item = item; + try + { + //准备参数 + SetRequest(item); + } + catch (Exception ex) + { + result = new HttpResult(); + result.Cookie = string.Empty; + result.Header = null; + result.Html = ex.Message; + result.StatusDescription = "配置参数时出错:" + ex.Message; + return result; + } + try + { + #region 得到请求的response + using (response = (HttpWebResponse)request.GetResponse()) + { + result.StatusCode = response.StatusCode; + result.StatusDescription = response.StatusDescription; + result.Header = response.Headers; + if (response.Cookies != null) result.CookieCollection = response.Cookies; + if (response.Headers["set-cookie"] != null) result.Cookie = response.Headers["set-cookie"]; + MemoryStream _stream = new MemoryStream(); + //GZIIP处理 + if (response.ContentEncoding != null && response.ContentEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase)) + { + //开始读取流并设置编码方式 + //new GZipStream(response.GetResponseStream(), CompressionMode.Decompress).CopyTo(_stream, 10240); + //.net4.0以下写法 + _stream = GetMemoryStream(new GZipStream(response.GetResponseStream(), CompressionMode.Decompress)); + + } + else + { + //开始读取流并设置编码方式 + //response.GetResponseStream().CopyTo(_stream, 10240); + //.net4.0以下写法 + _stream = GetMemoryStream(response.GetResponseStream()); + } + //获取Byte + byte[] ResponseByte = _stream.ToArray(); + _stream.Close(); + if (ResponseByte != null & ResponseByte.Length > 0) + { + //是否返回Byte类型数据 + if (item.ResultType == ResultType.Byte) result.ResultByte = ResponseByte; + //从这里开始我们要无视编码了 + if (encoding == null) + { + Match meta = Regex.Match(Encoding.Default.GetString(ResponseByte), " 1) ? meta.Groups[2].Value.ToLower().Trim() : string.Empty; + if (c.Length > 2) + { + try + { + if (c.IndexOf(" ") > 0) c = c.Substring(0, c.IndexOf(" ")); + encoding = Encoding.GetEncoding(c.Replace("\"", "").Replace("'", "").Replace(";", "").Replace("iso-8859-1", "gbk").Trim()); + } + catch + { + if (string.IsNullOrEmpty(response.CharacterSet)) encoding = Encoding.UTF8; + else encoding = Encoding.GetEncoding(response.CharacterSet); + } + } + else + { + if (string.IsNullOrEmpty(response.CharacterSet)) encoding = Encoding.UTF8; + else encoding = Encoding.GetEncoding(response.CharacterSet); + } + } + //得到返回的HTML + result.Html = encoding.GetString(ResponseByte); + } + else + { + //得到返回的HTML + result.Html = "本次请求并未返回任何数据"; + } + } + #endregion + } + catch (WebException ex) + { + //这里是在发生异常时返回的错误信息 + response = (HttpWebResponse)ex.Response; + result.Html = ex.Message; + if (response != null) + { + using (response) + { + result.StatusCode = response.StatusCode; + result.StatusDescription = response.StatusDescription; + result.Header = response.Headers; + // 保留错误响应正文,供微信调用方识别业务错误和登录失效。 + using (var source = response.GetResponseStream()) + using (var decoded = string.Equals(response.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase) + ? (Stream)new GZipStream(source, CompressionMode.Decompress) : source) + using (var reader = new StreamReader(decoded, item.Encoding ?? Encoding.UTF8)) + result.Html = reader.ReadToEnd(); + } + } + } + catch (Exception ex) + { + result.Html = ex.Message; + } + if (item.IsToLower) result.Html = result.Html.ToLower(); + return result; + } + /// + /// 4.0以下.net版本取数据使用 + /// + /// 流 + private MemoryStream GetMemoryStream(Stream streamResponse) + { + MemoryStream _stream = new MemoryStream(); + int Length = 256; + Byte[] buffer = new Byte[Length]; + int bytesRead = streamResponse.Read(buffer, 0, Length); + while (bytesRead > 0) + { + _stream.Write(buffer, 0, bytesRead); + bytesRead = streamResponse.Read(buffer, 0, Length); + } + return _stream; + } + /// + /// 为请求准备参数 + /// + ///参数列表 + private void SetRequest(HttpItem item) + + { + // 验证证书 + SetCer(item); + //设置Header参数 + if (item.Header != null && item.Header.Count > 0) foreach (string key in item.Header.AllKeys) + { + request.Headers.Add(key, item.Header[key]); + } + // 设置代理 + SetProxy(item); + if (item.ProtocolVersion != null) request.ProtocolVersion = item.ProtocolVersion; + request.ServicePoint.Expect100Continue = item.Expect100Continue; + //请求方式Get或者Post + request.Method = item.Method.ToString(); + request.Timeout = item.Timeout; + request.KeepAlive = item.KeepAlive; + request.ReadWriteTimeout = item.ReadWriteTimeout; + //Accept + request.Accept = item.Accept; + //ContentType返回类型 + request.ContentType = item.ContentType; + //UserAgent客户端的访问类型,包括浏览器版本和操作系统信息 + request.UserAgent = item.UserAgent; + // 编码 + encoding = item.Encoding; + //设置安全凭证 + request.Credentials = item.ICredentials; + //设置Cookie + SetCookie(item); + //来源地址 + request.Referer = item.Referer; + //是否执行跳转功能 + request.AllowAutoRedirect = item.Allowautoredirect; + //设置Post数据 + SetPostData(item); + //设置最大连接 + if (item.Connectionlimit > 0) request.ServicePoint.ConnectionLimit = item.Connectionlimit; + } + /// + /// 设置证书 + /// + /// + private void SetCer(HttpItem item) + { + if (!string.IsNullOrEmpty(item.CerPath)) + { + //这一句一定要写在创建连接的前面。使用回调的方法进行证书验证。 + ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult); + //初始化对像,并设置请求的URL地址 + request = (HttpWebRequest)WebRequest.Create(item.URL); + SetCerList(item); + //将证书添加到请求里 + request.ClientCertificates.Add(new X509Certificate(item.CerPath)); + } + else + { + //初始化对像,并设置请求的URL地址 + request = (HttpWebRequest)WebRequest.Create(item.URL); + SetCerList(item); + } + } + /// + /// 设置多个证书 + /// + /// + private void SetCerList(HttpItem item) + { + if (item.ClentCertificates != null && item.ClentCertificates.Count > 0) + { + foreach (X509Certificate c in item.ClentCertificates) + { + request.ClientCertificates.Add(c); + } + } + } + /// + /// 设置Cookie + /// + /// Http参数 + private void SetCookie(HttpItem item) + { + if (!string.IsNullOrEmpty(item.Cookie)) request.Headers[HttpRequestHeader.Cookie] = item.Cookie; + //设置CookieCollection + if (item.ResultCookieType == ResultCookieType.CookieCollection) + { + request.CookieContainer = new CookieContainer(); + if (item.CookieCollection != null && item.CookieCollection.Count > 0) + request.CookieContainer.Add(item.CookieCollection); + } + } + /// + /// 设置Post数据 + /// + /// Http参数 + private void SetPostData(HttpItem item) + { + //验证在得到结果时是否有传入数据 + if (request.Method.Trim().ToLower().Contains("post") || request.Method.Trim().ToLower().Contains("put")) + { + if (item.PostEncoding != null) + { + postencoding = item.PostEncoding; + } + byte[] buffer = null; + //写入Byte类型 + if (item.PostDataType == PostDataType.Byte && item.PostdataByte != null && item.PostdataByte.Length > 0) + { + //验证在得到结果时是否有传入数据 + buffer = item.PostdataByte; + }//写入文件 + else if (item.PostDataType == PostDataType.FilePath && !string.IsNullOrEmpty(item.Postdata)) + { + StreamReader r = new StreamReader(item.Postdata, postencoding); + buffer = postencoding.GetBytes(r.ReadToEnd()); + r.Close(); + } //写入字符串 + else if (!string.IsNullOrEmpty(item.Postdata)) + { + buffer = postencoding.GetBytes(item.Postdata); + } + if (buffer != null) + { + request.ContentLength = buffer.Length; + request.GetRequestStream().Write(buffer, 0, buffer.Length); + } + } + } + /// + /// 设置代理 + /// + /// 参数对象 + private void SetProxy(HttpItem item) + { + bool isIeProxy = item.ProxyIp.ToLower().Contains("ieproxy"); + if (!string.IsNullOrEmpty(item.ProxyIp) && !isIeProxy) + { + //设置代理服务器 + if (item.ProxyIp.Contains(":")) + { + string[] plist = item.ProxyIp.Split(':'); + WebProxy myProxy = new WebProxy(plist[0].Trim(), Convert.ToInt32(plist[1].Trim())); + //建议连接 + myProxy.Credentials = new NetworkCredential(item.ProxyUserName, item.ProxyPwd); + //给当前请求对象 + request.Proxy = myProxy; + } + else + { + WebProxy myProxy = new WebProxy(item.ProxyIp, false); + //建议连接 + myProxy.Credentials = new NetworkCredential(item.ProxyUserName, item.ProxyPwd); + //给当前请求对象 + request.Proxy = myProxy; + } + } + else if (isIeProxy) + { + //设置为IE代理 + } + else + { + request.Proxy = item.WebProxy; + } + } + /// + /// 回调验证证书问题 + /// + /// 流对象 + /// 证书 + /// X509Chain + /// SslPolicyErrors + /// bool + public bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; } + #endregion + } + /// + /// Http请求参考类 + /// + public class HttpItem + { + string _URL = string.Empty; + /// + /// 请求URL必须填写 + /// + public string URL + { + get { return _URL; } + set { _URL = value; } + } + MethodType _Method = MethodType.Get; + /// + /// 请求方式默认为GET方式,当为POST方式时必须设置Postdata的值 + public MethodType Method + /// + { + get { return _Method; } + set { _Method = value; } + } + int _Timeout = 100000; + /// + /// 默认请求超时时间 + /// + public int Timeout + { + get { return _Timeout; } + set { _Timeout = value; } + } + int _ReadWriteTimeout = 30000; + /// + /// 默认写入Post数据超时间 + /// + public int ReadWriteTimeout + { + get { return _ReadWriteTimeout; } + set { _ReadWriteTimeout = value; } + } + Boolean _KeepAlive = true; + /// + /// 获取或设置一个值,该值指示是否与 Internet 资源建立持久性连接默认为true。 + /// + public Boolean KeepAlive + { + get { return _KeepAlive; } + set { _KeepAlive = value; } + } + string _Accept = "text/html, application/xhtml+xml, */*"; + /// + /// 请求标头值 默认为text/html, application/xhtml+xml, */* + /// + public string Accept + { + get { return _Accept; } + set { _Accept = value; } + } + string _ContentType = "text/html"; + /// + /// 请求返回类型默认 text/html + /// + public string ContentType + { + get { return _ContentType; } + set { _ContentType = value; } + } + string _UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"; + /// + /// 客户端访问信息默认Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) + /// + public string UserAgent + { + get { return _UserAgent; } + set { _UserAgent = value; } + } + Encoding _Encoding = null; + /// + /// 返回数据编码默认为NUll,可以自动识别,一般为utf-8,gbk,gb2312 + /// + public Encoding Encoding + { + get { return _Encoding; } + set { _Encoding = value; } + } + private PostDataType _PostDataType = PostDataType.String; + /// + /// Post的数据类型 + /// + public PostDataType PostDataType + { + get { return _PostDataType; } + set { _PostDataType = value; } + } + string _Postdata = string.Empty; + /// + /// Post请求时要发送的字符串Post数据 + /// + public string Postdata + { + get { return _Postdata; } + set { _Postdata = value; } + } + private byte[] _PostdataByte = null; + /// + /// Post请求时要发送的Byte类型的Post数据 + /// + public byte[] PostdataByte + { + get { return _PostdataByte; } + set { _PostdataByte = value; } + } + private WebProxy _WebProxy; + /// + /// 设置代理对象,不想使用IE默认配置就设置为Null,而且不要设置ProxyIp + /// + public WebProxy WebProxy + { + get { return _WebProxy; } + set { _WebProxy = value; } + } + + CookieCollection cookiecollection = null; + /// + /// Cookie对象集合 + /// + public CookieCollection CookieCollection + { + get { return cookiecollection; } + set { cookiecollection = value; } + } + string _Cookie = string.Empty; + /// + /// 请求时的Cookie + /// + public string Cookie + { + get { return _Cookie; } + set { _Cookie = value; } + } + string _Referer = string.Empty; + /// + /// 来源地址,上次访问地址 + /// + public string Referer + { + get { return _Referer; } + set { _Referer = value; } + } + string _CerPath = string.Empty; + /// + /// 证书绝对路径 + /// + public string CerPath + { + get { return _CerPath; } + set { _CerPath = value; } + } + private Boolean isToLower = false; + /// + /// 是否设置为全文小写,默认为不转化 + /// + public Boolean IsToLower + { + get { return isToLower; } + set { isToLower = value; } + } + private Boolean allowautoredirect = false; + /// + /// 支持跳转页面,查询结果将是跳转后的页面,默认是不跳转 + /// + public Boolean Allowautoredirect + { + get { return allowautoredirect; } + set { allowautoredirect = value; } + } + private int connectionlimit = 1024; + /// + /// 最大连接数 + /// + public int Connectionlimit + { + get { return connectionlimit; } + set { connectionlimit = value; } + } + private string proxyusername = string.Empty; + /// + /// 代理Proxy 服务器用户名 + /// + public string ProxyUserName + { + get { return proxyusername; } + set { proxyusername = value; } + } + private string proxypwd = string.Empty; + /// + /// 代理 服务器密码 + /// + public string ProxyPwd + { + get { return proxypwd; } + set { proxypwd = value; } + } + private string proxyip = string.Empty; + /// + /// 代理 服务IP ,如果要使用IE代理就设置为ieproxy + /// + public string ProxyIp + { + get { return proxyip; } + set { proxyip = value; } + } + private ResultType resulttype = ResultType.String; + /// + /// 设置返回类型String和Byte + /// + public ResultType ResultType + { + get { return resulttype; } + set { resulttype = value; } + } + private WebHeaderCollection header = new WebHeaderCollection(); + /// + /// header对象 + /// + public WebHeaderCollection Header + { + get { return header; } + set { header = value; } + } + + private Version _ProtocolVersion; + + /// + // 获取或设置用于请求的 HTTP 版本。返回结果:用于请求的 HTTP 版本。默认为 System.Net.HttpVersion.Version11。 + /// + public Version ProtocolVersion + { + get { return _ProtocolVersion; } + set { _ProtocolVersion = value; } + } + private Boolean _expect100continue = true; + /// + /// 获取或设置一个 System.Boolean 值,该值确定是否使用 100-Continue 行为。如果 POST 请求需要 100-Continue 响应,则为 true;否则为 false。默认值为 true。 + /// + public Boolean Expect100Continue + { + get { return _expect100continue; } + set { _expect100continue = value; } + } + private X509CertificateCollection _ClentCertificates; + /// + /// 设置509证书集合 + /// + public X509CertificateCollection ClentCertificates + { + get { return _ClentCertificates; } + set { _ClentCertificates = value; } + } + private Encoding _PostEncoding; + /// + /// 设置或获取Post参数编码,默认的为Default编码 + /// + public Encoding PostEncoding + { + get { return _PostEncoding; } + set { _PostEncoding = value; } + } + private ResultCookieType _ResultCookieType = ResultCookieType.String; + /// + /// Cookie返回类型,默认的是只返回字符串类型 + /// + public ResultCookieType ResultCookieType + { + get { return _ResultCookieType; } + set { _ResultCookieType = value; } + } + + private ICredentials _ICredentials = CredentialCache.DefaultCredentials; + /// + /// 获取或设置请求的身份验证信息。 + /// + public ICredentials ICredentials + { + get { return _ICredentials; } + set { _ICredentials = value; } + } + + } + /// + /// Http返回参数类 + /// + public class HttpResult + { + private string _Cookie; + /// + /// Http请求返回的Cookie + /// + public string Cookie + { + get { return _Cookie; } + set { _Cookie = value; } + } + + private CookieCollection _CookieCollection; + /// + /// Cookie对象集合 + /// + public CookieCollection CookieCollection + { + get { return _CookieCollection; } + set { _CookieCollection = value; } + } + private string _Html; + /// + /// 返回的String类型数据 只有ResultType.String时才返回数据,其它情况为空 + /// + public string Html + { + get { return _Html; } + set { _Html = value; } + } + private byte[] _ResultByte; + /// + /// 返回的Byte数组 只有ResultType.Byte时才返回数据,其它情况为空 + /// + public byte[] ResultByte + { + get { return _ResultByte; } + set { _ResultByte = value; } + } + + private WebHeaderCollection _Header; + /// + /// header对象 + /// + public WebHeaderCollection Header + { + get { return _Header; } + set { _Header = value; } + } + + private string _StatusDescription; + /// + /// 返回状态说明 + /// + public string StatusDescription + { + get { return _StatusDescription; } + set { _StatusDescription = value; } + } + private HttpStatusCode _StatusCode; + /// + /// 返回状态码,默认为OK + /// + public HttpStatusCode StatusCode + { + get { return _StatusCode; } + set { _StatusCode = value; } + } + /// + /// HttpItem类,用于存储实际执行所用到的HttpItem对象 + /// + public HttpItem item { get; set; } + } + /// + /// 返回类型 + /// + public enum ResultType + { + /// + /// 表示只返回字符串 只有Html有数据 + /// + String, + /// + /// 表示返回字符串和字节流 ResultByte和Html都有数据返回 + /// + Byte + } + /// + /// Post的数据格式默认为string + /// + public enum PostDataType + { + /// + /// 字符串类型,这时编码Encoding可不设置 + /// + String, + /// + /// Byte类型,需要设置PostdataByte参数的值编码Encoding可设置为空 + /// + Byte, + /// + /// 传文件,Postdata必须设置为文件的绝对路径,必须设置Encoding的值 + /// + FilePath + } + /// + /// Cookie返回类型 + /// + public enum ResultCookieType + { + /// + /// 只返回字符串类型的Cookie + /// + String, + /// + /// CookieCollection格式的Cookie集合同时也返回String类型的cookie + /// + CookieCollection + } + public enum MethodType + { + Get, + Post, + Put + } + + + internal static class HttpCookieHelper + { + /// + /// 根据字符生成Cookie和精简串,将排除path,expires,domain以及重复项 + /// + /// Cookie字符串 + /// 精简串 + internal static string GetSmallCookie(string strcookie) + { + if (string.IsNullOrWhiteSpace(strcookie)) + { + return string.Empty; + } + List cookielist = new List(); + //将Cookie字符串以,;分开,生成一个字符数组,并删除里面的空项 + string[] list = strcookie.ToString().Split(new string[] { ",", ";" }, StringSplitOptions.RemoveEmptyEntries); + foreach (string item in list) + { + string itemcookie = item.ToLower().Trim().Replace("\r\n", string.Empty).Replace("\n", string.Empty); + //排除空字符串 + if (string.IsNullOrWhiteSpace(itemcookie)) continue; + //排除不存在=号的Cookie项 + if (!itemcookie.Contains("=")) continue; + //排除path项 + if (itemcookie.Contains("path=")) continue; + //排除expires项 + if (itemcookie.Contains("expires=")) continue; + //排除domain项 + if (itemcookie.Contains("domain=")) continue; + if (itemcookie.Contains("samesite=")) continue; + //排除重复项 + if (cookielist.Contains(item)) continue; + + //对接Cookie基本的Key和Value串 + cookielist.Add(string.Format("{0};", item)); + } + return string.Join(";", cookielist); + } + + /// + /// 将字符串Cookie转为CookieCollection + /// + /// Cookie字符串 + /// List-CookieItem + internal static CookieCollection StrCookieToCookieCollection(string strcookie) + { + //排除空字符串 + if (string.IsNullOrWhiteSpace(strcookie)) + { + return null; + } + CookieCollection cookielist = new CookieCollection(); + //先简化Cookie + strcookie = GetSmallCookie(strcookie); + //将Cookie字符串以,;分开,生成一个字符数组,并删除里面的空项 + string[] list = strcookie.ToString().Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); + foreach (string item in list) + { + string[] cookie = item.ToString().Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries); + if (cookie.Length == 2) + { + cookielist.Add(new Cookie() { Name = cookie[0].Trim(), Value = cookie[1].Trim() }); + } + } + return cookielist; + } + + /// + /// 将CookieCollection转为字符串Cookie + /// + /// Cookie字符串 + /// strcookie + internal static string CookieCollectionToStrCookie(CookieCollection cookie) + { + if (cookie == null) + { + return string.Empty; + } + string resultcookie = string.Empty; + foreach (Cookie item in cookie) + { + resultcookie += string.Format("{0}={1};", item.Name, item.Value); + } + return resultcookie; + } + + /// + /// 自动合并两个Cookie的值返回更新后结果 + /// + /// Cookie1 新 + /// Cookie2 旧 + /// 返回更新后的Cookie + internal static string GetMergeCookie(string cookie1, string cookie2) + { + if (string.IsNullOrWhiteSpace(cookie1))//新的是空的 + { + return cookie2;//返回老的 + } + if (string.IsNullOrWhiteSpace(cookie2))//老的是空的 + { + return cookie1;//返回新的 + } + Dictionary cookielist = new Dictionary();//结果 + string[] list_1 = cookie1.ToString().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + string[] list_2 = cookie2.ToString().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string item in list_1) + { + var dict = GetCookieDict(item); + //替换重复项 + if (cookielist.ContainsKey(dict.Key)) + { + continue; + } + else + { + cookielist.Add(dict.Key, dict.Value); + } + } + foreach (string item in list_2) + { + var dict = GetCookieDict(item); + //替换重复项 + if (cookielist.ContainsKey(dict.Key)) + { + continue; + } + else + { + cookielist.Add(dict.Key, dict.Value); + } + } + + return string.Join(";", cookielist.Select(a => { return $"{a.Key}={a.Value}"; })); + } + private static KeyValuePair GetCookieDict(string cookie) + { + string[] item = cookie.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries); + if (item.Length > 2) + { + item = new string[2]; + Match match = Regex.Match(cookie, "(?^.+?)=(?.*)"); + item[0] = match.Groups["key"].Value; + item[1] = match.Groups["value"].Value; + } + string key = ""; + string value = ""; + key = item.Length > 0 ? item[0] : ""; + value = item.Length > 1 ? item[1] : ""; + + + return new KeyValuePair(key, value); + } + } + +} + diff --git a/TEMUKJD/KJD.csproj.user b/TEMUKJD/KJD.csproj.user index af56d8a..76ca146 100644 --- a/TEMUKJD/KJD.csproj.user +++ b/TEMUKJD/KJD.csproj.user @@ -44,6 +44,9 @@ Form + + Form + Form diff --git a/TEMUKJD/KJDForms/SystemForms/SystemForm.Designer.cs b/TEMUKJD/KJDForms/SystemForms/SystemForm.Designer.cs index 1931c4e..7ad545c 100644 --- a/TEMUKJD/KJDForms/SystemForms/SystemForm.Designer.cs +++ b/TEMUKJD/KJDForms/SystemForms/SystemForm.Designer.cs @@ -29,8 +29,9 @@ private void InitializeComponent() { System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("解除限制"); + System.Windows.Forms.TreeNode violationNode = new System.Windows.Forms.TreeNode("违规检测"); System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("系统设置", new System.Windows.Forms.TreeNode[] { - treeNode1}); + treeNode1, violationNode}); this.panel1 = new System.Windows.Forms.Panel(); this.panel3 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel(); @@ -103,6 +104,8 @@ this.tv.Name = "tv"; treeNode1.Name = "解除限制"; treeNode1.Text = "解除限制"; + violationNode.Name = "违规检测"; + violationNode.Text = "违规检测"; treeNode2.Name = "系统设置"; treeNode2.Text = "系统设置"; this.tv.Nodes.AddRange(new System.Windows.Forms.TreeNode[] { @@ -162,4 +165,4 @@ private UserComponent.UcTreeView tv; private System.Windows.Forms.SplitContainer splitContainer1; } -} \ No newline at end of file +} diff --git a/TEMUKJD/KJDForms/SystemForms/SystemForm.cs b/TEMUKJD/KJDForms/SystemForms/SystemForm.cs index 4f406b3..ccfc34b 100644 --- a/TEMUKJD/KJDForms/SystemForms/SystemForm.cs +++ b/TEMUKJD/KJDForms/SystemForms/SystemForm.cs @@ -15,6 +15,7 @@ namespace TemuKJD.KJDForms.SystemForms public partial class SystemForm : BaseForm { public Lazy systemRemoveRestrictionsForm = new Lazy(); + public Lazy systemViolationDetectionForm = new Lazy(); public SystemForm() { InitializeComponent(); @@ -46,6 +47,9 @@ namespace TemuKJD.KJDForms.SystemForms case "解除限制": SetForm(systemRemoveRestrictionsForm.Value); break; + case "违规检测": + SetForm(systemViolationDetectionForm.Value); + break; //case "商品管理": // SetForm(shopProductForm.Value); // break; diff --git a/TEMUKJD/KJDForms/SystemForms/SystemViolationDetectionForm.Designer.cs b/TEMUKJD/KJDForms/SystemForms/SystemViolationDetectionForm.Designer.cs new file mode 100644 index 0000000..756d115 --- /dev/null +++ b/TEMUKJD/KJDForms/SystemForms/SystemViolationDetectionForm.Designer.cs @@ -0,0 +1,61 @@ +namespace TemuKJD.KJDForms.SystemForms +{ + partial class SystemViolationDetectionForm + { + private System.ComponentModel.IContainer components = null; + + protected override void Dispose(bool disposing) + { + if (disposing && components != null) + components.Dispose(); + base.Dispose(disposing); + } + + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.chkAutoReport = new TemuKJD.UserComponent.UcCheckBox(this.components); + this.chkAutoExcludeOnUpload = new TemuKJD.UserComponent.UcCheckBox(this.components); + this.chkAutoDelist = new TemuKJD.UserComponent.UcCheckBox(this.components); + this.SuspendLayout(); + // chkAutoReport + this.chkAutoReport.AutoSize = true; + this.chkAutoReport.Location = new System.Drawing.Point(24, 24); + this.chkAutoReport.Name = "chkAutoReport"; + this.chkAutoReport.TabIndex = 0; + this.chkAutoReport.Text = "自动上报违规商品"; + this.chkAutoReport.UseVisualStyleBackColor = true; + // chkAutoExcludeOnUpload + this.chkAutoExcludeOnUpload.AutoSize = true; + this.chkAutoExcludeOnUpload.Location = new System.Drawing.Point(24, 64); + this.chkAutoExcludeOnUpload.Name = "chkAutoExcludeOnUpload"; + this.chkAutoExcludeOnUpload.TabIndex = 1; + this.chkAutoExcludeOnUpload.Text = "上货自动剔除违规商品"; + this.chkAutoExcludeOnUpload.UseVisualStyleBackColor = true; + // chkAutoDelist + this.chkAutoDelist.AutoSize = true; + this.chkAutoDelist.Location = new System.Drawing.Point(24, 104); + this.chkAutoDelist.Name = "chkAutoDelist"; + this.chkAutoDelist.TabIndex = 2; + this.chkAutoDelist.Text = "自动下架违规商品"; + this.chkAutoDelist.UseVisualStyleBackColor = true; + // SystemViolationDetectionForm + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.AutoScroll = true; + this.BackColor = System.Drawing.Color.White; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.chkAutoReport); + this.Controls.Add(this.chkAutoExcludeOnUpload); + this.Controls.Add(this.chkAutoDelist); + this.Font = new System.Drawing.Font("微软雅黑", 9F); + this.Name = "SystemViolationDetectionForm"; + this.Text = "违规检测"; + this.ResumeLayout(false); + this.PerformLayout(); + } + + private UserComponent.UcCheckBox chkAutoReport; + private UserComponent.UcCheckBox chkAutoExcludeOnUpload; + private UserComponent.UcCheckBox chkAutoDelist; + } +} diff --git a/TEMUKJD/KJDForms/SystemForms/SystemViolationDetectionForm.cs b/TEMUKJD/KJDForms/SystemForms/SystemViolationDetectionForm.cs new file mode 100644 index 0000000..c6b83c4 --- /dev/null +++ b/TEMUKJD/KJDForms/SystemForms/SystemViolationDetectionForm.cs @@ -0,0 +1,32 @@ +using System; +using AllBase.Helper; +using TemuKJD.BaseForms; + +namespace TemuKJD.KJDForms.SystemForms +{ + public partial class SystemViolationDetectionForm : BaseForm + { + public SystemViolationDetectionForm() + { + InitializeComponent(); + + var settings = GlobalConfig.GlobalSettings; + chkAutoReport.Checked = settings.AutoReportViolationProducts; + chkAutoExcludeOnUpload.Checked = settings.AutoExcludeViolationProductsOnUpload; + chkAutoDelist.Checked = settings.AutoDelistViolationProducts; + + chkAutoReport.CheckedChanged += Settings_CheckedChanged; + chkAutoExcludeOnUpload.CheckedChanged += Settings_CheckedChanged; + chkAutoDelist.CheckedChanged += Settings_CheckedChanged; + } + + private void Settings_CheckedChanged(object sender, EventArgs e) + { + var settings = GlobalConfig.GlobalSettings; + settings.AutoReportViolationProducts = chkAutoReport.Checked; + settings.AutoExcludeViolationProductsOnUpload = chkAutoExcludeOnUpload.Checked; + settings.AutoDelistViolationProducts = chkAutoDelist.Checked; + StartUp.SaveGlobalSettings(); + } + } +} diff --git a/TEMUKJD/PDDForms/ProductForms/WXProdViolationManage.cs b/TEMUKJD/PDDForms/ProductForms/WXProdViolationManage.cs index 5e8cb41..a64b4db 100644 --- a/TEMUKJD/PDDForms/ProductForms/WXProdViolationManage.cs +++ b/TEMUKJD/PDDForms/ProductForms/WXProdViolationManage.cs @@ -12,168 +12,177 @@ using System.Threading.Tasks; using TemuKJD.BLL; using TemuKJD.WXForms.ProductForms; using Work; +using Work.Services; namespace TemuKJD.PDDForms.ProductForms { - // 微信店铺违规商品抓取 - public class WXProdViolationManage - { - public void GetShopProdViolation() - { - Task.Run(() => - { - if (!Directory.Exists("wxProdViolation")) - Directory.CreateDirectory("wxProdViolation"); + // 微信店铺违规商品抓取 + public class WXProdViolationManage + { + public void GetShopProdViolation() + { + Task.Run(() => + { + if (!Directory.Exists("wxProdViolation")) + Directory.CreateDirectory("wxProdViolation"); - while (true) - { - //break; - foreach (var shop in GlobalConfig.ShopLoginList) - { - if (shop.LoginStatus != "登录成功") - continue; + while (true) + { + //break; + foreach (var shop in GlobalConfig.ShopLoginList) + { + if (shop.LoginStatus != "登录成功") + continue; - Thread.Sleep(Randomx.GetId(500, 1500)); + // 开启自动下架时,后台检测当前店铺在售商品并下架违规商品。 + if (GlobalConfig.GlobalSettings.AutoDelistViolationProducts) + CloudGoodsService.CheckAndOffShelfViolations(shop); - WXProdViolationGetInfo info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.Now.ToString("yyMMdd") }; - try - { - info = NaiBa.Util.Helper.ConfigHelper.LoadConfig("wxProdViolation\\" + shop.ShopId); - if (info == null) - info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.MinValue.ToString("yyMMdd") }; + Thread.Sleep(Randomx.GetId(500, 1500)); - if (info.updateDay == DateTime.Now.ToString("yyMMdd")) - { - // 今日同步过,跳过,暂时关闭改为每次打开立即读取 - //continue; - } + WXProdViolationGetInfo info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.Now.ToString("yyMMdd") }; + try + { + info = NaiBa.Util.Helper.ConfigHelper.LoadConfig("wxProdViolation\\" + shop.ShopId); + if (info == null) + info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.MinValue.ToString("yyMMdd") }; - info.failReason = ""; + if (info.updateDay == DateTime.Now.ToString("yyMMdd")) + { + // 今日同步过,跳过,暂时关闭改为每次打开立即读取 + //continue; + } - var result = getShopiolationProd(shop); - Thread.Sleep(Randomx.GetId(1000, 2000)); - if (!result.Contains("\"code\":0")) - { - WXForms.ShopForms.WXShopListForm.CheckShopLogin(shop); + info.failReason = ""; - info.failReason = "读取失败"; - NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); - continue; - } + var result = getShopiolationProd(shop); + // 开启自动上报时,后台解析店铺处罚记录并上报违规商品。 + if (GlobalConfig.GlobalSettings.AutoReportViolationProducts) + CloudGoodsService.ReportViolationsFromJson(result, shop); - JObject recordObj = JsonHelper.JObjectParse(result)["record"] as JObject; - JArray jArr = JsonHelper.JObjectParse(result)["record"]["items"] as JArray; - if (recordObj != null && jArr == null) - { - jArr = new JArray(); - } - foreach(var jObj in jArr) - { - string sVioInfo = JsonHelper.SerializeObject(jObj); - var punishUuid = BaseRegex.GetStringJson(ref sVioInfo, "punishUuid"); - var punishReason = BaseRegex.GetStringJson(ref sVioInfo, "punishReason"); - var punishTime = BaseClass.StampToDateTime(BaseRegex.GetIntJson(ref sVioInfo, "punishTime")); - var productId = BaseRegex.GetStringJson(ref sVioInfo, "productId"); - var productDesc = BaseRegex.GetStringJson(ref sVioInfo, "productDesc"); - //相似同款低质商品 不当使用他人权利 - if (!punishReason.Contains("发布违禁信息") && !punishReason.Contains("材质夸大宣传") && !punishReason.Contains("相似同款低质商品") && - !punishReason.Contains("价格违规") && !punishReason.Contains("售假") && !punishReason.Contains("品牌资质问题") && - !punishReason.Contains("不当使用他人权利") && !punishReason.Contains("用户反馈虚假宣传")) - continue; - if (!string.IsNullOrWhiteSpace(productDesc) && !string.IsNullOrWhiteSpace(productId)) - { - var url = BaseClass.BuildPDDObjString("product/mdy_violation_prod"); - var data = UserLogin.GetSignaturex(); - data += "&wx_safe_status=违规商品"; - data += "¬es=" + GetHtml.UrlEncodeUtf8(("违规轮询-" + shop.ShopId + "-" + punishUuid + "-" + punishReason + "-" + punishTime.ToString("yy/MM/dd HH:mm")).Trim()); - data += "&product_name=" + productDesc; - var html = GetHtml.PostUtf8(url, data); - if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200")) - html = GetHtml.PostUtf8(url, data); - if (!html.Contains("\"code\":200")) - { - info.failReason = "更新商品状态失败"; - break; - } + Thread.Sleep(Randomx.GetId(1000, 2000)); + if (!result.Contains("\"code\":0")) + { + WXForms.ShopForms.WXShopListForm.CheckShopLogin(shop); - if(html.Contains("商品不存在")) - { - var getProdRst = WXUpload.searchProductWithTitle(shop, GetHtml.UrlEncodeUtf8(productDesc)); - var spuCode = BaseRegex.GetStringJson(ref getProdRst, "spuCode"); - var secrec_spu_id = ""; - var spu_id = ""; - if (string.IsNullOrEmpty(spuCode) || spuCode.Length < 10) - { - continue; - } - if (spuCode.StartsWith("EC1") && spuCode.EndsWith("A")) - { - spu_id = spuCode.Substring(3, spuCode.Length - 4); - } - else if (spuCode.StartsWith("P1-")) - { - spu_id = spuCode.Substring(3); - } - else - { - secrec_spu_id = spuCode; - } + info.failReason = "读取失败"; + NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); + continue; + } - url = BaseClass.BuildPDDObjString("product/mdy_violation_prod"); - data = UserLogin.GetSignaturex(); - data += "&wx_safe_status=违规商品"; - data += "¬es=" + ("违规轮询-" + shop.ShopId + "-" + punishUuid + "-" + punishReason).Trim(); - data += "&secret_spu_id=" + secrec_spu_id; - data += "&spu_id=" + spu_id; - html = GetHtml.PostUtf8(url, data); - if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200")) - html = GetHtml.PostUtf8(url, data); - if (!html.Contains("\"code\":200")) - { - info.failReason = "更新商品状态失败"; - break; - } - } - } - } + JObject recordObj = JsonHelper.JObjectParse(result)["record"] as JObject; + JArray jArr = JsonHelper.JObjectParse(result)["record"]["items"] as JArray; + if (recordObj != null && jArr == null) + { + jArr = new JArray(); + } + foreach (var jObj in jArr) + { + string sVioInfo = JsonHelper.SerializeObject(jObj); + var punishUuid = BaseRegex.GetStringJson(ref sVioInfo, "punishUuid"); + var punishReason = BaseRegex.GetStringJson(ref sVioInfo, "punishReason"); + var punishTime = BaseClass.StampToDateTime(BaseRegex.GetIntJson(ref sVioInfo, "punishTime")); + var productId = BaseRegex.GetStringJson(ref sVioInfo, "productId"); + var productDesc = BaseRegex.GetStringJson(ref sVioInfo, "productDesc"); + //相似同款低质商品 不当使用他人权利 + if (!punishReason.Contains("发布违禁信息") && !punishReason.Contains("材质夸大宣传") && !punishReason.Contains("相似同款低质商品") && + !punishReason.Contains("价格违规") && !punishReason.Contains("售假") && !punishReason.Contains("品牌资质问题") && + !punishReason.Contains("不当使用他人权利") && !punishReason.Contains("用户反馈虚假宣传")) + continue; + if (!string.IsNullOrWhiteSpace(productDesc) && !string.IsNullOrWhiteSpace(productId)) + { + var url = BaseClass.BuildPDDObjString("product/mdy_violation_prod"); + var data = UserLogin.GetSignaturex(); + data += "&wx_safe_status=违规商品"; + data += "¬es=" + GetHtml.UrlEncodeUtf8(("违规轮询-" + shop.ShopId + "-" + punishUuid + "-" + punishReason + "-" + punishTime.ToString("yy/MM/dd HH:mm")).Trim()); + data += "&product_name=" + productDesc; + var html = GetHtml.PostUtf8(url, data); + if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200")) + html = GetHtml.PostUtf8(url, data); + if (!html.Contains("\"code\":200")) + { + info.failReason = "更新商品状态失败"; + break; + } - if (string.IsNullOrEmpty(info.failReason)) - info.updateDay = DateTime.Now.ToString("yyMMdd"); + if (html.Contains("商品不存在")) + { + var getProdRst = WXUpload.searchProductWithTitle(shop, GetHtml.UrlEncodeUtf8(productDesc)); + var spuCode = BaseRegex.GetStringJson(ref getProdRst, "spuCode"); + var secrec_spu_id = ""; + var spu_id = ""; + if (string.IsNullOrEmpty(spuCode) || spuCode.Length < 10) + { + continue; + } + if (spuCode.StartsWith("EC1") && spuCode.EndsWith("A")) + { + spu_id = spuCode.Substring(3, spuCode.Length - 4); + } + else if (spuCode.StartsWith("P1-")) + { + spu_id = spuCode.Substring(3); + } + else + { + secrec_spu_id = spuCode; + } - NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); - Thread.Sleep(5000); - } - catch (Exception ex) - { - try - { - if (info == null) - info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.Now.ToString("yyMMdd") }; - info.failReason = ex.Message; - NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); - } - catch { } - } - } - // 休眠两个小时 - Thread.Sleep(1000 * 60 * 60 * 2); - } - }); - } + url = BaseClass.BuildPDDObjString("product/mdy_violation_prod"); + data = UserLogin.GetSignaturex(); + data += "&wx_safe_status=违规商品"; + data += "¬es=" + ("违规轮询-" + shop.ShopId + "-" + punishUuid + "-" + punishReason).Trim(); + data += "&secret_spu_id=" + secrec_spu_id; + data += "&spu_id=" + spu_id; + html = GetHtml.PostUtf8(url, data); + if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200")) + html = GetHtml.PostUtf8(url, data); + if (!html.Contains("\"code\":200")) + { + info.failReason = "更新商品状态失败"; + break; + } + } + } + } - public static string getShopiolationProd(ShopLoginInfo shop) - { - string url = "https://store.weixin.qq.com/shop-faas/mmecnodeviolationsec/violation/cgi/get?token=&lang=zh_CN&violationType=finder&shopId="; - string referer = "https://store.weixin.qq.com/shop/violation/home"; - var result = WXHttpResponseBLL.WeiXinGetResponse(shop, url, referer); - return result; - } - } + if (string.IsNullOrEmpty(info.failReason)) + info.updateDay = DateTime.Now.ToString("yyMMdd"); - public class WXProdViolationGetInfo - { - public string shopId { get; set; } - public string updateDay { get; set; } // yyMMdd - public string failReason { get; set; } // "", "掉线"... - } + NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); + Thread.Sleep(5000); + } + catch (Exception ex) + { + try + { + if (info == null) + info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.Now.ToString("yyMMdd") }; + info.failReason = ex.Message; + NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); + } + catch { } + } + } + // 休眠两个小时 + Thread.Sleep(1000 * 60 * 60 * 2); + } + }); + } + + public static string getShopiolationProd(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeviolationsec/violation/cgi/get?token=&lang=zh_CN&violationType=finder&shopId="; + string referer = "https://store.weixin.qq.com/shop/violation/home"; + var result = WXHttpResponseBLL.WeiXinGetResponse(shop, url, referer); + return result; + } + } + + public class WXProdViolationGetInfo + { + public string shopId { get; set; } + public string updateDay { get; set; } // yyMMdd + public string failReason { get; set; } // "", "掉线"... + } } diff --git a/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs b/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs index 799f8d5..cf3aae0 100644 --- a/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs +++ b/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs @@ -675,7 +675,7 @@ namespace TemuKJD.WXForms.ProductForms item.Cells["colSelect"].Value = wxUploadRule.SelectedShopIds.Contains(shopId); } - + } isInit = true; } @@ -1715,6 +1715,32 @@ namespace TemuKJD.WXForms.ProductForms })); } + // 开启上货自动剔除时,上传前检测违规状态;仅服务端明确返回违规时禁止上传。 + if (GlobalConfig.GlobalSettings.AutoExcludeViolationProductsOnUpload) + { + Invoke(new Action(() => cell.Value = "商品违规检测...")); + try + { + long goodsId = 0; + if ((!long.TryParse(dbProd.spu_id, out goodsId) || goodsId <= 0) && + (!long.TryParse(dbProd.skc_id, out goodsId) || goodsId <= 0)) + throw new Exception("商品ID无效"); + + var statuses = CloudGoodsService.GetViolationStatus(new List { goodsId }); + var status = statuses.FirstOrDefault(item => item.GoodsId == goodsId); + if (status == null) + throw new Exception("未返回当前商品的违规状态"); + if (status.IsViolation) + return "{\"success\":false,\"draftId\":0,\"status\":\"未上传\",\"errMsg\":\"当前商品违规,禁止上传\"}"; + } + catch (Exception ex) + { + Debug.WriteLine("商品违规检测失败,继续上传:" + ex.Message); + if (cell != null) + Invoke(new Action(() => cell.Value = "商品违规检测失败,继续上传")); + } + } + #region 裂变标题 if (BaseClass.UserAuthority.Contains("裂变调试") && chkFissionGoodsName.Checked) { diff --git a/Work/Api/Wx/WxActivityApi.cs b/Work/Api/Wx/WxActivityApi.cs new file mode 100644 index 0000000..654c7d1 --- /dev/null +++ b/Work/Api/Wx/WxActivityApi.cs @@ -0,0 +1,141 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店平台活动报名及活动商品接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxActivityApi + { + /// 取消商品活动报名;沿用原订单页面指定的活动请求结构。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 取消报名请求 JSON 对象,字段结构沿用活动接口。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.cancelActivity。 + /// 保留旧页面 Referer 中的活动 ID 1016;实际提交内容取自 activityData。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string CancelActivity(ShopLoginInfo shop, JObject activityData) + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolmarketingnode/activity/cgi/updateRequestProductList?token=&lang=zh_CN"; + string body = JsonHelper.SerializeObject(activityData); + string refere = "https://store.weixin.qq.com/shop/marketingkol/activity/detail?id=1016&curTab=2"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 获取店铺针对指定平台活动的报名信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信平台活动 ID;默认值沿用原活动页面,其他活动须显式传入。 默认值:"1269"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\ActivityForms\WXActivity618Form.cs,WXActivity.getShopCanActivity。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopCanActivity(ShopLoginInfo shop, string activityId = "1269") + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolmarketingnode/activity/cgi/queryShopPlatformActivity?token=&lang=zh_CN&platformActivityId=" + activityId; + string refere = "https://store.weixin.qq.com/shop/marketingkol/activity/detail?id=" + activityId + "&curTab=2"; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + + /// 提交店铺参加平台活动的申请。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信平台活动 ID;默认值沿用原活动页面,其他活动须显式传入。 默认值:"1269"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\ActivityForms\WXActivity618Form.cs,WXActivity.applyShopActivity。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string ApplyShopActivity(ShopLoginInfo shop, string activityId = "1269") + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolmarketingnode/activity/cgi/applyShopPlatformActivity?token=&lang=zh_CN"; + string refere = "https://store.weixin.qq.com/shop/marketingkol/activity/detail?id=" + activityId + "&curTab=2"; + string data = @"{""platformActivityId"":""" + activityId + @""",""finderCommunicationInfo"":{}}"; + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + + /// 扫描可参加指定平台活动的商品。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 每页条数,默认值沿用原调用。 默认值:10。 + /// 微信平台活动 ID;默认值沿用原活动页面,其他活动须显式传入。 默认值:"1269"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\ActivityForms\WXActivity618Form.cs,WXActivity.getActivityProdList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetActivityProdList(ShopLoginInfo shop, int page, int pageSize = 10, string activityId = "1269") + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolmarketingnode/activity/cgi/scanProductForPlatformInvestment?token=&lang=zh_CN"; + string refere = "https://store.weixin.qq.com/shop/marketingkol/activity/detail?id=" + activityId + "&goToUploadProduct=1"; + string data = @"{""keyword"":"""",""pageNum"":" + page + @",""pageSize"":" + pageSize + @",""platformActivityId"":""" + activityId + @"""}"; + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + + /// 提交平台活动商品报名或更新报名商品列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 活动商品报名 JSON 对象,按原活动接口结构填写。 + /// 微信平台活动 ID;默认值沿用原活动页面,其他活动须显式传入。 默认值:"1269"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\ActivityForms\WXActivity618Form.cs,WXActivity.signWxActivity。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SignWxActivity(ShopLoginInfo shop, JObject prodData, string activityId = "1269") + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolmarketingnode/activity/cgi/updateRequestProductList?token=&lang=zh_CN"; + string refere = "https://store.weixin.qq.com/shop/marketingkol/activity/detail?id=" + activityId + "&goToUploadProduct=1"; + string data = JsonHelper.SerializeObject(prodData); + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + + /// 查询已经进入指定平台活动的商品列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 每页条数,默认值沿用原调用。 默认值:10。 + /// 微信平台活动 ID;默认值沿用原活动页面,其他活动须显式传入。 默认值:"1269"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\ActivityForms\WXActivity618Form.cs,WXActivity.getProdInActivityList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetProdInActivityList(ShopLoginInfo shop, int page, int pageSize = 10, string activityId = "1269") + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolmarketingnode/activity/cgi/getPlatformInvestmentProductInfoList?token=&lang=zh_CN"; + string refere = "https://store.weixin.qq.com/shop/marketingkol/activity/detail?id=" + activityId + "&curTab=2"; + string data = @"{""platformActivityId"":""" + activityId + @""",""pageNum"":" + page + @",""pageSize"":" + pageSize + @",""auditStatus"":0,""productId"":""""}"; + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + + /// 分页查询店铺可报名参加指定平台活动的商品。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 每页条数,默认值沿用原调用。 默认值:10。 + /// 微信平台活动 ID;默认值沿用原活动页面,其他活动须显式传入。 默认值:"1269"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\ActivityForms\WXActivity618Form.cs,WXActivity.getShopActivityCanApplyProduct。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopActivityCanApplyProduct(ShopLoginInfo shop, int page, int pageSize = 10, string activityId = "1269") + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolmarketingnode/activity/cgi/getShopActivityCanApplyProduct?token=&lang=zh_CN"; + string refere = "https://store.weixin.qq.com/shop/marketingkol/activity/detail?id=" + activityId + "&curTab=2"; + string data = @"{""platformActivityId"":""" + activityId + @""",""page"":" + page + @",""pageSize"":" + pageSize + @",""queryWord"":""""}"; + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + } +} diff --git a/Work/Api/Wx/WxAddressApi.cs b/Work/Api/Wx/WxAddressApi.cs new file mode 100644 index 0000000..fa4b2cd --- /dev/null +++ b/Work/Api/Wx/WxAddressApi.cs @@ -0,0 +1,89 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店售后地址及运费模板接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxAddressApi + { + /// 查询商品发布时可选的运费模板。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getFreightTemplate。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetFreightTemplate(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductupload/express/cgi/goods/getFreightTemplate?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 查询商品发布时可选的售后地址,支持搜索条件。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 地址搜索关键词,空字符串表示不指定关键词。 默认值:""。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getAddress。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetAddress(ShopLoginInfo shop, string searchText = "") + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeexpress/cgi/getAddress?token=&lang=zh_CN"; + if (!string.IsNullOrEmpty(searchText)) + url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeexpress/cgi/getAddress?searchStr=" + GetHtml.UrlEncodeUtf8(searchText) + "&token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 按地址 ID 查询售后地址详情。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 售后地址 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getAddressById。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetAddressById(ShopLoginInfo shop, string AddressId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeexpress/cgi/getAddress?offset=0&limit=20&includeBlock=1&addressId=" + AddressId + "&token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/address/expressAddressMgmt"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 新增售后寄回地址。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 收件地址的省份名称。 + /// 收件地址的城市名称。 + /// 收件地址的区县名称。 + /// 详细街道及门牌地址。 + /// 地址联系人姓名。 + /// 收件联系电话或原查询接口支持的手机号条件。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.addAddress。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string AddAddress(ShopLoginInfo shop, string provinceName, string cityName, string countyName, string detailInfo, string userName, string telNumber) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeexpress/address/addAddress?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/address/expressAddressMgmt"; + string address = "{\"addressInfo\":{\"provinceName\":\"" + provinceName + "\",\"cityName\":\"" + cityName + "\",\"countyName\":\"" + countyName + "\",\"detailInfo\":\"" + detailInfo + "\",\"userName\":\"" + userName + "\",\"telNumber\":\"" + telNumber + "\",\"location\":{}},\"defaultSend\":0,\"defaultRecv\":0}"; + JObject obj = new JObject(); + obj["data"] = address; + string body = JsonHelper.SerializeObject(obj); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxAssistantApi.cs b/Work/Api/Wx/WxAssistantApi.cs new file mode 100644 index 0000000..86a9203 --- /dev/null +++ b/Work/Api/Wx/WxAssistantApi.cs @@ -0,0 +1,53 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店带货助手选品池接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxAssistantApi + { + /// 在微信带货助手选品池中查询商品;结构化参数重载执行列表筛选,商品 ID 重载执行关键词搜索。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 选品池列表筛选 JSON;原参数名沿用旧方法,实际并非类目资质参数。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\AdminForms\WXSaleAssistantForm.cs,WxGetAssistant.wxGetAssistant。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxGetAssistant(ShopLoginInfo shop, JObject wxCateAuthParam) + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolpoolnode/ecListSelectionSpuItems?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/talent/pool/home"; + string data = JsonHelper.SerializeObject(wxCateAuthParam); + var result = WxHttpClient.PostAssistant(shop, url, data, referer); + return result; + } + + /// 在微信带货助手选品池中查询商品;结构化参数重载执行列表筛选,商品 ID 重载执行关键词搜索。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\AdminForms\WXSaleAssistantForm.cs,WxGetAssistant.wxGetAssistant。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxGetAssistant(ShopLoginInfo shop, string prodId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmeckolpoolnode/ecSearchSelectionSpuItems?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/talent/pool/home?keyword=" + prodId; + string body = @"{""filter"":{""spuFilter"":{""monthlySalesRange"":null,""goodEvaluationRatioRange"":null,""shopScoreRange"":null}, +""spuSort"":{""sortOrder"":0,""sortType"":0},""category"":null},""pageContext"":{""pageContext"":null,""limit"":15}, +""requestContext"":{""sessionId"":""" + BaseClass.DateTimeToTimestampMilliseconds(DateTime.Now.AddSeconds(-Randomx.GetId(10, 100))) + @""", +""requestId"":""" + BaseClass.DateTimeToTimestampMilliseconds(DateTime.Now) + @""",""parentSearchId"":""53"",""displayedSpus"":null}, +""keyword"":""" + prodId + @"""}"; + var result = WxHttpClient.PostAssistant(shop, url, body, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxCategoryApi.cs b/Work/Api/Wx/WxCategoryApi.cs new file mode 100644 index 0000000..2dfe0f9 --- /dev/null +++ b/Work/Api/Wx/WxCategoryApi.cs @@ -0,0 +1,159 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店商品类目识别、品牌及资质接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxCategoryApi + { + /// 根据商品标题及本地图片识别推荐类目。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 商品标题;方法内部执行 UTF-8 URL 编码。 + /// 用于识别类目的本地图片文件完整路径列表;原参数名含 Url,但实际读取本地文件。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getProductClassify。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetProductClassify(ShopLoginInfo shop, string goodsName, List goodImgUrlsLst) + { + string boundaryLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + string boundaryStr = "----WebKitFormBoundary"; + for (int i = 0; i < 16; i++) + { + boundaryStr += boundaryLetters[Randomx.GetId(boundaryLetters.Length)]; + } + + string url = "https://store.weixin.qq.com/shop-faas/cgi/common/productClassify?from=B&title=" + GetHtml.UrlEncodeUtf8(goodsName) + "&token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + string body = @"--" + boundaryStr + @" +"; + foreach (var imgFullPath in goodImgUrlsLst) + { + string fileName = imgFullPath.Split('\\').LastOrDefault(); + body += @"Content-Disposition: form-data; name=""file""; filename=""" + fileName + @""" +Content-Type: image/jpeg + +" + imgFullPath + @" +--" + boundaryStr; + } + + body += @"-- +"; + var result = WxHttpClient.UploadMultipart(shop, url, body, boundaryStr, referer); + return result; + } + + /// 查询所选类目及品牌的上架要求。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 从上级到末级的类目 ID 列表,用于查询或申请类目资质。 + /// 微信品牌 ID,直接按原协议写入请求。 默认值:"2100000000"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getCategoryOnShelf。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetCategoryOnShelf(ShopLoginInfo shop, List catIdLst, string brandId = "2100000000") + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradecategory/cgi/product/getCategoryOnShelfV2?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + string body = @"{""catIdList"":[" + string.Join(",", catIdLst) + @"],""brandId"":" + brandId + "}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询类目可用品牌及品牌资质信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品类目 ID。 默认值:"547591"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getMixBrand。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetMixBrand(ShopLoginInfo shop, string catId = "547591") + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradecategory/cgi/getMixBrand?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + string body = @"{""categoryId"":""" + catId + @"""}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 检查店铺与指定商品类目的关联及经营资格。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品类目 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.batchCheckCategoryRelation。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string BatchCheckCategoryRelation(ShopLoginInfo shop, long catId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradecategory/cgi/product/batchCheckCategoryRelation?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + string body = @"{""categoryIdList"":[" + catId + "]}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 按原快捷申请流程提交类目经营资格申请。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品类目 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.addFinderCategoryV2。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string AddFinderCategoryV2(ShopLoginInfo shop, long catId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradecategory/cgi/addFinderCategoryV2?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + string body = @"{""categoryInfo"":{""categories"":[{""id"":""" + catId + @"""}]},""brandIdList"":[],""leafCategoryId"":""" + catId + @""",""reuse"":0,""applyType"":1}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 申请类目资质前查询该类目的详细要求。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品类目 ID。 + /// 从上级到末级的类目 ID 列表,用于查询或申请类目资质。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getSingleCategoryData。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetSingleCategoryData(ShopLoginInfo shop, long catId, List catIdLst) + { + var catIds = "[\"" + string.Join("\",\"", catIdLst) + "\"]"; + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradecategory/cgi/getSingleCategoryData?token=&lang=zh_CN&categoryId=" + catId; + string referer = "https://store.weixin.qq.com/shop/brandAndCat/category/apply?defaultCategoryIds=" + GetHtml.UrlEncodeUtf8(catIds) + "&source=goods&type=apply"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 提交类目资质申请资料。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 从上级到末级的类目 ID 列表,用于查询或申请类目资质。 + /// 类目资质申请模型,包含资质图片等平台要求字段。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.wxCateAuth。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxCateAuth(ShopLoginInfo shop, List catIdLst, WXCateAuthParam wxCateAuthParam) + { + var catIds = "[\"" + string.Join("\",\"", catIdLst) + "\"]"; + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradecategory/cgi/addFinderCategoryV2?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/brandAndCat/category/apply?defaultCategoryIds=" + GetHtml.UrlEncodeUtf8(catIds) + "&source=goods&type=apply"; + string data = JsonHelper.SerializeObject(wxCateAuthParam); + var result = WxHttpClient.Post(shop, url, data, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxConsignmentApi.cs b/Work/Api/Wx/WxConsignmentApi.cs new file mode 100644 index 0000000..e231ada --- /dev/null +++ b/Work/Api/Wx/WxConsignmentApi.cs @@ -0,0 +1,86 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店代发订单接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxConsignmentApi + { + /// 查询订单代发功能的开关状态。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXOrderConsignmentForm.cs,WXOrderConsignment.getConsignmentOnOffStatus。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetConsignmentOnOffStatus(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodesuppliercosignment/associate/cgi/getOpenStatus?token=&lang=zh_CN"; + string data = "{}"; + string refere = "https://store.weixin.qq.com/shop/order/consignment"; + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + + /// 分页查询代发订单,可筛选待分配订单。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 每页条数,默认值沿用原调用。 默认值:30。 + /// 是否只查询等待分配的代发订单。 默认值:false。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXOrderConsignmentForm.cs,WXOrderConsignment.searchConsignmentOrder。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchConsignmentOrder(ShopLoginInfo shop, int pageNum, int pageSize = 30, bool isWaitDevide = false) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodesuppliercosignment/order/cgi/orderSearch?token=&lang=zh_CN"; + string data = @"{""pageSize"":" + pageSize + @",""page"":" + pageNum + @",""tab"":1}"; + if (isWaitDevide) + data = @"{""pageSize"":" + pageSize + @",""page"":" + pageNum + @",""orderStatus"":20,""availCosignment"":true}"; + string refere = "https://store.weixin.qq.com/shop/order/consignment"; + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + + /// 提交代发订单分配请求。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 完整代发操作 JSON 对象,按原接口填入订单和分配信息。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXOrderConsignmentForm.cs,WXOrderConsignment.addConsignmentOrder。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string AddConsignmentOrder(ShopLoginInfo shop, JObject dataObj) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodesuppliercosignment/order/cgi/cancelAssignOrder?token=&lang=zh_CN"; + string data = JsonHelper.SerializeObject(dataObj); + string refere = "https://store.weixin.qq.com/shop/order/consignment"; + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + + /// 取消已分配的代发订单。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 完整代发操作 JSON 对象,按原接口填入订单和分配信息。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXOrderConsignmentForm.cs,WXOrderConsignment.cancelConsignmentOrder。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string CancelConsignmentOrder(ShopLoginInfo shop, JObject dataObj) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodesuppliercosignment/order/cgi/cancelAssignOrder?token=&lang=zh_CN"; + string data = JsonHelper.SerializeObject(dataObj); + string refere = "https://store.weixin.qq.com/shop/order/consignment"; + var result = WxHttpClient.Post(shop, url, data, refere); + return result; + } + } +} diff --git a/Work/Api/Wx/WxCouponApi.cs b/Work/Api/Wx/WxCouponApi.cs new file mode 100644 index 0000000..bbc0ac0 --- /dev/null +++ b/Work/Api/Wx/WxCouponApi.cs @@ -0,0 +1,89 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店店铺优惠券接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxCouponApi + { + /// 创建优惠券,保留原优惠券模型及忽略 null 字段的序列化方式。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 优惠券创建参数,沿用 AllBase 中的微信优惠券模型。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxSetCoupon。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxSetCoupon(ShopLoginInfo shop, WXCouponParamBase couponParam) + { + string url = "https://store.weixin.qq.com/shop-faas/market/coupon/cgi/makeQp?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/marketing/coupon/make?type=990"; + JsonSerializerSettings jsonSetting = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; + var prodStr = JsonConvert.SerializeObject(couponParam, jsonSetting); + string body = prodStr; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 按优惠券名称查询优惠券。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 查询用的优惠券名称。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxSearchCoupon。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxSearchCoupon(ShopLoginInfo shop, string couponName) + { + string url = "https://store.weixin.qq.com/shop-faas/market/coupon/cgi/searchCoupon?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/marketing/coupon"; + string body = "{\"pageSize\":20,\"page\":0,\"couponName\":\"" + couponName + "\",\"couponCustomizeChannel\":\"\",\"couponTypeList\":[],\"promoteTypeList\":[],\"couponStatusList\":[\"2\"]}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 结束指定优惠券的发放或活动。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信优惠券 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxEndCoupon。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxEndCoupon(ShopLoginInfo shop, string couponId) + { + string url = "https://store.weixin.qq.com/shop-faas/market/coupon/cgi/upCouponStatus?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/marketing/coupon"; + string body = @"{""couponId"":""" + couponId + @""",""status"":4}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 删除指定优惠券。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信优惠券 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxDelCoupon。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxDelCoupon(ShopLoginInfo shop, string couponId) + { + string url = "https://store.weixin.qq.com/shop-faas/market/coupon/cgi/upCouponStatus?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/marketing/coupon"; + string body = @"{""couponId"":""" + couponId + @""",""status"":5}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxCustomerServiceApi.cs b/Work/Api/Wx/WxCustomerServiceApi.cs new file mode 100644 index 0000000..28003ab --- /dev/null +++ b/Work/Api/Wx/WxCustomerServiceApi.cs @@ -0,0 +1,46 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店客服统计及消息回调接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxCustomerServiceApi + { + /// 提交 AI 客服消息推送回调验证,对应 WxAiTalkForm.Lf_SaveComplete 中的微信请求。 + /// 店铺登录信息,Cookies 为原程序保存的 Cookie 列表 JSON。 + /// 客服服务提供方返回的消息推送回调 URL。 + /// 回调验证 token,原流程传入客服应用 appId。 + /// 客服服务提供方返回的 Base64 消息加解密密钥。 + /// 微信原始 JSON 或传输错误文字;调用方检查 code,不自动重试。 + public static string VerifyCallback(ShopLoginInfo shop, string callbackUrl, string token, string base64Key) + { + var body = new JObject { ["url"] = callbackUrl, ["token"] = token, ["base64Key"] = base64Key }; + return WxHttpClient.Post(shop, WxHttpClient.ShopHost + "/shop-faas/mmecnodeservicemarket/channel/ChannelsEcVerifyCallbackInfo?token=&lang=zh_CN", + body.ToString(), WxHttpClient.ShopHost + "/shop/servicemarket/home"); + } + + /// 查询客服实时接待统计;原人工客服与 AI 客服共用此接口。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 开始时间的 Unix 毫秒时间戳;请求发送时转换为秒。 + /// 结束时间的 Unix 毫秒时间戳;请求发送时转换为含三位小数的秒。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\AiTalkForms\WxAiTalkForm.cs,WxAiTalkForm.GetKfMsgStatistics。 + /// 来源:TEMUKJD\WXForms\TalkForms\WxTalkForm.cs,WxTalkForm.GetKfMsgStatistics。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetKfMsgStatistics(ShopLoginInfo shop, long beginDateTick, long endDateTick) + { + string url = "https://store.weixin.qq.com/shop/kf/cgi/data/getRealTimeShopV2?" + "beginDate=" + (beginDateTick / 1000) + "&endDate=" + (endDateTick / 1000m).ToString("0.000"); + string referer = "https://store.weixin.qq.com/shop/kf/data/store"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxDeliveryModels.cs b/Work/Api/Wx/WxDeliveryModels.cs new file mode 100644 index 0000000..183f4bc --- /dev/null +++ b/Work/Api/Wx/WxDeliveryModels.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace Work.Api.Wx +{ + /// 修改订单物流信息的请求。对应原 WXOrderListOptimizeForm 的同名模型。 + public class UpdateDeliverInfoParam + { + /// 需要修改物流信息的微信订单 ID。 + public string orderId { get; set; } + /// 按原接口协议提供的物流变更字典列表;字典键沿用原订单返回的标识。 + public List> changeInfo { get; set; } + } + + /// 一个包裹的物流公司、运单及商品明细。 + public class UpdateDeliverChangeInfo + { + /// 微信物流公司编码;原代码以 OTHER 表示其他物流。 + public string deliveryId { get; set; } + /// 更新后的物流运单号。 + public string waybillId { get; set; } + /// 该包裹包含的商品与数量。 + public List productInfos { get; set; } = new List(); + } + + /// 物流包裹中的商品明细,字段名直接用于微信 JSON 协议。 + public class UpdateDeliverProductInfo + { + /// 微信商品 ID。 + public string productId { get; set; } + /// 商品规格 SKU ID。 + public string skuId { get; set; } + /// 本次发货商品件数;null 保留原接口的未指定语义。 + public int? productCnt { get; set; } + } +} diff --git a/Work/Api/Wx/WxFundsApi.cs b/Work/Api/Wx/WxFundsApi.cs new file mode 100644 index 0000000..711da4a --- /dev/null +++ b/Work/Api/Wx/WxFundsApi.cs @@ -0,0 +1,73 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店资金余额、保证金及订单流水接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxFundsApi + { + /// 查询店铺通用资金账户余额。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WxShopStatisticsForm.cs,WxShopStatisticsForm.GetShopAmount。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopAmount(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradefunds/cgi/generalAccount/getGeneralAccountBalance?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/funds/home"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 查询店铺保证金支付状态。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WxShopStatisticsForm.cs,WxShopStatisticsForm.GetShopBalance。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopBalance(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradedeposit/home/cgi/getDepositMPayState?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/funds/home"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 分页查询订单资金流水,用于原税务管理页面。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 默认值:1。 + /// 订单结算状态的接口筛选值,原税务查询使用 60。 默认值:60。 + /// 资金流水的订单状态筛选值,原税务查询使用 100。 默认值:100。 + /// 订单支付方式的接口筛选值,原税务查询使用 1。 默认值:1。 + /// 订单创建开始时间的 Unix 秒时间戳,0 沿用原接口的起始条件。 默认值:0。 + /// 订单创建结束时间的 Unix 秒时间戳;0 使用本机当天最后一秒。 默认值:0。 + /// 兼容原签名的参数;原实现固定请求每页 20 条,传入其他值不生效。 默认值:20。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXTaxManage.cs,WXTaxBLL.getListOrderFlow。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetListOrderFlow(ShopLoginInfo shop, int page = 1, int orderSettleState = 60, int orderState = 100, int orderPayMethod = 1, int createTimeBegin = 0, int createTimeEnd = 0, int pageSize = 20) + { + // shortcut: 原接口固定每页 20 条,确认平台支持自定义条数后再启用 pageSize 参数。 + if (createTimeEnd == 0) + createTimeEnd = BaseClass.DateTimeToTimestampSeconds(DateTime.Now.Date.AddDays(1).AddSeconds(-1)); + pageSize = 20; + var offset = Math.Max(page - 1, 0) * pageSize; + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradefunds/cgi/listOrderFlow?token=&lang=zh_CN"; + string body = @"{""orderSettleState"":" + orderSettleState + @",""orderState"":" + orderState + @",""orderPayMethod"":" + orderPayMethod + @",""createTimeBegin"":""" + createTimeBegin + @""",""createTimeEnd"":""" + createTimeEnd + @""",""paginationInfo"":{""limit"":20,""offset"":" + offset + "}}"; + string referer = "https://store.weixin.qq.com/shop/funds/moneyManage?section=3"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxHttpClient.cs b/Work/Api/Wx/WxHttpClient.cs new file mode 100644 index 0000000..a0ac421 --- /dev/null +++ b/Work/Api/Wx/WxHttpClient.cs @@ -0,0 +1,219 @@ +using AllBase; +using AllBase.Model.Result; +using CsharpHttpHelper; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; + +namespace Work.Api.Wx +{ + /// 微信小店公共 HTTP 请求层,使用指定来源的 CsharpHttpHelper.HttpHelper。 + /// 根据原 WXHttpResponseBLL 保留不同场景的 Cookie 和请求头。同步返回原始正文或错误文字;不更新 ShopLoginInfo、不重试。新业务优先调用分类 API。 + public static class WxHttpClient + { + /// 微信小店后台 HTTPS 根地址。 + public const string ShopHost = "https://store.weixin.qq.com"; + + // 原视频上传器的店铺请求发送完整 Cookie,并额外包含 logistics_magic。 + internal static string SendMedia(ShopLoginInfo shop, string path, string productId, string data = null) + { + return Send(() => + { + var item = CreateRequest(shop, ShopHost + path, ShopHost + "/shop/goods/entry?productId=" + Uri.EscapeDataString(productId), + data == null ? MethodType.Get : MethodType.Post, data); + var cookies = JsonHelper.DeserializeJsonToList(Convert.ToString(shop.Cookies)) ?? new List(); + item.Cookie = string.Concat(cookies.Select(c => c.Name + "=" + c.Value + "; ")); + item.Header["logistics_magic"] = ""; + return item; + }); + } + + /// 发送微信 GET 请求,沿用 WeiXinGetResponse 仅发送 biz 前缀 Cookie 的约定。 + /// 店铺登录信息,Cookies 为 CefSharp.Cookie 列表 JSON。 + /// store.weixin.qq.com 域名下的完整 HTTPS 请求地址。 + /// 来源页面;普通接口使用原页面地址,默认空字符串。 + /// true 模拟首页文档导航,并忽略 referer;false 模拟普通接口请求。 + /// 原始 JSON、HTML 或错误文字;调用方判断格式和业务状态。 + public static string Get(ShopLoginInfo shop, string url, string referer = "", bool isGetDocument = false) + { + return Send(() => CreateRequest(shop, url, referer, MethodType.Get, document: isGetDocument)); + } + + /// 发送微信 JSON POST 请求,携带完整 Cookie 和 biz_magic,对应 WeiXinPostResponse。 + /// 店铺登录信息,Cookies 为 CefSharp.Cookie 列表 JSON。 + /// store.weixin.qq.com 域名下的完整 HTTPS 请求地址。 + /// 完整 JSON 请求正文;按 UTF-8 编码,发送前检查 JSON 格式。 + /// 原业务页面 Referer。 + /// 原始响应正文或错误文字,不自动重试写操作。 + public static string Post(ShopLoginInfo shop, string url, string data, string referer) + { + return Send(() => CreateRequest(shop, url, referer, MethodType.Post, data)); + } + + /// 发送带货助手请求,对应 WeiXinAssistantPostResponse,使用 talent_magic 而非 biz_magic 请求头。 + /// 带货助手登录信息,Cookies 必须包含对应 talent_magic 凭证。 + /// store.weixin.qq.com 域名下的完整 HTTPS 请求地址。 + /// 完整 UTF-8 JSON 请求正文。 + /// 带货助手页面 Referer。 + /// 原始响应正文或错误文字,由调用方检查业务状态。 + public static string PostAssistant(ShopLoginInfo shop, string url, string data, string referer) + { + return Send(() => CreateRequest(shop, url, referer, MethodType.Post, data, assistant: true)); + } + + /// 发送达人客服会话请求,对应 WeiXinTalentOpenKfPostResponse,补充 user_scene=1002、kf_scene=6。 + /// 店铺登录信息,Cookies 为 CefSharp.Cookie 列表 JSON。 + /// store.weixin.qq.com 域名下的完整 HTTPS 请求地址。 + /// 完整 UTF-8 JSON 请求正文。 + /// 原达人客服业务页面 Referer。 + /// 原始响应正文或错误文字,由调用方检查业务状态。 + public static string PostCustomerService(ShopLoginInfo shop, string url, string data, string referer) + { + return Send(() => + { + var item = CreateRequest(shop, url, referer, MethodType.Post, data); + item.Header["user_scene"] = "1002"; + item.Header["kf_scene"] = "6"; + item.Header["logistics_magic"] = ""; + return item; + }); + } + + /// 对应 WeiXinImgPostResponse,将 Base64 解码为原始字节上传,不附带店铺 Cookie。 + /// 店铺登录信息,openid 用于 X-Arguments 上传签名上下文。 + /// westorealpha.video.qq.com 域名下的完整 HTTPS 图片上传地址。 + /// 纯 Base64 图片字符串,不含 data URI 前缀。 + /// 临时上传凭证 dis_k。 + /// 临时上传凭证 dis_t,原值透传。 + /// 临时上传凭证 ext,原值透传。 + /// 来源页面,默认微信小店根地址。 + /// 原始响应正文或错误文字;无效 Base64 不会发起请求。 + public static string UploadImage(ShopLoginInfo shop, string url, string base64, string dis_k, long dis_t, + string ext, string referer = ShopHost + "/") + { + return Send(() => + { + // 图片上传使用独立的视频域名和临时签名,不发送店铺 Cookie。 + var item = CreateRequest(shop, url, referer, MethodType.Post, image: true); + item.Header["X-Requested-With"] = "XMLHttpRequest"; + item.Header["X-Arguments"] = "dis_k=" + dis_k + "&dis_t=" + dis_t + "&ext=" + ext + + "&apptype=160&filetype=20304&openid=" + shop.openid + "&groupid=0&taskid=" + Guid.NewGuid(); + item.PostDataType = PostDataType.Byte; + item.PostdataByte = Convert.FromBase64String(base64); + return item; + }); + } + + /// 对应 WeiXinUploadResponse,构造 multipart 二进制正文并发送 biz 前缀 Cookie。 + /// 店铺登录信息,Cookies 为 CefSharp.Cookie 列表 JSON。 + /// store.weixin.qq.com 域名下的完整 HTTPS 上传地址。 + /// 原 multipart 文本模板;成对的 <!> 标记之间为本地文件完整路径,文本统一换行为 CRLF。 + /// 与正文分隔符一致的 multipart boundary,不含正文额外的两个前导短横线。 + /// 原上传页面 Referer。 + /// 原始响应正文或错误文字;文件不可读或标记不完整时不发送请求。 + public static string UploadMultipart(ShopLoginInfo shop, string url, string body, string boundary, string referer) + { + return Send(() => + { + var item = CreateRequest(shop, url, referer, MethodType.Post, bizCookiesOnly: true); + item.ContentType = "multipart/form-data; boundary=" + boundary; + item.PostDataType = PostDataType.Byte; + item.PostdataByte = BuildMultipartBody(body); + return item; + }); + } + + internal static byte[] BuildMultipartBody(string body) + { + if (body == null) throw new ArgumentNullException(nameof(body)); + using (var stream = new MemoryStream()) + { + // 保留原上传协议: 包围的片段为本地文件路径,其余片段规范化为 CRLF。 + var parts = body.Split(new[] { "" }, StringSplitOptions.None); + if (parts.Length % 2 == 0) throw new ArgumentException("上传文件标记必须成对出现。", nameof(body)); + for (int i = 0; i < parts.Length; i++) + { + string part = parts[i]; + byte[] bytes = i % 2 == 1 ? File.ReadAllBytes(part) : + Encoding.UTF8.GetBytes(part.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n")); + stream.Write(bytes, 0, bytes.Length); + } + return stream.ToArray(); + } + } + + internal static HttpItem CreateRequest(ShopLoginInfo shop, string url, string referer, MethodType method, + string data = null, bool assistant = false, bool document = false, bool image = false, bool bizCookiesOnly = false) + { + if (shop == null) throw new ArgumentNullException(nameof(shop)); + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || uri.Scheme != Uri.UriSchemeHttps || + (image ? uri.Host != "westorealpha.video.qq.com" : uri.Host != "store.weixin.qq.com")) + throw new ArgumentException("微信请求地址不在允许的 HTTPS 域名内。", nameof(url)); + + if (data != null) Newtonsoft.Json.Linq.JToken.Parse(data); + + var cookies = JsonHelper.DeserializeJsonToList(Convert.ToString(shop.Cookies)) ?? new List(); + var item = new HttpItem + { + URL = url, + Method = method, + Referer = document ? "" : referer, + Accept = document ? "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" : "application/json, text/plain, */*", + ContentType = method == MethodType.Post ? "application/json" : null, + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", + Encoding = Encoding.UTF8, + PostEncoding = Encoding.UTF8, + Postdata = data, + PostDataType = PostDataType.String, + KeepAlive = true, + Expect100Continue = false, + Allowautoredirect = true, + ReadWriteTimeout = 300000, + Cookie = image ? "" : string.Concat(cookies.Where(c => !(method == MethodType.Get || bizCookiesOnly) || c.Name.StartsWith("biz", StringComparison.Ordinal)).Select(c => c.Name + "=" + c.Value + ";")), + Header = new WebHeaderCollection() + }; + item.Header["sec-ch-ua"] = "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\""; + item.Header["sec-ch-ua-mobile"] = "?0"; + item.Header["sec-ch-ua-platform"] = "\"Windows\""; + item.Header["Sec-Fetch-Site"] = document ? "none" : image ? "same-site" : "same-origin"; + item.Header["Sec-Fetch-Mode"] = document ? "navigate" : "cors"; + item.Header["Sec-Fetch-Dest"] = document ? "document" : "empty"; + // HttpHelper 只解压 gzip,因此不宣告支持 br、zstd、deflate。 + item.Header[HttpRequestHeader.AcceptEncoding] = "gzip"; + item.Header[HttpRequestHeader.AcceptLanguage] = method == MethodType.Post && !image && !bizCookiesOnly ? + "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" : "zh-CN,zh;q=0.9"; + if (document) + { + item.Header["Upgrade-Insecure-Requests"] = "1"; + item.Header["Sec-Fetch-User"] = "?1"; + } + else if (!image) + { + item.Header["wecom_magic"] = ""; + item.Header["supplier_magic"] = ""; + item.Header["mcn_magic"] = ""; + item.Header["potter-scene"] = "weixinShop"; + item.Header["biz_magic"] = assistant ? "" : cookies.FirstOrDefault(c => c.Name == "biz_magic")?.Value ?? ""; + item.Header["talent_magic"] = assistant ? cookies.FirstOrDefault(c => c.Name == "talent_magic")?.Value ?? "" : ""; + } + if (method == MethodType.Post) item.Header["Origin"] = ShopHost; + return item; + } + + private static string Send(Func createRequest) + { + try + { + return HttpHelper.Request(createRequest()).Html; + } + catch (Exception ex) + { + // 兼容现有调用约定:传输失败返回错误文字,业务失败返回原 JSON。 + return ex.Message; + } + } + } +} diff --git a/Work/Api/Wx/WxMediaApi.cs b/Work/Api/Wx/WxMediaApi.cs new file mode 100644 index 0000000..279f1e1 --- /dev/null +++ b/Work/Api/Wx/WxMediaApi.cs @@ -0,0 +1,163 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店图片、文件分片及视频凭证接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxMediaApi + { + /// 获取商品视频上传临时凭证,对应 WXVideoUploader.GetUploadTempImgAuth 的店铺请求。 + /// 店铺登录信息,Cookies 为原程序保存的 Cookie 列表 JSON。 + /// 正在编辑的商品 ID,用于构造原视频上传页面 Referer。 + /// 视频上传场景,原视频上传流程使用 100。 + /// 含临时上传凭证的原始 JSON 或传输错误文字;调用方检查 code。 + /// 此方法只获取店铺端凭证;视频域名的分片、转码流程仍由原上传器实现。 + public static string GetVideoUploadAuth(ShopLoginInfo shop, string productId, string uploadScene = "100") + { + return WxHttpClient.SendMedia(shop, "/shop-faas/mmecchannelspicture/cgi/getUploadTempImgAuth?token=&lang=zh_CN&uploadScene=" + Uri.EscapeDataString(uploadScene), + productId); + } + + /// 将已完成转码的视频播放地址换成商品媒体地址,对应 WXVideoUploader.GetMultiMediaDownloadUrl。 + /// 店铺登录信息,Cookies 为原程序保存的 Cookie 列表 JSON。 + /// 正在编辑的商品 ID,用于构造原视频上传页面 Referer。 + /// 原视频上传流程轮询转码成功后返回的播放 URL。 + /// 媒体下载地址的原始 JSON 或传输错误文字;调用方检查 code。 + public static string GetMultiMediaDownloadUrl(ShopLoginInfo shop, string productId, string playUrl) + { + return WxHttpClient.SendMedia(shop, "/shop-faas/mmecchannelspicture/cgi/getMultiMediaDownloadUrl?token=&lang=zh_CN", + productId, new JObject { ["playUrl"] = playUrl }.ToString()); + } + + /// 获取图片上传临时凭证,供 UploadImage 使用。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 上传场景的接口值,图片上传原调用使用 3。 默认值:3。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getUploadTempImgAuth。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetUploadTempImgAuth(ShopLoginInfo shop, int uploadScene = 3) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecchannelspicture/cgi/getUploadTempImgAuth?token=&lang=zh_CN&uploadScene=" + uploadScene; + string refere = "https://store.weixin.qq.com/shop/goods/entry"; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + + /// 使用临时凭证向微信图片上传域名提交 Base64 图片。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 纯 Base64 图片数据,不含 data:image 前缀。 + /// GetUploadTempImgAuth 返回的临时上传签名 dis_k。 + /// GetUploadTempImgAuth 返回的临时上传时间 dis_t,原值透传。 + /// GetUploadTempImgAuth 返回的 ext 凭证,原值透传。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.adtimageupload。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string UploadImage(ShopLoginInfo shop, string imgBase64, string dis_k, long dis_t, string ext) + { + string url = "https://westorealpha.video.qq.com/adtimageupload"; + string referer = "https://store.weixin.qq.com/"; + string body = imgBase64; + var result = WxHttpClient.UploadImage(shop, url, body, dis_k, dis_t, ext, referer); + return result; + } + + /// 初始化商品编辑资料的分片文件上传。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 初始化分片上传请求的完整 JSON。 + /// 微信商品 ID。 + /// 原商品 lastEditDraftTime 值,按接口返回值透传到编辑页面上下文。 + /// 原商品 listingTime 值,按接口返回值透传到编辑页面上下文。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.initUploadPart。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string InitUploadPart(ShopLoginInfo shop, string submitJson, string prodId, int time1, int time2) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodefiletransfer/filecos/initUploadPart?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry?productId=" + prodId + "&isUpdateAuditFree=1&source=list_audit_free&continueEditInfo=" + "{%22isEditingContinued%22:true,%22lastEditDraftTime%22:" + time1 + "," + "%22listingTime%22:" + time2 + "}"; + string body = submitJson; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 上传商品编辑文件的第一个分片,使用原 multipart 协议。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 初始化分片上传接口返回的文件名。 + /// 待上传文件的本地完整路径;不是网络图片 URL。 + /// 初始化分片上传返回的上传任务 ID。 + /// 微信商品 ID。 + /// 原商品 lastEditDraftTime 值,按接口返回值透传到编辑页面上下文。 + /// 原商品 listingTime 值,按接口返回值透传到编辑页面上下文。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.uploadPartBinary。 + /// 保留旧行为:partSeq 固定为 1,本方法不负责多分片调度。文件必须存在且可读。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string UploadPartBinary(ShopLoginInfo shop, string initFileName, string blobPath, string uploadId, string prodId, int time1, int time2) + { + // shortcut: 沿用原实现仅上传 partSeq=1,需要多分片时再增加分片调度。 + string boundaryLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + string boundaryStr = "----WebKitFormBoundary"; + for (int i = 0; i < 16; i++) + { + boundaryStr += boundaryLetters[Randomx.GetId(boundaryLetters.Length)]; + } + + string url = "https://store.weixin.qq.com/shop-faas/mmecnodefiletransfer/filecos/uploadPartBinary?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry?productId=" + prodId + "&isUpdateAuditFree=1&source=list_audit_free&continueEditInfo=" + "{%22isEditingContinued%22:true,%22lastEditDraftTime%22:" + time1 + "," + "%22listingTime%22:" + time2 + "}"; + string body = @"--" + boundaryStr + @" +Content-Disposition: form-data; name=""name"" + +" + initFileName + @" +--" + boundaryStr + @" +Content-Disposition: form-data; name=""uploadId"" + +" + uploadId + @" +--" + boundaryStr + @" +Content-Disposition: form-data; name=""partSeq"" + +1 +--" + boundaryStr + @" +Content-Disposition: form-data; name=""file""; filename=""blob"" +Content-Type: application/octet-stream + +" + blobPath + @" +--" + boundaryStr + @"-- +"; + var result = WxHttpClient.UploadMultipart(shop, url, body, boundaryStr, referer); + return result; + } + + /// 提交分片上传完成信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 分片上传完成请求的完整 JSON,需包含此前上传返回的信息。 + /// 微信商品 ID。 + /// 原商品 lastEditDraftTime 值,按接口返回值透传到编辑页面上下文。 + /// 原商品 listingTime 值,按接口返回值透传到编辑页面上下文。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.completeUploadPart。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string CompleteUploadPart(ShopLoginInfo shop, string completeJson, string prodId, int time1, int time2) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodefiletransfer/filecos/completeUploadPart?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry?productId=" + prodId + "&isUpdateAuditFree=1&source=list_audit_free&continueEditInfo=" + "{%22isEditingContinued%22:true,%22lastEditDraftTime%22:" + time1 + "," + "%22listingTime%22:" + time2 + "}"; + string body = completeJson; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxOrderApi.cs b/Work/Api/Wx/WxOrderApi.cs new file mode 100644 index 0000000..37ace01 --- /dev/null +++ b/Work/Api/Wx/WxOrderApi.cs @@ -0,0 +1,245 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店订单查询、备注、解密及发货接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxOrderApi + { + /// 按订单状态分页查询订单,可指定创建时间范围。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单状态筛选值,原列表查询默认使用 20;以当前接口枚举为准。 默认值:20。 + /// 页码,从 1 开始。 默认值:1。 + /// 每页条数,默认值沿用原调用。 默认值:30。 + /// 订单创建开始时间的 Unix 毫秒时间戳;与 timeEnd 同时提供才生效。 默认值:null。 + /// 订单创建结束时间的 Unix 毫秒时间戳;与 timeStart 同时提供才生效。 默认值:null。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.orderListSearch。 + /// 保留旧行为:同时指定开始与结束时间时,实际 pageSize 固定为 30。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string OrderListSearch(ShopLoginInfo shop, int orderStatus = 20, int pageNum = 1, int pageSize = 30, long? timeStart = null, long? timeEnd = null) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/list/cgi/orderSearch?token=&lang=zh_CN"; + string body = @"{""isSearch"":false,""pageSize"":" + pageSize + @",""overTime"":0,""pengingShipOneDay"":0,""pengingShipSixHour"":0,""expediteDelivery"":0,""stockoutPunishAlert"":0,""waybillStatus"":0,""pageNum"":1" + @",""onAftersaleOrderExist"":0,""orderStatus"":" + orderStatus + @"}"; + if (pageNum > 1) + body = @"{""isSearch"":false,""pageSize"":" + pageSize + @",""overTime"":0,""pengingShipOneDay"":0,""pengingShipSixHour"":0,""expediteDelivery"":0,""stockoutPunishAlert"":0,""waybillStatus"":0,""pageNum"":1" + @",""onAftersaleOrderExist"":0,""orderStatus"":" + orderStatus + @",""page"":" + pageNum + @"}"; + if (timeStart != null && timeEnd != null) + body = @"{""isSearch"":true,""pageSize"":30,""orderId"":"""",""orderTime"":{""type"":0,""time"":[" + timeStart + @"," + timeEnd + @"]}, +""telNumber"":"""",""title"":"""",""receiverName"":"""",""finderNickName"":"""",""merchantNotes"":"""",""merchantNotesTagColor"":0, +""waybillId"":"""",""productCode"":"""",""inspectStatus"":"""",""finderWay"":0,""searchOrderType"":0,""payMethod"":0,""orderChannel"":0, +""bulkbuyActivityId"":"""",""deliveryNegotiationFlag"":0,""transactionId"":"""",""page"":" + pageNum + @",""overTime"":0,""pengingShipOneDay"":0, +""pengingShipSixHour"":0,""expediteDelivery"":0,""stockoutPunishAlert"":0,""waybillStatus"":0,""pageNum"":1,""onAftersaleOrderExist"":0, +""orderStatus"":" + orderStatus + @",""startCreateTime"":" + timeStart + @",""endCreateTime"":" + timeEnd + @"}"; + string refere = "https://store.weixin.qq.com/shop/order/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 查询订单;单参数重载按订单号查询,双参数重载按商品标题和收件人查询。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.orderSearch。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string OrderSearch(ShopLoginInfo shop, string orderId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/list/cgi/orderSearch?token=&lang=zh_CN"; + string body = @"{""isSearch"":true,""pageSize"":30,""overTime"":0,""pengingShipOneDay"":0,""pengingShipSixHour"":0,""expediteDelivery"":0,""stockoutPunishAlert"":0, +""waybillStatus"":0,""pageNum"":1,""onAftersaleOrderExist"":0,""orderStatus"":0,""orderId"":""" + orderId + @""", +""orderTime"":{""type"":0,""time"":[null,null]},""telNumber"":"""",""title"":"""",""receiverName"":"""",""finderNickName"":"""", +""merchantNotes"":"""",""merchantNotesTagColor"":0,""waybillId"":"""",""productCode"":"""",""inspectStatus"":"""",""finderWay"":0, +""searchOrderType"":0,""payMethod"":0,""orderChannel"":0,""bulkbuyActivityId"":"""",""deliveryNegotiationFlag"":0,""transactionId"":"""", +""page"":1,""startCreateTime"":null,""endCreateTime"":null}"; + string refere = "https://store.weixin.qq.com/shop/order/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 给订单添加商家备注及颜色标签。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单 ID。 + /// 商家订单备注文本。 + /// 商家备注颜色的接口值,0 沿用原代码默认值。 默认值:0。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.addNotes。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string AddNotes(ShopLoginInfo shop, string orderId, string notes, int tagColor = 0) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/common/cgi/batchMerchantNotes?token=&lang=zh_CN"; + string body = @"{""items"":[{""orderId"":""" + orderId + @""",""merchantNotes"":""" + notes + @""",""merchantNotesTagColor"":" + tagColor + @"}]}"; + string refere = "https://store.weixin.qq.com/shop/order/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 申请解密订单信息,返回平台允许显示的收件信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.decryptOrder。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string DecryptOrder(ShopLoginInfo shop, string orderId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/common/cgi/decryptOrder?token=&lang=zh_CN"; + string body = @"{""id"":""" + orderId + @"""}"; + string refere = "https://store.weixin.qq.com/shop/order/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 提交订单发货,原实现使用 OTHER 物流公司编码。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单 ID。 + /// 物流运单号。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.orderSendOut。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string OrderSendOut(ShopLoginInfo shop, string orderId, string waybillId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/ship/cgi/orderSendOut?token=&lang=zh_CN"; + string body = @"{""orderId"":""" + orderId + @""",""deliveryProduct"":{""deliveryId"":""OTHER"",""waybillId"":""" + waybillId + @""",""isAllProduct"":true,""deliverType"":1}}"; + string refere = "https://store.weixin.qq.com/shop/order/list?type=3"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 修改订单已登记的物流公司、运单号和包裹商品信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 物流变更参数,包含订单号及各包裹变更明细。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.updateDeliveryInfo。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string UpdateDeliveryInfo(ShopLoginInfo shop, UpdateDeliverInfoParam param) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/ship/cgi/updateDeliveryInfo?token=&lang=zh_CN"; + string body = JsonHelper.SerializeObject(param); + string refere = "https://store.weixin.qq.com/shop/order/list?type=3"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 查询订单发货初始化数据,用于取得包裹和物流相关信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.initDeliveryInfo。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string InitDeliveryInfo(ShopLoginInfo shop, string orderId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/ship/cgi/initShipData?token=&lang=zh_CN"; + string body = @"{""id"":""" + orderId + @"""}"; + string refere = "https://store.weixin.qq.com/shop/order/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 查询订单;单参数重载按订单号查询,双参数重载按商品标题和收件人查询。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 订单中的商品标题筛选条件。 + /// 收件人姓名筛选条件。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.orderSearch。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string OrderSearch(ShopLoginInfo shop, string prodTitle, string receiverName) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/list/cgi/orderSearch?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/order/list"; + string body = @"{""isSearch"":true,""pageSize"":30,""orderId"":"""",""orderTime"":{""type"":0,""time"":[null,null]}, +""telNumber"":"""",""title"":""复古刺绣盘扣麻棉衬衫女2022夏新款中式禅意茶服宽松短款v领上衣"",""receiverName"":""浅菊夏離、"",""finderNickName"":"""", +""merchantNotes"":"""",""merchantNotesTagColor"":0,""waybillId"":"""",""productCode"":"""",""inspectStatus"":"""",""finderWay"":0,""searchOrderType"":0, +""payMethod"":0,""orderChannel"":0,""bulkbuyActivityId"":"""",""deliveryNegotiationFlag"":0,""transactionId"":"""",""page"":1,""startCreateTime"":null, +""endCreateTime"":null}"; + body = @"{""isSearch"":true,""pageSize"":30,""orderId"":"""",""orderTime"":{""type"":0,""time"":[null,null]}, +""telNumber"":"""",""title"":""" + prodTitle + @""",""receiverName"":""" + receiverName + @""",""finderNickName"":"""",""merchantNotes"":"""",""merchantNotesTagColor"":0,""waybillId"":"""",""productCode"":"""",""inspectStatus"":"""",""finderWay"":0,""searchOrderType"":0,""payMethod"":0,""orderChannel"":0,""bulkbuyActivityId"":"""",""deliveryNegotiationFlag"":0,""transactionId"":"""",""page"":1,""startCreateTime"":null,""endCreateTime"":null}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 按订单号字符串查询订单列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 订单号查询字符串,格式沿用原订单查询接口。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.orderSearchByOrderId。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchOrdersByIds(ShopLoginInfo shop, string orderIds) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/list/cgi/orderSearch?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/order/list"; + string body = @"{""isSearch"":true,""pageSize"":30,""orderId"":""" + orderIds + @""",""orderTime"":{""type"":0,""time"":[null,null]},""telNumber"":"""",""title"":"""",""receiverName"":"""",""finderNickName"":"""",""merchantNotes"":"""",""merchantNotesTagColor"":0,""waybillId"":"""",""productCode"":"""",""inspectStatus"":"""",""finderWay"":0,""searchOrderType"":0,""payMethod"":0,""orderChannel"":0,""bulkbuyActivityId"":"""",""deliveryNegotiationFlag"":0,""transactionId"":"""",""page"":1,""startCreateTime"":null,""endCreateTime"":null}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 提交订单补发,包含物流和补发商品明细。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单 ID。 + /// 微信物流公司编码。 + /// 物流运单号。 + /// 微信商品 ID。 + /// 商品规格 SKU ID。 + /// 本次补发的商品件数。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.orderCompensationDelivery。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string OrderCompensationDelivery(ShopLoginInfo shop, string orderId, string deliveryId, string waybillId, string productId, string skuId, int productCnt) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/ship/cgi/compensationDelivery?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/order/list"; + string body = @"{""orderId"":""" + orderId + @""",""reason"":1,""deliveryProductInfo"":[{""deliveryId"":""" + deliveryId + @""",""waybillId"":""" + waybillId + @""",""productInfos"":[{""productId"":""" + productId + @""",""skuId"":""" + skuId + @""",""productCnt"":" + productCnt + @"}]}]}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 按订单号或手机号查询订单,支持指定订单时间范围。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单 ID。 + /// 收件联系电话或原查询接口支持的手机号条件。 + /// 微信订单状态筛选值,原列表查询默认使用 20;以当前接口枚举为准。 默认值:20。 + /// 页码,从 1 开始。 默认值:1。 + /// Unix 毫秒时间戳字符串;空值使用本机一个月前当天零点。 默认值:""。 + /// Unix 毫秒时间戳字符串;空值使用本机今天零点。 默认值:""。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\TalkForms\PDDBuyerOrderListForm.cs,WxOrder.OrderSeach。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchOrdersByContact(ShopLoginInfo shop, string orderId, string telNumber, int orderStatus = 20, int page = 1, string startTime = "", string endTime = "") + { + if (string.IsNullOrWhiteSpace(startTime)) + startTime = BaseClass.DateTimeToTimestampMilliseconds(DateTime.Now.Date.AddMonths(-1)).ToString(); + if (string.IsNullOrWhiteSpace(endTime)) + endTime = BaseClass.DateTimeToTimestampMilliseconds(DateTime.Now.Date).ToString(); + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeorder/list/cgi/orderSearch?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/order/list"; + string body = @"{""isSearch"":true,""pageSize"":100,""overTime"":0,""pengingShipOneDay"":0,""pengingShipSixHour"":0,""expediteDelivery"":0," + @"""stockoutPunishAlert"":0,""waybillStatus"":0,""pageNum"":1,""onAftersaleOrderExist"":0,""orderStatus"":" + orderStatus + @",""page"":" + page + @",""orderId"":""" + orderId + @""",""orderTime"":{""type"":0,""time"":[" + startTime + @"," + endTime + @"]}," + @"""telNumber"":""" + telNumber + @""",""title"":"""",""receiverName"":"""",""finderNickName"":"""",""merchantNotes"":""""," + @"""merchantNotesTagColor"":0,""waybillId"":"""",""productCode"":"""",""inspectStatus"":"""",""finderWay"":0,""searchOrderType"":0," + @"""payMethod"":0,""orderChannel"":0,""bulkbuyActivityId"":"""",""deliveryNegotiationFlag"":0,""transactionId"":""""," + @"""startCreateTime"":" + startTime + @",""endCreateTime"":" + endTime + @"}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxProductApi.cs b/Work/Api/Wx/WxProductApi.cs new file mode 100644 index 0000000..562d669 --- /dev/null +++ b/Work/Api/Wx/WxProductApi.cs @@ -0,0 +1,595 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店商品查询、发布、编辑、库存及清理接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxProductApi + { + /// 获取平台推荐商品及指定时间范围内的推荐表现。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 统计开始时间的 Unix 毫秒时间戳。 + /// 统计结束时间的 Unix 毫秒时间戳。 + /// 页码,从 1 开始。 默认值:1。 + /// 每页条数,默认值沿用原调用。 默认值:10。 + /// 统计区间对应的平台日期范围值,原调用使用 30 天。 默认值:30。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\BLL\Wx\WxPlatformRecommendProdManage.cs,WxPlatformRecommendProdManage.getPlatformRecommend。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetPlatformRecommend(ShopLoginInfo shop, long startMs, long endMs, int page = 1, int pageSize = 10, int dateRange = 30) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradebase/cgi/shop/getShopRecommendProducts?token=&lang=zh_CN"; + string data = @"{""isToday"":false,""offset"":" + Math.Max(0, page - 1) * pageSize + @",""limit"":" + pageSize + @",""sort"":[""product_view_uv"",""desc""]," + @"""time_query"":{""dateFormat"":2,""dateType"":0,""dateRange"":" + dateRange + @",""startMs"":" + startMs + @",""endMs"":" + endMs + @"}}"; + string referer = "https://store.weixin.qq.com/shop/platformrecommend/recommendationFeed"; + var result = WxHttpClient.Post(shop, url, data, referer); + return result; + } + + /// 按销售状态查询商品列表,使用原平台推荐页面的排序方式。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 默认值:1。 + /// 每页条数,默认值沿用原调用。 默认值:50。 + /// 商品销售状态:-1 全部、5 在售、11 下架,沿用原代码。 默认值:5。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\BLL\Wx\WxPlatformRecommendProdManage.cs,WxPlatformRecommendProdManage.searchProductWithSaleStatus。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchProductWithSaleStatus(ShopLoginInfo shop, int pageNum = 1, int pageSize = 50, int status = 5) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct?token=&lang=zh_CN&pageSize=" + pageSize + "&productStatus[]=-1&productSource=[1,16,32]&searchSource=1&useNew=true&fromProductManager=1&pageNum=" + pageNum + "&status[]=" + status + "&sortType=6"; + string refere = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_ON¤tPage=" + pageNum; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + + /// 批量查询商品指标,用于平台推荐商品的数据分析。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 SPU ID 列表。 + /// 页码,从 1 开始。 默认值:1。 + /// 每页条数,默认值沿用原调用。 默认值:50。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\BLL\Wx\WxPlatformRecommendProdManage.cs,WxPlatformRecommendProdManage.getProductMetricList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetProductMetricList(ShopLoginInfo shop, List spuIdLst, int pageNum = 1, int pageSize = 50) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/getProductMetricList?token=&lang=zh_CN"; + string refere = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_ON¤tPage=" + pageNum; + string body = @"{""spuIds"":[""10000941685997"",""10000941606843"",""10000941606678"",""10000941814176"",""10000941730758"",""10000941794530"",""10000941656198"",""10000941838792"",""10000941813664"",""10000941507483"",""10000941644842"",""10000941511634"",""10000941566070"",""10000941773596"",""10000941605208"",""10000941768333"",""10000941442087"",""10000941595429"",""10000941600162"",""10000941382412"",""10000941506207"",""10000941599882"",""10000941743120"",""10000941708791"",""10000941480748"",""10000941505847"",""10000941377556"",""10000941327242"",""10000941519895"",""10000941574393"",""10000941708024"",""10000941543916"",""10000941445436"",""10000941287155"",""10000941297349"",""10000941543530"",""10000941603084"",""10000941360832"",""10000941484113"",""10000941137242"",""10000941468968"",""10000941271777"",""10000941563120"",""10000941528202"",""10000941226651"",""10000941136287"",""10000940942388"",""10000941389756"",""10000941261318"",""10000940962314""],""limit"":300,""offset"":0,""sortMetricCode"":""shopxspu_growth_pay_product_cnt_30d_2"",""sortRule"":""desc"",""sceneType"":""0""}"; + var data = new Dictionary(); + data["spuIds"] = spuIdLst; + data["limit"] = pageSize * 6; + data["offset"] = 0; + data["sortMetricCode"] = "shopxspu_growth_pay_product_cnt_30d_2"; + data["sortRule"] = "desc"; + data["sceneType"] = "0"; + body = JsonHelper.SerializeObject(data); + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 修改商品 SKU 售价;结构化参数重载可提交完整价格变更列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 SPU ID。 + /// 商品规格 SKU ID。 + /// 新的 SKU 售价,单位为分,直接作为 salePrice 发送。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.EditPrice。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string EditPrice(ShopLoginInfo shop, string spuId, string skuId, int newPrice) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/listingUpdatePrice?token=&lang=zh_CN"; + string body = @"{""skuList"":[{""spuId"":" + spuId + @",""skuId"":""" + skuId + @""",""salePrice"":" + newPrice + @"}],""uploadSource"":6}"; + string refere = "https://store.weixin.qq.com/shop/goods/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 修改商品 SKU 售价;结构化参数重载可提交完整价格变更列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 包含 skuList 等字段的完整改价 JSON 对象;salePrice 单位为分。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.EditPrice。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string EditPrice(ShopLoginInfo shop, JObject priceData) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/listingUpdatePrice?token=&lang=zh_CN"; + string body = JsonHelper.SerializeObject(priceData); + string refere = "https://store.weixin.qq.com/shop/goods/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 按商品 ID 批量查询 SKU 库存明细。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 逗号分隔的商品 ID 数值字符串,直接放入 productIdList 数组。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.getSkuBySpu。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetSkuBySpu(ShopLoginInfo shop, string spuIds) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/getStockBySpu?token=&lang=zh_CN"; + string body = @"{""productIdList"":[" + spuIds + @"]}"; + string refere = "https://store.weixin.qq.com/shop/goods/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 按增量增加指定 SKU 库存。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 SPU ID。 + /// 商品规格 SKU ID。 + /// 增减库存件数的整数字符串,原代码直接写入 JSON 数值位置。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.batchUpdateStockAdd。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string BatchUpdateStockAdd(ShopLoginInfo shop, string spuId, string skuId, string num) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/batchUpdateStock?token=&lang=zh_CN"; + string body = @"{""skuList"":[{""spuId"":""" + spuId + @""",""skuId"":""" + skuId + @""",""diffType"":1,""num"":" + num + "}]}"; + string refere = "https://store.weixin.qq.com/shop/goods/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 按减量减少指定 SKU 库存。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 SPU ID。 + /// 商品规格 SKU ID。 + /// 增减库存件数的整数字符串,原代码直接写入 JSON 数值位置。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.batchUpdateStockReduce。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string BatchUpdateStockReduce(ShopLoginInfo shop, string spuId, string skuId, string num) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/batchUpdateStock?token=&lang=zh_CN"; + string body = @"{""skuList"":[{""spuId"":""" + spuId + @""",""skuId"":""" + skuId + @""",""diffType"":2,""num"":" + num + "}]}"; + string refere = "https://store.weixin.qq.com/shop/goods/list"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 按商家商品编码查询在售商品,可追加第二个编码。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 商家商品编码,用于 spuCode 查询。 + /// 可选的第二个商家商品编码,非空时与第一个编码用逗号连接。 默认值:""。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.searchProduct。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchProductByCode(ShopLoginInfo shop, string extCode, string extCode2 = "") + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct?" + "token=&lang=zh_CN&pageSize=20&productStatus[]=-1&productSource=[1,16,32]&searchSource=1&useNew=true&pageNum=1&status[]=5&spuCode=" + extCode; + if (!string.IsNullOrEmpty(extCode2)) + url += "," + extCode2; + string refere = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_ON"; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + + /// 按微信商品 SPU ID 查询商品。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 SPU ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.searchProductWithSpuId。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchProductWithSpuId(ShopLoginInfo shop, string spuId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct?" + "token=&lang=zh_CN&pageSize=20&productStatus[]=-1&productSource=[1,16,32]&searchSource=1&useNew=true&pageNum=1&status[]=-1&spuId=" + spuId; + string refere = "https://store.weixin.qq.com/shop/goods/list"; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + + /// 使用已经 URL 编码的标题查询商品。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 已经执行 UTF-8 URL 编码的商品标题,本方法不会再次编码。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.searchProductWithTitle。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchProductWithTitle(ShopLoginInfo shop, string urlEncodeTitle) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct?" + "token=&lang=zh_CN&pageSize=20&productStatus[]=-1&productSource=[1,16,32]&searchSource=1&useNew=true&pageNum=1&status[]=-1&title=" + urlEncodeTitle; + string refere = "https://store.weixin.qq.com/shop/goods/list"; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + + /// 根据商品标题、图片和类目生成短标题。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 短标题生成参数,包含原商品标题、图片及类目。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.genProductShortTitle。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GenProductShortTitle(ShopLoginInfo shop, WXGetShortTitleParam getShortTitleParam) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductupload/common/cgi/goods/genProductShortTitle?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + string body = JsonHelper.SerializeObject(getShortTitleParam); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 保存商品发布草稿;序列化时忽略 null 字段。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 商品发布或编辑参数,沿用 AllBase 中的 WXUploadProductParam。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.uploadProductDraft。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string UploadProductDraft(ShopLoginInfo shop, WXUploadProductParam prodParam) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductupload/common/cgi/goods/uploadProductDraft?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + JsonSerializerSettings jsonSetting = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; + prodParam.type = 1; + var prodStr = JsonConvert.SerializeObject(prodParam, jsonSetting); + JObject jObj = new JObject(); + jObj.Add("data", prodStr); + string body = JsonHelper.SerializeObject(jObj); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 提交商品发布或更新请求;序列化时忽略 null 字段。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 商品发布或编辑参数,沿用 AllBase 中的 WXUploadProductParam。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.upProductV2。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string UpProductV2(ShopLoginInfo shop, WXUploadProductParam prodParam) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/upProductV2?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + JsonSerializerSettings jsonSetting = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; + var prodStr = JsonConvert.SerializeObject(prodParam, jsonSetting); + JObject jObj = new JObject(); + jObj.Add("data", prodStr); + string body = JsonHelper.SerializeObject(jObj); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询商品草稿上传状态。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.checkProductUpload。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string CheckProductUpload(ShopLoginInfo shop, string productId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/checkUploadProductDraftStatus?token=&lang=zh_CN&productKey=%7B%22productId%22:" + productId + "%7D&preview=false"; + string referer = "https://store.weixin.qq.com/shop/goods/entry?productId=" + productId + "&isUpdateAuditFree=0&source=list_edit&continueEditInfo=" + "{%22isEditingContinued%22:false,%22lastEditDraftTime%22:0,%22listingTime%22:0}"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 在编辑状态商品列表中按商品 ID 查询上传结果。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.SearchProductUpload。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchProductUpload(ShopLoginInfo shop, string productId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct?token=&lang=zh_CN&pageSize=20&productStatus[]=-1" + "&productSource=[1,16,32]&searchSource=1&useNew=true&fromProductManager=1&pageNum=1&status[]=2&spuId=" + productId; + string referer = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_EDIT"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 提交原始商品 JSON 发布或更新请求。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 完整商品发布或编辑 JSON 字符串。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.upProductJObjectV2。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string UpProductJObjectV2(ShopLoginInfo shop, string prodStr) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/upProductV2?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry"; + JObject jObj = new JObject(); + jObj.Add("data", prodStr); + string body = JsonHelper.SerializeObject(jObj); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 获取商品编辑详情,用于在原商品数据基础上修改。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.getEditProductV2。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetEditProductV2(ShopLoginInfo shop, string productId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/getEditProductV2?token=&lang=zh_CN&productKey=%7B%22productId%22:" + productId + "%7D&needRealStock=true&preview=false&release=false"; + string referer = "https://store.weixin.qq.com/shop/goods/entry?productId=" + productId + "&isUpdateAuditFree=0&source=list_edit&continueEditInfo={%22isEditingContinued%22:false,%22lastEditDraftTime%22:0,%22listingTime%22:0}"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 提交免审商品编辑;支持商品模型或原始 JSON 请求体。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 商品发布或编辑参数,沿用 AllBase 中的 WXUploadProductParam。 + /// 微信商品 ID。 + /// 原商品 lastEditDraftTime 值,按接口返回值透传到编辑页面上下文。 + /// 原商品 listingTime 值,按接口返回值透传到编辑页面上下文。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.listingAuditFree。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string ListingAuditFree(ShopLoginInfo shop, WXUploadProductParam prodParam, string prodId, int time1, int time2) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/listingAuditFree?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry?productId=" + prodId + "&isUpdateAuditFree=1&source=list_audit_free&continueEditInfo=" + "{%22isEditingContinued%22:true,%22lastEditDraftTime%22:" + time1 + "," + "%22listingTime%22:" + time2 + "}"; + JsonSerializerSettings jsonSetting = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; + var prodStr = JsonConvert.SerializeObject(prodParam, jsonSetting); + JObject jObj = new JObject(); + jObj.Add("data", prodStr); + string body = JsonHelper.SerializeObject(jObj); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 提交免审商品编辑;支持商品模型或原始 JSON 请求体。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 完整商品发布或编辑 JSON 字符串。 + /// 微信商品 ID。 + /// 原商品 lastEditDraftTime 值,按接口返回值透传到编辑页面上下文。 + /// 原商品 listingTime 值,按接口返回值透传到编辑页面上下文。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.listingAuditFree。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string ListingAuditFree(ShopLoginInfo shop, string prodStr, string prodId, int time1, int time2) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/listingAuditFree?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry?productId=" + prodId + "&isUpdateAuditFree=1&source=list_audit_free&continueEditInfo=" + "{%22isEditingContinued%22:true,%22lastEditDraftTime%22:" + time1 + "," + "%22listingTime%22:" + time2 + "}"; + JsonSerializerSettings jsonSetting = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; + JObject jObj = new JObject(); + jObj.Add("data", prodStr); + string body = JsonHelper.SerializeObject(jObj); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 获取商品信息的 AI 推荐内容。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 商品信息推荐参数,包含商品 ID 等原接口字段。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.generalProductInfoRecommend。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GeneralProductInfoRecommend(ShopLoginInfo shop, WXUploadProductInfoRecommendParam recommendParam) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductupload/aiRecommendCompat/cgi/goods/generalProductInfoRecommend?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/entry?productId=" + recommendParam.productId + "&isUpdateAuditFree=0&source=list_edit&continueEditInfo={%22isEditingContinued%22:false,%22lastEditDraftTime%22:0,%22listingTime%22:0}"; + JsonSerializerSettings jsonSetting = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; + string body = JsonConvert.SerializeObject(recommendParam, jsonSetting); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 按状态、商品 ID 列表及排序查询商品,用于商品清理页面。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 每页条数,默认值沿用原调用。 默认值:50。 + /// 逗号分隔的商品 ID 字符串;批量操作直接放入 JSON 数组,不含方括号。 默认值:null。 + /// 商品销售状态:-1 全部、5 在售、11 下架,沿用原代码。 默认值:5。 + /// 商品排序的接口值;null 使用平台默认排序。 默认值:null。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdClearNoVisit260606Form.cs,WXClearNoVisit260606.searchProduct。 + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdClearNoVisitForm.cs,WXClearNoVisit.searchProduct。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchProduct(ShopLoginInfo shop, int pageNum, int pageSize = 50, string prodIds = null, int status = 5, int? sortType = null) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct?token=&lang=zh_CN&" + "pageSize=" + pageSize + "&productStatus[]=-1&productSource=[1,16,32]&searchSource=1&useNew=true&pageNum=" + pageNum + "&status[]=" + status; + if (prodIds != null) + url += "&spuId=" + prodIds; + if (sortType != null) + url += "&sortType=" + sortType; + string refere = "https://store.weixin.qq.com/shop/goods/list"; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + + /// 批量下架商品。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 逗号分隔的商品 ID 字符串;批量操作直接放入 JSON 数组,不含方括号。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdClearNoVisit260606Form.cs,WXClearNoVisit260606.DownProd。 + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdClearNoVisitForm.cs,WXClearNoVisit.DownProd。 + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdPreSaleForm.cs,WXProdNoCheckEditHelper.DownProd。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string DownProd(ShopLoginInfo shop, string prodIds) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/BatchUnListing?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/list"; + string body = @"{""ids"":[" + prodIds + @"],""uploadSource"":6}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 批量删除商品至回收站。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 逗号分隔的商品 ID 字符串;批量操作直接放入 JSON 数组,不含方括号。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdClearNoVisit260606Form.cs,WXClearNoVisit260606.DelProd。 + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdClearNoVisitForm.cs,WXClearNoVisit.DelProd。 + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdPreSaleForm.cs,WXProdNoCheckEditHelper.DelProd。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string DelProd(ShopLoginInfo shop, string prodIds) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/batchDel?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/list"; + string body = @"{""ids"":[" + prodIds + @"],""uploadSource"":6}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 从回收站彻底删除商品;调用前由业务方确认目标商品。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 逗号分隔的商品 ID 字符串;批量操作直接放入 JSON 数组,不含方括号。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdClearNoVisit260606Form.cs,WXClearNoVisit260606.RealDelProd。 + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdClearNoVisitForm.cs,WXClearNoVisit.RealDelProd。 + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdPreSaleForm.cs,WXProdNoCheckEditHelper.RealDelProd。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string RealDelProd(ShopLoginInfo shop, string prodIds) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductlist/cgi/goods/batchRealDel?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_RECYCLE_BIN"; + string body = @"{""ids"":[" + prodIds + @"],""uploadSource"":6}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 按预售类型、销售状态及排序查询商品。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 预售类型的接口筛选值;null 不增加预售类型筛选。 默认值:null。 + /// 每页条数,默认值沿用原调用。 默认值:50。 + /// 商品排序的接口值;null 使用平台默认排序。 默认值:null。 + /// 商品销售状态:-1 全部、5 在售、11 下架,沿用原代码。 默认值:5。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdPreSaleForm.cs,WXProdNoCheckEditHelper.searchProduct。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchPresaleProduct(ShopLoginInfo shop, int pageNum, int? presaleType = null, int pageSize = 50, int? sortType = null, int status = 5) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct?token=&lang=zh_CN&" + "pageSize=" + pageSize + "&productStatus[]=-1&productSource=[1,16,32]&searchSource=1&useNew=true&pageNum=" + pageNum + "&status[]=" + status; + if (presaleType != null) + url += "&presaleType[]=" + presaleType; + if (sortType != null) + url += "&sortType=" + sortType; + string refere = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_ON"; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + + /// 使用游标分页扫描商品预览信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 上一页返回的游标,首页传空字符串。 默认值:""。 + /// 每页条数,默认值沿用原调用。 默认值:50。 + /// 销售状态筛选值,原商品列表默认 5 表示在售。 默认值:5。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdPreSaleForm.cs,WXProdNoCheckEditHelper.scanProductPreview。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string ScanProductPreview(ShopLoginInfo shop, int page, string nextKey = "", int pageSize = 50, int saleStatus = 5) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/scanProductPreview?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_ON"; + if (page > 1) + referer = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_ON¤tPage=" + page; + JObject bodyObj = new JObject(); + bodyObj["pageSize"] = pageSize; + bodyObj["productStatus"] = new JArray() + { + -1 + }; + bodyObj["productSource"] = "[1,16,32]"; + bodyObj["searchSource"] = 1; + bodyObj["useNew"] = true; + bodyObj["pageNum"] = page; + bodyObj["status"] = new JArray() + { + saleStatus + }; + if (!string.IsNullOrEmpty(nextKey)) + { + bodyObj["nextKey"] = nextKey; + } + + string body = JsonHelper.SerializeObject(bodyObj); + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 获取商品推广素材或链接信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID。 + /// 页码,从 1 开始;部分接口仅用于页面 Referer。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdPreSaleForm.cs,WXProdNoCheckEditHelper.GetPromotionLink。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetPromotionLink(ShopLoginInfo shop, string prodId, int pageId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodematerial/product?token=&lang=zh_CN&productId=" + prodId; + string referer = "https://store.weixin.qq.com/shop/goods/list?currentPage=" + pageId; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxPromotionApi.cs b/Work/Api/Wx/WxPromotionApi.cs new file mode 100644 index 0000000..eadc60d --- /dev/null +++ b/Work/Api/Wx/WxPromotionApi.cs @@ -0,0 +1,131 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店商品联盟推广接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxPromotionApi + { + /// 在编辑商品时设置联盟及团长推广开关和佣金。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 普通联盟推广开关的接口原始值。 + /// 普通联盟推广佣金比例的接口原始整数值,不在本方法内换算。 + /// 团长推广开关的接口原始值。 + /// 团长推广佣金比例的接口原始整数值,不在本方法内换算。 + /// 微信商品 ID。 + /// 推广更新类型的接口原始值。 默认值:2。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.addLeagueItemForUpdatingProduct。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string AddLeagueItemForUpdatingProduct(ShopLoginInfo shop, int itemSwitch, int itemRatio, int headSupplierItemSwitch, int HeadSupplierItemRatio, long prodId, int updType = 2) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeproductupload/common/cgi/goods/addLeagueItemForUpdatingProduct?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/home"; + string body = @"{""itemSwitch"":" + itemSwitch + @",""itemRatio"":" + itemRatio + @",""headSupplierPublicItemSwitch"":" + headSupplierItemSwitch + @",""headSupplierPublicItemServiceRatio"":" + HeadSupplierItemRatio + @",""productId"":" + prodId + @",""scene"":2,""updType"":" + updType + @"}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询商品是否满足推广条件。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID 列表。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxGetCanPromotion。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxGetCanPromotion(ShopLoginInfo shop, List prodIdLst) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/plan/getItemList?token=&lang=zh_CN"; + foreach (var prodId in prodIdLst) + { + url += "&spuIdList[]=" + prodId; + } + + url += "&offset=0&list_type=1"; + string referer = "https://store.weixin.qq.com/shop/shopleague/home/normal-add"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 批量为商品添加推广计划并设置佣金比例。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID 列表。 + /// 推广佣金比例的接口原始整数值,不在本方法内换算。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxAddPromotion。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxAddPromotion(ShopLoginInfo shop, List prodIdLst, int ratio) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/plan/batchAddItem?token=&lang=zh_CN"; + string body = @"{""list"":["; + foreach (var prodId in prodIdLst) + body += @"{""spuId"":""" + prodId + @""",""ratio"":" + ratio + @",""finderCoupon"":0},"; + body = body.TrimEnd(','); + body += @"]}"; + string referer = "https://store.weixin.qq.com/shop/shopleague/home/normal-add"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 按商品 ID 查询推广计划。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxSearchPromotion。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxSearchPromotion(ShopLoginInfo shop, string prodId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/plan/getItemListBySearch?" + "token=&lang=zh_CN&offset=0&size=10&product_title=" + prodId + "&status=1&list_type=2&needWarning=true&needStockNum=true"; + string referer = "https://store.weixin.qq.com/shop/shopleague/home"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 结束指定商品的推广计划。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxEndPromotion。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxEndPromotion(ShopLoginInfo shop, string prodId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/plan/updateItem?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/shopleague/home"; + string body = @"{""spu_id"":""" + prodId + @""",""operate_type"":2}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 删除指定商品的推广计划。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXProdCouponOrderBoomForm.cs,WxCoupon.WxDelPromotion。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxDelPromotion(ShopLoginInfo shop, string prodId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/plan/updateItem?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/shopleague/home"; + string body = @"{""spu_id"":""" + prodId + @""",""operate_type"":3}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxShopApi.cs b/Work/Api/Wx/WxShopApi.cs new file mode 100644 index 0000000..1af3eb7 --- /dev/null +++ b/Work/Api/Wx/WxShopApi.cs @@ -0,0 +1,106 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店店铺登录、服务、状态及违规接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxShopApi + { + /// 查询店铺违规商品信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\PDDForms\ProductForms\WXProdViolationManage.cs,WXProdViolationManage.getShopiolationProd。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopViolationProducts(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeviolationsec/violation/cgi/get?token=&lang=zh_CN&violationType=finder&shopId="; + string referer = "https://store.weixin.qq.com/shop/violation/home"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 查询店铺当前已订购的服务列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.GetApiServiceList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetApiServiceList(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeservicemarket/service/GetMyServiceList?offset=0&limit=20&status=2&token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/servicemarket/myServices?status=2"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 获取并更新微信小店后台在线状态。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXDBProdUploadForm.cs,WXUpload.GetAndUpdateWebStatus。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetAndUpdateWebStatus(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradehome/home/getAndUpdateWebStatus?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/home"; + string body = @"{""timeQuery"":{""startMs"":" + BaseClass.DateTimeToTimestampMilliseconds(DateTime.Now.Date.AddDays(-1)) + @",""endMs"":" + BaseClass.DateTimeToTimestampMilliseconds(DateTime.Now.Date.AddSeconds(-1)) + @"}}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 读取店铺后台首页 HTML,用于原店铺登录信息提取流程。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 后台首页原始 HTML;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopListForm.cs,WXShopListForm.GetShopInfo。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopInfo(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop/home"; + string refere = ""; + var result = WxHttpClient.Get(shop, url, refere, true); + return result; + } + + /// 请求店铺通知配置,用于判断当前 Cookie 是否仍可访问后台。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 请求的 Referer 页面地址。 默认值:"https://store.weixin.qq.com/shop/home"。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopListForm.cs,WXShopListForm.CheckShopLogin。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string CheckShopLogin(ShopLoginInfo shop, string refere = "https://store.weixin.qq.com/shop/home") + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodebase/reachDiolog/getBizNotifyConfig?token=&lang=zh_CN"; + string body = @"{""path"":""https://store.weixin.qq.com/shop/home""}"; + return WxHttpClient.Post(shop, url, body, refere); + } + + /// 查询当前登录账号可切换的店铺列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopMultiLoginForm.cs,WXShopSwitch.getShopSwitchList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopSwitchList(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodelogin/session/getShopSwitchList?token=&lang=zh_CN"; + string refere = "https://store.weixin.qq.com/shop/home"; + var result = WxHttpClient.Get(shop, url, refere); + return result; + } + } +} diff --git a/Work/Api/Wx/WxStatisticsApi.cs b/Work/Api/Wx/WxStatisticsApi.cs new file mode 100644 index 0000000..5645933 --- /dev/null +++ b/Work/Api/Wx/WxStatisticsApi.cs @@ -0,0 +1,203 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店销售、评分及物流统计接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxStatisticsApi + { + /// 查询影响商品质量评分的退款订单明细。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始;部分接口仅用于页面 Referer。 + /// 统计开始时间的 Unix 秒时间戳。 + /// 统计结束时间的 Unix 秒时间戳。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.GetProductRefundOrder。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetProductRefundOrder(ShopLoginInfo shop, int pageId, int beginTick, int endTick) + { + string url = "https://store.weixin.qq.com/shop-faas/statistic/dsr/product/refund/order?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/setting/ratedetail?type=product&key=productQualityRatio_30d&detail=product"; + string body = @"{""page"":" + pageId + @",""pageSize"":20,""beginTs"":" + beginTick + @",""endTs"":" + endTick + @"}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询指定时间范围内的商品评价。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始;部分接口仅用于页面 Referer。 + /// 统计开始时间的 Unix 秒时间戳。 + /// 统计结束时间的 Unix 秒时间戳。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.GetProductEvaluation。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetProductEvaluation(ShopLoginInfo shop, int pageId, int beginTick, int endTick) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeevaluation/cgi/search?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/evaluate/home"; + string body = @"{""orderId"":"""",""productId"":"""",""productEvaluationId"":"""",""buyerEvaluationTimeStart"":" + beginTick + @",""buyerEvaluationTimeEnd"":" + endTick + @",""status"":2,""page"":" + pageId + @",""visibleType"":0}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询店铺评分项目,供业务方判断使用 24 小时或 48 小时揽收指标。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.Get24H48HFlag。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string Get24H48HFlag(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/statistic/cgi/search?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/setting/rate2"; + string body = "{\"days\":14,\"scoreTypeList\":[63,1000]}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询影响揽收及时率的订单明细。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始;部分接口仅用于页面 Referer。 + /// 统计开始时间的 Unix 秒时间戳。 + /// 统计结束时间的 Unix 秒时间戳。 + /// 是否使用 24 小时揽收相关页面上下文;false 使用 48 小时上下文。 默认值:false。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.GetProductOrderWayBill。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetProductOrderWayBill(ShopLoginInfo shop, int pageId, int beginTick, int endTick, bool is24 = false) + { + string url = "https://store.weixin.qq.com/shop-faas/statistic/dsr/logistics/pickup/insale?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/setting/ratedetail?type=logistics&key=collectIn_48hOrdRatio_30d&valid=collectIn_48hOrdRatio_30d|waybillArriveOntimeRatio_30d|waybillNegtiveOrdRatio_30d&detail=insale"; + if (is24) + referer = "https://store.weixin.qq.com/shop/setting/ratedetail?type=logistics&key=collectIn_24hOrdRatio_30d&valid=collectIn_24hOrdRatio_30d|waybillArriveOntimeRatio_30d|waybillNegtiveOrdRatio_30d&detail=insale"; + string body = @"{""page"":" + pageId + @",""pageSize"":20,""beginTs"":" + beginTick + @",""endTs"":" + endTick + @"}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询物流轨迹及时率订单明细,沿用原统计查询协议。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始;部分接口仅用于页面 Referer。 + /// 预期的开始时间(Unix 毫秒);原实现未使用此值,见 remarks。 + /// 结束时间(Unix 毫秒);原实现同时用于 startMs 和 endMs。 + /// 是否使用 24 小时揽收相关页面上下文;false 使用 48 小时上下文。 默认值:false。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.GetOrderLinerCgi。 + /// 保留旧行为:startMs 与 endMs 均取 endTick;需要修正时间范围时应单独确认业务影响。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetOrderLinerCgi(ShopLoginInfo shop, int pageId, long beginTick, long endTick, bool is24 = false) + { + // shortcut: 保留旧代码 startMs 取 endTick 的行为,业务确认时间范围修正后再改为 beginTick。 + string url = "https://store.weixin.qq.com/shop-faas/mmecnodelinercgi/cgi/liner/query?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/setting/ratedetail?type=logistics&key=waybillArriveOntimeRatio_30d&valid=collectIn_48hOrdRatio_30d|waybillArriveOntimeRatio_30d|waybillNegtiveOrdRatio_30d"; + if (is24) + referer = "https://store.weixin.qq.com/shop/setting/ratedetail?type=logistics&key=waybillArriveOntimeRatio_30d&valid=collectIn_24hOrdRatio_30d|waybillArriveOntimeRatio_30d|waybillNegtiveOrdRatio_30d"; + string body = @"{""time_query"":{""dateFormat"":2,""dateType"":0,""dateRange"":30,""startMs"":" + endTick + @",""endMs"":" + endTick + @"},""limit"":10,""offset"":" + (Math.Max(0, pageId - 1) * 10) + @",""id"":""ratedetail-wuliu-route-order-table"",""biz"":""store"",""is_std"":""0"",""sort"":[""std_route_rate"",""desc""]}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询物流负向反馈订单明细。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 统计开始时间的 Unix 秒时间戳。 + /// 统计结束时间的 Unix 秒时间戳。 + /// 是否使用 24 小时揽收相关页面上下文;false 使用 48 小时上下文。 默认值:false。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WXShopOptimizeForm.cs,ShopOptimize.GetFeedbackOrder。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetFeedbackOrder(ShopLoginInfo shop, int page, int beginTs, int endTs, bool is24 = false) + { + string url = "https://store.weixin.qq.com/shop-faas/statistic/dsr/logistics/feedback/order?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/setting/ratedetail?type=logistics&key=waybillNegtiveOrdRatio_30d&valid=collectIn_48hOrdRatio_30d|waybillArriveOntimeRatio_30d|waybillNegtiveOrdRatio_30d&detail=company"; + if (is24) + referer = "https://store.weixin.qq.com/shop/setting/ratedetail?type=logistics&key=waybillNegtiveOrdRatio_30d&valid=collectIn_24hOrdRatio_30d|waybillArriveOntimeRatio_30d|waybillNegtiveOrdRatio_30d&detail=company"; + string body = @"{""page"":" + page + @",""pageSize"":20,""beginTs"":" + beginTs + @",""endTs"":" + endTs + @"}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询店铺首页待办数量,包括待发货、售后和物流异常等。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WxShopStatisticsForm.cs,WxShopStatisticsForm.GetShopMainInfoList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopMainInfoList(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradehome/home/main?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/home"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 查询指定时间范围内的离线销售统计。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 统计开始时间的 Unix 毫秒时间戳。 + /// 统计结束时间的 Unix 毫秒时间戳。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WxShopStatisticsForm.cs,WxShopStatisticsForm.GetShopSaleInfoList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopSaleInfoList(ShopLoginInfo shop, long startTime, long endTime) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodelinercgi/cgi/liner_query/3/home-overview-shop-offline?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/home"; + string data = @"{""time_query"":{""dateFormat"":2,""dateType"":0,""dateRange"":7,""startMs"":" + startTime + @",""endMs"":" + endTime + "}}"; + var result = WxHttpClient.Post(shop, url, data, referer); + return result; + } + + /// 查询指定时间范围内的实时销售统计。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 统计开始时间的 Unix 毫秒时间戳。 + /// 统计结束时间的 Unix 毫秒时间戳。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WxShopStatisticsForm.cs,WxShopStatisticsForm.GetShopSaleInfoRealTimeList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopSaleInfoRealTimeList(ShopLoginInfo shop, long startTime, long endTime) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodelinercgi/cgi/liner_query/3/home-overview-shop-realtime?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/home"; + string data = @"{""time_query"":{""dateFormat"":1,""dateType"":0,""dateRange"":1,""startMs"":" + startTime + @",""endMs"":" + endTime + "}}"; + var result = WxHttpClient.Post(shop, url, data, referer); + return result; + } + + /// 查询店铺首页评分等次要统计信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ShopForms\WxShopStatisticsForm.cs,WxShopStatisticsForm.GetShopScore。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetShopScore(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradehome/home/minor?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/home"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxTalentApi.cs b/Work/Api/Wx/WxTalentApi.cs new file mode 100644 index 0000000..f361bc7 --- /dev/null +++ b/Work/Api/Wx/WxTalentApi.cs @@ -0,0 +1,191 @@ +using AllBase; +using NaiBa.Util.Extension; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店达人广场、邀约及带货数据接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxTalentApi + { + /// 按完整筛选 JSON 查询达人广场列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 符合原接口结构的完整筛选请求 JSON。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXFinderSquareForm.cs,WXFinderSquareBLL.getSquareTalentList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetSquareTalentList(ShopLoginInfo shop, string bodyJson) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/finderSquare/cgi/getSquareTalentList?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/findersquare/find"; + string body = bodyJson; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询指定达人橱窗中的商品列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 页码,从 1 开始。 + /// 达人视频号在平台中的 finderUsername 标识。 + /// 达人关联的 talentAppid 标识。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXFinderSquareForm.cs,WXFinderSquareBLL.WxFinderSquareGetProdList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string WxFinderSquareGetProdList(ShopLoginInfo shop, int page, string finderUsername, string talentAppid) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/finderSquare/cgi/getItemList?token=&lang=zh_CN&offset=" + (page - 1) * 10 + "&pageSize=10&productTitle="; + string referer = "https://store.weixin.qq.com/shop/findersquare/initiate-invite?finderUsername=" + finderUsername + "&talentAppid=" + talentAppid + "&from=5"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 批量查询商品,并按销量排序。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 SPU ID 列表。 + /// 页码,从 1 开始。 默认值:1。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXFinderSquareForm.cs,WXFinderSquareBLL.getSaleSortProduct。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetSaleSortProduct(ShopLoginInfo shop, List spuIdLst, int page = 1) + { + var spuIdStr = "+" + string.Join("+", spuIdLst); + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct" + "?token=&lang=zh_CN&pageSize=50&productStatus[]=-1&productSource=[1,16,32]&searchSource=1&useNew=true&pageNum=" + page + "&status[]=5&spuId=" + spuIdStr + "&sortType=6"; + string referer = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_ON"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 批量查询商品,并按上架时间排序。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 SPU ID 列表。 + /// 页码,从 1 开始。 默认值:1。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXFinderSquareForm.cs,WXFinderSquareBLL.getListingTimeSortProduct。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetListingTimeSortProduct(ShopLoginInfo shop, List spuIdLst, int page = 1) + { + var spuIdStr = "+" + string.Join("+", spuIdLst); + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeproductcore/cgi/goods/searchProduct" + "?token=&lang=zh_CN&pageSize=50&productStatus[]=-1&productSource=[1,16,32]&searchSource=1&useNew=true&pageNum=" + page + "&status[]=5&spuId=" + spuIdStr + "&sortType=18"; + string referer = "https://store.weixin.qq.com/shop/goods/list?currentTab=PRODUCT_STATUS_ON"; + var result = WxHttpClient.Get(shop, url, referer); + return result; + } + + /// 向指定达人提交商品推广邀请及商家联系方式。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信商品 ID 列表。 + /// 达人视频号在平台中的 finderUsername 标识。 + /// 达人关联的 talentAppid 标识。 + /// 推广邀请中展示的商家联系人姓名。 + /// 商家联系人的微信号。 + /// 商家联系人手机号。 + /// 发送给达人的推广邀请说明。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXFinderSquareForm.cs,WXFinderSquareBLL.createPromotionInvitation。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string CreatePromotionInvitation(ShopLoginInfo shop, List productIdLst, string finderUsername, string talentAppid, string contactName, string contactWxId, string contactPhoneNumber, string invitationIntroduction) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/finderSquare/cgi/createPromotionInvitation?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/findersquare/initiate-invite?finderUsername=" + "&talentAppid=" + "&from=5"; + string body = @"{""finderUsername"":""" + finderUsername + @""",""talentAppid"":""" + talentAppid + @""",""itemList"":{""itemInfo"":["; + foreach (var spuId in productIdLst) + { + body += @"{""spuId"":""" + spuId + @"""},"; + } + + body = body.TrimEnd(','); + body += @"]},""contactInfo"":{""contactName"":""" + contactName + @""",""contactWxId"":""" + contactWxId + @""",""contactPhoneNumber"":""" + contactPhoneNumber + @""",""invitationIntroduction"":""" + invitationIntroduction + @""",""inviteScene"":""5""}}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询达人在指定时间范围内的直播或短视频内容列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 达人视频号在平台中的 finderUsername 标识。 + /// 达人关联的 mpAppid 标识。 + /// 达人关联的 talentAppid 标识。 + /// 统计开始时间的 Unix 毫秒时间戳。 + /// 统计结束时间的 Unix 毫秒时间戳。 + /// 内容类型:1 直播、2 短视频,来自原页面调用。 默认值:1。 + /// 页码,从 1 开始。 默认值:1。 + /// 每页条数,默认值沿用原调用。 默认值:10。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXFinderSquareForm.cs,WXFinderSquareBLL.GetDaRenLiveFeedList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetDaRenLiveFeedList(ShopLoginInfo shop, string finderUsername, string mpAppid, string talentAppid, long startMs, long endMs, int liveOrFeed = 1, int page = 1, int pageSize = 10) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/finderSquare/cgi/GetSameIndustryDetailLiveFeedList?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/findersquare/finder-detail?from=5&fromTab=all&finderUsername=" + finderUsername; + if (!mpAppid.IsNullOrEmpty()) + referer += "&mpAppid=" + mpAppid; + referer += "&talentAppid=" + talentAppid; + string body = @"{""appid"":""" + talentAppid + @""",""liveOrFeed"":" + liveOrFeed + @",""page"":{""limit"":" + pageSize + @",""offset"":" + (page - 1) * 10 + @"},""startMs"":" + startMs + @",""endMs"":" + endMs + @",""sortControl"":null}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 查询达人指定直播或短视频内容中的商品列表。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 达人视频号在平台中的 finderUsername 标识。 + /// 达人关联的 mpAppid 标识。 + /// 达人关联的 talentAppid 标识。 + /// 目标直播或短视频内容的 objectId。 + /// 页码,从 1 开始。 默认值:1。 + /// 每页条数,默认值沿用原调用。 默认值:10。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXFinderSquareForm.cs,WXFinderSquareBLL.GetDaRenLiveProductList。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string GetDaRenLiveProductList(ShopLoginInfo shop, string finderUsername, string mpAppid, string talentAppid, string objectId, int page = 1, int pageSize = 10) + { + string url = "https://store.weixin.qq.com/shop-faas/mmchannelstradeleague/finderSquare/cgi/getLiveProductList?token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/findersquare/finder-detail?from=5&fromTab=all&finderUsername=" + finderUsername; + if (!mpAppid.IsNullOrEmpty()) + referer += "&mpAppid=" + mpAppid; + referer += "&talentAppid=" + talentAppid; + string body = @"{""appid"":""" + talentAppid + @""",""objectId"":""" + objectId + @""",""limit"":" + pageSize + @",""offset"":" + (page - 1) * 10 + @"}"; + var result = WxHttpClient.Post(shop, url, body, referer); + return result; + } + + /// 创建与指定达人的客服会话。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 达人视频号在平台中的 finderUsername 标识。 + /// 达人关联的 mpAppid 标识。 + /// 达人关联的 talentAppid 标识。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\ProductForms\WXFinderSquareForm.cs,WXFinderSquareBLL.userCreateChatRoom。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string UserCreateChatRoom(ShopLoginInfo shop, string finderUsername, string mpAppid, string talentAppid) + { + string url = "https://store.weixin.qq.com/shop/commkf/user_room?action=user_create_room&token=&lang=zh_CN"; + string referer = "https://store.weixin.qq.com/shop/findersquare/finder-detail?from=1&fromTab=all&finderUsername=" + finderUsername; + if (!mpAppid.IsNullOrEmpty()) + referer += "&mpAppid=" + mpAppid; + referer += "&talentAppid=" + talentAppid; + string body = @"{""open_account"":""" + talentAppid + @"""}"; + var result = WxHttpClient.PostCustomerService(shop, url, body, referer); + return result; + } + } +} diff --git a/Work/Api/Wx/WxWaybillApi.cs b/Work/Api/Wx/WxWaybillApi.cs new file mode 100644 index 0000000..6b4df1e --- /dev/null +++ b/Work/Api/Wx/WxWaybillApi.cs @@ -0,0 +1,84 @@ +using AllBase; +using AllBase.Model.Result; +using AllBase.Model.Param.WXParam; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Work.Api.Wx +{ + /// 微信小店在线电子面单接口;独立于原窗体,使用 HttpHelper 发送请求。 + /// 传入原 ShopLoginInfo 登录信息;方法同步执行,界面调用请置于后台任务。返回原始正文,不自动登录、重试或解释业务状态。 + public static class WxWaybillApi + { + /// 在线电子面单预下单,校验寄收件资料并获取下单准备信息。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 电子面单预下单参数,沿用 AllBase 中的请求模型。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.preCreateOrderWaybillOnline。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string PreCreateOrderWaybillOnline(ShopLoginInfo shop, WXPreCreateOrderWaybillOnlineParam preWaybillOnlineparam) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodewaybill/alltoall/platPreCreateOrder?token=&lang=zh_CN"; + string body = JsonHelper.SerializeObject(preWaybillOnlineparam); + string refere = "https://store.weixin.qq.com/shop/waybill/allToAll"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 创建在线电子面单订单,实际向平台提交面单申请。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 电子面单创建参数,沿用 AllBase 中的请求模型。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.createOrderWaybillOnline。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string CreateOrderWaybillOnline(ShopLoginInfo shop, WXCreateOrderWaybillOnlineParam waybillOnlineparam) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodewaybill/alltoall/platCreateOrder?token=&lang=zh_CN"; + string body = JsonHelper.SerializeObject(waybillOnlineparam); + string refere = "https://store.weixin.qq.com/shop/waybill/allToAll"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 按微信订单号查询已创建的在线电子面单。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 微信订单 ID。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.searchOnlineWaybill。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string SearchOnlineWaybill(ShopLoginInfo shop, string orderId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodewaybill/alltoall/searchBill?token=&lang=zh_CN"; + string body = @"{""createMode"":1,""orderId"":""" + orderId + @""",""cosignmentOrderId"":"""",""goodsTitle"":"""",""receiverPhone"":"""",""receiverName"":"""",""billStatus"":null,""orderStatus"":null,""offset"":0,""limit"":20,""waybillId"":"""",""payMethodList"":[],""platDisplayOrderStatusList"":[],""cosignmentOrderTime"":[],""cosignmentAssignTime"":[],""storeName"":"""",""orderNote"":"""",""deliveryOrderStatusList"":[10,20,40]}"; + string refere = "https://store.weixin.qq.com/shop/waybill/allToAll"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + + /// 取消指定在线电子面单订单。 + /// 店铺登录信息;Cookies 为原程序保存的 CefSharp.Cookie 列表 JSON,图片上传还需 openid。 + /// 平台返回的电子面单订单 ID。 + /// 物流运单号。 + /// 微信接口原始 JSON 正文;传输或登录参数配置失败可能返回错误文字。调用方须验证响应格式和业务状态,不能仅以非空判断成功。 + /// + /// 来源:TEMUKJD\WXForms\OrderForms\WXOrderListForm.cs,WXOrderList.cancelOnlineWaybill。 + /// 原方法中直接拼接的 ID、JSON 数值片段应按上述格式传入;不会自动转义或转换单位。参数模型构造或序列化失败会向调用方抛出异常。 + /// + public static string CancelOnlineWaybill(ShopLoginInfo shop, string ewaybillOrderId, string waybillId) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodewaybill/alltoall/platCancelOrder?token=&lang=zh_CN"; + string body = @"{""requestContent"":[{""ewaybillOrderId"":""" + ewaybillOrderId + @""",""waybillId"":""" + waybillId + @""",""source"":1,""merchCancelReason"":""临时缺货,库存不足""}]}"; + string refere = "https://store.weixin.qq.com/shop/waybill/allToAll"; + var result = WxHttpClient.Post(shop, url, body, refere); + return result; + } + } +} diff --git a/Work/Services/CloudGoodsService.cs b/Work/Services/CloudGoodsService.cs index 6064db0..2d6b8e2 100644 --- a/Work/Services/CloudGoodsService.cs +++ b/Work/Services/CloudGoodsService.cs @@ -2,11 +2,13 @@ using AllBase.Helper; using AllBase.Model.Result; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Work.Api.Wx; namespace Work.Services { @@ -46,6 +48,295 @@ namespace Work.Services return ret.Data; } + /// + /// 在后台解析微信处罚 JSON、匹配店铺商品编码并上报,异常在内部记录。 + /// + public static void ReportViolationsFromJson(string json, ShopLoginInfo shop) + { + _ = Task.Run(async () => + { + try + { + await ReportViolationsFromJsonCore(json, shop).ConfigureAwait(false); + } + catch (Exception ex) + { + try + { + LogSave.AddFile("解析并上报违规商品失败:" + ex.Message); + } + catch + { + // 日志失败也不能影响调用方。 + } + } + }); + } + + private static async Task> ReportViolationsFromJsonCore(string json, ShopLoginInfo shop) + { + if (string.IsNullOrWhiteSpace(json)) + { + throw new ArgumentException("处罚 JSON 文本不能为空。", nameof(json)); + } + + JObject root; + try + { + root = JObject.Parse(json); + } + catch (JsonException ex) + { + throw new ArgumentException("处罚 JSON 格式不正确。", nameof(json), ex); + } + + var record = root["record"] as JObject; + if (root["code"]?.ToString() != "0" || record?["code"]?.ToString() != "0") + { + throw new ArgumentException("处罚数据的 code 或 record.code 不为 0,或缺少 record。", nameof(json)); + } + if (!(record["items"] is JArray records)) + { + throw new ArgumentException("处罚数据缺少 record.items 数组。", nameof(json)); + } + + if (records.Count == 0) + return new List(); + var items = new List(); + foreach (var token in records) + { + if (!(token is JObject punishment)) + { + throw new ArgumentException("record.items 中的处罚记录必须是对象。", nameof(json)); + } + var objects = punishment["punishObject"]; + if (objects == null || objects.Type == JTokenType.Null) + { + continue; + } + if (!(objects is JArray products)) + { + throw new ArgumentException("punishObject 必须是数组。", nameof(json)); + } + foreach (var product in products) + { + if (!(product is JObject target)) + { + throw new ArgumentException("punishObject 中的处罚对象必须是对象。", nameof(json)); + } + var id = target["productId"]; + // 订单等非商品处罚对象不包含 productId。 + if (id == null) + { + continue; + } + if ((id.Type != JTokenType.String && id.Type != JTokenType.Integer) + || !long.TryParse(id.ToString(), out var goodsId) || goodsId <= 0) + { + throw new ArgumentException("商品 productId 必须是有效的正整数编号。", nameof(json)); + } + var reason = punishment["punishReason"]; + if (reason?.Type != JTokenType.String || string.IsNullOrWhiteSpace((string)reason)) + { + throw new ArgumentException($"商品 {goodsId} 缺少有效的 punishReason。", nameof(json)); + } + items.Add(new ReportGoodsViolationItem { GoodsId = goodsId, Reasons = ((string)reason).Trim() }); + } + } + + if (items.Count == 0) + { + return new List(); + } + var productCodes = GetWxProductCodes(shop, items.Select(item => item.GoodsId)); + var mappedItems = new List(); + foreach (var item in items) + { + if (!productCodes.TryGetValue(item.GoodsId, out var code) || string.IsNullOrWhiteSpace(code)) + { + LogSave.AddFile($"跳过违规商品 {item.GoodsId}:未找到对应的 spuCode。"); + continue; + } + if (!TryGetGoodsId(code, out var goodsId)) + { + LogSave.AddFile($"跳过违规商品 {item.GoodsId}:spuCode 无法转换为有效商品编号。"); + continue; + } + mappedItems.Add(new ReportGoodsViolationItem { GoodsId = goodsId, Reasons = item.Reasons }); + } + var reports = mappedItems.GroupBy(item => item.GoodsId).Select(group => new ReportGoodsViolationItem + { + GoodsId = group.Key, + Reasons = string.Join(";", group.Select(item => item.Reasons).Distinct()) + }).ToList(); + + return reports.Count == 0 + ? new List() : await ReportViolations(reports); + } + + private static bool TryGetGoodsId(string code, out long goodsId) + { + code = code?.Trim() ?? string.Empty; + if (code.StartsWith("P1-", StringComparison.Ordinal)) + { + code = code.Substring(3); + } + else if (code.StartsWith("EC1", StringComparison.Ordinal) && code.EndsWith("A", StringComparison.Ordinal)) + { + code = code.Substring(3, code.Length - 4); + } + return long.TryParse(code, out goodsId) && goodsId > 0; + } + + /// + /// 后台检查当前店铺所有在售商品,云端确认违规后立即下架;异常仅记录日志。 + /// + public static void CheckAndOffShelfViolations(ShopLoginInfo shop) + { + _ = Task.Run(async () => + { + try + { + await CheckAndOffShelfViolationsCore(shop).ConfigureAwait(false); + } + catch (Exception ex) + { + try + { + LogSave.AddFile($"店铺 {shop?.ShopId} 在售商品违规检查失败:{ex.Message}"); + } + catch { } + } + }); + } + + private static async Task CheckAndOffShelfViolationsCore(ShopLoginInfo shop) + { + // 先读取完整列表,避免下架改变分页位置而漏查后面的商品。 + var products = GetWxProductCodes(shop, status: 5); + var mappings = new Dictionary(); + foreach (var product in products) + { + if (TryGetGoodsId(product.Value, out var goodsId)) + { + mappings.Add(product.Key, goodsId); + } + else + { + LogSave.AddFile($"店铺 {shop.ShopId} 跳过商品 {product.Key}:spuCode 无法转换为有效商品编号。"); + } + } + var goodsIds = mappings.Values.Distinct().ToList(); + for (var offset = 0; offset < goodsIds.Count; offset += 50) + { + var batch = goodsIds.Skip(offset).Take(50).ToList(); + var statuses = GetViolationStatus(batch); + var violations = new HashSet(statuses.Where(item => item != null && item.IsViolation && batch.Contains(item.GoodsId)) + .Select(item => item.GoodsId)); + var productIds = mappings.Where(item => violations.Contains(item.Value)).Select(item => item.Key).ToList(); + // 同一云商品可能对应多个微信商品,每次下架最多 50 个。 + for (var index = 0; index < productIds.Count; index += 50) + { + var ids = string.Join(",", productIds.Skip(index).Take(50)); + var response = JObject.Parse(WxProductApi.DownProd(shop, ids)); + if (response["code"]?.ToString() != "0") + { + throw new Exception($"微信商品 {ids} 下架失败:{response}"); + } + LogSave.AddFile($"店铺 {shop.ShopId} 云端检测违规,已下架微信商品:{ids}"); + } + } + } + + private static Dictionary GetWxProductCodes(ShopLoginInfo shop, IEnumerable productIds = null, int status = -1) + { + if (shop == null) + { + throw new ArgumentNullException(nameof(shop)); + } + var codes = new Dictionary(); + var prodIds = productIds == null ? null : string.Join(",", productIds.Distinct()); + long received = 0; + for (var page = 1; ; page++) + { + var response = JObject.Parse(WxProductApi.SearchProduct(shop, page, pageSize: 50, prodIds: prodIds, status: status)); + if (response["ret"]?.ToString() != "0" || response["success"]?.Type != JTokenType.Boolean + || !(bool)response["success"] || !(response["productList"] is JArray products) + || !long.TryParse(response["totalNum"]?.ToString(), out var total) || total < 0) + { + throw new Exception($"获取微信商品列表第 {page} 页失败。"); + } + var previousCount = codes.Count; + foreach (var product in products) + { + if (!(product is JObject item) || !long.TryParse(item["productId"]?.ToString(), out var id) || id <= 0) + { + throw new Exception("微信商品列表包含无效的 productId。"); + } + codes[id] = item["spuCode"]?.Type == JTokenType.String ? (string)item["spuCode"] : null; + } + received += products.Count; + if (received >= total) + { + return codes; + } + if (codes.Count == previousCount) + { + throw new Exception("微信商品列表分页未返回新商品,无法获取全部搜索结果。"); + } + } + } + + /// + /// 批量上报违规商品。 + /// + public static async Task> ReportViolations(List items) + { + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + if (items.Any(item => item == null)) + { + throw new ArgumentException("违规商品列表不能包含空项。", nameof(items)); + } + + var html = await GetHtml.PostJsonAsync(ServerUrl + "/api/CloudGoods/ReportViolations", items, timeOut: 5); + var ret = JsonConvert.DeserializeObject>>(html); + if (ret == null) + { + throw new Exception("批量上报违规商品返回为空。"); + } + if (ret.Code != 200) + { + throw new Exception(ret.Msg); + } + return ret.Data ?? throw new Exception("批量上报违规商品返回数据为空。"); + } + + /// + /// 批量查询商品违规状态。 + /// + public static List GetViolationStatus(List goodsIds) + { + if (goodsIds == null) + { + throw new ArgumentNullException(nameof(goodsIds)); + } + + var html = GetHtml.PostJson(ServerUrl + "/api/CloudGoods/GetViolationStatus", goodsIds, timeOut: 5); + var ret = JsonConvert.DeserializeObject>>(html); + if (ret == null) + { + throw new Exception("批量查询商品违规状态返回为空。"); + } + if (ret.Code != 200) + { + throw new Exception(ret.Msg); + } + return ret.Data ?? throw new Exception("批量查询商品违规状态返回数据为空。"); + } + #region user /// /// 搜索收藏 @@ -117,6 +408,42 @@ namespace Work.Services return string.IsNullOrEmpty(BaseClass.ParentUserName) ? BaseClass.UserName : BaseClass.ParentUserName; } } + /// + /// 违规商品上报项。 + /// + public class ReportGoodsViolationItem + { + [JsonProperty("goodsId")] + public long GoodsId { get; set; } + + [JsonProperty("reasons")] + public string Reasons { get; set; } = string.Empty; + } + + /// + /// 单个商品的违规上报结果。 + /// + public class ReportGoodsViolationResult + { + [JsonProperty("goodsId")] + public long GoodsId { get; set; } + + [JsonProperty("isOffShelf")] + public bool IsOffShelf { get; set; } + } + + /// + /// 商品违规状态。 + /// + public class GoodsViolationStatus + { + [JsonProperty("goodsId")] + public long GoodsId { get; set; } + + [JsonProperty("isViolation")] + public bool IsViolation { get; set; } + } + /// /// 商品搜索请求 /// diff --git a/Work/Services/ImageCacheService.cs b/Work/Services/ImageCacheService.cs index a91d8db..2acb731 100644 --- a/Work/Services/ImageCacheService.cs +++ b/Work/Services/ImageCacheService.cs @@ -12,15 +12,9 @@ using System.Threading.Tasks; namespace Work.Services; -public class ImageCache -{ - public string Md5 { get; set; } - public int Platform { get; set; } - [JsonProperty("platform_url")] - public string PlatformUrl { get; set; } - public bool IsCache { get; set; } -} - +/// +/// 图片缓存服务 +/// public class ImageCacheService { private static string ServerUrl = "http://103.90.155.78:9001"; @@ -136,3 +130,11 @@ public class ImageCacheService } } +public class ImageCache +{ + public string Md5 { get; set; } + public int Platform { get; set; } + [JsonProperty("platform_url")] + public string PlatformUrl { get; set; } + public bool IsCache { get; set; } +}