Browse Source

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	parth5/parth5/src/app/views/tapp/tabs/tabs.module.ts
lizeyu 7 months ago
parent
commit
19dbd39d09
22 changed files with 549 additions and 48 deletions
  1. 7 0
      parth5/parth5/src/app/app.scss
  2. 17 15
      parth5/parth5/src/app/views/pages/examtest/examtest.component.html
  3. 12 3
      parth5/parth5/src/app/views/pages/examtest/examtest.component.scss
  4. 11 3
      parth5/parth5/src/app/views/pages/examtest/show/card/card.component.html
  5. 5 0
      parth5/parth5/src/app/views/pages/examtest/show/card/card.component.scss
  6. 4 2
      parth5/parth5/src/app/views/pages/examtest/show/card/card.component.ts
  7. 22 0
      parth5/parth5/src/app/views/pages/examtest/show/result/result.component.html
  8. 15 0
      parth5/parth5/src/app/views/pages/examtest/show/result/result.component.scss
  9. 70 0
      parth5/parth5/src/app/views/pages/examtest/show/result/result.component.ts
  10. 25 23
      parth5/parth5/src/app/views/pages/examtest/show/show.component.html
  11. 3 0
      parth5/parth5/src/app/views/pages/examtest/show/show.component.scss
  12. 35 2
      parth5/parth5/src/app/views/pages/examtest/show/show.component.ts
  13. 4 0
      parth5/parth5/src/app/views/tapp/tabs/tabs.module.ts
  14. 12 0
      pom.xml
  15. 85 0
      src/main/java/com/ghsc/partybuild/controller/wechat/MyWxCpConfig.java
  16. 42 0
      src/main/java/com/ghsc/partybuild/controller/wechat/WxCpController.java
  17. 12 0
      src/main/java/com/ghsc/partybuild/service/wechat/WechatCpService.java
  18. 48 0
      src/main/java/com/ghsc/partybuild/service/wechat/WechatCpServiceImpl.java
  19. 1 0
      src/main/java/com/ghsc/partybuild/shiro/ShiroConfiguration.java
  20. 34 0
      src/main/java/com/ghsc/partybuild/util/UrlUtils.java
  21. 8 0
      src/main/resources/application.yml
  22. 77 0
      src/main/resources/static/doc/template/党建知识题目模板.txt

+ 7 - 0
parth5/parth5/src/app/app.scss

@@ -524,3 +524,10 @@ ion-list {
     left: 0;
   }
 }
+.testresult-modal {
+  .modal-wrapper {
+    height: 45% !important;
+    width: 80% !important;
+    border-radius: 8px;
+  }
+}

+ 17 - 15
parth5/parth5/src/app/views/pages/examtest/examtest.component.html

@@ -18,31 +18,31 @@
           <ion-icon name="grid-outline"></ion-icon>
         </div>
       </ion-item>
-      <ion-item button detail="true" detail-icon="close-outline"  *ngFor="let qt of selectLibaryList;" (click)="remove(qt,false)">
+      <ion-item class="select-libary" detail="true" detail-icon="close-outline" *ngFor="let qt of selectLibaryList;"
+                (click)="remove(qt,false)">
         <ion-label>
           <h3> {{ qt.name }}</h3>
         </ion-label>
       </ion-item>
-    <!--  <span *ngFor="let qt of selectLibaryList;" style="text-wrap: nowrap;">
-                <ion-col size="2">  {{ qt.name }}
-                  <ion-icon name="close-outline" (click)="remove(qt,false)"></ion-icon></ion-col>
-      </span>-->
       <ion-item mode="md">
         <ion-label>选择题型
           <span class="danger">*</span>
         </ion-label>
       </ion-item>
-      <span *ngFor="let qt of questionTypeList;" style="text-wrap: nowrap;">
-            <ion-col size="1">
-              <ion-checkbox color="danger" labelPlacement="end"
-                            (click)="qt.checked=!qt.checked"></ion-checkbox></ion-col>
-            <ion-col size="2">  {{ qt.name }}</ion-col>
-      </span>
+      <div style="display: flex;justify-content: space-evenly">
+        <ng-container *ngFor="let qt of questionTypeList;">
+        <span>
+          <ion-checkbox color="danger" labelPlacement="end"
+                        (click)="qt.checked=!qt.checked"></ion-checkbox>
+          {{ qt.name }}
+        </span>
+        </ng-container>
+      </div>
       <ion-item mode="md">
         <ion-label>题目数量
           <span class="danger">*</span>
         </ion-label>
-        <ion-input type="number" [(ngModel)]="questionnumber" name ="questionnumber" required></ion-input>
+        <ion-input type="number" [(ngModel)]="questionnumber" name="questionnumber" required></ion-input>
       </ion-item>
       <div style="display:flex;align-items: center;margin-top: 10px">
         <ion-icon name="alert-outline" size="small"></ion-icon>
@@ -84,10 +84,12 @@
       </ion-item>
       <div style="margin-left: 10px;">
         <ion-label>已选题库:</ion-label>
-        <span *ngFor="let qt of bakLibaryList;" style="text-wrap: nowrap;">
-                <ion-col size="2">  {{ qt.name }}
-                  <ion-icon name="close-outline" (click)="remove(qt,true)"></ion-icon></ion-col>
+        <div style="display: flex;flex-flow: wrap;">
+        <span *ngFor="let qt of bakLibaryList;" class="item-subtitle-type" style="text-wrap: nowrap;">
+               {{ qt.name }}
+                  <ion-icon name="close-outline" (click)="remove(qt,true)"></ion-icon>
           </span>
+        </div>
       </div>
     </ion-list>
   </ion-content>

+ 12 - 3
parth5/parth5/src/app/views/pages/examtest/examtest.component.scss

@@ -40,6 +40,15 @@
     border-bottom-left-radius: 8px;
   }
 }
-
-
-
+.item-subtitle-type {
+  border: 1px solid #da2c36;
+  color: #da2c36;
+  background-color: white;
+  border-radius: 3px;
+  padding: 1px 5px 0px 5px;
+  margin: 5px;
+}
+.select-libary{
+  border: 1px solid #efeaea;    --min-height: 25px;
+  margin: 10px;--inner-padding-bottom:0px;--inner-padding-top:0px
+}

+ 11 - 3
parth5/parth5/src/app/views/pages/examtest/show/card/card.component.html

@@ -1,10 +1,15 @@
-<ion-header color="danger" style="text-align: center">
-  共{{ doneQuestionNumber + unNumber }}道题
+<ion-header class="header-theme2" id="main-content">
+  <ion-toolbar>
+    <ion-title>
+      共{{ doneQuestionNumber + unNumber }}道题
+    </ion-title>
+  </ion-toolbar>
 </ion-header>
 <ion-content>
   <ion-list>
     <div class="card-body">
       <div class="card-total">
+        客观题:
         <div>
           <ion-icon name="ellipse-outline" style="color: deepskyblue;vertical-align: middle;"></ion-icon>
           正确{{ rightNumber }}道
@@ -23,7 +28,10 @@
         <div class="question-num">
           <div *ngFor="let ques of it.question"
                (click)="selectQuestion(ques)"
-               [ngClass]="{'rightColor':ques.isRight,'unColor':ques.isRight==null,'errColor':ques.isRight==false}">
+               [ngClass]="{'rightColor':ques.isRight
+               ,'unColor':ques.isAnswer==null
+               ,'errColor':ques.isRight==false
+               ,'answerColor':ques.isAnswer&&ques.isRight==null}">
             {{ ques.order }}</div>
         </div>
       </div>

+ 5 - 0
parth5/parth5/src/app/views/pages/examtest/show/card/card.component.scss

@@ -4,6 +4,8 @@
 .card-total{
   display: flex;
   flex-wrap: nowrap;
+  border-bottom: 1px solid #5e5c5c52;
+  padding-bottom: 10px;
 
   div{
     margin-right: 10px;
@@ -40,3 +42,6 @@
 .errColor{
   border: 2px solid red !important;
 }
+.answerColor{
+  border: 2px solid #00ff00 !important;
+}

+ 4 - 2
parth5/parth5/src/app/views/pages/examtest/show/card/card.component.ts

@@ -24,8 +24,6 @@ export class CardComponent implements OnInit {
   ngOnInit() {
     this.autoReaderPaper().then(() => {
       this.getDoneNum();
-      this.unNumber = this.testQuestionList.length - this.doneQuestionNumber;
-      this.errNumber = this.testQuestionList.length - this.unNumber - this.rightNumber;
 
       this.getQuestionTypeList().then(()=>{
         this.questionTypeList = this.questionTypeList.sort((a, b) => a.diplayOrder - b.diplayOrder);
@@ -64,6 +62,10 @@ export class CardComponent implements OnInit {
           this.rightNumber = data.item.filter(function (x) {
             return x.isRight;
           }).length;
+          this.errNumber = data.item.filter(function (x) {
+            return x.isRight==false;
+          }).length;
+          this.unNumber =this.testQuestionList.length - data.item.filter(x => x.isAnswer).length;
         }
         resolve();
       });

+ 22 - 0
parth5/parth5/src/app/views/pages/examtest/show/result/result.component.html

@@ -0,0 +1,22 @@
+<ion-header class="header-theme2" id="main-content">
+  <ion-toolbar>
+    <ion-title>
+      练习结果
+    </ion-title>
+  </ion-toolbar>
+</ion-header>
+<ion-content>
+  <div class="card" style="margin-top: 10px;padding-bottom: 20px;">
+    <div class="result-body">
+      <div class="result-title">你已结束本次个人练习 </div>
+      <div class="result-correctrate">客观题的正确率:{{rightRate}}% </div>
+      <div class="result-questionnum">客观题正确题数:{{rightNumber}}题 </div>
+      <div class="result-questionnum">客观题错误题数:{{errNumber}}题 </div>
+      <div class="result-questionnum">客观题总题数:{{subjectiveCount}}题</div>
+      <div>
+        <ion-button size="small" slot="end" *ngIf="rightRate<100" (click)="dismiss(1);">查看错题</ion-button>
+        <ion-button size="small" slot="end" color="warning" (click)="dismiss(2)">结束考试</ion-button>
+      </div>
+    </div>
+  </div>
+</ion-content>

+ 15 - 0
parth5/parth5/src/app/views/pages/examtest/show/result/result.component.scss

@@ -0,0 +1,15 @@
+.result-body{
+  display: flex;
+  flex-flow: column;
+  align-content: center;
+  justify-items: center;
+  font-size: 15px;
+  text-align: center;
+}
+.result-body >div{
+  padding: 10px 0px;
+}
+.result-body >.result-title{
+  font-size: 18px;
+  font-weight: bold;
+}

+ 70 - 0
parth5/parth5/src/app/views/pages/examtest/show/result/result.component.ts

@@ -0,0 +1,70 @@
+import {Component, Input, OnInit} from '@angular/core';
+import {ConfigService, RequsetData} from "../../../../../service/config.service";
+import {ModalController} from "@ionic/angular";
+
+@Component({
+  selector: 'app-result',
+  templateUrl: './result.component.html',
+  styleUrls: ['./result.component.scss'],
+})
+export class ResultComponent implements OnInit {
+
+  @Input()
+  testQuestionList = [];
+  rightNumber = 0;
+  doneQuestionNumber = 0;
+  unNumber = 0;
+  errNumber = 0;
+  rightRate = 0;
+  subjectiveCount = 0;
+
+  constructor(private configService: ConfigService, public modalController: ModalController) {
+  }
+
+  ngOnInit() {
+    this.autoReaderPaper().then(() => {
+      this.getDoneNum();
+
+      this.subjectiveCount = this.testQuestionList.filter(function (type) {
+        return type.OBJECTIVE_TYPE == 2
+      }).length;
+      this.rightRate = this.rightNumber>0?Math.round(this.rightNumber / (this.rightNumber + this.errNumber) * 100):0;
+    })
+  }
+
+  //阅卷
+  autoReaderPaper() {
+    let pm = new Promise((resolve, reject) => {
+      this.configService.HttpPostRomote('/api/examtest/autoReaderPaper', {userQuestionList: this.testQuestionList,}).subscribe((data: RequsetData) => {
+        if (data.success) {
+          this.testQuestionList = data.item;
+          this.rightNumber = data.item.filter(function (x) {
+            return x.isRight;
+          }).length;
+          this.errNumber = data.item.filter(function (x) {
+            return x.isRight == false;
+          }).length;
+          this.unNumber = this.testQuestionList.length - data.item.filter(x => x.isAnswer).length;
+        }
+        resolve();
+      });
+    });
+    return pm;
+  }
+
+  getDoneNum() {
+    this.doneQuestionNumber = this.testQuestionList.filter(function (ques) {
+      return (ques.useranswer != null && ques.useranswer != "") || ques.ANSWERS.filter(function (ans) {
+        return ans.useranswer != null && ans.useranswer != ""
+      }).length > 0;
+    }).length;
+  }
+
+  dismiss(action) {
+    this.modalController.dismiss({
+      'dismissed': true,
+      'action': action,
+      'testQuestionList': this.testQuestionList
+    });
+  }
+}

+ 25 - 23
parth5/parth5/src/app/views/pages/examtest/show/show.component.html

@@ -13,9 +13,6 @@
       <div [ngSwitch]="ques.NAME">
         <div *ngSwitchCase="'单选题'">
           <span class="ion-text-wrap">{{ ques.order }}.({{ ques.NAME }}){{ ques.CONTENT }}</span>
-          <div style="width: 100%;text-align: right;color:red;" (click)="ques.showAnswer=true">
-            <ion-icon name="checkbox-outline" size="large"></ion-icon>
-          </div>
           <div [class]="{'isright-answer':ans.ISRIGHT&&ques.showAnswer}"
                *ngFor="let ans of ques.ANSWERS;let $index = index">
             <input name="rd_p_{{ques.row_num}}_{{$index}}" type="radio" [(ngModel)]="ques.useranswer"
@@ -26,25 +23,24 @@
           </div>
         </div>
         <div *ngSwitchCase="'填空题'">
-          <div style="width: 100%;text-align: right;color:red;" (click)="ques.showAnswer=true">
-            <ion-icon name="checkbox-outline" size="large"></ion-icon>
-          </div>
           <div class="input-container">
             {{ ques.order }}.({{ ques.NAME }})
             <ng-container *ngFor="let word of ques.CONTENT.split('___'); let j = index">
               {{ word }}
               <!-- 如果当前是句子中的最后一个词前的下划线,则不添加输入框 -->
               <ng-container *ngIf="j < ques.CONTENT.split('___').length - 1">
-                <ion-input [(ngModel)]="ques.ANSWERS[j].useranswer" placeholder="请输入" (ngModelChange)="answer(ques)"></ion-input>
+                <ion-input [(ngModel)]="ques.ANSWERS[j].useranswer" placeholder="请输入"
+                           (ngModelChange)="answer(ques)"></ion-input>
               </ng-container>
             </ng-container>
           </div>
         </div>
         <div *ngSwitchCase="'判断题'">
-          <span class="ion-text-wrap">{{ ques.order}}.({{ ques.NAME }}){{ ques.CONTENT }}</span>
+          <span class="ion-text-wrap">{{ ques.order }}.({{ ques.NAME }}){{ ques.CONTENT }}</span>
           <div [class]="{'isright-answer':ans.ISRIGHT&&ques.showAnswer}"
                *ngFor="let ans of ques.ANSWERS;let $index = index">
-            <input name="rd_p_{{ques.row_num}}_{{$index}}" type="radio" [(ngModel)]="ques.useranswer" (ngModelChange)="answer(ques)"
+            <input name="rd_p_{{ques.row_num}}_{{$index}}" type="radio" [(ngModel)]="ques.useranswer"
+                   (ngModelChange)="answer(ques)"
                    value="{{ans.PROVID_ANSWER_ID}}"
                    class="with-gap" id="rd_p_{{ques.row_num}}_{{$index}}"/>
             <label for="rd_p_{{ques.row_num}}_{{$index}}">{{ options($index) }}、{{ ans.ANSWER_NAME }}</label>
@@ -61,12 +57,10 @@
         </div>
         <div *ngSwitchCase="'多选题'">
           <span class="ion-text-wrap">{{ ques.order }}.({{ ques.NAME }}){{ ques.CONTENT }}</span>
-          <div style="width: 100%;text-align: right;color:red;" (click)="ques.showAnswer=true">
-            <ion-icon name="checkbox-outline" size="large"></ion-icon>
-          </div>
           <div [class]="{'isright-answer':ans.ISRIGHT&&ques.showAnswer}"
                *ngFor="let ans of ques.ANSWERS;let $index = index">
-            <input name="ckb_{{ques.row_num}}_{{$index}}" type="checkbox" [(ngModel)]="ans.useranswer" (ngModelChange)="answer(ques)"
+            <input name="ckb_{{ques.row_num}}_{{$index}}" type="checkbox" [(ngModel)]="ans.useranswer"
+                   (ngModelChange)="answer(ques)"
                    value="{{ans.PROVID_ANSWER_ID}}"
                    class="with-gap" id="ckb_{{ques.row_num}}_{{$index}}"/>
             <label for="ckb_{{ques.row_num}}_{{$index}}">{{ options($index) }}、{{ ans.ANSWER_NAME }}</label>
@@ -81,15 +75,23 @@
 </ion-content>
 <ion-footer>
   <ion-toolbar style="display: flex;align-items: center">
-    <ion-icon (click)="presentModal()" name="reorder-four-outline" style="color: red;vertical-align: middle;"
-              size="large"
-              aria-label="Favorite"></ion-icon>
-    <span>已答{{ answerCount }}/{{ questionCount }}</span>
-    <ion-button size="small" slot="end" *ngIf="order>1" (click)="order=order-1;changeView();">
-      上一题
-    </ion-button>
-    <ion-button size="small" slot="end" *ngIf="questionCount>order" (click)="order=order+1;changeView();">
-      下一题
-    </ion-button>
+    <ng-container *ngIf="!showErrorQuestion">
+      <ion-icon (click)="presentModal()" name="reorder-four-outline" style="color: red;vertical-align: middle;"
+                size="large"
+                aria-label="Favorite"></ion-icon>
+      <span>已答{{ answerCount }}/{{ questionCount }}</span>
+      <ion-icon slot="end" name="checkbox-outline" size="large" (click)="showAnswer()"></ion-icon>
+      <ion-button size="small" slot="end" *ngIf="order>1" (click)="order=order-1;changeView();">
+        上一题
+      </ion-button>
+      <ion-button size="small" slot="end" *ngIf="questionCount>order" (click)="order=order+1;changeView();">
+        下一题
+      </ion-button>
+      <ion-button size="small" slot="end" color="warning" (click)="resultModal()">结束练习</ion-button>
+    </ng-container>
+    <ng-container *ngIf="showErrorQuestion">
+      <ion-icon slot="end" name="checkbox-outline" size="large" (click)="showAnswer()"></ion-icon>
+      <ion-button size="small" slot="end" color="warning" (click)="close()">退出练习</ion-button>
+    </ng-container>
   </ion-toolbar>
 </ion-footer>

+ 3 - 0
parth5/parth5/src/app/views/pages/examtest/show/show.component.scss

@@ -4,6 +4,9 @@
 
 .question-body {
   margin: 10px;
+  div{
+    margin-top: 10px;
+  }
 }
 
 .question-body ion-input {

+ 35 - 2
parth5/parth5/src/app/views/pages/examtest/show/show.component.ts

@@ -3,6 +3,7 @@ import {ConfigService, RequsetData} from "../../../../service/config.service";
 import {AlertController, ModalController, MenuController} from "@ionic/angular";
 import {ActivatedRoute, Router} from "@angular/router";
 import {CardComponent} from "./card/card.component";
+import {ResultComponent} from "./result/result.component";
 
 @Component({
   selector: 'app-show',
@@ -18,6 +19,7 @@ export class ShowComponent implements OnInit {
   curQuestionList = [];
   questionCount = 0;
   answerCount = 0;
+  showErrorQuestion =false;
 
 
   constructor(private modalController: ModalController, public alertController: AlertController, private menu: MenuController, private router: Router, private configService: ConfigService, private routeInfo: ActivatedRoute) {
@@ -72,8 +74,17 @@ export class ShowComponent implements OnInit {
     this.answerCount = this.testQuestionList.filter(x => x.isAnswer).length;
   }
 
-  async presentModal() {
+  showAnswer() {
+    this.curQuestionList.forEach(x => x.showAnswer = true);
+  }
+  close(){
+    this.router.navigate(['../'], {
+      relativeTo: this.routeInfo,
+      queryParams: {id: 'id', random: Math.random()}
+    });
+  }
 
+  async presentModal() {
     const modal = await this.modalController.create({
       component: CardComponent,
       cssClass: 'examtest-modal',
@@ -85,9 +96,31 @@ export class ShowComponent implements OnInit {
 
     const {data} = await modal.onWillDismiss();
 
-    if (data.dismissed) {
+    if (data!=undefined&&data.dismissed) {
       this.order = data.order;
       this.changeView();
     }
   }
+
+  async resultModal() {
+    const modal = await this.modalController.create({
+      component: ResultComponent,
+      cssClass: 'testresult-modal',
+      componentProps: {
+        'testQuestionList': this.testQuestionList
+      }
+    });
+    await modal.present();
+
+    const {data} = await modal.onWillDismiss();
+
+    if (data!=undefined&&data.dismissed) {
+      if (data.action == 1) {
+        this.showErrorQuestion=true;
+        this.curQuestionList = data.testQuestionList.filter(x => x.OBJECTIVE_TYPE == 2 && x.isRight == false);
+      } else {
+        this.close();
+      }
+    }
+  }
 }

+ 4 - 0
parth5/parth5/src/app/views/tapp/tabs/tabs.module.ts

@@ -58,6 +58,7 @@ import {routes as MainRoutes} from "../tab-main/tab-main.route";
 import {routes as StudyRoutes} from "../tab-study/tab-study.route";
 import {routes as UserRoutes} from "../tab-user/tab-user.route";
 import {CardComponent} from "../../pages/examtest/show/card/card.component";
+import {ResultComponent} from "../../pages/examtest/show/result/result.component";
 
 import { ExamtestComponent} from "../../pages/examtest/examtest.component";
 import {DzzfcListComponent} from "../../pages/propagandawork/dzzfc/list.component";
@@ -92,6 +93,9 @@ const pageComponents = [
   RecordComponent,
   PartyUserRewardComponent,
   ExamtestComponent,
+  ExamtestShowComponent,
+  CardComponent,
+  ResultComponent,
   DzzfcListComponent,
   DzzfcEditComponent,
   DzzfcDetailComponent

+ 12 - 0
pom.xml

@@ -212,6 +212,18 @@
             <artifactId>fastjson</artifactId>
             <version>1.2.47</version>
         </dependency>
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-common</artifactId>
+            <version>4.6.0</version>
+        </dependency>
+        <!-- 企业微信 -->
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-cp</artifactId>
+            <version>4.6.0</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 85 - 0
src/main/java/com/ghsc/partybuild/controller/wechat/MyWxCpConfig.java

@@ -0,0 +1,85 @@
+package com.ghsc.partybuild.controller.wechat;
+
+import me.chanjar.weixin.cp.api.WxCpExternalContactService;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.impl.WxCpExternalContactServiceImpl;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.beans.factory.annotation.Value;
+
+@Configuration
+@ConditionalOnClass(WxCpService.class)
+public class MyWxCpConfig {
+    @Value("${wx.cp.configs.corpsecret}")
+    private String secret;
+    @Value("${wx.cp.configs.corpId}")
+    private String corpId;
+    @Value("${wx.cp.configs.agentId}")
+    private Integer agentId;
+    @Value("${wx.cp.configs.redirectUrl}")
+    private String redirectUrl;
+   /* @Value("${wx.cp.configs.chatId}")
+    private String chatId;*/
+    private static String APP_AGENT_PREFIX = "wx";
+
+    /*@Resource
+    private RedisService redisService;*/
+
+    @Bean
+    public WxCpService getWxCpService() {
+        WxCpService wxCpService = new WxCpServiceImpl();
+        WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
+        config.setAgentId(agentId);
+        config.setCorpSecret(secret);
+        config.setCorpId(corpId);
+        /*resetTokenAndJsApi(wxCpService, config);*/
+        wxCpService.setWxCpConfigStorage(config);
+        return wxCpService;
+    }
+
+    @Bean
+    public WxCpExternalContactService getwxCpExternalContactService() {
+        WxCpService wxCpService = new WxCpServiceImpl();
+        WxCpExternalContactService wxCpExternalContactService = new WxCpExternalContactServiceImpl(wxCpService);
+        WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
+        config.setAgentId(agentId);
+        config.setCorpSecret(secret);
+        config.setCorpId(corpId);
+        /*resetTokenAndJsApi(wxCpService, config);*/
+        wxCpService.setWxCpConfigStorage(config);
+        return wxCpExternalContactService;
+    }
+
+
+    /*public void resetTokenAndJsApi(WxCpService wxCpService, WxCpDefaultConfigImpl wxCpDefaultConfig) {
+        wxCpService.setWxCpConfigStorage(wxCpDefaultConfig);
+        String wxAccessToken = APP_AGENT_PREFIX + this.agentId;
+        String json = redisService.getCacheObject(wxAccessToken);
+        if (!StringUtils.isEmpty(json)) {
+            wxCpDefaultConfig = JSON.parseObject(json, WxCpDefaultConfigImpl.class);
+        }
+        if (wxCpDefaultConfig.isAccessTokenExpired()) {
+            try {
+                String accessToken = null;
+                accessToken = wxCpService.getAccessToken(false);
+                wxCpDefaultConfig.setAccessToken(accessToken);
+            } catch (WxErrorException e) {
+                e.printStackTrace();
+            }
+        }
+        if(wxCpDefaultConfig.isJsapiTicketExpired()){
+            String jsApi = null;
+            try {
+                jsApi = wxCpService.getJsapiTicket();
+                wxCpDefaultConfig.setJsapiTicket(jsApi);
+            } catch (WxErrorException e) {
+                e.printStackTrace();
+            }
+        }
+        redisService.setCacheObject(wxAccessToken, JSON.toJSONString(wxCpDefaultConfig));
+    }*/
+}
+

+ 42 - 0
src/main/java/com/ghsc/partybuild/controller/wechat/WxCpController.java

@@ -0,0 +1,42 @@
+package com.ghsc.partybuild.controller.wechat;
+
+import com.ghsc.partybuild.filter.exception.BaseResponse;
+import com.ghsc.partybuild.filter.exception.RespGenerstor;
+import com.ghsc.partybuild.service.wechat.WechatCpService;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+/**
+ * 企业微信对接接口
+ */
+@RestController
+@RequestMapping(value = "/wxapi/cp")
+@Slf4j
+public class WxCpController {
+
+    @Autowired
+    private WechatCpService wechatCpService;
+
+    @GetMapping("getOAuthUrl")
+    public BaseResponse<String> getOAuthUrl(String redirectUrl) throws UnsupportedEncodingException {
+
+        String url = wechatCpService.getOAuthUrl(URLDecoder.decode(redirectUrl, "UTF-8"));
+
+        log.info("getOAuthUrl:" + url);
+
+        return RespGenerstor.success(url);
+    }
+
+    @GetMapping("getWxCpUserId")
+    public BaseResponse<String> getWxCpUserId(String code) throws WxErrorException {
+        return RespGenerstor.success(wechatCpService.getWxCpUserId(code));
+    }
+
+}

+ 12 - 0
src/main/java/com/ghsc/partybuild/service/wechat/WechatCpService.java

@@ -0,0 +1,12 @@
+package com.ghsc.partybuild.service.wechat;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * 企业微信
+ */
+public interface WechatCpService {
+    String getOAuthUrl(String redirectUrl);
+
+    String getWxCpUserId(String code) throws WxErrorException;
+}

+ 48 - 0
src/main/java/com/ghsc/partybuild/service/wechat/WechatCpServiceImpl.java

@@ -0,0 +1,48 @@
+package com.ghsc.partybuild.service.wechat;
+
+import com.ghsc.partybuild.util.DateUtils;
+import com.ghsc.partybuild.util.StringUtils;
+import com.ghsc.partybuild.util.UrlUtils;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.WxCpChatService;
+import me.chanjar.weixin.cp.api.WxCpService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 企业微信
+ */
+@Service("WechatCpService")
+public class WechatCpServiceImpl implements WechatCpService {
+    @Autowired
+    private StringUtils stringUtils;
+
+    @Autowired
+    private DateUtils dateUtils;
+
+    @Autowired
+    private UrlUtils urlUtils;
+
+    @Autowired
+    private WxCpService wxCpService;
+
+    private final Logger logger;
+
+    public WechatCpServiceImpl() {
+        logger = LoggerFactory.getLogger(this.getClass());
+    }
+
+    public String getOAuthUrl(String redirectUrl) {
+        return wxCpService.getOauth2Service().buildAuthorizationUrl(redirectUrl, "");
+    }
+
+    @Override
+    public String getWxCpUserId(String code) throws WxErrorException {
+        String userId = wxCpService.getOauth2Service().getAuthUserInfo(code).getUserId();
+        logger.info("getWxCpUserId:code=" + code + ",userId=" + userId);
+        return userId;
+    }
+
+}

+ 1 - 0
src/main/java/com/ghsc/partybuild/shiro/ShiroConfiguration.java

@@ -119,6 +119,7 @@ public class ShiroConfiguration {
         filterChainDefinitionMap.put("/wxapi/pay/OAuth", "anon");
         filterChainDefinitionMap.put("/wxapi/pay/getOpenID", "anon");
         filterChainDefinitionMap.put("/appApi/fileMgr/showDoc/**", "anon");
+        filterChainDefinitionMap.put("/wxapi/cp/**", "anon");
         filterChainDefinitionMap.put("/**", "authc");
 
         // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面

+ 34 - 0
src/main/java/com/ghsc/partybuild/util/UrlUtils.java

@@ -0,0 +1,34 @@
+package com.ghsc.partybuild.util;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Component
+public class UrlUtils {
+
+    @Autowired
+    private StringUtils stringUtils;
+
+    public String getUrl(String url, Map<String, String> params) {
+        if (stringUtils.IsNullOrEmpty(url))
+            return "";
+
+        String fullUrl = url;
+
+        if (fullUrl.indexOf("?") < 0)
+            fullUrl = fullUrl + "?";
+
+        for (Map.Entry<String, String> param : params.entrySet()) {
+            if (!fullUrl.endsWith("?"))
+                fullUrl += "&";
+
+            fullUrl += (param.getKey() + "=" + param.getValue());
+        }
+
+
+        return fullUrl;
+    }
+
+}

+ 8 - 0
src/main/resources/application.yml

@@ -158,3 +158,11 @@ wechat-pay-config:
   #微信授权回调地址
   redirectURI: 'http://www.bowintek.com/hbPartyMobile/index.html'
   accessScope: 'snsapi_base'
+wx:
+  cp:
+    configs:
+      corpId: wwb4941d05cf8473e4
+      corpsecret: corpsecretid
+      agentId: 1000004
+      redirectUrl: www.bowintek.com/ghsc/mobile
+

+ 77 - 0
src/main/resources/static/doc/template/党建知识题目模板.txt

@@ -0,0 +1,77 @@
+//模板说明
+//凡是用//开头的行都是注释行,不会作为试题内容导入到题库中
+//每个试题间用空行隔开,作为格式用的符号如":","."是不分差距半角全角的,半角全角都有效
+
+//试题的开头行是题干,前面不能有注释行并列在上面,否则试题都无效
+
+//题干、题型、答案、题目解析是一个试题的必须项
+
+//在题干末尾须要备注提报单位名称
+
+//可用的题型有:单选、多选、判断、填空、简答
+
+//题目的难度有:难、中、易
+
+//填空题的空位处用三个"_",即"___"来表示,另外有多少个填空就有多少个答案,每个答案必须用“;”隔开,否则失效。
+
+//下面是一个最完整的例子,务必参考例子格式进行编写,否则无效。
+
+
+党内法规体系中,最高层次的法规是___?【出题:广州站】
+A.准则
+B.条例
+C.党章
+D.规定
+答案:C
+题型:单选
+题目难度:中
+题目解析:《中国共产党党内法规制定条例》第一章:“党章是最根本的党内法规,是制定其他党内法规的基础和依据。”
+
+党的十八大报告提出的建设“三型”马克思主义执政党是___?【出题:广州站】
+A.学习型、服务型、完善型
+B.学习型、完善型、创新型
+C.学习型、服务型、创新型
+D.学习型、完善型、创新型
+答案:C
+题型:单选
+题目难度:中
+题目解析:党的十八大报告提出,全党要增强紧迫感和责任感,牢牢把握加强党的执政能力建设、先进性和纯洁性建设这条主线,坚持解放思想、改革创新,坚持党要管党、从严治党,全面加强党的思想建设、组织建设、作风建设、反腐倡廉建设、制度建设,增强自我净化、自我完善、自我革新、自我提高能力,建设学习型、服务型、创新型的马克思主义执政党,确保党始终成为中国特色社会主义事业的坚强领导核心。
+
+
+党员违反国家法律法规,违反企事业单位或者其他社会组织规章制度受到其他纪律处分,应当追究党纪责任的,党组织在对有关方面___进行核实后,依照《中国共产党纪律处分条例》规定给予党纪处分或者组织处理。【出题:广州站】
+A.认定的事实
+B.认定的性质
+C.认定的情节
+D.认定的细节
+E.认定的梗概
+答案:A,B,C
+题型:多选
+题目难度:中
+题目解析:《中国共产党纪律处分条例》第四章第三十三条规定:“党员违反国家法律法规,违反企事业单位或者其他社会组织的规章制度受到其他纪律处分,应当追究党纪责任的,党组织在对有关方面认定的事实、性质和情节进行核实后,依照规定给予党纪处分或者组织处理。
+
+预备党员预备期满后,党组织经过考察认为其不履行党员义务,不具备党员条件的,应当取消预备党员资格。【出题:广州站】
+答案:正确
+题型:判断
+题目难度:中
+题目解析:《中国共产党发展党员工作细则》第五章第三十二条规定:“预备党员预备期满,党支部应当及时讨论其能否转为正式党员。认真履行党员义务、具备党员条件的,应当按期转为正式党员;需要继续考察和教育的,可以延长一次预备期,延长时间不能少于半年,最长不超过一年;不履行党员义务、不具备党员条件的,应当取消其预备党员资格。”
+
+
+党章总纲指出:___是我们党执政兴国的第一要务。【出题:广州站】
+答案:发展
+题型:填空
+题目难度:中
+题目解析:《中国共产党章程》总纲指出,发展是我们党执政兴国的第一要务。
+
+对违纪后下落不明的党员,下落不明时间超过___个月的,党组织应当按照党章规定对其予以___。【出题:广州站】
+答案:六;除名
+题型:填空
+题目难度:中
+题目解析:《中国共产党纪律处分条例》第五章第三十五条规定:“对违纪后下落不明的党员,应当区别情况作出处理: (一)对有严重违纪行为,应当给予开除党籍处分的,党组织应当作出决定,开除其党籍;(二)除前项规定的情况外,下落不明时间超过六个月的,党组织应当按照党章规定对其予以除名。”
+
+请写出入党誓词?【出题:广州站】
+答案:我志愿加入中国共产党,拥护党的纲领,遵守党的章程,履行党员义务,执行党的决定,严守党的纪律,保守党的秘密,对党忠诚,积极工作,为共产主义奋斗终身,随时准备为党和人民牺牲一切,永不叛党。
+题型:简答
+题目难度:中
+题目解析:《中国共产党章程》第一章第六条:“预备党员必须面向党旗进行入党宣誓。誓词如下:我志愿加入中国共产党,拥护党的纲领,遵守党的章程,履行党员义务,执行党的决定,严守党的纪律,保守党的秘密,对党忠诚,积极工作,为共产主义奋斗终身,随时准备为党和人民牺牲一切,永不叛党。”
+
+