Files
ctbjrj/成套报价软件/xiangmu/项目云.cs
T

590 lines
30 KiB
C#

// ============================================================
// 项目云窗体:把项目上传到会员服务器(项目云) / 从云下载还原 / 删除
// · 复用 主窗口.项目导入导出 的打包(BackupSQLiteDb+meta.json+zip)与还原(ImportProjectFromZip)
// · 传输走 会员服务Host.PostBinary / DownloadToFile(服务器= shezhi\会员服务.config.xml 指向的会员服务器)
// · 上传/下载按登录手机号隔离(服务器按手机号分目录存储)
// UI 全部代码构建(免设计器 resx 资源化坑,照 PickProjectDialogMulti 模式)
// ============================================================
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace 成套报价软件
{
public class 项目云窗体 : Form
{
private readonly MainForm _owner;
private ListView _lv本地; // 本地项目(可勾选上传)
private ListView _lv云端; // 云上项目(可勾选下载/删除)
private Button _btn上传;
private Button _btn刷新云;
private Button _btn下载;
private Button _btn删除云;
private Label _lbl状态;
private TextBox _txt手机号;
public 项目云窗体(MainForm owner)
{
_owner = owner;
初始化UI();
this.BackColor = System.Drawing.Color.White;
填本地项目();
if (!string.IsNullOrWhiteSpace(MainForm.当前登录手机号))
{ _txt手机号.Text = MainForm.当前登录手机号; 刷云端(); }
}
private void 初始化UI()
{
Text = "项目云(上传到会员服务器 / 下载还原)";
StartPosition = FormStartPosition.CenterParent;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
ClientSize = new Size(980, 560);
Font = new Font("宋体", 9F);
var lbl登录 = new Label { Text = "登录手机号:", Location = new Point(12, 14), Size = new Size(76, 20) };
Controls.Add(lbl登录);
_txt手机号 = new TextBox { Location = new Point(90, 11), Size = new Size(140, 23), ReadOnly = true, BackColor = SystemColors.Control };
Controls.Add(_txt手机号);
// ---- 左:本地项目 ----
var lbl本地 = new Label { Text = "本地项目(选中要上传的)", Location = new Point(12, 42), Size = new Size(300, 20) };
Controls.Add(lbl本地);
_lv本地 = new ListView
{
Location = new Point(12, 64),
Size = new Size(452, 300),
View = View.Details,
FullRowSelect = true,
MultiSelect = true,
HideSelection = false,
};
_lv本地.Columns.Add("项目编号", 110);
_lv本地.Columns.Add("项目名称", 170);
_lv本地.Columns.Add("客户名称", 110);
_lv本地.Columns.Add("项目日期", 60);
Controls.Add(_lv本地);
// ---- 右:云端项目 ----
var lbl云 = new Label { Text = "云端项目(会员服务器上,选中可下载/删除)", Location = new Point(516, 42), Size = new Size(320, 20) };
Controls.Add(lbl云);
_lv云端 = new ListView
{
Location = new Point(516, 64),
Size = new Size(452, 300),
View = View.Details,
FullRowSelect = true,
MultiSelect = true,
HideSelection = false,
};
_lv云端.Columns.Add("项目编号", 110);
_lv云端.Columns.Add("项目名称", 190);
_lv云端.Columns.Add("上传时间", 80);
_lv云端.Columns.Add("大小", 70);
Controls.Add(_lv云端);
// ---- 按钮(纯项目功能;库备份在主窗口 备份/恢复 按钮,不在这里) ----
_btn上传 = new Button { Text = "↑ 上传选中项目", Location = new Point(12, 474), Size = new Size(130, 30) };
_btn上传.Click += (s, e) => 上传选中();
Controls.Add(_btn上传);
var btn全选本地 = new Button { Text = "全选本地", Location = new Point(150, 474), Size = new Size(80, 30) };
btn全选本地.Click += (s, e) => { foreach (ListViewItem it in _lv本地.Items) it.Selected = true; };
Controls.Add(btn全选本地);
_btn刷新云 = new Button { Text = "刷新云端", Location = new Point(516, 474), Size = new Size(90, 30) };
_btn刷新云.Click += (s, e) => 刷云端();
Controls.Add(_btn刷新云);
_btn下载 = new Button { Text = "↓ 下载还原选中项", Location = new Point(614, 474), Size = new Size(140, 30) };
_btn下载.Click += (s, e) => 下载还原选中();
Controls.Add(_btn下载);
_btn删除云 = new Button { Text = "删除云端选中项", Location = new Point(762, 474), Size = new Size(130, 30), ForeColor = Color.Firebrick };
_btn删除云.Click += (s, e) => 删除云端选中();
Controls.Add(_btn删除云);
_lbl状态 = new Label { Text = "就绪。", Location = new Point(12, 520), Size = new Size(956, 26) };
Controls.Add(_lbl状态);
}
private void 设状态(string s) { _lbl状态.Text = s; Application.DoEvents(); }
/// <summary>写 diag 日志(MainForm.写日志 静态入口)。</summary>
private static void Log(string s)
{
try { MainForm.写日志(s); } catch { }
}
private string 登录手机号
{
get
{
string p = string.IsNullOrWhiteSpace(MainForm.当前登录手机号)
? (_txt手机号.Text ?? "").Trim() : MainForm.当前登录手机号.Trim();
return p;
}
}
// ---------- 本地项目列表 ----------
private void 填本地项目()
{
try
{
_lv本地.Items.Clear();
var projects = _owner.ReadAllProjectsFromDatabaseForCloud();
foreach (var p in projects)
{
string bh = 取字段(p, "项目编号");
var it = new ListViewItem(bh);
it.SubItems.Add(取字段(p, "项目名称"));
it.SubItems.Add(取字段(p, "客户名称"));
it.SubItems.Add(取字段(p, "项目日期"));
_lv本地.Items.Add(it);
}
}
catch (Exception ex) { Log("[项目云] 读本地项目失败: " + ex.Message); }
}
private static string 取字段(Dictionary<string, object> d, string k)
{
return d != null && d.ContainsKey(k) && d[k] != null ? d[k].ToString() : "";
}
// ---------- 上传 ----------
private void 上传选中()
{
string 手机号 = 登录手机号;
if (手机号.Length == 0 || string.IsNullOrEmpty(MainForm.当前登录密码)) { MessageBox.Show(this, "请先登录会员(新版协议需登录换取云操作凭证)。", "项目云", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
if (_lv本地.SelectedItems.Count == 0) { MessageBox.Show(this, "请先在左侧选中要上传的项目。", "项目云", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
var 选中 = new List<string>();
foreach (ListViewItem it in _lv本地.SelectedItems) 选中.Add(it.Text);
_btn上传.Enabled = false;
try
{
int ok = 0;
for (int i = 0; i < 选中.Count; i++)
{
设状态("正在上传 " + (i + 1) + "/" + 选中.Count + ": " + 选中[i] + " ...");
if (上传单个(手机号, 选中[i])) ok++;
}
设状态("上传完成:成功 " + ok + "/" + 选中.Count + "。");
刷云端();
}
catch (Exception ex)
{
Log("[项目云] 上传异常: " + ex.Message);
设状态("上传失败: " + ex.Message);
MessageBox.Show(this, "上传失败:\n" + ex.Message, "项目云", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally { _btn上传.Enabled = true; }
}
/// <summary>每日自动上传用的公开入口(内部走 登录态,单独线程调用须自带 Invoke 保护)。</summary>
public bool 上传单个公开(string pid)
{
return 上传单个(登录手机号, pid);
}
/// <summary>单库备份静态入口(每日自动上传用;不碰 UI,显式传手机号)。
/// 库清单收集在 主窗口.母排与备份.cs 收集云端库清单() 的同款逻辑——此处独立列一份避免跨窗体引用。</summary>
public static bool 备份单个库静态(MainForm owner, string 手机号, string 云编号, string 相对路径)
{
try
{
string 本地 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, 相对路径);
if (!File.Exists(本地)) return false;
string tempDir = Path.Combine(Path.GetTempPath(), "成套报价_库传_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempDir);
try
{
string 打包文件 = Path.Combine(tempDir, Path.GetFileName(相对路径));
if (相对路径.EndsWith(".db", StringComparison.OrdinalIgnoreCase))
owner.BackupSQLiteDbForCloud(本地, 打包文件);
else
File.Copy(本地, 打包文件, true);
byte[] zip;
using (var ms = new MemoryStream())
{
using (var ar = new ZipArchive(ms, ZipArchiveMode.Create, true))
ar.CreateEntryFromFile(打包文件, Path.GetFileName(打包文件), CompressionLevel.Optimal);
zip = ms.ToArray();
}
var 头 = new StringBuilder();
头.Append("{").Append("\"手机号\":\"").Append(owner.EscapeJsonStringForCloud(手机号)).Append("\",")
.Append("\"编号\":\"").Append(owner.EscapeJsonStringForCloud(云编号)).Append("\",")
.Append("\"名称\":\"").Append(owner.EscapeJsonStringForCloud(云编号.Substring(4) + "|" + 相对路径)).Append("\",")
.Append("\"时间\":\"").Append(DateTime.Now.ToString("yyyy-MM-dd")).Append("\"}");
string resp = 会员服务Host.PostBinary("/api/proj/upload", 头.ToString(), zip);
return resp != null && resp.IndexOf("\"ok\":true", StringComparison.OrdinalIgnoreCase) >= 0;
}
finally { try { Directory.Delete(tempDir, true); } catch { } }
}
catch { return false; }
}
/// <summary>打包并上传单个项目。复用导出的 BackupSQLiteDb+meta.json+zip 链路。</summary>
private bool 上传单个(string 手机号, string pid)
{
string dbPath = _owner.GetQuoteDbPathForCloud(pid);
if (!File.Exists(dbPath)) { Log("[项目云] 跳过(库不存在): " + pid); return false; }
var meta = _owner.ReadProjectMetaDataForCloud(pid);
string projName = meta != null && meta.ContainsKey("项目名称") && meta["项目名称"] != null ? meta["项目名称"].ToString() : "";
string tempDir = Path.Combine(Path.GetTempPath(), "成套报价_云传_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempDir);
try
{
string dbFile = Path.Combine(tempDir, "项目" + _owner.MakeSafeFileNameForCloud(pid) + ".db");
_owner.BackupSQLiteDbForCloud(dbPath, dbFile);
var sb = new StringBuilder();
sb.Append("{\n \"项目编号\": \"").Append(_owner.EscapeJsonStringForCloud(pid)).Append("\",\n \"字段\": {");
bool first = true;
if (meta != null)
{
foreach (var kv in meta)
{
if (first) first = false; else sb.Append(",");
sb.Append("\n \"").Append(_owner.EscapeJsonStringForCloud(kv.Key)).Append("\": \"")
.Append(_owner.EscapeJsonStringForCloud((kv.Value ?? "").ToString())).Append("\"");
}
}
sb.Append("\n }\n}");
File.WriteAllText(Path.Combine(tempDir, "meta.json"), sb.ToString(), new UTF8Encoding(false));
// ★ 打包到内存流(不用 CreateFromDirectory+ReadAllBytes:临时 zip 文件句柄
// 释放不及时,紧接着读会报"正由另一进程使用")
byte[] zip;
using (var ms = new MemoryStream())
{
using (var ar = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var f in Directory.GetFiles(tempDir))
{
ar.CreateEntryFromFile(f, Path.GetFileName(f), CompressionLevel.Optimal);
}
}
zip = ms.ToArray();
}
var 头 = new StringBuilder();
头.Append("{").Append("\"手机号\":\"").Append(_owner.EscapeJsonStringForCloud(手机号)).Append("\",")
.Append("\"编号\":\"").Append(_owner.EscapeJsonStringForCloud(pid)).Append("\",")
.Append("\"名称\":\"").Append(_owner.EscapeJsonStringForCloud(projName)).Append("\",")
.Append("\"时间\":\"").Append(DateTime.Now.ToString("yyyy-MM-dd")).Append("\"}");
string resp = 会员服务Host.PostBinary("/api/proj/upload", 头.ToString(), zip);
bool ok = resp != null && resp.IndexOf("\"ok\":true", StringComparison.OrdinalIgnoreCase) >= 0;
Log("[项目云] 上传 " + pid + "(" + zip.Length + "B): " + resp);
return ok;
}
finally
{
try { Directory.Delete(tempDir, true); } catch { }
}
}
// ---------- 云端列表 ----------
private void 刷云端()
{
string 手机号 = 登录手机号;
if (手机号.Length == 0) { 设状态("未登录,输入/登录手机号后点\"刷新云端\"。"); return; }
try
{
string q = Uri.EscapeDataString("手机号") + "=" + Uri.EscapeDataString(手机号);
string resp = 会员服务Host.Get("/api/proj/list?" + q);
_lv云端.Items.Clear();
if (resp == null || resp.IndexOf("\"ok\":true", StringComparison.OrdinalIgnoreCase) < 0)
{ 设状态("云端列表获取失败: " + (resp ?? "")); return; }
// 解析 [{编号,名称,时间,大小}] —— 项目名/值用轻量解析(照 ParseSimpleJsonFields 思路)
// ★ 过滤掉 LIB_ 开头的库备份条目(企业信息/方案库等在 备份/恢复 按钮的云端流程里管,不混进项目列表)
var 项目们 = 解析项目数组(resp);
int 库条目数 = 0;
foreach (var p in 项目们)
{
if (p.编号 != null && p.编号.StartsWith("LIB_", StringComparison.Ordinal)) { 库条目数++; continue; }
var it = new ListViewItem(p.编号);
it.SubItems.Add(p.名称);
it.SubItems.Add(p.时间);
it.SubItems.Add(格式大小(p.大小));
it.Tag = p.编号;
_lv云端.Items.Add(it);
}
设状态("云端共 " + _lv云端.Items.Count + " 个项目" + (库条目数 > 0 ? "(另有 " + 库条目数 + " 个库备份,在 备份/恢复 里管理)" : "") + "。");
}
catch (Exception ex)
{
Log("[项目云] 刷云端失败: " + ex.Message);
设状态("云端列表获取失败: " + ex.Message);
}
}
private class 云项目 { public string 编号, 名称, 时间; public long 大小; }
/// <summary>解析 /api/proj/list 的 项目:[{编号,名称,时间,大小}] 数组(中文键,轻量逐对象扫描)。</summary>
private static List<云项目> 解析项目数组(string json)
{
var 出 = new List<云项目>();
if (string.IsNullOrEmpty(json)) return 出;
int i = json.IndexOf('[');
if (i < 0) return 出;
int 深 = 0, 对象起 = -1;
var 字段 = new Dictionary<string, string>();
for (; i < json.Length; i++)
{
char c = json[i];
if (c == '{') { if (深 == 0) { 对象起 = i; 字段.Clear(); } 深++; }
else if (c == '}')
{
深--;
if (深 == 0 && 对象起 >= 0)
{
string obj = json.Substring(对象起, i - 对象起 + 1);
出.Add(new 云项目
{
编号 = 取json串(obj, "编号"),
名称 = 取json串(obj, "名称"),
时间 = 取json串(obj, "时间"),
大小 = 取json数(obj, "大小"),
});
对象起 = -1;
}
}
}
return 出;
}
/// <summary>从单个 JSON 对象文本里取字符串字段(处理 \uXXXX 与常规转义)。</summary>
private static string 取json串(string obj, string key)
{
int k = obj.IndexOf("\"" + key + "\"", StringComparison.Ordinal);
if (k < 0) return "";
int 冒 = obj.IndexOf(':', k + key.Length + 2);
if (冒 < 0) return "";
int 起 = obj.IndexOf('"', 冒 + 1);
if (起 < 0) return "";
var sb = new StringBuilder();
for (int i = 起 + 1; i < obj.Length; i++)
{
char c = obj[i];
if (c == '\\' && i + 1 < obj.Length)
{
char n = obj[++i];
if (n == 'u' && i + 4 < obj.Length)
{
sb.Append((char)Convert.ToInt32(obj.Substring(i + 1, 4), 16));
i += 4;
}
else sb.Append(n == 'n' ? '\n' : n);
}
else if (c == '"') break;
else sb.Append(c);
}
return sb.ToString();
}
private static long 取json数(string obj, string key)
{
int k = obj.IndexOf("\"" + key + "\"", StringComparison.Ordinal);
if (k < 0) return 0;
int 冒 = obj.IndexOf(':', k + key.Length + 2);
if (冒 < 0) return 0;
long v; int e = 冒 + 1;
while (e < obj.Length && (char.IsDigit(obj[e]) || obj[e] == '-')) e++;
long.TryParse(obj.Substring(冒 + 1, e - 冒 - 1), out v);
return v;
}
private static string 格式大小(long b)
{
if (b >= 1048576) return (b / 1048576d).ToString("0.#") + "M";
if (b >= 1024) return (b / 1024d).ToString("0.#") + "K";
return b + "B";
}
// ---------- 下载还原 ----------
private void 下载还原选中()
{
string 手机号 = 登录手机号;
if (手机号.Length == 0) { MessageBox.Show(this, "请先登录会员(新版协议需登录换取云操作凭证)。", "项目云", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
if (_lv云端.SelectedItems.Count == 0) { MessageBox.Show(this, "请先在右侧选中要下载的项目。", "项目云", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
var 选中 = new List<string>();
foreach (ListViewItem it in _lv云端.SelectedItems) 选中.Add((string)it.Tag ?? it.Text);
_btn下载.Enabled = false;
int ok = 0; var 新号 = new List<string>();
try
{
for (int i = 0; i < 选中.Count; i++)
{
设状态("正在下载 " + (i + 1) + "/" + 选中.Count + ": " + 选中[i] + " ...");
string q = Uri.EscapeDataString("手机号") + "=" + Uri.EscapeDataString(手机号)
+ "&" + Uri.EscapeDataString("编号") + "=" + Uri.EscapeDataString(选中[i]);
string zipPath = Path.Combine(Path.GetTempPath(), "成套报价_云取_" + Guid.NewGuid().ToString("N") + ".zip");
try
{
会员服务Host.DownloadToFile("/api/proj/download?" + q, zipPath);
string newPid = _owner.ImportProjectFromZipForCloud(zipPath);
if (newPid != null) { ok++; 新号.Add(newPid); }
}
finally { try { File.Delete(zipPath); } catch { } }
}
设状态("下载还原完成:成功 " + ok + "/" + 选中.Count + (新号.Count > 0 ? "(新编号: " + string.Join(", ", 新号.ToArray()) + ")" : ""));
MessageBox.Show(this, "下载还原完成:成功 " + ok + "/" + 选中.Count + "。" +
(新号.Count > 0 ? "\n新项目编号: " + string.Join(", ", 新号.ToArray()) : ""),
"项目云", MessageBoxButtons.OK, MessageBoxIcon.Information);
填本地项目();
}
catch (Exception ex)
{
Log("[项目云] 下载异常: " + ex.Message);
设状态("下载失败: " + ex.Message);
MessageBox.Show(this, "下载失败:\n" + ex.Message, "项目云", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally { _btn下载.Enabled = true; }
}
// ---------- 删除云端 ----------
private void 删除云端选中()
{
string 手机号 = 登录手机号;
if (手机号.Length == 0 || string.IsNullOrEmpty(MainForm.当前登录密码))
{ MessageBox.Show(this, "请先登录会员(删除云端项目需登录凭证)。", "项目云", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
if (_lv云端.SelectedItems.Count == 0) { MessageBox.Show(this, "请先在右侧选中要删除的云端项目。", "项目云", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
var 选中 = new List<string>();
foreach (ListViewItem it in _lv云端.SelectedItems) 选中.Add((string)it.Tag ?? it.Text);
if (MessageBox.Show(this, "确定删除云端上的 " + 选中.Count + " 个项目?\n(本地项目不受影响,删除后不可恢复)",
"项目云", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes) return;
_btn删除云.Enabled = false;
try
{
int ok = 0;
string 失败原因 = "";
for (int i = 0; i < 选中.Count; i++)
{
设状态("正在删除 " + (i + 1) + "/" + 选中.Count + ": " + 选中[i] + " ...");
// body 只带 手机号+编号;密码由 会员服务Host.Post 自动补当前登录密码
// (服务端 v1.5.1 起删除接口要求 手机号+密码+编号,漏带密码会报"手机号或密码错误")
string body = "{\"手机号\":\"" + _owner.EscapeJsonStringForCloud(手机号) + "\",\"编号\":\"" + _owner.EscapeJsonStringForCloud(选中[i]) + "\"}";
string resp = 会员服务Host.Post("/api/proj/delete", body);
if (resp != null && resp.IndexOf("\"ok\":true", StringComparison.OrdinalIgnoreCase) >= 0) ok++;
else if (失败原因.Length == 0) 失败原因 = 选中[i] + ": " + (resp ?? "无响应");
// 服务端建议逐条间隔 200ms,避免触发限速
if (i < 选中.Count - 1) System.Threading.Thread.Sleep(200);
}
设状态("云端删除完成:成功 " + ok + "/" + 选中.Count + (失败原因.Length > 0 ? "。失败:" + 失败原因 : "。"));
Log("[项目云] 云端删除 " + ok + "/" + 选中.Count + (失败原因.Length > 0 ? " 失败:" + 失败原因 : ""));
刷云端();
}
catch (Exception ex)
{
Log("[项目云] 删除异常: " + ex.Message);
设状态("删除失败: " + ex.Message);
}
finally { _btn删除云.Enabled = true; }
}
}
/// <summary>每日自动云上传(后台静默):每天首次进软件触发一次,全部项目覆盖上传。</summary>
internal static class 项目云自动上传
{
private static System.Windows.Forms.Timer _timer;
/// <summary>写 diag 日志。</summary>
private static void Log(string s)
{
try { MainForm.写日志(s); } catch { }
}
/// <summary>库文件清单(与 主窗口.母排与备份.cs 收集云端库清单 同款;方案库目录动态收集)。</summary>
private static System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string, string>> 收集库清单()
{
var 出 = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string, string>>();
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
出.Add(new System.Collections.Generic.KeyValuePair<string, string>("LIB_铜排库", @"sjk\advancedDataGridView8Table.db"));
出.Add(new System.Collections.Generic.KeyValuePair<string, string>("LIB_铝排库", @"sjk\advancedDataGridView8Table_LMY.db"));
出.Add(new System.Collections.Generic.KeyValuePair<string, string>("LIB_用户库", @"sjk\advancedDataGridView7Table.db"));
出.Add(new System.Collections.Generic.KeyValuePair<string, string>("LIB_柜体库", @"sjk\advancedDataGridView10Table.db"));
出.Add(new System.Collections.Generic.KeyValuePair<string, string>("LIB_fak方案库(含公式表)", @"sjk\fak.db"));
出.Add(new System.Collections.Generic.KeyValuePair<string, string>("LIB_企业信息", @"shezhi\企业信息.config.xml"));
string 方案目录 = Path.Combine(baseDir, "sjk", "方案库");
if (Directory.Exists(方案目录))
{
foreach (var f in Directory.GetFiles(方案目录, "*.db"))
出.Add(new System.Collections.Generic.KeyValuePair<string, string>(
"LIB_方案_" + Path.GetFileNameWithoutExtension(f),
"sjk\\方案库\\" + Path.GetFileName(f)));
}
return 出;
}
public static void 启动(MainForm owner)
{
if (_timer != null) return;
_timer = new System.Windows.Forms.Timer { Interval = 60000 }; // 每分钟看一眼
_timer.Tick += (s, e) =>
{
try
{
// 远程模式才自动传(本地 127.0.0.1 模式不传);未登录或无密码(新协议要求)不传
if (!会员服务Host.是远程) return;
if (string.IsNullOrWhiteSpace(MainForm.当前登录手机号)) return;
if (string.IsNullOrEmpty(MainForm.当前登录密码)) { Log("[项目云自动] 无登录密码,跳过(请重新登录)"); return; }
string today = DateTime.Now.ToString("yyyy-MM-dd");
string flag = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
Directory.CreateDirectory(flag);
flag = Path.Combine(flag, "项目云自动_" + MainForm.当前登录手机号 + ".txt");
if (File.Exists(flag) && File.ReadAllText(flag).Trim() == today) return; // 今天已传
// 后台线程执行,失败静默(只记 diag)
var 项目们 = owner.ReadAllProjectsFromDatabaseForCloud();
var ids = new List<string>();
foreach (var p in 项目们)
{
string bh = p.ContainsKey("项目编号") && p["项目编号"] != null ? p["项目编号"].ToString() : "";
if (bh.Length > 0) ids.Add(bh);
}
// 库文件清单(方案库/用户库/柜体库/铜排铝排库/企业信息)一并自动上传
var 库们 = 收集库清单();
File.WriteAllText(flag, today);
if (ids.Count == 0 && 库们.Count == 0) return;
var 窗 = new 项目云窗体(owner);
ThreadPool.QueueUserWorkItem(delegate
{
try
{
int ok = 0, 总 = ids.Count + 库们.Count;
foreach (var pid in ids)
{
try { if ((bool)窗.Invoke(new Func<string, bool>(窗.上传单个公开), pid)) ok++; }
catch { }
}
foreach (var kv in 库们)
{
try { if (项目云窗体.备份单个库静态(owner, MainForm.当前登录手机号, kv.Key, kv.Value)) ok++; }
catch { }
}
Log("[项目云自动] 每日自动上传完成: " + ok + "/" + 总);
}
catch (Exception ex) { Log("[项目云自动] 失败: " + ex.Message); }
finally { try { 窗.Invoke((Action)窗.Dispose); } catch { } }
});
}
catch (Exception ex) { Log("[项目云自动] 异常: " + ex.Message); }
};
_timer.Start();
}
}
}