Browse Source

导入第三批数据;企查查接口使用示例

lrf 8 months ago
parent
commit
81f782d7ea

+ 8 - 0
README.md

@@ -25,3 +25,11 @@
     ```
     导致文档模式查询会变得不可能,所以使用JSONB数据格式的情况,只有准确查询;
     将文档模式的设计变成表关联,联查出来
+
+## 2.数据库导入
+### 2.1 带id导入数据
+带id导入数据后,需要执行数据库命令,使id的序列从导入数据的最后一条数据开始,否则会出现id已存在问题:
+``` 
+  SELECT setval('company_id_seq', (SELECT max(id) FROM "company"));
+  SELECT setval('表名_唯一约束的字段_seq', (SELECT max(唯一约束的字段) FROM 表名));
+```

BIN
importData/20240802/2021-专精特新-企业.xlsx


BIN
importData/20240802/2024-专精特新-企业.xlsx


BIN
importData/20240802/赛事信息.xlsx


+ 90 - 11
package-lock.json

@@ -10,6 +10,7 @@
       "license": "MIT",
       "dependencies": {
         "@elastic/elasticsearch": "^8.12.2",
+        "@midwayjs/axios": "^3.16.5",
         "@midwayjs/bootstrap": "^3.12.0",
         "@midwayjs/core": "^3.12.0",
         "@midwayjs/info": "^3.12.0",
@@ -30,6 +31,7 @@
       },
       "devDependencies": {
         "@midwayjs/mock": "^3.12.0",
+        "@types/crypto-js": "^4.2.2",
         "@types/jest": "^29.2.0",
         "@types/lodash": "^4.17.4",
         "@types/node": "14",
@@ -1304,6 +1306,17 @@
         "node": ">=12.17.0"
       }
     },
+    "node_modules/@midwayjs/axios": {
+      "version": "3.16.5",
+      "resolved": "https://registry.npmmirror.com/@midwayjs/axios/-/axios-3.16.5.tgz",
+      "integrity": "sha512-6upLhAcmYSgOCUb9kMwfg7GzHJwQ+PAY0UqhbUhEyp23hr0Ht/UNEMzE7I3vWIWWRJ0BfhB0mG6t8IJ/31Eltg==",
+      "dependencies": {
+        "axios": "1.7.2"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/@midwayjs/bootstrap": {
       "version": "3.16.0",
       "resolved": "https://registry.npmmirror.com/@midwayjs/bootstrap/-/bootstrap-3.16.0.tgz",
@@ -1690,6 +1703,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/crypto-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.2.tgz",
+      "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
+      "dev": true
+    },
     "node_modules/@types/express": {
       "version": "4.17.21",
       "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.21.tgz",
@@ -2375,8 +2394,17 @@
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
-      "dev": true
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.2.tgz",
+      "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
     },
     "node_modules/babel-jest": {
       "version": "29.7.0",
@@ -3162,7 +3190,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "dependencies": {
         "delayed-stream": "~1.0.0"
       },
@@ -3527,7 +3554,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
       "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
-      "dev": true,
       "engines": {
         "node": ">=0.4.0"
       }
@@ -4423,6 +4449,25 @@
       "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
       "dev": true
     },
+    "node_modules/follow-redirects": {
+      "version": "1.15.6",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz",
+      "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/foreground-child": {
       "version": "3.1.1",
       "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -4453,7 +4498,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz",
       "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "dev": true,
       "dependencies": {
         "asynckit": "^0.4.0",
         "combined-stream": "^1.0.8",
@@ -7555,6 +7599,11 @@
         "node": ">= 6"
       }
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
     "node_modules/pump": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz",
@@ -10455,6 +10504,14 @@
       "resolved": "https://registry.npmmirror.com/@midwayjs/async-hooks-context-manager/-/async-hooks-context-manager-3.16.0.tgz",
       "integrity": "sha512-Omj/5Makf9xJLa8sNsOxfTR0Sk67qDGLD/f7LUlGnRxaevX6f1rVAeK3oqAcHiHP90gz8LqkqbSVxrkYEaspLw=="
     },
+    "@midwayjs/axios": {
+      "version": "3.16.5",
+      "resolved": "https://registry.npmmirror.com/@midwayjs/axios/-/axios-3.16.5.tgz",
+      "integrity": "sha512-6upLhAcmYSgOCUb9kMwfg7GzHJwQ+PAY0UqhbUhEyp23hr0Ht/UNEMzE7I3vWIWWRJ0BfhB0mG6t8IJ/31Eltg==",
+      "requires": {
+        "axios": "1.7.2"
+      }
+    },
     "@midwayjs/bootstrap": {
       "version": "3.16.0",
       "resolved": "https://registry.npmmirror.com/@midwayjs/bootstrap/-/bootstrap-3.16.0.tgz",
@@ -10778,6 +10835,12 @@
         "@types/node": "*"
       }
     },
+    "@types/crypto-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmmirror.com/@types/crypto-js/-/crypto-js-4.2.2.tgz",
+      "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
+      "dev": true
+    },
     "@types/express": {
       "version": "4.17.21",
       "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.21.tgz",
@@ -11310,8 +11373,17 @@
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
-      "dev": true
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "axios": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.2.tgz",
+      "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+      "requires": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
     },
     "babel-jest": {
       "version": "29.7.0",
@@ -11859,7 +11931,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "requires": {
         "delayed-stream": "~1.0.0"
       }
@@ -12127,8 +12198,7 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
-      "dev": true
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
     },
     "delegates": {
       "version": "1.0.0",
@@ -12811,6 +12881,11 @@
       "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
       "dev": true
     },
+    "follow-redirects": {
+      "version": "1.15.6",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz",
+      "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
+    },
     "foreground-child": {
       "version": "3.1.1",
       "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -12831,7 +12906,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz",
       "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "dev": true,
       "requires": {
         "asynckit": "^0.4.0",
         "combined-stream": "^1.0.8",
@@ -15189,6 +15263,11 @@
         "sisteransi": "^1.0.5"
       }
     },
+    "proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
     "pump": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz",

+ 2 - 0
package.json

@@ -5,6 +5,7 @@
   "private": true,
   "dependencies": {
     "@elastic/elasticsearch": "^8.12.2",
+    "@midwayjs/axios": "^3.16.5",
     "@midwayjs/bootstrap": "^3.12.0",
     "@midwayjs/core": "^3.12.0",
     "@midwayjs/info": "^3.12.0",
@@ -25,6 +26,7 @@
   },
   "devDependencies": {
     "@midwayjs/mock": "^3.12.0",
+    "@types/crypto-js": "^4.2.2",
     "@types/jest": "^29.2.0",
     "@types/lodash": "^4.17.4",
     "@types/node": "14",

+ 4 - 0
src/config/config.default.ts

@@ -6,4 +6,8 @@ export default {
   koa: {
     port: 7001,
   },
+  qichacha:{
+    key: 'd52e7d621b974b06a3f8a90645223c47',
+    secretKey:'230A5D226DFBD80F3BE73E42971EF837'
+  }
 } as MidwayConfig;

+ 2 - 2
src/config/config.self.ts

@@ -52,8 +52,8 @@ export default {
         port: 54321,
         entities: ['./entity'],
         type: 'postgres',
-        synchronize: true, // 如果第一次使用,不存在表,有同步的需求可以写 true,注意会丢数据
-        logging: true,
+        synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true,注意会丢数据
+        logging: false,
       },
       logs: {
         database: logsDB,

+ 2 - 0
src/configuration.ts

@@ -12,6 +12,7 @@ import { CustomErrorFilter } from './filter/customError.filter';
 import { DefaultErrorFilter } from './filter/defaultError.filter';
 import { CheckTokenMiddleware } from './middleware/checkToken.middleware';
 import { ResponseMiddleware } from './middleware/response.middleware';
+import * as axios from '@midwayjs/axios';
 @Configuration({
   imports: [
     koa,
@@ -19,6 +20,7 @@ import { ResponseMiddleware } from './middleware/response.middleware';
     jwt,
     redis,
     orm,
+    axios,
     {
       component: info,
       enabledEnvironment: ['local'],

+ 12 - 2
src/controller/home.controller.ts

@@ -2,23 +2,33 @@ import { Controller, Get, Inject } from '@midwayjs/core';
 import { initTwoService } from '../service/initData/initTwo.service';
 import { InitRegionService } from '../service/initData/initRegion.service';
 import { initOneService } from '../service/initData/initOne.service';
+import { QichachaService } from '../service/thirdParty/qichacha.service';
+import { initThreeService } from '../service/initData/initThree.service';
 
 @Controller('/')
 export class HomeController {
+  @Inject()
+  threeService: initThreeService;
   @Inject()
   twoService: initTwoService;
   @Inject()
   oneService: initOneService;
   @Inject()
   regionService: InitRegionService;
+  @Inject()
+  qichachaService: QichachaService;
   @Get('/')
   async home(): Promise<any> {
     // await this.oneService.addTags();
     // await this.oneService.addImportDataTags();
     // await this.twoService.addTags();
     // await this.twoService.addImportDataTags();
-    await this.oneService.dataToUse();
-    await this.twoService.dataToUse();
+    // await this.oneService.dataToUse();
+    // await this.twoService.dataToUse();
+    // const data = await this.qichachaService.searchByName('长春市福瑞科技');
+    // await this.threeService.import2021Company();
+    // await this.threeService.import2024Company();
+    // await this.threeService.importMatching();
     return 'ok';
     // return 'starting...';
   }

+ 1 - 0
src/controller/system/tags.controller.ts

@@ -11,6 +11,7 @@ const namePrefix = '标签';
 @ApiTags(['标签'])
 @Controller('/tags', { tagName: namePrefix })
 export class TagsController implements BaseController {
+  controllerCode = 'system_tags';
   @Inject()
   service: TagsService;
 

+ 3 - 0
src/entity/platform/sign.entity.ts

@@ -25,4 +25,7 @@ export class Sign extends BaseModel {
   remark: string;
   @Column({ type: 'character varying', nullable: true, comment: '状态', default: '0' })
   status: string;
+
+  @Column({ type: 'character varying', nullable: true, comment: '隶属单位' })
+  unit: string;
 }

+ 7 - 0
src/interface/platform/sign.interface.ts

@@ -30,6 +30,8 @@ export class FVO_sign {
   'remark': string = undefined;
   @ApiProperty({ description: '状态' })
   'status': string = undefined;
+  @ApiProperty({ description: '隶属单位' })
+  'unit': string = undefined;
 }
 
 export class QDTO_sign extends SearchBase {
@@ -43,6 +45,8 @@ export class QDTO_sign extends SearchBase {
   'time': string = undefined;
   @ApiProperty({ description: '状态' })
   'status': string = undefined;
+  @ApiProperty({ description: '隶属单位' })
+  'unit': string = undefined;
 }
 
 export class QVO_sign extends FVO_sign {
@@ -86,6 +90,9 @@ export class CDTO_sign {
   @ApiProperty({ description: '状态' })
   @Rule(RuleType['string']().empty(''))
   'status': string = undefined;
+  @ApiProperty({ description: '隶属单位' })
+  @Rule(RuleType['string']().empty(''))
+  'unit': string = undefined;
 }
 
 export class CVO_sign extends FVO_sign {

+ 217 - 0
src/service/initData/initThree.service.ts

@@ -0,0 +1,217 @@
+import { Provide } from '@midwayjs/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository } from 'typeorm';
+import { Tags } from '../../entity/system/tags.entity';
+import * as Excel from 'exceljs';
+import * as path from 'path';
+import { Region } from '../../entity/system/region.entity';
+import { get, isArray, isObject, omit, uniq, uniqBy } from 'lodash';
+import { Company } from '../../entity/users/company.entity';
+import dayjs = require('dayjs');
+import { Match } from '../../entity/platform/match.entity';
+import { Sign } from '../../entity/platform/sign.entity';
+@Provide()
+export class initThreeService {
+  tags = ['20240802'];
+  @InjectEntityModel(Tags)
+  tagsModel: Repository<Tags>;
+  @InjectEntityModel(Region)
+  regionModel: Repository<Region>;
+  @InjectEntityModel(Company)
+  companyModel: Repository<Company>;
+  @InjectEntityModel(Match)
+  matchModel: Repository<Match>;
+  @InjectEntityModel(Sign)
+  signModel: Repository<Sign>;
+
+  async initData() {}
+
+  /**
+   * 获取excel表头对应的字段
+   * @returns 表格列对应字段对象
+   */
+  companyMeta() {
+    return {
+      2: 'area1',
+      3: 'name',
+    };
+  }
+
+  async import2021Company() {
+    const p = path.resolve(__dirname, '../../../importData/20240802', '2021-专精特新-企业.xlsx');
+    const tags = [...this.tags, '2021专精特新'];
+    await this.importCompany(p, tags);
+    await this.addTags(tags);
+  }
+
+  async import2024Company() {
+    const p = path.resolve(__dirname, '../../../importData/20240802', '2024-专精特新-企业.xlsx');
+    const tags = [...this.tags, '2024专精特新'];
+    await this.importCompany(p, tags);
+    await this.addTags(tags);
+  }
+
+  async importCompany(path, tags) {
+    const wb = new Excel.Workbook();
+    await wb.xlsx.readFile(path);
+    const sheet = wb.getWorksheet(1);
+    if (!sheet) return;
+    const meta = this.companyMeta();
+    let allData = [];
+    sheet.eachRow((row, ri) => {
+      if (ri === 1) {
+        // 不处理
+      } else {
+        const obj = {};
+        row.eachCell((cell, ci) => {
+          let val = cell.value;
+          const key = meta[ci];
+          if (key) {
+            if (isObject(val)) {
+              if (isArray(get(val, 'richText'))) {
+                let text = '';
+                const richText = get(val, 'richText', []);
+                for (const t of richText) {
+                  text = `${text}${get(t, 'text')}`;
+                }
+                val = text;
+              }
+            }
+            obj[key] = val;
+          }
+        });
+        allData.push(obj);
+      }
+    });
+    for (const i of allData) {
+      // 验重,企业是否存在
+      const result = await this.companyModel.createQueryBuilder().where(`name = :name`, { name: i.name }).getOne();
+      if (result) {
+        i.id = get(result, 'id');
+        // 标签合并,之后有id的修改,没id的添加
+        i.tags = get(result, 'tags', []);
+        if (i.tags === null) i.tags = [];
+        i.tags = uniq([...i.tags, ...tags]);
+        // 有地区之后就不需要处理地区了
+        i.area = get(result, 'area', []);
+        if (i.area.length > 0) continue;
+      }
+      // #region 地区处理
+      i.tags = tags;
+      i.area = ['吉林省'];
+      const builder = this.regionModel.createQueryBuilder();
+      builder.where(`name like '%${i.area1}%'`);
+      builder.andWhere("level = 'city'");
+      builder.andWhere("code like '22%'");
+      const r1 = await builder.getOne();
+      if (r1) i.area.push(r1.name);
+      // 延边州和梅河口需要特殊处理
+      if (i.area1.includes('梅河口')) i.area = ['吉林省', '通化市', '梅河口市'];
+      else if (i.area1.includes('延边')) i.area.push('延边朝鲜族自治州');
+      // 地区处理不通过就不进行处理了
+      if (i.area.length > 1) delete i.area1;
+      else i.noarea = true;
+    }
+    allData = allData.filter(f => !f.noarea);
+    const updateList = allData.filter(f => f.id);
+    const insertList = allData.filter(f => !f.id);
+    await this.companyModel.createQueryBuilder().insert().into(Company).values(insertList).execute();
+    for (const i of updateList) {
+      await this.companyModel.createQueryBuilder().update(Company).set(omit(i, 'id')).where(`id = :id`, { id: i.id }).execute();
+    }
+  }
+
+  matchingMeta() {
+    return {
+      2: 'name',
+      3: 'start_time',
+      4: 'address',
+      5: 'sign.name',
+      6: 'sign.unit',
+    };
+  }
+
+  async importMatching() {
+    const p = path.resolve(__dirname, '../../../importData/20240802', '赛事信息.xlsx');
+    const wb = new Excel.Workbook();
+    await wb.xlsx.readFile(p);
+    const sheet = wb.getWorksheet(1);
+    if (!sheet) return;
+    // 导入数据分两个表, 培训名称和培训时间(开始时间),培训地点都属于赛事
+    // 培训人姓名和企业名称(隶属单位)都属于参赛人员部分
+    const meta = this.matchingMeta();
+    let allData = [];
+    sheet.eachRow((row, ri) => {
+      if (ri === 1) {
+        // 不处理
+      } else {
+        const obj = {};
+        row.eachCell((cell, ci) => {
+          let val = cell.value;
+          const key = meta[ci];
+          if (key) {
+            if (isObject(val)) {
+              if (isArray(get(val, 'richText'))) {
+                let text = '';
+                const richText = get(val, 'richText', []);
+                for (const t of richText) {
+                  text = `${text}${get(t, 'text')}`;
+                }
+                val = text;
+              }
+            }
+            obj[key] = val;
+          }
+        });
+        allData.push(obj);
+      }
+    });
+    let matchList = allData.map(i => ({ ...omit(i, ['sign.name', 'sign.unit', 'start_time']), start_time: dayjs(i.start_time).format('YYYY-MM-DD') }));
+    matchList = uniqBy(matchList, 'name');
+    for (const m of matchList) {
+      const name = get(m, 'name');
+      const match = await this.matchModel.createQueryBuilder().where('name = :name', { name }).getOne();
+      let mid;
+      // 有赛事,就合并标签
+      if (match) {
+        mid = match.id;
+        let otags = get(match, 'tags', []);
+        if (otags === null) otags = [];
+        const newTags = uniq([...otags, ...this.tags]);
+        const updateData: any = { tags: newTags };
+        await this.matchModel.createQueryBuilder().update(Match).set(updateData).where('id = :id', { id: match.id }).execute();
+      } else {
+        const insertData: any = { ...m, tags: this.tags };
+        const result = await this.matchModel.insert(insertData);
+        mid = get(result, 'identifiers.0.id');
+      }
+      // 已经过滤出赛事数据 m 和 参加该赛事的人员名单 signList.接下来先创建赛事数据,然后再将赛事数据id放到各个参赛人员数据中
+      let signList = allData.filter(f => f.name === name);
+      signList = signList.map(i => ({ ...omit(i, ['name', 'address', 'start_time']), match: mid }));
+      for (const i of signList) {
+        const obj = {};
+        for (const key in i) {
+          const val = i[key];
+          if (key.includes('sign.')) {
+            const nk = key.replace('sign.', '');
+            obj[nk] = val;
+          } else obj[key] = val;
+        }
+        const num = await this.signModel.createQueryBuilder().where('name = :name', { name: i.name }).andWhere('match = :match', { match: i.match }).getCount();
+        if (num <= 0) await this.signModel.insert(obj);
+      }
+    }
+    await this.addTags(this.tags)
+  }
+
+  async addTags(tags: Array<string>) {
+    const arr = [];
+    for (const i of tags) {
+      const num = await this.tagsModel.createQueryBuilder().where(`title = :title`, { title: i }).getCount();
+      if (num > 0) continue;
+      arr.push({ title: i });
+    }
+    if (arr.length <= 0) return;
+    await this.tagsModel.createQueryBuilder().insert().into(Tags).values(arr).updateEntity(false).execute();
+  }
+}

+ 54 - 0
src/service/thirdParty/qichacha.service.ts

@@ -0,0 +1,54 @@
+import { Config, Provide, InjectClient } from '@midwayjs/core';
+import * as CryptoJS from 'crypto-js';
+import dayjs = require('dayjs');
+import { HttpServiceFactory, HttpService } from '@midwayjs/axios';
+import { get } from 'lodash';
+import { ServiceError } from '../../error/service.error';
+/**
+ * 企查查服务
+ */
+@Provide()
+export class QichachaService {
+  @Config('qichacha')
+  qichacha: any;
+
+  @InjectClient(HttpServiceFactory, 'default')
+  axios: HttpService;
+
+  getToken() {
+    const { key, secretKey } = this.qichacha;
+    const Timespan = dayjs().unix();
+    const str = `${key}${Timespan}${secretKey}`;
+    const token = CryptoJS.MD5(str).toString().toUpperCase();
+    return { token, key, secretKey, Timespan, str };
+  }
+
+  /**
+   * 通过企业名称模糊搜索匹配企业
+   * https://openapi.qcc.com/dataApi/1027
+   * @param {String} searchName 企业名称
+   */
+  async searchByName(searchName: string) {
+    const info = this.getToken();
+    const uri = ' https://api.qichacha.com/NameSearch/GetList';
+    const url = `${uri}?key=${get(info, 'key')}&searchName=${searchName}`;
+    const result = await this.axios.get(url, {
+      headers: {
+        Token: get(info, 'token'),
+        Timespan: get(info, 'Timespan'),
+      },
+    });
+    if (!result) return;
+    const list = get(result, 'Data')
+    return list;
+  }
+
+  analysisResponse(response) {
+    const status = get(response, 'status');
+    if (status !== 200) return;
+    const result = get(response, 'data');
+    const apiStatus = get(result, 'Status');
+    if (status !== '200') return;
+    return get(result, 'Result');
+  }
+}