From 0763a2623b94e87a8d61526693c19cf414a76742 Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 18 Dec 2025 00:05:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E3=80=81=E8=AE=A2=E5=8D=95=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E5=92=8C=E6=89=93=E5=8C=85=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加验证码登录支持(图形验证码显示和输入) - 修复订单同步,正确解析收件人信息(从logisticses合并) - 修复API端点配置(user.api.it120.cc) - 添加Costura.Fody实现单文件EXE打包 - 添加应用图标(app.ico) - 添加Inno Setup安装脚本(支持Win7+) - 暂时禁用导入发货功能 - 添加.gitignore文件 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .gitignore | 75 ++++++++ PackagingMallShipper/App.config | 4 +- PackagingMallShipper/Data/SqliteHelper.cs | 150 +++++++++++++++- PackagingMallShipper/FodyWeavers.xml | 11 ++ PackagingMallShipper/Models/ApiResponses.cs | 116 +++++++++++++ PackagingMallShipper/Models/Order.cs | 17 +- .../PackagingMallShipper.csproj | 160 ++++-------------- PackagingMallShipper/Resources/Icons/app.ico | Bin 0 -> 13519 bytes PackagingMallShipper/Resources/Icons/app.svg | 60 +++++++ PackagingMallShipper/Services/AuthService.cs | 67 ++++++-- PackagingMallShipper/Services/ExcelService.cs | 5 +- PackagingMallShipper/Services/Interfaces.cs | 3 +- PackagingMallShipper/Services/OrderService.cs | 1 + PackagingMallShipper/Services/SyncService.cs | 31 +++- .../ViewModels/LoginViewModel.cs | 69 +++++++- PackagingMallShipper/Views/LoginWindow.xaml | 45 ++++- .../Views/LoginWindow.xaml.cs | 5 + PackagingMallShipper/Views/OrderListView.xaml | 4 +- README.md | 28 ++- build_installer.bat | 58 +++++++ setup.iss | 148 ++++++++++++++++ 21 files changed, 876 insertions(+), 181 deletions(-) create mode 100644 .gitignore create mode 100644 PackagingMallShipper/FodyWeavers.xml create mode 100644 PackagingMallShipper/Resources/Icons/app.ico create mode 100644 PackagingMallShipper/Resources/Icons/app.svg create mode 100644 build_installer.bat create mode 100644 setup.iss diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b417df5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,75 @@ +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +x86/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio +.vs/ +*.user +*.suo +*.userosscache +*.sln.docstates +*.userprefs + +# NuGet +*.nupkg +**/[Pp]ackages/* +!**/[Pp]ackages/build/ +*.nuget.props +*.nuget.targets + +# Build outputs +publish/ +installer/ +*.exe +*.dll +*.pdb +*.cache + +# Installer files +*.7z +*.zip +*.msi + +# Test results +[Tt]est[Rr]esult*/ +*.trx + +# Rider +.idea/ +*.sln.iml + +# OS files +Thumbs.db +ehthumbs.db +Desktop.ini +.DS_Store + +# Temp files +*.tmp +*.temp +*.log +*~ +nul + +# Local data +*.db +*.sqlite + +# Config with secrets (keep App.config template) +# App.config + +# Generated files +FodyWeavers.xsd + +# Backup files +*.bak +*.orig + +# API test files +*_response.json diff --git a/PackagingMallShipper/App.config b/PackagingMallShipper/App.config index aa19cf8..6cb018a 100644 --- a/PackagingMallShipper/App.config +++ b/PackagingMallShipper/App.config @@ -5,8 +5,8 @@ - - + + diff --git a/PackagingMallShipper/Data/SqliteHelper.cs b/PackagingMallShipper/Data/SqliteHelper.cs index 3f06142..aa17440 100644 --- a/PackagingMallShipper/Data/SqliteHelper.cs +++ b/PackagingMallShipper/Data/SqliteHelper.cs @@ -1,5 +1,6 @@ using System; using System.Data.SQLite; +using System.Diagnostics; using System.IO; using PackagingMallShipper.Helpers; @@ -19,13 +20,21 @@ namespace PackagingMallShipper.Data ); var dir = Path.GetDirectoryName(_dbPath); + Debug.WriteLine($"[数据库初始化] 数据库目录: {dir}"); + if (!Directory.Exists(dir)) + { Directory.CreateDirectory(dir); + Debug.WriteLine($"[数据库初始化] 创建数据库目录"); + } _connectionString = $"Data Source={_dbPath};Version=3;"; + Debug.WriteLine($"[数据库初始化] 数据库路径: {_dbPath}"); + Debug.WriteLine($"[数据库初始化] 数据库文件存在: {File.Exists(_dbPath)}"); if (!File.Exists(_dbPath)) { + Debug.WriteLine($"[数据库初始化] 创建新数据库文件"); SQLiteConnection.CreateFile(_dbPath); CreateTables(); } @@ -40,15 +49,146 @@ namespace PackagingMallShipper.Data private static void CreateTables() { - using (var conn = GetConnection()) + try { - conn.Open(); - var sql = ResourceHelper.GetEmbeddedResource("schema.sql"); - using (var cmd = new SQLiteCommand(sql, conn)) + Debug.WriteLine($"[数据库初始化] 开始创建数据库表"); + using (var conn = GetConnection()) { - cmd.ExecuteNonQuery(); + conn.Open(); + Debug.WriteLine($"[数据库初始化] 数据库连接已打开"); + + string sql; + try + { + // 尝试从嵌入资源读取 + sql = ResourceHelper.GetEmbeddedResource("schema.sql"); + Debug.WriteLine($"[数据库初始化] 从嵌入资源读取 schema.sql,长度: {sql?.Length ?? 0} 字符"); + } + catch (Exception ex) + { + // 如果嵌入资源读取失败,尝试从文件系统读取 + Debug.WriteLine($"[数据库初始化] 嵌入资源读取失败: {ex.Message}"); + var schemaPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data", "Resources", "schema.sql"); + Debug.WriteLine($"[数据库初始化] 尝试从文件读取: {schemaPath}"); + + if (!File.Exists(schemaPath)) + { + // 如果文件也不存在,使用硬编码的SQL + Debug.WriteLine($"[数据库初始化] schema.sql 文件不存在,使用硬编码SQL"); + sql = GetHardcodedSchema(); + } + else + { + sql = File.ReadAllText(schemaPath); + Debug.WriteLine($"[数据库初始化] 从文件读取成功,长度: {sql?.Length ?? 0} 字符"); + } + } + + using (var cmd = new SQLiteCommand(sql, conn)) + { + cmd.ExecuteNonQuery(); + Debug.WriteLine($"[数据库初始化] 数据库表创建成功"); + } } } + catch (Exception ex) + { + Debug.WriteLine($"[数据库初始化] 创建表失败: {ex.Message}"); + Debug.WriteLine($"[数据库初始化] StackTrace: {ex.StackTrace}"); + throw; + } + } + + private static string GetHardcodedSchema() + { + return @" +-- 1. 本地用户会话 +CREATE TABLE IF NOT EXISTS local_session ( + id INTEGER PRIMARY KEY, + mobile TEXT NOT NULL, + token TEXT NOT NULL, + uid INTEGER, + nickname TEXT, + enterprise_id INTEGER, + last_login_at DATETIME DEFAULT CURRENT_TIMESTAMP, + token_expires_at DATETIME +); + +-- 2. 订单缓存表 +CREATE TABLE IF NOT EXISTS orders_cache ( + id INTEGER PRIMARY KEY, + order_number TEXT NOT NULL UNIQUE, + status INTEGER NOT NULL, + amount REAL, + amount_real REAL, + uid INTEGER, + user_mobile TEXT, + logistics_name TEXT, + logistics_mobile TEXT, + logistics_province TEXT, + logistics_city TEXT, + logistics_district TEXT, + logistics_address TEXT, + goods_json TEXT, + express_company_id INTEGER, + express_company_name TEXT, + tracking_number TEXT, + date_ship DATETIME, + sync_status TEXT DEFAULT 'synced', + local_updated_at DATETIME, + date_add DATETIME, + date_pay DATETIME, + date_update DATETIME, + synced_at DATETIME +); + +-- 3. 发货队列表 +CREATE TABLE IF NOT EXISTS ship_queue ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + order_id INTEGER NOT NULL, + order_number TEXT NOT NULL, + express_company_id INTEGER NOT NULL, + tracking_number TEXT NOT NULL, + status TEXT DEFAULT 'pending', + retry_count INTEGER DEFAULT 0, + error_message TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME +); + +-- 4. 同步日志表 +CREATE TABLE IF NOT EXISTS sync_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + sync_type TEXT NOT NULL, + sync_mode TEXT, + sync_start DATETIME NOT NULL, + sync_end DATETIME, + total_count INTEGER DEFAULT 0, + new_count INTEGER DEFAULT 0, + updated_count INTEGER DEFAULT 0, + failed_count INTEGER DEFAULT 0, + status TEXT NOT NULL, + error_message TEXT +); + +-- 5. 操作日志表 +CREATE TABLE IF NOT EXISTS operation_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + operation_type TEXT NOT NULL, + target_id INTEGER, + target_number TEXT, + details TEXT, + result TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- 索引 +CREATE INDEX IF NOT EXISTS idx_orders_status ON orders_cache(status); +CREATE INDEX IF NOT EXISTS idx_orders_date_add ON orders_cache(date_add); +CREATE INDEX IF NOT EXISTS idx_orders_order_number ON orders_cache(order_number); +CREATE INDEX IF NOT EXISTS idx_ship_queue_status ON ship_queue(status); +CREATE INDEX IF NOT EXISTS idx_sync_logs_status ON sync_logs(status); +"; } public static void ExecuteNonQuery(string sql, params SQLiteParameter[] parameters) diff --git a/PackagingMallShipper/FodyWeavers.xml b/PackagingMallShipper/FodyWeavers.xml new file mode 100644 index 0000000..2af0dda --- /dev/null +++ b/PackagingMallShipper/FodyWeavers.xml @@ -0,0 +1,11 @@ + + + + + false + + false + + true + + diff --git a/PackagingMallShipper/Models/ApiResponses.cs b/PackagingMallShipper/Models/ApiResponses.cs index ffa59ab..809ec85 100644 --- a/PackagingMallShipper/Models/ApiResponses.cs +++ b/PackagingMallShipper/Models/ApiResponses.cs @@ -25,6 +25,18 @@ namespace PackagingMallShipper.Models public int Uid { get; set; } } + public class AdminLoginData + { + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("token")] + public string Token { get; set; } + + [JsonProperty("nickname")] + public string Nickname { get; set; } + } + public class UserInfo { [JsonProperty("nick")] @@ -43,18 +55,122 @@ namespace PackagingMallShipper.Models public string Token { get; set; } public string Message { get; set; } public UserInfo UserInfo { get; set; } + public bool RequireCaptcha { get; set; } // 是否需要验证码 } public class OrderListData { + // API可能返回不同的字段名 [JsonProperty("orderList")] public List OrderList { get; set; } + [JsonProperty("result")] + public List Result { get; set; } + + [JsonProperty("list")] + public List List { get; set; } + + // 物流/收件人信息列表(与订单通过id关联) + [JsonProperty("logisticses")] + public List Logisticses { get; set; } + + // 商品列表(与订单通过orderId关联) + [JsonProperty("goods")] + public List Goods { get; set; } + [JsonProperty("totalRow")] public int TotalRow { get; set; } + [JsonProperty("total")] + public int Total { get; set; } + [JsonProperty("totalPage")] public int TotalPage { get; set; } + + // 辅助方法:获取订单列表(兼容多种返回格式)并合并物流信息 + public List GetOrders() + { + var orders = Result ?? OrderList ?? List ?? new List(); + + // 合并物流信息到订单 + if (Logisticses != null && Logisticses.Count > 0) + { + var logisticsMap = new Dictionary(); + foreach (var log in Logisticses) + { + logisticsMap[log.Id] = log; + } + + foreach (var order in orders) + { + if (logisticsMap.TryGetValue(order.Id, out var logistics)) + { + order.LogisticsName = logistics.LinkMan; + order.LogisticsMobile = logistics.Mobile; + order.LogisticsProvince = logistics.ProvinceStr; + order.LogisticsCity = logistics.CityStr; + order.LogisticsDistrict = logistics.AreaStr; + order.LogisticsAddress = logistics.Address; + } + } + } + + // 合并商品信息到订单 + if (Goods != null && Goods.Count > 0) + { + var goodsMap = new Dictionary>(); + foreach (var item in Goods) + { + if (!goodsMap.ContainsKey(item.OrderId)) + goodsMap[item.OrderId] = new List(); + goodsMap[item.OrderId].Add(item); + } + + foreach (var order in orders) + { + if (goodsMap.TryGetValue(order.Id, out var items)) + { + order.Goods = items; + } + } + } + + return orders; + } + + // 辅助方法:获取总记录数 + public int GetTotalRow() + { + return TotalRow > 0 ? TotalRow : Total; + } + } + + // 物流/收件人信息 + public class LogisticsInfo + { + [JsonProperty("id")] + public int Id { get; set; } // 订单ID + + [JsonProperty("linkMan")] + public string LinkMan { get; set; } // 收件人 + + [JsonProperty("mobile")] + public string Mobile { get; set; } // 收件人电话 + + [JsonProperty("provinceStr")] + public string ProvinceStr { get; set; } + + [JsonProperty("cityStr")] + public string CityStr { get; set; } + + [JsonProperty("areaStr")] + public string AreaStr { get; set; } + + [JsonProperty("address")] + public string Address { get; set; } + + [JsonProperty("streetStr")] + public string StreetStr { get; set; } } public class OrderDto diff --git a/PackagingMallShipper/Models/Order.cs b/PackagingMallShipper/Models/Order.cs index 155ce31..10300cf 100644 --- a/PackagingMallShipper/Models/Order.cs +++ b/PackagingMallShipper/Models/Order.cs @@ -63,14 +63,29 @@ namespace PackagingMallShipper.Models public class GoodsItem { + [JsonProperty("orderId")] + public int OrderId { get; set; } + + [JsonProperty("goodsId")] + public int GoodsId { get; set; } + [JsonProperty("goodsName")] public string GoodsName { get; set; } [JsonProperty("number")] public int Number { get; set; } - [JsonProperty("price")] + [JsonProperty("amount")] + public decimal Amount { get; set; } + + [JsonProperty("amountSingle")] public decimal Price { get; set; } + + [JsonProperty("pic")] + public string Pic { get; set; } + + [JsonProperty("unit")] + public string Unit { get; set; } } public static class OrderStatus diff --git a/PackagingMallShipper/PackagingMallShipper.csproj b/PackagingMallShipper/PackagingMallShipper.csproj index d70aed1..1044175 100644 --- a/PackagingMallShipper/PackagingMallShipper.csproj +++ b/PackagingMallShipper/PackagingMallShipper.csproj @@ -1,139 +1,47 @@ - - - + + - Debug - AnyCPU - {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} WinExe + net48 PackagingMallShipper PackagingMallShipper - v4.8 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - true - true - 7.3 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - + true + false + 9.0 Resources\Icons\app.ico + false + + + 1.0.0 + 1.0.0.0 + 1.0.0.0 + PackagingMall + 包装商城发货助手 + Copyright © 2025 - - - - - - - - - - - 4.0 - - - - - - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - App.xaml - Code - - - - - - - - - - - - - - - - - - - - - - - - - - LoginWindow.xaml - - - MainWindow.xaml - - - OrderListView.xaml - - - ShippingDialog.xaml - - - - - - - - - - - - + + + + - + + + + + + + + + + + + PreserveNewest + + + diff --git a/PackagingMallShipper/Resources/Icons/app.ico b/PackagingMallShipper/Resources/Icons/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..0f1afd3b35b58f988e2722f11b9da7cc149b17c8 GIT binary patch literal 13519 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PackagingMallShipper/Services/AuthService.cs b/PackagingMallShipper/Services/AuthService.cs index dbc5b96..d735188 100644 --- a/PackagingMallShipper/Services/AuthService.cs +++ b/PackagingMallShipper/Services/AuthService.cs @@ -1,5 +1,6 @@ using System; using System.Data.SQLite; +using System.Diagnostics; using System.Net.Http; using System.Threading.Tasks; using Newtonsoft.Json; @@ -25,43 +26,74 @@ namespace PackagingMallShipper.Services public bool IsLoggedIn => !string.IsNullOrEmpty(GetToken()); - public async Task LoginAsync(string mobile, string password) + public string GetCaptchaUrl(string key) + { + return $"https://user.api.it120.cc/code?k={Uri.EscapeDataString(key)}"; + } + + public async Task LoginAsync(string userName, string password, string captcha = null, string captchaKey = null) { try { - var url = AppConfig.GetApiUrl($"/user/m/login?mobile={mobile}&pwd={password}"); + // 使用子账号登录接口 /login/userName/v2 + // 注意:该接口会优先判断手机号码登录,如果满足直接登录成功,其次才会尝试子账号登录 + // 该接口要求必须提供验证码参数 + var urlBuilder = new System.Text.StringBuilder(); + urlBuilder.Append("https://user.api.it120.cc/login/userName/v2"); + urlBuilder.Append($"?userName={Uri.EscapeDataString(userName)}"); + urlBuilder.Append($"&pwd={Uri.EscapeDataString(password)}"); + urlBuilder.Append($"&pdomain={Uri.EscapeDataString(AppConfig.SubDomain)}"); + urlBuilder.Append("&rememberMe=true"); + + // 验证码参数是必须的 + urlBuilder.Append($"&imgcode={Uri.EscapeDataString(captcha ?? "")}"); + urlBuilder.Append($"&k={Uri.EscapeDataString(captchaKey ?? "")}"); + + var url = urlBuilder.ToString(); + + // 详细日志:请求URL(隐藏密码) + var logUrl = url.Replace($"&pwd={Uri.EscapeDataString(password)}", "&pwd=***"); + System.Diagnostics.Debug.WriteLine($"[登录请求] URL: {logUrl}"); + System.Diagnostics.Debug.WriteLine($"[登录请求] 用户名: {userName}"); + System.Diagnostics.Debug.WriteLine($"[登录请求] 验证码: {captcha}"); + System.Diagnostics.Debug.WriteLine($"[登录请求] 验证码Key: {captchaKey}"); + System.Diagnostics.Debug.WriteLine($"[登录请求] 子域名: {AppConfig.SubDomain}"); var response = await _httpClient.PostAsync(url, null); var json = await response.Content.ReadAsStringAsync(); - var result = JsonConvert.DeserializeObject>(json); + + // 详细日志:API响应 + System.Diagnostics.Debug.WriteLine($"[登录响应] HTTP状态码: {response.StatusCode}"); + System.Diagnostics.Debug.WriteLine($"[登录响应] JSON原始内容: {json}"); + + var result = JsonConvert.DeserializeObject>(json); + + System.Diagnostics.Debug.WriteLine($"[登录响应] 解析结果 - Code: {result?.Code}, Msg: {result?.Msg}"); if (result?.Code != 0) { + System.Diagnostics.Debug.WriteLine($"[登录失败] 错误码: {result?.Code}, 错误信息: {result?.Msg}"); + return new LoginResult { Success = false, - Message = result?.Msg ?? "登录失败" + Message = result?.Msg ?? "登录失败", + RequireCaptcha = result?.Code == 700 // 700 表示需要验证码 }; } var token = result.Data.Token; - var uid = result.Data.Uid; + var adminId = result.Data.Id; - _httpClient.DefaultRequestHeaders.Clear(); - _httpClient.DefaultRequestHeaders.Add("X-Token", token); - - var userInfoUrl = AppConfig.GetApiUrl("/user/detail"); - var userResponse = await _httpClient.GetAsync(userInfoUrl); - var userJson = await userResponse.Content.ReadAsStringAsync(); - var userResult = JsonConvert.DeserializeObject>(userJson); + Debug.WriteLine($"[登录成功] Token: {token?.Substring(0, Math.Min(20, token?.Length ?? 0))}..., AdminId: {adminId}"); _currentSession = new LocalSession { Id = 1, - Mobile = mobile, + Mobile = userName, // 存储用户名/手机号 Token = token, - Uid = uid, - Nickname = userResult?.Data?.Nick ?? mobile, + Uid = adminId, + Nickname = result.Data.Nickname ?? userName, LastLoginAt = DateTime.Now, TokenExpiresAt = DateTime.Now.AddHours(AppConfig.TokenExpireHours) }; @@ -72,11 +104,14 @@ namespace PackagingMallShipper.Services { Success = true, Token = token, - UserInfo = userResult?.Data + UserInfo = null // 子账号登录不需要额外获取用户信息 }; } catch (Exception ex) { + Debug.WriteLine($"[登录异常] {ex.GetType().Name}: {ex.Message}"); + Debug.WriteLine($"[登录异常] StackTrace: {ex.StackTrace}"); + return new LoginResult { Success = false, diff --git a/PackagingMallShipper/Services/ExcelService.cs b/PackagingMallShipper/Services/ExcelService.cs index ca76d1e..b116165 100644 --- a/PackagingMallShipper/Services/ExcelService.cs +++ b/PackagingMallShipper/Services/ExcelService.cs @@ -49,8 +49,9 @@ namespace PackagingMallShipper.Services worksheet.Cell(row, 1).Value = order.OrderNumber; worksheet.Cell(row, 2).Value = order.DateAdd?.ToString("yyyy-MM-dd HH:mm:ss"); worksheet.Cell(row, 3).Value = order.LogisticsName; - worksheet.Cell(row, 4).Value = order.LogisticsMobile; - worksheet.Cell(row, 4).SetDataType(XLDataType.Text); + var mobileCell = worksheet.Cell(row, 4); + mobileCell.Value = order.LogisticsMobile; + mobileCell.Style.NumberFormat.Format = "@"; worksheet.Cell(row, 5).Value = order.LogisticsProvince; worksheet.Cell(row, 6).Value = order.LogisticsCity; worksheet.Cell(row, 7).Value = order.LogisticsDistrict; diff --git a/PackagingMallShipper/Services/Interfaces.cs b/PackagingMallShipper/Services/Interfaces.cs index b131ddf..04348a7 100644 --- a/PackagingMallShipper/Services/Interfaces.cs +++ b/PackagingMallShipper/Services/Interfaces.cs @@ -8,7 +8,8 @@ namespace PackagingMallShipper.Services { public interface IAuthService { - Task LoginAsync(string mobile, string password); + Task LoginAsync(string userName, string password, string captcha = null, string captchaKey = null); + string GetCaptchaUrl(string key); string GetToken(); bool IsLoggedIn { get; } LocalSession CurrentSession { get; } diff --git a/PackagingMallShipper/Services/OrderService.cs b/PackagingMallShipper/Services/OrderService.cs index 0485234..fe72a67 100644 --- a/PackagingMallShipper/Services/OrderService.cs +++ b/PackagingMallShipper/Services/OrderService.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Data.SQLite; using System.Threading.Tasks; diff --git a/PackagingMallShipper/Services/SyncService.cs b/PackagingMallShipper/Services/SyncService.cs index 210af36..89e6c4a 100644 --- a/PackagingMallShipper/Services/SyncService.cs +++ b/PackagingMallShipper/Services/SyncService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data.SQLite; +using System.Diagnostics; using System.Net.Http; using System.Threading.Tasks; using Newtonsoft.Json; @@ -57,22 +58,44 @@ namespace PackagingMallShipper.Services { OnSyncMessage?.Invoke($"正在同步第 {page}/{totalPages} 页..."); - var url = AppConfig.GetApiUrl($"/order/list?page={page}&pageSize={pageSize}"); + // 使用正确的API端点:/user/apiExtOrder/list (POST请求) + // 域名:user.api.it120.cc,路径不含subdomain + var urlBuilder = new System.Text.StringBuilder(); + urlBuilder.Append("https://user.api.it120.cc/user/apiExtOrder/list"); + urlBuilder.Append($"?page={page}&pageSize={pageSize}"); + urlBuilder.Append("&export=true"); // 获取更丰富的数据 if (lastSyncTime.HasValue) - url += $"&dateUpdateBegin={lastSyncTime:yyyy-MM-dd HH:mm:ss}"; + urlBuilder.Append($"&dateUpdateBegin={lastSyncTime:yyyy-MM-dd HH:mm:ss}"); + + var url = urlBuilder.ToString(); + + Debug.WriteLine($"[同步请求] URL: {url}"); + Debug.WriteLine($"[同步请求] Token: {token?.Substring(0, Math.Min(20, token?.Length ?? 0))}..."); _httpClient.DefaultRequestHeaders.Clear(); _httpClient.DefaultRequestHeaders.Add("X-Token", token); - var response = await _httpClient.GetAsync(url); + // 使用 POST 请求 + var response = await _httpClient.PostAsync(url, null); var json = await response.Content.ReadAsStringAsync(); + + Debug.WriteLine($"[同步响应] HTTP状态码: {response.StatusCode}"); + var data = JsonConvert.DeserializeObject>(json); if (data?.Code != 0) + { + if (data?.Msg?.Contains("token") == true || data?.Msg?.Contains("登录") == true) + throw new UnauthorizedAccessException($"当前登录token无效,请重新登录"); throw new Exception(data?.Msg ?? "获取订单失败"); + } - var orders = data.Data?.OrderList ?? new List(); + // 使用辅助方法获取订单列表(自动合并物流和商品信息) + var orders = data.Data?.GetOrders() ?? new List(); totalPages = data.Data?.TotalPage ?? 1; + var totalRow = data.Data?.GetTotalRow() ?? 0; + + Debug.WriteLine($"[同步响应] 订单数量: {orders.Count}, 总记录: {totalRow}, 总页数: {totalPages}"); if (orders.Count == 0) break; diff --git a/PackagingMallShipper/ViewModels/LoginViewModel.cs b/PackagingMallShipper/ViewModels/LoginViewModel.cs index 90a4c10..e11cde1 100644 --- a/PackagingMallShipper/ViewModels/LoginViewModel.cs +++ b/PackagingMallShipper/ViewModels/LoginViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using System.Windows; +using System.Windows.Media.Imaging; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using PackagingMallShipper.Services; @@ -10,33 +11,61 @@ namespace PackagingMallShipper.ViewModels public partial class LoginViewModel : ViewModelBase { private readonly IAuthService _authService; + private string _captchaKey = ""; [ObservableProperty] - private string _mobile = ""; + private string _userName = ""; [ObservableProperty] private string _password = ""; + [ObservableProperty] + private string _captcha = ""; + [ObservableProperty] private string _errorMessage = ""; [ObservableProperty] private bool _rememberMe = true; + [ObservableProperty] + private bool _showCaptcha = false; + + [ObservableProperty] + private BitmapImage _captchaImageSource; + public event Action OnLoginSuccess; public LoginViewModel(IAuthService authService) { _authService = authService; LoadSavedCredentials(); + + // 子账号登录必须提供验证码,默认显示 + ShowCaptcha = true; + RefreshCaptcha(); + } + + [RelayCommand] + public void RefreshCaptcha() + { + _captchaKey = Guid.NewGuid().ToString("N"); + var url = _authService.GetCaptchaUrl(_captchaKey); + + CaptchaImageSource = new BitmapImage(); + CaptchaImageSource.BeginInit(); + CaptchaImageSource.UriSource = new Uri(url); + CaptchaImageSource.CacheOption = BitmapCacheOption.None; + CaptchaImageSource.CreateOptions = BitmapCreateOptions.IgnoreImageCache; + CaptchaImageSource.EndInit(); } [RelayCommand] private async Task LoginAsync() { - if (string.IsNullOrWhiteSpace(Mobile)) + if (string.IsNullOrWhiteSpace(UserName)) { - ErrorMessage = "请输入手机号"; + ErrorMessage = "请输入用户名或手机号"; return; } @@ -46,12 +75,23 @@ namespace PackagingMallShipper.ViewModels return; } + if (ShowCaptcha && string.IsNullOrWhiteSpace(Captcha)) + { + ErrorMessage = "请输入验证码"; + return; + } + IsBusy = true; ErrorMessage = ""; try { - var result = await _authService.LoginAsync(Mobile, Password); + var result = await _authService.LoginAsync( + UserName, + Password, + ShowCaptcha ? Captcha : null, + ShowCaptcha ? _captchaKey : null + ); if (result.Success) { @@ -64,6 +104,19 @@ namespace PackagingMallShipper.ViewModels else { ErrorMessage = result.Message ?? "登录失败"; + + // 如果需要验证码,显示验证码输入 + if (result.RequireCaptcha && !ShowCaptcha) + { + ShowCaptcha = true; + RefreshCaptcha(); + } + else if (ShowCaptcha) + { + // 验证码错误,刷新验证码 + Captcha = ""; + RefreshCaptcha(); + } } } catch (Exception ex) @@ -80,10 +133,10 @@ namespace PackagingMallShipper.ViewModels { try { - var mobile = Properties.Settings.Default.SavedMobile; - if (!string.IsNullOrEmpty(mobile)) + var userName = Properties.Settings.Default.SavedMobile; + if (!string.IsNullOrEmpty(userName)) { - Mobile = mobile; + UserName = userName; RememberMe = true; } } @@ -97,7 +150,7 @@ namespace PackagingMallShipper.ViewModels { try { - Properties.Settings.Default.SavedMobile = Mobile; + Properties.Settings.Default.SavedMobile = UserName; Properties.Settings.Default.Save(); } catch diff --git a/PackagingMallShipper/Views/LoginWindow.xaml b/PackagingMallShipper/Views/LoginWindow.xaml index 1254635..97084bd 100644 --- a/PackagingMallShipper/Views/LoginWindow.xaml +++ b/PackagingMallShipper/Views/LoginWindow.xaml @@ -4,8 +4,8 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" - Title="包装商城发货助手 - 登录" - Height="400" Width="350" + Title="包装商城发货助手 - 登录" + Height="480" Width="350" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Background="#F5F5F5"> @@ -22,19 +22,46 @@ HorizontalAlignment="Center" Margin="0,0,0,30"/> - - + - + - - + + + + + + + + + + + + + + + + + diff --git a/PackagingMallShipper/Views/LoginWindow.xaml.cs b/PackagingMallShipper/Views/LoginWindow.xaml.cs index b91d7ab..f766be9 100644 --- a/PackagingMallShipper/Views/LoginWindow.xaml.cs +++ b/PackagingMallShipper/Views/LoginWindow.xaml.cs @@ -34,6 +34,11 @@ namespace PackagingMallShipper.Views } } + private void CaptchaImage_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + _viewModel.RefreshCaptchaCommand.Execute(null); + } + private void OnLoginSuccess() { OpenMainWindow(); diff --git a/PackagingMallShipper/Views/OrderListView.xaml b/PackagingMallShipper/Views/OrderListView.xaml index 048ff4b..9e87f55 100644 --- a/PackagingMallShipper/Views/OrderListView.xaml +++ b/PackagingMallShipper/Views/OrderListView.xaml @@ -77,10 +77,12 @@ Width="90" Height="30" Margin="0,0,5,0" IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBool}}"/> -