Files
29WxOrder/29WxOrderWpf/ViewModels/PddMobileLoginViewModel.cs
T
2026-09-24 10:29:44 +08:00

543 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using _29WxOrderWpf.CefHelper;
using _29WxOrderWpf.Commands;
using AllBase;
using AllBase.Helper;
using AllBase.Model.DBModel;
using AllBase.Model.MvvmBaseClasses;
using AllBase.Model.Result;
using CefSharp;
using CefSharp.Handler;
using CefSharp.Wpf;
using NaiBa.Util.Extension;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using TemuKJD.ToolForms;
using Work;
using Work._29Tools.BLL;
using Work._29Tools.CefTools;
namespace _29WxOrderWpf.ViewModels
{
/// <summary>
/// 拼多多买手移动版登录窗口 视图模型(WPF 版 PddMobileLoginForm)
/// </summary>
public class PddMobileLoginViewModel : PropertyNotifyObject
{
// 拼多多买手退货退款售后地址
// https://mobile.yangkeduo.com/complaint.html?order_sn=260718-101481213440980&after_sales_reason=0&after_sales_type=2&after_sales_code=1002
Dictionary<BuyerLoginInfo, PddPickInfo> dicPddPickInfo = new Dictionary<BuyerLoginInfo, PddPickInfo>();
public static PddMobileLoginViewModel PddMobileLoginInstance { get; set; } = null;
/// <summary>浏览器创建完成后触发(视图在此把浏览器加入可视树)</summary>
public event Action<ChromiumWebBrowser> BrowserCreated;
/// <summary>请求关闭窗口</summary>
public event Action RequestClose;
public int CloseAfterSecond { get; set; }
private EnumOpenType _openType = EnumOpenType.manage;
public EnumOpenType OpenType
{
get { return _openType; }
set
{
_openType = value;
OnPropertyChanged(nameof(OpenType));
OnPropertyChanged(nameof(IsLoginMode));
}
}
public string InitUrl { get; set; } = "";
private string shopCookieHead = "yangkeduo.com";
private string shopLoginUrl = "https://mobile.yangkeduo.com/login.html?from=https%3A%2F%2Fmobile.yangkeduo.com%2Fpersonal.html";
private string shopMainUrl = "https://mobile.yangkeduo.com/index.html";
public ChromiumWebBrowser CefWebBrowser { get; private set; }
private BuyerLoginInfo buyer = null;
private List<ShopCompany> lstShopCompany = new List<ShopCompany>();
private string currentUrl = "";
private volatile bool _isWindowLoaded = false;
#region 界面绑定属性
/// <summary>用户名</summary>
public string UserName
{
get { return this.GetValue(x => x.UserName); }
set { this.SetValue(x => x.UserName, value); }
}
/// <summary>密码</summary>
public string PassWord
{
get { return this.GetValue(x => x.PassWord); }
set { this.SetValue(x => x.PassWord, value); }
}
/// <summary>地址栏</summary>
public string Address
{
get { return this.GetValue(x => x.Address); }
set { this.SetValue(x => x.Address, value); }
}
/// <summary>登录成功后关闭窗口</summary>
public bool IsCloseAfterLogin
{
get { return this.GetValue(x => x.IsCloseAfterLogin); }
set { this.SetValue(x => x.IsCloseAfterLogin, value); }
}
/// <summary>登录模式(显示扫码提示和导入PDDAccessToken面板)</summary>
public bool IsLoginMode
{
get { return this.GetValue(x => x.IsLoginMode); }
set { this.SetValue(x => x.IsLoginMode, value); }
}
/// <summary>导入的PDDAccessToken</summary>
public string AccessToken
{
get { return this.GetValue(x => x.AccessToken); }
set { this.SetValue(x => x.AccessToken, value); }
}
#endregion
#region 命令
public ICommand ReloadCommand { get; }
public ICommand LoginAgainCommand { get; }
public ICommand StopCommand { get; }
public ICommand CopyUserNameCommand { get; }
public ICommand CopyPasswordCommand { get; }
public ICommand SaveCookieCommand { get; }
#endregion
public PddMobileLoginViewModel(BuyerLoginInfo buyerInfo)
{
PddMobileLoginInstance = this;
buyer = buyerInfo;
UserName = buyerInfo?.buyer_name;
PassWord = ""; //DataOpenSSL.DecryptString(buyerInfo.Password);
IsCloseAfterLogin = true;
IsLoginMode = OpenType == EnumOpenType.login;
ReloadCommand = new DelegateCommand(Reload);
LoginAgainCommand = new DelegateCommand(LoginAgain);
StopCommand = new DelegateCommand(StopButton);
CopyUserNameCommand = new DelegateCommand(CopyUserName);
CopyPasswordCommand = new DelegateCommand(CopyPassword);
SaveCookieCommand = new DelegateCommand(SaveCookie);
}
/// <summary>
/// 窗口加载完成时调用
/// </summary>
public void Start()
{
IsLoginMode = OpenType == EnumOpenType.login;
_isWindowLoaded = true;
if (CloseAfterSecond > 0)
{
Task.Run(() =>
{
Thread.Sleep(1000 * CloseAfterSecond);
if (_isWindowLoaded)
{
Application.Current?.Dispatcher.Invoke(new Action(() =>
{
if (_isWindowLoaded)
RequestClose?.Invoke();
}));
}
});
}
Task.Run(() =>
{
AutoLogin();
});
}
/// <summary>
/// 窗口关闭时调用,停止后台任务
/// </summary>
public void Stop()
{
_isWindowLoaded = false;
}
/// <summary>
/// 自动登录店铺后台
/// </summary>
private async void AutoLogin()
{
if (OpenType != EnumOpenType.login && buyer == null)
return;
_ = Application.Current.Dispatcher.InvokeAsync(async () =>
{
if (CefWebBrowser == null)
{
if (!string.IsNullOrEmpty(InitUrl))
CefWebBrowser = WPFCEFBrowser.CreateChromiumWebBrowser(InitUrl, false);
else
CefWebBrowser = WPFCEFBrowser.CreateChromiumWebBrowser(shopMainUrl, false);
LocalResourceRequestHandler resourceRequestHandler = new LocalResourceRequestHandler();
resourceRequestHandler.ResourceLoadCompleteEvent += RequestHandler_ResourceLoadComplete;
resourceRequestHandler.BeforeResourceLoadEvent += RequestHandler_BeforeResourceLoad;
CefWebBrowser.RequestHandler = new LocalRequestHandler(resourceRequestHandler);
var lifeSapnHandler = new LocalLifeSpanHandler(PopWindowType.SelfWindow);
CefWebBrowser.LifeSpanHandler = lifeSapnHandler;
CefWebBrowser.KeyboardHandler = new CefKeyboardHandler();
CefWebBrowser.FrameLoadEnd += CefWebBrowser_FrameLoadEnd;
CefWebBrowser.AddressChanged += CefWebBrowser_AddressChanged;
CefWebBrowser.LoadError += CefWebBrowser_LoadError;
// 通知视图把浏览器加入可视树(必须在等待浏览器初始化之前完成)
BrowserCreated?.Invoke(CefWebBrowser);
List<Cookie> cookieLst = new List<Cookie>();
try
{
if (buyer != null)
{
cookieLst = JsonHelper.DeserializeJsonToList<CefSharp.Cookie>(buyer?.cookie);
if (cookieLst == null)
cookieLst = new List<Cookie>();
}
}
catch
{
}
// cef138必须init后方可设置或删除cookie
while (!CefWebBrowser.IsBrowserInitialized || !CefWebBrowser.GetBrowser().MainFrame.IsValid)
await Task.Delay(20);
WPFCEFBrowser.DeleteCookie(CefWebBrowser.GetCookieManager(null));
//cef 138 如果没有设置RequestContext 可能为空,直接getCookieManager
var t = WPFCEFBrowser.SetCookieAsync(JsonHelper.SerializeObject(cookieLst), CefWebBrowser.GetCookieManager(null));
foreach (var cook in cookieLst)
{
//var t = CEFBrowser.SetCookieAsync(cook.Domain.Trim('.'), cook.Name + "=" + cook.Value + ";", CefWebBrowser.GetCookieManager(null));
//t.Wait(5000);
}
//根据登录的具体店铺设置local storage
if (!string.IsNullOrEmpty(buyer?.buyer_id))
{
//var storageJsCode = "window.localStorage.setItem('" + "mall-info-id" + "', '" + shop.ShopId + "');\r\n";
//storageJsCode += "window.localStorage.setItem('" + "agentseller-mall-info-id" + "', '" + shop.ShopId + "');\r\n";
//while (!CefWebBrowser.IsBrowserInitialized || !CefWebBrowser.GetBrowser().MainFrame.IsValid)
// Thread.Sleep(20);
//CefWebBrowser.GetBrowser().MainFrame.ExecuteJavaScriptAsync(storageJsCode);
}
if (!string.IsNullOrEmpty(InitUrl))
CefWebBrowser.Load(InitUrl);
else
CefWebBrowser.Load(shopMainUrl);
//不是登录的话就此返回
if (OpenType != EnumOpenType.login)
{
return;
}
for (int i = 0; i < 50; i++)
{
if (CefWebBrowser.IsLoading)
await Task.Delay(100);
//Thread.Sleep(100);
}
await Task.Delay(Randomx.GetId(1500, 2000));
if (!_isWindowLoaded)
return;
while (!CefWebBrowser.Address?.StartsWith(shopLoginUrl) ?? false)
{
await Task.Delay(1000);
if (!_isWindowLoaded)
return;
}
}
});
}
private void CefWebBrowser_LoadError(object sender, CefSharp.LoadErrorEventArgs e)
{
}
private void CefWebBrowser_AddressChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (Application.Current?.Dispatcher != null)
{
Application.Current.Dispatcher.Invoke(new Action(() =>
{
currentUrl = e.NewValue.ParseToString();
Address = e.NewValue.ParseToString();
}));
}
}
private void CefWebBrowser_FrameLoadEnd(object sender, CefSharp.FrameLoadEndEventArgs e)
{
//js绑定C#对象 html页面内声明绑定的C#对象
StringBuilder sb = new StringBuilder();
sb.Append("(function(){");
sb.Append("CefSharp.BindObjectAsync('shopObj');");
sb.Append("})();");
CefWebBrowser.GetBrowser().MainFrame.EvaluateScriptAsync(sb.ToString());
}
private bool RequestHandler_BeforeResourceLoad(object sender, string url, string postData, IRequest request)
{
return false;
}
private void RequestHandler_ResourceLoadComplete(object sender, string url, string postData, string responseData, IRequest request)
{
var data = responseData;
var send = sender;
if (data.Contains("\"pick_code\":\"取件码") /*|| data.Contains("21-2110")*/)
{
try
{
var combine_track_list = BaseRegex.GetJsonArrStrJson(ref data, "combine_track_list");
var noPickupPackageData = BaseRegex.GetJsonObjStrJson(ref data, "noPickupPackageData").FirstOrDefault();
if (!noPickupPackageData.IsNullOrEmpty() && send is ChromiumWebBrowser cef)
{
var buyer = GlobalConfig.PDDBuyerLoginList.FirstOrDefault(buy => buy.wpf_browser == cef);
if (buyer != null)
{
dicPddPickInfo[buyer] = JsonHelper.DeserializeJsonToObject<PddPickInfo>(noPickupPackageData);
}
}
}
catch
{
}
}
if (url.Contains("https://mobile.yangkeduo.com/index.html") && OpenType == EnumOpenType.login)
{
Task.Run(() => { CefWebBrowser.Load("https://mobile.yangkeduo.com/personal.html"); });
}
if (url.Contains("https://mobile.yangkeduo.com/personal.html"))
{
if (OpenType == EnumOpenType.login)
{
Task.Run(() =>
{
var data = responseData;
if (data.Contains("\"success\":true") || data.Contains("nickname"))
{
var uid = BaseRegex.GetStringJson(ref data, "uid");
var nickname = BaseRegex.GetStringJson(ref data, "nickname");
var username = nickname;
if (!string.IsNullOrEmpty(nickname) && !string.IsNullOrEmpty(uid))
{
Thread.Sleep(2000);
List<Cookie> cookieList = WPFCEFBrowser.GetCookieList(shopCookieHead, CefWebBrowser.GetCookieManager(null));
var buyerUrl = BaseClass.Build29PDDObjString("user/add_buyer");
if (this.buyer == null || this.buyer.buyer_id != uid)
this.buyer = GlobalConfig.PDDBuyerLoginList.FirstOrDefault(b => b.buyer_id == uid);
if (this.buyer != null)
{
this.buyer.user_name = username;
this.buyer.buyer_nick = nickname;
this.buyer.cookie = JsonHelper.SerializeObject(cookieList);
buyerUrl = BaseClass.Build29PDDObjString("user/mdy_buyer");
}
var buyerData = UserLogin.GetSignaturex()
+ "&buyer_id=" + uid
+ "&buyer_name=" + username
+ "&buyer_nick=" + nickname;
var result = GetHtml.PostUtf8(buyerUrl, buyerData);
if (string.IsNullOrEmpty(result) || !result.Contains("\"code\":200"))
result = GetHtml.PostUtf8(buyerUrl, buyerData);
buyerUrl = BaseClass.Build29PDDObjString("user/mdy_buyer_cookie");
buyerData = UserLogin.GetSignaturex()
+ "&buyer_id=" + uid
+ "&status=" + "在线"
+ "&cookie=" + GetHtml.UrlEncodeUtf8(JsonHelper.SerializeObject(cookieList));
result = GetHtml.PostUtf8(buyerUrl, buyerData);
if (string.IsNullOrEmpty(result) || !result.Contains("\"code\":200"))
result = GetHtml.PostUtf8(buyerUrl, buyerData);
Application.Current?.Dispatcher.Invoke(new Action(() =>
{
try
{
if (IsCloseAfterLogin)
RequestClose?.Invoke();
}
catch (Exception ex)
{ }
}));
}
}
});
}
}
}
private void StopButton()
{
}
private void CopyUserName()
{
if (!string.IsNullOrEmpty(UserName))
Clipboard.SetDataObject(UserName);
}
private void CopyPassword()
{
if (!string.IsNullOrEmpty(PassWord))
Clipboard.SetDataObject(PassWord);
}
private void Reload()
{
if (CefWebBrowser != null)
{
if (Address != "about:blank")
CefWebBrowser.Load(Address);
else
CefWebBrowser.Load(shopMainUrl);
}
}
private void LoginAgain()
{
if (this.currentUrl != shopLoginUrl)
this.CefWebBrowser?.Load(shopLoginUrl);
}
private void SaveCookie()
{
Cookie cookie = new Cookie();
cookie.Name = "PDDAccessToken";
cookie.Domain = "mobile.yangkeduo.com";
cookie.Expires = DateTime.Now.AddDays(465);
cookie.HttpOnly = true;
cookie.Secure = true;
cookie.Value = AccessToken.Trim();
if (this.CefWebBrowser != null)
{
var t = WPFCEFBrowser.SetCookieAsync(JsonHelper.SerializeObject(new List<Cookie> { cookie }), CefWebBrowser.GetCookieManager(null));
t.Wait(2000);
CefWebBrowser.Load("https://mobile.yangkeduo.com/personal.html");
}
}
#region 静态方法(检测/更新买手登录状态)
public static void CheckBuyerLogin(List<BuyerLoginInfo> checkBuyers, bool isShowProgress = true)
{
for (int i = 0; i < checkBuyers.Count; i++)
{
var buyer = GlobalConfig.PDDBuyerLoginList.FirstOrDefault(v => v.buyer_id == checkBuyers[i].buyer_id);
var buys = GlobalConfig.PDDBuyerLoginList.Where(v => v.buyer_id == checkBuyers[i].buyer_id).ToList();
if (buyer == null)
continue;
// 先从服务端取最新cookie
var url = BaseClass.Build29PDDObjString("user/get_buyer");
var data = UserLogin.GetSignaturex();
data += "&buyer_ids=" + buyer.buyer_id;
data += "&page_size=1";
var result = GetHtml.PostUtf8(url, data);
if (string.IsNullOrEmpty(result) || !result.Contains("\"code\":200"))
result = GetHtml.PostUtf8(url, data);
if (!result.Contains("\"code\":200"))
continue;
List<BuyerLoginInfo> loginInfoLst = JsonHelper.DeserializeJsonToList<BuyerLoginInfo>(JsonHelper.JObjectParse(result)["data"].ParseToString());
var serverBuyer = loginInfoLst.FirstOrDefault();
if (buyer != null && serverBuyer != null)
{
buyer.cookie = serverBuyer.cookie;
foreach (var buy in buys)
{
buy.cookie = serverBuyer.cookie;
}
}
if (buyer != null)
{
var loginResult = CheckBuyerLogin(buyer);
if (!loginResult.Contains("\"success\":true") && !loginResult.Contains("\"login\":true") && !loginResult.Contains("\"nickname\""))
{
buyer.cookie = "[]";
foreach (var buy in buys)
{
buy.status = "掉线";
buy.cookie = "[]";
}
}
else
{
foreach (var buy in buys)
{
buy.status = "在线";
}
}
UpdateServerBuyerCookies(buyer); // 店铺cookie和状态写入服务端
}
}
}
public static string CheckBuyerLogin(BuyerLoginInfo buyer, string refere = "https://mobile.yangkeduo.com/goods.html")
{
string url = "https://mobile.yangkeduo.com/proxy/api/api/apollo/v3/user/me?pdduid=" + buyer.buyer_id;
return HttpResponseYkdBLL.YangkeduoGetResponse(buyer, url, refere);
}
public static string UpdateServerBuyerCookies(BuyerLoginInfo buyer, string fromTool = null)
{
var url = BaseClass.Build29PDDObjString("user/mdy_buyer_cookie");
var data = UserLogin.GetSignaturex()
+ "&status=" + buyer.status
+ "&cookie=" + GetHtml.UrlEncodeUtf8(buyer.cookie)
+ "&buyer_id=" + buyer.buyer_id;
var result = GetHtml.PostUtf8(url, data);
if (!result.Contains("\"code\":200"))
result = GetHtml.PostUtf8(url, data);
return result;
}
#endregion
}
}