diff --git a/AllBase/Helper/GlobalConfig.cs b/AllBase/Helper/GlobalConfig.cs
index 1e41080..b9d1b34 100644
--- a/AllBase/Helper/GlobalConfig.cs
+++ b/AllBase/Helper/GlobalConfig.cs
@@ -127,8 +127,18 @@ namespace AllBase.Helper
Alibaba = 6,
}
+ public sealed class ComplianceOptions
+ {
+ public int? MainCount { get; set; }
+ public int? DetailCount { get; set; } = 0;
+ public bool IncludeSku { get; set; }
+ public string Keywords { get; set; }
+ }
+
public class GlobalSettings
{
+ public ComplianceOptions GoodsCompliance { get; set; } = new ComplianceOptions();
+
///
/// 微信使用软件设置的默认上货地址
///
diff --git a/AllBase/HttpHelper.cs b/AllBase/HttpHelper.cs
index f90ca82..0a9d4f6 100644
--- a/AllBase/HttpHelper.cs
+++ b/AllBase/HttpHelper.cs
@@ -8,6 +8,7 @@ using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
+using System.Threading;
using System.Threading.Tasks;
namespace CsharpHttpHelper
@@ -52,6 +53,16 @@ namespace CsharpHttpHelper
});
}
+ public static Task GetAsync(string Url)
+ {
+ return RequestAsync(new HttpItem { URL = Url, Method = MethodType.Get });
+ }
+
+ public static Task PostAsync(string Url, string PostData)
+ {
+ return RequestAsync(new HttpItem { URL = Url, Method = MethodType.Post, Postdata = PostData });
+ }
+
///
/// 下载文件
///
@@ -77,17 +88,13 @@ namespace CsharpHttpHelper
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;
+ return new HttpHelper().GetHtml(item);
}
+ public static Task RequestAsync(HttpItem item)
+ {
+ return new HttpHelper().GetHtmlCoreAsync(item);
+ }
#region Cookie
///
@@ -137,8 +144,6 @@ namespace CsharpHttpHelper
private Encoding postencoding = Encoding.Default;
//HttpWebRequest对象用来发起请求
private HttpWebRequest request = null;
- //获取影响流的数据对象
- private HttpWebResponse response = null;
///
/// 根据相传入的数据,得到相应页面数据
///
@@ -146,112 +151,49 @@ namespace CsharpHttpHelper
/// 返回HttpResult类型
public HttpResult GetHtml(HttpItem item)
{
- //返回参数
- HttpResult result = new HttpResult();
- result.item = item;
try
{
- //准备参数
SetRequest(item);
+ if (item.Method == MethodType.Post || item.Method == MethodType.Put)
+ {
+ byte[] body;
+ if (item.PostDataType == PostDataType.FilePath && !string.IsNullOrEmpty(item.Postdata))
+ {
+ using var reader = new StreamReader(item.Postdata, postencoding);
+ body = postencoding.GetBytes(reader.ReadToEnd());
+ }
+ else body = GetPostBytes(item);
+ if (body != null)
+ {
+ request.ContentLength = body.Length;
+ using var stream = request.GetRequestStream();
+ stream.Write(body, 0, body.Length);
+ }
+ }
}
catch (Exception ex)
{
- result = new HttpResult();
- result.Cookie = string.Empty;
- result.Header = null;
- result.Html = ex.Message;
- result.StatusDescription = "配置参数时出错:" + ex.Message;
- return result;
+ return ConfigurationError(ex);
}
+
+ var result = new HttpResult { item = item };
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
+ using var response = (HttpWebResponse)request.GetResponse();
+ SetResponseHeaders(result, response);
+ if (response.Cookies != null) result.CookieCollection = response.Cookies;
+ result.Cookie = response.Headers["set-cookie"];
+ using var source = OpenResponseStream(response);
+ using var buffer = new MemoryStream();
+ source.CopyTo(buffer);
+ DecodeBody(item, result, response, buffer.ToArray());
}
- catch (WebException ex)
+ catch (WebException ex) when (ex.Response is HttpWebResponse)
{
- //这里是在发生异常时返回的错误信息
- 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();
- }
- }
+ using var response = (HttpWebResponse)ex.Response;
+ SetResponseHeaders(result, response);
+ using var reader = new StreamReader(OpenResponseStream(response), item.Encoding ?? Encoding.UTF8);
+ result.Html = reader.ReadToEnd();
}
catch (Exception ex)
{
@@ -260,22 +202,127 @@ namespace CsharpHttpHelper
if (item.IsToLower) result.Html = result.Html.ToLower();
return result;
}
- ///
- /// 4.0以下.net版本取数据使用
- ///
- /// 流
- private MemoryStream GetMemoryStream(Stream streamResponse)
+
+ public Task GetHtmlAsync(HttpItem item)
{
- MemoryStream _stream = new MemoryStream();
- int Length = 256;
- Byte[] buffer = new Byte[Length];
- int bytesRead = streamResponse.Read(buffer, 0, Length);
- while (bytesRead > 0)
+ // 每次异步调用独立保存请求和编码,允许同一实例并发使用。
+ return new HttpHelper().GetHtmlCoreAsync(item);
+ }
+
+ private async Task GetHtmlCoreAsync(HttpItem item)
+ {
+ using var timeout = new CancellationTokenSource();
+ using var abort = timeout.Token.Register(() => request.Abort());
+ try
{
- _stream.Write(buffer, 0, bytesRead);
- bytesRead = streamResponse.Read(buffer, 0, Length);
+ SetRequest(item);
+ timeout.CancelAfter(request.Timeout);
+ if (item.Method == MethodType.Post || item.Method == MethodType.Put)
+ {
+ byte[] body;
+ if (item.PostDataType == PostDataType.FilePath && !string.IsNullOrEmpty(item.Postdata))
+ {
+ using var reader = new StreamReader(item.Postdata, postencoding);
+ body = postencoding.GetBytes(await reader.ReadToEndAsync().ConfigureAwait(false));
+ }
+ else body = GetPostBytes(item);
+ if (body != null)
+ {
+ request.ContentLength = body.Length;
+ using var stream = await request.GetRequestStreamAsync().ConfigureAwait(false);
+ await stream.WriteAsync(body, 0, body.Length).ConfigureAwait(false);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ return ConfigurationError(ex);
+ }
+
+ var result = new HttpResult { item = item };
+ try
+ {
+ using var response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false);
+ SetResponseHeaders(result, response);
+ if (response.Cookies != null) result.CookieCollection = response.Cookies;
+ result.Cookie = response.Headers["set-cookie"];
+ using var source = OpenResponseStream(response);
+ using var buffer = new MemoryStream();
+ await source.CopyToAsync(buffer).ConfigureAwait(false);
+ DecodeBody(item, result, response, buffer.ToArray());
+ }
+ catch (WebException ex) when (ex.Response is HttpWebResponse)
+ {
+ using var response = (HttpWebResponse)ex.Response;
+ SetResponseHeaders(result, response);
+ using var reader = new StreamReader(OpenResponseStream(response), item.Encoding ?? Encoding.UTF8);
+ result.Html = await reader.ReadToEndAsync().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ result.Html = ex.Message;
+ }
+ if (item.IsToLower) result.Html = result.Html.ToLower();
+ return result;
+ }
+
+ private static HttpResult ConfigurationError(Exception ex)
+ {
+ return new HttpResult { Cookie = string.Empty, Html = ex.Message, StatusDescription = "配置参数时出错:" + ex.Message };
+ }
+
+ private static void SetResponseHeaders(HttpResult result, HttpWebResponse response)
+ {
+ result.StatusCode = response.StatusCode;
+ result.StatusDescription = response.StatusDescription;
+ result.Header = response.Headers;
+ }
+
+ private static Stream OpenResponseStream(HttpWebResponse response)
+ {
+ var stream = response.GetResponseStream();
+ return string.Equals(response.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase)
+ ? new GZipStream(stream, CompressionMode.Decompress) : stream;
+ }
+
+ private void DecodeBody(HttpItem item, HttpResult result, HttpWebResponse response, byte[] bytes)
+ {
+ if (bytes.Length > 0)
+ {
+ //是否返回Byte类型数据
+ if (item.ResultType == ResultType.Byte) result.ResultByte = bytes;
+ //从这里开始我们要无视编码了
+ if (encoding == null)
+ {
+ Match meta = Regex.Match(Encoding.Default.GetString(bytes), " 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(bytes);
+ }
+ else
+ {
+ //得到返回的HTML
+ result.Html = "本次请求并未返回任何数据";
}
- return _stream;
}
///
/// 为请求准备参数
@@ -308,6 +355,7 @@ namespace CsharpHttpHelper
request.UserAgent = item.UserAgent;
// 编码
encoding = item.Encoding;
+ if (item.Method != MethodType.Get && item.PostEncoding != null) postencoding = item.PostEncoding;
//设置安全凭证
request.Credentials = item.ICredentials;
//设置Cookie
@@ -316,8 +364,6 @@ namespace CsharpHttpHelper
request.Referer = item.Referer;
//是否执行跳转功能
request.AllowAutoRedirect = item.Allowautoredirect;
- //设置Post数据
- SetPostData(item);
//设置最大连接
if (item.Connectionlimit > 0) request.ServicePoint.ConnectionLimit = item.Connectionlimit;
}
@@ -373,43 +419,13 @@ namespace CsharpHttpHelper
request.CookieContainer.Add(item.CookieCollection);
}
}
- ///
- /// 设置Post数据
- ///
- /// Http参数
- private void SetPostData(HttpItem item)
+ private byte[] GetPostBytes(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);
- }
- }
+ if (item.PostDataType == PostDataType.Byte && item.PostdataByte?.Length > 0)
+ return item.PostdataByte;
+ return string.IsNullOrEmpty(item.Postdata) ? null : postencoding.GetBytes(item.Postdata);
}
+
///
/// 设置代理
///
diff --git a/AllBase/Model/Result/PlatformType.cs b/AllBase/Model/Result/PlatformType.cs
index 02ff4c3..ca0fbae 100644
--- a/AllBase/Model/Result/PlatformType.cs
+++ b/AllBase/Model/Result/PlatformType.cs
@@ -8,8 +8,8 @@ namespace AllBase.Model.Result
{
public enum PlatformType
{
- None,
- PDD,
- WX,
+ None = 0,
+ PDD = 1,
+ WX = 2,
}
}
diff --git a/AllBase/Model/Result/Result.cs b/AllBase/Model/Result/Result.cs
index f8cfa9a..278775a 100644
--- a/AllBase/Model/Result/Result.cs
+++ b/AllBase/Model/Result/Result.cs
@@ -20,6 +20,8 @@ namespace AllBase.Model.Result
public int Code { get; set; }
public string Msg { get; set; }
+ public bool? Success { get; set; }
+ public string TraceId { get; set; }
}
public class Result : Result
{
diff --git a/TEMUKJD/BLL/ImportConverters/ConvertToPddFactory.cs b/TEMUKJD/BLL/ImportConverters/ConvertToPddFactory.cs
index c3a495e..2cfb607 100644
--- a/TEMUKJD/BLL/ImportConverters/ConvertToPddFactory.cs
+++ b/TEMUKJD/BLL/ImportConverters/ConvertToPddFactory.cs
@@ -12,7 +12,7 @@ namespace TemuKJD.BLL.ImportConverts
{
public class ConverterToPddFactory
{
- private static List _converters = new List
+ private static List CreateConverters() => new List
{
new ConverterToPddV5(),
new ConverterWXToPddV1(),
@@ -24,7 +24,7 @@ namespace TemuKJD.BLL.ImportConverts
public static PDDFrontProductInfo5 Convert(string filePath)
{
- foreach (IGoodsConverter item in _converters)
+ foreach (IGoodsConverter item in CreateConverters())
{
try
{
@@ -47,7 +47,7 @@ namespace TemuKJD.BLL.ImportConverts
public static PDDFrontProductInfo5 SetContentConvert(string content)
{
- foreach (IGoodsConverter item in _converters)
+ foreach (IGoodsConverter item in CreateConverters())
{
try
{
diff --git a/TEMUKJD/BLL/ImportConverters/IGoodsConverter.cs b/TEMUKJD/BLL/ImportConverters/IGoodsConverter.cs
index a9ba3bb..de13d78 100644
--- a/TEMUKJD/BLL/ImportConverters/IGoodsConverter.cs
+++ b/TEMUKJD/BLL/ImportConverters/IGoodsConverter.cs
@@ -8,7 +8,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using System.Web.UI.WebControls;
+
namespace TemuKJD.BLL.ImportConverters
{
diff --git a/TEMUKJD/KJD.csproj b/TEMUKJD/KJD.csproj
index 677b131..b2d2d84 100644
--- a/TEMUKJD/KJD.csproj
+++ b/TEMUKJD/KJD.csproj
@@ -68,10 +68,10 @@
-
+
Form
-
+
Form
@@ -86,4 +86,7 @@
Resources.Designer.cs
+
+
+
diff --git a/TEMUKJD/KJD.csproj.user b/TEMUKJD/KJD.csproj.user
index 0382c37..5149de2 100644
--- a/TEMUKJD/KJD.csproj.user
+++ b/TEMUKJD/KJD.csproj.user
@@ -44,6 +44,9 @@
Form
+
+ Form
+
Form
@@ -65,6 +68,9 @@
Form
+
+ UserControl
+
Form
diff --git a/TEMUKJD/KJDForms/SystemForms/SystemForm.Designer.cs b/TEMUKJD/KJDForms/SystemForms/SystemForm.Designer.cs
index 7ad545c..58858b1 100644
--- a/TEMUKJD/KJDForms/SystemForms/SystemForm.Designer.cs
+++ b/TEMUKJD/KJDForms/SystemForms/SystemForm.Designer.cs
@@ -30,8 +30,9 @@
{
System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("解除限制");
System.Windows.Forms.TreeNode violationNode = new System.Windows.Forms.TreeNode("违规检测");
+ System.Windows.Forms.TreeNode complianceNode = new System.Windows.Forms.TreeNode("商品 AI 检测");
System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("系统设置", new System.Windows.Forms.TreeNode[] {
- treeNode1, violationNode});
+ treeNode1, violationNode, complianceNode});
this.panel1 = new System.Windows.Forms.Panel();
this.panel3 = new System.Windows.Forms.Panel();
this.panel2 = new System.Windows.Forms.Panel();
@@ -104,6 +105,7 @@
this.tv.Name = "tv";
treeNode1.Name = "解除限制";
treeNode1.Text = "解除限制";
+ complianceNode.Name = "商品 AI 检测";
violationNode.Name = "违规检测";
violationNode.Text = "违规检测";
treeNode2.Name = "系统设置";
diff --git a/TEMUKJD/KJDForms/SystemForms/SystemForm.cs b/TEMUKJD/KJDForms/SystemForms/SystemForm.cs
index ccfc34b..ea3cb35 100644
--- a/TEMUKJD/KJDForms/SystemForms/SystemForm.cs
+++ b/TEMUKJD/KJDForms/SystemForms/SystemForm.cs
@@ -16,6 +16,8 @@ namespace TemuKJD.KJDForms.SystemForms
{
public Lazy systemRemoveRestrictionsForm = new Lazy();
public Lazy systemViolationDetectionForm = new Lazy();
+ private readonly Lazy goodsComplianceForm = new Lazy();
+
public SystemForm()
{
InitializeComponent();
@@ -47,6 +49,9 @@ namespace TemuKJD.KJDForms.SystemForms
case "解除限制":
SetForm(systemRemoveRestrictionsForm.Value);
break;
+ case "商品 AI 检测":
+ SetForm(goodsComplianceForm.Value);
+ break;
case "违规检测":
SetForm(systemViolationDetectionForm.Value);
break;
diff --git a/TEMUKJD/KJDForms/SystemForms/SystemGoodsComplianceForm.Designer.cs b/TEMUKJD/KJDForms/SystemForms/SystemGoodsComplianceForm.Designer.cs
new file mode 100644
index 0000000..f00358c
--- /dev/null
+++ b/TEMUKJD/KJDForms/SystemForms/SystemGoodsComplianceForm.Designer.cs
@@ -0,0 +1,186 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace TemuKJD.KJDForms.SystemForms
+{
+ partial class SystemGoodsComplianceForm
+ {
+ private GroupBox imagesGroup;
+ private Label mainLabel;
+ private ComboBox main;
+ private NumericUpDown mainCount;
+ private Label mainUnit;
+ private Label detailLabel;
+ private ComboBox detail;
+ private NumericUpDown detailCount;
+ private Label detailUnit;
+ private CheckBox sku;
+ private GroupBox keywordsGroup;
+ private TextBox keywords;
+ private Label keywordsHint;
+ private Button btnSaveDetection;
+
+ private void InitializeComponent()
+ {
+ this.imagesGroup = new GroupBox();
+ this.mainLabel = new Label();
+ this.main = new ComboBox();
+ this.mainCount = new NumericUpDown();
+ this.mainUnit = new Label();
+ this.detailLabel = new Label();
+ this.detail = new ComboBox();
+ this.detailCount = new NumericUpDown();
+ this.detailUnit = new Label();
+ this.sku = new CheckBox();
+ this.keywordsGroup = new GroupBox();
+ this.keywords = new TextBox();
+ this.keywordsHint = new Label();
+ this.btnSaveDetection = new Button();
+ this.imagesGroup.SuspendLayout();
+ this.keywordsGroup.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mainCount)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.detailCount)).BeginInit();
+ this.SuspendLayout();
+
+ this.imagesGroup.Name = "imagesGroup";
+ this.imagesGroup.Location = new Point(20, 16);
+ this.imagesGroup.TabIndex = 0;
+ this.imagesGroup.Size = new Size(760, 153);
+ this.imagesGroup.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
+ this.imagesGroup.Text = "检测图片";
+ this.imagesGroup.Controls.Add(this.mainLabel);
+ this.imagesGroup.Controls.Add(this.main);
+ this.imagesGroup.Controls.Add(this.mainCount);
+ this.imagesGroup.Controls.Add(this.mainUnit);
+ this.imagesGroup.Controls.Add(this.detailLabel);
+ this.imagesGroup.Controls.Add(this.detail);
+ this.imagesGroup.Controls.Add(this.detailCount);
+ this.imagesGroup.Controls.Add(this.detailUnit);
+ this.imagesGroup.Controls.Add(this.sku);
+
+ this.mainLabel.Name = "mainLabel";
+ this.mainLabel.Location = new Point(16, 34);
+ this.mainLabel.TabIndex = 0;
+ this.mainLabel.AutoSize = true;
+ this.mainLabel.Text = "商品主图";
+
+ this.main.Name = "main";
+ this.main.Location = new Point(106, 30);
+ this.main.TabIndex = 1;
+ this.main.Width = 110;
+ this.main.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.main.Items.AddRange(new object[] { "全部", "前几张" });
+ this.main.SelectedIndexChanged += new System.EventHandler(this.ImageRangeChanged);
+
+ this.mainCount.Name = "mainCount";
+ this.mainCount.Location = new Point(228, 30);
+ this.mainCount.TabIndex = 2;
+ this.mainCount.Enabled = false;
+ this.mainCount.Minimum = 1M;
+ this.mainCount.Maximum = 999M;
+ this.mainCount.Value = 1M;
+ this.mainCount.Width = 60;
+ this.mainCount.AccessibleName = "商品主图检测张数";
+
+ this.mainUnit.Name = "mainUnit";
+ this.mainUnit.Location = new Point(294, 34);
+ this.mainUnit.TabIndex = 3;
+ this.mainUnit.AutoSize = true;
+ this.mainUnit.Text = "张";
+
+ this.detailLabel.Name = "detailLabel";
+ this.detailLabel.Location = new Point(16, 72);
+ this.detailLabel.TabIndex = 4;
+ this.detailLabel.AutoSize = true;
+ this.detailLabel.Text = "详情图";
+
+ this.detail.Name = "detail";
+ this.detail.Location = new Point(106, 68);
+ this.detail.TabIndex = 5;
+ this.detail.Width = 110;
+ this.detail.DropDownStyle = ComboBoxStyle.DropDownList;
+ this.detail.Items.AddRange(new object[] { "不检测", "前几张", "全部" });
+ this.detail.SelectedIndexChanged += new System.EventHandler(this.ImageRangeChanged);
+
+ this.detailCount.Name = "detailCount";
+ this.detailCount.Location = new Point(228, 68);
+ this.detailCount.TabIndex = 6;
+ this.detailCount.Enabled = false;
+ this.detailCount.Minimum = 1M;
+ this.detailCount.Maximum = 999M;
+ this.detailCount.Value = 1M;
+ this.detailCount.Width = 60;
+ this.detailCount.AccessibleName = "详情图检测张数";
+
+ this.detailUnit.Name = "detailUnit";
+ this.detailUnit.Location = new Point(294, 72);
+ this.detailUnit.TabIndex = 7;
+ this.detailUnit.AutoSize = true;
+ this.detailUnit.Text = "张";
+
+ this.sku.Name = "sku";
+ this.sku.Location = new Point(106, 112);
+ this.sku.TabIndex = 8;
+ this.sku.AutoSize = true;
+ this.sku.Text = "检测 SKU 图片";
+
+ this.keywordsGroup.Name = "keywordsGroup";
+ this.keywordsGroup.Location = new Point(20, 183);
+ this.keywordsGroup.TabIndex = 1;
+ this.keywordsGroup.Size = new Size(760, 143);
+ this.keywordsGroup.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
+ this.keywordsGroup.Text = "检测关键词";
+ this.keywordsGroup.Controls.Add(this.keywords);
+ this.keywordsGroup.Controls.Add(this.keywordsHint);
+
+ this.keywords.Name = "keywords";
+ this.keywords.Location = new Point(16, 27);
+ this.keywords.TabIndex = 0;
+ this.keywords.Size = new Size(728, 68);
+ this.keywords.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
+ this.keywords.Multiline = true;
+ this.keywords.AcceptsReturn = true;
+ this.keywords.ScrollBars = ScrollBars.Vertical;
+ this.keywords.AccessibleName = "检测关键词";
+
+ this.keywordsHint.Name = "keywordsHint";
+ this.keywordsHint.Location = new Point(16, 105);
+ this.keywordsHint.TabIndex = 1;
+ this.keywordsHint.AutoSize = true;
+ this.keywordsHint.ForeColor = SystemColors.GrayText;
+ this.keywordsHint.Text = "多个关键词可用逗号、分号、空格或换行分隔。";
+
+ this.btnSaveDetection.Name = "btnSaveDetection";
+ this.btnSaveDetection.Location = new Point(20, 341);
+ this.btnSaveDetection.TabIndex = 2;
+ this.btnSaveDetection.Size = new Size(96, 32);
+ this.btnSaveDetection.Text = "保存条件";
+ this.btnSaveDetection.BackColor = Color.FromArgb(63, 126, 255);
+ this.btnSaveDetection.ForeColor = Color.White;
+ this.btnSaveDetection.FlatStyle = FlatStyle.Flat;
+ this.btnSaveDetection.UseVisualStyleBackColor = false;
+ this.btnSaveDetection.Cursor = Cursors.Hand;
+ this.btnSaveDetection.FlatAppearance.BorderSize = 0;
+ this.btnSaveDetection.Click += new System.EventHandler(this.SaveDetectionClick);
+
+ this.AutoScaleMode = AutoScaleMode.None;
+ this.AutoScroll = true;
+ this.AutoScrollMinSize = new Size(365, 390);
+ this.BackColor = Color.White;
+ this.ClientSize = new Size(800, 450);
+ this.Font = new Font("微软雅黑", 9F);
+ this.Controls.Add(this.imagesGroup);
+ this.Controls.Add(this.keywordsGroup);
+ this.Controls.Add(this.btnSaveDetection);
+ this.Name = "SystemGoodsComplianceForm";
+ this.Text = "商品 AI 检测";
+ this.imagesGroup.ResumeLayout(false);
+ this.imagesGroup.PerformLayout();
+ this.keywordsGroup.ResumeLayout(false);
+ this.keywordsGroup.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mainCount)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.detailCount)).EndInit();
+ this.ResumeLayout(false);
+ }
+ }
+}
diff --git a/TEMUKJD/KJDForms/SystemForms/SystemGoodsComplianceForm.cs b/TEMUKJD/KJDForms/SystemForms/SystemGoodsComplianceForm.cs
new file mode 100644
index 0000000..c37c529
--- /dev/null
+++ b/TEMUKJD/KJDForms/SystemForms/SystemGoodsComplianceForm.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Windows.Forms;
+using Work.Services;
+using AllBase.Helper;
+using TemuKJD.BaseForms;
+
+namespace TemuKJD.KJDForms.SystemForms
+{
+ public partial class SystemGoodsComplianceForm : BaseForm
+ {
+ public SystemGoodsComplianceForm()
+ {
+ InitializeComponent();
+
+ VisibleChanged += (sender, e) => { if (Visible) LoadSettings(); };
+ LoadSettings();
+ }
+
+ private void LoadSettings()
+ {
+ var options = GlobalConfig.GlobalSettings.GoodsCompliance ?? new ComplianceOptions();
+ main.SelectedIndex = options.MainCount.HasValue ? 1 : 0;
+ detail.SelectedIndex = options.DetailCount == null ? 2 : options.DetailCount == 0 ? 0 : 1;
+ mainCount.Value = Math.Min(mainCount.Maximum, Math.Max(1, options.MainCount ?? 1));
+ detailCount.Value = Math.Min(detailCount.Maximum, Math.Max(1, options.DetailCount ?? 1));
+ sku.Checked = options.IncludeSku;
+ keywords.Text = options.Keywords;
+ }
+
+ private void ImageRangeChanged(object sender, EventArgs e)
+ {
+ mainCount.Enabled = main.SelectedIndex == 1;
+ detailCount.Enabled = detail.SelectedIndex == 1;
+ }
+
+ private void SaveDetectionClick(object sender, EventArgs e)
+ {
+ if (GoodsComplianceService.Keywords(keywords.Text).Length == 0)
+ {
+ MessageBox.Show(this, "请输入检测关键词。");
+ keywords.Focus();
+ return;
+ }
+ var settings = GlobalConfig.GlobalSettings;
+ var previous = settings.GoodsCompliance;
+ settings.GoodsCompliance = new ComplianceOptions
+ {
+ MainCount = main.SelectedIndex == 0 ? (int?)null : (int)mainCount.Value,
+ DetailCount = detail.SelectedIndex == 2 ? (int?)null : detail.SelectedIndex == 0 ? 0 : (int)detailCount.Value,
+ IncludeSku = sku.Checked,
+ Keywords = keywords.Text.Trim()
+ };
+ try { StartUp.SaveGlobalSettings(); }
+ catch (Exception ex)
+ {
+ settings.GoodsCompliance = previous;
+ MessageBox.Show(this, "保存检测条件失败:" + ex.Message);
+ return;
+ }
+ MessageBox.Show(this, "已保存 AI 检测条件。");
+ }
+
+ }
+}
diff --git a/TEMUKJD/PDDForms/ProductForms/PDDProductForm.cs b/TEMUKJD/PDDForms/ProductForms/PDDProductForm.cs
index 9fc1e30..eb62e23 100644
--- a/TEMUKJD/PDDForms/ProductForms/PDDProductForm.cs
+++ b/TEMUKJD/PDDForms/ProductForms/PDDProductForm.cs
@@ -9,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TemuKJD.BaseForms;
+using TemuKJD.WXForms.ProductForms;
using AllBase;
using TemuKJD.PDDForms.ProductSelectForms;
using TemuKJD.PDDForms.ProductForms.CouponForms;
diff --git a/TEMUKJD/UserComponent/UcNumericRange.cs b/TEMUKJD/UserComponent/UcNumericRange.cs
new file mode 100644
index 0000000..2d1cdfd
--- /dev/null
+++ b/TEMUKJD/UserComponent/UcNumericRange.cs
@@ -0,0 +1,159 @@
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace TemuKJD.UserComponent
+{
+ [DefaultEvent(nameof(ValueChanged))]
+ public class UcNumericRange : UserControl
+ {
+ private const decimal DefaultMaximum = 1000000m;
+ private const int NormalWidth = 220;
+ private const int CompactWidth = 112;
+ private readonly Label separator = new Label { Text = "至", AutoSize = true, Anchor = AnchorStyles.None };
+ private readonly NumericUpDown lower = CreateEditor("起始值");
+ private readonly NumericUpDown upper = CreateEditor("结束值");
+ private bool updating;
+
+ public UcNumericRange()
+ {
+ upper.Value = DefaultMaximum;
+ var layout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 3, RowCount = 1 };
+ layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50));
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50));
+ layout.Controls.Add(CreateCell(lower), 0, 0);
+ layout.Controls.Add(separator, 1, 0);
+ layout.Controls.Add(CreateCell(upper), 2, 0);
+ Controls.Add(layout);
+ Size = new Size(NormalWidth, lower.PreferredHeight + 2);
+ lower.ValueChanged += (s, e) => UpdateRange(lower, upper);
+ upper.ValueChanged += (s, e) => UpdateRange(upper, lower);
+ }
+
+ [Description("两个数值之间显示的文字。"), DefaultValue("至")]
+ public string SeparatorText
+ {
+ get => separator.Text;
+ set => separator.Text = value;
+ }
+
+ [Description("使用窄布局并隐藏微调按钮,仍可用方向键调整数值。"), DefaultValue(false)]
+ public bool Compact
+ {
+ get => separator.Margin == Padding.Empty;
+ set
+ {
+ if (value == Compact) return;
+ separator.Margin = value ? Padding.Empty : new Padding(3, 0, 3, 0);
+ SetCompactBorder(lower, value);
+ SetCompactBorder(upper, value);
+ if (Dock == DockStyle.None && Width == (value ? NormalWidth : CompactWidth))
+ Width = value ? CompactWidth : NormalWidth;
+ }
+ }
+
+ private static void SetCompactBorder(NumericUpDown editor, bool compact)
+ {
+ var cell = (Panel)editor.Parent;
+ editor.BorderStyle = compact ? BorderStyle.None : BorderStyle.Fixed3D;
+ cell.BorderStyle = compact ? BorderStyle.FixedSingle : BorderStyle.None;
+ cell.BackColor = compact ? editor.BackColor : Color.Empty;
+ cell.PerformLayout();
+ }
+
+ private Panel CreateCell(NumericUpDown editor)
+ {
+ var cell = new Panel { Dock = DockStyle.Fill, Margin = Padding.Empty };
+ cell.Controls.Add(editor);
+ var buttons = editor.Controls[0] is TextBox ? editor.Controls[1] : editor.Controls[0];
+ // 紧凑模式将右侧按钮移出可见区域,保留原生数字输入和键盘操作。
+ cell.Layout += (s, e) =>
+ {
+ int clippedWidth = Compact ? editor.Width - buttons.Left : 0;
+ editor.Width = cell.ClientSize.Width + clippedWidth;
+ editor.Top = Compact ? Math.Max(0, (cell.ClientSize.Height - editor.Height) / 2) : 0;
+ };
+ return cell;
+ }
+
+ private static NumericUpDown CreateEditor(string name)
+ {
+ return new NumericUpDown { AccessibleName = name, Margin = Padding.Empty, Maximum = DefaultMaximum };
+ }
+
+ [Description("允许输入的最小值。"), DefaultValue(typeof(decimal), "0")]
+ public decimal Minimum
+ {
+ get => lower.Minimum;
+ set => SetLimits(value, Math.Max(value, Maximum));
+ }
+
+ [Description("允许输入的最大值。"), DefaultValue(typeof(decimal), "1000000")]
+ public decimal Maximum
+ {
+ get => lower.Maximum;
+ set => SetLimits(Math.Min(value, Minimum), value);
+ }
+
+ [Description("选中范围的起始值。"), DefaultValue(typeof(decimal), "0")]
+ public decimal LowerValue
+ {
+ get => lower.Value;
+ set => lower.Value = value;
+ }
+
+ [Description("选中范围的结束值。"), DefaultValue(typeof(decimal), "1000000")]
+ public decimal UpperValue
+ {
+ get => upper.Value;
+ set => upper.Value = value;
+ }
+
+ [Description("数字输入框显示的小数位数。"), DefaultValue(0)]
+ public int DecimalPlaces
+ {
+ get => lower.DecimalPlaces;
+ set
+ {
+ if (value < 0 || value > 28) throw new ArgumentOutOfRangeException(nameof(value));
+ lower.DecimalPlaces = upper.DecimalPlaces = value;
+ decimal increment = 1m;
+ for (int i = 0; i < value; i++)
+ increment /= 10m;
+ lower.Increment = upper.Increment = increment;
+ }
+ }
+
+ public event EventHandler ValueChanged;
+
+ private void SetLimits(decimal minimum, decimal maximum)
+ {
+ decimal oldLower = LowerValue, oldUpper = UpperValue;
+ updating = true;
+ try
+ {
+ lower.Minimum = upper.Minimum = minimum;
+ lower.Maximum = upper.Maximum = maximum;
+ }
+ finally { updating = false; }
+ if (LowerValue != oldLower || UpperValue != oldUpper)
+ ValueChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void UpdateRange(NumericUpDown edited, NumericUpDown other)
+ {
+ if (updating) return;
+ updating = true;
+ try
+ {
+ if (LowerValue > UpperValue)
+ other.Value = edited.Value;
+ }
+ finally { updating = false; }
+ ValueChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+}
diff --git a/TEMUKJD/UserForm/LoginForm.cs b/TEMUKJD/UserForm/LoginForm.cs
index b5b6ef8..93215cf 100644
--- a/TEMUKJD/UserForm/LoginForm.cs
+++ b/TEMUKJD/UserForm/LoginForm.cs
@@ -288,28 +288,28 @@ namespace TemuKJD.UserForm
- _ = Task.Run(() =>
- {
- try
- {
- StoreApiClient.Instance.Auth.Login(new LoginRequest
- {
- UserName = BaseClass.UserName,
- Password = BaseClass.PassWord
- });
- }
- catch (Exception ex)
- {
- try
- {
- LogSave.AddFile("Store API 登录失败:" + ex.Message);
- }
- catch
- {
- // 日志失败也不能影响主程序。
- }
- }
- });
+ //_ = Task.Run(() =>
+ //{
+ // try
+ // {
+ // StoreApiClient.Instance.Auth.Login(new LoginRequest
+ // {
+ // UserName = BaseClass.UserName,
+ // Password = BaseClass.PassWord
+ // });
+ // }
+ // catch (Exception ex)
+ // {
+ // try
+ // {
+ // LogSave.AddFile("Store API 登录失败:" + ex.Message);
+ // }
+ // catch
+ // {
+ // // 日志失败也不能影响主程序。
+ // }
+ // }
+ //});
Hide();
mf.Hide();
diff --git a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsFavoriteForm.Designer.cs b/TEMUKJD/WXForms/ProductForms/CloudGoodsFavoriteForm.Designer.cs
similarity index 74%
rename from TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsFavoriteForm.Designer.cs
rename to TEMUKJD/WXForms/ProductForms/CloudGoodsFavoriteForm.Designer.cs
index 40e20e3..ea108f4 100644
--- a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsFavoriteForm.Designer.cs
+++ b/TEMUKJD/WXForms/ProductForms/CloudGoodsFavoriteForm.Designer.cs
@@ -1,5 +1,5 @@
-namespace TemuKJD.PDDForms.ProductSelectForms
+namespace TemuKJD.WXForms.ProductForms
{
partial class CloudGoodsFavoriteForm
{
@@ -29,25 +29,25 @@ namespace TemuKJD.PDDForms.ProductSelectForms
///
private void InitializeComponent()
{
- System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle();
- System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle10 = new System.Windows.Forms.DataGridViewCellStyle();
+ System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+ System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+ this.cb_safe = new System.Windows.Forms.ComboBox();
+ this.label4 = new System.Windows.Forms.Label();
+ this.num_price = new TemuKJD.UserComponent.UcNumericRange();
+ this.num_sold = new TemuKJD.UserComponent.UcNumericRange();
this.panel1 = new System.Windows.Forms.Panel();
+ this.btnSafeDetection = new DevComponents.DotNetBar.PanelEx();
this.btn_removefavorite = new DevComponents.DotNetBar.PanelEx();
this.bt_importUploadList = new DevComponents.DotNetBar.PanelEx();
this.label5r = new System.Windows.Forms.Label();
- this.cb_Platform = new System.Windows.Forms.ComboBox();
+ this.cb_Platformr = new System.Windows.Forms.ComboBox();
this.cmbCatSelect2 = new System.Windows.Forms.ComboBox();
this.cmbCatSelect1 = new System.Windows.Forms.ComboBox();
this.cmbCatSelect4 = new System.Windows.Forms.ComboBox();
this.cmbCatSelect3 = new System.Windows.Forms.ComboBox();
- this.label4 = new System.Windows.Forms.Label();
- this.num_maxprice = new System.Windows.Forms.NumericUpDown();
- this.num_minprice = new System.Windows.Forms.NumericUpDown();
this.label7 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
- this.num_maxsold = new System.Windows.Forms.NumericUpDown();
- this.num_minsold = new System.Windows.Forms.NumericUpDown();
this.txt_title = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
@@ -57,6 +57,9 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.splitContainer2 = new System.Windows.Forms.SplitContainer();
this.pbLoading = new System.Windows.Forms.PictureBox();
this.dgvData = new TemuKJD.UserComponent.DataGridViewEx();
+ this.ucSplitPage1 = new TemuKJD.UserControls.UcSplitPage();
+ this.statusBar = new TemuKJD.UserControls.StatusBarUserControl();
+ this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
this.colSelect = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.c_Platform = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column1 = new System.Windows.Forms.DataGridViewImageColumn();
@@ -64,23 +67,19 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column4 = new System.Windows.Forms.DataGridViewTextBoxColumn();
- this.ucSplitPage1 = new TemuKJD.UserControls.UcSplitPage();
- this.statusBar = new TemuKJD.UserControls.StatusBarUserControl();
+ this.colDetectionResult = new System.Windows.Forms.DataGridViewTextBoxColumn();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.panel1.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)(this.num_maxprice)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.num_minprice)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.num_maxsold)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.num_minsold)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
this.splitContainer2.Panel1.SuspendLayout();
this.splitContainer2.Panel2.SuspendLayout();
this.splitContainer2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pbLoading)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dgvData)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
this.SuspendLayout();
//
// splitContainer1
@@ -94,20 +93,19 @@ namespace TemuKJD.PDDForms.ProductSelectForms
//
// splitContainer1.Panel1
//
+ this.splitContainer1.Panel1.Controls.Add(this.cb_safe);
+ this.splitContainer1.Panel1.Controls.Add(this.label4);
+ this.splitContainer1.Panel1.Controls.Add(this.num_price);
+ this.splitContainer1.Panel1.Controls.Add(this.num_sold);
this.splitContainer1.Panel1.Controls.Add(this.panel1);
this.splitContainer1.Panel1.Controls.Add(this.label5r);
- this.splitContainer1.Panel1.Controls.Add(this.cb_Platform);
+ this.splitContainer1.Panel1.Controls.Add(this.cb_Platformr);
this.splitContainer1.Panel1.Controls.Add(this.cmbCatSelect2);
this.splitContainer1.Panel1.Controls.Add(this.cmbCatSelect1);
this.splitContainer1.Panel1.Controls.Add(this.cmbCatSelect4);
this.splitContainer1.Panel1.Controls.Add(this.cmbCatSelect3);
- this.splitContainer1.Panel1.Controls.Add(this.label4);
- this.splitContainer1.Panel1.Controls.Add(this.num_maxprice);
- this.splitContainer1.Panel1.Controls.Add(this.num_minprice);
this.splitContainer1.Panel1.Controls.Add(this.label7);
this.splitContainer1.Panel1.Controls.Add(this.label3);
- this.splitContainer1.Panel1.Controls.Add(this.num_maxsold);
- this.splitContainer1.Panel1.Controls.Add(this.num_minsold);
this.splitContainer1.Panel1.Controls.Add(this.txt_title);
this.splitContainer1.Panel1.Controls.Add(this.label1);
this.splitContainer1.Panel1.Controls.Add(this.label6);
@@ -119,26 +117,133 @@ namespace TemuKJD.PDDForms.ProductSelectForms
//
this.splitContainer1.Panel2.Controls.Add(this.splitContainer2);
this.splitContainer1.Panel2.Margin = new System.Windows.Forms.Padding(4);
- this.splitContainer1.Size = new System.Drawing.Size(1643, 530);
+ this.splitContainer1.Size = new System.Drawing.Size(1719, 530);
this.splitContainer1.TabIndex = 35;
+ //
+ // cb_safe
+ //
+ this.cb_safe.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cb_safe.DropDownWidth = 150;
+ this.cb_safe.FormattingEnabled = true;
+ this.cb_safe.Items.AddRange(new object[] {
+ "全部",
+ "安全",
+ "违规",
+ "未检测"});
+ this.cb_safe.Location = new System.Drawing.Point(1141, 13);
+ this.cb_safe.Name = "cb_safe";
+ this.cb_safe.Size = new System.Drawing.Size(97, 25);
+ this.cb_safe.TabIndex = 930;
+ //
+ // label4
+ //
+ this.label4.AutoSize = true;
+ this.label4.Location = new System.Drawing.Point(1104, 17);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(44, 17);
+ this.label4.TabIndex = 929;
+ this.label4.Text = "检测:";
+ //
+ // num_price
+ //
+ this.num_price.Compact = true;
+ this.num_price.Location = new System.Drawing.Point(391, 15);
+ this.num_price.LowerValue = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.num_price.Maximum = new decimal(new int[] {
+ 9999999,
+ 0,
+ 0,
+ 0});
+ this.num_price.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.num_price.Name = "num_price";
+ this.num_price.SeparatorText = "";
+ this.num_price.Size = new System.Drawing.Size(108, 23);
+ this.num_price.TabIndex = 922;
+ this.num_price.UpperValue = new decimal(new int[] {
+ 9999999,
+ 0,
+ 0,
+ 0});
+ //
+ // num_sold
+ //
+ this.num_sold.Compact = true;
+ this.num_sold.Location = new System.Drawing.Point(218, 15);
+ this.num_sold.LowerValue = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.num_sold.Maximum = new decimal(new int[] {
+ 9999999,
+ 0,
+ 0,
+ 0});
+ this.num_sold.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.num_sold.Name = "num_sold";
+ this.num_sold.SeparatorText = "";
+ this.num_sold.Size = new System.Drawing.Size(108, 23);
+ this.num_sold.TabIndex = 921;
+ this.num_sold.UpperValue = new decimal(new int[] {
+ 9999999,
+ 0,
+ 0,
+ 0});
//
// panel1
//
+ this.panel1.Controls.Add(this.btnSafeDetection);
this.panel1.Controls.Add(this.btn_removefavorite);
this.panel1.Controls.Add(this.bt_importUploadList);
this.panel1.Dock = System.Windows.Forms.DockStyle.Right;
- this.panel1.Location = new System.Drawing.Point(1507, 0);
+ this.panel1.Location = new System.Drawing.Point(1509, 0);
this.panel1.Name = "panel1";
- this.panel1.Size = new System.Drawing.Size(136, 50);
+ this.panel1.Size = new System.Drawing.Size(210, 50);
this.panel1.TabIndex = 914;
//
+ // btnSafeDetection
+ //
+ this.btnSafeDetection.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled;
+ this.btnSafeDetection.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnSafeDetection.DisabledBackColor = System.Drawing.Color.Empty;
+ this.btnSafeDetection.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+ this.btnSafeDetection.Location = new System.Drawing.Point(132, 14);
+ this.btnSafeDetection.Name = "btnSafeDetection";
+ this.btnSafeDetection.Size = new System.Drawing.Size(58, 23);
+ this.btnSafeDetection.Style.Alignment = System.Drawing.StringAlignment.Center;
+ this.btnSafeDetection.Style.BackColor1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(126)))), ((int)(((byte)(255)))));
+ this.btnSafeDetection.Style.BackColor2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(126)))), ((int)(((byte)(255)))));
+ this.btnSafeDetection.Style.BorderColor.ColorSchemePart = DevComponents.DotNetBar.eColorSchemePart.PanelBorder;
+ this.btnSafeDetection.Style.BorderDashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ this.btnSafeDetection.Style.BorderSide = DevComponents.DotNetBar.eBorderSide.None;
+ this.btnSafeDetection.Style.ForeColor.Color = System.Drawing.Color.White;
+ this.btnSafeDetection.Style.GradientAngle = 90;
+ this.btnSafeDetection.StyleMouseOver.Alignment = System.Drawing.StringAlignment.Center;
+ this.btnSafeDetection.StyleMouseOver.BackColor1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(102)))), ((int)(((byte)(255)))));
+ this.btnSafeDetection.StyleMouseOver.BackColor2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(102)))), ((int)(((byte)(255)))));
+ this.btnSafeDetection.TabIndex = 451;
+ this.btnSafeDetection.Text = "AI检测";
+ this.btnSafeDetection.Click += new System.EventHandler(this.btnSafeDetection_Click);
+ //
// btn_removefavorite
//
this.btn_removefavorite.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled;
this.btn_removefavorite.Cursor = System.Windows.Forms.Cursors.Hand;
this.btn_removefavorite.DisabledBackColor = System.Drawing.Color.Empty;
this.btn_removefavorite.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
- this.btn_removefavorite.Location = new System.Drawing.Point(67, 14);
+ this.btn_removefavorite.Location = new System.Drawing.Point(68, 14);
this.btn_removefavorite.Name = "btn_removefavorite";
this.btn_removefavorite.Size = new System.Drawing.Size(58, 23);
this.btn_removefavorite.Style.Alignment = System.Drawing.StringAlignment.Center;
@@ -183,31 +288,31 @@ namespace TemuKJD.PDDForms.ProductSelectForms
// label5r
//
this.label5r.AutoSize = true;
- this.label5r.Location = new System.Drawing.Point(1048, 16);
+ this.label5r.Location = new System.Drawing.Point(942, 16);
this.label5r.Name = "label5r";
this.label5r.Size = new System.Drawing.Size(44, 17);
this.label5r.TabIndex = 907;
this.label5r.Text = "来源:";
//
- // cb_Platform
+ // cb_Platformr
//
- this.cb_Platform.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
- this.cb_Platform.DropDownWidth = 150;
- this.cb_Platform.FormattingEnabled = true;
- this.cb_Platform.Items.AddRange(new object[] {
+ this.cb_Platformr.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cb_Platformr.DropDownWidth = 150;
+ this.cb_Platformr.FormattingEnabled = true;
+ this.cb_Platformr.Items.AddRange(new object[] {
"全部",
"多多",
"微信"});
- this.cb_Platform.Location = new System.Drawing.Point(1098, 13);
- this.cb_Platform.Name = "cb_Platform";
- this.cb_Platform.Size = new System.Drawing.Size(97, 25);
- this.cb_Platform.TabIndex = 906;
+ this.cb_Platformr.Location = new System.Drawing.Point(992, 13);
+ this.cb_Platformr.Name = "cb_Platformr";
+ this.cb_Platformr.Size = new System.Drawing.Size(97, 25);
+ this.cb_Platformr.TabIndex = 906;
//
// cmbCatSelect2
//
this.cmbCatSelect2.DropDownWidth = 150;
this.cmbCatSelect2.FormattingEnabled = true;
- this.cmbCatSelect2.Location = new System.Drawing.Point(745, 12);
+ this.cmbCatSelect2.Location = new System.Drawing.Point(639, 12);
this.cmbCatSelect2.Name = "cmbCatSelect2";
this.cmbCatSelect2.Size = new System.Drawing.Size(97, 25);
this.cmbCatSelect2.TabIndex = 897;
@@ -216,7 +321,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
//
this.cmbCatSelect1.DropDownWidth = 150;
this.cmbCatSelect1.FormattingEnabled = true;
- this.cmbCatSelect1.Location = new System.Drawing.Point(648, 12);
+ this.cmbCatSelect1.Location = new System.Drawing.Point(542, 12);
this.cmbCatSelect1.Name = "cmbCatSelect1";
this.cmbCatSelect1.Size = new System.Drawing.Size(97, 25);
this.cmbCatSelect1.TabIndex = 896;
@@ -225,7 +330,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
//
this.cmbCatSelect4.DropDownWidth = 150;
this.cmbCatSelect4.FormattingEnabled = true;
- this.cmbCatSelect4.Location = new System.Drawing.Point(939, 12);
+ this.cmbCatSelect4.Location = new System.Drawing.Point(833, 12);
this.cmbCatSelect4.Name = "cmbCatSelect4";
this.cmbCatSelect4.Size = new System.Drawing.Size(97, 25);
this.cmbCatSelect4.TabIndex = 899;
@@ -234,50 +339,15 @@ namespace TemuKJD.PDDForms.ProductSelectForms
//
this.cmbCatSelect3.DropDownWidth = 150;
this.cmbCatSelect3.FormattingEnabled = true;
- this.cmbCatSelect3.Location = new System.Drawing.Point(842, 12);
+ this.cmbCatSelect3.Location = new System.Drawing.Point(736, 12);
this.cmbCatSelect3.Name = "cmbCatSelect3";
this.cmbCatSelect3.Size = new System.Drawing.Size(97, 25);
this.cmbCatSelect3.TabIndex = 898;
//
- // label4
- //
- this.label4.AutoSize = true;
- this.label4.ForeColor = System.Drawing.Color.Gray;
- this.label4.Location = new System.Drawing.Point(518, 17);
- this.label4.Margin = new System.Windows.Forms.Padding(0);
- this.label4.Name = "label4";
- this.label4.Size = new System.Drawing.Size(13, 17);
- this.label4.TabIndex = 488;
- this.label4.Text = "-";
- //
- // num_maxprice
- //
- this.num_maxprice.Location = new System.Drawing.Point(534, 14);
- this.num_maxprice.Maximum = new decimal(new int[] {
- 99999,
- 0,
- 0,
- 0});
- this.num_maxprice.Name = "num_maxprice";
- this.num_maxprice.Size = new System.Drawing.Size(70, 23);
- this.num_maxprice.TabIndex = 487;
- //
- // num_minprice
- //
- this.num_minprice.Location = new System.Drawing.Point(447, 14);
- this.num_minprice.Maximum = new decimal(new int[] {
- 99999,
- 0,
- 0,
- 0});
- this.num_minprice.Name = "num_minprice";
- this.num_minprice.Size = new System.Drawing.Size(70, 23);
- this.num_minprice.TabIndex = 486;
- //
// label7
//
this.label7.AutoSize = true;
- this.label7.Location = new System.Drawing.Point(388, 17);
+ this.label7.Location = new System.Drawing.Point(332, 17);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(64, 17);
this.label7.TabIndex = 485;
@@ -294,30 +364,6 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.label3.TabIndex = 484;
this.label3.Text = "-";
//
- // num_maxsold
- //
- this.num_maxsold.Location = new System.Drawing.Point(310, 14);
- this.num_maxsold.Maximum = new decimal(new int[] {
- 9999999,
- 0,
- 0,
- 0});
- this.num_maxsold.Name = "num_maxsold";
- this.num_maxsold.Size = new System.Drawing.Size(70, 23);
- this.num_maxsold.TabIndex = 483;
- //
- // num_minsold
- //
- this.num_minsold.Location = new System.Drawing.Point(223, 14);
- this.num_minsold.Maximum = new decimal(new int[] {
- 9999999,
- 0,
- 0,
- 0});
- this.num_minsold.Name = "num_minsold";
- this.num_minsold.Size = new System.Drawing.Size(70, 23);
- this.num_minsold.TabIndex = 482;
- //
// txt_title
//
this.txt_title.Location = new System.Drawing.Point(60, 14);
@@ -337,7 +383,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
// label6
//
this.label6.AutoSize = true;
- this.label6.Location = new System.Drawing.Point(610, 17);
+ this.label6.Location = new System.Drawing.Point(504, 17);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(44, 17);
this.label6.TabIndex = 440;
@@ -349,7 +395,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.btnSearch.Cursor = System.Windows.Forms.Cursors.Hand;
this.btnSearch.DisabledBackColor = System.Drawing.Color.Empty;
this.btnSearch.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
- this.btnSearch.Location = new System.Drawing.Point(1208, 14);
+ this.btnSearch.Location = new System.Drawing.Point(1257, 14);
this.btnSearch.Name = "btnSearch";
this.btnSearch.Size = new System.Drawing.Size(58, 23);
this.btnSearch.Style.Alignment = System.Drawing.StringAlignment.Center;
@@ -372,7 +418,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.pnlEmpty.CanvasColor = System.Drawing.SystemColors.Control;
this.pnlEmpty.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled;
this.pnlEmpty.DisabledBackColor = System.Drawing.Color.Empty;
- this.pnlEmpty.Location = new System.Drawing.Point(1272, 14);
+ this.pnlEmpty.Location = new System.Drawing.Point(1321, 15);
this.pnlEmpty.Name = "pnlEmpty";
this.pnlEmpty.Size = new System.Drawing.Size(58, 23);
this.pnlEmpty.Style.Alignment = System.Drawing.StringAlignment.Center;
@@ -420,7 +466,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
//
this.splitContainer2.Panel2.Controls.Add(this.ucSplitPage1);
this.splitContainer2.Panel2.Controls.Add(this.statusBar);
- this.splitContainer2.Size = new System.Drawing.Size(1643, 476);
+ this.splitContainer2.Size = new System.Drawing.Size(1719, 476);
this.splitContainer2.SplitterDistance = 447;
this.splitContainer2.TabIndex = 0;
//
@@ -428,7 +474,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
//
this.pbLoading.Anchor = System.Windows.Forms.AnchorStyles.None;
this.pbLoading.Image = global::TemuKJD.Properties.Resources.loading_blue;
- this.pbLoading.Location = new System.Drawing.Point(796, 198);
+ this.pbLoading.Location = new System.Drawing.Point(834, 198);
this.pbLoading.Name = "pbLoading";
this.pbLoading.Size = new System.Drawing.Size(50, 50);
this.pbLoading.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
@@ -439,20 +485,20 @@ namespace TemuKJD.PDDForms.ProductSelectForms
//
this.dgvData.AllowUserToAddRows = false;
this.dgvData.AllowUserToDeleteRows = false;
- dataGridViewCellStyle9.BackColor = System.Drawing.Color.WhiteSmoke;
- this.dgvData.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle9;
+ dataGridViewCellStyle1.BackColor = System.Drawing.Color.WhiteSmoke;
+ this.dgvData.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1;
this.dgvData.AutoRowNumber = true;
this.dgvData.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dgvData.BackgroundColor = System.Drawing.Color.White;
this.dgvData.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(126)))), ((int)(((byte)(255)))));
- dataGridViewCellStyle10.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
- dataGridViewCellStyle10.BackColor = System.Drawing.SystemColors.Control;
- dataGridViewCellStyle10.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
- dataGridViewCellStyle10.ForeColor = System.Drawing.SystemColors.WindowText;
- dataGridViewCellStyle10.SelectionBackColor = System.Drawing.SystemColors.Highlight;
- dataGridViewCellStyle10.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
- dataGridViewCellStyle10.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
- this.dgvData.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle10;
+ dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+ dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Control;
+ dataGridViewCellStyle2.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+ dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.WindowText;
+ dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+ dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+ dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+ this.dgvData.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle2;
this.dgvData.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgvData.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.colSelect,
@@ -461,7 +507,8 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.c_goodsid,
this.Column2,
this.Column3,
- this.Column4});
+ this.Column4,
+ this.colDetectionResult});
this.dgvData.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgvData.Location = new System.Drawing.Point(4, 0);
this.dgvData.MultiSelectCheckBox = true;
@@ -469,9 +516,44 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.dgvData.ReadOnly = true;
this.dgvData.RowTemplate.Height = 60;
this.dgvData.ShowCellErrors = false;
- this.dgvData.Size = new System.Drawing.Size(1635, 447);
+ this.dgvData.Size = new System.Drawing.Size(1711, 447);
this.dgvData.TabIndex = 5;
//
+ // ucSplitPage1
+ //
+ this.ucSplitPage1.CurrentPage = 1;
+ this.ucSplitPage1.Dock = System.Windows.Forms.DockStyle.Right;
+ this.ucSplitPage1.IsShowPageSize = true;
+ this.ucSplitPage1.IsShowTotalCount = true;
+ this.ucSplitPage1.Location = new System.Drawing.Point(1257, 0);
+ this.ucSplitPage1.Margin = new System.Windows.Forms.Padding(0);
+ this.ucSplitPage1.MaxPageSizeValue = 2000;
+ this.ucSplitPage1.Name = "ucSplitPage1";
+ this.ucSplitPage1.PageSize = 50;
+ this.ucSplitPage1.Size = new System.Drawing.Size(462, 25);
+ this.ucSplitPage1.TabIndex = 4;
+ this.ucSplitPage1.TotalCount = ((long)(0));
+ this.ucSplitPage1.TotalPage = 1;
+ //
+ // statusBar
+ //
+ this.statusBar.BackColor = System.Drawing.Color.White;
+ this.statusBar.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.statusBar.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
+ this.statusBar.Location = new System.Drawing.Point(0, 0);
+ this.statusBar.Margin = new System.Windows.Forms.Padding(0);
+ this.statusBar.Name = "statusBar";
+ this.statusBar.Size = new System.Drawing.Size(1719, 25);
+ this.statusBar.TabIndex = 1;
+ this.statusBar.Visible = false;
+ //
+ // numericUpDown1
+ //
+ this.numericUpDown1.Location = new System.Drawing.Point(918, 274);
+ this.numericUpDown1.Name = "numericUpDown1";
+ this.numericUpDown1.Size = new System.Drawing.Size(120, 21);
+ this.numericUpDown1.TabIndex = 7;
+ //
// colSelect
//
this.colSelect.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
@@ -537,38 +619,20 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.Column4.ReadOnly = true;
this.Column4.Width = 120;
//
- // ucSplitPage1
+ // colDetectionResult
//
- this.ucSplitPage1.CurrentPage = 1;
- this.ucSplitPage1.Dock = System.Windows.Forms.DockStyle.Right;
- this.ucSplitPage1.IsShowPageSize = true;
- this.ucSplitPage1.IsShowTotalCount = true;
- this.ucSplitPage1.Location = new System.Drawing.Point(1181, 0);
- this.ucSplitPage1.Margin = new System.Windows.Forms.Padding(0);
- this.ucSplitPage1.MaxPageSizeValue = 2000;
- this.ucSplitPage1.Name = "ucSplitPage1";
- this.ucSplitPage1.PageSize = 50;
- this.ucSplitPage1.Size = new System.Drawing.Size(462, 25);
- this.ucSplitPage1.TabIndex = 4;
- this.ucSplitPage1.TotalCount = ((long)(0));
- this.ucSplitPage1.TotalPage = 1;
- //
- // statusBar
- //
- this.statusBar.BackColor = System.Drawing.Color.White;
- this.statusBar.Dock = System.Windows.Forms.DockStyle.Fill;
- this.statusBar.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
- this.statusBar.Location = new System.Drawing.Point(0, 0);
- this.statusBar.Margin = new System.Windows.Forms.Padding(0);
- this.statusBar.Name = "statusBar";
- this.statusBar.Size = new System.Drawing.Size(1643, 25);
- this.statusBar.TabIndex = 1;
- this.statusBar.Visible = false;
+ this.colDetectionResult.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+ this.colDetectionResult.HeaderText = "AI检测结果";
+ this.colDetectionResult.MinimumWidth = 50;
+ this.colDetectionResult.Name = "colDetectionResult";
+ this.colDetectionResult.ReadOnly = true;
+ this.colDetectionResult.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+ this.colDetectionResult.Width = 160;
//
// CloudGoodsFavoriteForm
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
- this.ClientSize = new System.Drawing.Size(1643, 530);
+ this.ClientSize = new System.Drawing.Size(1719, 530);
this.Controls.Add(this.splitContainer1);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.KeyPreview = true;
@@ -581,16 +645,13 @@ namespace TemuKJD.PDDForms.ProductSelectForms
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
this.panel1.ResumeLayout(false);
- ((System.ComponentModel.ISupportInitialize)(this.num_maxprice)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.num_minprice)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.num_maxsold)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.num_minsold)).EndInit();
this.splitContainer2.Panel1.ResumeLayout(false);
this.splitContainer2.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
this.splitContainer2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pbLoading)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dgvData)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
this.ResumeLayout(false);
}
@@ -609,11 +670,6 @@ namespace TemuKJD.PDDForms.ProductSelectForms
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox txt_title;
private System.Windows.Forms.Label label1;
- private System.Windows.Forms.NumericUpDown num_maxsold;
- private System.Windows.Forms.NumericUpDown num_minsold;
- private System.Windows.Forms.Label label4;
- private System.Windows.Forms.NumericUpDown num_maxprice;
- private System.Windows.Forms.NumericUpDown num_minprice;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.ComboBox cmbCatSelect2;
@@ -621,7 +677,16 @@ namespace TemuKJD.PDDForms.ProductSelectForms
private System.Windows.Forms.ComboBox cmbCatSelect4;
private System.Windows.Forms.ComboBox cmbCatSelect3;
private System.Windows.Forms.Label label5r;
- private System.Windows.Forms.ComboBox cb_Platform;
+ private System.Windows.Forms.ComboBox cb_Platformr;
+ private System.Windows.Forms.Panel panel1;
+ private DevComponents.DotNetBar.PanelEx btn_removefavorite;
+ private DevComponents.DotNetBar.PanelEx bt_importUploadList;
+ private DevComponents.DotNetBar.PanelEx btnSafeDetection;
+ private System.Windows.Forms.NumericUpDown numericUpDown1;
+ private UserComponent.UcNumericRange num_sold;
+ private UserComponent.UcNumericRange num_price;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.ComboBox cb_safe;
private System.Windows.Forms.DataGridViewCheckBoxColumn colSelect;
private System.Windows.Forms.DataGridViewTextBoxColumn c_Platform;
private System.Windows.Forms.DataGridViewImageColumn Column1;
@@ -629,8 +694,6 @@ namespace TemuKJD.PDDForms.ProductSelectForms
private System.Windows.Forms.DataGridViewTextBoxColumn Column2;
private System.Windows.Forms.DataGridViewTextBoxColumn Column3;
private System.Windows.Forms.DataGridViewTextBoxColumn Column4;
- private System.Windows.Forms.Panel panel1;
- private DevComponents.DotNetBar.PanelEx btn_removefavorite;
- private DevComponents.DotNetBar.PanelEx bt_importUploadList;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colDetectionResult;
}
-}
\ No newline at end of file
+}
diff --git a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsFavoriteForm.cs b/TEMUKJD/WXForms/ProductForms/CloudGoodsFavoriteForm.cs
similarity index 77%
rename from TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsFavoriteForm.cs
rename to TEMUKJD/WXForms/ProductForms/CloudGoodsFavoriteForm.cs
index 6590cc1..ed9a9d9 100644
--- a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsFavoriteForm.cs
+++ b/TEMUKJD/WXForms/ProductForms/CloudGoodsFavoriteForm.cs
@@ -1,3 +1,4 @@
+using TemuKJD.BLL;
using AllBase;
using AllBase.Helper;
using AllBase.Model.Result;
@@ -19,28 +20,30 @@ using System.Net;
using System.Net.Http;
using System.Security.Policy;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TemuKJD.BaseForms;
using TemuKJD.Extension;
using TemuKJD.ToolForms;
using TemuKJD.UserControls;
-using TemuKJD.WXForms.ProductForms;
using Work;
using Work.GoodDetail;
using Work.Services;
-namespace TemuKJD.PDDForms.ProductSelectForms
+namespace TemuKJD.WXForms.ProductForms
{
public partial class CloudGoodsFavoriteForm : BaseForm
{
private GoodsSearchRequest search = new GoodsSearchRequest();
private List catBoxList = new List();
private BindingList dataList = new BindingList();
+ private CancellationTokenSource detection;
CheckBox chkSelectAll = null;
public CloudGoodsFavoriteForm()
{
InitializeComponent();
+ Disposed += (sender, e) => detection?.Cancel();
}
private void AdminCardCodeForm_Load(object sender, EventArgs e)
@@ -50,13 +53,14 @@ namespace TemuKJD.PDDForms.ProductSelectForms
InitCategory();
InitBindingDataGridView();
-
- num_minsold.Value = 0;
- num_maxsold.Value = 9999999;
- num_minprice.Value = 0;
- num_maxprice.Value = 999;
+ num_sold.LowerValue = 1;
+ num_sold.UpperValue = 9999999;
+ num_price.LowerValue = 1;
+ num_price.UpperValue = 9999999;
+
+ cb_Platformr.SelectedIndex = 0;
+ cb_safe.SelectedIndex = 0;
- cb_Platform.SelectedIndex = 0;
ucSplitPage1.TotalCount = 0;
@@ -79,6 +83,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
this.Column3.DataPropertyName = nameof(CloudGoods.Price);
this.Column4.DataPropertyName = nameof(CloudGoods.SoldQuantity);
this.c_goodsid.DataPropertyName = nameof(CloudGoods.GoodsId);
+ this.colDetectionResult.DataPropertyName = nameof(CloudGoods.DetectionResult);
this.c_Platform.DataPropertyName = nameof(CloudGoods.Platform);
// 2. 设置列样式
// 时间列格式化
@@ -311,6 +316,8 @@ namespace TemuKJD.PDDForms.ProductSelectForms
}
private async void btnSearch_Click(object sender, EventArgs e)
{
+ if (detection != null) return;
+ activeSearches++;
try
{
this.pbLoading.Visible = true;
@@ -318,12 +325,13 @@ namespace TemuKJD.PDDForms.ProductSelectForms
UserFavoritesGoodsSearchRequest search = new UserFavoritesGoodsSearchRequest();
- search.Platform = cb_Platform.SelectedIndex == 1 ? PlatformType.PDD : cb_Platform.SelectedIndex == 2 ? PlatformType.WX : PlatformType.None;
+ search.Platform = cb_Platformr.SelectedIndex == 1 ? PlatformType.PDD : cb_Platformr.SelectedIndex == 2 ? PlatformType.WX : PlatformType.None;
+ search.SafeStatus = cb_safe.SelectedIndex;
search.Title = txt_title.Text;
- search.MinSoldQuantity = (int)num_minsold.Value;
- search.MaxSoldQuantity = (int)num_maxsold.Value;
- search.MinPrice = (long)num_minprice.Value * 100;
- search.MaxPrice = (long)num_maxprice.Value * 100;
+ search.MinSoldQuantity = (int)num_sold.LowerValue;
+ search.MaxSoldQuantity = (int)num_sold.UpperValue;
+ search.MinPrice = (long)(num_price.LowerValue * 100);
+ search.MaxPrice = (long)(num_price.UpperValue * 100);
if (cmbCatSelect1.SelectedItem is PddCatInfo cat1)
{
search.CatId1 = (int)(cat1.cat_id);
@@ -349,7 +357,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
finally
{
this.pbLoading.Visible = false;
- btnSearch.Enabled = true;
+ btnSearch.Enabled = --activeSearches == 0;
}
@@ -357,26 +365,36 @@ namespace TemuKJD.PDDForms.ProductSelectForms
private void pnlEmpty_Click(object sender, EventArgs e)
{
txt_title.Text = "";
- num_minsold.Value = 0;
- num_maxsold.Value = 99999;
- num_minprice.Value = 0;
- num_maxprice.Value = 999;
+ num_sold.LowerValue = 1;
+ num_sold.UpperValue = 9999999;
+ num_price.LowerValue = 1;
+ num_price.UpperValue = 9999999;
+
cmbCatSelect1.SelectedIndex = 0;
- cb_Platform.SelectedIndex = 0;
+ cb_Platformr.SelectedIndex = 0;
+ cb_safe.SelectedIndex = 0;
ucSplitPage1.PageChange -= UcSplitPage1_PageChange;
ucSplitPage1.CurrentPage = 1;
ucSplitPage1.PageChange += UcSplitPage1_PageChange;
}
+ private int latestGoodsQuery;
+ private int activeSearches;
+
public async Task GetGoods(UserFavoritesGoodsSearchRequest request)
{
- dataList.Clear();
+ var query = ++latestGoodsQuery;
PageData ret = await CloudGoodsService.SearchFavorites(request);
Invoke(new Action(() =>
{
- ret.List.ForEach(a => dataList.Add(a));
+ if (query != latestGoodsQuery) return;
+ dataList.Clear();
+ foreach (var goods in ret.List)
+ {
+ dataList.Add(goods);
+ }
ucSplitPage1.TotalPage = Math.Max(1, ret.TotalPages);
ucSplitPage1.TotalCount = ret.Total;
this.pbLoading.Visible = false;
@@ -424,7 +442,7 @@ namespace TemuKJD.PDDForms.ProductSelectForms
product_name = item.Title,
cat_name = catName,
product_get_type_text = item.Platform == 1 ? ("多多") : item.Platform == 2 ? ("微信") : ("未知"),
- db_type = "云端数据",
+ db_type = "云端数据",
});
succ++;
}
@@ -444,13 +462,13 @@ namespace TemuKJD.PDDForms.ProductSelectForms
private async void btn_removefavorite_Click(object sender, EventArgs e)
{
int succ = 0;
- int err = 0;
+ int err = 0;
try
{
this.pbLoading.Visible = true;
btn_removefavorite.Enabled = false;
var goodsIds = dataList.Where(a => a.IsSelected).Select(a => a.GoodsId).ToList();
- if(goodsIds.Count==0)
+ if (goodsIds.Count == 0)
{
BaseMsgForm.ShowInfo($"请勾选商品");
return;
@@ -473,6 +491,69 @@ namespace TemuKJD.PDDForms.ProductSelectForms
btn_removefavorite.Enabled = true;
}
}
+ ///
+ /// 批量AI检测
+ ///
+ ///
+ ///
+ private async void btnSafeDetection_Click(object sender, EventArgs e)
+ {
+ if (detection != null)
+ {
+ detection.Cancel();
+ btnSafeDetection.Text = "正在停止";
+ btnSafeDetection.Enabled = false;
+ return;
+ }
+ if (!btnSearch.Enabled || !btn_removefavorite.Enabled)
+ {
+ BaseMsgForm.ShowInfo("请等待当前操作完成后再检测。");
+ return;
+ }
+ var selected = dataList.Where(x => x.IsSelected)
+ .Select(x => new ComplianceGoods
+ {
+ id = x.GoodsId.ToString(),
+ name = x.Title,
+ image = x.ThumbUrl,
+ type = ComplianceGoods.Cloud,
+ platform = x.Platform
+ }).ToList();
+ var locked = splitContainer1.Panel1.Controls.Cast().Where(c => c != panel1)
+ .Concat(panel1.Controls.Cast().Where(c => c != btnSafeDetection))
+ .Concat(new Control[] { splitContainer2 }).Where(c => c.Enabled).ToList();
+ var originalText = btnSafeDetection.Text;
+ detection = new CancellationTokenSource();
+ try
+ {
+ foreach (var control in locked) control.Enabled = false;
+ btnSafeDetection.Text = "停止检测";
+ var result = await GoodsComplianceService.DetectSelectedAsync(selected, ComplianceProductReader.Read, (id, status) =>
+ {
+ if (IsDisposed) return;
+ foreach (var goods in dataList.Where(x => x.GoodsId.ToString() == id))
+ goods.DetectionResult = status;
+ }, detection.Token);
+ if (!IsDisposed)
+ {
+ var title = result.Unfinished > 0 ? "AI 检测已停止" : "AI 检测完成";
+ BaseMsgForm.ShowInfo($"{title}:安全 {result.Safe},有风险 {result.Risk},失败 {result.Failed},未完成 {result.Unfinished}。");
+ }
+ }
+ catch (Exception ex) { if (!IsDisposed) MessageBox.Show(this, ex.Message); }
+ finally
+ {
+ detection.Dispose();
+ detection = null;
+ if (!IsDisposed)
+ {
+ foreach (var control in locked) control.Enabled = true;
+ btnSafeDetection.Text = originalText;
+ btnSafeDetection.Enabled = true;
+ }
+ }
+ }
+
}
diff --git a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsFavoriteForm.resx b/TEMUKJD/WXForms/ProductForms/CloudGoodsFavoriteForm.resx
similarity index 100%
rename from TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsFavoriteForm.resx
rename to TEMUKJD/WXForms/ProductForms/CloudGoodsFavoriteForm.resx
diff --git a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsForm.Designer.cs b/TEMUKJD/WXForms/ProductForms/CloudGoodsForm.Designer.cs
similarity index 99%
rename from TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsForm.Designer.cs
rename to TEMUKJD/WXForms/ProductForms/CloudGoodsForm.Designer.cs
index aafd523..7e00a66 100644
--- a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsForm.Designer.cs
+++ b/TEMUKJD/WXForms/ProductForms/CloudGoodsForm.Designer.cs
@@ -1,5 +1,5 @@
-namespace TemuKJD.PDDForms.ProductSelectForms
+namespace TemuKJD.WXForms.ProductForms
{
partial class CloudGoodsForm
{
diff --git a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsForm.cs b/TEMUKJD/WXForms/ProductForms/CloudGoodsForm.cs
similarity index 99%
rename from TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsForm.cs
rename to TEMUKJD/WXForms/ProductForms/CloudGoodsForm.cs
index b47d007..626fa97 100644
--- a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsForm.cs
+++ b/TEMUKJD/WXForms/ProductForms/CloudGoodsForm.cs
@@ -25,12 +25,11 @@ using TemuKJD.BaseForms;
using TemuKJD.Extension;
using TemuKJD.ToolForms;
using TemuKJD.UserControls;
-using TemuKJD.WXForms.ProductForms;
using Work;
using Work.GoodDetail;
using Work.Services;
-namespace TemuKJD.PDDForms.ProductSelectForms
+namespace TemuKJD.WXForms.ProductForms
{
public partial class CloudGoodsForm : BaseForm
{
diff --git a/TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsForm.resx b/TEMUKJD/WXForms/ProductForms/CloudGoodsForm.resx
similarity index 100%
rename from TEMUKJD/PDDForms/ProductSelectForms/CloudGoodsForm.resx
rename to TEMUKJD/WXForms/ProductForms/CloudGoodsForm.resx
diff --git a/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs b/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs
index 78e8db1..a4887a8 100644
--- a/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs
+++ b/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs
@@ -1540,54 +1540,7 @@ namespace TemuKJD.WXForms.ProductForms
/// 校验微信禁售商品
///
public static KJDDBProduct GetProductDetail(string prodDbId, out string errMsg, bool noViolationProduct = false, bool noBanWx = false)
- {
- errMsg = "";
-
- if (string.IsNullOrEmpty(prodDbId))
- {
- errMsg = "产品编号为空";
- return new KJDDBProduct();
- }
-
- List list = new List();
-
- var url = BaseClass.BuildObjString("product/get_pdd_product_detail_list");
- var data = UserLogin.GetSignaturex() + "&search_db_ids=" + prodDbId;
- if (noViolationProduct)
- data += "&wx_safe_status=安全商品,未校验";
- if (noBanWx)
- data += "&ban_wx=0";
- if (BaseClass.SaLevel > 0 || BaseClass.UserAuthority.Contains("爆款数据") || BaseClass.UserAuthority.Contains("学员数据") || BaseClass.ParentAuthority.Contains("学员数据"))
- data += "&special_prod_type=普通商品,爆款数据";
-
- var html = GetHtml.PostUtf8(url, data);
- if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200"))
- html = GetHtml.PostUtf8(url, data);
- if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200"))
- html = GetHtml.PostUtf8(url, data);
- if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200"))
- {
- return null;
- }
-
- var jArr = JObject.Parse(html)["data"];
- if (jArr.Count() == 0)
- return null;
- var sProd = JsonHelper.SerializeObject(jArr);
-
- try
- {
- list = JsonHelper.DeserializeJsonToList(sProd);
- var item = list.FirstOrDefault();
- if (item == null)
- item = new KJDDBProduct();
- return item;
- }
- catch (Exception ex)
- {
- return null;
- }
- }
+ => ComplianceProductReader.GetProductDetail(prodDbId, out errMsg, noViolationProduct, noBanWx);
public static KJDDBProduct GetCloudProductDetail(TemuDBProductSelect selectProd, out string errMsg, bool noViolationProduct = false, bool noBanWx = false)
{
diff --git a/Work/Services/CloudGoodsService.cs b/Work/Services/CloudGoodsService.cs
index 0c64d64..e304683 100644
--- a/Work/Services/CloudGoodsService.cs
+++ b/Work/Services/CloudGoodsService.cs
@@ -418,7 +418,13 @@ namespace Work.Services
public static async Task> SearchFavorites(UserFavoritesGoodsSearchRequest request)
{
request.UserName = GetUserName();
- return await StoreApiClient.Instance.CloudGoods.SearchFavorites(request);
+ var page = await StoreApiClient.Instance.CloudGoods.SearchFavorites(request);
+ foreach (var item in page.List)
+ {
+ item.DetectionResult = item.IsSafe == null ? "未检测" : item.IsSafe.Value ? "安全" : "违规";
+ }
+
+ return page;
}
///
@@ -576,6 +582,8 @@ namespace Work.Services
/// 平台类型
///
public PlatformType Platform { get; set; }
+ /// 安全状态:0 全部(默认),1 安全,2 违规,3 未检测。
+ public int SafeStatus { get; set; }
///
/// 页码(默认1)
@@ -629,6 +637,7 @@ namespace Work.Services
}
public class CloudGoods : BindableBase
{
+
public bool IsSelected
{
get => GetValue();
@@ -719,5 +728,18 @@ namespace Work.Services
/// 索引同步状态:0未同步 1已同步
///
public byte IndexStatus { get; set; }
+
+ ///
+ /// 安全检测
+ ///
+ public bool? IsSafe { get; set; }
+
+
+ [JsonIgnore]
+ public string? DetectionResult
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
}
}
diff --git a/Work/Services/ComplianceProductReader.cs b/Work/Services/ComplianceProductReader.cs
new file mode 100644
index 0000000..74b7838
--- /dev/null
+++ b/Work/Services/ComplianceProductReader.cs
@@ -0,0 +1,143 @@
+using AllBase.Model;
+using AllBase.Model.Result;
+using AllBase.Model.Result.PDDResult;
+using Newtonsoft.Json.Linq;
+using System;
+using System.IO;
+using System.Linq;
+using TemuKJD.BLL.ImportConverts;
+using AllBase;
+using System.Collections.Generic;
+using TemuKJD.BLL;
+
+namespace Work.Services;
+
+public static class ComplianceProductReader
+{
+ public static Product Read(ComplianceGoods item)
+ {
+ KJDDBProduct db;
+ if (item.type == ComplianceGoods.Cloud)
+ db = new KJDDBProduct { product_json = CloudGoodsService.GetGoodsDetail(long.Parse(item.id)) };
+ else if (item.type == ComplianceGoods.Library)
+ {
+ db = GetProductDetail(item.id, out var message);
+ if (!string.IsNullOrEmpty(message)) throw new InvalidDataException(message);
+ }
+ else throw new InvalidDataException("未知商品来源:" + item.type);
+
+ if (string.IsNullOrWhiteSpace(db?.product_json)) throw new InvalidDataException("未取得商品详情。");
+ var raw = db.product_json;
+ var root = JObject.Parse(raw);
+ PDDProductInfo info;
+ string error = null;
+ var converter = new PDDProdFormConverter();
+ if (root.SelectToken("detail.galleries") is JArray)
+ info = root.ToObject();
+ else if (db.product_get_type_text == "厂商直采") info = converter.JstPddProdToPddConvert(out error, db);
+ else if (db.product_get_type_text == "微信店采") info = converter.WXProdToPddConvert(out error, db);
+ else if (db.product_get_type_text == "微信小店") info = converter.WXClientToPddConvert(out error, db);
+ else if (db.product_get_type_text == "1688厂家") info = converter._1688ClientToPddConvert(out error, db);
+ else if (db.product_get_type_text == "1688外部数据") info = converter._1688OutToPddConvert(out error, db);
+ else
+ {
+ var front = ConverterToPddFactory.SetContentConvert(raw);
+ if (front != null)
+ {
+ var originalId = front.GoodsId > 0 ? front.GoodsId.ToString() : db.spu_id;
+ return new Product
+ {
+ ProductId = item.type == ComplianceGoods.Cloud ? item.id : originalId,
+ Platform = item.platform.HasValue ? (PlatformType)item.platform.Value : front.Platform,
+ Title = front.Title ?? item.name, Description = front.Description,
+ MainImages = front.MainImages, DetailImages = front.DetailImages,
+ Skus = front.Skus?.Select(x => new ProductSku { SkuId = x.SkuId, ImageUrl = x.SpecImage }).ToList()
+ };
+ }
+ string converted;
+ if (raw.Contains("\"spec_keys\"") && raw.Contains("\"spec_values\""))
+ converted = converter.PddFrontToPddBackConvert(raw, isSaveDb: false);
+ else if (raw.Contains("\"goods_id\"") && raw.Contains("\"gallery\""))
+ converted = converter.PddFrontToPddBackConvert2(raw, isSaveDb: false);
+ else if (raw.Contains("\"topGallery\"") && raw.Contains("\"detailGallery\""))
+ converted = converter.PddFrontToPddBackConvert3(raw, isSaveDb: false);
+ else if (raw.Contains("\"商品编号\"") && raw.Contains("\"商品链接\""))
+ converted = converter.PddFrontToPddBackConvertV4(raw, isSaveDb: false);
+ else throw new InvalidDataException("不支持的商品详情格式。");
+ if (converted == null || !converted.StartsWith("success", StringComparison.Ordinal))
+ throw new InvalidDataException(converted ?? "商品详情转换失败。");
+ info = JObject.Parse(converted.Substring(7)).ToObject();
+ }
+ if (!string.IsNullOrEmpty(error) || info?.detail == null)
+ throw new InvalidDataException(error ?? "商品详情转换失败。");
+ var detail = info.detail;
+ var platform = db.product_get_type_text switch
+ {
+ "微信店采" or "微信小店" or "微信外部数据" or "微信" => PlatformType.WX,
+ "厂商直采" or "1688厂家" or "1688外部数据" => PlatformType.None,
+ _ => PlatformType.PDD
+ };
+ var productId = db.product_get_type_text == "厂商直采"
+ ? (string)root.SelectToken("pddData.itemSpuId") ?? db.spu_id
+ : detail.goods_id > 0 ? detail.goods_id.ToString() : db.spu_id;
+ return new Product
+ {
+ Platform = item.platform.HasValue ? (PlatformType)item.platform.Value : platform,
+ ProductId = item.type == ComplianceGoods.Cloud ? item.id : productId,
+ Title = detail.goods_name ?? item.name, Description = detail.goods_desc,
+ MainImages = detail.galleries?.Where(x => x.type == 1).Select(x => x.url).ToList(),
+ DetailImages = detail.galleries?.Where(x => x.type == 2).Select(x => x.url).ToList(),
+ Skus = detail.sku?.Select(x => new ProductSku { SkuId = x.sku_id.ToString(), ImageUrl = x.thumb_url }).ToList()
+ };
+ }
+
+ public static KJDDBProduct GetProductDetail(string prodDbId, out string errMsg, bool noViolationProduct = false, bool noBanWx = false)
+ {
+ errMsg = "";
+
+ if (string.IsNullOrEmpty(prodDbId))
+ {
+ errMsg = "产品编号为空";
+ return new KJDDBProduct();
+ }
+
+ List list = new List();
+
+ var url = BaseClass.BuildObjString("product/get_pdd_product_detail_list");
+ var data = UserLogin.GetSignaturex() + "&search_db_ids=" + prodDbId;
+ if (noViolationProduct)
+ data += "&wx_safe_status=安全商品,未校验";
+ if (noBanWx)
+ data += "&ban_wx=0";
+ if (BaseClass.SaLevel > 0 || BaseClass.UserAuthority.Contains("爆款数据") || BaseClass.UserAuthority.Contains("学员数据") || BaseClass.ParentAuthority.Contains("学员数据"))
+ data += "&special_prod_type=普通商品,爆款数据";
+
+ var html = GetHtml.PostUtf8(url, data);
+ if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200"))
+ html = GetHtml.PostUtf8(url, data);
+ if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200"))
+ html = GetHtml.PostUtf8(url, data);
+ if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200"))
+ {
+ return null;
+ }
+
+ var jArr = JObject.Parse(html)["data"];
+ if (jArr.Count() == 0)
+ return null;
+ var sProd = JsonHelper.SerializeObject(jArr);
+
+ try
+ {
+ list = JsonHelper.DeserializeJsonToList(sProd);
+ var item = list.FirstOrDefault();
+ if (item == null)
+ item = new KJDDBProduct();
+ return item;
+ }
+ catch (Exception ex)
+ {
+ return null;
+ }
+ }
+}
diff --git a/Work/Services/GoodsComplianceService.cs b/Work/Services/GoodsComplianceService.cs
new file mode 100644
index 0000000..5a675cf
--- /dev/null
+++ b/Work/Services/GoodsComplianceService.cs
@@ -0,0 +1,265 @@
+using AllBase.Helper;
+using AllBase;
+using AllBase.Model;
+using Work.Store.Api;
+using System.Threading;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.Net.Http;
+using System.Linq;
+using System.Text;
+
+namespace Work.Services;
+
+public sealed class ComplianceGoods
+{
+ public const string Cloud = "云端收藏";
+ public const string Library = "我的商品库";
+
+ public string id { get; set; }
+ public string name { get; set; }
+ public string image { get; set; }
+ public string type { get; set; }
+ public byte? platform { get; set; }
+}
+
+public sealed class ComplianceResult
+{
+ public string Text { get; internal set; }
+ public string[] Hits { get; internal set; } = Array.Empty();
+ public int HitImageNumber { get; internal set; }
+ public string Error { get; internal set; }
+ public string Status => Error != null ? "失败,可重试" : Hits.Length == 0 ? "安全" : "有风险";
+ public string Summary => Error != null ? "失败:" + Error :
+ Hits.Length == 0 ? Status : $"有风险:第 {HitImageNumber} 张图片命中关键词:{string.Join(",", Hits)}";
+}
+
+public sealed class ComplianceBatchResult
+{
+ public int Safe { get; internal set; }
+ public int Risk { get; internal set; }
+ public int Failed { get; internal set; }
+ public int Unfinished { get; internal set; }
+}
+
+public static class GoodsComplianceService
+{
+ private static readonly HttpClient ImageClient = new HttpClient(new HttpClientHandler { UseCookies = false })
+ {
+ Timeout = TimeSpan.FromSeconds(30),
+ MaxResponseContentBufferSize = 32 * 1024 * 1024
+ };
+ private static readonly SemaphoreSlim BatchGate = new SemaphoreSlim(1, 1);
+
+ public static async Task DetectSelectedAsync(IReadOnlyList items,
+ Func readProduct, Action update, CancellationToken cancellationToken = default)
+ {
+ if (items == null || items.Count == 0) throw new ArgumentException("请选择商品。", nameof(items));
+ if (readProduct == null) throw new ArgumentNullException(nameof(readProduct));
+ if (update == null) throw new ArgumentNullException(nameof(update));
+ if (!BatchGate.Wait(0)) throw new InvalidOperationException("已有商品正在检测。");
+ try
+ {
+ var options = GlobalConfig.GlobalSettings.GoodsCompliance;
+ var words = ValidateOptions(options);
+ var path = QueuePath();
+ var pending = ReadPending(path);
+ if (items.Any(InvalidItem)) throw new ArgumentException("缺少商品 ID 或来源。", nameof(items));
+ var selected = items.GroupBy(x => Tuple.Create(x.type, x.id)).Select(g => g.First()).ToList();
+ var existing = new HashSet>(pending.Select(x => Tuple.Create(x.type, x.id)));
+ pending.AddRange(selected.Where(x => existing.Add(Tuple.Create(x.type, x.id))));
+ SavePending(path, pending);
+
+ var batch = new ComplianceBatchResult { Unfinished = selected.Count };
+ foreach (var item in selected)
+ {
+ if (cancellationToken.IsCancellationRequested) break;
+ update(item.id, "读取详情中");
+ ComplianceResult result;
+ try
+ {
+ var product = await Task.Run(() => readProduct(item), cancellationToken);
+ result = await DetectProductAsync(product, options, words,
+ number => update(item.id, $"正在检测第 {number} 张图片"), cancellationToken);
+ }
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ {
+ update(item.id, "已停止");
+ break;
+ }
+ catch (Exception ex) { result = new ComplianceResult { Error = ex.Message }; }
+ if (result.Error == null)
+ {
+ var remaining = pending.Where(x => x.type != item.type || x.id != item.id).ToList();
+ try { SavePending(path, remaining); pending = remaining; }
+ catch (Exception ex) { result.Error = ex.Message; }
+ }
+ if (result.Error != null) batch.Failed++;
+ else if (result.Hits.Length > 0) batch.Risk++;
+ else batch.Safe++;
+ batch.Unfinished--;
+ update(item.id, result.Summary);
+ }
+ return batch;
+ }
+ finally { BatchGate.Release(); }
+ }
+
+ private static string QueuePath()
+ {
+ var username = BaseClass.UserName;
+ if (string.IsNullOrWhiteSpace(username)) throw new InvalidOperationException("请先登录。");
+ var user = Convert.ToBase64String(Encoding.UTF8.GetBytes(username)).Replace('/', '_').Replace('+', '-');
+ return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data", "config", "goods_compliance_" + user + ".json");
+ }
+
+ private static bool InvalidItem(ComplianceGoods item) =>
+ item == null || string.IsNullOrWhiteSpace(item.id) || string.IsNullOrWhiteSpace(item.type);
+
+ private static List ReadPending(string path)
+ {
+ if (!File.Exists(path)) return new List();
+ var items = JsonConvert.DeserializeObject>(File.ReadAllText(path));
+ if (items == null || items.Any(InvalidItem))
+ throw new InvalidDataException("待检测配置无效,请检查 " + path);
+ return items;
+ }
+
+ private static void SavePending(string path, List items)
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ var temp = path + ".tmp";
+ File.WriteAllText(temp, JsonConvert.SerializeObject(items));
+ if (File.Exists(path)) File.Replace(temp, path, null);
+ else File.Move(temp, path);
+ }
+
+ public static async Task DetectAsync(IReadOnlyList products, ComplianceOptions options,
+ Action completed, Action started = null)
+ {
+ if (products == null) throw new ArgumentNullException(nameof(products));
+ if (completed == null) throw new ArgumentNullException(nameof(completed));
+ var words = ValidateOptions(options);
+ foreach (var product in products)
+ {
+ started?.Invoke(product);
+ completed(product, await DetectProductAsync(product, options, words));
+ }
+ }
+
+ private static string[] ValidateOptions(ComplianceOptions options)
+ {
+ var words = Keywords(options?.Keywords);
+ if (words.Length == 0) throw new ArgumentException("请先在系统设置的「商品 AI 检测」中保存 AI 检测关键词。");
+ if (options.MainCount <= 0 || options.DetailCount < 0)
+ throw new ArgumentException("请检查检测关键词和图片数量。");
+ return words;
+ }
+
+ private static async Task DetectProductAsync(Product product, ComplianceOptions options,
+ string[] words, Action imageStarted = null, CancellationToken cancellationToken = default)
+ {
+ var result = new ComplianceResult();
+ try
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ if (string.IsNullOrWhiteSpace(product?.ProductId)) throw new InvalidDataException("缺少商品 ID。");
+ var urls = SelectImages(product.MainImages, options.MainCount)
+ .Concat(SelectImages(product.DetailImages, options.DetailCount));
+ if (options.IncludeSku) urls = urls.Concat(SelectImages(product.Skus?.Select(s => s?.ImageUrl), null));
+ var texts = new List();
+ int imageNumber = 0;
+ foreach (var url in urls.Distinct(StringComparer.Ordinal))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ imageNumber++;
+ imageStarted?.Invoke(imageNumber);
+ try
+ {
+ var jpeg = await DownloadInspectionImageAsync(url);
+ cancellationToken.ThrowIfCancellationRequested();
+ var lines = await StoreApiClient.Instance.CloudGoods.RecognizeAsync(BaseClass.GetMd5(url), jpeg);
+ var imageText = string.Join(Environment.NewLine, lines);
+ texts.AddRange(lines);
+ result.Hits = Matches(imageText, words);
+ if (result.Hits.Length > 0)
+ {
+ result.HitImageNumber = imageNumber;
+ break;
+ }
+ }
+ catch (Exception ex) when (!cancellationToken.IsCancellationRequested)
+ {
+ throw new InvalidDataException($"第 {imageNumber} 张图片检测失败:{ex.Message}", ex);
+ }
+ }
+ cancellationToken.ThrowIfCancellationRequested();
+ if (imageNumber == 0) throw new InvalidDataException("所选检测范围内没有图片。");
+ result.Text = string.Join(Environment.NewLine, texts);
+ try
+ {
+ await StoreApiClient.Instance.CloudGoods.SaveProductSafety(new SaveProductSafetyRequest
+ {
+ UserName = GetUserName(),
+ ProductId = product.ProductId,
+ IsSafe = result.Hits.Length == 0
+ });
+ }
+ catch (Exception ex)
+ {
+ result.Error = $"检测结果为{result.Status},上报失败:{ex.Message}";
+ }
+ }
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { throw; }
+ catch (Exception ex) { result.Error = ex.Message; }
+ return result;
+ }
+
+ private static IEnumerable SelectImages(IEnumerable urls, int? count) =>
+ (urls ?? Enumerable.Empty()).Where(url => !string.IsNullOrWhiteSpace(url)).Select(url => url.Trim())
+ .Distinct(StringComparer.Ordinal).Take(count ?? int.MaxValue);
+
+ private static async Task DownloadInspectionImageAsync(string url)
+ {
+ if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) ||
+ (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps))
+ throw new InvalidDataException("图片链接必须为 HTTP 或 HTTPS 地址。");
+ var bytes = await ImageClient.GetByteArrayAsync(uri).ConfigureAwait(false);
+ const int maxSide = 600;
+ using (var input = new MemoryStream(bytes))
+ using (var source = Image.FromStream(input))
+ {
+ var scale = Math.Min(1d, (double)maxSide / Math.Max(source.Width, source.Height));
+ int width = Math.Max(1, (int)Math.Round(source.Width * scale));
+ int height = Math.Max(1, (int)Math.Round(source.Height * scale));
+ using var canvas = new Bitmap(width, height, PixelFormat.Format24bppRgb);
+ using var output = new MemoryStream();
+ using (var g = Graphics.FromImage(canvas))
+ {
+ g.Clear(Color.White);
+ g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ g.DrawImage(source, new Rectangle(0, 0, width, height),
+ 0, 0, source.Width, source.Height, GraphicsUnit.Pixel);
+ }
+ canvas.Save(output, ImageFormat.Jpeg);
+ return output.ToArray();
+ }
+ }
+
+ public static string[] Keywords(string value) =>
+ System.Text.RegularExpressions.Regex.Split(value ?? "", @"[,,;;|\s]+")
+ .Where(x => x.Length > 0).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
+
+ public static string[] Matches(string recognized, string[] keywords) =>
+ keywords.Where(x => recognized.IndexOf(x, StringComparison.OrdinalIgnoreCase) >= 0).ToArray();
+ public static string GetUserName()
+ {
+ return string.IsNullOrEmpty(BaseClass.ParentUserName) ? BaseClass.UserName : BaseClass.ParentUserName;
+ }
+}
diff --git a/Work/Store.Api/ApiException.cs b/Work/Store.Api/ApiException.cs
index 1243c8d..16532a5 100644
--- a/Work/Store.Api/ApiException.cs
+++ b/Work/Store.Api/ApiException.cs
@@ -8,10 +8,12 @@ public sealed class ApiException : HttpException
{
public int? Code { get; }
public JToken ResponseData { get; }
+ public string TraceId { get; }
- public ApiException(string message, int? code, JToken responseData) : base(message)
+ public ApiException(string message, int? code, JToken responseData, string traceId = null) : base(message)
{
Code = code;
ResponseData = responseData;
+ TraceId = traceId;
}
}
diff --git a/Work/Store.Api/ApiTransport.cs b/Work/Store.Api/ApiTransport.cs
index 6b7220e..93c3c38 100644
--- a/Work/Store.Api/ApiTransport.cs
+++ b/Work/Store.Api/ApiTransport.cs
@@ -1,4 +1,5 @@
using CsharpHttpHelper;
+using AllBase.Model.Result;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@@ -6,6 +7,8 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
namespace Work.Store.Api;
@@ -14,16 +17,14 @@ public sealed class ApiTransport
{
private readonly string _baseUrl;
private string _token;
- private Func _login;
- internal object SessionGate { get; } = new object();
+ private LoginRequest _credentials;
+ internal SemaphoreSlim SessionGate { get; } = new SemaphoreSlim(1, 1);
- internal void SetSession(string token, Func login)
+ // 调用方持有 SessionGate。
+ internal void SetSession(string token, LoginRequest credentials)
{
- lock (SessionGate)
- {
- _token = token;
- _login = login;
- }
+ _token = token;
+ _credentials = credentials;
}
public ApiTransport(string baseUrl)
@@ -39,29 +40,102 @@ public sealed class ApiTransport
/// 仅存于内存;手动赋值会清除自动登录凭据,设为 null 退出本地会话。
public string Token
{
- get { lock (SessionGate) return _token; }
+ get
+ {
+ SessionGate.Wait();
+ try { return _token; }
+ finally { SessionGate.Release(); }
+ }
set
{
if (value != null && (string.IsNullOrWhiteSpace(value) || value.IndexOfAny(new[] { '\r', '\n' }) >= 0))
throw new ArgumentException("Token 不能为空白或包含换行。", nameof(value));
- SetSession(value, null);
+ SessionGate.Wait();
+ try { SetSession(value, null); }
+ finally { SessionGate.Release(); }
}
}
- /// 同步请求;无 data 时返回 default(T),界面调用需放在后台线程。
public T Send(string route, MethodType method, object body = null,
IDictionary query = null, bool authenticated = true,
int timeoutMilliseconds = 30000)
+ {
+ var item = CreateRequest(route, method, body, query, timeoutMilliseconds);
+ string token = null;
+ if (authenticated)
+ {
+ //SessionGate.Wait();
+ //try { token = _token ?? throw new HttpException("请先登录。") { IsOnline = false }; }
+ //finally { SessionGate.Release(); }
+ }
+ for (int attempt = 0; ; attempt++)
+ {
+ if (token != null) item.Header["Authorization"] = "Bearer " + token;
+ var response = HttpHelper.Request(item);
+ try { return ReadResult(response); }
+ catch (HttpException ex) when (authenticated && !ex.IsOnline)
+ {
+ SessionGate.Wait();
+ try
+ {
+ if (_token == token)
+ {
+ _token = null;
+ if (attempt == 0 && _credentials != null)
+ _token = AuthApi.LoginCore(this, _credentials).Token;
+ }
+ if (attempt != 0 || _token == null) throw;
+ token = _token;
+ }
+ finally { SessionGate.Release(); }
+ }
+ }
+ }
+
+ public async Task SendAsync(string route, MethodType method, object body = null,
+ IDictionary query = null, bool authenticated = true,
+ int timeoutMilliseconds = 30000)
+ {
+ var item = CreateRequest(route, method, body, query, timeoutMilliseconds);
+ string token = null;
+ if (authenticated)
+ {
+ //await SessionGate.WaitAsync().ConfigureAwait(false);
+ //try { token = _token ?? throw new HttpException("请先登录。") { IsOnline = false }; }
+ //finally { SessionGate.Release(); }
+ }
+ for (int attempt = 0; ; attempt++)
+ {
+ if (token != null) item.Header["Authorization"] = "Bearer " + token;
+ var response = await HttpHelper.RequestAsync(item).ConfigureAwait(false);
+ try { return ReadResult(response); }
+ catch (HttpException ex) when (authenticated && !ex.IsOnline)
+ {
+ await SessionGate.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ if (_token == token)
+ {
+ _token = null;
+ if (attempt == 0 && _credentials != null)
+ _token = (await AuthApi.LoginCoreAsync(this, _credentials).ConfigureAwait(false)).Token;
+ }
+ if (attempt != 0 || _token == null) throw;
+ token = _token;
+ }
+ finally { SessionGate.Release(); }
+ }
+ }
+ }
+
+ private HttpItem CreateRequest(string route, MethodType method, object body,
+ IDictionary query, int timeoutMilliseconds)
{
if (string.IsNullOrWhiteSpace(route) ||
!Regex.IsMatch(route, @"\A[A-Za-z0-9_-]+(/[A-Za-z0-9_-]+)*\z"))
throw new ArgumentException("接口必须使用相对路由,例如 Auth/Login。", nameof(route));
if (body != null) Validator.ValidateObject(body, new ValidationContext(body), true);
if (timeoutMilliseconds <= 0) throw new ArgumentOutOfRangeException(nameof(timeoutMilliseconds));
- string token = authenticated ? Token : null;
- if (authenticated && token == null)
- throw new HttpException("请先登录。") { IsOnline = false };
-
var url = new StringBuilder(_baseUrl).Append(route);
if (query != null)
{
@@ -74,7 +148,7 @@ public sealed class ApiTransport
first = false;
}
}
- var item = new HttpItem
+ return new HttpItem
{
URL = url.ToString(),
Method = method,
@@ -87,54 +161,31 @@ public sealed class ApiTransport
ReadWriteTimeout = timeoutMilliseconds,
Allowautoredirect = false
};
- for (int attempt = 0; ; attempt++)
- {
- if (token != null) item.Header["Authorization"] = "Bearer " + token;
- var response = HttpHelper.Request(item);
- int status = (int)response.StatusCode;
- if (status == 0) throw new HttpException("无法连接服务器或请求超时。");
+ }
- JObject result = null;
- JsonException parseError = null;
- try { result = JObject.Parse(response.Html ?? ""); }
- catch (JsonException ex) { parseError = ex; }
+ private static T ReadResult(HttpResult response)
+ {
+ int status = (int)response.StatusCode;
+ if (status == 0) throw new HttpException("无法连接服务器或请求超时。");
- var code = result?["code"];
- if (status == 401 || (code?.Type == JTokenType.Integer && code.ToString() == "401"))
- {
- if (authenticated)
- {
- lock (SessionGate)
- {
- // 并发旧请求复用已更新的 Token;同一请求最多重新发送一次。
- if (_token == token)
- {
- _token = null;
- if (attempt == 0 && _login != null)
- _token = _login();
- }
- if (attempt == 0 && _token != null)
- {
- token = _token;
- continue;
- }
- }
- }
- throw new HttpException(result?["msg"]?.ToString() ?? "登录已失效,请重新登录。") { IsOnline = false };
- }
- if (status < 200 || status >= 300)
- throw new HttpException($"请求失败(HTTP {status}):{result?["msg"]}");
- if (result == null)
- throw new HttpException("服务器响应不是有效的 JSON 对象。", parseError);
- // 先检查业务码,避免错误响应的 data 结构掩盖服务端错误。
- if (code?.Type != JTokenType.Integer || code.ToString() != "200")
- throw new ApiException("请求失败:" + (result["msg"]?.ToString() ?? "响应缺少成功状态码。"),
- code?.Type == JTokenType.Integer && int.TryParse(code.ToString(), out var businessCode) ? businessCode : (int?)null,
- result["data"]);
- if (result["data"] == null || result["data"].Type == JTokenType.Null) return default;
- try { return result["data"].ToObject(); }
- catch (JsonException ex) { throw new HttpException("服务器响应 data 格式无效。", ex); }
- }
+ Result result = null;
+ JsonException parseError = null;
+ try { result = JsonConvert.DeserializeObject>(response.Html ?? ""); }
+ catch (JsonException ex) { parseError = ex; }
+
+ if (status == 401 || (result?.Success == false && result.Code == 401))
+ throw new HttpException(result?.Msg ?? "登录已失效,请重新登录。") { IsOnline = false };
+ // 统一错误响应即使使用 HTTP 4xx/5xx,也保留业务码、数据和追踪 ID。
+ if (result?.Success == false)
+ throw new ApiException("请求失败:" + (result.Msg ?? $"业务码 {result.Code}。"),
+ result.Code, result.Data, result.TraceId);
+ if (status < 200 || status >= 300)
+ throw new HttpException($"请求失败(HTTP {status}):{result?.Msg}");
+ if (result?.Success == null)
+ throw new HttpException("服务器响应不是有效的 Result 格式。", parseError);
+ if (result.Data == null || result.Data.Type == JTokenType.Null) return default;
+ try { return result.Data.ToObject(); }
+ catch (JsonException ex) { throw new HttpException("服务器响应 data 格式无效。", ex); }
}
public T GetRequired(string route, MethodType method, object body = null,
@@ -142,5 +193,11 @@ public sealed class ApiTransport
int timeoutMilliseconds = 30000) where T : class
=> Send(route, method, body, query, authenticated, timeoutMilliseconds)
?? throw new HttpException("服务器响应缺少 data。");
+
+ public async Task GetRequiredAsync(string route, MethodType method, object body = null,
+ IDictionary query = null, bool authenticated = true,
+ int timeoutMilliseconds = 30000) where T : class
+ => await SendAsync(route, method, body, query, authenticated, timeoutMilliseconds).ConfigureAwait(false)
+ ?? throw new HttpException("服务器响应缺少 data。");
}
diff --git a/Work/Store.Api/AuthApi.cs b/Work/Store.Api/AuthApi.cs
index dc890d6..e1299e6 100644
--- a/Work/Store.Api/AuthApi.cs
+++ b/Work/Store.Api/AuthApi.cs
@@ -1,5 +1,6 @@
using CsharpHttpHelper;
using System;
+using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
@@ -52,17 +53,30 @@ public sealed class AuthApi
DeviceId = _deviceId ?? request.DeviceId,
DeviceName = request.DeviceName
};
- lock (_transport.SessionGate)
+ _transport.SessionGate.Wait();
+ try
{
- var result = LoginCore(credentials);
- _transport.SetSession(result.Token, () => LoginCore(credentials).Token);
+ var result = LoginCore(_transport, credentials);
+ _transport.SetSession(result.Token, credentials);
return result;
}
+ finally { _transport.SessionGate.Release(); }
}
- private LoginResult LoginCore(LoginRequest request)
+ internal static LoginResult LoginCore(ApiTransport transport, LoginRequest request)
+ {
+ var result = transport.GetRequired("Auth/Login", MethodType.Post, request, authenticated: false);
+ return ValidateToken(result);
+ }
+
+ internal static async Task LoginCoreAsync(ApiTransport transport, LoginRequest request)
+ {
+ var result = await transport.GetRequiredAsync("Auth/Login", MethodType.Post, request, authenticated: false).ConfigureAwait(false);
+ return ValidateToken(result);
+ }
+
+ private static LoginResult ValidateToken(LoginResult result)
{
- var result = _transport.GetRequired("Auth/Login", MethodType.Post, request, authenticated: false);
if (result == null || string.IsNullOrWhiteSpace(result.Token) ||
result.Token.IndexOfAny(new[] { '\r', '\n' }) >= 0 ||
!string.Equals(result.TokenType, "Bearer", StringComparison.OrdinalIgnoreCase))
diff --git a/Work/Store.Api/CloudGoodsApi.cs b/Work/Store.Api/CloudGoodsApi.cs
index 76c9baf..d36c4ca 100644
--- a/Work/Store.Api/CloudGoodsApi.cs
+++ b/Work/Store.Api/CloudGoodsApi.cs
@@ -1,55 +1,86 @@
-using AllBase.Model.Result;
+using AllBase;
+using AllBase.Helper;
+using AllBase.Model.Result;
using CsharpHttpHelper;
+using NaiBa.Util.Helper;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Work.Services;
+using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel;
namespace Work.Store.Api;
/// 云商品接口,沿用服务端不带版本号的路由。
public sealed class CloudGoodsApi
{
- private readonly ApiTransport _transport;
+ private readonly ApiTransport _transport;
- public CloudGoodsApi(ApiTransport transport)
- => _transport = transport ?? throw new ArgumentNullException(nameof(transport));
+ public CloudGoodsApi(ApiTransport transport)
+ => _transport = transport ?? throw new ArgumentNullException(nameof(transport));
- public Task> GetGoods(GoodsSearchRequest request)
- => Task.Run(() => _transport.GetRequired>("CloudGoods/SearchGoods", MethodType.Post, request));
+ public Task> GetGoods(GoodsSearchRequest request)
+ => _transport.GetRequiredAsync>("CloudGoods/SearchGoods", MethodType.Post, request);
- public string GetGoodsDetail(long goodsId)
- => _transport.GetRequired("CloudGoods/GetGoodsRawData", MethodType.Post,
- query: new Dictionary { ["goodsId"] = goodsId.ToString(CultureInfo.InvariantCulture) });
+ public string GetGoodsDetail(long goodsId)
+ => _transport.GetRequired("CloudGoods/GetGoodsRawData", MethodType.Post,
+ query: new Dictionary { ["goodsId"] = goodsId.ToString(CultureInfo.InvariantCulture) });
- public Task> ReportViolations(List items)
- => Task.Run(() => _transport.GetRequired>("CloudGoods/ReportViolations", MethodType.Post, items,
- timeoutMilliseconds: 5000));
+ public Task> ReportViolations(List items)
+ => _transport.GetRequiredAsync>("CloudGoods/ReportViolations", MethodType.Post, items,
+ timeoutMilliseconds: 5000);
- public List GetViolationStatus(List goodsIds)
- => _transport.GetRequired>("CloudGoods/GetViolationStatus", MethodType.Post, goodsIds,
- timeoutMilliseconds: 5000);
+ public List GetViolationStatus(List goodsIds)
+ => _transport.GetRequired>("CloudGoods/GetViolationStatus", MethodType.Post, goodsIds,
+ timeoutMilliseconds: 5000);
- public Task RecordViolationLogs(IReadOnlyList requests)
- => Task.Run(() => _transport.Send