question.go 12 KB

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