فهرست منبع

Merge branch 'master' of http://114.55.67.98:8070/Natural_p1/zjugis_OA

songxy 1 سال پیش
والد
کامیت
401c8521f2

+ 2 - 0
client/package.json

@@ -55,6 +55,7 @@
     "echarts-wordcloud": "^2.1.0",
     "element-plus": "2.3.14",
     "fast-xml-parser": "^4.3.0",
+    "file-saver": "^2.0.5",
     "highlight.js": "^11.8.0",
     "intro.js": "^7.2.0",
     "js-base64": "^3.7.5",
@@ -79,6 +80,7 @@
     "vue-types": "^5.1.1",
     "vuedraggable": "^4.1.0",
     "web-storage-cache": "^1.1.1",
+    "xlsx": "^0.18.5",
     "xml-js": "^1.6.11"
   },
   "devDependencies": {

+ 64 - 0
client/src/components/ExportToExcel/index.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-button type="primary" @click="exportToExcel">
+    <img src="@/assets/imgs/OA/open.png" class="mr-8px" alt="" />
+    导出
+  </el-button>
+</template>
+<script setup lang="ts">
+/**
+ * @description 导出为excel
+ */
+import * as XLSX from 'xlsx'
+import { saveAs } from 'file-saver'
+interface IProp {
+  data: any[][] // 数据 [[表头,表头,表头],[数据,数据,数据]]
+  fileName?: string // 文件名
+  mergeRanges?: {
+    s: { r: number; c: number } // 合并的起始单元格
+    e: { r: number; c: number } // 合并的结束单元格
+  }[] // 合并单元格列表
+  colsWidth?: { wch: number }[] // 列宽
+}
+// 定义组件props
+const props = defineProps<IProp>()
+
+// 导出Excel函数
+const exportToExcel = () => {
+  // 从props获取数据
+  const data = props.data
+
+  // 创建一个工作簿
+  const wb = XLSX.utils.book_new()
+
+  // 创建一个工作表
+  const ws = XLSX.utils.aoa_to_sheet(data)
+
+  // 合并表头的单元格,定义多个合并范围
+  ws['!merges'] = props.mergeRanges ?? []
+
+  // 设置列宽,定义多个列宽
+  ws['!cols'] = props.colsWidth ?? []
+
+  // 将工作表添加到工作簿中
+  XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')
+
+  // 生成Excel文件
+  const wbout = XLSX.write(wb, { type: 'binary', bookType: 'xlsx' })
+
+  // 下载Excel文件
+  saveAs(
+    new Blob([s2ab(wbout)], { type: 'application/octet-stream' }),
+    props.fileName || 'exported_data.xlsx'
+  )
+}
+
+// 将二进制字符串转换为字节数组
+const s2ab = (s: string) => {
+  const buf = new ArrayBuffer(s.length)
+  const view = new Uint8Array(buf)
+  for (let i = 0; i < s.length; i++) view[i] = s.charCodeAt(i) & 0xff
+  return buf
+}
+</script>
+
+<style scoped></style>

+ 138 - 95
client/src/views/OaSystem/attendanceCenter/dep.vue

@@ -17,6 +17,7 @@
             default-expand-all
             node-key="id"
             placeholder="请选择部门"
+            :disabled="porps.deptsName !== '公司'"
           />
         </div>
         <div class="searBox">
@@ -28,10 +29,13 @@
             <img src="@/assets/imgs/OA/search.png" class="mr-8px" alt="" />
             查询</el-button
           >
-          <el-button type="primary">
-            <img src="@/assets/imgs/OA/open.png" class="mr-8px" alt="" />
-            导出</el-button
-          >
+          <ExportToExcel
+            v-if="porps.deptsName == '公司'"
+            :data="excelDataSource"
+            :file-name="`${moment(fromParams.month).format('YYYY-MM')}考勤统计.xlsx`"
+            :mergeRanges="mergeRanges"
+            :colsWidth="colsWidth"
+          />
           <el-button type="success" style="background-color: #05ce9e" v-if="false">
             <img src="@/assets/imgs/OA/kq/kqqrIcon.png" class="mr-8px" alt="" />
             考勤确认</el-button
@@ -98,21 +102,9 @@
                     src="@/assets/imgs/OA/kq/gouzi.png"
                     alt=""
                   />
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '2'"> 迟 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '3'"> 早 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '4'"> 旷 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '5'"> 差 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '11'"> 事 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '12'"> 病 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '13'"> 婚 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '14'"> 产 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '15'"> 陪 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '19'"> 丧 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '16'"> 伤 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '17'"> 年 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '18'"> 调 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 1) == '20'"> 他 </div>
-                  <div class="spans" v-else> </div>
+                  <div v-else class="spans">
+                    {{ attendanceTypeObj[sco(scope.row, item, 1)] ?? '' }}
+                  </div>
                 </div>
                 <div class="xw" @click="swStateClick(scope.row, item, 2)">
                   <img
@@ -120,85 +112,24 @@
                     src="@/assets/imgs/OA/kq/gouzi.png"
                     alt=""
                   />
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '2'"> 迟 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '3'"> 早 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '4'"> 旷 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '5'"> 差 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '11'"> 事 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '12'"> 病 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '13'"> 婚 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '14'"> 产 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '15'"> 陪 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '19'"> 丧 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '16'"> 伤 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '17'"> 年 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '18'"> 调 </div>
-                  <div class="spans" v-else-if="sco(scope.row, item, 2) == '20'"> 他 </div>
-                  <div class="spans" v-else> </div>
+                  <div v-else class="spans">
+                    {{ attendanceTypeObj[sco(scope.row, item, 1)] ?? '' }}
+                  </div>
                 </div>
               </div>
             </template>
           </el-table-column>
         </el-table-column>
-
-        <el-table-column prop="kg" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>旷工</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">天</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="cd" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>迟到</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">次</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="zt" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>早退</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">次</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="cc" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>出差</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">天</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="sj" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>事假</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">天</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="tx" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>调休</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">天</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="bj" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>病假</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">天</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="nj" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>年假</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">天</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="cj" width="40" label-class-name="labelModeRl">
-          <template #header>
-            <span>产假</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px">天</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="qt" width="40" label-class-name="labelModeRl">
+        <el-table-column
+          v-for="(item, index) in columList"
+          :prop="item.prop"
+          width="40"
+          label-class-name="labelModeRl"
+          :key="index"
+        >
           <template #header>
-            <span>其他</span>
-            <span class="c-#989FA8 font-size-14px m-t-8px"></span>
+            <span>{{ item.text }}</span>
+            <span class="c-#989FA8 font-size-14px m-t-8px">{{ item.unit }}</span>
           </template>
         </el-table-column>
       </el-table>
@@ -226,6 +157,10 @@ import * as MineApi from '@/api/oa/attendanceCenter'
 import { arrFlatten, depSort, isArrayDelOrNickname, allDeptsArr, dayOfWeekCall } from './attendAuth'
 import moment from 'moment'
 import { depsState } from './depsState'
+import ExportToExcel from '@/components/ExportToExcel/index.vue'
+import { getUserInfo } from '@/utils/tool'
+const userInfo = getUserInfo()
+
 const porps = defineProps(['deptsName'])
 
 const pagesList: any = ref({
@@ -234,7 +169,7 @@ const pagesList: any = ref({
   total: 0
 })
 const fromParams: any = ref({
-  deptId: '0a168fe9-2ba8-4302-b6b6-a524c1aef967',
+  deptId: userInfo.deptId ?? '',
   month: '',
   userName: ''
 })
@@ -274,6 +209,7 @@ const tableLoading = ref(true)
 const updataInit = (date) => {
   initInsMouth(date)
 }
+
 const initInsMouth = async (date: any) => {
   tableLoading.value = true
   let params = {
@@ -297,6 +233,10 @@ const initInsMouth = async (date: any) => {
       })
       let arr = allDeptsArr(restall, resArr, namesArr)
       let sbArr = depSort(deptSort.value, arr)
+
+      // 导出数据
+      excelDataSource.value = getExcelTable(sbArr)
+
       tableData.value = sbArr
       pagesList.value.total = arr.length
       tableLoading.value = false
@@ -344,17 +284,120 @@ const initWorkDay = async (date: any) => {
     item.day = moment(item.dateDay).format('DD')
   })
   tableHeadList.value = arr
-
   return await arr
 }
 const depsDetailRef = ref()
 const swStateClick = (row, item, index) => {
   //更改考勤
   if (porps.deptsName == '公司') {
+    depsDetailRef.value.initShow(row, item, index, fromParams.value.month)
     return
   }
-  depsDetailRef.value.initShow(row, item, index, fromParams.value.month)
 }
+// 考勤对应的字典
+const attendanceTypeObj = {
+  1: '√', // 出勤
+  2: '迟', // '迟到'
+  3: '早', // '早退'
+  4: '旷', // '旷工'
+  5: '差', // '出差'
+  11: '事', // '事假'
+  12: '病', // '病假'
+  13: '婚', // '婚假'
+  14: '产', // '产假'
+  15: '陪', // '陪产假'
+  19: '丧', // '丧假'
+  16: '伤', // '工伤'
+  17: '年', // '年假'
+  18: '调', // '调休'
+  20: '假' // '其他'
+}
+// 考勤统计
+const columList = [
+  { text: '旷 工', prop: 'kg', unit: '天' },
+  { text: '迟 到', prop: 'cd', unit: '次' },
+  { text: '早 退', prop: 'zt', unit: '次' },
+  { text: '出 差', prop: 'cc', unit: '天' },
+  { text: '事 假', prop: 'sj', unit: '天' },
+  { text: '调 休', prop: 'tx', unit: '天' },
+  { text: '病 假', prop: 'bj', unit: '天' },
+  { text: '年 假', prop: 'nj', unit: '天' },
+  { text: '产 假', prop: 'cj', unit: '天' },
+  { text: '其 他', prop: 'qt', unit: '天' }
+]
+
+// 考勤表格
+const excelDataSource = ref<any>()
+// 合并的单元格
+const mergeRanges = [
+  { s: { r: 0, c: 0 }, e: { r: 1, c: 0 } }, // 合并第一行的前两列
+  { s: { r: 0, c: 1 }, e: { r: 1, c: 1 } } // 合并第二行的前两列
+]
+// 列宽设置
+const colsWidth = [{ wch: 30 }, { wch: 10 }]
+// 获取导出的数据表格
+const getExcelTable = (attendanceData) => {
+  const tableHead = [
+    [
+      '部门',
+      '姓名',
+      ...tableHeadList.value.map((item) => item.day),
+      '旷工(天)',
+      '迟到(次)',
+      '早退(次)',
+      '出差(天)',
+      '事假(天)',
+      '调休(天)',
+      '病假(天)',
+      '年假(天)',
+      '产假(天)',
+      '其他(天)'
+    ],
+    [
+      '',
+      '',
+      ...tableHeadList.value.map((item) => item.dayOfWeek),
+      '',
+      '',
+      '',
+      '',
+      '',
+      '',
+      '',
+      '',
+      '',
+      ''
+    ]
+  ]
+  const tableBody = attendanceData.map((item) => {
+    const data = item.attendArray.map((arr, i) => {
+      // 判断是不是工作日
+      const isworkday = tableHeadList.value[i].isworkday
+      if (!isworkday) return ''
+      // 判断有没有数据
+      if (!arr.swAttendanceStatus) return ''
+      return `${attendanceTypeObj[arr.swAttendanceStatus] ?? ''} / ${attendanceTypeObj[arr.xwAttendanceStatus] ?? ''}`
+    })
+    return [
+      item.deptName,
+      item.nickName,
+      ...data,
+      item.kg,
+      item.cd,
+      item.zt,
+      item.cc,
+      item.sj,
+      item.tx,
+      item.bj,
+      item.nj,
+      item.cj,
+      item.qt
+    ]
+  })
+
+  return [...tableHead, ...tableBody]
+}
+
 onMounted(() => {
   let toMonth = moment().format('YYYY-MM')
   fromParams.value.month = toMonth

+ 1 - 1
zjugis-framework/zjugis-spring-boot-starter-biz-social/src/main/java/com/zjugis/framework/social/core/enums/AuthExtendSource.java

@@ -56,7 +56,7 @@ public enum AuthExtendSource implements AuthSource {
 
         @Override
         public String userInfo() {
-            return "https://api.dingtalk.com//v1.0/contact/users/{unionId}";
+            return "https://api.dingtalk.com/v1.0/contact/users/{unionId}";
         }
 
         @Override

+ 59 - 0
zjugis-framework/zjugis-spring-boot-starter-biz-social/src/main/java/com/zjugis/framework/social/core/request/AuthDingtalkRequest.java

@@ -6,13 +6,18 @@ import com.xingyuv.http.support.HttpHeader;
 import com.xingyuv.http.util.MapUtil;
 import com.xingyuv.jushauth.cache.AuthStateCache;
 import com.xingyuv.jushauth.config.AuthConfig;
+import com.xingyuv.jushauth.enums.AuthResponseStatus;
 import com.xingyuv.jushauth.enums.AuthUserGender;
 import com.xingyuv.jushauth.exception.AuthException;
+import com.xingyuv.jushauth.log.Log;
 import com.xingyuv.jushauth.model.AuthCallback;
+import com.xingyuv.jushauth.model.AuthResponse;
 import com.xingyuv.jushauth.model.AuthToken;
 import com.xingyuv.jushauth.model.AuthUser;
 import com.xingyuv.jushauth.request.AuthDefaultRequest;
+import com.xingyuv.jushauth.utils.AuthChecker;
 import com.xingyuv.jushauth.utils.HttpUtils;
+import com.xingyuv.jushauth.utils.StringUtils;
 import com.xingyuv.jushauth.utils.UrlBuilder;
 import com.zjugis.framework.social.core.enums.AuthExtendSource;
 
@@ -71,6 +76,60 @@ public class AuthDingtalkRequest extends AuthDefaultRequest {
                 .build();
     }
 
+    protected AuthToken getAccessTokenApp() {
+        HttpHeader httpHeader = new HttpHeader();
+        Map<String, Object> paramsMap = new HashMap<>();
+        paramsMap.put("appSecret", config.getClientSecret());
+        paramsMap.put("appKey", config.getClientId());
+        String response = new HttpUtils(config.getHttpConfig()).post("https://api.dingtalk.com/v1.0/oauth2/accessToken", JSONObject.toJSONString(paramsMap), httpHeader).getBody();
+        JSONObject object = JSON.parseObject(response);
+        return AuthToken.builder()
+                .accessToken(object.getString("accessToken"))
+                .expireIn(object.getIntValue("expireIn"))
+                .build();
+    }
+
+
+    public AuthUser getUserInfoApp(String authCode) {
+        AuthToken authToken = this.getAccessTokenApp();
+        String accessToken = authToken.getAccessToken();
+        HttpHeader httpHeader = new HttpHeader();
+        httpHeader.add("x-acs-dingtalk-access-token", accessToken);
+        Map<String, Object> paramsMap = new HashMap<>();
+        paramsMap.put("code", authCode);
+        String response = new HttpUtils(config.getHttpConfig()).post("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo?access_token="+accessToken, JSONObject.toJSONString(paramsMap), httpHeader).getBody();
+        JSONObject object = JSON.parseObject(response).getJSONObject("result");
+        return AuthUser.builder()
+                .rawUserInfo(object)
+                .uuid(object.getString("unionid"))
+                .nickname(object.getString("name"))
+                .username(object.getString("name"))
+                .gender(AuthUserGender.UNKNOWN)
+                .source(source.toString())
+                .token(authToken)
+                .build();
+    }
+
+
+    /**
+     * 处理{@link AuthDefaultRequest#login(AuthCallback)} 发生异常的情况,统一响应参数
+     *
+     * @param e 具体的异常
+     * @return AuthResponse
+     */
+    AuthResponse responseError(Exception e) {
+        int errorCode = AuthResponseStatus.FAILURE.getCode();
+        String errorMsg = e.getMessage();
+        if (e instanceof AuthException) {
+            AuthException authException = ((AuthException) e);
+            errorCode = authException.getErrorCode();
+            if (StringUtils.isNotEmpty(authException.getErrorMsg())) {
+                errorMsg = authException.getErrorMsg();
+            }
+        }
+        return AuthResponse.builder().code(errorCode).msg(errorMsg).build();
+    }
+
     /**
      * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
      *

+ 41 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/controller/app/auth/AppAuthController.java

@@ -0,0 +1,41 @@
+package com.zjugis.module.system.controller.app.auth;
+
+import com.zjugis.framework.common.pojo.CommonResult;
+import com.zjugis.framework.operatelog.core.annotations.OperateLog;
+import com.zjugis.module.system.controller.admin.auth.vo.AuthLoginReqVO;
+import com.zjugis.module.system.controller.admin.auth.vo.AuthLoginRespVO;
+import com.zjugis.module.system.service.auth.AdminAuthService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.annotation.security.PermitAll;
+import javax.validation.Valid;
+
+import static com.zjugis.framework.common.pojo.CommonResult.success;
+
+/**
+ * @Author 陈俊
+ * @Date 2024/4/22 11:33
+ * @Version 1.0
+ */
+@Tag(name = "用户 App - 认证")
+@RestController
+@RequestMapping("/system/auth")
+@Validated
+public class AppAuthController {
+
+    @Resource
+    private AdminAuthService authService;
+
+    @PostMapping("/login-ding")
+    @PermitAll
+    @Operation(summary = "钉钉内部应用免登")
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<AuthLoginRespVO> loginDing(@RequestParam("code") String requestAuthCode) {
+        return success(authService.loginDing(requestAuthCode));
+    }
+}

+ 6 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/auth/AdminAuthService.java

@@ -70,4 +70,10 @@ public interface AdminAuthService {
      */
     AuthLoginRespVO refreshToken(String refreshToken);
 
+    /**
+     * 钉钉内部应用免登
+     * @param requestAuthCode
+     * @return
+     */
+    AuthLoginRespVO loginDing(String requestAuthCode);
 }

+ 19 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/auth/AdminAuthServiceImpl.java

@@ -17,6 +17,7 @@ import com.zjugis.module.system.enums.logger.LoginLogTypeEnum;
 import com.zjugis.module.system.enums.logger.LoginResultEnum;
 import com.zjugis.module.system.enums.oauth2.OAuth2ClientConstants;
 import com.zjugis.module.system.enums.sms.SmsSceneEnum;
+import com.zjugis.module.system.enums.social.SocialTypeEnum;
 import com.zjugis.module.system.service.logger.LoginLogService;
 import com.zjugis.module.system.service.member.MemberService;
 import com.zjugis.module.system.service.oauth2.OAuth2TokenService;
@@ -206,6 +207,24 @@ public class AdminAuthServiceImpl implements AdminAuthService {
         return AuthConvert.INSTANCE.convert(accessTokenDO);
     }
 
+    @Override
+    public AuthLoginRespVO loginDing(String requestAuthCode) {
+        // 使用免登授权码获取用户ID
+        String userId = socialUserService.getBindUserIdByAuthCode(SocialTypeEnum.DINGTALK.getType(), requestAuthCode);
+        if (userId == null) {
+            throw exception(AUTH_MOBILE_NOT_EXISTS);
+        }
+
+        // 获得用户
+        AdminUserDO user = userService.getUser(userId);
+        if (user == null) {
+            throw exception(USER_NOT_EXISTS);
+        }
+
+        // 创建 Token 令牌,记录登录日志
+        return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
+    }
+
     @Override
     public void logout(String token, Integer logType) {
         // 删除访问令牌

+ 8 - 0
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/social/SocialUserService.java

@@ -78,4 +78,12 @@ public interface SocialUserService {
     String getBindUserId(Integer userType, Integer type, String code, String state);
 
     List<String> getBindUserIds(Collection<String> socialIds, Integer socialType);
+
+    /**
+     * 企业内部应用免登
+     * @param type 社交平台的类型 {@link SocialTypeEnum}
+     * @param requestAuthCode
+     * @return
+     */
+    String getBindUserIdByAuthCode(Integer type, String requestAuthCode);
 }

+ 16 - 1
zjugis-module-system/zjugis-module-system-biz/src/main/java/com/zjugis/module/system/service/social/SocialUserServiceImpl.java

@@ -8,8 +8,10 @@ import com.xingyuv.jushauth.model.AuthResponse;
 import com.xingyuv.jushauth.model.AuthUser;
 import com.xingyuv.jushauth.request.AuthRequest;
 import com.xingyuv.jushauth.utils.AuthStateUtils;
+import com.zjugis.framework.common.enums.UserTypeEnum;
 import com.zjugis.framework.common.util.http.HttpUtils;
 import com.zjugis.framework.social.core.ZjugisAuthRequestFactory;
+import com.zjugis.framework.social.core.request.AuthDingtalkRequest;
 import com.zjugis.module.system.api.social.dto.SocialUserBindReqDTO;
 import com.zjugis.module.system.dal.dataobject.social.SocialUserBindDO;
 import com.zjugis.module.system.dal.dataobject.social.SocialUserDO;
@@ -159,7 +161,7 @@ public class SocialUserServiceImpl implements SocialUserService {
             bindSocialUser(new SocialUserBindReqDTO(user.getId(), userType,
                     type, code, state));
             userId = user.getId();
-            if(avatarUrl != null){
+            if (avatarUrl != null) {
                 adminUserMapper.updateById(new AdminUserDO().setId(userId).setAvatar(avatarUrl.toString()));
             }
         } else {
@@ -179,6 +181,19 @@ public class SocialUserServiceImpl implements SocialUserService {
         }
     }
 
+    @Override
+    public String getBindUserIdByAuthCode(Integer type, String requestAuthCode) {
+        // 获得对应的 AuthRequest 实现
+        AuthDingtalkRequest authRequest = (AuthDingtalkRequest) zjugisAuthRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
+        AuthUser userInfoApp = authRequest.getUserInfoApp(requestAuthCode);
+        SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(type, userInfoApp.getUuid());
+        Assert.notNull(socialUser, "社交用户不能为空");
+        Integer userType = UserTypeEnum.ADMIN.getValue();
+        SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType, socialUser.getId());
+        Assert.notNull(socialUserBind, "社交用户未绑定");
+        return socialUserBind.getUserId();
+    }
+
     /**
      * 请求社交平台,获得授权的用户
      *