2026-01-21 19:31:27 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2026-01-23 16:25:57 +08:00
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
|
using System.Text;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using AlibabaCloud.SDK.Dingtalkoauth2_1_0.Models;
|
2026-01-29 20:29:12 +08:00
|
|
|
|
using MQTT_WinformV1;
|
2026-01-23 16:25:57 +08:00
|
|
|
|
using MySql.Data.MySqlClient;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
using Newtonsoft.Json;
|
2026-01-23 16:25:57 +08:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
using Tea;
|
|
|
|
|
|
using YiDa_WinForm.Config;
|
|
|
|
|
|
using YiDa_WinForm.Model;
|
2026-01-29 08:39:56 +08:00
|
|
|
|
using YiDa_WinForm.Utils;
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-22 16:59:41 +08:00
|
|
|
|
namespace YiDa_WinForm.Service
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
2026-01-29 08:39:56 +08:00
|
|
|
|
// 连杆上传状态事件参数(原有,保留不变)
|
|
|
|
|
|
public class RodUploadStatusEventArgs : EventArgs
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否上传成功(用于更新 MainForm 的 _isImageUploadedInCycle 标记)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsUploadSuccess { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 附加消息(可选,如上传失败原因)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string Message { get; set; } = string.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-21 19:31:27 +08:00
|
|
|
|
public class YiDaUploadService
|
|
|
|
|
|
{
|
2026-01-23 16:25:57 +08:00
|
|
|
|
private readonly ButtonOperationService _buttonService;
|
2026-01-29 08:39:56 +08:00
|
|
|
|
|
2026-01-23 16:25:57 +08:00
|
|
|
|
// 数据库连接配置
|
2026-01-29 20:29:12 +08:00
|
|
|
|
private readonly string _connectionString = AppConfigHelper.MySqlConnectionString;
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
// 通知UI层
|
|
|
|
|
|
public event Action<string> MessageReceived;
|
|
|
|
|
|
|
2026-01-21 19:31:27 +08:00
|
|
|
|
// 钉钉token
|
2026-01-29 20:29:12 +08:00
|
|
|
|
static readonly string _tokenAppKey = AppConfigHelper.YiDa_TokenAppKey;
|
|
|
|
|
|
static readonly string _tokenAppSecret = AppConfigHelper.YiDa_TokenAppSecret;
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
2026-01-21 19:31:27 +08:00
|
|
|
|
// 宜搭上传配置
|
2026-01-29 20:29:12 +08:00
|
|
|
|
static readonly string _appType = AppConfigHelper.YiDa_AppType;
|
|
|
|
|
|
static readonly string _systemToken = AppConfigHelper.YiDa_SystemToken;
|
|
|
|
|
|
static readonly string _userId = AppConfigHelper.YiDa_UserId;
|
|
|
|
|
|
static readonly string _formUuid = AppConfigHelper.YiDa_FormUuid;
|
|
|
|
|
|
static readonly string _processCode = AppConfigHelper.YiDa_ProcessCode;
|
2026-01-29 08:39:56 +08:00
|
|
|
|
|
2026-01-23 16:25:57 +08:00
|
|
|
|
// 报废图片
|
2026-01-29 08:39:56 +08:00
|
|
|
|
public string _reformationPicture { get; set; } = string.Empty;
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
2026-01-29 20:29:12 +08:00
|
|
|
|
// 钉钉配置
|
|
|
|
|
|
static readonly string _baseAddress = AppConfigHelper.DingDing_BaseAddress;
|
|
|
|
|
|
static readonly string _dingDingWebHook = AppConfigHelper.DingDing_RobotWebHook;
|
|
|
|
|
|
static readonly string _dingDingSecret = AppConfigHelper.DingDing_RobotSecret;
|
2026-01-29 08:39:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 定义公共事件(供 MainForm 订阅,连杆+报废)
|
|
|
|
|
|
public event EventHandler<RodUploadStatusEventArgs> RodUploadStatusChanged;
|
|
|
|
|
|
public event EventHandler<ScrapUploadStatusEventArgs> ScrapUploadStatusChanged;
|
|
|
|
|
|
|
|
|
|
|
|
// 构造函数(初始化 _buttonService)
|
|
|
|
|
|
public YiDaUploadService()
|
|
|
|
|
|
{
|
|
|
|
|
|
_buttonService = new ButtonOperationService();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================================== 宜搭上传(原有代码,保留不变) ====================================
|
2026-01-21 19:31:27 +08:00
|
|
|
|
private static AlibabaCloud.SDK.Dingtalkoauth2_1_0.Client CreateClient()
|
|
|
|
|
|
{
|
|
|
|
|
|
AlibabaCloud.OpenApiClient.Models.Config config = new AlibabaCloud.OpenApiClient.Models.Config();
|
|
|
|
|
|
config.Protocol = "https";
|
|
|
|
|
|
config.RegionId = "central";
|
|
|
|
|
|
return new AlibabaCloud.SDK.Dingtalkoauth2_1_0.Client(config);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string GetDingDingToken()
|
|
|
|
|
|
{
|
|
|
|
|
|
string strToken = string.Empty;
|
|
|
|
|
|
GetAccessTokenResponse token = null;
|
|
|
|
|
|
|
|
|
|
|
|
var client = CreateClient();
|
|
|
|
|
|
var getAccessTokenRequest = new GetAccessTokenRequest
|
|
|
|
|
|
{
|
|
|
|
|
|
AppKey = _tokenAppKey,
|
|
|
|
|
|
AppSecret = _tokenAppSecret,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
token = client.GetAccessToken(getAccessTokenRequest);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (TeaException err)
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = $"获取钉钉Token异常【TeaException】:错误码={err.Code},错误信息={err.Message}";
|
|
|
|
|
|
Console.WriteLine(errorMsg);
|
2026-01-29 08:39:56 +08:00
|
|
|
|
LogHelper.AppendLog(errorMsg);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = $"获取 token 异常:{ex.Message}";
|
|
|
|
|
|
Console.WriteLine(errorMsg);
|
2026-01-29 08:39:56 +08:00
|
|
|
|
LogHelper.AppendLog(errorMsg);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return token != null ? token.Body.AccessToken : string.Empty;
|
|
|
|
|
|
}
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
private static async Task RecordSuccessLog(List<MqttModel> mqttLists)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (mqttLists != null && mqttLists.Count > 0)
|
|
|
|
|
|
{
|
2026-01-23 16:25:57 +08:00
|
|
|
|
var buttonService = new ButtonOperationService();
|
|
|
|
|
|
await buttonService.CreateSuccessLog(mqttLists);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static AlibabaCloud.SDK.Dingtalkyida_2_0.Client CreateYiDaClient()
|
|
|
|
|
|
{
|
|
|
|
|
|
AlibabaCloud.OpenApiClient.Models.Config config = new AlibabaCloud.OpenApiClient.Models.Config();
|
|
|
|
|
|
config.Protocol = "https";
|
|
|
|
|
|
config.RegionId = "central";
|
|
|
|
|
|
return new AlibabaCloud.SDK.Dingtalkyida_2_0.Client(config);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-23 16:25:57 +08:00
|
|
|
|
public async Task UploadDatabaseDataToYiDaWithLogging(string accessToken, List<YiDaModel> yidaLists,
|
|
|
|
|
|
List<MqttModel> mqttLists, MainForm form)
|
2026-01-21 19:31:27 +08:00
|
|
|
|
{
|
|
|
|
|
|
var client = CreateYiDaClient();
|
|
|
|
|
|
|
2026-01-23 16:25:57 +08:00
|
|
|
|
AlibabaCloud.SDK.Dingtalkyida_2_0.Models.StartInstanceHeaders startInstanceHeaders =
|
|
|
|
|
|
new AlibabaCloud.SDK.Dingtalkyida_2_0.Models.StartInstanceHeaders();
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
startInstanceHeaders.XAcsDingtalkAccessToken = accessToken;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < yidaLists.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var item = yidaLists[i];
|
|
|
|
|
|
var mqttItem = mqttLists[i];
|
|
|
|
|
|
|
|
|
|
|
|
var dataDict = new Dictionary<string, object>
|
|
|
|
|
|
{
|
2026-01-23 16:25:57 +08:00
|
|
|
|
{ "textField_mha98neu", item.textField_mha98neu }, //供应商代码
|
|
|
|
|
|
{ "textField_mha98nev", item.textField_mha98nev }, //供应商名称
|
|
|
|
|
|
{ "textField_mha98new", item.textField_mha98new }, //车型
|
|
|
|
|
|
{ "textField_mha98nex", item.textField_mha98nex }, //零件号
|
|
|
|
|
|
{ "textField_mha98ney", item.textField_mha98ney }, //零件名称
|
|
|
|
|
|
{ "textField_mha98nf1", item.textField_mha98nf1 }, //参数名
|
|
|
|
|
|
{ "textField_mhx44i2i", item.textField_mhx44i2i }, //参数值
|
|
|
|
|
|
{ "textField_mhx44i2j", item.textField_mhx44i2j }, //下公差
|
|
|
|
|
|
{ "textField_mhx44i2k", item.textField_mhx44i2k }, //上公差
|
|
|
|
|
|
{ "textField_mha98nf7", item.textField_mha98nf7 }, //零件责任人
|
|
|
|
|
|
{ "textField_mhlvt8ht", DateTime.Now.ToString("yyyy-MM-dd") }, //写入时间
|
|
|
|
|
|
{ "textField_mha98nf5", item.textField_mha98nf5 }, //合格判断
|
|
|
|
|
|
{ "textField_mha98nf0", item.textField_mha98nf0 }, //工位
|
|
|
|
|
|
{ "textField_mha98nfh", item.textField_mha98nfh } //外保负责人
|
2026-01-21 19:31:27 +08:00
|
|
|
|
};
|
2026-01-23 16:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
string jsonData = JsonConvert.SerializeObject(dataDict);
|
|
|
|
|
|
|
|
|
|
|
|
AlibabaCloud.SDK.Dingtalkyida_2_0.Models.StartInstanceRequest startInstanceRequest =
|
|
|
|
|
|
new AlibabaCloud.SDK.Dingtalkyida_2_0.Models.StartInstanceRequest
|
|
|
|
|
|
{
|
|
|
|
|
|
AppType = _appType,
|
|
|
|
|
|
SystemToken = _systemToken,
|
|
|
|
|
|
UserId = _userId,
|
|
|
|
|
|
Language = "zh_CN",
|
|
|
|
|
|
FormUuid = _formUuid,
|
|
|
|
|
|
FormDataJson = jsonData,
|
|
|
|
|
|
ProcessCode = _processCode,
|
|
|
|
|
|
};
|
2026-01-21 19:31:27 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-01-23 16:25:57 +08:00
|
|
|
|
var response = client.StartInstanceWithOptions(startInstanceRequest, startInstanceHeaders,
|
|
|
|
|
|
new AlibabaCloud.TeaUtil.Models.RuntimeOptions());
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
if (response != null && response.Body != null)
|
|
|
|
|
|
{
|
2026-01-23 16:25:57 +08:00
|
|
|
|
await RecordSuccessLog(mqttLists);
|
2026-01-21 19:31:27 +08:00
|
|
|
|
string logData = JsonConvert.SerializeObject(dataDict, Formatting.Indented);
|
2026-01-29 08:39:56 +08:00
|
|
|
|
LogHelper.AppendLog($"上传数据成功:\r\n{logData}");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
Console.WriteLine($"上传数据成功:\r\n{logData}");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"上传返回空结果,数据未存入 MySQL。");
|
|
|
|
|
|
string logData = JsonConvert.SerializeObject(dataDict, Formatting.Indented);
|
2026-01-29 08:39:56 +08:00
|
|
|
|
LogHelper.AppendLog($"上传返回空结果,数据未存入 MySQL:\r\n{logData}");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (TeaException err)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"上传异常:{err.Code} --- {err.Message}");
|
|
|
|
|
|
string logData = JsonConvert.SerializeObject(dataDict, Formatting.Indented);
|
2026-01-29 08:39:56 +08:00
|
|
|
|
LogHelper.AppendLog($"上传异常:{err.Code} --- {err.Message}\r\n数据:\r\n{logData}");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"上传异常:{ex.Message}");
|
|
|
|
|
|
string logData = JsonConvert.SerializeObject(dataDict, Formatting.Indented);
|
2026-01-29 08:39:56 +08:00
|
|
|
|
LogHelper.AppendLog($"上传异常:{ex.Message}\r\n数据:\r\n{logData}");
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-29 08:39:56 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================================== 核心修复:签名生成方法(补充URL编码) ====================================
|
2026-01-23 16:25:57 +08:00
|
|
|
|
static string CalcSign(long timestamp, string secret)
|
|
|
|
|
|
{
|
2026-01-29 08:39:56 +08:00
|
|
|
|
if (string.IsNullOrWhiteSpace(secret))
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new ArgumentNullException(nameof(secret), "钉钉机器人密钥不能为空");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-23 16:25:57 +08:00
|
|
|
|
string stringToSign = $"{timestamp}\n{secret}";
|
|
|
|
|
|
var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
|
|
|
|
|
|
byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
|
2026-01-29 08:39:56 +08:00
|
|
|
|
string base64Sign = Convert.ToBase64String(hashBytes);
|
|
|
|
|
|
|
|
|
|
|
|
return base64Sign;
|
2026-01-23 16:25:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-29 08:39:56 +08:00
|
|
|
|
// ==================================== 报废证据上传(稳定成功版 + 事件触发) ====================================
|
2026-01-23 16:25:57 +08:00
|
|
|
|
public async Task UploadScrapCertificate(string token, string filePath)
|
|
|
|
|
|
{
|
2026-01-29 08:39:56 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = "报废图片路径为空或文件不存在";
|
|
|
|
|
|
OnScrapUploadStatusChanged(false, errorMsg);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 初始化
|
|
|
|
|
|
HttpClient client = new HttpClient
|
|
|
|
|
|
{
|
2026-01-29 20:29:12 +08:00
|
|
|
|
BaseAddress = new Uri(_baseAddress)
|
2026-01-29 08:39:56 +08:00
|
|
|
|
};
|
|
|
|
|
|
var form = new MultipartFormDataContent();
|
|
|
|
|
|
var fileStream = File.OpenRead(filePath);
|
|
|
|
|
|
var streamContent = new StreamContent(fileStream);
|
|
|
|
|
|
streamContent.Headers.ContentType =
|
|
|
|
|
|
new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg"); // 或 image/png
|
|
|
|
|
|
form.Add(streamContent, "media", Path.GetFileName(filePath));
|
|
|
|
|
|
form.Add(new StringContent("image"), "type"); // 接口要求字段 type=image
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 发送请求上传文件(access_token 放查询串)
|
|
|
|
|
|
var url = $"/media/upload?access_token={token}";
|
|
|
|
|
|
var resp = await client.PostAsync(url, form);
|
|
|
|
|
|
resp.EnsureSuccessStatusCode();
|
|
|
|
|
|
|
|
|
|
|
|
string json = await resp.Content.ReadAsStringAsync();
|
|
|
|
|
|
JObject jo = JObject.Parse(json);
|
|
|
|
|
|
string mediaId = jo["media_id"].Value<string>();
|
|
|
|
|
|
LogHelper.AppendLog($"报废图片上传完成,mediaId = {mediaId}");
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 发送群消息
|
|
|
|
|
|
long ts = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
|
|
|
string ymdhms = DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
2026-01-29 20:29:12 +08:00
|
|
|
|
string sign = CalcSign(ts, _dingDingSecret);
|
|
|
|
|
|
string webhook_url = $"{_dingDingWebHook}×tamp={ts}&sign={sign}";
|
2026-01-29 08:39:56 +08:00
|
|
|
|
|
|
|
|
|
|
var msg = new
|
|
|
|
|
|
{
|
|
|
|
|
|
msgtype = "markdown",
|
|
|
|
|
|
markdown = new
|
|
|
|
|
|
{
|
|
|
|
|
|
title = "报废证据上传",
|
|
|
|
|
|
text = $"## 报废凭证照片\n<img src=\"{mediaId}\" width=\"200\" />\n\n> 时间:{ymdhms}"
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
string transJson = JsonConvert.SerializeObject(msg);
|
|
|
|
|
|
var resp2 = await client.PostAsync(webhook_url,
|
|
|
|
|
|
new StringContent(transJson, Encoding.UTF8, "application/json"));
|
|
|
|
|
|
resp2.EnsureSuccessStatusCode();
|
|
|
|
|
|
|
|
|
|
|
|
string json2 = await resp2.Content.ReadAsStringAsync();
|
|
|
|
|
|
JObject res2 = JObject.Parse(json2);
|
|
|
|
|
|
string errmsg = res2["errmsg"].Value<string>();
|
|
|
|
|
|
LogHelper.AppendLog($"报废图片发送群消息完成,msg = {errmsg}");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = $"报废凭证图片上传异常:{ex.Message}";
|
|
|
|
|
|
LogHelper.AppendLog(errorMsg);
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================================== 连杆测试上传(和报废上传完全对齐,稳定成功 + 事件触发) ====================================
|
|
|
|
|
|
public async Task ConnectingRodTestUpload(string token, string filePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = "连杆图片路径为空或文件不存在";
|
|
|
|
|
|
OnRodUploadStatusChanged(false, errorMsg);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 初始化
|
|
|
|
|
|
HttpClient client = new HttpClient
|
|
|
|
|
|
{
|
2026-01-29 20:29:12 +08:00
|
|
|
|
BaseAddress = new Uri(_baseAddress)
|
2026-01-29 08:39:56 +08:00
|
|
|
|
};
|
|
|
|
|
|
var form = new MultipartFormDataContent();
|
|
|
|
|
|
var fileStream = File.OpenRead(filePath);
|
|
|
|
|
|
var streamContent = new StreamContent(fileStream);
|
|
|
|
|
|
streamContent.Headers.ContentType =
|
|
|
|
|
|
new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg"); // 或 image/png
|
|
|
|
|
|
form.Add(streamContent, "media", Path.GetFileName(filePath));
|
|
|
|
|
|
form.Add(new StringContent("image"), "type"); // 接口要求字段 type=image
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 发送请求上传文件(access_token 放查询串)
|
|
|
|
|
|
var url = $"/media/upload?access_token={token}";
|
|
|
|
|
|
var resp = await client.PostAsync(url, form);
|
|
|
|
|
|
resp.EnsureSuccessStatusCode();
|
|
|
|
|
|
|
|
|
|
|
|
string json = await resp.Content.ReadAsStringAsync();
|
|
|
|
|
|
JObject jo = JObject.Parse(json);
|
|
|
|
|
|
string mediaId = jo["media_id"].Value<string>();
|
|
|
|
|
|
LogHelper.AppendLog($"连杆图片上传完成,mediaId = {mediaId}");
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 发送群消息
|
|
|
|
|
|
long ts = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
|
|
|
string ymdhms = DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
2026-01-29 20:29:12 +08:00
|
|
|
|
string sign = CalcSign(ts, _dingDingSecret); // HMACSHA256→Base64
|
|
|
|
|
|
string webhook_url = $"{_dingDingWebHook}×tamp={ts}&sign={sign}";
|
2026-01-29 08:39:56 +08:00
|
|
|
|
|
|
|
|
|
|
var msg = new
|
|
|
|
|
|
{
|
|
|
|
|
|
msgtype = "markdown",
|
|
|
|
|
|
markdown = new
|
|
|
|
|
|
{
|
|
|
|
|
|
title = "连杆测试上传",
|
|
|
|
|
|
text = $"## 连杆测试照片\n<img src=\"{mediaId}\" width=\"200\" />\n\n> 时间:{ymdhms}"
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
string transJson = JsonConvert.SerializeObject(msg);
|
|
|
|
|
|
var resp2 = await client.PostAsync(webhook_url,
|
|
|
|
|
|
new StringContent(transJson, Encoding.UTF8, "application/json"));
|
|
|
|
|
|
resp2.EnsureSuccessStatusCode();
|
|
|
|
|
|
|
|
|
|
|
|
string json2 = await resp2.Content.ReadAsStringAsync();
|
|
|
|
|
|
JObject res2 = JObject.Parse(json2);
|
|
|
|
|
|
string errmsg = res2["errmsg"].Value<string>();
|
|
|
|
|
|
LogHelper.AppendLog($"连杆图片发送群消息完成,msg = {errmsg}");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = $"连杆测试图片上传异常:{ex.Message}";
|
|
|
|
|
|
LogHelper.AppendLog(errorMsg);
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================================== 钉钉报警(支持加签,稳定发送,适配连杆+报废) ====================================
|
|
|
|
|
|
public async Task<bool> SendDingDingTextAlarmAsync(string alarmContent, bool isRodAlarm = true)
|
|
|
|
|
|
{
|
2026-01-29 20:29:12 +08:00
|
|
|
|
// 前置校验
|
2026-01-29 08:39:56 +08:00
|
|
|
|
if (string.IsNullOrEmpty(alarmContent))
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.AppendLog("钉钉报警失败:报警内容不能为空");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-29 20:29:12 +08:00
|
|
|
|
// 初始化 HttpClient
|
2026-01-23 16:25:57 +08:00
|
|
|
|
HttpClient client = new HttpClient
|
|
|
|
|
|
{
|
2026-01-29 20:29:12 +08:00
|
|
|
|
BaseAddress = new Uri(_baseAddress)
|
2026-01-23 16:25:57 +08:00
|
|
|
|
};
|
2026-01-29 08:39:56 +08:00
|
|
|
|
|
|
|
|
|
|
try
|
2026-01-23 16:25:57 +08:00
|
|
|
|
{
|
2026-01-29 08:39:56 +08:00
|
|
|
|
// 4. 生成加签参数
|
|
|
|
|
|
long ts = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
|
|
|
|
string ymdhms = DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
2026-01-29 20:29:12 +08:00
|
|
|
|
string sign = CalcSign(ts, _dingDingSecret);
|
|
|
|
|
|
string webhook_url = $"{_dingDingWebHook}×tamp={ts}&sign={sign}";
|
2026-01-29 08:39:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 5. 构造报警消息(区分连杆/报废)
|
|
|
|
|
|
string alarmTitle = isRodAlarm ? "连杆测试报警" : "报废上传报警";
|
2026-01-30 10:10:14 +08:00
|
|
|
|
string alarmDesc = isRodAlarm ? "未及时提供强度测试图片" : "未及时提供报废图片";
|
2026-01-29 08:39:56 +08:00
|
|
|
|
var msg = new
|
2026-01-23 16:25:57 +08:00
|
|
|
|
{
|
2026-01-29 08:39:56 +08:00
|
|
|
|
msgtype = "markdown",
|
|
|
|
|
|
markdown = new
|
|
|
|
|
|
{
|
|
|
|
|
|
title = alarmTitle,
|
2026-01-30 10:10:14 +08:00
|
|
|
|
text = $"## **{alarmTitle}**<br>报警内容:{alarmContent}"
|
2026-01-29 08:39:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 序列化消息
|
|
|
|
|
|
string transJson = JsonConvert.SerializeObject(msg);
|
|
|
|
|
|
LogHelper.AppendLog($"钉钉报警:构造的 JSON 消息:\r\n{transJson}");
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 发送请求
|
|
|
|
|
|
var resp2 = await client.PostAsync(webhook_url,
|
|
|
|
|
|
new StringContent(transJson, Encoding.UTF8, "application/json"));
|
|
|
|
|
|
resp2.EnsureSuccessStatusCode();
|
|
|
|
|
|
|
|
|
|
|
|
// 8. 解析响应结果
|
|
|
|
|
|
string json2 = await resp2.Content.ReadAsStringAsync();
|
|
|
|
|
|
JObject res2 = JObject.Parse(json2);
|
|
|
|
|
|
string errmsg = res2["errmsg"].Value<string>();
|
|
|
|
|
|
LogHelper.AppendLog($"钉钉报警发送完成,msg = {errmsg}");
|
|
|
|
|
|
|
|
|
|
|
|
// 9. 判断是否真正成功
|
|
|
|
|
|
return errmsg.Equals("ok", StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
LogHelper.AppendLog($"钉钉报警异常:{ex.Message},堆栈信息:{ex.StackTrace}");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
// 10. 释放资源
|
|
|
|
|
|
client.Dispose();
|
|
|
|
|
|
}
|
2026-01-23 16:25:57 +08:00
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
|
2026-01-29 08:39:56 +08:00
|
|
|
|
// ==================================== 连杆上传内部封装 + 事件触发(优化稳定性) ====================================
|
|
|
|
|
|
public async Task<bool> UploadRodTestImageInternalAsync(string filePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = "图片路径为空或文件不存在";
|
|
|
|
|
|
OnRodUploadStatusChanged(false, errorMsg);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string token = GetDingDingToken();
|
|
|
|
|
|
if (string.IsNullOrEmpty(token))
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = "获取 DingDing Token 失败,无法执行上传";
|
|
|
|
|
|
OnRodUploadStatusChanged(false, errorMsg);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await ConnectingRodTestUpload(token, filePath);
|
|
|
|
|
|
|
|
|
|
|
|
string successMsg = "连杆测试图片上传成功,已发送至钉钉群";
|
|
|
|
|
|
OnRodUploadStatusChanged(true, successMsg);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = $"连杆测试图片上传异常:{ex.Message}";
|
|
|
|
|
|
LogHelper.AppendLog(errorMsg);
|
|
|
|
|
|
OnRodUploadStatusChanged(false, errorMsg);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================================== 报废上传内部封装 + 事件触发(和连杆对齐) ====================================
|
|
|
|
|
|
public async Task<bool> UploadScrapImageInternalAsync(string filePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = "图片路径为空或文件不存在";
|
|
|
|
|
|
OnScrapUploadStatusChanged(false, errorMsg);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string token = GetDingDingToken();
|
|
|
|
|
|
if (string.IsNullOrEmpty(token))
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = "获取 DingDing Token 失败,无法执行上传";
|
|
|
|
|
|
OnScrapUploadStatusChanged(false, errorMsg);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await UploadScrapCertificate(token, filePath);
|
|
|
|
|
|
|
|
|
|
|
|
string successMsg = "报废凭证图片上传成功,已发送至钉钉群";
|
|
|
|
|
|
OnScrapUploadStatusChanged(true, successMsg);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
string errorMsg = $"报废凭证图片上传异常:{ex.Message}";
|
|
|
|
|
|
LogHelper.AppendLog(errorMsg);
|
|
|
|
|
|
OnScrapUploadStatusChanged(false, errorMsg);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================================== 事件触发辅助方法(连杆+报废) ====================================
|
|
|
|
|
|
protected virtual void OnRodUploadStatusChanged(bool isUploadSuccess, string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
RodUploadStatusChanged?.Invoke(this, new RodUploadStatusEventArgs
|
|
|
|
|
|
{
|
|
|
|
|
|
IsUploadSuccess = isUploadSuccess,
|
|
|
|
|
|
Message = message
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected virtual void OnScrapUploadStatusChanged(bool isUploadSuccess, string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
ScrapUploadStatusChanged?.Invoke(this, new ScrapUploadStatusEventArgs
|
|
|
|
|
|
{
|
|
|
|
|
|
IsUploadSuccess = isUploadSuccess,
|
|
|
|
|
|
Message = message
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-01-21 19:31:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|