Преглед изворни кода

Merge branch 'master' of http://47.100.37.243:10191/wutt/manHourHousekeeper into master

seyason пре 5 месеци
родитељ
комит
0db2336448
57 измењених фајлова са 2205 додато и 105 уклоњено
  1. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/index.html
  2. 7 0
      fhKeeper/formulahousekeeper/customerBuler-crm/logo.svg
  3. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/login_logo.png
  4. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/login_logos.png
  5. 17 15
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue
  6. 19 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contract/api.ts
  7. 136 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contract/component/addEditor.vue
  8. 499 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contract/index.vue
  9. 0 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contract/type.d.ts
  10. 9 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/home.vue
  11. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/product/客户管家logo@3x.png
  12. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/type.d.ts
  13. 6 5
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts
  14. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm/vite.config.ts
  15. 27 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/BusinessOpportunityController.java
  16. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WeiXinCorpController.java
  17. 3 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/SysDict.java
  18. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/ActionLogMapper.java
  19. 2 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ContractDocumentServiceImpl.java
  20. 7 10
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ContractServiceImpl.java
  21. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SysFormServiceImpl.java
  22. 2 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SysRoleServiceImpl.java
  23. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  24. 6 8
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/BusinessOpportunityMapper.xml
  25. 83 6
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/ContractMapper.xml
  26. 18 6
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/FeishuInfoController.java
  27. 6 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/FinanceController.java
  28. 43 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/FinanceExcludeProjectController.java
  29. 61 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/FinanceExcludeProject.java
  30. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/AddOrUpdateFinanceExcludeProjectBO.java
  31. 47 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuClockCheckRecord.java
  32. 15 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuClockData.java
  33. 80 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuClockTaskRecord.java
  34. 17 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuClockUserTaskResult.java
  35. 11 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuFlexibleRule.java
  36. 25 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuPunchTimeRule.java
  37. 11 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuRestTimeRule.java
  38. 33 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuShiftResult.java
  39. 24 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/LoadTaskResultBO.java
  40. 11 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/FinanceExcludeProjectMapper.java
  41. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/FinanceMapper.java
  42. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/FinanceProjectsMapper.java
  43. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  44. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ExcelExportService.java
  45. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/FeishuInfoService.java
  46. 15 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/FinanceExcludeProjectService.java
  47. 2 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/FinanceService.java
  48. 203 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ExcelExportServiceImpl.java
  49. 198 11
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FeishuInfoServiceImpl.java
  50. 133 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceExcludeProjectServiceImpl.java
  51. 47 10
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceServiceImpl.java
  52. 282 12
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/ExcelUtil.java
  53. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/i18n/messages.properties
  54. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/i18n/messages_en_US.properties
  55. 13 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FinanceExcludeProjectMapper.xml
  56. 10 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FinanceProjectsMapper.xml
  57. 29 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/index.html

@@ -2,7 +2,7 @@
 
 <head>
   <meta charset="UTF-8" />
-  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+  <link rel="icon" type="image/svg+xml" href="./logo.svg" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>客户管家</title>
 </head>

Разлика између датотеке није приказан због своје велике величине
+ 7 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/logo.svg


BIN
fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/login_logo.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/login_logos.png


+ 17 - 15
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue

@@ -100,7 +100,7 @@
       </div>
     </div>
     <!-- 弹窗 -->
-    <el-dialog v-model="allVisible.newBusinessisible" width="1000" :show-close="false" top="10vh"
+    <el-dialog v-model="allVisible.newBusinessisible" width="1000" append-to-body :show-close="false" top="10vh"
       :before-close="handleClose">
       <template #header="{ close, titleId, titleClass }">
         <div class="flex justify-between items-center border-b pb-3 dialog-header">
@@ -298,24 +298,26 @@ function editBusiness(visibles: boolean) {
 }
 
 function editNewBusiness(item: any) {
-  console.log(item, '选择数据')
   showVisible('newBusinessisible')
-  allLoading.generateFormLading = true
-  if (item) {
-    editProduct(item)
-    businessTemplateValue.value = editBusinessData(item)
-    allText.newBusinessisibleText = '编辑商机'
-  }
-  if (!item) {
-    businessTemplateValue.value = {}
-    productTableListValue.value = []
-    allText.newBusinessisibleText = '新建商机'
-  }
   setTimeout(() => {
-    businessTemplateRef.value && businessTemplateRef.value.reset()
     businessTemplateKey.value++
+    if (item) {
+      allLoading.generateFormLading = true
+      editProduct(item)
+      businessTemplateValue.value = editBusinessData(item)
+      allText.newBusinessisibleText = '编辑商机'
+    }
+    if (!item) {
+      businessTemplateRef.value && businessTemplateRef.value.reset()
+      businessTemplate.value = businessTemplate.value
+      businessTemplateValue.value = {}
+      productTableListValue.value = []
+      allText.newBusinessisibleText = '新建商机'
+    }
+  }, 0)
+  setTimeout(() => {
     allLoading.generateFormLading = false
-  }, 500)
+  }, 700)
 }
 
 function newTask(item: any) {

+ 19 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contract/api.ts

@@ -0,0 +1,19 @@
+export const PATH = "/contract";
+export const GET_CONTRACT_TEMPLATE = "/sys-form/getListByCode" + PATH;
+export const OBTAIN_CONTRACT_TYPE = "/sys-dict/getListByCode?code=ContractType";
+export const ADD_CONTRACT = "/contract/addContract";
+export const EDIT_CONTRACT = "/contract/editContract";
+export const UPLOAD_ATTACHMENTS = "/contract-document/fileUpload";
+export const DELETE_CONTRACT_FILE = "/contract-document/fileDelete";
+export const GET_CONTRACT_LIST = "/contract/getContractPage";
+export const GET_PAYMENT_LIST = "/contract-payment/getList";
+export const CONTRACT_OPERATION = "/contract/processContract";
+export const CONTRACT_DELETION = "/contract/deleteContract";
+export const EXPORT_CONTRACT = "/contract/exportContract"
+export const IMPORITEM = "/contract/importContract"
+
+export const statusArray = [
+  { label: "审核通过", value: "0", color: "color:#67c23a;" },
+  { label: "待审核", value: "1", color: "color:#e6a23c;" },
+  { label: "已驳回", value: "2", color: "color:#f56c6c;" },
+];

+ 136 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contract/component/addEditor.vue

@@ -0,0 +1,136 @@
+<script lang="ts" setup>
+import { ref, reactive, onMounted, inject, watch } from "vue";
+import { UploadRequestOptions } from 'element-plus';
+import { Delete } from '@element-plus/icons-vue'
+import { tableShowOverflowTooltip } from '@/utils/globalVariables'
+import { formatDate } from '@/utils/times'
+import { downloadFile } from '@/utils/tools'
+import { post, get, uploadFile } from "@/utils/request";
+const props = defineProps<{
+  paymentPlan: any[],
+  enclosure: any[],
+  enclosureDetele: any[]
+}>()
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const paymentPlan = ref(props.paymentPlan)
+const enclosure = ref(props.enclosure)
+const enclosureDetele = ref<any[]>([])
+const uploadRef = ref<any>()
+
+watch(() => props.paymentPlan, (newVal, _oldVal) => {
+  paymentPlan.value = newVal || []
+})
+
+watch(() => props.enclosure, (newVal, _oldVal) => {
+  enclosure.value = newVal || []
+})
+
+watch(() => props.enclosureDetele, (newVal, _oldVal) => {
+  enclosureDetele.value = newVal || []
+})
+
+// 上传附件
+async function httpUploadFile(param: UploadRequestOptions) {
+  const { file } = param
+  enclosure.value.push({ name: file.name, file, needUpload: true })
+  return
+}
+
+function addPlan() {
+  paymentPlan.value.push({
+    isPayed: false,
+    payDate: formatDate(new Date()),
+    amount: '0'
+  })
+}
+
+function deteleTables(row: any, index: number, type: 'paymentPlan' | 'enclosure') {
+  if (type === 'paymentPlan') {
+    paymentPlan.value.splice(index, 1)
+  } else {
+    if(row.id) {
+      enclosureDetele.value.push(row.id)
+    }
+    enclosure.value.splice(index, 1)
+  }
+}
+
+function getAddEditorData() {
+  return {
+    paymentPlan: paymentPlan.value,
+    enclosure: enclosure.value,
+    enclosureDetele: enclosureDetele.value
+  }
+}
+
+defineExpose({
+  getAddEditorData
+})
+
+</script>
+<template>
+  <div>
+    <div>回款计划</div>
+    <el-table :show-overflow-tooltip="tableShowOverflowTooltip" :data="paymentPlan" height="260">
+      <el-table-column label="是否已回款">
+        <template #default="scope">
+          <el-checkbox v-model="scope.row.isPayed" label="已回款" />
+        </template>
+      </el-table-column>
+      <el-table-column label="回款日期">
+        <template #default="scope">
+          <el-date-picker v-model="scope.row.payDate" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD"
+            placeholder="回款日期" />
+        </template>
+      </el-table-column>
+      <el-table-column label="汇款金额">
+        <template #default="scope">
+          <el-input v-model="scope.row.amount" v-enter-number placeholder="回款金额">
+            <template #prefix>
+              <el-text class="mx-1" type="info">¥</el-text>
+            </template>
+          </el-input>
+        </template>
+      </el-table-column>
+      <el-table-column prop="operation" width="80">
+        <template #header>
+          <el-link type="primary" :underline="false" @click="addPlan()">添加</el-link>
+        </template>
+        <template #default="scope">
+          <el-button type="danger" :icon="Delete" size="small"
+            @click="deteleTables(scope.row, scope.$index, 'paymentPlan')"></el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <div>附件</div>
+    <el-table :show-overflow-tooltip="tableShowOverflowTooltip" :data="enclosure" height="260">
+      <el-table-column label="序号" width="100">
+        <template #default="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column label="文件名称" prop="name"></el-table-column>
+      <el-table-column label="操作" width="80">
+        <template #default="scope">
+          <el-link type="primary" :underline="false" v-if="scope.row.id" @click="downloadFile(scope.row.url, scope.row.name)">下载</el-link>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="80">
+        <template #header>
+          <el-upload ref="uploadRef" :http-request="httpUploadFile" :show-file-list="false" element-loading-text="正在上传"
+            multiple>
+            <template #trigger>
+              <el-link type="primary" :underline="false">添加</el-link>
+            </template>
+          </el-upload>
+        </template>
+        <template #default="scope">
+          <el-button type="danger" :icon="Delete" size="small"
+            @click="deteleTables(scope.row, scope.$index, 'enclosure')"></el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+<style lang="scss" scoped></style>

+ 499 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contract/index.vue

@@ -0,0 +1,499 @@
+<script lang="ts" setup>
+import { ref, reactive, onMounted, inject } from "vue";
+import { tableShowOverflowTooltip } from '@/utils/globalVariables'
+import { getFromValue, resetFromValue, setTemplateDataDisable, downloadFile, formatDate, confirmAction, getTemplateKey, downloadTemplate } from '@/utils/tools'
+import { GenerateForm } from '@zmjs/form-design';
+import { post, get, uploadFile } from "@/utils/request";
+import { CONTRACT_DELETION, CONTRACT_OPERATION, DELETE_CONTRACT_FILE, EDIT_CONTRACT, EXPORT_CONTRACT, GET_CONTRACT_LIST, GET_PAYMENT_LIST, IMPORITEM, OBTAIN_CONTRACT_TYPE, statusArray, UPLOAD_ATTACHMENTS } from './api'
+import { GET_CONTRACT_TEMPLATE, ADD_CONTRACT } from './api'
+import { UploadRequestOptions } from "element-plus";
+
+import AddEditor from "./component/addEditor.vue";
+
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const filterContractForm = reactive({
+  name: '',
+  number: '',
+  typeName: '', // 合同类型
+  status: '',
+  startDate: '',
+  endDate: '',
+  paymentStartDate: '',
+  paymentEndDate: '',
+})
+const fixedData = reactive({
+  typeOfContractList: [] as optionType[],
+  stateList: statusArray as optionType[],
+})
+const allLoading = reactive({
+  exportLoading: false,
+  contractTableLading: false,
+  addEditorSaveLoading: false,
+  importLoading: false
+})
+const allVisable = reactive({
+  addEditorVisable: false,
+  rejectDataVisable: false,
+  importVisible: false
+})
+const allDynamicText = reactive({
+  addEditorText: '新增合同',
+  importText: '合同管理导入模板.xlsx'
+})
+const paging = reactive({
+  pageIndex: 1,
+  pageSize: 20,
+  pageTotal: 0
+})
+
+const contractTableList = ref<any>([]);
+const addEditorVisableForm = reactive({
+  paymentPlan: [],
+  enclosure: [],
+  enclosureDetele: []
+})
+
+const editForm = ref<any>({})
+const generateForm = ref<any>(null) // 模板
+const addEditorRef = ref<any>(null)
+const generateFormKey = ref<number>(1)
+const contractTemplate = ref<any>({
+  list: [] as any[],
+  config: {}
+})
+const rejectData = reactive({
+  id: '',
+  status: 2,
+  msg: '',
+})
+
+
+
+onMounted(() => {
+  getObtainContractType()
+  getTemplateConfig()
+  getContractTableList()
+})
+
+function contractRejection(row: any, status: number) {
+  rejectData.id = row.id
+  rejectData.status = status
+  rejectData.msg = ''
+  allVisable.rejectDataVisable = true
+}
+
+function deleteContract(row: any) {
+  confirmAction('确定要删除该合同吗?', '合同删除').then(() => {
+    post(CONTRACT_DELETION, { id: row.id }).then(() => {
+      globalPopup?.showSuccess('删除成功')
+      getContractTableList()
+    })
+  })
+}
+
+function contractApproved(row: any, status: number) {
+  confirmAction('确认审核通过吗?,通过后合同基本信息无法修改', '合同通过').then(() => {
+    contractOperation(row, status)
+  })
+}
+
+function rejectTheContract() {
+  confirmAction('确认驳回吗?,驳回后合同基本信息无法修改', '合同驳回').then(() => {
+    contractOperation(rejectData, 2)
+  })
+}
+
+async function importBusiness(param: UploadRequestOptions) {
+  allLoading.importLoading = true
+  const formData = new FormData();
+  formData.append('file', param.file)
+  formData.append('userId', sessionStorage.getItem('token') || '')
+  const res = await uploadFile(IMPORITEM, formData).finally(() => {
+    allLoading.importLoading = false
+  })
+  if (res.code == 'ok') {
+    globalPopup?.showSuccess('导入成功' || '')
+    getContractTableList()
+    return
+  }
+  globalPopup?.showError(res.msg || '')
+}
+
+function exportContract() {
+  allLoading.exportLoading = true
+  post(EXPORT_CONTRACT, {}).then((res) => {
+    downloadFile('合同导出.xlsx', res.data)
+  }).finally(() => {
+    allLoading.exportLoading = false
+  })
+}
+
+async function addEditor(row?: any) {
+  allLoading.addEditorSaveLoading = false
+  editForm.value = row ? setEditForm(row) : {}
+  allDynamicText.addEditorText = row ? '编辑合同' : '新增合同'
+  contractTemplate.value.list = setTemplateDataDisable(contractTemplate.value.list, [...getTemplateKey(contractTemplate.value.list)], row?.status == 0)
+  generateFormKey.value++
+  setAddEditorVisableFormData(row)
+  if (row) {
+    const { data = [] } = await post(GET_PAYMENT_LIST, { contractId: row.id })
+    addEditorVisableForm.paymentPlan = data.map((item: any) => {
+      return {
+        isPayed: item.isPayed, amount: item.amount, payDate: item.payDate, id: item.id
+      }
+    })
+  }
+  allVisable.addEditorVisable = true
+}
+
+function setEditForm(row: any) {
+  const { id } = row
+  const filedList = getTemplateKey(contractTemplate.value.list)
+  const formFiled: any = {}
+  filedList.forEach((item: any) => {
+    formFiled[item] = row[item]
+  })
+  return {
+    ...formFiled, id
+  }
+}
+
+function setAddEditorVisableFormData(row: any) {
+  addEditorVisableForm.enclosureDetele = []
+  if (!row) {
+    addEditorVisableForm.paymentPlan = []
+    addEditorVisableForm.enclosure = []
+  }
+
+  if (row) {
+    const { files = [] } = row
+    addEditorVisableForm.enclosure = files.map((item: any) => {
+      return {
+        name: item.documentName,
+        id: item.id,
+        url: item.url
+      }
+    })
+  }
+}
+
+function resetFiltering() {
+  let newResetForm = resetFromValue(filterContractForm)
+  Object.assign(filterContractForm, newResetForm)
+}
+
+function contractOperation(row: any, status: number) {
+  let formVal = { status, id: row.id }
+  if (status == 2) {
+    formVal = row
+  }
+  post(CONTRACT_OPERATION, { ...formVal }).then(() => {
+    globalPopup?.showSuccess("操作成功")
+    if (status == 2) {
+      allVisable.rejectDataVisable = false
+    }
+    getContractTableList()
+  })
+}
+function getContractTableList() {
+  allLoading.contractTableLading = true
+  const formVal = getFromValue({ ...filterContractForm, pageIndex: paging.pageIndex, pageSize: paging.pageSize })
+  post(GET_CONTRACT_LIST, { ...formVal }).then((res) => {
+    // 赋值列表
+    const { total = 0, data = [] } = res.data
+    paging.pageTotal = total
+    contractTableList.value = data
+  }).finally(() => {
+    allLoading.contractTableLading = false
+  })
+}
+
+async function addEditorSave() {
+  const url = editForm.value.id ? EDIT_CONTRACT : ADD_CONTRACT
+  const data = await generateForm.value.getData()
+  const { paymentPlan = [], enclosure = [], enclosureDetele = [] } = addEditorRef.value.getAddEditorData()
+  let newPaymentPlan = paymentPlan
+  if (editForm.value.id) {
+    newPaymentPlan = paymentPlan.map((item: any) => { return { ...item, contractId: editForm.value.id } })
+  }
+
+  const formVal = getFromValue({
+    ...editForm.value,
+    ...data,
+    startDate: data.startDate ? formatDate(new Date(data.startDate)) : '',
+    endDate: data.endDate ? formatDate(new Date(data.endDate)) : '',
+    paymentListStr: JSON.stringify(newPaymentPlan)
+  })
+
+  let totalAmount = 0
+  // 判断
+  for(let i in paymentPlan) {
+    if(!paymentPlan[i].payDate) {
+      globalPopup?.showWarning('回款日期不能为空')
+      return
+    }
+
+    if(!paymentPlan[i].amount || paymentPlan[i].amount == 0) {
+      globalPopup?.showWarning('回款金额不能为空和0')
+      return
+    }
+
+    totalAmount += +paymentPlan[i].amount || 0
+  }
+
+  if(totalAmount > +(formVal.amounts || 0)) {
+    globalPopup?.showWarning('总回款金额不得大于合同金额')
+    return
+  }
+
+  allLoading.addEditorSaveLoading = true
+  post(url, { ...formVal }).then((res) => {
+    if (enclosure.length > 0) {
+      fileUpload(String(res.data || editForm.value.id), enclosure)
+    }
+    if (enclosureDetele.length > 0) {
+      fileUploadDetele(String(res.data || editForm.value.id), enclosureDetele)
+    }
+    if(enclosure.length == 0) {
+      allLoading.addEditorSaveLoading = false
+      globalPopup?.showSuccess('添加成功')
+      allVisable.addEditorVisable = false
+      getContractTableList()
+    }
+  }).catch(() => {
+    allVisable.addEditorVisable = false
+  })
+}
+
+function fileUpload(id: string, enclosure = []) {
+  const formData = new FormData()
+  formData.append('ContractId', id);
+  enclosure.forEach((item: any) => {
+    if (item.needUpload) {
+      formData.append('file', item.file);
+    }
+  });
+  post(UPLOAD_ATTACHMENTS, formData).then(re => {
+    globalPopup?.showSuccess(re.msg)
+    getContractTableList()
+  }).finally(() => {
+    allVisable.addEditorVisable = false
+  })
+}
+
+function fileUploadDetele(id: string, enclosureDetele = []) {
+  post(DELETE_CONTRACT_FILE, { contractId: id, fileIds: enclosureDetele.join(',') })
+}
+
+function handleSizeChange(val: number) {
+  paging.pageIndex = 1
+  paging.pageSize = val
+  getContractTableList()
+}
+
+function handleCurrentChange(val: number) {
+  paging.pageIndex = val
+  getContractTableList()
+}
+
+function getTemplateConfig() {
+  get(GET_CONTRACT_TEMPLATE).then(res => {
+    const datas = JSON.parse(res.data[0] && res.data[0].config)
+    contractTemplate.value = datas
+  })
+}
+
+function getObtainContractType() {
+  get(OBTAIN_CONTRACT_TYPE).then(res => {
+    const list = res.data || []
+    fixedData.typeOfContractList = list.map((item: any) => {
+      return {
+        label: item.name,
+        value: item.id
+      }
+    })
+  })
+}
+</script>
+
+<template>
+  <div class="h-full flex">
+    <div class="p-5 w-80 pr-0">
+      <div class="bg-white w-full h-full shadow-md rounded-md flex flex-col">
+        <div class="flex-1 p-3 overflow-y-auto">
+          <el-form :model="filterContractForm" label-width="100px" style="max-width: 600px">
+            <el-form-item label="合同编号">
+              <el-input v-model="filterContractForm.number" clearable placeholder="请输入"></el-input>
+            </el-form-item>
+            <el-form-item label="合同类型">
+              <el-select v-model="filterContractForm.typeName" placeholder="请选择" clearable filterable>
+                <el-option v-for="item in fixedData.typeOfContractList" :key="item.value" :label="item.label"
+                  :value="item.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="状态">
+              <el-select v-model="filterContractForm.status" placeholder="请选择" clearable filterable>
+                <el-option v-for="item in fixedData.stateList" :key="item.value" :label="item.label"
+                  :value="item.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="创建时间">
+              <el-date-picker v-model="filterContractForm.startDate" type="date" placeholder="请选择" :clearable="false"
+                format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
+            </el-form-item>
+            <el-form-item label="">
+              <el-date-picker v-model="filterContractForm.endDate" type="date" placeholder="请选择" :clearable="false"
+                format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
+            </el-form-item>
+            <el-form-item label="下笔回款日期">
+              <el-date-picker v-model="filterContractForm.paymentStartDate" type="date" placeholder="请选择"
+                :clearable="false" format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
+            </el-form-item>
+            <el-form-item label="">
+              <el-date-picker v-model="filterContractForm.paymentEndDate" type="date" placeholder="请选择"
+                :clearable="false" format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
+            </el-form-item>
+          </el-form>
+        </div>
+        <div class="w-full flex p-3 shadow-[0_-3px_5px_0px_rgba(0,0,0,0.2)]">
+          <El-button class="w-full" @click="resetFiltering()">重置</El-Button>
+          <El-button type="primary" class="w-full" :loading="allLoading.contractTableLading"
+            @click="getContractTableList()">搜索</El-Button>
+        </div>
+      </div>
+    </div>
+    <div class="flex-1 p-5 overflow-auto">
+      <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
+        <div class="flex justify-end pb-3">
+          <el-button type="primary" v-permission="['contractAdd']" @click="addEditor()">新增合同</el-button>
+          <el-button type="primary" v-permission="['contractImport']" @click="allVisable.importVisible = true">导入</el-button>
+          <el-button type="primary" v-permission="['contractExport']" :loading="allLoading.exportLoading"
+            @click="exportContract()">导出</el-button>
+        </div>
+        <div class="flex-1 w-full overflow-hidden">
+          <el-table ref="contractTableRef" :show-overflow-tooltip="tableShowOverflowTooltip" :data="contractTableList"
+            border v-loading="allLoading.contractTableLading" style="width: 100%;height: 100%;">
+            <el-table-column prop="number" label="合同编号" width="180"></el-table-column>
+            <el-table-column prop="name" label="合同名称" width="180"></el-table-column>
+            <el-table-column prop="amounts" label="合同金额" width="180">
+              <template #default="scope">
+                ¥ {{ scope.row.amounts ? scope.row.amounts.toFixed(2) : '0.00' }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="payment" label="已回款金额" width="180">
+              <template #default="scope">
+                ¥ {{ scope.row.payment ? scope.row.payment.toFixed(2) : '0.00' }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="payment" label="已回款进度" width="180">
+              <template #default="scope">
+                {{ scope.row.payment ? (100 * scope.row.payment / scope.row.amounts).toFixed(1) + '%' : '0%' }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="nextPaymentDate" label="下笔回款日期" width="180">
+              <template #default="scope">
+                {{ scope.row.nextPaymentDate ? scope.row.nextPaymentDate : '-' }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="nextPaymentAmount" label="下笔回款金额" width="180">
+              <template #default="scope">
+                {{ scope.row.nextPaymentAmount ? '¥' + scope.row.nextPaymentAmount.toFixed(2) : '-' }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="typeName" label="合同类型" width="180"></el-table-column>
+            <el-table-column prop="name" label="状态" width="180">
+              <template #default="scope">
+                <span :style="fixedData.stateList[scope.row.status].color">{{
+                  fixedData.stateList[scope.row.status].label }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="indate" label="创建时间" width="180"></el-table-column>
+            <el-table-column label="操作" fixed="right" width="200" v-permission="['contractAdd', 'auditContractData']">
+              <template #default="scope">
+                <el-button link type="primary" size="large" v-permission="['contractAdd']"
+                  @click="addEditor(scope.row)">编辑</el-button>
+                <el-button link type="success" size="large" v-permission="['auditContractData']"
+                  v-if="(scope.row.status == 1 || scope.row.status == 3)"
+                  @click="contractApproved(scope.row, 0)">通过</el-button>
+                <el-button link type="danger" size="large" v-permission="['auditContractData']"
+                  v-if="(scope.row.status == 1 || scope.row.status == 3)"
+                  @click="contractRejection(scope.row, 2)">驳回</el-button>
+                <el-button link type="danger" size="large" v-permission="['contractAdd']"
+                  @click="deleteContract(scope.row)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div class="flex justify-end pt-3">
+          <el-pagination layout="total, prev, pager, next, sizes" v-model:current-page="paging.pageIndex"
+            v-model:page-size="paging.pageSize" :page-sizes="[10, 20, 30, 50, 100]" @size-change="handleSizeChange"
+            @current-change="handleCurrentChange" :total="paging.pageTotal" />
+        </div>
+      </div>
+    </div>
+
+    <!-- 弹窗 -->
+    <el-dialog v-model="allVisable.addEditorVisable" width="1000" :show-close="false" top="10vh">
+      <template #header="{ close, titleId, titleClass }">
+        <div class="flex justify-between items-center border-b pb-3 dialog-header">
+          <h4 :id="titleId">{{ allDynamicText.addEditorText }}</h4>
+          <div>
+            <el-button type="primary" :loading="allLoading.addEditorSaveLoading" @click="addEditorSave">保存</el-button>
+            <el-button @click="allVisable.addEditorVisable = false">取消</el-button>
+          </div>
+        </div>
+      </template>
+      <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
+        <div class="ml-4 mr-4">
+          <GenerateForm ref="generateForm" :data="contractTemplate" :value="editForm" :key="generateFormKey" />
+          <AddEditor ref="addEditorRef" :payment-plan="addEditorVisableForm.paymentPlan"
+            :enclosure="addEditorVisableForm.enclosure" :enclosure-detele="addEditorVisableForm.enclosureDetele">
+          </AddEditor>
+        </div>
+      </div>
+    </el-dialog>
+
+    <el-dialog v-model="allVisable.rejectDataVisable" width="1000" :show-close="false" top="10vh">
+      <template #header="{ close, titleId, titleClass }">
+        <div class="flex justify-between items-center border-b pb-3 dialog-header">
+          <h4 :id="titleId">驳回合同</h4>
+          <div>
+            <el-button type="primary" @click="rejectTheContract">驳回</el-button>
+            <el-button @click="allVisable.rejectDataVisable = false">取消</el-button>
+          </div>
+        </div>
+      </template>
+      <div class="overflow-y-auto scroll-bar pt-3">
+        <div class="ml-4 mr-4">
+          <el-input v-model="rejectData.msg" style="width: 100%" :autosize="{ minRows: 6, maxRows: 6 }" type="textarea"
+            placeholder="请输入驳回原因" />
+        </div>
+      </div>
+    </el-dialog>
+
+    <!-- 导入 -->
+    <el-dialog v-model="allVisable.importVisible" width="680" :show-close="false" top="10vh">
+      <template #header="{ close, titleId, titleClass }">
+        <div class="flex justify-between items-center border-b pb-3 dialog-header">
+          <h4 :id="titleId">导入合同</h4>
+          <div class="flex">
+            <el-upload class="upload-demo mr-3" :limit="1" :show-file-list="false" accept=".xlsx"
+              :http-request="importBusiness">
+              <el-button type="primary" :loading="allLoading.importLoading">导入</el-button>
+            </el-upload>
+            <el-button @click="allVisable.importVisible = false">取消</el-button>
+          </div>
+        </div>
+      </template>
+      <div class="p-8">
+        <div class="ml-4 mr-4">
+          <div class="flex items-center">1、点击下载 <el-link type="primary"
+              @click="downloadTemplate('Contract', allDynamicText.importText)">{{ allDynamicText.importText }}</el-link></div>
+          <div class="mt-4">2、填写合同名称必填</div>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped></style>

+ 0 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contract/type.d.ts


+ 9 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/home.vue

@@ -6,7 +6,7 @@
       </el-header>
       <el-main>
         <router-view v-slot="{ Component }">
-          <transition name="router_animate">
+          <transition name="ranimate">
             <component :is="Component" />
           </transition>
         </router-view>
@@ -95,4 +95,12 @@ onMounted(async () => {
   overflow: auto;
   background: $backColor;
 }
+
+.ranimate-enter-active {
+  animation: fadeIn .6s;
+}
+
+.ranimate-leave-active {
+  animation: fadeOut .3s;
+}
 </style>

BIN
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/product/客户管家logo@3x.png


+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/type.d.ts

@@ -38,7 +38,7 @@ type componentType = "success" | "info" | "warning" | "error"
 
 type TaskResponse = { saveLoading: saveLoadingType, isClose: boolean, message?: string }
 
-type optionType = { value: number | string, label: string | number }
+type optionType = { value: number | string, label: string | number, color?: string }
 
 type sexTYpe = { value: number | string, label: string }
 

+ 6 - 5
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts

@@ -240,25 +240,26 @@ export function getTemplateKey(list: Array<any>) {
 }
 
 /**
- * 设置模板数据禁用
+ * 设置模板数据禁用和启用
  * @param list 模板数据
- * @param fieldList 需要禁用的字段
+ * @param fieldList 需要操作的字段
+ * @param filedFlag Boolean
  * @returns 
  */
-export function setTemplateDataDisable(list: Array<any>, fieldList: Array<any>) {
+export function setTemplateDataDisable(list: Array<any>, fieldList: Array<any>, filedFlag: boolean = true) {
   let result = list;
   result.forEach((item: any) => {
     if (item.type === 'grid') {
       item.columns.forEach((column: any) => {
         column.list.forEach((subItem: any) => {
           if (fieldList.includes(subItem.model)) {
-            subItem.options.disabled = true
+            subItem.options.disabled = filedFlag
           }
         });
       });
     } else {
       if (fieldList.includes(item.model)) {
-        item.options.disabled = true;
+        item.options.disabled = filedFlag;
       }
     }
   })

+ 2 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/vite.config.ts

@@ -4,8 +4,8 @@ import vue from '@vitejs/plugin-vue';
 import { resolve } from 'path';
 
 // const target = 'http://192.168.2.28:10010';
-// const target = 'http://192.168.2.17:10010';
-const target = "http://127.0.0.1:10010";
+const target = 'http://192.168.2.17:10010';
+// const target = "http://127.0.0.1:10010";
 // const target = "http://192.168.2.178:10010";
 // const target = 'http://47.101.180.183:10010';
 

+ 27 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/BusinessOpportunityController.java

@@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -318,6 +319,32 @@ public class BusinessOpportunityController {
         msg.setData(r);
         return msg;
 
+    }
+
+    @RequestMapping("getAllByStage")
+    public Object getAllByStage(HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        List<BusinessOpportunity> list = new ArrayList<>();
+        boolean isAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看全部商机");
+        boolean isNotAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看负责部门商机");
+        if (isAll) {
+            list = boMapper.getAllList(user.getCompanyId());
+        } else if (isNotAll) {
+            list = boMapper.getAllList1(user);
+        } else {
+            list = boMapper.getAllList2(user);
+        }
+        HttpRespMsg msg = new HttpRespMsg();
+        List<Stage> stageList = stageService.list(new QueryWrapper<Stage>().eq("company_id", user.getCompanyId()));
+        HashMap<String, Object> map = new HashMap<>();
+        for (Stage stage : stageList) {
+            List<BusinessOpportunity> collect = list.stream().filter(l -> l.getStageId() != null && l.getStageId().equals(stage.getId())).collect(Collectors.toList());
+            HashMap<String, Object> hashMap = new HashMap<>();
+            map.put(stage.getName(),collect);
+        }
+        msg.setData(map);
+        return msg;
+
     }
     @RequestMapping("deleterList")
     public HttpRespMsg deleterList(BusinessOpportunity bo, HttpServletRequest request) {

Разлика између датотеке није приказан због своје велике величине
+ 3 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WeiXinCorpController.java


+ 3 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/SysDict.java

@@ -59,7 +59,7 @@ public class SysDict extends Model<SysDict> {
         return this.id;
     }
 
-    public static final String[] TYPE_LIST=new String[]{"ClueSources","CustomLevel","CustomIndustry","CustomSources","BusinessStage","ProductType","ProductUnit","OrderType"};
+    public static final String[] TYPE_LIST=new String[]{"ClueSources","CustomLevel","CustomIndustry","CustomSources","BusinessStage","ProductType","ProductUnit","OrderType","ContractType"};
 
     public static List<Map<String,Object>> getSysDictList(){
         List<Map<String,Object>> itemList=new ArrayList<>();
@@ -83,6 +83,8 @@ public class SysDict extends Model<SysDict> {
                     break;
                 case "OrderType":map.put("name","订单类型");
                     break;
+                case "ContractType":map.put("name","合同类型");
+                    break;
             }
             itemList.add(map);
         }

+ 1 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/ActionLogMapper.java

@@ -19,7 +19,7 @@ public interface ActionLogMapper extends BaseMapper<ActionLog> {
 
     @Select("select a.*,u.`name` userName from action_log a left join `user` u on a.user_id = u.id where code = 'clue' and item_id = #{id}")
     List<ActionLog> selectByInfoList(Integer id);
-    @Select("select a.*,u.`name` userName from action_log a left join `user` u on a.user_id = u.id where code = 'business' and item_id = #{id}")
+    @Select("select a.*,u.`name` userName from action_log a left join `user` u on a.user_id = u.id where code = 'business' and item_id = #{id} order by creat_time desc")
     List<ActionLog> selectByInfoListBusiness(Integer id);
     @Select("select a.*,u.`name` userName from action_log a left join `user` u on a.user_id = u.id where code = 'custom' and item_id = #{id}")
     List<ActionLog> selectByInfoListCustom(Integer id);

+ 2 - 2
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ContractDocumentServiceImpl.java

@@ -63,7 +63,7 @@ public class ContractDocumentServiceImpl extends ServiceImpl<ContractDocumentMap
     public HttpRespMsg fileUpload(HttpServletRequest request, @RequestParam Integer ContractId, @RequestParam(required=false) Integer folderId, @RequestParam("file") MultipartFile[] files) {
         HttpRespMsg msg = new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("token"));
-        List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "管理全部合同");
+        List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部合同");
         if(functionContractList.size() <= 0){
             msg.setError(MessageUtils.message("access.operationError"));
             return msg;
@@ -205,7 +205,7 @@ public class ContractDocumentServiceImpl extends ServiceImpl<ContractDocumentMap
     public HttpRespMsg fileDelete(HttpServletRequest request, ContractFileDelVO contractFileDelVo) {
         HttpRespMsg msg = new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("Token"));
-        List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "管理全部合同");
+        List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部合同");
         if(functionContractList.size() <= 0){
             msg.setError(MessageUtils.message("access.deleteError"));
             return msg;

+ 7 - 10
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ContractServiceImpl.java

@@ -80,8 +80,6 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> i
     private ContractLogMapper contractLogMapper;
     @Resource
     private InformationMapper informationMapper;
-    @Resource
-    private CompanyDingdingMapper companyDingdingMapper;
     @Value(value = "${upload.path}")
     private String path;
     @Autowired
@@ -111,7 +109,6 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> i
             String token = request.getHeader("token");
             User user = userMapper.selectById(token);
             WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", user.getCompanyId()));
-            CompanyDingding dingding = companyDingdingMapper.selectOne(new LambdaQueryWrapper<CompanyDingding>().eq(CompanyDingding::getCompanyId, user.getCompanyId()));
             List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部合同");
             if(functionContractList.size() <= 0){
                 httpRespMsg.setError(MessageUtils.message("access.viewError"));
@@ -193,14 +190,17 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> i
                 contractPageVO.setPayment(contract.getPayment());
                 contractPageVO.setNextPaymentDate(contract.getNextPaymentDate());
                 contractPageVO.setNextPaymentAmount(contract.getNextPaymentAmount());
+                contractPageVO.setPlate1(contract.getPlate1());
+                contractPageVO.setPlate2(contract.getPlate2());
+                contractPageVO.setPlate3(contract.getPlate3());
+                contractPageVO.setPlate4(contract.getPlate4());
+                contractPageVO.setPlate5(contract.getPlate5());
                 if (contract.getStatus().equals(2)){
                     for (Map<String, Object> contractLog : contractLogs) {
                         if (contractLog.get("contractId").toString().equals(contract.getId().toString())){
                             String msg = "";
                             if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
                                 msg = "$userName=" + contractLog.get("operateCorpWxId") + "$" + contractLog.get("msg");
-                            }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
-                                msg = "$userName=" + contractLog.get("operateName") + "$" + contractLog.get("msg");
                             }else {
                                 msg = contractLog.get("operateName") + "" + contractLog.get("msg");
                             }
@@ -256,7 +256,6 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> i
             return httpRespMsg;
         }
         WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", user.getCompanyId()));
-        CompanyDingding dingding = companyDingdingMapper.selectOne(new QueryWrapper<CompanyDingding>().eq("company_id", user.getCompanyId()));
         HttpRespMsg contractPage = getContractPage(request, null, null, number, name, typeName, status, startDate, endDate, paymentStartDate, paymentEndDate, secTypeId,customerOrg,finishStatus);
         HashMap<String, Object> resultDate = (HashMap<String, Object>) contractPage.data;
         List<ContractPageVO> data = (List<ContractPageVO>)resultDate.get("data");
@@ -291,8 +290,6 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> i
             item.add(contract.getEndDate()==null?"":contract.getEndDate()+"");
             if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
                 contract.setCreatorName("$userName=" + contract.getCreatorWxCorpId() + "$");
-            }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
-                contract.setCreatorName("$userName=" + contract.getCreatorName() + "$");
             }else {
                 contract.setCreatorName(contract.getCreatorName());
             }
@@ -426,7 +423,7 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> i
     public HttpRespMsg editContract(HttpServletRequest request, Contract contract, ContractCustom custom, String paymentListStr) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("token"));
-        List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "管理全部合同");
+        List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部合同");
         if(functionContractList.size() <= 0){
             httpRespMsg.setError(MessageUtils.message("access.operationError"));
             return httpRespMsg;
@@ -1149,7 +1146,7 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> i
         HttpRespMsg msg = new HttpRespMsg();
         String token = request.getHeader("TOKEN");
         User user = userMapper.selectById(token);
-        List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "管理全部合同");
+        List<SysRichFunction> functionContractList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部合同");
         if(functionContractList.size() <= 0){
             msg.setError(MessageUtils.message("access.deleteError"));
             return msg;

+ 2 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SysFormServiceImpl.java

@@ -105,6 +105,8 @@ public class SysFormServiceImpl extends ServiceImpl<SysFormMapper, SysForm> impl
                 break;
             case "Business":title = company.getCompanyName()+"_商机导入模板";
                 break;
+            case "Contract":title = company.getCompanyName()+"_合同导入模板";
+                break;
             default:title="";
         }
         String result=MessageUtils.message("file.excelScu");

+ 2 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SysRoleServiceImpl.java

@@ -82,7 +82,8 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
             "负责部门线索数据","全部线索数据","负责部门商机数据","全部商机数据",
             "负责部门销售数据","全部销售数据","负责部门任务数据","全部任务数据",
             "查看全部产品数据","查看负责部门产品数据",
-            "查看负责部门订单","查看全部订单"
+            "查看负责部门订单","查看全部订单",
+            "查看全部合同","审核合同","新增合同","合同类型管理","导入合同","导出合同"
             };
     public static final HashMap<String, String[]> functionMaps = new HashMap();
     static {

Разлика између датотеке није приказан због своје велике величине
+ 3 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/UserServiceImpl.java


+ 6 - 8
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/BusinessOpportunityMapper.xml

@@ -279,32 +279,30 @@
         GROUP BY s.id
     </select>
     <select id="getAllList" resultType="com.management.platform.entity.BusinessOpportunity">
-        select
-        id,
-        name
+        select *
         from business_opportunity
         where company_id = #{companyId}
         and is_delete = 0
+        order by create_time desc
     </select>
 
     <select id="getAllList1" resultType="com.management.platform.entity.BusinessOpportunity">
-        select
-        id,
-        name
+        select *
         from business_opportunity
         where company_id = #{companyId}
         and is_delete = 0
         and (incharger_id in
         (SELECT id from `user` WHERE department_id = (SELECT department_id from `user` WHERe id = #{id}))
         or incharger_id is null)
+        order by create_time desc
     </select>
 
     <select id="getAllList2" resultType="com.management.platform.entity.BusinessOpportunity">
-        select id,
-               name
+        select *
         from business_opportunity
         where company_id = #{companyId}
           and is_delete = 0
           and (incharger_id = #{id} or incharger_id is null)
+        order by create_time desc
     </select>
 </mapper>

+ 83 - 6
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/ContractMapper.xml

@@ -20,16 +20,93 @@
         <result column="remarks" property="remarks" />
         <result column="payment" property="payment" />
         <result column="sec_type_id" property="secTypeId" />
-        <result column="plate1" property="plate1" />
-        <result column="plate2" property="plate2" />
-        <result column="plate3" property="plate3" />
-        <result column="plate4" property="plate4" />
-        <result column="plate5" property="plate5" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_id, creator_id, start_date, end_date, number, project_id, name, amounts, type_id, checkerId, status, indate, remarks, payment, sec_type_id, plate1, plate2, plate3, plate4, plate5
+        id, company_id, creator_id, start_date, end_date, number, project_id, name, amounts, type_id, checkerId, status, indate, remarks, payment, sec_type_id
     </sql>
+    <select id="selectContract" resultType="com.management.platform.entity.Contract">
+        select contract.id,contract.company_id,contract.creator_id,contract.number,contract.name,contract.amounts,contract.type_id,sys_dict.name typeName,contract.status,contract.indate,contract.remarks,
+        contract.start_date as startDate,contract.end_date as endDate,contract_type_sec.sec_type_name,contract.sec_type_id,
+        user.name as creatorName,user.corpwx_userid as creatorWxCorpId, contract.project_id, contract.payment, cp.pay_date AS next_payment_date, cp.amount AS next_payment_amount,
+        contract.plate1,contract.plate2,contract.plate3,contract.plate4,contract.plate5
+        from contract
+        left join sys_dict on contract.type_id = sys_dict.id
+        left join contract_type_sec on contract.sec_type_id = contract_type_sec.id
+        LEFT JOIN (SELECT contract_id, pay_date, amount FROM contract_payment WHERE is_payed = 0 GROUP BY contract_id) cp ON cp.contract_id=contract.id
+        left join user
+        on contract.creator_id = user.id
+        left join contract_custom cc on cc.contract_id=contract.id
+        where contract.company_id = #{companyId}
+        <if test="number!=null">
+            and number like CONCAT('%', #{number}, '%')
+        </if>
+        <if test="name!=null">
+            and contract.name like CONCAT('%', #{name}, '%')
+        </if>
+        <if test="typeName!=null">
+            and sys_dict.name = #{typeName}
+        </if>
+        <if test="secTypeId!=null">
+            and contract.sec_type_id = #{secTypeId}
+        </if>
+        <if test="status!=null">
+            and status = #{status}
+        </if>
+        <if test="startDate!=null and endDate !=null">
+            and indate between #{startDate} and #{endDate}
+        </if>
+        <if test="paymentStartDate != null and paymentEndDate != null">
+            and cp.pay_date between #{paymentStartDate} and #{paymentEndDate}
+        </if>
+        <if test="finishStatus!=null">
+            and cc.finish_status = #{finishStatus}
+        </if>
+        <if test="customerOrg!=null">
+            and cc.customer_org like  CONCAT('%', #{customerOrg}, '%')
+        </if>
+        order by contract.number desc
+        <if test="pageStart!=null and pageSize!=null">
+            limit #{pageStart},#{pageSize}
+        </if>
+    </select>
 
+    <select id="selectContractCnt" resultType="java.lang.Long">
+        select count(1) from contract
+        left join contract_type
+        on contract.type_id = contract_type.id
+        LEFT JOIN (SELECT contract_id, pay_date, amount FROM contract_payment WHERE is_payed = 0 GROUP BY contract_id) cp ON cp.contract_id=contract.id
+        left join user
+        on contract.creator_id = user.id
+        left join contract_custom cc on cc.contract_id=contract.id
+        where contract.company_id = #{companyId}
+        <if test="number!=null">
+            and number like CONCAT('%', #{number}, '%')
+        </if>
+        <if test="name!=null">
+            and contract.name like CONCAT('%', #{name}, '%')
+        </if>
+        <if test="typeName!=null">
+            and contract_type.id = #{typeName}
+        </if>
+        <if test="secTypeId!=null">
+            and contract.sec_type_id = #{secTypeId}
+        </if>
+        <if test="status!=null">
+            and status = #{status}
+        </if>
+        <if test="startDate!=null and endDate !=null">
+            and indate between #{startDate} and #{endDate}
+        </if>
+        <if test="paymentStartDate != null and paymentEndDate != null">
+            and cp.pay_date between #{paymentStartDate} and #{paymentEndDate}
+        </if>
+        <if test="finishStatus!=null">
+            and cc.finish_status = #{finishStatus}
+        </if>
+        <if test="customerOrg!=null">
+            and cc.customer_org like  CONCAT('%', #{customerOrg}, '%')
+        </if>
+    </select>
 </mapper>

+ 18 - 6
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/FeishuInfoController.java

@@ -1,28 +1,25 @@
 package com.management.platform.controller;
 
 
-import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.google.gson.JsonObject;
 import com.management.platform.constant.Constant;
 import com.management.platform.entity.*;
+import com.management.platform.entity.bo.LoadTaskResultBO;
 import com.management.platform.entity.vo.UserVO;
 import com.management.platform.mapper.*;
 import com.management.platform.service.*;
 import com.management.platform.service.impl.FeishuInfoServiceImpl;
 import com.management.platform.util.*;
-import com.taobao.api.ApiException;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.*;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
-
 import org.springframework.web.client.RestTemplate;
 
 import javax.annotation.Resource;
@@ -32,7 +29,10 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 /**
@@ -93,6 +93,18 @@ public class FeishuInfoController {
     @Resource
     DepartmentOtherManagerService departmentOtherManagerService;
 
+    @PostMapping("/loadTaskResult")
+    public HttpRespMsg loadTaskResult(@RequestBody @Validated LoadTaskResultBO queryBO,HttpServletRequest request){
+        HttpRespMsg msg = new HttpRespMsg();
+        try {
+            msg = feishuInfoService.loadTaskResult(queryBO,request);
+        } catch (Exception e) {
+            e.printStackTrace();
+            msg.setError(MessageUtils.message("other.error"));
+            return msg;
+        }
+        return msg;
+    }
 
     /**
      * 初始化内部应用的系统数据

+ 6 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/FinanceController.java

@@ -2,7 +2,10 @@ package com.management.platform.controller;
 
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.management.platform.entity.*;
+import com.management.platform.entity.Company;
+import com.management.platform.entity.FinanceFixedcolname;
+import com.management.platform.entity.FinanceTblcuscol;
+import com.management.platform.entity.TimeType;
 import com.management.platform.mapper.CompanyMapper;
 import com.management.platform.mapper.FinanceTblcuscolMapper;
 import com.management.platform.mapper.TimeTypeMapper;
@@ -10,7 +13,6 @@ import com.management.platform.mapper.WxCorpInfoMapper;
 import com.management.platform.service.ExcelExportService;
 import com.management.platform.service.FinanceFixedcolnameService;
 import com.management.platform.service.FinanceService;
-import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.MessageUtils;
 import com.management.platform.util.UserNotFoundException;
@@ -100,8 +102,9 @@ public class FinanceController {
     //导出分摊数据
     @RequestMapping("/exportData")
     public HttpRespMsg exportData(@RequestParam(required = false, defaultValue = "0") Integer groupByCategory,
+                                  @RequestParam(defaultValue = "0")Integer onlyTotal,
                                   @RequestParam String date, Boolean assignNoProUser,HttpServletRequest request) {
-        return financeService.exportData(groupByCategory, date, assignNoProUser, request);
+        return financeService.exportData(groupByCategory,onlyTotal, date, assignNoProUser, request);
     }
 
     /**

+ 43 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/FinanceExcludeProjectController.java

@@ -0,0 +1,43 @@
+package com.management.platform.controller;
+
+import com.management.platform.entity.bo.AddOrUpdateFinanceExcludeProjectBO;
+import com.management.platform.service.FinanceExcludeProjectService;
+import com.management.platform.util.HttpRespMsg;
+import com.management.platform.util.MessageUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("/financeExcludeProject")
+public class FinanceExcludeProjectController {
+
+    @Resource
+    private FinanceExcludeProjectService financeExcludeProjectService;
+
+    @GetMapping("/getProjects")
+    public HttpRespMsg getProjects(@RequestParam(value = "useYM")String useYM, HttpServletRequest request){
+        HttpRespMsg msg = new HttpRespMsg();
+        try {
+            msg = financeExcludeProjectService.getProjects(useYM,request);
+        } catch (Exception e) {e.printStackTrace();
+            msg.setError(MessageUtils.message("other.error"));
+            return msg;
+        }
+        return msg;
+    }
+
+    @PostMapping("/addOrUpdateProjects")
+    public HttpRespMsg addProjects(AddOrUpdateFinanceExcludeProjectBO addBO, HttpServletRequest request){
+        HttpRespMsg msg = new HttpRespMsg();
+        try {
+            msg = financeExcludeProjectService.addOrUpdateProjects(addBO,request);
+        } catch (Exception e) {e.printStackTrace();
+            msg.setError(MessageUtils.message("other.error"));
+            return msg;
+        }
+        return msg;
+    }
+
+}

+ 61 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/FinanceExcludeProject.java

@@ -0,0 +1,61 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+import java.time.LocalDateTime;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class FinanceExcludeProject extends Model<FinanceExcludeProject> {
+    private static final long serialVersionUID=1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**项目id*/
+    @TableField("project_id")
+    private Integer projectId;
+
+    /**
+     * 公司表外键
+     */
+    @TableField("company_id")
+    private Integer companyId;
+
+    /**
+     * 创建人id
+     */
+    @TableField("create_by")
+    private String createBy;
+
+
+    /**
+     * 创建日期
+     */
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 年月
+     * */
+    @TableField("use_ym")
+    @NotBlank(message = "年月不能为空")
+    @Pattern(regexp = "^[0-9]{4}-[0-9]{2}$",message = "年月格式错误")
+    private String useYM;
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/AddOrUpdateFinanceExcludeProjectBO.java

@@ -0,0 +1,16 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+@Data
+public class AddOrUpdateFinanceExcludeProjectBO {
+//    @NotEmpty(message = "项目不能为空")
+    private String[] projects;
+
+//    @NotBlank(message = "年月不能为空")
+//    @Pattern(regexp = "^[0-9]{4}-[0-9]{2}$",message = "年月格式错误")
+    private String useYM;
+
+    /**0非全部 1是全部*/
+    private int isAll;
+}

+ 47 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuClockCheckRecord.java

@@ -0,0 +1,47 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+@Data
+public class FeishuClockCheckRecord {
+    /**用户 ID,对应employee_type*/
+    private String user_id;
+    /**记录创建者 ID,对应employee_type*/
+    private String creator_id;
+    /**打卡位置名称信息*/
+    private String location_name;
+    /**打卡时间,精确到秒的时间戳*/
+    private String check_time;
+    /**打卡备注*/
+    private String comment;
+    /**考勤内部的打卡记录ID(导入时此参数无效)*/
+    private String record_id;
+    /**打卡 Wi-Fi 的 SSID*/
+    private String ssid;
+    /**打卡 Wi-Fi 的 MAC 地址*/
+    private String bssid;
+    /**是否为外勤打卡*/
+    private boolean is_field;
+    /**是否为 Wi-Fi 打卡*/
+    private boolean is_wifi;
+    /**记录生成方式
+     * 0:用户打卡
+     * 1:管理员修改
+     * 2:用户补卡
+     * 3:系统自动生成
+     * 4:下班免打卡
+     * 5:考勤机
+     * 6:极速打卡
+     * 7:考勤开放平台导入*/
+    private int type;
+    /**打卡照片列表*/
+    private String[] photo_urls;
+    /**打卡设备ID(只支持小程序打卡,导入时无效)*/
+    private String device_id;
+    /**打卡结果。目前仅返回 PendingApproval,表示待生效*/
+    private String check_result;
+    /**用户导入的外部打卡记录ID*/
+    private String external_id;
+    /**唯一幂等键*/
+    private String idempotent_id;
+}

+ 15 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuClockData.java

@@ -0,0 +1,15 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FeishuClockData {
+    /**打卡任务列表*/
+    private List<FeishuClockUserTaskResult> user_task_results;
+    /**无效用户 ID 列表,对应employee_type*/
+    private String[] invalid_user_ids;
+    /**没有权限用户 ID 列表,对应employee_type*/
+    private String[] unauthorized_user_ids;
+}

+ 80 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuClockTaskRecord.java

@@ -0,0 +1,80 @@
+package com.management.platform.entity.bo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+@Data
+public class FeishuClockTaskRecord {
+    /**上班打卡记录 ID*/
+    private String check_in_record_id;
+    /**上班打卡记录*/
+    private FeishuClockCheckRecord check_in_record;
+    /**下班打卡记录 ID*/
+    private String check_out_record_id;
+    /**下班打卡记录*/
+    private FeishuClockCheckRecord check_out_record;
+    /**上班打卡结果
+     * NoNeedCheck:无需打卡
+     * SystemCheck:系统打卡(已弃用)
+     * Normal:正常
+     * Early:早退
+     * Late:迟到
+     * Lack:缺卡
+     * Todo:未打卡*/
+    private String check_in_result;
+    /**下班打卡结果
+     * NoNeedCheck:无需打卡
+     * SystemCheck:系统打卡(已弃用)
+     * Normal:正常
+     * Early:早退
+     * Late:迟到
+     * Lack:缺卡
+     * Todo:未打卡*/
+    private String check_out_result;
+    /**上班打卡结果补充
+     None:无
+     ManagerModification:管理员修改
+     CardReplacement:补卡通过
+     ShiftChange:换班
+     Travel:出差
+     Leave:请假
+     GoOut:外出
+     CardReplacementApplication:补卡申请中
+     FieldPunch:外勤打卡*/
+    private String check_in_result_supplement;
+    /**下班打卡结果补充
+     None:无
+     ManagerModification:管理员修改
+     CardReplacement:补卡通过
+     ShiftChange:换班
+     Travel:出差
+     Leave:请假
+     GoOut:外出
+     CardReplacementApplication:补卡申请中
+     FieldPunch:外勤打卡*/
+    private String check_out_result_supplement;
+    /**上班打卡时间,秒级时间戳*/
+    private String check_in_shift_time;
+    /**下班打卡时间,秒级时间戳*/
+    private String check_out_shift_time;
+    /**班次类型,0正常,1加班班次*/
+    private int task_shift_type;
+
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" ,timezone="GMT+8")
+    private Date checkInShiftDateTime;
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
+    private Date checkOutShiftDateTime;
+
+    public void setDateTime() {
+        Long l1 = Long.valueOf(this.check_in_shift_time) * 1000L;
+        Long l2 = Long.valueOf(this.check_out_shift_time) * 1000L;
+        this.checkInShiftDateTime = new Date(l1);
+        this.checkOutShiftDateTime = new Date(l2);
+    }
+
+}

+ 17 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuClockUserTaskResult.java

@@ -0,0 +1,17 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FeishuClockUserTaskResult {
+    private String result_id;
+    private String user_id;
+    private String employee_name;
+    private int day;
+    private String group_id;
+    private String shift_id;
+    private List<FeishuClockTaskRecord> records;
+
+}

+ 11 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuFlexibleRule.java

@@ -0,0 +1,11 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+@Data
+public class FeishuFlexibleRule {
+    /**下班最多可早走,单位:分钟(上班早到几分钟,下班可早走几分钟)*/
+    private int flexible_early_minutes;
+    /**上班最多可晚到,单位:分钟(上班晚到几分钟,下班须晚走几分钟)*/
+    private int flexible_late_minutes;
+}

+ 25 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuPunchTimeRule.java

@@ -0,0 +1,25 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+@Data
+public class FeishuPunchTimeRule {
+    /**上班时间,格式为hh:mm*/
+    private String on_time;
+    /**下班时间,格式为hh:mm。如果是第二天凌晨2点, 则为26:00*/
+    private String off_time;
+//    /**晚到多久记为迟到,单位:分钟*/
+//    private int late_minutes_as_late;
+    /**晚到多久记为缺卡,单位:分钟*/
+    private int late_minutes_as_lack;
+//    /**最早多久可打上班卡,单位:分钟*/
+//    private int on_advance_minutes;
+//    /**早退多久记为早退,单位:分钟*/
+//    private int early_minutes_as_early;
+    /**早退多久记为缺卡,单位:分钟*/
+    private int early_minutes_as_lack;
+//    /**最晚多久可打下班卡,单位:分钟*/
+//    private int off_delay_minutes;
+//    /**晚到多久记为严重迟到,单位:分钟*/
+//    private int late_minutes_as_serious_late;
+}

+ 11 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuRestTimeRule.java

@@ -0,0 +1,11 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+@Data
+public class FeishuRestTimeRule {
+    /**休息开始,格式为mm:ss*/
+    private String rest_begin_time;
+    /**休息结束,格式为mm:ss*/
+    private String rest_end_time;
+}

+ 33 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/FeishuShiftResult.java

@@ -0,0 +1,33 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FeishuShiftResult {
+    /**班次ID*/
+    private String shift_id;
+    /**班次名称*/
+    private String shift_name;
+    /**打卡次数*/
+    private int punch_times;
+    /**是否弹性打卡*/
+    private boolean is_flexible;
+    /**弹性打卡时间,单位:分钟,设置【上班最多可晚到】与【下班最多可早走】时间,如果不设置flexible_rule则生效*/
+    private Integer flexible_minutes;
+    /**班次ID*/
+    private List<FeishuFlexibleRule> flexible_rule;
+    /**不需要打下班卡*/
+    private boolean no_need_off;
+    /**班次ID*/
+    private List<FeishuPunchTimeRule> punch_time_rule;
+//    /**晚走晚到规则(仅飞书人事企业版可用)*/
+//    private String late_off_late_on_rule;
+    /**休息规则*/
+    private List<FeishuRestTimeRule> rest_time_rule;
+//    /**加班规则(仅飞书人事企业版可用)*/
+//    private String overtime_rule;
+//    /**是否允许在非打卡时段申请打卡*/
+//    private boolean allow_punch_approval;
+}

+ 24 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/LoadTaskResultBO.java

@@ -0,0 +1,24 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+@Data
+public class LoadTaskResultBO {
+    @NotBlank(message = "appId不能为空")
+    private String appId;
+    /**employee_no 或 employee_id 列表,长度不超过 50*/
+    @Size(min = 1, max = 50,message = "工号数组大小有误")
+    private String[] userIds;
+    /**工时管家用户id*/
+//    @Size(min = 1,max = 50,message = "用户id数组大小有误")
+    private String[] wtUserIds;
+    /**查询的起始工作日,格式为yyyyMMdd*/
+    private Integer checkDateFrom;
+    /**查询的结束工作日,格式为yyyyMMdd*/
+    private Integer checkDateTo;
+    /**是否需要加班班段打卡结果*/
+    private Boolean needOverTimeResult = false;
+}

+ 11 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/FinanceExcludeProjectMapper.java

@@ -0,0 +1,11 @@
+package com.management.platform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.entity.FinanceExcludeProject;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface FinanceExcludeProjectMapper extends BaseMapper<FinanceExcludeProject> {
+    void batchInsert(@Param("projects") List<FinanceExcludeProject> projects);
+}

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/FinanceMapper.java

@@ -1,7 +1,7 @@
 package com.management.platform.mapper;
 
-import com.management.platform.entity.Finance;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.entity.Finance;
 
 /**
  * <p>

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/FinanceProjectsMapper.java

@@ -2,6 +2,9 @@ package com.management.platform.mapper;
 
 import com.management.platform.entity.FinanceProjects;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +16,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface FinanceProjectsMapper extends BaseMapper<FinanceProjects> {
 
+    List<FinanceProjects> getExcludeFinanceProjects(@Param("companyId") Integer companyId, @Param("yearMonth") String yearMonth);
 }

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java

@@ -124,6 +124,8 @@ public interface ReportMapper extends BaseMapper<Report> {
 
     List<Map<String, Object>> getRealProjectTime(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate, Integer companyId);
 
+    List<Map<String, Object>> getRealProjectTimeNew(@Param("ym")String ym,@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate, Integer companyId);
+
     List<Map<String, Object>> getReportFillStatus(String startDate, String endDate, String userId);
 
     List<Map<String, Object>> getDepartmentDetailByState(@Param("departmentIds") List<Integer> departmentIds,
@@ -133,6 +135,8 @@ public interface ReportMapper extends BaseMapper<Report> {
 
     List<Map<String, Object>> getUserWorkingTimeByRange(Integer companyId, String startDate, String endDate);
 
+    List<Map<String, Object>> getUserWorkingTimeByRangeWithExclude(Integer companyId,String yearMonth, String startDate, String endDate);
+
     List<Report> selectSimpleTime(Integer companyId, String startDate, String endDate);
 
     List<Map<String, Object>> getDeptImportAuditList(@Param("companyId") Integer companyId,

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ExcelExportService.java

@@ -16,4 +16,8 @@ public interface ExcelExportService {
     public HttpRespMsg exportTranForwx(WxCorpInfo wxCorpInfo, CompanyDingding dingding,String title) throws Exception;
     void testAdd(String jobId);
     HttpRespMsg exportGeneralExcelForExpense(WxCorpInfo wxCorpInfo, CompanyDingding dingding, String fileName, List<List<String>> allList, List<Map> mapList, String path) throws Exception;
+
+    HttpRespMsg exportMultiSheetGeneralExcelTotal(WxCorpInfo wxCorpInfo, CompanyDingding dingding, String title, List<List<String>>[] multiSheetList, String downloadPath, String[] sheetsName) throws Exception;
+
+    HttpRespMsg exportGeneralExcelTotal(WxCorpInfo wxCorpInfo, CompanyDingding dingding, String fileName, List<List<String>> allList, String path) throws Exception ;
 }

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/FeishuInfoService.java

@@ -4,8 +4,10 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.management.platform.entity.FeishuInfo;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.entity.bo.LoadTaskResultBO;
 import com.management.platform.util.HttpRespMsg;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.List;
 import java.util.Map;
 
@@ -39,4 +41,6 @@ public interface FeishuInfoService extends IService<FeishuInfo> {
     void batchSendCardMessage(FeishuInfo feishuInfo, List<String> userIds, String sendTypeName, Map<String, String> templateVariableDetail, String pushUrl) throws Exception;
 
     HttpRespMsg initSuperManager(String corpid, String name);
+
+    HttpRespMsg loadTaskResult(LoadTaskResultBO queryBO, HttpServletRequest request);
 }

+ 15 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/FinanceExcludeProjectService.java

@@ -0,0 +1,15 @@
+package com.management.platform.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.entity.FinanceExcludeProject;
+import com.management.platform.entity.bo.AddOrUpdateFinanceExcludeProjectBO;
+import com.management.platform.util.HttpRespMsg;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface FinanceExcludeProjectService extends IService<FinanceExcludeProject> {
+    HttpRespMsg getProjects(String useYM, HttpServletRequest request);
+
+    HttpRespMsg addOrUpdateProjects(AddOrUpdateFinanceExcludeProjectBO addBO, HttpServletRequest request);
+
+}

+ 2 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/FinanceService.java

@@ -1,14 +1,11 @@
 package com.management.platform.service;
 
-import com.management.platform.entity.Finance;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.entity.Finance;
 import com.management.platform.util.HttpRespMsg;
-import com.management.platform.util.UserNotFoundException;
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
 
 /**
  * <p>
@@ -24,7 +21,7 @@ public interface FinanceService extends IService<Finance> {
 
     HttpRespMsg importData(Integer companyId, String yearMonth, Boolean syncUserCost, Boolean syncHistoryReport, MultipartFile multipartFile, HttpServletRequest request) throws Exception;
 
-    HttpRespMsg exportData(Integer groupByCategory, String date, Boolean assignNoProUser, HttpServletRequest request);
+    HttpRespMsg exportData(Integer groupByCategory,Integer onlyTotal, String date, Boolean assignNoProUser, HttpServletRequest request);
 
     HttpRespMsg getTimeCost(String yearMonth, Boolean assignNoProUser, HttpServletRequest request);
 

+ 203 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ExcelExportServiceImpl.java

@@ -70,6 +70,132 @@ public class ExcelExportServiceImpl implements ExcelExportService {
         }
     }
 
+    @Override
+    public HttpRespMsg exportGeneralExcelTotal(WxCorpInfo wxCorpInfo, CompanyDingding dingding, String title, List<List<String>> list, String downloadPath) throws Exception {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        if (title.contains("/")) {
+            //文件名不能含有路径,得替换掉
+            title = title.replace("/", "@");
+        }
+        if (title.contains("\\")) {
+            //文件名不能含有路径,得替换掉
+            title = title.replace("\\", "@");
+        }
+        String resp = ExcelUtil.exportGeneralExcelTotal(title, list, downloadPath);
+        String fileUrlSuffix = title + ".xlsx";
+        if(wxCorpInfo != null && wxCorpInfo.getSaasSyncContact() == 1){
+            FileSystemResource fileSystemResource = new FileSystemResource(path+fileUrlSuffix);
+//            String md5 = DigestUtils.md5Hex(FileUtil.readBytes(fileSystemResource.getFile()));
+
+            Long checkSize = 20L*1024*1024;
+            if(fileSystemResource.getFile().length() >= checkSize){
+                httpRespMsg.setError("文件过大,请缩短查询日期范围");
+                return httpRespMsg;
+            }
+            String jobId = "";
+            String mediaId = wxCorpInfoService.getTranslationMediaId(fileUrlSuffix);
+            jobId = wxCorpInfoService.syncTranslation(wxCorpInfo.getCorpid(),mediaId,fileUrlSuffix, null);
+//            if(fileSystemResource.getFile().length() >= checkSize){
+//                String tmpFileJobId = wxCorpInfoService.getTranslationJobId(fileUrlSuffix,md5,wxCorpInfo);
+//                if(!StringUtils.isEmpty(tmpFileJobId)){
+//                    String mediaId = wxCorpInfoService.getAsyncJobResult(tmpFileJobId,wxCorpInfo);
+//                    jobId = wxCorpInfoService.syncTranslation(wxCorpInfo.getCorpid(),mediaId,fileUrlSuffix, null);
+//                }
+//            }else{
+//                String mediaId = wxCorpInfoService.getTranslationMediaId(fileUrlSuffix);
+//                jobId = wxCorpInfoService.syncTranslation(wxCorpInfo.getCorpid(),mediaId,fileUrlSuffix, null);
+//            }
+
+            System.out.println("上传待转译文件到企业微信, jobId==" + jobId);
+//            if(StringUtils.isEmpty(jobId)){
+//                httpRespMsg.setError("转义jobId为空,请联系管理员");
+//                return httpRespMsg;
+//            }
+            int i = 0;
+            String syncTranslationResult = null;
+            /**
+             * 异步上传转译文件的任务完成时会触发回调,在WeiXinCorpController中的commonDevCallbackPost实现了对回调的处理,存储到corpwxJobResult表中
+             * 此处轮询查询本地数据库,检测到有任务的回调数据时继续执行查询操作
+             */
+            long t = System.currentTimeMillis();
+            while (i < 40) {
+                if (i < 10) {
+                    Thread.sleep(300);
+                } else if (i < 20){
+                    Thread.sleep(1000);
+                } else {
+                    Thread.sleep(3000);
+                }
+                System.out.println("i=="+i+", "+LocalDateTime.now());
+                CorpwxJobResult corpwxJobResult = corpwxJobCenter.get(jobId);
+                if (corpwxJobResult != null) {
+                    if (corpwxJobResult.getErrCode() == 0) {
+                        syncTranslationResult = wxCorpInfoService.getSyncTranslationResult(jobId);
+                        corpwxJobCenter.remove(jobId);
+                    } else {
+                        long t2 = System.currentTimeMillis();
+                        System.out.println("222企业微信转译报错:"+corpwxJobResult.getErrMsg()+",耗时:" + (t2 - t) + "ms");
+                        httpRespMsg.setError(corpwxJobResult.getErrMsg());
+                        return httpRespMsg;
+                    }
+                    break;
+                }
+                i++;
+            }
+            if (syncTranslationResult != null) {
+                long t2 = System.currentTimeMillis();
+                System.out.println("企业微信转译文件后地址是:"+syncTranslationResult+",耗时:" + (t2 - t) + "ms");
+                httpRespMsg.data = syncTranslationResult;
+            } else {
+                //httpRespMsg.setError("处理超时...");
+                httpRespMsg.setError(MessageUtils.message("request.outTime"));
+            }
+        }else if(dingding != null && dingding.getContactNeedTranslate() == 1){
+            User user = userMapper.selectById(request.getHeader("token"));
+            String mediaId = dingDingService.getTranslationMediaId(fileUrlSuffix,dingding);
+            String jobId = dingDingService.syncTranslation(mediaId,fileUrlSuffix, user.getDingdingUnionid(),dingding);
+            System.out.println("上传待转译文件到钉钉, jobId==" + jobId);
+            int i = 0;
+            String syncTranslationResult = null;
+            /**
+             * 异步上传转译文件的任务完成时会触发回调,在DingDingController中的callback实现了对回调的处理,存储到corpddJobCenter缓存中
+             * 此处轮询查询本地数据库,检测到有任务的回调数据时继续执行查询操作
+             */
+            long t = System.currentTimeMillis();
+            while (i < 30) {
+                if (i < 10) {
+                    Thread.sleep(300);
+                } else if (i < 20){
+                    Thread.sleep(1000);
+                } else {
+                    Thread.sleep(3000);
+                }
+                System.out.println("i=="+i+", "+LocalDateTime.now());
+                Integer status = corpddJobCenter.get(jobId);
+                if (status != null) {
+                    if (status == 1) {
+                        syncTranslationResult = dingDingService.getSyncTranslationResult(jobId,dingding);
+                        corpddJobCenter.remove(jobId);
+                    }
+                    break;
+                }
+                i++;
+            }
+            if (syncTranslationResult != null) {
+                long t2 = System.currentTimeMillis();
+                System.out.println("钉钉转译文件后地址是:"+syncTranslationResult+",耗时:" + (t2 - t) + "ms");
+                httpRespMsg.data = syncTranslationResult;
+            } else {
+                //httpRespMsg.setError("处理超时...");
+                httpRespMsg.setError(MessageUtils.message("request.outTime"));
+            }
+        }else {
+            httpRespMsg.data = resp;
+        }
+        return httpRespMsg;
+    }
+
+
     public HttpRespMsg exportGeneralExcelByTitleAndList(WxCorpInfo wxCorpInfo, CompanyDingding dingding, String title, List<List<String>> list, String downloadPath) throws Exception {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         if (title.contains("/")) {
@@ -448,6 +574,83 @@ public class ExcelExportServiceImpl implements ExcelExportService {
         return httpRespMsg;
     }
 
+    @Override
+    public  HttpRespMsg exportMultiSheetGeneralExcelTotal(WxCorpInfo wxCorpInfo, CompanyDingding dingding, String title, List<List<String>>[] multiSheetList, String downloadPath, String[] sheetsName) throws Exception {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        String resp = ExcelUtil.exportMultiSheetGeneralExcelTotal(title,multiSheetList, downloadPath,sheetsName);
+        if (title.contains("/")) {
+            //文件名不能含有路径,得替换掉
+            title = title.replace("/", "@");
+        }
+        if (title.contains("\\")) {
+            //文件名不能含有路径,得替换掉
+            title = title.replace("\\", "@");
+        }
+        String fileUrlSuffix = title + ".xlsx";
+        if(wxCorpInfo != null && wxCorpInfo.getSaasSyncContact() == 1){
+            String mediaId = wxCorpInfoService.getTranslationMediaId(fileUrlSuffix);
+            String jobId = wxCorpInfoService.syncTranslation(wxCorpInfo.getCorpid(),mediaId,fileUrlSuffix, null);
+            int i = 0;
+            String syncTranslationResult = null;
+            /**
+             * 异步上传转译文件的任务完成时会触发回调,在WeiXinCorpController中的commonDevCallbackPost实现了对回调的处理,存储到corpwxJobResult表中
+             * 此处轮询查询本地数据库,检测到有任务的回调数据时继续执行查询操作
+             */
+            while (i < 10) {
+                Thread.sleep(300);
+                CorpwxJobResult corpwxJobResult = corpwxJobCenter.get(jobId);
+                if (corpwxJobResult != null) {
+                    if (corpwxJobResult.getErrCode() == 0) {
+                        syncTranslationResult = wxCorpInfoService.getSyncTranslationResult(jobId);
+                        corpwxJobCenter.remove(jobId);
+                    } else {
+                        httpRespMsg.setError(corpwxJobResult.getErrMsg());
+                        return httpRespMsg;
+                    }
+                    break;
+                }
+                i++;
+            }
+            if (syncTranslationResult != null) {
+                httpRespMsg.data = syncTranslationResult;
+            } else {
+                //httpRespMsg.setError("处理超时...");
+                httpRespMsg.setError(MessageUtils.message("request.outTime"));
+            }
+        }else if(dingding != null && dingding.getContactNeedTranslate() == 1){
+            User user = userMapper.selectById(request.getHeader("token"));
+            String mediaId = dingDingService.getTranslationMediaId(fileUrlSuffix,dingding);
+            String jobId = dingDingService.syncTranslation(mediaId,fileUrlSuffix,user.getDingdingUnionid(), dingding);
+            int i = 0;
+            String syncTranslationResult = null;
+            /**
+             * 异步上传转译文件的任务完成时会触发回调,在DingDingController中的callback实现了对回调的处理,存储到corpddJobCenter缓存中
+             * 此处轮询查询本地数据库,检测到有任务的回调数据时继续执行查询操作
+             */
+            while (i < 10) {
+                Thread.sleep(300);
+                Integer status = corpddJobCenter.get(jobId);
+                if (status != null) {
+                    if (status == 1) {
+                        syncTranslationResult = dingDingService.getSyncTranslationResult(jobId,dingding);
+                        corpddJobCenter.remove(jobId);
+                    }
+                    break;
+                }
+                i++;
+            }
+            if (syncTranslationResult != null) {
+                httpRespMsg.data = syncTranslationResult;
+            } else {
+                //httpRespMsg.setError("处理超时...");
+                httpRespMsg.setError(MessageUtils.message("request.outTime"));
+            }
+        }else {
+            httpRespMsg.data = resp;
+        }
+        return httpRespMsg;
+    }
+
 
     public  HttpRespMsg exportMultiSheetGeneralExcelByTitleAndList(WxCorpInfo wxCorpInfo, CompanyDingding dingding,String title, List<List<String>>[] multiSheetList, String downloadPath,String[] sheetsName) throws Exception {
         HttpRespMsg httpRespMsg = new HttpRespMsg();

+ 198 - 11
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FeishuInfoServiceImpl.java

@@ -1,37 +1,39 @@
 package com.management.platform.service.impl;
 
-import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.google.gson.JsonObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.constant.Constant;
 import com.management.platform.entity.*;
+import com.management.platform.entity.bo.*;
 import com.management.platform.mapper.FeishuInfoMapper;
 import com.management.platform.mapper.FeishuSendMapper;
 import com.management.platform.mapper.SysRoleMapper;
 import com.management.platform.mapper.UserMapper;
-import com.management.platform.service.DepartmentOtherManagerService;
 import com.management.platform.service.FeishuInfoService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.MD5Util;
 import com.management.platform.util.MessageUtils;
-import com.management.platform.util.UserAgentUtils;
-import org.apache.http.client.methods.HttpGet;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.*;
 import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
-import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.client.RestTemplate;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.view.RedirectView;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.time.ZoneOffset;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -63,6 +65,12 @@ public class FeishuInfoServiceImpl extends ServiceImpl<FeishuInfoMapper, FeishuI
         public static final String BATCH_SEND_MESSAGE="https://open.feishu.cn/open-apis/message/v4/batch_send/";
         /*获取单个用户信息*/
         public static final String GET_USER_INFO="https://open.feishu.cn/open-apis/contact/v3/users/:user_id";
+        /**获取多用户打卡结果*/
+        public static final String GET_USERS_CLOCK_RES="https://open.feishu.cn/open-apis/attendance/v1/user_tasks/query";
+        /**获取多用户审批结果*/
+        public static final String GET_USERS_APPROVE_RES = "https://open.feishu.cn/open-apis/attendance/v1/user_approvals/query";
+
+        public static final String GET_USERS_SHIFT_RES = "https://open.feishu.cn/open-apis/attendance/v1/shifts/";
 
 
         @Resource
@@ -74,6 +82,9 @@ public class FeishuInfoServiceImpl extends ServiceImpl<FeishuInfoMapper, FeishuI
         @Resource
         SysRoleMapper sysRoleMapper;
 
+        @Autowired
+        private RestTemplate restTemplate;
+
         @Override
         public String getAppAccessToken(FeishuInfo feishuInfo) {
                 String result="";
@@ -482,4 +493,180 @@ public class FeishuInfoServiceImpl extends ServiceImpl<FeishuInfoMapper, FeishuI
                 return msg;
         }
 
+        @Override
+        public HttpRespMsg loadTaskResult(LoadTaskResultBO queryBO, HttpServletRequest request) {
+                HttpRespMsg msg = new HttpRespMsg();
+//                User user = userMapper.selectById(request.getHeader("TOKEN"));
+//                if(null == user){
+//                        msg.setError("token有误,用户不存在");
+//                        return msg;
+//                }
+//                Integer companyId = user.getCompanyId();
+
+                LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
+                lqw.select(User::getId,User::getJobNumber).isNotNull(User::getJobNumber)
+                        .in(User::getId,Arrays.asList(queryBO.getWtUserIds()));
+                List<User> users = userMapper.selectList(lqw);
+                if(CollectionUtils.isEmpty(users)){
+                        msg.setError("未查询到飞书工号");
+                        return msg;
+                }
+                Map<String, String> jobNumIdMap = users.stream().collect(Collectors.toMap(User::getJobNumber, User::getId));
+                String[] feishuUserIdArr = users.stream().map(User::getJobNumber).toArray(String[]::new);
+                queryBO.setUserIds(feishuUserIdArr);
+
+                FeishuClockData feishuClockData = getFeishuClockData(queryBO);
+                if(null == feishuClockData){
+                        msg.setError("飞书获取用户考勤数据失败,请联系管理员");
+                        return msg;
+                }
+                for (FeishuClockUserTaskResult userTaskResult : feishuClockData.getUser_task_results()) {
+                        for (FeishuClockTaskRecord record : userTaskResult.getRecords()) {
+                                record.setDateTime();
+                        }
+                }
+//                                msg.setData(userTaskResults);
+
+                //用户id、day、考勤组、班次、打卡记录list
+
+                //获取所有班次信息
+                List<String> allShiftList = feishuClockData.getUser_task_results().stream()
+                        .map(FeishuClockUserTaskResult::getShift_id)
+                        .distinct().collect(Collectors.toList());
+                //获取所有班次对应的上下班、弹性、休息时间
+                Map<String,FeishuShiftResult> shiftResultMap = new HashMap<>(allShiftList.size());
+                for (String shiftId : allShiftList) {
+                        FeishuShiftResult shiftResult = getFeishuShiftResult(queryBO.getAppId(), shiftId);
+                        if(null != shiftResult){
+                                shiftResultMap.put(shiftId,shiftResult);
+                        }
+                }
+
+                List<Map<String,Object>> res = new ArrayList();
+                SimpleDateFormat HMFormat = new SimpleDateFormat("HH:mm");
+                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                for (FeishuClockUserTaskResult userTaskResult : feishuClockData.getUser_task_results()) {
+                        int day = userTaskResult.getDay();//日期 yyyyMMdd
+                        String dayStr = String.valueOf(day).substring(0,4)+"-"+String.valueOf(day).substring(4,6)+"-"+String.valueOf(day).substring(6,7);
+                        //获取正常上班时间段
+                        Optional<FeishuClockTaskRecord> first = userTaskResult.getRecords().stream()
+                                .filter(t -> 0 == t.getTask_shift_type())
+                                .findFirst();
+                        if(first.isPresent()){
+                                Map<String,Object> map = new HashMap<>();
+                                FeishuClockTaskRecord record = first.get();
+                                UserFvTime userFvTime = new UserFvTime();
+                                //计算时长 需考虑考勤--待处理
+
+                                FeishuShiftResult shiftResult = shiftResultMap.get(userTaskResult.getShift_id());
+                                //判断弹性上下班
+                                Integer flexibleMinutes = null;
+                                if(shiftResult.is_flexible()){
+                                        //可以弹性上下班
+                                        flexibleMinutes = shiftResult.getFlexible_minutes();
+                                        if(null == flexibleMinutes || 0 == flexibleMinutes){
+                                                //从规则中获取
+                                                if(CollectionUtils.isNotEmpty(shiftResult.getFlexible_rule())){
+                                                        //此处仅获取第一个作为规则
+                                                        flexibleMinutes = shiftResult.getFlexible_rule().get(0).getFlexible_early_minutes();
+                                                }
+                                        }
+
+                                }
+                                flexibleMinutes = null==flexibleMinutes?0:flexibleMinutes;
+                                //获取打卡规则
+                                FeishuPunchTimeRule feishuPunchTimeRule = null;
+                                if(CollectionUtils.isNotEmpty(shiftResult.getPunch_time_rule())){
+                                        //仅获取第一个
+                                        feishuPunchTimeRule  = shiftResult.getPunch_time_rule().get(0);
+                                }
+                                /**
+                                 * 弹性时间 0 非0
+                                 * 0 仅判断是否缺卡
+                                 * 非0 先判断弹性,再判断是否缺卡
+                                 * */
+                                if(null != feishuPunchTimeRule){
+                                        LocalDateTime.parse(feishuPunchTimeRule.getOn_time(),DateTimeFormatter.ofPattern("HH:mm"));
+                                }
+
+
+                                Instant checkInInstant = Instant.ofEpochSecond(Long.parseLong(record.getCheck_in_record().getCheck_time()));
+                                LocalDateTime checkInLocalTime = LocalDateTime.ofInstant(checkInInstant, ZoneId.of("GMT+8"));
+                                Instant checkOutInstant = Instant.ofEpochSecond(Long.parseLong(record.getCheck_out_record().getCheck_time()));
+                                LocalDateTime checkOutLocalTime = LocalDateTime.ofInstant(checkOutInstant, ZoneId.of("GMT+8"));
+
+                                System.out.println("checkInInstant=== "+checkInLocalTime+",checkOutLocalTime=== "+checkOutLocalTime);
+
+
+
+
+                                long secondCost = Long.parseLong(record.getCheck_out_record().getCheck_time())
+                                        -Long.parseLong(record.getCheck_in_record().getCheck_time());
+//                                                long hourCost = BigDecimal.valueOf(secondCost / (60 * 60))
+//                                                        .setScale(0, RoundingMode.UP).longValue();
+                                float hourCost = BigDecimal.valueOf(secondCost / (60 * 60f)).setScale(2, RoundingMode.HALF_UP).floatValue();
+                                userFvTime.setWorkHours(hourCost);
+                                userFvTime.setWorkDate(LocalDate.parse(String.valueOf(userTaskResult.getDay()), DateTimeFormatter.ofPattern("yyyyMMdd")));
+                                //需要关联user表job_number
+                                userFvTime.setUserId(jobNumIdMap.get(userTaskResult.getUser_id()));
+                                Date checkInTime = new Date(Long.parseLong(record.getCheck_in_record().getCheck_time()) * 1000);
+                                Date checkOutTime = new Date(Long.parseLong(record.getCheck_out_record().getCheck_time()) * 1000);
+                                userFvTime.setStartTime(HMFormat.format(checkInTime));
+                                userFvTime.setStartTime(HMFormat.format(checkOutTime));
+
+                                map.put("data",userFvTime);
+                                map.put("startDateTime",format.format(checkInTime));
+                                map.put("endDateTime",format.format(checkOutTime));
+                                res.add(map);
+                        }
+                }
+                msg.setData(res);
+                return msg;
+        }
+
+
+        public FeishuClockData getFeishuClockData(LoadTaskResultBO queryBO){
+                FeishuClockData result = null;
+                HttpHeaders headers = new HttpHeaders();
+                MediaType type = MediaType.parseMediaType("application/json; charset=utf-8");
+                headers.setContentType(type);
+                headers.add("Authorization","Bearer "+getTenantAccessToken(queryBO.getAppId()));
+
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("user_ids",queryBO.getUserIds());
+                jsonObject.put("check_date_from",queryBO.getCheckDateFrom());
+                jsonObject.put("check_date_to",queryBO.getCheckDateTo());
+                jsonObject.put("need_overtime_result",queryBO.getNeedOverTimeResult());
+
+                HttpEntity<JSONObject> httpEntity = new HttpEntity<>(jsonObject, headers);
+
+                ResponseEntity<String> responseEntity = restTemplate.exchange(GET_USERS_CLOCK_RES+"?employee_type=employee_id"
+                        , HttpMethod.POST,httpEntity,String.class);
+                if (responseEntity.getStatusCode() == HttpStatus.OK) {
+                        String resp = responseEntity.getBody();
+                        JSONObject respJson = JSONObject.parseObject(resp);
+                        if (respJson.getInteger("code")==0){
+                                result = JSONObject.toJavaObject(respJson.getJSONObject("data"), FeishuClockData.class);
+                        }
+                }
+                return result;
+        }
+
+        public FeishuShiftResult getFeishuShiftResult(String appId,String shiftId){
+                FeishuShiftResult result = null;
+                HttpHeaders shiftHttpHeader = new HttpHeaders();
+                shiftHttpHeader.add("Authorization","Bearer "+getTenantAccessToken(appId));
+                HttpEntity<JSONObject> shiftEntity = new HttpEntity<>(null, shiftHttpHeader);
+                ResponseEntity<String> tmpResponse = restTemplate.exchange(GET_USERS_SHIFT_RES+shiftId
+                        , HttpMethod.GET,shiftEntity,String.class);
+                if (tmpResponse.getStatusCode() == HttpStatus.OK) {
+                        String tmpResp = tmpResponse.getBody();
+                        JSONObject tmpRespJson = JSONObject.parseObject(tmpResp);
+                        if (tmpRespJson.getInteger("code")==0){
+                                result = JSONObject.toJavaObject(tmpRespJson.getJSONObject("data"), FeishuShiftResult.class);
+                        }
+                }
+                return result;
+        }
+
 }

+ 133 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceExcludeProjectServiceImpl.java

@@ -0,0 +1,133 @@
+package com.management.platform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.entity.FinanceExcludeProject;
+import com.management.platform.entity.Project;
+import com.management.platform.entity.User;
+import com.management.platform.entity.bo.AddOrUpdateFinanceExcludeProjectBO;
+import com.management.platform.mapper.FinanceExcludeProjectMapper;
+import com.management.platform.mapper.ProjectMapper;
+import com.management.platform.mapper.UserMapper;
+import com.management.platform.service.FinanceExcludeProjectService;
+import com.management.platform.util.HttpRespMsg;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+@Service
+public class FinanceExcludeProjectServiceImpl extends ServiceImpl<FinanceExcludeProjectMapper, FinanceExcludeProject> implements FinanceExcludeProjectService {
+
+    @Resource
+    private FinanceExcludeProjectMapper financeExcludeProjectMapper;
+
+    @Resource
+    private UserMapper userMapper;
+
+    @Resource
+    private ProjectMapper projectMapper;
+
+
+    public static final String DATE_TIME_FORMAT = "^[0-9]{4}-[0-9]{2}$";
+
+    public static final Pattern DATE_TIME_CHECK = Pattern.compile(DATE_TIME_FORMAT);
+
+    @Override
+    public HttpRespMsg getProjects(String useYM, HttpServletRequest request) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        Map<String,Object> map = new HashMap<>();
+        User user = userMapper.selectById(request.getHeader("TOKEN"));
+        if(null ==user){
+            httpRespMsg.setError("登录凭证有误,请联系管理员");
+            return httpRespMsg;
+        }
+        Matcher matcher = DATE_TIME_CHECK.matcher(useYM);
+        boolean res = matcher.find();
+        if(!res){
+            httpRespMsg.setError("年月格式错误");
+            return httpRespMsg;
+        }
+        Integer projectCount = projectMapper.selectCount(new LambdaQueryWrapper<Project>()
+                .eq(Project::getCompanyId, user.getCompanyId()));
+
+        List<FinanceExcludeProject> resList = financeExcludeProjectMapper
+                .selectList(new LambdaQueryWrapper<FinanceExcludeProject>().eq(FinanceExcludeProject::getUseYM,useYM));
+        if(projectCount == resList.size()){
+            map.put("isAll",1);
+        }else{
+            map.put("isAll",0);
+        }
+        map.put("projectList",resList);
+        httpRespMsg.setData(map);
+        return httpRespMsg;
+    }
+
+    @Override
+    @Transactional
+    public HttpRespMsg addOrUpdateProjects(AddOrUpdateFinanceExcludeProjectBO addBO, HttpServletRequest request) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("TOKEN"));
+        if(null ==user){
+            httpRespMsg.setError("登录凭证有误,请联系管理员");
+            return httpRespMsg;
+        }
+        if(null == addBO.getUseYM()){
+            httpRespMsg.setError("年月不能为空");
+            return httpRespMsg;
+        }
+        Matcher matcher = DATE_TIME_CHECK.matcher(addBO.getUseYM());
+        boolean res = matcher.find();
+        if(!res){
+            httpRespMsg.setError("年月格式错误");
+            return httpRespMsg;
+        }
+        List<FinanceExcludeProject> resList = new ArrayList<>();
+        if(1==addBO.getIsAll()){
+            List<Project> allProjects = projectMapper.selectList(new QueryWrapper<Project>().eq("company_id", user.getCompanyId()));
+            List<FinanceExcludeProject> collect = allProjects.stream().map(t -> {
+                FinanceExcludeProject excludeProject = new FinanceExcludeProject();
+                excludeProject.setProjectId(t.getId());
+                excludeProject.setCompanyId(user.getCompanyId());
+                excludeProject.setCreateBy(user.getId());
+                excludeProject.setUseYM(addBO.getUseYM());
+                return excludeProject;
+            }).collect(Collectors.toList());
+            resList = collect;
+        }else{
+//            if(null == addBO.getProjects() || addBO.getProjects().length <=0){
+//                httpRespMsg.setError("项目不能为空");
+//                return httpRespMsg;
+//            }
+            if(null != addBO.getProjects()){
+                for (int i = 0; i < addBO.getProjects().length; i++) {
+                    FinanceExcludeProject excludeProject = new FinanceExcludeProject();
+                    excludeProject.setProjectId(Integer.parseInt(addBO.getProjects()[i]));
+                    excludeProject.setCompanyId(user.getCompanyId());
+                    excludeProject.setCreateBy(user.getId());
+                    excludeProject.setUseYM(addBO.getUseYM());
+                    resList.add(excludeProject);
+                }
+            }
+
+        }
+
+        financeExcludeProjectMapper.delete(new LambdaQueryWrapper<FinanceExcludeProject>()
+                .eq(FinanceExcludeProject::getCompanyId,user.getCompanyId()));
+        if(CollectionUtils.isNotEmpty(resList)){
+            financeExcludeProjectMapper.batchInsert(resList);
+        }
+        return httpRespMsg;
+    }
+
+}

+ 47 - 10
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceServiceImpl.java

@@ -2,6 +2,7 @@ package com.management.platform.service.impl;
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.entity.*;
@@ -10,8 +11,8 @@ import com.management.platform.service.*;
 import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.MessageUtils;
-import com.management.platform.util.UserNotFoundException;
 import com.taobao.api.internal.util.StringUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.io.FileUtils;
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
@@ -89,6 +90,9 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
     @Resource
     private CompanyDingdingMapper companyDingdingMapper;
 
+    @Resource
+    private FinanceExcludeProjectMapper financeExcludeProjectMapper;
+
     @Resource
     private ProjectMapper projectMapper;
     @Value(value = "${upload.path}")
@@ -180,7 +184,8 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             //获取人员该月份填写的日报的总时长
             List<Map<String, Object>> userTimeList = null;
             if (syncUserCost || syncHistoryReport) {
-                userTimeList = reportMapper.getUserWorkingTimeByRange(companyId, startStr, endStr);
+//                userTimeList = reportMapper.getUserWorkingTimeByRange(companyId, startStr, endStr);
+                userTimeList = reportMapper.getUserWorkingTimeByRangeWithExclude(companyId, yearMonth,startStr, endStr);
             }
 
             //设置财务核算薪资模板配置
@@ -554,6 +559,17 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                 }
                 userService.updateBatchById(dbUpUserList);
             }
+            /**添加分摊成本置零*/
+            List<FinanceExcludeProject> financeExcludeProjects = financeExcludeProjectMapper
+                    .selectList(new LambdaQueryWrapper<FinanceExcludeProject>()
+                    .eq(FinanceExcludeProject::getCompanyId, companyId)
+                    .eq(FinanceExcludeProject::getUseYM, yearMonth));
+            List<Integer> projectIds = new ArrayList<>();
+            if(CollectionUtils.isNotEmpty(financeExcludeProjects)){
+                projectIds = financeExcludeProjects.stream()
+                        .map(FinanceExcludeProject::getProjectId).collect(Collectors.toList());
+            }
+
             //如果有必要,更新该月份的日报相关的成本
             if (syncHistoryReport) {
                 List<Report> reportList = reportMapper.selectSimpleTime(companyId, startStr, endStr);
@@ -564,6 +580,9 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                         if (first.isPresent()) {
                             BigDecimal hourCost = first.get().getCost();
                             r.setCost(hourCost.multiply(new BigDecimal(r.getWorkingTime())));
+                            if(projectIds.contains(r.getProjectId())){
+                                r.setCost(new BigDecimal(0));
+                            }
                             r.setCreatorId(null);
                             r.setWorkingTime(null);
                             updateReportList.add(r);
@@ -622,7 +641,7 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
     }
 
     @Override
-    public HttpRespMsg exportData(Integer groupByCategory, String yearMonth, Boolean assignNoProUser, HttpServletRequest request) {
+    public HttpRespMsg exportData(Integer groupByCategory,Integer onlyTotal, String yearMonth, Boolean assignNoProUser, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         try {
             Integer companyId = userMapper.selectById(request.getHeader("Token")).getCompanyId();
@@ -711,7 +730,9 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             LocalDate endDate = LocalDate.parse(dateStr,df);
             endDate = endDate.plusMonths(1);
 
-            List<Map<String, Object>> projectTimeList = reportMapper.getRealProjectTime(startDate, endDate, companyId);
+//            List<Map<String, Object>> projectTimeList = reportMapper.getRealProjectTime(startDate, endDate, companyId);
+            /**添加非分摊项目过滤*/
+            List<Map<String, Object>> projectTimeList = reportMapper.getRealProjectTimeNew(yearMonth,startDate, endDate, companyId);
             //如果定义了可分摊项目的过滤,按照数据处理一下
             CostProjectSetting setting = costProjectSettingMapper.selectOne(new QueryWrapper<CostProjectSetting>().eq("company_id", companyId));
             if (setting != null && setting.getSettingType() > 0) {
@@ -742,7 +763,7 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             HashMap<String, UserWorkTime> userTime = new HashMap<String, UserWorkTime>();
             for (Map<String, Object> map : projectTimeList) {
                 String creatorId = (String) map.get("creatorId");
-                totalCostTime += (Double)map.get("workingTime");
+//                totalCostTime += (Double)map.get("workingTime");
                 if (userTime.get(creatorId) == null) {
                     UserWorkTime user = new UserWorkTime();
                     user.workingTime = new BigDecimal((Double)map.get("workingTime"));
@@ -770,7 +791,9 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             }
 
             ProjectPercentage percentage = null;
-            List<FinanceProjects> financeProjects = financeProjectsMapper.selectList(new QueryWrapper<FinanceProjects>().eq("company_id", companyId).eq("ymonth", yearMonth));
+//            List<FinanceProjects> financeProjects = financeProjectsMapper.selectList(new QueryWrapper<FinanceProjects>().eq("company_id", companyId).eq("ymonth", yearMonth));
+            /**添加非分摊项目筛选*/
+            List<FinanceProjects> financeProjects = financeProjectsMapper.getExcludeFinanceProjects(companyId,yearMonth);
             //填充项目分类
             financeProjects.forEach(f->{
                 Optional<Project> curP = allProjects.stream().filter(p->p.getId().equals(f.getProjectId())).findFirst();
@@ -932,6 +955,7 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                         item.field6 = field6Item;
                         item.field7 = field7Item;
                     }
+                    totalCostTime += workingTime;
                     totalMoneyCost = totalMoneyCost.add(cost);
                     totalSalary = totalSalary.add(salary);
                     totalBonus = totalBonus.add(bonus);
@@ -1541,9 +1565,18 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                             multiSheetDataList[i].add(newRow);
                         }
                     }
-                    return excelExportService.exportMultiSheetGeneralExcelByTitleAndList(wxCorpInfo,dingding,fileName , multiSheetDataList, path, sheetNameList.toArray(new String[0]));
+                    if(0 == onlyTotal){
+                        return excelExportService.exportMultiSheetGeneralExcelTotal(wxCorpInfo,dingding,fileName , multiSheetDataList, path, sheetNameList.toArray(new String[0]));
+
+                    }else{
+                        return excelExportService.exportMultiSheetGeneralExcelByTitleAndList(wxCorpInfo,dingding,fileName , multiSheetDataList, path, sheetNameList.toArray(new String[0]));
+                    }
                 } else {
-                    return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,dingding,fileName , allList, path);
+                    if(0 == onlyTotal){
+                        return excelExportService.exportGeneralExcelTotal(wxCorpInfo,dingding,fileName , allList, path);
+                    }else{
+                        return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,dingding,fileName , allList, path);
+                    }
                 }
             } catch (Exception e) {
                 e.printStackTrace();
@@ -1570,7 +1603,9 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             LocalDate endDate = LocalDate.parse(dateStr,df);
             endDate = endDate.plusMonths(1);
 
-            List<Map<String, Object>> projectTimeList = reportMapper.getRealProjectTime(startDate, endDate, companyId);
+//            List<Map<String, Object>> projectTimeList = reportMapper.getRealProjectTime(startDate, endDate, companyId);
+            /**添加非分摊项目过滤*/
+            List<Map<String, Object>> projectTimeList = reportMapper.getRealProjectTimeNew(yearMonth,startDate, endDate, companyId);
             //如果定义了可分摊项目的过滤,按照数据处理一下
             CostProjectSetting setting = costProjectSettingMapper.selectOne(new QueryWrapper<CostProjectSetting>().eq("company_id", companyId));
             if (setting != null && setting.getSettingType() > 0) {
@@ -1687,7 +1722,9 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             BigDecimal percentTotal = new BigDecimal(0);
             //加上待分摊的无工时项目,如果当前项目列表没有的话
             if (assignNoProUser != null && assignNoProUser && noProjectItem.project != null) {
-                List<FinanceProjects> financeProjects = financeProjectsMapper.selectList(new QueryWrapper<FinanceProjects>().eq("company_id", companyId).eq("ymonth", yearMonth));
+//                List<FinanceProjects> financeProjects = financeProjectsMapper.selectList(new QueryWrapper<FinanceProjects>().eq("company_id", companyId).eq("ymonth", yearMonth));
+                /**添加非分摊项目筛选*/
+                List<FinanceProjects> financeProjects = financeProjectsMapper.getExcludeFinanceProjects(companyId,yearMonth);
                 if (financeProjects.size() == 0) {
                     //httpRespMsg.setError("缺少待分摊项目,请重新设置并保存分摊比例设置");
                     httpRespMsg.setError(MessageUtils.message("project.lackApportion"));

+ 282 - 12
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/ExcelUtil.java

@@ -1,22 +1,12 @@
 package com.management.platform.util;
 
-import com.management.platform.entity.CorpwxJobResult;
-import com.management.platform.entity.ExpenseSheet;
-import com.management.platform.entity.WxCorpInfo;
-import com.management.platform.mapper.CorpwxJobResultMapper;
-import com.management.platform.service.CorpwxJobResultService;
-import com.management.platform.service.WxCorpInfoService;
-import org.apache.poi.hssf.usermodel.*;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellRangeAddress;
-import org.apache.poi.ss.util.RegionUtil;
-import org.apache.poi.xssf.streaming.SXSSFRow;
 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
-import org.apache.poi.xssf.usermodel.*;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFColor;
 import org.springframework.stereotype.Component;
 
-import javax.annotation.PostConstruct;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.util.List;
@@ -179,6 +169,171 @@ public class ExcelUtil {
 //        return "";
     }
 
+    public static String exportGeneralExcelTotal(String title, List<List<String>> list, String downloadPath) {
+        String result="系统提示:Excel文件导出成功!";
+        String fileName= title+".xlsx";
+        try {
+//            response.reset();
+//            response.setHeader("Content-disposition",
+//                "attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));
+//            //设置文件头编码格式
+//            response.setContentType("APPLICATION/OCTET-STREAM;charset=UTF-8");//设置类型
+//            response.setHeader("Cache-Control","no-cache");//设置头
+//            response.setDateHeader("Expires", 0);//设置日期头
+            // 创建工作簿, 换成XSSSF 来支持万以上的数据
+            SXSSFWorkbook workBook = new SXSSFWorkbook();
+//            HSSFWorkbook workBook = new HSSFWorkbook();
+            // 创建工作类
+            Sheet sheet = workBook.createSheet();
+            //设置首行冻结
+            sheet.createFreezePane(0, 1);
+            sheet.setDefaultColumnWidth(16);
+            //设置字体样式
+            Font headFont = workBook.createFont();
+            headFont.setBold(true);
+            headFont.setFontHeightInPoints((short) 10);
+            headFont.setFontName("黑体");
+
+            Font titleFont = workBook.createFont();
+            titleFont.setBold(true);
+            titleFont.setFontHeightInPoints((short) 10);
+            titleFont.setFontName("黑体");
+
+            Font font = workBook.createFont();
+            font.setFontHeightInPoints((short) 10);
+            font.setFontName("宋体");
+
+            //设置单元格样式
+            XSSFCellStyle  headStyle = (XSSFCellStyle) workBook.createCellStyle();
+            headStyle.setFont(headFont);
+            headStyle.setAlignment(HorizontalAlignment.CENTER);
+            headStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
+            headStyle.setWrapText(true);
+            headStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            headStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            headStyle.setBorderTop(BorderStyle.THIN);//上边框
+            headStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            String color = "c0c0c0";    //此处得到的color为16进制的字符串
+            //转为RGB码
+            int r = Integer.parseInt((color.substring(0,2)),16);   //转为16进制
+            int g = Integer.parseInt((color.substring(2,4)),16);
+            int b = Integer.parseInt((color.substring(4,6)),16);
+
+            //自定义cell颜色
+//            HSSFPalette palette = workBook.getCustomPalette();
+            //这里的9是索引
+//            palette.setColorAtIndex((short)9, (byte) r, (byte) g, (byte) b);
+
+            //设置自定义颜色
+            XSSFColor xssfColor = new XSSFColor();
+            byte[] colorRgb = { (short)9, (byte) r, (byte) g, (byte) b };
+            xssfColor.setRGB(colorRgb);
+
+            headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
+            headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 填充模式(和背景颜色成对使用)
+            /*headStyle.setFillForegroundColor(IndexedColors.AQUA.getIndex());*/ //设置自带的颜色
+
+            CellStyle titleStyle = workBook.createCellStyle();
+            titleStyle.setFont(titleFont);
+            titleStyle.setAlignment(HorizontalAlignment.CENTER);
+            titleStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
+            titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);  //填充单元格
+            titleStyle.setFillForegroundColor((short)9);    //填色
+            titleStyle.setWrapText(true);
+            titleStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            titleStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            titleStyle.setBorderTop(BorderStyle.THIN);//上边框
+            titleStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+            CellStyle cellStyle = workBook.createCellStyle();
+            cellStyle.setFont(font);
+            cellStyle.setAlignment(HorizontalAlignment.CENTER);
+            cellStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
+            cellStyle.setWrapText(true);
+            cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
+            cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
+            cellStyle.setBorderTop(BorderStyle.THIN);//上边框
+            cellStyle.setBorderRight(BorderStyle.THIN);//右边框
+            DataFormat dataFormat = workBook.createDataFormat();
+            cellStyle.setDataFormat(dataFormat.getFormat("@"));
+
+            if(list.size() > 0) {
+                //标题(如果需要在EXCEL内容最上面加标题,请打开下面的注释,修改start)
+                /*
+                HSSFRow titleRow = sheet.createRow(0);
+                titleRow.setHeightInPoints(30);
+                HSSFCell titleCell = titleRow.createCell(0);
+                titleCell.setCellStyle(headStyle);
+                titleCell.setCellValue(title);
+                //合并单元格
+                CellRangeAddress cellRangeAddress = new CellRangeAddress(0,0,0, list.get(0).size() - 1);
+                //加入合并单元格对象
+                sheet.addMergedRegion(cellRangeAddress);
+                //使用RegionUtil类为合并后的单元格添加边框
+			    RegionUtil.setBorderBottom(BorderStyle.THIN, cellRangeAddress, sheet); // 下边框
+                RegionUtil.setBorderLeft(BorderStyle.THIN, cellRangeAddress, sheet); // 左边框
+                RegionUtil.setBorderRight(BorderStyle.THIN, cellRangeAddress, sheet); // 有边框
+                RegionUtil.setBorderTop(BorderStyle.THIN, cellRangeAddress, sheet); // 上边框
+                */
+                int start = 0;
+                for (int j = 0; j < list.size(); j++) {
+                    List<String> rowList = list.get(j);
+                    Row row = sheet.createRow(start);
+                    row.setHeightInPoints(24);
+                    for(int i = 0; i < rowList.size(); i++) {
+                        Cell cell = row.createCell(i);
+                        if(start == 0) {
+                            cell.setCellStyle(headStyle);
+                        }else {
+                            cell.setCellStyle(cellStyle);
+                        }
+                        cell.setCellValue(rowList.get(i));
+                    }
+                    start++;
+                }
+//                for(List<String> rowList : list) {
+//                    Row row = sheet.createRow(start);
+//                    row.setHeightInPoints(24);
+//
+//                    for(int i = 0; i < rowList.size(); i++) {
+//                        Cell cell = row.createCell(i);
+//                        if(start == 0) {
+//                            cell.setCellStyle(headStyle);
+//                        }else {
+//                            cell.setCellStyle(cellStyle);
+//                        }
+//                        cell.setCellValue(rowList.get(i));
+//                    }
+//                    start++;
+//                }
+            }
+            //用于非传统ajax;
+//            String headStr = "attachment; filename=\"" + fileName + "\"";
+//            response.setContentType("APPLICATION/OCTET-STREAM");//返回格式为流
+//            response.setHeader("Content-Disposition", headStr);
+//            //普通下载不需要以上三行,注掉即可
+//            OutputStream os = response.getOutputStream();//在线下载
+            File dir = null;
+            dir = new File(downloadPath);
+            // D://cloud/upload 文件上传后所存储的位置,部署到服务器上时配置服务器地址即可
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            FileOutputStream os = new FileOutputStream(downloadPath+fileName);//保存到本地
+            workBook.write(os);
+            os.flush();
+            os.close();
+        }catch(Exception e) {
+            System.out.println(result);
+            e.printStackTrace();
+        }
+        return "/upload/"+fileName;
+//        return "";
+    }
+
+
+
     public static String exportGeneralExcelByTitleAndList2(String title, List<List<String>> list, String downloadPath) {
         String result="系统提示:Excel文件导出成功!";
         String fileName= title+".xlsx";
@@ -473,6 +628,121 @@ public class ExcelUtil {
         return "/upload/"+fileName;
     }
 
+    public static String exportMultiSheetGeneralExcelTotal(String title, List<List<String>>[] multiSheetList, String downloadPath,String[] sheetsName) {
+        String result="系统提示:Excel文件导出成功!";
+        String fileName= title+".xlsx";
+        try {
+            // 创建工作簿
+            SXSSFWorkbook workBook = new SXSSFWorkbook();
+            // 创建工作类
+            for (int sheetIndex=0;sheetIndex< multiSheetList.length; sheetIndex++) {
+                Sheet sheetOne = workBook.createSheet();
+                workBook.setSheetName(sheetIndex,sheetsName[sheetIndex]);
+                sheetOne.setDefaultColumnWidth(16);
+
+                //设置字体样式
+                Font headFont = workBook.createFont();
+                headFont.setBold(true);
+                headFont.setFontHeightInPoints((short) 10);
+                headFont.setFontName("黑体");
+
+                Font titleFont = workBook.createFont();
+                titleFont.setBold(true);
+                titleFont.setFontHeightInPoints((short) 10);
+                titleFont.setFontName("黑体");
+
+                Font font = workBook.createFont();
+                font.setFontHeightInPoints((short) 10);
+                font.setFontName("宋体");
+
+                //设置单元格样式
+                XSSFCellStyle  headStyle = (XSSFCellStyle) workBook.createCellStyle();
+                headStyle.setFont(headFont);
+                headStyle.setAlignment(HorizontalAlignment.CENTER);
+                headStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
+                headStyle.setWrapText(true);
+                headStyle.setBorderBottom(BorderStyle.THIN); //下边框
+                headStyle.setBorderLeft(BorderStyle.THIN);//左边框
+                headStyle.setBorderTop(BorderStyle.THIN);//上边框
+                headStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+                String color = "c0c0c0";    //此处得到的color为16进制的字符串
+                //转为RGB码
+                int r = Integer.parseInt((color.substring(0,2)),16);   //转为16进制
+                int g = Integer.parseInt((color.substring(2,4)),16);
+                int b = Integer.parseInt((color.substring(4,6)),16);
+
+                //自定义cell颜色
+//            HSSFPalette palette = workBook.getCustomPalette();
+                //这里的9是索引
+//            palette.setColorAtIndex((short)9, (byte) r, (byte) g, (byte) b);
+
+                //设置自定义颜色
+                XSSFColor xssfColor = new XSSFColor();
+                byte[] colorRgb = { (byte)118, (byte)147, (byte)60 };
+                xssfColor.setRGB(colorRgb);
+                headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
+                headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 填充模式(和背景颜色成对使用)
+                //cs.setFillForegroundColor(IndexedColors.AQUA.getIndex()); //设置自带的颜色
+
+                CellStyle titleStyle = workBook.createCellStyle();
+                titleStyle.setFont(titleFont);
+                titleStyle.setAlignment(HorizontalAlignment.CENTER);
+                titleStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
+                titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);  //填充单元格
+                titleStyle.setFillForegroundColor((short)9);    //填色
+                titleStyle.setWrapText(true);
+                titleStyle.setBorderBottom(BorderStyle.THIN); //下边框
+                titleStyle.setBorderLeft(BorderStyle.THIN);//左边框
+                titleStyle.setBorderTop(BorderStyle.THIN);//上边框
+                titleStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+                CellStyle cellStyle = workBook.createCellStyle();
+                cellStyle.setFont(font);
+                cellStyle.setAlignment(HorizontalAlignment.CENTER);
+                cellStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
+                cellStyle.setWrapText(true);
+                cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
+                cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
+                cellStyle.setBorderTop(BorderStyle.THIN);//上边框
+                cellStyle.setBorderRight(BorderStyle.THIN);//右边框
+
+                if(multiSheetList[sheetIndex].size() > 0) {
+                    int start = 0;
+                    for (int j = 0; j <=1; j++) {
+                        List<String> rowList = multiSheetList[sheetIndex].get(j);
+                        Row row = sheetOne.createRow(start);
+                        row.setHeightInPoints(24);
+                        for(int i = 0; i < rowList.size(); i++) {
+                            Cell cell = row.createCell(i);
+                            if(start == 0) {
+                                cell.setCellStyle(headStyle);
+                            }else {
+                                cell.setCellStyle(cellStyle);
+                            }
+                            cell.setCellValue(rowList.get(i));
+                        }
+                        start++;
+                    }
+                }
+            }
+
+
+            File dir = new File(downloadPath);
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            FileOutputStream os = new FileOutputStream(downloadPath+fileName);//保存到本地
+            workBook.write(os);
+            os.flush();
+            os.close();
+        }catch(Exception e) {
+            System.out.println(result);
+            e.printStackTrace();
+        }
+        return "/upload/"+fileName;
+    }
+
     public static String exportMultiSheetGeneralExcelByTitleAndList(String title, List<List<String>>[] multiSheetList, String downloadPath,String[] sheetsName) {
         String result="系统提示:Excel文件导出成功!";
         String fileName= title+".xlsx";

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/i18n/messages.properties

@@ -158,7 +158,7 @@ finance.skipData=跳过以下已审核数据:{0}。
 finance.importErrorByAllAdopt=本次数据全部已审核通过,无法导入。
 finance.review=专业审核
 finance.dept=部门审核
-finance.hasApprovedReport=已存在审核人审核通过,无法撤回。请联系管理员操作
+finance.hasApprovedReport=请联系审核人撤销审核后修改
 # pdf相关
 pdf.previewError=该格式不支持在线预览
 # 日报相关

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/i18n/messages_en_US.properties

@@ -158,7 +158,7 @@ finance.skipData=Skipping the following audited data: '{0}'.
 finance.importErrorByAllAdopt=This data has been approved and cannot be imported.
 finance.review=Professional review
 finance.dept=Department review
-finance.hasApprovedReport=Some daily reports have been approved, unable to operate. Please contact your system manager.
+finance.hasApprovedReport=Please contact the auditor to reject your report.
 # pdf相关
 pdf.previewError=This format does not support online preview.
 # 日报相关

+ 13 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FinanceExcludeProjectMapper.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.management.platform.mapper.FinanceExcludeProjectMapper">
+
+    <insert id="batchInsert">
+        insert into finance_exclude_project (project_id, company_id, create_by, use_ym)
+        values
+            <foreach collection="projects" item="project" separator=",">
+                (#{project.projectId},#{project.companyId},#{project.createBy},#{project.useYM})
+            </foreach>
+    </insert>
+</mapper>

+ 10 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FinanceProjectsMapper.xml

@@ -16,5 +16,15 @@
     <sql id="Base_Column_List">
         id, company_id, ymonth, project_id, project_name, project_code
     </sql>
+    <select id="getExcludeFinanceProjects" resultType="com.management.platform.entity.FinanceProjects">
+        select fp.id, fp.company_id, fp.ymonth, fp.project_id, fp.project_name, fp.project_code
+        from finance_projects fp
+        where company_id = #{companyId}
+          and ymonth = #{yearMonth}
+          and not exists(
+            select 1 from finance_exclude_project fep where fep.company_id = fp.company_id and fep.use_ym = #{yearMonth}
+                                                        and fp.project_id = fep.project_id
+        )
+    </select>
 
 </mapper>

+ 29 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml

@@ -988,6 +988,25 @@
         GROUP BY project_id, report.creator_id;
     </select>
 
+    <select id="getRealProjectTimeNew" resultType="java.util.Map">
+        SELECT ifnull(project.id, 0) as projectId, project.project_code as projectCode, IFNULL(project.category_name,'其他') as categoryName,
+        IFNULL(project.project_name,'其他') as project,report.creator_id AS creatorId,sum(working_time) as workingTime, SUM(cost) as cost
+        FROM report
+        left join project on project.id = report.project_id
+        WHERE report.company_id = #{companyId}
+        and state = 1
+        <if test="startDate != null">
+            AND report.create_date &gt;= #{startDate}
+        </if>
+        <if test="endDate != null">
+            AND report.create_date &lt; #{endDate}
+        </if>
+        AND not exists(
+        select 1 from finance_exclude_project fep where fep.company_id = report.company_id and fep.use_ym = #{ym} and fep.project_id = report.project_id
+        )
+        GROUP BY project_id, report.creator_id;
+    </select>
+
     <select id="getUploadThirdReportData" resultType="java.util.Map">
         select IFNULL(p.project_code,'其他') as projectCode,IFNULL(tg.wbs_code,'其他')as wbsCode,DATE_FORMAT(r.create_date, '%Y') as ofyear,
                DATE_FORMAT(r.create_date, '%m') as ofmonth,u.name as userName,u.job_number as jobNumber,d.department_name as departmentName,SUM(r.cost) as cost
@@ -1036,8 +1055,17 @@
                                                                                        AND create_date BETWEEN #{startDate} and #{endDate}
         GROUP BY creator_id
     </select>
+    <select id="getUserWorkingTimeByRangeWithExclude" resultType="java.util.Map">
+        SELECT creator_id as creatorId, SUM(working_time) AS workingTime FROM report r
+        WHERE state = 1 AND creator_id IN(SELECT id FROM user WHERE company_id = #{companyId})
+        AND create_date BETWEEN #{startDate} and #{endDate}
+        AND not exists(
+            select 1 from finance_exclude_project fep where fep.company_id = r.company_id and fep.use_ym = #{yearMonth} and fep.project_id = r.project_id
+        )
+        GROUP BY creator_id
+    </select>
     <select id="selectSimpleTime" resultMap="BaseResultMap">
-        SELECT id,creator_id, working_time, cost FROM report WHERE state = 1 AND creator_id IN(SELECT id FROM user WHERE company_id = #{companyId})
+        SELECT id,creator_id, working_time, cost,project_id FROM report WHERE state = 1 AND creator_id IN(SELECT id FROM user WHERE company_id = #{companyId})
                                                                AND create_date BETWEEN #{startDate} and #{endDate}
     </select>