Procházet zdrojové kódy

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

# Conflicts:
#	fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/AIQuestionServiceImpl.java
zhouyy před 1 měsícem
rodič
revize
e187c47f1e
20 změnil soubory, kde provedl 313 přidání a 77 odebrání
  1. 15 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/api.ts
  2. 124 24
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/components/AIChat.vue
  3. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/index.vue
  4. 17 5
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/biReport/cusTotalAnalysis/api.ts
  5. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/biReport/cusTotalAnalysis/index.vue
  6. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/kanbanView.vue
  7. 4 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue
  8. 1 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/bo/QuestionBO.java
  9. 23 14
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/AIQuestionServiceImpl.java
  10. 10 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserController.java
  11. 1 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java
  12. 25 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Task.java
  13. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/UserVO.java
  14. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/UserService.java
  15. 0 9
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  16. 64 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  17. 12 8
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  18. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/CodeGenerator.java
  19. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TaskMapper.xml
  20. 1 5
      fhKeeper/formulahousekeeper/timesheet/src/views/project/detail.vue

+ 15 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/api.ts

@@ -151,3 +151,18 @@ export interface ChatContent {
 export async function getLatestQuestionList(): Promise<LatestQuestionResponse> {
   return await post('/aiQuestion/getLatestQuestionList');
 }
+
+export interface CustomReport {
+  id: string;
+  storeName: string;
+  relateFormId: string;
+}
+
+export interface CustomReportResponse {
+  code: string;
+  data: CustomReport[];
+}
+
+export async function getCustomReports(): Promise<CustomReportResponse> {
+  return await post('/aiQuestion/getCusReportForm');
+}

+ 124 - 24
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/components/AIChat.vue

@@ -1,6 +1,9 @@
 <template>
-  <div class="border-gray-200 border rounded p-3 h-full flex flex-col" style="min-height: 750px; max-height: 750px;">
-    <div class="text-sm font-medium mb-3">DeepSeek大模型CRM数据分析</div>
+  <div class="border-gray-200 p-3 h-full flex flex-col" style="min-height: 750px; max-height: 750px;">
+    <div class="text-sm font-medium mb-3 flex justify-between">
+      <span style="color: #075985">DeepSeek大模型CRM数据分析</span>
+      <span class="text-gray-500 text-right">开会做PPT利器!</span>
+    </div>
     
     <!-- Chat messages container with fixed height and scrolling -->
     <div class="mb-3 border-gray-200 border rounded overflow-y-auto flex-grow" style="min-height: 0;">
@@ -11,6 +14,9 @@
               <el-icon><ChatLineRound /></el-icon>
             </el-avatar>
             <div class="border-gray-200 border rounded p-2 text-sm max-w-[80%] bg-gray-50 relative">
+              <div class="text-xs text-gray-500 mb-1" v-if="message.dataSource">
+                [{{ message.dataSource }}{{ message.dataName ? '-' + message.dataName : '' }}]
+              </div>
               <div class="markdown-body" v-html="renderMarkdown(message.content)"></div>
               <div v-if="message.loading" class="loading-dots">
                 <span></span>
@@ -18,7 +24,7 @@
                 <span></span>
               </div>
               <el-button 
-                v-if="!message.loading && message.role === 'assistant' && index > 0"
+                v-if="!message.loading && message.role === 'assistant' && index > 0 && '抱歉,请求处理失败,请稍后再试' != message.content && '无数据' != message.content"
                 @click="exportToWord(message.content)"
                 size="small" 
                 type="text" 
@@ -28,7 +34,11 @@
             </div>
           </div>
           <div v-if="message.role === 'user'" class="flex items-start gap-2 justify-end">
-            <div class="border-gray-200 border rounded p-2 text-sm max-w-[80%] bg-blue-50 markdown-body" v-html="renderMarkdown(message.content)">
+            <div class="border-gray-200 border rounded p-2 text-sm max-w-[80%] bg-blue-50">
+              <div class="text-xs text-gray-500 mb-1" v-if="message.dataSource">
+                [{{ message.dataSource }}{{ message.dataName ? '-' + message.dataName : '' }}]
+              </div>
+              <div class="markdown-body" v-html="renderMarkdown(message.content)"></div>
             </div>
             <el-avatar :size="24" class="bg-gray-200 flex items-center justify-center">
               <el-icon><User /></el-icon>
@@ -54,13 +64,30 @@
         <el-select v-model="systemTable" size="small" style="width: 120px">
           <el-option label="线索" value="clue" />
           <el-option label="商机" value="business_opportunity" />
-          <el-option label="客户" value="customer" />
-          <el-option label="联系人" value="contact" />
+          <el-option label="客户" value="custom" />
+          <el-option label="联系人" value="contacts" />
           <el-option label="合同" value="contract" />
-          <el-option label="销售订单" value="order" />
+          <el-option label="销售订单" value="sales_order" />
           <el-option label="产品" value="product" />
         </el-select>
       </div>
+
+      <div v-if="dataSource === 'custom'">
+        <el-select 
+          v-model="selectedReportId" 
+          size="small" 
+          style="width: 200px"
+          placeholder="选择报表"
+          @focus="loadCustomReports"
+        >
+          <el-option 
+            v-for="report in customReports"
+            :key="report.relateFormId"
+            :label="report.storeName"
+            :value="report.relateFormId"
+          />
+        </el-select>
+      </div>
       
       <div v-if="dataSource === 'upload'">
         <el-upload
@@ -73,7 +100,7 @@
         </el-upload>
       </div>
       
-      <div class="ml-auto">
+      <div v-if="dataSource === 'system'" class="ml-auto">
         <span class="text-sm mr-2">时间段</span>
         <el-date-picker
           v-model="dateRange"
@@ -96,6 +123,7 @@
         placeholder="请进行数据分析,给一个总结报告,不超过300字"
         class="flex-1"
         resize="none"
+        @keyup.enter="inputMessage.trim() && !loading ? sendMessage() : null"
       />
       <el-button
         type="primary"
@@ -119,11 +147,13 @@ import {
   askAIQuestion,
   uploadFileApi, 
   getLatestQuestionList,
+  getCustomReports,
   type AIQuestionParams,
   type UploadFileResponse,
   type ChatContent,
   type LatestQuestionResponse,
-  type AIQuestionResponse
+  type AIQuestionResponse,
+  type CustomReport
 } from '../api';
 import { ElMessage } from 'element-plus/es'
 import * as internal from 'stream';
@@ -265,17 +295,21 @@ const exportToWord = async (content: string) => {
 };
 
 type DataSourceType = 'system' | 'custom' | 'upload' | 'free';
-type SystemTableType = 'clue' | 'business_opportunity' | 'customer' | 'contact' | 'contract' | 'order' | 'product';
+type SystemTableType = 'clue' | 'business_opportunity' | 'custom' | 'contacts' | 'contract' | 'sales_order' | 'product';
 
 interface ChatMessage {
   role: 'user' | 'assistant';
   content: string;
   loading?: boolean;
+  dataSource?: string;
+  dataName?: string;
 }
 
 // Data source selection
 const dataSource = ref<DataSourceType>('system');
 const systemTable = ref<SystemTableType>('clue');
+const customReports = ref<CustomReport[]>([]);
+const selectedReportId = ref<string>('');
 const getFirstDayOfMonth = () => {
   const date = new Date();
   return new Date(date.getFullYear(), date.getMonth(), 1);
@@ -313,6 +347,20 @@ const loading = ref(false);
 const messages = reactive<ChatMessage[]>([]);
 const questionId = ref<number | null>(null);
 
+const loadCustomReports = async () => {
+  if (customReports.value.length === 0) {
+    try {
+      const result = await getCustomReports();
+      if (result.code === 'ok') {
+        customReports.value = result.data;
+      }
+    } catch (error) {
+      console.error('Failed to load custom reports:', error);
+      ElMessage.error('加载自定义报表失败');
+    }
+  }
+};
+
 const isSameDay = (dateString: string, compareDate: Date) => {
   // Parse yyyy-MM-dd hh:mm:ss format
   const [datePart] = dateString.split(' ');
@@ -348,30 +396,75 @@ onMounted(async () => {
     console.error('Failed to load chat history:', error);
     messages.push({ role: 'assistant', content: '你好,需要分析查询哪些数据,请交给我' });
   }
+
+  // 触发滚动到底部
+  nextTick(() => {
+    const container = document.querySelector('.overflow-y-auto');
+    if (container) {
+      container.scrollTop = container.scrollHeight;
+    }
+  });
 });
 
 const sendMessage = async () => {
   if (!inputMessage.value.trim() || loading.value) return;
-
+  type DataSourceType = 'system' | 'custom' | 'upload' | 'free';
+  const dataSourceNameMap: Record<DataSourceType, string> = {
+    'system': '系统表',
+    'custom': '自定义报表',
+    'upload': '本地上传', 
+    'free': '自由交流'
+  };
+  const dataSourceMap: Record<DataSourceType, number> = {
+    'system': 1,
+    'custom': 2,
+    'upload': 3, 
+    'free': 4
+  };
+  const sourceName = dataSourceNameMap[dataSource.value]
+  const dataName = dataSource.value === 'system' ? 
+              systemTable.value === 'clue' ? '线索' :
+              systemTable.value === 'business_opportunity' ? '商机' :
+              systemTable.value === 'custom' ? '客户' :
+              systemTable.value === 'contacts' ? '联系人' :
+              systemTable.value === 'contract' ? '合同' :
+              systemTable.value === 'sales_order' ? '销售订单' : '产品' :
+            dataSource.value === 'custom' ? 
+              customReports.value.find(r => r.relateFormId === selectedReportId.value)?.storeName || '' :
+            dataSource.value === 'upload' ? 
+              uploadFile.value?.name || '' : ''
+  const finalContent = '['+sourceName+(dataName?('-'+dataName):'')+']'+'<br>' + inputMessage.value;
+  if (!dataName) {
+    if (dataSource.value == 'custom') {
+        //提示:请选择报表
+        ElMessage.error({
+          message: "请选择报表",
+          type: "error",
+          duration: 2000,
+        })
+        return
+    } else if (dataSource.value == 'upload') {
+        ElMessage.error({
+            message: "请上传数据文件(仅支持excel格式)",
+            type: "error",
+            duration: 2000,
+          })
+          return
+    }
+  }
   loading.value = true;
-  const userMessage: ChatMessage = { role: 'user', content: inputMessage.value };
+  const userMessage: ChatMessage = { role: 'user', content: finalContent };
   messages.push(userMessage);
   const thinkingIndex = messages.length;
   messages.push({ role: 'assistant', content: 'AI正在思考', loading: true });
   
   try {
-    type DataSourceType = 'system' | 'custom' | 'upload' | 'free';
-    const dataSourceMap: Record<DataSourceType, number> = {
-      'system': 1,
-      'custom': 2,
-      'upload': 3, 
-      'free': 4
-    };
-
+    
     const params: AIQuestionParams & { questionId?: number } = {
       questionDataSource: dataSourceMap[dataSource.value],
-      sourceContent: dataSource.value === 'system' ? systemTable.value : '',
-      content: inputMessage.value,
+      sourceContent: dataSource.value === 'system' ? systemTable.value : 
+                   dataSource.value === 'custom' ? selectedReportId.value : '',
+      content: finalContent,
       startDate: dateRange.value[0]?.toISOString().split('T')[0],
       endDate: dateRange.value[1]?.toISOString().split('T')[0],
       url: dataSource.value === 'upload' ? uploadedFilePath.value : ''
@@ -381,7 +474,15 @@ const sendMessage = async () => {
     if (questionId.value) {
       params.questionId = questionId.value;
     }
-
+    // reset message input
+    inputMessage.value = '';
+    //上方聊天记录页面自动滚动到最下面
+    nextTick(() => {
+      const container = document.querySelector('.overflow-y-auto');
+      if (container) {
+        container.scrollTop = container.scrollHeight;
+      }
+    });
     const result = await askAIQuestion(params);
     messages[thinkingIndex] = {
       role: 'assistant',
@@ -398,7 +499,6 @@ const sendMessage = async () => {
     };
   }
 
-  inputMessage.value = '';
   loading.value = false;
   // 触发滚动到底部
   nextTick(() => {

+ 3 - 3
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/index.vue

@@ -185,10 +185,10 @@ watchEffect(() => {
 </script>
 
 <template>
-  <div class="m-5 bg-white min-h-full p-4 rounded">
+  <div class="m-5">
     <section class="flex gap-4">
       <!-- Left side - Original functionality -->
-      <div class="w-1/2 min-w-[650px] flex-shrink-0">
+      <div class="w-1/2 min-w-[650px] flex-shrink-0 bg-white p-4 rounded">
         <div class="flex gap-3 mb-4">
           <div class="w-40">
             <el-select
@@ -465,7 +465,7 @@ watchEffect(() => {
       </div>
       
       <!-- Right side - AI Chat -->
-      <div class="w-1/2">
+      <div class="w-1/2 bg-white p-4 rounded">
         <AIChat />
       </div>
     </section>

+ 17 - 5
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/biReport/cusTotalAnalysis/api.ts

@@ -64,13 +64,25 @@ export const dateCollections = [
     end_time: dayjs().add(-1, 'month').endOf('month').format('YYYY-MM-DD HH:mm:ss')
   },
   {
-    name: '本季度',
-    start_time: dayjs().month(0).format('YYYY-MM-DD HH:mm:ss'),
-    end_time: dayjs().month(2).endOf('month').format('YYYY-MM-DD HH:mm:ss')
+    name: "本季度",
+    start_time: dayjs()
+      .month(Math.floor(dayjs().month() / 3) * 3) // 当前季度的起始月份
+      .startOf("month")
+      .format("YYYY-MM-DD HH:mm:ss"),
+    end_time: dayjs()
+      .month(Math.floor(dayjs().month() / 3) * 3 + 2) // 当前季度的结束月份
+      .endOf("month")
+      .format("YYYY-MM-DD HH:mm:ss"),
   },
   {
     name: '上季度',
-    start_time: dayjs().add(-1, 'year').month(9).format('YYYY-MM-DD HH:mm:ss'),
-    end_time: dayjs().add(-1, 'year').month(11).endOf('month').format('YYYY-MM-DD HH:mm:ss')
+    start_time: dayjs()
+      .month(Math.floor(dayjs().month() / 3) * 3 - 3) // 上季度的起始月份
+      .startOf('month')
+      .format('YYYY-MM-DD HH:mm:ss'),
+    end_time: dayjs()
+      .month(Math.floor(dayjs().month() / 3) * 3 - 1) // 上季度的结束月份
+      .endOf('month')
+      .format('YYYY-MM-DD HH:mm:ss')
   }
 ];

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/biReport/cusTotalAnalysis/index.vue

@@ -88,7 +88,7 @@ const queryOverall = async (payload?: RequestProps) => {
   seachLoading.value = false
 
   const sourceData = data.map((d: any) => {
-    const base = {
+    const base: any = {
       name: form.type === 1 ? d.name : d.departmentName,
       ['成交客户数']: d.customerDeal,
       ['新增客户数']: d.customertotal

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/kanbanView.vue

@@ -22,7 +22,7 @@ const allLoading = reactive({
 })
 
 onMounted(() => {
-  getKanbanViewData();
+  // getKanbanViewData();
 })
 
 function promotionStage() {

+ 4 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue

@@ -533,9 +533,11 @@ function getBusinessTableList() {
 }
 
 function resetForm() {
+  const formValue = getFromValue(businessOpportunityForm)
   let reset = {
-    startTime: getFirstDayOfMonth(new Date()),
-    endTime: formatDate(new Date()),
+    // startTime: getFirstDayOfMonth(new Date()),
+    // endTime: formatDate(new Date()),
+    ...formValue,
     pageIndex: 1,
     pageFrom: 10
   }

+ 1 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/bo/QuestionBO.java

@@ -18,6 +18,7 @@ public class QuestionBO {
 
     /**来源内容 系统表存表名 报表存formId 文件存文件名*/
     private String sourceContent;
+
 //    private String tblName;
 //
 //    private Integer formId;

+ 23 - 14
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/AIQuestionServiceImpl.java

@@ -222,7 +222,8 @@ public class AIQuestionServiceImpl extends ServiceImpl<AIQuestionMapper, AIQuest
     public HttpRespMsg ask(QuestionBO questionBO, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("token"));
-
+//        String[] typeList = {"系统表", "自定义报表", "本地上传", "自由交流"};
+//        String typeName = typeList[questionBO.getQuestionDataSource() - 1];
         String queryRes = "";
         if(1 == questionBO.getQuestionDataSource()){
             /**
@@ -415,7 +416,11 @@ public class AIQuestionServiceImpl extends ServiceImpl<AIQuestionMapper, AIQuest
 //                if(responseEntity.getStatusCode().is2xxSuccessful()){
 //                    JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
 //                    System.out.println("jsonObject=== "+jsonObject);
-//                    queryRes = jsonObject.getString("data");
+//                    if (jsonObject.getString("code").equals("ok")) {
+//                        queryRes = jsonObject.getString("data");
+//                    } else {
+//                        queryRes = jsonObject.getString("msg");
+//                    }
 //                }else{
 //                    queryRes = "AI分析有误,稍后再试";
 //                }
@@ -568,6 +573,8 @@ public class AIQuestionServiceImpl extends ServiceImpl<AIQuestionMapper, AIQuest
             cusAsk.setSeq(startSeq+1);
             cusAsk.setQuestionId(questionId);
             cusAsk.setType(1);
+            //拼接问话内容:[typeName]- [questionBO.getDataName()]+ [content]
+//            String content = "[" + typeName + (questionBO.getDataName() == null?"":("-"+questionBO.getDataName()) + "]") + "<br>"+ questionBO.getContent();
             cusAsk.setContent(questionBO.getContent());
             cusAsk.setQuestionDataSource(questionBO.getQuestionDataSource());
             cusAsk.setSourceContent(questionBO.getSourceContent());
@@ -575,18 +582,20 @@ public class AIQuestionServiceImpl extends ServiceImpl<AIQuestionMapper, AIQuest
             cusAsk.setEndDate(questionBO.getEndDate());
             aiQuestionDetailMapper.insert(cusAsk);
 
-            AIQuestionDetail aiAnswer = new AIQuestionDetail();
-            aiAnswer.setCreatorId(user.getId());
-            aiAnswer.setCompanyId(user.getCompanyId());
-            aiAnswer.setSeq(startSeq+2);
-            aiAnswer.setQuestionId(questionId);
-            aiAnswer.setType(0);
-            aiAnswer.setContent(queryRes);
-            aiAnswer.setQuestionDataSource(questionBO.getQuestionDataSource());
-            aiAnswer.setSourceContent(questionBO.getSourceContent());
-            aiAnswer.setStartDate(questionBO.getStartDate());
-            aiAnswer.setEndDate(questionBO.getEndDate());
-            aiQuestionDetailMapper.insert(aiAnswer);
+            if (queryRes != null) {
+                AIQuestionDetail aiAnswer = new AIQuestionDetail();
+                aiAnswer.setCreatorId(user.getId());
+                aiAnswer.setCompanyId(user.getCompanyId());
+                aiAnswer.setSeq(startSeq+2);
+                aiAnswer.setQuestionId(questionId);
+                aiAnswer.setType(0);
+                aiAnswer.setContent(queryRes);
+                aiAnswer.setQuestionDataSource(questionBO.getQuestionDataSource());
+                aiAnswer.setSourceContent(questionBO.getSourceContent());
+                aiAnswer.setStartDate(questionBO.getStartDate());
+                aiAnswer.setEndDate(questionBO.getEndDate());
+                aiQuestionDetailMapper.insert(aiAnswer);
+            }
 
             resMap.put("questionId",questionId);
         }

+ 10 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserController.java

@@ -170,6 +170,16 @@ public class UserController {
         return userService.getSimpleActiveUserList(departmentId,request,keyword,cursor);
     }
 
+    @RequestMapping("/getFirstCheckUser")
+    public HttpRespMsg getFirstCheckUserList() {
+        return userService.getFirstCheckUserList(request);
+    }
+
+    @RequestMapping("/getSecondCheckUser")
+    public HttpRespMsg getSecondCheckUser() {
+        return userService.getSecondCheckUserList(request);
+    }
+
     @RequestMapping("/getSimpleActiveUserListPage")
     public HttpRespMsg getSimpleActiveUserListPage(@RequestParam Integer pageIndex, @RequestParam Integer pageSize,Integer departmentId,String keyword,String cursor,@RequestParam(required = false) String userIds) throws Exception {
         return userService.getSimpleActiveUserListPage(pageIndex,pageSize,departmentId,request,keyword,cursor,userIds);

+ 1 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java

@@ -705,7 +705,6 @@ public class WeiXinCorpController {
             // 解密失败,失败原因请查看异常
             e.printStackTrace();
         }
-        System.out.println("dataCallback POST立即返回");
         return "success";
     }
 
@@ -1188,7 +1187,7 @@ public class WeiXinCorpController {
                             } else if (!StringUtils.isEmpty(department)){
                                 curUserWXDeptid = Integer.valueOf(department);
                             }
-                        } else if (jsonObject.has("MainDepartment") && !StringUtils.isEmpty(jsonObject.getString("MainDepartment"))) {
+                        } else if (jsonObject.has("MainDepartment") && !StringUtils.isEmpty(jsonObject.get("MainDepartment"))) {
                             //取主部门
                             curUserWXDeptid = jsonObject.getInt("MainDepartment");
                         } else {
@@ -2105,7 +2104,6 @@ public class WeiXinCorpController {
         } else {
             end = LocalDateTime.parse(startDate + " 23:59:59", dtf);
         }
-
         return wxCorpInfoService.getUserPunchRecord(companyId, userId, start, end, true);
     }
 

+ 25 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Task.java

@@ -85,7 +85,7 @@ public class Task extends Model<Task> {
     private Integer taskLevel;
 
     /**
-     * 任务状态,0-进行中 1-已完成 2-已撤销
+     * 任务状态,0-进行中 1-已完成 2-已撤销 3-待第一审核人审核 4-待第二审核人审核 5-驳回
      */
     @TableField("task_status")
     private Integer taskStatus;
@@ -213,6 +213,30 @@ public class Task extends Model<Task> {
     @TableField("sap_task_code")
     private String sapTaskCode;
 
+    /**
+     * 任务计划类型 1.出差 2.在办公事处 3.请假
+     */
+    @TableField("task_plan_type")
+    private Integer taskPlanType;
+
+    /**
+     * 是否是任务计划 :0 否 ,1 是 ,默认 0
+     */
+    @TableField("is_task_plan")
+    private Integer isTaskPlan;
+
+    /**
+     * 任务计划第一审核人id
+     */
+    @TableField("check_first_id")
+    private String checkFirstId;
+
+    /**
+     * 任务计划第二审核人id
+     */
+    @TableField("check_second_id")
+    private String checkSecondId;
+
 //    /**文件审核人一[部门负责人]id*/
 //    @TableField("charge_one_id")
 //    private String chargeOneId;

+ 2 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/UserVO.java

@@ -16,6 +16,8 @@ public class UserVO extends User {
     private Long remainingTime;
     //是否是项目经理
     private boolean isLeader;
+    //是否是项目小组长
+    private boolean isProjectLeader;
     private Company company;
 
     //模块菜单的访问权限

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

@@ -114,4 +114,8 @@ public interface UserService extends IService<User> {
     HttpRespMsg removeUserIsMachine(String id, HttpServletRequest request);
 
     HttpRespMsg getUserIsMachineList(HttpServletRequest request);
+
+    HttpRespMsg getFirstCheckUserList(HttpServletRequest request);
+
+    HttpRespMsg getSecondCheckUserList(HttpServletRequest request);
 }

+ 0 - 9
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -276,11 +276,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 //需要看可见部门(部门主要负责人和其他负责人以及查看本部门工时权限)所有人员的日报
                 nameList = reportMapper.getReportNameByDateAndDept(date,
                         allVisibleDeptIdList.size() > 0?allVisibleDeptIdList:null, targetUid, companyId, (deptId == null?viewUserId:null), pageStart, pageSize);
-                long middle = System.currentTimeMillis();
-                System.out.println("中间获取getReportNameByDateAndDept 耗时:" + (middle - start) + "ms");
-//                totalMembCount = reportMapper.getReportNameByDateAndDeptCount(date, allVisibleDeptIdList.size() > 0?allVisibleDeptIdList:null, targetUid, companyId, (deptId == null?viewUserId:null));
-                long end = System.currentTimeMillis();
-                System.out.println("获取totalMembCount 耗时:" + (end - middle) + "ms");
             } else {
                 //查看全公司的数据
                 List<Integer> ids = null;
@@ -306,10 +301,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 nameList = reportMapper.getReportNameByDateAndDept(date,
                         ids, targetUid, companyId, null, pageStart, pageSize);
                 long middle = System.currentTimeMillis();
-                System.out.println("中间获取getReportNameByDateAndDept 耗时:" + (middle - start) + "ms");
-//                totalMembCount = reportMapper.getReportNameByDateAndDeptCount(date, ids, targetUid, companyId, null);
-//                long end = System.currentTimeMillis();
-//                System.out.println("获取totalMembCount 耗时:" + (end - middle) + "ms");
             }
             if (nameList.size() > 0) {
                 List<String> userIds = new ArrayList<>();

+ 64 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserServiceImpl.java

@@ -223,6 +223,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
     @Resource
     private ProjectDeptRelateMapper projectDeptRelateMapper;
 
+    @Resource
+    private ProjectLeaderService projectLeaderService;
+
     @Resource
     private LdapTemplate ldapTemplate;
     @Resource
@@ -293,6 +296,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
                 //检测是否是项目审核人,有没有权限进行审核
                 userVO.setLeader(judgeIsLeader(userVO.getId()));
 
+                if(company.getId()==Constant.MLD_COMPANY_ID) {
+                    //检测是否是项目小组长,有没有权限进行添加计划
+                    userVO.setProjectLeader(judgeIsProjectLeader(userVO.getId(),company.getId()));
+                }
+
                 userVO.setTimeType(timeTypeMapper.selectById(company.getId()));
                 List<Department> manageDeptList = departmentMapper.selectList(new QueryWrapper<Department>().eq("manager_id", userVO.getId()));
                 List<Integer> deptIds = manageDeptList.stream().map(Department::getDepartmentId).collect(Collectors.toList());
@@ -755,6 +763,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
         return cnt>0;
     }
 
+    private boolean judgeIsProjectLeader(String userId,Integer companyId) {
+        int cnt = projectLeaderService.count(new QueryWrapper<ProjectLeader>().eq("leader_id", userId).eq("company_id", companyId));
+        return cnt>0;
+    }
+
     //获取用户信息
     @Override
     public HttpRespMsg getUserInfo(String id) {
@@ -2764,6 +2777,57 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
         }
     }
 
+    @Override
+    public HttpRespMsg getFirstCheckUserList(HttpServletRequest request) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        if (user.getCompanyId() != Constant.MLD_COMPANY_ID) {
+            msg.setData(new ArrayList<User>());
+            return msg;
+        } else {
+
+            List<SysRole> sysRoles = sysRoleMapper.selectList(new QueryWrapper<SysRole>().eq("company_id", user.getCompanyId()).eq("rolename", "项目经理"));
+            if (!sysRoles.isEmpty()) {
+                List<User> userList = userMapper.selectList(new QueryWrapper<User>()
+                        .eq("company_id", user.getCompanyId())
+                        .eq("is_active", 1)
+                        .eq("role_id", sysRoles.get(0).getId()));
+                msg.setData(userList);
+            } else {
+                msg.setData(new ArrayList<User>());
+            }
+            return msg;
+        }
+    }
+
+    @Override
+    public HttpRespMsg getSecondCheckUserList(HttpServletRequest request) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        if (user.getCompanyId() != Constant.MLD_COMPANY_ID) {
+            msg.setData(new ArrayList<User>());
+            return msg;
+        } else {
+
+            List<SysRole> sysRoles = sysRoleMapper.selectList(new QueryWrapper<SysRole>().eq("company_id", user.getCompanyId()).eq("rolename", "区域经理&PM"));
+            if (!sysRoles.isEmpty()) {
+                List<User> userList = userMapper.selectList(new QueryWrapper<User>()
+                        .eq("company_id", user.getCompanyId())
+                        .eq("is_active", 1)
+                        .eq("role_id", sysRoles.get(0).getId()));
+                msg.setData(userList);
+            } else {
+                msg.setData(new ArrayList<User>());
+            }
+            return msg;
+        }
+    }
+
+
+
+
     @Override
     public HttpRespMsg getUserListByRole(HttpServletRequest request,String tableName) {
         HttpRespMsg httpRespMsg=new HttpRespMsg();

+ 12 - 8
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java

@@ -639,7 +639,6 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
 
     //获取企业AccessToken
     private String getCorpAccessToken(WxCorpInfo corpInfo) throws Exception {
-        System.out.println("in getCorpAccessToken=== ");
         if (corpInfo.getExpireTime().isBefore(LocalDateTime.now())) {
             String url = WeiXinCorpController.GET_CORP_ACCESSTOKEN_URL.replace("SUITE_ACCESS_TOKEN", getSuiteAccessToken());
             HttpHeaders headers = new HttpHeaders();
@@ -738,6 +737,10 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
             msg.setError(MessageUtils.message("wx.dockError"));
             return msg;
         }
+        if (companyId == 469 && endDateTime.isBefore(LocalDateTime.of(2025, 4, 1, 0, 0, 0))) {
+            System.out.println("赛元刷新每日打卡,skip");
+            return msg;
+        }
         String url = null;
         try {
             startDateTime = startDateTime.withHour(0).withMinute(0).withSecond(0).withNano(0);
@@ -1356,7 +1359,6 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                     ct.setStartTime(DateTimeUtil.getTimeFromSeconds(sTime));
                     ct.setEndTime(DateTimeUtil.getTimeFromSeconds(eTime));
                     boolean isCrossDay = eTime >= 24 * 3600;//加班至第二天
-                    if (showLog) System.out.println("初始startTime="+ct.getStartTime()+", endTime="+ct.getEndTime());
                     //下班时间和上班时间不一样,正常应该有regular_work_sec,但是企业微信存在问题,传过来的是0,需要校正
                     if (regular_work_sec == 0 && ct.getEndTime() != null && !ct.getEndTime().equals(ct.getStartTime())) {
                         ct.setEndTime(ct.getStartTime());
@@ -1625,7 +1627,6 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                         double timeDelta = 0;
                         //时间有变化,需要重新计算
                         if (needRecaculate) {
-                            System.out.println("corpwxUserId=="+ct.getCorpwxUserid()+", name=="+ct.getName()+"s=="+ct.getStartTime()+", e=="+ct.getEndTime());
                             timeDelta = DateTimeUtil.getHoursFromSeconds(DateTimeUtil.getSecondsFromTime(ct.getEndTime()) - DateTimeUtil.getSecondsFromTime(ct.getStartTime()));
                             //超过下午上班的开始时间,需要减去午休的时间
                             if (ct.getEndTime().compareTo(baseAfternoonStart) >= 0) {
@@ -1635,7 +1636,6 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                         } else {
                             timeDelta = ct.getCardTime();
                         }
-                        if (showLog) System.out.println("上下班间隔时长为==" + timeDelta);
                         ct.setName(name);
                         //解析请假和外出的情况
                         JSONArray sp_items = jsonObject.getJSONArray("sp_items");
@@ -1654,7 +1654,6 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                                         outdoorTime = 0.0;
                                     }
                                     double otTime = convertDayTimeToHours(DateTimeUtil.getHoursFromSeconds(spItem.getInteger("duration")));
-                                    if (showLog) System.out.println("补的时长:"+otTime);
                                     if (otTime > 8.0) {
                                         otTime = 8.0;
                                     }
@@ -1729,7 +1728,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                         if (ct.getAskLeaveTime() > 8.0) {
                             ct.setAskLeaveTime(8.0);//超过8小时都以8小时计算
                         }
-                        if (showLog) System.out.println("校正后请假时长=" + ct.getAskLeaveTime());
+//                        if (showLog) System.out.println("校正后请假时长=" + ct.getAskLeaveTime());
                         //如果有出差的,但是没有打卡,则用出差的时间作为timeDelta
                         if (timeDelta < 7.5 && ct.getOutdoorTime() > 0) {
                             timeDelta += ct.getOutdoorTime();
@@ -1753,12 +1752,12 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                         if (showLog) System.out.println("工作时长==" + workHours);
 
                         if (corpInfo.getCompanyId() == 481) {
-                            //给盛立安元和赛元微电子0.5单位进位
+                            //给盛立安元0.5单位进位
                             ct.setWorkHours(DateTimeUtil.getHalfHoursFromDouble(workHours));
                         } else {
                             ct.setWorkHours(DateTimeUtil.getHoursFromDouble(workHours));
                         }
-                        if (showLog) System.out.println("ct.getStartTime()==" + ct.getStartTime()+", ct.getEndTime()="+ct.getEndTime());
+//                        if (showLog) System.out.println("ct.getStartTime()==" + ct.getStartTime()+", ct.getEndTime()="+ct.getEndTime());
 
                         //针对赛元微电子,请半天假的情况下。重新校正打卡和工作时长
                         if (corpInfo.getCorpid().equals("wpy9TkCAAAgNp4jvqxKTpXrhvwFvyFHg")) {
@@ -1866,6 +1865,11 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                             .eq("create_date", localDate));
                     //有工作时长或者打卡时长或者请假时长,外出时长,都算有效时间
                     boolean hasTimeRecord = ct.getWorkHours() > 0 || ct.getCardTime() >= 1.0 || ct.getAskLeaveTime() > 0 || ct.getOutdoorTime() > 0;
+                    //对于赛元微电子,2025年4月1日之前的都不做处理,防止把数据覆盖掉
+                    if(corpInfo.getCompanyId() == 469 && ct.getCreateDate().isBefore(LocalDate.of(2025, 4, 1))) {
+                        System.out.println("赛元微电子Skip");
+                        continue;
+                    }
                     if (itemList.size() > 0) {
                         UserCorpwxTime item = itemList.get(0);
                         if (itemList.size() > 1) {

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/CodeGenerator.java

@@ -92,7 +92,7 @@ public class CodeGenerator {
 
         // 数据源配置
         DataSourceConfig dsc = new DataSourceConfig();
-        dsc.setUrl("jdbc:mysql://1.94.62.58:17089/man_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
+        dsc.setUrl("jdbc:mysql://1.94.62.58:17089/man_mld?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
 //        dsc.setSchemaName("public");
         dsc.setDriverName("com.mysql.cj.jdbc.Driver");
         dsc.setUsername("root");

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TaskMapper.xml

@@ -33,6 +33,10 @@
         <result column="ahead_tid" property="aheadTid" />
         <result column="sap_task_code" property="sapTaskCode" />
         <result column="plan_cost" property="planCost" />
+        <result column="task_plan_type" property="taskPlanType" />
+        <result column="is_task_plan" property="isTaskPlan" />
+        <result column="check_first_id" property="checkFirstId" />
+        <result column="check_second_id" property="checkSecondId" />
         <result column="attach_file_required" property="attachFileRequired" />
     </resultMap>
 

+ 1 - 5
fhKeeper/formulahousekeeper/timesheet/src/views/project/detail.vue

@@ -10,7 +10,7 @@
                 <el-form-item>
                     <span class="workName">{{detailName}}</span>
                 </el-form-item>
-                <el-form-item style="float:right;">
+                <el-form-item style="float:right;" v-if="permissions.countCost">
                     <span style="font-size:18px;">{{ $t('xiang-mu-cheng-ben') }}:<span class="themeFontColor">{{cost.toFixed(2)}}{{ $t('yuan') }}</span></span>
                 </el-form-item>
             </el-form>
@@ -194,13 +194,9 @@
                         formatter: function (params,ticket,callback) {
                             var res
                             if(_this.user.userNameNeedTranslate == 1 && _this.radio == _this.$t('ren-yuan')) {
-                                // res = '' + "<br/>"+_this.$t('workcost')+" : " + params[0].data.money 
-                                // + _this.$t('yuan')+"<br/>"+_this.$t('screening.workTime')+" : " + params[0].data.cost + _this.$t('time.hour');
                                 res = ((_this.permissions.countCost) ? _this.$t('workcost') + ":" + params[0].data.money + _this.$t('yuan') + "</br>" : '') + 
                                 ((_this.permissions.countHours) ? _this.$t('screening.workTime') + ":" + params[0].data.cost + _this.$t('time.hour') + "</br>" : '')
                             } else {
-                                // res = params[0].name + "<br/>"+_this.$t('workcost')+" : " + params[0].data.money 
-                                // + _this.$t('yuan')+"<br/>"+_this.$t('screening.workTime')+" : " + params[0].data.cost + _this.$t('time.hour');
                                 res = ((_this.permissions.countCost) ? _this.$t('workcost') + ":" + params[0].data.money + _this.$t('yuan') + "</br>" : '') + 
                                 ((_this.permissions.countHours) ? _this.$t('screening.workTime') + ":" + params[0].data.cost + _this.$t('time.hour') + "</br>" : '')
                             }