数学公式计算逻辑处理。
闲来无事,决定动手写一些东西,想了想决定写一个用于计算公式的类。
用于计算IF运算MAX运算MIN运算和+-*/、">" 、"<" 、 ">="、"<=" 、"<>" 、"=" 、"&&"、"||"、"!" 等这些组合在一起的逻辑运算。
无规矩不成方圆,对于各种运算搅合在一起的复杂逻辑运算。首先得制定一些规矩,这样才能方便程序处理。
一、首先对各种运算进行分类。
1 package Formula; 2 3 enum EnumDataType { 4 /** 5 * 最大值运算 6 * */ 7 Max,//最大运算 8 /** 9 * 最小值运算 10 * */ 11 Min,//最小运算 12 /** 13 * IF运算 14 * */ 15 If,//IF语句 16 /** 17 * 比较运算 18 * */ 19 Compare,//比较运算 20 /** 21 * 计算运算 22 * */ 23 Calculate,//计算运算 24 /** 25 * 逻辑运算 26 * */ 27 Logic,//逻辑运算 28 /** 29 * 域限定符号 30 * */ 31 Region, 32 /** 33 * 正常数据 34 * */ 35 Data 36 }
在这个枚举类将公式里面的数据大致分为了八类
1、Max:最大运算类;
2、Min:最小运算累;
3、If:IF运算累;
4、Compare:比较运算类;
5、Calculate:数学运算类;
6、Logic:逻辑运算类;
7、Region:域限定类;
8、Data:正常数据类;
将数据分为八类,可以使你方便清晰的知道需要处理的逻辑方式。
二、为了更方便的处理计算的逻辑,我将把公式里面的每一个运算符放入到一个类中。这个类中记录它的运算类型,以及值。
1 package Formula; 2 /** 3 * 公式——项 4 * */ 5 final class FormulaItem { 6 /** 7 * 项类型 8 * */ 9 private EnumDataType ItemType = null; 10 11 /** 12 * 获取项类型 13 * */ 14 public EnumDataType getItemType() { 15 return ItemType; 16 } 17 18 /** 19 * 设置项类型 20 * 21 * @param itemType 22 * 项类型 23 * */ 24 public void setItemType(EnumDataType itemType) { 25 ItemType = itemType; 26 } 27 28 /** 29 * 项值 30 * */ 31 private String ItemValue = null; 32 33 /** 34 * 获取项值 35 * */ 36 public String getItemValue() { 37 return ItemValue; 38 } 39 40 /** 41 * 设置项值 42 * 43 * @param itemValue 44 * 项值 45 * */ 46 public void setItemValue(Object itemValue) { 47 ItemValue = itemValue.toString(); 48 } 49 50 /** 51 * 项公式(内嵌公式——子公式) 52 * */ 53 private Formula subFormula = null; 54 55 /** 56 * 获取项公式(内嵌公式——子公式) 57 * */ 58 public Formula getSubFormula() { 59 return subFormula; 60 } 61 62 /** 63 * 设置项公式(内嵌公式——子公式) 64 * 65 * @param subFormula 66 * 项公式(内嵌公式——子公式) 67 * */ 68 public void setSubFormula(Formula subFormula) { 69 this.subFormula = subFormula; 70 } 71 72 public FormulaItem(){ 73 //this.subFormula=new Formula(); 74 } 75 }
三、在这些公式里面有很多的项需要放在一起处理(比如IF运算从IF 到THEN 再到 ELSE 再到 END)这是一个完整的项,需要把它放在一个集合里面。于是需要再建立一个公式的集合类。
1 /** 2 * @author LC 3 * 4 */ 5 package Formula; 6 7 import java.util.ArrayList; 8 import java.util.List; 9 10 public class Formula { 11 /** 12 * 需要计算的公式 13 * */ 14 private String formula = null; 15 16 /** 17 * 获取需要计算的公式 18 * */ 19 public String getFormula() { 20 return formula; 21 } 22 23 /** 24 * 设置需要计算的公式 25 * 26 * @param formula 27 * 需要计算的公式 28 * @throws Exception 29 * 可能抛出公式定义错误的错误! 30 * */ 31 public void setFormula(String formula) throws Exception { 32 if (!judgmentFormula(formula)) 33 throw new ArithmeticException("公式错误,请重新定义!"); 34 this.formula = formula; 35 } 36 37 /** 38 * 公式项集合 39 * */ 40 private List<FormulaItem> formulas = null; 41 42 /** 43 * 获取公式各项 44 * */ 45 public List<FormulaItem> getSubFormula() { 46 return formulas; 47 } 48 49 /** 50 * 获取指定运算符在项中的位置。 51 * 52 * @param value 53 * 运算符 54 * */ 55 private int getSubFormulaIndex(String value) { 56 for (FormulaItem formulaItem : this.formulas) { 57 if (formulaItem.getItemValue().equals(value)) 58 return this.formulas.indexOf(formulaItem); 59 } 60 return -1; 61 } 62 63 /** 64 * 设置公式各项 65 * 66 * @param Formulas 67 * 公式项集合 68 * */ 69 private void setSubFormula(List<FormulaItem> formulas) { 70 this.formulas = formulas; 71 } 72 73 private String division = "§"; 74 75 public String getDivision() { 76 return this.division; 77 } 78 79 public String setDivision(String divi) throws Exception { 80 try { 81 EnumDataType.valueOf(divi); 82 } catch (Exception e) { 83 return this.division; 84 } 85 throw new ArithmeticException("你不能设置(" + divi + ")这个分隔符,因为这是保留字符!"); 86 } 87 88 /** 89 * 创建一个需要计算的公式 90 * */ 91 public Formula() { 92 this.formulas = new ArrayList<FormulaItem>(); 93 } 94 95 /** 96 * 创建一个需要计算的公式 97 * 98 * @param formula 99 * 需要计算的公式 100 * @throws Exception 101 * 可能抛出公式定义错误的错误! 102 * */ 103 public Formula(String formula) throws Exception { 104 setFormula(formula); 105 } 106 107 /** 108 * 判断公式是否成立 109 * 110 * @param formula 111 * 需要计算的公式 112 * */ 113 private boolean judgmentFormula(String formula) { 114 return tryParseItem(formula); 115 } 116 117 /** 118 * 尝试把公式转换项 119 * 120 * @param formula 121 * 需要计算的公式 122 * */ 123 private boolean tryParseItem(String formula) { 124 List<FormulaItem> listFormulaItem = new ArrayList<FormulaItem>(); 125 String[] formulaItems = formula.split(this.division); 126 for (String formulaItem : formulaItems) { 127 try { 128 listFormulaItem.add(ParseItem(formulaItem)); 129 } catch (Exception e) { 130 return false; 131 } 132 } 133 try { 134 setSubFormula(ParseItem(listFormulaItem).formulas); 135 return true; 136 } catch (Exception e) { 137 return false; 138 } 139 } 140 141 /** 142 * 递归,为公式分层次 143 * */ 144 private Formula ParseItem(List<FormulaItem> listFormulaItem) 145 throws Exception { 146 Formula formula = new Formula(); 147 formula.formulas = new ArrayList<FormulaItem>(); 148 boolean bool = true; 149 int i = 0; 150 while (bool) { 151 FormulaItem formulaItem = listFormulaItem.get(i); 152 // 语句判断 153 if (formulaItem.getItemValue().contains("IFB") 154 || formulaItem.getItemValue().contains("MAB") 155 || formulaItem.getItemValue().contains("MIB") 156 || formulaItem.getItemValue().contains("(")) { 157 FormulaItem item = new FormulaItem(); 158 Formula theformula = new Formula(); 159 theformula.formulas.add(formulaItem); 160 listFormulaItem.remove(formulaItem); 161 theformula.formulas.addAll(ParseItem(listFormulaItem).formulas); 162 item.setItemType(formulaItem.getItemType()); 163 item.setSubFormula(theformula); 164 formula.formulas.add(item); 165 } else if (formulaItem.getItemValue().contains("IFE") 166 || formulaItem.getItemValue().contains("MAE") 167 || formulaItem.getItemValue().contains("MIE") 168 || formulaItem.getItemValue().contains(")")) { 169 formula.formulas.add(formulaItem); 170 listFormulaItem.remove(formulaItem); 171 return formula; 172 } else { 173 formula.formulas.add(formulaItem); 174 listFormulaItem.remove(formulaItem); 175 } 176 // 语句判断 177 if (listFormulaItem.size() == 0) 178 bool = false; 179 } 180 return formula; 181 } 182 183 /** 184 * 转换公式为项 185 * */ 186 private FormulaItem ParseItem(String formulaItem) throws Exception { 187 FormulaItem FormulaItem = new FormulaItem(); 188 switch (formulaItem) { 189 case "MAB": 190 case "MAE": 191 FormulaItem.setItemType(EnumDataType.Max); 192 FormulaItem.setItemValue(formulaItem); 193 break; 194 case "MIB": 195 case "MIE": 196 FormulaItem.setItemType(EnumDataType.Min); 197 FormulaItem.setItemValue(formulaItem); 198 break; 199 case "IFB": 200 case "IFT": 201 case "IFL": 202 case "IFD": 203 FormulaItem.setItemType(EnumDataType.If); 204 FormulaItem.setItemValue(formulaItem); 205 break; 206 case "&&": 207 case "||": 208 case "!": 209 FormulaItem.setItemType(EnumDataType.Logic); 210 FormulaItem.setItemValue(formulaItem); 211 break; 212 case "+": 213 case "-": 214 case "*": 215 case "/": 216 case "%": 217 FormulaItem.setItemType(EnumDataType.Calculate); 218 FormulaItem.setItemValue(formulaItem); 219 break; 220 case "(": 221 case ")": 222 FormulaItem.setItemType(EnumDataType.Region); 223 FormulaItem.setItemValue(formulaItem); 224 break; 225 case "=": 226 case ">": 227 case ">=": 228 case "<": 229 case "<=": 230 case "<>": 231 FormulaItem.setItemType(EnumDataType.Compare); 232 FormulaItem.setItemValue(formulaItem); 233 break; 234 default: 235 FormulaItem.setItemType(EnumDataType.Data); 236 FormulaItem.setItemValue(formulaItem); 237 break; 238 } 239 return FormulaItem; 240 } 241 242 /** 243 * 计算所有的公式并返回 244 * 245 * @return double 公式最红计算值 246 * */ 247 public String compute() { 248 return run(this).getItemValue(); 249 } 250 251 private FormulaItem run(Formula formula) { 252 boolean bool = true; 253 for (int i = 0; i < formula.formulas.size(); i++) { 254 if (formula.formulas.get(i).getSubFormula() != null) { 255 formula.formulas.set(i, run(formula.formulas.get(i) 256 .getSubFormula())); 257 } 258 } 259 String[] Operators = new String[] { "*", "/", "%", "+", "-", ">", ">=", 260 "<", "<=", "<>", "=", "&&", "||", "!" }; 261 for (String Operator : Operators) { 262 while (bool) { 263 int index = formula.getSubFormulaIndex(Operator); 264 if (index == -1) 265 break; 266 FormulaItem subFormulaItem = formula.formulas.get(index); 267 Object data = null; 268 if (subFormulaItem.getItemType() == EnumDataType.Calculate) 269 data = mathematicalOperation(formula.formulas 270 .get(index - 1).getItemValue(), Operator, 271 formula.formulas.get(index + 1).getItemValue()); 272 else if (subFormulaItem.getItemType() == EnumDataType.Compare) 273 data = comparisonOperation(formula.formulas.get(index - 1) 274 .getItemValue(), Operator, 275 formula.formulas.get(index + 1).getItemValue()); 276 else if (subFormulaItem.getItemType() == EnumDataType.Logic) 277 data = logicalOperation(formula.formulas.get(index - 1) 278 .getItemValue(), Operator, 279 formula.formulas.get(index + 1).getItemValue()); 280 subFormulaItem.setItemType(EnumDataType.Data); 281 subFormulaItem.setItemValue(data); 282 subFormulaItem.setSubFormula(null); 283 284 List<FormulaItem> lfItem = new ArrayList<FormulaItem>(); 285 lfItem.add(formula.formulas.get(index - 1)); 286 lfItem.add(formula.formulas.get(index + 1)); 287 formula.formulas.removeAll(lfItem); 288 } 289 } 290 Operators = new String[] { "MA", "MI", "IF" }; 291 for (String Operator : Operators) { 292 Object data = null; 293 int index = formula.getSubFormulaIndex(Operator + "B"); 294 if (index == -1) 295 continue; 296 if (index != -1 297 && (formula.formulas.get(index).getItemType() == EnumDataType.Max || formula.formulas 298 .get(index).getItemType() == EnumDataType.Min)) 299 data = mathematicalOperation(formula.formulas.get(index + 1) 300 .getItemValue(), formula.formulas.get(index) 301 .getItemType().toString(), 302 formula.formulas.get(index + 3).getItemValue()); 303 if (index != -1 304 && formula.formulas.get(index).getItemType() == EnumDataType.If) 305 data = ifOperation(formula.formulas.get(index + 1) 306 .getItemValue(), formula.formulas.get(index + 3) 307 .getItemValue().toString(), 308 formula.formulas.get(index + 5).getItemValue()); 309 formula.formulas.clear(); 310 FormulaItem subFormulaItem = new FormulaItem(); 311 subFormulaItem.setItemType(EnumDataType.Data); 312 subFormulaItem.setItemValue(data); 313 subFormulaItem.setSubFormula(null); 314 formula.formulas.add(subFormulaItem); 315 } 316 bool = true; 317 Operators = new String[] { "(", ")" }; 318 for (String Operator : Operators) { 319 while (bool) { 320 int index = formula.getSubFormulaIndex(Operator); 321 if (index != -1) 322 formula.formulas.remove(index); 323 else 324 break; 325 } 326 } 327 return formula.formulas.get(0); 328 } 329 330 /** 331 * 数学运算 ——加,减,乘,除,最大,最小 332 * 333 * @param a 334 * 第一个运算数 335 * @param perator 336 * 运算符号 337 * @param b 338 * 第二个运算数 339 * */ 340 private double mathematicalOperation(String a, String perator, String b) { 341 switch (perator) { 342 case "+": 343 return Double.parseDouble(a) + Double.parseDouble(b); 344 case "-": 345 return Double.parseDouble(a) - Double.parseDouble(b); 346 case "*": 347 return Double.parseDouble(a) * Double.parseDouble(b); 348 case "/": 349 return Double.parseDouble(a) / Double.parseDouble(b); 350 case "%": 351 return Double.parseDouble(a) % Double.parseDouble(b); 352 case "Max": 353 return Double.parseDouble(a) > Double.parseDouble(b) ? Double 354 .parseDouble(a) : Double.parseDouble(b); 355 case "Min": 356 return Double.parseDouble(a) < Double.parseDouble(b) ? Double 357 .parseDouble(a) : Double.parseDouble(b); 358 } 359 return 0; 360 } 361 362 /** 363 * 逻辑运算 ——与,或,非 364 * 365 * @param a 366 * 第一个运算数 367 * @param perator 368 * 运算符号 369 * @param b 370 * 第二个运算数 371 * */ 372 private boolean logicalOperation(String a, String perator, String b) { 373 switch (perator) { 374 case "&&": 375 return (a == "true" ? true : false) && (b == "true" ? true : false); 376 case "||": 377 return (a == "true" ? true : false) || (b == "true" ? true : false); 378 case "!": 379 return (a == "true" ? true : false) != (b == "true" ? true : false); 380 } 381 return true; 382 } 383 384 /** 385 * 比较运算 ——大于,大于等于,小于,小于等于,不等于,等于, 386 * 387 * @param a 388 * 第一个运算数 389 * @param perator 390 * 运算符号 391 * @param b 392 * 第二个运算数 393 * */ 394 private boolean comparisonOperation(String a, String perator, String b) { 395 switch (perator) { 396 case ">": 397 return Double.parseDouble(a) > Double.parseDouble(b); 398 case ">=": 399 return Double.parseDouble(a) >= Double.parseDouble(b); 400 case "<": 401 return Double.parseDouble(a) < Double.parseDouble(b); 402 case "<=": 403 return Double.parseDouble(a) <= Double.parseDouble(b); 404 case "<>": 405 return Double.parseDouble(a) != Double.parseDouble(b); 406 case "=": 407 return Double.parseDouble(a) == Double.parseDouble(b); 408 } 409 return true; 410 } 411 412 /** 413 * IF运算 414 * 415 * @param bool 416 * IF判断的条件 417 * @param T 418 * 当bool为真的时候取值 419 * @param E 420 * 当bool为假的时候取值 421 * */ 422 private String ifOperation(String bool, String T, String E) { 423 if (bool == "true") 424 return T; 425 else 426 return E; 427 } 428 }
公式集合的这个类,是整个包的核心类,所有的计算都在里面完成。
公式集合类里面有一个属性:
/** * 需要计算的公式 * */ private String formula = null;
这个属性记录传入的需要计算的公式,对于这个公式每一项与每一项之间需要用【§】进行分割。
也可以使用自己的分隔符:
/** * 分割符号 * */ private String division = "§"; /** * 获取分隔符号 * */ public String getDivision() { return this.division; } /** * 设置分隔符号 * @param divi 需要设置的分隔符 * */ public String setDivision(String divi) throws Exception { try { EnumDataType.valueOf(divi); } catch (Exception e) { return this.division; } throw new ArithmeticException("你不能设置(" + divi + ")这个分隔符,因为这是保留字符!"); }
不过对于传入的分割符,不能与枚举类中的枚举值相同。不让程序就无法运行了。
因为需要传入计算公式所以对于计算公式的写法也需要规定:
case "MAB":最大值开始符号 case "MAE":最大值结束符号case "MIB":最小值开始符号 case "MIE":最小值结束符号case "IFB":IF运算开始符号 case "IFT":IF运算THEN符号 case "IFL":IF运算ELSE符号 case "IFD":IF运算结束符号case "&&":与运算 case "||":或预算 case "!":非运算case "+":加运算 case "-":减运算 case "*":乘运算 case "/":除运算 case "%":末除以运算case "(":左括号 case ")":右括号case "=":等于比较 case ">":大于比较 case ">=":大于等于比较 case "<":小于比较 case "<=":小于等于比较 case "<>":不等于比较
在传入公式以后,程序会用【§】符号或自定义的符号对公式进行分割。
分割完成以后程序就会更具分割玩的字符把所有的单个运算符号经过判断以后转换乘一个运算项。
得到一个集合这个集合里面装着所有的运算符号,但是这样还没有办法完成计算。
接下来需要用递归的方法把运算公式拆分,拆分成单个的完整的运算。
/** * 递归,为公式分层次 * */ private Formula ParseItem(List<FormulaItem> listFormulaItem)//在这里传入一个公式的集合 throws Exception { Formula formula = new Formula(); formula.formulas = new ArrayList<FormulaItem>(); boolean bool = true; int i = 0; while (bool) { FormulaItem formulaItem = listFormulaItem.get(i);//获取该组公式的第一个运算符,因为i不会自增。所以他每次获取的都是第一个。 // 语句判断 if (formulaItem.getItemValue().contains("IFB") || formulaItem.getItemValue().contains("MAB") || formulaItem.getItemValue().contains("MIB") || formulaItem.getItemValue().contains("(")) {//如果是IF或者MAB或者MIB也就是IF运算或最大值运算或最小值运算的开始符号的话就进入到这个if体里面 FormulaItem item = new FormulaItem(); Formula theformula = new Formula(); theformula.formulas.add(formulaItem); listFormulaItem.remove(formulaItem);//删除处理后的运算符 theformula.formulas.addAll(ParseItem(listFormulaItem).formulas);//在红色字符那里开始递归,将删除后的公式集合再次传入到这个方法 item.setItemType(formulaItem.getItemType()); item.setSubFormula(theformula); formula.formulas.add(item); } else if (formulaItem.getItemValue().contains("IFE") || formulaItem.getItemValue().contains("MAE") || formulaItem.getItemValue().contains("MIE") || formulaItem.getItemValue().contains(")")) {//如果是IF运算MAX运算MIN运算的结束符号就进入到这个if体里面 formula.formulas.add(formulaItem); listFormulaItem.remove(formulaItem);//删除处理后的公式 return formula;//返回一个公式集合,将和开始项祝贺乘一个完整的单独运算公式 } else { formula.formulas.add(formulaItem); listFormulaItem.remove(formulaItem); } // 语句判断 if (listFormulaItem.size() == 0) bool = false; } return formula; }
通过递归完成对公式集合的处理。这样就可以开始计算了。
这是公式计算里面最复杂的一个方法,用于处理最后的计算工作:
/** * 开始计算 * * @param formula * 需要计算的公式 * */ private FormulaItem run(Formula formula) { boolean bool = true; for (int i = 0; i < formula.formulas.size(); i++) { if (formula.formulas.get(i).getSubFormula() != null) {//对于每一个子项公式不是空的项都需要将子公式集合递归到本方法计算然后返回计算后的唯一结果。 formula.formulas.set(i, run(formula.formulas.get(i) .getSubFormula())); } } String[] Operators = new String[] { "*", "/", "%", "+", "-", ">", ">=", "<", "<=", "<>", "=", "&&", "||", "!" };//给计算分优先级,括号里面的运算优先级最高。但是括号里面的运算都会放到一个子公式集合。所以这里不需要处理 for (String Operator : Operators) {//循环处理每一种运算符 while (bool) { int index = formula.getSubFormulaIndex(Operator);//查找在当前传入的公式集合里面是否有当前运算符。 if (index == -1) break;//没有该运算符,跳过该运算符 FormulaItem subFormulaItem = formula.formulas.get(index);//获取该运算符的项 Object data = null; if (subFormulaItem.getItemType() == EnumDataType.Calculate)//判断是那一总运算,更具不同的运算将使用不同的处理方式 data = mathematicalOperation(formula.formulas .get(index - 1).getItemValue(), Operator, formula.formulas.get(index + 1).getItemValue()); else if (subFormulaItem.getItemType() == EnumDataType.Compare) data = comparisonOperation(formula.formulas.get(index - 1) .getItemValue(), Operator, formula.formulas.get(index + 1).getItemValue()); else if (subFormulaItem.getItemType() == EnumDataType.Logic) data = logicalOperation(formula.formulas.get(index - 1) .getItemValue(), Operator, formula.formulas.get(index + 1).getItemValue()); subFormulaItem.setItemType(EnumDataType.Data); subFormulaItem.setItemValue(data); subFormulaItem.setSubFormula(null); List<FormulaItem> lfItem = new ArrayList<FormulaItem>(); lfItem.add(formula.formulas.get(index - 1)); lfItem.add(formula.formulas.get(index + 1)); formula.formulas.removeAll(lfItem); } } Operators = new String[] { "MA", "MI", "IF" };//在上面处理完优先级别较高的运算符,在这里处理优先级别最低的运算 for (String Operator : Operators) { Object data = null; int index = formula.getSubFormulaIndex(Operator + "B"); if (index == -1) continue; if (index != -1 && (formula.formulas.get(index).getItemType() == EnumDataType.Max || formula.formulas .get(index).getItemType() == EnumDataType.Min)) data = mathematicalOperation(formula.formulas.get(index + 1) .getItemValue(), formula.formulas.get(index) .getItemType().toString(), formula.formulas.get(index + 3).getItemValue()); if (index != -1 && formula.formulas.get(index).getItemType() == EnumDataType.If) data = ifOperation(formula.formulas.get(index + 1) .getItemValue(), formula.formulas.get(index + 3) .getItemValue().toString(), formula.formulas.get(index + 5).getItemValue()); formula.formulas.clear(); FormulaItem subFormulaItem = new FormulaItem(); subFormulaItem.setItemType(EnumDataType.Data); subFormulaItem.setItemValue(data); subFormulaItem.setSubFormula(null); formula.formulas.add(subFormulaItem); } bool = true; Operators = new String[] { "(", ")" };//在除里面各种运算以后,需要删除不需要的符号"(",")"在这里括号主要用于产生子项公式集合解决他的优先级别问题。在运算完成以后这就是没用的符号需要删除。以方便获取最终计算值。 for (String Operator : Operators) { while (bool) { int index = formula.getSubFormulaIndex(Operator); if (index != -1) formula.formulas.remove(index); else break; } } return formula.formulas.get(0);//返回最终的计算值,通过一层一层的递归将产生一个最终的结果。 }
到这里整个公式的处理逻辑已经基本讲完。
整个逻辑总结下来,用到最主要的是递归方法,通过递归完成公式的分组(才分到子项集合生成单独的完整的独立公式);
可能逻辑或者处理方面还会有一些漏洞,或则有一些处理方法不够好。希望大家可以提出来。
最后提供一个测试代码:
try { Formula formula = new Formula( "IFB§(§1§+§20§)§>§8§IFT§10§IFL§(§MAB§(§100§)§,§2§MAE§)§IFE"); System.out.println(formula.compute()); } catch (Exception e) { System.out.println(e.toString()); }
程序项目连接: http://pan.baidu.com/s/1nGu8u
浙公网安备 33010602011771号