question.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. package learning
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "io/ioutil"
  8. "lims_adapter/dao/learning"
  9. "lims_adapter/model/learning"
  10. "net/http"
  11. "strconv"
  12. "strings"
  13. "dashoo.cn/micro_libary/micro_srv"
  14. "dashoo.cn/micro_libary/myerrors"
  15. "dashoo.cn/micro_libary/request"
  16. "github.com/gogf/gf/os/gtime"
  17. "github.com/gogf/gf/util/gvalid"
  18. "github.com/xuri/excelize/v2"
  19. )
  20. type LearningQuestionService struct {
  21. Dao *dao.LearningQuestionDao
  22. Tenant string
  23. userInfo request.UserInfo
  24. }
  25. func NewLearningQuestionService(ctx context.Context) (*LearningQuestionService, error) {
  26. tenant, err := micro_srv.GetTenant(ctx)
  27. if err != nil {
  28. return nil, fmt.Errorf("获取组合码异常:%s", err.Error())
  29. }
  30. // 获取用户信息
  31. userInfo, err := micro_srv.GetUserInfo(ctx)
  32. if err != nil {
  33. return nil, fmt.Errorf("获取用户信息异常:%s", err.Error())
  34. }
  35. return &LearningQuestionService{
  36. Dao: dao.NewLearningQuestionDao(tenant),
  37. Tenant: tenant,
  38. userInfo: userInfo,
  39. }, nil
  40. }
  41. func (s LearningQuestionService) Get(ctx context.Context, req *learning.LearningQuestionGetReq) (ent *learning.LearningQuestionGetRsp, err error) {
  42. validErr := gvalid.CheckStruct(ctx, req, nil)
  43. if validErr != nil {
  44. return nil, myerrors.NewMsgError(nil, validErr.Current().Error())
  45. }
  46. q, err := s.Dao.Where("Id = ?", req.Id).One()
  47. if err != nil {
  48. return nil, err
  49. }
  50. if q == nil {
  51. return nil, myerrors.NewMsgError(nil, "题目不存在")
  52. }
  53. content := []learning.LearningQuestionOption{}
  54. err = json.Unmarshal([]byte(q.Content), &content)
  55. return &learning.LearningQuestionGetRsp{
  56. LearningQuestion: *q,
  57. Content: content,
  58. }, err
  59. }
  60. func (s LearningQuestionService) List(ctx context.Context, req *learning.LearningQuestionListReq) (int, []*learning.LearningQuestionGetRsp, error) {
  61. dao := &s.Dao.LearningQuestionDao
  62. if req.Name != "" {
  63. dao = dao.Where("Name LIKE ?", fmt.Sprintf("%%%s%%", req.Name))
  64. }
  65. if req.SkillId != 0 {
  66. dao = dao.Where("SkillId = ?", req.SkillId)
  67. }
  68. if req.Type != 0 {
  69. dao = dao.Where("Type = ?", req.Type)
  70. }
  71. total, err := dao.Count()
  72. if err != nil {
  73. return 0, nil, err
  74. }
  75. if req.Page != nil {
  76. if req.Page.Current == 0 {
  77. req.Page.Current = 1
  78. }
  79. if req.Page.Size == 0 {
  80. req.Page.Size = 10
  81. }
  82. dao = dao.Page(req.Page.Current, req.Page.Size)
  83. }
  84. if req.OrderBy != nil && req.OrderBy.Value != "" {
  85. order := "asc"
  86. if req.OrderBy.Type == "desc" {
  87. order = "desc"
  88. }
  89. dao = dao.Order(req.OrderBy.Value, order)
  90. }
  91. ent, err := dao.All()
  92. if err != nil {
  93. return 0, nil, err
  94. }
  95. questions, err := ConvLearningQuestionGetRsp(ent)
  96. if err != nil {
  97. return 0, nil, err
  98. }
  99. return total, questions, err
  100. }
  101. func ConvLearningQuestionGetRsp(ent []*learning.LearningQuestion) ([]*learning.LearningQuestionGetRsp, error) {
  102. var questions []*learning.LearningQuestionGetRsp
  103. for _, q := range ent {
  104. content := []learning.LearningQuestionOption{}
  105. err := json.Unmarshal([]byte(q.Content), &content)
  106. if err != nil {
  107. return nil, err
  108. }
  109. answer := []string{}
  110. for _, item := range content {
  111. if item.IsCorrect {
  112. answer = append(answer, item.Name)
  113. }
  114. }
  115. questions = append(questions, &learning.LearningQuestionGetRsp{
  116. LearningQuestion: *q,
  117. Answer: strings.Join(answer, " "),
  118. Content: content,
  119. })
  120. }
  121. return questions, nil
  122. }
  123. func (s LearningQuestionService) Add(ctx context.Context, req *learning.LearningQuestionAddReq) (int, error) {
  124. validErr := gvalid.CheckStruct(ctx, req, nil)
  125. if validErr != nil {
  126. return 0, myerrors.NewMsgError(nil, validErr.Current().Error())
  127. }
  128. if req.Name == "" && req.NameImage == "" {
  129. return 0, myerrors.NewMsgError(nil, "请输入题目或题目图片")
  130. }
  131. if len(req.Content) < 2 {
  132. return 0, myerrors.NewMsgError(nil, "至少需要两个选项")
  133. }
  134. correctCount := 0
  135. for _, o := range req.Content {
  136. if o.IsCorrect {
  137. correctCount += 1
  138. }
  139. }
  140. if correctCount < 1 {
  141. return 0, myerrors.NewMsgError(nil, "正确答案未设置")
  142. }
  143. if (req.Type == 1 || req.Type == 3) && correctCount != 1 {
  144. return 0, myerrors.NewMsgError(nil, "正确答案只能设置一个")
  145. }
  146. content, err := json.Marshal(req.Content)
  147. if err != nil {
  148. return 0, err
  149. }
  150. id, err := s.Dao.InsertAndGetId(learning.LearningQuestion{
  151. SkillId: req.SkillId,
  152. Name: req.Name,
  153. NameImage: req.NameImage,
  154. Type: req.Type,
  155. Enable: req.Enable,
  156. Content: string(content),
  157. Explanation: req.Explanation,
  158. ExplanationImage: req.ExplanationImage,
  159. OperateBy: s.userInfo.RealName,
  160. CreatedAt: gtime.Now(),
  161. UpdatedAt: gtime.Now(),
  162. })
  163. if err != nil {
  164. return 0, err
  165. }
  166. return int(id), err
  167. }
  168. func (s LearningQuestionService) Update(ctx context.Context, req *learning.LearningQuestionUpdateReq) error {
  169. validErr := gvalid.CheckStruct(ctx, req, nil)
  170. if validErr != nil {
  171. return myerrors.NewMsgError(nil, validErr.Current().Error())
  172. }
  173. if len(req.Content) != 0 {
  174. if req.Type == nil {
  175. return myerrors.NewMsgError(nil, "请输入题型")
  176. }
  177. questionType := *req.Type
  178. if len(req.Content) < 2 {
  179. return myerrors.NewMsgError(nil, "至少需要两个选项")
  180. }
  181. correctCount := 0
  182. for _, o := range req.Content {
  183. if o.IsCorrect {
  184. correctCount += 1
  185. }
  186. }
  187. if correctCount < 1 {
  188. return myerrors.NewMsgError(nil, "正确答案未设置")
  189. }
  190. if (questionType == 1 || questionType == 3) && correctCount != 1 {
  191. return myerrors.NewMsgError(nil, "正确答案只能设置一个")
  192. }
  193. }
  194. q, err := s.Dao.Where("Id = ?", req.Id).One()
  195. if err != nil {
  196. return err
  197. }
  198. if q == nil {
  199. return myerrors.NewMsgError(nil, fmt.Sprintf("题目不存在: %d", req.Id))
  200. }
  201. if req.SkillId != 0 {
  202. r, err := s.Dao.DB.Table("learning_skill").Where("Id", req.SkillId).One()
  203. if err != nil {
  204. return err
  205. }
  206. if r.IsEmpty() {
  207. return myerrors.NewMsgError(nil, fmt.Sprintf("技能不存在: %d", req.SkillId))
  208. }
  209. }
  210. dao := &s.Dao.LearningQuestionDao
  211. toupdate := map[string]interface{}{}
  212. if req.SkillId != 0 {
  213. toupdate["SkillId"] = req.SkillId
  214. }
  215. if req.Name != nil {
  216. toupdate["Name"] = req.Name
  217. }
  218. if req.NameImage != nil {
  219. toupdate["NameImage"] = req.NameImage
  220. }
  221. if req.Type != nil {
  222. toupdate["Type"] = req.Type
  223. }
  224. if req.Enable != nil {
  225. toupdate["Enable"] = req.Enable
  226. }
  227. if req.Content != nil {
  228. content, err := json.Marshal(req.Content)
  229. if err != nil {
  230. return err
  231. }
  232. toupdate["Content"] = content
  233. }
  234. if req.Explanation != nil {
  235. toupdate["Explanation"] = req.Explanation
  236. }
  237. if req.ExplanationImage != nil {
  238. toupdate["ExplanationImage"] = req.ExplanationImage
  239. }
  240. if len(toupdate) == 0 {
  241. return nil
  242. }
  243. toupdate["OperateBy"] = s.userInfo.RealName
  244. _, err = dao.Where("Id", req.Id).Data(toupdate).Update()
  245. return err
  246. }
  247. func (s LearningQuestionService) Delete(ctx context.Context, id []int) error {
  248. _, err := s.Dao.Where("Id IN (?)", id).Delete()
  249. return err
  250. }
  251. func (s LearningQuestionService) BatchUpload(ctx context.Context, req *learning.LearningQuestionBatchUploadReq) error {
  252. r, err := s.Dao.DB.Table("learning_skill").Where("Id", req.SkillId).One()
  253. if err != nil {
  254. return err
  255. }
  256. if r.IsEmpty() {
  257. return myerrors.NewMsgError(nil, fmt.Sprintf("技能不存在: %d", req.SkillId))
  258. }
  259. b, err := DownFile(req.ExcelUrl)
  260. if err != nil {
  261. return myerrors.NewMsgError(nil, fmt.Sprintf("下载 excel 异常 %s", err.Error()))
  262. }
  263. question, err := ParseQuestionExcel(req.SkillId, s.userInfo.RealName, b)
  264. if err != nil {
  265. return myerrors.NewMsgError(nil, fmt.Sprintf("解析 excel 异常 %s", err.Error()))
  266. }
  267. _, err = s.Dao.Insert(question)
  268. return err
  269. }
  270. func DownFile(url string) ([]byte, error) {
  271. r, err := http.Get(url)
  272. if err != nil {
  273. return nil, err
  274. }
  275. if r.StatusCode != http.StatusOK {
  276. return nil, fmt.Errorf("DownFile from %s StatusCode %d", url, r.StatusCode)
  277. }
  278. defer r.Body.Close()
  279. return ioutil.ReadAll(r.Body)
  280. }
  281. var allowAnswer = []string{"A", "B", "C", "D"}
  282. func ParseQuestionExcel(skillId int, operateBy string, b []byte) ([]learning.LearningQuestion, error) {
  283. f, err := excelize.OpenReader(bytes.NewBuffer(b))
  284. if err != nil {
  285. return nil, err
  286. }
  287. sheet := "Sheet1"
  288. rows, err := f.GetRows(sheet)
  289. if err != nil {
  290. return nil, err
  291. }
  292. questions := []learning.LearningQuestion{}
  293. for rown, row := range rows[1:] {
  294. rown += 1
  295. if len(row) < 9 {
  296. return nil, fmt.Errorf("excel 格式错误:列数小于9列")
  297. }
  298. name := strings.TrimSpace(row[2])
  299. typeStr := strings.TrimSpace(row[1])
  300. explanation := strings.TrimSpace(row[8])
  301. a := strings.TrimSpace(row[3])
  302. b := strings.TrimSpace(row[4])
  303. c := strings.TrimSpace(row[5])
  304. d := strings.TrimSpace(row[6])
  305. var qtype int
  306. switch typeStr {
  307. case "单选题":
  308. qtype = 1
  309. case "多选题":
  310. qtype = 2
  311. case "判断题":
  312. qtype = 3
  313. default:
  314. return nil, fmt.Errorf("excel 格式错误:不合法的题型 '%s' %d", typeStr, rown)
  315. }
  316. answerStr := strings.ToUpper(strings.TrimSpace(row[7]))
  317. answer := strings.Split(answerStr, " ")
  318. for i := range answer {
  319. pass := false
  320. for _, allow := range allowAnswer {
  321. if answer[i] == allow {
  322. pass = true
  323. break
  324. }
  325. }
  326. if !pass {
  327. return nil, fmt.Errorf("excel 格式错误:不合法的答案:'%s' %d", answer[i], rown)
  328. }
  329. }
  330. options := []learning.LearningQuestionOption{}
  331. if a != "" {
  332. options = append(options, learning.LearningQuestionOption{
  333. Name: "A",
  334. Content: a,
  335. IsCorrect: strings.Contains(answerStr, "A"),
  336. })
  337. }
  338. if b != "" {
  339. options = append(options, learning.LearningQuestionOption{
  340. Name: "B",
  341. Content: b,
  342. IsCorrect: strings.Contains(answerStr, "B"),
  343. })
  344. }
  345. if c != "" {
  346. options = append(options, learning.LearningQuestionOption{
  347. Name: "C",
  348. Content: c,
  349. IsCorrect: strings.Contains(answerStr, "C"),
  350. })
  351. }
  352. if d != "" {
  353. options = append(options, learning.LearningQuestionOption{
  354. Name: "D",
  355. Content: d,
  356. IsCorrect: strings.Contains(answerStr, "D"),
  357. })
  358. }
  359. correctCount := 0
  360. for _, o := range options {
  361. if o.IsCorrect {
  362. correctCount += 1
  363. }
  364. }
  365. if correctCount < 1 {
  366. return nil, fmt.Errorf("excel 格式错误:含有未设置正确答案的题目 %d", rown)
  367. }
  368. if (qtype == 1 || qtype == 3) && correctCount != 1 {
  369. return nil, fmt.Errorf("excel 格式错误:含有设置正确答案和题型不符的题目 %d", rown)
  370. }
  371. content, _ := json.Marshal(options)
  372. q := learning.LearningQuestion{
  373. SkillId: skillId,
  374. Name: name,
  375. Type: qtype,
  376. Enable: 1,
  377. Content: string(content),
  378. Explanation: explanation,
  379. OperateBy: operateBy,
  380. CreatedAt: gtime.New(),
  381. UpdatedAt: gtime.New(),
  382. }
  383. questions = append(questions, q)
  384. }
  385. return questions, nil
  386. }
  387. func QuestionTemplate() (*excelize.File, error) {
  388. f := excelize.NewFile()
  389. sheet := "Sheet1"
  390. header := []string{
  391. "序号", "题型", "题目", "选项A", "选项B", "选项C", "选项D", "正确答案", "解析",
  392. }
  393. colWidth := []float64{
  394. 12, 12, 40, 20, 20, 20, 20, 12, 20,
  395. }
  396. tempData := [][]string{
  397. {"1", "单选题", "凝胶成像系统不能对以下哪些进行图像采集分析?", "蛋白凝胶", "培养皿", "DNA凝胶", "96孔细胞板", "B", "无"},
  398. {"2", "多选题", "凝胶成像系统有哪几种紫外光源可以选择?", "254mm为中心的宽波长紫外光源", "302mm为中心的宽波长紫外光源", "365mm为中心的宽波长紫外光源", "380mm为中心的宽波长紫外光源", "A B C", "无"},
  399. {"3", "判断题", "凝胶成像系统对紫外线光源有保护功能,如暗室门未关紧,系统将自动切断紫外,是否正确?", "正确", "错误", "", "", "A", "无"},
  400. }
  401. colStyle, err := f.NewStyle(&excelize.Style{
  402. Alignment: &excelize.Alignment{
  403. Horizontal: "center",
  404. Vertical: "center",
  405. WrapText: true,
  406. },
  407. Font: &excelize.Font{
  408. Size: 11,
  409. Family: "宋体",
  410. },
  411. })
  412. if err != nil {
  413. return nil, err
  414. }
  415. headerStyle, err := f.NewStyle(&excelize.Style{
  416. Alignment: &excelize.Alignment{
  417. Horizontal: "center",
  418. },
  419. Fill: excelize.Fill{
  420. Type: "pattern",
  421. Color: []string{"#a6a6a6"},
  422. Pattern: 1,
  423. },
  424. Border: []excelize.Border{
  425. {Type: "left", Color: "#000000", Style: 1},
  426. {Type: "top", Color: "#000000", Style: 1},
  427. {Type: "bottom", Color: "#000000", Style: 1},
  428. {Type: "right", Color: "#000000", Style: 1},
  429. },
  430. })
  431. if err != nil {
  432. return nil, err
  433. }
  434. err = f.SetColStyle(sheet, "A:I", colStyle)
  435. if err != nil {
  436. return nil, err
  437. }
  438. err = f.SetCellStyle(sheet, "A1", "I1", headerStyle)
  439. if err != nil {
  440. return nil, err
  441. }
  442. for i := range header {
  443. n, err := excelize.ColumnNumberToName(i + 1)
  444. if err != nil {
  445. return nil, err
  446. }
  447. f.SetCellValue(sheet, n+"1", header[i])
  448. }
  449. for i, w := range colWidth {
  450. n, err := excelize.ColumnNumberToName(i + 1)
  451. if err != nil {
  452. return nil, err
  453. }
  454. err = f.SetColWidth(sheet, n, n, w)
  455. if err != nil {
  456. return nil, err
  457. }
  458. }
  459. for row, item := range tempData {
  460. for col, v := range item {
  461. colName, err := excelize.ColumnNumberToName(col + 1)
  462. if err != nil {
  463. return nil, err
  464. }
  465. rowName := strconv.Itoa(row + 2)
  466. f.SetCellValue(sheet, colName+rowName, v)
  467. }
  468. }
  469. index := f.NewSheet(sheet)
  470. f.SetActiveSheet(index)
  471. return f, nil
  472. }