Pārlūkot izejas kodu

Squashed commit of the following:

commit 3383b165327e890c42422dd82de5753756ae39aa
Author: benbo <lbxx007@qq.com>
Date:   Fri Feb 10 20:51:14 2023 +0800

    fix

commit 226f07dc47b1057ec69b81087f898d81cf1b6ffd
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Feb 10 16:09:28 2023 +0800

    脚本文件名修改

commit 83437ab269e882c060f25cf4fe2b6b086506fc4e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Feb 9 13:35:36 2023 +0800

    update

commit 377f8129f53a5b0323be0cd8587d89aad0d3927e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Feb 9 11:30:50 2023 +0800

    新增考务数据同步时间和卡格式同步时间

commit 7a408fd3d16eefaa1bf67aba04f026dd58aae9d1
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Feb 7 17:19:36 2023 +0800

    update

commit d42c40ecff71d5296db12e13dee247be77924ee5
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Feb 7 13:42:48 2023 +0800

    上传试卷/答案并传到云阅卷

commit b67279f978f1b03620dfebc9097582676e61b398
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Feb 7 10:31:55 2023 +0800

    服务端状态新增系统模式

commit 2628141666408a5437eccb6a8465f2a2d5703e61
Author: xiatian <xiatian@qmth.com.cn>
Date:   Tue Feb 7 08:38:40 2023 +0800

    多线程事务

commit 2798ca199f999c2174e79434e4ff8423de4cea24
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Feb 6 10:08:21 2023 +0800

    扫描结果上传时问号处理

commit 60ffa37101f18f740e9d6dc7a6fd283cf26e8b0f
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Feb 3 08:37:24 2023 +0800

    撤销问号处理

commit e15f45db978dc8edb40790a0bcbabf20026a94d9
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Feb 2 11:43:59 2023 +0800

    导出结果处理问号

commit 25fa7a0b8248fe1049cf18bf941b4a5eba5aab87
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Dec 22 13:32:26 2022 +0800

    优化云阅卷模式下文件与数据上传逻辑

commit c02f4e4eb5d6d64a661b6d8fedf067873700e3e0
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Dec 20 11:19:13 2022 +0800

    上传error的重试

commit 0942491fd588c68880b579667f1e117c6e18103a
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Dec 7 16:23:57 2022 +0800

    修改下载卡格式时异常抛出方式;增加云阅卷服务地址兼容,自动去除地址结尾的/

commit 2b926381da95d5963baaae3fe442a4179666abeb
Author: xiatian <xiatian@qmth.com.cn>
Date:   Tue Dec 6 11:25:16 2022 +0800

    上传进度fixbug

commit dfd9fe5512ba35d4b3a531ff62dbe5b7c722a75a
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Nov 15 10:11:43 2022 +0800

    修改云阅卷模式下的初始化SQL,考生库索引类型修改

commit 98e631981c708a4d21fd20591893071502f6ba32
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Nov 15 09:32:11 2022 +0800

    去掉配置文件里云阅卷fileServer配置,改为云阅卷管理员登录后从接口返回内容动态获取

commit 111ab14905c421b242db24f68c4f35058cf2f9a3
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Nov 9 17:27:28 2022 +0800

    fix bugs

commit 418127ba465d0c7900acbeb4f1f9d0388c893c66
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Nov 9 16:45:42 2022 +0800

    按机构查询审核员

commit 447dfc8b83ef1e876bbaaa974647d88f86984080
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Nov 9 09:44:46 2022 +0800

    减少线程数量

commit 0e4a1bf8a2d65840b7c0b3628ebe48929d2cc206
Merge: f37c6a8 a300c7b
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Nov 9 09:28:38 2022 +0800

    Merge branch 'dev_1.0.1' of http://git.qmth.com.cn/scancloud/server.git into dev_1.0.1

commit f37c6a86a29a2784b934624263375d3eed33ba69
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Nov 9 09:28:30 2022 +0800

    fix

commit a300c7bb033e7c9afacfc0e92187dae2d769a0e8
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Nov 8 14:37:17 2022 +0800

    修改不同模式下student表的索引问题

commit f74de216dbfcfafa19444751323ea340e21cef19
Author: xiatian <xiatian@qmth.com.cn>
Date:   Tue Nov 8 13:47:10 2022 +0800

    fix

commit 4bdc35b79e9d966219c60081aa95adf64835566f
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Nov 4 18:10:51 2022 +0800

    cet导出优化

commit c4e2479848c6927261ccbe4518a00c6564e3d6af
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Nov 4 13:56:08 2022 +0800

    bugfix

commit 82f01a1cf7212ab4ec908d2d96b591e24b397db6
Merge: c52d8ff 7e8e012
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Nov 4 12:12:04 2022 +0800

    Merge remote-tracking branch 'origin/dev_1.0.1' into dev_1.0.1

commit c52d8ff8f80c376ff9bd358790728187018c5c9e
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Nov 4 12:11:57 2022 +0800

    修改考生各种状态统计查询方法,加快速度

commit 7e8e01298dc053c730048d4400b213a32d8b6ae2
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Nov 4 10:51:55 2022 +0800

    图片审核的任务锁改为60分钟

commit d6cb9b816c277209a6afc1e6e929831e65bae138
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Nov 3 15:28:20 2022 +0800

    增加sc_batch表索引

commit e30f1655970edeb9556ecc047b22723c8e8151b9
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Nov 2 11:32:13 2022 +0800

    fix bugs

commit b318a6b3ed05d5f16944a433d4af3f88b67012ad
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Nov 1 17:35:50 2022 +0800

    fix

commit d96f9cd211a844908540101b6e8d197217c09dfe
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Nov 1 10:32:46 2022 +0800

    fix bugs

commit b4bcdba8b451d51f4c2674b2df18f8489a15e775
Merge: 63f82d1 16eb6fc
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Nov 1 10:14:47 2022 +0800

    Merge remote-tracking branch 'origin/dev_1.0.1' into dev_1.0.1

commit 63f82d1329873ba149af6022abb9d71044cc6310
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Nov 1 10:14:39 2022 +0800

    修改paper重新绑定接口逻辑,增加考生不变情况下只修改paperNumber的情况

commit 16eb6fcaecbe529965e4387f80c4d7c960aba2a9
Author: xiatian <xiatian@qmth.com.cn>
Date:   Tue Nov 1 09:52:25 2022 +0800

    fix

commit 3523a64267aeb19d2eefdd5de8d6e40a492fefd0
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Oct 31 17:17:40 2022 +0800

    fix

commit b6c6615d6d579e1211e3c6f785f301e2346d2cab
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Oct 31 17:08:14 2022 +0800

    fix

commit 3f1a14b017422d48765f3d0ebebac6c046dd60e5
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Oct 31 16:39:53 2022 +0800

    fix

commit 0717a437c7d216cfd0322223a44e16572dab619c
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Oct 31 16:00:07 2022 +0800

    fix

commit 0f8f6f0026fead53fb2da543395bc190794db624
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Oct 28 15:15:18 2022 +0800

    fixbug

commit 02d62fc2b424828e19ec48117a489c57f7a2c7b1
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Oct 28 11:29:55 2022 +0800

    fix

commit 635805fcc47747c8cb7004bce9a7a0d4ab6d5578
Merge: 4472570 ec62d38
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Oct 28 11:29:44 2022 +0800

    Merge branch 'dev_1.0.1' of http://git.qmth.com.cn/scancloud/server.git into dev_1.0.1

commit 4472570fe2e78eb0485c10f0686bb519318dfd10
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Oct 28 11:29:42 2022 +0800

    merge

commit ec62d389e54256ab395e0c78d719141944d7b055
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Oct 28 10:18:04 2022 +0800

    去掉examStatus中的OK

commit 6ced6e7c00bcb3d75ec6d9ab15519fd4b11f877f
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Oct 28 09:47:40 2022 +0800

    fix

commit 2bc436c80e2e586a8c3e2f40b0a51c71af65e65f
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Oct 28 09:36:17 2022 +0800

    fix

commit 93a20410eb75b3e18b41b11e8cec4550e5c95aa7
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Oct 27 13:43:09 2022 +0800

    fix

commit 4837dc7cb4688c61286e44a42fd4f094a021c234
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Oct 27 11:59:51 2022 +0800

    fixbug

commit 3f5ac202d4e2c8aae8ad310e5a415b52ae122ab6
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Oct 27 11:52:11 2022 +0800

    fixbug

commit 605a2bd46212d18ed913a1b6aa7071474fea4d28
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Oct 21 10:42:48 2022 +0800

    去掉指定缺考模版文件,全部采用动态生成方式

commit b0db8c8c5061d88c24354fa7abeee2f8a9a66e2e
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Oct 21 10:32:42 2022 +0800

    配套tools-poi的更新,修改指定缺考名单的导入与模版导出功能调整

commit 1e4f8dbee3abd7175191f3ec87e75e3818cbc938
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Oct 20 10:45:33 2022 +0800

    修改paperType空值保存的逻辑顺序

commit 593b2ffc7e1d3ccf20351fbe43fd1baa7cb6bd65
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Oct 13 18:42:15 2022 +0800

    答题卡扫描详情查询结果增加incomplete字段

commit 98525a32ba0e0322abec4e9b0cab0cf58f64bfc3
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Oct 13 11:13:56 2022 +0800

    修改上传进度计算逻辑

commit d1a5af215d801c9849ac369643f9a433d544ccbd
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Oct 13 08:45:24 2022 +0800

    update

commit 15924cd9efb57ec5d236e927bc0f3daad17726d0
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Oct 12 16:01:54 2022 +0800

    修复考生数据上传时,由已上传重置为未上传状态的逻辑(需要云阅卷同步修改bug)

commit d91acc3d298224b4f71f495751994c7b3c6b7571
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Oct 12 13:58:14 2022 +0800

    修改裁切图上传接口逻辑,去掉操作paper必须关联考生的强制校验

commit 6f2fbad9e40adef29316504fb15d7a416553619f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Oct 11 10:56:28 2022 +0800

    更新优先根据科目paperType识别,否则为?

commit cf758f966f50ca25886f52dda626604d7e4d4cce
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Oct 11 10:25:59 2022 +0800

    fix

commit 6c2c2925e2631a8065d3925f114b2abf0e8aca24
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Oct 11 10:12:56 2022 +0800

    fix bug

commit b96bbf0887d8b902188cc5cab6ce055a87278ecc
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Oct 11 09:54:03 2022 +0800

    fix

commit a2205ffdf71cc47b2d9cb1bd6a372e8e4ecaf828
Author: luoshi <luoshi@qmth.com.cn>
Date:   Mon Oct 10 15:55:07 2022 +0800

    增加云阅卷模式用户ID冲突处理逻辑

commit d9fd9e0b490cc7c19d5fe6a1c63e553827a4b310
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Oct 10 10:23:48 2022 +0800

    update

commit 828b4dd98297266ee8b3e0cb7c9f137d79218d79
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sun Oct 9 18:09:59 2022 +0800

    修改图片检查任务数量统计逻辑,增加角色判断

commit 9f29853259a22b45d02e1d061e28e91975bab1e3
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sun Oct 9 15:50:16 2022 +0800

    修复数据上传到云阅卷时取paperType的错误

commit 01f7393cd0e502bf08a772292e2961c113ef532b
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sat Oct 8 17:43:30 2022 +0800

    扫描详情查询对象增加examStatus字段

commit f036f84045ebfc8d8dd20074e5717cc75082510c
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sat Oct 8 17:35:41 2022 +0800

    扫描详情查询对象增加examSiteName字段

commit 5c955c94c698a8aea15f794200c21f3de2ff6d02
Author: luoshi <luoshi@qmth.com.cn>
Date:   Sat Oct 8 17:20:06 2022 +0800

    答题卡扫描统计查询,增加分组字段的非null判断

commit 1131aab8da14fe047d68d89590b062bc8f7b44df
Merge: c0650f9 4533799
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 30 17:48:48 2022 +0800

    Merge branch 'dev_1.0.1' of http://git.qmth.com.cn/scancloud/server.git into dev_1.0.1

commit c0650f9328459e0a2642ab4336cfe46878283108
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 30 17:48:36 2022 +0800

    mapper里返回json字段需要处理

commit 45337992933baf9d185a4985d464363b457b5bfb
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Sep 30 17:39:27 2022 +0800

    修改考生同步逻辑,整合重复代码,增加可选字段的空数据判断

commit 1a1593a1470b3f5bdeb1ba26e677a87d99e500c4
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Sep 30 12:42:50 2022 +0800

    修改考务数据同步线程名

commit c6b5c156b92946527d73e58b160ad87e66c107a1
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Sep 30 12:40:27 2022 +0800

    考务数据同步过程增加卡格式同步

commit fe08cb566b9ea560a0f55e958ca9895b6589b356
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Sep 29 18:28:03 2022 +0800

    缺考查询接口支持不传任何缺考状态

commit 34879e4f7abc88e163efa786d8d22987d85b8fc7
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Sep 29 10:30:28 2022 +0800

    增加考试初始化时图片抽查比例设置

commit 7dd1d53f79b8ae86e14fa9d51e197bb8a342edae
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Sep 29 10:23:24 2022 +0800

    卡格式同步增加paperCount与singlePage属性更新逻辑

commit fb604c7dcc002b82180e44e4569f65f7826df435
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Sep 28 14:54:27 2022 +0800

    bugfix

commit f133dce95857c31be1c7af633f8e4bd01f01f201
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Sep 28 14:46:46 2022 +0800

    修正已扫描考生再次考务同步过程中的报错

commit b4525c66540d5803990485594de698a75ebd5f47
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Sep 28 13:49:16 2022 +0800

    修改sc_question表结构定义

commit 7c7dfdeb937aea31acb82a32f1b32a692c047c1d
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Sep 28 13:35:16 2022 +0800

    考生上传batchCode字段填充为首张paper所属batchId

commit b1661b4a89565b0e01d37795ca5fd530ff76a153
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Sep 28 11:14:11 2022 +0800

    修改试卷结构同步接口地址

commit 543054ee8a29db3582791506533ede4cd9928746
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Sep 27 09:39:38 2022 +0800

    update

commit 4bc5c2c20f8a5f1ef6141bc8424172ac41d36f2e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Sep 26 17:30:12 2022 +0800

    fix

commit 94b365874d26f287ce62f9753d9c98ecaca2b335
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Sep 26 16:19:06 2022 +0800

    FIX

commit 466228860edcab9ccf4c82051d57276cad020536
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Sep 26 16:02:50 2022 +0800

    fix bug

commit 5f97c95a94c4ae9e25e9f21bd0e1f52f6b184103
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Sep 23 18:53:34 2022 +0800

    bugfix

commit 45ab14f60ed51b6c79530e3a538b6d2e3181b121
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Sep 21 10:09:36 2022 +0800

    fix

commit ae09d53d7feefb30e8d9d2455009855d5bc6aa7b
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Sep 20 10:38:51 2022 +0800

    fix

commit d10f2e5f81934ceb2d483e341d39359bbb44181c
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Sep 20 09:17:50 2022 +0800

    fixbug

commit bfcc80d2d97da87c48804d3800a3594c613c8efc
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Sep 19 16:53:59 2022 +0800

    fix bug

commit 7d7898ae226034b45ddaa83cef7c15c8054256d3
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 19 15:30:06 2022 +0800

    fix

commit 59d40806fcd2f7785da56c74ea17f84963632925
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 19 15:06:00 2022 +0800

    fix

commit f70521d9d39eb9deafe768c5dddfa9a7e0b5be8e
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 19 14:33:22 2022 +0800

    fix

commit dfd6b0998ca5c6edbabff40fae5edd3da2cbd4d8
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 19 14:30:12 2022 +0800

    fix

commit e18c595a6802846d8a8388f2b2d927503ec06c2c
Merge: 0ab59f3 938ecdd
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 19 14:08:06 2022 +0800

    Merge branch 'dev_1.0.1' of http://git.qmth.com.cn/scancloud/server.git into dev_1.0.1

commit 0ab59f318a9f3e305131bf87916bcc6bed3df427
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 19 14:07:56 2022 +0800

    fix

commit 938ecdd6f661d521a7f8363de62add338a75118f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 16 15:38:20 2022 +0800

    fixbugs

commit be21a636b80b2bebf5d4fee36d2e6f74505bb7b1
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 16 15:30:00 2022 +0800

    fix

commit 0736dec648706769b425c8aafab08a77ce44bf3f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 16 15:20:19 2022 +0800

    fix

commit daf64041853da3649b5be7afd9c8e91cba1bbf8f
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 16 09:27:01 2022 +0800

    fix

commit 0e18106342e2842bb0fd70bab61ab86a13f5dafd
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Sep 15 18:02:26 2022 +0800

    fix

commit 5666d12ff3c6f182dc073f9b9c745a0f9a59ace4
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Sep 15 17:28:47 2022 +0800

    fix

commit e082f403637ad3e98e6d513440aca6421480d4f9
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Sep 15 17:27:57 2022 +0800

    fix

commit a9f14a142403e340cd12f4d3e86a0192fac0a839
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Sep 15 17:09:52 2022 +0800

    update

commit 55a984430a9f7a34cb8fde7d2a2e353cd69a0566
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Sep 15 17:06:53 2022 +0800

    fixbugs

commit 841ad79d3879250b8d3029d048db1289c73efc61
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Sep 15 16:54:45 2022 +0800

    fix

commit 020c6005667bba736819fc22cea87117327b6714
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Sep 15 15:37:53 2022 +0800

    fix

commit 69096ddb75226ea2bd164e68b3afbf65d2e7a7b3
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Sep 15 15:27:49 2022 +0800

    fix

commit 48622332cb4b0fa880fa51146f6b6d9540f92a72
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Sep 15 15:19:08 2022 +0800

    fix

commit 944774cd6f44d6d339d41ccca8701b2db199b997
Author: xiatian <xiatian@qmth.com.cn>
Date:   Thu Sep 15 14:37:48 2022 +0800

    cet转存

commit d6265839d818931494de5698548878772968388d
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Sep 15 10:21:07 2022 +0800

    fix bugs

commit c35e8ceafdb90775888f1f068edd157303b2e3e7
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Sep 14 16:27:25 2022 +0800

    update

commit 9d3a855dacf52dddfc5fb1fd27c9526115d4257c
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Sep 14 15:42:38 2022 +0800

    update

commit 4c0ff8433e25260dd28483292f17dcc3b0d3aaac
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 9 16:13:21 2022 +0800

    fix

commit 8ff5c2d08ee1d36176b816271c4fef22c14d1e9b
Merge: 0d8e11d fa73903
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 9 15:39:42 2022 +0800

    Merge branch 'dev_1.0.1' of http://git.qmth.com.cn/scancloud/server into dev_1.0.1

    * 'dev_1.0.1' of http://git.qmth.com.cn/scancloud/server:
      fix

commit 0d8e11d6167ee30e03ed5985158a80ebfe239be8
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 9 15:39:32 2022 +0800

    fixbugs

commit fa7390302772eed6a75c3b928c614af1d6a686ed
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 9 14:32:56 2022 +0800

    fix

commit dc932aee649476a69698497c8da01bb25fe602f2
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 9 14:12:39 2022 +0800

    fixbugs

commit e279adf667e905ba2f70fdb8261b733f678c6dd3
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 9 14:03:01 2022 +0800

    fix

commit bfccf1536aff30512d13874d9aee87677453f715
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 9 13:57:25 2022 +0800

    fixbug

commit 731f1990c981b30248309e6522529398c87b1ba7
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 9 11:34:18 2022 +0800

    试卷结构相关;获取图片审核任务;

commit 96a5c44e98441e6966a00fb9b5dcf990c116e251
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 9 09:31:35 2022 +0800

    fix

commit a29ef46bb38363edb3519b82c37e88cc099542e8
Author: ting.yin <yinting@qmth.com.cn>
Date:   Thu Sep 8 11:33:25 2022 +0800

    fixbugs

commit 376773065c3aec540a9c03e435502318f1194a97
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Sep 7 17:38:51 2022 +0800

    答题卡扫描图片检查任务提交
    答题卡扫描图片检查任务释放

commit 2d2d7daf889a756bea9b746fe5a70f2c3a3b2132
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Sep 7 16:42:44 2022 +0800

    fix

commit f213411d6b12b036aebd2c5a3fea68914eb2e0ce
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Sep 7 14:59:33 2022 +0800

    管理员更新原图

commit 6396fa0d4b1680210fb6d3f3d00e712d38097e42
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Sep 7 14:27:45 2022 +0800

    fix

commit 00978cd61cec801f3bd5080667bc2fd8daa25b90
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Sep 7 13:57:08 2022 +0800

    fix

commit 90889c2bdb97b37e0d01a08ef624fe2df63eb0ea
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Sep 7 13:35:36 2022 +0800

    fix

commit 07ef41cfe4051ee37957e55d1315b52878141edf
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Sep 7 09:59:29 2022 +0800

    修改答题卡扫描图片抽查比例
    答题卡扫描图片检查任务状态

commit 1249cb6a95554e30d3bfbb2d6acdbd0878eb8464
Author: xiatian <xiatian@qmth.com.cn>
Date:   Tue Sep 6 13:44:06 2022 +0800

    fix

commit 74b9372b7cc0509b03ce4f4adce39b66c19cda0a
Author: xiatian <xiatian@qmth.com.cn>
Date:   Tue Sep 6 11:41:24 2022 +0800

    cet导出扫描结果

commit d33a29a01ae5d21563422b081912ef8760e936b1
Author: xiatian <xiatian@qmth.com.cn>
Date:   Tue Sep 6 11:14:40 2022 +0800

    扫描结果导出

commit 6e20a81c97b0dad8d7a561b8395f7a05a791abfd
Author: xiatian <xiatian@qmth.com.cn>
Date:   Tue Sep 6 10:37:59 2022 +0800

    fix

commit 37c2346e361aeec0c009927776d538c523cbb493
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Sep 6 10:35:27 2022 +0800

    新增审核员相关

commit f9d1eb168c0a81b82389c6fbb5ee25e4fffa6593
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 5 16:30:38 2022 +0800

    fix

commit a9479c83c5b294289a644a526e3f996e1ab0a648
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 5 15:35:11 2022 +0800

    fix

commit ea7f122aad352607531938adb326c4abc2c4be63
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 5 15:31:16 2022 +0800

    导出cet评卷数据

commit a587e691486f613abc1b21673ba9db78bd79c282
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Sep 5 13:47:54 2022 +0800

    update

commit 28df0b77442db1c4aff16d712f299e4bf5a9c152
Author: xiatian <xiatian@qmth.com.cn>
Date:   Mon Sep 5 11:45:47 2022 +0800

    fix

commit ec79dd1457ca50ee3d399d2f18ffbd88beb4d8f6
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 2 17:43:15 2022 +0800

    卡格式解析新增sliceName

commit a4f404e3b59947f3f4fa7a65cdb2a9e5917349d9
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 2 17:24:43 2022 +0800

    启用新考试

commit cee849394ab81de0eb7bfaf95667f9491c2eaee1
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 2 17:13:18 2022 +0800

    fix

commit 76b8f2543db5183645775aa36f8c96a78bf76e9b
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 2 17:11:09 2022 +0800

    获取考试详情

commit 8a3dc90fb21f6f7b8a8b44d23316a8bf3f9c6b2e
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 2 17:08:18 2022 +0800

    fix

commit ea0c239d96216ba64f9a3227c108b77ab1a0dafc
Author: ting.yin <yinting@qmth.com.cn>
Date:   Fri Sep 2 16:20:23 2022 +0800

    获取考生信息
    获取所有校区
    获取所有考点

commit 194e48a983b9c190fce9cc18f3ea3ab0f2b1d45a
Author: xiatian <xiatian@qmth.com.cn>
Date:   Fri Sep 2 14:27:58 2022 +0800

    cet缺考导入

commit d6f10e7effed9ec5456c49f021d13d03aaf260ec
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Aug 30 16:43:07 2022 +0800

    修改一些数据模型

commit 4c41a2bfc44b59ae4873b26e2164353013ae1bdf
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Aug 22 13:45:34 2022 +0800

    update

commit c75f3d84b1c68a6c32fdc2b6cac5eb972558898a
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Aug 19 14:56:41 2022 +0800

    修改适配文件模型保存方法

commit db12110af4113f4de48887897e1526199645c808
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Aug 19 14:34:38 2022 +0800

    调整卡格式适配文件表结构,修改device保存逻辑与管理员更新逻辑,增加管理员单独的适配文件上传接口

commit d2733613d1fd591fa8d8c9e078f9f192afabc51c
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Aug 17 15:59:15 2022 +0800

    update

commit ff3ca6e10a30ec1815efc810c685d05ed8e85789
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Aug 17 11:49:46 2022 +0800

    fixbugs

commit 12d2e96b918ac15227d143b39740da07843850b9
Author: ting.yin <yinting@qmth.com.cn>
Date:   Wed Aug 17 11:43:39 2022 +0800

    update

commit 4f3912200e4f8680e224683c69c3978abfb71b5e
Author: ting.yin <yinting@qmth.com.cn>
Date:   Tue Aug 9 17:15:45 2022 +0800

    新增适配卡格式相关

commit b9297a0c7b3728468b5d930a6cfae457fe6b61bb
Author: ting.yin <yinting@qmth.com.cn>
Date:   Mon Aug 8 15:36:08 2022 +0800

    update

commit 27bc44b8d1b60205a5a3e3968259e28da5f51937
Author: luoshi <luoshi@qmth.com.cn>
Date:   Mon Jul 25 16:31:47 2022 +0800

    导入工具兼容图片转存未配置情况

commit 0404b87c10a138dd9bf85764711d54c776fdcdf5
Author: luoshi <luoshi@qmth.com.cn>
Date:   Fri Jul 22 14:43:15 2022 +0800

    修改批次详情查询接口,增加批次基本信息字段

commit 4c0f12e62323e935523d01811af78623bb18f081
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Jul 21 17:48:25 2022 +0800

    修改缺考汇总查询SQL

commit c1758833a913299469b52cb4f92c69028512af19
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Jul 21 17:45:27 2022 +0800

    修改缺考汇总查询SQL

commit c044cd968497efd2954115d978457e169973ea14
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Jul 21 14:48:37 2022 +0800

    修改缺考汇总查询SQL

commit 5d99754bc1dac352d641b812b06978745973e3a9
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Jul 21 14:31:39 2022 +0800

    扩展缺考汇总接口,增加可选条件筛选支持

commit 9a1900929b06a839337cea67e295c23074d513b5
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 19 19:05:50 2022 +0800

    二次识别接口增加mismatch字段

commit 5aef185beee69659727529684bfd28a3c690a51d
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 19 17:48:50 2022 +0800

    修改OMR识别结果接口,兼容单个page修改的情况

commit d9aa5d16341abb0948f4d27343cb0707291dd3cc
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 19 16:40:45 2022 +0800

    修改二次识别接口,兼容缺页的考生

commit 2eb0d6d9db57625fae37625117967e647aca1a67
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 19 15:46:07 2022 +0800

    修改考生扫描详情查询bug

commit 2f2550f8fe4b4aaffa9ee384dec881c364cfa34b
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 19 15:24:59 2022 +0800

    修改二次识别接口配置

commit c85a2863ceb564913dec4c19bad65d93044d1e57
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Jul 14 17:07:38 2022 +0800

    修改answer_card的slice_config字段为必填

commit 420416a4315faa930ac28bea64fd7e9c7442f0a1
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Jul 14 13:40:17 2022 +0800

    修改卡格式解析中裁切坐标构造的bug

commit 76f73b34950dccecb80a0fad8a7c1f3de65ee748
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Jul 14 13:40:04 2022 +0800

    修改卡格式解析中裁切坐标构造的bug

commit 9bc052d2e48f1ecd3f260b33ffc43a358c42127d
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jul 13 15:39:03 2022 +0800

    云阅卷接口上传考生扫描结果增加cardNumber字段

commit e1ac51155c048f8ba7c5253a37cd4d78064aa913
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jul 13 13:48:09 2022 +0800

    增加卡格式内容模型CardFile,提供裁切坐标与客观题/选做题结构获取方法;answer_card增加slice_config字段,并与云阅卷后台同步;补充卡格式解析与同步相关逻辑

commit 49a808e1994786f3262a7875caeb13a642930a4f
Author: luoshi <luoshi@qmth.com.cn>
Date:   Thu Jul 7 14:58:26 2022 +0800

    迁移图片转存配置到考试中,增加新的配置项维护

commit 448329ab65ed5f6de4d8d9f1fd5b286a70919d5c
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jul 6 18:21:47 2022 +0800

    增加缺考核对、扫描批次、扫描详情三个查询对应的全量标识获取接口;修改数据导出接口逻辑,改为循环分页模式;优化xml文件,提取公共sql片断;

commit 67a336e43aded8deeb85928a1665ed03ec5d441c
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jul 6 11:04:15 2022 +0800

    修正部分修改page结果时去掉强制检查转换paperType的逻辑

commit e5a824cc1c0a924efcaf2802bb2a7928100577cb
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jul 6 10:34:12 2022 +0800

    去掉paper_page表无用的exam_id字段;精简部分表的无用字段

commit 65e26e4db5ab7a6d5a2769a27503e9ad7b11ad40
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 5 18:13:11 2022 +0800

    修改删除单张接口地址与实体名称;增加指定缺考操作的状态判断

commit 0b457cba266953438d562836761f047c0f457a18
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 5 17:33:55 2022 +0800

    修改配置文件默认配置

commit fc93799290fa4c2545f14caea8704242dff616ee
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 5 17:31:04 2022 +0800

    修改BatchPaperEntity类型

commit 580fa757d89e71f6351b007b6d6bcf3d71ae5d93
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 5 15:51:14 2022 +0800

    二次识别补充卡格式更新;部分接口名称调整

commit 1b0f7e537125d7f610916d078034f95189e8364c
Author: luoshi <luoshi@qmth.com.cn>
Date:   Tue Jul 5 14:35:02 2022 +0800

    sc_batch_paper与sc_paper结构调整;合并paper、page、student等公共更新代码;增加二次识别接口

commit 3d35a1d4be6e9d3afdb4bb7096dc811d36251e57
Author: luoshi <luoshi@qmth.com.cn>
Date:   Wed Jun 29 15:38:54 2022 +0800

    修复删除所有扫描结果时,考生属性字段更新错误

commit d0628ca13fa57f0d45b1c4eb18c1434c24b77a94
Author: xiatian <xiatian@qmth.com.cn>
Date:   Wed Jun 29 11:00:42 2022 +0800

    jsonhandle
luoshi 2 gadi atpakaļ
vecāks
revīzija
5b1dc359f5
100 mainītis faili ar 4434 papildinājumiem un 2101 dzēšanām
  1. 7 0
      db/init_markingcloud.sql
  2. 12 0
      db/init_standalone.sql
  3. 109 53
      db/scan_db.sql
  4. 0 3
      db/system_config_cet.sql
  5. 0 1
      db/system_config_marking.sql
  6. 0 3
      db/system_config_ow.sql
  7. 88 60
      src/main/java/cn/com/qmth/scancloud/bean/AbsentQueryDomain.java
  8. 25 14
      src/main/java/cn/com/qmth/scancloud/bean/AnswerDeleteDomain.java
  9. 200 117
      src/main/java/cn/com/qmth/scancloud/bean/AnswerQueryDomain.java
  10. 26 15
      src/main/java/cn/com/qmth/scancloud/bean/BatchCreateDomain.java
  11. 55 37
      src/main/java/cn/com/qmth/scancloud/bean/BatchQueryDomain.java
  12. 119 73
      src/main/java/cn/com/qmth/scancloud/bean/ExamConfigDomain.java
  13. 43 0
      src/main/java/cn/com/qmth/scancloud/bean/ImportCetAbsentDomain.java
  14. 122 86
      src/main/java/cn/com/qmth/scancloud/bean/ImportExamDomain.java
  15. 22 0
      src/main/java/cn/com/qmth/scancloud/bean/ImportStudentDomain.java
  16. 25 16
      src/main/java/cn/com/qmth/scancloud/bean/MismatchQueryDomain.java
  17. 25 15
      src/main/java/cn/com/qmth/scancloud/bean/MismatchToggleDomain.java
  18. 39 22
      src/main/java/cn/com/qmth/scancloud/bean/PageDeleteDomain.java
  19. 42 0
      src/main/java/cn/com/qmth/scancloud/bean/SubjectConfigDomain.java
  20. 53 30
      src/main/java/cn/com/qmth/scancloud/bean/answersave/AnswerDomain.java
  21. 95 64
      src/main/java/cn/com/qmth/scancloud/bean/answersave/AnswerPage.java
  22. 80 23
      src/main/java/cn/com/qmth/scancloud/bean/answersave/AnswerPaper.java
  23. 14 14
      src/main/java/cn/com/qmth/scancloud/bean/answersave/StringResult.java
  24. 29 0
      src/main/java/cn/com/qmth/scancloud/bean/card/AnswerArea.java
  25. 110 0
      src/main/java/cn/com/qmth/scancloud/bean/card/CardFile.java
  26. 86 0
      src/main/java/cn/com/qmth/scancloud/bean/card/CardPage.java
  27. 14 0
      src/main/java/cn/com/qmth/scancloud/bean/card/CardPageWrapper.java
  28. 59 0
      src/main/java/cn/com/qmth/scancloud/bean/card/FillArea.java
  29. 40 0
      src/main/java/cn/com/qmth/scancloud/bean/card/FillItem.java
  30. 27 0
      src/main/java/cn/com/qmth/scancloud/bean/card/Locator.java
  31. 41 0
      src/main/java/cn/com/qmth/scancloud/bean/card/SliceConfig.java
  32. 40 22
      src/main/java/cn/com/qmth/scancloud/bean/omredit/OmrEditDomain.java
  33. 68 42
      src/main/java/cn/com/qmth/scancloud/bean/omredit/OmrEditPage.java
  34. 53 15
      src/main/java/cn/com/qmth/scancloud/bean/omredit/OmrEditPaper.java
  35. 68 45
      src/main/java/cn/com/qmth/scancloud/bean/papermigrate/PaperMigrateDomain.java
  36. 97 57
      src/main/java/cn/com/qmth/scancloud/bean/papermigrate/PaperMigratePage.java
  37. 67 0
      src/main/java/cn/com/qmth/scancloud/bean/refix/AnswerRefixDomain.java
  38. 108 0
      src/main/java/cn/com/qmth/scancloud/bean/refix/PageRefixDomain.java
  39. 57 0
      src/main/java/cn/com/qmth/scancloud/bean/refix/PaperRefixDomain.java
  40. 40 33
      src/main/java/cn/com/qmth/scancloud/client/MarkingcloudApiClient.java
  41. 6 1
      src/main/java/cn/com/qmth/scancloud/client/MarkingcloudApiConfiguration.java
  42. 6 15
      src/main/java/cn/com/qmth/scancloud/client/MarkingcloudProperties.java
  43. 40 19
      src/main/java/cn/com/qmth/scancloud/controller/BaseController.java
  44. 82 59
      src/main/java/cn/com/qmth/scancloud/controller/SystemController.java
  45. 13 25
      src/main/java/cn/com/qmth/scancloud/controller/admin/ArbitrateController.java
  46. 54 0
      src/main/java/cn/com/qmth/scancloud/controller/admin/AuditorController.java
  47. 2 1
      src/main/java/cn/com/qmth/scancloud/controller/admin/AuthController.java
  48. 38 23
      src/main/java/cn/com/qmth/scancloud/controller/admin/CardController.java
  49. 142 166
      src/main/java/cn/com/qmth/scancloud/controller/admin/CheckController.java
  50. 73 0
      src/main/java/cn/com/qmth/scancloud/controller/admin/CheckImageController.java
  51. 48 41
      src/main/java/cn/com/qmth/scancloud/controller/admin/ExamController.java
  52. 6 3
      src/main/java/cn/com/qmth/scancloud/controller/admin/ExamWorkController.java
  53. 106 0
      src/main/java/cn/com/qmth/scancloud/controller/admin/FileController.java
  54. 139 81
      src/main/java/cn/com/qmth/scancloud/controller/admin/ScanAnswerController.java
  55. 40 44
      src/main/java/cn/com/qmth/scancloud/controller/admin/ScannerController.java
  56. 33 1
      src/main/java/cn/com/qmth/scancloud/controller/admin/ToolController.java
  57. 14 22
      src/main/java/cn/com/qmth/scancloud/controller/admin/VerifyController.java
  58. 79 75
      src/main/java/cn/com/qmth/scancloud/controller/scan/AnswerController.java
  59. 16 27
      src/main/java/cn/com/qmth/scancloud/controller/scan/OmrTaskController.java
  60. 9 12
      src/main/java/cn/com/qmth/scancloud/controller/scan/ScannerAuthController.java
  61. 13 15
      src/main/java/cn/com/qmth/scancloud/controller/scan/StudentController.java
  62. 9 0
      src/main/java/cn/com/qmth/scancloud/dao/AdapteFileDao.java
  63. 25 16
      src/main/java/cn/com/qmth/scancloud/dao/BatchDao.java
  64. 15 0
      src/main/java/cn/com/qmth/scancloud/dao/BatchPaperDao.java
  65. 16 16
      src/main/java/cn/com/qmth/scancloud/dao/PaperDao.java
  66. 8 0
      src/main/java/cn/com/qmth/scancloud/dao/PaperPageDao.java
  67. 8 0
      src/main/java/cn/com/qmth/scancloud/dao/QuestionDao.java
  68. 31 21
      src/main/java/cn/com/qmth/scancloud/dao/StudentDao.java
  69. 78 0
      src/main/java/cn/com/qmth/scancloud/entity/AdapteFileEntity.java
  70. 27 4
      src/main/java/cn/com/qmth/scancloud/entity/AnswerCardEntity.java
  71. 33 2
      src/main/java/cn/com/qmth/scancloud/entity/BatchEntity.java
  72. 86 0
      src/main/java/cn/com/qmth/scancloud/entity/BatchPaperEntity.java
  73. 64 36
      src/main/java/cn/com/qmth/scancloud/entity/ExamEntity.java
  74. 12 0
      src/main/java/cn/com/qmth/scancloud/entity/FilePropertyEntity.java
  75. 2 2
      src/main/java/cn/com/qmth/scancloud/entity/PackageResultEntity.java
  76. 0 31
      src/main/java/cn/com/qmth/scancloud/entity/PaperEntity.java
  77. 132 131
      src/main/java/cn/com/qmth/scancloud/entity/PaperPageEntity.java
  78. 108 0
      src/main/java/cn/com/qmth/scancloud/entity/QuestionEntity.java
  79. 60 0
      src/main/java/cn/com/qmth/scancloud/entity/StudentEntity.java
  80. 13 5
      src/main/java/cn/com/qmth/scancloud/entity/StudentPaperEntity.java
  81. 16 1
      src/main/java/cn/com/qmth/scancloud/entity/SubjectEntity.java
  82. 9 24
      src/main/java/cn/com/qmth/scancloud/entity/SystemConfigEntity.java
  83. 9 1
      src/main/java/cn/com/qmth/scancloud/entity/UserEntity.java
  84. 30 0
      src/main/java/cn/com/qmth/scancloud/enums/CheckStatus.java
  85. 16 9
      src/main/java/cn/com/qmth/scancloud/enums/ConditionType.java
  86. 3 1
      src/main/java/cn/com/qmth/scancloud/enums/ExamMode.java
  87. 26 0
      src/main/java/cn/com/qmth/scancloud/enums/ExamStatus.java
  88. 42 40
      src/main/java/cn/com/qmth/scancloud/enums/FileType.java
  89. 14 7
      src/main/java/cn/com/qmth/scancloud/enums/GroupType.java
  90. 2 0
      src/main/java/cn/com/qmth/scancloud/enums/ImageTransferMode.java
  91. 3 0
      src/main/java/cn/com/qmth/scancloud/exception/NotFoundExceptions.java
  92. 1 1
      src/main/java/cn/com/qmth/scancloud/exception/ParameterExceptions.java
  93. 2 0
      src/main/java/cn/com/qmth/scancloud/exception/UnauthorizedExceptions.java
  94. 10 0
      src/main/java/cn/com/qmth/scancloud/model/AnswerCardInfo.java
  95. 90 57
      src/main/java/cn/com/qmth/scancloud/model/DataUploadDto.java
  96. 31 0
      src/main/java/cn/com/qmth/scancloud/model/ManualAbsentImportDTO.java
  97. 10 4
      src/main/java/cn/com/qmth/scancloud/model/StudentInfo.java
  98. 11 2
      src/main/java/cn/com/qmth/scancloud/model/UserInfo.java
  99. 62 46
      src/main/java/cn/com/qmth/scancloud/multithread/BatchConsumer.java
  100. 156 154
      src/main/java/cn/com/qmth/scancloud/multithread/BatchProducer.java

+ 7 - 0
db/init_markingcloud.sql

@@ -0,0 +1,7 @@
+use scan_db;
+CREATE INDEX `exam_student_subject` ON sc_student (`exam_id`, `student_code`, `subject_code`);
+
+INSERT INTO `sc_system_config` (`id`, `create_time`, `update_time`, `creator_id`, `updater_id`, `scanner_enable_login`,
+                                `scanner_password`, `system_mode`, `client_version`, `client_uri`, `client_md5`,
+                                `client_update_time`)
+VALUES (1, NULL, NULL, NULL, NULL, 1, NULL, 'MARKING_CLOUD', NULL, NULL, NULL, NULL);

+ 12 - 0
db/init_standalone.sql

@@ -0,0 +1,12 @@
+use scan_db;
+CREATE UNIQUE INDEX `exam_student_subject` ON sc_student (`exam_id`, `student_code`, `subject_code`);
+
+INSERT INTO `sc_user` (`school_id`, `role`, `create_time`, `update_time`, `enable`, `login_name`, `name`, `password`,
+                       `creator_id`, `updater_id`)
+VALUES (1, 'SCHOOL_ADMIN', unix_timestamp(now()) * 1000, unix_timestamp(now()) * 1000, 1, 'admin', '学校管理员', '123456', 1,
+        1);
+
+INSERT INTO `sc_system_config` (`id`, `create_time`, `update_time`, `creator_id`, `updater_id`, `scanner_enable_login`,
+                                `scanner_password`, `system_mode`, `client_version`, `client_uri`, `client_md5`,
+                                `client_update_time`)
+VALUES (1, NULL, NULL, NULL, NULL, 1, NULL, 'STANDALONE', NULL, NULL, NULL, NULL);

+ 109 - 53
db/init.sql → db/scan_db.sql

@@ -1,3 +1,4 @@
+use scan_db;
 -- ----------------------------
 -- Table structure for sc_answer_card
 -- ----------------------------
@@ -14,6 +15,8 @@ CREATE TABLE `sc_answer_card`
     `source`       varchar(16)  NOT NULL,
     `need_adapte`  bit(1)       NOT NULL,
     `single_page`  bit(1)       NOT NULL,
+    `slice_config` text         NOT NULL,
+    `slice_name`   text         DEFAULT NULL,
     `parameter`    text         DEFAULT NULL,
     `remark`       varchar(255) DEFAULT NULL,
     `creator_id`   bigint       DEFAULT NULL,
@@ -40,11 +43,14 @@ CREATE TABLE `sc_exam`
     `allow_unexist_paper`        bit(1)       NOT NULL,
     `answer_front_card_type`     int          NOT NULL,
     `enable_single_page_answer`  bit(1)       NOT NULL,
-    `answer_paper_number_figure` int          NOT NULL,
     `enable_upload`              bit(1)       NOT NULL,
     `enable_sync_verify`         bit(1)       NOT NULL,
     `paper_type_barcode_content` text        DEFAULT NULL,
     `absent_barcode_content`     varchar(16) DEFAULT NULL,
+    `image_transfer_mode`        varchar(32) DEFAULT NULL,
+    `image_check_ratio`          double      DEFAULT NULL,
+	`data_sync_time`             bigint      DEFAULT NULL,
+	`card_sync_time`             bigint      DEFAULT NULL,
     `creator_id`                 bigint      DEFAULT NULL,
     `updater_id`                 bigint      DEFAULT NULL,
     `create_time`                bigint      DEFAULT NULL,
@@ -70,10 +76,10 @@ CREATE TABLE `sc_omr_task`
     `status`       varchar(16) NOT NULL,
     `device`       varchar(64) DEFAULT NULL,
     `pages`        text        NOT NULL,
-    `creator_id`   bigint DEFAULT NULL,
-    `updater_id`   bigint DEFAULT NULL,
-    `create_time`  bigint DEFAULT NULL,
-    `update_time`  bigint DEFAULT NULL,
+    `creator_id`   bigint      DEFAULT NULL,
+    `updater_id`   bigint      DEFAULT NULL,
+    `create_time`  bigint      DEFAULT NULL,
+    `update_time`  bigint      DEFAULT NULL,
     PRIMARY KEY (`id`),
     UNIQUE KEY `group_paper` (`group_id`, `paper_id`),
     KEY `exam_status` (`exam_id`, `status`, `student_id`)
@@ -143,7 +149,6 @@ CREATE TABLE `sc_system_config`
     `id`                   bigint      NOT NULL AUTO_INCREMENT,
     `scanner_enable_login` bit(1)       DEFAULT NULL,
     `scanner_password`     varchar(16)  DEFAULT NULL,
-    `image_transfer_mode`  varchar(16)  DEFAULT NULL,
     `system_mode`          varchar(16) NOT NULL,
     `client_version`       varchar(16)  DEFAULT NULL,
     `client_uri`           varchar(255) DEFAULT NULL,
@@ -191,42 +196,58 @@ CREATE TABLE `sc_scanner_card`
 DROP TABLE IF EXISTS `sc_batch`;
 CREATE TABLE `sc_batch`
 (
-    `id`             bigint      NOT NULL AUTO_INCREMENT,
-    `exam_id`        bigint      NOT NULL,
-    `device`         varchar(64) NOT NULL,
-    `package_code`   varchar(64) DEFAULT NULL,
-    `scan_count`     int         NOT NULL,
-    `assigned_count` int         NOT NULL,
-    `status`         varchar(16) NOT NULL,
-    `verify_status`  varchar(16) DEFAULT NULL,
-    `creator_id`     bigint      DEFAULT NULL,
-    `updater_id`     bigint      DEFAULT NULL,
-    `create_time`    bigint      DEFAULT NULL,
-    `update_time`    bigint      DEFAULT NULL,
-    PRIMARY KEY (`id`)
+    `id`                  bigint      NOT NULL AUTO_INCREMENT,
+    `exam_id`             bigint      NOT NULL,
+    `device`              varchar(64) NOT NULL,
+    `package_code`        varchar(64) DEFAULT NULL,
+    `scan_count`          int         NOT NULL,
+    `assigned_count`      int         NOT NULL,
+    `status`              varchar(16) NOT NULL,
+    `verify_status`       varchar(16) DEFAULT NULL,
+    `check_status`        varchar(16) DEFAULT NULL,
+    `check_image_user_id` bigint      DEFAULT NULL,
+    `check_image_time`    bigint      DEFAULT NULL,
+    `creator_id`          bigint      DEFAULT NULL,
+    `updater_id`          bigint      DEFAULT NULL,
+    `create_time`         bigint      DEFAULT NULL,
+    `update_time`         bigint      DEFAULT NULL,
+    PRIMARY KEY (`id`),
+    KEY `exam_verify_status` (`exam_id`, `verify_status`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4;
+
+DROP TABLE IF EXISTS `sc_batch_paper`;
+CREATE TABLE `sc_batch_paper`
+(
+    `batch_id`     bigint(20) NOT NULL,
+    `student_id`   bigint(20) NOT NULL,
+    `paper_number` int(11)    NOT NULL,
+    `paper_id`     bigint(20) NOT NULL,
+    `card_number`  int(11)    NOT NULL,
+    `assigned`     bit(1)     NOT NULL,
+    `need_check`   bit(1) DEFAULT NULL,
+    PRIMARY KEY (`batch_id`, `student_id`, `paper_number`),
+    UNIQUE KEY `paper_id` (`paper_id`)
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4;
 
 DROP TABLE IF EXISTS `sc_paper`;
 CREATE TABLE `sc_paper`
 (
-    `id`              bigint       NOT NULL AUTO_INCREMENT,
-    `exam_id`         bigint       NOT NULL,
-    `student_id`      bigint       NOT NULL,
-    `batch_id`        bigint       NOT NULL,
-    `card_number`     int          NOT NULL,
-    `exam_number`     varchar(128) NOT NULL,
-    `number`          int          NOT NULL,
-    `page_count`      int          NOT NULL,
-    `mismatch`        bit(1)       NOT NULL,
-    `assigned`        bit(1)       NOT NULL,
-    `question_filled` bit(1)       NOT NULL,
+    `id`              bigint NOT NULL AUTO_INCREMENT,
+    `exam_id`         bigint NOT NULL,
+    `card_number`     int    NOT NULL,
+    `number`          int    NOT NULL,
+    `page_count`      int    NOT NULL,
+    `mismatch`        bit(1) NOT NULL,
+    `assigned`        bit(1) NOT NULL,
+    `question_filled` bit(1) NOT NULL,
     `creator_id`      bigint DEFAULT NULL,
     `updater_id`      bigint DEFAULT NULL,
     `create_time`     bigint DEFAULT NULL,
     `update_time`     bigint DEFAULT NULL,
     PRIMARY KEY (`id`),
-    UNIQUE KEY `batch_student_number` (`batch_id`, `student_id`, `number`)
+    KEY `mismatch` (`exam_id`, `mismatch`)
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4;
 
@@ -235,9 +256,6 @@ CREATE TABLE `sc_paper_page`
 (
     `paper_id`    bigint       NOT NULL,
     `page_index`  int          NOT NULL,
-    `batch_id`    bigint       NOT NULL,
-    `student_id`  bigint       NOT NULL,
-    `exam_id`     bigint       NOT NULL,
     `absent`      text     DEFAULT NULL,
     `breach`      text     DEFAULT NULL,
     `paper_type`  text     DEFAULT NULL,
@@ -246,10 +264,10 @@ CREATE TABLE `sc_paper_page`
     `sheet_path`  varchar(255) NOT NULL,
     `slice_path`  text     DEFAULT NULL,
     `recog_data`  longtext DEFAULT NULL,
-    `create_time` bigint   DEFAULT NULL,
-    `update_time` bigint   DEFAULT NULL,
     `creator_id`  bigint   DEFAULT NULL,
     `updater_id`  bigint   DEFAULT NULL,
+    `create_time` bigint   DEFAULT NULL,
+    `update_time` bigint   DEFAULT NULL,
     PRIMARY KEY (`paper_id`, `page_index`)
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4;
@@ -275,6 +293,27 @@ CREATE TABLE `sc_paper_structure`
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4;
 
+DROP TABLE IF EXISTS `sc_question`;
+CREATE TABLE `sc_question`
+(
+    `id`           int(11)      NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `exam_id`      int(11)      NOT NULL COMMENT '考试ID',
+    `subject_code` varchar(32)  NOT NULL COMMENT '科目代码',
+    `paper_type`   varchar(8)   NOT NULL COMMENT '试卷类型',
+    `objective`    tinyint(1)   NOT NULL COMMENT '是否客观题',
+    `main_number`  int(11)      NOT NULL COMMENT '大题号',
+    `sub_number`   varchar(32)  NOT NULL COMMENT '小题号',
+    `main_title`   varchar(128) NOT NULL COMMENT '大题名称',
+    `total_score`  double DEFAULT NULL COMMENT '满分',
+    `creator_id`   bigint DEFAULT NULL,
+    `updater_id`   bigint DEFAULT NULL,
+    `create_time`  bigint DEFAULT NULL,
+    `update_time`  bigint DEFAULT NULL,
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `index1` (`exam_id`, `subject_code`, `objective`, `paper_type`, `main_number`, `sub_number`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4 COMMENT ='小题信息表';
+
 
 DROP TABLE IF EXISTS `sc_omr_group`;
 CREATE TABLE `sc_omr_group`
@@ -325,9 +364,11 @@ CREATE TABLE `sc_student`
     `subject_code`       varchar(64) NOT NULL,
     `package_code`       varchar(64) DEFAULT NULL,
     `exam_site`          varchar(64) DEFAULT NULL,
+    `exam_site_name`     varchar(64) DEFAULT NULL,
     `exam_room`          varchar(64) DEFAULT NULL,
     `seat_number`        varchar(16) DEFAULT NULL,
     `campus_name`        varchar(64) DEFAULT NULL,
+    `campus_code`        varchar(64) DEFAULT NULL,
     `status`             varchar(16) NOT NULL,
     `omr_absent`         bit(1)      NOT NULL,
     `question_filled`    bit(1)      NOT NULL,
@@ -335,7 +376,10 @@ CREATE TABLE `sc_student`
     `absent_suspect`     bit(1)      NOT NULL,
     `incomplete`         bit(1)      NOT NULL,
     `card_number`        int         DEFAULT NULL,
+    `device`             varchar(32) DEFAULT NULL,
     `paper_type`         varchar(16) NOT NULL,
+    `exam_status`        varchar(32) DEFAULT NULL,
+    `breach_code`        varchar(32) DEFAULT NULL,
     `file_upload_status` varchar(16) DEFAULT NULL,
     `data_upload_status` varchar(16) DEFAULT NULL,
     `creator_id`         bigint      DEFAULT NULL,
@@ -343,8 +387,7 @@ CREATE TABLE `sc_student`
     `create_time`        bigint      DEFAULT NULL,
     `update_time`        bigint      DEFAULT NULL,
     PRIMARY KEY (`id`),
-    UNIQUE KEY `exam_number` (`exam_id`, `exam_number`),
-    UNIQUE KEY `exam_student_subject` (`exam_id`, `student_code`, `subject_code`)
+    UNIQUE KEY `exam_number` (`exam_id`, `exam_number`)
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4;
 
@@ -354,10 +397,6 @@ CREATE TABLE `sc_student_paper`
     `student_id`   bigint NOT NULL,
     `paper_number` int    NOT NULL,
     `paper_id`     bigint NOT NULL,
-    `creator_id`   bigint DEFAULT NULL,
-    `updater_id`   bigint DEFAULT NULL,
-    `create_time`  bigint DEFAULT NULL,
-    `update_time`  bigint DEFAULT NULL,
     PRIMARY KEY (`student_id`, `paper_number`),
     UNIQUE KEY `paper_id` (`paper_id`)
 ) ENGINE = InnoDB
@@ -366,13 +405,14 @@ CREATE TABLE `sc_student_paper`
 DROP TABLE IF EXISTS `sc_subject`;
 CREATE TABLE `sc_subject`
 (
-    `exam_id`     bigint      NOT NULL,
-    `code`        varchar(64) NOT NULL,
-    `name`        varchar(64) NOT NULL,
-    `creator_id`  bigint DEFAULT NULL,
-    `updater_id`  bigint DEFAULT NULL,
-    `create_time` bigint DEFAULT NULL,
-    `update_time` bigint DEFAULT NULL,
+    `exam_id`                    bigint      NOT NULL,
+    `code`                       varchar(64) NOT NULL,
+    `name`                       varchar(64) NOT NULL,
+    `paper_type_barcode_content` text   DEFAULT NULL,
+    `creator_id`                 bigint DEFAULT NULL,
+    `updater_id`                 bigint DEFAULT NULL,
+    `create_time`                bigint DEFAULT NULL,
+    `update_time`                bigint DEFAULT NULL,
     PRIMARY KEY (`exam_id`, `code`)
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4;
@@ -383,6 +423,7 @@ CREATE TABLE `sc_file_property`
     `path`        varchar(255) NOT NULL,
     `md5`         varchar(32)  NOT NULL,
     `exam_id`     bigint       NOT NULL,
+    `file_size`   bigint       NOT NULL,
     `create_time` bigint DEFAULT NULL,
     `update_time` bigint DEFAULT NULL,
     PRIMARY KEY (`path`)
@@ -399,12 +440,27 @@ CREATE TABLE `sc_user`
     `role`        varchar(16) NOT NULL,
     `enable`      tinyint(1)  NOT NULL,
     `school_id`   bigint      NOT NULL,
-    `creator_id`  bigint DEFAULT NULL,
-    `updater_id`  bigint DEFAULT NULL,
-    `create_time` bigint DEFAULT NULL,
-    `update_time` bigint DEFAULT NULL,
+    `device`      varchar(64) DEFAULT NULL,
+    `creator_id`  bigint      DEFAULT NULL,
+    `updater_id`  bigint      DEFAULT NULL,
+    `create_time` bigint      DEFAULT NULL,
+    `update_time` bigint      DEFAULT NULL,
     PRIMARY KEY (`id`),
     UNIQUE KEY `login_name` (`login_name`)
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4;
 
+DROP TABLE IF EXISTS `sc_adapte_file`;
+CREATE TABLE `sc_adapte_file`
+(
+    `exam_id`     bigint       NOT NULL,
+    `card_number` int          NOT NULL,
+    `role`        varchar(16)  NOT NULL,
+    `device`      varchar(64)  NOT NULL,
+    `uri`         varchar(255) NOT NULL,
+    `md5`         varchar(32)  NOT NULL,
+    `create_time` bigint DEFAULT NULL,
+    `update_time` bigint DEFAULT NULL,
+    PRIMARY KEY (`exam_id`, `card_number`, `role`, `device`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4;

+ 0 - 3
db/system_config_cet.sql

@@ -1,3 +0,0 @@
-INSERT INTO `sc_user` (`id`,`school_id`,`role`, `create_time`, `update_time`, `enable`, `login_name`, `name`, `password`,`creator_id`,`updater_id`) VALUES (1,1,'SCHOOL_ADMIN', unix_timestamp(now())*1000, unix_timestamp(now())*1000, 1, 'admin', '学校管理员', UPPER(SHA2('123456',256)),1,1);
-
-INSERT INTO `sc_system_config` (`id`, `create_time`, `update_time`, `creator_id`, `updater_id`, `scanner_enable_login`, `scanner_password`, `system_mode`, `client_version`, `client_uri`, `client_md5`, `client_update_time`,`image_transfer_mode`) VALUES (1, NULL, NULL, NULL, NULL, 1, NULL, 'STANDALONE', NULL, NULL, NULL, NULL,'CET');

+ 0 - 1
db/system_config_marking.sql

@@ -1 +0,0 @@
-INSERT INTO `sc_system_config` (`id`, `create_time`, `update_time`, `creator_id`, `updater_id`, `scanner_enable_login`, `scanner_password`, `system_mode`, `client_version`, `client_uri`, `client_md5`, `client_update_time`,`image_transfer_mode`) VALUES (1, NULL, NULL, NULL, NULL, 1, NULL, 'MARKING_CLOUD', NULL, NULL, NULL, NULL,NULL);

+ 0 - 3
db/system_config_ow.sql

@@ -1,3 +0,0 @@
-INSERT INTO `sc_user` (`id`,`school_id`,`role`, `create_time`, `update_time`, `enable`, `login_name`, `name`, `password`,`creator_id`,`updater_id`) VALUES (1,1,'SCHOOL_ADMIN', unix_timestamp(now())*1000, unix_timestamp(now())*1000, 1, 'admin', '学校管理员', UPPER(SHA2('123456',256)),1,1);
-
-INSERT INTO `sc_system_config` (`id`, `create_time`, `update_time`, `creator_id`, `updater_id`, `scanner_enable_login`, `scanner_password`, `system_mode`, `client_version`, `client_uri`, `client_md5`, `client_update_time`,`image_transfer_mode`) VALUES (1, NULL, NULL, NULL, NULL, 1, NULL, 'STANDALONE', NULL, NULL, NULL, NULL,'OW');

+ 88 - 60
src/main/java/cn/com/qmth/scancloud/bean/AbsentQueryDomain.java

@@ -1,66 +1,94 @@
 package cn.com.qmth.scancloud.bean;
 
-import java.util.List;
-
 import cn.com.qmth.scancloud.enums.GroupType;
 import cn.com.qmth.scancloud.util.PagerQuery;
 
-public class AbsentQueryDomain extends PagerQuery{
-	private Long examId;
-	private GroupType groupType;
-	private List<String> status;
-	private String campusName;
-	private String subjectCode;
-	private String examSite;
-	private String examRoom;
-	private Boolean scanned;
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	public GroupType getGroupType() {
-		return groupType;
-	}
-	public void setGroupType(GroupType groupType) {
-		this.groupType = groupType;
-	}
-	public List<String> getStatus() {
-		return status;
-	}
-	public void setStatus(List<String> status) {
-		this.status = status;
-	}
-	public String getCampusName() {
-		return campusName;
-	}
-	public void setCampusName(String campusName) {
-		this.campusName = campusName;
-	}
-	public String getSubjectCode() {
-		return subjectCode;
-	}
-	public void setSubjectCode(String subjectCode) {
-		this.subjectCode = subjectCode;
-	}
-	public String getExamSite() {
-		return examSite;
-	}
-	public void setExamSite(String examSite) {
-		this.examSite = examSite;
-	}
-	public String getExamRoom() {
-		return examRoom;
-	}
-	public void setExamRoom(String examRoom) {
-		this.examRoom = examRoom;
-	}
-	public Boolean getScanned() {
-		return scanned;
-	}
-	public void setScanned(Boolean scanned) {
-		this.scanned = scanned;
-	}
-	
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AbsentQueryDomain extends PagerQuery {
+
+    @NotNull(message = "考试ID不能为空")
+    private Long examId;
+
+    @NotNull(message = "分组类型不能为空")
+    private GroupType groupType;
+
+    private List<String> status = new ArrayList<>();
+
+    private String campusCode;
+
+    private String subjectCode;
+
+    private String examSite;
+
+    private String examRoom;
+
+    private Boolean scanned;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public GroupType getGroupType() {
+        return groupType;
+    }
+
+    public void setGroupType(GroupType groupType) {
+        this.groupType = groupType;
+    }
+
+    public List<String> getStatus() {
+        return status;
+    }
+
+    public void setStatus(List<String> status) {
+        this.status = status;
+    }
+
+    public String getCampusCode() {
+        return campusCode;
+    }
+
+    public void setCampusCode(String campusCode) {
+        this.campusCode = campusCode;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public String getExamSite() {
+        return examSite;
+    }
+
+    public void setExamSite(String examSite) {
+        this.examSite = examSite;
+    }
+
+    public String getExamRoom() {
+        return examRoom;
+    }
+
+    public void setExamRoom(String examRoom) {
+        this.examRoom = examRoom;
+    }
+
+    public Boolean getScanned() {
+        return scanned;
+    }
+
+    public void setScanned(Boolean scanned) {
+        this.scanned = scanned;
+    }
+
 }

+ 25 - 14
src/main/java/cn/com/qmth/scancloud/bean/AnswerDeleteDomain.java

@@ -1,18 +1,29 @@
 package cn.com.qmth.scancloud.bean;
 
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
 public class AnswerDeleteDomain {
-	private Long examId;
-	private String examNumber;
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	public String getExamNumber() {
-		return examNumber;
-	}
-	public void setExamNumber(String examNumber) {
-		this.examNumber = examNumber;
-	}
+
+    @NotNull(message = "考试Id不能为空")
+    private Long examId;
+
+    @NotBlank(message = "准考证号不能为空")
+    private String examNumber;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
 }

+ 200 - 117
src/main/java/cn/com/qmth/scancloud/bean/AnswerQueryDomain.java

@@ -2,131 +2,214 @@ package cn.com.qmth.scancloud.bean;
 
 import java.util.List;
 
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.scancloud.enums.ExamStatus;
 import cn.com.qmth.scancloud.enums.ScanStatus;
 import cn.com.qmth.scancloud.util.PagerQuery;
 
-public class AnswerQueryDomain extends PagerQuery{
-	private Long examId;
-	private List<ScanStatus> status;
-	private String examNumber;
-	private String studentCode;
-	private String name;
-	private String packageCode;
-	private String campusName;
-	private String subjectCode;
-	private String examSite;
-	private String examRoom;
-	private Boolean absentSuspect;
-	private Boolean omrAbsent;
-	private Boolean assigned;
-	private Boolean incomplete;
-	private Boolean questionFilled;
-	private Boolean withOmrDetail;
-	private String paperType;
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	
+public class AnswerQueryDomain extends PagerQuery {
+
+    @NotNull(message = "考试ID不能为空")
+    private Long examId;
+
+    private List<ScanStatus> status;
+
+    private ExamStatus examStatus;
+
+    private String examNumber;
+
+    private String studentCode;
+
+    private String name;
+
+    private String packageCode;
+
+    private String campusCode;
+    
+    private String campusName;
+
+    private String subjectCode;
+
+    private String examSite;
+
+    private String examRoom;
+
+    private String paperTypeStatus;
+
+    private String device;
+
+    private Boolean absentSuspect;
+
+    private Boolean omrAbsent;
+
+    private Boolean assigned;
+
+    private Boolean incomplete;
+
+    private Boolean questionFilled;
+
+    private Boolean withOmrDetail;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
     public List<ScanStatus> getStatus() {
         return status;
     }
-    
+
     public void setStatus(List<ScanStatus> status) {
         this.status = status;
     }
+
     public String getExamNumber() {
-		return examNumber;
-	}
-	public void setExamNumber(String examNumber) {
-		this.examNumber = examNumber;
-	}
-	public String getStudentCode() {
-		return studentCode;
-	}
-	public void setStudentCode(String studentCode) {
-		this.studentCode = studentCode;
-	}
-	public String getName() {
-		return name;
-	}
-	public void setName(String name) {
-		this.name = name;
-	}
-	public String getPackageCode() {
-		return packageCode;
-	}
-	public void setPackageCode(String packageCode) {
-		this.packageCode = packageCode;
-	}
-	public String getCampusName() {
-		return campusName;
-	}
-	public void setCampusName(String campusName) {
-		this.campusName = campusName;
-	}
-	public String getSubjectCode() {
-		return subjectCode;
-	}
-	public void setSubjectCode(String subjectCode) {
-		this.subjectCode = subjectCode;
-	}
-	public String getExamSite() {
-		return examSite;
-	}
-	public void setExamSite(String examSite) {
-		this.examSite = examSite;
-	}
-	public String getExamRoom() {
-		return examRoom;
-	}
-	public void setExamRoom(String examRoom) {
-		this.examRoom = examRoom;
-	}
-	public Boolean getAbsentSuspect() {
-		return absentSuspect;
-	}
-	public void setAbsentSuspect(Boolean absentSuspect) {
-		this.absentSuspect = absentSuspect;
-	}
-	public Boolean getOmrAbsent() {
-		return omrAbsent;
-	}
-	public void setOmrAbsent(Boolean omrAbsent) {
-		this.omrAbsent = omrAbsent;
-	}
-	public Boolean getAssigned() {
-		return assigned;
-	}
-	public void setAssigned(Boolean assigned) {
-		this.assigned = assigned;
-	}
-	public Boolean getIncomplete() {
-		return incomplete;
-	}
-	public void setIncomplete(Boolean incomplete) {
-		this.incomplete = incomplete;
-	}
-	public Boolean getQuestionFilled() {
-		return questionFilled;
-	}
-	public void setQuestionFilled(Boolean questionFilled) {
-		this.questionFilled = questionFilled;
-	}
-	public Boolean getWithOmrDetail() {
-		return withOmrDetail;
-	}
-	public void setWithOmrDetail(Boolean withOmrDetail) {
-		this.withOmrDetail = withOmrDetail;
-	}
-	public String getPaperType() {
-		return paperType;
-	}
-	public void setPaperType(String paperType) {
-		this.paperType = paperType;
-	}
-	
-	
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPackageCode() {
+        return packageCode;
+    }
+
+    public void setPackageCode(String packageCode) {
+        this.packageCode = packageCode;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public String getExamSite() {
+        return examSite;
+    }
+
+    public void setExamSite(String examSite) {
+        this.examSite = examSite;
+    }
+
+    public String getExamRoom() {
+        return examRoom;
+    }
+
+    public void setExamRoom(String examRoom) {
+        this.examRoom = examRoom;
+    }
+
+    public Boolean getAbsentSuspect() {
+        return absentSuspect;
+    }
+
+    public void setAbsentSuspect(Boolean absentSuspect) {
+        this.absentSuspect = absentSuspect;
+    }
+
+    public Boolean getOmrAbsent() {
+        return omrAbsent;
+    }
+
+    public void setOmrAbsent(Boolean omrAbsent) {
+        this.omrAbsent = omrAbsent;
+    }
+
+    public Boolean getAssigned() {
+        return assigned;
+    }
+
+    public void setAssigned(Boolean assigned) {
+        this.assigned = assigned;
+    }
+
+    public Boolean getIncomplete() {
+        return incomplete;
+    }
+
+    public void setIncomplete(Boolean incomplete) {
+        this.incomplete = incomplete;
+    }
+
+    public Boolean getQuestionFilled() {
+        return questionFilled;
+    }
+
+    public void setQuestionFilled(Boolean questionFilled) {
+        this.questionFilled = questionFilled;
+    }
+
+    public Boolean getWithOmrDetail() {
+        return withOmrDetail;
+    }
+
+    public void setWithOmrDetail(Boolean withOmrDetail) {
+        this.withOmrDetail = withOmrDetail;
+    }
+
+    public ExamStatus getExamStatus() {
+        return examStatus;
+    }
+
+    public void setExamStatus(ExamStatus examStatus) {
+        this.examStatus = examStatus;
+    }
+
+    public String getCampusCode() {
+        return campusCode;
+    }
+
+    public void setCampusCode(String campusCode) {
+        this.campusCode = campusCode;
+    }
+
+    public String getPaperTypeStatus() {
+        return paperTypeStatus;
+    }
+
+    public void setPaperTypeStatus(String paperTypeStatus) {
+        this.paperTypeStatus = paperTypeStatus;
+    }
+
+    public String getDevice() {
+        return device;
+    }
+
+    public void setDevice(String device) {
+        this.device = device;
+    }
+
+    
+    public String getCampusName() {
+        return campusName;
+    }
+
+    public void setCampusName(String campusName) {
+        this.campusName = campusName;
+    }
+
 }

+ 26 - 15
src/main/java/cn/com/qmth/scancloud/bean/BatchCreateDomain.java

@@ -1,21 +1,32 @@
 package cn.com.qmth.scancloud.bean;
 
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 import java.util.List;
 
 public class BatchCreateDomain {
-	private Long examId;
-	private List<String> examNumbers;
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	public List<String> getExamNumbers() {
-		return examNumbers;
-	}
-	public void setExamNumbers(List<String> examNumbers) {
-		this.examNumbers = examNumbers;
-	}
-	
+
+    @NotNull(message = "考试ID不能为空")
+    private Long examId;
+
+    @NotNull(message = "准考证号不能为空")
+    @Size(min = 1, message = "准考证号不能为空")
+    private List<String> examNumbers;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public List<String> getExamNumbers() {
+        return examNumbers;
+    }
+
+    public void setExamNumbers(List<String> examNumbers) {
+        this.examNumbers = examNumbers;
+    }
+
 }

+ 55 - 37
src/main/java/cn/com/qmth/scancloud/bean/BatchQueryDomain.java

@@ -2,41 +2,59 @@ package cn.com.qmth.scancloud.bean;
 
 import cn.com.qmth.scancloud.util.PagerQuery;
 
-public class BatchQueryDomain extends PagerQuery{
-	private Long examId;
-	private String device;
-	private Long startTime;
-	private Long endTime;
-	private Boolean withVerify;
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	public String getDevice() {
-		return device;
-	}
-	public void setDevice(String device) {
-		this.device = device;
-	}
-	public Long getStartTime() {
-		return startTime;
-	}
-	public void setStartTime(Long startTime) {
-		this.startTime = startTime;
-	}
-	public Long getEndTime() {
-		return endTime;
-	}
-	public void setEndTime(Long endTime) {
-		this.endTime = endTime;
-	}
-	public Boolean getWithVerify() {
-		return withVerify;
-	}
-	public void setWithVerify(Boolean withVerify) {
-		this.withVerify = withVerify;
-	}
-	
+import javax.validation.constraints.NotNull;
+
+public class BatchQueryDomain extends PagerQuery {
+
+    @NotNull(message = "考试ID不能为空")
+    private Long examId;
+
+    private String device;
+
+    private Long startTime;
+
+    private Long endTime;
+
+    private Boolean withVerify;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getDevice() {
+        return device;
+    }
+
+    public void setDevice(String device) {
+        this.device = device;
+    }
+
+    public Long getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Long startTime) {
+        this.startTime = startTime;
+    }
+
+    public Long getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Long endTime) {
+        this.endTime = endTime;
+    }
+
+    public Boolean getWithVerify() {
+        return withVerify;
+    }
+
+    public void setWithVerify(Boolean withVerify) {
+        this.withVerify = withVerify;
+    }
+
 }

+ 119 - 73
src/main/java/cn/com/qmth/scancloud/bean/ExamConfigDomain.java

@@ -1,79 +1,125 @@
 package cn.com.qmth.scancloud.bean;
 
-import java.util.List;
-
 import cn.com.qmth.scancloud.enums.ExamMode;
+import cn.com.qmth.scancloud.enums.ImageTransferMode;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import java.util.List;
 
 public class ExamConfigDomain {
-	private Long id;
-	private ExamMode mode;
-	private Boolean scanByPackage;
-	private Boolean enableSyncVerify;
-	private Boolean allowUnexistPaper;
-	private Integer answerFrontCardType;
-	private Boolean enableSinglePageAnswer;
-	private Integer answerPaperNumberFigure;
-	private List<String> paperTypeBarcodeContent;
-	private String absentBarcodeContent;
-	public Long getId() {
-		return id;
-	}
-	public void setId(Long id) {
-		this.id = id;
-	}
-	public ExamMode getMode() {
-		return mode;
-	}
-	public void setMode(ExamMode mode) {
-		this.mode = mode;
-	}
-	public Boolean getScanByPackage() {
-		return scanByPackage;
-	}
-	public void setScanByPackage(Boolean scanByPackage) {
-		this.scanByPackage = scanByPackage;
-	}
-	public Boolean getEnableSyncVerify() {
-		return enableSyncVerify;
-	}
-	public void setEnableSyncVerify(Boolean enableSyncVerify) {
-		this.enableSyncVerify = enableSyncVerify;
-	}
-	public Boolean getAllowUnexistPaper() {
-		return allowUnexistPaper;
-	}
-	public void setAllowUnexistPaper(Boolean allowUnexistPaper) {
-		this.allowUnexistPaper = allowUnexistPaper;
-	}
-	public Integer getAnswerFrontCardType() {
-		return answerFrontCardType;
-	}
-	public void setAnswerFrontCardType(Integer answerFrontCardType) {
-		this.answerFrontCardType = answerFrontCardType;
-	}
-	public Boolean getEnableSinglePageAnswer() {
-		return enableSinglePageAnswer;
-	}
-	public void setEnableSinglePageAnswer(Boolean enableSinglePageAnswer) {
-		this.enableSinglePageAnswer = enableSinglePageAnswer;
-	}
-	public Integer getAnswerPaperNumberFigure() {
-		return answerPaperNumberFigure;
-	}
-	public void setAnswerPaperNumberFigure(Integer answerPaperNumberFigure) {
-		this.answerPaperNumberFigure = answerPaperNumberFigure;
-	}
-	public List<String> getPaperTypeBarcodeContent() {
-		return paperTypeBarcodeContent;
-	}
-	public void setPaperTypeBarcodeContent(List<String> paperTypeBarcodeContent) {
-		this.paperTypeBarcodeContent = paperTypeBarcodeContent;
-	}
-	public String getAbsentBarcodeContent() {
-		return absentBarcodeContent;
-	}
-	public void setAbsentBarcodeContent(String absentBarcodeContent) {
-		this.absentBarcodeContent = absentBarcodeContent;
-	}
-	
+
+    public interface ExamConfigInit {
+
+    }
+
+    @NotNull(message = "考试ID不能为空")
+    private Long id;
+
+    @NotNull(message = "扫描模式不能为空", groups = ExamConfigInit.class)
+    private ExamMode mode;
+
+    @NotNull(message = "整袋扫描不能为空", groups = ExamConfigInit.class)
+    private Boolean scanByPackage;
+
+    @NotNull(message = "启用实时审核不能为空", groups = ExamConfigInit.class)
+    private Boolean enableSyncVerify;
+
+    @NotNull(message = "是否允许缺页不能为空", groups = ExamConfigInit.class)
+    private Boolean allowUnexistPaper;
+
+    @NotNull(message = "答题卡正面题卡类型不能为空", groups = ExamConfigInit.class)
+    @Min(value = 1, message = "题卡类型不能小于1", groups = ExamConfigInit.class)
+    private Integer answerFrontCardType;
+
+    @NotNull(message = "是否允许单页题卡不能为空", groups = ExamConfigInit.class)
+    private Boolean enableSinglePageAnswer;
+
+    @NotNull(message = "图片转存规则不能为空", groups = ExamConfigInit.class)
+    private ImageTransferMode imageTransferMode;
+
+    private List<String> paperTypeBarcodeContent;
+
+    private String absentBarcodeContent;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public ExamMode getMode() {
+        return mode;
+    }
+
+    public void setMode(ExamMode mode) {
+        this.mode = mode;
+    }
+
+    public Boolean getScanByPackage() {
+        return scanByPackage;
+    }
+
+    public void setScanByPackage(Boolean scanByPackage) {
+        this.scanByPackage = scanByPackage;
+    }
+
+    public Boolean getEnableSyncVerify() {
+        return enableSyncVerify;
+    }
+
+    public void setEnableSyncVerify(Boolean enableSyncVerify) {
+        this.enableSyncVerify = enableSyncVerify;
+    }
+
+    public Boolean getAllowUnexistPaper() {
+        return allowUnexistPaper;
+    }
+
+    public void setAllowUnexistPaper(Boolean allowUnexistPaper) {
+        this.allowUnexistPaper = allowUnexistPaper;
+    }
+
+    public Integer getAnswerFrontCardType() {
+        return answerFrontCardType;
+    }
+
+    public void setAnswerFrontCardType(Integer answerFrontCardType) {
+        this.answerFrontCardType = answerFrontCardType;
+    }
+
+    public Boolean getEnableSinglePageAnswer() {
+        return enableSinglePageAnswer;
+    }
+
+    public void setEnableSinglePageAnswer(Boolean enableSinglePageAnswer) {
+        this.enableSinglePageAnswer = enableSinglePageAnswer;
+    }
+
+    public ImageTransferMode getImageTransferMode() {
+        return imageTransferMode;
+    }
+
+    public void setImageTransferMode(ImageTransferMode imageTransferMode) {
+        this.imageTransferMode = imageTransferMode;
+    }
+
+    public List<String> getPaperTypeBarcodeContent() {
+        return paperTypeBarcodeContent;
+    }
+
+    public void setPaperTypeBarcodeContent(List<String> paperTypeBarcodeContent) {
+        this.paperTypeBarcodeContent = paperTypeBarcodeContent;
+    }
+
+    public String getAbsentBarcodeContent() {
+        return absentBarcodeContent;
+    }
+
+    public void setAbsentBarcodeContent(String absentBarcodeContent) {
+        this.absentBarcodeContent = absentBarcodeContent;
+    }
+
 }

+ 43 - 0
src/main/java/cn/com/qmth/scancloud/bean/ImportCetAbsentDomain.java

@@ -0,0 +1,43 @@
+package cn.com.qmth.scancloud.bean;
+
+public class ImportCetAbsentDomain {
+
+    private Long examId;
+    private String examNumber;
+    private Boolean absent;
+    private String breachCode;
+    
+    public Long getExamId() {
+        return examId;
+    }
+    
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+    
+    public String getExamNumber() {
+        return examNumber;
+    }
+    
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+    
+    public Boolean getAbsent() {
+        return absent;
+    }
+    
+    public void setAbsent(Boolean absent) {
+        this.absent = absent;
+    }
+    
+    public String getBreachCode() {
+        return breachCode;
+    }
+    
+    public void setBreachCode(String breachCode) {
+        this.breachCode = breachCode;
+    }
+
+
+}

+ 122 - 86
src/main/java/cn/com/qmth/scancloud/bean/ImportExamDomain.java

@@ -1,104 +1,140 @@
 package cn.com.qmth.scancloud.bean;
 
-import java.util.List;
-
 import cn.com.qmth.scancloud.enums.ExamMode;
+import cn.com.qmth.scancloud.enums.ImageTransferMode;
+
+import java.util.List;
 
 public class ImportExamDomain {
-	private Long id;
-	private Long schoolId;
-	private String schoolName;
-	private String name;
-	private ExamMode mode;
-	private Boolean scanByPackage;
-	private Boolean enableSyncVerify;
-	private Boolean allowUnexistPaper;
-	private Integer answerFrontCardType;
-	private Boolean enableSinglePageAnswer;
-	private Integer answerPaperNumberFigure;
-	private List<String> paperTypeBarcodeContent;
-	private String absentBarcodeContent;
-	public Long getId() {
-		return id;
-	}
-	public void setId(Long id) {
-		this.id = id;
-	}
-	public ExamMode getMode() {
-		return mode;
-	}
-	public void setMode(ExamMode mode) {
-		this.mode = mode;
-	}
-	public Boolean getScanByPackage() {
-		return scanByPackage;
-	}
-	public void setScanByPackage(Boolean scanByPackage) {
-		this.scanByPackage = scanByPackage;
-	}
-	public Boolean getEnableSyncVerify() {
-		return enableSyncVerify;
-	}
-	public void setEnableSyncVerify(Boolean enableSyncVerify) {
-		this.enableSyncVerify = enableSyncVerify;
-	}
-	public Boolean getAllowUnexistPaper() {
-		return allowUnexistPaper;
-	}
-	public void setAllowUnexistPaper(Boolean allowUnexistPaper) {
-		this.allowUnexistPaper = allowUnexistPaper;
-	}
-	public Integer getAnswerFrontCardType() {
-		return answerFrontCardType;
-	}
-	public void setAnswerFrontCardType(Integer answerFrontCardType) {
-		this.answerFrontCardType = answerFrontCardType;
-	}
-	public Boolean getEnableSinglePageAnswer() {
-		return enableSinglePageAnswer;
-	}
-	public void setEnableSinglePageAnswer(Boolean enableSinglePageAnswer) {
-		this.enableSinglePageAnswer = enableSinglePageAnswer;
-	}
-	public Integer getAnswerPaperNumberFigure() {
-		return answerPaperNumberFigure;
-	}
-	public void setAnswerPaperNumberFigure(Integer answerPaperNumberFigure) {
-		this.answerPaperNumberFigure = answerPaperNumberFigure;
-	}
-	public List<String> getPaperTypeBarcodeContent() {
-		return paperTypeBarcodeContent;
-	}
-	public void setPaperTypeBarcodeContent(List<String> paperTypeBarcodeContent) {
-		this.paperTypeBarcodeContent = paperTypeBarcodeContent;
-	}
-	public String getAbsentBarcodeContent() {
-		return absentBarcodeContent;
-	}
-	public void setAbsentBarcodeContent(String absentBarcodeContent) {
-		this.absentBarcodeContent = absentBarcodeContent;
-	}
-    
+
+    private Long id;
+
+    private Long schoolId;
+
+    private String schoolName;
+
+    private String name;
+
+    private ExamMode mode;
+
+    private Boolean scanByPackage;
+
+    private Boolean enableSyncVerify;
+
+    private Boolean allowUnexistPaper;
+
+    private Integer answerFrontCardType;
+
+    private Boolean enableSinglePageAnswer;
+
+    private ImageTransferMode imageTransferMode;
+
+    private List<String> paperTypeBarcodeContent;
+
+    private String absentBarcodeContent;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public ExamMode getMode() {
+        return mode;
+    }
+
+    public void setMode(ExamMode mode) {
+        this.mode = mode;
+    }
+
+    public Boolean getScanByPackage() {
+        return scanByPackage;
+    }
+
+    public void setScanByPackage(Boolean scanByPackage) {
+        this.scanByPackage = scanByPackage;
+    }
+
+    public Boolean getEnableSyncVerify() {
+        return enableSyncVerify;
+    }
+
+    public void setEnableSyncVerify(Boolean enableSyncVerify) {
+        this.enableSyncVerify = enableSyncVerify;
+    }
+
+    public Boolean getAllowUnexistPaper() {
+        return allowUnexistPaper;
+    }
+
+    public void setAllowUnexistPaper(Boolean allowUnexistPaper) {
+        this.allowUnexistPaper = allowUnexistPaper;
+    }
+
+    public Integer getAnswerFrontCardType() {
+        return answerFrontCardType;
+    }
+
+    public void setAnswerFrontCardType(Integer answerFrontCardType) {
+        this.answerFrontCardType = answerFrontCardType;
+    }
+
+    public Boolean getEnableSinglePageAnswer() {
+        return enableSinglePageAnswer;
+    }
+
+    public void setEnableSinglePageAnswer(Boolean enableSinglePageAnswer) {
+        this.enableSinglePageAnswer = enableSinglePageAnswer;
+    }
+
+    public ImageTransferMode getImageTransferMode() {
+        return imageTransferMode;
+    }
+
+    public void setImageTransferMode(ImageTransferMode imageTransferMode) {
+        this.imageTransferMode = imageTransferMode;
+    }
+
+    public List<String> getPaperTypeBarcodeContent() {
+        return paperTypeBarcodeContent;
+    }
+
+    public void setPaperTypeBarcodeContent(List<String> paperTypeBarcodeContent) {
+        this.paperTypeBarcodeContent = paperTypeBarcodeContent;
+    }
+
+    public String getAbsentBarcodeContent() {
+        return absentBarcodeContent;
+    }
+
+    public void setAbsentBarcodeContent(String absentBarcodeContent) {
+        this.absentBarcodeContent = absentBarcodeContent;
+    }
+
     public Long getSchoolId() {
         return schoolId;
     }
-    
+
     public void setSchoolId(Long schoolId) {
         this.schoolId = schoolId;
     }
-    
+
     public String getName() {
         return name;
     }
-    
+
     public void setName(String name) {
         this.name = name;
     }
-	public String getSchoolName() {
-		return schoolName;
-	}
-	public void setSchoolName(String schoolName) {
-		this.schoolName = schoolName;
-	}
-	
+
+    public String getSchoolName() {
+        return schoolName;
+    }
+
+    public void setSchoolName(String schoolName) {
+        this.schoolName = schoolName;
+    }
+
 }

+ 22 - 0
src/main/java/cn/com/qmth/scancloud/bean/ImportStudentDomain.java

@@ -10,9 +10,11 @@ public class ImportStudentDomain {
     private String studentCode;
     private String packageCode;
     private String examSite;
+    private String examSiteName;
     private String examRoom;
     private String seatNumber;
     private String campusName;
+    private String campusCode;
 
     public Long getExamId() {
         return examId;
@@ -118,5 +120,25 @@ public class ImportStudentDomain {
 		this.subjectName = subjectName;
 	}
 
+    
+    public String getExamSiteName() {
+        return examSiteName;
+    }
+
+    
+    public void setExamSiteName(String examSiteName) {
+        this.examSiteName = examSiteName;
+    }
+
+    
+    public String getCampusCode() {
+        return campusCode;
+    }
+
+    
+    public void setCampusCode(String campusCode) {
+        this.campusCode = campusCode;
+    }
+
 
 }

+ 25 - 16
src/main/java/cn/com/qmth/scancloud/bean/MismatchQueryDomain.java

@@ -2,20 +2,29 @@ package cn.com.qmth.scancloud.bean;
 
 import cn.com.qmth.scancloud.util.PagerQuery;
 
-public class MismatchQueryDomain extends PagerQuery{
-	private Long examId;
-	private String subjectCode;
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	public String getSubjectCode() {
-		return subjectCode;
-	}
-	public void setSubjectCode(String subjectCode) {
-		this.subjectCode = subjectCode;
-	}
-	
+import javax.validation.constraints.NotNull;
+
+public class MismatchQueryDomain extends PagerQuery {
+
+    @NotNull(message = "examId不能为空")
+    private Long examId;
+
+    private String subjectCode;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
 }

+ 25 - 15
src/main/java/cn/com/qmth/scancloud/bean/MismatchToggleDomain.java

@@ -1,19 +1,29 @@
 package cn.com.qmth.scancloud.bean;
 
+import javax.validation.constraints.NotNull;
+
 public class MismatchToggleDomain {
-	private Long paperId;
-	private Boolean enable;
-	public Long getPaperId() {
-		return paperId;
-	}
-	public void setPaperId(Long paperId) {
-		this.paperId = paperId;
-	}
-	public Boolean getEnable() {
-		return enable;
-	}
-	public void setEnable(Boolean enable) {
-		this.enable = enable;
-	}
-	
+
+    @NotNull(message = "paperId不能为空")
+    private Long paperId;
+
+    @NotNull(message = "enable不能为空")
+    private Boolean enable;
+
+    public Long getPaperId() {
+        return paperId;
+    }
+
+    public void setPaperId(Long paperId) {
+        this.paperId = paperId;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+
 }

+ 39 - 22
src/main/java/cn/com/qmth/scancloud/bean/PageDeleteDomain.java

@@ -1,26 +1,43 @@
 package cn.com.qmth.scancloud.bean;
 
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
 public class PageDeleteDomain {
-	private Long examId;
-	private String examNumber;
-	private Integer paperNumber;
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	public String getExamNumber() {
-		return examNumber;
-	}
-	public void setExamNumber(String examNumber) {
-		this.examNumber = examNumber;
-	}
-	public Integer getPaperNumber() {
-		return paperNumber;
-	}
-	public void setPaperNumber(Integer paperNumber) {
-		this.paperNumber = paperNumber;
-	}
-	
+
+    @NotNull(message = "考试Id不能为空")
+    private Long examId;
+
+    @NotBlank(message = "准考证号不能为空")
+    private String examNumber;
+
+    @NotNull(message = "张数不能为空")
+    @Min(value = 1, message = "张数不能小于1")
+    private Integer paperNumber;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public Integer getPaperNumber() {
+        return paperNumber;
+    }
+
+    public void setPaperNumber(Integer paperNumber) {
+        this.paperNumber = paperNumber;
+    }
+
 }

+ 42 - 0
src/main/java/cn/com/qmth/scancloud/bean/SubjectConfigDomain.java

@@ -0,0 +1,42 @@
+package cn.com.qmth.scancloud.bean;
+
+import java.util.List;
+
+import javax.validation.constraints.NotNull;
+
+public class SubjectConfigDomain {
+
+    @NotNull(message = "考试ID不能为空")
+    private Long examId;
+
+    @NotNull(message = "扫描模式不能为空")
+    private String subjectCode;
+
+    private List<String> paperTypeBarcodeContent;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public List<String> getPaperTypeBarcodeContent() {
+        return paperTypeBarcodeContent;
+    }
+
+    public void setPaperTypeBarcodeContent(List<String> paperTypeBarcodeContent) {
+        this.paperTypeBarcodeContent = paperTypeBarcodeContent;
+    }
+
+}

+ 53 - 30
src/main/java/cn/com/qmth/scancloud/bean/answersave/AnswerDomain.java

@@ -1,36 +1,59 @@
 package cn.com.qmth.scancloud.bean.answersave;
 
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 import java.util.List;
 
 public class AnswerDomain {
-	private Long batchId;
-	private String examNumber;
-	private Integer cardNumber;
-	private List<AnswerPaper> papers;
-	public Long getBatchId() {
-		return batchId;
-	}
-	public void setBatchId(Long batchId) {
-		this.batchId = batchId;
-	}
-	public String getExamNumber() {
-		return examNumber;
-	}
-	public void setExamNumber(String examNumber) {
-		this.examNumber = examNumber;
-	}
-	public Integer getCardNumber() {
-		return cardNumber;
-	}
-	public void setCardNumber(Integer cardNumber) {
-		this.cardNumber = cardNumber;
-	}
-	public List<AnswerPaper> getPapers() {
-		return papers;
-	}
-	public void setPapers(List<AnswerPaper> papers) {
-		this.papers = papers;
-	}
-	
-	
+
+    @NotNull(message = "batchId不能为空")
+    private Long batchId;
+
+    @NotBlank(message = "examNumber不能为空")
+    private String examNumber;
+
+    @NotNull(message = "cardNumber不能为空")
+    @Min(value = 1, message = "cardNumber不能小于1")
+    private Integer cardNumber;
+
+    @Valid
+    @NotNull(message = "papers不能为空")
+    @Size(min = 1, message = "papers不能为空")
+    private List<AnswerPaper> papers;
+
+    public Long getBatchId() {
+        return batchId;
+    }
+
+    public void setBatchId(Long batchId) {
+        this.batchId = batchId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public Integer getCardNumber() {
+        return cardNumber;
+    }
+
+    public void setCardNumber(Integer cardNumber) {
+        this.cardNumber = cardNumber;
+    }
+
+    public List<AnswerPaper> getPapers() {
+        return papers;
+    }
+
+    public void setPapers(List<AnswerPaper> papers) {
+        this.papers = papers;
+    }
+
 }

+ 95 - 64
src/main/java/cn/com/qmth/scancloud/bean/answersave/AnswerPage.java

@@ -1,72 +1,103 @@
 package cn.com.qmth.scancloud.bean.answersave;
 
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
 import java.util.List;
 
 public class AnswerPage {
-	private Integer index;
+
+    @NotNull(message = "paper.page.index不能为空")
+    @Min(value = 1, message = "paper.page.index不能小于1")
+    private Integer index;
+
+    @NotBlank(message = "paper.page.sheetUri不能为空")
     private String sheetUri;
+
     private List<String> sliceUri;
-	private BoolResult absent;
-	private BoolResult breach;
-	private StringResult paperType;
-	private ArrayResult question;
-	private ArrayResult selective;
-	private String recogData;
-	public Integer getIndex() {
-		return index;
-	}
-	public void setIndex(Integer index) {
-		this.index = index;
-	}
-	public BoolResult getAbsent() {
-		return absent;
-	}
-	public void setAbsent(BoolResult absent) {
-		this.absent = absent;
-	}
-	public BoolResult getBreach() {
-		return breach;
-	}
-	public void setBreach(BoolResult breach) {
-		this.breach = breach;
-	}
-	public StringResult getPaperType() {
-		return paperType;
-	}
-	public void setPaperType(StringResult paperType) {
-		this.paperType = paperType;
-	}
-	public ArrayResult getQuestion() {
-		return question;
-	}
-	public void setQuestion(ArrayResult question) {
-		this.question = question;
-	}
-	public ArrayResult getSelective() {
-		return selective;
-	}
-	public void setSelective(ArrayResult selective) {
-		this.selective = selective;
-	}
-	public String getSheetUri() {
-		return sheetUri;
-	}
-	public void setSheetUri(String sheetUri) {
-		this.sheetUri = sheetUri;
-	}
-	public List<String> getSliceUri() {
-		return sliceUri;
-	}
-	public void setSliceUri(List<String> sliceUri) {
-		this.sliceUri = sliceUri;
-	}
-	public String getRecogData() {
-		return recogData;
-	}
-	public void setRecogData(String recogData) {
-		this.recogData = recogData;
-	}
-	
-	
-	
+
+    private BoolResult absent;
+
+    private BoolResult breach;
+
+    private StringResult paperType;
+
+    private ArrayResult question;
+
+    private ArrayResult selective;
+
+    private String recogData;
+
+    public Integer getIndex() {
+        return index;
+    }
+
+    public void setIndex(Integer index) {
+        this.index = index;
+    }
+
+    public BoolResult getAbsent() {
+        return absent;
+    }
+
+    public void setAbsent(BoolResult absent) {
+        this.absent = absent;
+    }
+
+    public BoolResult getBreach() {
+        return breach;
+    }
+
+    public void setBreach(BoolResult breach) {
+        this.breach = breach;
+    }
+
+    public StringResult getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(StringResult paperType) {
+        this.paperType = paperType;
+    }
+
+    public ArrayResult getQuestion() {
+        return question;
+    }
+
+    public void setQuestion(ArrayResult question) {
+        this.question = question;
+    }
+
+    public ArrayResult getSelective() {
+        return selective;
+    }
+
+    public void setSelective(ArrayResult selective) {
+        this.selective = selective;
+    }
+
+    public String getSheetUri() {
+        return sheetUri;
+    }
+
+    public void setSheetUri(String sheetUri) {
+        this.sheetUri = sheetUri;
+    }
+
+    public List<String> getSliceUri() {
+        return sliceUri;
+    }
+
+    public void setSliceUri(List<String> sliceUri) {
+        this.sliceUri = sliceUri;
+    }
+
+    public String getRecogData() {
+        return recogData;
+    }
+
+    public void setRecogData(String recogData) {
+        this.recogData = recogData;
+    }
+
 }

+ 80 - 23
src/main/java/cn/com/qmth/scancloud/bean/answersave/AnswerPaper.java

@@ -1,30 +1,87 @@
 package cn.com.qmth.scancloud.bean.answersave;
 
+import java.util.ArrayList;
 import java.util.List;
 
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import com.qmth.boot.core.exception.ParameterException;
+
+import cn.com.qmth.scancloud.entity.AnswerCardEntity;
+import cn.com.qmth.scancloud.entity.PaperPageEntity;
 
 public class AnswerPaper {
-	private Integer number;
-	private Boolean assigned;
-	private List<AnswerPage> pages;
-	public Integer getNumber() {
-		return number;
-	}
-	public void setNumber(Integer number) {
-		this.number = number;
-	}
-	public Boolean getAssigned() {
-		return assigned;
-	}
-	public void setAssigned(Boolean assigned) {
-		this.assigned = assigned;
-	}
-	public List<AnswerPage> getPages() {
-		return pages;
-	}
-	public void setPages(List<AnswerPage> pages) {
-		this.pages = pages;
-	}
-	
-	
+
+    @NotNull(message = "paper.number不能为空")
+    @Min(value = 1, message = "paper.number不能小于1")
+    private Integer number;
+
+    @NotNull(message = "paper.assigned不能为空")
+    private Boolean assigned;
+
+    @Valid
+    @NotNull(message = "paper.pages不能为空")
+    @Size(min = 1, message = "paper.pages不能为空")
+    private List<AnswerPage> pages;
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public Boolean getAssigned() {
+        return assigned;
+    }
+
+    public void setAssigned(Boolean assigned) {
+        this.assigned = assigned;
+    }
+
+    public List<AnswerPage> getPages() {
+        return pages;
+    }
+
+    public void setPages(List<AnswerPage> pages) {
+        this.pages = pages;
+    }
+
+    /**
+     * 根据卡格式定义验证页数
+     *
+     * @param answerCard
+     */
+    public void answerCardValidate(AnswerCardEntity answerCard) {
+        if (answerCard.getSinglePage() && getPages().size() != 1) {
+            throw new ParameterException("卡格式页数不一致");
+        }
+        if (!answerCard.getSinglePage() && getPages().size() != 2) {
+            throw new ParameterException("卡格式页数不一致");
+        }
+    }
+
+    public List<PaperPageEntity> buildPageList(List<String> barcodeContents) {
+        List<PaperPageEntity> list = new ArrayList<>();
+        for (AnswerPage answerPage : pages) {
+            PaperPageEntity page = new PaperPageEntity();
+            page.setPageIndex(answerPage.getIndex());
+            page.setSheetPath(answerPage.getSheetUri());
+            page.setSlicePath(answerPage.getSliceUri());
+            page.setAbsent(answerPage.getAbsent());
+            page.setBreach(answerPage.getBreach());
+            page.setPaperType(answerPage.getPaperType());
+            page.setQuestion(answerPage.getQuestion());
+            page.setSelective(answerPage.getSelective());
+            page.setRecogData(answerPage.getRecogData());
+            page.checkPaperType(barcodeContents);
+            list.add(page);
+        }
+        return list;
+    }
+
 }

+ 14 - 14
src/main/java/cn/com/qmth/scancloud/bean/answersave/StringResult.java

@@ -3,25 +3,25 @@ package cn.com.qmth.scancloud.bean.answersave;
 import cn.com.qmth.scancloud.enums.OmrType;
 
 public class StringResult {
+
     private OmrType type;
 
     private String result;
 
-	public OmrType getType() {
-		return type;
-	}
+    public OmrType getType() {
+        return type;
+    }
+
+    public void setType(OmrType type) {
+        this.type = type;
+    }
 
-	public void setType(OmrType type) {
-		this.type = type;
-	}
+    public String getResult() {
+        return result;
+    }
 
-	public String getResult() {
-		return result;
-	}
+    public void setResult(String result) {
+        this.result = result;
+    }
 
-	public void setResult(String result) {
-		this.result = result;
-	}
-    
-    
 }

+ 29 - 0
src/main/java/cn/com/qmth/scancloud/bean/card/AnswerArea.java

@@ -0,0 +1,29 @@
+package cn.com.qmth.scancloud.bean.card;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AnswerArea {
+
+    @JsonProperty("main_number")
+    private Integer mainNumber;
+
+    private double[] area;
+
+    public Integer getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(Integer mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
+    public double[] getArea() {
+        return area;
+    }
+
+    public void setArea(double[] area) {
+        this.area = area;
+    }
+}

+ 110 - 0
src/main/java/cn/com/qmth/scancloud/bean/card/CardFile.java

@@ -0,0 +1,110 @@
+package cn.com.qmth.scancloud.bean.card;
+
+import java.awt.Image;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Base64;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.qmth.boot.core.exception.ParameterException;
+
+/**
+ * 卡格式文件内容结构
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CardFile {
+
+    private String version;
+
+    private boolean adapted;
+
+    private List<CardPageWrapper> pages;
+
+    @JsonIgnore
+    private SliceConfig sliceConfig;
+    
+    @JsonIgnore
+    private List<String> sliceName;
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public boolean isAdapted() {
+        return adapted;
+    }
+
+    public void setAdapted(boolean adapted) {
+        this.adapted = adapted;
+    }
+
+    public List<CardPageWrapper> getPages() {
+        return pages;
+    }
+
+    public void setPages(List<CardPageWrapper> pages) {
+        this.pages = pages;
+    }
+
+    /**
+     * 解析卡格式文件中的裁切图坐标,用于云阅卷同步
+     *
+     * @return
+     */
+    public SliceConfig getSliceConfig() throws IOException {
+        if (sliceConfig == null) {
+            sliceConfig = new SliceConfig();
+            if (pages != null) {
+                int pageNumber = 0;
+                for (CardPageWrapper pageWrapper : pages) {
+                    pageNumber++;
+                    CardPage page = pageWrapper.getExchange();
+                    //遮盖模式
+                    if (page.getInfoArea() != null && page.getInfoArea().length > 0) {
+                        sliceConfig.addConfig(pageNumber, 0, 0, 0, 0);
+                    }
+                    //裁切模式
+                    else if (page.getAnswerArea() != null && !page.getAnswerArea().isEmpty()) {
+                        //获取卡格式图片原始尺寸
+                        //此处图片base64编码特殊,需要采用Mime标准
+                        Image image = ImageIO
+                                .read(new ByteArrayInputStream(Base64.getMimeDecoder().decode(page.getPageImage())));
+                        int width = image.getWidth(null);
+                        int height = image.getHeight(null);
+                        for (AnswerArea area : page.getAnswerArea()) {
+                            sliceConfig.addConfig(pageNumber, (int) (area.getArea()[0] * width),
+                                    (int) (area.getArea()[1] * height), (int) (area.getArea()[2] * width),
+                                    (int) (area.getArea()[3] * height));
+                        }
+                    }
+                }
+            }
+        }
+        return sliceConfig;
+    }
+    
+    public List<String> getSliceName() throws IOException {
+        List<String> sliceName=new LinkedList<String>();
+        if (pages != null) {
+            for (CardPageWrapper pageWrapper : pages) {
+                CardPage page = pageWrapper.getExchange();
+                for (AnswerArea area : page.getAnswerArea()) {
+                    if(area.getMainNumber()==null) {
+                        throw new ParameterException("CET模式下裁切图必须指定大题号");
+                    }
+                    sliceName.add(area.getMainNumber().toString());
+                }
+            }
+        }
+        return sliceName;
+    }
+}

+ 86 - 0
src/main/java/cn/com/qmth/scancloud/bean/card/CardPage.java

@@ -0,0 +1,86 @@
+package cn.com.qmth.scancloud.bean.card;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CardPage {
+
+    @JsonProperty("card_type")
+    private int cardType;
+
+    @JsonProperty("page_size")
+    private String pageSize;
+
+    @JsonProperty("page_image")
+    private String pageImage;
+
+    private Locator locator;
+
+    @JsonProperty("info_area")
+    private double[][] infoArea;
+
+    @JsonProperty("answer_area")
+    private List<AnswerArea> answerArea;
+
+    @JsonProperty("fill_area")
+    private List<FillArea> fillArea;
+
+    public int getCardType() {
+        return cardType;
+    }
+
+    public void setCardType(int cardType) {
+        this.cardType = cardType;
+    }
+
+    public String getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(String pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public String getPageImage() {
+        return pageImage;
+    }
+
+    public void setPageImage(String pageImage) {
+        this.pageImage = pageImage;
+    }
+
+    public Locator getLocator() {
+        return locator;
+    }
+
+    public void setLocator(Locator locator) {
+        this.locator = locator;
+    }
+
+    public double[][] getInfoArea() {
+        return infoArea;
+    }
+
+    public void setInfoArea(double[][] infoArea) {
+        this.infoArea = infoArea;
+    }
+
+    public List<AnswerArea> getAnswerArea() {
+        return answerArea;
+    }
+
+    public void setAnswerArea(List<AnswerArea> answerArea) {
+        this.answerArea = answerArea;
+    }
+
+    public List<FillArea> getFillArea() {
+        return fillArea;
+    }
+
+    public void setFillArea(List<FillArea> fillArea) {
+        this.fillArea = fillArea;
+    }
+}

+ 14 - 0
src/main/java/cn/com/qmth/scancloud/bean/card/CardPageWrapper.java

@@ -0,0 +1,14 @@
+package cn.com.qmth.scancloud.bean.card;
+
+public class CardPageWrapper {
+
+    private CardPage exchange;
+
+    public CardPage getExchange() {
+        return exchange;
+    }
+
+    public void setExchange(CardPage exchange) {
+        this.exchange = exchange;
+    }
+}

+ 59 - 0
src/main/java/cn/com/qmth/scancloud/bean/card/FillArea.java

@@ -0,0 +1,59 @@
+package cn.com.qmth.scancloud.bean.card;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import java.util.List;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FillArea {
+
+    private String field;
+
+    private int index;
+
+    private boolean single;
+
+    private boolean horizontal;
+
+    private List<FillItem> items;
+
+    public String getField() {
+        return field;
+    }
+
+    public void setField(String field) {
+        this.field = field;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    public boolean isSingle() {
+        return single;
+    }
+
+    public void setSingle(boolean single) {
+        this.single = single;
+    }
+
+    public boolean isHorizontal() {
+        return horizontal;
+    }
+
+    public void setHorizontal(boolean horizontal) {
+        this.horizontal = horizontal;
+    }
+
+    public List<FillItem> getItems() {
+        return items;
+    }
+
+    public void setItems(List<FillItem> items) {
+        this.items = items;
+    }
+}

+ 40 - 0
src/main/java/cn/com/qmth/scancloud/bean/card/FillItem.java

@@ -0,0 +1,40 @@
+package cn.com.qmth.scancloud.bean.card;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FillItem {
+
+    @JsonProperty("main_number")
+    private Integer mainNumber;
+
+    @JsonProperty("sub_number")
+    private Integer subNumber;
+
+    private double[][] options;
+
+    public Integer getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(Integer mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
+    public Integer getSubNumber() {
+        return subNumber;
+    }
+
+    public void setSubNumber(Integer subNumber) {
+        this.subNumber = subNumber;
+    }
+
+    public double[][] getOptions() {
+        return options;
+    }
+
+    public void setOptions(double[][] options) {
+        this.options = options;
+    }
+}

+ 27 - 0
src/main/java/cn/com/qmth/scancloud/bean/card/Locator.java

@@ -0,0 +1,27 @@
+package cn.com.qmth.scancloud.bean.card;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Locator {
+
+    private double[][] top;
+
+    private double[][] bottom;
+
+    public double[][] getTop() {
+        return top;
+    }
+
+    public void setTop(double[][] top) {
+        this.top = top;
+    }
+
+    public double[][] getBottom() {
+        return bottom;
+    }
+
+    public void setBottom(double[][] bottom) {
+        this.bottom = bottom;
+    }
+}

+ 41 - 0
src/main/java/cn/com/qmth/scancloud/bean/card/SliceConfig.java

@@ -0,0 +1,41 @@
+package cn.com.qmth.scancloud.bean.card;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+public class SliceConfig {
+
+    private List<ConfigItem> items;
+
+    public SliceConfig() {
+        this.items = new LinkedList<>();
+    }
+
+    public void addConfig(int number, int left, int top, int width, int height) {
+        ConfigItem item = new ConfigItem();
+        item.number = number;
+        item.left = left;
+        item.top = top;
+        item.width = width;
+        item.height = height;
+        this.items.add(item);
+    }
+
+    @Override
+    public String toString() {
+        return StringUtils.join(items, ",");
+    }
+
+    static class ConfigItem {
+
+        int number, left, top, width, height;
+
+        @Override
+        public String toString() {
+            return StringUtils.join(Arrays.asList(number, left, top, width, height), ":");
+        }
+    }
+}

+ 40 - 22
src/main/java/cn/com/qmth/scancloud/bean/omredit/OmrEditDomain.java

@@ -1,28 +1,46 @@
 package cn.com.qmth.scancloud.bean.omredit;
 
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 import java.util.List;
 
 public class OmrEditDomain {
-	private Long examId;
-	private String examNumber;
-	private List<OmrEditPaper> papers;
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	public String getExamNumber() {
-		return examNumber;
-	}
-	public void setExamNumber(String examNumber) {
-		this.examNumber = examNumber;
-	}
-	public List<OmrEditPaper> getPapers() {
-		return papers;
-	}
-	public void setPapers(List<OmrEditPaper> papers) {
-		this.papers = papers;
-	}
-	
+
+    @NotNull(message = "考试ID不能为空")
+    private Long examId;
+
+    @NotBlank(message = "准考证号不能为空")
+    private String examNumber;
+
+    @Valid
+    @NotNull(message = "papers不能为空")
+    @Size(min = 1, message = "papers不能为空")
+    private List<OmrEditPaper> papers;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public List<OmrEditPaper> getPapers() {
+        return papers;
+    }
+
+    public void setPapers(List<OmrEditPaper> papers) {
+        this.papers = papers;
+    }
+
 }

+ 68 - 42
src/main/java/cn/com/qmth/scancloud/bean/omredit/OmrEditPage.java

@@ -4,47 +4,73 @@ import cn.com.qmth.scancloud.bean.answersave.ArrayResult;
 import cn.com.qmth.scancloud.bean.answersave.BoolResult;
 import cn.com.qmth.scancloud.bean.answersave.StringResult;
 
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
 public class OmrEditPage {
-	private Integer index;
-	private BoolResult absent;
-	private BoolResult breach;
-	private StringResult paperType;
-	private ArrayResult question;
-	private ArrayResult selective;
-	public Integer getIndex() {
-		return index;
-	}
-	public void setIndex(Integer index) {
-		this.index = index;
-	}
-	public BoolResult getAbsent() {
-		return absent;
-	}
-	public void setAbsent(BoolResult absent) {
-		this.absent = absent;
-	}
-	public BoolResult getBreach() {
-		return breach;
-	}
-	public void setBreach(BoolResult breach) {
-		this.breach = breach;
-	}
-	public StringResult getPaperType() {
-		return paperType;
-	}
-	public void setPaperType(StringResult paperType) {
-		this.paperType = paperType;
-	}
-	public ArrayResult getQuestion() {
-		return question;
-	}
-	public void setQuestion(ArrayResult question) {
-		this.question = question;
-	}
-	public ArrayResult getSelective() {
-		return selective;
-	}
-	public void setSelective(ArrayResult selective) {
-		this.selective = selective;
-	}
+
+    @NotNull(message = "pageIndex不能为空")
+    @Min(value = 1, message = "pageIndex不能小于1")
+    @Max(value = 2, message = "pageIndex不能大于2")
+    private Integer index;
+
+    private BoolResult absent;
+
+    private BoolResult breach;
+
+    private StringResult paperType;
+
+    private ArrayResult question;
+
+    private ArrayResult selective;
+
+    public Integer getIndex() {
+        return index;
+    }
+
+    public void setIndex(Integer index) {
+        this.index = index;
+    }
+
+    public BoolResult getAbsent() {
+        return absent;
+    }
+
+    public void setAbsent(BoolResult absent) {
+        this.absent = absent;
+    }
+
+    public BoolResult getBreach() {
+        return breach;
+    }
+
+    public void setBreach(BoolResult breach) {
+        this.breach = breach;
+    }
+
+    public StringResult getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(StringResult paperType) {
+        this.paperType = paperType;
+    }
+
+    public ArrayResult getQuestion() {
+        return question;
+    }
+
+    public void setQuestion(ArrayResult question) {
+        this.question = question;
+    }
+
+    public ArrayResult getSelective() {
+        return selective;
+    }
+
+    public void setSelective(ArrayResult selective) {
+        this.selective = selective;
+    }
+
 }

+ 53 - 15
src/main/java/cn/com/qmth/scancloud/bean/omredit/OmrEditPaper.java

@@ -1,21 +1,59 @@
 package cn.com.qmth.scancloud.bean.omredit;
 
+import cn.com.qmth.scancloud.entity.PaperPageEntity;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 import java.util.List;
 
 public class OmrEditPaper {
-	private Integer number;
-	private List<OmrEditPage> pages;
-	public Integer getNumber() {
-		return number;
-	}
-	public void setNumber(Integer number) {
-		this.number = number;
-	}
-	public List<OmrEditPage> getPages() {
-		return pages;
-	}
-	public void setPages(List<OmrEditPage> pages) {
-		this.pages = pages;
-	}
-	
+
+    @NotNull(message = "paper.number不能为空")
+    @Min(value = 1, message = "paper.number不能小于1")
+    private Integer number;
+
+    @Valid
+    @NotNull(message = "paper.pages不能为空")
+    @Size(min = 1, message = "paper.pages不能为空")
+    private List<OmrEditPage> pages;
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public List<OmrEditPage> getPages() {
+        return pages;
+    }
+
+    public void setPages(List<OmrEditPage> pages) {
+        this.pages = pages;
+    }
+
+    public void updatePage(PaperPageEntity page) {
+        for (OmrEditPage pageEdit : pages) {
+            if (pageEdit.getIndex().equals(page.getPageIndex())) {
+                if (pageEdit.getAbsent() != null) {
+                    page.setAbsent(pageEdit.getAbsent());
+                }
+                if (pageEdit.getBreach() != null) {
+                    page.setBreach(pageEdit.getBreach());
+                }
+                if (pageEdit.getPaperType() != null) {
+                    page.setPaperType(pageEdit.getPaperType());
+                }
+                if (pageEdit.getQuestion() != null) {
+                    page.setQuestion(pageEdit.getQuestion());
+                }
+                if (pageEdit.getSelective() != null) {
+                    page.setSelective(pageEdit.getSelective());
+                }
+            }
+        }
+    }
 }

+ 68 - 45
src/main/java/cn/com/qmth/scancloud/bean/papermigrate/PaperMigrateDomain.java

@@ -1,51 +1,74 @@
 package cn.com.qmth.scancloud.bean.papermigrate;
 
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
 import java.util.List;
 
 public class PaperMigrateDomain {
-	private Long paperId;
-	private Long examId;
-	private String examNumber;
-	private Integer paperNumber;
-	private Integer cardNumber;
-	private List<PaperMigratePage> pages;
-	public Long getPaperId() {
-		return paperId;
-	}
-	public void setPaperId(Long paperId) {
-		this.paperId = paperId;
-	}
-	public Long getExamId() {
-		return examId;
-	}
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-	public String getExamNumber() {
-		return examNumber;
-	}
-	public void setExamNumber(String examNumber) {
-		this.examNumber = examNumber;
-	}
-	public Integer getPaperNumber() {
-		return paperNumber;
-	}
-	public void setPaperNumber(Integer paperNumber) {
-		this.paperNumber = paperNumber;
-	}
-	
-	
-	public Integer getCardNumber() {
-		return cardNumber;
-	}
-	public void setCardNumber(Integer cardNumber) {
-		this.cardNumber = cardNumber;
-	}
-	public List<PaperMigratePage> getPages() {
-		return pages;
-	}
-	public void setPages(List<PaperMigratePage> pages) {
-		this.pages = pages;
-	}
-	
+
+    @NotNull(message = "paperId不能为空")
+    private Long paperId;
+
+    @NotNull(message = "考试ID不能为空")
+    private Long examId;
+
+    @NotBlank(message = "准考证号不能为空")
+    private String examNumber;
+
+    @NotNull(message = "paperNumber不能为空")
+    private Integer paperNumber;
+
+    @NotNull(message = "cardNumber不能为空")
+    private Integer cardNumber;
+
+    private List<PaperMigratePage> pages;
+
+    public Long getPaperId() {
+        return paperId;
+    }
+
+    public void setPaperId(Long paperId) {
+        this.paperId = paperId;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public Integer getPaperNumber() {
+        return paperNumber;
+    }
+
+    public void setPaperNumber(Integer paperNumber) {
+        this.paperNumber = paperNumber;
+    }
+
+    public Integer getCardNumber() {
+        return cardNumber;
+    }
+
+    public void setCardNumber(Integer cardNumber) {
+        this.cardNumber = cardNumber;
+    }
+
+    public List<PaperMigratePage> getPages() {
+        return pages;
+    }
+
+    public void setPages(List<PaperMigratePage> pages) {
+        this.pages = pages;
+    }
+
 }

+ 97 - 57
src/main/java/cn/com/qmth/scancloud/bean/papermigrate/PaperMigratePage.java

@@ -2,67 +2,107 @@ package cn.com.qmth.scancloud.bean.papermigrate;
 
 import java.util.List;
 
+import com.qmth.boot.core.exception.ParameterException;
+
 import cn.com.qmth.scancloud.bean.answersave.ArrayResult;
 import cn.com.qmth.scancloud.bean.answersave.BoolResult;
 import cn.com.qmth.scancloud.bean.answersave.StringResult;
+import cn.com.qmth.scancloud.entity.PaperPageEntity;
 
 public class PaperMigratePage {
-	private Integer index;
+
+    private Integer index;
+
     private List<String> sliceUri;
-	private BoolResult absent;
-	private BoolResult breach;
-	private StringResult paperType;
-	private ArrayResult question;
-	private ArrayResult selective;
-	private String recogData;
-	public Integer getIndex() {
-		return index;
-	}
-	public void setIndex(Integer index) {
-		this.index = index;
-	}
-	public BoolResult getAbsent() {
-		return absent;
-	}
-	public void setAbsent(BoolResult absent) {
-		this.absent = absent;
-	}
-	public BoolResult getBreach() {
-		return breach;
-	}
-	public void setBreach(BoolResult breach) {
-		this.breach = breach;
-	}
-	public StringResult getPaperType() {
-		return paperType;
-	}
-	public void setPaperType(StringResult paperType) {
-		this.paperType = paperType;
-	}
-	public ArrayResult getQuestion() {
-		return question;
-	}
-	public void setQuestion(ArrayResult question) {
-		this.question = question;
-	}
-	public ArrayResult getSelective() {
-		return selective;
-	}
-	public void setSelective(ArrayResult selective) {
-		this.selective = selective;
-	}
-	public List<String> getSliceUri() {
-		return sliceUri;
-	}
-	public void setSliceUri(List<String> sliceUri) {
-		this.sliceUri = sliceUri;
-	}
-	public String getRecogData() {
-		return recogData;
-	}
-	public void setRecogData(String recogData) {
-		this.recogData = recogData;
-	}
-	
-	
+
+    private BoolResult absent;
+
+    private BoolResult breach;
+
+    private StringResult paperType;
+
+    private ArrayResult question;
+
+    private ArrayResult selective;
+
+    private String recogData;
+
+    public Integer getIndex() {
+        return index;
+    }
+
+    public void setIndex(Integer index) {
+        this.index = index;
+    }
+
+    public BoolResult getAbsent() {
+        return absent;
+    }
+
+    public void setAbsent(BoolResult absent) {
+        this.absent = absent;
+    }
+
+    public BoolResult getBreach() {
+        return breach;
+    }
+
+    public void setBreach(BoolResult breach) {
+        this.breach = breach;
+    }
+
+    public StringResult getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(StringResult paperType) {
+        this.paperType = paperType;
+    }
+
+    public ArrayResult getQuestion() {
+        return question;
+    }
+
+    public void setQuestion(ArrayResult question) {
+        this.question = question;
+    }
+
+    public ArrayResult getSelective() {
+        return selective;
+    }
+
+    public void setSelective(ArrayResult selective) {
+        this.selective = selective;
+    }
+
+    public List<String> getSliceUri() {
+        return sliceUri;
+    }
+
+    public void setSliceUri(List<String> sliceUri) {
+        this.sliceUri = sliceUri;
+    }
+
+    public String getRecogData() {
+        return recogData;
+    }
+
+    public void setRecogData(String recogData) {
+        this.recogData = recogData;
+    }
+
+    public PaperPageEntity update(PaperPageEntity page, List<String> barcodeContents) {
+        if (page == null) {
+            throw new ParameterException("找不到对应的page信息, pageIndex=" + index);
+        }
+        page.setSlicePath(sliceUri);
+        page.setAbsent(absent);
+        page.setBreach(breach);
+        page.setPaperType(paperType);
+        page.setQuestion(question);
+        page.setSelective(selective);
+        page.setRecogData(recogData);
+        page.checkPaperType(barcodeContents);
+        return page;
+    }
 }

+ 67 - 0
src/main/java/cn/com/qmth/scancloud/bean/refix/AnswerRefixDomain.java

@@ -0,0 +1,67 @@
+package cn.com.qmth.scancloud.bean.refix;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+public class AnswerRefixDomain {
+
+    @NotNull(message = "考试Id不能为空")
+    private Long examId;
+
+    @NotBlank(message = "准考证号不能为空")
+    private String examNumber;
+
+    @NotNull(message = "卡格式编号不能为空")
+    @Min(value = 1, message = "卡格式编号不能小于1")
+    private Integer cardNumber;
+
+    @Valid
+    @NotNull(message = "paper不能为空")
+    @Size(min = 1, message = "paper不能为空")
+    private List<PaperRefixDomain> papers;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public Integer getCardNumber() {
+        return cardNumber;
+    }
+
+    public void setCardNumber(Integer cardNumber) {
+        this.cardNumber = cardNumber;
+    }
+
+    public List<PaperRefixDomain> getPapers() {
+        return papers;
+    }
+
+    public void setPapers(List<PaperRefixDomain> papers) {
+        this.papers = papers;
+    }
+
+    public PaperRefixDomain findPaperDomain(Integer paperNumber, Long paperId) {
+        for (PaperRefixDomain domain : papers) {
+            if (domain.getId().equals(paperId) && domain.getNumber().equals(paperNumber)) {
+                return domain;
+            }
+        }
+        return null;
+    }
+}

+ 108 - 0
src/main/java/cn/com/qmth/scancloud/bean/refix/PageRefixDomain.java

@@ -0,0 +1,108 @@
+package cn.com.qmth.scancloud.bean.refix;
+
+import java.util.List;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+import cn.com.qmth.scancloud.bean.answersave.ArrayResult;
+import cn.com.qmth.scancloud.bean.answersave.BoolResult;
+import cn.com.qmth.scancloud.bean.answersave.StringResult;
+import cn.com.qmth.scancloud.entity.PaperPageEntity;
+
+public class PageRefixDomain {
+
+    @NotNull(message = "paper.page.index不能为空")
+    @Min(value = 1, message = "paper.page.index不能小于1")
+    private Integer index;
+
+    private List<String> sliceUri;
+
+    private BoolResult absent;
+
+    private BoolResult breach;
+
+    private StringResult paperType;
+
+    private ArrayResult question;
+
+    private ArrayResult selective;
+
+    private String recogData;
+
+    public Integer getIndex() {
+        return index;
+    }
+
+    public void setIndex(Integer index) {
+        this.index = index;
+    }
+
+    public List<String> getSliceUri() {
+        return sliceUri;
+    }
+
+    public void setSliceUri(List<String> sliceUri) {
+        this.sliceUri = sliceUri;
+    }
+
+    public BoolResult getAbsent() {
+        return absent;
+    }
+
+    public void setAbsent(BoolResult absent) {
+        this.absent = absent;
+    }
+
+    public BoolResult getBreach() {
+        return breach;
+    }
+
+    public void setBreach(BoolResult breach) {
+        this.breach = breach;
+    }
+
+    public StringResult getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(StringResult paperType) {
+        this.paperType = paperType;
+    }
+
+    public ArrayResult getQuestion() {
+        return question;
+    }
+
+    public void setQuestion(ArrayResult question) {
+        this.question = question;
+    }
+
+    public ArrayResult getSelective() {
+        return selective;
+    }
+
+    public void setSelective(ArrayResult selective) {
+        this.selective = selective;
+    }
+
+    public String getRecogData() {
+        return recogData;
+    }
+
+    public void setRecogData(String recogData) {
+        this.recogData = recogData;
+    }
+
+    public PaperPageEntity update(PaperPageEntity page,List<String> barcodeContents) {
+        page.setSlicePath(sliceUri);
+        page.setAbsent(absent);
+        page.setBreach(breach);
+        page.setPaperType(paperType);
+        page.setQuestion(question);
+        page.setSelective(selective);
+        page.setRecogData(recogData);
+        page.checkPaperType(barcodeContents);
+        return page;
+    }
+}

+ 57 - 0
src/main/java/cn/com/qmth/scancloud/bean/refix/PaperRefixDomain.java

@@ -0,0 +1,57 @@
+package cn.com.qmth.scancloud.bean.refix;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+public class PaperRefixDomain {
+
+    @NotNull(message = "paper.id不能为空")
+    private Long id;
+
+    @NotNull(message = "paper.number不能为空")
+    @Min(value = 1, message = "paper.number不能小于1")
+    private Integer number;
+
+    @NotNull(message = "paper.mismatch不能为空")
+    private Boolean mismatch;
+
+    @Valid
+    @NotNull(message = "pages不能为空")
+    @Size(min = 1, message = "pages不能为空")
+    private List<PageRefixDomain> pages;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public Boolean getMismatch() {
+        return mismatch;
+    }
+
+    public void setMismatch(Boolean mismatch) {
+        this.mismatch = mismatch;
+    }
+
+    public List<PageRefixDomain> getPages() {
+        return pages;
+    }
+
+    public void setPages(List<PageRefixDomain> pages) {
+        this.pages = pages;
+    }
+}

+ 40 - 33
src/main/java/cn/com/qmth/scancloud/client/MarkingcloudApiClient.java

@@ -1,28 +1,14 @@
 package cn.com.qmth.scancloud.client;
 
-import java.util.List;
-
+import cn.com.qmth.scancloud.entity.QuestionEntity;
+import cn.com.qmth.scancloud.enums.CardSource;
+import cn.com.qmth.scancloud.model.*;
 import com.qmth.boot.core.retrofit.annotatioin.RetrofitClient;
 import com.qmth.boot.core.retrofit.utils.SignatureInfo;
-
-import cn.com.qmth.scancloud.enums.CardSource;
-import cn.com.qmth.scancloud.model.AnswerCardInfo;
-import cn.com.qmth.scancloud.model.DataUploadDto;
-import cn.com.qmth.scancloud.model.DataUploadResponse;
-import cn.com.qmth.scancloud.model.ExamInfoDto;
-import cn.com.qmth.scancloud.model.FileUploadResponse;
-import cn.com.qmth.scancloud.model.StudentInfo;
-import cn.com.qmth.scancloud.model.UserInfo;
 import okhttp3.MultipartBody;
-import retrofit2.http.Body;
-import retrofit2.http.Field;
-import retrofit2.http.FormUrlEncoded;
-import retrofit2.http.Header;
-import retrofit2.http.Multipart;
-import retrofit2.http.POST;
-import retrofit2.http.Part;
-import retrofit2.http.Path;
-import retrofit2.http.Query;
+import retrofit2.http.*;
+
+import java.util.List;
 
 @RetrofitClient(configuration = MarkingcloudApiConfiguration.class)
 public interface MarkingcloudApiClient {
@@ -43,8 +29,8 @@ public interface MarkingcloudApiClient {
             @Query("number") Integer number, @Query("source") CardSource source,
             @Query("subjectCode") String subjectCode, @Query("parameter") String parameter,
             @Query("remark") String remark, @Query("paperCount") Integer paperCount,
-            @Query("singlePage") Boolean singlePage, @Query("needAdapte") Boolean needAdapte, @Query("md5") String md5,
-            @Part MultipartBody.Part file);
+            @Query("singlePage") Boolean singlePage, @Query("needAdapte") Boolean needAdapte,
+            @Query("sliceConfig") String sliceConfig, @Query("md5") String md5, @Part MultipartBody.Part file);
 
     @Multipart
     @POST("file/sheet/upload")
@@ -57,18 +43,30 @@ public interface MarkingcloudApiClient {
     FileUploadResponse sliceFileUpload(@Header(SignatureInfo.HEADER_NAME) String signatureInfo,
             @Query("examId") Long examId, @Query("examNumber") String examNumber, @Query("index") Integer index,
             @Query("md5") String md5, @Part MultipartBody.Part file);
-
-//    @FormUrlEncoded
-//    @POST("scan/student/{examId}")
-//    DataUploadResponse dataUpload(@Header(SignatureInfo.HEADER_NAME) String signatureInfo, @Path("examId") Long examId,
-//            @Field("examNumber") String examNumber, @Field("sliceCount") Integer sliceCount,
-//            @Field("sheetCount") Integer sheetCount, @Field("answers") String answers, @Field("absent") Boolean absent,
-//            @Field("batchCode") String batchCode, @Field("paperType") String paperType,
-//            @Field("manual") Boolean manual);
     
+    @Multipart
+    @POST("file/paper/upload")
+    FileUploadResponse paperFileUpload(@Header(SignatureInfo.HEADER_NAME) String signatureInfo,
+            @Query("examId") Long examId, @Query("subjectCode") String subjectCode, @Query("format") String format,
+            @Query("md5") String md5, @Part MultipartBody.Part file);
+    
+    @Multipart
+    @POST("file/answer/upload")
+    FileUploadResponse answerFileUpload(@Header(SignatureInfo.HEADER_NAME) String signatureInfo,
+            @Query("examId") Long examId, @Query("subjectCode") String subjectCode, @Query("format") String format,
+            @Query("md5") String md5, @Part MultipartBody.Part file);
+
+    //    @FormUrlEncoded
+    //    @POST("scan/student/{examId}")
+    //    DataUploadResponse dataUpload(@Header(SignatureInfo.HEADER_NAME) String signatureInfo, @Path("examId") Long examId,
+    //            @Field("examNumber") String examNumber, @Field("sliceCount") Integer sliceCount,
+    //            @Field("sheetCount") Integer sheetCount, @Field("answers") String answers, @Field("absent") Boolean absent,
+    //            @Field("batchCode") String batchCode, @Field("paperType") String paperType,
+    //            @Field("manual") Boolean manual);
+
     @POST("scan/student/{examId}")
-    List<DataUploadResponse> dataUpload(@Header(SignatureInfo.HEADER_NAME) String signatureInfo, @Path("examId") Long examId,
-    		@Body List<DataUploadDto> scStudentParameter);
+    List<DataUploadResponse> dataUpload(@Header(SignatureInfo.HEADER_NAME) String signatureInfo,
+            @Path("examId") Long examId, @Body List<DataUploadDto> scStudentParameter);
 
     @FormUrlEncoded
     @POST("admin/login")
@@ -92,5 +90,14 @@ public interface MarkingcloudApiClient {
     @POST("exam/students")
     List<StudentInfo> getStudents(@Header(SignatureInfo.HEADER_NAME) String signatureInfo, @Field("examId") Long examId,
             @Field("pageNumber") int pageNumber, @Field("pageSize") int pageSize);
-    
+
+    @POST("scan/student/delete")
+    String studentFileDelete(@Header(SignatureInfo.HEADER_NAME) String signatureInfo, @Query("examId") Long examId,
+            @Query("examNumber") String examNumber);
+
+    @FormUrlEncoded
+    @POST("exam/question/list")
+    List<QuestionEntity> getQuestion(@Header(SignatureInfo.HEADER_NAME) String signatureInfo,
+            @Field("examId") Long examId, @Field("subjectCode") String subjectCode);
+
 }

+ 6 - 1
src/main/java/cn/com/qmth/scancloud/client/MarkingcloudApiConfiguration.java

@@ -12,10 +12,15 @@ public class MarkingcloudApiConfiguration implements CustomizeRetrofitConfigurat
     }
 
     public String getBaseUrl() {
-        return properties.getServer() + "/api/";
+        String server = properties.getServer();
+        if (server.endsWith("/")) {
+            server = server.substring(0, server.length() - 1);
+        }
+        return server + "/api/";
     }
 
     public SignatureProvider getSignature() {
         return null;
     }
+
 }

+ 6 - 15
src/main/java/cn/com/qmth/scancloud/client/MarkingcloudProperties.java

@@ -1,25 +1,22 @@
 package cn.com.qmth.scancloud.client;
 
-import javax.validation.constraints.NotNull;
-
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 
+import javax.validation.constraints.NotNull;
+
 @Component
 @ConfigurationProperties(prefix = "scancloud.markingcloud")
 public class MarkingcloudProperties {
 
     @NotNull
     private String server = "https://www.markingcloud.com";
-    
-    @NotNull
-    private String fileServer = "https://www.markingcloud.com/";
-    
+
+    private String fileServer;
+
     private String invoker;
-    
-     private String accessToken;
 
-    // private String license;
+    private String accessToken;
 
     public String getServer() {
         return server;
@@ -29,32 +26,26 @@ public class MarkingcloudProperties {
         this.server = server;
     }
 
-    
     public String getAccessToken() {
         return accessToken;
     }
 
-    
     public void setAccessToken(String accessToken) {
         this.accessToken = accessToken;
     }
 
-    
     public String getInvoker() {
         return invoker;
     }
 
-    
     public void setInvoker(String invoker) {
         this.invoker = invoker;
     }
 
-    
     public String getFileServer() {
         return fileServer;
     }
 
-    
     public void setFileServer(String fileServer) {
         this.fileServer = fileServer;
     }

+ 40 - 19
src/main/java/cn/com/qmth/scancloud/controller/BaseController.java

@@ -1,30 +1,25 @@
 package cn.com.qmth.scancloud.controller;
 
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import cn.com.qmth.scancloud.bean.User;
+import cn.com.qmth.scancloud.util.ObjectUtil;
+import cn.com.qmth.scancloud.util.ServletUtil;
+import cn.com.qmth.scancloud.vo.UpdateTimeVo;
+import com.qmth.boot.tools.excel.ExcelWriter;
+import com.qmth.boot.tools.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
 import java.lang.reflect.Field;
 import java.net.URLEncoder;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-
-import cn.com.qmth.scancloud.bean.User;
-import cn.com.qmth.scancloud.util.ObjectUtil;
-import cn.com.qmth.scancloud.util.ServletUtil;
-import cn.com.qmth.scancloud.vo.UpdateTimeVo;
-
 public class BaseController {
 
     protected HttpServletRequest currentRequest() {
@@ -108,6 +103,7 @@ public class BaseController {
     protected void trim(Object bean) {
         trim(bean, false);
     }
+
     @SuppressWarnings("deprecation")
     protected void exportFile(String fileName, InputStream in) {
         OutputStream out = null;
@@ -127,6 +123,25 @@ public class BaseController {
             IOUtils.closeQuietly(in);
         }
     }
+
+    protected void exportFile(String fileName, ExcelWriter excelWriter) {
+        OutputStream out = null;
+        try {
+            fileName = URLEncoder.encode(fileName, "UTF-8");
+            HttpServletResponse response = getResponse();
+            response.reset();
+            response.setHeader("Content-Disposition", "inline; filename=" + fileName);
+            response.setContentType("application/octet-stream;charset=UTF-8");
+            out = response.getOutputStream();
+            excelWriter.output(out);
+            out.flush();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            IOUtils.closeQuietly(out);
+        }
+    }
+
     @SuppressWarnings("deprecation")
     protected void exportFile(String fileName, File file) {
         OutputStream out = null;
@@ -162,4 +177,10 @@ public class BaseController {
         vo.setUpdateTime(System.currentTimeMillis());
         return vo;
     }
+
+    protected Object success(boolean success) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("success", success);
+        return map;
+    }
 }

+ 82 - 59
src/main/java/cn/com/qmth/scancloud/controller/SystemController.java

@@ -1,92 +1,115 @@
 package cn.com.qmth.scancloud.controller;
 
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-import com.qmth.boot.core.exception.ParameterException;
-
 import cn.com.qmth.scancloud.bean.User;
 import cn.com.qmth.scancloud.config.SysProperty;
 import cn.com.qmth.scancloud.entity.AnswerCardEntity;
 import cn.com.qmth.scancloud.entity.ExamEntity;
+import cn.com.qmth.scancloud.entity.SystemConfigEntity;
+import cn.com.qmth.scancloud.enums.Role;
 import cn.com.qmth.scancloud.exception.ParameterExceptions;
+import cn.com.qmth.scancloud.service.AdapteFileService;
 import cn.com.qmth.scancloud.service.AnswerCardService;
 import cn.com.qmth.scancloud.service.ExamService;
 import cn.com.qmth.scancloud.service.SessionService;
+import cn.com.qmth.scancloud.service.SystemConfigService;
 import cn.com.qmth.scancloud.support.WithoutStackTrace;
 import cn.com.qmth.scancloud.vo.ActiveTimeVo;
 import cn.com.qmth.scancloud.vo.AnswerCardVo;
 import cn.com.qmth.scancloud.vo.LogoutTimeVo;
 import cn.com.qmth.scancloud.vo.apistatus.ApiStatusVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.api.utils.RequestUtil;
+import com.qmth.boot.core.exception.ParameterException;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
 
 @RestController
 @Api(tags = "系统接口")
 @RequestMapping(ApiConstant.DEFAULT_URI_PREFIX)
 @Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
 public class SystemController extends BaseController {
-	
-	@Autowired
-	private SessionService sessionService;
-	
-	@Autowired
-	private SysProperty sysProperty;
-	
-	 @Autowired
-	private AnswerCardService answerCardService;
-	 
-	 @Autowired
-	 private ExamService examService;
 
-	@WithoutStackTrace
-	@ApiOperation(value = "心跳接口")
-	@PostMapping("heartbeat")
-	public ActiveTimeVo heartbeat() {
-		User user=getAccessUser();
-		ActiveTimeVo vo=new ActiveTimeVo();
-		vo.setActiveTime(sessionService.updateUserSession(user));
-		return vo;
-	}
+    @Autowired
+    private SessionService sessionService;
+
+    @Autowired
+    private SysProperty sysProperty;
+
+    @Autowired
+    private AnswerCardService answerCardService;
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private AdapteFileService adapteFileService;
+    
+    @Autowired
+    private SystemConfigService systemConfigService;
+
+    @WithoutStackTrace
+    @ApiOperation(value = "心跳接口")
+    @PostMapping("heartbeat")
+    public ActiveTimeVo heartbeat() {
+        User user = getAccessUser();
+        ActiveTimeVo vo = new ActiveTimeVo();
+        vo.setActiveTime(sessionService.updateUserSession(user));
+        return vo;
+    }
+
+    @ApiOperation(value = "登出接口")
+    @PostMapping("logout")
+    public LogoutTimeVo logout() {
+        User user = getAccessUser();
+        sessionService.userLogout(user);
+        LogoutTimeVo vo = new LogoutTimeVo();
+        vo.setLogoutTime(System.currentTimeMillis());
+        return vo;
+    }
+
+    @Aac(strict = BOOL.FALSE, auth = BOOL.FALSE)
+    @ApiOperation(value = "服务端状态")
+    @PostMapping("status")
+    public ApiStatusVo status(HttpServletRequest request) {
+        ApiStatusVo vo = new ApiStatusVo();
+        vo.setFileUriPrefix(sysProperty.getFileUriPrefix());
+        vo.setVersion(sysProperty.getVersion());
+        vo.setTime(System.currentTimeMillis());
+        vo.setClientIP(RequestUtil.getIpAddress(request));
+        SystemConfigEntity conf = systemConfigService.findConfig();
+        vo.setSystemMode(conf.getSystemMode());
+        return vo;
+    }
 
-	@ApiOperation(value = "登出接口")
-	@PostMapping("logout")
-	public LogoutTimeVo logout() {
-		User user=getAccessUser();
-		sessionService.userLogout(user);
-		LogoutTimeVo vo=new LogoutTimeVo();
-		vo.setLogoutTime(System.currentTimeMillis());
-		return vo;
-	}
-	
-	@Aac(strict = BOOL.FALSE, auth = BOOL.FALSE)
-	@ApiOperation(value = "服务端状态")
-	@PostMapping("status")
-	public ApiStatusVo status() {
-		ApiStatusVo vo=new ApiStatusVo();
-		vo.setFileUriPrefix(sysProperty.getFileUriPrefix());
-		vo.setVersion(sysProperty.getVersion());
-		vo.setTime(System.currentTimeMillis());
-		return vo;
-	}
-	
     @ApiOperation(value = "答题卡卡格式查询")
     @PostMapping("/card/answer")
-    public AnswerCardVo cardAnswer(@RequestParam Long examId,@RequestParam Integer number) {
-        ExamEntity exam =examService.getById(examId);
-        if(exam ==null ||!exam.getEnable()) {
+    public AnswerCardVo cardAnswer(HttpServletRequest request, @RequestParam Long examId,
+            @RequestParam Integer number) {
+        ExamEntity exam = examService.getById(examId);
+        if (exam == null || !exam.getEnable()) {
             throw ParameterExceptions.EXAM_NOT_FOUND;
         }
-        AnswerCardEntity  card =answerCardService.findByExamAndNumber(examId, number);
-        if(card ==null ) {
+        AnswerCardEntity card = answerCardService.findByExamAndNumber(examId, number);
+        if (card == null) {
             throw new ParameterException("找不到卡格式");
         }
-        return new AnswerCardVo(card);
+        AnswerCardVo vo = new AnswerCardVo(card);
+        User user = getAccessUser();
+        if (user.getRole().equals(Role.SCANNER)) {
+            vo.setAdapteFile(adapteFileService
+                    .listByExamIdAndCardNumberForScanner(examId, number, RequestUtil.getIpAddress(request)));
+        } else {
+            vo.setAdapteFile(adapteFileService.listByExamIdAndCardNumber(examId, card.getNumber()));
+        }
+        return vo;
     }
 }

+ 13 - 25
src/main/java/cn/com/qmth/scancloud/controller/admin/ArbitrateController.java

@@ -1,18 +1,5 @@
 package cn.com.qmth.scancloud.controller.admin;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-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.RestController;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-
 import cn.com.qmth.scancloud.bean.User;
 import cn.com.qmth.scancloud.controller.BaseController;
 import cn.com.qmth.scancloud.service.OmrTaskService;
@@ -20,9 +7,15 @@ import cn.com.qmth.scancloud.vo.task.TaskResultVo;
 import cn.com.qmth.scancloud.vo.task.TaskSaveVo;
 import cn.com.qmth.scancloud.vo.task.TaskStatusVo;
 import cn.com.qmth.scancloud.vo.task.TaskVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import net.sf.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
 
 @RestController
 @Api(tags = "识别对照接口")
@@ -32,8 +25,6 @@ public class ArbitrateController extends BaseController {
 
     protected static final Logger log = LoggerFactory.getLogger(ArbitrateController.class);
 
-    @Autowired
-    private OmrTaskService taskService;
     @Autowired
     private OmrTaskService omrTaskService;
 
@@ -41,31 +32,28 @@ public class ArbitrateController extends BaseController {
     @RequestMapping(value = "/get", method = RequestMethod.POST)
     public TaskVo get(@RequestParam Long examId) {
         User user = getAccessUser();
-        TaskVo task = taskService.getArbitrateTask(examId, user.getAccount());
-        return task;
+        return omrTaskService.getArbitrateTask(examId, user.getAccount());
     }
 
     @ApiOperation(value = "识别对照仲裁结果提交")
     @RequestMapping(value = "/save", method = RequestMethod.POST)
     public TaskSaveVo save(@RequestBody TaskResultVo result) {
         User user = getAccessUser();
-        return taskService.submitTask(result, user.getAccount());
+        return omrTaskService.submitTask(result, user);
     }
 
     @ApiOperation(value = "识别对照仲裁进度状态")
     @RequestMapping(value = "/status", method = RequestMethod.POST)
     public TaskStatusVo status(@RequestParam Long examId) {
         User user = getAccessUser();
-        return taskService.getStatus(examId, true,user.getAccount());
+        return omrTaskService.getStatus(examId, true, user.getAccount());
     }
-    
+
     @ApiOperation(value = "识别对照仲裁任务释放")
     @RequestMapping(value = "/release", method = RequestMethod.POST)
-    public JSONObject release(@RequestParam Long examId) {
+    public Object release(@RequestParam Long examId) {
         User user = getAccessUser();
         omrTaskService.releaseByUser(examId, user.getAccount());
-        JSONObject result = new JSONObject();
-        result.accumulate("success", true);
-        return result;
+        return success(true);
     }
 }

+ 54 - 0
src/main/java/cn/com/qmth/scancloud/controller/admin/AuditorController.java

@@ -0,0 +1,54 @@
+package cn.com.qmth.scancloud.controller.admin;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+
+import cn.com.qmth.scancloud.bean.User;
+import cn.com.qmth.scancloud.controller.BaseController;
+import cn.com.qmth.scancloud.service.UserService;
+import cn.com.qmth.scancloud.vo.AuditorVo;
+import cn.com.qmth.scancloud.vo.CreateCountVo;
+import cn.com.qmth.scancloud.vo.UpdateCountVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "审核员管理接口")
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/admin/auditor")
+@Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
+public class AuditorController extends BaseController {
+
+    @Autowired
+    private UserService userService;
+
+    @ApiOperation(value = "审核员列表")
+    @PostMapping("list")
+    public List<AuditorVo> list() {
+        User user = getAccessUser();
+        return userService.findAuditor(user.getSchoolId());
+    }
+
+    @ApiOperation(value = "审核员修改密码")
+    @PostMapping("password")
+    public UpdateCountVo password(@RequestParam List<Long> id, @RequestParam String password) {
+        return userService.updatePassword(id, password);
+    }
+
+    @ApiOperation(value = "批量创建审核员")
+    @PostMapping("create")
+    public CreateCountVo create(@RequestParam String prefix, @RequestParam Integer count,
+            @RequestParam String password) {
+        User user = getAccessUser();
+        return userService.create(user.getSchoolId(),prefix, count, password);
+    }
+
+}

+ 2 - 1
src/main/java/cn/com/qmth/scancloud/controller/admin/AuthController.java

@@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.api.utils.RequestUtil;
 
 import cn.com.qmth.scancloud.controller.BaseController;
 import cn.com.qmth.scancloud.service.AuthService;
@@ -27,7 +28,7 @@ public class AuthController extends BaseController {
     @ApiOperation(value = "管理员登录")
     @PostMapping("/login")
     public AdminLoginVo login(@RequestParam String loginName, @RequestParam String password, HttpServletRequest request) {
-        return AdminLoginVo.of(authService.adminLogin(loginName, password));
+        return AdminLoginVo.of(authService.adminLogin(loginName, password,RequestUtil.getIpAddress(request)));
     }
 
 }

+ 38 - 23
src/main/java/cn/com/qmth/scancloud/controller/admin/CardController.java

@@ -1,16 +1,22 @@
 package cn.com.qmth.scancloud.controller.admin;
 
+import cn.com.qmth.scancloud.controller.BaseController;
+import cn.com.qmth.scancloud.entity.AnswerCardEntity;
+import cn.com.qmth.scancloud.enums.LockType;
+import cn.com.qmth.scancloud.service.AdapteFileService;
+import cn.com.qmth.scancloud.service.AnswerCardService;
+import cn.com.qmth.scancloud.service.ExamService;
+import cn.com.qmth.scancloud.task.CardSyncThread;
+import cn.com.qmth.scancloud.vo.UriVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.api.utils.RequestUtil;
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
+import com.qmth.boot.core.exception.ReentrantException;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.annotation.Resource;
-
 import net.sf.json.JSONObject;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -21,17 +27,11 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
-import cn.com.qmth.scancloud.controller.BaseController;
-import cn.com.qmth.scancloud.entity.AnswerCardEntity;
-import cn.com.qmth.scancloud.enums.LockType;
-import cn.com.qmth.scancloud.service.AnswerCardService;
-import cn.com.qmth.scancloud.task.CardSyncThread;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-import com.qmth.boot.core.concurrent.service.ConcurrentService;
-import com.qmth.boot.core.exception.ReentrantException;
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
 
 @RestController
 @Api(tags = "卡格式接口")
@@ -47,6 +47,12 @@ public class CardController extends BaseController {
     @Resource
     private ConcurrentService concurrentService;
 
+    @Autowired
+    private AdapteFileService adapteFileService;
+    
+    @Autowired
+    private ExamService examService;
+
     // @Qualifier("task-executor")
     @Autowired
     private AsyncTaskExecutor taskExecutor;
@@ -56,7 +62,7 @@ public class CardController extends BaseController {
     public Map<String, Object> info(@RequestParam Long examId) {
         Map<String, Object> status = new HashMap<String, Object>();
         status.put("synching", concurrentService.isLocked(LockType.CARD_SYNC + "-" + examId));
-        status.put("updateTime", System.currentTimeMillis());
+        status.put("updateTime", examService.getById(examId).getCardSyncTime());
         Map<String, Object> result = new HashMap<String, Object>();
         result.put("status", status);
         result.put("answer", answerCardService.listByExamId(examId));
@@ -83,7 +89,7 @@ public class CardController extends BaseController {
     public JSONObject syncStatus(@RequestParam Long examId) {
         JSONObject status = new JSONObject();
         status.accumulate("synching", concurrentService.isLocked(LockType.CARD_SYNC + "-" + examId));
-        status.accumulate("updateTime", System.currentTimeMillis());
+        status.accumulate("updateTime",  examService.getById(examId).getCardSyncTime());
         return status;
     }
 
@@ -96,8 +102,9 @@ public class CardController extends BaseController {
         if (concurrentService.getLock(LockType.CARD_SYNC + "-" + examId).tryLock()) {
             try {
                 JSONObject result = new JSONObject();
-                AnswerCardEntity card = answerCardService.save(getAccessUser(), examId, number, subjectCode, parameter,
-                        remark, paperCount, singlePage, md5, file);
+                AnswerCardEntity card = answerCardService
+                        .save(getAccessUser(), examId, number, subjectCode, parameter, remark, paperCount, singlePage,
+                                md5, file);
                 result.accumulate("number", card.getNumber());
                 result.accumulate("updateTime", System.currentTimeMillis());
                 return result;
@@ -127,6 +134,14 @@ public class CardController extends BaseController {
         }
     }
 
+    @ApiOperation(value = "答题卡适配卡格式上传")
+    @RequestMapping(value = "/answer/adapte/upload", method = RequestMethod.POST)
+    public UriVo adapteUpload(HttpServletRequest request, @RequestParam Long examId, @RequestParam Integer cardNumber,
+            @RequestParam MultipartFile file, @RequestParam String md5) {
+        return adapteFileService
+                .save(RequestUtil.getIpAddress(request), getAccessUser().getRole(), examId, cardNumber, md5, file);
+    }
+
     @ApiOperation(value = "创建/修改签到表卡格式")
     @RequestMapping(value = "/package/save", method = RequestMethod.POST)
     public JSONObject packageSave() {

+ 142 - 166
src/main/java/cn/com/qmth/scancloud/controller/admin/CheckController.java

@@ -1,23 +1,14 @@
 package cn.com.qmth.scancloud.controller.admin;
 
-import java.io.IOException;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-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.RestController;
-import org.springframework.web.multipart.MultipartFile;
-
+import cn.com.qmth.scancloud.bean.AbsentQueryDomain;
+import cn.com.qmth.scancloud.controller.BaseController;
+import cn.com.qmth.scancloud.entity.SubjectEntity;
+import cn.com.qmth.scancloud.enums.ConditionType;
+import cn.com.qmth.scancloud.enums.GroupType;
+import cn.com.qmth.scancloud.model.ManualAbsentImportDTO;
+import cn.com.qmth.scancloud.service.StudentService;
+import cn.com.qmth.scancloud.service.SubjectService;
+import cn.com.qmth.scancloud.vo.*;
 import com.qmth.boot.api.annotation.Aac;
 import com.qmth.boot.api.annotation.BOOL;
 import com.qmth.boot.api.constant.ApiConstant;
@@ -26,22 +17,21 @@ import com.qmth.boot.core.exception.StatusException;
 import com.qmth.boot.tools.excel.ExcelWriter;
 import com.qmth.boot.tools.excel.enums.ExcelType;
 import com.qmth.boot.tools.excel.model.DataMap;
-
-import cn.com.qmth.scancloud.bean.AbsentQueryDomain;
-import cn.com.qmth.scancloud.controller.BaseController;
-import cn.com.qmth.scancloud.entity.SubjectEntity;
-import cn.com.qmth.scancloud.enums.ConditionType;
-import cn.com.qmth.scancloud.enums.GroupType;
-import cn.com.qmth.scancloud.service.StudentService;
-import cn.com.qmth.scancloud.service.SubjectService;
-import cn.com.qmth.scancloud.util.ResouceUtil;
-import cn.com.qmth.scancloud.vo.AbsentInfoVo;
-import cn.com.qmth.scancloud.vo.AbsentManualImportVo;
-import cn.com.qmth.scancloud.vo.AbsentQueryVo;
-import cn.com.qmth.scancloud.vo.OmrConditionsVo;
-import cn.com.qmth.scancloud.vo.UpdateTimeVo;
+import com.qmth.boot.tools.iterator.PageListIterator;
+import com.qmth.boot.tools.iterator.SingletonIterator;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @RestController
 @Api(tags = "缺考接口")
@@ -49,142 +39,128 @@ import io.swagger.annotations.ApiOperation;
 @Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
 public class CheckController extends BaseController {
 
-	@Autowired
-	private StudentService studentService;
-	
-	@Autowired
-	private SubjectService subjectService;
-	
-	@ApiOperation(value = "所有可用的识别对照条件")
-	@PostMapping("omr/conditions")
-	public List<OmrConditionsVo> omrConditions() {
-		List<OmrConditionsVo> list=new ArrayList<>();
-		for(ConditionType c:ConditionType.values()) {
-			OmrConditionsVo vo=new OmrConditionsVo();
-			vo.setCode(c);
-			vo.setName(c.getName());
-			if (ConditionType.QUESTION_SINGLE_BLANK.equals(c)
-            		||ConditionType.QUESTION_MULTI_BLANK.equals(c)
-            		||ConditionType.SELECTIVE_EXCEED.equals(c)
-            		||ConditionType.SELECTIVE_BLANK.equals(c)) {
-				vo.setNeedValue(true);
-			}else {
-				vo.setNeedValue(false);
-			}
-			list.add(vo);
-		}
-		return list;
-	}
-
-	@ApiOperation(value = "缺考数据汇总")
-	@PostMapping("absent/info")
-	public AbsentInfoVo absentInfo(@RequestParam Long examId) {
-		return studentService.absentInfo(examId);
-	}
-
-	@ApiOperation(value = "查询缺考数据")
-	@RequestMapping(value = "absent/query", method = RequestMethod.POST)
-	public PageResult<AbsentQueryVo> absentQuery(AbsentQueryDomain query) {
-		return studentService.absentQuery(query);
-	}
-
-	@ApiOperation(value = "导出缺考数据")
-	@RequestMapping(value = "absent/export", method = RequestMethod.POST)
-	public void absentExport(AbsentQueryDomain query, HttpServletResponse response) throws IOException {
+    @Autowired
+    private StudentService studentService;
+
+    @Autowired
+    private SubjectService subjectService;
+
+    @ApiOperation(value = "所有可用的识别对照条件")
+    @PostMapping("omr/conditions")
+    public List<OmrConditionsVo> omrConditions() {
+        return Arrays.stream(ConditionType.values()).map(OmrConditionsVo::new).collect(Collectors.toList());
+    }
+
+    @ApiOperation(value = "缺考数据汇总")
+    @PostMapping("absent/info")
+    public AbsentInfoVo absentInfo(@RequestParam Long examId, @RequestParam(required = false) GroupType groupType,
+            @RequestParam(required = false) String groupName) {
+        return studentService.absentInfo(examId, groupType, groupName);
+    }
+
+    @ApiOperation(value = "查询缺考数据")
+    @RequestMapping(value = "absent/query", method = RequestMethod.POST)
+    public PageResult<AbsentQueryVo> absentQuery(@Validated AbsentQueryDomain query) {
+        return studentService.absentQuery(query);
+    }
+
+    @ApiOperation(value = "查询缺考数据标识")
+    @RequestMapping(value = "absent/summary", method = RequestMethod.POST)
+    public List<String> absentSummary(@Validated AbsentQueryDomain query) {
+        return studentService.absentSummary(query);
+    }
+
+    @ApiOperation(value = "导出缺考数据")
+    @RequestMapping(value = "absent/export", method = RequestMethod.POST)
+    public void absentExport(@Validated AbsentQueryDomain query, HttpServletResponse response) throws IOException {
         String fileName = URLEncoder.encode("缺考数据", "UTF-8");
         response.setHeader("Content-Disposition", "inline; filename=" + fileName + ".xlsx");
         response.setContentType("application/vnd.ms-excel");
 
-		List<AbsentQueryVo> list = studentService.absentExportList(query);
-		List<DataMap> ret=new ArrayList<>();
-		ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX);
-		
-		String groupNameCol = null;
-		if (GroupType.PACKAGE.equals(query.getGroupType())) {
-			groupNameCol = "卷袋";
-		}
-		if (GroupType.SUBJECT.equals(query.getGroupType())) {
-			groupNameCol = "科目代码";
-		}
-		if (GroupType.CAMPUS.equals(query.getGroupType())) {
-			groupNameCol = "学习中心";
-		}
-		if (GroupType.EXAM_SITE.equals(query.getGroupType())) {
-			groupNameCol = "考点";
-		}
-		if (GroupType.EXAM_ROOM.equals(query.getGroupType())) {
-			groupNameCol = "考场";
-		}
-		if (GroupType.STUDENT_CODE.equals(query.getGroupType())) {
-			groupNameCol = "学号";
-		}
-		if(CollectionUtils.isNotEmpty(list)) {
-			Map<String,SubjectEntity> subMap=new HashMap<>();
-			for(AbsentQueryVo vo:list) {
-				DataMap map=new DataMap();
-				ret.add(map);
-				map.put(groupNameCol,vo.getGroupName());
-				if (GroupType.SUBJECT.equals(query.getGroupType())) {
-					map.put("科目名称",getSubjectName(subMap, vo.getGroupName(), query.getExamId()));
-				}
-				map.put("考生总数",getStringVal(vo.getTotalCount()));
-				map.put("已扫描",getStringVal(vo.getScannedCount()));
-				map.put("未扫描",getStringVal(vo.getUnexistCount()));
-				map.put("指定缺考",getStringVal(vo.getManualAbsentCount()));
-				map.put("识别缺考",getStringVal(vo.getOmrAbsentCount()));
-				map.put("缺考异常",getStringVal(vo.getAbsentSuspectCount()));
-			}
-		}
-		String[] head;
-		if (GroupType.SUBJECT.equals(query.getGroupType())) {
-			head=new String[] {groupNameCol,"科目名称","考生总数","已扫描","未扫描","指定缺考","识别缺考","缺考异常"};
-		}else {
-			head=new String[] {groupNameCol,"考生总数","已扫描","未扫描","指定缺考","识别缺考","缺考异常"};
-		}
-		writer.writeDataMaps("sheet1", null,head, ret.iterator());
-		writer.output(response.getOutputStream());
-	}
-	
-	private String getSubjectName(Map<String,SubjectEntity> subMap,String subjectCode,Long examId) {
-		SubjectEntity  sub=subMap.get(subjectCode);
-		if(subMap.get(subjectCode)==null) {
-			sub=subjectService.findByExamIdAndCode(examId, subjectCode);
-			if(sub==null) {
-				throw new StatusException("未找到科目信息:"+subjectCode);
-			}
-			subMap.put(subjectCode, sub);
-		}
-		return sub.getName();
-	}
-	
-	private String getStringVal(Integer val) {
-		if(val==null) {
-			return "0";
-		}
-		return val.toString();
-	}
-
-	@PostMapping("absent/manual/import")
-	@ApiOperation(value = "导入指定缺考名单")
-	public AbsentManualImportVo absentManualImport(@RequestParam Long examId, @RequestParam MultipartFile file) {
-		return studentService.absentManualImport(examId, file);
-	}
-
-	@ApiOperation(value = "下载导入指定缺考名单模板")
-	@PostMapping("absent/manual/template")
-	public void getImportTemplate() throws IOException {
-		exportFile("指定缺考名单导入模板.xlsx", ResouceUtil.getStream("importtemplates/absentManualImportTemplate.xlsx"));
-	}
-
-	@ApiOperation(value = "更新为指定缺考")
-	@PostMapping("absent/manual/update")
-	public UpdateTimeVo absentManualUpdate(@RequestParam Long examId, @RequestParam String examNumber) {
-		return studentService.absentManualUpdate(examId, examNumber);
-	}
-
-	@ApiOperation(value = "取消缺考标记嫌疑")
-	@PostMapping("absent/suspect/remove")
-	public UpdateTimeVo absentSuspectRemove(@RequestParam Long examId, @RequestParam String examNumber) {
-		return studentService.absentSuspectRemove(examId, examNumber);
-	}
+        Map<String, SubjectEntity> subjectMap = new HashMap<>();
+        PageListIterator<DataMap> iterator = new PageListIterator<DataMap>(100) {
+
+            @Override
+            public Collection<DataMap> getPageList(int pageNumber, int pageSize) {
+                query.setPageNumber(pageNumber);
+                query.setPageSize(pageSize);
+                List<AbsentQueryVo> list = studentService.absentExportList(query);
+                List<DataMap> ret = new ArrayList<>();
+                if (CollectionUtils.isNotEmpty(list)) {
+                    for (AbsentQueryVo vo : list) {
+                        DataMap map = new DataMap();
+                        if (GroupType.SUBJECT.equals(query.getGroupType())) {
+                            map.put("科目代码", vo.getGroupName());
+                            map.put("科目名称", getSubjectName(subjectMap, vo.getGroupName(), query.getExamId()));
+                        } else {
+                            map.put(query.getGroupType().getName(), vo.getGroupName());
+                        }
+                        map.put("考生总数", getStringVal(vo.getTotalCount()));
+                        map.put("已扫描", getStringVal(vo.getScannedCount()));
+                        map.put("未扫描", getStringVal(vo.getUnexistCount()));
+                        map.put("指定缺考", getStringVal(vo.getManualAbsentCount()));
+                        map.put("识别缺考", getStringVal(vo.getOmrAbsentCount()));
+                        map.put("缺考异常", getStringVal(vo.getAbsentSuspectCount()));
+                        ret.add(map);
+                    }
+                }
+                return ret;
+            }
+        };
+        String[] head;
+        if (GroupType.SUBJECT.equals(query.getGroupType())) {
+            head = new String[] { "科目代码", "科目名称", "考生总数", "已扫描", "未扫描", "指定缺考", "识别缺考", "缺考异常" };
+        } else {
+            head = new String[] { query.getGroupType().getName(), "考生总数", "已扫描", "未扫描", "指定缺考", "识别缺考", "缺考异常" };
+        }
+        ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX);
+        writer.writeDataMaps("缺考核对", null, head, iterator);
+        writer.output(response.getOutputStream());
+    }
+
+    private String getSubjectName(Map<String, SubjectEntity> subMap, String subjectCode, Long examId) {
+        SubjectEntity subject = subMap.get(subjectCode);
+        if (subMap.get(subjectCode) == null) {
+            subject = subjectService.findByExamIdAndCode(examId, subjectCode);
+            if (subject == null) {
+                throw new StatusException("未找到科目信息:" + subjectCode);
+            }
+            subMap.put(subjectCode, subject);
+        }
+        return subject.getName();
+    }
+
+    private String getStringVal(Integer val) {
+        if (val == null) {
+            return "0";
+        }
+        return val.toString();
+    }
+
+    @PostMapping("absent/manual/import")
+    @ApiOperation(value = "导入指定缺考名单")
+    public AbsentManualImportVo absentManualImport(@RequestParam Long examId, @RequestParam MultipartFile file) {
+        return studentService.absentManualImport(examId, file);
+    }
+
+    @ApiOperation(value = "下载导入指定缺考名单模板")
+    @PostMapping("absent/manual/template")
+    public void getImportTemplate() throws IOException {
+        ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX);
+        writer.writeObjects("指定缺考名单", null, ManualAbsentImportDTO.class,
+                new SingletonIterator<>(new ManualAbsentImportDTO()));
+        exportFile("指定缺考名单导入模板.xlsx", writer);
+    }
+
+    @ApiOperation(value = "更新为指定缺考")
+    @PostMapping("absent/manual/update")
+    public UpdateTimeVo absentManualUpdate(@RequestParam Long examId, @RequestParam String examNumber) {
+        return studentService.absentManualUpdate(examId, examNumber);
+    }
+
+    @ApiOperation(value = "取消缺考标记嫌疑")
+    @PostMapping("absent/suspect/remove")
+    public UpdateTimeVo absentSuspectRemove(@RequestParam Long examId, @RequestParam String examNumber) {
+        return studentService.absentSuspectUpdate(examId, examNumber, false);
+    }
 }

+ 73 - 0
src/main/java/cn/com/qmth/scancloud/controller/admin/CheckImageController.java

@@ -0,0 +1,73 @@
+package cn.com.qmth.scancloud.controller.admin;
+
+import cn.com.qmth.scancloud.bean.User;
+import cn.com.qmth.scancloud.controller.BaseController;
+import cn.com.qmth.scancloud.service.BatchService;
+import cn.com.qmth.scancloud.service.ExamService;
+import cn.com.qmth.scancloud.vo.checkimage.CheckImageSubmitVo;
+import cn.com.qmth.scancloud.vo.checkimage.RatioVo;
+import cn.com.qmth.scancloud.vo.task.TaskStatusVo;
+import cn.com.qmth.scancloud.vo.verify.VerifyTaskVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@Api(tags = "图片审核接口")
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/admin/check/image")
+@Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
+public class CheckImageController extends BaseController {
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private BatchService batchService;
+
+    @ApiOperation(value = "修改答题卡扫描图片抽查比例")
+    @PostMapping("ratio")
+    public RatioVo ratio(@RequestParam Long examId, @RequestParam Double ratio) {
+        return examService.updateRatio(examId, ratio);
+    }
+
+    @ApiOperation(value = "答题卡扫描图片检查任务状态")
+    @PostMapping("status")
+    public TaskStatusVo status(@RequestParam Long examId) {
+        User user = getAccessUser();
+        return batchService.getCheckImageStatus(examId, user);
+    }
+
+    @ApiOperation(value = "获取答题卡扫描图片检查任务")
+    @RequestMapping(value = "get", method = RequestMethod.POST)
+    public VerifyTaskVo get(@RequestParam Long examId) {
+        User user = getAccessUser();
+        return batchService.getCheckImageTask(examId, user);
+    }
+
+    @ApiOperation(value = "历史答题卡扫描图片检查任务")
+    @RequestMapping(value = "history", method = RequestMethod.POST)
+    public VerifyTaskVo history(@RequestParam Long examId, @RequestParam(required = false) Long batchId) {
+        User user = getAccessUser();
+        return batchService.getHistoryCheckImageTask(examId, batchId, user);
+    }
+
+    @ApiOperation(value = "答题卡扫描图片检查任务提交")
+    @RequestMapping(value = "submit", method = RequestMethod.POST)
+    public CheckImageSubmitVo submit(@RequestParam Long examId, @RequestParam Long batchId) {
+        User user = getAccessUser();
+        return batchService.submitCheckImageTask(examId, batchId, user);
+    }
+
+    @ApiOperation(value = "答题卡扫描图片检查任务释放")
+    @PostMapping("release")
+    public Object release(@RequestParam Long examId) {
+        User user = getAccessUser();
+        batchService.releaseCheckImageTask(examId, user);
+        return success(true);
+    }
+
+}

+ 48 - 41
src/main/java/cn/com/qmth/scancloud/controller/admin/ExamController.java

@@ -1,26 +1,7 @@
 package cn.com.qmth.scancloud.controller.admin;
 
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.annotation.Resource;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.task.AsyncTaskExecutor;
-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.RestController;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-import com.qmth.boot.core.collection.PageResult;
-import com.qmth.boot.core.concurrent.service.ConcurrentService;
-import com.qmth.boot.core.exception.ReentrantException;
-import com.qmth.boot.mybatis.query.BaseQuery;
-
 import cn.com.qmth.scancloud.bean.ExamConfigDomain;
+import cn.com.qmth.scancloud.bean.SubjectConfigDomain;
 import cn.com.qmth.scancloud.bean.User;
 import cn.com.qmth.scancloud.controller.BaseController;
 import cn.com.qmth.scancloud.entity.ExamEntity;
@@ -28,16 +9,32 @@ import cn.com.qmth.scancloud.enums.LockType;
 import cn.com.qmth.scancloud.exception.ParameterExceptions;
 import cn.com.qmth.scancloud.service.ExamService;
 import cn.com.qmth.scancloud.service.StudentService;
-import cn.com.qmth.scancloud.task.ExamStudentSyncThread;
-import cn.com.qmth.scancloud.vo.ExamConfigVo;
-import cn.com.qmth.scancloud.vo.ExamRemoteVo;
-import cn.com.qmth.scancloud.vo.ExamUploadVo;
-import cn.com.qmth.scancloud.vo.ExamVo;
+import cn.com.qmth.scancloud.service.SubjectService;
+import cn.com.qmth.scancloud.task.ExamDataSyncThread;
+import cn.com.qmth.scancloud.vo.*;
 import cn.com.qmth.scancloud.vo.examinfo.ExamInfoVo;
 import cn.com.qmth.scancloud.vo.examinfo.ExamQuery;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.core.collection.PageResult;
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
+import com.qmth.boot.core.exception.ReentrantException;
+import com.qmth.boot.mybatis.query.BaseQuery;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import net.sf.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.validation.annotation.Validated;
+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.RestController;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
 
 @RestController
 @Api(tags = "考试接口")
@@ -47,7 +44,7 @@ public class ExamController extends BaseController {
 
     @Autowired
     private ExamService examService;
-    
+
     @Autowired
     private StudentService studentService;
 
@@ -57,24 +54,27 @@ public class ExamController extends BaseController {
     @Autowired
     private AsyncTaskExecutor taskExecutor;
 
+    @Autowired
+    private SubjectService subjectService;
+
     @ApiOperation(value = "获取考试详情")
     @RequestMapping(value = "/info", method = RequestMethod.POST)
     public ExamInfoVo info(@RequestParam Long id) {
-    	User user = getAccessUser();
-        return examService.getExamInfoVo(id,user);
+        User user = getAccessUser();
+        return examService.getExamInfoVo(id, user);
     }
 
     @ApiOperation(value = "获取考试列表")
     @RequestMapping(value = "/list", method = RequestMethod.POST)
     public PageResult<ExamVo> list(ExamQuery query) {
-    	User user = getAccessUser();
-        return examService.pageQuery(query,user);
+        User user = getAccessUser();
+        return examService.pageQuery(query, user);
     }
 
     @ApiOperation(value = "启用/关闭考试")
     @RequestMapping(value = "/toggle", method = RequestMethod.POST)
     public JSONObject enable(@RequestParam Long id, @RequestParam Boolean enable) {
-    	User user = getAccessUser();
+        User user = getAccessUser();
         JSONObject result = new JSONObject();
         ExamEntity exam = examService.getById(id);
         if (exam == null) {
@@ -84,6 +84,8 @@ public class ExamController extends BaseController {
             throw ParameterExceptions.EXAM_NOT_FOUND;
         }
         exam.setEnable(enable);
+        exam.setUpdaterId(user.getId());
+        exam.setUpdateTime(System.currentTimeMillis());
         examService.saveOrUpdate(exam);
         result.accumulate("enable", enable);
         result.accumulate("updateTime", exam.getUpdateTime());
@@ -99,17 +101,15 @@ public class ExamController extends BaseController {
 
     @ApiOperation(value = "启用新考试")
     @RequestMapping(value = "/init", method = RequestMethod.POST)
-    public Map<String, Object> init(ExamConfigDomain domain) {
+    public ExamInitVo init(@Validated(ExamConfigDomain.ExamConfigInit.class) ExamConfigDomain domain) {
         User user = getAccessUser();
-        ExamConfigVo vo=examService.init(user, domain);
-        Map<String, Object> result = new HashMap<String, Object>();
-        result.put("updateTime", vo.getUpdateTime());
-        return result;
+        ExamInitVo vo = examService.init(user, domain);
+        return vo;
     }
 
     @ApiOperation(value = "修改考试参数")
     @RequestMapping(value = "/config", method = RequestMethod.POST)
-    public ExamConfigVo config(ExamConfigDomain domain) {
+    public ExamConfigVo config(@Validated ExamConfigDomain domain) {
         User user = getAccessUser();
         return examService.config(user, domain);
     }
@@ -117,7 +117,7 @@ public class ExamController extends BaseController {
     @ApiOperation(value = "修改考试上传状态")
     @RequestMapping(value = "/upload", method = RequestMethod.POST)
     public ExamUploadVo upload(@RequestParam Long id, @RequestParam Boolean enable) {
-    	User user = getAccessUser();
+        User user = getAccessUser();
         ExamUploadVo vo = new ExamUploadVo();
         ExamEntity exam = examService.getById(id);
         if (exam == null) {
@@ -146,14 +146,14 @@ public class ExamController extends BaseController {
             throw ParameterExceptions.EXAM_NOT_FOUND;
         }
         if (!concurrentService.isLocked(LockType.EXAM_SYNC + "-" + examId)) {
-            taskExecutor.submit(new ExamStudentSyncThread(getAccessUser(), examId, examService, concurrentService));
+            taskExecutor.submit(new ExamDataSyncThread(getAccessUser(), examId, examService, concurrentService));
         } else {
             throw new ReentrantException("正在同步,请稍后再试");
         }
         JSONObject result = new JSONObject();
         result.put("synching", concurrentService.isLocked(LockType.EXAM_SYNC + "-" + examId));
         result.put("progress", examService.syncProgress(user, examId));
-        result.put("updateTime", examService.getById(examId).getUpdateTime());
+        result.put("updateTime", examService.getById(examId).getDataSyncTime());
         return result;
     }
 
@@ -171,7 +171,14 @@ public class ExamController extends BaseController {
         Map<String, Object> result = new HashMap<String, Object>();
         result.put("synching", concurrentService.isLocked(LockType.EXAM_SYNC + "-" + examId));
         result.put("progress", examService.syncProgress(user, examId));
-        result.put("updateTime", examService.getById(examId).getUpdateTime());
+        result.put("updateTime", examService.getById(examId).getDataSyncTime());
         return result;
     }
+
+    @ApiOperation(value = "修改考试科目参数")
+    @RequestMapping(value = "/subject/config", method = RequestMethod.POST)
+    public SubjectConfigVo subjectConfig(@Validated SubjectConfigDomain domain) {
+        User user = getAccessUser();
+        return subjectService.config(user, domain);
+    }
 }

+ 6 - 3
src/main/java/cn/com/qmth/scancloud/controller/admin/ExamWorkController.java

@@ -3,6 +3,7 @@ package cn.com.qmth.scancloud.controller.admin;
 import java.util.List;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -15,6 +16,8 @@ import com.qmth.boot.api.constant.ApiConstant;
 import cn.com.qmth.scancloud.entity.SubjectEntity;
 import cn.com.qmth.scancloud.service.StudentService;
 import cn.com.qmth.scancloud.service.SubjectService;
+import cn.com.qmth.scancloud.vo.CampusVo;
+import cn.com.qmth.scancloud.vo.ExamSiteVo;
 import cn.com.qmth.scancloud.vo.student.StudentQuery;
 import cn.com.qmth.scancloud.vo.student.StudentVo;
 import io.swagger.annotations.Api;
@@ -34,7 +37,7 @@ public class ExamWorkController {
 
     @ApiOperation(value = "获取考生信息")
     @RequestMapping(value = "/student/find", method = RequestMethod.POST)
-    public StudentVo student(StudentQuery query) {
+    public StudentVo student(@Validated StudentQuery query) {
         return studentService.findOne(query);
     }
 
@@ -52,13 +55,13 @@ public class ExamWorkController {
 
     @ApiOperation(value = "获取所有学习中心")
     @RequestMapping(value = "/campus/list", method = RequestMethod.POST)
-    public List<String> campusList(@RequestParam Long examId) {
+    public List<CampusVo> campusList(@RequestParam Long examId) {
         return studentService.listCampusByExamId(examId);
     }
 
     @ApiOperation(value = "获取所有考点")
     @RequestMapping(value = "/exam_site/list", method = RequestMethod.POST)
-    public List<String> siteList(@RequestParam Long examId) {
+    public List<ExamSiteVo> siteList(@RequestParam Long examId) {
         return studentService.listSiteByExamId(examId);
     }
 }

+ 106 - 0
src/main/java/cn/com/qmth/scancloud/controller/admin/FileController.java

@@ -0,0 +1,106 @@
+package cn.com.qmth.scancloud.controller.admin;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+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.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.core.exception.ParameterException;
+import com.qmth.boot.core.exception.StatusException;
+import com.qmth.boot.core.retrofit.utils.SignatureInfo;
+import com.qmth.boot.core.retrofit.utils.UploadFile;
+import com.qmth.boot.tools.signature.SignatureType;
+
+import cn.com.qmth.scancloud.bean.User;
+import cn.com.qmth.scancloud.client.MarkingcloudApiClient;
+import cn.com.qmth.scancloud.controller.BaseController;
+import cn.com.qmth.scancloud.entity.SubjectEntity;
+import cn.com.qmth.scancloud.entity.SystemConfigEntity;
+import cn.com.qmth.scancloud.enums.FormatType;
+import cn.com.qmth.scancloud.enums.SystemMode;
+import cn.com.qmth.scancloud.model.FileUploadResponse;
+import cn.com.qmth.scancloud.service.SubjectService;
+import cn.com.qmth.scancloud.service.SystemConfigService;
+import cn.com.qmth.scancloud.vo.UpdateTimeVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@Api(tags = "试卷答案文件上传接口")
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/admin/file")
+@Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
+public class FileController extends BaseController {
+
+    private static final Logger log = LoggerFactory.getLogger(FileController.class);
+
+    @Autowired
+    private SubjectService subjectService;
+
+    @Autowired
+    private SystemConfigService systemConfigService;
+
+    @Autowired
+    private MarkingcloudApiClient markingcloudApiClient;
+
+    @ApiOperation(value = "答案上传接口")
+    @RequestMapping(value = "/answer/upload", method = RequestMethod.POST)
+    public UpdateTimeVo answerUpload(@RequestParam Long examId, @RequestParam String subjectCode,
+            @RequestParam String md5, @RequestParam MultipartFile file) {
+        SystemConfigEntity conf = systemConfigService.findConfig();
+        if (!SystemMode.MARKING_CLOUD.equals(conf.getSystemMode())) {
+            throw new ParameterException("K12模式不允许创建通卡");
+        }
+        SubjectEntity sub = subjectService.findByExamIdAndCode(examId, subjectCode);
+        if (sub == null) {
+            throw new ParameterException("未找到科目,请先同步或创建考生获取科目");
+        }
+        User user = getAccessUser();
+        SignatureInfo signatureInfo = new SignatureInfo(SignatureType.TOKEN, user.getAccount(),
+                user.getMarkingCloudToken());
+        try {
+            FileUploadResponse res = markingcloudApiClient.answerFileUpload(signatureInfo.toString(), examId,
+                    subjectCode, FormatType.PDF.toString(), md5, UploadFile.build("file", "", file.getBytes()));
+            if (res == null || !res.getSuccess()) {
+                throw new StatusException("答案上传出错,examId=" + examId + ", subjectCode=" + subjectCode);
+            }
+        } catch (Exception e) {
+            log.error("答案上传出错,examId=" + examId + ", subjectCode=" + subjectCode, e);
+        }
+        return result(System.currentTimeMillis());
+    }
+
+    @ApiOperation(value = "试卷上传接口")
+    @RequestMapping(value = "/paper/upload", method = RequestMethod.POST)
+    public UpdateTimeVo paperUpload(@RequestParam Long examId, @RequestParam String subjectCode,
+            @RequestParam String md5, @RequestParam MultipartFile file) {
+        SystemConfigEntity conf = systemConfigService.findConfig();
+        if (!SystemMode.MARKING_CLOUD.equals(conf.getSystemMode())) {
+            throw new ParameterException("K12模式不允许创建通卡");
+        }
+        SubjectEntity sub = subjectService.findByExamIdAndCode(examId, subjectCode);
+        if (sub == null) {
+            throw new ParameterException("未找到科目,请先同步或创建考生获取科目");
+        }
+        User user = getAccessUser();
+        SignatureInfo signatureInfo = new SignatureInfo(SignatureType.TOKEN, user.getAccount(),
+                user.getMarkingCloudToken());
+        try {
+            FileUploadResponse res = markingcloudApiClient.paperFileUpload(signatureInfo.toString(), examId,
+                    subjectCode, FormatType.PDF.toString(), md5, UploadFile.build("file", "", file.getBytes()));
+            if (res == null || !res.getSuccess()) {
+                throw new StatusException("试卷上传出错,examId=" + examId + ", subjectCode=" + subjectCode);
+            }
+        } catch (Exception e) {
+            log.error("试卷上传出错,examId=" + examId + ", subjectCode=" + subjectCode, e);
+        }
+        return result(System.currentTimeMillis());
+    }
+
+}

+ 139 - 81
src/main/java/cn/com/qmth/scancloud/controller/admin/ScanAnswerController.java

@@ -2,11 +2,13 @@ package cn.com.qmth.scancloud.controller.admin;
 
 import java.io.IOException;
 import java.net.URLEncoder;
+import java.util.Collection;
 import java.util.List;
 
 import javax.servlet.http.HttpServletResponse;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -21,6 +23,7 @@ import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.boot.core.collection.PageResult;
 import com.qmth.boot.tools.excel.ExcelWriter;
 import com.qmth.boot.tools.excel.enums.ExcelType;
+import com.qmth.boot.tools.iterator.PageListIterator;
 
 import cn.com.qmth.scancloud.bean.AnswerDeleteDomain;
 import cn.com.qmth.scancloud.bean.AnswerQueryDomain;
@@ -30,16 +33,21 @@ import cn.com.qmth.scancloud.bean.MismatchToggleDomain;
 import cn.com.qmth.scancloud.bean.PageDeleteDomain;
 import cn.com.qmth.scancloud.bean.omredit.OmrEditDomain;
 import cn.com.qmth.scancloud.bean.papermigrate.PaperMigrateDomain;
+import cn.com.qmth.scancloud.bean.refix.AnswerRefixDomain;
 import cn.com.qmth.scancloud.controller.BaseController;
+import cn.com.qmth.scancloud.entity.ExamEntity;
+import cn.com.qmth.scancloud.enums.ExamMode;
 import cn.com.qmth.scancloud.service.BatchService;
-import cn.com.qmth.scancloud.service.PaperPageService;
+import cn.com.qmth.scancloud.service.ExamService;
 import cn.com.qmth.scancloud.service.PaperService;
 import cn.com.qmth.scancloud.service.StudentService;
 import cn.com.qmth.scancloud.vo.AnswerDeleteVo;
+import cn.com.qmth.scancloud.vo.AnswerExportK12Vo;
 import cn.com.qmth.scancloud.vo.AnswerExportVo;
+import cn.com.qmth.scancloud.vo.AnswerRefixVo;
 import cn.com.qmth.scancloud.vo.BatchQueryVo;
 import cn.com.qmth.scancloud.vo.MismatchToggleVo;
-import cn.com.qmth.scancloud.vo.PageDeleteVo;
+import cn.com.qmth.scancloud.vo.PaperDeleteVo;
 import cn.com.qmth.scancloud.vo.PaperMigrateVo;
 import cn.com.qmth.scancloud.vo.ScanAnswerInfoVo;
 import cn.com.qmth.scancloud.vo.UpdateTimeVo;
@@ -56,108 +64,158 @@ import io.swagger.annotations.ApiOperation;
 @Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
 public class ScanAnswerController extends BaseController {
 
+    @Autowired
+    private BatchService batchService;
 
-	@Autowired
-	private BatchService batchService;
-	
-	@Autowired
-	private PaperService paperService;
-	
-	@Autowired
-	private PaperPageService paperPageService;
-	
-	@Autowired
-	private StudentService studentService;
+    @Autowired
+    private PaperService paperService;
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private StudentService studentService;
 
     @ApiOperation(value = "查询答题卡扫描批次所有关联的扫描员")
     @RequestMapping(value = "batch/scanner", method = RequestMethod.POST)
     public List<String> batchScanner(@RequestParam Long examId) {
         return batchService.batchScanner(examId);
     }
-	
+
     @ApiOperation(value = "查询答题卡扫描批次信息")
     @RequestMapping(value = "batch/query", method = RequestMethod.POST)
-    public PageResult<BatchQueryVo> batchQuery(BatchQueryDomain query) {
+    public PageResult<BatchQueryVo> batchQuery(@Validated BatchQueryDomain query) {
         return batchService.batchQuery(query);
     }
-    
+
+    @ApiOperation(value = "查询答题卡扫描批次概要")
+    @RequestMapping(value = "batch/summary", method = RequestMethod.POST)
+    public List<Long> batchSummary(@Validated BatchQueryDomain query) {
+        return batchService.batchSummary(query);
+    }
+
     @ApiOperation(value = "查询答题卡扫描批次详情")
     @RequestMapping(value = "batch/detail", method = RequestMethod.POST)
-    public List<BatchDetailVo> batchDetail(@RequestParam Long id) {
+    public BatchDetailVo batchDetail(@RequestParam Long id) {
         return batchService.batchDetail(id);
     }
-    
+
     @ApiOperation(value = "查询异常答题卡信息")
     @RequestMapping(value = "mismatch/query", method = RequestMethod.POST)
-    public PageResult<MismatchQueryVo> mismatchQuery(MismatchQueryDomain query) {
-        return batchService.mismatchQuery(query);
-    }
-    
-	@ApiOperation(value = "修改异常答题卡标记")
-	@RequestMapping(value = "mismatch/toggle", method = RequestMethod.POST)
-	public MismatchToggleVo mismatchToggle(MismatchToggleDomain domain) {
-		return paperService.mismatchToggle(domain);
-	}
-	
-	@ApiOperation(value = "答题卡扫描汇总")
-	@PostMapping("info")
-	public ScanAnswerInfoVo scanAnswerInfo(@RequestParam Long examId) {
-		return studentService.scanAnswerInfo(examId);
-	}
-	
+    public PageResult<MismatchQueryVo> mismatchQuery(@Validated MismatchQueryDomain query) {
+        return paperService.mismatchQuery(query);
+    }
+
+    @ApiOperation(value = "修改异常答题卡标记")
+    @RequestMapping(value = "mismatch/toggle", method = RequestMethod.POST)
+    public MismatchToggleVo mismatchToggle(@Validated MismatchToggleDomain domain) {
+        return paperService.mismatchToggle(domain);
+    }
+
+    @ApiOperation(value = "答题卡扫描汇总")
+    @PostMapping("info")
+    public ScanAnswerInfoVo scanAnswerInfo(@RequestParam Long examId) {
+        return studentService.scanAnswerInfo(examId);
+    }
+
     @ApiOperation(value = "查询答题卡扫描详情")
     @RequestMapping(value = "query", method = RequestMethod.POST)
-    public PageResult<AnswerQueryVo> query(AnswerQueryDomain query) {
+    public PageResult<AnswerQueryVo> query(@Validated AnswerQueryDomain query) {
         return studentService.query(query);
     }
-    
+
+    @ApiOperation(value = "查询答题卡扫描概要")
+    @RequestMapping(value = "summary", method = RequestMethod.POST)
+    public List<String> summary(@Validated AnswerQueryDomain query) {
+        return studentService.summary(query);
+    }
+
     @ApiOperation(value = "导出答题卡扫描详情")
     @RequestMapping(value = "export", method = RequestMethod.POST)
-    public void export(AnswerQueryDomain query, HttpServletResponse response) throws IOException {
-    	String fileName = URLEncoder.encode("答题卡扫描详情", "UTF-8");
+    public void export(@Validated AnswerQueryDomain query, HttpServletResponse response) throws IOException {
+        String fileName = URLEncoder.encode("答题卡扫描详情", "UTF-8");
         response.setHeader("Content-Disposition", "inline; filename=" + fileName + ".xlsx");
         response.setContentType("application/vnd.ms-excel");
-        List<AnswerExportVo> list = studentService.exportList(query);
-		ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX);
-		writer.writeObjects("sheet1", null, AnswerExportVo.class, list.iterator());
-		writer.output(response.getOutputStream());
-    }
-    
-	@ApiOperation(value = "修改答题卡识别结果")
-	@RequestMapping(value = "omr/edit", method = RequestMethod.POST)
-	public UpdateTimeVo omrEdit(@RequestBody OmrEditDomain domain) {
-		return paperPageService.omrEdit(domain);
-	}
-	
-	@ApiOperation(value = "更新答题卡裁切图")
-	@RequestMapping(value = "slice/update", method = RequestMethod.POST)
-	public UriVo sliceUpdate(@RequestParam Long paperId, @RequestParam Integer pageIndex,@RequestParam Integer index, @RequestParam MultipartFile file,
-			@RequestParam String md5) {
-		return paperService.adminSliceUpdate(paperId,pageIndex,index, file, md5);
-	}
-	
-	@ApiOperation(value = "上传答题卡裁切图")
-	@RequestMapping(value = "slice/upload", method = RequestMethod.POST)
-	public UriVo sliceUpload(@RequestParam Long paperId, @RequestParam Integer pageIndex,@RequestParam Integer index, @RequestParam MultipartFile file,
-			@RequestParam String md5) {
-		return paperService.adminSliceUpload(paperId,pageIndex,index, file, md5);
-	}
-	
-	@ApiOperation(value = "修改答题卡扫描图片绑定考生")
-	@RequestMapping(value = "paper/migrate", method = RequestMethod.POST)
-	public PaperMigrateVo paperMigrate(@RequestBody PaperMigrateDomain domain) {
-		return paperPageService.paperMigrate(getAccessUser(),domain);
-	}
-	
-	@ApiOperation(value = "删除答题卡单页扫描结果")
-	@RequestMapping(value = "page/delete", method = RequestMethod.POST)
-	public PageDeleteVo pageDelete(PageDeleteDomain domain) {
-		return paperService.pageDelete(getAccessUser(),domain);
-	}
-	
-	@ApiOperation(value = "删除答题卡扫描结果")
-	@RequestMapping(value = "delete", method = RequestMethod.POST)
-	public AnswerDeleteVo answerDelete(AnswerDeleteDomain domain) {
-		return paperService.answerDelete(getAccessUser(),domain);
-	}
+        ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX);
+        ExamEntity exam = examService.getById(query.getExamId());
+        if (ExamMode.K12.equals(exam.getMode())) {
+            PageListIterator<AnswerExportK12Vo> iterator = new PageListIterator<AnswerExportK12Vo>(100) {
+
+                @Override
+                public Collection<AnswerExportK12Vo> getPageList(int pageNumber, int pageSize) {
+                    query.setPageNumber(pageNumber);
+                    query.setPageSize(pageSize);
+                    return studentService.exportListK12(query);
+                }
+            };
+            writer.writeObjects("扫描详情", null, AnswerExportK12Vo.class, iterator);
+            writer.output(response.getOutputStream());
+        } else {
+            PageListIterator<AnswerExportVo> iterator = new PageListIterator<AnswerExportVo>(100) {
+
+                @Override
+                public Collection<AnswerExportVo> getPageList(int pageNumber, int pageSize) {
+                    query.setPageNumber(pageNumber);
+                    query.setPageSize(pageSize);
+                    return studentService.exportList(query);
+                }
+            };
+            writer.writeObjects("扫描详情", null, AnswerExportVo.class, iterator);
+            writer.output(response.getOutputStream());
+
+        }
+
+    }
+
+    @ApiOperation(value = "修改答题卡识别结果")
+    @RequestMapping(value = "omr/edit", method = RequestMethod.POST)
+    public UpdateTimeVo omrEdit(@Validated @RequestBody OmrEditDomain domain) {
+        return studentService.omrEdit(getAccessUser(), domain);
+    }
+
+    @ApiOperation(value = "更新答题卡裁切图")
+    @RequestMapping(value = "slice/update", method = RequestMethod.POST)
+    public UriVo sliceUpdate(@RequestParam Long paperId, @RequestParam Integer pageIndex, @RequestParam Integer index,
+            @RequestParam MultipartFile file, @RequestParam String md5) {
+        return paperService.adminSliceUpdate(paperId, pageIndex, index, file, md5);
+    }
+
+    @ApiOperation(value = "更新答题卡原图")
+    @RequestMapping(value = "sheet/update", method = RequestMethod.POST)
+    public UriVo sheetUpdate(@RequestParam Long paperId, @RequestParam Integer pageIndex,
+            @RequestParam MultipartFile file, @RequestParam String md5) {
+        return paperService.adminSheetUpdate(paperId, pageIndex,file, md5);
+    }
+
+    @ApiOperation(value = "上传答题卡裁切图")
+    @RequestMapping(value = "slice/upload", method = RequestMethod.POST)
+    public UriVo sliceUpload(@RequestParam Long paperId, @RequestParam Integer pageIndex, @RequestParam Integer index,
+            @RequestParam MultipartFile file, @RequestParam String md5) {
+        return paperService.adminSliceUpload(paperId, pageIndex, index, file, md5);
+    }
+
+    @ApiOperation(value = "修改答题卡扫描图片绑定考生")
+    @RequestMapping(value = "paper/migrate", method = RequestMethod.POST)
+    public PaperMigrateVo paperMigrate(@Validated @RequestBody PaperMigrateDomain domain) {
+        return paperService.paperMigrate(getAccessUser(), domain);
+    }
+
+    @ApiOperation(value = "删除答题卡单页扫描结果")
+    @RequestMapping(value = "paper/delete", method = RequestMethod.POST)
+    public PaperDeleteVo paperDelete(@Validated PageDeleteDomain domain) {
+        return studentService.paperDelete(getAccessUser(), domain);
+    }
+
+    @ApiOperation(value = "删除答题卡扫描结果")
+    @RequestMapping(value = "delete", method = RequestMethod.POST)
+    public AnswerDeleteVo answerDelete(@Validated AnswerDeleteDomain domain) {
+        return studentService.answerDelete(getAccessUser(), domain);
+    }
+
+    @ApiOperation(value = "答题卡二次识别")
+    @RequestMapping(value = "refix", method = RequestMethod.POST)
+    public AnswerRefixVo refix(@Validated @RequestBody AnswerRefixDomain query) {
+        return studentService.answerRefix(getAccessUser(), query);
+    }
+
 }

+ 40 - 44
src/main/java/cn/com/qmth/scancloud/controller/admin/ScannerController.java

@@ -1,21 +1,9 @@
 package cn.com.qmth.scancloud.controller.admin;
 
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-
 import cn.com.qmth.scancloud.bean.CardAnswerDomain;
 import cn.com.qmth.scancloud.bean.WorkloadDomain;
 import cn.com.qmth.scancloud.controller.BaseController;
-import cn.com.qmth.scancloud.service.PaperService;
+import cn.com.qmth.scancloud.service.BatchService;
 import cn.com.qmth.scancloud.service.ScannerCardService;
 import cn.com.qmth.scancloud.service.ScannerService;
 import cn.com.qmth.scancloud.service.SystemConfigService;
@@ -23,8 +11,18 @@ import cn.com.qmth.scancloud.vo.ScannerWorkloadVo;
 import cn.com.qmth.scancloud.vo.UpdateCountVo;
 import cn.com.qmth.scancloud.vo.scannerinfo.Control;
 import cn.com.qmth.scancloud.vo.scannerinfo.ScannerInfoVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
 
 @RestController
 @Api(tags = "扫描员管理接口")
@@ -32,41 +30,39 @@ import io.swagger.annotations.ApiOperation;
 @Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
 public class ScannerController extends BaseController {
 
-	@Autowired
-	private ScannerService scannerService;
-	
-	@Autowired
-	private ScannerCardService scannerCardService;
-	
-	@Autowired
-	private PaperService paperService; 
-	
-	@Autowired
-	private SystemConfigService systemConfigService;
+    @Autowired
+    private ScannerService scannerService;
+
+    @Autowired
+    private ScannerCardService scannerCardService;
 
-	@ApiOperation(value = "扫描员管理详情")
-	@PostMapping("info")
-	public ScannerInfoVo scannerInfo(@RequestParam Long examId) {
+    @Autowired
+    private BatchService batchService;
 
-		return scannerService.scannerInfo(examId);
-	}
+    @Autowired
+    private SystemConfigService systemConfigService;
 
-	@ApiOperation(value = "扫描员工作量统计")
-	@PostMapping("workload")
-	public List<ScannerWorkloadVo> workload(WorkloadDomain domain) {
+    @ApiOperation(value = "扫描员管理详情")
+    @PostMapping("info")
+    public ScannerInfoVo scannerInfo(@RequestParam Long examId) {
+        return scannerService.scannerInfo(examId);
+    }
 
-		return paperService.workload(domain);
-	}
-	@ApiOperation(value = "修改扫描员登陆控制")
-	@PostMapping("control")
-	public Control control(Control control) {
+    @ApiOperation(value = "扫描员工作量统计")
+    @PostMapping("workload")
+    public List<ScannerWorkloadVo> workload(WorkloadDomain domain) {
+        return batchService.workload(domain);
+    }
 
-		return systemConfigService.control(control);
-	}
-	@ApiOperation(value = "修改扫描员绑定卡格式")
-	@PostMapping("card/answer")
-	public UpdateCountVo cardAnswer(CardAnswerDomain domain) {
+    @ApiOperation(value = "修改扫描员登陆控制")
+    @PostMapping("control")
+    public Control control(Control control) {
+        return systemConfigService.control(control);
+    }
 
-		return scannerCardService.cardAnswer(domain);
-	}
+    @ApiOperation(value = "修改扫描员绑定卡格式")
+    @PostMapping("card/answer")
+    public UpdateCountVo cardAnswer(CardAnswerDomain domain) {
+        return scannerCardService.cardAnswer(domain);
+    }
 }

+ 33 - 1
src/main/java/cn/com/qmth/scancloud/controller/admin/ToolController.java

@@ -14,19 +14,24 @@ import org.springframework.web.bind.annotation.RestController;
 import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.boot.core.exception.ParameterException;
 
+import cn.com.qmth.scancloud.bean.ImportCetAbsentDomain;
 import cn.com.qmth.scancloud.bean.ImportExamDomain;
 import cn.com.qmth.scancloud.bean.ImportStudentDomain;
 import cn.com.qmth.scancloud.bean.ImportSubjectDomain;
 import cn.com.qmth.scancloud.bean.ImportUserDomain;
 import cn.com.qmth.scancloud.controller.BaseController;
 import cn.com.qmth.scancloud.entity.ExamEntity;
+import cn.com.qmth.scancloud.entity.QuestionEntity;
 import cn.com.qmth.scancloud.entity.SubjectEntity;
 import cn.com.qmth.scancloud.service.ExamService;
+import cn.com.qmth.scancloud.service.QuestionService;
 import cn.com.qmth.scancloud.service.StudentService;
 import cn.com.qmth.scancloud.service.SubjectService;
 import cn.com.qmth.scancloud.service.ToolExportService;
 import cn.com.qmth.scancloud.service.UserService;
 import cn.com.qmth.scancloud.vo.CountVo;
+import cn.com.qmth.scancloud.vo.ExportCetMarkingQueryVo;
+import cn.com.qmth.scancloud.vo.ExportCetVo;
 import cn.com.qmth.scancloud.vo.ImportExamVo;
 import cn.com.qmth.scancloud.vo.ImportStudentQueryVo;
 import cn.com.qmth.scancloud.vo.ImportStudentVo;
@@ -53,6 +58,9 @@ public class ToolController extends BaseController {
 
     @Autowired
     private ToolExportService toolExportService;
+    
+    @Autowired
+    private QuestionService questionService;
 
     @ApiOperation(value = "批量创建管理员用户接口")
     @PostMapping("/import/user")
@@ -121,5 +129,29 @@ public class ToolController extends BaseController {
     public void studentClean(@RequestParam Long examId) {
         studentService.studentClean(examId);
     }
-
+    
+    @ApiOperation(value = "cet导入缺考接口")
+    @PostMapping("/import/cet/absent")
+    public CountVo importCetAbsent(@RequestBody List<ImportCetAbsentDomain> students) {
+        return new CountVo(studentService.importCetAbsent(students));
+    }
+    
+    @ApiOperation(value = "cet导出扫描结果和评卷分配数据")
+    @PostMapping("/export/cet/data")
+    public List<ExportCetVo> exportCetData(@RequestBody ExportCetMarkingQueryVo query) {
+        return studentService.exportCetData(query);
+    }
+    
+    @ApiOperation(value = "查询试卷结构")
+    @PostMapping("/import/question/query")
+    public List<QuestionEntity> questionQuery(@RequestParam Long examId,@RequestParam String subjectCode) {
+        return questionService.findByExamIdAndSubjectCode(examId, subjectCode);
+    }
+    
+    @ApiOperation(value = "保存试卷结构")
+    @PostMapping("/import/question/save")
+    public CountVo importQuestion(@RequestBody List<QuestionEntity> questionList) {
+        return new CountVo(questionService.importQuestion(questionList));
+    }
+    
 }

+ 14 - 22
src/main/java/cn/com/qmth/scancloud/controller/admin/VerifyController.java

@@ -1,25 +1,20 @@
 package cn.com.qmth.scancloud.controller.admin;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-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.RestController;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-
 import cn.com.qmth.scancloud.bean.User;
 import cn.com.qmth.scancloud.controller.BaseController;
 import cn.com.qmth.scancloud.service.BatchService;
 import cn.com.qmth.scancloud.vo.UpdateTimeVo;
 import cn.com.qmth.scancloud.vo.verify.VerifyTaskVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import net.sf.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+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.RestController;
 
 @RestController
 @Api(tags = "答题卡扫描实时审核接口")
@@ -27,8 +22,6 @@ import net.sf.json.JSONObject;
 @Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
 public class VerifyController extends BaseController {
 
-    protected static final Logger log = LoggerFactory.getLogger(VerifyController.class);
-
     @Autowired
     private BatchService batchService;
 
@@ -43,16 +36,15 @@ public class VerifyController extends BaseController {
     @RequestMapping(value = "/answer/submit", method = RequestMethod.POST)
     public UpdateTimeVo answerSubmit(@RequestParam Long batchId, @RequestParam Boolean confirm) {
         User user = getAccessUser();
-        batchService.verify(batchId, confirm,user);
+        batchService.verify(batchId, confirm, user);
         return result(System.currentTimeMillis());
     }
+
     @ApiOperation(value = "答题卡扫描实时审核任务释放")
     @RequestMapping(value = "/answer/release", method = RequestMethod.POST)
-    public JSONObject answerRelease(@RequestParam Long examId) {
-    	User user = getAccessUser();
-    	batchService.releaseByUser(examId, user.getAccount());
-        JSONObject result = new JSONObject();
-        result.accumulate("success", true);
-        return result;
+    public Object answerRelease(@RequestParam Long examId) {
+        User user = getAccessUser();
+        batchService.releaseCheckImageTask(examId, user);
+        return success(true);
     }
 }

+ 79 - 75
src/main/java/cn/com/qmth/scancloud/controller/scan/AnswerController.java

@@ -1,37 +1,30 @@
 package cn.com.qmth.scancloud.controller.scan;
 
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-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.RestController;
-import org.springframework.web.multipart.MultipartFile;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-
 import cn.com.qmth.scancloud.bean.BatchCreateDomain;
 import cn.com.qmth.scancloud.bean.MismatchToggleDomain;
 import cn.com.qmth.scancloud.bean.User;
 import cn.com.qmth.scancloud.bean.answersave.AnswerDomain;
 import cn.com.qmth.scancloud.controller.BaseController;
+import cn.com.qmth.scancloud.service.AdapteFileService;
 import cn.com.qmth.scancloud.service.AnswerCardService;
 import cn.com.qmth.scancloud.service.BatchService;
 import cn.com.qmth.scancloud.service.PaperService;
-import cn.com.qmth.scancloud.vo.AnswerCardVo;
-import cn.com.qmth.scancloud.vo.BatchFinishVo;
-import cn.com.qmth.scancloud.vo.BatchVerifyVo;
-import cn.com.qmth.scancloud.vo.MismatchToggleVo;
-import cn.com.qmth.scancloud.vo.SheetUploadVo;
-import cn.com.qmth.scancloud.vo.SliceUploadVo;
+import cn.com.qmth.scancloud.vo.*;
 import cn.com.qmth.scancloud.vo.batch.AnswerSaveVo;
 import cn.com.qmth.scancloud.vo.batch.BatchCreateVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.api.utils.RequestUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
 
 @RestController
 @Api(tags = "扫描功能-答题卡接口")
@@ -39,65 +32,76 @@ import io.swagger.annotations.ApiOperation;
 @Aac(strict = BOOL.FALSE, auth = BOOL.TRUE)
 public class AnswerController extends BaseController {
 
-	@Autowired
-	private AnswerCardService answerCardService;
+    @Autowired
+    private AnswerCardService answerCardService;
+
+    @Autowired
+    private BatchService batchService;
 
-	@Autowired
-	private BatchService batchService;
+    @Autowired
+    private PaperService paperService;
+
+    @Autowired
+    private AdapteFileService adapteFileService;
+
+    @ApiOperation(value = "答题卡卡格式列表")
+    @RequestMapping(value = "/card/list", method = RequestMethod.POST)
+    public List<AnswerCardVo> cardList(HttpServletRequest request, @RequestParam Long examId) {
+        return answerCardService.listByExamIdForScanner(examId, getAccessUser(), RequestUtil.getIpAddress(request));
+    }
+
+    @ApiOperation(value = "答题卡适配卡格式上传")
+    @RequestMapping(value = "/card/adapte/upload", method = RequestMethod.POST)
+    public UriVo adapteUpload(HttpServletRequest request, @RequestParam Long examId, @RequestParam Integer cardNumber,
+            @RequestParam MultipartFile file, @RequestParam String md5) {
+        return adapteFileService
+                .save(RequestUtil.getIpAddress(request), getAccessUser().getRole(), examId, cardNumber, md5, file);
+    }
 
-	@Autowired
-	private PaperService paperService;
-	
     @ApiOperation(value = "答题卡扫描批次创建")
     @RequestMapping(value = "/batch/create", method = RequestMethod.POST)
-    public BatchCreateVo batchCreate(@RequestBody BatchCreateDomain domain) {
-        return batchService.batchCreate(domain,getAccessUser());
+    public BatchCreateVo batchCreate(@Validated @RequestBody BatchCreateDomain domain) {
+        return batchService.batchCreate(domain, getAccessUser());
+    }
+
+    @ApiOperation(value = "答题卡扫描结果保存")
+    @RequestMapping(value = "/batch/save", method = RequestMethod.POST)
+    public AnswerSaveVo batchSave(@Validated @RequestBody AnswerDomain domain) {
+        User user = getAccessUser();
+        return batchService.batchSave(domain, user);
+    }
+
+    @ApiOperation(value = "答题卡扫描历史标记/取消异常")
+    @RequestMapping(value = "/mismatch/toggle", method = RequestMethod.POST)
+    public MismatchToggleVo mismatchToggle(@Validated MismatchToggleDomain domain) {
+        return paperService.mismatchToggle(domain);
+    }
+
+    @ApiOperation(value = "答题卡扫描原图上传")
+    @RequestMapping(value = "/sheet/upload", method = RequestMethod.POST)
+    public SheetUploadVo sheetUpload(@RequestParam Long batchId, @RequestParam String examNumber,
+            @RequestParam Integer paperNumber, @RequestParam Integer pageIndex, @RequestParam MultipartFile file,
+            @RequestParam String md5) {
+        return batchService.sheetUpload(batchId, examNumber, paperNumber, pageIndex, file, md5);
+    }
+
+    @ApiOperation(value = "答题卡扫描裁切图上传")
+    @RequestMapping(value = "/slice/upload", method = RequestMethod.POST)
+    public SliceUploadVo sliceUpload(@RequestParam Long batchId, @RequestParam String examNumber,
+            @RequestParam Integer paperNumber, @RequestParam Integer pageIndex, @RequestParam Integer index,
+            @RequestParam MultipartFile file, @RequestParam String md5) {
+        return batchService.sliceUpload(batchId, examNumber, paperNumber, pageIndex, index, file, md5);
+    }
+
+    @ApiOperation(value = "答题卡扫描批次提交审核/查询审核结果")
+    @RequestMapping(value = "/batch/verify", method = RequestMethod.POST)
+    public BatchVerifyVo batchVerify(@RequestParam Long id) {
+        return batchService.batchVerify(id);
     }
 
-	@ApiOperation(value = "答题卡卡格式列表")
-	@RequestMapping(value = "/card/list", method = RequestMethod.POST)
-	public List<AnswerCardVo> cardList(@RequestParam Long examId) {
-		return answerCardService.listByExamIdForScanner(examId, getAccessUser());
-	}
-
-	@ApiOperation(value = "答题卡扫描结果保存")
-	@RequestMapping(value = "/batch/save", method = RequestMethod.POST)
-	public AnswerSaveVo batchSave(@RequestBody AnswerDomain domain) {
-	    User user = getAccessUser();
-		return batchService.batchSave(domain,user);
-	}
-
-	@ApiOperation(value = "答题卡扫描历史标记/取消异常")
-	@RequestMapping(value = "/mismatch/toggle", method = RequestMethod.POST)
-	public MismatchToggleVo mismatchToggle(MismatchToggleDomain domain) {
-		return paperService.mismatchToggle(domain);
-	}
-
-	@ApiOperation(value = "答题卡扫描原图上传")
-	@RequestMapping(value = "/sheet/upload", method = RequestMethod.POST)
-	public SheetUploadVo sheetUpload(@RequestParam Long batchId, @RequestParam String examNumber,
-			@RequestParam Integer paperNumber, @RequestParam Integer pageIndex, @RequestParam MultipartFile file,
-			@RequestParam String md5) {
-		return paperService.sheetUpload(batchId, examNumber, paperNumber, pageIndex, file, md5);
-	}
-	
-	@ApiOperation(value = "答题卡扫描裁切图上传")
-	@RequestMapping(value = "/slice/upload", method = RequestMethod.POST)
-	public SliceUploadVo sliceUpload(@RequestParam Long batchId, @RequestParam String examNumber,
-			@RequestParam Integer paperNumber, @RequestParam Integer pageIndex,@RequestParam Integer index, @RequestParam MultipartFile file,
-			@RequestParam String md5) {
-		return paperService.sliceUpload(batchId, examNumber, paperNumber, pageIndex,index, file, md5);
-	}
-	
-	@ApiOperation(value = "答题卡扫描批次提交审核/查询审核结果")
-	@RequestMapping(value = "/batch/verify", method = RequestMethod.POST)
-	public BatchVerifyVo batchVerify(@RequestParam Long id) {
-		return batchService.batchVerify(id);
-	}
-	
-	@ApiOperation(value = "答题卡扫描批次完成")
-	@RequestMapping(value = "/batch/finish", method = RequestMethod.POST)
-	public BatchFinishVo batchFinish(@RequestParam Long id) {
-		return batchService.batchFinish(id);
-	}
+    @ApiOperation(value = "答题卡扫描批次完成")
+    @RequestMapping(value = "/batch/finish", method = RequestMethod.POST)
+    public BatchFinishVo batchFinish(@RequestParam Long id) {
+        return batchService.batchFinish(id);
+    }
 }

+ 16 - 27
src/main/java/cn/com/qmth/scancloud/controller/scan/OmrTaskController.java

@@ -1,18 +1,5 @@
 package cn.com.qmth.scancloud.controller.scan;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-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.RestController;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-
 import cn.com.qmth.scancloud.bean.User;
 import cn.com.qmth.scancloud.controller.BaseController;
 import cn.com.qmth.scancloud.service.OmrTaskService;
@@ -20,9 +7,16 @@ import cn.com.qmth.scancloud.vo.task.TaskResultVo;
 import cn.com.qmth.scancloud.vo.task.TaskSaveVo;
 import cn.com.qmth.scancloud.vo.task.TaskStatusVo;
 import cn.com.qmth.scancloud.vo.task.TaskVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import net.sf.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
 
 @RestController
 @Api(tags = "识别对照任务接口")
@@ -32,40 +26,35 @@ public class OmrTaskController extends BaseController {
 
     protected static final Logger log = LoggerFactory.getLogger(OmrTaskController.class);
 
-    @Autowired
-    private OmrTaskService taskService;
     @Autowired
     private OmrTaskService omrTaskService;
-    
+
     @ApiOperation(value = "识别对照任务获取")
     @RequestMapping(value = "/get", method = RequestMethod.POST)
     public TaskVo get(@RequestParam Long examId) {
         User user = getAccessUser();
-        TaskVo task = taskService.getTask(examId, user.getAccount());
-        return task;
+        return omrTaskService.getTask(examId, user.getAccount());
     }
 
     @ApiOperation(value = "识别对照任务提交")
     @RequestMapping(value = "/save", method = RequestMethod.POST)
-    public TaskSaveVo save(@RequestBody TaskResultVo result) {
+    public TaskSaveVo save(@Validated @RequestBody TaskResultVo result) {
         User user = getAccessUser();
-        return taskService.submitTask(result, user.getAccount());
+        return omrTaskService.submitTask(result, user);
     }
 
     @ApiOperation(value = "识别对照任务状态")
     @RequestMapping(value = "/status", method = RequestMethod.POST)
     public TaskStatusVo status(@RequestParam Long examId) {
         User user = getAccessUser();
-        return taskService.getStatus(examId, false,user.getAccount());
+        return omrTaskService.getStatus(examId, false, user.getAccount());
     }
-    
+
     @ApiOperation(value = "识别对照任务状态")
     @RequestMapping(value = "/release", method = RequestMethod.POST)
-    public JSONObject release(@RequestParam Long examId) {
+    public Object release(@RequestParam Long examId) {
         User user = getAccessUser();
         omrTaskService.releaseByUser(examId, user.getAccount());
-        JSONObject result = new JSONObject();
-        result.accumulate("success", true);
-        return result;
+        return success(true);
     }
 }

+ 9 - 12
src/main/java/cn/com/qmth/scancloud/controller/scan/ScannerAuthController.java

@@ -8,11 +8,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.api.utils.RequestUtil;
 
 import cn.com.qmth.scancloud.bean.ScannerLoginInfo;
 import cn.com.qmth.scancloud.controller.BaseController;
 import cn.com.qmth.scancloud.service.AuthService;
-import cn.com.qmth.scancloud.util.IpUtil;
 import cn.com.qmth.scancloud.vo.ScannerLoginVo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -22,17 +22,14 @@ import io.swagger.annotations.ApiOperation;
 @RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/scan")
 public class ScannerAuthController extends BaseController {
 
-	@Autowired
-	private AuthService authService;
-
-	@ApiOperation(value = "扫描员登录")
-	@PostMapping("login")
-	public ScannerLoginVo login(ScannerLoginInfo loginInfo, HttpServletRequest request) {
-
-		String ip = IpUtil.getRemoteIp(request);
-
-		return ScannerLoginVo.of(authService.login(ip,loginInfo));
-	}
+    @Autowired
+    private AuthService authService;
 
+    @ApiOperation(value = "扫描员登录")
+    @PostMapping("login")
+    public ScannerLoginVo login(ScannerLoginInfo loginInfo, HttpServletRequest request) {
+        String ip = RequestUtil.getIpAddress(request);
+        return ScannerLoginVo.of(authService.login(ip, loginInfo));
+    }
 
 }

+ 13 - 15
src/main/java/cn/com/qmth/scancloud/controller/scan/StudentController.java

@@ -1,22 +1,21 @@
 package cn.com.qmth.scancloud.controller.scan;
 
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.qmth.boot.api.annotation.Aac;
-import com.qmth.boot.api.annotation.BOOL;
-import com.qmth.boot.api.constant.ApiConstant;
-
 import cn.com.qmth.scancloud.controller.BaseController;
 import cn.com.qmth.scancloud.service.StudentService;
 import cn.com.qmth.scancloud.vo.student.StudentQuery;
 import cn.com.qmth.scancloud.vo.student.StudentVo;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
 
 @RestController
 @Api(tags = "扫描功能-考生接口")
@@ -27,17 +26,16 @@ public class StudentController extends BaseController {
     @Autowired
     private StudentService studentService;
 
-    
     @ApiOperation(value = "整袋考生查询")
     @RequestMapping(value = "/package/list", method = RequestMethod.POST)
     public List<StudentVo> packageList(StudentQuery query) {
         return studentService.packageList(query);
     }
-    
+
     @ApiOperation(value = "考生查询")
     @RequestMapping(value = "/find", method = RequestMethod.POST)
-    public StudentVo student(StudentQuery query) {
+    public StudentVo student(@Validated StudentQuery query) {
         return studentService.findOne(query);
     }
-    
+
 }

+ 9 - 0
src/main/java/cn/com/qmth/scancloud/dao/AdapteFileDao.java

@@ -0,0 +1,9 @@
+package cn.com.qmth.scancloud.dao;
+
+import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
+
+import cn.com.qmth.scancloud.entity.AdapteFileEntity;
+
+public interface AdapteFileDao extends MppBaseMapper<AdapteFileEntity> {
+
+}

+ 25 - 16
src/main/java/cn/com/qmth/scancloud/dao/BatchDao.java

@@ -1,20 +1,19 @@
 package cn.com.qmth.scancloud.dao;
 
-import java.util.List;
-
-import org.apache.ibatis.annotations.Param;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-
 import cn.com.qmth.scancloud.bean.BatchQueryDomain;
-import cn.com.qmth.scancloud.bean.MismatchQueryDomain;
 import cn.com.qmth.scancloud.entity.BatchEntity;
+import cn.com.qmth.scancloud.enums.BatchStatus;
+import cn.com.qmth.scancloud.enums.CheckStatus;
 import cn.com.qmth.scancloud.enums.VerifyStatus;
 import cn.com.qmth.scancloud.vo.BatchQueryVo;
-import cn.com.qmth.scancloud.vo.batchdetail.BatchDetailVo;
-import cn.com.qmth.scancloud.vo.mismatchquery.MismatchQueryVo;
+import cn.com.qmth.scancloud.vo.ScannerWorkloadVo;
+import cn.com.qmth.scancloud.vo.batchdetail.BatchDetailDO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 public interface BatchDao extends BaseMapper<BatchEntity> {
 
@@ -22,16 +21,26 @@ public interface BatchDao extends BaseMapper<BatchEntity> {
 
     void updateAssignedCount(@Param("id") Long id);
 
+    List<Long> batchSummary(@Param("query") BatchQueryDomain query);
+
     IPage<BatchQueryVo> batchQueryPage(Page<BatchQueryVo> page, @Param("query") BatchQueryDomain query);
 
-    List<BatchDetailVo> batchDetailList(@Param("id") Long id);
+    List<BatchDetailDO> batchDetailList(@Param("id") Long id);
+
+    IPage<BatchEntity> findUnVerify(Page<BatchEntity> page, @Param("examId") Long examId,
+            @Param("status") VerifyStatus status);
+
+    Integer findStudentCountByBatch(@Param(value = "batchId") Long batchId);
+
+    List<String> batchScanner(@Param("examId") Long examId);
 
-    IPage<MismatchQueryVo> mismatchQueryPage(Page<MismatchQueryVo> page, @Param("query") MismatchQueryDomain query);
+    List<ScannerWorkloadVo> workload(@Param(value = "examId") Long examId, @Param(value = "startTime") Long startTime,
+            @Param(value = "endTime") Long endTime);
 
-    IPage<BatchEntity> findUnVerify(Page<BatchEntity> page,@Param("examId") Long examId,@Param("status") VerifyStatus status);
+    IPage<BatchEntity> findUnCheck(Page<BatchEntity> page,@Param("examId") Long examId,@Param("batchStatus")BatchStatus batchStatus,@Param("checkStatus") CheckStatus checkStatus);
 
-	List<BatchQueryVo> findAssignedCount(@Param("batchIds")List<Long> batchIds);
+    List<BatchDetailDO> batchDetailListToCheck(@Param("id")Long id,@Param("needCheck")Boolean needCheck);
 
-	List<String> batchScanner(@Param("examId")Long examId);
+    BatchEntity getHistory(@Param(value = "examId")Long examId,@Param("batchId") Long batchId,@Param("userId") Long userId,@Param("status") CheckStatus status);
 
 }

+ 15 - 0
src/main/java/cn/com/qmth/scancloud/dao/BatchPaperDao.java

@@ -0,0 +1,15 @@
+package cn.com.qmth.scancloud.dao;
+
+import cn.com.qmth.scancloud.entity.BatchPaperEntity;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
+
+public interface BatchPaperDao extends MppBaseMapper<BatchPaperEntity> {
+
+    List<Long> findStudentIdByBatchId(@Param("batchId")Long batchId);
+
+}

+ 16 - 16
src/main/java/cn/com/qmth/scancloud/dao/PaperDao.java

@@ -1,30 +1,30 @@
 package cn.com.qmth.scancloud.dao;
 
-import java.util.List;
-
-import org.apache.ibatis.annotations.Param;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
+import cn.com.qmth.scancloud.bean.MismatchQueryDomain;
 import cn.com.qmth.scancloud.entity.PaperEntity;
-import cn.com.qmth.scancloud.vo.ScannerWorkloadVo;
 import cn.com.qmth.scancloud.vo.answerquery.StudentPaperVo;
 import cn.com.qmth.scancloud.vo.mismatchquery.MismatchQueryVo;
+import cn.com.qmth.scancloud.vo.mismatchquery.PaperPageDO;
+import cn.com.qmth.scancloud.vo.paper.PaperCetVo;
 import cn.com.qmth.scancloud.vo.paper.PaperVo;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 public interface PaperDao extends BaseMapper<PaperEntity> {
-	
-	public List<PaperEntity> findStudentPaperEffective(@Param(value = "studentId") Long studentId);
-	public List<PaperVo> findStudentPaper(@Param(value = "studentId") Long studentId);
 
-	public List<ScannerWorkloadVo> workload(@Param(value = "examId") Long examId,
-			@Param(value = "startTime") Long startTime, @Param(value = "endTime") Long endTime);
+    List<StudentPaperVo> listByStudentIds(@Param(value = "paramList") List<Long> paramList);
+
+    List<PaperVo> findStudentPaper(@Param("studentId") Long studentId);
 
-	public Integer findStudentCountByBatch(@Param(value = "batchId")Long batchId);
+    List<PaperPageDO> findMismatchByBatchIdAndStudentId(@Param("batchId") Long batchId,
+            @Param("studentId") Long studentId);
 
-	public List<PaperEntity> listByMismatchQuery(@Param(value = "paramList")List<MismatchQueryVo> paramList);
+    IPage<MismatchQueryVo> mismatchQueryPage(Page<MismatchQueryVo> page, @Param("query") MismatchQueryDomain query);
 
-	public List<StudentPaperVo> listByStudentIds(@Param(value = "paramList")List<Long> paramList);
+    List<PaperCetVo> findByStudentIds(@Param("studentIds")List<Long> studentIds);
 
-	public List<PaperEntity> findAssigneByBatch(@Param(value = "batchId")Long batchId);
 }

+ 8 - 0
src/main/java/cn/com/qmth/scancloud/dao/PaperPageDao.java

@@ -1,9 +1,17 @@
 package cn.com.qmth.scancloud.dao;
 
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
 import com.github.jeffreyning.mybatisplus.base.MppBaseMapper;
 
 import cn.com.qmth.scancloud.entity.PaperPageEntity;
+import cn.com.qmth.scancloud.vo.paper.PaperPageCetVo;
 
 public interface PaperPageDao extends MppBaseMapper<PaperPageEntity> {
 
+
+    List<PaperPageCetVo> listByStudentIds(@Param("studentIds")List<Long> studentIds);
+
 }

+ 8 - 0
src/main/java/cn/com/qmth/scancloud/dao/QuestionDao.java

@@ -0,0 +1,8 @@
+package cn.com.qmth.scancloud.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import cn.com.qmth.scancloud.entity.QuestionEntity;
+
+public interface QuestionDao extends BaseMapper<QuestionEntity> {
+}

+ 31 - 21
src/main/java/cn/com/qmth/scancloud/dao/StudentDao.java

@@ -1,40 +1,44 @@
 package cn.com.qmth.scancloud.dao;
 
-import java.util.List;
-
-import org.apache.ibatis.annotations.Param;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-
 import cn.com.qmth.scancloud.bean.AbsentQueryDomain;
 import cn.com.qmth.scancloud.bean.AnswerQueryDomain;
 import cn.com.qmth.scancloud.entity.StudentEntity;
-import cn.com.qmth.scancloud.vo.AbsentQueryVo;
-import cn.com.qmth.scancloud.vo.AnswerExportVo;
-import cn.com.qmth.scancloud.vo.ImportStudentQueryVo;
-import cn.com.qmth.scancloud.vo.ImportStudentVo;
-import cn.com.qmth.scancloud.vo.StudentUploadVo;
+import cn.com.qmth.scancloud.vo.*;
 import cn.com.qmth.scancloud.vo.answerquery.AnswerQueryVo;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 public interface StudentDao extends BaseMapper<StudentEntity> {
 
-    List<String> listCampusByExamId(@Param("examId") Long examId);
+    List<CampusVo> listCampusByExamId(@Param("examId") Long examId);
+
+    List<ExamSiteVo> listSiteByExamId(@Param("examId") Long examId);
 
-    List<String> listSiteByExamId(@Param("examId") Long examId);
+    AbsentInfoVo absentInfo(@Param("examId") Long examId, @Param("groupType") String groupType,
+            @Param("groupName") String groupName);
+
+    List<String> absentQuerySummary(@Param("groupNameCol") String groupNameCol,
+            @Param("query") AbsentQueryDomain query);
 
     IPage<AbsentQueryVo> absentQueryPage(Page<AbsentQueryVo> page, @Param("groupNameCol") String groupNameCol,
             @Param("query") AbsentQueryDomain query);
 
+    List<AbsentQueryVo> absentExportList(Page<AbsentQueryVo> page, @Param("groupNameCol") String groupNameCol,
+            @Param("query") AbsentQueryDomain query);
+
     IPage<AnswerQueryVo> queryPage(Page<AnswerQueryVo> page, @Param("query") AnswerQueryDomain query);
 
-    List<StudentUploadVo> findToUpload(@Param("pageSize") Integer pageSize, @Param("schoolId") Long schoolId);
+    List<String> querySummary(@Param("query") AnswerQueryDomain query);
 
-    List<AbsentQueryVo> absentQueryList(@Param("groupNameCol") String groupNameCol,
-            @Param("query") AbsentQueryDomain query);
+    List<AnswerExportVo> exportList(Page<AnswerQueryVo> page, @Param("query") AnswerQueryDomain query);
+
+    List<StudentUploadVo> findToUpload(@Param("pageSize") Integer pageSize, @Param("schoolId") Long schoolId);
 
-    List<AnswerExportVo> queryList(@Param("query") AnswerQueryDomain query);
+    List<StudentUploadVo> findUploadError(@Param("pageSize") Integer pageSize, @Param("schoolId") Long schoolId);
 
     int getPackageCountByExam(@Param("examId") Long examId);
 
@@ -42,8 +46,14 @@ public interface StudentDao extends BaseMapper<StudentEntity> {
 
     IPage<ImportStudentVo> listPageQuery(Page<ImportStudentVo> page, @Param("query") ImportStudentQueryVo query);
 
-	List<StudentUploadVo> findToPictureCopy(@Param("pageSize") Integer pageSize);
+    List<StudentUploadVo> findToPictureCopy(@Param("pageSize") Integer pageSize);
+
+    int getUploadedCount(@Param("examId") Long examId);
+
+    int getNeedUploadCount(@Param("examId") Long examId);
+
+    IPage<ExportCetVo> listCetMarkingPage(Page<ExportCetVo> page, @Param("query") ExportCetMarkingQueryVo query);
 
-	int getUploadedCount(@Param("examId")Long examId);
+    ScanAnswerInfoVo getInfoCountByExam(@Param("examId") Long examId);
 
 }

+ 78 - 0
src/main/java/cn/com/qmth/scancloud/entity/AdapteFileEntity.java

@@ -0,0 +1,78 @@
+package cn.com.qmth.scancloud.entity;
+
+import cn.com.qmth.scancloud.entity.base.BaseEntity;
+import cn.com.qmth.scancloud.enums.Role;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
+
+// 适配卡格式
+@TableName("sc_adapte_file")
+public class AdapteFileEntity extends BaseEntity {
+
+    private static final long serialVersionUID = 2899842570632873472L;
+
+    @MppMultiId
+    private Long examId;
+
+    @MppMultiId
+    private Integer cardNumber;
+
+    @MppMultiId
+    private Role role;
+
+    @MppMultiId
+    private String device;
+
+    private String uri;
+
+    private String md5;
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public Integer getCardNumber() {
+        return cardNumber;
+    }
+
+    public void setCardNumber(Integer cardNumber) {
+        this.cardNumber = cardNumber;
+    }
+
+    public String getDevice() {
+        return device;
+    }
+
+    public void setDevice(String device) {
+        this.device = device;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public String getMd5() {
+        return md5;
+    }
+
+    public void setMd5(String md5) {
+        this.md5 = md5;
+    }
+
+    public Role getRole() {
+        return role;
+    }
+
+    public void setRole(Role role) {
+        this.role = role;
+    }
+
+}

+ 27 - 4
src/main/java/cn/com/qmth/scancloud/entity/AnswerCardEntity.java

@@ -3,16 +3,17 @@ package cn.com.qmth.scancloud.entity;
 import cn.com.qmth.scancloud.entity.base.AuditingWithoutIdEntity;
 import cn.com.qmth.scancloud.enums.CardSource;
 
+import java.util.List;
+
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
 
 // 试卷卡格式
-@TableName("sc_answer_card")
+@TableName(value="sc_answer_card", autoResultMap = true)
 public class AnswerCardEntity extends AuditingWithoutIdEntity {
 
-    /**
-     * 
-     */
     private static final long serialVersionUID = -7904338527706136352L;
 
     @MppMultiId
@@ -40,6 +41,11 @@ public class AnswerCardEntity extends AuditingWithoutIdEntity {
 
     private String parameter;
 
+    private String sliceConfig;
+    
+    @TableField(value = "slice_name", typeHandler = JacksonTypeHandler.class)
+    private List<String> sliceName;
+
     private String remark;
 
     public Long getExamId() {
@@ -138,4 +144,21 @@ public class AnswerCardEntity extends AuditingWithoutIdEntity {
         this.needAdapte = needAdapte;
     }
 
+    public String getSliceConfig() {
+        return sliceConfig;
+    }
+
+    public void setSliceConfig(String sliceConfig) {
+        this.sliceConfig = sliceConfig;
+    }
+
+    
+    public List<String> getSliceName() {
+        return sliceName;
+    }
+
+    public void setSliceName(List<String> sliceName) {
+        this.sliceName = sliceName;
+    }
+    
 }

+ 33 - 2
src/main/java/cn/com/qmth/scancloud/entity/BatchEntity.java

@@ -1,11 +1,12 @@
 package cn.com.qmth.scancloud.entity;
 
+import com.baomidou.mybatisplus.annotation.TableName;
+
 import cn.com.qmth.scancloud.entity.base.AuditingEntity;
 import cn.com.qmth.scancloud.enums.BatchStatus;
+import cn.com.qmth.scancloud.enums.CheckStatus;
 import cn.com.qmth.scancloud.enums.VerifyStatus;
 
-import com.baomidou.mybatisplus.annotation.TableName;
-
 // 扫描批次
 @TableName("sc_batch")
 public class BatchEntity extends AuditingEntity {
@@ -32,6 +33,12 @@ public class BatchEntity extends AuditingEntity {
 
     private VerifyStatus verifyStatus;
 
+    private CheckStatus checkStatus;
+
+    private Long checkImageUserId;
+
+    private Long checkImageTime;
+
     public Long getExamId() {
         return examId;
     }
@@ -88,4 +95,28 @@ public class BatchEntity extends AuditingEntity {
         this.verifyStatus = verifyStatus;
     }
 
+    public CheckStatus getCheckStatus() {
+        return checkStatus;
+    }
+
+    public void setCheckStatus(CheckStatus checkStatus) {
+        this.checkStatus = checkStatus;
+    }
+
+    public Long getCheckImageUserId() {
+        return checkImageUserId;
+    }
+
+    public void setCheckImageUserId(Long checkImageUserId) {
+        this.checkImageUserId = checkImageUserId;
+    }
+
+    public Long getCheckImageTime() {
+        return checkImageTime;
+    }
+
+    public void setCheckImageTime(Long checkImageTime) {
+        this.checkImageTime = checkImageTime;
+    }
+
 }

+ 86 - 0
src/main/java/cn/com/qmth/scancloud/entity/BatchPaperEntity.java

@@ -0,0 +1,86 @@
+package cn.com.qmth.scancloud.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
+
+import java.io.Serializable;
+
+// 批次和试卷绑定关系
+@TableName("sc_batch_paper")
+public class BatchPaperEntity implements Serializable {
+
+    private static final long serialVersionUID = 2688940244963672452L;
+
+    @MppMultiId
+    private Long batchId;
+
+    @MppMultiId
+    private Long studentId;
+
+    @MppMultiId
+    private Integer paperNumber;
+
+    private Long paperId;
+
+    private Integer cardNumber;
+
+    private Boolean assigned;
+    
+    private Boolean needCheck;
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public Integer getPaperNumber() {
+        return paperNumber;
+    }
+
+    public void setPaperNumber(Integer paperNumber) {
+        this.paperNumber = paperNumber;
+    }
+
+    public Long getPaperId() {
+        return paperId;
+    }
+
+    public void setPaperId(Long paperId) {
+        this.paperId = paperId;
+    }
+
+    public Long getBatchId() {
+        return batchId;
+    }
+
+    public void setBatchId(Long batchId) {
+        this.batchId = batchId;
+    }
+
+    public Integer getCardNumber() {
+        return cardNumber;
+    }
+
+    public void setCardNumber(Integer cardNumber) {
+        this.cardNumber = cardNumber;
+    }
+
+    public Boolean getAssigned() {
+        return assigned;
+    }
+
+    public void setAssigned(Boolean assigned) {
+        this.assigned = assigned;
+    }
+    
+    public Boolean getNeedCheck() {
+        return needCheck;
+    }
+    
+    public void setNeedCheck(Boolean needCheck) {
+        this.needCheck = needCheck;
+    }
+}

+ 64 - 36
src/main/java/cn/com/qmth/scancloud/entity/ExamEntity.java

@@ -1,23 +1,20 @@
 package cn.com.qmth.scancloud.entity;
 
-import java.util.List;
-
+import cn.com.qmth.scancloud.entity.base.AuditingWithoutIdEntity;
+import cn.com.qmth.scancloud.enums.ExamMode;
+import cn.com.qmth.scancloud.enums.ImageTransferMode;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 
-import cn.com.qmth.scancloud.entity.base.AuditingWithoutIdEntity;
-import cn.com.qmth.scancloud.enums.ExamMode;
+import java.util.List;
 
 // 考试
-@TableName(value ="sc_exam",autoResultMap = true)
+@TableName(value = "sc_exam", autoResultMap = true)
 public class ExamEntity extends AuditingWithoutIdEntity {
 
-    /**
-     * 
-     */
     private static final long serialVersionUID = 2232451399940497087L;
 
     @TableId(type = IdType.INPUT)
@@ -41,16 +38,23 @@ public class ExamEntity extends AuditingWithoutIdEntity {
 
     private Boolean enableSinglePageAnswer;
 
-    private Integer answerPaperNumberFigure;
+    private ImageTransferMode imageTransferMode;
 
     private Boolean enableUpload;
-    
+
     private Boolean enableSyncVerify;
-    
-    @TableField(value = "paper_type_barcode_content", typeHandler = GsonTypeHandler.class)
+
+    @TableField(value = "paper_type_barcode_content", typeHandler = JacksonTypeHandler.class)
     private List<String> paperTypeBarcodeContent;
-    
+
     private String absentBarcodeContent;
+    
+    //图片抽查比例
+    private Double imageCheckRatio;
+    //考生同步时间
+    private Long dataSyncTime;
+    //卡格式同步时间
+    private Long cardSyncTime;
 
     public Long getId() {
         return id;
@@ -140,37 +144,61 @@ public class ExamEntity extends AuditingWithoutIdEntity {
         this.enableUpload = enableUpload;
     }
 
-    public Integer getAnswerPaperNumberFigure() {
-        return answerPaperNumberFigure;
+    public ImageTransferMode getImageTransferMode() {
+        return imageTransferMode;
+    }
+
+    public void setImageTransferMode(ImageTransferMode imageTransferMode) {
+        this.imageTransferMode = imageTransferMode;
     }
 
-    public void setAnswerPaperNumberFigure(Integer answerPaperNumberFigure) {
-        this.answerPaperNumberFigure = answerPaperNumberFigure;
+    public Boolean getEnableSyncVerify() {
+        return enableSyncVerify;
     }
 
-	public Boolean getEnableSyncVerify() {
-		return enableSyncVerify;
-	}
+    public void setEnableSyncVerify(Boolean enableSyncVerify) {
+        this.enableSyncVerify = enableSyncVerify;
+    }
 
-	public void setEnableSyncVerify(Boolean enableSyncVerify) {
-		this.enableSyncVerify = enableSyncVerify;
-	}
+    public List<String> getPaperTypeBarcodeContent() {
+        return paperTypeBarcodeContent;
+    }
 
-	public List<String> getPaperTypeBarcodeContent() {
-		return paperTypeBarcodeContent;
-	}
+    public void setPaperTypeBarcodeContent(List<String> paperTypeBarcodeContent) {
+        this.paperTypeBarcodeContent = paperTypeBarcodeContent;
+    }
 
-	public void setPaperTypeBarcodeContent(List<String> paperTypeBarcodeContent) {
-		this.paperTypeBarcodeContent = paperTypeBarcodeContent;
-	}
+    public String getAbsentBarcodeContent() {
+        return absentBarcodeContent;
+    }
 
-	public String getAbsentBarcodeContent() {
-		return absentBarcodeContent;
-	}
+    public void setAbsentBarcodeContent(String absentBarcodeContent) {
+        this.absentBarcodeContent = absentBarcodeContent;
+    }
 
-	public void setAbsentBarcodeContent(String absentBarcodeContent) {
-		this.absentBarcodeContent = absentBarcodeContent;
-	}
     
+    public Double getImageCheckRatio() {
+        return imageCheckRatio;
+    }
+
+    public void setImageCheckRatio(Double imageCheckRatio) {
+        this.imageCheckRatio = imageCheckRatio;
+    }
     
+    public Long getDataSyncTime() {
+        return dataSyncTime;
+    }
+
+    public void setDataSyncTime(Long dataSyncTime) {
+        this.dataSyncTime = dataSyncTime;
+    }
+
+    public Long getCardSyncTime() {
+        return cardSyncTime;
+    }
+
+    public void setCardSyncTime(Long cardSyncTime) {
+        this.cardSyncTime = cardSyncTime;
+    }
+
 }

+ 12 - 0
src/main/java/cn/com/qmth/scancloud/entity/FilePropertyEntity.java

@@ -20,6 +20,8 @@ public class FilePropertyEntity extends BaseEntity {
     private String path;
 
     private String md5;
+    
+    private Long fileSize;
 
 	public Long getExamId() {
 		return examId;
@@ -45,5 +47,15 @@ public class FilePropertyEntity extends BaseEntity {
 		this.md5 = md5;
 	}
 
+    
+    public Long getFileSize() {
+        return fileSize;
+    }
+
+    
+    public void setFileSize(Long fileSize) {
+        this.fileSize = fileSize;
+    }
+
 
 }

+ 2 - 2
src/main/java/cn/com/qmth/scancloud/entity/PackageResultEntity.java

@@ -6,7 +6,7 @@ import cn.com.qmth.scancloud.entity.base.AuditingWithoutIdEntity;
 
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
 
 // 签到表
@@ -24,7 +24,7 @@ public class PackageResultEntity extends AuditingWithoutIdEntity {
 
     private String device;
 
-    @TableField(value = "path", typeHandler = GsonTypeHandler.class)
+    @TableField(value = "path", typeHandler = JacksonTypeHandler.class)
     private List<String> path;
 
     private Boolean assigned;

+ 0 - 31
src/main/java/cn/com/qmth/scancloud/entity/PaperEntity.java

@@ -1,7 +1,6 @@
 package cn.com.qmth.scancloud.entity;
 
 import cn.com.qmth.scancloud.entity.base.AuditingEntity;
-
 import com.baomidou.mybatisplus.annotation.TableName;
 
 // 题卡扫描结果
@@ -12,14 +11,8 @@ public class PaperEntity extends AuditingEntity {
 
     private Long examId;
 
-    private Long studentId;
-
-    private Long batchId;
-
     private Integer cardNumber;
 
-    private String examNumber;
-
     private Integer number;
 
     private Integer pageCount;
@@ -38,22 +31,6 @@ public class PaperEntity extends AuditingEntity {
         this.examId = examId;
     }
 
-    public Long getStudentId() {
-        return studentId;
-    }
-
-    public void setStudentId(Long studentId) {
-        this.studentId = studentId;
-    }
-
-    public Long getBatchId() {
-        return batchId;
-    }
-
-    public void setBatchId(Long batchId) {
-        this.batchId = batchId;
-    }
-
     public Integer getCardNumber() {
         return cardNumber;
     }
@@ -62,14 +39,6 @@ public class PaperEntity extends AuditingEntity {
         this.cardNumber = cardNumber;
     }
 
-    public String getExamNumber() {
-        return examNumber;
-    }
-
-    public void setExamNumber(String examNumber) {
-        this.examNumber = examNumber;
-    }
-
     public Integer getNumber() {
         return number;
     }

+ 132 - 131
src/main/java/cn/com/qmth/scancloud/entity/PaperPageEntity.java

@@ -1,23 +1,22 @@
 package cn.com.qmth.scancloud.entity;
 
-import java.util.List;
-
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;
-import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
-
 import cn.com.qmth.scancloud.bean.answersave.ArrayResult;
 import cn.com.qmth.scancloud.bean.answersave.BoolResult;
 import cn.com.qmth.scancloud.bean.answersave.StringResult;
 import cn.com.qmth.scancloud.entity.base.AuditingWithoutIdEntity;
+import cn.com.qmth.scancloud.enums.OmrType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 
-@TableName(value ="sc_paper_page",autoResultMap = true)
+import java.util.List;
+
+@TableName(value = "sc_paper_page", autoResultMap = true)
 public class PaperPageEntity extends AuditingWithoutIdEntity {
 
-    /**
-     * 
-     */
     private static final long serialVersionUID = -182520914017960004L;
 
     @MppMultiId
@@ -25,140 +24,142 @@ public class PaperPageEntity extends AuditingWithoutIdEntity {
 
     @MppMultiId
     private Integer pageIndex;
-    
-    private Long batchId;
-    
-    private Long examId;
-    
-    private Long studentId;
-
-    @TableField(value = "absent", typeHandler = GsonTypeHandler.class)
+
+    @TableField(value = "absent", typeHandler = JacksonTypeHandler.class)
     private BoolResult absent;
 
-    @TableField(value = "breach", typeHandler = GsonTypeHandler.class)
+    @TableField(value = "breach", typeHandler = JacksonTypeHandler.class)
     private BoolResult breach;
 
-    @TableField(value = "paper_type", typeHandler = GsonTypeHandler.class)
+    @TableField(value = "paper_type", typeHandler = JacksonTypeHandler.class)
     private StringResult paperType;
 
-    @TableField(value = "question", typeHandler = GsonTypeHandler.class)
+    @TableField(value = "question", typeHandler = JacksonTypeHandler.class)
     private ArrayResult question;
 
-    @TableField(value = "selective", typeHandler = GsonTypeHandler.class)
+    @TableField(value = "selective", typeHandler = JacksonTypeHandler.class)
     private ArrayResult selective;
 
     private String sheetPath;
 
-    @TableField(value = "slice_path", typeHandler = GsonTypeHandler.class)
+    @TableField(value = "slice_path", typeHandler = JacksonTypeHandler.class)
     private List<String> slicePath;
 
     private String recogData;
-    
-    
-	public Long getPaperId() {
-		return paperId;
-	}
-
-	public void setPaperId(Long paperId) {
-		this.paperId = paperId;
-	}
-
-
-	public Integer getPageIndex() {
-		return pageIndex;
-	}
-
-	public void setPageIndex(Integer pageIndex) {
-		this.pageIndex = pageIndex;
-	}
-
-	public BoolResult getAbsent() {
-		return absent;
-	}
-
-	public void setAbsent(BoolResult absent) {
-		this.absent = absent;
-	}
-
-	public BoolResult getBreach() {
-		return breach;
-	}
-
-	public void setBreach(BoolResult breach) {
-		this.breach = breach;
-	}
-
-	public StringResult getPaperType() {
-		return paperType;
-	}
-
-	public void setPaperType(StringResult paperType) {
-		this.paperType = paperType;
-	}
-
-	public ArrayResult getQuestion() {
-		return question;
-	}
-
-	public void setQuestion(ArrayResult question) {
-		this.question = question;
-	}
-
-	public ArrayResult getSelective() {
-		return selective;
-	}
-
-	public void setSelective(ArrayResult selective) {
-		this.selective = selective;
-	}
-
-	public String getSheetPath() {
-		return sheetPath;
-	}
-
-	public void setSheetPath(String sheetPath) {
-		this.sheetPath = sheetPath;
-	}
-
-	public List<String> getSlicePath() {
-		return slicePath;
-	}
-
-	public void setSlicePath(List<String> slicePath) {
-		this.slicePath = slicePath;
-	}
-
-	public Long getBatchId() {
-		return batchId;
-	}
-
-	public void setBatchId(Long batchId) {
-		this.batchId = batchId;
-	}
-
-	public Long getStudentId() {
-		return studentId;
-	}
-
-	public void setStudentId(Long studentId) {
-		this.studentId = studentId;
-	}
-
-	public Long getExamId() {
-		return examId;
-	}
-
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
-
-	public String getRecogData() {
-		return recogData;
-	}
-
-	public void setRecogData(String recogData) {
-		this.recogData = recogData;
-	}
 
-    
+    public Long getPaperId() {
+        return paperId;
+    }
+
+    public void setPaperId(Long paperId) {
+        this.paperId = paperId;
+    }
+
+    public Integer getPageIndex() {
+        return pageIndex;
+    }
+
+    public void setPageIndex(Integer pageIndex) {
+        this.pageIndex = pageIndex;
+    }
+
+    public BoolResult getAbsent() {
+        return absent;
+    }
+
+    public void setAbsent(BoolResult absent) {
+        this.absent = absent;
+    }
+
+    public BoolResult getBreach() {
+        return breach;
+    }
+
+    public void setBreach(BoolResult breach) {
+        this.breach = breach;
+    }
+
+    public StringResult getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(StringResult paperType) {
+        this.paperType = paperType;
+    }
+
+    public ArrayResult getQuestion() {
+        return question;
+    }
+
+    public void setQuestion(ArrayResult question) {
+        this.question = question;
+    }
+
+    public ArrayResult getSelective() {
+        return selective;
+    }
+
+    public void setSelective(ArrayResult selective) {
+        this.selective = selective;
+    }
+
+    public String getSheetPath() {
+        return sheetPath;
+    }
+
+    public void setSheetPath(String sheetPath) {
+        this.sheetPath = sheetPath;
+    }
+
+    public List<String> getSlicePath() {
+        return slicePath;
+    }
+
+    public void setSlicePath(List<String> slicePath) {
+        this.slicePath = slicePath;
+    }
+
+    public String getRecogData() {
+        return recogData;
+    }
+
+    public void setRecogData(String recogData) {
+        this.recogData = recogData;
+    }
+
+    public boolean isQuestionFilled() {
+        if (question != null) {
+            if (CollectionUtils.isEmpty(question.getResult())) {
+                return false;
+            }
+            for (String s : question.getResult()) {
+                if (!"#".equals(s)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        return false;
+    }
+
+    public void checkPaperType(List<String> barcodeContents) {
+        if (paperType != null) {
+            paperType.setResult(StringUtils.trimToEmpty(paperType.getResult()));
+            if (paperType.getType() == OmrType.BARCODE) {
+                if (paperType.getResult().length() == 0) {
+                    paperType.setResult("#");
+                } else if (CollectionUtils.isNotEmpty(barcodeContents) && !barcodeContents
+                        .contains(paperType.getResult())) {
+                    paperType.setResult("?");
+                }
+            } else if (paperType.getType() == OmrType.FILL_AREA) {
+                if (paperType.getResult().startsWith("?")) {
+                    paperType.setResult(paperType.getResult().substring(1));
+                }
+                if (paperType.getResult().length() == 0) {
+                    paperType.setResult("#");
+                }
+            }
+        }
+    }
 }

+ 108 - 0
src/main/java/cn/com/qmth/scancloud/entity/QuestionEntity.java

@@ -0,0 +1,108 @@
+package cn.com.qmth.scancloud.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import cn.com.qmth.scancloud.entity.base.AuditingEntity;
+
+@TableName("sc_question")
+public class QuestionEntity extends AuditingEntity {
+
+    private static final long serialVersionUID = 4615922017570826243L;
+
+    private Long examId;
+
+    private String subjectCode;
+
+    private String paperType;
+
+    private Boolean objective;
+
+    private Integer mainNumber;
+
+    private String subNumber;
+
+    private String mainTitle;
+
+    private Double totalScore;
+
+    
+    public Long getExamId() {
+        return examId;
+    }
+
+    
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    
+    public String getPaperType() {
+        return paperType;
+    }
+
+    
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    
+    public Boolean getObjective() {
+        return objective;
+    }
+
+    
+    public void setObjective(Boolean objective) {
+        this.objective = objective;
+    }
+
+    
+    public Integer getMainNumber() {
+        return mainNumber;
+    }
+
+    
+    public void setMainNumber(Integer mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
+    
+    public String getSubNumber() {
+        return subNumber;
+    }
+
+    
+    public void setSubNumber(String subNumber) {
+        this.subNumber = subNumber;
+    }
+
+    
+    public String getMainTitle() {
+        return mainTitle;
+    }
+
+    
+    public void setMainTitle(String mainTitle) {
+        this.mainTitle = mainTitle;
+    }
+
+    
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+}

+ 60 - 0
src/main/java/cn/com/qmth/scancloud/entity/StudentEntity.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 
 import cn.com.qmth.scancloud.entity.base.AuditingWithoutIdEntity;
+import cn.com.qmth.scancloud.enums.ExamStatus;
 import cn.com.qmth.scancloud.enums.ScanStatus;
 import cn.com.qmth.scancloud.enums.UploadStatus;
 
@@ -35,12 +36,18 @@ public class StudentEntity extends AuditingWithoutIdEntity {
     private String packageCode;
 
     private String examSite;
+    
+    private String examSiteName;
 
     private String examRoom;
 
     private String seatNumber;
 
     private String campusName;
+    
+    private String campusCode;
+    
+    private String device;
 
     private ScanStatus status;
     //识别缺考
@@ -56,6 +63,11 @@ public class StudentEntity extends AuditingWithoutIdEntity {
 
     // 是否有缺页
     private Boolean incomplete;
+    
+    //考试状态
+    private ExamStatus examStatus;
+    //违纪码
+    private String breachCode;
 
     @TableField(updateStrategy = FieldStrategy.IGNORED)
     private Integer cardNumber;
@@ -235,5 +247,53 @@ public class StudentEntity extends AuditingWithoutIdEntity {
 		this.paperType = paperType;
 	}
 
+    
+    public String getDevice() {
+        return device;
+    }
+    
+    public void setDevice(String device) {
+        this.device = device;
+    }
+
+    
+    public ExamStatus getExamStatus() {
+        return examStatus;
+    }
+
+    
+    public void setExamStatus(ExamStatus examStatus) {
+        this.examStatus = examStatus;
+    }
+
+    
+    public String getExamSiteName() {
+        return examSiteName;
+    }
+
+    
+    public void setExamSiteName(String examSiteName) {
+        this.examSiteName = examSiteName;
+    }
+
+    
+    public String getCampusCode() {
+        return campusCode;
+    }
+
+    
+    public void setCampusCode(String campusCode) {
+        this.campusCode = campusCode;
+    }
+
+    
+    public String getBreachCode() {
+        return breachCode;
+    }
+
+    
+    public void setBreachCode(String breachCode) {
+        this.breachCode = breachCode;
+    }
 	
 }

+ 13 - 5
src/main/java/cn/com/qmth/scancloud/entity/StudentPaperEntity.java

@@ -1,16 +1,14 @@
 package cn.com.qmth.scancloud.entity;
 
-import cn.com.qmth.scancloud.entity.base.AuditingWithoutIdEntity;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
 
+import java.io.Serializable;
+
 // 考生和试卷绑定关系
 @TableName("sc_student_paper")
-public class StudentPaperEntity extends AuditingWithoutIdEntity {
+public class StudentPaperEntity implements Serializable {
 
-    /**
-     *
-     */
     private static final long serialVersionUID = 305711335687760851L;
 
     @MppMultiId
@@ -21,6 +19,16 @@ public class StudentPaperEntity extends AuditingWithoutIdEntity {
 
     private Long paperId;
 
+    public StudentPaperEntity() {
+
+    }
+
+    public StudentPaperEntity(Long studentId, Integer paperNumber, Long paperId) {
+        this.studentId = studentId;
+        this.paperNumber = paperNumber;
+        this.paperId = paperId;
+    }
+
     public Long getStudentId() {
         return studentId;
     }

+ 16 - 1
src/main/java/cn/com/qmth/scancloud/entity/SubjectEntity.java

@@ -1,12 +1,16 @@
 package cn.com.qmth.scancloud.entity;
 
+import java.util.List;
+
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import com.github.jeffreyning.mybatisplus.anno.MppMultiId;
 
 import cn.com.qmth.scancloud.entity.base.AuditingWithoutIdEntity;
 
 // 科目
-@TableName("sc_subject")
+@TableName(value = "sc_subject", autoResultMap = true)
 public class SubjectEntity extends AuditingWithoutIdEntity {
 
     /**
@@ -22,6 +26,9 @@ public class SubjectEntity extends AuditingWithoutIdEntity {
 
     private String name;
 
+    @TableField(value = "paper_type_barcode_content", typeHandler = JacksonTypeHandler.class)
+    private List<String> paperTypeBarcodeContent;
+
     public SubjectEntity(Long examId, String subjectCode, String subjectName) {
         this.examId = examId;
         this.code = subjectCode;
@@ -55,4 +62,12 @@ public class SubjectEntity extends AuditingWithoutIdEntity {
         this.name = name;
     }
 
+    public List<String> getPaperTypeBarcodeContent() {
+        return paperTypeBarcodeContent;
+    }
+
+    public void setPaperTypeBarcodeContent(List<String> paperTypeBarcodeContent) {
+        this.paperTypeBarcodeContent = paperTypeBarcodeContent;
+    }
+
 }

+ 9 - 24
src/main/java/cn/com/qmth/scancloud/entity/SystemConfigEntity.java

@@ -1,17 +1,15 @@
 package cn.com.qmth.scancloud.entity;
 
-import com.baomidou.mybatisplus.annotation.TableName;
-
 import cn.com.qmth.scancloud.entity.base.AuditingEntity;
-import cn.com.qmth.scancloud.enums.ImageTransferMode;
 import cn.com.qmth.scancloud.enums.SystemMode;
+import com.baomidou.mybatisplus.annotation.TableName;
 
 // 系统配置
 @TableName("sc_system_config")
 public class SystemConfigEntity extends AuditingEntity {
 
     /**
-     * 
+     *
      */
     private static final long serialVersionUID = 6940554165427530335L;
 
@@ -26,10 +24,8 @@ public class SystemConfigEntity extends AuditingEntity {
     private String clientMd5;
 
     private Long clientUpdateTime;
-    
+
     private SystemMode systemMode;
-    
-    private ImageTransferMode imageTransferMode;
 
     public Boolean getScannerEnableLogin() {
         return scannerEnableLogin;
@@ -79,23 +75,12 @@ public class SystemConfigEntity extends AuditingEntity {
         this.clientUpdateTime = clientUpdateTime;
     }
 
-	public SystemMode getSystemMode() {
-		return systemMode;
-	}
-
-	public void setSystemMode(SystemMode systemMode) {
-		this.systemMode = systemMode;
-	}
-
-	public ImageTransferMode getImageTransferMode() {
-		return imageTransferMode;
-	}
+    public SystemMode getSystemMode() {
+        return systemMode;
+    }
 
-	public void setImageTransferMode(ImageTransferMode imageTransferMode) {
-		this.imageTransferMode = imageTransferMode;
-	}
+    public void setSystemMode(SystemMode systemMode) {
+        this.systemMode = systemMode;
+    }
 
-	
-	
-    
 }

+ 9 - 1
src/main/java/cn/com/qmth/scancloud/entity/UserEntity.java

@@ -21,6 +21,7 @@ public class UserEntity extends AuditingEntity {
 	private Role role;
 	private Boolean enable;
 	private Long schoolId;
+	private String device;
 	
 	
 	public String getLoginName() {
@@ -59,6 +60,13 @@ public class UserEntity extends AuditingEntity {
 	public void setSchoolId(Long schoolId) {
 		this.schoolId = schoolId;
 	}
+    
+    public String getDevice() {
+        return device;
+    }
+    
+    public void setDevice(String device) {
+        this.device = device;
+    }
 
-	
 }

+ 30 - 0
src/main/java/cn/com/qmth/scancloud/enums/CheckStatus.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.scancloud.enums;
+/**
+ * 图片检查状态
+ */
+public enum CheckStatus {
+
+	WAITING("未处理"),
+
+    FINISH("已处理");
+
+    private String name;
+
+    CheckStatus(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static CheckStatus getByName(String name) {
+        for (CheckStatus r : CheckStatus.values()) {
+            if (r.getName().equals(name)) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+}

+ 16 - 9
src/main/java/cn/com/qmth/scancloud/enums/ConditionType.java

@@ -2,27 +2,34 @@ package cn.com.qmth.scancloud.enums;
 
 public enum ConditionType {
 
-    FILL_SUSPECT("填涂识别嫌疑"),
+    FILL_SUSPECT("填涂识别嫌疑", false),
     // ABSENT_FILL("缺考已填涂"),
     // BREACH_FILL("违纪已填涂"),
-    QUESTION_SINGLE_EXCEED("单选题多填涂"),
-    QUESTION_SINGLE_BLANK("单选题空选题数大于"),
-    QUESTION_MULTI_BLANK("多选题空选题数大于"),
-    SELECTIVE_EXCEED("选做题填涂超过"),
-    SELECTIVE_BLANK("选做题空选题数大于"),
-    PAPER_TYPE_EXCEED("卷型多填涂"),
-    PAPER_TYPE_BLANK("卷型空选");
+    QUESTION_SINGLE_EXCEED("单选题多填涂", false),
+    QUESTION_SINGLE_BLANK("单选题空选题数大于", true),
+    QUESTION_MULTI_BLANK("多选题空选题数大于", true),
+    SELECTIVE_EXCEED("选做题填涂超过", true),
+    SELECTIVE_BLANK("选做题空选题数大于", true),
+    PAPER_TYPE_EXCEED("卷型多填涂", false),
+    PAPER_TYPE_BLANK("卷型空选", false);
 
     private String name;
 
-    ConditionType(String name) {
+    private boolean needValue;
+
+    ConditionType(String name, boolean needValue) {
         this.name = name;
+        this.needValue = needValue;
     }
 
     public String getName() {
         return name;
     }
 
+    public boolean isNeedValue() {
+        return needValue;
+    }
+
     public static ConditionType getByName(String name) {
         for (ConditionType r : ConditionType.values()) {
             if (r.getName().equals(name)) {

+ 3 - 1
src/main/java/cn/com/qmth/scancloud/enums/ExamMode.java

@@ -6,7 +6,9 @@ public enum ExamMode {
 
     K12("基础教育"),
 
-    POSTGRADUATE("研究生");
+    POSTGRADUATE("研究生"),
+    
+    CET("四六级");
 
     private String name;
 

+ 26 - 0
src/main/java/cn/com/qmth/scancloud/enums/ExamStatus.java

@@ -0,0 +1,26 @@
+package cn.com.qmth.scancloud.enums;
+
+public enum ExamStatus {
+
+     ABSENT("缺考");
+
+    private String name;
+
+    ExamStatus(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static ExamStatus getByName(String name) {
+        for (ExamStatus r : ExamStatus.values()) {
+            if (r.getName().equals(name)) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+}

+ 42 - 40
src/main/java/cn/com/qmth/scancloud/enums/FileType.java

@@ -5,45 +5,47 @@ package cn.com.qmth.scancloud.enums;
  */
 public enum FileType {
 
-	SHEET("原图", "%d/answer/%d/%s/%d-%d.%s"), 
-	SLICE("裁切图", "%d/answer/%d/%s/%d-%d-%d.%s"), 
-	PACKAGE("签到表", "%d/card/package.%s"),
-	CARD("题卡", "%s/card/answer/%d.%s"), 
-	ADMIN_SLICE("管理员上传裁切图", "%d/upload/%s/%d-%d-%d/%s.%s"),
-	;
-
-	private String name;
-
-	private String pattern;
-
-	private FileType(String name, String pattern) {
-		this.name = name;
-		this.pattern = pattern;
-	}
-
-	public String getName() {
-		return name;
-	}
-
-	public String getPattern() {
-		return pattern;
-	}
-
-	public boolean equals(String type) {
-		return toString().equalsIgnoreCase(type);
-	}
-
-	public static FileType findByText(String text) {
-		for (FileType type : values()) {
-			if (type.equals(text)) {
-				return type;
-			}
-		}
-		return null;
-	}
-
-	public String getPath(Object... param) {
-		return String.format(pattern, param);
-	}
+    SHEET("原图", "%d/answer/%d/%s/%d-%d.%s"),
+    SLICE("裁切图", "%d/answer/%d/%s/%d-%d-%d.%s"),
+    PACKAGE("签到表", "%d/card/package.%s"),
+    CARD("题卡", "%d/card/answer/%d.%s"),
+    ADMIN_SLICE("管理员上传裁切图", "%d/upload/%s/%d-%d-%d/%s.%s"),
+    ADMIN_SHEET("管理员上传裁原图", "%d/upload/%s/%d-%d/%s.%s"),
+    ADAPTE_FILE("适配题卡", "%d/card/answer/%d/%s.%s"),
+    ;
+
+    private String name;
+
+    private String pattern;
+
+    private FileType(String name, String pattern) {
+        this.name = name;
+        this.pattern = pattern;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getPattern() {
+        return pattern;
+    }
+
+    public boolean equals(String type) {
+        return toString().equalsIgnoreCase(type);
+    }
+
+    public static FileType findByText(String text) {
+        for (FileType type : values()) {
+            if (type.equals(text)) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+    public String getPath(Object... param) {
+        return String.format(pattern, param);
+    }
 
 }

+ 14 - 7
src/main/java/cn/com/qmth/scancloud/enums/GroupType.java

@@ -2,23 +2,30 @@ package cn.com.qmth.scancloud.enums;
 
 public enum GroupType {
 
-    SUBJECT("科目"),
-    PACKAGE("签到表"), 
-    CAMPUS("学习中心"), 
-    EXAM_SITE("考点"), 
-    EXAM_ROOM("考场"),
-    STUDENT_CODE("学号");
+    SUBJECT("科目", "subject_code"),
+    PACKAGE("卷袋", "package_code"),
+    CAMPUS("学习中心", "campus_name"),
+    EXAM_SITE("考点", "exam_site"),
+    EXAM_ROOM("考场", "exam_room"),
+    STUDENT_CODE("学号", "student_code");
 
     private String name;
 
-    GroupType(String name) {
+    private String fieldName;
+
+    GroupType(String name, String fieldName) {
         this.name = name;
+        this.fieldName = fieldName;
     }
 
     public String getName() {
         return name;
     }
 
+    public String getFieldName() {
+        return fieldName;
+    }
+
     public static GroupType getByName(String name) {
         for (GroupType r : GroupType.values()) {
             if (r.getName().equals(name)) {

+ 2 - 0
src/main/java/cn/com/qmth/scancloud/enums/ImageTransferMode.java

@@ -2,6 +2,8 @@ package cn.com.qmth.scancloud.enums;
 
 public enum ImageTransferMode {
 
+    OFF("OFF"),
+
     CET("CET"),
 
     OW("OW"),

+ 3 - 0
src/main/java/cn/com/qmth/scancloud/exception/NotFoundExceptions.java

@@ -11,5 +11,8 @@ public class NotFoundExceptions {
 
     public static final NotFoundException NO_SYNC_VERIFY_TASK = new NotFoundException(102, "no sync verify task",
             "没有实时审核任务", null);
+    
+    public static final NotFoundException NO_CHECK_IMAGE_TASK = new NotFoundException(102, "no check image task",
+            "没有图片审核任务", null);
 
 }

+ 1 - 1
src/main/java/cn/com/qmth/scancloud/exception/ParameterExceptions.java

@@ -4,6 +4,6 @@ import com.qmth.boot.core.exception.ParameterException;
 
 public interface ParameterExceptions {
 
-    public static final ParameterException EXAM_NOT_FOUND = new ParameterException(101, null, "考试不存在或已关闭", null);
+    ParameterException EXAM_NOT_FOUND = new ParameterException(101, null, "考试不存在或已关闭", null);
 
 }

+ 2 - 0
src/main/java/cn/com/qmth/scancloud/exception/UnauthorizedExceptions.java

@@ -4,4 +4,6 @@ import com.qmth.boot.core.exception.UnauthorizedException;
 
 public interface UnauthorizedExceptions {
 	public static final UnauthorizedException SCANNER_PASSWORD_INVALID = new UnauthorizedException(101, null, "密码错误", null);
+	
+	public static final UnauthorizedException MARKINGCLOUD_LOGIN_ERROR = new UnauthorizedException(101, null, "云阅卷鉴权失败", null);
 }

+ 10 - 0
src/main/java/cn/com/qmth/scancloud/model/AnswerCardInfo.java

@@ -29,6 +29,8 @@ public class AnswerCardInfo {
 
     private String remark;
 
+    private String sliceConfig;
+
     private Long updateTime;
 
     public AnswerCardInfo() {
@@ -134,6 +136,14 @@ public class AnswerCardInfo {
         this.subjectName = subjectName;
     }
 
+    public String getSliceConfig() {
+        return sliceConfig;
+    }
+
+    public void setSliceConfig(String sliceConfig) {
+        this.sliceConfig = sliceConfig;
+    }
+
     public Long getUpdateTime() {
         return updateTime;
     }

+ 90 - 57
src/main/java/cn/com/qmth/scancloud/model/DataUploadDto.java

@@ -1,61 +1,94 @@
 package cn.com.qmth.scancloud.model;
 
 public class DataUploadDto {
-	private String examNumber;
-	private Integer sliceCount;
-	private Integer sheetCount;
-	private String answers;
-	private Boolean absent;
-	private String batchCode;
-	private String paperType;
-	private Boolean manual;
-	public String getExamNumber() {
-		return examNumber;
-	}
-	public void setExamNumber(String examNumber) {
-		this.examNumber = examNumber;
-	}
-	public Integer getSliceCount() {
-		return sliceCount;
-	}
-	public void setSliceCount(Integer sliceCount) {
-		this.sliceCount = sliceCount;
-	}
-	public Integer getSheetCount() {
-		return sheetCount;
-	}
-	public void setSheetCount(Integer sheetCount) {
-		this.sheetCount = sheetCount;
-	}
-	public String getAnswers() {
-		return answers;
-	}
-	public void setAnswers(String answers) {
-		this.answers = answers;
-	}
-	public Boolean getAbsent() {
-		return absent;
-	}
-	public void setAbsent(Boolean absent) {
-		this.absent = absent;
-	}
-	public String getBatchCode() {
-		return batchCode;
-	}
-	public void setBatchCode(String batchCode) {
-		this.batchCode = batchCode;
-	}
-	public String getPaperType() {
-		return paperType;
-	}
-	public void setPaperType(String paperType) {
-		this.paperType = paperType;
-	}
-	public Boolean getManual() {
-		return manual;
-	}
-	public void setManual(Boolean manual) {
-		this.manual = manual;
-	}
-	
+
+    private String examNumber;
+
+    private Integer sliceCount;
+
+    private Integer sheetCount;
+
+    private String answers;
+
+    private Boolean absent;
+
+    private String batchCode;
+
+    private String paperType;
+
+    private Boolean manual;
+
+    private Integer cardNumber;
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public Integer getSliceCount() {
+        return sliceCount;
+    }
+
+    public void setSliceCount(Integer sliceCount) {
+        this.sliceCount = sliceCount;
+    }
+
+    public Integer getSheetCount() {
+        return sheetCount;
+    }
+
+    public void setSheetCount(Integer sheetCount) {
+        this.sheetCount = sheetCount;
+    }
+
+    public String getAnswers() {
+        return answers;
+    }
+
+    public void setAnswers(String answers) {
+        this.answers = answers;
+    }
+
+    public Boolean getAbsent() {
+        return absent;
+    }
+
+    public void setAbsent(Boolean absent) {
+        this.absent = absent;
+    }
+
+    public String getBatchCode() {
+        return batchCode;
+    }
+
+    public void setBatchCode(String batchCode) {
+        this.batchCode = batchCode;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public Boolean getManual() {
+        return manual;
+    }
+
+    public void setManual(Boolean manual) {
+        this.manual = manual;
+    }
+
+    public Integer getCardNumber() {
+        return cardNumber;
+    }
+
+    public void setCardNumber(Integer cardNumber) {
+        this.cardNumber = cardNumber;
+    }
 }

+ 31 - 0
src/main/java/cn/com/qmth/scancloud/model/ManualAbsentImportDTO.java

@@ -0,0 +1,31 @@
+package cn.com.qmth.scancloud.model;
+
+import com.qmth.boot.tools.excel.annotation.ExcelColumn;
+
+/**
+ * 人工制定缺考导入名单对象
+ */
+public class ManualAbsentImportDTO {
+
+    @ExcelColumn(name = "考生编号", nullable = false, index = 1)
+    private String studentCode;
+
+    @ExcelColumn(name = "科目代码", nullable = false, index = 2)
+    private String subjectCode;
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+}

+ 10 - 4
src/main/java/cn/com/qmth/scancloud/model/StudentInfo.java

@@ -5,7 +5,7 @@ public class StudentInfo {
     private Long id;
 
     private Long examId;
-    
+
     private Long schoolId;
 
     private String examNumber;
@@ -26,6 +26,8 @@ public class StudentInfo {
 
     private String campusName;
 
+    private String paperType;
+
     public Long getId() {
         return id;
     }
@@ -106,7 +108,6 @@ public class StudentInfo {
         this.examRoom = examRoom;
     }
 
-
     public String getCampusName() {
         return campusName;
     }
@@ -115,14 +116,19 @@ public class StudentInfo {
         this.campusName = campusName;
     }
 
-    
     public Long getSchoolId() {
         return schoolId;
     }
 
-    
     public void setSchoolId(Long schoolId) {
         this.schoolId = schoolId;
     }
 
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
 }

+ 11 - 2
src/main/java/cn/com/qmth/scancloud/model/UserInfo.java

@@ -16,9 +16,11 @@ public class UserInfo {
     private String schoolName;
 
     private Role role;
-    
+
     private String token;
 
+    private String fileServer;
+
     public Long getId() {
         return id;
     }
@@ -58,7 +60,7 @@ public class UserInfo {
     public void setRole(Role role) {
         this.role = role;
     }
-    
+
     public String getToken() {
         return token;
     }
@@ -67,4 +69,11 @@ public class UserInfo {
         this.token = token;
     }
 
+    public String getFileServer() {
+        return fileServer;
+    }
+
+    public void setFileServer(String fileServer) {
+        this.fileServer = fileServer;
+    }
 }

+ 62 - 46
src/main/java/cn/com/qmth/scancloud/multithread/BatchConsumer.java

@@ -5,50 +5,66 @@ import java.util.concurrent.locks.LockSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public abstract class BatchConsumer<T>  extends Thread{
-	private static final Logger LOG = LoggerFactory.getLogger(BatchConsumer.class);
-	private Basket basket;
-	
-	public BatchConsumer() {
-	}
-	@SuppressWarnings("unchecked")
-	@Override
-	public void run() {
-		try {
-			for (;;) {
-				//先判断是否有异常结束
-				if(basket.isExcuteError()) {
-					break;
-				}
-				//取消费数据
-				Object o= basket.consume();
-				//判断消费数据是否是结束
-				if(o instanceof EndObject) {
-					basket.countDown();
-					break;
-				}
-				if(o instanceof ParkObject) {
-					basket.countDown();
-					LockSupport.park();
-				}else {
-					T t=(T)o;
-					//消费数据实现
-					consume(t);
-				}
-			}
-		} catch (Exception e) {
-			basket.setExcuteError(true);
-			LOG.error("消费线程处理出错",e);
-		} finally {
-			basket.countDown();
-		}
-	}
-	public abstract void consume(T t);
-	protected Basket getBasket() {
-		return basket;
-	}
-	protected void setBasket(Basket basket) {
-		this.basket = basket;
-	}
-	
+public abstract class BatchConsumer<T> extends Thread {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BatchConsumer.class);
+
+    private Basket basket;
+
+    private BatchConsumer<T> batchConsumer;
+
+    public BatchConsumer<T> getBatchConsumer() {
+        return batchConsumer;
+    }
+
+    public void setBatchConsumer(BatchConsumer<T> batchConsumer) {
+        this.batchConsumer = batchConsumer;
+    }
+
+    public BatchConsumer() {
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void run() {
+        try {
+            for (;;) {
+                // 先判断是否有异常结束
+                if (basket.isExcuteError()) {
+                    break;
+                }
+                // 取消费数据
+                Object o = basket.consume();
+                // 判断消费数据是否是结束
+                if (o instanceof EndObject) {
+                    basket.countDown();
+                    break;
+                }
+                if (o instanceof ParkObject) {
+                    basket.countDown();
+                    LockSupport.park();
+                } else {
+                    T t = (T) o;
+                    // 消费数据实现
+                    batchConsumer.consume(t);
+                }
+            }
+        } catch (Exception e) {
+            basket.setExcuteError(true);
+            LOG.error("消费线程处理出错", e);
+        } finally {
+            basket.countDown();
+        }
+    }
+
+    public abstract void consume(T t);
+
+    protected Basket getBasket() {
+        return basket;
+    }
+
+    protected void setBasket(Basket basket) {
+        this.basket = basket;
+    }
+
 }

+ 156 - 154
src/main/java/cn/com/qmth/scancloud/multithread/BatchProducer.java

@@ -1,169 +1,171 @@
 package cn.com.qmth.scancloud.multithread;
 
+import cn.com.qmth.scancloud.support.SpringContextHolder;
+import cn.com.qmth.scancloud.util.AopTargetUtils;
+import com.qmth.boot.core.exception.StatusException;
+import org.apache.commons.collections4.CollectionUtils;
+
 import java.lang.reflect.ParameterizedType;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.locks.LockSupport;
 
-import org.apache.commons.collections4.CollectionUtils;
-
-import com.qmth.boot.core.exception.StatusException;
-
-import cn.com.qmth.scancloud.support.SpringContextHolder;
-import cn.com.qmth.scancloud.util.AopTargetUtils;
-
 /**
- *
  * @param <T> 生产消费对象
  * @param <C> 消费线程类
  */
 public abstract class BatchProducer<T, C extends BatchConsumer<T>> {
-	private List<BatchConsumer<T>> consumers;
-
-	private Basket basket;
-	public void startDispose(int consumerCount) throws InterruptedException {
-		startDispose(consumerCount, null);
-	}
-	/**
-	 * 处理开始方法
-	 * 
-	 * @param consumerCount 消费线程数
-	 * @param param         生产者业务参数
-	 */
-	public void startDispose(int consumerCount, Map<String, Object> param) throws InterruptedException {
-		// 启动消费者
-		try {
-			startConsumer(consumerCount);
-			for (;;) {
-				List<T> dtos = findOneBatchData(param);
-				if (CollectionUtils.isEmpty(dtos)) {
-					// 拿不到数据,结束消费
-					endConsumer();
-					break;
-				}
-				// 开始处理一轮
-				disposeBatch(dtos);
-				//重置计数器
-				basket.endGateReset();
-				// 唤醒消费者
-				unParkConsumer();
-			}
-		} catch (StatusException e) {
-			// 获取异常时发送异常结束信息
-			endConsumerAsError();
-			throw e;
-		} catch (Exception e) {
-			// 获取异常时发送异常结束信息
-			endConsumerAsError();
-			throw new StatusException("处理失败", e);
-		}
-	}
-
-	/**获取一个批次数据,都被消费处理完毕后才再取下一批
-	 * @param param
-	 * @return
-	 */
-	public abstract List<T> findOneBatchData(Map<String, Object> param);
-
-	@SuppressWarnings("unchecked")
-	private void startConsumer(int consumerCount) {
-		if (consumerCount <= 0) {
-			consumerCount = 1;
-		}
-		ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
-		Class<C> clazz = (Class<C>) pt.getActualTypeArguments()[1];
-		consumers = new ArrayList<>();
-		this.basket = new Basket(consumerCount);
-		// 启动消费者
-		int count = basket.getConsumerCount();
-		for (int i = 0; i < count; i++) {
-			BatchConsumer<T> co = SpringContextHolder.getBean(clazz);
-			co.setBasket(basket);
-			co.start();
-			consumers.add((BatchConsumer<T>)AopTargetUtils.getTarget(co));
-		}
-	}
-
-	private void disposeBatch(List<T> dtos) throws InterruptedException {
-		// 生产数据
-		for (T t : dtos) {
-			offer(t);
-		}
-		// 发送一轮结束信息
-		parkConsumer();
-		// 等待子线程结束
-		await();
-		// 判断子线程是否正常结束
-		if (basket.isExcuteError()) {
-			throw new StatusException("处理失败,线程异常");
-		}
-	}
-
-	/**
-	 * 出异常后修改标识
-	 * 
-	 */
-	private void endConsumerAsError() {
-		basket.setExcuteError(true);
-	}
-
-	/**
-	 * 正常结束消费者
-	 * 
-	 * @throws InterruptedException
-	 */
-	private void endConsumer() throws InterruptedException {
-		int count = basket.getConsumerCount();
-		EndObject eo = new EndObject();
-		for (int i = 0; i < count; i++) {
-			basket.offer(eo);
-		}
-
-	}
-
-	/**
-	 * 正常暂停消费者
-	 * 
-	 * @throws InterruptedException
-	 */
-	private void parkConsumer() throws InterruptedException {
-		int count = basket.getConsumerCount();
-		ParkObject eo = new ParkObject();
-		for (int i = 0; i < count; i++) {
-			basket.offer(eo);
-		}
-
-	}
-
-	/**
-	 * 正常唤醒消费者
-	 * 
-	 * @throws InterruptedException
-	 */
-	private void unParkConsumer() {
-		for (BatchConsumer<T> c : consumers) {
-			LockSupport.unpark(c);
-		}
-	}
-
-	/**
-	 * 生产数据
-	 * 
-	 * @param ob
-	 * @throws InterruptedException
-	 */
-	private void offer(Object ob) throws InterruptedException {
-		basket.offer(ob);
-	}
-
-	/**
-	 * 等待所有消费者结束
-	 * 
-	 * @throws InterruptedException
-	 */
-	private void await() throws InterruptedException {
-		basket.await();
-	}
+
+    private List<BatchConsumer<T>> consumers;
+
+    private Basket basket;
+
+    public void startDispose(int consumerCount) throws InterruptedException {
+        startDispose(consumerCount, null);
+    }
+
+    /**
+     * 处理开始方法
+     *
+     * @param consumerCount 消费线程数
+     * @param param         生产者业务参数
+     */
+    public void startDispose(int consumerCount, Map<String, Object> param) throws InterruptedException {
+        // 启动消费者
+        try {
+            startConsumer(consumerCount);
+            for (; ; ) {
+                List<T> dtos = findOneBatchData(param);
+                if (CollectionUtils.isEmpty(dtos)) {
+                    // 拿不到数据,结束消费
+                    endConsumer();
+                    break;
+                }
+                // 开始处理一轮
+                disposeBatch(dtos);
+                //重置计数器
+                basket.endGateReset();
+                // 唤醒消费者
+                unParkConsumer();
+            }
+        } catch (StatusException e) {
+            // 获取异常时发送异常结束信息
+            endConsumerAsError();
+            throw e;
+        } catch (Exception e) {
+            // 获取异常时发送异常结束信息
+            endConsumerAsError();
+            throw new StatusException("处理失败", e);
+        }
+    }
+
+    /**
+     * 获取一个批次数据,都被消费处理完毕后才再取下一批
+     *
+     * @param param
+     * @return
+     */
+    public abstract List<T> findOneBatchData(Map<String, Object> param);
+
+    @SuppressWarnings("unchecked")
+    private void startConsumer(int consumerCount) {
+        if (consumerCount <= 0) {
+            consumerCount = 1;
+        }
+        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
+        Class<C> clazz = (Class<C>) pt.getActualTypeArguments()[1];
+        consumers = new ArrayList<>();
+        this.basket = new Basket(consumerCount);
+        // 启动消费者
+        int count = basket.getConsumerCount();
+        for (int i = 0; i < count; i++) {
+            BatchConsumer<T> co = SpringContextHolder.getBean(clazz);
+            co.setBasket(basket);
+            co.setBatchConsumer(co);
+            co.start();
+            consumers.add((BatchConsumer<T>) AopTargetUtils.getTarget(co));
+        }
+    }
+
+    private void disposeBatch(List<T> dtos) throws InterruptedException {
+        // 生产数据
+        for (T t : dtos) {
+            offer(t);
+        }
+        // 发送一轮结束信息
+        parkConsumer();
+        // 等待子线程结束
+        await();
+        // 判断子线程是否正常结束
+        if (basket.isExcuteError()) {
+            throw new StatusException("处理失败,线程异常");
+        }
+    }
+
+    /**
+     * 出异常后修改标识
+     */
+    private void endConsumerAsError() {
+        basket.setExcuteError(true);
+    }
+
+    /**
+     * 正常结束消费者
+     *
+     * @throws InterruptedException
+     */
+    private void endConsumer() throws InterruptedException {
+        int count = basket.getConsumerCount();
+        EndObject eo = new EndObject();
+        for (int i = 0; i < count; i++) {
+            basket.offer(eo);
+        }
+
+    }
+
+    /**
+     * 正常暂停消费者
+     *
+     * @throws InterruptedException
+     */
+    private void parkConsumer() throws InterruptedException {
+        int count = basket.getConsumerCount();
+        ParkObject eo = new ParkObject();
+        for (int i = 0; i < count; i++) {
+            basket.offer(eo);
+        }
+
+    }
+
+    /**
+     * 正常唤醒消费者
+     *
+     * @throws InterruptedException
+     */
+    private void unParkConsumer() {
+        for (BatchConsumer<T> c : consumers) {
+            LockSupport.unpark(c);
+        }
+    }
+
+    /**
+     * 生产数据
+     *
+     * @param ob
+     * @throws InterruptedException
+     */
+    private void offer(Object ob) throws InterruptedException {
+        basket.offer(ob);
+    }
+
+    /**
+     * 等待所有消费者结束
+     *
+     * @throws InterruptedException
+     */
+    private void await() throws InterruptedException {
+        basket.await();
+    }
 
 }

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels