This commit is contained in:
wule committed 2026-11-02 18:10:32 +08:00
commit f9a1af6073
8 files changed
+884 -18

No files matched your search

+4 -1
View File
@@ -10,7 +10,7 @@
<ErrorReportUrlHistory />
<FallbackCulture>zh-CN</FallbackCulture>
<VerifyUploadedFiles>false</VerifyUploadedFiles>
<ActiveDebugProfile>Production</ActiveDebugProfile>
<ActiveDebugProfile>Development</ActiveDebugProfile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
@@ -182,6 +182,9 @@
<Compile Update="WXForms\ProductForms\WXFinderSquareForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="WXForms\ProductForms\WXViolationLogsForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="WXForms\ShopForms\WXOemPointsTopUpListForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -10,18 +10,29 @@ namespace TemuKJD.KJDForms.SystemForms
{
InitializeComponent();
var settings = GlobalConfig.GlobalSettings;
chkAutoReport.Checked = settings.AutoReportViolationProducts;
chkAutoExcludeOnUpload.Checked = settings.AutoExcludeViolationProductsOnUpload;
chkAutoDelist.Checked = settings.AutoDelistViolationProducts;
VisibleChanged += (s, e) => { if (Visible) LoadSettings(); };
LoadSettings();
chkAutoReport.CheckedChanged += Settings_CheckedChanged;
chkAutoExcludeOnUpload.CheckedChanged += Settings_CheckedChanged;
chkAutoDelist.CheckedChanged += Settings_CheckedChanged;
}
private bool loading;
private void LoadSettings()
{
loading = true;
var settings = GlobalConfig.GlobalSettings;
chkAutoReport.Checked = settings.AutoReportViolationProducts;
chkAutoExcludeOnUpload.Checked = settings.AutoExcludeViolationProductsOnUpload;
chkAutoDelist.Checked = settings.AutoDelistViolationProducts;
loading = false;
}
private void Settings_CheckedChanged(object sender, EventArgs e)
{
if (loading) return;
var settings = GlobalConfig.GlobalSettings;
settings.AutoReportViolationProducts = chkAutoReport.Checked;
settings.AutoExcludeViolationProductsOnUpload = chkAutoExcludeOnUpload.Checked;
@@ -1718,7 +1718,8 @@ namespace TemuKJD.WXForms.ProductForms
// 开启上货自动剔除时,上传前检测违规状态;仅服务端明确返回违规时禁止上传。
if (GlobalConfig.GlobalSettings.AutoExcludeViolationProductsOnUpload)
{
Invoke(new Action(() => cell.Value = "商品违规检测..."));
if (cell != null)
Invoke(new Action(() => cell.Value = "商品违规检测..."));
try
{
long goodsId = 0;
@@ -1731,7 +1732,21 @@ namespace TemuKJD.WXForms.ProductForms
if (status == null)
throw new Exception("未返回当前商品的违规状态");
if (status.IsViolation)
{
var shopId = shop.ShopId;
_ = Task.Run(async () =>
{
try
{
await CloudGoodsService.RecordShopActionLogs(shopId, status, ViolationLogAction.AutoListingExcluded);
}
catch (Exception ex)
{
Debug.WriteLine("剔除违规商品日志记录失败:" + ex.Message);
}
});
return "{\"success\":false,\"draftId\":0,\"status\":\"未上传\",\"errMsg\":\"当前商品违规,禁止上传\"}";
}
}
catch (Exception ex)
{
@@ -6329,6 +6344,37 @@ namespace TemuKJD.WXForms.ProductForms
}
}
/// <summary>
/// 图片宽高均达到指定尺寸时返回 true;尺寸不足或无效输入返回 false。
/// </summary>
public bool IsBase64ImageSizeValid(string base64Image, int width, int height)
{
if (string.IsNullOrWhiteSpace(base64Image) || width <= 0 || height <= 0)
return false;
try
{
using (var stream = new MemoryStream(Convert.FromBase64String(base64Image)))
using (var image = System.Drawing.Image.FromStream(stream))
{
return image.Width >= width && image.Height >= height;
}
}
catch (FormatException)
{
return false;
}
catch (ArgumentException)
{
return false;
}
catch (OutOfMemoryException)
{
// GDI+ 对不支持或损坏的图片也可能抛出此异常。
return false;
}
}
public string UploadImg(ShopLoginInfo shop, string img_url, DataGridViewCell cell = null, bool randomBlock = false)
{
string getImgError;
@@ -35,9 +35,11 @@ namespace TemuKJD.WXForms.ProductForms
public Lazy<WXProdNoCheckEditAddressForm> prodNoCheckEditForm = new Lazy<WXProdNoCheckEditAddressForm>();
public Lazy<CloudGoodsForm> cloudgoodsForm = new Lazy<CloudGoodsForm>();
public Lazy<CloudGoodsFavoriteForm> cloudgoodsFavoriteForm = new Lazy<CloudGoodsFavoriteForm>();
private readonly Lazy<WXViolationLogsForm> violationLogsForm = new Lazy<WXViolationLogsForm>();
public WXProductForm()
{
InitializeComponent();
tv.Nodes[0].Nodes.Insert(1, "违规记录", "违规记录");
try
{
@@ -102,6 +104,9 @@ namespace TemuKJD.WXForms.ProductForms
case "云端数据":
SetForm(cloudgoodsForm.Value);
break;
case "违规记录":
SetForm(violationLogsForm.Value);
break;
case "我的商品库":
SetForm(userCollectAuthProdListForm.Value);
break;
@@ -0,0 +1,357 @@
using System.Drawing;
using System.Windows.Forms;
using DevComponents.DotNetBar;
using TemuKJD.UserComponent;
namespace TemuKJD.WXForms.ProductForms
{
partial class WXViolationLogsForm
{
private TableLayoutPanel layout;
private FlowLayoutPanel settings;
private FlowLayoutPanel filters;
private CheckBox report;
private CheckBox exclude;
private CheckBox delist;
private PanelEx btnSave;
private Label shopLabel;
private ComboBox shop;
private Label reasonLabel;
private ComboBox reason;
private Label actionLabel;
private ComboBox action;
private PanelEx btnQuery;
private UserControls.UcSplitPage pagination;
private Label message;
private DataGridViewEx grid;
private DataGridViewTextBoxColumn colAction;
private void InitializeComponent()
{
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
this.layout = new System.Windows.Forms.TableLayoutPanel();
this.settings = new System.Windows.Forms.FlowLayoutPanel();
this.report = new System.Windows.Forms.CheckBox();
this.exclude = new System.Windows.Forms.CheckBox();
this.delist = new System.Windows.Forms.CheckBox();
this.btnSave = new DevComponents.DotNetBar.PanelEx();
this.filters = new System.Windows.Forms.FlowLayoutPanel();
this.shopLabel = new System.Windows.Forms.Label();
this.shop = new System.Windows.Forms.ComboBox();
this.reasonLabel = new System.Windows.Forms.Label();
this.reason = new System.Windows.Forms.ComboBox();
this.actionLabel = new System.Windows.Forms.Label();
this.action = new System.Windows.Forms.ComboBox();
this.btnQuery = new DevComponents.DotNetBar.PanelEx();
this.pagination = new UserControls.UcSplitPage();
this.message = new System.Windows.Forms.Label();
this.grid = new TemuKJD.UserComponent.DataGridViewEx();
this.ShopId = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.GoodsId = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.ReasonId = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colAction = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.CreatedTime = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.layout.SuspendLayout();
this.settings.SuspendLayout();
this.filters.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.grid)).BeginInit();
this.SuspendLayout();
//
// layout
//
this.layout.ColumnCount = 1;
this.layout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.layout.Controls.Add(this.settings, 0, 0);
this.layout.Controls.Add(this.filters, 0, 1);
this.layout.Controls.Add(this.grid, 0, 2);
this.layout.Controls.Add(this.pagination, 0, 3);
this.layout.Dock = System.Windows.Forms.DockStyle.Fill;
this.layout.Location = new System.Drawing.Point(0, 0);
this.layout.Name = "layout";
this.layout.RowCount = 4;
this.layout.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.layout.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.layout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
this.layout.Size = new System.Drawing.Size(1100, 530);
this.layout.TabIndex = 0;
//
// settings
//
this.settings.AutoSize = true;
this.settings.Controls.Add(this.report);
this.settings.Controls.Add(this.exclude);
this.settings.Controls.Add(this.delist);
this.settings.Controls.Add(this.btnSave);
this.settings.Dock = System.Windows.Forms.DockStyle.Fill;
this.settings.Location = new System.Drawing.Point(3, 3);
this.settings.Name = "settings";
this.settings.Padding = new System.Windows.Forms.Padding(6, 4, 6, 4);
this.settings.Size = new System.Drawing.Size(1094, 39);
this.settings.TabIndex = 0;
//
// report
//
this.report.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.report.AutoSize = true;
this.report.Location = new System.Drawing.Point(9, 9);
this.report.Name = "report";
this.report.Size = new System.Drawing.Size(123, 21);
this.report.TabIndex = 0;
this.report.Text = "自动上报违规商品";
//
// exclude
//
this.exclude.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.exclude.AutoSize = true;
this.exclude.Location = new System.Drawing.Point(138, 9);
this.exclude.Name = "exclude";
this.exclude.Size = new System.Drawing.Size(147, 21);
this.exclude.TabIndex = 1;
this.exclude.Text = "上货自动剔除违规商品";
//
// delist
//
this.delist.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.delist.AutoSize = true;
this.delist.Location = new System.Drawing.Point(291, 9);
this.delist.Name = "delist";
this.delist.Size = new System.Drawing.Size(123, 21);
this.delist.TabIndex = 2;
this.delist.Text = "自动下架违规商品";
//
// btnSave
//
this.btnSave.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton;
this.btnSave.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled;
this.btnSave.Cursor = System.Windows.Forms.Cursors.Hand;
this.btnSave.DisabledBackColor = System.Drawing.Color.Empty;
this.btnSave.Location = new System.Drawing.Point(420, 7);
this.btnSave.Name = "btnSave";
this.btnSave.Size = new System.Drawing.Size(80, 25);
this.btnSave.Style.Alignment = System.Drawing.StringAlignment.Center;
this.btnSave.Style.BackColor1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(126)))), ((int)(((byte)(255)))));
this.btnSave.Style.BackColor2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(126)))), ((int)(((byte)(255)))));
this.btnSave.Style.BorderSide = DevComponents.DotNetBar.eBorderSide.None;
this.btnSave.Style.ForeColor.Color = System.Drawing.Color.White;
this.btnSave.StyleMouseOver.Alignment = System.Drawing.StringAlignment.Center;
this.btnSave.StyleMouseOver.BackColor1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(102)))), ((int)(((byte)(255)))));
this.btnSave.StyleMouseOver.BackColor2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(102)))), ((int)(((byte)(255)))));
this.btnSave.TabIndex = 4;
this.btnSave.Text = "保存配置";
this.btnSave.Click += new System.EventHandler(this.Save_Click);
//
// filters
//
this.filters.AutoSize = true;
this.filters.Controls.Add(this.shopLabel);
this.filters.Controls.Add(this.shop);
this.filters.Controls.Add(this.reasonLabel);
this.filters.Controls.Add(this.reason);
this.filters.Controls.Add(this.actionLabel);
this.filters.Controls.Add(this.action);
this.filters.Controls.Add(this.btnQuery);
this.filters.Controls.Add(this.message);
this.filters.Dock = System.Windows.Forms.DockStyle.Fill;
this.filters.Location = new System.Drawing.Point(3, 48);
this.filters.Name = "filters";
this.filters.Padding = new System.Windows.Forms.Padding(6, 4, 6, 4);
this.filters.Size = new System.Drawing.Size(1094, 39);
this.filters.TabIndex = 1;
//
// shopLabel
//
this.shopLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.shopLabel.AutoSize = true;
this.shopLabel.Location = new System.Drawing.Point(9, 11);
this.shopLabel.Name = "shopLabel";
this.shopLabel.Size = new System.Drawing.Size(32, 17);
this.shopLabel.TabIndex = 0;
this.shopLabel.Text = "店铺";
//
// shop
//
this.shop.DisplayMember = "Value";
this.shop.SelectionChangeCommitted += this.Query_Click;
this.shop.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.shop.Location = new System.Drawing.Point(47, 7);
this.shop.Name = "shop";
this.shop.Size = new System.Drawing.Size(150, 25);
this.shop.TabIndex = 1;
this.shop.ValueMember = "Key";
//
// reasonLabel
//
this.reasonLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.reasonLabel.AutoSize = true;
this.reasonLabel.Location = new System.Drawing.Point(203, 11);
this.reasonLabel.Name = "reasonLabel";
this.reasonLabel.Size = new System.Drawing.Size(56, 17);
this.reasonLabel.TabIndex = 2;
this.reasonLabel.Text = "违规类型";
//
// reason
//
this.reason.DisplayMember = "Value";
this.reason.SelectionChangeCommitted += this.Query_Click;
this.reason.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.reason.Location = new System.Drawing.Point(265, 7);
this.reason.Name = "reason";
this.reason.Size = new System.Drawing.Size(150, 25);
this.reason.TabIndex = 3;
this.reason.ValueMember = "Key";
//
// actionLabel
//
this.actionLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.actionLabel.AutoSize = true;
this.actionLabel.Location = new System.Drawing.Point(421, 11);
this.actionLabel.Name = "actionLabel";
this.actionLabel.Size = new System.Drawing.Size(56, 17);
this.actionLabel.TabIndex = 4;
this.actionLabel.Text = "日志动作";
//
// action
//
this.action.SelectionChangeCommitted += this.Query_Click;
this.action.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.action.Items.AddRange(new object[] {
"全部动作",
"违规上报",
"上货剔除",
"违规下架"});
this.action.Location = new System.Drawing.Point(483, 7);
this.action.Name = "action";
this.action.Size = new System.Drawing.Size(150, 25);
this.action.TabIndex = 5;
//
// btnQuery
//
this.btnQuery.AccessibleRole = System.Windows.Forms.AccessibleRole.PushButton;
this.btnQuery.ColorSchemeStyle = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled;
this.btnQuery.Cursor = System.Windows.Forms.Cursors.Hand;
this.btnQuery.DisabledBackColor = System.Drawing.Color.Empty;
this.btnQuery.Location = new System.Drawing.Point(639, 7);
this.btnQuery.Name = "btnQuery";
this.btnQuery.Size = new System.Drawing.Size(58, 25);
this.btnQuery.Style.Alignment = System.Drawing.StringAlignment.Center;
this.btnQuery.Style.BackColor1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(126)))), ((int)(((byte)(255)))));
this.btnQuery.Style.BackColor2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(126)))), ((int)(((byte)(255)))));
this.btnQuery.Style.BorderSide = DevComponents.DotNetBar.eBorderSide.None;
this.btnQuery.Style.ForeColor.Color = System.Drawing.Color.White;
this.btnQuery.StyleMouseOver.Alignment = System.Drawing.StringAlignment.Center;
this.btnQuery.StyleMouseOver.BackColor1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(102)))), ((int)(((byte)(255)))));
this.btnQuery.StyleMouseOver.BackColor2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(102)))), ((int)(((byte)(255)))));
this.btnQuery.TabIndex = 6;
this.btnQuery.Text = "查询";
this.btnQuery.Click += new System.EventHandler(this.Query_Click);
//
// pagination
//
this.pagination.Dock = DockStyle.Right;
this.pagination.Name = "pagination";
this.pagination.Size = new Size(462, 25);
this.pagination.PageSize = 50;
this.pagination.IsShowPageSize = false;
this.pagination.IsShowTotalCount = false;
this.pagination.PageChange += this.Pagination_PageChange;
//
// message
//
this.message.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.message.AutoSize = true;
this.message.Location = new System.Drawing.Point(789, 11);
this.message.Name = "message";
this.message.Size = new System.Drawing.Size(0, 17);
this.message.TabIndex = 14;
//
// grid
//
this.grid.AllowUserToAddRows = false;
this.grid.AllowUserToDeleteRows = false;
dataGridViewCellStyle1.BackColor = System.Drawing.Color.WhiteSmoke;
this.grid.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1;
this.grid.AutoRowNumber = true;
this.grid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.grid.BackgroundColor = System.Drawing.Color.White;
this.grid.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(126)))), ((int)(((byte)(255)))));
this.grid.Columns.AddRange(new DataGridViewColumn[] { this.ShopId, this.GoodsId, this.ReasonId, this.colAction, this.CreatedTime });
this.grid.CellFormatting += new DataGridViewCellFormattingEventHandler(this.Grid_CellFormatting);
this.grid.Dock = System.Windows.Forms.DockStyle.Fill;
this.grid.Location = new System.Drawing.Point(3, 93);
this.grid.MultiSelectCheckBox = true;
this.grid.Name = "grid";
this.grid.ReadOnly = true;
this.grid.RowHeadersVisible = true;
this.grid.Size = new System.Drawing.Size(1094, 434);
this.grid.TabIndex = 2;
//
//
// ShopId
//
this.ShopId.DataPropertyName = "ShopId";
this.ShopId.HeaderText = "店铺";
this.ShopId.Name = "ShopId";
this.ShopId.Width = 38;
//
// GoodsId
//
this.GoodsId.DataPropertyName = "GoodsId";
this.GoodsId.HeaderText = "商品编号";
this.GoodsId.Name = "GoodsId";
this.GoodsId.Width = 48;
//
// ReasonId
//
this.ReasonId.DataPropertyName = "ReasonId";
this.ReasonId.HeaderText = "违规类型";
this.ReasonId.Name = "ReasonId";
this.ReasonId.Width = 64;
//
// colAction
//
this.colAction.DataPropertyName = "Action";
this.colAction.HeaderText = "日志动作";
this.colAction.Name = "colAction";
this.colAction.Width = 96;
//
// CreatedTime
//
this.CreatedTime.DataPropertyName = "CreatedTime";
dataGridViewCellStyle2.Format = "yyyy-MM-dd HH:mm:ss";
this.CreatedTime.DefaultCellStyle = dataGridViewCellStyle2;
this.CreatedTime.HeaderText = "记录时间";
this.CreatedTime.Name = "CreatedTime";
this.CreatedTime.Width = 191;
//
// WXViolationLogsForm
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(1100, 530);
this.Controls.Add(this.layout);
this.Font = new System.Drawing.Font("微软雅黑", 9F);
this.KeyPreview = true;
this.Name = "WXViolationLogsForm";
this.Text = "违规日志";
this.Shown += new System.EventHandler(this.Query_Click);
this.VisibleChanged += new System.EventHandler(this.Form_VisibleChanged);
this.layout.ResumeLayout(false);
this.layout.PerformLayout();
this.settings.ResumeLayout(false);
this.settings.PerformLayout();
this.filters.ResumeLayout(false);
this.filters.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.grid)).EndInit();
this.ResumeLayout(false);
}
private DataGridViewTextBoxColumn ShopId;
private DataGridViewTextBoxColumn GoodsId;
private DataGridViewTextBoxColumn ReasonId;
private DataGridViewTextBoxColumn CreatedTime;
}
}
@@ -0,0 +1,160 @@
using AllBase;
using AllBase.Helper;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using TemuKJD.BaseForms;
using Work.Services;
namespace TemuKJD.WXForms.ProductForms
{
public partial class WXViolationLogsForm : BaseForm
{
private readonly BindingList<GoodsViolationLogEntity> rows = new BindingList<GoodsViolationLogEntity>();
private Dictionary<int, string> reasons;
private Dictionary<string, string> shops;
private const int PageSize = 50;
private readonly List<long> pageCursors = new List<long> { 0 };
private int currentPage;
public WXViolationLogsForm()
{
InitializeComponent();
action.SelectedIndex = 0;
grid.AutoGenerateColumns = false;
grid.DataSource = rows;
}
private void Save_Click(object sender, EventArgs e)
{
var config = GlobalConfig.GlobalSettings;
config.AutoReportViolationProducts = report.Checked;
config.AutoExcludeViolationProductsOnUpload = exclude.Checked;
config.AutoDelistViolationProducts = delist.Checked;
StartUp.SaveGlobalSettings();
message.Text = "配置已保存";
}
private void Form_VisibleChanged(object sender, EventArgs e)
{
if (!Visible) return;
var config = GlobalConfig.GlobalSettings;
report.Checked = config.AutoReportViolationProducts;
exclude.Checked = config.AutoExcludeViolationProductsOnUpload;
delist.Checked = config.AutoDelistViolationProducts;
var selected = shop.SelectedValue as string;
shops = GlobalConfig.ShopLoginList.Where(x => !string.IsNullOrWhiteSpace(x.ShopId))
.GroupBy(x => x.ShopId).ToDictionary(g => g.Key, g => string.IsNullOrWhiteSpace(g.First().ShopAccount)
? "未知店铺" : g.First().ShopAccount);
shop.DataSource = new[] { new KeyValuePair<string, string>("", "全部店铺") }.Concat(shops).ToList();
if (selected != null && shops.ContainsKey(selected)) shop.SelectedValue = selected;
}
private async void Query_Click(object sender, EventArgs e) => await QueryAsync(0, reset: true);
private async void Pagination_PageChange(object sender)
{
int targetPage = pagination.CurrentPage - 1;
if (targetPage != currentPage) await QueryAsync(targetPage);
}
private void Grid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.Value == null) return;
switch (grid.Columns[e.ColumnIndex].DataPropertyName)
{
case nameof(GoodsViolationLogEntity.ShopId):
e.Value = shops.TryGetValue((string)e.Value, out var name) ? name : "未知店铺";
e.FormattingApplied = true;
break;
case nameof(GoodsViolationLogEntity.ReasonId):
e.Value = reasons.TryGetValue((int)e.Value, out var label) ? label : "未知违规类型";
e.FormattingApplied = true;
break;
case nameof(GoodsViolationLogEntity.Action):
var index = (int)(ViolationLogAction)e.Value;
e.Value = index > 0 && index < action.Items.Count ? action.Items[index] : "未知日志动作";
e.FormattingApplied = true;
break;
}
}
private async Task QueryAsync(int targetPage, bool reset = false)
{
if (!filters.Enabled) return;
filters.Enabled = false;
pagination.Enabled = false;
try
{
if (reset)
{
currentPage = 0;
pageCursors.Clear();
pageCursors.Add(0);
rows.Clear();
}
message.Text = "正在查询…";
if (reasons == null)
{
var types = await CloudGoodsService.GetViolationReasons();
if (IsDisposed) return;
reasons = types.ToDictionary(type => type.Id, type => type.Name);
reason.DataSource = new[] { new KeyValuePair<int, string>(0, "全部类型") }
.Concat(types.Where(type => type.IsViolation).Select(type => new KeyValuePair<int, string>(type.Id, type.Name))).ToList();
}
var selected = shop.SelectedValue as string;
var ids = string.IsNullOrEmpty(selected) ? shops.Keys.ToArray() : new[] { selected };
if (ids.Length == 0) { message.Text = "暂无可查询的店铺"; return; }
var reasonId = (int)reason.SelectedValue;
var actionId = action.SelectedIndex;
var cursor = pageCursors[targetPage];
var matches = new List<GoodsViolationLogEntity>();
while (matches.Count <= PageSize)
{
var batch = await CloudGoodsService.GetViolationLogs(ids, beforeId: cursor, size: PageSize);
if (IsDisposed) return;
foreach (var log in batch)
{
if (cursor != 0 && log.Id >= cursor) throw new InvalidOperationException("违规日志分页顺序异常。");
cursor = log.Id;
if ((reasonId == 0 || reasonId == log.ReasonId) && (actionId == 0 || actionId == (int)log.Action))
matches.Add(log);
if (matches.Count > PageSize) break;
}
if (batch.Count < PageSize) break;
}
if (matches.Count <= PageSize || pageCursors.ElementAtOrDefault(targetPage + 1) != matches[PageSize - 1].Id)
{
pageCursors.RemoveRange(targetPage + 1, pageCursors.Count - targetPage - 1);
if (matches.Count > PageSize) pageCursors.Add(matches[PageSize - 1].Id);
}
currentPage = targetPage;
rows.Clear();
foreach (var log in matches.Take(PageSize)) rows.Add(log);
message.Text = rows.Count == 0 ? "无匹配日志" : $"本页 {rows.Count} 条";
}
catch (Exception ex)
{
message.Text = "查询失败:" + ex.Message;
}
finally
{
if (!IsDisposed)
{
pagination.TotalPage = pageCursors.Count;
if (pagination.CurrentPage != currentPage + 1) pagination.CurrentPage = currentPage + 1;
filters.Enabled = true;
pagination.Enabled = true;
}
}
}
}
}
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>