浏览代码

Merge branch 'main' of http://git.cc-lotus.info/Information/cxyy-service into main

zs 7 月之前
父节点
当前提交
bf1b456a4f

+ 69 - 21
src/controller/users/contactApply.controller.ts

@@ -1,28 +1,59 @@
 import { Body, Controller, Get, Inject, Param, Post, Query } from '@midwayjs/core';
 import { ApiQuery, ApiTags } from '@midwayjs/swagger';
 import { ContactApplyService } from '../../service/users/contactApply.service';
-import { get, omit, pick } from 'lodash';
+import { get, isArray, omit, pick } from 'lodash';
 import { Validate } from '@midwayjs/validate';
 import { ServiceError, ErrorCode } from '../../error/service.error';
 import { MessageService } from '../../service/system/message.service';
+import { Context } from '@midwayjs/koa';
+import { ESService } from '../../service/elasticsearch/es.service';
 
 const namePrefix = '联系方式';
 @ApiTags(['联系方式'])
 @Controller('/contactApply', { tagName: namePrefix })
 export class ContactApplyController {
+  @Inject()
+  ctx: Context;
   @Inject()
   service: ContactApplyService;
   @Inject()
   msgService: MessageService;
+  @Inject()
+  esService: ESService;
+
+  @Post('/examine', { routerName: `审核${namePrefix}` })
+  @ApiTags('信息审核')
+  @ApiQuery({ name: 'examine' })
+  async examine(@Body('id') id: number, @Body('status') status: string) {
+    if (!id || !status) throw new ServiceError(ErrorCode.BODY_ERROR);
+    const result = await this.service.update({ id }, { status });
+    if (status === '1') {
+      try {
+        // 审核成功的发送通知, 生成message,然后发送消息
+        const messageData = await this.service.successMessage(id);
+        /**创建消息数据 */
+        const messageResult = await this.msgService.create(messageData);
+        const msgId = get(messageResult, 'id');
+        this.msgService.toSendMsg(msgId);
+      } catch (error) {
+        console.error('审核联系方式 消息创建失败');
+      }
+    }
+    return result;
+  }
 
   @Get('/')
   @ApiTags('列表查询')
   @ApiQuery({ name: 'query' })
   async index(@Query() query: object) {
-    const qobj = omit(query, ['skip', 'limit']);
+    const qobj: any = omit(query, ['skip', 'limit']);
     const others = pick(query, ['skip', 'limit']);
-    const result = await this.service.query(qobj, others);
-    return result;
+    // 需要查询当前管理员所在部门是否有审核的能力,再查询可见的数据
+    const source = await this.service.checkUserSourceRange();
+    if (isArray(source) && source.length > 0) qobj.source = source;
+    const { data, total } = await this.service.query(qobj, others, { source: this.service.Opera.In });
+    const nd = await this.service.dealData(data);
+    return { data: nd, total };
   }
 
   @Post('/', { routerName: `申请${namePrefix}` })
@@ -30,29 +61,46 @@ export class ContactApplyController {
   @Validate()
   async create(@Body() data: object) {
     /**
-     * 数据应该只有:
-     *  apply_user, source,source_id
-     * 根据source和source_id获取到数据,再根据数据的user获取普通用户信息,先将普通用户信息中的
-     * 用户id(user/user表的id),电话(phone)和邮箱(email)拿来给申请填充个数据
+     * 根据keyword去查询 来源数据 与 当前用户 填写的数据 是否有达到4星匹配度的数据,没有直接拒绝
+     * 因为词库很少,所以很简单就能达到.要是没达到,那就说是真的差太多了
      */
-    const otherData = await this.service.searchCompleteData(get(data, 'source'), get(data, 'source_id'));
+    const source = get(data, 'source');
+    const source_id = get(data, 'source_id');
+    // 检查当前 来源数据是否是当前用户的.是当前用户的也不需要继续了,自己找自己么?
+    const otherData = await this.service.searchCompleteData(source, source_id);
+    const sourceData = get(otherData, 'sourceData');
+    // TODO: 判断是否是 孵化基地 数据,如果是孵化基地,则不需要计算匹配度,就是单纯要信息
+    // 需求,供给,项目,成果都是用name去查询匹配度
+    const keyword = get(sourceData, 'name');
+    // 没有keyword,无法计算匹配度,不用继续往下了.
+    if (!keyword) return; //TODO:抛出异常: 无关键词,无法进行匹配计算
+    // 查询匹配度
+    const result = await this.esService.getCS(keyword, source);
+    if (!result) return; // TODO: 抛出异常,无匹配度到4星,无法进行预约
+    const source_user = get(sourceData, 'user');
+    const user_id = get(this.ctx, 'user.id');
+    if (user_id === source_user) return; // TODO:抛出异常: 来源数据是当前用户的数据
+    /**组织数据,填充联系方式申请表:将被申请方的id填充 */
     const userData = get(otherData, 'userData');
     if (!userData) throw new ServiceError(ErrorCode.CONTACTAPPLY_TARGET_USER_NOT_FOUND);
     const target_user = get(userData, 'id');
     data['target_user'] = target_user;
-    const res = await this.service.checkHasApply(data);
-    if (res) {
-      const status = get(res, 'status');
-      // 已有申请并通过审核,直接获取联系方式
-      if (status === '1') return { data: get(res, 'content') };
-    }
+    /**创建申请数据 */
     const dbData = await this.service.create(data);
-    const msgObj = await this.service.sendMqMsg(get(dbData, 'id'));
-    // 发送mq消息
-    await this.msgService.create(msgObj);
-    const to = get(msgObj, 'to', []);
-    for (const i of to) {
-      await this.msgService.toSendMq(get(i, 'user'), get(i, 'user_type'), get(dbData, 'id'));
+    /**找到应该接收该消息的管理员:通过消息类型分配的部门,发送给部门下的所有管理员
+     * 1.找到 管该类型数据的部门下的管理员id
+     */
+    const adminIds = await this.service.getSourceDept(source);
+    if (!adminIds) return; // 部门下无管理人员,请联系平台维护人员
+    try {
+      /**给这些管理员发送消息,通知他们去个人审核这个信息 */
+      const messageData = await this.service.getAdminToExamData(data, adminIds);
+      /**创建消息数据 */
+      const messageResult = await this.msgService.create(messageData);
+      const msgId = get(messageResult, 'id');
+      this.msgService.toSendMsg(msgId);
+    } catch (error) {
+      console.error('申请联系方式 消息创建失败');
     }
     return dbData;
   }

+ 7 - 7
src/entity/users/contactApply.entity.ts

@@ -1,5 +1,5 @@
-import { Entity, Column } from "typeorm";
-import { BaseModel } from "../../frame/BaseModel";
+import { Entity, Column } from 'typeorm';
+import { BaseModel } from '../../frame/BaseModel';
 
 /**联系方式申请表 */
 @Entity('contactApply')
@@ -10,12 +10,12 @@ export class ContactApply extends BaseModel {
   target_user: number;
   // 电话,联系人姓名,邮箱都动态获取
   @Column({ type: 'character varying', nullable: true, comment: '申请来源' })
-  source: string; // 哪个表:需求,供给;专家
+  source: string; // 哪个表:投资部:需求(demand);供给(supply);项目(project); 创合部:成果(achievement), 孵化基地(incubator)
   @Column({ type: 'character varying', nullable: true, comment: '申请来源id' })
   source_id: string; // 哪个数据
 
-
-
-  @Column({ type: 'character varying', nullable: true, default: '1', comment: '审核状态: 0-未审核;1通过;-1拒绝' })
+  @Column({ type: 'integer', nullable: true, comment: '审核的管理员id' })
+  exam_admin: number;
+  @Column({ type: 'character varying', nullable: true, default: '0', comment: '审核状态: 0-未审核;1通过;-1拒绝' })
   status: string;
-}
+}

+ 22 - 2
src/service/elasticsearch/es.service.ts

@@ -1,4 +1,4 @@
-import { Config, Provide } from '@midwayjs/core';
+import { Config, Inject, Provide } from '@midwayjs/core';
 import { get } from 'lodash';
 import { InjectEntityModel } from '@midwayjs/typeorm';
 import { Achievement } from '../../entity/platform/achievement.entity';
@@ -15,10 +15,12 @@ import { Incubator } from '../../entity/users/incubator.entity';
 import { School } from '../../entity/users/school.entity';
 import { Unit } from '../../entity/users/unit.entity';
 import Axios from 'axios';
+import { Context } from '@midwayjs/koa';
 
 @Provide()
 export class ESService {
-
+  @Inject()
+  ctx:Context
   @InjectEntityModel(Achievement)
   achievement: Repository<Achievement>;
   @InjectEntityModel(Demand)
@@ -83,4 +85,22 @@ export class ESService {
       }
     }
   }
+  /**
+   * 计算匹配度
+   * @param keyword 查询关键词
+   * @param index 表
+   */
+  async getCS(keyword, index) {
+    const csUrl = `${this.esServiceHttpPrefix}/cs/${index}?keyword=${keyword}`;
+    let res;
+    const token = get(this.ctx.request, 'header.token')
+    try {
+      const response = await Axios.get(csUrl, { responseType: 'json', headers: { token } });
+      res = get(response, 'data.data', false);
+    } catch (error) {
+      console.error(error);
+    } finally {
+      return res;
+    }
+  }
 }

+ 186 - 24
src/service/users/contactApply.service.ts

@@ -3,13 +3,14 @@ import { InjectEntityModel } from '@midwayjs/typeorm';
 import { Repository } from 'typeorm';
 import { ContactApply } from '../../entity/users/contactApply.entity';
 import { BaseServiceV2 } from '../../frame/BaseServiceV2';
-import { get } from 'lodash';
+import { compact, get, head, isArray, uniq } from 'lodash';
 import { Demand } from '../../entity/platform/demand.entity';
 import { Supply } from '../../entity/platform/supply.entity';
 import { Expert } from '../../entity/users/expert.entity';
 import { User } from '../../entity/system/user.entity';
 import { ErrorCode, ServiceError } from '../../error/service.error';
 import { MessageUserType } from '../../public/var';
+import { Admin } from '../../entity/system/admin.entity';
 
 @Provide()
 export class ContactApplyService extends BaseServiceV2 {
@@ -30,6 +31,154 @@ export class ContactApplyService extends BaseServiceV2 {
 
   @InjectEntityModel(User)
   user: Repository<User>;
+  @InjectEntityModel(Admin)
+  admin: Repository<Admin>;
+
+  /**
+   * 审核成功后,返回给申请人申请的 联系方式
+   */
+  async successMessage(id) {
+    const apply = await this.model.createQueryBuilder().where(`id =:id`, { id }).getOne();
+    // TODO: 抛异常,未找到数据
+    if (!apply) return;
+    const source = get(apply, 'source');
+    const source_id = get(apply, 'source_id');
+    const mqTarget = get(apply, 'apply_user');
+    const applyUserData = await this.getUserData(mqTarget);
+    const mqTargetUserNickName = get(applyUserData, 'nick_name');
+    let sourceStr = this.getSourceZh(source);
+    const otherData = await this.searchCompleteData(source, source_id);
+    const sourceData = get(otherData, 'sourceData');
+    const sourcePropValue = get(sourceData, 'name');
+    const contacts = this.getContactObject(otherData, source);
+    let msgStr = `您通过 ${sourceStr}数据 ${sourcePropValue ? `- ${sourcePropValue}` : ''} 获取对方联系方式的申请已通过.`;
+    // 拼接联系方式
+    const person = get(contacts, 'person');
+    if (person) msgStr = `${msgStr} 联系人: ${person};`;
+    const phone = get(contacts, 'phone');
+    if (phone) msgStr = `${msgStr} 联系电话: ${phone};`;
+    const email = get(contacts, 'email');
+    if (email) msgStr = `${msgStr} 邮箱: ${email}`;
+    const to = [{ user: mqTarget, user_type: MessageUserType.USER, user_nick_name: mqTargetUserNickName, is_read: '0' }];
+    const msgObj = { content: msgStr, to, type: '0' };
+    return msgObj;
+  }
+
+  /**处理数据,将显示内容取出 */
+  async dealData(data) {
+    let list = [];
+    // 统一组织成数组形式处理,如果只是1条数据,那最后head取出返回即可
+    const isArr = isArray(data);
+    if (isArr) list = data;
+    else list.push(data);
+    /**需要组织 apply_user , target_user 的用户数据的name; source需要转换成中文, source_id需要取出name; exam_admin:需要转换成管理员昵称*/
+    let auIds = list.map(i => i.apply_user); // 提取apply_user为一维数组Array<number>
+    auIds = compact(uniq(auIds)); // 清除假值(去重数组)
+    let tuIds = list.map(i => i.target_user);
+    tuIds = compact(uniq(tuIds));
+    const uIds = uniq([...auIds, ...tuIds]); // 两个ids结合,去重,一次都查出来
+    const userList = await this.user.createQueryBuilder().where(`id IN (:...ids)`, { ids: uIds }).getMany();
+    const returnData = [];
+    for (const i of list) {
+      const obj: any = {};
+      // apply_user
+      obj.apply_user = get(i, 'apply_user');
+      const auInfo = userList.find(f => f.id === i.apply_user);
+      if (auInfo) obj.apply_user_name = get(auInfo, 'nick_name');
+      // target_user
+      obj.target_user = get(i, 'target_user');
+      const tuInfo = userList.find(f => f.id === i.target_user);
+      if (tuInfo) obj.target_user_name = get(tuInfo, 'nick_name');
+      // source
+      obj.source = get(i, 'source');
+      const sourceZh = this.getSourceZh(get(i, 'source'));
+      if (sourceZh) obj.sourceZh = sourceZh;
+      obj.source_id = get(i, 'source_id');
+      const otherData = await this.searchCompleteData(get(i, 'source'), get(i, 'source_id'));
+      const sourceData = get(otherData, 'sourceData');
+      const sourcePropValue = get(sourceData, 'name');
+      if (sourcePropValue) obj.source_name = sourcePropValue;
+      // exam_admin
+      if (get(i, 'exam_admin')) {
+        obj.exam_admin = get(i, 'exam_admin');
+        const adminInfo = await this.admin
+          .createQueryBuilder()
+          .where(`id =:id`, { id: get(i, 'exam_admin') })
+          .getOne();
+        if (adminInfo) obj.exam_admin_name = get(adminInfo, 'nick_name');
+      }
+      // status
+      obj.status = get(i, 'status');
+      // id
+      obj.id = get(i, 'id');
+      returnData.push(obj);
+    }
+    if (isArr) return returnData;
+    else return head(returnData);
+  }
+
+  /**
+   * 查询本用户可审批的数据来源
+   */
+  async checkUserSourceRange() {
+    const user = get(this.ctx, 'user', {});
+    const is_super = get(user, 'is_super');
+    // 超级管理员都查
+    if (is_super === '0') return [];
+    // 接下来取出部门
+    const dept = get(user, 'dept');
+    // TODO: 抛出异常: 本管理员无部门,无法查询预约申请
+    if (!dept) return;
+    // TODO: 需要从系统设置中查询配置
+    const config = { messageConfig: { demand: 20, supply: 20, project: 20, achievement: 21, incubator: 21 } };
+    const msgConfig = get(config, 'messageConfig', {});
+    // 整理出本部门管理的数据来源
+    const sources = [];
+    for (const key in msgConfig) {
+      const val = msgConfig[key];
+      if (val === dept) sources.push(key);
+    }
+    return sources;
+  }
+
+  /**
+   * 根据 数据来源 找到对应管理的部门
+   * @param source 数据来源
+   */
+  async getSourceDept(source: string) {
+    // TODO: 需要从系统设置中查询配置
+    const config = { messageConfig: { demand: 20, supply: 20, project: 20, achievement: 21, incubator: 21 } };
+    const dept_id = get(config, `messageConfig.${source}`);
+    if (!dept_id) return;
+    const adminList = await this.admin.createQueryBuilder().where(`dept =:dept`, { dept: dept_id }).getMany();
+    const ids = adminList.map(i => i.id);
+    return ids;
+  }
+
+  /**
+   * 组织数据,通知管理员进行审核
+   * @param data 联系方式申请数据
+   * @param adminIds 接收通知的管理员id数组
+   */
+  async getAdminToExamData(data, adminIds) {
+    const source = get(data, 'source');
+    const source_id = get(data, 'source_id');
+    const otherData = await this.searchCompleteData(source, source_id);
+    const sourceZh = this.getSourceZh(source);
+    const applyUserData = await this.getUserData(get(data, 'apply_user'));
+    const apply_userName = get(applyUserData, 'nick_name');
+    const sourceData = get(otherData, 'sourceData');
+    const sourcePropValue = get(sourceData, 'name');
+    const content = `用户${apply_userName} 通过 ${sourceZh}数据 ${sourcePropValue ? `- ${sourcePropValue}` : ''} 申请获取联系方式!`;
+    // 组织message消息数据
+    const adminList = await this.admin.createQueryBuilder().where(`id IN (:...value)`, { value: adminIds }).getMany();
+    const to = adminList.map(i => {
+      const obj = { user: get(i, 'id'), user_nick_name: get(i, 'nick_name'), user_type: MessageUserType.ADMIN, is_read: '0' };
+      return obj;
+    });
+    const messageData = { content, to, type: '1' };
+    return messageData;
+  }
 
   /**
    * 发送mq消息
@@ -46,28 +195,13 @@ export class ContactApplyService extends BaseServiceV2 {
     const otherData = await this.searchCompleteData(source, source_id);
     const targetUserData = await this.getUserData(get(data, 'target_user'));
     const applyUserData = await this.getUserData(get(data, 'apply_user'));
-    let sourceStr;
-    let sourceProp;
+    let sourceStr = this.getSourceZh(source);
+    let sourceProp = 'name';
     let sourcePropValue;
     let mqTarget;
     let msgStr;
     let mqUserType = MessageUserType.USER;
     let mqUserNickName;
-    switch (source) {
-      case 'demand':
-        sourceStr = '需求';
-        sourceProp = 'name';
-        break;
-      case 'supply':
-        sourceStr = '供给';
-        sourceProp = 'name';
-        break;
-      case 'expert':
-        sourceStr = '专家';
-        break;
-      default:
-        break;
-    }
     const sourceData = get(otherData, 'sourceData');
     if (sourceProp) sourcePropValue = get(sourceData, sourceProp);
     switch (status) {
@@ -76,7 +210,7 @@ export class ContactApplyService extends BaseServiceV2 {
         mqTarget = get(data, 'target_user');
         mqUserNickName = get(targetUserData, 'nick_name');
         const apply_userName = get(applyUserData, 'nick_name');
-        msgStr = `用户${apply_userName} 通过 ${sourceStr}数据 ${sourcePropValue ? `- ${sourcePropValue}` : ''} 申请获取您的联系方式,尽快审核避免错失良机!`;
+        msgStr = `用户${apply_userName} 通过 ${sourceStr}数据 ${sourcePropValue ? `- ${sourcePropValue}` : ''} 申请获取您的联系方式!`;
         break;
       case '1':
         // 给申请人发送消息和mq消息
@@ -141,6 +275,7 @@ export class ContactApplyService extends BaseServiceV2 {
   }
   /**
    * 组织联系信息
+   * 需求是先从需求中找联系方式,没有再从用户中找,其余的都直接从用户中找
    * @param data searchCompleteData的返回
    * @param source 来源
    */
@@ -148,14 +283,14 @@ export class ContactApplyService extends BaseServiceV2 {
     const contacts: any = {};
     if (source === 'demand') {
       const sourceData = get(data, 'sourceData');
-      contacts.phone = get(sourceData, 'tel');
-      contacts.person = get(sourceData, 'contacts');
+      if (get(sourceData, 'tel')) contacts.phone = get(sourceData, 'tel');
+      if (get(sourceData, 'contacts')) contacts.person = get(sourceData, 'contacts');
     }
     if (Object.keys(contacts).length <= 0) {
       const user = get(data, 'userData');
-      contacts.phone = get(user, 'phone');
-      contacts.person = get(user, 'nick_name');
-      contacts.email = get(user, 'email');
+      if (get(user, 'phone')) contacts.phone = get(user, 'phone');
+      if (get(user, 'nick_name')) contacts.person = get(user, 'nick_name');
+      if (get(user, 'email')) contacts.email = get(user, 'email');
     }
     return contacts;
   }
@@ -178,4 +313,31 @@ export class ContactApplyService extends BaseServiceV2 {
     result['userData'] = user;
     return result;
   }
+  /**
+   * 数据来源中文
+   * @param source 数据来源
+   */
+  getSourceZh(source: string) {
+    let str;
+    switch (source) {
+      case 'demand':
+        str = '需求';
+        break;
+      case 'supply':
+        str = '供给';
+        break;
+      case 'project':
+        str = '项目';
+        break;
+      case 'achievement':
+        str = '成果';
+        break;
+      case 'incubator':
+        str = '孵化基地';
+        break;
+      default:
+        break;
+    }
+    return str;
+  }
 }