using log4net; using CounsellorBL.Helper; using MonumentDefine; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OT.COM.ArsenalDB; using SoldierData.EnterprizeV4; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using static CounsellorBL.GROUP.ConstDefinition.GrpBLWording; using static MonumentDefine.Enums; using System.Text; namespace CounsellorBL.GROUP.Helper { /// /// 類別名稱:Mailer /// 類別說明: /// 起始作者: /// 起始日期: /// 最新修改人: /// 最新修改日: /// public class FbHelper : DBService { private static ILog _inst = null; private static ILog Logger { get { if (_inst == null) { _inst = LogManager.GetLogger(typeof(FbHelper)); } return _inst; } } public override string MainTable => null; #region 類別 /// /// Fb留言資料 /// private class FbCommentData { public List data { get; set; } public JObject paging { get; set; } } private class data { public DateTime created_time { get; set; } public from from { get; set; } public string message { get; set; } public string id { get; set; } public parent parent { get; set; } public int comment_count { get; set; } } private class from { public string name { get; set; } public string id { get; set; } public Picture picture { get; set; } public class Picture { public PictureData data { get; set; } public class PictureData { public string url { get; set; } } } } private class parent { public string id { get; set; } } /// /// Fb 延長token /// private class FbToken { public string access_token { get; set; } public string type { get; set; } } /// /// Fb token 驗證 /// public class FbTokenVerification { public VerificationContent data { get; set; } } public class VerificationContent { public string app_id { get; set; } public string type { get; set; } public int data_access_expires_at { get; set; } //public error error { get; set; } public int expires_at { get; set; } public bool is_valid { get; set; } public string user_id { get; set; } } private class error { public int code { get; set; } public string message { get; set; } public int subcode { get; set; } } /// /// FB API get token response information /// public class FbGetTokenEntity { public string access_token { get; set; } public string token_type { get; set; } public string expires_in { get; set; } } public class FbGroupEntity { public string name { get; set; } public string id { get; set; } public string privacy { get; set; } } APIURLHelper ApiUrlHelper = new APIURLHelper(); /// /// fb 貼文 /// public class FbGetPost { public List data { get; set; } public JObject paging { get; set; } } public class FbGetPostdata { public string id { get; set; } public string message { get; set; } public string full_picture { get; set; } public string source { get; set; } public DateTime updated_time { get; set; } public DateTime created_time { get; set; } } public class PushPost { public string id { get; set; } } #endregion public enum CommentStatus { /// /// 無問題 (綠) /// OK = 1, /// /// 有問題【無數量】【無分店】【無產品】【數字有更新】 (紅) /// InvalidFormat = 2, /// /// 會員無登入系統 (橘) /// UnknownMember = 3, /// /// 黑名單或其他回覆的留言 (黑) /// Duplicate = 4, /// /// 已轉成訂單,不顯示其中 /// TransferOrder = 9 } class CommentModel { public string uid { get; set; } public string group_user_id { get; set; } public string comment { get; set; } public DateTime comment_time { get; set; } } #region FbAPI // 針對應用程式 可以使用{應用程式ID}|{應用程式密鑰} /// /// 上傳貼文的POST /// /// /// /// 圖片路徑 public string CallFbPostPushAPI(string authorizationToken, string message, string groupId, List path, out string o_sPostFBID) { string sMsg = null; string sPostFBID = null; Logger.Info($"CallFbPostPushAPI start groupId={groupId}"); if(path != null) { path.ForEach(p => { Logger.Info($"CallFbPostPushAPI path={p}"); }); } try { do { // 送出資料 string uri = string.Format("https://graph.facebook.com/v8.0/{0}/feed", groupId); var lsData = new List() { new APIHelper.DataContent { Key = "access_token" , Type = typeof(StringContent) , Content = authorizationToken }, new APIHelper.DataContent { Key = "message", Type = typeof(StringContent), Content = message }, new APIHelper.DataContent { Key = "published", Type = typeof(StringContent), Content = "false" } }; Logger.Info($"CallFbPostPushAPI f2 uri={uri}"); if (path != null && path.Any()) { int i = 0; path.ForEach(x => { if (x != null) { lsData.Add(new APIHelper.DataContent { Key = $"attached_media[{i}]", Type = typeof(StringContent), Content = "{\"media_fbid\":" + x + "}" }); } i++; }); } Logger.Info($"CallFbPostPushAPI f3 uri={uri}"); sMsg = APIHelper.BasePost(uri, null, lsData, out HttpResponseMessage responseMessage); if (sMsg != null) { Logger.Error(sMsg); break; } Logger.Info($"CallFbPostPushAPI responseMessage.IsSuccessStatusCode = {responseMessage.IsSuccessStatusCode}"); if (responseMessage.IsSuccessStatusCode) { var fb_error = GetFbApiError(responseMessage); if (!string.IsNullOrWhiteSpace(fb_error)) { sMsg = fb_error; Logger.Error($"CallFbPostPushAPI f7"); Logger.Error($"CallFbPostPushAPI sMsg={sMsg}"); break; } Logger.Info($"CallFbPostPushAPI f6"); string sContent = responseMessage.Content.ReadAsStringAsync().Result; Logger.Info($"CallFbPostPushAPI f6 sContent={sContent}"); sPostFBID = JsonConvert.DeserializeObject(sContent).id; Logger.Info($"CallFbPostPushAPI f6 sPostFBID={sPostFBID}"); } else { Logger.Error($"CallFbPostPushAPI f7"); sMsg = JsonConvert.SerializeObject(responseMessage); Logger.Error($"CallFbPostPushAPI sMsg={sMsg}"); break; } } while (false); } catch (Exception ex) { sMsg = ex.Message; Logger.Error($"Exception={sMsg}"); } o_sPostFBID = sPostFBID; Logger.Info($"CallFbPostPushAPI end groupId={groupId}"); return sMsg; } /// /// 上傳圖片的POST /// /// /// /// 圖片路徑 public string CallFbPicturePushAPI(string authorizationToken, string groupId, string path, out string o_sPicID) { string sMsg = null; string sPicID = null; Logger.Info($"CallFbPicturePushAPI start groupId={groupId} path={path}"); try { do { // 送出資料 //string uri = string.Format("https://graph.facebook.com/v7.0/{0}/photos", groupId); //Logger.Info($"CallFbPicturePushAPI uri = {uri}"); //var lsData = new List() //{ // new APIHelper.DataContent { Key = "access_token" , Type = typeof(StringContent) , Content = authorizationToken }, // new APIHelper.DataContent { Key = "url", Type = typeof(StringContent), Content = path }, // new APIHelper.DataContent { Key = "published", Type = typeof(StringContent), Content = "false" }, // new APIHelper.DataContent { Key = "attempt", Type = typeof(StringContent), Content = "3" } //}; // 送出資料 string uri = string.Format("https://graph.facebook.com/v7.0/{0}/photos?access_token={1}", groupId, authorizationToken); Logger.Info($"CallFbPicturePushAPI uri = {uri}"); var lsData = new List() { //new APIHelper.DataContent { Key = "access_token" , Type = typeof(StringContent) , Content = authorizationToken }, new APIHelper.DataContent { Key = "url", Type = typeof(StringContent), Content = path }, new APIHelper.DataContent { Key = "published", Type = typeof(StringContent), Content = "false" }, new APIHelper.DataContent { Key = "attempt", Type = typeof(StringContent), Content = "3" }, new APIHelper.DataContent { Key = "suppress_http_code", Type = typeof(StringContent), Content = "1" }, new APIHelper.DataContent { Key = "format", Type = typeof(StringContent), Content = "json" }, new APIHelper.DataContent { Key = "pretty", Type = typeof(StringContent), Content = "0" }, //new APIHelper.DataContent { Key = "debug", Type = typeof(StringContent), Content = "all" }, new APIHelper.DataContent { Key = "transport", Type = typeof(StringContent), Content = "cors" }, }; sMsg = APIHelper.BasePost(uri, null, lsData, out HttpResponseMessage responseMessage); if (sMsg != null) { Logger.Error($"CallFbPicturePushAPI uri = {uri} sMsg={sMsg}"); break; } if (responseMessage.IsSuccessStatusCode) { var fb_error = GetFbApiError(responseMessage); if (!string.IsNullOrWhiteSpace(fb_error)) { sMsg = fb_error; Logger.Error($"CallFbPicturePushAPI f7"); Logger.Error($"CallFbPicturePushAPI sMsg={sMsg}"); break; } string sContent = responseMessage.Content.ReadAsStringAsync().Result; Logger.Info($"CallFbPicturePushAPI sContent={sContent}"); sPicID = JsonConvert.DeserializeObject(sContent).id; Logger.Info($"CallFbPicturePushAPI sPicID={sPicID}"); } else { Logger.Error($"CallFbPicturePushAPI f7"); sMsg = JsonConvert.SerializeObject(responseMessage); Logger.Error($"CallFbPicturePushAPI sMsg={sMsg}"); break; } } while (false); } catch (Exception ex) { sMsg = ex.Message; Logger.Error($"CallFbPicturePushAPI exception={sMsg}"); } o_sPicID = sPicID; Logger.Info($"CallFbPicturePushAPI end groupId={groupId} path={path} sResult={sPicID}"); return sMsg; } /// /// 貼文的Get(抓貼文、圖片、影片) /// /// /// /// 圖片路徑 public bool CallFbPostGetAPI(string authorizationToken, string groupId) { // 送出資料 取1000筆資料 string uri = string.Format("https://graph.facebook.com/v7.0/{0}/feed?fields=full_picture,message,source,updated_time,created_time&limit=1000&", groupId); var dicData = new Dictionary() { { "access_token", authorizationToken } }; APIHelper.BaseGet(uri, null, dicData, out HttpResponseMessage responseMessage); if (responseMessage.IsSuccessStatusCode) { GetFbPostData(JsonConvert.DeserializeObject(responseMessage.Content.ReadAsStringAsync().Result)); } return responseMessage.IsSuccessStatusCode; } /// /// 貼文的Get(抓貼文、圖片、影片) /// /// /// /// 圖片路徑 public FbGetPost CallFbPostGetDataAPI(string authorizationToken, string groupId, int grabDate_ = 0) { if (grabDate_ == 0 || grabDate_ > 30) grabDate_ = 30;// 30日為預設天數 // 送出資料 撈取30天內之文章 並重複取至無資料 string uri = string.Format("https://graph.facebook.com/v7.0/{0}/feed?fields=full_picture,message,source,updated_time,created_time&limit=25&since={1}&", groupId, DateTime.Now.AddDays(-grabDate_).ToString("yyyy-MM-dd") ); FbGetPost rawResult = null; FbGetPost result = new FbGetPost() { data = new List(), paging = new JObject() }; bool isOver = false; var dicData = new Dictionary() { { "access_token", authorizationToken } }; while (!isOver) { APIHelper.BaseGet(uri, null, dicData, out HttpResponseMessage responseMessage); if (responseMessage.IsSuccessStatusCode) { rawResult = JsonConvert.DeserializeObject(responseMessage.Content.ReadAsStringAsync().Result); result.data.AddRange(rawResult.data); uri = rawResult.paging != null ? rawResult.paging["next"].ToString() : null; if (rawResult.data.Count == 0) { isOver = true; } } else { isOver = true; } } return result; } /// /// 貼文的Get(抓單篇貼文) /// /// /// /// 圖片路徑 public FbGetPostdata CallSingleFbPostGetDataAPI(string authorizationToken, string postId) { string uri = string.Format("https://graph.facebook.com/v7.0/{0}?fields=created_time&", postId); FbGetPost result = new FbGetPost() { data = new List(), paging = new JObject() }; var dicData = new Dictionary() { { "access_token", authorizationToken } }; FbGetPostdata rawResult = null; APIHelper.BaseGet(uri, null, dicData, out HttpResponseMessage responseMessage); if (responseMessage.IsSuccessStatusCode) { rawResult = JsonConvert.DeserializeObject(responseMessage.Content.ReadAsStringAsync().Result); } return rawResult; } /// /// 取回留言的Get /// /// /// /// 圖片路徑 public bool CallFbCommentGetAPI(string articleId, string authorizationToken, string postId, out List comments) { // 送出資料 string uri = string.Format("https://graph.facebook.com/v7.0/{0}/comments?filter=stream&fields={1}&access_token={2}&limit={3}", postId, "id,created_time,from{name,id,picture},message,parent,comment_count", authorizationToken, "150"); FbCommentData rawResult = null; FbCommentData result = new FbCommentData() { data = new List(), paging = new JObject() }; bool isOver = false; while (!isOver) { APIHelper.BaseGet(uri, null, new Dictionary(), out HttpResponseMessage responseMessage); if (responseMessage.IsSuccessStatusCode) { var fb_error = GetFbApiError(responseMessage); if (!string.IsNullOrWhiteSpace(fb_error)) { Logger.Error($"{nameof(CallFbCommentGetAPI)} FB API response error message: {fb_error} "); } rawResult = JsonConvert.DeserializeObject(responseMessage.Content.ReadAsStringAsync().Result); result.data.AddRange(rawResult.data); uri = rawResult.paging != null && rawResult.paging.ContainsKey("next") ? rawResult.paging["next"].ToString() : null; if (rawResult.data.Count == 0 || uri == null) { isOver = true; } } else { var error = $"{nameof(CallFbCommentGetAPI)} Error, ReasonPhrase:{responseMessage.ReasonPhrase} hrmResult.Headers.WwwAuthenticate:{responseMessage.Headers.WwwAuthenticate} "; Logger.Error(error); isOver = true; } } if (result.data.Count > 0) { string sMsg = GetFCommentsData(articleId, result, postId, out comments); // 儲存出錯 if (sMsg != null) { comments = null; } } else { comments = null; } return true; } /// /// 延長token /// /// /// /// 圖片路徑 public bool CallFbLongTokenGetAPI(string authorizationToken, string client_id, string client_secret) { // 送出資料 string uri = "https://graph.facebook.com/oauth/access_token?"; var dicData = new Dictionary() { { "grant_type", "fb_exchange_token" }, { "client_id", client_id }, { "client_secret", client_secret }, { "fb_exchange_token", authorizationToken }, }; APIHelper.BaseGet(uri, null, dicData, out HttpResponseMessage responseMessage); if (responseMessage.IsSuccessStatusCode) { var responseData = JsonConvert.DeserializeObject(responseMessage.Content.ReadAsStringAsync().Result); } return responseMessage.IsSuccessStatusCode; } /// /// 驗證token, 可改用 Function FbApiCheckToken /// /// /// /// 圖片路徑 public string CallFbCheckTokenGetAPI(string authorizationToken, string client_id, string client_secret, out int status) { // 送出資料 status = 0; // 預設0: 正常 1: 即將到期 2: 驗證失敗 string uri = "https://graph.facebook.com/debug_token?"; var dicData = new Dictionary() { { "input_token", authorizationToken }, { "access_token", string.Format("{0}|{1}",client_id,client_secret) }, }; APIHelper.BaseGet(uri, null, dicData, out HttpResponseMessage responseMessage); if (responseMessage.IsSuccessStatusCode) { var responseData = JsonConvert.DeserializeObject(responseMessage.Content.ReadAsStringAsync().Result); if (responseData.data.is_valid) { var timeStamp = responseData.data.expires_at - Convert.ToInt32(DateTime.UtcNow.AddHours(8).Subtract(new DateTime(1970, 1, 1)).TotalSeconds); if (timeStamp > 20 * 86400) // 20天 { return null; } else { status = 1; return "Token即將到期"; } } else { status = 2; return "Token驗證失敗"; } } else { status = 2; return "Request失敗"; } } /// /// Send FB Message /// /// /// /// /// /// public void SendMessage(ref string messenger_id_, ref string message_, ref string access_token_, Action successHandle_, Action errorHandle_) { string url = ApiUrlHelper.SendMessage(access_token_); var dataContents = new { recipient = new { id = messenger_id_ }, messaging_type = "MESSAGE_TAG", message = new { text = message_}, tag = "POST_PURCHASE_UPDATE" }; SendPostFbApi(url, dataContents, successHandle_, errorHandle_); } /// /// /// /// public void CheckAccessToken(ref string access_token_, Action successHandle_, Action errorHandle_) { string url = ApiUrlHelper.CheckAccessToken(access_token_); SendGetFbApi(url, successHandle_, errorHandle_); } public void GetUserAccessToken(ref string user_access_token_, ref string app_id_, ref string app_secret_, Action successHandle_, Action errorHandle_) { string url = ApiUrlHelper.GetUserAccessToken(user_access_token_, app_id_, app_secret_); SendGetFbApi(url, successHandle_, errorHandle_); } public void GetPageAccessToken(ref string user_id_, ref string long_lived_user_access_token, Action successHandle_, Action errorHandle_) { string url = ApiUrlHelper.GetPageAccessToken(user_id_, long_lived_user_access_token); SendGetFbApi(url, successHandle_, errorHandle_); } public void GetGroupPublicDescription(string group_id_, string user_access_token_, Action successHandle_, Action errorHandle_) { string url = ApiUrlHelper.GetGroupPublicDescription(group_id_, user_access_token_); SendGetFbApi(url, successHandle_, errorHandle_); } #endregion #region 專案獨有 ///// ///// 貼文資料新增到SQL ///// ///// ///// private void GetFbPostData(FbGetPost fbGetPost) { ArsenalInterface ai; List lCmds = new List(); // 取社團 QueryJsonElementCollection lBlocks = new QueryJsonElementCollection(); QueryJsonElement qjeOrigin = lBlocks.GetInst(); qjeOrigin.table = tb_grp_group.TABLENAME; qjeOrigin.displaycols = new List() { tb_grp_group.CN_UID, tb_grp_group.CN_FB_GROUP_ID }; qjeOrigin.wherecols = new WhereNode(tb_grp_group.CN_FB_GROUP_ID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_grp_group), fbGetPost.data[0].id.Split("_")[0]); lBlocks.Add(qjeOrigin); var sMsg = MakeSelectJoinByBlocks(lBlocks, out Command cRes); ai = ArsenalDBMgr.GetInst(cRes); List group = ai.RunQueryList(cRes); // 取貼文資料 lBlocks.Clear(); QueryJsonElement qjeArticle = lBlocks.GetInst(); qjeArticle.table = tb_grp_article.TABLENAME; qjeArticle.displaycols = new List() { tb_grp_article.CN_UID, tb_grp_article.CN_GROUP_UID, tb_grp_article.CN_FB_ARTICLE_ID }; qjeArticle.wherecols = new WhereNode(tb_grp_article.CN_GROUP_UID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_grp_article), group.First().uid); lBlocks.Add(qjeArticle); sMsg = MakeSelectJoinByBlocks(lBlocks, out Command cArt); ai = ArsenalDBMgr.GetInst(cArt); List article = ai.RunQueryList(cArt); // 取媒體資料 lBlocks.Clear(); QueryJsonElement qjeArticleMedia = lBlocks.GetInst(); qjeArticleMedia.table = tb_grp_article_media.TABLENAME; qjeArticleMedia.displaycols = new List() { tb_grp_article_media.CN_UID, tb_grp_article_media.CN_ARTICLE_UID, tb_grp_article_media.CN_MEDIA_ID, tb_grp_article_media.CN_URI }; lBlocks.Add(qjeArticleMedia); sMsg = MakeSelectJoinByBlocks(lBlocks, out Command cMedia); ai = ArsenalDBMgr.GetInst(cMedia); List lsmedia = ai.RunQueryList(cMedia); fbGetPost.data.ForEach(x => { var uid = string.Empty; // 如果貼文存在則更新 否則新增 if (!article.Any(y => y.fb_article_id == x.id)) { uid = Guid.NewGuid().ToString(); tb_grp_article cNew = new tb_grp_article() { uid = uid, group_uid = group.First().uid, message = x.message, post_status = (int)EPostStatus.EPS_FETCH_FROM_FB, fb_article_id = x.id, release_date = x.created_time }; lCmds.Add(Command.SetupInsertCmd(cNew)); } else { uid = article.First(y => y.fb_article_id == x.id).uid; tb_grp_article cWhere = new tb_grp_article() { uid = uid }; tb_grp_article cUpdate = new tb_grp_article() { group_uid = group.First().uid, message = x.message, post_status = (int)EPostStatus.EPS_FETCH_FROM_FB, fb_article_id = x.id, release_date = x.created_time }; lCmds.Add(Command.SetupUpdateCmd(cUpdate, cWhere)); } if (!lsmedia.Any(y => y.uri == x.full_picture) && x.full_picture != null) { tb_grp_article_media media = new tb_grp_article_media() { article_uid = uid, uri = x.full_picture, type = BLWording.MediaType.Photo, status_flag = (int)MediaType.GetFb }; lCmds.Add(Command.SetupInsertCmd(media)); } if (!lsmedia.Any(y => y.uri == x.full_picture) && x.source != null) { tb_grp_article_media media = new tb_grp_article_media() { article_uid = uid, uri = x.source, type = BLWording.MediaType.Video, status_flag = (int)MediaType.GetFb }; lCmds.Add(Command.SetupInsertCmd(media)); } }); if (lCmds.Any()) { ai = ArsenalDBMgr.GetInst(lCmds[0], GetDefaultSystemColumnInfo()); ai.RunEditCmds(lCmds); sMsg = GetLastErrorCode(lCmds); } } /// /// 貼文留言存進SQL /// /// /// private string GetFCommentsData(string articleId, FbCommentData fbGetPost, string post_id, out List comments) { // 沒有留言 if (fbGetPost.data.Count == 0) { comments = null; return null; } List lCmds = new List(); // 用文章 ID取文章 data var article = GetArticleByPostID(articleId); comments = new List(); // 取回原先的留言 List originComments = GetComments(article.uid); // 如果先前已有匯入留言,已匯入過的留言不再重複匯入 List existFbCommentID = originComments.Select(x => x.fb_comment_id).ToList(); // 取回該社團可以取貨的地點 var branches = GetBranches(article.group_uid).ToDictionary(x => x.uid, x => x.branch_name); var products = GetProducts(article.uid); // 會員預設取貨地點(中文)的對應表,沒設定取貨地點不會放進列表 var memberDefaultBranchDict = GetMembers(post_id.Split('_')[0]) .Where(x => !string.IsNullOrEmpty(x.default_branch) && branches.ContainsKey(x.default_branch)) .ToDictionary(x => x.group_user_id, x => branches[x.default_branch]); // 將名稱以['A', 'B'...]這樣依序產生,如果產品有3個,就要放入['A', 'B', 'C'] List productInfoNames = new List(); //productInfoNames = products.Select((x, i) => new GroupBuyParser.ProductInfo //{ // Name = x.name ?? "", // Specifications = string.IsNullOrEmpty(x.specification) ? null : x.specification.Split(',', StringSplitOptions.RemoveEmptyEntries) //}).ToList(); var productInfos = products.Select((x, i) => new GroupBuyParser.ProductInfo { Name = ((char)('A' + x.seq)).ToString(), Specifications = string.IsNullOrEmpty(x.specification) ? null : x.specification.Split(',', StringSplitOptions.RemoveEmptyEntries).OrderByDescending(x => x.Count()).ToArray() }).ToList(); //var productInfos = productInfoNames.Concat(productInfosChar).ToList(); Logger.Info($"開始解析貼文 文章ID: {article.uid} 設定: {JsonConvert.SerializeObject(productInfos)}"); var parser = new GroupBuyParser.GroupBuyParser { Settings = new GroupBuyParser.ParserSettings { Products = productInfos, Branches = branches.Values.ToList() } }; StringBuilder parseCommentErrorLog = new StringBuilder(); foreach (var item in fbGetPost.data) { // 沒有留言就不處理 if (string.IsNullOrEmpty(item.message)) { continue; } // 判斷已經重複的ID if (existFbCommentID.Contains(item.id)) { continue; } // 判斷是否為回覆留言 string parentUid = null; if (item.parent != null) { parentUid = item.parent.id; } // 如果取不回fb_id(使用者沒有允許應用程式查看貼文)仍要處理 string group_user_id = item.from?.id; string json = ""; int status = 0; // 傳入該會員預設取貨地點(非會員則為null) string defaultBranch = null; if (!string.IsNullOrEmpty(group_user_id) && memberDefaultBranchDict.ContainsKey(group_user_id)) { defaultBranch = memberDefaultBranchDict[group_user_id]; } var parseData = new GroupBuyParser.ParserData { Text = item.message, DefaultBranch = defaultBranch }; parser.Parse(parseData); if (parseData.Success) { bool isProductCorrect = true; foreach (var product in parseData.Product.ToList()) { if (product.Name == "I") { isProductCorrect = false; break; } } if (isProductCorrect) // 名稱有I 不Insert進資料庫 { json = JsonConvert.SerializeObject(parseData.Product); status = (int)CommentStatus.OK; } else { status = (int)CommentStatus.InvalidFormat; } } else { status = (int)CommentStatus.InvalidFormat; // 增加log紀錄確認解析失敗的原因 parseCommentErrorLog.AppendLine($"解析貼文失敗 姓名: {item.from?.name} 內容: {item.message} 原因: {parseData.ErrorMessage}"); } if (string.IsNullOrEmpty(group_user_id)) { status = (int)CommentStatus.UnknownMember; } else if (!memberDefaultBranchDict.ContainsKey(group_user_id)) { status = (int)CommentStatus.UnknownMember; } else if (comments.Any(x => x.group_user_id == group_user_id)) // 是否有重複留言 { status = (int)CommentStatus.Duplicate; } if (parentUid != null) { status = (int)CommentStatus.Duplicate; } tb_grp_comment cNew = new tb_grp_comment { uid = Guid.NewGuid().ToString(), article_uid = article.uid, origin_comment = item.message, comment = json, group_user_id = group_user_id, comment_time = item.created_time, fb_comment_id = item.id, status = status, user_name = item.from?.name, user_picture = item.from?.picture.data.url, parent_comment_id = parentUid == null ? null : parentUid }; comments.Add(cNew); } if(parseCommentErrorLog.Length != 0) { // 參考原本寫法, 解析貼文失敗訊息標記為Info parseCommentErrorLog.Insert(0, Environment.NewLine); Logger.Info(parseCommentErrorLog.ToString()); } // 沒有新增任何留言就不處理 if (comments.Count == 0) { return null; } lCmds.AddRange(comments.Select(x => Command.SetupInsertCmd(x))); var ai = ArsenalDBMgr.GetInst(lCmds[0], GetDefaultSystemColumnInfo()); ai.RunEditCmds(lCmds); var sMsg = GetLastErrorCode(lCmds); return sMsg; } /// /// 將留言轉成訂單(結單) /// /// public string CheckOrder(string article_uid, JArray jData) { // 貼文資料 var article = GetArticle(article_uid); // 商品資料 var productData = GetProducts(article_uid).OrderBy(x => x.seq).ToArray(); // 貼文對應的群組UID var groupUid = article.group_uid; // 取得社團成員ID對應的UID var fb_group_id = article.fb_article_id.Split('_')[0]; var memberDict = GetMembers(fb_group_id).ToDictionary(x => x.group_user_id, x => x.uid); // 分店的對應表 var branchDict = GetBranches(groupUid).ToDictionary(x => x.branch_name, x => x.uid); // 取回過去的訂單主檔 var IsOrderMaster = GetOrderMasters(article_uid); // 如果過去已經有結單,使用同一個order_code,反之取回新的訂單編號(這不就是article_uid嗎?) var orderCode = IsOrderMaster.Any() ? IsOrderMaster.First().order_code : GetOrderCode(); var orderName = IsOrderMaster.Any() ? IsOrderMaster.First().name : article.name; // 有結單過的貼文使用舊名稱 var orderMasterDic = new Dictionary(); // 再次結單屬於新的order_master // 先取回所有留言 var comments = GetComments(article_uid); // 留言解析成功的才更新 List data = new List(); foreach (JToken token in jData) { Dictionary dicData = token.ToObject>(); var commentData = comments.Where(x => x.status != (int)CommentStatus.TransferOrder && !string.IsNullOrEmpty(x.comment) && x.status_flag == BLWording.STATUS_FLAG_ON && !string.IsNullOrEmpty(x.group_user_id) && x.uid == dicData["uid"].ToString()).Select(x => new { x.uid, x.group_user_id, x.comment, x.comment_time }); if (commentData.Any()) { CommentModel tempComment = new CommentModel() { uid = commentData.Select(x => x.uid).FirstOrDefault(), comment = commentData.Select(x => x.comment).FirstOrDefault(), group_user_id = commentData.Select(x => x.group_user_id).FirstOrDefault(), comment_time = commentData.Select(x => x.comment_time).FirstOrDefault() }; data.Add(tempComment); } } if (!data.Any()) { return null; } List lCmds = new List(); List lOrderDetails = new List(); ArsenalInterface ai = null; foreach (var item in data) { // 解析留言訂購格式,以每一項為單位塞到DB去 var products = JsonConvert.DeserializeObject>(item.comment); foreach (var product in products) { // 目前暫定A = 0 B = 1... var productIndex = product.Name[0] - 'A'; var productItem = productData.FirstOrDefault(x => x.seq == productIndex); if (product.Branch == "未知" || string.IsNullOrWhiteSpace(product.Branch)) { break; } var branch_uid = branchDict[product.Branch]; // 如果目前沒有主檔,新增主檔 if (!orderMasterDic.ContainsKey(branch_uid)) { // 查詢group_uid tb_grp_branch cBranch = new tb_grp_branch(); cBranch.SetDirty(tb_grp_branch.CN_GROUP_UID); tb_grp_branch cCon = new tb_grp_branch() { uid = branch_uid }; Command cSelect = Command.SetupSelectCmd(cBranch, cCon); ai = ArsenalDBMgr.GetInst(cSelect); tb_grp_branch qds = ai.RunQuerySingleORM(cSelect); var newOrderMaster = new tb_ord_order_master { uid = Guid.NewGuid().ToString(), type = 1, name = orderName, order_code = orderCode, article_uid = article_uid, branch_uid = branch_uid, group_uid = qds.group_uid, status = (int)OrderStatus.NotArrived, }; lCmds.Add(Command.SetupInsertCmd(newOrderMaster)); // 增加Dictionary的內容,避免內容被重複新增 orderMasterDic.Add(branch_uid, newOrderMaster); } if (product.Qty > 0) { var order = new tb_ord_order_detail { uid = Guid.NewGuid().ToString(), order_uid = orderMasterDic[branch_uid].uid, article2product_uid = productItem.uid, member_uid = memberDict[item.group_user_id], specification = product.Specification != "" ? product.Specification : null, order_qty = product.Qty, price = productItem.price * product.Qty, comment_time = item.comment_time, comment_uid = item.uid, status = (int)OrderStatus.NotArrived }; lOrderDetails.Add(order); // 更新留言變成不顯示 tb_grp_comment updateComment = new tb_grp_comment { status = (int)CommentStatus.TransferOrder }; tb_grp_comment updateCommentCond = new tb_grp_comment { uid = item.uid }; lCmds.Add(Command.SetupUpdateCmd(updateComment, updateCommentCond)); } } } if (lOrderDetails.Count == 0) { return "結單錯誤(請確認訂單資訊)"; } if (article.post_status < 64) { tb_grp_article updateArticle = new tb_grp_article { post_status = (int)EPostStatus.EPS_ORDER }; tb_grp_article updateArticleCond = new tb_grp_article { uid = article_uid }; lCmds.Add(Command.SetupUpdateCmd(updateArticle, updateArticleCond)); } lCmds.AddRange(lOrderDetails.Select(x => Command.SetupInsertCmd(x))); ai = ArsenalDBMgr.GetInst(lCmds[0], GetDefaultSystemColumnInfo()); ai.RunEditCmds(lCmds); var sMsg = GetLastErrorCode(lCmds); return sMsg; } private List GetOrderMasters(string article_uid) { tb_ord_order_master cDisplay = new tb_ord_order_master(); cDisplay.SetFullDirty(); var whereNode = new WhereNode(tb_ord_order_master.CN_ARTICLE_UID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_ord_order_master), article_uid); var cSelect = Command.SetupSelectCmd(cDisplay, whereNode); var ai = ArsenalDBMgr.GetInst(cSelect); var data = ai.RunQueryList(cSelect); return data; } /// /// 用文章FB ID取回貼文部分資訊 /// /// /// private static tb_grp_article GetArticleByPostID(string articleId) { QueryJsonElementCollection lBlocks = new QueryJsonElementCollection(); QueryJsonElement qjeOrigin = lBlocks.GetInst(); qjeOrigin.table = tb_grp_article.TABLENAME; qjeOrigin.displaycols = new List() { tb_grp_article.CN_UID, tb_grp_article.CN_FB_ARTICLE_ID, tb_grp_article.CN_GROUP_UID }; qjeOrigin.wherecols = new WhereNode(tb_grp_article.CN_UID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_grp_article), articleId); lBlocks.Add(qjeOrigin); string sMsg = MakeSelectJoinByBlocks(lBlocks, out Command cRes); var ai = ArsenalDBMgr.GetInst(cRes); var articles = ai.RunQueryList(cRes); return articles.First(); } /// /// 用文章UID取回貼文部分資訊 /// /// /// private tb_grp_article GetArticle(string article_uid) { QueryJsonElementCollection lBlocks = new QueryJsonElementCollection(); QueryJsonElement qjeOrigin = lBlocks.GetInst(); qjeOrigin.table = tb_grp_article.TABLENAME; qjeOrigin.displaycols = new List { tb_grp_article.CN_GROUP_UID, tb_grp_article.CN_FB_ARTICLE_ID, tb_grp_article.CN_POST_STATUS, tb_grp_article.CN_NAME }; qjeOrigin.wherecols = new WhereNode(tb_grp_article.CN_UID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_grp_article), article_uid); lBlocks.Add(qjeOrigin); string sMsg = MakeSelectJoinByBlocks(lBlocks, out Command cRes); var ai = ArsenalDBMgr.GetInst(cRes); var articles = ai.RunQueryList(cRes); return articles.First(); } /// /// 用文章UID取回原先的留言 /// /// /// private List GetComments(string article_uid) { tb_grp_comment cDisplay = new tb_grp_comment(); cDisplay.SetFullDirty(); var whereNode = new WhereNode(tb_grp_comment.CN_ARTICLE_UID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_grp_comment), article_uid); var cSelect = Command.SetupSelectCmd(cDisplay, whereNode); var ai = ArsenalDBMgr.GetInst(cSelect); var originComments = ai.RunQueryList(cSelect); return originComments; } /// /// 用社團UID取回取貨地點 /// /// /// private List GetBranches(string group_uid) { tb_grp_branch cDisplay = new tb_grp_branch(); cDisplay.SetFullDirty(); var whereNode = new WhereNode(tb_grp_branch.CN_GROUP_UID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_grp_branch), group_uid); var cSelect = Command.SetupSelectCmd(cDisplay, whereNode); var ai = ArsenalDBMgr.GetInst(cSelect); var data = ai.RunQueryList(cSelect); return data; } /// /// 用貼文UID取回該貼文所有商品的部分資訊 /// /// /// private List GetProducts(string article_uid) { /**開始組指令**/ QueryJsonElementCollection lBlocks = new QueryJsonElementCollection(); QueryJsonElement qjeArticleToProduct = lBlocks.GetInst(); qjeArticleToProduct.table = tb_prd_article2product.TABLENAME; qjeArticleToProduct.displaycols = new List() { tb_prd_article2product.CN_UID, tb_prd_article2product.CN_NAME, tb_prd_article2product.CN_PRICE, tb_prd_article2product.CN_SEQ, tb_prd_article2product.CN_SPECIFICATION }; qjeArticleToProduct.wherecols = new WhereNode(tb_prd_article2product.CN_ARTICLE_UID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_prd_article2product), article_uid); lBlocks.Add(qjeArticleToProduct); QueryJsonElement qjeProduct = lBlocks.GetInst(); qjeProduct.table = tb_prd_product.TABLENAME; qjeProduct.jointable = qjeArticleToProduct; qjeProduct.joincols = new Dictionary() { { tb_prd_product.CN_UID, tb_prd_article2product.CN_PRD_UID } }; lBlocks.Add(qjeProduct); var sMsg = MakeSelectJoinByBlocks(lBlocks, out Command cRes); var ai = ArsenalDBMgr.GetInst(cRes); List data = ai.RunQueryList(cRes); return data.ToList(); } /// /// 用FB社團ID取回所有使用者資訊 /// /// /// private List GetMembers(string group_id) { tb_meb_member tDisplay = new tb_meb_member(); tDisplay.SetFullDirty(); List lwWhereData = new List(); lwWhereData.Add(new WhereNode(tb_meb_member.CN_GROUP_ID, WhereNode.EColumnOperation.EOT_EQ, typeof(tb_meb_member), group_id)); lwWhereData.Add(new WhereNode(tb_meb_member.CN_NAME, WhereNode.EColumnOperation.EOT_NEQ, typeof(tb_meb_member), "現貨銷售")); Command cSelect = Command.SetupSelectCmd(tDisplay, new WhereNode(WhereNode.ENodeOperation.ENO_AND, lwWhereData.ToArray())); ArsenalInterface ai = ArsenalDBMgr.GetInst(cSelect); List members = ai.RunQueryList(cSelect); return members; } /// /// 取得目前的訂單號碼 /// /// /// public string GetOrderCode() { string dateString = DateTime.Now.ToString("yyMMdd"); QueryJsonElementCollection lBlocks = new QueryJsonElementCollection(); QueryJsonElement qjeOrigin = lBlocks.GetInst(); qjeOrigin.table = tb_ord_order_master.TABLENAME; qjeOrigin.displaycols = new List() { tb_ord_order_master.CN_ORDER_CODE }; qjeOrigin.wherecols = new WhereNode(tb_ord_order_master.CN_ORDER_CODE, WhereNode.EColumnOperation.EOT_LIKE, typeof(tb_ord_order_master), dateString + "%"); lBlocks.Add(qjeOrigin); string sMsg = MakeSelectJoinByBlocks(lBlocks, out Command cRes); var ai = ArsenalDBMgr.GetInst(cRes); var orders = ai.RunQueryList(cRes); // 兩碼流水號,例如20082801 = 2020/8/28 第一批訂單 var lastOrderNo = orders.Select(x => x.order_code).OrderByDescending(x => x).FirstOrDefault(); int newIndex = 1; if (!string.IsNullOrEmpty(lastOrderNo)) { newIndex = int.Parse(lastOrderNo.Substring(6, 2)) + 1; } return dateString + newIndex.ToString("00"); } #endregion private void SendGetFbApi(string url, Action successHandle_, Action errorHandle_) { APIHelper.BaseGet(url, null, new Dictionary(), out HttpResponseMessage responseMessage); FbApiResponseHandler(responseMessage, successHandle_, errorHandle_); } private void SendPostFbApi(string url, dynamic postContent, Action successHandle_, Action errorHandle_) { APIHelper.BasePost(url, null, postContent, out HttpResponseMessage responseMessage, "json"); FbApiResponseHandler(responseMessage, successHandle_, errorHandle_); } /// /// If FB API response SUCCESS return true, else you can defined your error handle function like write log /// /// /// /// private void FbApiResponseHandler(HttpResponseMessage httpResponseMessage_, Action successHandle_, Action errorHandle_) { // check response header if (!httpResponseMessage_.IsSuccessStatusCode) { var headerError = $"{httpResponseMessage_.ReasonPhrase}, {httpResponseMessage_.Headers.WwwAuthenticate}"; errorHandle_(httpResponseMessage_, headerError); return; } // check response content var bodyError = GetFbApiError(httpResponseMessage_); if (!string.IsNullOrWhiteSpace(bodyError)) { errorHandle_(httpResponseMessage_, bodyError); return; } successHandle_(httpResponseMessage_); } /// /// 檢查Response code 200後, FB回傳的內容是否包含錯誤訊息 /// /// /// public static string GetFbApiError(HttpResponseMessage httpMessage_) { var sContent = httpMessage_.Content.ReadAsStringAsync().Result; var jsonObject = JObject.Parse(sContent); if (jsonObject["error"] != null) { return jsonObject["error"].ToString(); } return ""; } } }