Jelajahi Sumber

Squashed commit of the following:

commit 828d56d71a4b9c5e7eff0b96564f3388bf9aa5f0
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Jun 4 16:46:09 2021 +0800

    subjectQuestion的sub number改为string

commit 025873773ae91eb9f8f18f0aaf3a4a36e06007ee
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Jun 3 17:56:09 2021 +0800

    fix bugs

commit 170c16de16ed4d9a2039c37c1c93b4c934fe577e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Jun 3 17:27:55 2021 +0800

    fixbugs

commit 567e59cb13a3b0de375429d453b5ab7e8d924d85
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Jun 3 16:26:44 2021 +0800

    fix bugs

commit 1bd856fb8e8932406758eeb2e292447c039e7d69
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Jun 3 11:00:39 2021 +0800

    fix bugs

commit 256d197fce14c4fa8e43320f67a299e1ae4ed56e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Jun 1 17:16:13 2021 +0800

    fix bugs

commit 59fb6af96ad6edbf5ce36f54a53894ee7a8595b7
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Jun 1 15:03:11 2021 +0800

    fixbugs

commit be359cb2dcef7959f76cf401bed0bb9d80c2f72d
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Jun 1 09:47:00 2021 +0800

    fixbugs

commit 31085889909e9360db63eec356c229f9f88f3cf8
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 28 14:16:23 2021 +0800

    fix bugs

commit 4ec32e2322449fa487bba7313e04f63bc6d3e254
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu May 27 17:58:28 2021 +0800

    fix bugs

commit 49ed543a5db9e6b088e281e9182b0df11b220f8f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed May 26 15:50:48 2021 +0800

    fixbugs

commit 3a850b070474f57183b18256ed51a6213c0f678f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed May 26 15:34:12 2021 +0800

    fix bugs

commit 602167a8fda678904ad6ec66572614abe22bcf57
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue May 25 16:36:54 2021 +0800

    fixbugs

commit a8d5b48686614888af33efe35f46411e8bb2f0ba
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 21 17:59:31 2021 +0800

    fix bugs

commit 7f72a2e6ff471a4dba3a66b9bbb47b46c3d0605c
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 21 17:39:07 2021 +0800

    fixbugs

commit d75bb34d7417a8c2c540202ac2b138bf28baccbc
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 21 14:57:53 2021 +0800

    fixbugs

commit 08ce1a42cef250249cd33e1d2e15ff4415d92427
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 21 14:07:27 2021 +0800

    fix bugs

commit 8d5e6ddd48f90cbfefe4b426cd2915efa9646695
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 21 11:03:10 2021 +0800

    fixbugs

commit e98040a7823cc03466fbba80c8b545229d1cfeb0
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 21 10:04:44 2021 +0800

    fixbugs

commit e4dd938520e034ca980f42f2ed7aa709c102f8f7
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu May 20 16:21:55 2021 +0800

    fix bugs

commit 069c77e30290cfa5a26ca4cb6ff5f327cc348f2e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu May 20 15:17:36 2021 +0800

    fix bugs

commit ed765c211a54e4880f44d94c25da8ede4ab74911
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed May 19 17:46:01 2021 +0800

    fix bugs

commit 3f294d450a6dd07ba8934a3776d854ae34096586
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed May 19 17:34:07 2021 +0800

    fix bugs

commit 055a88012288fb1c1ec4254b344414e0ea14fc89
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed May 19 16:59:00 2021 +0800

    fix bugs

commit e60969ae949ed9f4f69fd3d0c7f5ba08937e44f5
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed May 19 15:05:17 2021 +0800

    fixbugs

commit 64c2b55aa13967917cf0d4c1c479c23c54eec392
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue May 18 17:22:34 2021 +0800

    fix bugs

commit c875355719b064a50fdf7962a1c4f5ab67db85e1
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue May 18 16:40:59 2021 +0800

    fix bugs

commit 1b694e78655653213181ede20b0c323712f5baec
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue May 18 15:56:24 2021 +0800

    fix bugs

commit be7251e668330c780cd4e2f375f6c6e042c36cf5
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue May 18 11:49:32 2021 +0800

    fixbugs

commit 0ee1675ff95aeec36325b167244798f37255ba2d
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 14 17:11:00 2021 +0800

    fix bugs

commit 58afcdab8c635c7b84244111689ec79b563f48f0
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 14 16:58:35 2021 +0800

    新增考试控制原卷显示

commit 0bda22f2f92356fd442dfe473a674df5db14ef53
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 14 11:43:08 2021 +0800

    fixbugs

commit 2f5e8f3048174de1f5614040439bfa0d6d358101
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri May 14 10:16:36 2021 +0800

    fixbugs

commit 21336291e38242a60b60921c171240d0173f3783
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu May 13 17:47:52 2021 +0800

    新增成绩分析,评卷进度,勾选题目

commit 946209aabb1c7f00ffa70d2b6a16b0adba7806c8
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue May 11 14:36:06 2021 +0800

    fix bugs

commit 8342c439bdd345f58c69d4b85e56ab38fee11086
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon May 10 11:47:24 2021 +0800

    update

commit 2b0330a5c3c6deebbda8e9ca6b75170bc9171942
Author: ting.yin <yinting@qmth.com.cn>
Date:   Sat May 8 16:46:25 2021 +0800

    update

commit 4d383541c8c4be702a7d4bf5426dc92750730e3b
Author: ting.yin <yinting@qmth.com.cn>
Date:   Sat May 8 16:15:19 2021 +0800

    更新导入主观题结果和主观题分组,新增题目删除,多分组关闭

commit 398938d2ef0a997b323ac9459829fa0d2e2e9725
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Apr 29 15:54:06 2021 +0800

    update

commit feafed17c767c4f7012e137fc247e785d19aafdb
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Apr 29 15:53:58 2021 +0800

    打回试卷只能评卷人取到,新增评卷时间设置

commit 7392ce5f35522c3ebe464143ce4d2b8579e29527
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Apr 28 17:54:25 2021 +0800

    fix bugs

commit 7ea267092f977ed83b051c3e651588ae37a4ca74
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Apr 28 17:03:12 2021 +0800

    fix bugs

commit d94deb2e40b38f7b28a3b9eeba5b3741d6bbed1f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Apr 28 14:38:27 2021 +0800

    fix bugs

commit 83f03dc08275b5d3ec87ccef5a220253770c2558
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Apr 27 19:48:37 2021 +0800

    修复登录后,评卷员解绑或被删除前端报500的问题

commit 0e64f8a11614c5c460989dc25c0f1eb497c27408
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Apr 27 14:42:21 2021 +0800

    fix bugs

commit df5f083460589271164e158a881e3af3dd7bbec0
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Apr 27 14:27:26 2021 +0800

    update

commit 63a8b63d17e6c8cf405332f4133181aef094173f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Apr 27 14:06:48 2021 +0800

    fix bugs

commit 726f6f7fb0cc890f5efd852f999b5cebebeace0e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Apr 27 11:21:59 2021 +0800

    fixbugs

commit 3d4f40157caaa6d3a09f0a54c0de1a632f17eb95
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Apr 27 11:05:08 2021 +0800

    fixbugs

commit 542e727c1293d67916a13440899d574253f2fd83
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Apr 26 16:44:18 2021 +0800

    fix bugs

commit efc3c6b0e69b81c2229e0170b2cfd6c80483cc3b
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Apr 26 15:17:21 2021 +0800

    fix bug

commit 6da267242b2632d7b96f7bb517c84f6d16cbfaa3
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Apr 26 14:08:47 2021 +0800

    update

commit 62578d3add9b582ff1e7949f456e0ba0b58934b7
Author: ting.yin <yinting@qmth.com.cn>
Date:   Sun Apr 25 14:17:26 2021 +0800

    update

commit 783660b82042ebdc014f9fa25e588b65ccfaa92a
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Apr 9 10:31:19 2021 +0800

    更新评卷端接口

commit 3fdc6bebb424aca87231ebddfaa8c64b150191f8
Merge: 521ce98f c139ffd0
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Mar 22 15:48:07 2021 +0800

    Merge remote-tracking branch 'origin/release_1.2.7' into dev_1.3.0

    Conflicts:
    	stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamQuestionServiceImpl.java

commit 521ce98f1b2893cd93e8bf84a98cb7e99c82eb16
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Mar 16 14:02:41 2021 +0800

    update

commit a23c37a89a7912bdf34ca5cacc4c2844c862fe24
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Mar 12 17:09:39 2021 +0800

    更新未答题也显示在页面上

commit c139ffd067327e389b8f6cd9a382e6f40cccd945
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Mar 12 13:50:46 2021 +0800

    修复刷新取任务时间刷新问题

commit da7892f3f37ffaef8d7a4267e3a1652501eafdf6
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Mar 12 09:46:59 2021 +0800

    fix

commit 4d976d993887fdb425ed4c09e733241298862915
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Mar 12 09:10:37 2021 +0800

    update

commit bae50518a8811069f6d672ae7f4a20b630b43ef1
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Mar 12 09:05:46 2021 +0800

    修改评卷任务提交后的释放策略,需要等到数据库事务提交后才进行

commit 4975213c2cf1384a45c2dcd06a11262e76bf6e75
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Mar 11 17:04:24 2021 +0800

    update

commit e2200b14f1906f09b8da587c7a54ea9872aa0fcf
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Mar 11 16:43:28 2021 +0800

    修改领取任务策略,加入上锁成功后的任务状态强制校验,避免重复领取已评任务

commit 9726a0a50bc1c07ed8bf3dc106a48a0b8aa6ac60
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Mar 11 15:52:36 2021 +0800

    优化TaskLockUtil性能;修改TaskLock.list方法放回数据类型;修改领取任务分页,尝试解决重复领取已评任务问题;

commit 399540cc714ddcc49c4f365ddd6fee859af067f8
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Mar 10 18:00:40 2021 +0800

    update

commit 3515cdcb0cda705550608e82525aa6c9c0022d5b
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Mar 10 16:07:40 2021 +0800

    解析套题问题

commit fef3f0dc8c8115441d56b83aba13a0c39fd600ec
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Mar 10 09:44:17 2021 +0800

    修复chrome版本88后期无window.opener导致框图失败问题

commit e4ae5abc436404beb5da86e80ebbaaf9830b604d
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Mar 10 09:39:42 2021 +0800

    update

commit f644821c7c28691df95750fd57956229a6cb3282
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Mar 8 17:49:50 2021 +0800

    fixbugs

commit 76a0c9b8017251e6361ed6b69d4cf8f9ee94a8c0
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Mar 8 17:33:21 2021 +0800

    update

commit 99aa5ab14839f737092b5633b324880446437ae1
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Mar 5 09:36:53 2021 +0800

    修复同时提交统分问题

commit d6845058eb5f681a17512b04a83bc147051228bb
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Mar 4 15:43:58 2021 +0800

    修改统分计算时java对浮点数的加减计算

commit 43fffbaf51f070d228008c69b0f4fd1b20b8b94f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Mar 4 10:47:46 2021 +0800

    给分板小数相减问题

commit cbb9efd0290b242d24f0af352ae240adabc0fceb
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Mar 3 15:15:51 2021 +0800

    修复间隔分为0.1时给分板steplist数量不对问题

commit 5e7f1ec0cc2a6293e5d89295d432f8eccf9be3ec
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Mar 3 14:25:37 2021 +0800

    修复导入复核排序

commit 087b6fc1a6f0dd457f14b5a0e51e8f0100d16dde
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Mar 3 11:14:19 2021 +0800

    修复间隔必须大于0.1和给分板相加问题

commit 334779730aa482708e61a590f8e7d7a7a9515224
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Feb 26 15:15:46 2021 +0800

    fixbugs

commit fdd6cfb9e60d960aa5662c40c32f2ad3a343c0cb
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Feb 26 15:10:33 2021 +0800

    fixbugs

commit f1dc8747101a76e80a1ef0862832600f27a7c461
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Feb 26 15:04:04 2021 +0800

    update

commit ba30c910ec65f19c424b581543de7a6b56570c8c
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Feb 26 11:22:27 2021 +0800

    update

commit 61f9c5f8f74fb07d2b9978dc7a78dd8f9b360f0f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Feb 25 16:56:21 2021 +0800

    update

commit 120525d669f40d637e934df5328cabad5c797eb4
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Feb 25 10:46:44 2021 +0800

    update

commit 9de6a555a7252c254fe8ef823bda7f6126fa1fcb
Author: ting.yin <ting.yin@192.168.11.32>
Date:   Thu Feb 25 10:09:57 2021 +0800

    update

commit 1975082071d24086366fc4c8624d5f25e489a1ae
Author: ting.yin <ting.yin@192.168.11.32>
Date:   Thu Feb 25 10:09:27 2021 +0800

    更新锁卷逻辑,修复评卷页面拖拽问题,间隔分不能为0

commit 24a3dbf2d8188c7816a70dc6c6d22867278a37c0
Merge: 9bbcb54a 08cbffbd
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Jan 18 17:05:44 2021 +0800

    Merge remote-tracking branch 'origin/hotfix_1.2.4' into dev_1.3.0

    Conflicts:
    	stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamQuestionServiceImpl.java

commit 08cbffbdabb1a01c13f6817602a1e37315aa2381
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jan 13 17:19:04 2021 +0800

    评卷任务复核功能里,补充漏掉的切割规则,自动从配置文件读取

commit 18bc983d930ade5cae26caf87a40db3d43f6ef2d
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jan 13 11:24:34 2021 +0800

    修改评卷错误信息key为mark.control.task.error

commit 52769a509cf42fda428d0df6fd711050becfa900
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jan 13 11:21:45 2021 +0800

    增加登录页面公司官网链接

commit 9169834f6075c619a7d1a6f0605b75942ccbdb2e
Author: luoshi <luoshi@qmth.com.cn>
Date:   Mon Jan 11 10:25:14 2021 +0800

    修改评卷员拦截器中/mark/reset路径配置错误

commit 96710f974e75a5b288d696356b8d70834561a02c
Author: luoshi <luoshi@qmth.com.cn>
Date:   Mon Jan 11 09:48:43 2021 +0800

    修改ExamQuestionDao的查询语句错误

commit 7e761b22005f7be1dd8f7075071dec8a2a474a52
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sun Jan 10 17:07:40 2021 +0800

    修复按科目导出成绩明细时,若首个考生未上传且科目启用了AB卷,构造excel标题时客观题按paperType=#条件读取为空的bug

commit fe7656646946b12ae79c5dd2d5bdf7a30f77dd68
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sun Jan 10 17:05:52 2021 +0800

    修复按科目导出成绩明细时,若首个考生未上传且科目启用了AB卷,构造excel标题时客观题按paperType=#条件读取为空的bug

commit 96af3d53590756bdf401eed1376cc4b7ca6501de
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sun Jan 10 17:04:22 2021 +0800

    修复按科目导出成绩明细时,若首个考生未上传且科目启用了AB卷,构造excel标题时客观题按paperType=#条件读取为空的bug
    修改ExamQuestion按examId和subjectCode查询时的默认排序规则,groupNumber字段不参与排序,使得评卷分组排序与mainNumber排序不一致时,不会出现小题得分顺序匹配错误

commit 3343805723a2d99486c149c2aaa85d7ada9bae2c
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sun Jan 10 16:49:17 2021 +0800

    修复按科目导出成绩明细时,若首个考生未上传且科目启用了AB卷,构造excel标题时客观题按paperType=#条件读取为空的bug

commit 9bbcb54aeba67856bf6255654570883892904db1
Merge: e2520088 624db2ba
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Jan 4 13:48:38 2021 +0800

    Merge branch 'dev_20201016' into dev_1.3.0

commit 624db2ba06263884d7e77dac6ae87b16240d3fee
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Dec 25 16:38:05 2020 +0800

    update

commit 99c716b7153b5b72a87d3f9eca73ec2975f3dbfe
Merge: 1ba764a5 f16f31a3
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Dec 14 11:24:18 2020 +0800

    Merge remote-tracking branch 'origin/test20200828' into dev_20201016

commit 1ba764a50c31ef20d066afff5d796ae0ce44b27e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Dec 14 11:24:07 2020 +0800

    更新考试接口参数考试时间改为必填

commit 3e035c60d351b22f2b61229a754e275ccf950af5
Merge: e3cf3bc6 5b2bb371
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Dec 9 09:29:41 2020 +0800

    Merge remote-tracking branch 'origin/test20200828' into dev_20201016

commit e3cf3bc6576718d708dec2f3a0fc148818d45b11
Merge: 6230148c 4c765d3d
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Dec 4 16:49:41 2020 +0800

    Merge remote-tracking branch 'origin/test20200828' into dev_20201016

commit 6230148c97e81b5327aa9e04c2b5a37dac218b94
Merge: c66d05f7 a47d0e29
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Dec 4 15:33:37 2020 +0800

    Merge remote-tracking branch 'origin/test20200828' into dev_20201016

commit c66d05f7aea718ead60ca877224e91bb81a7a323
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Dec 4 15:33:25 2020 +0800

    update

commit 20b8225c08777feb48b0126d1a818d1028732592
Merge: f2c8dacd e353cc9f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Nov 25 16:30:04 2020 +0800

    Merge remote-tracking branch 'origin/test20200828' into dev_20201016

commit f2c8dacd472c3252965a6a04c18ac1275ee308e4
Merge: d07365d0 b24bbc8c
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Nov 23 13:35:57 2020 +0800

    Merge remote-tracking branch 'origin/test20200828' into dev_20201016

commit d07365d073d4c2a3a6403aa43a2dd9ae314be16d
Merge: 3b7e5c44 e6ed6573
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Nov 20 10:39:25 2020 +0800

    Merge remote-tracking branch 'origin/test20200828' into dev_20201016

commit 3b7e5c44300a5c3b00e4307b89b91d62a7011f58
Merge: 582b4635 08553764
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Nov 18 15:30:26 2020 +0800

    Merge remote-tracking branch 'origin/test20200828' into dev_20201016

commit 582b46353b55ac909b50752035a28e0cb1e5871d
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Nov 18 15:05:08 2020 +0800

    question的subNumber改为string
luoshi 4 tahun lalu
induk
melakukan
9a7cad3dfb
100 mengubah file dengan 4181 tambahan dan 1944 penghapusan
  1. 20 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamQuestionDao.java
  2. 2 8
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java
  3. 0 10
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkerDao.java
  4. 1 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/SubjectiveScoreDao.java
  5. 44 14
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/DataSync.java
  6. 60 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/Exam.java
  7. 4 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamQuestion.java
  8. 31 10
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamStudent.java
  9. 4 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroupStudent.java
  10. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/SubjectiveScore.java
  11. 3 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/SubjectiveScorePK.java
  12. 15 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/query/ExamStudentSearchQuery.java
  13. 13 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamQuestionService.java
  14. 17 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/InspectedService.java
  15. 2 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java
  16. 0 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkerService.java
  17. 59 26
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamQuestionServiceImpl.java
  18. 7 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamStudentServiceImpl.java
  19. 10 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamSubjectServiceImpl.java
  20. 74 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/InspectedServiceImpl.java
  21. 19 15
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkGroupServiceImpl.java
  22. 0 11
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkerServiceImpl.java
  23. 20 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/query/ExamSubjectSearchQuery.java
  24. 12 8
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkLibraryDao.java
  25. 4 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkTrackDao.java
  26. 40 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/ArbitrateHistory.java
  27. 10 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkLibrary.java
  28. 31 27
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkResult.java
  29. 38 96
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkStepDTO.java
  30. 140 170
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/Task.java
  31. 25 9
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/TrackDTO.java
  32. 22 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/TrialHistory.java
  33. 10 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/query/ArbitrateHistorySearchQuery.java
  34. 5 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/ArbitrateHistoryServiceImpl.java
  35. 54 10
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkLibraryServiceImpl.java
  36. 334 29
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  37. 207 182
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java
  38. 14 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkLibraryService.java
  39. 27 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java
  40. 7 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkSpecialTagService.java
  41. 2 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/TaskService.java
  42. 3 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/model/ReportSubjectQuestion.java
  43. 13 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/query/ReportSubjectQuery.java
  44. 10 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/query/ReportSubjectRangeQuery.java
  45. 1 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/service/ReportSubjectQuestionService.java
  46. 37 22
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/service/impl/ReportSubjectQuestionServiceImpl.java
  47. 1 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectQuestionModule.java
  48. 1 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectQuestionOptionModule.java
  49. 31 31
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreCalculateUtil.java
  50. 5 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreInfo.java
  51. 6 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreItem.java
  52. 17 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TaskLock.java
  53. 41 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TaskLockUtil.java
  54. 2 0
      stmms-biz/src/main/resources/META-INF/persistence.xml
  55. 34 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/ExamSource.java
  56. 4 10
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/LibraryStatus.java
  57. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ExamQuestionDTO.java
  58. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ExceptionQuestionDTO.java
  59. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ObjectiveQuestionDTO.java
  60. 40 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/RejectResult.java
  61. 48 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ReportSubjectRangeDTO.java
  62. 15 8
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectQuestionDTO.java
  63. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectiveQuestionDTO.java
  64. 143 121
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java
  65. 1 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/CheckStudentController.java
  66. 44 26
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/DataSyncController.java
  67. 7 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java
  68. 202 75
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/InspectedController.java
  69. 148 145
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/LibraryController.java
  70. 27 7
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkController.java
  71. 101 169
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkGroupController.java
  72. 31 65
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkQualityController.java
  73. 59 45
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkTrackController.java
  74. 6 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkerController.java
  75. 205 53
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java
  76. 33 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/report/ReportSubjectQuestionController.java
  77. 146 41
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/report/ReportSubjectRangeController.java
  78. 4 4
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/DataSyncThread.java
  79. 298 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/OnlineExamThread.java
  80. 2 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreCalculateThread.java
  81. 2 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreReportThread.java
  82. 14 4
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/user/UserClassController.java
  83. 7 2
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/user/UserController.java
  84. 237 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/utils/OnlineExamHttpUtil.java
  85. 13 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/vo/SubjectLibraryVO.java
  86. 42 2
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/CoreController.java
  87. 17 5
      stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/BaseController.java
  88. 0 4
      stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/LoginController.java
  89. 307 179
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java
  90. 23 11
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/interceptor/MarkInterceptor.java
  91. 6 5
      stmms-web/src/main/webapp/WEB-INF/application.properties
  92. 2 2
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/arbitrateList.jsp
  93. 42 5
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/dataSync.jsp
  94. 43 3
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examEdit.jsp
  95. 42 2
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examForm.jsp
  96. 2 2
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examIndex.jsp
  97. 2 2
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examList.jsp
  98. 89 79
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupAdd.jsp
  99. 82 77
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupEditFull.jsp
  100. 42 34
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupEditSimple.jsp

+ 20 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamQuestionDao.java

@@ -1,6 +1,7 @@
 package cn.com.qmth.stmms.biz.exam.dao;
 
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+
 import org.springframework.data.domain.Sort;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -8,6 +9,7 @@ import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 
 import java.util.List;
+import java.util.Set;
 
 public interface ExamQuestionDao extends JpaRepository<ExamQuestion, Integer>, JpaSpecificationExecutor<ExamQuestion> {
 
@@ -27,7 +29,7 @@ public interface ExamQuestionDao extends JpaRepository<ExamQuestion, Integer>, J
 
     @Query("select count(q) from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 and q.mainNumber=?4 and q.subNumber=?5")
     public long countByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber);
+            boolean objective, Integer mainNumber, String subNumber);
 
     // @Query("select q from ExamQuestion q where q.examId=?1 and
     // q.subjectCode=?2 and q.objective=?3 "
@@ -84,6 +86,22 @@ public interface ExamQuestionDao extends JpaRepository<ExamQuestion, Integer>, J
             boolean objective, Integer mainNumber, String paperType);
 
     public ExamQuestion findByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(Integer examId,
-            String subjectCode, boolean objective, Integer mainNumber, Integer subNumber);
+            String subjectCode, boolean objective, Integer mainNumber, String subNumber);
+
+    @Query("select sum(q.totalScore) from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 ")
+    public Double sumTotalScore(int examId, String subjectCode, boolean objective);
+
+    @Modifying
+    @Query("update ExamQuestion q set q.groupNumber = null"
+            + " where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 and q.groupNumber=?4")
+    public void resetByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(Integer examId, String subjectCode,
+            boolean objective, Integer number);
+
+    @Query("select count(q) from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 and q.groupNumber is null ")
+    public long countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(int examId, String subjectCode,
+            boolean objective);
+
+    @Query("select distinct q.subjectCode from ExamQuestion q where q.examId=?1 and q.objective=?2 and q.groupNumber is null  ")
+    public Set<String> FindSubjectCodeByExamIdAndObjectiveAndGroupNumberIsNull(int examId, boolean objective);
 
 }

+ 2 - 8
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java

@@ -14,8 +14,8 @@ import org.springframework.data.repository.PagingAndSortingRepository;
 import java.util.Date;
 import java.util.List;
 
-public interface MarkGroupDao
-        extends PagingAndSortingRepository<MarkGroup, MarkGroupPK>, JpaSpecificationExecutor<MarkGroup> {
+public interface MarkGroupDao extends PagingAndSortingRepository<MarkGroup, MarkGroupPK>,
+        JpaSpecificationExecutor<MarkGroup> {
 
     @Query("select q from MarkGroup q where q.pk.examId=?1 and q.pk.subjectCode=?2 and q.pk.number=?3")
     MarkGroup findOne(Integer examId, String subjectCode, Integer groupNumber);
@@ -67,12 +67,6 @@ public interface MarkGroupDao
     @Query("select sum(g.totalScore) from MarkGroup g where g.pk.examId=?1 and g.pk.subjectCode=?2")
     Double sumTotalScore(Integer examId, String subjectCode);
 
-    @Query("select sum(g.libraryCount) from MarkGroup g where g.pk.examId=?1")
-    Integer sumLibraryCount(Integer examId);
-
-    @Query("select sum(g.markedCount) from MarkGroup g where g.pk.examId=?1")
-    Integer sumMarkedCount(Integer examId);
-
     @Modifying(clearAutomatically = true)
     @Query("update MarkGroup g set g.libraryCount=?4, g.leftCount=(?4-g.markedCount) where g.pk.examId=?1 and g.pk.subjectCode=?2 and g.pk.number=?3")
     void updateLibraryCount(Integer examId, String subjectCode, Integer number, Integer totalCount);

+ 0 - 10
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkerDao.java

@@ -19,10 +19,6 @@ public interface MarkerDao extends PagingAndSortingRepository<Marker, Integer>,
     @Query("select count(m) from Marker m where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3")
     public long countByExamIdAndSubjectCodeAndGroupNumber(int examId, String subjectCode, int number);
 
-    public List<Marker> findByExamId(int examId);
-
-    public List<Marker> findByExamIdAndSubjectCode(int examId, String subjectCode, Pageable page);
-
     public List<Marker> findByExamIdAndSubjectCodeAndGroupNumber(int examId, String subjectCode, int number,
             Pageable page);
 
@@ -49,10 +45,6 @@ public interface MarkerDao extends PagingAndSortingRepository<Marker, Integer>,
     public void updateQualityById(Integer id, int finishCount, int validCount, double avgSpeed, double avgScore,
             double stdevScore);
 
-    @Modifying
-    @Query("update Marker m set m.markSetting=?2 where m.id=?1")
-    public void updateMarkSettingById(Integer id, String setting);
-
     @Modifying
     @Query("update Marker m set m.finishCount=null, m.validCount=null, m.avgSpeed=null, m.avgScore=null, m.stdevScore=null "
             + "where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3")
@@ -61,8 +53,6 @@ public interface MarkerDao extends PagingAndSortingRepository<Marker, Integer>,
     @Query(value = "select e.* from eb_marker e,m_library m where 1=1 and e.id = m.marker_id and m.student_id =?1", nativeQuery = true)
     public List<Marker> findByStudentId(int studentId);
 
-    public List<Marker> findByMode(String common);
-
     public Marker findByExamIdAndSubjectCodeAndGroupNumberAndUserId(int examId, String subjectCode, Integer number,
             Integer userId);
 

+ 1 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/SubjectiveScoreDao.java

@@ -22,7 +22,7 @@ public interface SubjectiveScoreDao extends JpaRepository<SubjectiveScore, Subje
     List<SubjectiveScore> findByStudentIdAndGroupNumber(Integer studentId, Integer groupNumber);
 
     @Query("select s from SubjectiveScore s where s.pk.studentId=?1 and s.pk.mainNumber=?2 and s.pk.subNumber=?3")
-    SubjectiveScore findByStudentIdAndMainNumberAndSubNumber(Integer studentId, Integer mainNumber, Integer subNumber);
+    SubjectiveScore findByStudentIdAndMainNumberAndSubNumber(Integer studentId, Integer mainNumber, String subNumber);
 
     @Modifying(clearAutomatically = true)
     @Query("delete SubjectiveScore s where s.pk.studentId=?1")

+ 44 - 14
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/DataSync.java

@@ -5,11 +5,15 @@ import java.util.Date;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.Id;
 import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 
+import cn.com.qmth.stmms.common.enums.ExamSource;
+
 @Entity
 @Table(name = "eb_data_sync")
 public class DataSync implements Serializable {
@@ -22,6 +26,13 @@ public class DataSync implements Serializable {
     @Column(name = "school_id")
     private Integer schoolId;
 
+    /**
+     * 来源
+     */
+    @Column(name = "source", length = 16, nullable = false)
+    @Enumerated(EnumType.STRING)
+    private ExamSource source;
+
     @Temporal(TemporalType.TIMESTAMP)
     @Column(name = "create_time")
     private Date createTime;
@@ -30,11 +41,11 @@ public class DataSync implements Serializable {
     @Column(name = "update_time")
     private Date updateTime;
 
-    @Column(name = "root_org_id", nullable = false)
+    @Column(name = "root_org_id")
     private String rootOrgId;
 
     @Column(name = "cloud_exam_id", nullable = false)
-    private Long cloudExamId;
+    private String cloudExamId;
 
     @Column(name = "subject_code")
     private String subjectCode;
@@ -42,18 +53,21 @@ public class DataSync implements Serializable {
     @Column(name = "next_id")
     private Long nextId;
 
-    @Column(name = "student_url", nullable = false)
-    private String studentUrl;
-
-    @Column(name = "app_id", nullable = false)
+    @Column(name = "app_id")
     private String appId;
 
-    @Column(name = "secret_key", nullable = false)
-    private String secretKey;
+    @Column(name = "access_key", nullable = true, length = 64)
+    private String accessKey;
+
+    @Column(name = "access_secret", length = 64)
+    private String accessSecret;
 
     @Column(name = "finished", nullable = false)
     private boolean finished;
 
+    @Column(name = "student_url", nullable = false)
+    private String studentUrl;
+
     @Column(name = "subject_url", nullable = false)
     private String subjectUrl;
 
@@ -100,11 +114,11 @@ public class DataSync implements Serializable {
         this.rootOrgId = rootOrgId;
     }
 
-    public Long getCloudExamId() {
+    public String getCloudExamId() {
         return cloudExamId;
     }
 
-    public void setCloudExamId(Long cloudExamId) {
+    public void setCloudExamId(String cloudExamId) {
         this.cloudExamId = cloudExamId;
     }
 
@@ -156,12 +170,28 @@ public class DataSync implements Serializable {
         this.appId = appId;
     }
 
-    public String getSecretKey() {
-        return secretKey;
+    public ExamSource getSource() {
+        return source;
+    }
+
+    public void setSource(ExamSource source) {
+        this.source = source;
+    }
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getAccessSecret() {
+        return accessSecret;
     }
 
-    public void setSecretKey(String secretKey) {
-        this.secretKey = secretKey;
+    public void setAccessSecret(String accessSecret) {
+        this.accessSecret = accessSecret;
     }
 
     public String getSubjectPaperUrl() {

+ 60 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/Exam.java

@@ -22,6 +22,7 @@ import cn.com.qmth.stmms.biz.file.enums.FormatType;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
 import cn.com.qmth.stmms.common.enums.ExamType;
+import cn.com.qmth.stmms.common.enums.MarkMode;
 import cn.com.qmth.stmms.common.enums.ObjectiveStatus;
 
 @Entity
@@ -122,6 +123,33 @@ public class Exam implements Serializable {
     @Column(name = "objective_status", length = 16, nullable = false)
     private ObjectiveStatus objectiveStatus;
 
+    /**
+     * 评卷起始时间
+     */
+    @Temporal(TemporalType.TIMESTAMP)
+    @Column(name = "mark_start_time")
+    private Date startTime;
+
+    /**
+     * 评卷结束时间
+     */
+    @Temporal(TemporalType.TIMESTAMP)
+    @Column(name = "mark_end_time")
+    private Date endTime;
+
+    /**
+     * 强制评卷模式
+     */
+    @Column(name = "mark_mode", nullable = true, length = 16)
+    @Enumerated(EnumType.STRING)
+    private MarkMode markMode;
+
+    /**
+     * 开启/关闭 原卷显示
+     */
+    @Column(name = "sheet_view", nullable = false)
+    private boolean sheetView;
+
     public Integer getId() {
         return id;
     }
@@ -302,4 +330,36 @@ public class Exam implements Serializable {
         this.objectiveStatus = objectiveStatus;
     }
 
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public MarkMode getMarkMode() {
+        return markMode;
+    }
+
+    public void setMarkMode(MarkMode markMode) {
+        this.markMode = markMode;
+    }
+
+    public boolean isSheetView() {
+        return sheetView;
+    }
+
+    public void setSheetView(boolean sheetView) {
+        this.sheetView = sheetView;
+    }
+
 }

+ 4 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamQuestion.java

@@ -33,12 +33,12 @@ public class ExamQuestion implements Serializable {
     private Integer mainNumber;
 
     @Column(name = "sub_number", nullable = false)
-    private Integer subNumber;
+    private String subNumber;
 
     @Column(name = "main_title", nullable = false, length = 32)
     private String mainTitle;
 
-    @Column(name = "group_number", nullable = false)
+    @Column(name = "group_number", nullable = true)
     private Integer groupNumber;
 
     @Column(name = "answer", nullable = true, length = 16)
@@ -126,11 +126,11 @@ public class ExamQuestion implements Serializable {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 31 - 10
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamStudent.java

@@ -1,21 +1,31 @@
 package cn.com.qmth.stmms.biz.exam.model;
 
-import cn.com.qmth.stmms.biz.mark.model.ProblemType;
-import cn.com.qmth.stmms.biz.utils.ScoreItem;
-import cn.com.qmth.stmms.common.annotation.ExcelField;
-import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
-
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.math.RandomUtils;
-
-import javax.persistence.*;
-
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import javax.persistence.Transient;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.RandomUtils;
+
+import cn.com.qmth.stmms.biz.mark.model.ProblemType;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.utils.ScoreItem;
+import cn.com.qmth.stmms.common.annotation.ExcelField;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+
 /**
  * 针对某次考试的考生信息
  *
@@ -306,6 +316,9 @@ public class ExamStudent implements Serializable {
     @Transient
     private String examName;
 
+    @Transient
+    private User inspector;
+
     @Transient
     private List<ExamQuestion> subjectiveQuestionList;
 
@@ -832,4 +845,12 @@ public class ExamStudent implements Serializable {
         this.inspectorId = inspectorId;
     }
 
+    public User getInspector() {
+        return inspector;
+    }
+
+    public void setInspector(User inspector) {
+        this.inspector = inspector;
+    }
+
 }

+ 4 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroupStudent.java

@@ -5,7 +5,10 @@ import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
 import javax.persistence.Column;
 import javax.persistence.EmbeddedId;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.Table;
+
 import java.io.Serializable;
 
 /**
@@ -36,6 +39,7 @@ public class MarkGroupStudent implements Serializable {
      * 状态
      */
     @Column(name = "status", length = 16, nullable = false)
+    @Enumerated(EnumType.STRING)
     private SubjectiveStatus status;
 
     public MarkGroupStudent() {

+ 2 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/SubjectiveScore.java

@@ -74,11 +74,11 @@ public class SubjectiveScore implements Serializable {
         this.pk.setMainNumber(mainNumber);
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return pk.getSubNumber();
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.pk.setSubNumber(subNumber);
     }
 

+ 3 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/SubjectiveScorePK.java

@@ -16,7 +16,7 @@ public class SubjectiveScorePK implements Serializable {
     private Integer mainNumber;
 
     @Column(name = "sub_number", nullable = false)
-    private Integer subNumber;
+    private String subNumber;
 
     public Integer getStudentId() {
         return studentId;
@@ -34,11 +34,11 @@ public class SubjectiveScorePK implements Serializable {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 15 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/query/ExamStudentSearchQuery.java

@@ -94,6 +94,8 @@ public class ExamStudentSearchQuery extends BaseQuery<ExamStudent> {
 
     private List<Integer> studentIds;
 
+    private Integer inspectorId;
+
     public ExamStudentSearchQuery() {
         super();
         this.statusSet = new HashSet<>();
@@ -107,6 +109,10 @@ public class ExamStudentSearchQuery extends BaseQuery<ExamStudent> {
         setSort(new Sort(Direction.DESC, "examId"));
     }
 
+    public void orderByInspectTimeDesc() {
+        setSort(new Sort(Direction.DESC, "inspectTime"));
+    }
+
     public Integer getExamId() {
         return examId;
     }
@@ -430,4 +436,13 @@ public class ExamStudentSearchQuery extends BaseQuery<ExamStudent> {
     public void setStudentIds(List<Integer> studentIds) {
         this.studentIds = studentIds;
     }
+
+    public Integer getInspectorId() {
+        return inspectorId;
+    }
+
+    public void setInspectorId(Integer inspectorId) {
+        this.inspectorId = inspectorId;
+    }
+
 }

+ 13 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamQuestionService.java

@@ -1,11 +1,12 @@
 package cn.com.qmth.stmms.biz.exam.service;
 
+import java.util.List;
+import java.util.Set;
+
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;
 import cn.com.qmth.stmms.common.enums.ObjectivePolicy;
 
-import java.util.List;
-
 public interface ExamQuestionService {
 
     ExamQuestion save(ExamQuestion question);
@@ -52,15 +53,23 @@ public interface ExamQuestionService {
             boolean objective, Integer mainNumber, String paperType);
 
     long countByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber);
+            boolean objective, Integer mainNumber, String subNumber);
 
     ExamQuestion updateObjectivePolicy(Integer questionId, ObjectivePolicy objectivePolicy);
 
     ExamQuestion findById(Integer questionId);
 
     ExamQuestion findByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber);
+            boolean objective, Integer mainNumber, String subNumber);
 
     List<ExamQuestion> findMainByExamAndSubjectAndObjective(Integer examId, String subjectCode, boolean objective);
 
+    double sumTotalScore(int examId, String subjectCode, boolean objective);
+
+    void deleteById(Integer questionId);
+
+    long countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(Integer examId, String subjectCode, boolean objective);
+
+    Set<String> FindSubjectCodeByExamIdAndObjectiveAndGroupNumberIsNull(int examId, boolean objective);
+
 }

+ 17 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/InspectedService.java

@@ -14,4 +14,21 @@ public interface InspectedService {
     Integer countByQuery(ExamStudentSearchQuery query, SubjectiveStatus status, Integer mainNumber,
             Double mainStartScore, Double mainEndScore, Double questionScore);
 
+<<<<<<< HEAD
+=======
+    void releaseByUserId(Integer examId, String subjectCode, Integer userId);
+
+    void releaseByStudent(ExamStudent student);
+
+    /**
+     * 申请领取某个任务
+     */
+    boolean applyStudent(ExamStudent student, Integer userId);
+
+    /**
+     * 是否已领取了某个任务
+     */
+    boolean hasApplied(ExamStudent student, Integer userId);
+
+>>>>>>> release_1.3.1
 }

+ 2 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java

@@ -42,10 +42,6 @@ public interface MarkGroupService {
 
     void updateEnableAllZero(int examId, String subjectCode, Integer number, boolean enableAllZero);
 
-    long sumLibraryCount(Integer examId);
-
-    long sumMarkedCount(Integer examId);
-
     double sumTotalScore(Integer examId, String subjectCode);
 
     boolean updateStatus(int examId, String subjectCode, Integer number, MarkStatus newStatus,
@@ -61,4 +57,6 @@ public interface MarkGroupService {
 
     boolean validateStatus(Integer examId, String subjectCode, Integer number, MarkStatus... status);
 
+    int findMaxNumberByExamIdAndSubjectCode(int examId, String subjectCode);
+
 }

+ 0 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkerService.java

@@ -25,12 +25,8 @@ public interface MarkerService {
 
     List<Marker> getMarkCount(int examId, Set<String> subjectCodes);
 
-    List<Marker> findMode(String commo);
-
     public int batchSave(List<Marker> list);
 
-    void updateMarkSetting(Integer id, String setting);
-
     List<Marker> findByExamAndUserId(Integer examId, Integer userId);
 
     Marker findByExamAndSubjectAndNumberAndUserId(int examId, String subjectCode, Integer number, Integer userId);

+ 59 - 26
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamQuestionServiceImpl.java

@@ -8,6 +8,7 @@ import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;
 import cn.com.qmth.stmms.common.enums.ObjectivePolicy;
 import cn.com.qmth.stmms.common.enums.ObjectiveStatus;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Sort;
@@ -20,6 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
+
 import java.util.*;
 
 @Service
@@ -77,20 +79,35 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
     @Override
     public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndPaperType(Integer examId, String subjectCode,
             boolean objective, String paperType) {
-        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndPaperType(examId, subjectCode, objective, paperType,
-                new Sort(Direction.ASC, "mainNumber", "subNumber"));
+        List<ExamQuestion> list = questionDao.findByExamIdAndSubjectCodeAndObjectiveAndPaperType(examId, subjectCode,
+                objective, paperType, new Sort(Direction.ASC, "mainNumber", "subNumber"));
+        if (objective) {
+            Collections.sort(list, new Comparator<ExamQuestion>() {
+
+                @Override
+                public int compare(ExamQuestion o1, ExamQuestion o2) {
+                    int i = o1.getMainNumber() - o2.getMainNumber();
+                    if (i == 0) {
+                        return Integer.parseUnsignedInt(o1.getSubNumber())
+                                - Integer.parseUnsignedInt(o2.getSubNumber());
+                    }
+                    return i;
+                }
+            });
+        }
+        return list;
     }
 
     @Override
     public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndGroupNumber(Integer examId, String subjectCode,
             boolean objective, Integer groupNumber) {
-        return questionDao
-                .findByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(examId, subjectCode, objective, groupNumber);
+        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(examId, subjectCode, objective,
+                groupNumber);
     }
 
     @Override
-    public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndGroupNumberNotEqual(Integer examId, String subjectCode,
-            boolean objective, Integer groupNumber) {
+    public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndGroupNumberNotEqual(Integer examId,
+            String subjectCode, boolean objective, Integer groupNumber) {
         return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndGroupNumberNotEqual(examId, subjectCode, objective,
                 groupNumber);
     }
@@ -98,8 +115,8 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
     @Override
     public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode,
             boolean objective, Integer mainNumber) {
-        return questionDao
-                .findByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective, mainNumber);
+        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective,
+                mainNumber);
     }
 
     @Override
@@ -118,8 +135,8 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
     @Override
     public long countByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode, boolean objective,
             Integer mainNumber) {
-        return questionDao
-                .countByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective, mainNumber);
+        return questionDao.countByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective,
+                mainNumber);
     }
 
     private Specification<ExamQuestion> buildSpecification(final ExamQuestionSearchQuery query) {
@@ -154,9 +171,8 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
                 if (query.getMainNumber() != null) {
                     predicates.add(cb.equal(root.get("mainNumber"), query.getMainNumber()));
                 }
-                return predicates.isEmpty() ?
-                        cb.conjunction() :
-                        cb.and(predicates.toArray(new Predicate[predicates.size()]));
+                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
+                        .size()]));
             }
         };
     }
@@ -175,17 +191,15 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
     @Override
     public long countByExamAndSubjectAndObjectiveAndMainNumberAndPaperType(Integer examId, String subjectCode,
             boolean objective, Integer mainNumber, String paperType) {
-        return questionDao
-                .countByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective, mainNumber,
-                        paperType);
+        return questionDao.countByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective,
+                mainNumber, paperType);
     }
 
     @Override
     public long countByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber) {
-        return questionDao
-                .countByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(examId, subjectCode, objective,
-                        mainNumber, subNumber);
+            boolean objective, Integer mainNumber, String subNumber) {
+        return questionDao.countByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(examId, subjectCode,
+                objective, mainNumber, subNumber);
     }
 
     @Transactional
@@ -205,15 +219,13 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
 
     @Override
     public ExamQuestion findByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber) {
-        return questionDao
-                .findByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(examId, subjectCode, objective,
-                        mainNumber, subNumber);
+            boolean objective, Integer mainNumber, String subNumber) {
+        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(examId, subjectCode,
+                objective, mainNumber, subNumber);
     }
 
     @Override
-    public List<ExamQuestion> findMainByExamAndSubjectAndObjective(Integer examId, String subjectCode,
-            boolean objective) {
+    public List<ExamQuestion> findMainByExamAndSubjectAndObjective(Integer examId, String subjectCode, boolean objective) {
         List<ExamQuestion> list = new ArrayList<ExamQuestion>();
         Set<Integer> mainNumbers = new LinkedHashSet<>();
         List<ExamQuestion> all = questionDao.findByExamIdAndSubjectCodeAndObjective(examId, subjectCode, objective);
@@ -226,4 +238,25 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
         return list;
     }
 
+    @Override
+    public double sumTotalScore(int examId, String subjectCode, boolean objective) {
+        Double score = questionDao.sumTotalScore(examId, subjectCode, objective);
+        return score != null ? score.doubleValue() : 0d;
+    }
+
+    @Override
+    public void deleteById(Integer questionId) {
+        questionDao.delete(questionId);
+    }
+
+    @Override
+    public long countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(Integer examId, String subjectCode,
+            boolean objective) {
+        return questionDao.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(examId, subjectCode, objective);
+    }
+
+    @Override
+    public Set<String> FindSubjectCodeByExamIdAndObjectiveAndGroupNumberIsNull(int examId, boolean objective) {
+        return questionDao.FindSubjectCodeByExamIdAndObjectiveAndGroupNumberIsNull(examId, objective);
+    }
 }

+ 7 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamStudentServiceImpl.java

@@ -658,6 +658,9 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
                 if (StringUtils.isNotBlank(query.getTeacher())) {
                     predicates.add(cb.equal(root.get("teacher"), query.getTeacher()));
                 }
+                if (query.getInspectorId() != null) {
+                    predicates.add(cb.equal(root.get("inspectorId"), query.getInspectorId()));
+                }
                 return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                         .size()]));
             }
@@ -932,6 +935,9 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
      */
     private List<OriginTag> buildOriginTags(ExamStudent student, MarkGroup group, List<ExamQuestion> questions,
             List<ScoreItem> scoreList, boolean withGroupScore) {
+        if (scoreList.size() == 0) {
+            return new LinkedList<>();
+        }
         DecimalFormat format = new DecimalFormat("####.###");
         // 从考生主观题得分中拆解出本大题得分
         double score = 0;
@@ -939,7 +945,7 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
         int i = -1;
         for (ExamQuestion question : questions) {
             i++;
-            if (question.getGroupNumber().equals(group.getNumber())) {
+            if (group.getNumber().equals(question.getGroupNumber())) {
                 double value = scoreList.size() > i ? scoreList.get(i).getScore() : 0;
                 score += value;
                 details.add(value);

+ 10 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamSubjectServiceImpl.java

@@ -164,6 +164,16 @@ public class ExamSubjectServiceImpl extends BaseQueryService<ExamSubject> implem
                         predicates.add(cb.or(sub));
                     }
                 }
+                if (StringUtils.isNotBlank(query.getCodeNotIn())) {
+                    String[] list = query.getCodeNotIn().split(",");
+                    if (list.length > 0) {
+                        Predicate[] sub = new Predicate[list.length];
+                        for (int i = 0; i < list.length; i++) {
+                            sub[i] = cb.notEqual(root.get("pk").get("code"), list[i]);
+                        }
+                        predicates.add(cb.and(sub));
+                    }
+                }
                 return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                         .size()]));
             }

+ 74 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/InspectedServiceImpl.java

@@ -16,6 +16,12 @@ import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.InspectedService;
+<<<<<<< HEAD
+=======
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.biz.utils.TaskLock;
+import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
+>>>>>>> release_1.3.1
 import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
 
 @Service
@@ -27,22 +33,43 @@ public class InspectedServiceImpl extends BaseQueryService<ExamStudent> implemen
     @Autowired
     private ExamStudentService studentService;
 
+<<<<<<< HEAD
+=======
+    @Autowired
+    private UserService userService;
+
+>>>>>>> release_1.3.1
     @SuppressWarnings("unchecked")
     @Override
     public List<ExamStudent> findByQuery(ExamStudentSearchQuery query, SubjectiveStatus status, Integer mainNumber,
             Double mainStartScore, Double mainEndScore, Double questionScore) {
+<<<<<<< HEAD
         StringBuilder dataSql = new StringBuilder("select distinct s.id "
+=======
+        StringBuilder dataSql = new StringBuilder("select distinct s.id , s.inspect_time "
+>>>>>>> release_1.3.1
                 + " from eb_exam_student s left join eb_subjective_score e on e.student_id = s.id ");
         StringBuilder limitSql = new StringBuilder(" limit :offset,:pageSize");
         Query dataQuery = getQuery(query, status, mainNumber, mainStartScore, mainEndScore, questionScore, dataSql,
                 limitSql);
         dataQuery.setParameter("offset", (query.getPageNumber() - 1) * query.getPageSize());
         dataQuery.setParameter("pageSize", query.getPageSize());
+<<<<<<< HEAD
         List<Integer> list = dataQuery.getResultList();
         List<ExamStudent> resultList = new ArrayList<ExamStudent>();
         if (list != null && !list.isEmpty()) {
             for (Integer id : list) {
                 ExamStudent e = studentService.findById(id);
+=======
+        List<Object[]> list = dataQuery.getResultList();
+        List<ExamStudent> resultList = new ArrayList<ExamStudent>();
+        if (list != null && !list.isEmpty()) {
+            for (Object[] array : list) {
+                ExamStudent e = studentService.findById((Integer) array[0]);
+                if (e.getInspectorId() != null) {
+                    e.setInspector(userService.findById(e.getInspectorId()));
+                }
+>>>>>>> release_1.3.1
                 resultList.add(e);
             }
         }
@@ -84,8 +111,13 @@ public class InspectedServiceImpl extends BaseQueryService<ExamStudent> implemen
             whereSql.append(" and e.score = :questionScore");
         }
         dataSql.append(whereSql);
+<<<<<<< HEAD
         // StringBuilder orderSql = new StringBuilder("order by s.id desc ");
         // dataSql.append(orderSql);
+=======
+        StringBuilder orderSql = new StringBuilder(" order by s.inspect_time desc ");
+        dataSql.append(orderSql);
+>>>>>>> release_1.3.1
         if (limitSql != null) {
             dataSql.append(limitSql);
         }
@@ -133,4 +165,46 @@ public class InspectedServiceImpl extends BaseQueryService<ExamStudent> implemen
         return count;
     }
 
+<<<<<<< HEAD
+=======
+    @Override
+    public void releaseByUserId(Integer examId, String subjectCode, Integer userId) {
+        String key = examId + "_" + subjectCode;
+        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(key);
+        taskLock.clear(userId);
+    }
+
+    @Override
+    public void releaseByStudent(ExamStudent student) {
+        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(getKey(student));
+        taskLock.remove(student.getId(), 1);
+    }
+
+    @Override
+    public boolean applyStudent(ExamStudent student, Integer userId) {
+        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(getKey(student));
+        boolean lock = taskLock.add(student.getId(), 1, userId);
+        // 上锁失败直接返回
+        if (!lock) {
+            return false;
+        }
+        // 重复校验任务状态
+        if (student.getSubjectiveStatus().equals(SubjectiveStatus.MARKED)) {
+            return true;
+        } else {
+            taskLock.remove(student.getId(), 1, userId);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean hasApplied(ExamStudent student, Integer userId) {
+        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(getKey(student));
+        return taskLock.exist(student.getId(), 1, userId);
+    }
+
+    private String getKey(ExamStudent student) {
+        return student.getExamId() + "_" + student.getSubjectCode();
+    }
+>>>>>>> release_1.3.1
 }

+ 19 - 15
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkGroupServiceImpl.java

@@ -8,6 +8,7 @@ import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
 import cn.com.qmth.stmms.common.enums.MarkMode;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
+
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -37,9 +38,12 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
     @Transactional
     @Override
     public void updatePicList(int examId, String subjectCode, int number, List<PictureConfigItem> configList) {
-        groupDao.updatePicList(examId, subjectCode, number, configList != null && configList.size() > 0 ?
-                StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER) :
-                "");
+        groupDao.updatePicList(
+                examId,
+                subjectCode,
+                number,
+                configList != null && configList.size() > 0 ? StringUtils.join(configList,
+                        PictureConfigItem.DB_ITEM_JOINER) : "");
     }
 
     @Transactional
@@ -145,18 +149,6 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
         return groupDao.countByExamId(examId);
     }
 
-    @Override
-    public long sumLibraryCount(Integer examId) {
-        Integer count = groupDao.sumLibraryCount(examId);
-        return count != null ? count : 0;
-    }
-
-    @Override
-    public long sumMarkedCount(Integer examId) {
-        Integer count = groupDao.sumMarkedCount(examId);
-        return count != null ? count : 0;
-    }
-
     @Override
     public double sumTotalScore(Integer examId, String subjectCode) {
         Double score = groupDao.sumTotalScore(examId, subjectCode);
@@ -168,4 +160,16 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
         return groupDao.countByExamIdAndSubjectCodeAndNumberAndStatus(examId, subjectCode, number, status) > 0;
     }
 
+    @Override
+    public int findMaxNumberByExamIdAndSubjectCode(int examId, String subjectCode) {
+        List<MarkGroup> groups = groupDao.findByExamIdAndSubjectCode(examId, subjectCode);
+        int number = 0;
+        for (MarkGroup group : groups) {
+            if (number < group.getNumber()) {
+                number = group.getNumber();
+            }
+        }
+        return number;
+    }
+
 }

+ 0 - 11
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkerServiceImpl.java

@@ -46,12 +46,6 @@ public class MarkerServiceImpl extends BaseQueryService<Marker> implements Marke
         return markerDao.save(marker);
     }
 
-    @Transactional
-    @Override
-    public void updateMarkSetting(Integer id, String setting) {
-        markerDao.updateMarkSettingById(id, setting);
-    }
-
     @Override
     public Marker findById(Integer id) {
         return markerDao.findOne(id);
@@ -148,11 +142,6 @@ public class MarkerServiceImpl extends BaseQueryService<Marker> implements Marke
         return list.size();
     }
 
-    @Override
-    public List<Marker> findMode(String commo) {
-        return markerDao.findByMode(commo);
-    }
-
     @Override
     public Marker findByExamAndSubjectAndNumberAndUserId(int examId, String subjectCode, Integer number, Integer userId) {
         return markerDao.findByExamIdAndSubjectCodeAndGroupNumberAndUserId(examId, subjectCode, number, userId);

+ 20 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/query/ExamSubjectSearchQuery.java

@@ -23,6 +23,10 @@ public class ExamSubjectSearchQuery extends BaseQuery<ExamSubject> {
 
     private String codeIn;
 
+    private String codeNotIn;
+
+    private Boolean finished;
+
     public int getExamId() {
         return examId;
     }
@@ -99,4 +103,20 @@ public class ExamSubjectSearchQuery extends BaseQuery<ExamSubject> {
         this.codeIn = codeIn;
     }
 
+    public Boolean getFinished() {
+        return finished;
+    }
+
+    public void setFinished(Boolean finished) {
+        this.finished = finished;
+    }
+
+    public String getCodeNotIn() {
+        return codeNotIn;
+    }
+
+    public void setCodeNotIn(String codeNotIn) {
+        this.codeNotIn = codeNotIn;
+    }
+
 }

+ 12 - 8
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkLibraryDao.java

@@ -2,6 +2,7 @@ package cn.com.qmth.stmms.biz.mark.dao;
 
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
+
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -22,13 +23,13 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     List<MarkLibrary> findByExamIdAndSubjectCodeAndGroupNumberAndStatusIn(Integer examId, String subjectCode,
             Integer groupNumber, Set<LibraryStatus> statusSet, Pageable page);
 
-    @Query("select l1 from MarkLibrary l1 where l1.examId=?1 and l1.subjectCode=?2 and l1.groupNumber=?3 and l1.status in (?5) "
-            + "and not exists (select l2 from MarkLibrary l2 where l2.studentId=l1.studentId and l2.id!=l1.id and l2.markerId=?4)")
+    @Query("select l1 from MarkLibrary l1 where l1.examId=?1 and l1.subjectCode=?2 and l1.groupNumber=?3 and l1.status in (?5) and (l1.markerId=?4 or l1.markerId is null ) "
+            + "and not exists (select l2 from MarkLibrary l2 where l2.studentId=l1.studentId and l2.id!=l1.id and l2.markerId=?4 and l2.markerScore is not null) ")
     List<MarkLibrary> findUnMarked(Integer examId, String subjectCode, Integer groupNumber, Integer markerId,
             Set<LibraryStatus> statusSet, Pageable page);
 
-    @Query("select l1 from MarkLibrary l1 where l1.examId=?1 and l1.subjectCode=?2 and l1.groupNumber=?3 and l1.status in (?6) "
-            + "and not exists (select l2 from MarkLibrary l2 where l2.studentId=l1.studentId and l2.id!=l1.id and l2.markerId=?4) "
+    @Query("select l1 from MarkLibrary l1 where l1.examId=?1 and l1.subjectCode=?2 and l1.groupNumber=?3 and l1.status in (?6) and (l1.markerId=?4 or l1.markerId is null ) "
+            + "and not exists (select l2 from MarkLibrary l2 where l2.studentId=l1.studentId and l2.id!=l1.id and l2.markerId=?4 and l2.markerScore is not null) "
             + "and exists (select mc.id from MarkerClass mc, ExamStudent s where l1.studentId=s.id and mc.userId=?5 and s.className=mc.className)")
     List<MarkLibrary> findUnMarkedFilterClass(Integer examId, String subjectCode, Integer groupNumber, Integer markerId,
             Integer userId, Set<LibraryStatus> statusSet, Pageable page);
@@ -84,9 +85,9 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     void resetByMarkerId(Integer markerId, LibraryStatus status, LibraryStatus... notInStatus);
 
     @Modifying(clearAutomatically = true)
-    @Query("update MarkLibrary m set m.status=?2, m.markerId=null, m.markerTime=null, m.markerScore=null, m.markerScoreList=null, m.markerSpent=null, "
-            + "m.headerId=null , m.headerTime=null , m.headerScore=null , m.headerScoreList=null where m.id=?1 and m.status in (?3)")
-    int resetById(Integer id, LibraryStatus newStatus, LibraryStatus... previousStatus);
+    @Query("update MarkLibrary m set m.status=?3, m.markerId=?2, m.markerTime=null, m.markerScore=null, m.markerScoreList=null, m.markerSpent=null, "
+            + "m.headerId=null , m.headerTime=null , m.headerScore=null , m.headerScoreList=null where m.id=?1 and m.status in (?4)")
+    int resetById(Integer id, Integer markerId, LibraryStatus newStatus, LibraryStatus... previousStatus);
 
     @Query("select f.markerId, count(f) as markerCount from MarkLibrary f where f.examId=?1 and f.status in (?2) group by f.markerId")
     List<Object[]> countMarkerAndStatus(Integer examId, LibraryStatus... status);
@@ -142,9 +143,12 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     MarkLibrary findByStudentIdAndGroupNumberAndTaskNumber(Integer studentId, Integer groupNumber, Integer taskNumber);
 
     @Modifying(clearAutomatically = true)
-    @Query("update MarkLibrary l set l.status=?2, l.markerId=?3, l.markerTime=?4, "
+    @Query("update MarkLibrary l set l.status=?2, l.markerId=?3, l.markerTime=?4,l.markerScore=0, "
             + "l.markerSpent=?5 where l.id=?1 and l.status in (?6) and (l.markerId=null or l.markerId=?3)")
     int updateProblemResult(Integer id, LibraryStatus newStatus, Integer markerId, Date markerTime, Integer spent,
             LibraryStatus... previousStatus);
 
+    @Query("select distinct m.subjectCode from MarkLibrary m where m.examId=?1 and m.status not in (?2) ")
+    Set<String> findSubjectUnFinishByExamId(int examId, LibraryStatus... status);
+
 }

+ 4 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkTrackDao.java

@@ -33,4 +33,8 @@ public interface MarkTrackDao extends JpaRepository<MarkTrack, MarkTrackPK>, Jpa
     @Query("delete from MarkTrack t where t.examId=?1 and t.subjectCode=?2 and t.groupNumber=?3")
     void deleteByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer groupNumber);
 
+    @Modifying(clearAutomatically = true)
+    @Query("delete from MarkTrack t where t.pk.libraryId=?1 and t.pk.questionNumber=?2")
+    void deleteByLibraryIdAndQuestionNumber(Integer id, String questionNumber);
+
 }

+ 40 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/ArbitrateHistory.java

@@ -1,11 +1,24 @@
 package cn.com.qmth.stmms.biz.mark.model;
 
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.common.enums.HistoryStatus;
-
-import javax.persistence.*;
 import java.io.Serializable;
 import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import org.apache.commons.lang.StringUtils;
+
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.utils.ScoreItem;
+import cn.com.qmth.stmms.common.enums.HistoryStatus;
 
 /**
  * 多评仲裁记录表
@@ -210,4 +223,27 @@ public class ArbitrateHistory implements Serializable {
         this.user = user;
     }
 
+    public List<ScoreItem> getScoreItemList() {
+        List<ScoreItem> list = new LinkedList<ScoreItem>();
+        if (StringUtils.isNotBlank(scoreList)) {
+            try {
+                String[] values = scoreList.split(",");
+                for (String value : values) {
+                    if (value.equals("")) {
+                        list.add(new ScoreItem(false));
+                    } else {
+                        ScoreItem item = ScoreItem.parse(value, false);
+                        if (item != null) {
+                            list.add(item);
+                        }
+                    }
+                }
+                if (scoreList.endsWith(",")) {
+                    list.add(new ScoreItem(false));
+                }
+            } catch (Exception e) {
+            }
+        }
+        return list;
+    }
 }

+ 10 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkLibrary.java

@@ -226,11 +226,18 @@ public class MarkLibrary implements Serializable {
             try {
                 String[] values = scoreList.split(",");
                 for (String value : values) {
-                    ScoreItem item = ScoreItem.parse(value, false);
-                    if (item != null) {
-                        list.add(item);
+                    if (value.equals("")) {
+                        list.add(new ScoreItem(false));
+                    } else {
+                        ScoreItem item = ScoreItem.parse(value, false);
+                        if (item != null) {
+                            list.add(item);
+                        }
                     }
                 }
+                if (scoreList.endsWith(",")) {
+                    list.add(new ScoreItem(false));
+                }
             } catch (Exception e) {
             }
         }
@@ -300,5 +307,4 @@ public class MarkLibrary implements Serializable {
     public void setMarkerLoginName(String markerLoginName) {
         this.markerLoginName = markerLoginName;
     }
-
 }

+ 31 - 27
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkResult.java

@@ -5,6 +5,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.lang.StringUtils;
+
 import cn.com.qmth.stmms.biz.exam.model.Marker;
 
 /**
@@ -15,10 +17,12 @@ import cn.com.qmth.stmms.biz.exam.model.Marker;
  */
 public class MarkResult {
 
+    public static final String SPLIT = ",";
+
     /**
      * 评卷状态
      */
-    private int statusValue;
+    private String statusValue;
 
     /**
      * 考生编号
@@ -33,12 +37,12 @@ public class MarkResult {
     /**
      * 总分
      */
-    private double totalScore;
+    private double markerScore;
 
     /**
      * 分值列表
      */
-    private String scoreList;
+    private Double[] scoreList;
 
     /**
      * 阅卷轨迹列表
@@ -48,7 +52,7 @@ public class MarkResult {
     /**
      * 特殊标记列表
      */
-    private SpecialTagDTO[] tagList;
+    private SpecialTagDTO[] specialTagList;
 
     /**
      * 所花时间
@@ -63,13 +67,13 @@ public class MarkResult {
     /**
      * 问题类型
      */
-    private int reason;
+    private Integer problemTypeId;
 
-    public int getStatusValue() {
+    public String getStatusValue() {
         return statusValue;
     }
 
-    public void setStatusValue(int statusValue) {
+    public void setStatusValue(String statusValue) {
         this.statusValue = statusValue;
     }
 
@@ -81,19 +85,19 @@ public class MarkResult {
         this.libraryId = libraryId;
     }
 
-    public double getTotalScore() {
-        return totalScore;
+    public double getMarkerScore() {
+        return markerScore;
     }
 
-    public void setTotalScore(double totalScore) {
-        this.totalScore = totalScore;
+    public void setMarkerScore(double markerScore) {
+        this.markerScore = markerScore;
     }
 
     public String getScoreList() {
-        return scoreList;
+        return StringUtils.join(scoreList, SPLIT);
     }
 
-    public void setScoreList(String scoreList) {
+    public void setScoreList(Double[] scoreList) {
         this.scoreList = scoreList;
     }
 
@@ -105,12 +109,12 @@ public class MarkResult {
         this.trackList = trackList;
     }
 
-    public SpecialTagDTO[] getTagList() {
-        return tagList;
+    public SpecialTagDTO[] getSpecialTagList() {
+        return specialTagList;
     }
 
-    public void setTagList(SpecialTagDTO[] tagList) {
-        this.tagList = tagList;
+    public void setSpecialTagList(SpecialTagDTO[] specialTagList) {
+        this.specialTagList = specialTagList;
     }
 
     public int getSpent() {
@@ -133,7 +137,7 @@ public class MarkResult {
         Map<String, List<TrackDTO>> map = new HashMap<String, List<TrackDTO>>();
         if (trackList != null) {
             for (TrackDTO dto : trackList) {
-                String questionNumber = dto.getQuestionNumber();
+                String questionNumber = dto.getMainNumber() + "." + dto.getSubNumber();
                 List<TrackDTO> list = map.get(questionNumber);
                 if (list == null) {
                     list = new LinkedList<TrackDTO>();
@@ -149,7 +153,7 @@ public class MarkResult {
         Map<String, List<MarkTrack>> map = new HashMap<>();
         if (trackList != null) {
             for (TrackDTO dto : trackList) {
-                String questionNumber = dto.getQuestionNumber();
+                String questionNumber = dto.getMainNumber() + "." + dto.getSubNumber();
                 List<MarkTrack> list = map.get(questionNumber);
                 if (list == null) {
                     list = new LinkedList<MarkTrack>();
@@ -183,8 +187,8 @@ public class MarkResult {
 
     public List<MarkSpecialTag> getSpecialTagList(MarkLibrary library, Marker marker) {
         List<MarkSpecialTag> list = new LinkedList<>();
-        if (tagList != null) {
-            for (SpecialTagDTO dto : tagList) {
+        if (specialTagList != null) {
+            for (SpecialTagDTO dto : specialTagList) {
                 list.add(dto.transform(library));
             }
         }
@@ -193,8 +197,8 @@ public class MarkResult {
 
     public List<TrialTag> getTagList(TrialHistory history) {
         List<TrialTag> list = new LinkedList<>();
-        if (tagList != null) {
-            for (SpecialTagDTO dto : tagList) {
+        if (specialTagList != null) {
+            for (SpecialTagDTO dto : specialTagList) {
                 list.add(dto.transform(history));
             }
         }
@@ -209,12 +213,12 @@ public class MarkResult {
         this.isProblem = isProblem;
     }
 
-    public int getReason() {
-        return reason;
+    public Integer getProblemTypeId() {
+        return problemTypeId;
     }
 
-    public void setReason(int reason) {
-        this.reason = reason;
+    public void setProblemTypeId(Integer problemTypeId) {
+        this.problemTypeId = problemTypeId;
     }
 
 }

+ 38 - 96
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkStepDTO.java

@@ -1,7 +1,5 @@
 package cn.com.qmth.stmms.biz.mark.model;
 
-import org.apache.commons.lang3.StringUtils;
-
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
@@ -10,73 +8,25 @@ public class MarkStepDTO implements Serializable {
 
     private static final long serialVersionUID = 3542801746602688750L;
 
-    private String questionNumber;
+    private int groupNumber;
 
     private int mainNumber;
 
-    private int subNumber;
-
-    private int number;
-
-    private String title;
-
-    private double totalScore;
+    private String subNumber;
 
-    private double max;
-
-    private double min;
+    private double intervalScore;
 
     private double defaultScore;
 
-    private double interval;
-
-    private double[] scoreList;
-
-    private String remark;
-
-    private List<TrackDTO> trackList = new ArrayList<TrackDTO>();
-
-    public String getQuestionNumber() {
-        return questionNumber;
-    }
-
-    public void setQuestionNumber(String questionNumber) {
-        this.questionNumber = questionNumber;
-    }
+    private String title;
 
-    public double[] getScoreArray() {
-        return scoreList;
-    }
+    private Double score;
 
-    public double[] getScoreList() {
-        return scoreList;
-    }
+    private double maxScore;
 
-    public void setScoreList(double[] scoreList) {
-        this.scoreList = scoreList;
-    }
+    private double minScore;
 
-    public void setScoreList(String scoreListValue) {
-        String[] values = StringUtils.split(StringUtils.trimToEmpty(scoreListValue), ",");
-        if (values != null && values.length > 0) {
-            List<Double> list = new ArrayList<Double>(values.length);
-            for (String value : values) {
-                try {
-                    list.add(Double.valueOf(value));
-                } catch (Exception e) {
-                    continue;
-                }
-            }
-            int length = list.size();
-            if (length > 0) {
-                double[] array = new double[length];
-                for (int i = 0; i < length; i++) {
-                    array[i] = list.get(i);
-                }
-                this.scoreList = array;
-            }
-        }
-    }
+    private List<TrackDTO> trackList = new ArrayList<TrackDTO>();
 
     public int getMainNumber() {
         return mainNumber;
@@ -86,44 +36,36 @@ public class MarkStepDTO implements Serializable {
         this.mainNumber = mainNumber;
     }
 
-    public int getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(int subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 
-    public int getNumber() {
-        return number;
+    public double getIntervalScore() {
+        return intervalScore;
     }
 
-    public void setNumber(int number) {
-        this.number = number;
+    public void setIntervalScore(double intervalScore) {
+        this.intervalScore = intervalScore;
     }
 
-    public double getTotalScore() {
-        return totalScore;
+    public double getMaxScore() {
+        return maxScore;
     }
 
-    public void setTotalScore(double totalScore) {
-        this.totalScore = totalScore;
+    public void setMaxScore(double maxScore) {
+        this.maxScore = maxScore;
     }
 
-    public double getMax() {
-        return max;
+    public double getMinScore() {
+        return minScore;
     }
 
-    public void setMax(double max) {
-        this.max = max;
-    }
-
-    public double getMin() {
-        return min;
-    }
-
-    public void setMin(double min) {
-        this.min = min;
+    public void setMinScore(double minScore) {
+        this.minScore = minScore;
     }
 
     public double getDefaultScore() {
@@ -134,14 +76,6 @@ public class MarkStepDTO implements Serializable {
         this.defaultScore = defaultScore;
     }
 
-    public String getRemark() {
-        return remark;
-    }
-
-    public void setRemark(String remark) {
-        this.remark = remark;
-    }
-
     public String getTitle() {
         return title;
     }
@@ -150,14 +84,6 @@ public class MarkStepDTO implements Serializable {
         this.title = title;
     }
 
-    public double getInterval() {
-        return interval;
-    }
-
-    public void setInterval(double interval) {
-        this.interval = interval;
-    }
-
     public List<TrackDTO> getTrackList() {
         return trackList;
     }
@@ -170,4 +96,20 @@ public class MarkStepDTO implements Serializable {
         this.trackList.add(track);
     }
 
+    public int getGroupNumber() {
+        return groupNumber;
+    }
+
+    public void setGroupNumber(int groupNumber) {
+        this.groupNumber = groupNumber;
+    }
+
+    public Double getScore() {
+        return score;
+    }
+
+    public void setScore(Double score) {
+        this.score = score;
+    }
+
 }

+ 140 - 170
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/Task.java

@@ -4,89 +4,86 @@ import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
 
-public class Task extends MarkResult implements Serializable {
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+
+public class Task implements Serializable {
 
     private static final long serialVersionUID = 4912665442008033200L;
 
     /**
-     * 评卷状态
+     * 评卷任务编号
      */
-    private String statusName;
+    private Integer libraryId;
 
     /**
-     * 显示任务编号
+     * 考生编号
      */
-    private String taskNumber;
+    private Integer studentId;
 
     /**
-     * 显示考生
+     * 显示考生
      */
-    private String studentNumber;
+    private String secretNumber;
 
     /**
-     * 题卡图片地址
+     * 考生名称
      */
-    private List<String> pictureUrls;
+    private String studentName;
 
     /**
-     * 题卡图片拼接配置
+     * 学号
      */
-    private List<PictureConfigItem> pictureConfig;
+    private String studentCode;
 
     /**
-     * 题卡原图地址
+     * 准考证号
      */
-    private List<String> sheetUrls;
+    private String examNumber;
 
     /**
-     * 答案文档地址
+     * 科目
      */
-    private String answerUrl;
+    private ExamSubject subject;
 
     /**
-     * 试卷文档地址
+     * 裁切图地址
      */
-    private String paperUrl;
+    private List<String> sliceUrls;
 
     /**
-     * 多媒体答案地址
+     * 题卡图片拼接配置
      */
-    private String jsonUrl;
+    private List<PictureConfigItem> sliceConfig;
 
     /**
-     * 客观题总分
+     * 多媒体地址
      */
-    private Double objectiveScore;
+    private String jsonUrl;
 
     /**
      * 给分步骤
      */
-    private List<MarkStepDTO> markStepList;
+    private List<MarkStepDTO> questionList;
 
     /**
-     * 是否自评
+     * 特殊标记列表
      */
-    private boolean isSelf;
+    private SpecialTagDTO[] specialTagList;
 
     /**
-     * 是否回评
-     */
-    private boolean isPrevious;
-
-    /**
-     * 是否打回
+     * 题卡原图地址
      */
-    private boolean isBack;
+    private List<String> sheetUrls;
 
     /**
-     * 是否问题卷
+     * 客观题总分
      */
-    private boolean isProblem;
+    private Double objectiveScore;
 
     /**
-     * 问题类型
+     * 评分
      */
-    private int reason;
+    private Double markerScore;
 
     /**
      * 评卷时间
@@ -94,236 +91,209 @@ public class Task extends MarkResult implements Serializable {
     private Date markTime;
 
     /**
-     * 任务是否存在
+     * 是否自评
      */
-    private boolean isExist;
-
-    private String message;
-
-    private Integer headerId;
-
-    private Date headerTime;
-
-    private Double headerScore;
-
-    private String headerScoreList;
-
-    // 仲裁记录集合
-    private List<ArbitrationDTO> arbitrationList;
+    private boolean isSelf;
 
     /**
-     * 显示考生名称
+     * 是否回评
      */
-    private String studentName;
-
-    private Integer markerId;
-
-    public List<String> getPictureUrls() {
-        return pictureUrls;
-    }
-
-    public void setPictureUrls(List<String> pictureUrls) {
-        this.pictureUrls = pictureUrls;
-    }
-
-    public String getAnswerUrl() {
-        return answerUrl;
-    }
+    private boolean isPrevious;
 
-    public void setAnswerUrl(String answerUrl) {
-        this.answerUrl = answerUrl;
-    }
+    /**
+     * 是否打回
+     */
+    private boolean isRejected;
 
-    public String getPaperUrl() {
-        return paperUrl;
-    }
+    /**
+     * 评卷员登录名
+     */
+    private String markerName;
 
-    public void setPaperUrl(String paperUrl) {
-        this.paperUrl = paperUrl;
-    }
+    /**
+     * 复核时间
+     */
+    private Date inspectTime;
 
-    public List<MarkStepDTO> getMarkStepList() {
-        return markStepList;
-    }
+    /**
+     * 错误信息
+     */
+    private String message;
 
-    public void setMarkStepList(List<MarkStepDTO> markStepList) {
-        this.markStepList = markStepList;
+    public Integer getLibraryId() {
+        return libraryId;
     }
 
-    public int getReason() {
-        return reason;
+    public void setLibraryId(Integer libraryId) {
+        this.libraryId = libraryId;
     }
 
-    public void setReason(int reason) {
-        this.reason = reason;
+    public Integer getStudentId() {
+        return studentId;
     }
 
-    public Date getMarkTime() {
-        return markTime;
+    public void setStudentId(Integer studentId) {
+        this.studentId = studentId;
     }
 
-    public void setMarkTime(Date markTime) {
-        this.markTime = markTime;
+    public String getSecretNumber() {
+        return secretNumber;
     }
 
-    public String getMessage() {
-        return message;
+    public void setSecretNumber(String secretNumber) {
+        this.secretNumber = secretNumber;
     }
 
-    public void setMessage(String message) {
-        this.message = message;
-    }
-
-    public Double getObjectiveScore() {
-        return objectiveScore;
+    public String getStudentName() {
+        return studentName;
     }
 
-    public void setObjectiveScore(Double objectiveScore) {
-        this.objectiveScore = objectiveScore;
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
     }
 
-    public List<String> getSheetUrls() {
-        return sheetUrls;
+    public String getStudentCode() {
+        return studentCode;
     }
 
-    public void setSheetUrls(List<String> sheetUrls) {
-        this.sheetUrls = sheetUrls;
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
     }
 
-    public boolean isSelf() {
-        return isSelf;
+    public String getExamNumber() {
+        return examNumber;
     }
 
-    public void setSelf(boolean isSelf) {
-        this.isSelf = isSelf;
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
     }
 
-    public boolean isPrevious() {
-        return isPrevious;
+    public ExamSubject getSubject() {
+        return subject;
     }
 
-    public void setPrevious(boolean isPrevious) {
-        this.isPrevious = isPrevious;
+    public void setSubject(ExamSubject subject) {
+        this.subject = subject;
     }
 
-    public boolean isBack() {
-        return isBack;
+    public List<String> getSliceUrls() {
+        return sliceUrls;
     }
 
-    public void setBack(boolean isBack) {
-        this.isBack = isBack;
+    public void setSliceUrls(List<String> sliceUrls) {
+        this.sliceUrls = sliceUrls;
     }
 
-    public boolean isProblem() {
-        return isProblem;
+    public List<PictureConfigItem> getSliceConfig() {
+        return sliceConfig;
     }
 
-    public void setProblem(boolean isProblem) {
-        this.isProblem = isProblem;
+    public void setSliceConfig(List<PictureConfigItem> sliceConfig) {
+        this.sliceConfig = sliceConfig;
     }
 
-    public boolean isExist() {
-        return isExist;
+    public String getJsonUrl() {
+        return jsonUrl;
     }
 
-    public void setExist(boolean isExist) {
-        this.isExist = isExist;
+    public void setJsonUrl(String jsonUrl) {
+        this.jsonUrl = jsonUrl;
     }
 
-    public Integer getHeaderId() {
-        return headerId;
+    public List<MarkStepDTO> getQuestionList() {
+        return questionList;
     }
 
-    public void setHeaderId(Integer headerId) {
-        this.headerId = headerId;
+    public void setQuestionList(List<MarkStepDTO> questionList) {
+        this.questionList = questionList;
     }
 
-    public Date getHeaderTime() {
-        return headerTime;
+    public SpecialTagDTO[] getSpecialTagList() {
+        return specialTagList;
     }
 
-    public void setHeaderTime(Date headerTime) {
-        this.headerTime = headerTime;
+    public void setSpecialTagList(SpecialTagDTO[] specialTagList) {
+        this.specialTagList = specialTagList;
     }
 
-    public Double getHeaderScore() {
-        return headerScore;
+    public List<String> getSheetUrls() {
+        return sheetUrls;
     }
 
-    public void setHeaderScore(Double headerScore) {
-        this.headerScore = headerScore;
+    public void setSheetUrls(List<String> sheetUrls) {
+        this.sheetUrls = sheetUrls;
     }
 
-    public String getHeaderScoreList() {
-        return headerScoreList;
+    public Double getObjectiveScore() {
+        return objectiveScore;
     }
 
-    public void setHeaderScoreList(String headerScoreList) {
-        this.headerScoreList = headerScoreList;
+    public void setObjectiveScore(Double objectiveScore) {
+        this.objectiveScore = objectiveScore;
     }
 
-    public List<PictureConfigItem> getPictureConfig() {
-        return pictureConfig;
+    public Double getMarkerScore() {
+        return markerScore;
     }
 
-    public void setPictureConfig(List<PictureConfigItem> pictureConfig) {
-        this.pictureConfig = pictureConfig;
+    public void setMarkerScore(Double markerScore) {
+        this.markerScore = markerScore;
     }
 
-    public List<ArbitrationDTO> getArbitrationList() {
-        return arbitrationList;
+    public Date getMarkTime() {
+        return markTime;
     }
 
-    public void setArbitrationList(List<ArbitrationDTO> arbitrationList) {
-        this.arbitrationList = arbitrationList;
+    public void setMarkTime(Date markTime) {
+        this.markTime = markTime;
     }
 
-    public String getStatusName() {
-        return statusName;
+    public boolean isSelf() {
+        return isSelf;
     }
 
-    public void setStatusName(String statusName) {
-        this.statusName = statusName;
+    public void setSelf(boolean isSelf) {
+        this.isSelf = isSelf;
     }
 
-    public String getTaskNumber() {
-        return taskNumber;
+    public boolean isPrevious() {
+        return isPrevious;
     }
 
-    public void setTaskNumber(String taskNumber) {
-        this.taskNumber = taskNumber;
+    public void setPrevious(boolean isPrevious) {
+        this.isPrevious = isPrevious;
     }
 
-    public String getStudentNumber() {
-        return studentNumber;
+    public boolean isRejected() {
+        return isRejected;
     }
 
-    public void setStudentNumber(String studentNumber) {
-        this.studentNumber = studentNumber;
+    public void setRejected(boolean isRejected) {
+        this.isRejected = isRejected;
     }
 
-    public String getStudentName() {
-        return studentName;
+    public String getMarkerName() {
+        return markerName;
     }
 
-    public void setStudentName(String studentName) {
-        this.studentName = studentName;
+    public void setMarkerName(String markerName) {
+        this.markerName = markerName;
     }
 
-    public Integer getMarkerId() {
-        return markerId;
+    public Date getInspectTime() {
+        return inspectTime;
     }
 
-    public void setMarkerId(Integer markerId) {
-        this.markerId = markerId;
+    public void setInspectTime(Date inspectTime) {
+        this.inspectTime = inspectTime;
     }
 
-    public String getJsonUrl() {
-        return jsonUrl;
+    public String getMessage() {
+        return message;
     }
 
-    public void setJsonUrl(String jsonUrl) {
-        this.jsonUrl = jsonUrl;
+    public void setMessage(String message) {
+        this.message = message;
     }
 
 }

+ 25 - 9
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/TrackDTO.java

@@ -13,7 +13,9 @@ public class TrackDTO implements Serializable {
 
     private static final long serialVersionUID = 4336042741848228793L;
 
-    private String questionNumber;
+    private Integer mainNumber;
+
+    private String subNumber;
 
     private int number;
 
@@ -34,7 +36,10 @@ public class TrackDTO implements Serializable {
     }
 
     public TrackDTO(MarkTrack track) {
-        setQuestionNumber(track.getQuestionNumber());
+        String questionNumber = track.getQuestionNumber();
+        String str[] = questionNumber.split("\\.");
+        setMainNumber(Integer.parseInt(str[0]));
+        setSubNumber(str[1]);
         setNumber(track.getNumber());
         setScore(track.getScore());
         setPositionX(track.getPositionX());
@@ -45,7 +50,9 @@ public class TrackDTO implements Serializable {
     }
 
     public TrackDTO(TrialTrack track) {
-        setQuestionNumber(track.getQuestionNumber());
+        String str[] = track.getQuestionNumber().split("\\.");
+        setMainNumber(Integer.parseInt(str[0]));
+        setSubNumber(str[1]);
         setNumber(track.getNumber());
         setScore(track.getScore());
         setPositionX(track.getPositionX());
@@ -58,7 +65,7 @@ public class TrackDTO implements Serializable {
     public MarkTrack transform(MarkLibrary library, Marker marker) {
         MarkTrack track = new MarkTrack();
         track.setLibraryId(library.getId());
-        track.setQuestionNumber(getQuestionNumber());
+        track.setQuestionNumber(getMainNumber() + "." + getSubNumber());
         track.setNumber(getNumber());
         track.setStudentId(library.getStudentId());
         track.setExamId(library.getExamId());
@@ -78,7 +85,7 @@ public class TrackDTO implements Serializable {
         TrialTrack track = new TrialTrack();
         track.setLibraryId(history.getLibraryId());
         track.setMarkerId(history.getMarkerId());
-        track.setQuestionNumber(getQuestionNumber());
+        track.setQuestionNumber(getMainNumber() + "." + getSubNumber());
         track.setNumber(getNumber());
         track.setStudentId(history.getStudentId());
         track.setExamId(history.getExamId());
@@ -94,12 +101,20 @@ public class TrackDTO implements Serializable {
         return track;
     }
 
-    public String getQuestionNumber() {
-        return questionNumber;
+    public Integer getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(Integer mainNumber) {
+        this.mainNumber = mainNumber;
     }
 
-    public void setQuestionNumber(String questionNumber) {
-        this.questionNumber = questionNumber;
+    public String getSubNumber() {
+        return subNumber;
+    }
+
+    public void setSubNumber(String subNumber) {
+        this.subNumber = subNumber;
     }
 
     public int getNumber() {
@@ -157,4 +172,5 @@ public class TrackDTO implements Serializable {
     public void setOffsetY(int offsetY) {
         this.offsetY = offsetY;
     }
+
 }

+ 22 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/TrialHistory.java

@@ -1,10 +1,16 @@
 package cn.com.qmth.stmms.biz.mark.model;
 
 import cn.com.qmth.stmms.biz.exam.model.Marker;
+import cn.com.qmth.stmms.biz.utils.ScoreItem;
 
 import javax.persistence.*;
+
+import org.apache.commons.lang.StringUtils;
+
 import java.io.Serializable;
 import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
 
 /**
  * 试评任务给分记录表
@@ -163,4 +169,20 @@ public class TrialHistory implements Serializable {
         this.secretNumber = secretNumber;
     }
 
+    public List<ScoreItem> getScoreList() {
+        List<ScoreItem> list = new LinkedList<ScoreItem>();
+        if (StringUtils.isNotBlank(markerScoreList)) {
+            try {
+                String[] values = markerScoreList.split(",");
+                for (String value : values) {
+                    ScoreItem item = ScoreItem.parse(value, false);
+                    if (item != null) {
+                        list.add(item);
+                    }
+                }
+            } catch (Exception e) {
+            }
+        }
+        return list;
+    }
 }

+ 10 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/query/ArbitrateHistorySearchQuery.java

@@ -23,6 +23,8 @@ public class ArbitrateHistorySearchQuery extends BaseQuery<ArbitrateHistory> {
 
     private Integer userId;
 
+    private String secretNumber;
+
     public void orderByIdDesc() {
         setSort(new Sort(Direction.DESC, "id"));
     }
@@ -87,4 +89,12 @@ public class ArbitrateHistorySearchQuery extends BaseQuery<ArbitrateHistory> {
         this.userId = userId;
     }
 
+    public String getSecretNumber() {
+        return secretNumber;
+    }
+
+    public void setSecretNumber(String secretNumber) {
+        this.secretNumber = secretNumber;
+    }
+
 }

+ 5 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/ArbitrateHistoryServiceImpl.java

@@ -99,8 +99,11 @@ public class ArbitrateHistoryServiceImpl extends BaseQueryService<ArbitrateHisto
                 if (query.getStatus() != null) {
                     predicates.add(cb.equal(root.get("status").as(HistoryStatus.class), query.getStatus()));
                 }
-                return predicates.isEmpty() ? cb.conjunction()
-                        : cb.and(predicates.toArray(new Predicate[predicates.size()]));
+                if (StringUtils.isNotBlank(query.getSecretNumber())) {
+                    predicates.add(cb.equal(root.get("secretNumber"), query.getSecretNumber()));
+                }
+                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
+                        .size()]));
             }
         };
     }

+ 54 - 10
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkLibraryServiceImpl.java

@@ -1,6 +1,5 @@
 package cn.com.qmth.stmms.biz.mark.service.Impl;
 
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -21,6 +20,8 @@ import cn.com.qmth.stmms.biz.mark.dao.MarkLibraryDao;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
+import cn.com.qmth.stmms.biz.utils.TaskLock;
+import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 
 @Service
@@ -46,10 +47,7 @@ public class MarkLibraryServiceImpl extends BaseQueryService<MarkLibrary> implem
 
     @Override
     public List<MarkLibrary> findUnMarked(Integer examId, String subjectCode, Integer groupNumber, Integer markerId,
-            Integer userId, boolean filterClass, int pageNumber, int pageSize) {
-        Set<LibraryStatus> statusSet = new HashSet<>();
-        statusSet.add(LibraryStatus.WAITING);
-
+            Integer userId, boolean filterClass, int pageNumber, int pageSize, Set<LibraryStatus> statusSet) {
         MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
         query.setPageNumber(pageNumber);
         query.setPageSize(pageSize);
@@ -61,12 +59,14 @@ public class MarkLibraryServiceImpl extends BaseQueryService<MarkLibrary> implem
 
     @Override
     public long countByExamAndSubjectAndGroupAndStatus(int examId, String subjectCode, int groupNumber,
-            LibraryStatus status) {
+            LibraryStatus... status) {
         MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
         query.setExamId(examId);
         query.setSubjectCode(subjectCode);
         query.setGroupNumber(groupNumber);
-        query.addStatus(status);
+        for (LibraryStatus libraryStatus : status) {
+            query.addStatus(libraryStatus);
+        }
         return countByQuery(query);
     }
 
@@ -136,9 +136,8 @@ public class MarkLibraryServiceImpl extends BaseQueryService<MarkLibrary> implem
                     Predicate predicate2 = cb.le(root.get("markerScore"), query.getEndScroe());
                     predicates.add(cb.and(predicate1, predicate2));
                 }
-                return predicates.isEmpty() ?
-                        cb.conjunction() :
-                        cb.and(predicates.toArray(new Predicate[predicates.size()]));
+                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
+                        .size()]));
             }
         };
     }
@@ -164,4 +163,49 @@ public class MarkLibraryServiceImpl extends BaseQueryService<MarkLibrary> implem
         return libraryDao.save(library);
     }
 
+    @Override
+    public void releaseByLibrary(MarkLibrary library) {
+        TaskLock taskLock = TaskLockUtil.getInspectedLibraryTask(getGroupKey(library));
+        taskLock.remove(library.getId(), 1);
+    }
+
+    @Override
+    public void releaseByUserId(Integer examId, String subjectCode, Integer groupNumber, Integer userId) {
+        TaskLock taskLock = TaskLockUtil.getInspectedLibraryTask(examId + "_" + subjectCode + "_" + groupNumber);
+        taskLock.clear(userId);
+    }
+
+    @Override
+    public boolean hasApplied(MarkLibrary library, Integer userId) {
+        TaskLock taskLock = TaskLockUtil.getInspectedLibraryTask(getGroupKey(library));
+        return taskLock.exist(library.getId(), 1, userId);
+    }
+
+    @Override
+    public boolean applyLibrary(MarkLibrary library, Integer userId) {
+        TaskLock taskLock = TaskLockUtil.getInspectedLibraryTask(getGroupKey(library));
+        boolean lock = taskLock.add(library.getId(), 1, userId);
+        // 上锁失败直接返回
+        if (!lock) {
+            return false;
+        }
+        // 重复校验任务状态
+        if (library.getStatus().equals(LibraryStatus.MARKED)) {
+            return true;
+        } else {
+            taskLock.remove(library.getId(), 1, userId);
+            return false;
+        }
+    }
+
+    private String getGroupKey(MarkLibrary library) {
+        return library.getExamId() + "_" + library.getSubjectCode() + "_" + library.getGroupNumber();
+    }
+
+    @Override
+    public Set<String> findSubjectUnFinishByExamId(int examId) {
+        return libraryDao.findSubjectUnFinishByExamId(examId, LibraryStatus.MARKED, LibraryStatus.ARBITRATED,
+                LibraryStatus.INSPECTED);
+    }
+
 }

+ 334 - 29
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java

@@ -1,5 +1,6 @@
 package cn.com.qmth.stmms.biz.mark.service.Impl;
 
+<<<<<<< HEAD
 import cn.com.qmth.stmms.biz.exam.dao.*;
 import cn.com.qmth.stmms.biz.exam.model.*;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
@@ -13,6 +14,15 @@ import cn.com.qmth.stmms.biz.utils.TaskLock;
 import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
 import cn.com.qmth.stmms.common.enums.*;
 import cn.com.qmth.stmms.common.utils.BigDecimalUtils;
+=======
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+>>>>>>> release_1.3.1
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,8 +31,58 @@ import org.springframework.data.domain.Sort;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+<<<<<<< HEAD
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
+=======
+import cn.com.qmth.stmms.biz.exam.dao.ExamQuestionDao;
+import cn.com.qmth.stmms.biz.exam.dao.MarkGroupDao;
+import cn.com.qmth.stmms.biz.exam.dao.MarkGroupStudentDao;
+import cn.com.qmth.stmms.biz.exam.dao.MarkerDao;
+import cn.com.qmth.stmms.biz.exam.dao.SubjectiveScoreDao;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroupStudent;
+import cn.com.qmth.stmms.biz.exam.model.Marker;
+import cn.com.qmth.stmms.biz.exam.model.SubjectiveScore;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.mark.dao.ArbitrateHistoryDao;
+import cn.com.qmth.stmms.biz.mark.dao.MarkLibraryDao;
+import cn.com.qmth.stmms.biz.mark.dao.MarkSpecialTagDao;
+import cn.com.qmth.stmms.biz.mark.dao.MarkTrackDao;
+import cn.com.qmth.stmms.biz.mark.dao.ProblemHistoryDao;
+import cn.com.qmth.stmms.biz.mark.dao.TrialHistoryDao;
+import cn.com.qmth.stmms.biz.mark.dao.TrialLibraryDao;
+import cn.com.qmth.stmms.biz.mark.dao.TrialTagDao;
+import cn.com.qmth.stmms.biz.mark.dao.TrialTrackDao;
+import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
+import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
+import cn.com.qmth.stmms.biz.mark.model.MarkResult;
+import cn.com.qmth.stmms.biz.mark.model.MarkStepDTO;
+import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
+import cn.com.qmth.stmms.biz.mark.model.ProblemHistory;
+import cn.com.qmth.stmms.biz.mark.model.SubmitResult;
+import cn.com.qmth.stmms.biz.mark.model.TrialHistory;
+import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
+import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
+import cn.com.qmth.stmms.biz.mark.service.MarkService;
+import cn.com.qmth.stmms.biz.utils.ScoreItem;
+import cn.com.qmth.stmms.biz.utils.TaskLock;
+import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
+import cn.com.qmth.stmms.common.enums.HistoryStatus;
+import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
+import cn.com.qmth.stmms.common.enums.ObjectiveStatus;
+import cn.com.qmth.stmms.common.enums.ScorePolicy;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+import cn.com.qmth.stmms.common.enums.ThirdPolicy;
+import cn.com.qmth.stmms.common.utils.BigDecimalUtils;
+>>>>>>> release_1.3.1
 
 /**
  * 与评卷相关的所有修改操作(非传播性的新增操作除外),全部汇总到这里进行集中控制
@@ -82,6 +142,12 @@ public class MarkServiceImpl implements MarkService {
     @Autowired
     private ProblemHistoryDao problemHistoryDao;
 
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
     private Map<Integer, Long> markerLastUpdateTime = new ConcurrentHashMap<>();
 
     /**
@@ -213,7 +279,7 @@ public class MarkServiceImpl implements MarkService {
         markerDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                 group.getNumber());
         // 小题数据
-        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+        questionDao.resetByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                 false, group.getNumber());
         // 考生分组状态与得分明细
         groupStudentDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
@@ -223,12 +289,12 @@ public class MarkServiceImpl implements MarkService {
         // 删除分组
         releaseByGroup(group);
         groupDao.delete(group);
-        // 科目总分
-        subjectService.updateScore(group.getExamId(), group.getSubjectCode(), false,
-                sumTotalScore(group.getExamId(), group.getSubjectCode()));
+        // 未分组的题目
+        long unGroupQuestionCount = questionDao.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(
+                group.getExamId(), group.getSubjectCode(), false);
         // 考生整体状态与总分更新
         long groupCount = groupDao.countByExamIdAndSubjectCode(group.getExamId(), group.getSubjectCode());
-        if (groupCount == 0) {
+        if (groupCount == 0 || unGroupQuestionCount > 0) {
             studentService.updateSubjectiveStatusAndScoreAndInspectorId(group.getExamId(), group.getSubjectCode(),
                     SubjectiveStatus.UNMARK, 0, null, null, null);
         } else {
@@ -236,7 +302,7 @@ public class MarkServiceImpl implements MarkService {
                     group.getExamId(), group.getSubjectCode(), SubjectiveStatus.UNMARK, SubjectiveStatus.MARKED,
                     SubjectiveStatus.INSPECTED);
             for (Integer studentId : studentList) {
-                checkStudentSubjective(studentId, groupCount);
+                checkStudentSubjective(studentId, groupCount, unGroupQuestionCount);
             }
         }
     }
@@ -250,8 +316,12 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void updateGroup(MarkGroup group, List<ExamQuestion> questionList, ScorePolicy policy, ThirdPolicy third) {
-        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                false, group.getNumber());
+        List<ExamQuestion> old = questionDao.findByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(),
+                group.getSubjectCode(), false, group.getNumber());
+        for (ExamQuestion question : old) {
+            question.setGroupNumber(null);
+            questionDao.saveAndFlush(question);
+        }
         double totalScore = 0d;
         for (ExamQuestion question : questionList) {
             totalScore += question.getTotalScore();
@@ -264,6 +334,16 @@ public class MarkServiceImpl implements MarkService {
         subjectService.updateScore(group.getExamId(), group.getSubjectCode(), false,
                 sumTotalScore(group.getExamId(), group.getSubjectCode()));
         resetGroup(group);
+        if (group.getStatus() == MarkStatus.FORMAL) {
+            libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                    group.getNumber());
+        }
+        if (group.getStatus() == MarkStatus.TRIAL) {
+            trialLibraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                    group.getNumber());
+        }
+        updateLibraryCount(group);
+        groupDao.updateBuildTime(group.getExamId(), group.getSubjectCode(), group.getNumber(), null);
     }
 
     /**
@@ -304,7 +384,11 @@ public class MarkServiceImpl implements MarkService {
     @Override
     public boolean applyLibrary(TrialLibrary library, Marker marker) {
         TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
+<<<<<<< HEAD
         return taskLock.add(getApplyTaskId(library, marker), 1, marker.getId());
+=======
+        return taskLock.add(library.getStudentId(), 1, marker.getId());
+>>>>>>> release_1.3.1
     }
 
     /**
@@ -334,7 +418,11 @@ public class MarkServiceImpl implements MarkService {
     @Override
     public boolean hasApplied(TrialLibrary library, Marker marker) {
         TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
+<<<<<<< HEAD
         return taskLock.exist(getApplyTaskId(library, marker), 1, marker.getId());
+=======
+        return taskLock.exist(library.getStudentId(), 1, marker.getId());
+>>>>>>> release_1.3.1
     }
 
     /**
@@ -354,7 +442,11 @@ public class MarkServiceImpl implements MarkService {
             taskLock.refresh(marker.getId());
         } else if (result.getTrialLibrary() != null) {
             TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
+<<<<<<< HEAD
             taskLock.remove(getApplyTaskId(result.getTrialLibrary(), marker), 1, marker.getId());
+=======
+            taskLock.remove(result.getTrialLibrary().getStudentId(), 1, marker.getId());
+>>>>>>> release_1.3.1
         }
     }
 
@@ -368,6 +460,11 @@ public class MarkServiceImpl implements MarkService {
     public void releaseByMarker(Marker marker) {
         TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
         taskLock.clear(marker.getId());
+<<<<<<< HEAD
+=======
+        TaskLock lock = TaskLockUtil.getTrialTask(getGroupKey(marker));
+        lock.clear(marker.getId());
+>>>>>>> release_1.3.1
     }
 
     /**
@@ -418,7 +515,7 @@ public class MarkServiceImpl implements MarkService {
             for (MarkLibrary library : list) {
                 trackDao.deleteByLibraryId(library.getId());
                 specialTagDao.deleteByLibraryId(library.getId());
-                libraryDao.resetById(library.getId(), LibraryStatus.WAITING, library.getStatus());
+                libraryDao.resetById(library.getId(), null, LibraryStatus.WAITING, library.getStatus());
                 updateStudentGroupStatus(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
                         library.getGroupNumber(), SubjectiveStatus.UNMARK);
                 studentService.updateSubjectiveStatusAndScore(library.getStudentId(), SubjectiveStatus.UNMARK, 0, null);
@@ -474,7 +571,11 @@ public class MarkServiceImpl implements MarkService {
         // 判断评卷分组是否存在/评卷是否结束
         MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
         if (group == null || group.getStatus() == MarkStatus.FINISH
+<<<<<<< HEAD
                 || group.getStatus().getValue() != result.getStatusValue()) {
+=======
+                || !group.getStatus().toString().equals(result.getStatusValue())) {
+>>>>>>> release_1.3.1
             return SubmitResult.faile();
         }
 
@@ -485,20 +586,33 @@ public class MarkServiceImpl implements MarkService {
                 // 状态更新
                 Date now = new Date();
                 if (libraryDao.updateProblemResult(library.getId(), LibraryStatus.PROBLEM, marker.getId(), now,
-                        result.getSpent(), LibraryStatus.WAITING, LibraryStatus.MARKED, LibraryStatus.INSPECTED) != 0) {
+                        result.getSpent(), LibraryStatus.WAITING, LibraryStatus.MARKED, LibraryStatus.INSPECTED,
+                        LibraryStatus.REJECTED) != 0) {
                     saveProblemHistory(result, library);
                     updateMarkedCount(group);
+<<<<<<< HEAD
                     // releaseLibrary(library, marker);
+=======
+                    // 未评完
+                    resetStudentGroup(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
+                            library.getGroupNumber());
+                    trackDao.deleteByLibraryId(library.getId());
+                    specialTagDao.deleteByLibraryId(library.getId());
+>>>>>>> release_1.3.1
                     return SubmitResult.success(library);
                 }
             }
             if (library != null && library.getExamId().equals(group.getExamId())
                     && library.getSubjectCode().equals(group.getSubjectCode())
                     && library.getGroupNumber().equals(group.getNumber())
-                    && result.getTotalScore() <= group.getTotalScore() && StringUtils.isNotBlank(result.getScoreList())) {
+                    && result.getMarkerScore() <= group.getTotalScore()
+                    && StringUtils.isNotBlank(result.getScoreList())) {
                 if (submitLibrary(library, marker, group, result)) {
                     updateMarkedCount(group);
+<<<<<<< HEAD
                     // releaseLibrary(library, marker);
+=======
+>>>>>>> release_1.3.1
                     return SubmitResult.success(library);
                 }
             }
@@ -507,7 +621,8 @@ public class MarkServiceImpl implements MarkService {
             if (library != null && library.getExamId().equals(group.getExamId())
                     && library.getSubjectCode().equals(group.getSubjectCode())
                     && library.getGroupNumber().equals(group.getNumber())
-                    && result.getTotalScore() <= group.getTotalScore() && StringUtils.isNotBlank(result.getScoreList())) {
+                    && result.getMarkerScore() <= group.getTotalScore()
+                    && StringUtils.isNotBlank(result.getScoreList())) {
                 TrialHistory history = new TrialHistory();
                 history.setExamId(library.getExamId());
                 history.setSubjectCode(library.getSubjectCode());
@@ -516,7 +631,7 @@ public class MarkServiceImpl implements MarkService {
                 history.setStudentId(library.getStudentId());
                 history.setMarkerId(marker.getId());
                 history.setMarkerTime(new Date());
-                history.setMarkerScore(result.getTotalScore());
+                history.setMarkerScore(result.getMarkerScore());
                 history.setMarkerScoreList(result.getScoreList());
                 history.setSecretNumber(library.getSecretNumber());
                 history = trialHistoryDao.save(history);
@@ -525,7 +640,7 @@ public class MarkServiceImpl implements MarkService {
                         trialTrackDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
                         trialTrackDao.save(result.getTrackList(history));
                     }
-                    if (result.getTagList() != null) {
+                    if (result.getSpecialTagList() != null) {
                         trialTagDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
                         trialTagDao.save(result.getTagList(history));
                     }
@@ -551,7 +666,7 @@ public class MarkServiceImpl implements MarkService {
         history.setLibraryId(library.getId());
         history.setSubjectCode(library.getSubjectCode());
         history.setGroupNumber(library.getGroupNumber());
-        history.setProblemId(result.getReason());
+        history.setProblemId(result.getProblemTypeId());
         history.setStatus(HistoryStatus.WAITING);
         problemHistoryDao.save(history);
     }
@@ -570,7 +685,8 @@ public class MarkServiceImpl implements MarkService {
      */
     private boolean submitLibrary(MarkLibrary library, Marker marker, MarkGroup group, MarkResult result) {
         // 非本人领取的待评任务
-        if (library.getStatus() == LibraryStatus.WAITING && !hasApplied(library, marker)) {
+        if ((library.getStatus() == LibraryStatus.WAITING || library.getStatus() == LibraryStatus.REJECTED)
+                && !hasApplied(library, marker)) {
             return false;
         }
         // 非本人的回评任务
@@ -579,15 +695,15 @@ public class MarkServiceImpl implements MarkService {
             return false;
         }
         // 是否多评情况下已处理过该考生评卷任务
-        if (libraryDao.countByStudentIdAndMarkerIdAndIdNotEqual(library.getStudentId(), library.getMarkerId(),
-                library.getId()) > 0) {
+        if (libraryDao
+                .countByStudentIdAndMarkerIdAndIdNotEqual(library.getStudentId(), marker.getId(), library.getId()) > 0) {
             return false;
         }
         // 尝试提交评卷结果
         Date now = new Date();
         if (libraryDao.updateMarkerResult(library.getId(), LibraryStatus.MARKED, marker.getId(),
-                result.getTotalScore(), result.getScoreList(), now, result.getSpent(), LibraryStatus.WAITING,
-                LibraryStatus.MARKED, LibraryStatus.INSPECTED) == 0) {
+                result.getMarkerScore(), result.getScoreList(), now, result.getSpent(), LibraryStatus.WAITING,
+                LibraryStatus.MARKED, LibraryStatus.INSPECTED, LibraryStatus.REJECTED) == 0) {
             // 条件不符更新失败,直接返回
             return false;
         }
@@ -600,7 +716,7 @@ public class MarkServiceImpl implements MarkService {
             }
         }
         // 保存特殊标记
-        if (result.getTagList() != null) {
+        if (result.getSpecialTagList() != null) {
             specialTagDao.deleteByLibraryId(library.getId());
             specialTagDao.save(result.getSpecialTagList(library, marker));
         }
@@ -616,7 +732,7 @@ public class MarkServiceImpl implements MarkService {
                     continue;
                 }
                 // 分差超过阀值,
-                if (Math.abs(other.getMarkerScore() - result.getTotalScore()) > group.getArbitrateThreshold()) {
+                if (Math.abs(other.getMarkerScore() - result.getMarkerScore()) > group.getArbitrateThreshold()) {
                     // 开启三评
                     if (group.getThirdPolicy().equals(ThirdPolicy.LOW_DIFF_HIGH_AVG)) {
                         if (libraryDao.countByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber()) == 2) {
@@ -715,8 +831,8 @@ public class MarkServiceImpl implements MarkService {
         if (group.getStatus() == MarkStatus.FINISH) {
             return false;
         }
-        if (libraryDao.resetById(library.getId(), LibraryStatus.WAITING, LibraryStatus.MARKED, LibraryStatus.PROBLEM,
-                LibraryStatus.INSPECTED) > 0) {
+        if (libraryDao.resetById(library.getId(), library.getMarkerId(), LibraryStatus.REJECTED, LibraryStatus.MARKED,
+                LibraryStatus.PROBLEM, LibraryStatus.INSPECTED) > 0) {
             trackDao.deleteByLibraryId(library.getId());
             specialTagDao.deleteByLibraryId(library.getId());
             resetStudentGroup(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
@@ -1095,6 +1211,7 @@ public class MarkServiceImpl implements MarkService {
         }
     }
 
+<<<<<<< HEAD
     /**
      * 领取试评评卷任务时,用来区分的唯一标识
      *
@@ -1103,6 +1220,14 @@ public class MarkServiceImpl implements MarkService {
      */
     private String getApplyTaskId(TrialLibrary library, Marker marker) {
         return library.getId() + "_" + marker.getId();
+=======
+    private String getGroupKey(MarkGroup group) {
+        return group.getExamId() + "_" + group.getSubjectCode() + "_" + group.getNumber();
+    }
+
+    private String getGroupKey(Marker marker) {
+        return marker.getExamId() + "_" + marker.getSubjectCode() + "_" + marker.getGroupNumber();
+>>>>>>> release_1.3.1
     }
 
     private String getGroupKey(MarkGroup group) {
@@ -1220,8 +1345,9 @@ public class MarkServiceImpl implements MarkService {
      *
      * @param studentId
      */
-    private void checkStudentSubjective(Integer studentId, long groupCount) {
-        if (groupStudentDao.countByStudentIdAndStatus(studentId, SubjectiveStatus.MARKED) == groupCount) {
+    private void checkStudentSubjective(Integer studentId, long groupCount, long unGroupQuestionCount) {
+        if (groupStudentDao.countByStudentIdAndStatus(studentId, SubjectiveStatus.MARKED) == groupCount
+                && unGroupQuestionCount == 0) {
             scoreCalculate(studentId);
         } else {
             studentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.UNMARK, 0, null);
@@ -1240,8 +1366,11 @@ public class MarkServiceImpl implements MarkService {
                     SubjectiveStatus.MARKED);
             updateStudentGroupScore(studentId, group.getExamId(), group.getSubjectCode(), group.getNumber(),
                     group.getMarkScore(), group.getMarkScoreDetail());
-            checkStudentSubjective(studentId,
-                    groupDao.countByExamIdAndSubjectCode(group.getExamId(), group.getSubjectCode()));
+            // 未分组的题目
+            long unGroupQuestionCount = questionDao.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(
+                    group.getExamId(), group.getSubjectCode(), false);
+            long groupCount = groupDao.countByExamIdAndSubjectCode(group.getExamId(), group.getSubjectCode());
+            checkStudentSubjective(studentId, groupCount, unGroupQuestionCount);
         } else {
             updateStudentGroupStatus(studentId, group.getExamId(), group.getSubjectCode(), group.getNumber(),
                     SubjectiveStatus.UNMARK);
@@ -1321,7 +1450,7 @@ public class MarkServiceImpl implements MarkService {
         List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumber(student.getId(), groupNumber);
         int count = 0;
         for (MarkLibrary library : list) {
-            if (libraryDao.resetById(library.getId(), LibraryStatus.WAITING, LibraryStatus.MARKED,
+            if (libraryDao.resetById(library.getId(), null, LibraryStatus.REJECTED, LibraryStatus.MARKED,
                     LibraryStatus.PROBLEM, LibraryStatus.INSPECTED) > 0) {
                 count++;
                 trackDao.deleteByLibraryId(library.getId());
@@ -1346,4 +1475,180 @@ public class MarkServiceImpl implements MarkService {
         return false;
     }
 
+    @Override
+    @Transactional
+    public boolean rejectedStudent(ExamStudent student, MarkStepDTO[] markStepDTOs, Integer userId) {
+        Map<Integer, List<MarkStepDTO>> map = new HashMap<Integer, List<MarkStepDTO>>();
+
+        for (MarkStepDTO markStepDTO : markStepDTOs) {
+            List<MarkStepDTO> list = map.get(markStepDTO.getGroupNumber());
+            if (list == null) {
+                list = new ArrayList<MarkStepDTO>();
+            }
+            list.add(markStepDTO);
+            map.put(markStepDTO.getGroupNumber(), list);
+        }
+        for (Integer groupNumber : map.keySet()) {
+            MarkGroup group = groupDao.findOne(student.getExamId(), student.getSubjectCode(), groupNumber);
+            if (group.getStatus() == MarkStatus.FINISH) {
+                return false;
+            }
+        }
+        for (Integer groupNumber : map.keySet()) {
+            List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumber(student.getId(), groupNumber);
+            List<ExamQuestion> questions = questionDao.findByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(
+                    student.getExamId(), student.getSubjectCode(), false, groupNumber);
+            List<MarkStepDTO> qList = map.get(groupNumber);
+            MarkGroup group = groupDao.findOne(student.getExamId(), student.getSubjectCode(), groupNumber);
+            int count = 0;
+            for (MarkLibrary library : list) {
+                if (library.getTaskNumber() == 3) {
+                    // 出现三评时删除第三条任务
+                    problemHistoryDao.deleteByLibraryId(library.getId());
+                    libraryDao.delete(library);
+                    updateLibraryCount(group);
+                    count++;
+                    continue;
+                }
+                if (library.getStatus().equals(LibraryStatus.ARBITRATED) || qList.size() == questions.size()) {
+                    // 仲裁任务直接重置 或者该分组下所有分数被打回也重置
+                    libraryDao.resetById(library.getId(), library.getMarkerId(), LibraryStatus.REJECTED,
+                            LibraryStatus.MARKED, LibraryStatus.PROBLEM, LibraryStatus.INSPECTED);
+                    trackDao.deleteByLibraryId(library.getId());
+                    specialTagDao.deleteByLibraryId(library.getId());
+                    count++;
+                    continue;
+                }
+                if (library.getStatus().equals(LibraryStatus.MARKED)) {
+                    List<ScoreItem> sList = library.getScoreList();
+                    for (MarkStepDTO markStepDTO : qList) {
+                        trackDao.deleteByLibraryIdAndQuestionNumber(library.getId(), markStepDTO.getMainNumber() + "."
+                                + markStepDTO.getSubNumber());
+                        for (int i = 0; i < questions.size(); i++) {
+                            ExamQuestion question = questions.get(i);
+                            if (markStepDTO.getMainNumber() == question.getMainNumber()
+                                    && markStepDTO.getSubNumber().equals(question.getSubNumber())) {
+                                sList.remove(i);
+                                sList.add(i, new ScoreItem(false));
+                            }
+                        }
+                    }
+                    StringBuilder markerScoreList = new StringBuilder();
+                    for (int i = 0; i < sList.size(); i++) {
+                        ScoreItem scoreItem = sList.get(i);
+                        if (scoreItem.getScore() != null) {
+                            markerScoreList.append(scoreItem.getScore());
+                        }
+                        if (i < sList.size() - 1) {
+                            markerScoreList.append(",");
+                        }
+                    }
+                    if (libraryDao.updateMarkerResult(library.getId(), LibraryStatus.REJECTED, library.getMarkerId(),
+                            null, markerScoreList.toString(), null, null, LibraryStatus.MARKED) == 1) {
+                        count++;
+                    }
+                }
+            }
+            if (count > 0) {
+                updateMarkedCount(group);
+                resetStudentGroup(student.getId(), student.getExamId(), student.getSubjectCode(), groupNumber);
+                studentService.updateSubjectiveStatusAndTimeAndInspectorId(student.getId(), SubjectiveStatus.UNMARK,
+                        null, null);
+            }
+        }
+        return true;
+    }
+
+    @Transactional
+    @Override
+    public boolean deleteByQuestion(ExamQuestion question) {
+        if (!question.isObjective() && question.getGroupNumber() != null) {
+            return false;
+        } else {
+            int examId = question.getExamId();
+            String subjectCode = question.getSubjectCode();
+            boolean objective = question.isObjective();
+            questionDao.delete(question);
+            if (objective) {
+                examService.updateObjectiveStatus(examId, ObjectiveStatus.WAITING);
+            } else {
+                // 未分组的题目
+                long unGroupQuestionCount = questionDao.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(examId,
+                        subjectCode, false);
+                // 考生整体状态与总分更新
+                long groupCount = groupDao.countByExamIdAndSubjectCode(examId, subjectCode);
+                if (groupCount == 0 || unGroupQuestionCount > 0) {
+                    studentService.updateSubjectiveStatusAndScoreAndInspectorId(examId, subjectCode,
+                            SubjectiveStatus.UNMARK, 0, null, null, null);
+                } else {
+                    List<Integer> studentList = studentService.findIdByExamIdAndSubjectCodeAndSubjectiveStatus(examId,
+                            subjectCode, SubjectiveStatus.UNMARK, SubjectiveStatus.MARKED, SubjectiveStatus.INSPECTED);
+                    for (Integer studentId : studentList) {
+                        checkStudentSubjective(studentId, groupCount, unGroupQuestionCount);
+                    }
+                }
+            }
+
+            subjectService.updateScore(examId, subjectCode, objective,
+                    questionService.sumTotalScore(examId, subjectCode, objective));
+
+            return true;
+        }
+    }
+
+    @Override
+    @Transactional
+    public boolean rejectLibrary(MarkLibrary library, MarkStepDTO[] questionList, Integer userId) {
+        MarkGroup group = groupDao.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
+        if (group.getStatus() == MarkStatus.FINISH) {
+            return false;
+        }
+        List<ExamQuestion> questions = questionDao.findByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(
+                library.getExamId(), library.getSubjectCode(), false, library.getGroupNumber());
+        List<ScoreItem> sList = library.getScoreList();
+        for (MarkStepDTO markStepDTO : questionList) {
+            trackDao.deleteByLibraryIdAndQuestionNumber(library.getId(), markStepDTO.getMainNumber() + "."
+                    + markStepDTO.getSubNumber());
+            for (int i = 0; i < questions.size(); i++) {
+                ExamQuestion question = questions.get(i);
+                if (markStepDTO.getMainNumber() == question.getMainNumber()
+                        && markStepDTO.getSubNumber().equals(question.getSubNumber())) {
+                    sList.remove(i);
+                    sList.add(i, new ScoreItem(false));
+                }
+            }
+        }
+        StringBuilder markerScoreList = new StringBuilder();
+        for (int i = 0; i < sList.size(); i++) {
+            ScoreItem scoreItem = sList.get(i);
+            if (scoreItem.getScore() != null) {
+                markerScoreList.append(scoreItem.getScore());
+            }
+            if (i < sList.size() - 1) {
+                markerScoreList.append(",");
+            }
+        }
+        if (libraryDao.updateMarkerResult(library.getId(), LibraryStatus.REJECTED, library.getMarkerId(), null,
+                markerScoreList.toString(), null, null, LibraryStatus.MARKED) == 1) {
+            resetStudentGroup(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
+                    library.getGroupNumber());
+            problemHistoryDao.resetByLibraryId(library.getId(), HistoryStatus.WAITING, userId, HistoryStatus.BACK,
+                    new Date());
+            // 开启三评时,打回1,2任务则删除第3条任务
+            long count = libraryDao.countByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber());
+            if (library.getTaskNumber() != 3 && count == 3) {
+                MarkLibrary third = libraryDao.findByStudentIdAndGroupNumberAndTaskNumber(library.getStudentId(),
+                        library.getGroupNumber(), 3);
+                trackDao.deleteByLibraryId(third.getId());
+                specialTagDao.deleteByLibraryId(third.getId());
+                problemHistoryDao.deleteByLibraryId(third.getId());
+                libraryDao.delete(third);
+                updateLibraryCount(group);
+            }
+            updateMarkedCount(group);
+            return true;
+        } else {
+            return false;
+        }
+    }
 }

+ 207 - 182
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java

@@ -1,23 +1,49 @@
 package cn.com.qmth.stmms.biz.mark.service.Impl;
 
-import cn.com.qmth.stmms.biz.exam.model.*;
-import cn.com.qmth.stmms.biz.exam.service.*;
-import cn.com.qmth.stmms.biz.file.service.FileService;
-import cn.com.qmth.stmms.biz.mark.model.*;
-import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
-import cn.com.qmth.stmms.biz.mark.service.*;
-import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.common.enums.LibraryStatus;
-import cn.com.qmth.stmms.common.enums.MarkStatus;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
+import cn.com.qmth.stmms.biz.exam.model.Exam;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
+import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
+import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
+import cn.com.qmth.stmms.biz.mark.model.MarkStepDTO;
+import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
+import cn.com.qmth.stmms.biz.mark.model.SpecialTagDTO;
+import cn.com.qmth.stmms.biz.mark.model.Task;
+import cn.com.qmth.stmms.biz.mark.model.TrackDTO;
+import cn.com.qmth.stmms.biz.mark.model.TrialHistory;
+import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
+import cn.com.qmth.stmms.biz.mark.model.TrialTag;
+import cn.com.qmth.stmms.biz.mark.model.TrialTrack;
+import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
+import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
+import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
+import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
+import cn.com.qmth.stmms.biz.mark.service.TaskService;
+import cn.com.qmth.stmms.biz.mark.service.TrialService;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.biz.utils.ScoreItem;
+import cn.com.qmth.stmms.common.enums.ExamType;
+import cn.com.qmth.stmms.common.enums.LibraryStatus;
 
 /**
  * 评卷任务服务实现
@@ -62,6 +88,9 @@ public class TaskServiceImpl implements TaskService {
     @Autowired
     private FileService fileService;
 
+    @Autowired
+    private ExamService examService;
+
     @Override
     public List<Task> findByQuery(MarkLibrarySearchQuery query) {
         List<Task> list = new LinkedList<Task>();
@@ -76,136 +105,149 @@ public class TaskServiceImpl implements TaskService {
 
     @Override
     public Task build(ArbitrateHistory history, MarkGroup group) {
-        ExamSubject subject = subjectService.find(group.getExamId(), group.getSubjectCode());
         ExamStudent student = studentService.findByExamIdAndExamNumber(history.getExamId(), history.getExamNumber());
-        List<MarkLibrary> libraryList = libraryService.findByStudentAndGroup(student.getId(), group.getNumber());
         Task task = new Task();
-        task.setExist(true);
         task.setStudentId(history.getStudentId());
         task.setLibraryId(history.getId());
-        task.setStudentNumber(history.getSecretNumber());
-        task.setMarkStepList(buildMarkStep(group, null));
-        task.setPictureUrls(fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1,
+        task.setSecretNumber(history.getSecretNumber());
+        task.setQuestionList(buildArbitrateStep(group, history));
+        task.setSliceUrls(fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1,
                 student.getSliceCount()));
-        task.setPictureConfig(group.getPictureConfigList());
+        task.setSliceConfig(group.getPictureConfigList());
         task.setJsonUrl(fileService.getJsonUri(student.getExamId(), student.getSecretNumber()));
-        if (subject.getPaperFileType() != null) {
-            task.setPaperUrl(fileService.getPaperUri(subject.getExamId(), subject.getCode(), subject.getPaperFileType()));
-        }
-        if (subject.getAnswerFileType() != null) {
-            task.setAnswerUrl(fileService.getAnswerUri(subject.getExamId(), subject.getCode(),
-                    subject.getAnswerFileType()));
+        if (StringUtils.isNotBlank(student.getObjectiveScoreList())) {
+            task.setObjectiveScore(student.getObjectiveScore());
         }
-        task.setObjectiveScore(student.getObjectiveScore() != null ? student.getObjectiveScore() : 0);
         task.setMarkTime(history.getUpdateTime());
-        // 构造仲裁信息
-        List<ArbitrationDTO> list = new ArrayList<ArbitrationDTO>();
-        for (MarkLibrary library : libraryList) {
-            if (library.getStatus() != LibraryStatus.WAITING) {
-                Marker marker = markerService.findById(library.getMarkerId());
-                marker.setUser(userService.findById(marker.getUserId()));
-                list.add(new ArbitrationDTO(library, marker));
-            }
-        }
-        task.setArbitrationList(list);
-        // 回评状态下的打分信息
         if (history.getTotalScore() != null) {
-            task.setTotalScore(history.getTotalScore());
-        }
-        if (history.getScoreList() != null) {
-            task.setScoreList(history.getScoreList());
+            task.setMarkerScore(history.getTotalScore());
         }
         return task;
     }
 
+    private List<MarkStepDTO> buildArbitrateStep(MarkGroup group, ArbitrateHistory history) {
+        List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
+        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
+                group.getSubjectCode(), false, group.getNumber());
+        List<MarkTrack> tracks = new ArrayList<MarkTrack>();
+        List<ScoreItem> sItems = new ArrayList<ScoreItem>();
+        if (history != null) {
+            sItems = history.getScoreItemList();
+        }
+        for (int i = 0; i < sList.size(); i++) {
+            ExamQuestion question = sList.get(i);
+            MarkStepDTO step = buildStep(question);
+            if (history != null) {
+                if (!sItems.isEmpty() && sItems.size() == sList.size()) {
+                    step.setScore(sItems.get(i).getScore());
+                }
+                // 增加阅卷轨迹列表获取
+                String questionNumber = question.getQuestionNumber();
+                for (MarkTrack track : tracks) {
+                    if (track.getQuestionNumber().equals(questionNumber)) {
+                        step.addTrack(new TrackDTO(track));
+                    }
+                }
+            }
+            list.add(step);
+        }
+        return list;
+    }
+
     @Override
     public Task build(MarkLibrary library) {
+        Exam exam = examService.findById(library.getExamId());
         ExamSubject subject = subjectService.find(library.getExamId(), library.getSubjectCode());
         ExamStudent student = studentService.findByExamIdAndExamNumber(library.getExamId(), library.getExamNumber());
         MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
 
         Task task = new Task();
-        task.setExist(true);
-        task.setStatusValue(MarkStatus.FORMAL.getValue());
-        task.setStudentId(library.getStudentId());
         task.setLibraryId(library.getId());
-        // 正评显示考生密号
-        task.setStudentNumber(library.getSecretNumber());
-        task.setMarkStepList(buildMarkStep(group, library.getId()));
-        task.setPictureUrls(fileService.getSliceUris(library.getExamId(), library.getSecretNumber(), 1,
-                student.getSliceCount()));
-        task.setPictureConfig(group.getPictureConfigList());
-        task.setJsonUrl(fileService.getJsonUri(library.getExamId(), library.getSecretNumber()));
-        if (subject.getPaperFileType() != null) {
-            task.setPaperUrl(fileService.getPaperUri(subject.getExamId(), subject.getCode(), subject.getPaperFileType()));
+        task.setStudentId(library.getStudentId());
+        task.setSecretNumber(library.getSecretNumber());
+        task.setStudentCode(student.getStudentCode());
+        task.setStudentName(student.getName());
+        task.setExamNumber(library.getExamNumber());
+        task.setSubject(subject);
+        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
+            task.setJsonUrl(fileService.getJsonUri(library.getExamId(), library.getSecretNumber()));
+        } else {
+            task.setSliceUrls(fileService.getSliceUris(library.getExamId(), library.getSecretNumber(), 1,
+                    student.getSliceCount()));
+            task.setSliceConfig(group.getPictureConfigList());
+            task.setSheetUrls(fileService.getSheetUris(library.getExamId(), library.getExamNumber(), 1,
+                    student.getSheetCount()));
         }
-        if (subject.getAnswerFileType() != null) {
-            task.setAnswerUrl(fileService.getAnswerUri(subject.getExamId(), subject.getCode(),
-                    subject.getAnswerFileType()));
+        task.setQuestionList(buildMarkStep(group, library));
+        task.setSpecialTagList(getMarkSpecialTagList(library.getId()));
+        if (StringUtils.isNotBlank(student.getObjectiveScoreList())) {
+            task.setObjectiveScore(student.getObjectiveScore());
         }
-        task.setObjectiveScore(student.getObjectiveScore() != null ? student.getObjectiveScore() : 0);
         task.setMarkTime(library.getMarkerTime());
-        task.setTagList(getMarkSpecialTagList(library.getId()));
-        if (library.getMarkerScore() != null) {
-            task.setTotalScore(library.getMarkerScore());
+        if (library.getStatus() == LibraryStatus.REJECTED) {
+            task.setRejected(true);
         }
-        if (library.getMarkerScoreList() != null) {
-            task.setScoreList(library.getMarkerScoreList());
+        if (library.getStatus() == LibraryStatus.MARKED || library.getStatus() == LibraryStatus.INSPECTED) {
+            task.setMarkerScore(library.getMarkerScore());
+            task.setPrevious(true);
         }
-        // if (library.getStatus() == LibraryStatus.BACKED &&
-        // library.getMarkerId() != null) {
-        // task.setBack(true);
-        // }
         return task;
     }
 
     @Override
     public Task build(TrialLibrary library, TrialHistory history) {
+        Exam exam = examService.findById(library.getExamId());
         ExamSubject subject = subjectService.find(library.getExamId(), library.getSubjectCode());
         ExamStudent student = studentService.findByExamIdAndExamNumber(library.getExamId(), library.getExamNumber());
         MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
 
         Task task = new Task();
-        task.setExist(true);
-        task.setStatusValue(MarkStatus.TRIAL.getValue());
-        task.setStatusName(MarkStatus.TRIAL.getName());
-        // 试评显示考生密号
-        task.setStudentNumber(library.getSecretNumber());
-        task.setStudentId(library.getStudentId());
         task.setLibraryId(library.getId());
-        task.setMarkStepList(buildTrialStep(group, history));
-        task.setPictureUrls(fileService.getSliceUris(library.getExamId(), library.getSecretNumber(), 1,
-                student.getSliceCount()));
-        task.setPictureConfig(group.getPictureConfigList());
-        task.setJsonUrl(fileService.getJsonUri(library.getExamId(), library.getSecretNumber()));
-        if (subject.getPaperFileType() != null) {
-            task.setPaperUrl(fileService.getPaperUri(subject.getExamId(), subject.getCode(), subject.getPaperFileType()));
+        task.setStudentId(library.getStudentId());
+        task.setSecretNumber(library.getSecretNumber());
+        task.setStudentCode(student.getStudentCode());
+        task.setStudentName(student.getName());
+        task.setExamNumber(library.getExamNumber());
+        task.setSubject(subject);
+        task.setQuestionList(buildTrialStep(group, history));
+        if (StringUtils.isNotBlank(student.getObjectiveScoreList())) {
+            task.setObjectiveScore(student.getObjectiveScore());
         }
-        if (subject.getAnswerFileType() != null) {
-            task.setAnswerUrl(fileService.getAnswerUri(subject.getExamId(), subject.getCode(),
-                    subject.getAnswerFileType()));
+        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
+            task.setJsonUrl(fileService.getJsonUri(library.getExamId(), library.getSecretNumber()));
+        } else {
+            task.setSliceUrls(fileService.getSliceUris(library.getExamId(), library.getSecretNumber(), 1,
+                    student.getSliceCount()));
+            task.setSliceConfig(group.getPictureConfigList());
+            task.setSheetUrls(fileService.getSheetUris(library.getExamId(), library.getExamNumber(), 1,
+                    student.getSheetCount()));
         }
-        task.setObjectiveScore(student.getObjectiveScore() != null ? student.getObjectiveScore() : 0);
         if (history != null) {
+            task.setSpecialTagList(getTrialTagList(history));
             task.setMarkTime(history.getMarkerTime());
-            task.setTotalScore(history.getMarkerScore());
-            task.setScoreList(history.getMarkerScoreList());
-            task.setTagList(getTrialTagList(history));
+            task.setMarkerScore(history.getMarkerScore());
+            task.setPrevious(true);
         }
         return task;
     }
 
-    private List<MarkStepDTO> buildMarkStep(MarkGroup group, Integer libraryId) {
+    private List<MarkStepDTO> buildMarkStep(MarkGroup group, MarkLibrary library) {
         List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
         List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
                 group.getSubjectCode(), false, group.getNumber());
-        int number = 0;
-        List<MarkTrack> tracks = trackService.findByLibraryId(libraryId);
-        for (ExamQuestion question : sList) {
-            number++;
-
-            MarkStepDTO step = buildStep(number, question);
-            if (libraryId != null) {
+        List<MarkTrack> tracks = new ArrayList<MarkTrack>();
+        List<ScoreItem> sItems = new ArrayList<ScoreItem>();
+        if (library != null) {
+            tracks = trackService.findByLibraryId(library.getId());
+            sItems = library.getScoreList();
+        }
+        for (int i = 0; i < sList.size(); i++) {
+            ExamQuestion question = sList.get(i);
+            MarkStepDTO step = buildStep(question);
+            if (library != null) {
+                if (!sItems.isEmpty() && sItems.size() == sList.size()) {
+                    step.setScore(sItems.get(i).getScore());
+                }
                 // 增加阅卷轨迹列表获取
                 String questionNumber = question.getQuestionNumber();
                 for (MarkTrack track : tracks) {
@@ -223,14 +265,18 @@ public class TaskServiceImpl implements TaskService {
         List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
         List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
                 group.getSubjectCode(), false, group.getNumber());
-        int number = 0;
         List<TrialTrack> tracks = new ArrayList<TrialTrack>();
+        List<ScoreItem> sItems = new ArrayList<ScoreItem>();
         if (history != null) {
             tracks = trialService.findTrack(history.getLibraryId(), history.getMarkerId());
+            sItems = history.getScoreList();
         }
-        for (ExamQuestion question : sList) {
-            number++;
-            MarkStepDTO step = buildStep(number, question);
+        for (int i = 0; i < sList.size(); i++) {
+            ExamQuestion question = sList.get(i);
+            MarkStepDTO step = buildStep(question);
+            if (!sItems.isEmpty() && sItems.size() == sList.size()) {
+                step.setScore(sItems.get(i).getScore());
+            }
             // 增加阅卷轨迹列表获取
             String questionNumber = question.getQuestionNumber();
             for (TrialTrack track : tracks) {
@@ -243,19 +289,16 @@ public class TaskServiceImpl implements TaskService {
         return list;
     }
 
-    private MarkStepDTO buildStep(int number, ExamQuestion question) {
+    private MarkStepDTO buildStep(ExamQuestion question) {
         MarkStepDTO step = new MarkStepDTO();
-        step.setQuestionNumber(question.getQuestionNumber());
         step.setMainNumber(question.getMainNumber());
         step.setSubNumber(question.getSubNumber());
-        step.setNumber(number);
-        step.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
-        step.setTotalScore(question.getTotalScore());
+        step.setGroupNumber(question.getGroupNumber());
+        step.setTitle(question.getMainTitle());
         step.setDefaultScore(0d);
-        step.setMax(question.getTotalScore());
-        step.setMin(0d);
-        step.setInterval(question.getIntervalScore());
-        step.setScoreList(question.getScoreListArray());
+        step.setMaxScore(question.getTotalScore());
+        step.setMinScore(0d);
+        step.setIntervalScore(question.getIntervalScore());
         return step;
     }
 
@@ -298,99 +341,81 @@ public class TaskServiceImpl implements TaskService {
     }
 
     @Override
-    public Task build(Integer studentId) {
-        ExamStudent student = studentService.findById(studentId);
+    public Task build(ExamStudent student) {
         ExamSubject subject = subjectService.find(student.getExamId(), student.getSubjectCode());
+        Exam exam = examService.findById(student.getExamId());
+
         Task task = new Task();
-        task.setExist(true);
-        task.setStudentId(studentId);
-        task.setStudentNumber(student.getSecretNumber());
-        task.setMarkStepList(buildMarkStep(student));
-        task.setPictureUrls(fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1,
-                student.getSliceCount()));
-        task.setJsonUrl(fileService.getJsonUri(student.getExamId(), student.getSecretNumber()));
-        if (subject.getPaperFileType() != null) {
-            task.setPaperUrl(fileService.getPaperUri(subject.getExamId(), subject.getCode(), subject.getPaperFileType()));
+        task.setStudentId(student.getId());
+        task.setSecretNumber(student.getSecretNumber());
+        task.setStudentCode(student.getStudentCode());
+        task.setStudentName(student.getName());
+        task.setExamNumber(student.getExamNumber());
+        task.setSubject(subject);
+        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
+            task.setJsonUrl(fileService.getJsonUri(student.getExamId(), student.getSecretNumber()));
+        } else {
+            task.setSliceUrls(fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1,
+                    student.getSliceCount()));
+            task.setSheetUrls(fileService.getSheetUris(student.getExamId(), student.getExamNumber(), 1,
+                    student.getSheetCount()));
         }
-        if (subject.getAnswerFileType() != null) {
-            task.setAnswerUrl(fileService.getAnswerUri(subject.getExamId(), subject.getCode(),
-                    subject.getAnswerFileType()));
+        task.setQuestionList(buildMarkStep(student));
+        task.setSpecialTagList(getMarkSpecialTagList(student));
+        if (StringUtils.isNotBlank(student.getObjectiveScoreList())) {
+            task.setObjectiveScore(student.getObjectiveScore());
         }
-        task.setObjectiveScore(student.getObjectiveScore());
-        task.setBack(true);
+        task.setMarkerScore(student.getTotalScore());
+        task.setInspectTime(student.getInspectTime());
         return task;
     }
 
-    private List<PictureConfigItem> buildPictureConfig(int picCount) {
-        List<PictureConfigItem> list = new LinkedList<PictureConfigItem>();
-        for (int i = 1; i <= picCount; i++) {
-            list.add(new PictureConfigItem(String.valueOf(i)));
+    private SpecialTagDTO[] getMarkSpecialTagList(ExamStudent student) {
+        List<SpecialTagDTO> list = new ArrayList<SpecialTagDTO>();
+        List<MarkGroup> groups = groupService.findByExamAndSubject(student.getExamId(), student.getSubjectCode());
+        for (MarkGroup group : groups) {
+            List<MarkLibrary> libraryList = libraryService.findByStudentAndGroup(student.getId(), group.getNumber());
+            if (libraryList.size() == 1) {
+                SpecialTagDTO[] tags = getMarkSpecialTagList(libraryList.get(0).getId());
+                if (tags != null) {
+                    for (SpecialTagDTO specialTagDTO : tags) {
+                        list.add(specialTagDTO);
+                    }
+                }
+            }
         }
-        return list;
+        SpecialTagDTO[] specialTags = new SpecialTagDTO[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            specialTags[i] = list.get(i);
+        }
+        return specialTags;
     }
 
     private List<MarkStepDTO> buildMarkStep(ExamStudent student) {
         List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
         List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
                 student.getSubjectCode(), false);
-        int number = 0;
-        for (ExamQuestion question : sList) {
-            number++;
-
-            MarkStepDTO step = buildStep(number, question);
+        List<ScoreItem> sItems = student.getScoreList(false);
+        for (int i = 0; i < sList.size(); i++) {
+            ExamQuestion question = sList.get(i);
+            MarkStepDTO step = buildStep(question);
+            if (!sItems.isEmpty() && sItems.size() == sList.size()) {
+                step.setScore(sItems.get(i).getScore());
+            }
             // 增加阅卷轨迹列表获取
-            List<MarkTrack> tracks = trackService.findByStudentId(student.getId());
-            String questionNumber = question.getQuestionNumber();
-            for (MarkTrack track : tracks) {
-                if (track.getQuestionNumber().equals(questionNumber)) {
-                    step.addTrack(new TrackDTO(track));
+            List<MarkLibrary> libraryList = libraryService.findByStudentAndGroup(student.getId(),
+                    question.getGroupNumber());
+            if (libraryList.size() == 1) {
+                List<MarkTrack> tracks = trackService.findByStudentId(student.getId());
+                String questionNumber = question.getQuestionNumber();
+                for (MarkTrack track : tracks) {
+                    if (track.getQuestionNumber().equals(questionNumber)) {
+                        step.addTrack(new TrackDTO(track));
+                    }
                 }
             }
             list.add(step);
         }
         return list;
     }
-
-    @Transactional
-    @Override
-    public boolean submitByStudent(Task task) {
-        if (task == null) {
-            return false;
-        }
-        try {
-            List<MarkLibrary> libraries = libraryService.findByStudentId(task.getLibraryId());
-            int start = 0;
-            int end = 0;
-            for (int i = 0; i < libraries.size(); i++) {
-                MarkLibrary library = libraries.get(i);
-                Task stepTask = new Task();
-                stepTask.setProblem(task.isProblem());
-                stepTask.setBack(task.isBack());
-                stepTask.setLibraryId(library.getId());
-                stepTask.setHeaderId(task.getHeaderId());
-                List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
-                        library.getExamId(), library.getSubjectCode(), false, library.getGroupNumber());
-                if (start == end) {
-                    end = end + sList.size() * 2 - 1;
-                } else {
-                    start = end + 1;
-                    end = start + sList.size() * 2 - 1;
-                }
-                String scoreList = task.getScoreList().substring(start, end);
-                stepTask.setHeaderScoreList(scoreList);
-                String[] scores = scoreList.split(",");
-                double totalScore = 0;
-                for (String score : scores) {
-                    totalScore = totalScore + Double.parseDouble(score);
-                }
-                stepTask.setHeaderScore(totalScore);
-                // submitTask(stepTask);
-            }
-            return true;
-        } catch (Exception e) {
-            log.error("task submit faile", e);
-            return false;
-        }
-    }
-
 }

+ 14 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkLibraryService.java

@@ -1,6 +1,7 @@
 package cn.com.qmth.stmms.biz.mark.service;
 
 import java.util.List;
+import java.util.Set;
 
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
@@ -17,9 +18,10 @@ public interface MarkLibraryService {
     MarkLibrarySearchQuery findByQuery(MarkLibrarySearchQuery query);
 
     List<MarkLibrary> findUnMarked(Integer examId, String subjectCode, Integer groupNumber, Integer markerId,
-            Integer userId, boolean filterClass, int pageNumber, int pageSize);
+            Integer userId, boolean filterClass, int pageNumber, int pageSize, Set<LibraryStatus> statusSet);
 
-    long countByExamAndSubjectAndGroupAndStatus(int examId, String subjectCode, int groupNumber, LibraryStatus status);
+    long countByExamAndSubjectAndGroupAndStatus(int examId, String subjectCode, int groupNumber,
+            LibraryStatus... status);
 
     long countByMarker(int markerId);
 
@@ -34,4 +36,14 @@ public interface MarkLibraryService {
 
     MarkLibrary save(MarkLibrary library);
 
+    void releaseByLibrary(MarkLibrary library);
+
+    void releaseByUserId(Integer examId, String subjectCode, Integer groupNumber, Integer userId);
+
+    boolean hasApplied(MarkLibrary library, Integer userId);
+
+    boolean applyLibrary(MarkLibrary library, Integer userId);
+
+    Set<String> findSubjectUnFinishByExamId(int examId);
+
 }

+ 27 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java

@@ -228,4 +228,31 @@ public interface MarkService {
      * @return
      */
     boolean backStudentByGroup(ExamStudent student, Integer groupNumber, Integer userId);
+
+    /**
+     * 管理员/组长打回某个学生的指定题目
+     * 
+     * @param student
+     * @param questionList
+     * @param userId
+     * @return
+     */
+    boolean rejectedStudent(ExamStudent student, MarkStepDTO[] questionList, Integer userId);
+
+    /**
+     * 删除某个小题
+     * 
+     * @param question
+     */
+    boolean deleteByQuestion(ExamQuestion question);
+
+    /**
+     * 管理员/组长打回某个任务的指定题目
+     * 
+     * @param library
+     * @param questionList
+     * @param userId
+     * @return
+     */
+    boolean rejectLibrary(MarkLibrary library, MarkStepDTO[] questionList, Integer userId);
 }

+ 7 - 6
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkSpecialTagService.java

@@ -5,14 +5,15 @@ import java.util.List;
 import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
 
 public interface MarkSpecialTagService {
-	public MarkSpecialTag save(MarkSpecialTag tag);
 
-	public void deleteByLibraryId(Integer libraryId);
+    public MarkSpecialTag save(MarkSpecialTag tag);
 
-	public List<MarkSpecialTag> findByLibraryId(Integer libraryId);
+    public void deleteByLibraryId(Integer libraryId);
 
-	public void deleteByMarkerId(Integer markerId);
+    public List<MarkSpecialTag> findByLibraryId(Integer libraryId);
+
+    public void deleteByMarkerId(Integer markerId);
+
+    public void deleteByExamAndSubjectAndGroup(Integer examId, String subjectCode, Integer number);
 
-	public void deleteByExamAndSubjectAndGroup(Integer examId,
-			String subjectCode, Integer number);
 }

+ 2 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/TaskService.java

@@ -2,6 +2,7 @@ package cn.com.qmth.stmms.biz.mark.service;
 
 import java.util.List;
 
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
@@ -20,8 +21,6 @@ public interface TaskService {
 
     Task build(MarkLibrary library);
 
-    Task build(Integer studentId);
-
-    boolean submitByStudent(Task task);
+    Task build(ExamStudent student);
 
 }

+ 3 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/model/ReportSubjectQuestion.java

@@ -69,7 +69,7 @@ public class ReportSubjectQuestion implements Serializable {
      */
     @ExcelField(title = "小题号", align = 2, sort = 50)
     @Column(name = "sub_number", nullable = false)
-    private Integer subNumber;
+    private String subNumber;
 
     /**
      * 总分
@@ -283,11 +283,11 @@ public class ReportSubjectQuestion implements Serializable {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 13 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/query/ReportSubjectQuery.java

@@ -25,7 +25,7 @@ public class ReportSubjectQuery extends BaseQuery<ReportSubject> {
 
     private Integer mainNumber;
 
-    private Integer subNumber;
+    private String subNumber;
 
     private Boolean objective;
 
@@ -35,6 +35,8 @@ public class ReportSubjectQuery extends BaseQuery<ReportSubject> {
 
     private String subjectCodeIn;
 
+    private Integer range;
+
     public Integer getExamId() {
         return examId;
     }
@@ -99,11 +101,11 @@ public class ReportSubjectQuery extends BaseQuery<ReportSubject> {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 
@@ -131,4 +133,12 @@ public class ReportSubjectQuery extends BaseQuery<ReportSubject> {
         this.subjectCodeIn = subjectCodeIn;
     }
 
+    public Integer getRange() {
+        return range;
+    }
+
+    public void setRange(Integer range) {
+        this.range = range;
+    }
+
 }

+ 10 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/query/ReportSubjectRangeQuery.java

@@ -12,6 +12,8 @@ public class ReportSubjectRangeQuery extends BaseQuery<ReportSubjectRange> {
 
     private String subjectCode;
 
+    private Integer range;
+
     public Integer getExamId() {
         return examId;
     }
@@ -28,4 +30,12 @@ public class ReportSubjectRangeQuery extends BaseQuery<ReportSubjectRange> {
         this.subjectCode = subjectCode;
     }
 
+    public Integer getRange() {
+        return range;
+    }
+
+    public void setRange(Integer range) {
+        this.range = range;
+    }
+
 }

+ 1 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/service/ReportSubjectQuestionService.java

@@ -20,7 +20,7 @@ public interface ReportSubjectQuestionService {
     List<ReportSubjectQuestion> findByQuery(ReportSubjectQuery query);
 
     ReportSubjectQuestion findOne(Integer examId, String subjectCode, boolean isObjective, String paperType,
-            Integer mainNumber, Integer subNumber);
+            Integer mainNumber, String subNumber);
 
     List<ReportSubjectQuestion> findByExamIdAndSubjectCodeAndObjectiveAndPaperType(Integer examId, String subjectCode,
             boolean isObjective, String paperType);

+ 37 - 22
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/service/impl/ReportSubjectQuestionServiceImpl.java

@@ -1,10 +1,15 @@
 package cn.com.qmth.stmms.biz.report.service.impl;
 
-import cn.com.qmth.stmms.biz.common.BaseQueryService;
-import cn.com.qmth.stmms.biz.report.dao.ReportSubjectQuestionDao;
-import cn.com.qmth.stmms.biz.report.model.ReportSubjectQuestion;
-import cn.com.qmth.stmms.biz.report.query.ReportSubjectQuery;
-import cn.com.qmth.stmms.biz.report.service.ReportSubjectQuestionService;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
@@ -14,12 +19,11 @@ import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Predicate;
-import javax.persistence.criteria.Root;
-import java.util.LinkedList;
-import java.util.List;
+import cn.com.qmth.stmms.biz.common.BaseQueryService;
+import cn.com.qmth.stmms.biz.report.dao.ReportSubjectQuestionDao;
+import cn.com.qmth.stmms.biz.report.model.ReportSubjectQuestion;
+import cn.com.qmth.stmms.biz.report.query.ReportSubjectQuery;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectQuestionService;
 
 /**
  * 客、主观题分析 service 实现
@@ -70,30 +74,30 @@ public class ReportSubjectQuestionServiceImpl extends BaseQueryService<ReportSub
                         if (query.getMainNumber() != null) {
                             predicates.add(cb.equal(root.get("mainNumber"), query.getMainNumber()));
                         }
-                        if (query.getSubNumber() != null) {
+                        if (StringUtils.isNotBlank(query.getSubNumber())) {
                             predicates.add(cb.equal(root.get("subNumber"), query.getSubNumber()));
                         }
                         if (query.getObjective() != null) {
                             predicates.add(cb.equal(root.get("objective"), query.getObjective()));
                         }
-                        if (query.getObjective() != null && query.getObjective() && StringUtils
-                                .isNotBlank(query.getPaperType())) {
+                        if (query.getObjective() != null && query.getObjective()
+                                && StringUtils.isNotBlank(query.getPaperType())) {
                             predicates.add(cb.equal(root.get("paperType"), query.getPaperType()));
                         }
                         if (query.getNullPaperType() != null && query.getNullPaperType().booleanValue()) {
                             predicates.add(cb.isNull(root.get("paperType")));
                         }
-                        return predicates.isEmpty() ?
-                                cb.conjunction() :
-                                cb.and(predicates.toArray(new Predicate[predicates.size()]));
+                        return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates
+                                .toArray(new Predicate[predicates.size()]));
                     }
                 }, query);
-        return result.getContent();
+        List<ReportSubjectQuestion> list = result.getContent();
+        return list;
     }
 
     @Override
     public ReportSubjectQuestion findOne(Integer examId, String subjectCode, boolean objective, String paperType,
-            Integer mainNumber, Integer subNumber) {
+            Integer mainNumber, String subNumber) {
         ReportSubjectQuery query = new ReportSubjectQuery();
         query.setExamId(examId);
         query.setSubjectCode(subjectCode);
@@ -108,9 +112,20 @@ public class ReportSubjectQuestionServiceImpl extends BaseQueryService<ReportSub
     @Override
     public List<ReportSubjectQuestion> findByExamIdAndSubjectCodeAndObjectiveAndPaperType(Integer examId,
             String subjectCode, boolean objective, String paperType) {
-        return reportSubjectQuestionDao
-                .findByExamIdAndSubjectCodeAndObjectiveAndPaperType(examId, subjectCode, objective, paperType,
-                        new Sort(Direction.ASC, "mainNumber", "subNumber"));
+        List<ReportSubjectQuestion> list = reportSubjectQuestionDao.findByExamIdAndSubjectCodeAndObjectiveAndPaperType(
+                examId, subjectCode, objective, paperType, new Sort(Direction.ASC, "mainNumber", "subNumber"));
+        Collections.sort(list, new Comparator<ReportSubjectQuestion>() {
+
+            @Override
+            public int compare(ReportSubjectQuestion o1, ReportSubjectQuestion o2) {
+                int i = o1.getMainNumber() - o2.getMainNumber();
+                if (i == 0) {
+                    return Integer.parseUnsignedInt(o1.getSubNumber()) - Integer.parseUnsignedInt(o2.getSubNumber());
+                }
+                return i;
+            }
+        });
+        return list;
     }
 
 }

+ 1 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectQuestionModule.java

@@ -80,7 +80,7 @@ public class SubjectQuestionModule implements Module, QuestionCalculatorProvider
                 Boolean objective = Boolean.parseBoolean(s[1]);
                 String paperType = StringUtils.trimToNull(s[2]);
                 Integer mainNumber = Integer.parseInt(s[3]);
-                Integer subNumber = Integer.parseInt(s[4]);
+                String subNumber = StringUtils.trimToEmpty(s[4]);
                 BaseCalculatorUnit unit = calculators.get(key);
                 ReportSubjectQuestion r = new ReportSubjectQuestion();
                 r.setExamId(this.context.getExamId());

+ 1 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/report/utils/module/SubjectQuestionOptionModule.java

@@ -87,7 +87,7 @@ public class SubjectQuestionOptionModule implements Module {
                 String subjectCode = s[0];
                 String paperType = StringUtils.trimToNull(s[2]);
                 Integer mainNumber = Integer.parseInt(s[3]);
-                Integer subNumber = Integer.parseInt(s[4]);
+                String subNumber = s[4];
                 OptionCounter optionCounter = this.counters.get(key);
                 Set<String> optionSet = optionMap.get(subjectCode);
                 if (optionSet == null) {

+ 31 - 31
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreCalculateUtil.java

@@ -104,37 +104,37 @@ public class ScoreCalculateUtil {
         ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
 
         List<ExamQuestion> oList = new LinkedList<ExamQuestion>();
-        ExamQuestion q = new ExamQuestion();
-        q.setMainNumber(1);
-        q.setMainTitle("单选");
-        q.setSubNumber(1);
-        q.setAnswer("B");
-        q.setTotalScore(2d);
-        oList.add(q);
-
-        q = new ExamQuestion();
-        q.setMainNumber(1);
-        q.setMainTitle("单选");
-        q.setSubNumber(2);
-        q.setAnswer("B");
-        q.setTotalScore(2d);
-        oList.add(q);
-
-        q = new ExamQuestion();
-        q.setMainNumber(2);
-        q.setMainTitle("多选");
-        q.setSubNumber(1);
-        q.setAnswer("AC");
-        q.setTotalScore(3d);
-        oList.add(q);
-
-        q = new ExamQuestion();
-        q.setMainNumber(2);
-        q.setMainTitle("多选");
-        q.setSubNumber(2);
-        q.setAnswer("C");
-        q.setTotalScore(3d);
-        oList.add(q);
+        // ExamQuestion q = new ExamQuestion();
+        // q.setMainNumber(1);
+        // q.setMainTitle("单选");
+        // q.setSubNumber(1);
+        // q.setAnswer("B");
+        // q.setTotalScore(2d);
+        // oList.add(q);
+        //
+        // q = new ExamQuestion();
+        // q.setMainNumber(1);
+        // q.setMainTitle("单选");
+        // q.setSubNumber(2);
+        // q.setAnswer("B");
+        // q.setTotalScore(2d);
+        // oList.add(q);
+        //
+        // q = new ExamQuestion();
+        // q.setMainNumber(2);
+        // q.setMainTitle("多选");
+        // q.setSubNumber(1);
+        // q.setAnswer("AC");
+        // q.setTotalScore(3d);
+        // oList.add(q);
+        //
+        // q = new ExamQuestion();
+        // q.setMainNumber(2);
+        // q.setMainTitle("多选");
+        // q.setSubNumber(2);
+        // q.setAnswer("C");
+        // q.setTotalScore(3d);
+        // oList.add(q);
 
         ScoreInfo info = util.calculate(oList, null);
         System.out.println(info.getObjectiveScore());

+ 5 - 6
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreInfo.java

@@ -7,7 +7,7 @@ public class ScoreInfo {
 
     private int mainNumber;
 
-    private int subNumber;
+    private String subNumber;
 
     private String mainTitle;
 
@@ -21,7 +21,7 @@ public class ScoreInfo {
 
     public ScoreInfo() {
         this.mainNumber = 0;
-        this.subNumber = 0;
+        this.subNumber = "";
         this.objectiveScore = 0d;
         this.subjectiveScore = 0d;
         this.totalScore = 0d;
@@ -31,16 +31,15 @@ public class ScoreInfo {
     public int incrMainNumber(String title) {
         mainTitle = title;
         mainNumber++;
-        subNumber = 0;
+        subNumber = "";
         return mainNumber;
     }
 
-    public void addScore(String title, int number, double score, String answer, boolean objective) {
-        subNumber++;
+    public void addScore(String title, String number, double score, String answer, boolean objective) {
 
         ScoreItem item = new ScoreItem(objective);
         item.setMainNumber(mainNumber);
-        item.setSubNumber(number > 0 ? number : subNumber);
+        item.setSubNumber(number);
         item.setTitle(title != null ? title : mainTitle + "-" + item.getSubNumber());
         item.setAnswer(answer);
         item.setScore(score);

+ 6 - 6
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreItem.java

@@ -12,11 +12,11 @@ public class ScoreItem {
 
     private int mainNumber;
 
-    private int subNumber;
+    private String subNumber;
 
     private String title;
 
-    private double score;
+    private Double score;
 
     private String answer;
 
@@ -41,11 +41,11 @@ public class ScoreItem {
         this.mainNumber = mainNumber;
     }
 
-    public int getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(int subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 
@@ -57,11 +57,11 @@ public class ScoreItem {
         this.title = title;
     }
 
-    public double getScore() {
+    public Double getScore() {
         return score;
     }
 
-    public void setScore(double score) {
+    public void setScore(Double score) {
         this.score = score;
     }
 

+ 17 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TaskLock.java

@@ -80,6 +80,23 @@ public class TaskLock {
         return false;
     }
 
+<<<<<<< HEAD
+=======
+    public synchronized boolean remove(Object id, int number) {
+        LockNode node = head.next;
+        while (node != null) {
+            if (node.isId(id) && node.isNumber(number)) {
+                node.remove();
+                count--;
+                return true;
+            } else {
+                node = node.next;
+            }
+        }
+        return false;
+    }
+
+>>>>>>> release_1.3.1
     public synchronized void clear() {
         head.next = null;
         count = 0;

+ 41 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TaskLockUtil.java

@@ -11,6 +11,15 @@ public class TaskLockUtil {
     // 试评任务并发处理互斥锁
     private static final Map<String, TaskLock> trialTaskMap = new HashMap<>();
 
+<<<<<<< HEAD
+=======
+    // 复核整卷并发处理互斥锁
+    private static final Map<String, TaskLock> inspectedStudentMap = new HashMap<>();
+
+    // 复核评卷任务并发处理互斥锁
+    private static final Map<String, TaskLock> inspectedLibraryMap = new HashMap<>();
+
+>>>>>>> release_1.3.1
     public static void clearTimeoutTask(long timeoutMinute) {
         for (TaskLock taskLock : trialTaskMap.values()) {
             taskLock.expire(timeoutMinute);
@@ -18,6 +27,15 @@ public class TaskLockUtil {
         for (TaskLock taskLock : formalTaskMap.values()) {
             taskLock.expire(timeoutMinute);
         }
+<<<<<<< HEAD
+=======
+        for (TaskLock taskLock : inspectedStudentMap.values()) {
+            taskLock.expire(timeoutMinute);
+        }
+        for (TaskLock taskLock : inspectedLibraryMap.values()) {
+            taskLock.expire(timeoutMinute);
+        }
+>>>>>>> release_1.3.1
     }
 
     public static TaskLock getTrialTask(String key) {
@@ -40,4 +58,27 @@ public class TaskLockUtil {
         return taskLock;
     }
 
+<<<<<<< HEAD
+=======
+    public static TaskLock getInspectedStudentTask(String key) {
+        TaskLock taskLock = inspectedStudentMap.get(key);
+        if (taskLock == null) {
+            synchronized (inspectedStudentMap) {
+                taskLock = inspectedStudentMap.computeIfAbsent(key, k -> new TaskLock());
+            }
+        }
+        return taskLock;
+    }
+
+    public static TaskLock getInspectedLibraryTask(String key) {
+        TaskLock taskLock = inspectedLibraryMap.get(key);
+        if (taskLock == null) {
+            synchronized (inspectedLibraryMap) {
+                taskLock = inspectedLibraryMap.computeIfAbsent(key, k -> new TaskLock());
+            }
+        }
+        return taskLock;
+    }
+
+>>>>>>> release_1.3.1
 }

+ 2 - 0
stmms-biz/src/main/resources/META-INF/persistence.xml

@@ -14,6 +14,8 @@
 				value="true" />
 			<property name="hibernate.cache.provider_class" value="org.hibernate.cache.SingletonEhCacheProvider"/>
 			<property name="hibernate.cache.use_query_cache" value="true" />
+			<property name="hibernate.query.plan_cache_max_size" value="512"/>
+			<property name="hibernate.query.plan_parameter_metadata_max_size" value="64"/>
 		</properties>
 	</persistence-unit>
 </persistence>  

+ 34 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/ExamSource.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.stmms.common.enums;
+
+public enum ExamSource {
+
+    EXAM_CLOUD("云平台", 1), ONLINE_EXAM("在线考试平台", 2);
+
+    private String name;
+
+    private int value;
+
+    private ExamSource(String name, int value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public static ExamSource findByValue(int value) {
+        ExamSource status = null;
+        for (ExamSource s : ExamSource.values()) {
+            if (s.getValue() == value) {
+                status = s;
+                break;
+            }
+        }
+        return status;
+    }
+}

+ 4 - 10
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/LibraryStatus.java

@@ -4,13 +4,8 @@ import java.util.LinkedList;
 import java.util.List;
 
 public enum LibraryStatus {
-    WAITING("未处理", 0), 
-    MARKED("已给分", 1), 
-    BACKED("已打回", 2), 
-    WAIT_ARBITRATE("等待仲裁", 3), 
-    ARBITRATED("已仲裁", 4),
-    PROBLEM("问题卷",5), 
-    INSPECTED("已复核",6);
+    WAITING("未处理", 0), MARKED("已给分", 1), REJECTED("已打回", 2), WAIT_ARBITRATE("等待仲裁", 3), ARBITRATED("已仲裁", 4), PROBLEM(
+            "问题卷", 5), INSPECTED("已复核", 6);
 
     private String name;
 
@@ -44,11 +39,10 @@ public enum LibraryStatus {
         if (options == null) {
             options = new LinkedList<>();
             for (LibraryStatus status : LibraryStatus.values()) {
-                if (status != BACKED) {
-                    options.add(status);
-                }
+                options.add(status);
             }
         }
         return options;
     }
+
 }

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ExamQuestionDTO.java

@@ -12,7 +12,7 @@ public class ExamQuestionDTO {
 
     private String mainTitle;
 
-    private Integer subNumber;
+    private String subNumber;
 
     private List<Double> scoreList;
 
@@ -26,11 +26,11 @@ public class ExamQuestionDTO {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ExceptionQuestionDTO.java

@@ -19,7 +19,7 @@ public class ExceptionQuestionDTO {
     private Integer mainNumber;
 
     @ExcelField(title = "小题号", align = 2, sort = 50)
-    private Integer subNumber;
+    private String subNumber;
 
     public ExceptionQuestionDTO(ExamQuestion question, ExamSubject subject) {
         setSubjectCode(question.getSubjectCode());
@@ -61,11 +61,11 @@ public class ExceptionQuestionDTO {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 }

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ObjectiveQuestionDTO.java

@@ -26,7 +26,7 @@ public class ObjectiveQuestionDTO implements QuestionDTO {
     private Integer mainNumber;
 
     @ExcelField(title = "小题号(只能用小写数字)", align = 2, sort = 60)
-    private Integer subNumber;
+    private String subNumber;
 
     @ExcelField(title = "标准答案", align = 2, sort = 70)
     private String answer;
@@ -91,11 +91,11 @@ public class ObjectiveQuestionDTO implements QuestionDTO {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 40 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/RejectResult.java

@@ -0,0 +1,40 @@
+package cn.com.qmth.stmms.admin.dto;
+
+import cn.com.qmth.stmms.biz.mark.model.MarkStepDTO;
+
+public class RejectResult {
+
+    /**
+     * 考生编号
+     */
+    private Integer studentId;
+
+    private Integer libraryId;
+
+    private MarkStepDTO[] questionList;
+
+    public Integer getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Integer studentId) {
+        this.studentId = studentId;
+    }
+
+    public MarkStepDTO[] getQuestionList() {
+        return questionList;
+    }
+
+    public void setQuestionList(MarkStepDTO[] questionList) {
+        this.questionList = questionList;
+    }
+
+    public Integer getLibraryId() {
+        return libraryId;
+    }
+
+    public void setLibraryId(Integer libraryId) {
+        this.libraryId = libraryId;
+    }
+
+}

+ 48 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ReportSubjectRangeDTO.java

@@ -0,0 +1,48 @@
+package cn.com.qmth.stmms.admin.dto;
+
+import java.text.DecimalFormat;
+
+import cn.com.qmth.stmms.common.annotation.ExcelField;
+
+public class ReportSubjectRangeDTO {
+
+    @ExcelField(title = "分数段", align = 2, sort = 10)
+    private String score;
+
+    @ExcelField(title = "人数", align = 2, sort = 20)
+    private Integer rangeCount;
+
+    @ExcelField(title = "频率", align = 2, sort = 30)
+    private String rangeRate;
+
+    public ReportSubjectRangeDTO(String score, Integer rangeCount, Double rangeRate) {
+        this.score = score;
+        this.rangeCount = rangeCount;
+        this.rangeRate = rangeRate > 0 ? (new DecimalFormat("####.###").format(rangeRate) + "%") : "0%";
+    }
+
+    public String getScore() {
+        return score;
+    }
+
+    public void setScore(String score) {
+        this.score = score;
+    }
+
+    public Integer getRangeCount() {
+        return rangeCount;
+    }
+
+    public void setRangeCount(Integer rangeCount) {
+        this.rangeCount = rangeCount;
+    }
+
+    public String getRangeRate() {
+        return rangeRate;
+    }
+
+    public void setRangeRate(String rangeRate) {
+        this.rangeRate = rangeRate;
+    }
+
+}

+ 15 - 8
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectQuestionDTO.java

@@ -37,7 +37,7 @@ public class SubjectQuestionDTO {
     }
 
     public void addQuestion(ExamQuestion question) {
-        if (question != null && question.getTotalScore() != null) {
+        if (question != null) {
             question.setExamId(examId);
             list.add(question);
 
@@ -63,8 +63,9 @@ public class SubjectQuestionDTO {
                     groups.put(question.getGroupNumber(), group);
                 }
                 group.getImportQuestionList().add(question);
-                group.setTotalScore(group.getTotalScore() + question.getTotalScore());
-                totalScore += question.getTotalScore();
+                // group.setTotalScore(group.getTotalScore() +
+                // question.getTotalScore());
+                // totalScore += question.getTotalScore();
             }
         }
     }
@@ -100,14 +101,10 @@ public class SubjectQuestionDTO {
                         error.add("[" + subjectCode + "] 有大题名称超过30个字的记录");
                         return false;
                     }
-                    if (question.getMainNumber() == null || question.getSubNumber() == null) {
+                    if (question.getMainNumber() == null || StringUtils.isBlank(question.getSubNumber())) {
                         error.add("[" + subjectCode + "] 有题号为空的记录");
                         return false;
                     }
-                    if (question.getGroupNumber() == null || question.getGroupNumber() < 0) {
-                        error.add("[" + subjectCode + "] 有评卷分组为空的记录");
-                        return false;
-                    }
                     if (question.getTotalScore() == null) {
                         error.add("[" + subjectCode + "] 有满分为空的记录");
                         return false;
@@ -239,4 +236,14 @@ public class SubjectQuestionDTO {
         }
         return null;
     }
+
+    public boolean validateGroupNumber(List<String> error) {
+        for (ExamQuestion question : list) {
+            if (question.getGroupNumber() == null || question.getGroupNumber() < 0) {
+                error.add("[" + subjectCode + "] 有评卷分组为空的记录");
+                return false;
+            }
+        }
+        return true;
+    }
 }

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectiveQuestionDTO.java

@@ -25,7 +25,7 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
     private Integer mainNumber;
 
     @ExcelField(title = "小题号(只能用小写数字)", align = 2, sort = 50)
-    private Integer subNumber;
+    private String subNumber;
 
     @ExcelField(title = "小题满分", align = 2, sort = 60)
     private Double totalScore;
@@ -142,11 +142,11 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
         this.title = title;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 143 - 121
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java

@@ -1,40 +1,64 @@
 package cn.com.qmth.stmms.admin.exam;
 
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.model.Marker;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
+import cn.com.qmth.stmms.biz.mark.model.ArbitrationDTO;
+import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
+import cn.com.qmth.stmms.biz.mark.model.MarkResult;
 import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.query.ArbitrateHistorySearchQuery;
 import cn.com.qmth.stmms.biz.mark.service.ArbitrateHistoryService;
+import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.user.service.UserService;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.*;
+import cn.com.qmth.stmms.common.enums.HistoryStatus;
+import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
-import net.sf.json.JSONObject;
-
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort.Direction;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.*;
 
 @Controller("arbitrateController")
 @RequestMapping("/admin/exam/arbitrate")
@@ -72,6 +96,15 @@ public class ArbitrateController extends BaseExamController {
     @Autowired
     private FileService fileService;
 
+    @Autowired
+    private MarkerService markerService;
+
+    @Autowired
+    private MarkLibraryService libraryService;
+
+    @Value("${slice.split.config}")
+    private String splitConfig;
+
     // 并发处理互斥锁
     private Map<Integer, Integer> currentTaskMap = new HashMap<Integer, Integer>();
 
@@ -87,21 +120,22 @@ public class ArbitrateController extends BaseExamController {
         if (StringUtils.isBlank(query.getSubjectCode()) && !subjectList.isEmpty()) {
             query.setSubjectCode(subjectList.get(0).getCode());
         }
-        List<MarkGroup> groupList = groupService
-                .findByExamAndSubjectWithDouble(query.getExamId(), query.getSubjectCode());
-        if (query.getGroupNumber() == null && groupList.size() > 0) {
-            query.setGroupNumber(groupList.get(0).getNumber());
-        }
-        for (MarkGroup group : groupList) {
-            group.setQuestionList(questionService
-                    .findByExamAndSubjectAndObjectiveAndGroupNumber(query.getExamId(), group.getSubjectCode(), false,
-                            group.getNumber()));
-        }
-        query.orderByIdDesc();
-        query = arbitrateService.findByQuery(query);
-        for (ArbitrateHistory history : query.getResult()) {
-            if (history.getUserId() != null) {
-                history.setUser(userService.findById(history.getUserId()));
+        List<MarkGroup> groupList = groupService.findByExamAndSubjectWithDouble(query.getExamId(),
+                query.getSubjectCode());
+        if (!groupList.isEmpty()) {
+            for (MarkGroup group : groupList) {
+                group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(query.getExamId(),
+                        group.getSubjectCode(), false, group.getNumber()));
+            }
+            if (query.getGroupNumber() == null && groupList.size() > 0) {
+                query.setGroupNumber(groupList.get(0).getNumber());
+            }
+            query.orderByIdDesc();
+            query = arbitrateService.findByQuery(query);
+            for (ArbitrateHistory history : query.getResult()) {
+                if (history.getUserId() != null) {
+                    history.setUser(userService.findById(history.getUserId()));
+                }
             }
         }
         model.addAttribute("query", query);
@@ -111,63 +145,45 @@ public class ArbitrateController extends BaseExamController {
         return "modules/exam/arbitrateList";
     }
 
-    @RequestMapping("/process")
+    @RequestMapping("/getSetting")
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public String singleProcess(Model model, HttpServletRequest request, @RequestParam Integer historyId) {
+    @ResponseBody
+    public JSONObject getSetting(Model model, HttpServletRequest request,
+            @RequestParam(required = false) String subjectCode, @RequestParam(required = false) Integer groupNumber,
+            @RequestParam(required = false) Integer historyId) {
         int examId = getSessionExamId(request);
         WebUser wu = RequestUtils.getWebUser(request);
-        ArbitrateHistory history = arbitrateService.findById(historyId);
-        if (history == null || !history.getExamId().equals(examId) || !subjectCheck(history.getSubjectCode(), wu)) {
-            return "redirect:/admin/exam/arbitrate";
-        }
-        MarkGroup group = groupService.findOne(examId, history.getSubjectCode(), history.getGroupNumber());
-        if (group == null) {
-            return "redirect:/admin/exam/arbitrate";
-        }
-        ExamSubject subject = subjectService.find(group.getExamId(), group.getSubjectCode());
-        if (subject == null) {
-            return "redirect:/admin/exam/arbitrate";
-        }
         releaseByUser(wu.getUser().getId());
-        subject.setPaperAnswerUrl(fileService);
-        model.addAttribute("fileServer", fileService.getFileServer());
-        model.addAttribute("subject", subject);
-        model.addAttribute("group", group);
-        model.addAttribute("history", history);
-        Exam exam = examService.findById(examId);
-        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
-            return "modules/exam/arbitrateSingleProcessJson";
-        }
-        return "modules/exam/arbitrateSingleProcess";
-    }
+        JSONObject setting = new JSONObject();
 
-    @RequestMapping("/batchProcess")
-    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public String batchProcess(Model model, HttpServletRequest request, @RequestParam String subjectCode,
-            @RequestParam Integer groupNumber) {
-        int examId = getSessionExamId(request);
-        WebUser wu = RequestUtils.getWebUser(request);
-        if (!subjectCheck(subjectCode, wu)) {
-            return "redirect:/admin/exam/arbitrate";
+        if (historyId != null) {
+            ArbitrateHistory history = arbitrateService.findById(historyId);
+            subjectCode = history.getSubjectCode();
+            groupNumber = history.getGroupNumber();
         }
-        MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
-        if (group == null) {
-            return "redirect:/admin/exam/arbitrate";
-        }
-        ExamSubject subject = subjectService.find(group.getExamId(), group.getSubjectCode());
-        if (subject == null) {
-            return "redirect:/admin/exam/arbitrate";
-        }
-        releaseByUser(wu.getUser().getId());
-        subject.setPaperAnswerUrl(fileService);
-        model.addAttribute("fileServer", fileService.getFileServer());
-        model.addAttribute("subject", subject);
-        model.addAttribute("group", group);
+        ExamSubject examSubject = subjectService.find(examId, subjectCode);
+        setting.accumulate("fileServer", fileService.getFileServer());
         Exam exam = examService.findById(examId);
-        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
-            return "modules/exam/arbitrateBatchProcessJson";
+        setting.accumulate("examType", exam.getType());
+        setting.accumulate("userName", wu.getName());
+        JSONObject subject = new JSONObject();
+        subject.accumulate("name", examSubject.getName());
+        subject.accumulate("code", examSubject.getCode());
+        examSubject.setPaperAnswerUrl(fileService);
+        subject.accumulate("paperUrl", examSubject.getPaperUrl() == null ? "" : examSubject.getPaperUrl());
+        subject.accumulate("answerUrl", examSubject.getAnswerUrl() == null ? "" : examSubject.getAnswerUrl());
+        setting.accumulate("subject", subject);
+        setting.accumulate("splitConfig", getSplitConfig());
+        return setting;
+    }
+
+    private double[] getSplitConfig() {
+        String strs[] = splitConfig.split(",");
+        double[] config = new double[strs.length];
+        for (int i = 0; i < strs.length; i++) {
+            config[i] = Double.parseDouble(strs[i]);
         }
-        return "modules/exam/arbitrateBatchProcess";
+        return config;
     }
 
     @Logging(menu = "打回仲裁任务", type = LogType.UPDATE)
@@ -197,26 +213,24 @@ public class ArbitrateController extends BaseExamController {
         return obj;
     }
 
-    @RequestMapping(value = "/history/{subjectCode}/{groupNumber}", method = RequestMethod.POST)
+    @RequestMapping(value = "/getHistory", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public List<Task> getHistory(HttpServletRequest request, @PathVariable String subjectCode,
-            @PathVariable Integer groupNumber, @RequestParam(required = false) Integer pageNumber,
+    public List<Task> getHistory(HttpServletRequest request, @RequestParam String subjectCode,
+            @RequestParam Integer groupNumber, @RequestParam(required = false) Integer pageNumber,
             @RequestParam(required = false) Integer pageSize, @RequestParam String order, @RequestParam String sort,
             @RequestParam(required = false, defaultValue = "false") Boolean isTag,
-            @RequestParam(required = false) Integer studentId) {
+            @RequestParam(required = false) String secretNumber) {
         int examId = getSessionExamId(request);
         WebUser wu = RequestUtils.getWebUser(request);
         Direction d = Direction.DESC;
         Sort querySort = null;
-        if (sort.equals("asc")) {
+        if (sort.equalsIgnoreCase("ASC")) {
             d = Direction.ASC;
         }
-        if (order.equals("time")) {
+        if (order.equals("markerTime")) {
             querySort = new Sort(d, "updateTime");
-        } else if (order.equals("studentId")) {
-            querySort = new Sort(d, "studentId");
-        } else if (order.equals("score")) {
+        } else if (order.equals("markerScore")) {
             querySort = new Sort(d, "totalScore");
         }
         MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
@@ -230,46 +244,34 @@ public class ArbitrateController extends BaseExamController {
             query.setUserId(wu.getUser().getId());
             query.setPageNumber(pageNumber);
             query.setPageSize(pageSize);
+            query.setSecretNumber(secretNumber);
             if (querySort != null) {
                 query.setSort(querySort);
             } else {
                 query.orderByUpdateTimeDesc();
             }
-            if (studentId != null) {
-                query.setStudentId(studentId);
-            }
             query = arbitrateService.findByQuery(query);
             for (ArbitrateHistory history : query.getResult()) {
-                list.add(taskService.build(history, group));
+                Task task = taskService.build(history, group);
+                task.setPrevious(true);
+                list.add(task);
             }
         }
         return list;
     }
 
-    @RequestMapping("/singleTask")
+    @RequestMapping("/getTask")
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public Task getSingleTask(HttpServletRequest request, @RequestParam Integer historyId) {
+    public Task getTask(HttpServletRequest request, @RequestParam(required = false) String subjectCode,
+            @RequestParam(required = false) Integer groupNumber, @RequestParam(required = false) Integer historyId) {
         int examId = getSessionExamId(request);
         WebUser wu = RequestUtils.getWebUser(request);
-        ArbitrateHistory history = arbitrateService.findById(historyId);
-        if (history != null && history.getExamId().equals(examId) && subjectCheck(history.getSubjectCode(), wu)) {
-            MarkGroup group = groupService
-                    .findOne(history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
+        if (historyId != null) {
+            ArbitrateHistory history = arbitrateService.findById(historyId);
+            MarkGroup group = groupService.findOne(examId, history.getSubjectCode(), history.getGroupNumber());
             return taskService.build(history, group);
         }
-        Task task = new Task();
-        task.setExist(false);
-        return task;
-    }
-
-    @RequestMapping("/getTask")
-    @ResponseBody
-    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public Task getTask(HttpServletRequest request, @RequestParam String subjectCode,
-            @RequestParam Integer groupNumber) {
-        int examId = getSessionExamId(request);
-        WebUser wu = RequestUtils.getWebUser(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
         if (subjectCheck(subjectCode, wu) && group != null) {
             ArbitrateHistorySearchQuery query = new ArbitrateHistorySearchQuery();
@@ -298,7 +300,7 @@ public class ArbitrateController extends BaseExamController {
             }
         }
         Task task = new Task();
-        task.setExist(false);
+        // task.setExist(false);
         return task;
     }
 
@@ -306,20 +308,19 @@ public class ArbitrateController extends BaseExamController {
     @RequestMapping(value = "/saveTask", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public JSONObject saveTask(HttpServletRequest request, @RequestBody Task task) {
+    public JSONObject saveTask(HttpServletRequest request, @RequestBody MarkResult markResult) {
         WebUser wu = RequestUtils.getWebUser(request);
         JSONObject result = new JSONObject();
-        ArbitrateHistory history = arbitrateService.findById(task.getLibraryId());
-        if (history != null && subjectCheck(history.getSubjectCode(), wu) && task.getTotalScore() >= 0
-                && task.getScoreList() != null) {
+        ArbitrateHistory history = arbitrateService.findById(markResult.getLibraryId());
+        if (history != null && subjectCheck(history.getSubjectCode(), wu) && markResult.getMarkerScore() >= 0) {
             try {
                 lockService.watch(LockType.EXAM_SUBJECT, history.getExamId(), history.getSubjectCode());
-                lockService
-                        .watch(LockType.GROUP, history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
+                lockService.watch(LockType.GROUP, history.getExamId(), history.getSubjectCode(),
+                        history.getGroupNumber());
 
                 history.setUserId(wu.getUser().getId());
-                history.setTotalScore(task.getTotalScore());
-                history.setScoreList(task.getScoreList());
+                history.setTotalScore(markResult.getMarkerScore());
+                history.setScoreList(markResult.getScoreList());
                 history.setStatus(HistoryStatus.MARKED);
                 history.setUpdateTime(new Date());
                 markService.processArbitrate(history);
@@ -340,7 +341,7 @@ public class ArbitrateController extends BaseExamController {
         return result;
     }
 
-    @RequestMapping("/status")
+    @RequestMapping("/getStatus")
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public JSONObject status(HttpServletRequest request, @RequestParam String subjectCode,
@@ -370,17 +371,38 @@ public class ArbitrateController extends BaseExamController {
         return status;
     }
 
+    @RequestMapping("/getArbitrationList")
+    @ResponseBody
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
+    public Object getList(HttpServletRequest request, @RequestParam Integer historyId) {
+        ArbitrateHistory history = arbitrateService.findById(historyId);
+        List<ArbitrationDTO> list = new ArrayList<ArbitrationDTO>();
+        List<MarkLibrary> libraryList = libraryService.findByStudentAndGroup(history.getStudentId(),
+                history.getGroupNumber());
+        for (MarkLibrary library : libraryList) {
+            if (library.getStatus() != LibraryStatus.WAITING) {
+                Marker marker = markerService.findById(library.getMarkerId());
+                marker.setUser(userService.findById(marker.getUserId()));
+                list.add(new ArbitrationDTO(library, marker));
+            }
+        }
+        return list;
+    }
+
     @RequestMapping("/clear")
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public Object clear(HttpServletRequest request, @RequestParam(required = false) Integer libraryId) {
         WebUser wu = RequestUtils.getWebUser(request);
+        JSONObject obj = new JSONObject();
         if (libraryId != null) {
             releaseTask(libraryId);
+            obj.accumulate("success", true);
         } else {
             releaseByUser(wu.getUser().getId());
+            obj.accumulate("success", true);
         }
-        return true;
+        return obj;
     }
 
     private void releaseByUser(Integer userId) {

+ 1 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/CheckStudentController.java

@@ -86,6 +86,7 @@ public class CheckStudentController extends BaseExamController {
         CheckStudent cs = checkStudentService.findByStudentId(studentId);
         if (student != null && cs != null) {
             student.setAnswers(answers);
+            studentService.save(student);
             cs.setChecked(true);
             cs.setUpdateTime(new Date());
             checkStudentService.save(cs);

+ 44 - 26
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/DataSyncController.java

@@ -1,19 +1,11 @@
 package cn.com.qmth.stmms.admin.exam;
 
-import cn.com.qmth.stmms.admin.dto.SubjectiveQuestionDTO;
-import cn.com.qmth.stmms.admin.thread.DataSyncThread;
-import cn.com.qmth.stmms.admin.utils.HttpUtil;
-import cn.com.qmth.stmms.biz.exam.model.DataSync;
-import cn.com.qmth.stmms.biz.exam.service.*;
-import cn.com.qmth.stmms.biz.file.service.FileService;
-import cn.com.qmth.stmms.biz.lock.LockService;
-import cn.com.qmth.stmms.common.annotation.Logging;
-import cn.com.qmth.stmms.common.annotation.RoleRequire;
-import cn.com.qmth.stmms.common.enums.LockType;
-import cn.com.qmth.stmms.common.enums.LogType;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.utils.ExportExcel;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 
@@ -30,11 +22,27 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import java.util.ArrayList;
-import java.util.List;
+import cn.com.qmth.stmms.admin.dto.SubjectiveQuestionDTO;
+import cn.com.qmth.stmms.admin.thread.DataSyncThread;
+import cn.com.qmth.stmms.admin.thread.OnlineExamThread;
+import cn.com.qmth.stmms.admin.utils.HttpUtil;
+import cn.com.qmth.stmms.biz.exam.model.DataSync;
+import cn.com.qmth.stmms.biz.exam.service.DataSyncService;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.enums.ExamSource;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
 
 @Controller
 @RequestMapping("/admin/exam/data/sync")
@@ -67,6 +75,9 @@ public class DataSyncController extends BaseExamController {
     @Autowired
     private FileService fileService;
 
+    @Autowired
+    private MarkGroupService groupService;
+
     @Value("${qmth.examcloud.host}")
     private String host;
 
@@ -107,11 +118,12 @@ public class DataSyncController extends BaseExamController {
             dataSync.setSubjectUrl(host + ":" + port + subjectUri);
             dataSync.setSubjectPaperUrl(host + ":" + port + subjectPaperUri);
             dataSync.setAppId(appId);
-            dataSync.setSecretKey(secretKey);
+            dataSync.setAccessKey(secretKey);
         }
         ModelAndView view = new ModelAndView("modules/exam/dataSync");
         view.addObject("running", lockService.isLocked(LockType.DATA_SYNC, examId));
         view.addObject("exam", examService.findById(examId));
+        view.addObject("sourceList", ExamSource.values());
         view.addObject("dataSync", dataSync);
         return view;
     }
@@ -122,9 +134,15 @@ public class DataSyncController extends BaseExamController {
     public String save(HttpServletRequest request, DataSync dataSync, RedirectAttributes redirectAttributes) {
         int examId = getSessionExamId(request);
         if (lockService.trylock(LockType.DATA_SYNC, examId)) {
-            DataSyncThread thread = new DataSyncThread(dataSync, pageSize, lockService, dataSyncService, examService,
-                    studentService, subjectService, fileService);
-            taskExecutor.submit(thread);
+            if (dataSync.getSource().equals(ExamSource.EXAM_CLOUD)) {
+                DataSyncThread thread = new DataSyncThread(dataSync, pageSize, lockService, dataSyncService,
+                        examService, studentService, subjectService, fileService);
+                taskExecutor.submit(thread);
+            } else if (dataSync.getSource().equals(ExamSource.ONLINE_EXAM)) {
+                OnlineExamThread thread = new OnlineExamThread(dataSync, lockService, dataSyncService, examService,
+                        studentService, subjectService, questionService, groupService, fileService);
+                taskExecutor.submit(thread);
+            }
         }
         dataSync = dataSyncService.findByExamId(examId);
         if (lockService.isLocked(LockType.DATA_SYNC, examId) || (dataSync != null && dataSync.isFinished())) {
@@ -157,10 +175,10 @@ public class DataSyncController extends BaseExamController {
     private List<SubjectiveQuestionDTO> getPaperStruct(DataSync sync) throws Exception {
         List<SubjectiveQuestionDTO> list = new ArrayList<SubjectiveQuestionDTO>();
 
-        HttpUtil subjectHttp = new HttpUtil(sync.getSubjectUrl(), sync.getSecretKey(), sync.getAppId(),
+        HttpUtil subjectHttp = new HttpUtil(sync.getSubjectUrl(), sync.getAccessKey(), sync.getAppId(),
                 sync.getRootOrgId());
         JSONObject datas = new JSONObject();
-        datas.accumulate("examId", sync.getCloudExamId());
+        datas.accumulate("examId", Long.parseLong(sync.getCloudExamId()));
         String subjectResult = subjectHttp.httpAction(null, datas.toString());
         JSONObject subjectJson = JSONObject.fromObject(subjectResult);
 
@@ -181,7 +199,7 @@ public class DataSyncController extends BaseExamController {
                 questionDTO.setSubjectName(subject.getString("subjectName"));
                 questionDTO.setMainNumber(question.getInt("mainNumber"));
                 questionDTO.setTitle(question.getString("mainTitle"));
-                questionDTO.setSubNumber(question.getInt("subNumber"));
+                questionDTO.setSubNumber(question.getString("subNumber"));
                 questionDTO.setTotalScore(question.getDouble("totalScore"));
                 list.add(questionDTO);
             }

+ 7 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java

@@ -42,6 +42,7 @@ import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
 import cn.com.qmth.stmms.common.enums.ExamType;
 import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.MarkMode;
 import cn.com.qmth.stmms.common.enums.ObjectiveStatus;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
@@ -90,6 +91,7 @@ public class ExamController extends BaseExamController {
         model.addAttribute("exam", exam);
         model.addAttribute("statusList", ExamStatus.values());
         model.addAttribute("typeList", ExamType.values());
+        model.addAttribute("markModeList", MarkMode.values());
         return "modules/exam/examForm";
     }
 
@@ -102,6 +104,7 @@ public class ExamController extends BaseExamController {
         model.addAttribute("pictureConfig", buildPictureConfig(exam.getSheetConfig()));
         model.addAttribute("passScore", exam.getPassScore());
         model.addAttribute("excellentScore", exam.getExcellentScore());
+        model.addAttribute("markModeList", MarkMode.values());
         return "modules/exam/examEdit";
     }
 
@@ -157,6 +160,10 @@ public class ExamController extends BaseExamController {
                 List<PictureConfigItem> list = JSONArray.toList(array, new PictureConfigItem(), new JsonConfig());
                 oldExam.setSheetConfig(list == null ? null : StringUtils.join(list, PictureConfigItem.DB_ITEM_JOINER));
             }
+            oldExam.setStartTime(exam.getStartTime());
+            oldExam.setEndTime(exam.getEndTime());
+            oldExam.setMarkMode(exam.getMarkMode());
+            oldExam.setSheetView(exam.isSheetView());
             examService.save(oldExam);
         }
         return "redirect:/admin/exam/list";

+ 202 - 75
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/InspectedController.java

@@ -2,48 +2,47 @@ package cn.com.qmth.stmms.admin.exam;
 
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 
-import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
+import cn.com.qmth.stmms.admin.dto.RejectResult;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
-import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
-import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
-import cn.com.qmth.stmms.biz.exam.model.SubjectiveScore;
 import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+<<<<<<< HEAD
 import cn.com.qmth.stmms.biz.exam.service.InspectedService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.SubjectiveScoreService;
+=======
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.InspectedService;
+>>>>>>> release_1.3.1
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
-import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
+import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
@@ -65,6 +64,7 @@ public class InspectedController extends BaseExamController {
 
     @Autowired
     private InspectedService inspectedService;
+<<<<<<< HEAD
 
     @Autowired
     private ExamQuestionService questionService;
@@ -74,9 +74,11 @@ public class InspectedController extends BaseExamController {
 
     @Autowired
     private MarkSpecialTagService markSpecialTagService;
+=======
+>>>>>>> release_1.3.1
 
     @Autowired
-    private MarkGroupService groupService;
+    private ExamQuestionService questionService;
 
     @Autowired
     private FileService fileService;
@@ -84,17 +86,20 @@ public class InspectedController extends BaseExamController {
     @Autowired
     private LockService lockService;
 
-    @Autowired
-    private SubjectiveScoreService scoreService;
-
     @Autowired
     private MarkService markService;
 
     @Autowired
     private ExamService examService;
 
-    // 并发处理互斥锁
-    private Map<Integer, Integer> currentTaskMap = new HashMap<Integer, Integer>();
+    @Autowired
+    private TaskService taskService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Value("${slice.split.config}")
+    private String splitConfig;
 
     @RequestMapping
     public String list(Model model, HttpServletRequest request, ExamStudentSearchQuery query,
@@ -114,12 +119,23 @@ public class InspectedController extends BaseExamController {
         }
         List<ExamStudent> list = inspectedService.findByQuery(query, status, mainNumber, mainStartScore, mainEndScore,
                 questionScore);
+<<<<<<< HEAD
         Integer count = inspectedService.countByQuery(query, status, mainNumber, mainStartScore, mainEndScore,
                 questionScore);
         query.setResult(list);
         query.setTotalCount(count);
         model.addAttribute("query", query);
         model.addAttribute("inspectCount", count);
+=======
+        Integer totalCount = inspectedService.countByQuery(query, null, mainNumber, mainStartScore, mainEndScore,
+                questionScore);
+        Integer inspectCount = inspectedService.countByQuery(query, SubjectiveStatus.MARKED, mainNumber,
+                mainStartScore, mainEndScore, questionScore);
+        query.setResult(list);
+        query.setTotalCount(totalCount);
+        model.addAttribute("query", query);
+        model.addAttribute("inspectCount", inspectCount);
+>>>>>>> release_1.3.1
         model.addAttribute("questionList",
                 questionService.findMainByExamAndSubjectAndObjective(examId, query.getSubjectCode(), false));
         model.addAttribute("mainNumber", mainNumber);
@@ -134,14 +150,15 @@ public class InspectedController extends BaseExamController {
     }
 
     @Logging(menu = "开始考生复核", type = LogType.QUERY)
-    @RequestMapping("/start")
+    @RequestMapping(value = "/getTask", method = RequestMethod.POST)
     @ResponseBody
-    public ModelAndView start(HttpServletRequest request, RedirectAttributes redirectAttributes,
+    public Task getTask(HttpServletRequest request, RedirectAttributes redirectAttributes,
             ExamStudentSearchQuery query, @RequestParam(required = false) Integer mainNumber,
             @RequestParam(required = false) Double mainStartScore, @RequestParam(required = false) Double mainEndScore,
-            @RequestParam(required = false) Double questionScore) {
+            @RequestParam(required = false) Double questionScore, @RequestParam(required = false) Integer studentId) {
         int examId = getSessionExamId(request);
         WebUser wu = RequestUtils.getWebUser(request);
+<<<<<<< HEAD
         releaseByUser(wu.getUser().getId());
         ModelAndView view = new ModelAndView("modules/exam/inspected");
         query.setExamId(examId);
@@ -225,10 +242,39 @@ public class InspectedController extends BaseExamController {
                     obj.accumulate("questionNumber", scoreItem.getMainNumber() + "-" + scoreItem.getSubNumber());
                     obj.accumulate("score", scoreItem.getScore());
                     array.add(obj);
+=======
+        Task task = null;
+        if (studentId != null) {
+            releaseStudent(studentId);
+            ExamStudent student = studentService.findById(studentId);
+            if (inspectedService.applyStudent(student, wu.getId())) {
+                task = taskService.build(student);
+            }
+        }
+        int retry = 1;
+        while (task == null) {
+            List<ExamStudent> list = new ArrayList<ExamStudent>();
+            // 需要判断评卷员是否绑定了班级
+            query.setExamId(examId);
+            query.setPageNumber(retry);
+            query.setPageSize(20);
+            List<ExamSubject> subjectList = getExamSubject(examId, wu);
+            if (StringUtils.isBlank(query.getSubjectCode()) && !subjectList.isEmpty()) {
+                query.setSubjectCode(subjectList.get(0).getCode());
+            }
+            list = inspectedService.findByQuery(query, SubjectiveStatus.MARKED, mainNumber, mainStartScore,
+                    mainEndScore, questionScore);
+            if (list.isEmpty()) {
+                break;
+            }
+            for (ExamStudent student : list) {
+                if (inspectedService.applyStudent(student, wu.getId())) {
+                    task = taskService.build(student);
+                    break;
+>>>>>>> release_1.3.1
                 }
-                group.accumulate("questions", array);
-                groupArray.add(group);
             }
+<<<<<<< HEAD
             result.accumulate("groups", groupArray);
             List<String> picUrls = fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1,
                     student.getSliceCount());
@@ -236,45 +282,69 @@ public class InspectedController extends BaseExamController {
             result.accumulate("success", true);
         } else {
             result.accumulate("success", false);
+=======
+            if (task == null) {
+                retry++;
+            }
+>>>>>>> release_1.3.1
         }
-        return result;
+        return task;
     }
 
     @Logging(menu = "考生评卷复核", type = LogType.UPDATE)
-    @RequestMapping("/save")
+    @RequestMapping(value = "/save", method = RequestMethod.POST)
     @ResponseBody
     public Object save(HttpServletRequest request, @RequestParam Integer studentId) {
         WebUser wu = RequestUtils.getWebUser(request);
         ExamStudent student = studentService.findById(studentId);
-        if (student != null && student.getSubjectiveStatus().equals(SubjectiveStatus.MARKED)) {
-            studentService.updateSubjectiveStatusAndTimeAndInspectorId(studentId, SubjectiveStatus.INSPECTED,
-                    new Date(), wu.getUser().getId());
-            return true;
-        } else {
-            return false;
+        JSONObject obj = new JSONObject();
+        try {
+            if (lockService.trylock(LockType.STUDENT, student.getId())
+                    && inspectedService.hasApplied(student, wu.getId())
+                    && SubjectiveStatus.MARKED.equals(student.getSubjectiveStatus())) {
+                studentService.updateSubjectiveStatusAndTimeAndInspectorId(studentId, SubjectiveStatus.INSPECTED,
+                        new Date(), wu.getUser().getId());
+                inspectedService.releaseByStudent(student);
+                obj.accumulate("success", true);
+            } else {
+                obj.accumulate("success", false);
+                obj.accumulate("message", "无法复核,请刷新页面");
+            }
+        } catch (Exception e) {
+            obj.accumulate("success", false);
+            log.error("inspected save error", e);
+        } finally {
+            lockService.unlock(LockType.STUDENT, student.getId());
         }
+        return obj;
     }
 
-    @RequestMapping("/clear")
+    @RequestMapping(value = "/clear", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.INSPECTOR })
-    public Object clear(HttpServletRequest request, @RequestParam(required = false) Integer studentId) {
-        WebUser wu = RequestUtils.getWebUser(request);
+    public JSONObject clear(HttpServletRequest request, @RequestParam(required = false) String subjectCode,
+            @RequestParam(required = false) Integer studentId) {
+        JSONObject obj = new JSONObject();
         if (studentId != null) {
-            releaseTask(studentId);
+            releaseStudent(studentId);
+            obj.accumulate("success", true);
+        } else if (subjectCode != null) {
+            int examId = getSessionExamId(request);
+            releaseUser(examId, subjectCode, RequestUtils.getWebUser(request).getId());
+            obj.accumulate("success", true);
         } else {
-            releaseByUser(wu.getUser().getId());
+            obj.accumulate("success", false);
         }
-        return true;
+        return obj;
     }
 
     @Logging(menu = "取消复核", type = LogType.UPDATE)
     @RequestMapping(value = "/cancel", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.INSPECTOR })
-    public JSONObject cancel(HttpServletRequest request, @RequestParam Integer id) {
+    public JSONObject cancel(HttpServletRequest request, @RequestParam Integer studentId) {
         JSONObject obj = new JSONObject();
-        ExamStudent student = studentService.findById(id);
+        ExamStudent student = studentService.findById(studentId);
         WebUser wu = RequestUtils.getWebUser(request);
         if (student != null && student.getSubjectiveStatus().equals(SubjectiveStatus.INSPECTED)) {
             if (subjectCheck(student.getSubjectCode(), wu)) {
@@ -305,21 +375,21 @@ public class InspectedController extends BaseExamController {
         return obj;
     }
 
-    @Logging(menu = "复核打回", type = LogType.UPDATE)
-    @RequestMapping(value = "/back", method = RequestMethod.POST)
+    @Logging(menu = "打回", type = LogType.UPDATE)
+    @RequestMapping(value = "/rejected", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.INSPECTOR })
-    public JSONObject back(HttpServletRequest request, @RequestParam Integer studentId,
-            @RequestParam Integer groupNumber) {
+    public JSONObject rejected(HttpServletRequest request, @RequestBody RejectResult rejectResult) {
         JSONObject obj = new JSONObject();
-        ExamStudent student = studentService.findById(studentId);
+        ExamStudent student = studentService.findById(rejectResult.getStudentId());
         WebUser wu = RequestUtils.getWebUser(request);
-        if (student != null && student.getSubjectiveStatus().equals(SubjectiveStatus.MARKED)) {
+        if (student != null
+                && (student.getSubjectiveStatus().equals(SubjectiveStatus.MARKED) || student.getSubjectiveStatus()
+                        .equals(SubjectiveStatus.INSPECTED))) {
             if (subjectCheck(student.getSubjectCode(), wu) && lockService.trylock(LockType.STUDENT, student.getId())) {
                 try {
                     lockService.watch(LockType.EXAM_SUBJECT, student.getExamId(), student.getSubjectCode());
-                    lockService.watch(LockType.GROUP, student.getExamId(), student.getSubjectCode(), groupNumber);
-                    if (markService.backStudentByGroup(student, groupNumber, wu.getId())) {
+                    if (markService.rejectedStudent(student, rejectResult.getQuestionList(), wu.getId())) {
                         obj.accumulate("success", true);
                     } else {
                         obj.accumulate("success", false);
@@ -330,7 +400,6 @@ public class InspectedController extends BaseExamController {
                     obj.accumulate("message", "打回失败");
                     log.error("back inspected error", e);
                 } finally {
-                    lockService.unwatch(LockType.GROUP, student.getExamId(), student.getSubjectCode(), groupNumber);
                     lockService.unwatch(LockType.EXAM_SUBJECT, student.getExamId(), student.getSubjectCode());
                     lockService.unlock(LockType.STUDENT, student.getId());
                 }
@@ -340,44 +409,102 @@ public class InspectedController extends BaseExamController {
             }
         } else {
             obj.accumulate("success", false);
-            obj.accumulate("message", "无法取消复核");
+            obj.accumulate("message", "无法打回");
         }
         return obj;
     }
 
-    private boolean setCurrent(Integer taskId, Integer userId) {
-        Integer value = currentTaskMap.get(taskId);
-        if (value == null) {
-            synchronized (currentTaskMap) {
-                value = currentTaskMap.get(taskId);
-                if (value == null) {
-                    currentTaskMap.put(taskId, userId);
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-        } else {
-            return false;
+    @Logging(menu = "回看复核任务", type = LogType.QUERY)
+    @RequestMapping(value = "/getHistory", method = RequestMethod.POST)
+    @ResponseBody
+    public Object getHistory(HttpServletRequest request, @RequestParam String subjectCode,
+            @RequestParam int pageNumber, @RequestParam int pageSize) throws Exception {
+        int examId = getSessionExamId(request);
+        WebUser wu = RequestUtils.getWebUser(request);
+        List<Task> list = new ArrayList<>();
+        ExamStudentSearchQuery query = new ExamStudentSearchQuery();
+        query.setExamId(examId);
+        query.setSubjectCode(subjectCode);
+        query.setInspectorId(wu.getId());
+        query.addStatus(SubjectiveStatus.INSPECTED);
+        query.setPageNumber(pageNumber);
+        query.setPageSize(pageSize);
+        query.orderByInspectTimeDesc();
+        query = studentService.findByQuery(query);
+        for (ExamStudent student : query.getResult()) {
+            Task task = taskService.build(student);
+            task.setPrevious(true);
+            list.add(task);
+        }
+        return list;
+    }
+
+    @RequestMapping(value = "/getStatus", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject status(HttpServletRequest request, ExamStudentSearchQuery query,
+            @RequestParam(required = false) Integer mainNumber, @RequestParam(required = false) Double mainStartScore,
+            @RequestParam(required = false) Double mainEndScore, @RequestParam(required = false) Double questionScore) {
+        JSONObject status = new JSONObject();
+        int examId = getSessionExamId(request);
+        query.setExamId(examId);
+        Integer totalCount = inspectedService.countByQuery(query, SubjectiveStatus.MARKED, mainNumber, mainStartScore,
+                mainEndScore, questionScore);
+        status.accumulate("totalCount", totalCount);
+        status.accumulate("valid", true);
+        return status;
+    }
+
+    @RequestMapping(value = "/getSetting", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject getSetting(HttpServletRequest request, @RequestParam(required = false) String subjectCode) {
+        JSONObject setting = new JSONObject();
+        WebUser wu = RequestUtils.getWebUser(request);
+        setting.accumulate("fileServer", fileService.getFileServer());
+        setting.accumulate("userName", wu.getName());
+        setting.accumulate("splitConfig", getSplitConfig());
+        if (StringUtils.isNotBlank(subjectCode)) {
+            int examId = getSessionExamId(request);
+            ExamSubject examSubject = subjectService.find(examId, subjectCode);
+            JSONObject subject = new JSONObject();
+            subject.accumulate("name", examSubject.getName());
+            subject.accumulate("code", examSubject.getCode());
+            examSubject.setPaperAnswerUrl(fileService);
+            subject.accumulate("paperUrl", examSubject.getPaperUrl() == null ? "" : examSubject.getPaperUrl());
+            subject.accumulate("answerUrl", examSubject.getAnswerUrl() == null ? "" : examSubject.getAnswerUrl());
+            setting.accumulate("subject", subject);
         }
+        return setting;
     }
 
-    private void releaseTask(Integer taskId) {
-        synchronized (currentTaskMap) {
-            currentTaskMap.remove(taskId);
+    private double[] getSplitConfig() {
+        String strs[] = splitConfig.split(",");
+        double[] config = new double[strs.length];
+        for (int i = 0; i < strs.length; i++) {
+            config[i] = Double.parseDouble(strs[i]);
         }
+        return config;
     }
 
-    private void releaseByUser(Integer userId) {
-        Set<Integer> taskIds = new HashSet<>();
-        taskIds.addAll(currentTaskMap.keySet());
-        synchronized (currentTaskMap) {
-            for (Integer taskId : taskIds) {
-                Integer value = currentTaskMap.get(taskId);
-                if (value != null && value.equals(userId)) {
-                    currentTaskMap.remove(taskId);
-                }
-            }
+    private void releaseUser(Integer examId, String subjectCode, Integer userId) {
+        try {
+            lockService.waitlock(LockType.USER, userId);
+            inspectedService.releaseByUserId(examId, subjectCode, userId);
+        } catch (Exception e) {
+            log.error("release user error", e);
+        } finally {
+            lockService.unlock(LockType.USER, userId);
+        }
+    }
+
+    private void releaseStudent(Integer studentId) {
+        try {
+            lockService.waitlock(LockType.STUDENT, studentId);
+            ExamStudent student = studentService.findById(studentId);
+            inspectedService.releaseByStudent(student);
+        } catch (Exception e) {
+            log.error("release user error", e);
+        } finally {
+            lockService.unlock(LockType.STUDENT, studentId);
         }
     }
 }

+ 148 - 145
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/LibraryController.java

@@ -2,15 +2,10 @@ package cn.com.qmth.stmms.admin.exam;
 
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 
-import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 
 import org.apache.commons.lang.StringUtils;
@@ -19,13 +14,14 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
+import cn.com.qmth.stmms.admin.dto.RejectResult;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
@@ -41,16 +37,12 @@ import cn.com.qmth.stmms.biz.file.enums.FormatType;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
-import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
-import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
+import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
-import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
-import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.biz.utils.ScoreItem;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
@@ -98,13 +90,7 @@ public class LibraryController extends BaseExamController {
     private UserService userService;
 
     @Autowired
-    private MarkTrackService markTrackService;
-
-    @Autowired
-    private MarkSpecialTagService markSpecialTagService;
-
-    // 并发处理互斥锁
-    private Map<Integer, Integer> currentTaskMap = new HashMap<Integer, Integer>();
+    private TaskService taskService;
 
     @Logging(menu = "评卷任务查询", type = LogType.QUERY)
     @RequestMapping
@@ -125,9 +111,6 @@ public class LibraryController extends BaseExamController {
         }
         List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(examId, query.getSubjectCode(),
                 MarkStatus.FORMAL);
-        // if (groupList.isEmpty()) {
-        // return "redirect:/admin/exam/mark";
-        // }
         if (!groupList.isEmpty()) {
             if (query.getGroupNumber() == 0 && !groupList.isEmpty()) {
                 query.setGroupNumber(groupList.get(0).getNumber());
@@ -145,12 +128,15 @@ public class LibraryController extends BaseExamController {
                         group.getSubjectCode(), false, group.getNumber()));
             }
         }
-        MarkLibrarySearchQuery query2 = new MarkLibrarySearchQuery();
-        query2.setExamId(examId);
-        query2.setSubjectCode(query.getSubjectCode());
-        query2.setGroupNumber(query.getGroupNumber());
-        query2.addStatus(LibraryStatus.MARKED);
-        long inspectedCount = libraryService.countByQuery(query2);
+        if (query.getGroupNumber() != 0) {
+            MarkLibrarySearchQuery query2 = new MarkLibrarySearchQuery();
+            query2.setExamId(examId);
+            query2.setSubjectCode(query.getSubjectCode());
+            query2.setGroupNumber(query.getGroupNumber());
+            query2.addStatus(LibraryStatus.MARKED);
+            long inspectedCount = libraryService.countByQuery(query2);
+            model.addAttribute("inspectedCount", inspectedCount);
+        }
         model.addAttribute("query", query);
         model.addAttribute("subjectList", getExamSubject(examId, wu));
         model.addAttribute("groupList", groupList);
@@ -162,7 +148,6 @@ public class LibraryController extends BaseExamController {
             marker.setLoginName(userService.findById(marker.getUserId()).getLoginName());
         }
         model.addAttribute("markerList", markerList);
-        model.addAttribute("inspectedCount", inspectedCount);
         Exam exam = examService.findById(examId);
         model.addAttribute("examType", exam.getType());
         return "modules/exam/libraryList";
@@ -217,16 +202,18 @@ public class LibraryController extends BaseExamController {
             @RequestParam(required = false) Integer groupNumber) {
         int examId = getSessionExamId(request);
         ExamStudent student = studentService.findById(studentId);
+        List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjective(examId,
+                student.getSubjectCode(), false);
         if (groupNumber != null) {
-            List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
+            questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
                     student.getSubjectCode(), false, groupNumber);
-            List<String> strings = new ArrayList<String>();
-            for (ExamQuestion examQuestion : questions) {
-                strings.add(examQuestion.getQuestionNumber());
-            }
-            String questionNumbers = String.join(",", strings);
-            model.addAttribute("questionNumbers", questionNumbers);
         }
+        List<String> strings = new ArrayList<String>();
+        for (ExamQuestion examQuestion : questions) {
+            strings.add(examQuestion.getQuestionNumber());
+        }
+        String questionNumbers = String.join(",", strings);
+        model.addAttribute("questionNumbers", questionNumbers);
         model.addAttribute("fileServer", fileService.getFileServer());
         model.addAttribute("answerUrl", fileService.getJsonUri(student.getExamId(), student.getSecretNumber()));
         model.addAttribute("paperUrl",
@@ -235,158 +222,174 @@ public class LibraryController extends BaseExamController {
     }
 
     @Logging(menu = "开始任务复核", type = LogType.QUERY)
-    @RequestMapping("/inspected/start")
+    @RequestMapping(value = "/getTask", method = RequestMethod.POST)
     @ResponseBody
-    public ModelAndView start(HttpServletRequest request, RedirectAttributes redirectAttributes,
+    public Task getTask(HttpServletRequest request, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer groupNumber) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
-        WebUser wu = RequestUtils.getWebUser(request);
-        releaseByUser(wu.getUser().getId());
-        ModelAndView view = new ModelAndView("modules/exam/inspectedLibrary");
         if (group == null) {
-            view.addObject("message", "大题不存在或评卷已结束");
-            return view;
+            return null;
         } else if (group.getStatus() == MarkStatus.FINISH) {
-            view.addObject("message", "评卷已结束");
-            return view;
+            return null;
         } else if (group.getStatus() == MarkStatus.TRIAL) {
-            view.addObject("message", "试评任务无需复核");
-            return view;
-        } else {
-            List<Integer> ids = new ArrayList<Integer>();
+            return null;
+        }
+        Task task = null;
+        WebUser wu = RequestUtils.getWebUser(request);
+        int retry = 1;
+        while (task == null) {
             MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
             query.setExamId(examId);
             query.setSubjectCode(subjectCode);
             query.setGroupNumber(groupNumber);
-            query.setPageNumber(1);
-            query.setPageSize(1000);
+            query.setPageNumber(retry);
+            query.setPageSize(20);
             query.addStatus(LibraryStatus.MARKED);
             query = libraryService.findByQuery(query);
-            if (query.getResult() != null && query.getResult().size() > 0) {
-                for (MarkLibrary library : query.getResult()) {
-                    ids.add(library.getId());
+            if (query.getResult().isEmpty()) {
+                break;
+            }
+            for (MarkLibrary library : query.getResult()) {
+                if (libraryService.applyLibrary(library, wu.getId())) {
+                    task = taskService.build(library);
+                    break;
                 }
             }
-            view.addObject("inspectCount", ids.size());
-            view.addObject("fileServer", fileService.getFileServer());
-            view.addObject("ids", StringUtils.join(ids, ","));
-            view.addObject("message", ids.size() > 0 ? "" : "没有待复核的任务");
-            return view;
-        }
-    }
-
-    @RequestMapping("/inspected/info")
-    @ResponseBody
-    public Object info(HttpServletRequest request, @RequestParam Integer libraryId) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        MarkLibrary library = libraryService.findById(libraryId);
-        JSONObject result = new JSONObject();
-        if (setCurrent(library.getId(), wu.getUser().getId())) {
-            ExamStudent student = studentService.findById(library.getStudentId());
-            result.accumulate("id", libraryId);
-            result.accumulate("studentId", library.getSecretNumber());
-            result.accumulate("subjectCode", library.getSubjectCode());
-            result.accumulate("subjectName", student.getSubjectName());
-            Marker marker = markerService.findById(library.getMarkerId());
-            User user = userService.findById(marker.getUserId());
-            result.accumulate("markerName", user.getLoginName());
-            result.accumulate("markerScore", library.getMarkerScore());
-
-            JSONArray array = new JSONArray();
-            List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
-                    library.getExamId(), library.getSubjectCode(), false, library.getGroupNumber());
-            List<ScoreItem> scores = library.getScoreList();
-            for (int i = 0; i < questions.size(); i++) {
-                ExamQuestion question = questions.get(i);
-                JSONObject obj = new JSONObject();
-                obj.accumulate("questionNumber", question.getMainTitle() + " " + question.getQuestionNumber());
-                obj.accumulate("score", scores.get(i).getScore());
-                array.add(obj);
+            if (task == null) {
+                retry++;
             }
-            result.accumulate("questions", array);
-
-            MarkGroup group = groupService.findOne(student.getExamId(), student.getSubjectCode(),
-                    library.getGroupNumber());
-            List<String> picUrls = fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1,
-                    student.getSliceCount());
-            List<MarkTrack> markTracks = markTrackService.findByLibraryId(library.getId());
-            List<MarkSpecialTag> markSpecialTagList = markSpecialTagService.findByLibraryId(library.getId());
-            result.accumulate("picUrls", picUrls);
-            result.accumulate("pictureConfig", group.getPictureConfigList());
-            result.accumulate("markTracks", markTracks);
-            result.accumulate("markSpecialTagList", markSpecialTagList);
-            result.accumulate("success", true);
-        } else {
-            result.accumulate("success", false);
         }
-        return result;
+        return task;
     }
 
     @Logging(menu = "考生评卷复核", type = LogType.UPDATE)
-    @RequestMapping("/inspected/save")
+    @RequestMapping(value = "/inspected/save", method = RequestMethod.POST)
     @ResponseBody
     public Object save(HttpServletRequest request, @RequestParam Integer libraryId) {
         WebUser wu = RequestUtils.getWebUser(request);
         MarkLibrary library = libraryService.findById(libraryId);
-        if (libraryId != null && library.getStatus().equals(LibraryStatus.MARKED)) {
-            library.setHeaderId(wu.getUser().getId());
-            library.setHeaderTime(new Date());
-            library.setStatus(LibraryStatus.INSPECTED);
-            libraryService.save(library);
-            releaseTask(libraryId);
-            return true;
-        } else {
-            return false;
+        JSONObject obj = new JSONObject();
+        try {
+            if (libraryId != null && library.getStatus().equals(LibraryStatus.MARKED)
+                    && lockService.trylock(LockType.GROUP_LIBRARY, library.getId())
+                    && libraryService.hasApplied(library, wu.getId())) {
+                library.setHeaderId(wu.getUser().getId());
+                library.setHeaderTime(new Date());
+                library.setStatus(LibraryStatus.INSPECTED);
+                libraryService.save(library);
+                libraryService.releaseByLibrary(library);
+                obj.accumulate("success", true);
+            } else {
+                obj.accumulate("success", false);
+                obj.accumulate("message", "无法复核,请刷新页面");
+            }
+        } catch (Exception e) {
+            obj.accumulate("success", false);
+            obj.accumulate("message", "无法复核,请刷新页面!");
+            log.error("inspected library save error", e);
         }
+        return obj;
     }
 
-    @RequestMapping("/inspected/clear")
+    @RequestMapping(value = "/clear", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public Object clear(HttpServletRequest request, @RequestParam(required = false) Integer libraryId) {
+    public Object clear(HttpServletRequest request, @RequestParam String subjectCode,
+            @RequestParam Integer groupNumber, @RequestParam(required = false) Integer libraryId) {
         WebUser wu = RequestUtils.getWebUser(request);
+        JSONObject obj = new JSONObject();
         if (libraryId != null) {
-            releaseTask(libraryId);
+            releaseLibrary(libraryId);
+            obj.accumulate("success", true);
         } else {
-            releaseByUser(wu.getUser().getId());
+            int examId = getSessionExamId(request);
+            releaseUser(examId, subjectCode, groupNumber, wu.getUser().getId());
+            obj.accumulate("success", true);
         }
-        return true;
+        return obj;
     }
 
-    private boolean setCurrent(Integer taskId, Integer userId) {
-        Integer value = currentTaskMap.get(taskId);
-        if (value == null) {
-            synchronized (currentTaskMap) {
-                value = currentTaskMap.get(taskId);
-                if (value == null) {
-                    currentTaskMap.put(taskId, userId);
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-        } else {
-            return false;
+    @RequestMapping(value = "/getStatus", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject status(HttpServletRequest request, @RequestParam String subjectCode,
+            @RequestParam Integer groupNumber) {
+        JSONObject status = new JSONObject();
+        int examId = getSessionExamId(request);
+        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
+        query.setExamId(examId);
+        query.setSubjectCode(subjectCode);
+        query.setGroupNumber(groupNumber);
+        query.addStatus(LibraryStatus.MARKED);
+        long inspectedCount = libraryService.countByQuery(query);
+        status.accumulate("totalCount", inspectedCount);
+        status.accumulate("valid", true);
+        return status;
+    }
+
+    private void releaseUser(Integer examId, String subjectCode, Integer groupNumber, Integer userId) {
+        try {
+            lockService.waitlock(LockType.USER, userId);
+            libraryService.releaseByUserId(examId, subjectCode, groupNumber, userId);
+        } catch (Exception e) {
+            log.error("release user error", e);
+        } finally {
+            lockService.unlock(LockType.USER, userId);
         }
     }
 
-    private void releaseTask(Integer taskId) {
-        synchronized (currentTaskMap) {
-            currentTaskMap.remove(taskId);
+    private void releaseLibrary(Integer libraryId) {
+        try {
+            lockService.waitlock(LockType.GROUP_LIBRARY, libraryId);
+            MarkLibrary library = libraryService.findById(libraryId);
+            libraryService.releaseByLibrary(library);
+        } catch (Exception e) {
+            log.error("release user error", e);
+        } finally {
+            lockService.unlock(LockType.GROUP_LIBRARY, libraryId);
         }
     }
 
-    private void releaseByUser(Integer userId) {
-        Set<Integer> taskIds = new HashSet<>();
-        taskIds.addAll(currentTaskMap.keySet());
-        synchronized (currentTaskMap) {
-            for (Integer taskId : taskIds) {
-                Integer value = currentTaskMap.get(taskId);
-                if (value != null && value.equals(userId)) {
-                    currentTaskMap.remove(taskId);
+    @Logging(menu = "打回", type = LogType.UPDATE)
+    @RequestMapping(value = "/rejected", method = RequestMethod.POST)
+    @ResponseBody
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.INSPECTOR })
+    public JSONObject rejected(HttpServletRequest request, @RequestBody RejectResult rejectResult) {
+        JSONObject obj = new JSONObject();
+        WebUser wu = RequestUtils.getWebUser(request);
+        MarkLibrary library = libraryService.findById(rejectResult.getLibraryId());
+        if (library != null) {
+            if (subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
+                try {
+                    lockService.watch(LockType.EXAM_SUBJECT, library.getExamId(), library.getSubjectCode());
+                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
+                            library.getGroupNumber());
+                    if ((library.getStatus().equals(LibraryStatus.MARKED)
+                            || library.getStatus().equals(LibraryStatus.PROBLEM) || library.getStatus().equals(
+                            LibraryStatus.INSPECTED))
+                            && markService.rejectLibrary(library, rejectResult.getQuestionList(), wu.getId())) {
+                        obj.accumulate("success", true);
+                    } else {
+                        obj.accumulate("success", false);
+                        obj.accumulate("message", "无法打回该评卷任务");
+                    }
+                } catch (Exception e) {
+                    obj.accumulate("success", false);
+                    obj.accumulate("message", "打回评卷任务失败");
+                    log.error("back library error", e);
+                } finally {
+                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
+                            library.getGroupNumber());
+                    lockService.unwatch(LockType.EXAM_SUBJECT, library.getExamId(), library.getSubjectCode());
                 }
+            } else {
+                obj.accumulate("success", false);
+                obj.accumulate("message", "没有操作该评卷任务的权限");
             }
+        } else {
+            obj.accumulate("success", false);
+            obj.accumulate("message", "该评卷任务不存在");
         }
+        return obj;
     }
 }

+ 27 - 7
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkController.java

@@ -4,6 +4,7 @@ import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -81,8 +82,17 @@ public class MarkController extends BaseExamController {
         WebUser wu = RequestUtils.getWebUser(request);
         int examId = getSessionExamId(request);
         query.setExamId(examId);
-        // query.setUploadCountGt(0);
-        if (wu.isSubjectHeader() && StringUtils.isBlank(query.getCode())) {
+        Set<String> unFinishSet = libraryService.findSubjectUnFinishByExamId(examId);
+        unFinishSet.addAll(questionService.FindSubjectCodeByExamIdAndObjectiveAndGroupNumberIsNull(examId, false));
+        if (query.getFinished() != null) {
+            String subjectCodeIn = StringUtils.join(unFinishSet, ",");
+            if (query.getFinished()) {
+                query.setCodeNotIn(subjectCodeIn);
+            } else {
+                query.setCodeIn(subjectCodeIn);
+            }
+        }
+        if (wu.isSubjectHeader()) {
             String subjectCodeIn = StringUtils.join(wu.getSubjectCodeSet(), ",");
             query.setCodeIn(subjectCodeIn);
         }
@@ -105,13 +115,23 @@ public class MarkController extends BaseExamController {
             String percent = libraryCount > 0 ? (new DecimalFormat("####.###").format(markedCount * 100.0
                     / libraryCount) + "%") : "0%";
             vo.setPercent(percent);
+            long count = questionService.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(examId,
+                    subject.getCode(), false);
+            vo.setGroupFinish(count == 0);
             list.add(vo);
         }
-
-        double total = (double) groupService.sumLibraryCount(examId);
-        double finish = (double) groupService.sumMarkedCount(examId);
-        double percent = total > 0 ? (finish * 100 / total) : 0;
-        model.addAttribute("percent", new DecimalFormat("####.###").format(percent));
+        MarkLibrarySearchQuery mQuery = new MarkLibrarySearchQuery();
+        mQuery.setExamId(examId);
+        long total = libraryService.countByQuery(mQuery);
+        mQuery.addStatus(LibraryStatus.MARKED);
+        mQuery.addStatus(LibraryStatus.ARBITRATED);
+        mQuery.addStatus(LibraryStatus.INSPECTED);
+        long markedCount = libraryService.countByQuery(mQuery);
+        long subjectCount = subjectService.count(examId);
+        model.addAttribute("unMarkedCount", total - markedCount);
+        model.addAttribute("markedCount", markedCount);
+        model.addAttribute("unFinishCount", unFinishSet.size());
+        model.addAttribute("finishCount", subjectCount - unFinishSet.size());
         model.addAttribute("resultList", list);
         model.addAttribute("query", query);
         model.addAttribute("subjectList", getExamSubject(examId, wu));

+ 101 - 169
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkGroupController.java

@@ -1,22 +1,9 @@
 package cn.com.qmth.stmms.admin.exam;
 
-import cn.com.qmth.stmms.admin.dto.ExamQuestionDTO;
-import cn.com.qmth.stmms.admin.thread.MarkGroupDeleteThread;
-import cn.com.qmth.stmms.biz.exam.model.*;
-import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
-import cn.com.qmth.stmms.biz.exam.service.*;
-import cn.com.qmth.stmms.biz.file.service.FileService;
-import cn.com.qmth.stmms.biz.lock.LockService;
-import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
-import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.common.annotation.Logging;
-import cn.com.qmth.stmms.common.annotation.RoleRequire;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.*;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
+import java.util.ArrayList;
+import java.util.List;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import javax.servlet.http.HttpServletRequest;
 
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
@@ -37,9 +24,39 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
-import javax.servlet.http.HttpServletRequest;
+import cn.com.qmth.stmms.admin.thread.MarkGroupDeleteThread;
+import cn.com.qmth.stmms.biz.exam.model.Exam;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
+import cn.com.qmth.stmms.biz.mark.service.MarkService;
+import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.ExamType;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.MarkMode;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.enums.ScorePolicy;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+import cn.com.qmth.stmms.common.enums.ThirdPolicy;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
 
-import java.util.*;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 @Controller("markGroupController")
 @RequestMapping("/admin/exam/group")
@@ -47,8 +64,6 @@ public class MarkGroupController extends BaseExamController {
 
     protected static Logger log = LoggerFactory.getLogger(MarkGroupController.class);
 
-    private static final String NULL_PAPER_TYPE_PLACEHOLDER = "#";
-
     @Autowired
     private ExamSubjectService subjectService;
 
@@ -140,7 +155,7 @@ public class MarkGroupController extends BaseExamController {
 
     @Logging(menu = "大题数量校对", type = LogType.UPDATE)
     @RequestMapping("/check-count")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public String ckeckCount(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode) {
         int examId = getSessionExamId(request);
@@ -156,7 +171,7 @@ public class MarkGroupController extends BaseExamController {
 
     @Logging(menu = "大题任务回收", type = LogType.UPDATE)
     @RequestMapping("/release")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public String release(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number) {
         int examId = getSessionExamId(request);
@@ -176,7 +191,7 @@ public class MarkGroupController extends BaseExamController {
 
     @Logging(menu = "大题重置", type = LogType.UPDATE)
     @RequestMapping("/reset")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public String reset(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number) {
         int examId = getSessionExamId(request);
@@ -202,7 +217,7 @@ public class MarkGroupController extends BaseExamController {
 
     @Logging(menu = "大题状态修改", type = LogType.QUERY)
     @RequestMapping("/changeStatus")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public String changeStatus(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam MarkStatus status) {
         int examId = getSessionExamId(request);
@@ -240,7 +255,7 @@ public class MarkGroupController extends BaseExamController {
     }
 
     @RequestMapping("/add")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public String add(HttpServletRequest request, Model model, @RequestParam String subjectCode) {
         int examId = getSessionExamId(request);
         ExamSubject subject = subjectService.find(examId, subjectCode);
@@ -249,17 +264,20 @@ public class MarkGroupController extends BaseExamController {
         }
         MarkGroup group = new MarkGroup();
         group.setSubjectCode(subjectCode);
+        Integer number = groupService.findMaxNumberByExamIdAndSubjectCode(examId, subjectCode);
+        group.setNumber(number + 1);
         model.addAttribute("group", group);
+        model.addAttribute("subject", subject);
         model.addAttribute("markModeList", MarkMode.values());
         model.addAttribute("scorePolicyList", ScorePolicy.values());
         model.addAttribute("thirdPolicyList", ThirdPolicy.values());
-        Exam exam = examService.findById(examId);
-        model.addAttribute("examType", exam.getType());
+        model.addAttribute("exam", examService.findById(examId));
+        model.addAttribute("questionList", questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false));
         return "modules/exam/groupAdd";
     }
 
     @RequestMapping("/edit-simple")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public String editSimple(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number) {
         int examId = getSessionExamId(request);
@@ -278,8 +296,7 @@ public class MarkGroupController extends BaseExamController {
             model.addAttribute("markModeList", MarkMode.values());
             model.addAttribute("scorePolicyList", ScorePolicy.values());
             model.addAttribute("thirdPolicyList", ThirdPolicy.values());
-            Exam exam = examService.findById(examId);
-            model.addAttribute("examType", exam.getType());
+            model.addAttribute("exam", examService.findById(examId));
             return "modules/exam/groupEditSimple";
         } else {
             redirectAttributes.addAttribute("subjectCode", subjectCode);
@@ -288,38 +305,22 @@ public class MarkGroupController extends BaseExamController {
     }
 
     @RequestMapping("/edit-full")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public String editFull(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group != null) {
             group.setPicList(buildPictureConfig(group));
-            List<ExamQuestion> list = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
-                    subjectCode, false, number);
-            List<ExamQuestionDTO> questionList = new ArrayList<>();
-            Map<Integer, ExamQuestionDTO> mainMap = new HashMap<>();
-            for (ExamQuestion question : list) {
-                ExamQuestionDTO dto = mainMap.get(question.getMainNumber());
-                if (dto == null) {
-                    dto = new ExamQuestionDTO();
-                    dto.setMainNumber(question.getMainNumber());
-                    dto.setMainTitle(question.getMainTitle());
-                    dto.setSubNumber(question.getSubNumber());
-                    dto.setScoreList(new ArrayList<>());
-                    mainMap.put(question.getMainNumber(), dto);
-                    questionList.add(dto);
-                }
-                dto.getScoreList().add(question.getTotalScore());
-            }
+            List<ExamQuestion> list = questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false);
             model.addAttribute("group", group);
-            model.addAttribute("questionList", questionList);
+            model.addAttribute("questionList", list);
             model.addAttribute("pictureConfig", group.getPicList());
             model.addAttribute("markModeList", MarkMode.values());
             model.addAttribute("scorePolicyList", ScorePolicy.values());
             model.addAttribute("thirdPolicyList", ThirdPolicy.values());
-            Exam exam = examService.findById(examId);
-            model.addAttribute("examType", exam.getType());
+            model.addAttribute("subject", subjectService.find(examId, subjectCode));
+            model.addAttribute("exam", examService.findById(examId));
             return "modules/exam/groupEditFull";
         } else {
             redirectAttributes.addAttribute("subjectCode", subjectCode);
@@ -329,7 +330,7 @@ public class MarkGroupController extends BaseExamController {
 
     @Logging(menu = "删除大题", type = LogType.DELETE)
     @RequestMapping("/delete")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public String delete(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number) {
         int examId = getSessionExamId(request);
@@ -368,10 +369,10 @@ public class MarkGroupController extends BaseExamController {
 
     @Logging(menu = "修改大题", type = LogType.UPDATE)
     @SuppressWarnings("unchecked")
-    @RequestMapping("/save")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RequestMapping("/update")
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     @Transactional
-    public String save(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
+    public String update(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam Boolean reset,
             @RequestParam(required = false) String picList, @RequestParam(required = false) Double doubleRate,
             @RequestParam(required = false) Double arbitrateThreshold,
@@ -379,49 +380,18 @@ public class MarkGroupController extends BaseExamController {
             @RequestParam(required = false) MarkMode markMode, @RequestParam(required = false) Integer trialCount,
             @RequestParam(required = false, defaultValue = "false") Boolean sheetView,
             @RequestParam(required = false, defaultValue = "false") Boolean enableAllZero,
-            @RequestParam(required = false) String questionDetail,
+            @RequestParam(required = false) Integer[] questionIds,
             @RequestParam(required = false) String intervalScoreList) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group != null) {
             try {
-                if (questionDetail != null && reset.booleanValue()) {
+                if (questionIds != null && reset.booleanValue()) {
                     // advance update
-                    questionDetail = StringEscapeUtils.unescapeHtml(questionDetail);
-                    JSONArray array = JSONArray.fromObject(questionDetail);
-                    List<ExamQuestionDTO> detailList = JSONArray.toList(array, new ExamQuestionDTO(), new JsonConfig());
-                    for (int i = 0; i < detailList.size(); i++) {
-                        ExamQuestionDTO dto = detailList.get(i);
-                        Object scoreListArray[] = array.getJSONObject(i).getJSONArray("scoreList").toArray();
-                        List<Double> scoreList = new ArrayList<Double>();
-                        for (int j = 0; j < scoreListArray.length; j++) {
-                            scoreList.add(Double.parseDouble(scoreListArray[j].toString()));
-                        }
-                        dto.setScoreList(scoreList);
-                    }
-                    List<ExamQuestion> questionList = buildQuestionList(group, detailList);
-                    List<ExamQuestion> others = questionService.findByExamAndSubjectAndObjectiveAndGroupNumberNotEqual(
-                            examId, subjectCode, false, number);
-                    Set<String> numbers = new HashSet<>();
-                    Map<Integer, String> titles = new HashMap<>();
-                    for (ExamQuestion question : others) {
-                        numbers.add(question.getQuestionNumber());
-                        titles.put(question.getMainNumber(), question.getMainTitle());
-                    }
-                    for (ExamQuestion question : questionList) {
-                        if (numbers.contains(question.getQuestionNumber())) {
-                            addMessage(redirectAttributes, "题号不能重复");
-                            redirectAttributes.addAttribute("subjectCode", subjectCode);
-                            redirectAttributes.addAttribute("number", number);
-                            return "redirect:/admin/exam/group/edit-full";
-                        }
-                        if (titles.get(question.getMainNumber()) != null
-                                && !titles.get(question.getMainNumber()).equals(question.getMainTitle())) {
-                            addMessage(redirectAttributes, "大题名称不一致");
-                            redirectAttributes.addAttribute("subjectCode", subjectCode);
-                            redirectAttributes.addAttribute("number", number);
-                            return "redirect:/admin/exam/group/edit-full";
-                        }
+                    List<ExamQuestion> questionList = new ArrayList<ExamQuestion>();
+                    for (Integer questionId : questionIds) {
+                        ExamQuestion question = questionService.findById(questionId);
+                        questionList.add(question);
                     }
                     if (!questionList.isEmpty()) {
                         ScorePolicy policy = scorePolicy != null ? ScorePolicy.findByValue(scorePolicy) : null;
@@ -459,12 +429,8 @@ public class MarkGroupController extends BaseExamController {
                 if (list != null && !list.isEmpty()) {
                     groupService.updatePicList(examId, subjectCode, number, list);
                 }
-                if (doubleRate != null && doubleRate >= 0 && doubleRate <= 1) {
-                    groupService.updateDoubleRate(examId, subjectCode, number, doubleRate);
-                }
-                if (arbitrateThreshold != null && arbitrateThreshold >= 0) {
-                    groupService.updateArbitrateThreshold(examId, subjectCode, number, arbitrateThreshold);
-                }
+                groupService.updateDoubleRate(examId, subjectCode, number, doubleRate);
+                groupService.updateArbitrateThreshold(examId, subjectCode, number, arbitrateThreshold);
                 groupService.updateMarkMode(examId, subjectCode, number, markMode);
                 if (trialCount != null && trialCount > 0 && group.getStatus() == MarkStatus.TRIAL) {
                     groupService.updateTrialCount(examId, subjectCode, number, trialCount);
@@ -497,10 +463,10 @@ public class MarkGroupController extends BaseExamController {
     @Logging(menu = "新增大题", type = LogType.ADD)
     @SuppressWarnings("unchecked")
     @RequestMapping("/insert")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     @Transactional
     public String insert(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
-            @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam String questionDetail,
+            @RequestParam String subjectCode, @RequestParam Integer number, @RequestParam Integer[] questionIds,
             @RequestParam String picList, @RequestParam(required = false) Double doubleRate,
             @RequestParam(required = false) Double arbitrateThreshold,
             @RequestParam(required = false) Integer thirdPolicy, @RequestParam(required = false) Integer scorePolicy,
@@ -514,8 +480,8 @@ public class MarkGroupController extends BaseExamController {
             addMessage(redirectAttributes, "评卷分组序号不能重复");
             redirectAttributes.addAttribute("subjectCode", subjectCode);
             return "redirect:/admin/exam/group/add";
-        } else if (StringUtils.isBlank(questionDetail)) {
-            addMessage(redirectAttributes, "大题详情必须设置");
+        } else if (questionIds == null || questionIds.length <= 0) {
+            addMessage(redirectAttributes, "题目不能为空");
             redirectAttributes.addAttribute("subjectCode", subjectCode);
             return "redirect:/admin/exam/group/add";
         } else {
@@ -528,52 +494,21 @@ public class MarkGroupController extends BaseExamController {
                     JSONArray array = JSONArray.fromObject(picList);
                     picConfigList = JSONArray.toList(array, new PictureConfigItem(), new JsonConfig());
                 }
-                // build questionDetail
-                questionDetail = StringEscapeUtils.unescapeHtml(questionDetail);
-                JSONArray array = JSONArray.fromObject(questionDetail);
-                List<ExamQuestionDTO> detailList = JSONArray.toList(array, new ExamQuestionDTO(), new JsonConfig());
-                for (int i = 0; i < detailList.size(); i++) {
-                    ExamQuestionDTO dto = detailList.get(i);
-                    Object o[] = array.getJSONObject(i).getJSONArray("scoreList").toArray();
-                    List<Double> scoreList = new ArrayList<Double>();
-                    for (int j = 0; j < o.length; j++) {
-                        scoreList.add(Double.parseDouble(o[j].toString()));
+                if (questionIds != null && questionIds.length > 0) {
+                    List<ExamQuestion> list = new ArrayList<ExamQuestion>();
+                    double totalScore = 0d;
+                    for (Integer questionId : questionIds) {
+                        ExamQuestion question = questionService.findById(questionId);
+                        question.setGroupNumber(number);
+                        list.add(question);
+                        totalScore = totalScore + question.getTotalScore();
                     }
-                    dto.setScoreList(scoreList);
-                }
-                List<ExamQuestion> current = questionService.findByExamAndSubjectAndObjectiveAndGroupNumberNotEqual(
-                        examId, subjectCode, false, number);
-                Set<String> numbers = new HashSet<>();
-                Map<Integer, String> titles = new HashMap<>();
-                for (ExamQuestion question : current) {
-                    numbers.add(question.getQuestionNumber());
-                    titles.put(question.getMainNumber(), question.getMainTitle());
-                }
-                if (detailList != null && detailList.size() > 0) {
-                    group = new MarkGroup(examId, subjectCode, number, picConfigList, 0d, doubleRate,
+                    group = new MarkGroup(examId, subjectCode, number, picConfigList, totalScore, doubleRate,
                             arbitrateThreshold, scorePolicy, markMode, trialCount, sheetView, enableAllZero,
                             thirdPolicy);
-                    List<ExamQuestion> list = buildQuestionList(group, detailList);
-                    for (ExamQuestion question : list) {
-                        if (numbers.contains(question.getQuestionNumber())) {
-                            addMessage(redirectAttributes, "题号不能重复");
-                            redirectAttributes.addAttribute("subjectCode", subjectCode);
-                            return "redirect:/admin/exam/group/add";
-                        }
-                        if (titles.get(question.getMainNumber()) != null
-                                && !titles.get(question.getMainNumber()).equals(question.getMainTitle())) {
-                            addMessage(redirectAttributes, "大题名称不一致");
-                            redirectAttributes.addAttribute("subjectCode", subjectCode);
-                            return "redirect:/admin/exam/group/add";
-                        }
-                    }
                     // clear and replace exam_question
-                    questionService
-                            .deleteByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number);
                     questionService.save(list);
                     groupService.save(group);
-                    subjectService.updateScore(examId, subjectCode, false,
-                            groupService.sumTotalScore(examId, subjectCode));
                     studentService.updateSubjectiveStatusAndScoreAndInspectorId(examId, subjectCode,
                             SubjectiveStatus.UNMARK, 0, null, null, null);
                     redirectAttributes.addAttribute("subjectCode", subjectCode);
@@ -593,7 +528,6 @@ public class MarkGroupController extends BaseExamController {
     }
 
     @RequestMapping("/getPictureConfig")
-    @RoleRequire(Role.SCHOOL_ADMIN)
     public String get(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number) {
         int examId = getSessionExamId(request);
@@ -635,32 +569,6 @@ public class MarkGroupController extends BaseExamController {
         return fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1, student.getSliceCount());
     }
 
-    private List<ExamQuestion> buildQuestionList(MarkGroup group, List<ExamQuestionDTO> detailList) {
-        List<ExamQuestion> list = new LinkedList<>();
-        double totalScore = 0d;
-        for (ExamQuestionDTO detail : detailList) {
-            int i = 0;
-            for (double score : detail.getScoreList()) {
-                totalScore += score;
-                ExamQuestion question = new ExamQuestion();
-                question.setExamId(group.getExamId());
-                question.setSubjectCode(group.getSubjectCode());
-                question.setPaperType(NULL_PAPER_TYPE_PLACEHOLDER);
-                question.setMainTitle(detail.getMainTitle());
-                question.setMainNumber(detail.getMainNumber());
-                question.setSubNumber(detail.getSubNumber() + i);
-                question.setGroupNumber(group.getNumber());
-                question.setObjective(false);
-                question.setTotalScore(score);
-                question.setIntervalScore(1d);
-                list.add(question);
-                i++;
-            }
-        }
-        group.setTotalScore(totalScore);
-        return list;
-    }
-
     private List<Double> buildDoubleList(String content) {
         List<Double> list = new ArrayList<Double>();
         content = StringUtils.trimToNull(content);
@@ -681,4 +589,28 @@ public class MarkGroupController extends BaseExamController {
         }
         return list;
     }
+
+    @Logging(menu = "大题关闭", type = LogType.QUERY)
+    @RequestMapping("/finish")
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
+    public String finish(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
+            @RequestParam String subjectCode, @RequestParam Integer[] groupNumbers) {
+        int examId = getSessionExamId(request);
+        for (Integer number : groupNumbers) {
+            MarkGroup group = groupService.findOne(examId, subjectCode, number);
+            if (group == null) {
+                continue;
+            }
+            try {
+                lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+                if (group.getStatus() == MarkStatus.FORMAL && group.getLeftCount() == 0) {
+                    groupService.updateStatus(examId, subjectCode, number, MarkStatus.FINISH, group.getStatus());
+                }
+            } finally {
+                lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+            }
+        }
+        redirectAttributes.addAttribute("subjectCode", subjectCode);
+        return "redirect:/admin/exam/group";
+    }
 }

+ 31 - 65
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkQualityController.java

@@ -1,12 +1,36 @@
 package cn.com.qmth.stmms.admin.exam;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
 import cn.com.qmth.stmms.admin.vo.MarkerVO;
-import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.Marker;
 import cn.com.qmth.stmms.biz.exam.query.MarkerSearchQuery;
-import cn.com.qmth.stmms.biz.exam.service.*;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.Task;
@@ -22,29 +46,13 @@ import cn.com.qmth.stmms.biz.user.service.UserService;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.*;
+import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
+import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.core.task.AsyncTaskExecutor;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.servlet.mvc.support.RedirectAttributes;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 @Controller("markQualityController")
 @RequestMapping("/admin/exam/quality")
 public class MarkQualityController extends BaseExamController {
@@ -79,9 +87,6 @@ public class MarkQualityController extends BaseExamController {
     @Autowired
     private TrialService trialService;
 
-    @Autowired
-    private ExamStudentService studentService;
-
     @Autowired
     private ExamService examService;
 
@@ -224,37 +229,6 @@ public class MarkQualityController extends BaseExamController {
     }
 
     @Logging(menu = "质量监控查询给分记录详情", type = LogType.QUERY)
-    @RequestMapping("/batchProcess")
-    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public String batchProcess(Model model, HttpServletRequest request, @RequestParam Integer markerId,
-            @RequestParam Double markerScore) {
-        int examId = getSessionExamId(request);
-        Marker marker = markerService.findById(markerId);
-        WebUser wu = RequestUtils.getWebUser(request);
-        if (!subjectCheck(marker.getSubjectCode(), wu)) {
-            return "redirect:/admin/exam/mark";
-        }
-        MarkGroup group = groupService.findOne(examId, marker.getSubjectCode(), marker.getGroupNumber());
-        if (group == null) {
-            return "redirect:/admin/exam/mark";
-        }
-        ExamSubject subject = subjectService.find(group.getExamId(), group.getSubjectCode());
-        if (subject == null) {
-            return "redirect:/admin/exam/mark";
-        }
-        subject.setPaperAnswerUrl(fileService);
-        model.addAttribute("fileServer", fileService.getFileServer());
-        model.addAttribute("subject", subject);
-        model.addAttribute("group", group);
-        model.addAttribute("markerId", markerId);
-        model.addAttribute("markerScore", markerScore);
-        Exam exam = examService.findById(examId);
-        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
-            return "modules/exam/qualityProcessJson";
-        }
-        return "modules/exam/qualityProcess";
-    }
-
     @RequestMapping(value = "/history", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
@@ -283,11 +257,6 @@ public class MarkQualityController extends BaseExamController {
             query.orderByMarkerTimeDesc();
 
             list = taskService.findByQuery(query);
-            for (Task task : list) {
-                task.setPrevious(true);
-                task.setStudentName(studentService.findById(task.getStudentId()).getName());
-                task.setMarkerId(markerId);
-            }
         } else if (group != null && group.getStatus() == MarkStatus.TRIAL) {
             // 试评查找给分历史记录
             List<TrialHistory> historyList = new ArrayList<TrialHistory>();
@@ -297,9 +266,6 @@ public class MarkQualityController extends BaseExamController {
                 TrialLibrary library = trialService.findLibrary(history.getLibraryId());
                 if (library != null) {
                     Task task = taskService.build(library, history);
-                    task.setPrevious(true);
-                    task.setStudentName(studentService.findById(task.getStudentId()).getName());
-                    task.setMarkerId(markerId);
                     list.add(task);
                 }
             }

+ 59 - 45
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkTrackController.java

@@ -1,5 +1,22 @@
 package cn.com.qmth.stmms.admin.exam;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import net.sf.json.JSONObject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
@@ -9,30 +26,14 @@ import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
 import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
+import cn.com.qmth.stmms.biz.mark.model.SpecialTagDTO;
+import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
 import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
+import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.utils.OriginTag;
 
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.servlet.ModelAndView;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
 @Controller("trackController")
 @RequestMapping("/admin/exam/track")
 public class MarkTrackController extends BaseExamController {
@@ -60,36 +61,41 @@ public class MarkTrackController extends BaseExamController {
     @Autowired
     private FileService fileService;
 
-    @RequestMapping("/student/{studentId}")
-    public ModelAndView view(@PathVariable Integer studentId) {
-        ModelAndView view = new ModelAndView("modules/exam/studentTrack");
-        view.addObject("fileServer", fileService.getFileServer());
+    @Autowired
+    private TaskService taskService;
+
+    @Value("${slice.split.config}")
+    private String splitConfig;
+
+    @ResponseBody
+    @RequestMapping("/student")
+    public JSONObject studentTrack(@RequestParam Integer studentId) {
+        JSONObject map = new JSONObject();
+        map.accumulate("fileServer", fileService.getFileServer());
         ExamStudent student = studentService.findById(studentId);
+
         if (student != null && student.isUpload()) {
             List<String> sliceUrls = fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1,
                     student.getSliceCount());
-            view.addObject("urls", StringUtils.join(sliceUrls, ","));
-
-            // JSONArray result = new JSONArray();
+            map.accumulate("sliceUrls", sliceUrls);
             Map<MarkGroup, List<OriginTag>> maps = studentService.getSliceTags(student, false);
             List<OriginTag> tags = new ArrayList<OriginTag>();
             for (Entry<MarkGroup, List<OriginTag>> entry : maps.entrySet()) {
-                // MarkGroup group = entry.getKey();
-                // JSONObject item = new JSONObject();
-                // item.accumulate("config", group.getPictureConfigList());
-                // item.accumulate("tags", entry.getValue());
-                // result.add(item);
                 tags.addAll(entry.getValue());
             }
-            ObjectMapper mapper = new ObjectMapper();
-            try {
-                view.addObject("tags", mapper.writeValueAsString(tags));
-            } catch (JsonProcessingException e) {
-                e.printStackTrace();
-                log.error("MarkTrackController-轨迹坐标获取出错", e);
+            List<SpecialTagDTO> list = new ArrayList<SpecialTagDTO>();
+            for (OriginTag originTag : tags) {
+                SpecialTagDTO dto = new SpecialTagDTO();
+                dto.setOffsetIndex(originTag.getOffsetIndex());
+                dto.setOffsetX(originTag.getOffsetX());
+                dto.setOffsetY(originTag.getOffsetY());
+                dto.setTagName(originTag.getContent());
+                list.add(dto);
             }
+            map.accumulate("tagList", list);
+            map.accumulate("examNumber", student.getExamNumber());
         }
-        return view;
+        return map;
     }
 
     /**
@@ -115,19 +121,27 @@ public class MarkTrackController extends BaseExamController {
     }
 
     @ResponseBody
-    @RequestMapping("/byLibrary")
-    public HashMap<String, Object> byLibrary(Integer libraryId) {
+    @RequestMapping("/library")
+    public HashMap<String, Object> byLibrary(@RequestParam Integer libraryId) {
         MarkLibrary library = libraryService.findById(libraryId);
-        ExamStudent examStudent = studentService.findById(library.getStudentId());
-        List<Object> list = new ArrayList<Object>();
         HashMap<String, Object> map = new HashMap<String, Object>();
-        HashMap<String, Object> groups = set(library, examStudent);
-        list.add(groups);
-        map.put("list", list);
+        Task task = taskService.build(library);
+        map.put("task", task);
+        map.put("groupNumber", library.getGroupNumber());
         map.put("fileServer", fileService.getFileServer());
+        map.put("splitConfig", getSplitConfig());
         return map;
     }
 
+    private double[] getSplitConfig() {
+        String strs[] = splitConfig.split(",");
+        double[] config = new double[strs.length];
+        for (int i = 0; i < strs.length; i++) {
+            config[i] = Double.parseDouble(strs[i]);
+        }
+        return config;
+    }
+
     private HashMap<String, Object> set(MarkLibrary library, ExamStudent student) {
         HashMap<String, Object> groups = new HashMap<String, Object>();
         MarkGroup group = groupService.findOne(student.getExamId(), student.getSubjectCode(), library.getGroupNumber());

+ 6 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkerController.java

@@ -21,9 +21,13 @@ import cn.com.qmth.stmms.common.utils.EncryptUtils;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.ImportExcel;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
+
 import com.google.common.collect.Lists;
+
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,6 +42,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.util.*;
 
 @Controller("examMarkerController")
@@ -312,6 +317,7 @@ public class MarkerController extends BaseExamController {
         User user = userService.findById(marker.getUserId());
         JSONObject obj = new JSONObject();
         if (user != null) {
+            password = StringEscapeUtils.unescapeHtml(password);
             user.setPassword(EncryptUtils.md5(password));
             user = userService.save(user);
             obj.accumulate("success", true);

+ 205 - 53
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java

@@ -1,5 +1,38 @@
 package cn.com.qmth.stmms.admin.exam;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
 import cn.com.qmth.stmms.admin.dto.ObjectiveQuestionDTO;
 import cn.com.qmth.stmms.admin.dto.QuestionDTO;
 import cn.com.qmth.stmms.admin.dto.SubjectQuestionDTO;
@@ -10,39 +43,29 @@ import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
-import cn.com.qmth.stmms.biz.exam.service.*;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamSubjectSearchQuery;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.report.service.ReportService;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.*;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.ObjectivePolicy;
+import cn.com.qmth.stmms.common.enums.ObjectiveStatus;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.ImportExcel;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.core.task.AsyncTaskExecutor;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort.Direction;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.mvc.support.RedirectAttributes;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import java.util.*;
 
 @Controller("examPaperController")
 @RequestMapping("/admin/exam/paper")
@@ -76,6 +99,9 @@ public class PaperController extends BaseExamController {
     @Autowired
     private ReportService reportService;
 
+    @Autowired
+    private MarkService markService;
+
     @Logging(menu = "试卷管理查询", type = LogType.QUERY)
     @RequestMapping
     public String list(Model model, HttpServletRequest request, ExamSubjectSearchQuery query,
@@ -118,10 +144,42 @@ public class PaperController extends BaseExamController {
         if (subject != null) {
             List<ExamQuestion> list = new LinkedList<ExamQuestion>();
             if (objective == null || objective) {
-                list.addAll(questionService.findByExamAndSubjectAndObjective(examId, subjectCode, true));
+                List<ExamQuestion> objectiveList = questionService.findByExamAndSubjectAndObjective(examId,
+                        subjectCode, true);
+
+                Collections.sort(objectiveList, new Comparator<ExamQuestion>() {
+
+                    @Override
+                    public int compare(ExamQuestion o1, ExamQuestion o2) {
+                        int i = o1.getPaperType().compareTo(o2.getPaperType());
+                        if (i == 0) {
+                            i = o1.getMainNumber() - o2.getMainNumber();
+                        }
+                        if (i == 0) {
+                            return Integer.parseUnsignedInt(o1.getSubNumber())
+                                    - Integer.parseUnsignedInt(o2.getSubNumber());
+                        }
+                        return i;
+                    }
+                });
+                list.addAll(objectiveList);
             }
             if (objective == null || !objective) {
-                list.addAll(questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false));
+                List<ExamQuestion> subjectiveList = questionService.findByExamAndSubjectAndObjective(examId,
+                        subjectCode, false);
+                Collections.sort(subjectiveList, new Comparator<ExamQuestion>() {
+
+                    @Override
+                    public int compare(ExamQuestion o1, ExamQuestion o2) {
+                        int i = o1.getMainNumber() - o2.getMainNumber();
+                        if (i == 0) {
+                            return Integer.parseUnsignedInt(o1.getSubNumber())
+                                    - Integer.parseUnsignedInt(o2.getSubNumber());
+                        }
+                        return i;
+                    }
+                });
+                list.addAll(subjectiveList);
             }
             model.addAttribute("questionList", list);
             model.addAttribute("subject", subject);
@@ -210,7 +268,7 @@ public class PaperController extends BaseExamController {
                 if (subject != null) {
                     Map<Integer, String> titleMap = new HashMap<>();
                     List<ExamQuestion> current = questionService.findByExamAndSubjectAndObjective(examId,
-                            subject.getCode(), false);
+                            subject.getCode(), objective);
                     for (ExamQuestion question : current) {
                         titleMap.put(question.getMainNumber(), question.getMainTitle());
                     }
@@ -222,37 +280,33 @@ public class PaperController extends BaseExamController {
                             }
                             subjectService.updateScore(examId, subject.getCode(), objective, dto.getTotalScore());
                         } else {
-                            for (MarkGroup group : dto.getGroups().values()) {
-                                MarkGroup old = groupService.findOne(examId, group.getSubjectCode(), group.getNumber());
-                                // 已存在的评卷分组跳过
-                                if (old == null && group.getImportQuestionList() != null) {
-                                    boolean validate = true;
-                                    for (ExamQuestion question : group.getImportQuestionList()) {
-                                        if (questionService.countByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(
-                                                question.getExamId(), question.getSubjectCode(),
-                                                question.isObjective(), question.getMainNumber(),
-                                                question.getSubNumber()) > 0) {
-                                            error.add("[" + group.getSubjectCode() + "_" + group.getNumber()
-                                                    + "] 有小题已存在");
-                                            validate = false;
-                                            break;
-                                        }
-                                    }
-                                    // 校验通过小题才保存
-                                    if (validate && !group.getImportQuestionList().isEmpty()) {
-                                        questionService.save(group.getImportQuestionList());
-                                    }
-                                    // 有题目的分组才保存
-                                    if (questionService.countByExamAndSubjectAndObjectiveAndGroupNumber(examId,
-                                            group.getSubjectCode(), objective, group.getNumber()) != 0) {
-                                        groupService.save(group);
-                                        studentService.updateSubjectiveStatusAndScoreAndInspectorId(examId,
-                                                group.getSubjectCode(), SubjectiveStatus.UNMARK, 0, null, null, null);
+                            int questionCount = 0;
+                            for (ExamQuestion question : dto.getQuestionList()) {
+                                ExamQuestion old = questionService
+                                        .findByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(examId,
+                                                subject.getCode(), objective, question.getMainNumber(),
+                                                question.getSubNumber());
+                                if (old != null) {
+                                    if (old.getGroupNumber() != null) {
+                                        error.add("[" + subject.getCode() + "] 大题号" + question.getMainNumber() + " 小题号"
+                                                + question.getSubNumber() + "已有分组");
+                                        continue;
                                     }
+                                    old.setTotalScore(question.getTotalScore());
+                                    old.setIntervalScore(question.getIntervalScore());
+                                    questionService.save(old);
+                                } else {
+                                    question.setGroupNumber(null);
+                                    questionService.save(question);
+                                    questionCount++;
                                 }
                             }
+                            if (questionCount > 0) {
+                                studentService.updateSubjectiveStatusAndScoreAndInspectorId(examId, subject.getCode(),
+                                        SubjectiveStatus.UNMARK, 0, null, null, null);
+                            }
                             subjectService.updateScore(examId, subject.getCode(), objective,
-                                    groupService.sumTotalScore(examId, dto.getSubjectCode()));
+                                    questionService.sumTotalScore(examId, subject.getCode(), objective));
                         }
                         success++;
                     }
@@ -273,6 +327,89 @@ public class PaperController extends BaseExamController {
         return "redirect:/admin/exam/paper";
     }
 
+    @Logging(menu = "导入主观题分组", type = LogType.IMPORT_FILE)
+    @RequestMapping(value = "/importGroup", method = RequestMethod.POST)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String importGroupFile(HttpServletRequest request, MultipartFile file, RedirectAttributes redirectAttributes) {
+        int examId = getSessionExamId(request);
+        List<String> error = new LinkedList<String>();
+        Map<String, SubjectQuestionDTO> map = parseQuestion(file, examId, false, error);
+        if (error.isEmpty()) {
+            int success = 0;
+            for (SubjectQuestionDTO dto : map.values()) {
+                // 每次导入都需要重新统分
+                examService.updateObjectiveStatus(examId, ObjectiveStatus.WAITING);
+                ExamSubject subject = subjectService.find(examId, dto.getSubjectCode());
+                if (subject != null) {
+                    // 验证是否存在分组
+                    List<MarkGroup> groups = groupService.findByExamAndSubject(examId, subject.getCode());
+                    if (groups != null && groups.size() > 0) {
+                        error.add("[" + subject.getCode() + "] 已存在分组");
+                        continue;
+                    }
+                    Map<Integer, String> titleMap = new HashMap<>();
+                    List<ExamQuestion> current = questionService.findByExamAndSubjectAndObjective(examId,
+                            subject.getCode(), false);
+                    for (ExamQuestion question : current) {
+                        titleMap.put(question.getMainNumber(), question.getMainTitle());
+                    }
+                    if (dto.validate(error, titleMap) && dto.validateGroupNumber(error)
+                            && dto.getQuestionList().size() == current.size()) {
+                        int successGroup = 0;
+                        for (MarkGroup group : dto.getGroups().values()) {
+                            if (group.getImportQuestionList() != null) {
+                                boolean validate = true;
+                                List<ExamQuestion> questionGroup = new ArrayList<ExamQuestion>();
+                                for (ExamQuestion question : group.getImportQuestionList()) {
+                                    ExamQuestion old = questionService
+                                            .findByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(examId,
+                                                    subject.getCode(), false, question.getMainNumber(),
+                                                    question.getSubNumber());
+                                    if (old == null) {
+                                        error.add("[" + group.getSubjectCode() + "_" + group.getNumber() + "] 分组有小题不存在");
+                                        validate = false;
+                                        break;
+                                    }
+                                    old.setGroupNumber(group.getNumber());
+                                    questionGroup.add(old);
+                                }
+                                // 校验通过小题才保存
+                                if (validate && !questionGroup.isEmpty()) {
+                                    questionService.save(questionGroup);
+                                }
+                                // 有题目的分组才保存
+                                if (questionService.countByExamAndSubjectAndObjectiveAndGroupNumber(examId,
+                                        group.getSubjectCode(), false, group.getNumber()) != 0) {
+                                    double totalScore = 0d;
+                                    for (ExamQuestion q : questionGroup) {
+                                        totalScore = totalScore + q.getTotalScore();
+                                    }
+                                    group.setTotalScore(totalScore);
+                                    groupService.save(group);
+                                    studentService.updateSubjectiveStatusAndScoreAndInspectorId(examId,
+                                            group.getSubjectCode(), SubjectiveStatus.UNMARK, 0, null, null, null);
+                                    successGroup++;
+                                }
+                            }
+                        }
+                        if (successGroup > 0) {
+                            success++;
+                        }
+                    }
+                } else {
+                    error.add("[" + dto.getSubjectCode() + "] 科目代码不存在;");
+                }
+            }
+            error.add(0, success + "个科目导入成功;");
+            RequestUtils.setLog(request, success + "个科目导入成功;");
+        }
+        if (error.size() > 0) {
+            error.add("<br\\> 导入完毕后请重新统分!");
+            addMessage(redirectAttributes, StringUtils.join(error, "<br\\>"));
+        }
+        return "redirect:/admin/exam/paper";
+    }
+
     private Map<String, SubjectQuestionDTO> parseQuestion(MultipartFile file, int examId, boolean objective,
             List<String> error) {
         Map<String, SubjectQuestionDTO> map = new HashMap<String, SubjectQuestionDTO>();
@@ -331,9 +468,24 @@ public class PaperController extends BaseExamController {
         return "redirect:/admin/exam/paper/detail?subjectCode=" + question.getSubjectCode();
     }
 
+    @Logging(menu = "删除题目", type = LogType.DELETE)
+    @RequestMapping(value = "/question-delete/{questionId}", method = RequestMethod.GET)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String delete(HttpServletRequest request, RedirectAttributes redirectAttributes,
+            @PathVariable Integer questionId) {
+        ExamQuestion question = questionService.findById(questionId);
+        String subjectCode = question.getSubjectCode();
+        if (markService.deleteByQuestion(question)) {
+            addMessage(redirectAttributes, "删除成功");
+        } else {
+            addMessage(redirectAttributes, "删除失败,该题目已有分组");
+        }
+        return "redirect:/admin/exam/paper/detail?subjectCode=" + subjectCode;
+    }
+
     @Logging(menu = "客观题统分", type = LogType.UPDATE)
     @RequestMapping("/calculate")
-    @RoleRequire(Role.SCHOOL_ADMIN)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public ModelAndView calculate(HttpServletRequest request, RedirectAttributes redirectAttributes,
             @RequestParam(required = false) String subjectCode) {
         WebUser wu = RequestUtils.getWebUser(request);

+ 33 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/report/ReportSubjectQuestionController.java

@@ -2,6 +2,9 @@ package cn.com.qmth.stmms.admin.report;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
@@ -55,6 +58,22 @@ public class ReportSubjectQuestionController extends BaseExamController {
         query.setPageSize(Integer.MAX_VALUE);
         if (StringUtils.isNotBlank(query.getSubjectCode()) && query.getObjective() != null) {
             List<ReportSubjectQuestion> questions = reportSubjectQuestionService.findByQuery(query);
+            questions = new ArrayList<>(questions);
+            Collections.sort(questions, new Comparator<ReportSubjectQuestion>() {
+
+                @Override
+                public int compare(ReportSubjectQuestion o1, ReportSubjectQuestion o2) {
+                    int i = o1.getPaperType().compareTo(o2.getPaperType());
+                    if (i == 0) {
+                        i = o1.getMainNumber() - o2.getMainNumber();
+                    }
+                    if (i == 0) {
+                        return Integer.parseUnsignedInt(o1.getSubNumber())
+                                - Integer.parseUnsignedInt(o2.getSubNumber());
+                    }
+                    return i;
+                }
+            });
             model.addAttribute("list", questions);
             model.addAttribute("typeList", questionService.getPaperType(examId, query.getSubjectCode()));
         }
@@ -72,6 +91,20 @@ public class ReportSubjectQuestionController extends BaseExamController {
         query.setPageNumber(1);
         query.setPageSize(Integer.MAX_VALUE);
         List<ReportSubjectQuestion> list = reportSubjectQuestionService.findByQuery(query);
+        Collections.sort(list, new Comparator<ReportSubjectQuestion>() {
+
+            @Override
+            public int compare(ReportSubjectQuestion o1, ReportSubjectQuestion o2) {
+                int i = o1.getPaperType().compareTo(o2.getPaperType());
+                if (i == 0) {
+                    i = o1.getMainNumber() - o2.getMainNumber();
+                }
+                if (i == 0) {
+                    return Integer.parseUnsignedInt(o1.getSubNumber()) - Integer.parseUnsignedInt(o2.getSubNumber());
+                }
+                return i;
+            }
+        });
         for (ReportSubjectQuestion r : list) {
             r.setAvgScore(new BigDecimal(r.getAvgScore()).setScale(2, RoundingMode.HALF_UP).doubleValue());
             r.setStdev(new BigDecimal(r.getStdev()).setScale(2, RoundingMode.HALF_UP).doubleValue());

+ 146 - 41
stmms-web/src/main/java/cn/com/qmth/stmms/admin/report/ReportSubjectRangeController.java

@@ -1,27 +1,46 @@
 package cn.com.qmth.stmms.admin.report;
 
-import java.math.BigDecimal;
-import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.task.AsyncTaskExecutor;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 
+import cn.com.qmth.stmms.admin.dto.ReportSubjectRangeDTO;
 import cn.com.qmth.stmms.admin.exam.BaseExamController;
-import cn.com.qmth.stmms.biz.report.model.ReportSubjectRange;
-import cn.com.qmth.stmms.biz.report.query.ReportSubjectRangeQuery;
-import cn.com.qmth.stmms.biz.report.service.ReportSubjectRangeService;
+import cn.com.qmth.stmms.admin.thread.ScoreReportThread;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.biz.report.model.ReportSubject;
+import cn.com.qmth.stmms.biz.report.query.ReportSubjectQuery;
+import cn.com.qmth.stmms.biz.report.service.ReportService;
+import cn.com.qmth.stmms.biz.report.service.ReportSubjectService;
 import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 
@@ -38,58 +57,127 @@ public class ReportSubjectRangeController extends BaseExamController {
     protected static Logger log = LoggerFactory.getLogger(ReportSubjectRangeController.class);
 
     @Autowired
-    private ReportSubjectRangeService reportSubjectRangeService;
+    private ReportSubjectService reportSubjectService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private LockService lockService;
+
+    @Qualifier("task-executor")
+    @Autowired
+    private AsyncTaskExecutor taskExecutor;
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private ReportService reportService;
 
     @Logging(menu = "课程分段统计", type = LogType.QUERY)
     @RequestMapping
-    public String list(Model model, HttpServletRequest request, ReportSubjectRangeQuery query) {
-        WebUser webuser = RequestUtils.getWebUser(request);
+    public String list(Model model, HttpServletRequest request, ReportSubjectQuery query) {
+        WebUser wu = RequestUtils.getWebUser(request);
         int examId = getSessionExamId(request);
-        // reportSubjectRangeService.saveReportRangeSubjectData(1);
-        query.setExamId(examId);
-        if (webuser.isSubjectHeader()) {
-            // TODO - subjectheader check
-            //query.setSubjectCode(webuser.getUser().getSubjectCode());
+        if (StringUtils.isNotBlank(query.getSubjectCode()) && query.getRange() != null) {
+            ReportSubject subject = reportSubjectService.findOne(examId, query.getSubjectCode());
+            if (subject != null && subject.getScoreRange() != null && subject.getTotalScore() != null
+                    && subject.getRealityCount() != null) {
+                model.addAttribute(
+                        "total",
+                        getScoreRange(subject.getScoreRange(), subject.getTotalScore(), subject.getRealityCount(),
+                                query.getRange()));
+            }
+            model.addAttribute("locked", lockService.isLocked(LockType.SCORE_CALCULATE, examId, query.getSubjectCode()));
         }
-        query = reportSubjectRangeService.findByQuery(query);
-        if (query.getCurrentCount() > 0) {
-            model.addAttribute("list", query.getResult());
-        }
-        model.addAttribute("subjectList", getExamSubject(examId, webuser));
+        model.addAttribute("subjectList", getExamSubject(examId, wu));
         model.addAttribute("query", query);
         return "modules/report/reportSubjectRange";
     }
 
+    private JSONArray getScoreRange(String scoreRange, double totalScore, Integer totalCount, int range) {
+        JSONArray result = new JSONArray();
+        JSONObject jsonObject = JSONObject.fromObject(scoreRange);
+        int rangeCount = 0;
+        int sumCount = 0;
+        int total = (int) Math.ceil(totalScore / range);
+        for (int i = total; i >= 0; i--) {
+            int start = i * range;
+            int end = (i - 1) * range + 1;
+            if (start > totalScore) {
+                start = (int) Math.ceil(totalScore);
+            }
+            if (end < 0) {
+                end = 0;
+            }
+            rangeCount = getSumCount(jsonObject, start, end);
+            sumCount = sumCount + rangeCount;
+            result.add(getRangeJson(totalCount, rangeCount, sumCount, i * range));
+        }
+        return result;
+    }
+
+    private int getSumCount(JSONObject jsonObject, int start, int end) {
+        int sumCount = 0;
+        int currentCount = 0;
+        if (start < end) {
+            for (int i = start; i <= end; i++) {
+                currentCount = jsonObject.getInt(String.valueOf(i));
+                sumCount = sumCount + currentCount;
+            }
+        } else if (start >= end) {
+            for (int i = end; i <= start; i++) {
+                currentCount = jsonObject.getInt(String.valueOf(i));
+                sumCount = sumCount + currentCount;
+            }
+        } else {
+            sumCount = jsonObject.getInt(String.valueOf(start));
+        }
+        return sumCount;
+    }
+
+    private JSONObject getRangeJson(Integer totalCount, int rangeCount, int sumCount, int score) {
+        JSONObject value = new JSONObject();
+        value.accumulate("score", score);
+        value.accumulate("rangeCount", rangeCount);
+        value.accumulate("rangeRate", rangeCount * 100.0 / totalCount);
+        value.accumulate("sumCount", sumCount);
+        value.accumulate("sumRate", sumCount * 100.0 / totalCount);
+        return value;
+    }
+
     @Logging(menu = "课程分段统计导出", type = LogType.EXPORT)
     @RequestMapping("/export")
-    public String export(ReportSubjectRangeQuery query, HttpServletRequest request, HttpServletResponse response,
+    public String export(ReportSubjectQuery query, HttpServletRequest request, HttpServletResponse response,
             RedirectAttributes redirectAttributes) {
-        WebUser webuser = RequestUtils.getWebUser(request);
+        List<ReportSubjectRangeDTO> list = new ArrayList<ReportSubjectRangeDTO>();
         int examId = getSessionExamId(request);
-        if (webuser.isSubjectHeader()) {
-            // TODO - subjectheader check
-            //query.setSubjectCode(webuser.getUser().getSubjectCode());
-        }
-        query.setExamId(examId);
-        query.setPageNumber(1);
-        query.setPageSize(Integer.MAX_VALUE);
-        query = reportSubjectRangeService.findByQuery(query);
-        List<ReportSubjectRange> list = query.getResult();
-        for (ReportSubjectRange r : list) {
-            r.setPercent0_49(new BigDecimal(r.getPercent0_49() * 100).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            r.setPercent50_59(new BigDecimal(r.getPercent50_59() * 100).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            r.setPercent60_69(new BigDecimal(r.getPercent60_69() * 100).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            r.setPercent70_79(new BigDecimal(r.getPercent70_79() * 100).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            r.setPercent80_89(new BigDecimal(r.getPercent80_89() * 100).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            r.setPercent90_100(new BigDecimal(r.getPercent90_100() * 100).setScale(2, RoundingMode.HALF_UP)
-                    .doubleValue());
-            r.setPercent_lt60(new BigDecimal(r.getPercent_lt60() * 100).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            r.setPercent_mte60(new BigDecimal(r.getPercent_mte60() * 100).setScale(2, RoundingMode.HALF_UP)
-                    .doubleValue());
+        if (StringUtils.isNotBlank(query.getSubjectCode()) && query.getRange() != null) {
+            ReportSubject subject = reportSubjectService.findOne(examId, query.getSubjectCode());
+            if (subject != null && subject.getScoreRange() != null && subject.getTotalScore() != null
+                    && subject.getRealityCount() != null) {
+                JSONArray array = getScoreRange(subject.getScoreRange(), subject.getTotalScore(),
+                        subject.getRealityCount(), query.getRange());
+                for (int i = 0; i < array.size(); i++) {
+                    JSONObject jsonObject = array.getJSONObject(i);
+                    String score = jsonObject.getInt("score") + "-";
+                    Integer rangeCount = jsonObject.getInt("rangeCount");
+                    Double rangeRate = jsonObject.getDouble("rangeRate");
+                    list.add(new ReportSubjectRangeDTO(score, rangeCount, rangeRate));
+                }
+            }
         }
         String fileName = "课程分段统计.xlsx";
         try {
-            new ExportExcel("课程分段统计", ReportSubjectRange.class).setDataList(list).write(response, fileName).dispose();
+            new ExportExcel("课程分段统计", ReportSubjectRangeDTO.class).setDataList(list).write(response, fileName)
+                    .dispose();
             return null;
         } catch (Exception e) {
             addMessage(redirectAttributes, "导出成绩失败!" + e.getMessage());
@@ -97,4 +185,21 @@ public class ReportSubjectRangeController extends BaseExamController {
         }
     }
 
+    @Logging(menu = "客观题统分", type = LogType.UPDATE)
+    @RequestMapping("/report")
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
+    public String calculate(HttpServletRequest request, RedirectAttributes redirectAttributes,
+            @RequestParam String subjectCode) {
+        int examId = getSessionExamId(request);
+        Set<String> subjectSet = new HashSet<String>();
+        if (!lockService.isLocked(LockType.SCORE_CALCULATE, examId, subjectCode)) {
+            subjectSet.add(subjectCode);
+        }
+        if (!subjectSet.isEmpty()) {
+            ScoreReportThread thread = new ScoreReportThread(examId, subjectSet, lockService, studentService,
+                    questionService, reportService, examService, subjectService, false);
+            taskExecutor.submit(thread);
+        }
+        return "redirect:/admin/exam/reportSubjectRange?subjectCode=" + subjectCode + "&range=" + 10;
+    }
 }

+ 4 - 4
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/DataSyncThread.java

@@ -86,11 +86,11 @@ public class DataSyncThread implements Runnable {
         this.subjectService = subjectService;
         this.fileService = fileService;
         this.pageSize = pageSize;
-        this.subjectHttp = new HttpUtil(dataSync.getSubjectUrl(), dataSync.getSecretKey(), dataSync.getAppId(),
+        this.subjectHttp = new HttpUtil(dataSync.getSubjectUrl(), dataSync.getAccessKey(), dataSync.getAppId(),
                 dataSync.getRootOrgId());
-        this.studentHttp = new HttpUtil(dataSync.getStudentUrl(), dataSync.getSecretKey(), dataSync.getAppId(),
+        this.studentHttp = new HttpUtil(dataSync.getStudentUrl(), dataSync.getAccessKey(), dataSync.getAppId(),
                 dataSync.getRootOrgId());
-        this.subjectPaperHttp = new HttpUtil(dataSync.getSubjectPaperUrl(), dataSync.getSecretKey(),
+        this.subjectPaperHttp = new HttpUtil(dataSync.getSubjectPaperUrl(), dataSync.getAccessKey(),
                 dataSync.getAppId(), dataSync.getRootOrgId());
     }
 
@@ -99,7 +99,7 @@ public class DataSyncThread implements Runnable {
         log.info("start data sync for examId=" + dataSync.getExamId());
         try {
             JSONObject datas = new JSONObject();
-            datas.accumulate("examId", dataSync.getCloudExamId());
+            datas.accumulate("examId", Long.parseLong(dataSync.getCloudExamId()));
             String subjectResult = subjectHttp.httpAction(null, datas.toString());
             JSONObject subjectJson = JSONObject.fromObject(subjectResult);
             // 考试返回错误,无法创建阅卷

+ 298 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/OnlineExamThread.java

@@ -0,0 +1,298 @@
+package cn.com.qmth.stmms.admin.thread;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import cn.com.qmth.stmms.admin.utils.OnlineExamHttpUtil;
+import cn.com.qmth.stmms.biz.exam.model.DataSync;
+import cn.com.qmth.stmms.biz.exam.model.Exam;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.service.DataSyncService;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.file.enums.FormatType;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.MarkMode;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+
+import com.aliyun.oss.common.utils.BinaryUtil;
+
+public class OnlineExamThread implements Runnable {
+
+    protected static Logger log = LoggerFactory.getLogger(OnlineExamThread.class);
+
+    private DataSync dataSync;
+
+    private ExamStudentService studentService;
+
+    private ExamSubjectService subjectService;
+
+    private ExamQuestionService questionService;
+
+    private MarkGroupService groupService;
+
+    private ExamService examService;
+
+    private FileService fileService;
+
+    private LockService lockService;
+
+    private DataSyncService dataSyncService;
+
+    private OnlineExamHttpUtil studentHttp;
+
+    private OnlineExamHttpUtil subjectPaperHttp;
+
+    public static final String PAPER_STRUCT = "paperStructList";
+
+    public static final String ID = "id";
+
+    public static final String EXAM_ID = "examId";
+
+    public static final String PAPER_ID = "paperId";
+
+    public static final String COURSE_CODE = "courseCode";
+
+    public static final String COURSE_NAME = "courseName";
+
+    public static final String ANSWERS = "answers";
+
+    public static final String RECORDS = "records";
+
+    public static final String START_ID = "examStudentIdGt";
+
+    public static final String DEFAULT_NULL = "#";
+
+    public static final String COUNT = "count";
+
+    public static final String FILTER = "filter";
+
+    public OnlineExamThread(DataSync dataSync, LockService lockService, DataSyncService dataSyncService,
+            ExamService examService, ExamStudentService studentService, ExamSubjectService subjectService,
+            ExamQuestionService questionService, MarkGroupService groupService, FileService fileService) {
+        this.dataSync = dataSync;
+        this.lockService = lockService;
+        this.dataSyncService = dataSyncService;
+        this.examService = examService;
+        this.studentService = studentService;
+        this.subjectService = subjectService;
+        this.questionService = questionService;
+        this.groupService = groupService;
+        this.fileService = fileService;
+        this.studentHttp = new OnlineExamHttpUtil(dataSync.getAccessKey(), dataSync.getAccessSecret(),
+                dataSync.getStudentUrl());
+        this.subjectPaperHttp = new OnlineExamHttpUtil(dataSync.getAccessKey(), dataSync.getAccessSecret(),
+                dataSync.getSubjectPaperUrl());
+    }
+
+    @Override
+    public void run() {
+        log.info("start online data sync for examId=" + dataSync.getExamId());
+        try {
+            // 获取考试信息
+            Exam exam = examService.findById(dataSync.getExamId());
+            DataSync sync = dataSyncService.findByExamId(dataSync.getExamId());
+            if (sync == null) {
+                sync = dataSync;
+                sync.setCreateTime(new Date());
+                dataSyncService.save(sync);
+            }
+            Map<String, ExamSubject> subjectMap = new HashMap<String, ExamSubject>();
+            Map<String, Object> datas = new HashMap<String, Object>();
+            datas.put(EXAM_ID, dataSync.getCloudExamId());
+
+            // 获取考生
+            Long startId = 0L;
+            if (sync.getNextId() != null) {
+                startId = sync.getNextId();
+            }
+            while (startId != null) {
+                datas.put(START_ID, startId);
+                String studentResult = studentHttp.httpAction(null, datas);
+                JSONArray studentArray = JSONArray.fromObject(studentResult);
+                if (studentArray.size() == 0) {
+                    startId = null;
+                }
+                for (int i = 0; i < studentArray.size(); i++) {
+                    JSONObject student = studentArray.getJSONObject(i);
+                    String subjectName = student.getString(COURSE_NAME);
+                    JSONArray recordArray = student.getJSONArray(RECORDS);
+                    List<ExamStudent> list = new ArrayList<ExamStudent>();
+                    Date now = new Date();
+                    for (int j = 0; j < recordArray.size(); j++) {
+                        JSONObject record = recordArray.getJSONObject(j);
+                        // 保存科目试卷
+                        datas.put(ID, record.getString(PAPER_ID));
+                        datas.put(FILTER, "subjective");
+                        String paperResult = subjectPaperHttp.httpAction(null, datas);
+                        JSONObject paperJson = JSONObject.fromObject(paperResult);
+                        String subjectCode = paperJson.getString(ID);
+                        this.saveSubject(subjectMap, exam.getId(), subjectCode, subjectName, paperResult);
+                        // 保存考生数据
+                        ExamStudent examStudent = getExamStudent(exam, student, subjectCode, record.getString(ID), now);
+                        list.add(examStudent);
+                        // 保存考生作答
+                        String answerJson = record.getString(ANSWERS);
+                        byte[] jsonData = answerJson.getBytes(StandardCharsets.UTF_8);
+                        fileService.uploadJson(new ByteArrayInputStream(jsonData), BinaryUtil.encodeMD5(jsonData),
+                                exam.getId(), examStudent.getSecretNumber());
+                        studentService.save(examStudent);
+                        subjectService.updateUploadCount(exam.getId(), subjectCode,
+                                (int) studentService.countUploadedByExamIdAndSubjectCode(exam.getId(), subjectCode));
+                    }
+                    startId = student.getLong(ID);
+                }
+                sync.setUpdateTime(new Date());
+                sync.setNextId(startId);
+                dataSyncService.save(sync);
+            }
+
+            sync.setFinished(true);
+            dataSyncService.save(sync);
+        } catch (Exception e) {
+            log.error("data sync exception for examId=" + dataSync.getExamId(), e);
+        } finally {
+            lockService.unlock(LockType.DATA_SYNC, dataSync.getExamId());
+            log.info("finish data sync for examId=" + dataSync.getExamId());
+        }
+    }
+
+    private ExamStudent getExamStudent(Exam exam, JSONObject student, String paperCode, String examNumber, Date now) {
+        ExamStudent examStudent = new ExamStudent();
+        examStudent.setExamId(exam.getId());
+        examStudent.setStudentCode(student.getString("identity"));
+        examStudent.setName(student.getString("name"));
+        examStudent.setSubjectCode(paperCode);
+        examStudent.setSubjectName(student.getString(COURSE_NAME));
+        examStudent.setExamNumber(examNumber);
+        examStudent.setSecretNumber(examNumber);
+        examStudent.setCollege(DEFAULT_NULL);
+        examStudent.setClassName(DEFAULT_NULL);
+        examStudent.setTeacher(DEFAULT_NULL);
+        examStudent.setCampusName(DEFAULT_NULL);
+        examStudent.setPaperType(DEFAULT_NULL);
+        examStudent.setSchoolId(exam.getSchoolId());
+        examStudent.setAbsent(false);
+        examStudent.setUpload(true);
+        examStudent.setManualAbsent(false);
+        examStudent.setBreach(false);
+        examStudent.setException(false);
+        examStudent.setSliceCount(0);
+        examStudent.setSheetCount(0);
+        examStudent.setObjectiveScore(0d);
+        examStudent.setSubjectiveScore(0d);
+        examStudent.setSubjectiveStatus(SubjectiveStatus.UNMARK);
+        examStudent.setAnswers(null);
+        examStudent.setBatchCode(null);
+        examStudent.setUploadTime(now);
+        return examStudent;
+    }
+
+    private ExamSubject saveSubject(Map<String, ExamSubject> subjectMap, Integer examId, String subjectCode,
+            String subjectName, String paperResult) throws Exception {
+        ExamSubject subject = subjectMap.get(subjectCode);
+        if (subject != null) {
+            return subject;
+        }
+        subject = subjectService.find(examId, subjectCode);
+        if (subject != null) {
+            subjectMap.put(subjectCode, subject);
+            return subject;
+        }
+        // 保存paperjson
+        byte[] paperData = paperResult.getBytes(StandardCharsets.UTF_8);
+        fileService.uploadPaper(new ByteArrayInputStream(paperData), BinaryUtil.encodeMD5(paperData), examId,
+                subjectCode, FormatType.JSON);
+        // 保存subject
+        subject = new ExamSubject();
+        subject.setExamId(examId);
+        subject.setCode(subjectCode);
+        subject.setName(subjectName);
+        subject.setPaperFileType(FormatType.JSON);
+        subject.setAnswerFileType(FormatType.JSON);
+        subject.setObjectiveScore(0d);
+        subject.setSubjectiveScore(0d);
+        subject.setTotalScore(0d);
+        subject.setUploadCount(0);
+        subjectService.save(subject);
+        subjectMap.put(subjectCode, subject);
+        // 保存question
+        JSONObject paperJson = JSONObject.fromObject(paperResult);
+        JSONArray questionArray = JSONArray.fromObject(paperJson.getString("details"));
+        double totalScore = 0d;
+        List<ExamQuestion> list = new ArrayList<>();
+        for (int i = 0; i < questionArray.size(); i++) {
+            JSONObject questionJson = questionArray.getJSONObject(i);
+            int mainNumber = questionJson.getInt("number");
+            String mainTitle = questionJson.getString("name");
+            JSONArray qArray = questionJson.getJSONArray("questions");
+            for (int j = 0; j < qArray.size(); j++) {
+                JSONObject sub = qArray.getJSONObject(j);
+                if (sub.get("subQuestions") != null) {
+                    JSONArray subJson = sub.getJSONArray("subQuestions");
+                    for (int k = 0; k < subJson.size(); k++) {
+                        JSONObject question = subJson.getJSONObject(k);
+                        ExamQuestion q = new ExamQuestion();
+                        q.setExamId(examId);
+                        q.setSubjectCode(subjectCode);
+                        q.setPaperType("#");
+                        q.setObjective(false);
+                        q.setMainNumber(mainNumber);
+                        String subNumber = sub.getString("number") + "-" + question.getString("number");
+                        q.setSubNumber(subNumber);
+                        q.setMainTitle(mainTitle);
+                        q.setGroupNumber(1);
+                        double score = question.getDouble("score");
+                        q.setTotalScore(score);
+                        q.setIntervalScore(0.5);
+                        totalScore = totalScore + score;
+                        list.add(q);
+                    }
+                } else {
+                    ExamQuestion q = new ExamQuestion();
+                    q.setExamId(examId);
+                    q.setSubjectCode(subjectCode);
+                    q.setPaperType("#");
+                    q.setObjective(false);
+                    q.setMainNumber(mainNumber);
+                    String subNumber = sub.getString("number");
+                    q.setSubNumber(subNumber);
+                    q.setMainTitle(mainTitle);
+                    q.setGroupNumber(1);
+                    double score = sub.getDouble("score");
+                    q.setTotalScore(score);
+                    q.setIntervalScore(0.5);
+                    totalScore = totalScore + score;
+                    list.add(q);
+                }
+            }
+        }
+        questionService.save(list);
+        // 保存group
+        MarkGroup group = new MarkGroup(examId, subjectCode, 1, null, totalScore, 0d, null, null,
+                MarkMode.COMMON.toString(), 0, false, false, null);
+        groupService.save(group);
+
+        subjectService.updateScore(examId, subjectCode, false, totalScore);
+        return subject;
+    }
+}

+ 2 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreCalculateThread.java

@@ -1,5 +1,7 @@
 package cn.com.qmth.stmms.admin.thread;
 
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;

+ 2 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreReportThread.java

@@ -1,5 +1,7 @@
 package cn.com.qmth.stmms.admin.thread;
 
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;

+ 14 - 4
stmms-web/src/main/java/cn/com/qmth/stmms/admin/user/UserClassController.java

@@ -72,6 +72,7 @@ public class UserClassController extends BaseExamController {
             Map<String, User> userMap = new HashMap<String, User>();
             List<MarkerClass> saveList = new ArrayList<MarkerClass>();
             Map<Integer, List<String>> classMap = new HashMap<Integer, List<String>>();
+            Map<String, MarkerClass> markerClassMap = new HashMap<String, MarkerClass>();
             Integer schoolId = wu.getUser().getSchoolId();
             for (MarkerClass markerClass : list) {
                 if (markerClass.getExamId() == null) {
@@ -119,8 +120,11 @@ public class UserClassController extends BaseExamController {
                         user = getUser(userMap, markerClass.getLoginName().trim(), schoolId);
                     }
                 }
-                markerClass = getMarkerClass(user.getId(), markerClass.getExamId(), markerClass.getClassName());
-                saveList.add(markerClass);
+                markerClass = getMarkerClass(user.getId(), markerClass.getExamId(), markerClass.getClassName(),
+                        markerClassMap);
+                if (markerClass != null) {
+                    saveList.add(markerClass);
+                }
             }
             successNum = markerClassService.batchSave(saveList);
             if (failureNum > 0) {
@@ -136,11 +140,17 @@ public class UserClassController extends BaseExamController {
         return "redirect:/admin/user/list";
     }
 
-    private MarkerClass getMarkerClass(Integer userId, Integer examId, String className) {
-        MarkerClass m = markerClassService.findByUserIdAndExamIdAndClassName(userId, examId, className);
+    private MarkerClass getMarkerClass(Integer userId, Integer examId, String className,
+            Map<String, MarkerClass> markerClassMap) {
+        MarkerClass m = markerClassMap.get(userId + "_" + examId + "_" + className);
+        if (m != null) {
+            return null;
+        }
+        m = markerClassService.findByUserIdAndExamIdAndClassName(userId, examId, className);
         if (m == null) {
             m = new MarkerClass(userId, examId, className);
         }
+        markerClassMap.put(userId + "_" + examId + "_" + className, m);
         return m;
     }
 

+ 7 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/admin/user/UserController.java

@@ -12,6 +12,8 @@ import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.enums.UserSource;
 import cn.com.qmth.stmms.common.utils.EncryptUtils;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
@@ -22,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.servlet.ModelAndView;
 
 import javax.servlet.http.HttpServletRequest;
+
 import java.util.Date;
 import java.util.HashSet;
 import java.util.Set;
@@ -97,7 +100,8 @@ public class UserController extends BaseController {
         if (previous == null) {
             String message = validate(user, subjectCodeString);
             if (message == null) {
-                user.setPassword(EncryptUtils.md5(user.getPassword()));
+                String password = StringEscapeUtils.unescapeHtml(user.getPassword());
+                user.setPassword(EncryptUtils.md5(password));
                 user.setSchoolId(current.getSchoolId());
                 user.setSource(UserSource.INTERNAL);
                 user.setCreatedTime(new Date());
@@ -116,7 +120,8 @@ public class UserController extends BaseController {
             previous.setLoginName(user.getLoginName());
             previous.setName(user.getName());
             if (StringUtils.isNotBlank(user.getPassword())) {
-                previous.setPassword(EncryptUtils.md5(user.getPassword()));
+                String password = StringEscapeUtils.unescapeHtml(user.getPassword());
+                previous.setPassword(EncryptUtils.md5(password));
             }
             previous.setEnable(user.isEnable());
             String message = validate(previous, subjectCodeString);

+ 237 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/utils/OnlineExamHttpUtil.java

@@ -0,0 +1,237 @@
+package cn.com.qmth.stmms.admin.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import cn.com.qmth.stmms.common.signature.SignatureInfo;
+import cn.com.qmth.stmms.common.signature.SignatureType;
+
+public class OnlineExamHttpUtil {
+
+    private static Logger log = LoggerFactory.getLogger(OnlineExamHttpUtil.class);
+
+    /** 默认的编码格式 */
+    public static final String DEFAULT_CHARSET = "UTF-8";
+
+    public static final String CONTENT_TYPE = "Content-Type";
+
+    public static final String APPLICATION_JSON = "application/x-www-form-urlencoded;charset=utf-8";
+
+    public static final String METHOD_POST = "POST";
+
+    public static final String AUTH = "Authorization";
+
+    protected String uri = null;
+
+    protected String accessKey = null;
+
+    protected String accessSecret = null;
+
+    public OnlineExamHttpUtil(String accessKey, String accessSecret, String uri) {
+        this.accessKey = accessKey;
+        this.accessSecret = accessSecret;
+        this.uri = uri;
+    }
+
+    /**
+     * 
+     * @param params
+     *            headers参数
+     * @param datas
+     *            requestParams参数
+     * @return
+     */
+    public String httpAction(Map<String, String> params, Map<String, Object> datas) {
+        String result = null;
+        HttpsURLConnection conn = null;
+        OutputStream os = null;
+        InputStream is = null;
+
+        try {
+
+            // 获取链接
+            URL url = new URL(uri);
+            conn = (HttpsURLConnection) url.openConnection();
+
+            conn.setRequestMethod(METHOD_POST);
+            conn.setRequestProperty(CONTENT_TYPE, APPLICATION_JSON);
+            // 设置鉴权
+            long ss = System.currentTimeMillis();
+            String signature = SignatureInfo.build(SignatureType.SECRET, METHOD_POST, uri, ss, accessKey, accessSecret);
+            conn.setRequestProperty(AUTH, signature);
+            conn.setRequestProperty("time", String.valueOf(ss));
+            // ssl
+            SSLContext context = SSLContext.getInstance("SSL", "SunJSSE");
+            TrustManager[] tm = new TrustManager[] { new X509TrustManager() {
+
+                @Override
+                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                }
+
+                @Override
+                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                }
+
+                @Override
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+            } };
+            // 初始化
+            context.init(null, tm, new java.security.SecureRandom());
+            // 获取SSLSocketFactory对象
+            SSLSocketFactory ssf = context.getSocketFactory();
+            conn.setSSLSocketFactory(ssf);
+
+            conn.setUseCaches(false);
+            conn.setDoOutput(true);
+
+            // 设置额外的参数
+            if (params != null && !params.isEmpty()) {
+
+                for (Map.Entry<String, String> param : params.entrySet()) {
+                    conn.setRequestProperty(param.getKey(), param.getValue());
+                }
+            }
+            // 创建链接
+            conn.connect();
+            // 设置请求参数
+            if (datas != null) {
+                StringBuilder sb = new StringBuilder();
+                for (Map.Entry<String, Object> data : datas.entrySet()) {
+                    sb.append(data.getKey()).append("=").append(data.getValue()).append("&");
+                }
+                os = conn.getOutputStream();
+                os.write(sb.toString().getBytes());
+                os.flush();
+            }
+
+            result = getResult(conn);
+        } catch (Exception e) {
+            // 操作失败
+            log.error("OnlineExamHttpUtil connection error!", e);
+            e.printStackTrace();
+            return null;
+        } finally {
+            try {
+                if (os != null) {
+                    os.close();
+                    os = null;
+                }
+                if (is != null) {
+                    is.close();
+                    is = null;
+                }
+            } catch (IOException e) {
+                log.error("OnlineExamHttpUtil connection error!", e);
+            }
+
+            if (conn != null) {
+                conn.disconnect();
+                conn = null;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 获得连接请求的返回数据
+     * 
+     * @param conn
+     * 
+     * @return 字符串
+     */
+    private String getResult(HttpURLConnection conn) throws IOException {
+
+        StringBuilder text = new StringBuilder();
+
+        InputStream is = null;
+        InputStreamReader sr = null;
+        BufferedReader br = null;
+
+        int code = conn.getResponseCode();
+
+        try {
+            is = code >= 400 ? conn.getErrorStream() : conn.getInputStream();
+
+            sr = new InputStreamReader(is, DEFAULT_CHARSET);
+            br = new BufferedReader(sr);
+
+            char[] chars = new char[4096];
+            int length = 0;
+
+            while ((length = br.read(chars)) != -1) {
+                text.append(chars, 0, length);
+            }
+        } finally {
+            if (br != null) {
+                br.close();
+                br = null;
+            }
+            if (sr != null) {
+                sr.close();
+                sr = null;
+            }
+            if (is != null) {
+                is.close();
+                is = null;
+            }
+        }
+        if (code >= 400) {
+            throw new IOException(text.toString());
+        }
+        return text.toString();
+    }
+
+    public static void main(String[] args) throws IOException {
+        Map<String, Object> datas = new HashMap<String, Object>();
+        datas.put("examId", "99513313280946176");
+        String url = "/api/open/exam/course/query";
+        // OnlineExamHttpUtil subjectHttp = new OnlineExamHttpUtil("123456",
+        // "123", "https://m01.online-exam-test.cn", url);
+        // String subjectJson = subjectHttp.httpAction(null, datas);
+        // System.out.println(subjectJson);
+
+        datas.put("courseCode", "A0002");
+        OnlineExamHttpUtil studentHttp = new OnlineExamHttpUtil("123456", "123",
+                "https://m01.online-exam-test.cn/api/open/exam/record/need_mark");
+        String studentJson = studentHttp.httpAction(null, datas);
+        System.out.println(studentJson);
+
+        // datas.put("id", "62212585906769920");
+        // url = "https://test1.online-exam-test.cn/api/open/exam/paper/detail";
+        // OnlineExamHttpUtil paperHttp = new OnlineExamHttpUtil("123456",
+        // "123", url);
+        // String paperJson = paperHttp.httpAction(null, datas);
+        // System.out.println(paperJson);
+        // ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        // IOUtils.copy(new
+        // ByteArrayInputStream(subjectJson.getBytes(StandardCharsets.UTF_8)),
+        // buffer);
+        // byte[] data = buffer.toByteArray();
+        // File target = new File("/Users/ting.yin/Desktop/test123.json");
+        // target.getParentFile().mkdirs();
+        // FileOutputStream ous = new FileOutputStream(target);
+        // ous.write(data);
+        // IOUtils.closeQuietly(ous);
+    }
+}

+ 13 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/vo/SubjectLibraryVO.java

@@ -12,6 +12,11 @@ public class SubjectLibraryVO {
 
     private String percent;
 
+    /**
+     * 是否完成主观题分组
+     */
+    private boolean groupFinish;
+
     public ExamSubject getSubject() {
         return subject;
     }
@@ -44,4 +49,12 @@ public class SubjectLibraryVO {
         this.percent = percent;
     }
 
+    public boolean isGroupFinish() {
+        return groupFinish;
+    }
+
+    public void setGroupFinish(boolean groupFinish) {
+        this.groupFinish = groupFinish;
+    }
+
 }

+ 42 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/CoreController.java

@@ -4,10 +4,12 @@ import cn.com.qmth.stmms.api.exception.ApiException;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
@@ -55,12 +57,15 @@ public class CoreController extends BaseApiController {
     @Autowired
     private FileService fileService;
 
+    @Autowired
+    private ExamSubjectService subjectService;
+
     @RequestMapping(value = "/exam/save", method = RequestMethod.POST)
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCHOOL_DEV })
     public JSONObject examSave(HttpServletRequest request, @RequestParam(required = false) Integer id,
-            @RequestParam(required = false) String code, @RequestParam String name,
-            @RequestParam(required = false) String examTime) {
+            @RequestParam(required = false) String code, @RequestParam String name, @RequestParam String examTime,
+            @RequestParam String type) {
         ApiUser user = RequestUtils.getApiUser(request);
         JSONObject result = new JSONObject();
         // 输入字段预处理并初步校验
@@ -93,6 +98,7 @@ public class CoreController extends BaseApiController {
             current.setForbiddenInfo(false);
             current.setObjectiveStatus(ObjectiveStatus.WAITING);
             current.setCreateTime(new Date());
+            current.setType(ExamType.valueOf(type));
         } else if (!current.getSchoolId().equals(user.getSchoolId()) || current.getStatus() != ExamStatus.START) {
             throw ApiException.EXAM_NOT_ACCESSIBLED;
         }
@@ -408,4 +414,38 @@ public class CoreController extends BaseApiController {
         result.accumulate("totalCount", count);
         return result;
     }
+
+    @RequestMapping(value = "/exam/subject/save", method = RequestMethod.POST)
+    @ResponseBody
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCHOOL_DEV })
+    public JSONObject subjectSave(HttpServletRequest request, @RequestParam Integer examId, @RequestParam String code,
+            @RequestParam String name, @RequestParam(required = false) String remark) {
+        ApiUser user = RequestUtils.getApiUser(request);
+        JSONObject result = new JSONObject();
+        // 输入字段预处理并初步校验
+        code = validate("code", code, true, 32);
+        name = validate("name", name, true, 32);
+        remark = validate("remark", remark, false, 64);
+        Exam exam = examService.findById(examId);
+        if (exam == null) {
+            throw ApiException.QUERY_PARAM_ERROR.replaceMessage("examId invalid");
+        } else if (!exam.getSchoolId().equals(user.getSchoolId()) || exam.getStatus() != ExamStatus.START) {
+            throw ApiException.EXAM_NOT_ACCESSIBLED;
+        }
+        ExamSubject subject = subjectService.find(examId, code);
+        if (subject == null) {
+            subject = new ExamSubject();
+            subject.setCode(code);
+            subject.setExamId(examId);
+            subject.setObjectiveScore(0d);
+            subject.setSubjectiveScore(0d);
+            subject.setTotalScore(0d);
+            subject.setUploadCount(0);
+        }
+        subject.setName(name);
+        subject.setRemark(remark);
+        subject = subjectService.save(subject);
+        result.accumulate("updateTime", DateUtils.formatDateTime(new Date()));
+        return result;
+    }
 }

+ 17 - 5
stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/BaseController.java

@@ -247,6 +247,18 @@ public class BaseController {
                 }
             }
         });
+        // ExamSource 类型转换
+        binder.registerCustomEditor(ExamSource.class, new PropertyEditorSupport() {
+
+            @Override
+            public void setAsText(String text) {
+                try {
+                    setValue(ExamSource.findByValue(Integer.valueOf(text)));
+                } catch (Exception e) {
+                    setValue(null);
+                }
+            }
+        });
         // SubjectiveStatus 类型转换
         binder.registerCustomEditor(SubjectiveStatus.class, new PropertyEditorSupport() {
 
@@ -265,8 +277,8 @@ public class BaseController {
     protected boolean saveUploadStudent(ExamStudent student) {
         ExamStudent old = studentService.findById(student.getId());
         if (!student.isAbsent()) {// 正考
-            List<MarkGroup> groupList = groupService
-                    .findByExamAndSubjectAndStatus(student.getExamId(), student.getSubjectCode(), MarkStatus.FINISH);
+            List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(student.getExamId(),
+                    student.getSubjectCode(), MarkStatus.FINISH);
             for (MarkGroup markGroup : groupList) {
                 groupService.updateStatus(student.getExamId(), student.getSubjectCode(), markGroup.getNumber(),
                         MarkStatus.FORMAL, MarkStatus.FINISH);
@@ -295,9 +307,9 @@ public class BaseController {
     private void calculateObjectiveScore(ExamStudent student) {
         ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
 
-        ScoreInfo info = util.calculate(questionService
-                .findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(), student.getSubjectCode(), true,
-                        student.getPaperType()), null);
+        ScoreInfo info = util.calculate(
+                questionService.findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(),
+                        student.getSubjectCode(), true, student.getPaperType()), null);
 
         student.setObjectiveScore(info.getObjectiveScore());
         student.setScoreList(info.getScoreList(), true);

+ 0 - 4
stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/LoginController.java

@@ -104,7 +104,6 @@ public class LoginController {
                 u.refreshAccessToken();
                 userService.save(u);
                 WebUser wu = new WebUser(u);
-                wu.setLogoutUrl(MARK_LOGIN);
                 session.saveWebUser(wu);
                 sessionService.put(request, response, session);
                 return new ModelAndView("redirect:/mark/reset");
@@ -114,7 +113,6 @@ public class LoginController {
             u.refreshAccessToken();
             u = userService.save(u);
             WebUser wu = new WebUser(u);
-            wu.setLogoutUrl(MARK_LOGIN);
             session.saveWebUser(wu);
             sessionService.put(request, response, session);
             return new ModelAndView("redirect:/mark/subject-select");
@@ -159,7 +157,6 @@ public class LoginController {
             u.refreshAccessToken();
             userService.save(u);
             WebUser wu = new WebUser(u);
-            wu.setLogoutUrl(MARK_LOGIN);
             session.saveWebUser(wu);
             sessionService.put(request, response, session);
             return new ModelAndView("redirect:/mark/reset");
@@ -169,7 +166,6 @@ public class LoginController {
         u.refreshAccessToken();
         u = userService.save(u);
         WebUser wu = new WebUser(u);
-        wu.setLogoutUrl(MARK_LOGIN);
         session.saveWebUser(wu);
         sessionService.put(request, response, session);
         return new ModelAndView("redirect:/mark/subject-select");

+ 307 - 179
stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java

@@ -1,45 +1,81 @@
 package cn.com.qmth.stmms.mark;
 
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
 import cn.com.qmth.stmms.admin.utils.SessionExamUtils;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.Marker;
-import cn.com.qmth.stmms.biz.exam.service.*;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerClassService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
-import cn.com.qmth.stmms.biz.mark.model.*;
+import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
+import cn.com.qmth.stmms.biz.mark.model.MarkResult;
+import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
+import cn.com.qmth.stmms.biz.mark.model.ProblemType;
+import cn.com.qmth.stmms.biz.mark.model.SubmitResult;
+import cn.com.qmth.stmms.biz.mark.model.Task;
+import cn.com.qmth.stmms.biz.mark.model.TrialHistory;
+import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
-import cn.com.qmth.stmms.biz.mark.service.*;
+import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
+import cn.com.qmth.stmms.biz.mark.service.MarkService;
+import cn.com.qmth.stmms.biz.mark.service.ProblemTypeService;
+import cn.com.qmth.stmms.biz.mark.service.TaskService;
+import cn.com.qmth.stmms.biz.mark.service.TrialService;
 import cn.com.qmth.stmms.biz.user.model.User;
 import cn.com.qmth.stmms.biz.user.service.UserService;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.controller.BaseController;
 import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.*;
+import cn.com.qmth.stmms.common.enums.ExamStatus;
+import cn.com.qmth.stmms.common.enums.ExamType;
+import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.MarkMode;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.session.model.StmmsSession;
 import cn.com.qmth.stmms.common.session.service.SessionService;
 import cn.com.qmth.stmms.common.utils.EncryptUtils;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
+
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-import org.apache.commons.lang.StringEscapeUtils;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort.Direction;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.servlet.ModelAndView;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.util.*;
 
 @Controller
 @RequestMapping("/mark")
@@ -89,6 +125,15 @@ public class MarkController extends BaseController {
     @Autowired
     private FileService fileService;
 
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Value("${slice.split.config}")
+    private String splitConfig;
+
+    @Value("${marker.prefetch.count}")
+    private long prefetchCount;
+
     @Value("${marker.forceMode}")
     private String forceMarkMode;
 
@@ -160,8 +205,7 @@ public class MarkController extends BaseController {
     }
 
     @RequestMapping(value = "/subject-select", method = RequestMethod.POST)
-    public ModelAndView select(HttpServletRequest request, HttpServletResponse response,
-            @RequestParam Integer markerId) {
+    public ModelAndView select(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer markerId) {
         WebUser user = RequestUtils.getWebUser(request);
         ModelAndView modelAndView = new ModelAndView("modules/mark/subjectSelect");
         Calendar rightNow = Calendar.getInstance();
@@ -171,136 +215,94 @@ public class MarkController extends BaseController {
         modelAndView.addObject("examList", examList);
 
         Marker marker = markerService.findById(markerId);
+        if (marker == null) {
+            modelAndView.addObject("message", "评卷分组不存在");
+            return modelAndView;
+        }
         Exam exam = examService.findById(marker.getExamId());
         modelAndView.addObject("exam", exam);
+        Date now = new Date();
+        if ((exam.getStartTime() != null && now.before(exam.getStartTime()))
+                || (exam.getEndTime() != null && now.after(exam.getEndTime()))) {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            String start = exam.getStartTime() == null ? "" : " 开始时间:" + sdf.format(exam.getStartTime());
+            String end = exam.getEndTime() == null ? "" : " 结束时间:" + sdf.format(exam.getEndTime());
+            modelAndView.addObject("message", "不在评卷时间范围 " + start + " " + end);
+            return modelAndView;
+        }
         MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
         if (group == null) {
-            modelAndView.addObject("message", "user.login.error.group");
+            modelAndView.addObject("message", "评卷分组不存在");
             return modelAndView;
         }
         if (group.getStatus() == MarkStatus.FINISH) {
-            modelAndView.addObject("message", "user.login.error.finish");
+            modelAndView.addObject("message", "评卷分组已结束");
             return modelAndView;
         }
-
         user.setMarkerId(marker.getId());
         StmmsSession session = RequestUtils.getSession(request);
         session.saveWebUser(user);
         sessionService.put(request, response, session);
         SessionExamUtils.setExamId(request, exam);
-        return new ModelAndView("redirect:/mark/index");
-    }
-
-    @RequestMapping("/index")
-    public ModelAndView index(HttpServletRequest request, @RequestParam(value = "mode", required = false) String mode) {
-        Marker marker = RequestUtils.getWebUser(request).getMarker();
-        if (marker == null) {
-            return new ModelAndView("redirect:/mark/subject-select");
-        }
-        ModelAndView modelAndView = getMarkModeView(marker, MarkMode.findByName(mode));
-        preProcess(marker, modelAndView);
-        return modelAndView;
-    }
-
-    private ModelAndView getMarkModeView(Marker marker, MarkMode mode) {
-        // 多媒体阅卷
-        Exam exam = examService.findById(marker.getExamId());
-        boolean forceMode = false;
-        MarkMode sysMode = MarkMode.findByName(forceMarkMode);
-        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
-        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
-            ModelAndView view = new ModelAndView("modules/mark/markJson");
-            view.addObject("forceMode", false);
-            view.addObject("sheetView", false);
-            view.addObject("isFormal", group.getStatus() == MarkStatus.FORMAL);
-            return view;
-        }
-        if (sysMode != null) {
-            // 全局配置的强制评卷模式
-            mode = sysMode;
-            forceMode = true;
-        } else {
-            // 没有全局配置,优先从大题配置取强制评卷模式
-            if (group != null && group.getMarkMode() != null) {
-                mode = group.getMarkMode();
-                forceMode = true;
-            }
-            // 否则取评卷员当前记录的评卷模式
-            if (mode == null) {
-                mode = marker.getMode();
-            }
-            if (mode == null) {
-                mode = MarkMode.COMMON;
-            }
-            if (marker.getMode() != mode) {
-                marker.setMode(mode);
-                markerService.save(marker);
-            }
-        }
-        ModelAndView view = new ModelAndView(
-                mode == MarkMode.TRACK ? "modules/mark/markTrack" : "modules/mark/markNew");
-        view.addObject("forceMode", forceMode);
-        view.addObject("sheetView", group.isSheetView());
-        view.addObject("enableAllZero", group.isEnableAllZero());
-        view.addObject("isFormal", group.getStatus() == MarkStatus.FORMAL);
-        return view;
+        return new ModelAndView("redirect:/web/mark");
     }
 
     @RequestMapping("/logout")
     public ModelAndView logout(HttpServletRequest request) {
         WebUser wu = RequestUtils.getWebUser(request);
         releaseMarker(wu.getMarker());
-        return new ModelAndView("redirect:/logout");
+        StmmsSession session = RequestUtils.getSession(request);
+        String logoutUrl = session.getWebUserLogoutUrl();
+        session.setInvalid(true);
+        if (StringUtils.isNotBlank(logoutUrl)) {
+            return new ModelAndView("redirect:" + logoutUrl);
+        } else {
+            return new ModelAndView("redirect:/mark-login");
+        }
     }
 
-    /**
-     * 进入评卷界面后的通用预处理
-     *
-     * @param marker
-     * @param modelAndView
-     */
-    private void preProcess(Marker marker, ModelAndView modelAndView) {
-        modelAndView.addObject("fileServer", fileService.getFileServer());
-        modelAndView.addObject("marker", marker);
-        ExamSubject subject = subjectService.find(marker.getExamId(), marker.getSubjectCode());
-        subject.setPaperAnswerUrl(fileService);
-        modelAndView.addObject("subject", subject);
+    @RequestMapping(value = "/getSetting", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONObject getSetting(HttpServletRequest request) {
+        JSONObject setting = new JSONObject();
+        WebUser wu = RequestUtils.getWebUser(request);
+        Marker marker = wu.getMarker();
         Exam exam = examService.findById(marker.getExamId());
-        modelAndView.addObject("forceSpecialTag", exam.isForceSpecialTag());
-        modelAndView.addObject("defaultSetting", StringUtils.trimToNull(marker.getMarkSetting()));
-        String sheetConfig = "";
-        if (StringUtils.isNotBlank(subject.getSheetConfig())) {
-            sheetConfig = buildPictureConfig(subject.getSheetConfig());
-        } else if (StringUtils.isNotBlank(exam.getSheetConfig())) {
-            sheetConfig = buildPictureConfig(exam.getSheetConfig());
-        }
-        modelAndView.addObject("sheetConfig", sheetConfig);
-        releaseMarker(marker);
-
-        List<ProblemType> problemTypes = problemTypeService.findByExamId(marker.getExamId());
-        List<Marker> markers = markerService
-                .findByExamAndSubjectAndUserIdAndEnable(marker.getExamId(), marker.getSubjectCode(), marker.getUserId(),
-                        true);
-        markers.remove(marker);
-        for (Marker m : markers) {
-            m.setMarkSetting(null);
-        }
-        ObjectMapper mapper = new ObjectMapper();
-        try {
-            modelAndView.addObject("problemTypes", mapper.writeValueAsString(problemTypes));
-            modelAndView.addObject("groups", mapper.writeValueAsString(markers));
-        } catch (JsonProcessingException e) {
-            log.error("MarkController-问题类型获取出错", e);
-        }
+        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
+        setting.accumulate("examType", exam.getType());
+        setting.accumulate("startTime", exam.getStartTime() == null ? 0 : exam.getStartTime().getTime());
+        setting.accumulate("endTime", exam.getEndTime() == null ? 0 : exam.getEndTime().getTime());
+        setting.accumulate("topCount", marker.getTopCount() != null ? marker.getTopCount() : 0);
+        setting.accumulate("sheetView", group.isSheetView() && exam.isSheetView());
+        setting.accumulate("enableAllZero", group.isEnableAllZero());
+        setting.accumulate("statusValue", group.getStatus());
+        setting.accumulate("groupNumber", group.getNumber());
+        group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
+                group.getSubjectCode(), false, group.getNumber()));
+        setting.accumulate("groupTitle", group.getTitle());
+        setting.accumulate("fileServer", fileService.getFileServer());
+        setting.accumulate("forceSpecialTag", exam.isForceSpecialTag());
+        setting.accumulate("uiSetting",
+                StringUtils.isBlank(marker.getMarkSetting()) ? new JSONObject() : marker.getMarkSetting());
+        setting.accumulate("splitConfig", getSplitConfig());
+        setting.accumulate("prefetchCount", prefetchCount);
+        setModeAndForceMode(setting, exam, marker, group);
+        setSubjectAndSheetConfig(setting, marker, exam);
+        setting.accumulate("userName", RequestUtils.getWebUser(request).getName());
+        setProblemType(setting, marker);
+        return setting;
     }
 
-    @RequestMapping("/clear")
-    @ResponseBody
-    public void clear(HttpServletRequest request) {
-        releaseMarker(RequestUtils.getWebUser(request).getMarker());
+    private double[] getSplitConfig() {
+        String strs[] = splitConfig.split(",");
+        double[] config = new double[strs.length];
+        for (int i = 0; i < strs.length; i++) {
+            config[i] = Double.parseDouble(strs[i]);
+        }
+        return config;
     }
 
-    @RequestMapping("/status")
+    @RequestMapping(value = "/getStatus", method = RequestMethod.POST)
     @ResponseBody
     public JSONObject status(HttpServletRequest request) {
         JSONObject status = new JSONObject();
@@ -314,11 +316,9 @@ public class MarkController extends BaseController {
         long totalCount = 0;
         long personCount = 0;
         long markedCount = 0;
-        long exceptionCount = 0;
-        long topCount = 0;
+        long problemCount = 0;
+        long arbitrateCount = 0;
         if (group.getStatus() == MarkStatus.FORMAL) {
-            topCount = marker.getTopCount() != null ? marker.getTopCount() : 0;
-
             MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
             query.setExamId(marker.getExamId());
             query.setSubjectCode(marker.getSubjectCode());
@@ -326,18 +326,21 @@ public class MarkController extends BaseController {
             totalCount = libraryService.countByQuery(query);
 
             query.setMarkerId(marker.getId());
+            query.addStatus(LibraryStatus.MARKED);
+            query.addStatus(LibraryStatus.INSPECTED);
             personCount = libraryService.countByQuery(query);
 
             query.setMarkerId(0);
-            query.addStatus(LibraryStatus.MARKED);
             query.addStatus(LibraryStatus.ARBITRATED);
-            query.addStatus(LibraryStatus.INSPECTED);
             markedCount = libraryService.countByQuery(query);
 
             query.clearStatus();
-            query.addStatus(LibraryStatus.WAIT_ARBITRATE);
             query.addStatus(LibraryStatus.PROBLEM);
-            exceptionCount = libraryService.countByQuery(query);
+            problemCount = libraryService.countByQuery(query);
+
+            query.clearStatus();
+            query.addStatus(LibraryStatus.WAIT_ARBITRATE);
+            arbitrateCount = libraryService.countByQuery(query);
         } else if (group.getStatus() == MarkStatus.TRIAL) {
             totalCount = trialService.countLibrary(group.getExamId(), group.getSubjectCode(), group.getNumber());
             personCount = trialService.countMarkerHistory(marker.getId());
@@ -346,13 +349,39 @@ public class MarkController extends BaseController {
         status.accumulate("personCount", personCount);
         status.accumulate("totalCount", totalCount);
         status.accumulate("markedCount", markedCount);
-        status.accumulate("exceptionCount", exceptionCount);
+        status.accumulate("problemCount", problemCount);
+        status.accumulate("arbitrateCount", arbitrateCount);
         status.accumulate("valid", totalCount > 0);
-        status.accumulate("topCount", topCount);
         return status;
     }
 
-    @RequestMapping("/gettask")
+    @RequestMapping(value = "/getGroup", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONArray getGroup(HttpServletRequest request) {
+        Marker marker = RequestUtils.getWebUser(request).getMarker();
+        JSONArray array = new JSONArray();
+        List<Marker> markers = markerService.findByExamAndSubjectAndUserIdAndEnable(marker.getExamId(),
+                marker.getSubjectCode(), marker.getUserId(), true);
+        for (Marker m : markers) {
+            JSONObject json = new JSONObject();
+            json.accumulate("markerId", m.getId());
+            json.accumulate("number", m.getGroupNumber());
+            MarkGroup group = groupService.findOne(m.getExamId(), m.getSubjectCode(), m.getGroupNumber());
+            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
+                    group.getSubjectCode(), false, group.getNumber()));
+            json.accumulate("title", group.getTitle());
+            json.accumulate("totalCount", group.getLibraryCount());
+            if (group.getStatus() == MarkStatus.TRIAL) {
+                json.accumulate("markedCount", trialService.countMarkerHistory(m.getId()));
+            } else {
+                json.accumulate("markedCount", group.getMarkedCount());
+            }
+            array.add(json);
+        }
+        return array;
+    }
+
+    @RequestMapping(value = "/getTask", method = RequestMethod.POST)
     @ResponseBody
     public Task getTask(HttpServletRequest request) {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
@@ -365,12 +394,10 @@ public class MarkController extends BaseController {
                     .findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
             if (group == null) {
                 task = new Task();
-                task.setExist(false);
-                task.setMessage("mark.control.task.not.exist");
+                task.setMessage("评卷大题不存在");
             } else if (group.getStatus() == MarkStatus.FINISH) {
                 task = new Task();
-                task.setExist(false);
-                task.setMessage("mark.control.task.finish");
+                task.setMessage("评卷已结束");
             } else if (group.getStatus() == MarkStatus.TRIAL) {
                 task = getTrialTask(marker);
             } else if (group.getStatus() == MarkStatus.FORMAL) {
@@ -378,8 +405,7 @@ public class MarkController extends BaseController {
             }
             if (task == null) {
                 task = new Task();
-                task.setExist(false);
-                task.setMessage("mark.control.task.null");
+                task.setMessage("当前无评卷任务");
             }
         } catch (Exception e) {
             log.error("get task error", e);
@@ -393,13 +419,15 @@ public class MarkController extends BaseController {
     private Task getFormalTask(Marker marker) {
         int retry = 1;
         Task task = null;
+        List<MarkLibrary> list = new ArrayList<MarkLibrary>();
         while (task == null) {
-            List<MarkLibrary> list = new ArrayList<MarkLibrary>();
             // 需要判断评卷员是否绑定了班级
+            Set<LibraryStatus> statusSet = new HashSet<>();
+            statusSet.add(LibraryStatus.WAITING);
+            statusSet.add(LibraryStatus.REJECTED);
             long classCount = markerClassService.countByUserIdAndExamId(marker.getUserId(), marker.getExamId());
-            list = libraryService
-                    .findUnMarked(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(), marker.getId(),
-                            marker.getUserId(), classCount > 0, retry, 20);
+            list = libraryService.findUnMarked(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(),
+                    marker.getId(), marker.getUserId(), classCount > 0, retry, 20, statusSet);
             if (list.isEmpty()) {
                 break;
             }
@@ -420,9 +448,8 @@ public class MarkController extends BaseController {
         int retry = 1;
         Task task = null;
         while (task == null) {
-            List<TrialLibrary> list = trialService
-                    .findUnMarkedLibrary(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(),
-                            marker.getId(), retry, 10);
+            List<TrialLibrary> list = trialService.findUnMarkedLibrary(marker.getExamId(), marker.getSubjectCode(),
+                    marker.getGroupNumber(), marker.getId(), retry, 10);
             if (list.isEmpty()) {
                 break;
             }
@@ -438,7 +465,7 @@ public class MarkController extends BaseController {
     }
 
     @Logging(menu = "评卷", type = LogType.UPDATE)
-    @RequestMapping(value = "/savetask", method = RequestMethod.POST)
+    @RequestMapping(value = "/saveTask")
     @ResponseBody
     public JSONObject saveTask(HttpServletRequest request, @RequestBody MarkResult markResult) {
         JSONObject result = new JSONObject();
@@ -463,32 +490,29 @@ public class MarkController extends BaseController {
             lockService.unwatch(LockType.EXAM_SUBJECT, marker.getExamId(), marker.getSubjectCode());
         }
         result.accumulate("success", success);
-        result.accumulate("status", status(request));
+        // result.accumulate("status", status(request));
         if (!success) {
-            result.accumulate("message", "mark.control.task.error");
+            result.accumulate("message", "评卷任务提交失败,请刷新页面");
         }
         return result;
     }
 
     @Logging(menu = "查询回评任务", type = LogType.QUERY)
-    @RequestMapping("/gethistory")
+    @RequestMapping(value = "/getHistory", method = RequestMethod.POST)
     @ResponseBody
-    public Object history(HttpServletRequest request, @RequestParam int pageNumber, @RequestParam int pageSize,
-            @RequestParam String order, @RequestParam String sort,
-            @RequestParam(required = false, defaultValue = "false") Boolean isTag,
-            @RequestParam(required = false) String secretNumber) throws Exception {
+    public Object getHistory(HttpServletRequest request, @RequestParam int pageNumber, @RequestParam int pageSize,
+            @RequestParam String order, @RequestParam String sort, @RequestParam(required = false) String secretNumber)
+            throws Exception {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
         List<Task> list = new ArrayList<>();
         Direction d = Direction.DESC;
         Sort querySort = null;
-        if (sort.equals("asc")) {
+        if (sort.equalsIgnoreCase("ASC")) {
             d = Direction.ASC;
         }
-        if (order.equals("time")) {
+        if (order.equals("markerTime")) {
             querySort = new Sort(d, "markerTime");
-        } else if (order.equals("studentId")) {
-            querySort = new Sort(d, "studentId");
-        } else if (order.equals("score")) {
+        } else if (order.equals("markerScore")) {
             querySort = new Sort(d, "markerScore");
         }
         MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
@@ -510,20 +534,15 @@ public class MarkController extends BaseController {
                 query.orderByMarkerTimeDesc();
             }
             list = taskService.findByQuery(query);
-            for (Task task : list) {
-                task.setPrevious(true);
-            }
         } else if (group != null && group.getStatus() == MarkStatus.TRIAL) {
             // 试评查找给分历史记录
             List<TrialHistory> historyList = new ArrayList<TrialHistory>();
             if (StringUtils.isNotBlank(secretNumber)) {
-                historyList = trialService
-                        .findHistory(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(),
-                                marker.getId(), secretNumber, pageNumber, pageSize, querySort);
+                historyList = trialService.findHistory(marker.getExamId(), marker.getSubjectCode(),
+                        marker.getGroupNumber(), marker.getId(), secretNumber, pageNumber, pageSize, querySort);
             } else {
-                historyList = trialService
-                        .findHistory(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber(),
-                                marker.getId(), pageNumber, pageSize, querySort, null);
+                historyList = trialService.findHistory(marker.getExamId(), marker.getSubjectCode(),
+                        marker.getGroupNumber(), marker.getId(), pageNumber, pageSize, querySort, null);
             }
             for (TrialHistory history : historyList) {
                 TrialLibrary library = trialService.findLibrary(history.getLibraryId());
@@ -538,20 +557,20 @@ public class MarkController extends BaseController {
     }
 
     @Logging(menu = "修改密码", type = LogType.UPDATE)
-    @RequestMapping("/change-name")
+    @RequestMapping(value = "/changeName", method = RequestMethod.POST)
     @ResponseBody
     public JSONObject changeName(HttpServletRequest request, @RequestParam String name,
             @RequestParam(required = false) String password) {
         User user = userService.findById(RequestUtils.getWebUser(request).getId());
         JSONObject result = new JSONObject();
         user.setName(name);
-        if (StringUtils.isNotEmpty(password)) {
+        if (StringUtils.isNotBlank(password)) {
+            password = StringEscapeUtils.unescapeHtml(password);
             user.setPassword(EncryptUtils.md5(password));
         }
         try {
             user = userService.save(user);
             result.accumulate("success", true);
-            result.accumulate("name", user.getName());
         } catch (Exception e) {
             result.accumulate("success", false);
             log.error("MarkController-修改名字出错", e);
@@ -559,17 +578,59 @@ public class MarkController extends BaseController {
         return result;
     }
 
-    @RequestMapping("/update-setting")
+    @RequestMapping(value = "/updateSetting", method = RequestMethod.POST)
     @ResponseBody
-    public JSONObject updateSetting(HttpServletRequest request, @RequestParam String setting) {
+    public JSONObject updateSetting(HttpServletRequest request, @RequestParam(required = false) String uiSetting,
+            @RequestParam(required = false) String mode) {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
+        if (uiSetting != null) {
+            marker.setMarkSetting(StringEscapeUtils.unescapeHtml(StringUtils.trimToNull(uiSetting)));
+        }
+        if (mode != null) {
+            marker.setMode(MarkMode.findByName(mode));
+        }
+        markerService.save(marker);
         JSONObject result = new JSONObject();
-        markerService
-                .updateMarkSetting(marker.getId(), StringEscapeUtils.unescapeHtml(StringUtils.trimToNull(setting)));
         result.accumulate("success", true);
         return result;
     }
 
+    @RequestMapping("/clear")
+    @ResponseBody
+    public void clear(HttpServletRequest request) {
+        releaseMarker(RequestUtils.getWebUser(request).getMarker());
+    }
+
+    @ResponseBody
+    @RequestMapping(value = "/subjectSelect", method = RequestMethod.POST)
+    public Object subjectSelect(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer markerId) {
+        WebUser user = RequestUtils.getWebUser(request);
+        Marker old = user.getMarker();
+        Marker marker = markerService.findById(markerId);
+        JSONObject result = new JSONObject();
+        if (marker == null) {
+            result.accumulate("success", false);
+            result.accumulate("message", "评卷分组不存在");
+            return result;
+        }
+        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
+        if (group == null) {
+            result.accumulate("success", false);
+            result.accumulate("message", "评卷分组不存在");
+        } else if (group.getStatus() == MarkStatus.FINISH) {
+            result.accumulate("success", false);
+            result.accumulate("message", "评卷分组已结束");
+        } else {
+            releaseMarker(old);
+            user.setMarkerId(marker.getId());
+            StmmsSession session = RequestUtils.getSession(request);
+            session.saveWebUser(user);
+            sessionService.put(request, response, session);
+            result.accumulate("success", true);
+        }
+        return result;
+    }
+
     private void releaseMarker(Marker marker) {
         if (marker == null) {
             return;
@@ -597,4 +658,71 @@ public class MarkController extends BaseController {
         return json;
     }
 
+    private void setProblemType(JSONObject setting, Marker marker) {
+        List<ProblemType> problemTypes = problemTypeService.findByExamId(marker.getExamId());
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            setting.accumulate("problemTypes", mapper.writeValueAsString(problemTypes));
+        } catch (JsonProcessingException e) {
+            log.error("MarkController-问题类型获取出错", e);
+        }
+    }
+
+    private void setSubjectAndSheetConfig(JSONObject setting, Marker marker, Exam exam) {
+        ExamSubject examSubject = subjectService.find(marker.getExamId(), marker.getSubjectCode());
+        String sheetConfig = "";
+        if (StringUtils.isNotBlank(examSubject.getSheetConfig())) {
+            sheetConfig = buildPictureConfig(examSubject.getSheetConfig());
+        } else if (StringUtils.isNotBlank(exam.getSheetConfig())) {
+            sheetConfig = buildPictureConfig(exam.getSheetConfig());
+        }
+        setting.accumulate("sheetConfig", sheetConfig);
+
+        JSONObject subject = new JSONObject();
+        subject.accumulate("name", examSubject.getName());
+        subject.accumulate("code", examSubject.getCode());
+        examSubject.setPaperAnswerUrl(fileService);
+        subject.accumulate("paperUrl", examSubject.getPaperUrl() == null ? "" : examSubject.getPaperUrl());
+        subject.accumulate("answerUrl", examSubject.getAnswerUrl() == null ? "" : examSubject.getAnswerUrl());
+        setting.accumulate("subject", subject);
+    }
+
+    private void setModeAndForceMode(JSONObject setting, Exam exam, Marker marker, MarkGroup group) {
+        MarkMode mode = null;
+        boolean forceMode = false;
+        if (MarkMode.findByName(forceMarkMode) != null) {
+            // 全局配置的强制评卷模式
+            mode = MarkMode.findByName(forceMarkMode);
+            forceMode = true;
+        } else {
+            // 没有全局配置,优先从考试取强制评卷模式
+            if (exam != null && exam.getMarkMode() != null) {
+                mode = exam.getMarkMode();
+                forceMode = true;
+            } else if (group != null && group.getMarkMode() != null) {
+                // 没有全局配置,优先从大题配置取强制评卷模式
+                mode = group.getMarkMode();
+                forceMode = true;
+            }
+            // 否则取评卷员当前记录的评卷模式
+            if (mode == null) {
+                mode = marker.getMode();
+            }
+            if (mode == null) {
+                mode = MarkMode.COMMON;
+            }
+            if (marker.getMode() != mode) {
+                marker.setMode(mode);
+                markerService.save(marker);
+            }
+        }
+        setting.accumulate("mode", mode);
+        setting.accumulate("forceMode", forceMode);
+
+        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
+            setting.accumulate("forceMode", true);
+            setting.accumulate("sheetView", false);
+            setting.accumulate("forceSpecialTag", false);
+        }
+    }
 }

+ 23 - 11
stmms-web/src/main/java/cn/com/qmth/stmms/mark/interceptor/MarkInterceptor.java

@@ -1,15 +1,17 @@
 package cn.com.qmth.stmms.mark.interceptor;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import cn.com.qmth.stmms.biz.exam.model.Marker;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.user.service.UserService;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.interceptor.SessionInterceptor;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 
 /**
  * Mark模块操作拦截器
@@ -25,22 +27,29 @@ public class MarkInterceptor extends SessionInterceptor {
     private MarkerService markerService;
 
     @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-            throws Exception {
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         WebUser wu = buildUser(request);
         if (wu != null) {
             if (wu.getUser().getLastLoginTime() == null && request.getServletPath().startsWith("/mark/reset")) {
                 // 首次登录,强制重置密码及姓名
                 return true;
-            } else if (wu.getMarkerId() == null && !request.getServletPath().startsWith("/mark/subject")) {
+            } else if ((wu.getMarkerId() == null ||wu.getMarker() == null)&& !request.getServletPath().startsWith("/mark/subject")) {
                 // 通用帐号未选择科目,首先选择强制选择评卷科目
                 return redirect(request, response, "/mark/subject-select");
+            } else if (request.getServletPath().startsWith("/mark/logout")) {
+                return true;
+            } else if (wu.getMarkerId() != null&& wu.getMarker() == null) {
+                // 用户存在但评卷员解绑
+                return sessionExpire(request, response, "/mark-login");
+            } else if (request.getServletPath().startsWith("/mark/logout")) {
+                return true;
             }
             return true;
         }
-        if (request.getServletPath().startsWith("/mark/subject-select") || request.getServletPath()
-                .startsWith("/mark/index") || request.getServletPath().startsWith("/mark/reset") || request
-                .getServletPath().startsWith("/mark/logout")) {
+        if (request.getServletPath().startsWith("/mark/subject-select")
+                || request.getServletPath().startsWith("/mark/index")
+                || request.getServletPath().startsWith("/mark/reset")
+                || request.getServletPath().startsWith("/mark/logout")) {
             return sessionExpire(request, response, "/mark-login");
         }
         return sessionExpireAjax(request, response, "/mark-login");
@@ -50,7 +59,10 @@ public class MarkInterceptor extends SessionInterceptor {
         WebUser wu = WebUser.buildFromSession(RequestUtils.getSession(request), userService);
         if (wu != null && wu.getRole().equals(Role.MARKER)) {
             if (wu.getMarkerId() != null) {
-                wu.setMarker(markerService.findById(wu.getMarkerId()));
+                Marker marker = markerService.findById(wu.getMarkerId());
+                if (marker != null) {
+                    wu.setMarker(marker);
+                }
             }
             RequestUtils.setWebUser(request, wu);
             return wu;

+ 6 - 5
stmms-web/src/main/webapp/WEB-INF/application.properties

@@ -1,13 +1,13 @@
 #\u6570\u636e\u5e93\u914d\u7f6e
 jdbc.driver=com.mysql.jdbc.Driver
-jdbc.url=jdbc:mysql://localhost:3306/stmms_ft_new?useUnicode=true&characterEncoding=UTF-8
-jdbc.username=root
-jdbc.password=root
+jdbc.url=jdbc:mysql://192.168.10.223:3309/stmms_ft_new?useUnicode=true&characterEncoding=UTF-8
+jdbc.username=stmms
+jdbc.password=stmms
 jdbc.maxActive=50
 jdbc.initSize=5
 ##\u6587\u4ef6\u5b58\u50a8\u914d\u7f6e
-file.server=http://localhost:9000/stmms-ft/
-file.store=/Users/luoshi/develop/data/stmms-ft
+file.server=http://192.168.10.223:9000/
+file.store=/Users/ting.yin/work/static/stmms-ft
 ##\u88c1\u5207\u56fe\u9ed8\u8ba4\u5207\u5272\u89c4\u5219
 slice.split.config=0,0.55,0.45,0.55
 ##\u9ed8\u8ba4\u95ee\u9898\u5377\u7c7b\u578b
@@ -24,6 +24,7 @@ mark.cleanLockSchedule=0 0 3 * * ?
 marker.showBtnImportAndBtnUpdateImport=false
 marker.forceMode=
 marker.name.verify=true
+marker.prefetch.count=3
 ##\u9996\u9875\u53ef\u9009\u7684logo\u6587\u4ef6
 index.logo=
 ##\u4e91\u5e73\u53f0\u5bf9\u63a5\u914d\u7f6e

+ 2 - 2
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/arbitrateList.jsp

@@ -102,7 +102,7 @@
                 </td>
 				<td>
 				    <c:if test="${result.status.value==0 || result.status.value==1}">
-                    <a href="${ctx}/admin/exam/arbitrate/process?historyId=${result.id}" target="_blank" class="process-link">处理</a>
+                    <a href="${ctx}/web/admin/exam/arbitrate/start?historyId=${result.id}" target="_blank" class="process-link">处理</a>
 					</c:if>
 				</td>
 			</tr>
@@ -135,7 +135,7 @@ $('#batch-process-link').click(function(){
         alert('请选择分组')
         return false;
     }
-    $('#batch-process-link').attr('href', '${ctx}/admin/exam/arbitrate/batchProcess?subjectCode='+subjectCode+'&groupNumber='+groupNumber);
+    $('#batch-process-link').attr('href', '${ctx}/web/admin/exam/arbitrate/start?subjectCode='+subjectCode+'&groupNumber='+groupNumber);
     return true;
 });
 $('#subject-select').change(function(){

+ 42 - 5
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/dataSync.jsp

@@ -22,6 +22,24 @@
 					}
 				}
 			});
+			$("#source").change(function () {
+                if ($("#source").val()==1) {
+                    $(".sourceDiv").show();
+                } 
+                if($("#source").val()==2) {
+                	$("#rootOrgId").attr("value", "");
+                	$("#appId").attr("value", "");
+                    $(".sourceDiv").hide();
+                }
+            });
+			if ($("#source").val()==1) {
+                $(".sourceDiv").show();
+            } 
+            if($("#source").val()==2) {
+            	$("#rootOrgId").attr("value", "");
+            	$("#appId").attr("value", "");
+                $(".sourceDiv").hide();
+            }
 		});
 	</script>
 </head>
@@ -40,25 +58,44 @@
 			</div>
 		</div>
 		<div class="control-group">
+			<label class="control-label">同步来源</label>
+			<div class="controls">
+				<select class="input-small" name="source" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if> id="source">
+	            	<c:forEach items="${sourceList}" var="item">
+	                    <option value="${item.value}"
+	                            <c:if test="${item.value==dataSync.source.value}">selected</c:if>>${item.name}</option>
+	                </c:forEach>
+            	</select>
+			</div>
+		</div>
+		<div class="sourceDiv">
+ 		<div class="control-group">
 			<label class="control-label">机构ID</label>
 			<div class="controls">
-				<input name="rootOrgId" value="${dataSync.rootOrgId }" class="required" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>/>
+				<input name="rootOrgId" id='rootOrgId' value="${dataSync.rootOrgId }" class="required" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>/>
 			</div>
 		</div>
 		<div class="control-group">
 			<label class="control-label">appId</label>
 			<div class="controls">
-				<input name="appId" value="${dataSync.appId }" class="required" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>/>
+				<input name="appId" id='appId' value="${dataSync.appId }" class="required" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>/>
+			</div>
+		</div> 
+		</div>
+		<div class="control-group">
+			<label class="control-label">密匙</label>
+			<div class="controls">
+				<input name="accessKey" value="${dataSync.accessKey }" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>/>
 			</div>
 		</div>
 		<div class="control-group">
 			<label class="control-label">密钥</label>
 			<div class="controls">
-				<input name="secretKey" value="${dataSync.secretKey }" class="required" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>/>
+				<input name="accessSecret" value="${dataSync.accessSecret }" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>/>
 			</div>
 		</div>
 		<div class="control-group">
-			<label class="control-label">云平台考试ID</label>
+			<label class="control-label">考试ID</label>
 			<div class="controls">
 				<input name="cloudExamId" value="${dataSync.cloudExamId }" class="required" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>/>
 			</div>
@@ -75,7 +112,7 @@
 				<textarea name="subjectUrl" class="input-xxlarge required" <c:if test="${dataSync.cloudExamId!=null }">readonly</c:if>>${dataSync.subjectUrl }</textarea>
 			</div>
 		</div>
-		<c:if test="${dataSync.cloudExamId!=null }">
+		<c:if test="${dataSync.cloudExamId!=null && dataSync.source=='EXAM_CLOUD'}">
 		<div class="control-group">
 			<label class="control-label">试卷结构</label>
 			<div class="controls">

+ 43 - 3
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examEdit.jsp

@@ -12,8 +12,15 @@
             window.localStorage.setItem("pictureConfig", pictureConfig);
             $("#inputForm").validate({
                 submitHandler: function (form) {
-                    loading('正在提交,请稍等...');
-                    form.submit();
+                	var endTime = new Date($("#endTime").val()).getTime();
+					var startTime = new Date($("#startTime").val()).getTime();
+					if(startTime>=endTime){
+						alert("评卷结束时间必须晚于开始时间");
+						return;
+					}else{
+						loading('正在提交,请稍等...');
+						form.submit();
+					}
                 },
                 errorContainer: "#messageBox",
                 errorPlacement: function (error, element) {
@@ -27,7 +34,6 @@
             });
             
             var forbiddenInfo = ${exam.forbiddenInfo};
-            debugger;
             if (forbiddenInfo != null && forbiddenInfo ==true) {
                 $("#forbiddenScoreDiv").show();
             } else {
@@ -76,7 +82,35 @@
                    onclick="WdatePicker({dateFmt:'yyyy-MM-dd',isShowClear:true});"/>
         </div>
     </div>
+    <div class="control-group">
+			<label class="control-label">评卷开始日期:</label>
+			<div class="controls">
+				<input name="startTime" type="text" readonly="readonly" maxlength="30" class="Wdate " id="startTime"
+					value="<fmt:formatDate value="${exam.startTime}" pattern="yyyy-MM-dd HH:mm:ss" />"
+					onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',isShowClear:true});"/>
+			</div>
+	</div>
+	<div class="control-group">
+			<label class="control-label">评卷结束日期:</label>
+			<div class="controls">
+				<input name="endTime" type="text" readonly="readonly" maxlength="30" class="Wdate " id="endTime"
+					value="<fmt:formatDate value="${exam.endTime}" pattern="yyyy-MM-dd HH:mm:ss" />"
+					onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',isShowClear:true});"/>
+			</div>
+	</div>
     <c:if test="${exam.type!='MULTI_MEDIA'}">
+    	<div class="control-group">
+	        <label class="control-label">评卷模式</label>
+	        <div class="controls">
+	            <select name="markMode">
+	                <option value="">不限</option>
+	                <c:forEach items="${markModeList}" var="item">
+	                    <option value="${item}"
+	                            <c:if test="${exam.markMode!=null && exam.markMode==item}">selected</c:if>>${item.name}</option>
+	                </c:forEach>
+	            </select>
+	        </div>
+    	</div>
         <div class="control-group">
             <label class="control-label">强制标记</label>
             <div class="controls">
@@ -133,6 +167,12 @@
                        id="configuration" rel="opener">设置</a>
                 </div>
             </div>
+			<div class="control-group">
+		        <label class="control-label">原卷显示</label>
+		        <div class="controls">
+		        <input name="sheetView" type="checkbox" <c:if test="${exam.sheetView}">checked</c:if>/>
+		        </div>
+			</div>
         </c:if>
     </c:if>
     <div class="control-group">

+ 42 - 2
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examForm.jsp

@@ -10,8 +10,15 @@
             $("#name").focus();
             $("#inputForm").validate({
                 submitHandler: function (form) {
-                    loading('正在提交,请稍等...');
-                    form.submit();
+                	var endTime = new Date($("#endTime").val()).getTime();
+					var startTime = new Date($("#startTime").val()).getTime();
+					if(startTime>=endTime){
+						alert("评卷结束时间必须晚于开始时间");
+						return;
+					}else{
+						loading('正在提交,请稍等...');
+						form.submit();
+					}
                 },
                 errorContainer: "#messageBox",
                 errorPlacement: function (error, element) {
@@ -75,6 +82,33 @@
                    onclick="WdatePicker({dateFmt:'yyyy-MM-dd',isShowClear:true});"/>
         </div>
     </div>
+    <div class="control-group">
+			<label class="control-label">评卷开始日期:</label>
+			<div class="controls">
+				<input name="startTime" type="text" readonly="readonly" maxlength="30" class="Wdate " id="startTime"
+					value="<fmt:formatDate value="${exam.startTime}" pattern="yyyy-MM-dd HH:mm:ss" />"
+					onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',isShowClear:true});"/>
+			</div>
+	</div>
+	<div class="control-group">
+			<label class="control-label">评卷结束日期:</label>
+			<div class="controls">
+				<input name="endTime" type="text" readonly="readonly" maxlength="30" class="Wdate " id="endTime"
+					value="<fmt:formatDate value="${exam.endTime}" pattern="yyyy-MM-dd HH:mm:ss" />"
+					onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss',isShowClear:true});"/>
+			</div>
+	</div>
+	<div class="control-group">
+        <label class="control-label">评卷模式</label>
+        <div class="controls">
+            <select name="markMode">
+                <option value="">不限</option>
+                <c:forEach items="${markModeList}" var="item">
+                    <option value="${item}">${item.name}</option>
+                </c:forEach>
+            </select>
+        </div>
+    </div>
     <div id="forceSpecialTagDiv">
         <div class="control-group">
             <label class="control-label">强制标记</label>
@@ -122,6 +156,12 @@
             </div>
         </div>
     </c:if>
+	<div class="control-group">
+	        <label class="control-label">原卷显示</label>
+	        <div class="controls">
+	        <input name="sheetView" type="checkbox" <c:if test="${exam.sheetView}">checked</c:if>/>
+	        </div>
+	</div>
     <div class="control-group">
         <label class="control-label">描述</label>
         <div class="controls">

+ 2 - 2
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examIndex.jsp

@@ -137,7 +137,7 @@
                                         <li><a href="${ctx}/admin/exam/inspected" target="mainFrame"><i class="icon-flag"></i><span data-i18n-text="index.inspected">成绩复核</span></a></li>
                                         <li><a href="${ctx}/admin/exam/problem/history" target="mainFrame"><i class="icon-tag"></i><span data-i18n-text="index.problem">问题试卷</span></a></li>
                                         <li><a href="${ctx}/admin/exam/reportSubject" target="mainFrame"><i class="icon-signal"></i><span data-i18n-text="index.report.total">总量分析</span></a></li>
-                                        <li><a href="${ctx}/admin/exam/reportSubjectCollege" target="mainFrame"><i class="icon-asterisk"></i><span data-i18n-text="index.report.subject">科目分析</span></a></li>
+                                        <li><a href="${ctx}/admin/exam/reportSubjectRange" target="mainFrame"><i class="icon-asterisk"></i><span data-i18n-text="index.report.subject">科目分析</span></a></li>
                                         <li><a href="${ctx}/admin/exam/check/answer" target="mainFrame"><i class="icon-check"></i><span data-i18n-text="index.check">数据检查</span></a></li>
                                         <li><a href="${ctx}/admin/operation/log" target="mainFrame"><i class="icon-tasks"></i><span data-i18n-text="index.log">操作日志</span></a></li>
                                     </c:if>
@@ -150,7 +150,7 @@
                                         <li><a href="${ctx}/admin/exam/inspected" target="mainFrame"><i class="icon-flag"></i><span data-i18n-text="index.inspected">成绩复核</span></a></li>
                                         <li><a href="${ctx}/admin/exam/problem/history" target="mainFrame"><i class="icon-tag"></i><span data-i18n-text="index.problem">问题试卷</span></a></li>
                                         <li><a href="${ctx}/admin/exam/reportSubject" target="mainFrame"><i class="icon-signal"></i><span data-i18n-text="index.report.total">总量分析</span></a></li>
-                                        <li><a href="${ctx}/admin/exam/reportSubjectCollege" target="mainFrame"><i class="icon-asterisk"></i><span data-i18n-text="index.report.subject">科目分析</span></a></li>
+                                        <li><a href="${ctx}/admin/exam/reportSubjectRange" target="mainFrame"><i class="icon-asterisk"></i><span data-i18n-text="index.report.subject">科目分析</span></a></li>
                                     </c:if>
                                     
                                     <c:if test="${web_user.inspector==true}">

+ 2 - 2
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examList.jsp

@@ -19,8 +19,8 @@
     </c:if>
 </ul>
 <form id="searchForm" action="${ctx}/admin/exam/list" method="post" class="breadcrumb form-search">
-    <input id="pageNumber" name="pageNumber" type="hidden"/>
-    <input id="pageSize" name="pageSize" type="hidden"/>
+    <input type="hidden" id="pageNumber" name="pageNumber" value="${query.pageNumber }"/>
+    <input type="hidden" id="pageSize" name="pageSize" value="${query.pageSize }"/>
     <div>
         <label>名称</label>
         <input type="text" id="name" name="name" value="${query.name}" htmlEscape="false" maxlength="20" class="input-medium"/>

+ 89 - 79
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupAdd.jsp

@@ -35,12 +35,36 @@
                     $(".doubleDiv").hide();
                 }
             });
+            $("#btnNext").click(function () {
+            	var check_list = []
+            	$("input[name='questionIds']:checked").each(function(){
+	            	if($(this).val()!=""){
+	            		check_list.push($(this).val())
+            		}
+            	})
+                if(check_list.length==0) {
+                    alert('请选择题目');
+                    return false;
+                }
+            	$("#nextDiv").show();
+            	$("#nextLi").attr("class","active");
+            	$("#preDiv").hide();
+            	$("#preLi").attr("class","");
+            	
+            });
+			$("#btnPre").click(function () {
+            	$("#nextDiv").hide();
+				$("#preDiv").show();
+            	$("#nextLi").attr("class","");
+				$("#preLi").attr("class","active");
+            });
         });
     </script>
 </head>
 <body>
 <ul class="nav nav-tabs">
-    <li class="active"><a href="##">新增分组</a></li>
+    <li class="active" id="preLi"><a>勾选试题分组</a></li>
+    <li id="nextLi"><a>分组参数设置</a></li>
 </ul>
 <br/>
 <form:form id="inputForm" modelAttribute="group" action="${ctx}/admin/exam/group/insert" method="post"
@@ -48,31 +72,40 @@
     <tags:message content="${message}"/>
     <form:hidden path="subjectCode"/>
     <input type="hidden" id="questionDetail" name="questionDetail"/>
-    <div class="control-group">
-        <label class="control-label">序号</label>
-        <div class="controls">
-            <form:input path="number" type="number" htmlEscape="false" max="100" min="1" class="required digits"/>
-        </div>
-    </div>
-    <div id="questionDiv">
-        <div class="control-group" id="questionBaseDiv">
-            <label class="control-label">大题</label>
-            <div class="controls">
-                <input name="mainNumber" type="number" htmlEscape="false" max="100" min="1" class="required digits input-mini"
-                       placeholder="大题号"/>
-                <input name="mainTitle" type="text" htmlEscape="false" maxlength="30" class="required"
-                       placeholder="名称"/>
-                <input name="subNumber" type="number" htmlEscape="false" max="100" min="1" class="required digits input-small"
-                       placeholder="起始小题号"/>
-                <input name="scoreList" type="text" htmlEscape="false" maxlength="100" class="required"
-                       placeholder="步骤分"/>
-                <input type="button" class="btn question-delete hide" onclick="questionDelete(this)" value="删除"/>
-            </div>
-        </div>
+<div id="preDiv">
+	<div>
+        <table id="contentTable" class="table table-striped table-bordered table-condensed">
+        <caption><h4>${subject.code }-${subject.name } 主观题${subject.subjectiveScore }分</h4></caption>
+        	<thead>
+        	<tr>
+        		 <th>名称</th>
+       			 <th>大题号</th>
+        		 <th>小题号</th>
+        		 <th>分数</th>
+        		 <th></th>
+			</tr>
+        	</thead>
+			<c:forEach items="${questionList}" var="item">
+		    <tr>
+		    <td>${item.mainNumber }</td>
+		    <td>${item.subNumber }</td>
+			<td>${item.mainTitle }</td>
+			<td><fmt:formatNumber pattern="###.#" value="${item.totalScore}"/></td>
+			<td><input type="checkbox" name="questionIds" value="${item.id }" <c:if test="${item.groupNumber!=null}"> disabled="disabled"</c:if>></td>
+			</tr>
+			</c:forEach>
+		</table>
+	</div>
+    <div class="pull-right">
+        <a href="${ctx}/admin/exam/group?subjectCode=${group.subjectCode}" class="btn">取&nbsp;消</a>&nbsp;&nbsp;
+        <a id="btnNext" href="##" class="btn btn-primary">下一步 </a>
     </div>
+</div>
+<div id="nextDiv" class ="hide">
     <div class="control-group">
+        <label class="control-label">分组号</label>
         <div class="controls">
-            <input type="button" class="btn" value="增加大题" id="quetion-add">
+            <form:input path="number" type="number" htmlEscape="false" max="100" min="1" class="required digits" value="${group.number }"/>
         </div>
     </div>
     <div class="control-group">
@@ -82,18 +115,18 @@
             <label>0表示跳过试评</label>
         </div>
     </div>
-    <div class="control-group">
-        <label class="control-label">评卷模式</label>
-        <div class="controls">
-            <select name="markMode">
-                <option value="">不限</option>
-                <c:forEach items="${markModeList}" var="item">
-                    <option value="${item}">${item.name}</option>
-                </c:forEach>
-            </select>
-        </div>
-    </div>
     <c:if test="${examType!='MULTI_MEDIA'}">
+	    <div class="control-group">
+	        <label class="control-label">评卷模式</label>
+	        <div class="controls">
+	            <select name="markMode">
+	                <option value="">不限</option>
+	                <c:forEach items="${markModeList}" var="item">
+	                    <option value="${item}">${item.name}</option>
+	                </c:forEach>
+	            </select>
+	        </div>
+	    </div>
         <div class="control-group">
             <label class="control-label">图片显示</label>
             <div class="controls">
@@ -145,69 +178,46 @@
             </div>
         </div>
     </div>
-    <c:if test="${examType!='MULTI_MEDIA'}">
-        <!--<div class="control-group">
-        <label class="control-label">原卷显示</label>
-        <div class="controls">
-        <input name="sheetView" type="checkbox" <c:if test="${group.sheetView}">checked</c:if>/>
-        </div>
+    <c:if test="${exam.type!='MULTI_MEDIA'}">
+    	<c:if test="${exam.sheetView}">
+        <div class="control-group">
+	        <label class="control-label">原卷显示</label>
+	        <div class="controls">
+	        <input name="sheetView" type="checkbox" <c:if test="${group.sheetView}">checked</c:if>/>
+	        </div>
         </div>
-        -->
+        </c:if>
         <div class="control-group">
             <label class="control-label">启用全零分</label>
             <div class="controls">
                 <input name="enableAllZero" type="checkbox" value="1" <c:if test="${group.enableAllZero}">checked</c:if>/>
             </div>
         </div>
-    </c:if>
+    </c:if>    
     <div class="form-actions">
-        <a id="btnSubmit" href="##" class="btn btn-primary">保 存</a>&nbsp;
-        <a href="${ctx}/admin/exam/group?subjectCode=${group.subjectCode}" class="btn">返回</a>
+        <a id="btnPre" class="btn">上一步</a>&nbsp;
+        <a id="btnSubmit" href="##" class="btn btn-primary">保 存</a>
     </div>
+</div>
 </form:form>
 <script type="text/javascript">
-    $("#quetion-add").click(function () {
-        var dom = $('#questionBaseDiv').clone();
-        dom.find('.question-delete').show();
-        dom.find("input[type='text'],input[type='number']").val('');
-        $("#questionDiv").append(dom)
-    });
-
-    function questionDelete(obj) {
-        $(obj).parent().parent().remove();
-    }
 
     $('#btnSubmit').click(function () {
     	if($("#arbitrateThreshold").val()<0){
     		alert("仲裁阈值不能小于0");
     		return;
     	}
-    	
-        var questionDetail = [];
-        var fill = true;
-        $("#questionDiv div div").each(function () {
-            var question = []
-            $(this).children("input[type='text'],input[type='number']").each(function () {
-                var q = $(this).val();
-                if (q == '') {
-                    fill = false;
-                }
-                question.push(q);
-            });
-            var detail = {
-                mainNumber: question[0],
-                mainTitle: question[1],
-                subNumber: question[2],
-                scoreList: question[3].split(",")
-            };
-            questionDetail.push(detail);
-        });
-        if (fill) {
-            $('#questionDetail').val(JSON.stringify(questionDetail));
-            $('#inputForm').submit();
-        } else {
-            alert('大题不能为空');
+    	var check_list = []
+    	$("input[name='questionIds']:checked").each(function(){
+        	if($(this).val()!=""){
+        		check_list.push($(this).val())
+    		}
+    	})
+        if(check_list.length==0) {
+            alert('请选择题目');
+            return false;
         }
+       $('#inputForm').submit();
     });
 </script>
 </body>

+ 82 - 77
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupEditFull.jsp

@@ -28,7 +28,6 @@
             var openDouble = "${group.doubleRate}";
             if (openDouble != null && openDouble > 0) {
                 $("#openDouble").prop("checked", true);
-                $("#openDouble").attr("disabled", "disabled");
                 $(".doubleDiv").show();
             } else {
                 $("#doubleRate").attr("value", "");
@@ -46,53 +45,80 @@
                     $("#arbitrateThreshold").attr("value", "");
                     $(".doubleDiv").hide();
                 }
-
+            });
+            
+            $("#btnNext").click(function () {
+            	var check_list = []
+            	$("input[name='questionIds']:checked").each(function(){
+	            	if($(this).val()!=""){
+	            		check_list.push($(this).val())
+            		}
+            	})
+                if(check_list.length==0) {
+                    alert('请选择题目');
+                    return false;
+                }
+            	$("#nextDiv").show();
+            	$("#nextLi").attr("class","active");
+            	$("#preDiv").hide();
+            	$("#preLi").attr("class","");
+            	
+            });
+			$("#btnPre").click(function () {
+            	$("#nextDiv").hide();
+            	$("#nextLi").attr("class","");
+				$("#preDiv").show();
+				$("#preLi").attr("class","active");
             });
         });
     </script>
 </head>
 <body>
 <ul class="nav nav-tabs">
-    <li><a href="${ctx}/admin/exam/group?subjectCode=${group.subjectCode}">分组列表</a></li>
-    <li><a href="${ctx}/admin/exam/group/edit-simple?subjectCode=${group.subjectCode}&number=${group.number}">简要修改</a>
-    </li>
-    <li class="active"><a href="##">重置修改</a></li>
+    <li class="active"  id="preLi"><a>勾选试题分组</a></li>
+    <li id="nextLi"><a>分组参数设置</a></li>
 </ul>
 <br/>
-<form:form id="inputForm" modelAttribute="group" action="${ctx}/admin/exam/group/save" method="post"
+<form:form id="inputForm" modelAttribute="group" action="${ctx}/admin/exam/group/update" method="post"
            class="form-horizontal">
     <tags:message content="${message}"/>
     <form:hidden path="subjectCode"/>
     <input type="hidden" id="rest" name="reset" value="true"/>
     <input type="hidden" id="number" name="number" value="${group.number }"/>
-    <input type="hidden" id="questionDetail" name="questionDetail"/>
-    <div class="control-group">
-        <label class="control-label">分组序号</label>
-        <div class="controls">
-            <form:input path="number" htmlEscape="false" class="required" readonly="true"/>
-        </div>
+<div id="preDiv">
+	<div>
+        <table id="contentTable" class="table table-striped table-bordered table-condensed">
+			<caption><h4>${subject.code }-${subject.name } 主观题${subject.subjectiveScore }分</h4></caption>
+        	<thead>
+        	<tr>
+        		 <th>名称</th>
+       			 <th>大题号</th>
+        		 <th>小题号</th>
+        		 <th>分数</th>
+        		 <th></th>
+			</tr>
+        	</thead>
+			<c:forEach items="${questionList}" var="item">
+		    <tr>
+		    <td>${item.mainNumber }</td>
+		    <td>${item.subNumber }</td>
+			<td>${item.mainTitle }</td>
+			<td><fmt:formatNumber pattern="###.#" value="${item.totalScore}"/></td>
+			<td><input type="checkbox" name="questionIds" value="${item.id }" <c:if test="${item.groupNumber!=null && item.groupNumber!=group.number}"> disabled="disabled"</c:if> <c:if test="${item.groupNumber==group.number}"> checked</c:if>></td>
+			</tr>
+			</c:forEach>
+		</table>
     </div>
-    <div id="questionDiv">
-        <c:forEach items="${questionList}" var="question" varStatus="questionStatus">
-            <div class="control-group">
-                <label class="control-label">大题</label>
-                <div class="controls">
-                    <input name="mainNumber" type="number" htmlEscape="false" max="100" min="1"
-                           class="required digits input-mini" placeholder="大题号" value="${question.mainNumber }"/>
-                    <input name="mainTitle" type="text" htmlEscape="false" maxlength="30" class="required"
-                           placeholder="名称" value="${question.mainTitle }"/>
-                    <input name="subNumber" type="number" htmlEscape="false" max="100" min="1"
-                           class="required digits input-small" placeholder="起始小题号" value="${question.subNumber }"/>
-                    <input name="scoreList" type="text" htmlEscape="false" maxlength="100" class="required"
-                           placeholder="步骤分" value="${question.scoreString }"/>
-                    <input type="button" class="btn question-delete <c:if test="${questionStatus.first}">hide</c:if>" onclick="questionDelete(this)" value="删除"/>
-                </div>
-            </div>
-        </c:forEach>
+    <div class="pull-right">
+        <a href="${ctx}/admin/exam/group?subjectCode=${group.subjectCode}" class="btn">取&nbsp;消</a>&nbsp;&nbsp;
+        <a id="btnNext" href="##" class="btn btn-primary">下一步 </a>
     </div>
+</div>
+<div id="nextDiv" class ="hide">
     <div class="control-group">
+        <label class="control-label">分组序号</label>
         <div class="controls">
-            <input type="button" class="btn" value="增加大题" id="quetion-add">
+            <form:input path="number" htmlEscape="false" class="required" readonly="true"/>
         </div>
     </div>
     <c:if test="${group.status.value==1}">
@@ -169,14 +195,15 @@
             </div>
         </div>
     </div>
-    <c:if test="${examType!='MULTI_MEDIA'}">
-        <!--<div class="control-group">
-        <label class="control-label">原卷显示</label>
-        <div class="controls">
-        <input name="sheetView" type="checkbox" <c:if test="${group.sheetView}">checked</c:if>/>
-        </div>
+    <c:if test="${exam.type!='MULTI_MEDIA'}">
+    	<c:if test="${exam.sheetView}">
+        <div class="control-group">
+	        <label class="control-label">原卷显示</label>
+	        <div class="controls">
+	        <input name="sheetView" type="checkbox" <c:if test="${group.sheetView}">checked</c:if>/>
+	        </div>
         </div>
-        -->
+		</c:if>
         <div class="control-group">
             <label class="control-label">启用全零分</label>
             <div class="controls">
@@ -187,57 +214,35 @@
     <div class="control-group">
         <label class="control-label">重要提示</label>
         <div class="controls">
-            <span class="label label-important"><h5>保存后分组关联所有评卷任务都将重置</h5></span>
+            <span class="label label-important"><h5>保存后分组关联所有评卷任务都将删除,并生成新的分组任务</h5></span>
         </div>
     </div>
     <div class="form-actions">
         <a id="btnSubmit" href="##" class="btn btn-primary">保 存</a>&nbsp;
-        <a href="${ctx}/admin/exam/group/delete?subjectCode=${group.subjectCode}&number=${group.number}" data-number="${group.number}" class="delete-button btn btn-danger">删除</a>
-        <a href="${ctx}/admin/exam/group?subjectCode=${group.subjectCode}&number=${group.number}" class="btn">返回</a>
+        <a href="${ctx}/admin/exam/group/delete?subjectCode=${group.subjectCode}&number=${group.number}" data-number="${group.number}" class="delete-button btn btn-danger">删除</a>&nbsp;
+        <a id="btnPre" class="btn">上一步</a>
     </div>
+</div>
 </form:form>
 <script type="text/javascript">
-    $("#quetion-add").click(function () {
-        var dom = $('#questionDiv div:first').clone();
-        dom.find('.question-delete').show();
-        dom.find("input[type='text'],input[type='number']").val('');
-        $("#questionDiv").append(dom);
-    });
-
-    function questionDelete(obj) {
-        $(obj).parent().parent().remove();
-    }
-
     $('#btnSubmit').click(function () {
     	if($("#arbitrateThreshold").val()<0){
     		alert("仲裁阈值不能小于0");
     		return;
     	}
-    	
-        var questionDetail = [];
-        var fill = true;
-        $("#questionDiv div div").each(function () {
-            var question = []
-            $(this).children("input[type='text'],input[type='number']").each(function () {
-                var q = $(this).val();
-                if (q == '') {
-                    fill = false;
-                }
-                question.push(q);
-            });
-            var detail = {
-                mainNumber: question[0],
-                mainTitle: question[1],
-                subNumber: question[2],
-                scoreList: question[3].split(",")
-            };
-            questionDetail.push(detail);
-        });
-        if (fill) {
-            $('#questionDetail').val(JSON.stringify(questionDetail));
-            $('#inputForm').submit();
-        } else {
-            alert('大题不能为空');
+    	var check_list = []
+    	$("input[name='questionIds']:checked").each(function(){
+        	if($(this).val()!=""){
+        		check_list.push($(this).val())
+    		}
+    	})
+        if(check_list.length==0) {
+            alert('请选择题目');
+            return false;
+        }
+    	var r=confirm("是否确定删除原分组任务,生成新的分组任务?");
+        if (r==true){
+	        $('#inputForm').submit();
         }
     });
 

+ 42 - 34
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupEditSimple.jsp

@@ -25,7 +25,6 @@
                     }
                 }
             });
-
             var openDouble = "${group.doubleRate}";
             if (openDouble != null && openDouble > 0) {
                 $("#openDouble").prop("checked", true);
@@ -56,17 +55,23 @@
 <ul class="nav nav-tabs">
     <li><a href="${ctx}/admin/exam/group?subjectCode=${group.subjectCode}">分组列表</a></li>
     <li class="active"><a href="##">简要修改</a></li>
-    <li><a href="${ctx}/admin/exam/group/edit-full?subjectCode=${group.subjectCode}&number=${group.number}&">重置修改</a>
-    </li>
 </ul>
 <br/>
-<form:form id="inputForm" modelAttribute="group" action="${ctx}/admin/exam/group/save" method="post"
+<form:form id="inputForm" modelAttribute="group" action="${ctx}/admin/exam/group/update" method="post"
            class="form-horizontal">
     <tags:message content="${message}"/>
     <form:hidden path="subjectCode"/>
     <form:hidden path="number"/>
+    <form:hidden path="doubleRate"/>
     <input type="hidden" id="rest" name="reset" value="false"/>
     <input type="hidden" id="intervalScoreList" name="intervalScoreList" value=""/>
+    
+	<div class="control-group">
+        <label class="control-label">分组序号</label>
+        <div class="controls">
+            <form:input path="number" htmlEscape="false" class="required" readonly="true"/>
+        </div>
+    </div>
     <div class="control-group">
         <label class="control-label">名称</label>
         <div class="controls">
@@ -105,41 +110,44 @@
     <form:input path="picList" class="required" id="picList" type="hidden"/>
     <c:forEach items="${questions}" var="question">
         <div class="control-group">
-            <label class="control-label">小题${question.subNumber}间隔分</label>
+            <label class="control-label">${question.mainNumber}-${question.subNumber}间隔分</label>
             <div class="controls">
                 <input type="text" class="required interval-score-input" htmlEscape="false" maxlength="10"
                        value="${question.intervalScore}"/>
             </div>
         </div>
     </c:forEach>
-    <!-- <div class="control-group"> -->
-    <!-- <label class="control-label">双评</label> -->
-    <!-- <div class="controls"> -->
-    <!-- <input type="checkbox" id="openDouble">开启 -->
-    <!-- </div> -->
-    <!-- </div> -->
-    <!-- <div class="doubleDiv"> -->
-    <!-- <div class="control-group"> -->
-    <!-- <label class="control-label">双评比例</label> -->
-    <!-- <div class="controls"> -->
-    <%--                 <form:input path="doubleRate" htmlEscape="false" maxlength="100" class="required" type="number" id="doubleRate"/>*比例范围为0-1 --%>
-    <!-- </div> -->
-    <!-- </div> -->
-    <!-- <div class="control-group"> -->
-    <!-- <label class="control-label">仲裁阀值</label> -->
-    <!-- <div class="controls"> -->
-    <%--                 <form:input path="arbitrateThreshold" htmlEscape="false" maxlength="100" class="required" type="number" id="arbitrateThreshold"/>*阈值为分数 --%>
-    <!-- </div> -->
-    <!-- </div> -->
-    <!-- </div> -->
-    <c:if test="${examType!='MULTI_MEDIA'}">
-        <!--<div class="control-group">
-        <label class="control-label">原卷显示</label>
-        <div class="controls">
-        <input name="sheetView" type="checkbox" value="1" <c:if test="${group.sheetView}">checked</c:if>/>
-        </div>
+<%-- 	<div class="control-group">
+		<label class="control-label">双评</label>
+		<div class="controls"> 
+		<input type="checkbox" id="openDouble">开启
+		</div> 
+	</div> 
+	<div class="doubleDiv">
+	<div class="control-group" >
+		<label class="control-label">双评比例</label>
+		<div class="controls"> 
+			<form:input path="doubleRate" htmlEscape="false" maxlength="100" class="required" type="number" id="doubleRate"/>*比例范围为0-1
+		</div> 
+	</div> --%>
+	<c:if test="${group.doubleRate>0}">
+    <div class="control-group">
+    	<label class="control-label">仲裁阀值</label>
+    	<div class="controls"> 
+			<form:input path="arbitrateThreshold" htmlEscape="false" maxlength="100" class="required" type="number" id="arbitrateThreshold"/>*阈值为分数 
+		</div>
+	</div> 
+	</c:if>
+<!-- 	</div>  -->
+    <c:if test="${exam.type!='MULTI_MEDIA'}">
+   		<c:if test="${exam.sheetView}">
+        <div class="control-group">
+	        <label class="control-label">原卷显示</label>
+	        <div class="controls">
+	        <input name="sheetView" type="checkbox" value="1" <c:if test="${group.sheetView}">checked</c:if>/>
+	        </div>
         </div>
-        -->
+        </c:if>
         <div class="control-group">
             <label class="control-label">启用全零分</label>
             <div class="controls">
@@ -148,8 +156,8 @@
         </div>
     </c:if>
     <div class="form-actions">
-        <a id="btnSubmit" href="##" class="btn btn-primary">保 存</a>&nbsp;
-        <a href="${ctx}/admin/exam/group?subjectCode=${group.subjectCode}" class="btn">返回</a>
+    	<a class="btn" href="${ctx}/admin/exam/group/edit-full?subjectCode=${group.subjectCode}&number=${group.number}">重置修改</a>&nbsp;
+        <a id="btnSubmit" href="##" class="btn btn-primary">保 存</a>
     </div>
 </form:form>
 <script type="text/javascript">

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini