lrf 3 years ago
parent
commit
e544b81e7c

+ 23 - 0
app/controller/statistics/.index.js

@@ -0,0 +1,23 @@
+module.exports = {
+  dockProduct: {
+    parameters: {
+      query: {
+        dock_id: "dock_id",
+        type: "type",
+        name: "productList.name",
+        type: "productList.type",
+        field: "productList.field",
+        cooperation: "productList.cooperation",
+        "create_time@start": "create_time@start",
+        "create_time@end": "create_time@end",
+      },
+      options: {
+        "productList.status": "1", // 默认条件
+      },
+    },
+    service: "dockProduct",
+    options: {
+      query: ["skip", "limit"],
+    },
+  },
+};

+ 108 - 0
app/controller/statistics/index.js

@@ -0,0 +1,108 @@
+'use strict';
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose-free/lib/controller');
+const meta = require('./.index.js');
+
+// 首页-数据动态统计
+class IndexController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.statistics.index;
+  }
+  async index() {
+    const patent = await this.service.patent(this.ctx.query);
+    const product = await this.service.product(this.ctx.query);
+    const expert = await this.service.expert(this.ctx.query);
+    const circle = await this.service.circle(this.ctx.query);
+    this.ctx.ok({ data: { patent, product, expert, circle } });
+  }
+
+  /**
+   * 首页专利统计
+   */
+  async patent() {
+    const data = await this.service.patent(this.ctx.query);
+    this.ctx.ok({ data });
+  }
+
+  /**
+   * 首页成果统计
+   */
+  async product() {
+    const data = await this.service.product(this.ctx.query);
+    this.ctx.ok({ data });
+  }
+
+  /**
+   * 首页成果统计
+   */
+  async expert() {
+    const data = await this.service.expert(this.ctx.query);
+    this.ctx.ok({ data });
+  }
+  /**
+   * 首页数据统计
+   */
+  async circle() {
+    const data = await this.service.circle(this.ctx.query);
+    this.ctx.ok({ data });
+  }
+
+  async dockIndex() {
+    const res = await this.service.dockIndex(this.ctx.query);
+    this.ctx.ok({ data: res });
+  }
+
+  /**
+   * 企业数量统计
+   */
+  async orgCount() {
+    const res = await this.ctx.service.users.organization.count(this.ctx.query);
+    this.ctx.ok({ data: res });
+  }
+
+  /**
+   * 创新券申请统计
+   */
+  async policyApplyCount() {
+    const data = await this.service.pac();
+    this.ctx.ok({ data });
+  }
+
+  /**
+   * 高企申报个步骤统计
+   */
+  async declare() {
+    const data = await this.service.declare();
+    this.ctx.ok({ data });
+  }
+
+  async dockProduct() {
+    const query = this.ctx.query;
+    const arr = [ 'skip', 'limit', 'dock_id' ];
+    for (const key in query) {
+      const r = arr.find(f => f === key);
+      if (!r) {
+        query[`productList.${key}`] = query[key];
+        delete query[key];
+      }
+    }
+    const data = await this.service.dockProduct(query);
+    this.ctx.ok(data);
+  }
+
+  // 专利运营用户首页统计
+  async patentUserIndex() {
+    const data = await this.service.patentUserIndex(this.ctx.query);
+    this.ctx.ok({ data });
+  }
+
+  /**
+   * 根据申请人统计,申请人所在的专利信息数量
+   */
+  async patentInfoByApplyPerson() {
+    const data = await this.service.patentInfoByApplyPerson();
+    this.ctx.ok({ data });
+  }
+}
+module.exports = CrudController(IndexController, meta);

+ 3 - 0
app/router.js

@@ -86,5 +86,8 @@ module.exports = app => {
   require('./z_router/system/menu')(app); // 菜单
   require('./z_router/system/role')(app); // 角色
 
+  // statistics
+  require('./z_router/statistics/index')(app); // 统计
+
   // patent
 };

+ 452 - 0
app/service/statistics/index.js

@@ -0,0 +1,452 @@
+'use strict';
+const { CrudService } = require('naf-framework-mongoose-free/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const { ObjectId } = require('mongoose').Types;
+const _ = require('lodash');
+const assert = require('assert');
+
+// 首页-数据动态统计
+class IndexService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'index');
+    this.redis = this.app.redis;
+    this.patentModel = this.ctx.model.Dock.Patent;
+    this.code = this.ctx.model.System.Code;
+    this.productModel = this.ctx.model.News.Product;
+    this.expertModel = this.ctx.model.User.Expert;
+    // this.userModel = this.ctx.model.User;
+    this.personalModel = this.ctx.model.User.Personal;
+    this.organizationModel = this.ctx.model.User.Organization;
+    this.surveyModel = this.ctx.model.News.Survey;
+    this.dockUser = this.ctx.model.Dock.DockUser; // 没改
+    this.tranModel = this.ctx.model.Dock.DockTranscation; // 没改
+  }
+  /**
+   * 首页专利统计
+   */
+  async patent() {
+    const res = await this.patentModel.aggregate([
+      {
+        $group: {
+          _id: '$apply_personal',
+          value: { $sum: 1 },
+        },
+      },
+    ]);
+    let arr = [];
+    const other = { name: '其他', value: 0 };
+    for (const i of res) {
+      const { _id, value } = i;
+      const unitList = this.patentUnitList();
+      const unit = unitList.find(f => (_id && _id.includes(f.name)) || f.name.includes(_id));
+      if (unit) {
+        // 说明是需要单拎出来的数据,现查arr中是否有该单位:有=>数字合并;没有=>创建条目
+        const { name } = unit;
+        const arrItem = arr.find(f => f.name === name);
+        if (arrItem) {
+          const index = arr.findIndex(f => f.name === name);
+          arr[index] = { name, value: (arrItem.value || 0) + value };
+        } else {
+          arr.push({ name, value });
+        }
+      } else {
+        other.value += value;
+      }
+    }
+    arr = _.orderBy(arr, [ 'value' ], [ 'desc' ]);
+    arr.push(other);
+    return arr;
+  }
+
+  /**
+   * 首页成果统计
+   */
+  async product() {
+    const categroy = await this.code.find({ category: '01' });
+    const condition = { type: '1', status: '2' };
+    const arr = [];
+    for (const c of categroy) {
+      const { name } = c;
+      const value = await this.productModel.count({
+        ...condition,
+        field: name,
+      });
+      arr.push({ value, name });
+    }
+    return arr;
+  }
+
+  /**
+   * 首页统计专家
+   */
+  async expert() {
+    const res = await this.expertModel.aggregate([
+      {
+        $group: {
+          _id: '$company',
+          value: { $sum: 1 },
+        },
+      },
+    ]);
+    let arr = [];
+    const other = { name: '其他', value: 0 };
+    for (const i of res) {
+      const { _id, value } = i;
+      const unitList = this.expertList();
+      const unit = unitList.find(f => (_id && _id.includes(f.name)) || f.name.includes(_id));
+      if (unit) {
+        // 说明是需要单拎出来的数据,现查arr中是否有该单位:有=>数字合并;没有=>创建条目
+        const { name, sort } = unit;
+        const arrItem = arr.find(f => f.name === name);
+        if (arrItem) {
+          const index = arr.findIndex(f => f.name === name);
+          arr[index] = { ...arr[index], value: (arrItem.value || 0) + value };
+        } else {
+          arr.push({ name, value, sort });
+        }
+      } else {
+        other.value += value;
+      }
+    }
+    arr = _.orderBy(arr, [ 'sort' ], [ 'asc' ]);
+    arr.push(other);
+    // 换名
+    arr.map(i => {
+      const r = this.expertList().find(f => f.name === i.name);
+      if (r && r.alias) i.name = r.alias;
+      return i;
+    });
+    return arr;
+  }
+
+  /**
+   * 首页数据统计
+   */
+  async circle() {
+    const arr = [];
+    const organization = await this.organizationModel.count({ status: '1' });
+    arr.push({
+      name: '企业注册数量',
+      value: organization,
+    });
+    // TODO:如果需要专家和个人用户区分开,则is_expert:false, 混查则不需要is_expert条件
+    const user = await this.personalModel.count({
+      status: '1',
+      is_expert: false,
+    });
+    arr.push({
+      name: '个人注册数量',
+      value: user,
+    });
+    const exports = await this.expertModel.count();
+    arr.push({
+      name: '专家注册数量',
+      value: exports,
+    });
+    const products = await this.productModel.count({ status: '2' });
+    arr.push({
+      name: '产品发布数量',
+      value: products,
+    });
+    const surveys = await this.surveyModel.count();
+    arr.push({
+      name: '交流互动',
+      value: surveys,
+    });
+    const trans = await this.tranModel.aggregate([
+      { $match: { status: { $in: [ '0', '1', '3' ] } } },
+      {
+        $group: {
+          _id: '$status',
+          value: { $sum: 1 },
+        },
+      },
+    ]);
+    arr.push({
+      name: '正在洽谈',
+      value: _.get(
+        trans.find(f => f._id === '0'),
+        'value',
+        0
+      ),
+    });
+    arr.push({
+      name: '达成意向',
+      value: _.get(
+        trans.find(f => f._id === '1'),
+        'value',
+        0
+      ),
+    });
+    arr.push({
+      name: '对接完成',
+      value: _.get(
+        trans.find(f => f._id === '3'),
+        'value',
+        0
+      ),
+    });
+    return arr;
+  }
+
+  /**
+   * 专家列表
+   */
+  expertList() {
+    return [
+      { name: '中科院长春光学精密机械与物理研究所', alias: '光机所', sort: 1 },
+      { name: '中国科学院长春应用化学研究所', alias: '应化所', sort: 2 },
+      { name: '中国科学院东北地理与农业生态研究所', alias: '地理所', sort: 3 },
+      { name: '吉林大学', sort: 4 },
+      { name: '长春工业大学', sort: 5 },
+    ];
+  }
+
+  /**
+   * 专利单位列表
+   */
+  patentUnitList() {
+    return [
+      { name: '吉林大学' },
+      { name: '长春理工大学' },
+      { name: '东北电力大学' },
+      { name: '吉林工程技术师范学院' },
+      { name: '长春工业大学' },
+      { name: '北华大学' },
+      { name: '吉林农业大学' },
+      { name: '吉林师范大学' },
+      { name: '长春工程学院' },
+      { name: '吉林建筑大学' },
+      { name: '通化师范学院' },
+      { name: '长春大学' },
+      { name: '东北师范大学' },
+      { name: '吉林农业科技学院' },
+    ];
+  }
+
+  // 展会首页统计
+  async dockIndex({ dock_id }) {
+    // 同时在线人数(伪)
+    let tszx = _.random(100, 1000);
+    if (this.redis) {
+      tszx = (await this.redis.get('login_number')) || tszx;
+    }
+    // 特邀嘉宾
+    // const tyjb = await this.personalModel.count({ is_expert: true });
+    const tyjb = 81;
+    console.log(tyjb);
+    // 洽谈合作 达成意向 交易完成
+    const trans = await this.tranModel.aggregate([
+      { $match: { status: { $in: [ '0', '1', '3' ] } } },
+      {
+        $group: {
+          _id: '$status',
+          value: { $sum: 1 },
+        },
+      },
+    ]);
+    const qthz = _.get(
+      trans.find(f => f._id === '0'),
+      'value',
+      0
+    );
+    const dcyx = _.get(
+      trans.find(f => f._id === '1'),
+      'value',
+      0
+    );
+    const jywc = _.get(
+      trans.find(f => f._id === '3'),
+      'value',
+      0
+    );
+    // 参展项目
+    const res = await this.dockUser
+      .aggregate()
+      .match({
+        dock_id: ObjectId(dock_id),
+        'productList.status': '1',
+      })
+      .unwind('$productList')
+      .group({
+        _id: '$dock_id',
+        count: { $sum: 1 },
+      });
+    const czxm = _.get(res[0], 'count');
+    const arr = [
+      { name: '同时在线', num: tszx, unit: '人' },
+      { name: '特邀嘉宾', num: tyjb, unit: '人' },
+      { name: '洽谈合作', num: qthz, unit: '项' },
+      { name: '达成意向', num: dcyx, unit: '项' },
+      { name: '交易完成', num: jywc, unit: '项' },
+      { name: '参展项目', num: czxm, unit: '项' },
+    ];
+    return arr;
+  }
+
+  /**
+   * 高企申报, 兑付成功(is_cashing=='1')
+   * 研发补贴/奖励兑现, 兑付成功(is_cashing==='1'),类型要区分
+   */
+  async pac() {
+    const gq = await this.ctx.model.Cysci.Declare.count({ is_cashing: '1' });
+    const yfjl = await this.ctx.model.Cysci.Reward.aggregate([
+      { $match: { is_cashing: '1' } },
+      {
+        $group: {
+          _id: '$type',
+          count: { $sum: 1 },
+        },
+      },
+    ]);
+    const arr = [
+      { key: '奖励兑现', name: '奖励兑现' },
+      { key: '研发补贴', name: '研发补贴' },
+    ];
+    const obj = this.setData(yfjl, arr);
+    obj.push({ name: '高企申报', value: gq });
+    return obj;
+  }
+
+  /**
+   * 高企申报个步骤统计
+   */
+  async declare() {
+    const res = await this.ctx.model.Cysci.Declare.aggregate([
+      {
+        $group: {
+          _id: '$status',
+          count: { $sum: 1 },
+        },
+      },
+    ]);
+    const arr = [
+      { key: '0', name: '企业信息待审中' },
+      { key: '1', name: '企业信息审核成功' },
+      { key: '-1', name: '企业信息审核失败' },
+      { key: '2', name: '高企申报成功' },
+      { key: '-2', name: '高企申报失败' },
+    ];
+    const obj = this.setData(res, arr);
+    return obj;
+  }
+
+  /**
+   * 安排返回值
+   * @param {Array} list 数据[{_id,count}]
+   * @param {*} meta 变换成的另一部信息[{key:_id,name:zh}]
+   */
+  setData(list, meta) {
+    const obj = [];
+    for (const i of meta) {
+      const { key, name } = i;
+      const r = list.find(f => f._id === key);
+      if (r) {
+        obj.push({ name, value: r.count });
+      } else {
+        obj.push({ name, value: 0 });
+      }
+    }
+    return obj;
+  }
+
+  async dockProduct({ skip = 0, limit = 10, ...query } = {}) {
+    const productQuery = {};
+    const userQuery = {};
+    for (const key in query) {
+      if (query[key].length > 12) {
+        if (ObjectId.isValid(query[key])) query[key] = ObjectId(query[key]);
+      }
+      if (key.includes('.')) productQuery[key] = query[key];
+      else userQuery[key] = query[key];
+    }
+    const publics = [{ $match: userQuery }, { $project: { productList: 1, _id: 0 } }, { $unwind: '$productList' }, { $match: productQuery }];
+    const data = await this.ctx.model.Dock.DockUser.aggregate([ ...publics, { $skip: parseInt(skip) }, { $limit: parseInt(limit) }]).replaceRoot('productList');
+    let total = await this.ctx.model.Dock.DockUser.aggregate([
+      ...publics,
+      {
+        $group: {
+          _id: null,
+          count: { $sum: 1 },
+        },
+      },
+    ]);
+    if (total && _.isArray(total)) {
+      total = _.get(_.head(total), 'count', 0);
+    }
+    return { data, total };
+  }
+
+  async patentUserIndex({ id }) {
+    // 审核通知
+    const patentExamine = this.ctx.model.Patent.Patentexamine;
+    // 未读消息
+    const unread = await patentExamine.count({ to: id, is_read: false });
+    // 通知信息
+    const patentNotice = this.ctx.model.Patent.Patentnotice;
+    const notice = await patentNotice.count({ to_id: { $elemMatch: { $in: [ ObjectId(id) ] } }, is_read: false });
+    // 专利申请
+    const papply = this.ctx.model.Patent.Patentapply;
+    const apply = await papply.count({
+      status: [ '0', '1', '-1', '2', '3', '4', '-4', '5', '-5', '6', '-6', '7' ],
+      user_id: id,
+    });
+    // 查询检索
+    const palysis = this.ctx.model.Patent.Patentanalysis;
+    const analysis = await palysis.count({ status: [ '0', '1', '-1', '2' ], user_id: id });
+    // 价值评估
+    const paccess = this.ctx.model.Patent.Patentassess;
+    const access = await paccess.count({ status: [ '0', '1', '-1', '2' ], user_id: id });
+    // 专利信息
+    const patentInfo = this.ctx.model.Patent.Patentinfo;
+    const information = await patentInfo.count({ user_id: { $elemMatch: { user_id: id } } });
+    // 专利维权
+    const patentsafeg = this.ctx.model.Patent.Patentsafeg;
+    const safeg = await patentsafeg.count({ status: [ '0', '1', '2' ], user_id: id });
+    // 专利预警
+    const patentEarly = this.ctx.model.Patent.Patentearly;
+    const early = await patentEarly.count({ user_id: { $elemMatch: { $in: [ ObjectId(id) ] } } });
+    // 专利交易
+    const patentTrans = this.ctx.model.Patent.Patenttrans;
+    const trans1 = await patentTrans.count({ type: '转让', user_id: id });
+    const trans2 = await patentTrans.count({ type: '许可', user_id: id });
+    const trans3 = await patentTrans.count({ type: '免费许可', user_id: id });
+    const trans4 = await patentTrans.count({ type: '招商', user_id: id });
+    const trans5 = await patentTrans.count({ type: '质押', user_id: id });
+    return {
+      message: { unread, notice },
+      apply: { apply, analysis, access },
+      patent: { information, safeg, early },
+      transaction: { trans1, trans2, trans3, trans4, trans5 },
+    };
+  }
+
+  /**
+   * 根据申请人统计,申请人所在的专利信息数量
+   */
+  async patentInfoByApplyPerson() {
+    console.time('p1');
+    const pImodel = this.ctx.model.Patent.Patentinfo;
+    // const data = await pImodel.find({}, { apply_personal: 1 });
+    const data = await pImodel.aggregate([{ $project: { apply_personal: 1 } }]);
+    if (data.length <= 0) {
+      return '未找到专利信息相关数据';
+    }
+    let nameList = data.map(i => i.apply_personal).map(i => i.split(';'));
+    nameList = _.uniq(_.flattenDeep(nameList).map(i => _.trim(i)));
+    const arr = [];
+    for (const i of nameList) {
+      const reg = new RegExp(i);
+      const query = { apply_personal: reg };
+      const res = await pImodel.aggregate([{ $match: query }, { $count: 'id' }]);
+      if (res) {
+        const head = _.head(res);
+        const num = _.get(head, 'id', 0);
+        arr.push({ name: i, num });
+      }
+    }
+    return arr;
+  }
+}
+
+module.exports = IndexService;

+ 42 - 0
app/z_router/statistics/index.js

@@ -0,0 +1,42 @@
+'use strict';
+// 路由配置
+const rkey = 'statistics/index';
+const ckey = 'statistics.index';
+const keyZh = '统计';
+const routes = [
+  { method: 'get', path: `${rkey}/dockProduct`, controller: `${ckey}.dockProduct`, name: `${ckey}dockProduct`, zh: `${keyZh}-展会产品统计` },
+  { method: 'get', path: `${rkey}/dockIndex`, controller: `${ckey}.dockIndex`, name: `${ckey}dockIndex`, zh: `${keyZh}-不知道` },
+  { method: 'get', path: `${rkey}/index`, controller: `${ckey}.index`, name: `${ckey}Index`, zh: `${keyZh}-展会首页统计` },
+  { method: 'get', path: `${rkey}/patent`, controller: `${ckey}.patent`, name: `${ckey}patent`, zh: `${keyZh}-首页专利统计` },
+  { method: 'get', path: `${rkey}/product`, controller: `${ckey}.product`, name: `${ckey}product`, zh: `${keyZh}-首页成果统计` },
+  { method: 'get', path: `${rkey}/expert`, controller: `${ckey}.expert`, name: `${ckey}expert`, zh: `${keyZh}-首页专家统计` },
+  { method: 'get', path: `${rkey}/circle`, controller: `${ckey}.circle`, name: `${ckey}circle`, zh: `${keyZh}-首页数据统计` },
+  { method: 'get', path: `${rkey}/orgCount`, controller: `${ckey}.orgCount`, name: `${ckey}orgCount`, zh: `${keyZh}-企业数量统计` },
+  { method: 'get', path: `${rkey}/pac`, controller: `${ckey}.policyApplyCount`, name: `${ckey}policyApplyCount`, zh: `${keyZh}-创新券申请统计` },
+  { method: 'get', path: `${rkey}/declare`, controller: `${ckey}.declare`, name: `${ckey}declare`, zh: `${keyZh}-高企申报个步骤统计` },
+  { method: 'get', path: `${rkey}/patentUserIndex`, controller: `${ckey}.patentUserIndex`, name: `${ckey}patentUserIndex`, zh: `${keyZh}-专利运营用户首页统计` },
+  {
+    method: 'get',
+    path: `${rkey}/patentInfoByApplyPerson`,
+    controller: `${ckey}.patentInfoByApplyPerson`,
+    name: `${ckey}patentInfoByApplyPerson`,
+    zh: `${keyZh}-根据申请人统计,申请人所在的专利信息数量`,
+  },
+];
+
+module.exports = app => {
+  const { router, config } = app;
+  const mwares = app.middleware;
+  console.log(`${keyZh}:  ${rkey}`);
+  for (const route of routes) {
+    const { method, path, controller: ctl, zh } = route;
+    let { middleware = [] } = route;
+    if (!method || !path || !ctl) continue;
+    // 拼全路径
+    const allPath = `${config.routePrefix}/${path}`;
+    // 处理中间件
+    if (middleware.length > 0) middleware = middleware.map(i => mwares[i]({ enable: true }));
+    // 注册路由
+    router[method](zh, allPath, ...middleware, ctl);
+  }
+};