Просмотр исходного кода

feat: 根据ghID一键登陆接口

zhangyuting 5 лет назад
Родитель
Сommit
fb8913a91b

+ 5 - 0
config/default.js

@@ -11,6 +11,11 @@ module.exports = {
     env: process.env.NODE_ENV,
     cache: {
       prefix: 'MINI_GAME_AUTO_AD_SERVER'
+    },
+    // 灯塔系统-一键登陆小游戏 https://mp.weixin.qq.com/
+    one_key_load: {
+      token: 'd8d7a9139328f5f00f9b04302414a7e3',
+      client: 'crawler'
     }
   }
 }

+ 42 - 0
src/app/controller/base.ts

@@ -0,0 +1,42 @@
+import { ExtendableContext } from 'koa'
+import { Response } from '@/app/types/response'
+
+export interface ICurd {
+  create (ctx:ExtendableContext): Promise<Response<unknown>>
+  update (ctx:ExtendableContext): Promise<Response<unknown>>
+  get (ctx:ExtendableContext): Promise<Response<unknown>>
+  list (ctx:ExtendableContext): Promise<Response<unknown>>
+  all (ctx:ExtendableContext): Promise<Response<unknown>>
+  destroy (ctx:ExtendableContext): Promise<Response<unknown>>
+}
+
+export class Base implements ICurd {
+  public prefix = 'base'
+  // constructor () {
+  //   this.prefix = '/' + Parser.getClassName(this).toLowerCase()
+  // }
+
+  all (ctx: ExtendableContext): Promise<Response<unknown>> {
+    return Promise.resolve(undefined)
+  }
+
+  create (ctx: ExtendableContext): Promise<Response<unknown>> {
+    return Promise.resolve(undefined)
+  }
+
+  destroy (ctx: ExtendableContext): Promise<Response<unknown>> {
+    return Promise.resolve(undefined)
+  }
+
+  get (ctx: ExtendableContext): Promise<Response<unknown>> {
+    return Promise.resolve(undefined)
+  }
+
+  list (ctx: ExtendableContext): Promise<Response<unknown>> {
+    return Promise.resolve(undefined)
+  }
+
+  update (ctx: ExtendableContext): Promise<Response<unknown>> {
+    return Promise.resolve(undefined)
+  }
+}

+ 48 - 0
src/app/controller/oneLoadKey.ts

@@ -0,0 +1,48 @@
+import { Base } from '@/app/controller/base'
+
+import { OneLoadKeyService } from '@/app/service/oneLoadKeyService'
+import { ExtendableContext } from 'koa'
+import { Response } from '@/app/types/response'
+
+export class OneLoadKey extends Base {
+  protected service: OneLoadKeyService
+  public prefix = '/one_load_key'
+
+  constructor () {
+    super()
+    this.service = new OneLoadKeyService()
+  }
+
+  async get (ctx:ExtendableContext):Promise<Response<IData>> {
+    const params = ctx.request.body
+
+    const ret: Response<IData> = {
+      code: 404,
+      msg: '没有数据',
+      data: null
+    }
+    try {
+      if (!params.ghID) {
+        throw new Error('没有参数')
+      }
+      const result = await this.service.gen(params.ghID)
+      console.log(result)
+      if (result && result.status.toLowerCase() === 'ok') {
+        ret.msg = 'ok'
+        ret.code = 200
+        let cookieAsString = ''
+        Object.entries(result.data.cookies).forEach(item => {
+          cookieAsString += `${item[0]}=${item[1]}; `
+        })
+        result.data.cookie_as_string = cookieAsString.slice(0, -2)
+        ret.data = result.data
+      } else {
+        throw new Error(result.status)
+      }
+    } catch (e) {
+      ret.code = 500
+      ret.msg = '操作失败'
+    }
+    return ret
+  }
+}

+ 21 - 0
src/app/middleware/request.ts

@@ -0,0 +1,21 @@
+import { ExtendableContext, Next } from 'koa'
+import _ from 'lodash'
+
+export class Request {
+  static async handle (ctx: ExtendableContext, next: Next): Promise<unknown> {
+    const query = ctx.request.query
+    ctx.request.body = _.merge(query, ctx.request.body)
+    ctx.header.ip = await Request.ip(ctx)
+    return next()
+  }
+
+  static async ip (ctx: ExtendableContext): Promise<string> {
+    let ip = ctx.request.headers['x-forwarded-for'] ||
+      ctx.request.ip ||
+      ctx.req.socket.remoteAddress
+    if (typeof ip === 'string' && ip?.split(',').length > 0) {
+      ip = ip.split(',')[0]
+    }
+    return ip.toString()
+  }
+}

+ 16 - 0
src/app/provider/apiMiddleware.ts

@@ -0,0 +1,16 @@
+// 对外提供格式化好的中间件
+import { Request } from '@/app/middleware/request'
+
+export class ApiMiddleware {
+  static get ():any[] {
+    return [
+      Request.handle
+    ]
+  }
+
+  static login ():any[] {
+    return [
+      Request.handle
+    ]
+  }
+}

+ 35 - 0
src/app/service/oneLoadKeyService.ts

@@ -0,0 +1,35 @@
+import { Random } from '@/tool/random'
+import moment from 'moment'
+import { Encrypt } from '@/tool/encrypt'
+import config from 'config'
+import got from 'got'
+
+const token = config.get<string>('app.one_key_load.token')
+const client = config.get<string>('app.one_key_load.client')
+
+export class OneLoadKeyService {
+  // http://onekeyload.mokasz.com/v1/onekeyload/dictionary/gh_bf0ce3ecc330/cookies
+  private url = 'http://onekeyload.mokasz.com/v1/onekeyload/dictionary/'
+
+  async gen (ghID:string):Promise<IOneLoadKeyResponse> {
+    const url = `${this.url}${ghID}/cookies`
+    const params = await this.getParams()
+    return await got.get<IOneLoadKeyResponse>(url, { searchParams: params }).json()
+  }
+
+  async getParams (): Promise<Record<string, string>> {
+    const nonce = Random.number(10086, 99999).toString()
+    const echoStr = Random.number(1000, 5000).toString()
+    const timestamp = moment().unix().toString()
+    const signature = await Encrypt.sha1([timestamp, nonce, token].sort((a, b) => {
+      return a.localeCompare(b)
+    }).join(''))
+    return {
+      client: client,
+      nonce: nonce,
+      echostr: echoStr,
+      timestamp: timestamp,
+      signature: signature
+    }
+  }
+}

+ 56 - 0
src/app/types/oneLoadKeyResponse.ts

@@ -0,0 +1,56 @@
+interface IOneLoadKey {
+  client?: string
+  nonce?: number
+  echostr?: number
+  timestamp?: number
+  signature?: string
+}
+
+interface IOneLoadKeyResponse {
+  status?: string;
+  data?: IData;
+}
+
+/* 自动生成的 Interface */
+
+interface IData {
+  type?: number;
+  token?: string;
+  cookies_status?: number;
+  cookies_at?: number;
+  cookies_src?: string;
+  expires_in?: number;
+  err_msg?: string;
+  err_at?: string;
+  cookies?: {
+    bizuin?: string;
+    cert?: string;
+    data_bizuin?: string;
+    data_ticket?: string;
+    fake_id?: string;
+    login_certificate?: string;
+    login_sid_ticket?: string;
+    master_sid?: string;
+    master_ticket?: string;
+    master_user?: string;
+    mm_lang?: string;
+    'openid2ticket_osjX35e94xe584pQm-oNKPKF2CQM'?: string;
+    rand_info?: string;
+    safecode?: string;
+    slave_bizuin?: string;
+    slave_sid?: string;
+    slave_user?: string;
+    sp_sid?: string;
+    sp_slave_user?: string;
+    sp_user?: string;
+    ticket?: string;
+    ticket_certificate?: string;
+    ticket_id?: string;
+    ticket_uin?: string;
+    ua_id?: string;
+    uuid?: string;
+    xid?: string;
+  }
+  cookie_as_string?:string
+
+}

+ 0 - 0
src/app/types/request.ts


+ 25 - 0
src/app/types/response.ts

@@ -0,0 +1,25 @@
+export interface Response<T> {
+  code?:number
+  msg?:string
+  data?: T
+}
+export interface OperateResponse{
+  code:number
+  msg:string
+}
+
+// page list 分页
+interface IPageInfo {
+  page: number // 页数
+  page_size: number // 页面大小
+  total_number: number // 总数
+  total_page: number // 总页数
+}
+
+// 返回列表
+export type IOEResponseList<T> = Response<{
+  list: T[]
+  page_info?: IPageInfo
+}>
+
+export type IFilterResponse<T, F extends keyof T> = IOEResponseList<Pick<T, F>>

+ 10 - 1
src/router/index.ts

@@ -1,12 +1,21 @@
 import config from 'config'
 import Router from 'koa-router'
+import { OneLoadKey } from '@/app/controller/oneLoadKey'
+import { ApiMiddleware } from '@/app/provider/apiMiddleware'
 
 const env = config.get<number>('app.env')
 const version = config.get<number>('app.version')
 
 const router = new Router()
-router.get('/', async (ctx) => {
+const middleware = ApiMiddleware.get()
+
+router.get('/', ...middleware, async (ctx) => {
   ctx.body = { version, env }
 })
 
+router.get('/one_load_key', ...middleware, async (ctx) => {
+  const oneLoadKeyController = new OneLoadKey()
+  ctx.body = await oneLoadKeyController.get(ctx)
+})
+
 export default router

+ 13 - 0
src/tool/encrypt.ts

@@ -0,0 +1,13 @@
+import crypto from 'crypto'
+
+export class Encrypt {
+  // md5 加密
+  static async md5 (value: string): Promise<string> {
+    const md5 = crypto.createHash('md5')
+    return md5.update(value).digest('hex') // 把输出编程16进制的格式
+  }
+
+  static async sha1 (v: string): Promise<string> {
+    return crypto.createHash('sha1').update(v).digest('hex')
+  }
+}

+ 57 - 0
src/tool/parser/convent2type.js

@@ -0,0 +1,57 @@
+(() => {
+  const table = document.querySelectorAll('.x-table')
+
+  const result = new Map()
+  table.forEach((v, k1) => {
+    let content = ''
+    const tbody = v.querySelector('tbody')
+    const tr = tbody.querySelectorAll('tr')
+
+    // 父级title
+    const grand = v.closest('pre')
+    const title = grand.previousElementSibling.innerHTML
+
+    tr.forEach((v, k2) => {
+      let curLevel = v.getAttribute('data-level')
+      const td = v.querySelectorAll('td')
+      if (td.length === 3) {
+        let field = td[0].querySelector('div>span').innerText
+        field = field || ''
+        let typ = td[1].innerText
+        typ = typ || ''
+        typ = toNumber(typ)
+        let des = td[2].innerText
+        des = des || ''
+        let space = ''
+        // if (des.length > 30) {
+        //   des = des.substr(0, 30)
+        //   des += '...'
+        // }
+        des = des.replace(/[\r\n]/g, ' ')
+        curLevel = Number(curLevel)
+        if (curLevel > 0) {
+          for (let i = 0; i < curLevel; i++) {
+            space += '\t'
+          }
+        }
+        content += `${space}${field}?: ${typ} // ${des} \n`
+      }
+    })
+    // if (curLevel )
+    console.log(content)
+    result.set(title, content)
+  })
+  // console.log(result);
+})()
+
+function toNumber (type) {
+  let result = type
+  switch (type) {
+    case 'float':
+      result = 'number'
+      break
+    default:
+      break
+  }
+  return result
+}

+ 74 - 0
src/tool/parser/enum.js

@@ -0,0 +1,74 @@
+// https://ad.oceanengine.com/openapi/doc/index.html?id=528
+
+function gOne (text, name = 'AutoGen') {
+  let result = `export enum ${name} { \n`
+
+  text.split('\t').map(function (v, i) {
+    // AD_STATUS_DELIVERY_OK='OK' //投放中
+    console.log(v)
+    console.log(i % 2)
+    if (i % 2 === 0) {
+      result += `\t${v} = '${v.split('_').slice(-1)}'`
+    } else {
+      result += ` // ${v} \n`
+    }
+  })
+  result += '}'
+  console.log(result)
+}
+
+function gOne4ThreeColumn (text, name = 'AutoGen') {
+  let result = `export enum ${name} { \n`
+
+  text.split('\t').map(function (v, i) {
+    // AD_STATUS_DELIVERY_OK='OK' //投放中
+    if (i % 3 === 0) {
+      result += `\t${v}`
+    } else if (i % 3 === 1) {
+      if (isNaN(v)) {
+        result += ` = '${v}',`
+      } else {
+        result += ` = ${v},`
+      }
+    } else {
+      result += ` // ${v} \n`
+    }
+  })
+  result += '}'
+  console.log(result)
+}
+
+// 获取所有 枚举
+
+(() => {
+  function getAllEnum () {
+    const table = document.querySelectorAll('table')
+    let result = ''
+    const regx = /[-,.]/g
+    table.forEach(function (v, i) {
+      const title = v.previousElementSibling.innerText
+      const tbody = v.querySelector('tbody')
+      result += `// ${title} \n`
+      result += `export enum ${title.replace(regx, '_')} { \n`
+      tbody.querySelectorAll('tr').forEach(function (v2, i2) {
+        const td = v2.querySelectorAll('td')
+        const td1Text = td[0].innerText
+        let td1Temp = td1Text
+        const td2Text = td[1].innerText.replace(/[\r\n]/g, ' ')
+
+        if (!isNaN(td1Temp) || !isNaN(td1Temp.substr(0, 1))) {
+          td1Temp = `Num_${td1Temp}`
+        }
+
+        td1Temp = td1Temp.replace(regx, '_')
+
+        // result += `\t${td1Temp} = '${td1Text.split('_').slice(-1)}', // ${td2Text} \n`
+        result += `\t${td1Temp} = '${td1Text}', // ${td2Text} \n`
+      })
+      result += '} \n\n'
+    })
+    console.log(result)
+  }
+
+  getAllEnum()
+})()

+ 95 - 0
src/tool/parser/interface2schema.js

@@ -0,0 +1,95 @@
+const s = `export interface IAuto{ 
+  app_id ?:string
+  config_id?:number
+  ghid ?:string
+  date ?:string
+  observer ?:string
+  begin_date ?:string
+  fans_package ?:string
+  rate ?:number
+  run_group ?:string
+  group ?:string
+  book_name ?:string
+  type ?:string
+  delivery_date ?:string
+  year ?:string
+  website_name ?:string
+  ads_position ?:string
+  run_days ?:string
+  wechat_name ?:string
+  run_name ?:string
+  main_input_recharge ?:string
+  history_paid_user ?:string
+  history_fans ?:string
+  remain_rate ?:string
+  cs_rate ?:string
+  active_rate ?:string
+  income ?:string
+  total_income ?:string
+  total_cost ?:string
+  total_new_income ?:string
+  daily_roi_increase ?:string
+  main_book_ratio ?:string
+  arppu ?:string
+  payment_cost ?:string
+  total_roi ?:string
+}`
+
+run(s)
+
+function run (s, keys = [], suffix = 'Schema') {
+  const token = splitTokens(s)
+  const schema = genSchema(token, keys, suffix)
+  console.log(schema)
+}
+
+function splitTokens (interfaceString) {
+  const tokens = []
+  const patternTitle = /\w*\s*interface\s*(\w+)\s*{/
+  const patternToken = /(\w+)\s*(\??):\s*(\w+)\s*/
+
+  if (interfaceString.search('interface') === -1) {
+    throw Error('can not found interface')
+  }
+  interfaceString = interfaceString.trim()
+  const splitInterface = interfaceString.split('\n')
+
+  // 去掉第一个和最后一个
+  const firstLine = splitInterface.shift()
+  const [, title] = firstLine.match(patternTitle)
+  splitInterface.pop()
+
+  splitInterface.map((v) => {
+    // 每行分为3个token, : 前面是否有? 则是 required的 true or false, 后面则是类型, 不能匹配到的默认字符串
+    // name
+    let [, name, isRequired, type] = v.match(patternToken)
+    let note = v.split('//')[1]
+    note = note ? note.trim() : ''
+    isRequired = !isRequired
+    if (type.length < 0) {
+      throw Error('can not find value type')
+    }
+    type = type[0].toUpperCase() + type.slice(1)
+    tokens.push({ name: name, isRequired: isRequired, type: type, note: note })
+  })
+  return { title: title, tokens: tokens }
+}
+
+function genSchema (token, keys = [], suffix = 'Schema') {
+  let schema = ''
+  const title = token.title
+  const tokens = token.tokens
+  schema += `export const ${title}${suffix} = new Schema({ \n`
+  tokens.map(v => {
+    let index = false
+    if (keys.indexOf(v.name) !== -1) {
+      index = true
+    }
+    if (v.note) {
+      v.note = ` // ${v.note}`
+    }
+    schema += `  ${v.name}: { type: ${v.type}, required: ${v.isRequired}, index: ${index} },${v.note}\n`
+  })
+  schema += '})'
+  return schema
+}

+ 4 - 0
src/tool/parser/json2interface.js

@@ -0,0 +1,4 @@
+
+function json2interface (s) {
+
+}

+ 130 - 0
src/tool/parser/mysqltable2interface.js

@@ -0,0 +1,130 @@
+const s = `-- auto-generated definition
+create table g_novel_monitor_data_source
+(
+    id                             int unsigned auto_increment primary key,
+    config_id                      int                 default 0                 not null comment '配置表的id,表g_novel_monitor_data_config',
+    account_status                 varchar(15)         default ''                not null comment '帐号状态',
+    begin_date                     varchar(15)         default ''                not null comment '起投日期',
+    delivery_date                  varchar(15)         default ''                not null comment '交付日期',
+    observer                       varchar(20)         default ''                not null comment '投放人员',
+    date                           varchar(15)         default ''                not null comment '当日日期',
+    ghid                           varchar(100)        default ''                not null comment '公众号ghid',
+    wechat_name                    varchar(50)         default ''                not null comment '公众号名称',
+    type                           varchar(15)         default ''                not null comment '账号类型',
+    website_name                   varchar(30)         default ''                not null comment '所属平台',
+    rate                           varchar(10)         default ''                not null comment '书城分成',
+    increase                       varchar(10)         default ''                not null comment '新增用户',
+    new_user_recharge_num          varchar(10)         default ''                not null comment '当日新增充值人数',
+    increase_recharge              varchar(10)         default ''                not null comment '当天总付费人数',
+    new_user_recharge_money        varchar(15)         default ''                not null comment '当日新增充值(分成前)',
+    total_recharge_amount          varchar(15)         default ''                not null comment '当日总充值(分成前)',
+    business_income                varchar(15)         default ''                not null comment '商务收入',
+    traffic_income                 varchar(15)         default ''                not null comment '流量主收入',
+    new_user                       varchar(10)         default ''                not null comment '公众号新增',
+    netgain_user                   varchar(10)         default ''                not null comment '公众号净增',
+    cost                           varchar(15)         default ''                not null comment '账户币消耗',
+    action_count                   varchar(10)         default ''                not null comment '转化数',
+    run_name                       varchar(20)         default ''                not null comment '运营人员',
+    \`group\`                        varchar(15)         default ''                not null comment '分组',
+    main_input_book_name           varchar(30)         default ''                not null comment '主推书名',
+    main_role                      varchar(20)         default ''                not null comment '主角名',
+    direct_one                     varchar(20)         default ''                not null comment '定向1',
+    direct_two                     varchar(20)         default ''                not null comment '定向2',
+    history_fllow                  varchar(10)         default ''                not null comment '总关注用户',
+    total_register_user            varchar(10)         default ''                not null comment '平台注册用户',
+    history_paid_user              varchar(10)         default ''                not null comment '累计付费用户',
+    net_follow_num                 varchar(10)         default ''                not null comment '净关注用户',
+    custom_send_people_num         varchar(10)         default ''                not null comment '客服消息发送人数',
+    custom_total_uv                varchar(10)         default ''                not null comment '客服消息UV',
+    custom_daily_uv                varchar(10)         default ''                not null comment '客服消息当日UV',
+    custom_total_recharge          varchar(15)         default ''                not null comment '客服消息总充值',
+    custom_daily_recharge          varchar(15)         default ''                not null comment '客服消息当日充值',
+    menu_uv                        varchar(10)         default ''                not null comment '菜单链接总UV',
+    menu_dayuv                     varchar(10)         default ''                not null comment '菜单链接当日UV',
+    menu_money                     varchar(15)         default ''                not null comment '菜单链接总充值',
+    menu_daymt                     varchar(15)         default ''                not null comment '菜单链接当日充值',
+    promote_uv                     varchar(10)         default ''                not null comment '推广链接总UV',
+    promote_dayuv                  varchar(10)         default ''                not null comment '推广连接当日UV',
+    promote_money                  varchar(15)         default ''                not null comment '推广链接总充值',
+    promote_daymt                  varchar(15)         default ''                not null comment '推广链接当日充值',
+    history_user_paid_transactions varchar(10)         default ''                not null comment '累计总完成订单',
+    history_user_transactions      varchar(10)         default ''                not null comment '累计总订单',
+    user_paid_transactions         varchar(10)         default ''                not null comment '当日总完成订单',
+    main_input_recharge            varchar(15)         default ''                not null comment '主投书充值',
+    other_input_recharge           varchar(15)         default ''                not null comment '其他书充值',
+    history_fans                   varchar(10)         default ''                not null comment '累计粉丝',
+    fans_package                   varchar(15)         default ''                not null comment '人群包',
+    ads_position                   varchar(30)         default ''                not null comment '朋友圈 底部 小程序',
+    run_group                      varchar(30)         default ''                not null comment '运营部门',
+    year                           varchar(10)         default ''                not null comment '年份',
+    new_add_roi                    varchar(10)         default ''                not null comment '新增ROI',
+    refer_roi                      varchar(10)         default ''                not null comment '参考ROI',
+    fix_roi                        varchar(10)         default ''                not null comment '修正ROI',
+    control_roi                    varchar(10)         default ''                not null comment '控制ROI',
+    advertise_rate                 varchar(10)         default ''                not null comment '服务商返点',
+    quarter                        tinyint(2) unsigned default 0                 not null comment '季度',
+    uv                             int                                           not null comment 'uv数',
+    pv                             int                 default 0                 not null comment 'pv数',
+    quarter_calc                   tinyint(2) unsigned default 0                 not null comment '计算季度',
+    create_time                    timestamp           default CURRENT_TIMESTAMP not null comment '创建时间',
+    update_time                    timestamp           default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '修改时间'
+)
+    comment '小说监控室数据源表';
+`
+
+// s.replace('/\n/g','')
+
+function getToken (s) {
+  let token = s.trim().split('\n')
+
+  token = token.slice(3, -3)
+
+  const result = []
+  for (let i = 0; i < token.length; i++) {
+    const minToken = token[i].replace(/(\s+)/g, '$')
+    const key = minToken.split('$')[1].replace(/`/g, '')
+    let type = minToken.split('$')[2]
+    if (type.indexOf('char') !== -1) {
+      type = 'string'
+    } else if (type.indexOf('int') !== -1) {
+      type = 'number'
+    }
+    result.push({ key: key, type: type })
+  }
+  return result
+}
+
+function genInterface (token) {
+  let inter = 'interface AutoGen {' + '\n'
+  token.map(({ key, type }) => {
+    inter += `\t${key} ?: ${type} \n`
+  })
+
+  inter += '}'
+
+  console.log(inter)
+}
+
+function genSequelizeClass (token) {
+  let inter = 'interface AutoGen {' + '\n'
+  token.map(({ key, type }) => {
+    inter += `public ${key} !: ${type} \n`
+  })
+
+  inter += '}'
+
+  console.log(inter)
+}
+
+function genSequelizeInit (token) {
+  let inter = ''
+  token.map(({ key, type }) => {
+    const defaultValue = type === 'number' ? 0 : "''"
+    type = `DataTypes.${type.toUpperCase()}`
+    inter += `${key} :{ type: ${type} ,allowNull:true ,defaultValue: ${defaultValue} },\n`
+  })
+
+  console.log(inter)
+}
+
+genSequelizeInit(getToken(s))

+ 35 - 0
src/tool/parser/table2interface.ts

@@ -0,0 +1,35 @@
+// 将 html table 中的数据格式化 到接口中
+(() => {
+  const table = document.querySelectorAll('table')
+
+  const result = new Map()
+  table.forEach((v, k1) => {
+    let content = ''
+    const tbody = v.querySelector('tbody')
+    const tr = tbody.querySelectorAll('tr')
+
+    // 父级title
+    const title = `${k1}table`
+
+    tr.forEach((v, k2) => {
+      const td = v.querySelectorAll('td')
+      let field = td[0].innerText.trim()
+      field = field || ''
+      let des = td[1].innerText.trim()
+      des = des || ''
+      let space = ''
+      // if (des.length > 30) {
+      //   des = des.substr(0, 30)
+      //   des += '...'
+      // }
+      field = field.replace(/∟/g, '')
+      des = des.replace(/[\r\n]/g, ' ').trim()
+      space += '\t'
+      content += `${space}${field}?: string // ${des}\n`
+    })
+    // if (curLevel )
+    console.log(content)
+    result.set(title, content)
+  })
+  // console.log(result);
+})()

+ 6 - 0
src/tool/random.ts

@@ -0,0 +1,6 @@
+export class Random {
+  // 生成指定范围内的随机数
+  static number (min: number, max: number) {
+    return min + Math.round(Math.random() * (max - min))
+  }
+}