using _29WxOrderWpf.CefHelper;
using _29WxOrderWpf.Commands;
using AllBase;
using AllBase.Helper;
using AllBase.Model.DBModel;
using AllBase.Model.MvvmBaseClasses;
using CefSharp;
using CefSharp.Handler;
using CefSharp.Wpf;
using NaiBa.Util.Extension;
using Org.BouncyCastle.Asn1.Ocsp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Security.Policy;
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
{
///
/// 拼多多买手移动版登录窗口 视图模型(WPF 版 TaobaoLoginForm)
///
public class TaobaoLoginViewModel : PropertyNotifyObject
{
// 拼多多买手退货退款售后地址
// https://mobile.yangkeduo.com/complaint.html?order_sn=260718-101481213440980&after_sales_reason=0&after_sales_type=2&after_sales_code=1002
List cefSubProcesses = new List();
public static TaobaoLoginViewModel TaobaoLoginInstance { get; set; } = null;
/// 浏览器创建完成后触发(视图在此把浏览器加入可视树)
public event Action BrowserCreated;
/// 请求关闭窗口
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 = "taobao.com";
private string buyerLoginUrl = "https://login.taobao.com/havanaone/login/login.htm";
private string buyerMainUrl = "https://i.taobao.com/my_itaobao"; //"https://buyertrade.taobao.com/trade/itemlist/list_bought_items.htm?";
public ChromiumWebBrowser CefWebBrowser { get; private set; }
private BuyerLoginInfo buyer = null;
private List lstShopCompany = new List();
private string currentUrl = "";
private volatile bool _isWindowLoaded = false;
#region 界面绑定属性
/// 用户名
public string UserName
{
get { return this.GetValue(x => x.UserName); }
set { this.SetValue(x => x.UserName, value); }
}
/// 密码
public string PassWord
{
get { return this.GetValue(x => x.PassWord); }
set { this.SetValue(x => x.PassWord, value); }
}
/// 地址栏
public string Address
{
get { return this.GetValue(x => x.Address); }
set { this.SetValue(x => x.Address, value); }
}
/// 登录成功后关闭窗口
public bool IsCloseAfterLogin
{
get { return this.GetValue(x => x.IsCloseAfterLogin); }
set { this.SetValue(x => x.IsCloseAfterLogin, value); }
}
/// 登录模式
public bool IsLoginMode
{
get { return this.GetValue(x => x.IsLoginMode); }
set { this.SetValue(x => x.IsLoginMode, value); }
}
#endregion
#region 命令
public ICommand ReloadCommand { get; }
public ICommand LoginAgainCommand { get; }
public ICommand StopCommand { get; }
public ICommand CopyUserNameCommand { get; }
public ICommand CopyPasswordCommand { get; }
#endregion
public TaobaoLoginViewModel(BuyerLoginInfo buyerInfo)
{
TaobaoLoginInstance = 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);
}
///
/// 窗口加载完成时调用
///
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();
});
}
///
/// 窗口关闭时调用,停止后台任务
///
public void Stop()
{
_isWindowLoaded = false;
// 1. 解除 JS 绑定
CefWebBrowser?.JavascriptObjectRepository?.UnRegisterAll();
CefWebBrowser?.CloseDevTools();
// 2. 优雅关闭浏览器
CefWebBrowser?.GetBrowser()?.CloseBrowser(false);
// 3. 清理残留子进程(按路径判断,避免误杀)
CleanupBrowserSubprocesses(this.cefSubProcesses);
// 4. 释放控件
CefWebBrowser?.Dispose();
CefWebBrowser = null;
}
public int GetRenderProcessId()
{
int parentPid = Process.GetCurrentProcess().Id;
// 查找所有名为 CefSharp.BrowserSubprocess 且父进程为当前应用的进程
var subprocesses = Process.GetProcessesByName("CefSharp.BrowserSubprocess")
.Where(p =>
{
try
{
return p.SessionId == Process.GetCurrentProcess().SessionId && GetParentProcessId(p.Id) == parentPid;
}
catch { return false; }
});
// 返回第一个匹配的 Render 进程 ID,实际可能有多个
return subprocesses.FirstOrDefault()?.Id ?? -1;
}
// 辅助方法:获取父进程 ID (需引用 System.Management 或使用 P/Invoke)
private static int GetParentProcessId(int processId)
{
using (var query = new System.Management.ManagementObjectSearcher(
$"SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {processId}"))
{
using (var results = query.Get())
{
foreach (System.Management.ManagementObject obj in results)
{
return Convert.ToInt32(obj["ParentProcessId"]);
}
}
}
return -1;
}
public static void CleanupBrowserSubprocesses(List processes = null)
{
// 有可能释放错误导致程序崩溃,废弃
return;
try
{
if (processes == null)
processes = new List();
string appBasePath = AppDomain.CurrentDomain.BaseDirectory.TrimEnd('\\', '/');
Process[] subprocesses = Process.GetProcessesByName("CefSharp.BrowserSubprocess");
foreach (Process process in subprocesses)
{
bool shouldKill = false;
try
{
// 安全地获取进程模块信息,进程可能在我们检查时已退出
if (process != null && !process.HasExited)
{
string moduleFileName = process.MainModule?.FileName;
if (!string.IsNullOrEmpty(moduleFileName))
{
// 判断进程是否从当前应用目录启动
string processDir = System.IO.Path.GetDirectoryName(moduleFileName);
if (!string.IsNullOrEmpty(processDir) &&
(processDir.Equals(appBasePath, StringComparison.OrdinalIgnoreCase)
|| appBasePath.Contains(processDir) // 处理子目录情况
|| moduleFileName.Contains(appBasePath)))
{
if (!processes.Any(p => p.Handle.ParseToInt() == process.Handle.ParseToInt()))
shouldKill = true;
}
}
}
}
catch (InvalidOperationException)
{
// 进程可能在检查过程中已退出,忽略
continue;
}
catch (Win32Exception ex) when (ex.NativeErrorCode == 5) // 访问被拒绝
{
// 可能是权限问题或其他应用的进程,跳过
continue;
}
if (shouldKill)
{
try
{
process.Kill();
process.WaitForExit(500); // 等待最多500毫秒,确保进程终止
}
catch (Exception killEx)
{
// 记录日志,但通常进程已死
//System.Diagnostics.Debug.WriteLine($"终止进程时出错: {killEx.Message}");
}
}
}
}
catch (Exception ex)
{
// 这里最好不要用MessageBox,尤其是在关闭流程中,可能引发线程问题
// 建议记录到日志文件
//System.Diagnostics.Debug.WriteLine($"清理子进程时发生异常: {ex}");
}
}
///
/// 自动登录店铺后台
///
private async void AutoLogin()
{
if (OpenType != EnumOpenType.login && buyer == null)
return;
_ = Application.Current.Dispatcher.InvokeAsync(async () =>
{
if (CefWebBrowser == null)
{
this.cefSubProcesses = Process.GetProcessesByName("CefSharp.BrowserSubprocess").ToList();
if (!string.IsNullOrEmpty(InitUrl))
CefWebBrowser = WPFCEFBrowser.CreateChromiumWebBrowser(InitUrl, false);
else
CefWebBrowser = WPFCEFBrowser.CreateChromiumWebBrowser(buyerMainUrl, 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 KeyboardHandler();
CefWebBrowser.KeyboardHandler = new CefKeyboardHandler();
CefWebBrowser.FrameLoadEnd += CefWebBrowser_FrameLoadEnd;
CefWebBrowser.AddressChanged += CefWebBrowser_AddressChanged;
CefWebBrowser.LoadError += CefWebBrowser_LoadError;
// 通知视图把浏览器加入可视树(必须在等待浏览器初始化之前完成)
BrowserCreated?.Invoke(CefWebBrowser);
List cookieLst = new List();
try
{
if (buyer != null)
{
cookieLst = JsonHelper.DeserializeJsonToList(buyer?.cookie);
if (cookieLst == null)
cookieLst = new List();
}
}
catch
{
}
// cef138必须init后方可设置或删除cookie
while (!CefWebBrowser.IsBrowserInitialized || !CefWebBrowser.GetBrowser().MainFrame.IsValid)
await Task.Delay(20);
//await WPFCEFBrowser.DeleteCookieAsync(CefWebBrowser.GetCookieManager(null));
//cef 138 如果没有设置RequestContext 可能为空,直接getCookieManager
//await WPFCEFBrowser.SetCookieAsync(JsonHelper.SerializeObject(cookieLst), CefWebBrowser.GetCookieManager(null));
if (OpenType != EnumOpenType.login)
foreach (var cook in cookieLst)
{
await WPFCEFBrowser.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(buyerMainUrl);
//不是登录的话就此返回
if (OpenType != EnumOpenType.login)
{
return;
}
for (int i = 0; i < 50; i++)
{
if (CefWebBrowser.IsLoading)
await Task.Delay(100);
}
await Task.Delay(Randomx.GetId(1500, 2000));
if (!_isWindowLoaded)
return;
while (!CefWebBrowser.Address?.StartsWith(buyerLoginUrl) ?? 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 async void RequestHandler_ResourceLoadComplete(object sender, string url, string postData, string responseData, IRequest request)
{
await DoResourceLoadComplete(url, responseData);
}
private async Task DoResourceLoadComplete(string url, string responseData)
{
//if (url.Contains(buyerLoginUrl) && OpenType == EnumOpenType.login)
//{
// Task.Run(() => { CefWebBrowser.Load(buyerLoginUrl); });
//}
var data = responseData;
//if (data.Contains("karl_malone_32") || data.Contains("jtt_fan"))
{
//https://h5api.m.taobao.com/h5/mtop.user.getusersimple/1.0/?jsv=2.7.2&appKey=12574478&t=1789984970076&sign=6bb7d3b5cd8bd3ab32bd97c1a0df1c0f&jsonpIncPrefix=tbnavnew&api=mtop.user.getUserSimple&v=1.0&dataType=json&type=originaljson&data=%7B%7D
//{"api":"mtop.user.getUserSimple","v":"1.0","ret":["SUCCESS::调用成功"],"data":{"nick":"jtt_fan","userNumId":440631048,"displayNick":"jtt_fan"}}
}
if (url.Contains(buyerMainUrl) || (data.Contains("\"displayNick\"") && data.Contains("\"nick\"") && data.Contains("\"userNumId\"")))
{
if (OpenType == EnumOpenType.login)
{
await Task.Run(async () =>
{
if (data.Contains("\"ret\":[\"SUCCESS::调用成功\"]"))
{
var uid = BaseRegex.GetStringJson(ref data, "userNumId");
var nickname = BaseRegex.GetStringJson(ref data, "displayNick");
var username = nickname;
if (!string.IsNullOrEmpty(nickname) && !string.IsNullOrEmpty(uid) && !isAddOrUpdateBuyer)
{
isAddOrUpdateBuyer = true;
try
{
AddOrUpdateBuyer(uid, username, nickname);
}
catch
{
}
isAddOrUpdateBuyer = false;
}
}
});
}
}
}
bool isAddOrUpdateBuyer = false;
void AddOrUpdateBuyer(string uid, string username, string nickname)
{
Thread.Sleep(2000);
//List cookieList = WPFCEFBrowser.GetCookieList(shopCookieHead, CefWebBrowser.GetCookieManager(null));
List cookieList = WPFCEFBrowser.GetCookieList("", CefWebBrowser.GetCookieManager(null));
var buyerUrl = BaseClass.Build29WxObjString("user/add_taobao_buyer");
if (this.buyer == null || this.buyer.buyer_id != uid)
this.buyer = GlobalConfig.TaobaoBuyerLoginList.FirstOrDefault(b => b.buyer_id == uid);
if (this.buyer != null)
{
this.buyer.status = "在线";
this.buyer.user_name = username;
this.buyer.buyer_nick = nickname;
this.buyer.cookie = JsonHelper.SerializeObject(cookieList);
buyerUrl = BaseClass.Build29WxObjString("user/mdy_taobao_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.Build29WxObjString("user/mdy_taobao_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(buyerMainUrl);
}
}
private void LoginAgain()
{
if (this.currentUrl != buyerLoginUrl)
this.CefWebBrowser?.Load(buyerLoginUrl);
}
#region 静态方法(检测/更新买手登录状态)
public static void CheckBuyerLogin(List checkBuyers, bool isShowProgress = true)
{
for (int i = 0; i < checkBuyers.Count; i++)
{
var buyer = GlobalConfig.TaobaoBuyerLoginList.FirstOrDefault(v => v.buyer_id == checkBuyers[i].buyer_id);
var buys = GlobalConfig.TaobaoBuyerLoginList.Where(v => v.buyer_id == checkBuyers[i].buyer_id).ToList();
if (buyer == null)
continue;
// 先从服务端取最新cookie
var url = BaseClass.Build29WxObjString("user/get_taobao_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 loginInfoLst = JsonHelper.DeserializeJsonToList(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)
{
return;
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.Build29WxObjString("user/mdy_taobao_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;
}
///
/// 设置淘宝店铺掉线
///
///
///
///
public static string UpdateServerBuyerDisconnect(BuyerLoginInfo buyer, string fromTool = null)
{
//buyer.status = "掉线";
//buyer.cookie = "[]";
//var url = BaseClass.Build29WxObjString("user/mdy_taobao_buyer_cookie");
//var data = UserLogin.GetSignaturex()
// + "&status=" + "掉线"
// + "&cookie=" + "[]"
// + "&buyer_id=" + buyer.buyer_id;
//var result = GetHtml.PostUtf8(url, data);
//if (!result.Contains("\"code\":200"))
// result = GetHtml.PostUtf8(url, data);
//return result;
return "";
}
#endregion
}
internal class CefKeyboardHandler : IKeyboardHandler
{
public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey)
{
if (type == KeyType.KeyUp && Enum.IsDefined(typeof(System.Windows.Forms.Keys), windowsKeyCode))
{
var key = (System.Windows.Forms.Keys)windowsKeyCode;
switch (key)
{
case System.Windows.Forms.Keys.F12:
browser.ShowDevTools();
break;
case System.Windows.Forms.Keys.F5:
if (modifiers == CefEventFlags.ControlDown)
{
//MessageBox.Show("ctrl+f5");
browser.Reload(true); //强制忽略缓存
}
else
{
//MessageBox.Show("f5");
browser.Reload();
}
break;
}
}
return false;
}
public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut)
{
return false;
}
}
}