public override bool TryGetMember(GetMemberBinder binder, out object result) { if (!_dictionary.TryGetValue(binder.Name, out result)) { result = null; return true; } var dictionary = result as IDictionary<string, object>; if (dictionary != null) { result = new DynamicJsonObject(dictionary); return true; } var arrayList = result as ArrayList; if (arrayList != null && arrayList.Count > 0) { if (arrayList[0] is IDictionary<string, object>) result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x))); else result = new List<object>(arrayList.Cast<object>()); } return true; } } #endregion }
次に、GetContent メソッドです。このメソッドの目的は非常に簡単で、顧客によって渡されたテンプレート変数パラメーターのキーと値のペアと SMS テンプレートのコンテンツに基づいて最終的なテキスト メッセージのコンテンツを組み立てることです。はハードコードされていましたので、動的取得に変更する必要があります。
テキストメッセージテンプレートの内容の例:
【一应生活】您有一件单号为expressNumbers company,已到communityName收发室,请打开一应生活APP“收发室”获取取件码进行取件。 点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life
このようなテンプレートの内容には問題があることがわかりました。テンプレート内の変数パラメータは直接英単語で表現されており、本文中に英単語が含まれる場合があります。メッセージの内容を確認したら、すべての変数パラメーターに {} を追加するだけです。変更は次のとおりです:
【一应生活】您有一件单号为{expressNumbers} {company},已到{communityName}收发室, 请打开一应生活APP“收发室”获取取件码进行取件。点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life
SMS テンプレート内の変数パラメーターを、顧客から渡されたオブジェクトに基づいて変数パラメーターに対応する値に置き換える必要があります。次に、最初にこのオブジェクト内のキーと値のペアの情報を解析する必要があります。
/// 把object对象的属性反射获取到字典列表中 /// </summary> /// <param name="data">object对象</param> /// <returns>返回Dictionary(属性名,属性值)列表</returns> static Dictionary<string, string> GetProperties(object data) { Dictionary<string, string> dict = new Dictionary<string, string>(); Type type = data.GetType(); string[] propertyNames = type.GetProperties().Select(p => p.Name).ToArray(); foreach (var prop in propertyNames) { object propValue = type.GetProperty(prop).GetValue(data, null); string value = (propValue != null) ? propValue.ToString() : ""; if (!dict.ContainsKey(prop)) { dict.Add(prop, value); } } return dict; }
次のステップは、正規表現を使用してテキスト メッセージ テンプレートのコンテンツを照合することです。
/// 多个匹配内容 /// </summary> /// <param name="sInput">输入内容</param> /// <param name="sRegex">表达式字符串</param> /// <param name="sGroupName">分组名, ""代表不分组</param> static List<string> GetList(string sInput, string sRegex, string sGroupName) { List<string> list = new List<string>(); Regex re = new Regex(sRegex, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline); MatchCollection mcs = re.Matches(sInput); foreach (Match mc in mcs) { if (sGroupName != "") { list.Add(mc.Groups[sGroupName].Value); } else { list.Add(mc.Value); } } return list; } public static string ReplaceTemplate(string template, object data) { var regex = @"\{(?<name>.*?)\}"; List<string> itemList = GetList(template, regex, "name"); //获取模板变量对象 Dictionary<string, string> dict = GetProperties(data); foreach (string item in itemList) { //如果属性存在,则替换模板,并修改模板值 if (dict.ContainsKey(item)) { template = template.Replace("{"+item+"}", dict.First(x => x.Key == item).Value); } } return template; }
このようにして、顧客から渡されたオブジェクトは、解析コードから切り離されます。顧客から渡されたオブジェクトは、コード実装には依存せず、データ テーブル内のテンプレート コンテンツの構成に依存します。
これらのメソッドを作成したので、単体テストを実行して、必要な効果があるかどうかを確認します。残念ながら、このプロジェクトでは単体テストはまったく使用されていません。自分で単体テストを作成するしかありません。
[TestClass] public class MatchHelperTest { [TestMethod] public void ReplaceTemplate() { //模板文本 var template = "【一应生活】您有一件单号为{expressNumbers} {company},已到{communityName}收发室, 请打开一应生活APP“收发室”获取取件码进行取件。点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life"; //数据对象 var data = new { expressNumbers = "2016", company = "长城", communityName = "长怡花园"}; string str = "【一应生活】您有一件单号为2016 长城,已到长怡花园收发室, 请打开一应生活APP“收发室”获取取件码进行取件。点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life"; string str1=MatchHelper.ReplaceTemplate(template, data); Assert.AreEqual(str1,str); //重复标签的测试 template = "【一应生活】您有一件单号为{expressNumbers} {company},已到{communityName}收发室,单号:{expressNumbers}"; str = "【一应生活】您有一件单号为2016 长城,已到长怡花园收发室,单号:2016"; str1=MatchHelper.ReplaceTemplate(template, data); Assert.AreEqual(str1, str); } }
単体テストと言えば、あまりにも多くの理由から多くの企業で使用されていないと思います。また、ビジネスが単純であれば、単体テストを書く必要はないと思います。中国の多くの起業家が単体テストの作成に時間がかからないと言ったら、それは間違いなく嘘です。単体テストを作成すると、プロジェクトの効率が向上し、手戻り率が減少します。単体テストを作成しなくても、他の多くのことで補うことができるため、個人的にはこれを言うのは困難です。個人的な意見なので批判しないでください。
次に、GetContent メソッドを次のように変更します:
public string GetContent(dynamic messageContext) { string strMsg = ""; string TypeCode = string.IsNullOrEmpty(messageContext.serviceCode) ? "001" : messageContext.serviceCode; string channel = messageContext.channel; try{ var Module = unitOfWork.MessageModule.Get(c => c.Type == channel && c.TypeNo == TypeCode).FirstOrDefault(); if (!string.IsNullOrEmpty(Module.Content)) { var content = Module.Content; strMsg = MatchHelper.ReplaceTemplate(content, messageContext); } return strMsg; } catch (Exception ex) { strMsg = ex.Message; } return strMsg; }
(それに、前の変数の名前付け、MessageContext messageContext と文字列 messageContent について文句を言わせてください。見た目が似すぎています。最初にリファクタリングしたときに間違いを犯しました。混乱を避けるために、同じメソッド内で同様の変数名を使用しないでください。くそー、また騙されたので、我慢できずに思い切って名前を変更しました。ビジネス ロジックを呼び出します。コードは直接次のようになります。
MessageModuleBusiness message ModuleBusiness = new MessageModuleBusiness()
は具象クラスの実装に依存します。具象クラスは不安定で、抽象クラスは安定しているため、インターフェイス用にプログラムする必要があることがわかっています。今日はテキスト メッセージを送信し、明日は電子メールを送信したり、ログ レコードを追加したりするかもしれません。
public interface IMessageModuleBusiness { /// <summary> /// 组装消息内容 /// </summary> /// <param name="messageContext">动态参数对象</param> /// <returns>组装后的消息内容</returns> string GetContent(dynamic messageContext); }その後、呼び出しコードは次のように変更されます:
private IMessageModuleBusiness message ModuleBusiness = new MessageModuleBusiness();最終的な externalMerchantSendMessage コードは:
/// 外部商户发送信息 public ActionResult externalMerchantSendMessage() { try { dynamic param = null; string json = Request.QueryString.ToString(); if (Request.QueryString.Count != 0) //ajax get请求 { //兼容旧的客户调用写法,暂时硬编了 if (json.Contains("param.")) { json = json.Replace("param.", ""); } json = "{" + json.Replace("=", ":'").Replace("&", "',") + "'}"; } else //ajax Post请求 { Request.InputStream.Position = 0;//切记这里必须设置流的起始位置为0,否则无法读取到数据 json = new StreamReader(Request.InputStream).ReadToEnd(); } var serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new[] { new DynamicJsonConverter() }); param = serializer.Deserialize(json, typeof(object)); logger.Info("[externalMerchantSendMessage]param:" + param); bool isAuth = authModelBusiness.isAuth(param.channel, param.phone, param.sign); if (!isAuth) { return Json(new Result<string>() { resultCode = ((int)ResultCode.NoPermission).ToString(), resultMsg = "签名或无权限访问" }, JsonRequestBehavior.AllowGet); } var meaage = messageModuleBusiness.GetContent(param); if (string.IsNullOrEmpty(meaage)) { return Json(new Result<string>() { resultCode = ((int)ResultCode.failure).ToString(), resultMsg = "发送失败" }, JsonRequestBehavior.AllowGet); } SMSHelper helper = new SMSHelper(); helper.SendSMS(meaage, param.phone); //发送短信 return Json(new Result<string>() { resultCode = ((int)ResultCode.success).ToString(), resultMsg = "发送成功" }, JsonRequestBehavior.AllowGet); } catch (Exception ex) { return Json(new Result<string>() { resultCode = ((int)ResultCode.failure).ToString(), resultMsg = "发送失败"+ex.Message }, JsonRequestBehavior.AllowGet); } }この場合、将来リフレクションまたは IOC を通じて再度分離すると便利です。
このステップバイステップの再構築により、元のテーブル構造を変更せず、顧客の呼び出しに影響を与えることなく、顧客のテンプレートパラメータ変数が変更された場合に変更点をカプセル化しました。コードを変更する必要があるのは、テーブル内のテンプレートの内容のみです。
リファクタリングするときは、コード構造が一目でわかるようにクラス図を描くのがとても良い習慣です。
上記は .NET コードの再構成 (パート 2) の内容です。さらに関連する内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。