CmsWing源码分析(七)

2021SC@SDUSC

此次分析文件src/model/csmwing/action.js
该文件中的方法主要是对行为进行处理。

一、get_action()

此方法用于获取行为数据。
此方法有两个参数,参数 id 是行为id,参数 field 是需要获取的字段。
方法 think.isEmpty(obj) 用于判断参数是否为空,方法 think.isNumberString(str) 用于判断输入是不是一个字符串类型的数字。
id 需不为空且为字符串类型的数字。首先对 id 进行判断,若不满足以上任意一点,方法无法执行,返回false。
通过 map 限定的条件,在模型中查找数据。若 field 即需要获取的字段不为空,返回查找到的数据中的字段;若 field 为空,返回完整数据。

async get_action(id, field) {
    id = id || null, field = field || null;
    if (think.isEmpty(id) && !think.isNumberString(id)) {
      return false;
    }
    const list = {};
    const map = {'status': ['>', -1], 'id': id};
    list[id] = await this.where(map).field(true).find();

    return think.isEmpty(field) ? list[id] : list[id][field];
  }

二、log()

此方法用于记录行为日志,并执行该行为的规则。
此方法有六个参数,参数 action 是行为标识,参数 model 是触发行为的模型名,参数 record_id 是触发行为的记录id,参数 user_id 是执行行为的用户id。
首先进行参数检查。若 action、model、record_id 任意一个为空,方法无法执行,返回字符串 ‘参数不能为空’。
查询行为,判断行为是否执行。若行为状态参数不为1,说明行为未执行,返回字符串 ‘该行为被禁用’。随后插入行为日志并解析行为规则,生成日志备注。若未定义日志规则,日志中会记录操作url。

async log(action, model, record_id, user_id, ip, url) {
    if (think.isEmpty(action) || think.isEmpty(model) || think.isEmpty(record_id)) {
      return '参数不能为空';
    }

    if (think.isEmpty(user_id)) {
      const user = await think.session('userInfo');
      const id = user.id;
      user_id = id;
    }

    const action_info = await this.where({name: action}).find();
    if (action_info.status != 1) {
      return '该行为被禁用';
    }
    
    const data = {
      action_id: action_info.id,
      user_id: user_id,
      action_ip: _ip2int(ip),
      model: model,
      record_id: record_id,
      create_time: new Date().valueOf()
    };
    data.remark = '';
    if (!think.isEmpty(action_info.log)) {
      const match = action_info.log.match(/\[(\S+?)\]/g);
      if (!think.isEmpty(match)) {
        const log = {
          user: user_id,
          record: record_id,
          model: model,
          time: new Date().valueOf(),
          data: {
            user: user_id,
            record: record_id,
            model: model,
            time: new Date().valueOf()
          }
        };

        const replace = [];
        for (let val of match) {
          val = val.replace(/(^\[)|(\]$)/g, '');
          const param = val.split('|');
          if (!think.isEmpty(param[1])) {
            if (param[0] == 'user') {
              replace.push(await call_user_func(param[1], log[param[0]]));
            } else {
              replace.push(call_user_func(param[1], log[param[0]]));
            }
          } else {
            replace.push(log[param[0]]);
          }
        }

        data.remark = str_replace(match, replace, action_info.log);
      } else {
        data.remark = action_info.log;
      }
    } else {
      data.remark = '操作url:' + url;
    }
    if (!think.isNumber(record_id)) {
      data.record_id = 0;
    }
    await this.model('action_log').add(data);

    if (!think.isEmpty(action_info.rule)) {
      const rules = await this.parse_action(action, user_id);
      const res = await this.execute_action(rules, action_info.id, user_id);
    }
  }

三、parse_action()

此方法用于解析行为规则。
此方法共有两个参数,参数 action 是行为id或者name,参数 self 用于替换规则里的变量为执行用户的id。若解析出错,返回布尔值false;成功返回规则数组。
下面是对项目中规则定义和规则字段的解释。

规则定义 table:table|field:field|condition:condition|rule:rule[|cycle:cycle|max:max][;…]
规则字段解释:
table->要操作的数据表,不需要加表前缀;
field->要操作的字段;
condition->操作的条件,目前支持字符串,默认变量 ${self} 为执行行为的用户;
rule->对字段进行的具体操作,目前支持加或者减,如:10,-10;
cycle->执行周期,单位(小时),表示cycle小时内最多执行max次;
max->单个周期内的最大执行次数(cycle和max必须同时定义,否则无效)。
单个行为后可加 ; 连接其他规则

首先确认行为不为空。若行为为空,方法无法执行,返回false。
查询行为信息。这里的行为信息仍通过设置一个 map 来进行查询,在数据库中查找到的 id 为 action、name 为 action 的数据即为我们要查找的信息。将查找到的信息赋值给 info。
解析规则。方法 str_replace() 是在文件 src/bootstrap/global.js 中定义的方法,在这里用来将规则 rules 中的参数 self 设置为此方法中传入的参数 self,即执行用户的id。for 循环通过 split 方法将规则按照字符 “|” 和 “:” 分隔开,并push到数组变量 ret 中。
方法最后返回 ret ,即解析完成的规则。

async parse_action(action, self) {
    if (think.isEmpty(action)) {
      return false;
    }
    let map;
    if (think.isNumberString(action)) {
      map = {'id': action};
    } else {
      map = {'name': action};
    }
    const info = await this.where(map).find();
    if (!info || info.status != 1) {
      return false;
    }
    
    let rules = info.rule;
    rules = str_replace('${self}', self, rules);
    rules = rules.split(';');
    const ret = [];
    for (const val of rules) {
      if (val) {
        const obj = {};
        const rule = val.split('|');
        for (const v of rule) {
          const field = think.isEmpty(v) ? [] : v.split(':');
          console.log(field);
          if (!think.isEmpty(field)) {
            obj[field[0]] = field[1];
          }
        }
        ret.push(obj);
      }
    }
    return ret;
  }

四、execute_action()

此方法用于执行行为。
此方法有三个参数,参数 rules 是解析后的规则数组,参数 action_id 是行为id,参数 user_id 是执行的用户id。若执行成功返回布尔值 true;执行失败返回布尔值 false。
首先确认规则可执行,行为id和执行的用户id不为空。若以上任意条件不满足,方法无法执行,返回false。
对规则数组中的每一条规则进行执行周期的检查。若规则不处于执行周期内,那么for循环内执行 continue 语句,跳过该规则转而检查下一条规则。若规则处于执行周期内,那么执行规则要求的数据库操作。

async execute_action(rules, action_id, user_id) {

    if (!rules || think.isEmpty(action_id) || think.isEmpty(user_id)) {
      return false;
    }

    let ret = true;
    for (const rule of rules) {
      const map = {
        action_id: action_id,
        user_id: user_id
      };
      if (!think.isEmpty(rule.cycle) && !think.isEmpty(rule.max)) {
        map.create_time = ['>', new Date().valueOf() - rule.cycle * 3600 * 1000];
        const exec_count = await this.model('action_log').where(map).count();
        if (exec_count > rule.max) {
          continue;
        }
      }
      const model = this.model(rule.table);
      const field = rule.field;
      let step = parseInt(rule.rule);
      let res;
      if (step >= 0) {
        res = await model.where(rule.condition).increment(field, step);
      } else {
        step = Math.abs(step);
        res = await model.where(rule.condition).decrement(field, step);
      }
      if (!res) {
        ret = false;
      }
    }
    return ret;
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值