Переглянути джерело

Merge branch 'Psycho/feature-xkw' into develop

OnePsycho 3 роки тому
батько
коміт
58d79a2c75

+ 0 - 8
TEAMModelOS.SDK/Helper/Common/ReflectorExtensions/ReflectorExtensions.cs

@@ -53,14 +53,6 @@ namespace TEAMModelOS.SDK.Helper.Common.ReflectorExtensions
                 foreach (var model in ScanModel)
                 {
                     Assembly assembly = Assembly.LoadFrom(currentDirectory + "\\" + model + ".dll");
-                    var TypeInModelS = assembly.GetTypes().SelectMany(x => x.GetMethods()).GroupBy(z=>z.Name).ToList();
-                    TypeInModelS.ForEach(x => {
-                        if (x.Key.Equals("GetTeacherInfo"))
-                        {
-                            var at = x.ToList().SelectMany(z => z.GetCustomAttributes()).Where(m=>m.GetType().Equals(attr));
-                            string ke = x.Key;
-                        }
-                    });
                     var  TypeInModel = assembly.GetTypes().Select(x => x.GetMethods()).SelectMany(y => y).Select(z=>z.GetCustomAttribute(attr,true)).Where(n=>n!=null);
                     attributes.AddRange(TypeInModel);
                 }

+ 6 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/BaseItem.cs

@@ -59,5 +59,11 @@ namespace TEAMModelOS.SDK.Models
         /// //选项数量
         /// </summary>
         public int opts { get; set; }
+
+        /// <summary>
+        /// 来源 0.(学校,个人)文档导入 1.(学校,个人)自建 3.学科网
+        /// </summary>
+        public int source { get; set; }
+        public string tag { get; set; }
     }
 }

+ 4 - 0
TEAMModelOS/ClientApp/src/api/auth.js

@@ -15,5 +15,9 @@ export default {
     xkwAuthorize: function (data) {
         return post('/xkw/authorize', data)
     },
+    /* 获取学科网的试题集合 */
+    getXkwItems: function (data) {
+        return post('/xkw/get-paper-items', data)
+    },
 
 }

+ 9 - 0
TEAMModelOS/ClientApp/src/router/routes.js

@@ -441,6 +441,15 @@ export const routes = [{
 				activeName: 'schoolBank'
 			},
 		},
+		{
+			path: 'xkwPage',
+			name: 'xkwPage',
+			// component: resolve => require(['@/view/answersheet/index.vue'], resolve),
+			component: resolve => require(['@/view/evaluation/index/XkwPage.vue'], resolve),
+			meta: {
+				activeName: 'schoolBank'
+			},
+		},
 		{
 			path: 'testPaper',
 			name: 'testPaper',

+ 16 - 2
TEAMModelOS/ClientApp/src/view/evaluation/bank/index.vue

@@ -31,9 +31,9 @@
               <Icon type="md-hand" size="16" />
               <span>{{ $t('evaluation.index.manualCreate') }}</span>
             </span>
-            <span @click="goCreatePaper('import',true)" class="bank-tools-btn">
+            <span @click="goXkwPick()" class="bank-tools-btn">
               <Icon type="ios-send" size="16" />
-              <span>学科网组卷</span>
+              <span>{{ $t('evaluation.xkwMode') }}</span>
             </span>
           </div>
         </div>
@@ -170,6 +170,20 @@ export default {
       })
     },
 
+    goXkwPick() {
+      this.$api.auth.xkwOauth({
+        module: 'ezj',
+        agree: 1
+      }).then(res => {
+        this.$router.push({
+          name: 'xkwPage',
+          params: {
+            iframeSrc: res.redirect,
+          }
+        })
+      })
+    },
+
     /** 前往组卷页面 */
     goCreatePaper(type, isXkwMode) {
       if (isXkwMode) {

+ 60 - 19
TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue

@@ -83,12 +83,8 @@
                 <TabPane :label="evaluationInfo.item.length ? $t('evaluation.paperList.tab3') : $t('evaluation.paperList.tab4')" name="import" v-if="evaluationInfo.createType == 'import' && !isXkwMode" :index="3" tab="createTest">
                   <BaseImport @importFinish="onImportFinish"></BaseImport>
                 </TabPane>
-                <!-- 学科网 -->
-                <TabPane :label="$t('evaluation.xkwMode')" name="import" v-if="isXkwMode" :index="4" tab="createTest">
-                  <iframe id="xkwIframe" :src="iframeSrc" frameborder="0" width="100%" height="100%"></iframe>
-                </TabPane>
                 <!-- 预览试卷 -->
-                <TabPane :label="$t('evaluation.paperList.tab5')" name="preview" :index="5" tab="createTest">
+                <TabPane :label="$t('evaluation.paperList.tab5')" name="preview" :index="4" tab="createTest">
                   <vuescroll ref="paperRef" @handle-scroll="handleScroll">
                     <TestPaper v-if="!examAnalysisStatus" :paper="evaluationInfo" :class="examAnalysisStatus ? '':'animated fadeIn'" ref="testPaper" @onViewModelChange="onViewModelChange" :isPreviewItems="!isGeneratePaper">
                     </TestPaper>
@@ -118,6 +114,7 @@ export default {
   },
   data() {
     return {
+      isLoadingXkw: false,
       isXkwMode: false,
       iframeSrc: '',
       tags: [], //试卷标签
@@ -174,13 +171,24 @@ export default {
     }
   },
   async created() {
-    /* 查询当前是否有保存的组卷方式,如果已经保存则直接读取,优先读取传过来的参数 */
-    // let curCreateType = localStorage.getItem('curCreateType')
-    this.evaluationInfo.createType = this.$route.params.type || 'edit'
-    this.isXkwMode = this.$route.params.isXkwMode
-    this.iframeSrc = this.isXkwMode ? this.$route.params.iframeSrc : ''
-    this.setActiveTab(this.evaluationInfo.createType)
-    localStorage.setItem('curCreateType', this.evaluationInfo.createType)
+    let xkwItems = null
+    if (this.$route.query.paperid && this.$route.query.openid) {
+      this.isXkwMode = true
+      this.isLoadingXkw = true
+      xkwItems = await this.getXkwItems()
+      this.isLoading = false
+      this.isLoadingXkw = false
+    }
+    if (xkwItems) {
+      this.evaluationInfo.createType = 'import'
+      this.activeTab = 'preview'
+      this.renderXkwPaperItems(xkwItems)
+    } else {
+      this.evaluationInfo.createType = this.$route.params.type || 'manual'
+      this.setActiveTab(this.evaluationInfo.createType)
+      localStorage.setItem('curCreateType', this.evaluationInfo.createType)
+    }
+
     this.evaluationInfo.type = this.$route.name === 'newSchoolPaper' ? 'school' : 'private'
     this.evaluationInfo.scope = this.evaluationInfo.type
     if (this.$route.params.isFromItemBank) {
@@ -194,15 +202,48 @@ export default {
       this.isGeneratePaper = true
       this.curMode = 'paper'
     }
-
-    // this.getSchoolBaseInfo().then(res => {
-    // 	if (!res) return
-    // 	this.schoolInfo = res
-    // 	this.onPeriodChange(0)
-    // })
     sessionStorage.setItem('isSave', 0)
   },
   methods: {
+    /* 获取学科网的数据 */
+    getXkwItems() {
+      return new Promise((r, j) => {
+        this.isLoading = true
+        this.$api.auth.getXkwItems(this.$route.query).then(res => {
+          if (res.itemInfos) {
+            r(res)
+          } else {
+            j(null)
+          }
+        }).catch(err => {
+          j(null)
+        })
+      })
+    },
+    /* 渲染学科网选择的试题内容 */
+    renderXkwPaperItems(res) {
+      if (!res.error) {
+        this.onImportFinish({
+          list: res.itemInfos,
+        })
+        let curPeriodName = this.schoolInfo.period[this.evaluationInfo.paperPeriod].name
+        let curSubjectName = this.subjectList[this.evaluationInfo.paperSubject].name
+        if (curPeriodName !== res.periodName) {
+          this.$Notice.info({
+            title: '温馨提示',
+            desc: '题目来源学段与当前所选试题学段不一致,建议您进行调整!',
+            duration: 10
+          });
+        }
+        if (curSubjectName !== res.subjectName) {
+          this.$Notice.info({
+            title: '温馨提示',
+            desc: '题目来源科目与当前所选试题科目不一致,建议您进行调整!',
+            duration: 10
+          });
+        }
+      }
+    },
     /* 学段切换 */
     onPeriodChange(val) {
       this.gradeList = this.schoolInfo.period[val].grades
@@ -1600,7 +1641,7 @@ export default {
                   size: 18
                 }
               }),
-              h('div', this.$t('evaluation.saving'))
+              h('div', this.isLoadingXkw ? 'Loading' : this.$t('evaluation.saving'))
             ])
           }
         });

+ 26 - 0
TEAMModelOS/ClientApp/src/view/evaluation/index/XkwPage.vue

@@ -0,0 +1,26 @@
+<template>
+  <div class="xkw-iframe-container">
+    <iframe id="xkwIframe" :src="iframeSrc" frameborder="0" width="100%" height="100%"></iframe>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      iframeSrc: ""
+    }
+  },
+  created() {
+    this.iframeSrc = this.$route.params.iframeSrc
+  }
+}
+</script>
+
+<style>
+.xkw-iframe-container {
+  width: 100%;
+  height: calc(100vh - 60px);
+  overflow: hidden;
+}
+</style>

+ 2 - 2
TEAMModelOS/Controllers/School/SchoolController.cs

@@ -1425,12 +1425,12 @@ namespace TEAMModelOS.Controllers
             }
             catch (CosmosException ex)
             {
-                await _dingDing.SendBotMsg($"OS,{_option.Location},schoool/teacher-space\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"OS,{_option.Location},schoool/teacher-space\n{ex.Message}\n{ex.StackTrace}\n{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"OS,{_option.Location},schoool/teacher-space\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"OS,{_option.Location},schoool/teacher-space\n{ex.Message}\n{ex.StackTrace}\n{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
             }
         }

+ 26 - 8
TEAMModelOS/Controllers/Third/Xkw/Sdk/XkwConstant.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using TEAMModelOS.SDK.Models;
 
 namespace TEAMModelOS.Controllers.Third.Xkw.Sdk
 {
@@ -116,14 +117,14 @@ namespace TEAMModelOS.Controllers.Third.Xkw.Sdk
         public static readonly List<XkwDifficulty> xkwDifficulties = new List<XkwDifficulty>()
         {
             //new XkwDifficulty{ id=17,name="容易",ceiling=0.99,flooring=0.9  },
-            new XkwDifficulty{ id=17,name="容易",ceiling=1.0,flooring=0.9  },
-            new XkwDifficulty{ id=18,name="较易",ceiling=0.9,flooring=0.8  },
-            new XkwDifficulty{ id=19,name="一般",ceiling=0.8,flooring=0.5  },
-            new XkwDifficulty{ id=20,name="较难",ceiling=0.5,flooring=0.3  },
-            new XkwDifficulty{ id=21,name="困难",ceiling=0.3,flooring=0.0  },
+            new XkwDifficulty{ id=17,name="容易",ceiling=1.0,flooring=0.9 ,level=1  },
+            new XkwDifficulty{ id=18,name="较易",ceiling=0.9,flooring=0.8 ,level=2 },
+            new XkwDifficulty{ id=19,name="一般",ceiling=0.8,flooring=0.5 ,level=3 },
+            new XkwDifficulty{ id=20,name="较难",ceiling=0.5,flooring=0.3 ,level=4 },
+            new XkwDifficulty{ id=21,name="困难",ceiling=0.3,flooring=0.0 ,level=5 },
         };
 
-        public static readonly List<XkwQuestionType> xkwQuestionTypees = new List<XkwQuestionType>()
+        public static readonly List<XkwQuestionType> xkwQuestionTypes = new List<XkwQuestionType>()
         {
             new XkwQuestionType{id="0101",name="选择题",objective=true,course_id=1},
             new XkwQuestionType{id="0118",name="互动连线题",objective=false,course_id=1},
@@ -722,6 +723,7 @@ namespace TEAMModelOS.Controllers.Third.Xkw.Sdk
         public string name { get; set; }
         public double ceiling { get; set; }
         public double flooring { get; set; }
+        public int level { get; set; }
     }
     public class XkwDto { 
         public int id { get; set; }
@@ -736,8 +738,24 @@ namespace TEAMModelOS.Controllers.Third.Xkw.Sdk
     /// <summary>
     /// 学科网题目结构
     /// </summary>
-    public class XkwPaper { 
-    
+    public class XkwPaperPart
+    {
+        public List<XkwBody> part_body { get; set; } = new List<XkwBody>();
+    }
+    /// <summary>
+    /// 学科网题目结构
+    /// </summary>
+    public class XkwBody {
+        public List<XkwItem> questions { get; set; } = new List<XkwItem>();
+    }
+
+    public class XkwItemInfo : ItemInfo
+    {
+        public string stemHtml { get; set; }
+        public string answerHtml { get; set; }
+        public string subjectName { get; set; }
+        public string periodName { get; set; }
+        public List<string> gradeNames { get; set; }
     }
     /// <summary>
     /// 学科网题目结构

+ 2 - 1
TEAMModelOS/Controllers/Third/Xkw/XkwOAuth2Controller.cs

@@ -300,7 +300,8 @@ namespace TEAMModelOS.Controllers
                                 domain_port = "kong.sso.com:5001";
                             }
                             OAuth_Xkw_ServiceUrl = OAuth_Xkw_ServiceUrl.Replace("{{iframe}}", HttpUtility.UrlEncode($"https://{domain_port}/home/newSchoolPaper"))
-                                .Replace("{{notice}}", HttpUtility.UrlEncode($"https://{domain_port}/xkw/paper-notice"))
+                                .Replace("{{notice}}", HttpUtility.UrlEncode($"https://{domain_port}/home/newSchoolPaper"))
+                                //.Replace("{{notice}}", HttpUtility.UrlEncode($"https://{domain_port}/xkw/paper-notice"))
                                 .Replace("{{openid}}", openId).Replace("{{target}}", "");
                         }
                     }

+ 215 - 6
TEAMModelOS/Controllers/Third/Xkw/XkwServiceController.cs

@@ -35,6 +35,9 @@ using Microsoft.Extensions.Primitives;
 using System.Net.Http;
 using TEAMModelOS.Controllers.Third.Xkw.Sdk;
 using TEAMModelOS.SDK.Models.Table;
+using System.Text.RegularExpressions;
+using HtmlAgilityPack;
+using TEAMModelOS.SDK.Helper.Security.ShaHash;
 
 namespace TEAMModelOS.Controllers.Third.Xkw
 { 
@@ -62,6 +65,15 @@ namespace TEAMModelOS.Controllers.Third.Xkw
         private readonly HttpTrigger _httpTrigger;
         private readonly IWebHostEnvironment _environment;
         private readonly XkwAPIHttpService _xkwAPIHttpService;
+        /// <summary>
+        /// 全角
+        /// </summary>
+        string[] aza = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
+        /// <summary>
+        /// 半角
+        /// </summary>
+        string[] azh = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
+
         public IConfiguration _configuration { get; set; }
         public XkwServiceController(XkwAPIHttpService xkwAPIHttpService, IWebHostEnvironment environment, AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage,
             AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IConfiguration configuration, CoreAPIHttpService coreAPIHttpService, ThirdApisService scsApisService, HttpTrigger httpTrigger)
@@ -109,13 +121,210 @@ namespace TEAMModelOS.Controllers.Third.Xkw
             return Ok(authCode);
         }
 
-        [HttpGet("get-paper-items")]
-        //[Authorize(Roles = "IES")]
-        //[AuthToken(Roles = "teacher,admin,area,student")]
-        public async Task<IActionResult> GetPaperItems([FromQuery] OAuthCode authCode)
+        [HttpPost("get-paper-items")]
+#if DEBUG
+#else
+        [Authorize(Roles = "IES")]
+        [AuthToken(Roles = "teacher,admin,area,student")]
+#endif
+        public async Task<IActionResult> GetPaperItems(JsonElement json)
         {
-            await _dingDing.SendBotMsg($"学科网推送消息:{authCode.ToJsonString()}", GroupNames.成都开发測試群組);
-            return Ok(authCode);
+            if (!json.TryGetProperty("paperid", out JsonElement paperid)) { return BadRequest(); }
+            if (!json.TryGetProperty("openid", out JsonElement openid)) { return BadRequest(); }
+            Dictionary<string, string> dict = new Dictionary<string, string>() { { "id", $"{paperid}" }, { "user_id", $"{openid}" } };
+            var paper = _xkwAPIHttpService.Get<JsonElement>("/xopqbm/zj-saas/users/download-papers/details", dict);
+            List<XkwItem> items = new List<XkwItem>();
+            string ps = paper.ToJsonString();
+            XkwDto course = null ;
+            if (paper.TryGetProperty("code", out JsonElement code) && $"{code}".Equals("2000000"))
+            {
+                
+                    if (paper.TryGetProperty("data", out JsonElement data)  &&  data.TryGetProperty("course_id", out JsonElement course_id)) {
+                    course =  XkwConstant.xkwCourses.Find(x => $"{course_id}".Equals($"{x.id}"));
+                    if (course != null) {
+                        if (data.TryGetProperty("body", out JsonElement body)) {
+                            List<XkwPaperPart> paperParts = body.ToObject<List<XkwPaperPart>>();
+                            items.AddRange(paperParts.SelectMany(x => x.part_body).SelectMany(z=>z.questions));
+                        }
+                    }
+                }
+            }
+            List<XkwItemInfo> itemInfos = new List<XkwItemInfo>();
+            List<XkwDto> xkw_points = new List<XkwDto>();
+            var kpoint_ids=items.SelectMany(x => x.kpoint_ids).ToHashSet();
+            if (kpoint_ids.Any()) {
+                Dictionary<string, string> dict_pointids = new Dictionary<string, string>() { { "ids", String.Join(",", kpoint_ids.Select(x => x)) } };
+                var points = _xkwAPIHttpService.Get<JsonElement>("/xopqbm/knowledge-points", dict_pointids);
+
+                if (points.TryGetProperty("code", out JsonElement pcode) && $"{pcode}".Equals("2000000")) {
+                    if (points.TryGetProperty("data", out JsonElement pdata)) {
+                        xkw_points = pdata.ToObject<List<XkwDto>>();
+                    }
+                }
+            }
+            int index = 1;
+            items.ForEach(x => {
+                //x.kpoint_ids
+                XkwItemInfo itemInfo = new XkwItemInfo
+                {
+                    source = 3,
+                    shaCode = ShaHashHelper.GetSHA1(x.stem),
+                    id = x.id,
+                    order = index,
+                    explain = x.explanation,
+                    subjectName = course?.subject_name,
+                    periodName = course?.stage_name,
+                    // explain = x.explanation.Replace("【分析】", "分析:").Replace("【详解】", "详解:")
+                     
+                };
+                index += 1;
+                //设置默认难度,和默认认知层次
+                itemInfo.field = 1;
+                itemInfo.level = 3;
+                foreach (var difct in XkwConstant.xkwDifficulties) {
+                    if (x.difficulty < difct.ceiling && x.difficulty >= difct.flooring) {
+                        itemInfo.level = difct.level;
+                    }
+                }
+                var grades=    XkwConstant.xkwGrades.FindAll(g => x.grade_ids.Contains(g.id));
+                if (grades.Any()) {
+                    itemInfo.gradeNames = grades.Select(g => g.name).ToList();
+                }
+                if (x.kpoint_ids.Any()) {
+                    var points =  xkw_points.FindAll(p => x.kpoint_ids.Contains(p.id));
+                    if (points.Any()) {
+                        
+                        itemInfo.knowledge.AddRange(points.Select(x => x.name));
+                    }
+                }
+                var type= XkwConstant.xkwQuestionTypes.Find(t => t.id == x.type_id);
+                //判断是否 主客观题
+                if (type != null) {
+                    itemInfo.objective =type.objective;
+                    itemInfo.tag = type.name;
+                }
+                else { 
+                    itemInfo.objective = false; 
+                }
+                //如果是客观题,则需要处理选项和答案。
+                if (itemInfo.objective)
+                {
+                    string classpattern = "class=\"([^\"]*)\"";
+                    string pattern = "<span([^>]{0,})>";
+                    string table_pattern = "<table([^>]{0,})>";
+                    string apattern = "<a([^>]{0,})></a>";
+                    string shtml = x.stem;
+                    //去除class 以及span标签"
+                    shtml = Regex.Replace(shtml, table_pattern, "").Replace("</table>", "").Replace("<tr>", "").Replace("<td>", "").Replace("</td>", "").Replace("</tr>", "");
+                    //shtml = Regex.Replace(shtml, classpattern, "");
+                    //shtml = Regex.Replace(shtml, pattern, "");
+                    //shtml = Regex.Replace(shtml, apattern, "");
+                    //shtml = shtml.Replace(" close=\"\" separators=\" | \">", "");
+                    //shtml = shtml.Replace("\t", " ").Replace("<span>", "").Replace("</span>", "").Replace("dir=\"ltr\"", "");
+                    //处理 标签中包含的空格字符
+
+                    //处理题干
+                    (List<CodeValue> options, string question) = OptionProcess(shtml);
+                    itemInfo.option = options;
+                    itemInfo.opts = options.Count;
+                    itemInfo.question = question;
+
+                    //处理答案
+                    //去除class 以及span标签"
+                    string ahtml = x.answer;
+                    ahtml = Regex.Replace(ahtml, classpattern, "");
+                    ahtml = Regex.Replace(ahtml, pattern, "");
+                    ahtml = Regex.Replace(ahtml, apattern, "");
+                    ahtml = ahtml.Replace(" close=\"\" separators=\" | \">", "");
+                    ahtml = ahtml.Replace("\t", " ").Replace("<span>", "").Replace("</span>", "").Replace("dir=\"ltr\"", "");
+                    HashSet<string> ans = new HashSet<string>();
+                    var anstr = BlankTag(ahtml);
+                    for (int idx = 0; idx < 26; idx++)
+                    {
+                        anstr = anstr.Replace(aza[idx], azh[idx]);
+                    }
+
+                    anstr.Select(s => s.ToString()).ToList().ForEach(x =>
+                    {
+                        ans.Add(x);
+                    });
+                    itemInfo.answerHtml = x.answer;
+                    itemInfo.answer = ans.ToList() ;
+                    if (ans.Count > 1)
+                    {
+                        itemInfo.type = "multiple";
+                    }
+                    else {
+                        itemInfo.type = "single";
+                    }
+                }
+                else {
+                    itemInfo.type = "subjective";
+                    itemInfo.question = x.stem;
+                    itemInfo.answer = new List<string>() { HtmlHelper.DoUselessTag(x.answer.ToString()) };
+                }
+                itemInfos.Add(itemInfo);
+
+
+            });
+            HashSet<string> gradeNames = new HashSet<string>();
+            gradeNames = itemInfos.SelectMany(x => x.gradeNames).ToHashSet();
+           // await _dingDing.SendBotMsg($"学科网推送消息:{authCode.ToJsonString()}", GroupNames.成都开发測試群組);
+            return Ok(new { periodName=course.stage_name, subjectName=course.subject_name, gradeNames, knowledge=xkw_points.Select(x=>x.name), itemInfos, paper });
+        }
+
+        private string BlankTag(string tagHtml)
+        {
+            //去掉标签中的Html
+            HtmlDocument doc = new HtmlDocument();
+            doc.LoadHtml(tagHtml);
+            var tagValue = doc.DocumentNode.InnerText.Replace("{", "").Replace("}", "")
+                        .Replace("\n", "").Replace(" ", "").Replace("\t", "").Replace("\r", "")
+                        .Replace("&nbsp;", "").Replace("&emsp;", "").Replace("&emsp;", "");
+            // tagValue = Regex.Replace(tagValue, @"\d", "");
+            tagValue = Regex.Replace(tagValue, @"\s", "");
+            return tagValue;
+        }
+
+        public (List<CodeValue> options, string question) OptionProcess(string question) {
+
+            string Options = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+            string[] optionsKeys = Options.Select(s => s.ToString()).ToArray();
+            for (int idx = 0; idx < 26; idx++)
+            {
+                question = question.Replace(aza[idx], azh[idx]);
+            }
+            List<CodeValue> options = new List<CodeValue>();
+            string optsRgex = optionsKeys[0] + "\\s*(\\.|\\.|\\、|\\:|\\:)([\\s\\S]*?).*"; ;
+            string optsHtml = Regex.Match(question, optsRgex).Value;
+            StringBuilder textImg = new StringBuilder();
+            for (int i = 0; i < optionsKeys.Length - 1; i++)
+            {
+                string optRgex = optionsKeys[i] + "\\s*(\\.|\\.|\\、|\\:|\\:)([\\s\\S]*?)" + optionsKeys[i + 1] + "\\s*(\\.|\\.|\\、|\\:|\\:)";
+                string optHtml = Regex.Match(optsHtml, optRgex).Value;
+                if (string.IsNullOrWhiteSpace(optHtml))
+                {
+                    optRgex = optionsKeys[i] + "\\s*(\\.|\\.|\\、|\\:|\\:).*";
+                    optHtml = Regex.Match(optsHtml, optRgex).Value;
+                }
+                if (!string.IsNullOrEmpty(optHtml))
+                {
+                    optHtml = Regex.Replace(optHtml, optionsKeys[i + 1] + "\\s*(\\.|\\.|\\、|\\:|\\:)", "");
+                    optHtml = optHtml.Substring(2, optHtml.Length - 2);
+                    optHtml = HtmlHelper.DoUselessTag(optHtml);
+                    optHtml = optHtml.TrimStart().TrimEnd();
+                    textImg.Append(HtmlHelper.DoTextImg(optHtml));
+                    options.Add(new CodeValue { code = optionsKeys[i], value = optHtml });
+                }
+            }
+            if (!string.IsNullOrWhiteSpace(optsHtml))
+            {
+                return (options, question.Replace(optsHtml, ""));
+            }
+            else
+            {
+                return (new List<CodeValue>(), question);
+            }
         }
     }
 }