Kaynağa Gözat

feat(limit-purchase): 添加限购规则管理功能

- 引入 six-shop/limit-purchase 包依赖
- 创建限购规则表 limit_purchase_rule 数据库迁移文件
- 实现后台管理页面 index.vue 和规则管理组件 LimitPurchaseRule.vue
- 添加规则增删改查及地区设置功能
- 配置后台路由和控制器 RuleController
- 创建实体类 LimitPurchaseRuleEntity 和模型类 LimitPurchaseRuleModel
runphp 3 ay önce
ebeveyn
işleme
96718c4d62

+ 35 - 0
database/migrations/20251207111526_limit_purchase_rule.php

@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+use Phinx\Migration\AbstractMigration;
+
+final class LimitPurchaseRule extends AbstractMigration
+{
+    public function change(): void
+    {
+        $this->table('limit_purchase_rule', ['comment' => '限购规则表'])
+            ->addColumn('name', 'string', [
+                'length'  => 64,
+                'comment' => '规则名称',
+            ])
+            ->addColumn('regions', 'json', [
+                'null'    => true,
+                'comment' => '限购地区 JSON',
+            ])
+            ->addColumn('is_default', 'tinyinteger', [
+                'limit'   => 1,
+                'signed'  => false,
+                'default' => 0,
+                'comment' => '是否默认规则:0 否,1 是',
+            ])
+            ->addColumn('status', 'tinyinteger', [
+                'limit'   => 1,
+                'signed'  => false,
+                'default' => 1,
+                'comment' => '状态:1 启用,0 停用',
+            ])
+            ->addTimestamps('create_time', 'update_time')
+            ->create();
+    }
+}

+ 699 - 0
resource/admin/LimitPurchaseRule.vue

@@ -0,0 +1,699 @@
+<template>
+  <div class="limit-purchase-rule-container">
+    <!-- 规则列表区域 -->
+    <el-card class="rule-list-card">
+      <div slot="header" class="card-header">
+        <span>限购规则列表</span>
+        <el-button type="primary" size="small" @click="handleCreateRule">
+          新建规则
+        </el-button>
+      </div>
+
+      <el-table
+        v-loading="loading.list"
+        :data="ruleList"
+        border
+        style="width: 100%"
+      >
+        <el-table-column prop="id" label="ID" width="80" />
+        <el-table-column prop="name" label="规则名称" min-width="160" />
+        <el-table-column label="限购地区" min-width="260">
+          <template slot-scope="scope">
+            <div v-if="scope.row.regions && scope.row.regions.length">
+              <el-tag
+                v-for="(region, index) in scope.row.regions"
+                :key="index"
+                type="danger"
+                size="mini"
+                class="region-tag"
+              >
+                {{ formatRegionLabel(region) }}
+              </el-tag>
+            </div>
+            <span v-else class="text-muted">未设置</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="100">
+          <template slot-scope="scope">
+            <el-tag :type="scope.row.status === 1 ? 'success' : 'info'" size="small">
+              {{ scope.row.status === 1 ? '启用' : '停用' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="200" fixed="right">
+          <template slot-scope="scope">
+            <el-button type="text" size="small" @click="handleEditRule(scope.row)">
+              编辑
+            </el-button>
+            <el-button type="text" size="small" @click="handleEditRegions(scope.row)">
+              设置限购地区
+            </el-button>
+            <el-button type="text" size="small" class="danger-text" @click="handleDeleteRule(scope.row)">
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+
+    <!-- 规则表单弹窗:新建 / 编辑规则基本信息 -->
+    <el-dialog
+      :title="ruleForm.id ? '编辑限购规则' : '新建限购规则'"
+      :visible.sync="ruleDialog.visible"
+      width="720px"
+      :close-on-click-modal="false"
+    >
+      <el-form
+        ref="ruleFormRef"
+        :model="ruleForm"
+        :rules="ruleRules"
+        label-width="90px"
+      >
+        <el-form-item label="规则名称" prop="name">
+          <el-input v-model="ruleForm.name" maxlength="64" show-word-limit />
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-switch
+            v-model="ruleForm.status"
+            :active-value="1"
+            :inactive-value="0"
+          />
+        </el-form-item>
+
+        <!-- 在新建 / 编辑规则里直接设置限购地区(本地省市区 JSON 级联) -->
+        <el-form-item label="限购地区">
+          <div class="region-selector-wrapper">
+            <el-form label-width="60px" class="region-form inner-region-form">
+              <el-row :gutter="20" type="flex" align="middle">
+                <el-col :span="6">
+                  <el-form-item label="省份">
+                    <el-select
+                      v-model="selectedProvince"
+                      placeholder="请选择省份"
+                      filterable
+                      clearable
+                      @change="onProvinceChange"
+                    >
+                      <el-option
+                        v-for="p in provinces"
+                        :key="p.name"
+                        :label="p.name"
+                        :value="p.name"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                  <el-form-item label="城市">
+                    <el-select
+                      v-model="selectedCity"
+                      placeholder="请选择城市"
+                      :disabled="!selectedProvince"
+                      filterable
+                      clearable
+                      @change="onCityChange"
+                    >
+                      <el-option
+                        v-for="c in cities"
+                        :key="c.name"
+                        :label="c.name"
+                        :value="c.name"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                  <el-form-item label="区县">
+                    <el-select
+                      v-model="selectedArea"
+                      placeholder="请选择区县"
+                      :disabled="!selectedCity"
+                      filterable
+                      clearable
+                    >
+                      <el-option
+                        v-for="a in areas"
+                        :key="a"
+                        :label="a"
+                        :value="a"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="6" class="text-right">
+                  <el-button type="primary" @click="addRegion">
+                    <i class="el-icon-plus" /> 添加限购地区
+                  </el-button>
+                </el-col>
+              </el-row>
+            </el-form>
+
+            <div class="limited-region-list">
+              <div class="list-title">已设置的限购地区</div>
+              <div v-if="currentRuleRegions.length === 0" class="empty-tip">
+                暂未添加限购地区
+              </div>
+              <div
+                v-for="(area, index) in currentRuleRegions"
+                :key="index"
+                class="limited-area-item"
+              >
+                <span>{{ formatRegionLabel(area) }}</span>
+                <el-button
+                  type="text"
+                  class="danger-text"
+                  icon="el-icon-delete"
+                  @click="removeRegion(index)"
+                >
+                  删除
+                </el-button>
+              </div>
+            </div>
+          </div>
+        </el-form-item>
+      </el-form>
+
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="ruleDialog.visible = false">取 消</el-button>
+        <el-button type="primary" :loading="loading.saveRule" @click="submitRuleForm">
+          确 认
+        </el-button>
+      </span>
+    </el-dialog>
+
+    <!-- 地区设置弹窗:管理单条规则下的多个限购地区 -->
+    <el-dialog
+      title="设置限购地区"
+      :visible.sync="regionDialog.visible"
+      width="720px"
+      :close-on-click-modal="false"
+    >
+      <div class="region-selector-wrapper">
+        <div class="region-selector-title">
+          <i class="el-icon-warning-outline text-danger" />
+          <span> 设置限购地区</span>
+        </div>
+
+        <el-form label-width="60px" class="region-form">
+          <el-row :gutter="20" type="flex" align="middle">
+            <el-col :span="6">
+              <el-form-item label="省份">
+                <el-select
+                  v-model="selectedProvince"
+                  placeholder="请选择省份"
+                  filterable
+                  clearable
+                  @change="onProvinceChange"
+                >
+                  <el-option
+                    v-for="p in provinces"
+                    :key="p.name"
+                    :label="p.name"
+                    :value="p.name"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="城市">
+                <el-select
+                  v-model="selectedCity"
+                  placeholder="请选择城市"
+                  :disabled="!selectedProvince"
+                  filterable
+                  clearable
+                  @change="onCityChange"
+                >
+                  <el-option
+                    v-for="c in cities"
+                    :key="c.name"
+                    :label="c.name"
+                    :value="c.name"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="区县">
+                <el-select
+                  v-model="selectedArea"
+                  placeholder="请选择区县"
+                  :disabled="!selectedCity"
+                  filterable
+                  clearable
+                >
+                  <el-option
+                    v-for="a in areas"
+                    :key="a"
+                    :label="a"
+                    :value="a"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6" class="text-right">
+              <el-button type="primary" @click="addRegion">
+                <i class="el-icon-plus" /> 添加限购地区
+              </el-button>
+            </el-col>
+          </el-row>
+        </el-form>
+
+        <div class="limited-region-list">
+          <div class="list-title">已设置的限购地区</div>
+          <div v-if="currentRuleRegions.length === 0" class="empty-tip">
+            暂未添加限购地区
+          </div>
+          <div
+            v-for="(area, index) in currentRuleRegions"
+            :key="index"
+            class="limited-area-item"
+          >
+            <span>{{ formatRegionLabel(area) }}</span>
+            <el-button
+              type="text"
+              class="danger-text"
+              icon="el-icon-delete"
+              @click="removeRegion(index)"
+            >
+              删除
+            </el-button>
+          </div>
+        </div>
+      </div>
+
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="regionDialog.visible = false">取 消</el-button>
+        <el-button type="primary" :loading="loading.saveRegions" @click="submitRegions">
+          确 认 设置
+        </el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 前端内置省市区 JSON,不走接口
+// 这里只演示北京和天津两组数据,后续可按同样格式扩展
+const REGION_TREE = [
+  {
+    name: '北京市',
+    city: [
+      {
+        name: '北京市',
+        area: [
+          '东城区',
+          '西城区',
+          '崇文区',
+          '宣武区',
+          '朝阳区',
+          '丰台区',
+          '石景山区',
+          '海淀区',
+          '门头沟区',
+          '房山区',
+          '通州区',
+          '顺义区',
+          '昌平区',
+          '大兴区',
+          '平谷区',
+          '怀柔区',
+          '密云县',
+          '延庆县'
+        ]
+      }
+    ]
+  },
+  {
+    name: '天津市',
+    city: [
+      {
+        name: '天津市',
+        area: [
+          '和平区',
+          '河东区',
+          '河西区',
+          '南开区',
+          '河北区',
+          '红桥区',
+          '塘沽区',
+          '汉沽区',
+          '大港区',
+          '东丽区',
+          '西青区',
+          '津南区',
+          '北辰区',
+          '武清区',
+          '宝坻区',
+          '宁河县',
+          '静海县',
+          '蓟县'
+        ]
+      }
+    ]
+  }
+]
+
+export default {
+  name: 'LimitPurchaseRule',
+  props: {
+    axiosInstance: {
+      type: Object,
+      default: null
+    }
+  },
+  data() {
+    return {
+      // 列表数据
+      ruleList: [],
+      loading: {
+        list: false,
+        saveRule: false,
+        saveRegions: false
+      },
+
+      // 规则表单
+      ruleDialog: {
+        visible: false
+      },
+      ruleForm: {
+        id: null,
+        name: '',
+        status: 1
+      },
+      ruleRules: {
+        name: [
+          { required: true, message: '请输入规则名称', trigger: 'blur' }
+        ]
+      },
+
+      // 地区相关
+      regionDialog: {
+        visible: false,
+        ruleId: null
+      },
+      // 使用本地省市区 JSON 级联
+      provinces: REGION_TREE,
+      cities: [],
+      areas: [],
+      selectedProvince: '',
+      selectedCity: '',
+      selectedArea: '',
+      currentRuleRegions: [] // 当前规则下的地区数组
+    }
+  },
+  created() {
+    this.fetchRuleList()
+  },
+  methods: {
+    http() {
+      // 默认用全局 axios,如果父组件传入则优先用父组件的
+      return this.axiosInstance || this.$axios || this.$http
+    },
+
+    // ========== 列表相关 ==========
+    async fetchRuleList() {
+      if (!this.http()) return
+      this.loading.list = true
+      try {
+        const { data } = await this.http().get('/limit_purchase/rule')
+        // 按项目通用返回结构处理
+        this.ruleList = Array.isArray(data?.data) ? data.data : (data || [])
+      } catch (e) {
+        console.error(e)
+        this.$message.error('加载规则列表失败')
+      } finally {
+        this.loading.list = false
+      }
+    },
+
+    handleCreateRule() {
+      this.ruleForm = {
+        id: null,
+        name: '',
+        status: 1
+      }
+      // 新建规则时清空当前规则地区
+      this.currentRuleRegions = []
+      this.selectedProvince = ''
+      this.selectedCity = ''
+      this.selectedArea = ''
+      this.cities = []
+      this.areas = []
+
+      this.ruleDialog.visible = true
+      this.$nextTick(() => {
+        this.$refs.ruleFormRef && this.$refs.ruleFormRef.clearValidate()
+      })
+    },
+
+    handleEditRule(row) {
+      this.ruleForm = {
+        id: row.id,
+        name: row.name,
+        status: row.status
+      }
+      // 编辑规则时加载该规则已有的地区
+      this.currentRuleRegions = Array.isArray(row.regions) ? [...row.regions] : []
+      this.selectedProvince = ''
+      this.selectedCity = ''
+      this.selectedArea = ''
+      this.cities = []
+      this.areas = []
+
+      this.ruleDialog.visible = true
+      this.$nextTick(() => {
+        this.$refs.ruleFormRef && this.$refs.ruleFormRef.clearValidate()
+      })
+    },
+
+    submitRuleForm() {
+      this.$refs.ruleFormRef.validate(async valid => {
+        if (!valid || !this.http()) return
+        this.loading.saveRule = true
+        try {
+          if (this.ruleForm.id) {
+            // 更新:名称 + 状态 + 限购地区
+            await this.http().put(`/limit_purchase/rule/${this.ruleForm.id}`, {
+              name: this.ruleForm.name,
+              status: this.ruleForm.status,
+              regions: this.currentRuleRegions
+            })
+            this.$message.success('更新成功')
+          } else {
+            // 创建:名称 + 状态 + 限购地区
+            await this.http().post('/limit_purchase/rule', {
+              name: this.ruleForm.name,
+              status: this.ruleForm.status,
+              regions: this.currentRuleRegions
+            })
+            this.$message.success('创建成功')
+          }
+          this.ruleDialog.visible = false
+          this.fetchRuleList()
+        } catch (e) {
+          console.error(e)
+          this.$message.error('保存失败')
+        } finally {
+          this.loading.saveRule = false
+        }
+      })
+    },
+
+    handleDeleteRule(row) {
+      if (!this.http()) return
+      this.$confirm(`确认删除规则「${row.name}」吗?`, '提示', {
+        type: 'warning'
+      })
+        .then(async () => {
+          try {
+            await this.http().delete(`/limit_purchase/rule/${row.id}`)
+            this.$message.success('删除成功')
+            this.fetchRuleList()
+          } catch (e) {
+            console.error(e)
+            this.$message.error('删除失败')
+          }
+        })
+        .catch(() => {})
+    },
+
+    // ========== 地区设置相关 ==========
+
+    handleEditRegions(row) {
+      if (!row || !row.id) return
+      this.regionDialog.ruleId = row.id
+      this.selectedProvince = ''
+      this.selectedCity = ''
+      this.selectedArea = ''
+      this.cities = []
+      this.areas = []
+      this.currentRuleRegions = Array.isArray(row.regions) ? [...row.regions] : []
+      this.regionDialog.visible = true
+    },
+
+    onProvinceChange() {
+      this.selectedCity = ''
+      this.selectedArea = ''
+      this.areas = []
+      const province = REGION_TREE.find(p => p.name === this.selectedProvince)
+      this.cities = province ? province.city : []
+    },
+
+    onCityChange() {
+      this.selectedArea = ''
+      const province = REGION_TREE.find(p => p.name === this.selectedProvince)
+      const city = province ? province.city.find(c => c.name === this.selectedCity) : null
+      this.areas = city ? city.area : []
+    },
+
+    addRegion() {
+      if (!this.selectedProvince || !this.selectedCity || !this.selectedArea) {
+        this.$message.warning('请选择省、市、区县')
+        return
+      }
+
+      const region = {
+        province_id: null,
+        province_name: this.selectedProvince,
+        city_id: null,
+        city_name: this.selectedCity,
+        district_id: null,
+        district_name: this.selectedArea
+      }
+
+      // 去重:相同省市区不重复添加
+      const exists = this.currentRuleRegions.some(item =>
+        item.province_id === region.province_id &&
+        item.city_id === region.city_id &&
+        item.district_id === region.district_id
+      )
+      if (exists) {
+        this.$message.warning('该地区已在列表中')
+        return
+      }
+
+      this.currentRuleRegions.push(region)
+    },
+
+    removeRegion(index) {
+      this.currentRuleRegions.splice(index, 1)
+    },
+
+    async submitRegions() {
+      if (!this.http() || !this.regionDialog.ruleId) return
+      this.loading.saveRegions = true
+      try {
+        await this.http().post(`/limit_purchase/rule/${this.regionDialog.ruleId}/regions`, {
+          regions: this.currentRuleRegions
+        })
+        this.$message.success('限购地区设置成功')
+        this.regionDialog.visible = false
+        this.fetchRuleList()
+      } catch (e) {
+        console.error(e)
+        this.$message.error('保存限购地区失败')
+      } finally {
+        this.loading.saveRegions = false
+      }
+    },
+
+    // ========== 工具方法 ==========
+    formatRegionLabel(region) {
+      const parts = []
+      if (region.province_name) parts.push(region.province_name)
+      if (region.city_name) parts.push(region.city_name)
+      if (region.district_name) parts.push(region.district_name)
+      return parts.join(' - ') || '未知地区'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.limit-purchase-rule-container {
+  padding: 0;
+}
+
+.rule-list-card {
+  margin-bottom: 0;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.region-tag {
+  margin-right: 4px;
+  margin-bottom: 4px;
+}
+
+.text-muted {
+  color: #999;
+}
+
+.danger-text {
+  color: #f56c6c;
+}
+
+.region-selector-wrapper {
+  padding: 10px 0 0;
+}
+
+.region-selector-title {
+  display: flex;
+  align-items: center;
+  margin-bottom: 16px;
+  font-size: 14px;
+}
+
+.region-selector-title .text-danger {
+  color: #f56c6c;
+}
+
+.region-form {
+  margin-bottom: 16px;
+}
+
+.limited-region-list {
+  background: #fff7f7;
+  border-radius: 4px;
+  padding: 12px 16px;
+}
+
+.inner-region-form {
+  margin-bottom: 8px;
+}
+
+.limited-region-list .list-title {
+  font-size: 14px;
+  margin-bottom: 8px;
+}
+
+.limited-region-list .empty-tip {
+  font-size: 13px;
+  color: #999;
+}
+
+.limited-area-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 0;
+  border-bottom: 1px solid #f5d1d1;
+}
+
+.limited-area-item:last-child {
+  border-bottom: none;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+
+.text-right {
+  text-align: right;
+}
+</style>

+ 87 - 0
resource/admin/index.vue

@@ -0,0 +1,87 @@
+<template>
+  <div class="limit-purchase-container">
+    <el-container>
+      <el-header height="60px">
+        <el-menu
+          :default-active="activeMenu"
+          mode="horizontal"
+          @select="handleMenuSelect"
+          class="el-menu-horizontal"
+        >
+          <el-menu-item index="limitPurchaseRule">
+            <span>限购规则管理</span>
+          </el-menu-item>
+        </el-menu>
+      </el-header>
+      
+      <el-main>
+        <component 
+          :is="currentComponent" 
+          :axios-instance="axiosInstance" 
+        />
+      </el-main>
+    </el-container>
+  </div>
+</template>
+
+<script>
+import LimitPurchaseRule from './LimitPurchaseRule.vue'
+
+export default {
+  name: 'LimitPurchaseAdmin',
+  components: {
+    LimitPurchaseRule
+  },
+  props: {
+    axiosInstance: {
+      type: Object,
+      default: null
+    }
+  },
+  data() {
+    return {
+      activeMenu: 'limitPurchaseRule'
+    }
+  },
+  computed: {
+    currentComponent() {
+      if (this.activeMenu === 'limitPurchaseRule') {
+        return 'LimitPurchaseRule'
+      }
+      return 'LimitPurchaseRule'
+    }
+  },
+  methods: {
+    handleMenuSelect(key) {
+      this.activeMenu = key
+    }
+  }
+}
+</script>
+
+<style scoped>
+.limit-purchase-container {
+  padding: 0;
+  height: calc(100vh - 120px);
+}
+
+.el-menu-horizontal {
+  height: 60px;
+  font-size: 16px;
+}
+
+.el-header {
+  padding: 0;
+  background-color: #fff;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+
+.el-main {
+  padding: 30px;
+  background: #fff;
+  margin: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  flex: 1;
+}
+</style>

+ 8 - 0
route/admin.php

@@ -0,0 +1,8 @@
+<?php
+declare(strict_types=1);
+
+use SixShop\LimitPurchase\Controller\Admin\RuleController;
+use think\facade\Route;
+use SixShop\System\Middleware\MacroPageMiddleware;
+
+Route::resource('rule', RuleController::class)->middleware(['auth', MacroPageMiddleware::class]);

+ 24 - 0
src/Controller/Admin/RuleController.php

@@ -0,0 +1,24 @@
+<?php
+declare(strict_types=1);
+namespace SixShop\LimitPurchase\Controller\Admin;
+
+
+use SixShop\Core\Request;
+use SixShop\LimitPurchase\Entity\LimitPurchaseRuleEntity;
+use think\Response;
+use function SixShop\Core\page_response;
+
+class RuleController
+{
+
+    /**
+     * 规则列表
+     */
+    public function index(Request $request, LimitPurchaseRuleEntity $limitPurchaseRuleEntity):Response
+    {
+        $params = $request->get([
+            'status/b'
+        ]);
+        return page_response($limitPurchaseRuleEntity->getList($params, $request->pageAndLimit()));
+    }
+}

+ 19 - 0
src/Entity/LimitPurchaseRuleEntity.php

@@ -0,0 +1,19 @@
+<?php
+declare(strict_types=1);
+
+namespace SixShop\LimitPurchase\Entity;
+
+use SixShop\Core\Entity\BaseEntity;
+
+/**
+ * 限购规则实体类
+ * 用于管理商品限购规则
+ */
+class LimitPurchaseRuleEntity extends BaseEntity
+{
+    public function getList(array $params, array $pageAndLimit)
+    {
+        return $this->withSearch(['status'], $params)
+            ->paginate($pageAndLimit);
+    }
+}

+ 35 - 0
src/Model/LimitPurchaseRuleModel.php

@@ -0,0 +1,35 @@
+<?php
+declare(strict_types=1);
+
+namespace SixShop\LimitPurchase\Model;
+
+use think\Model;
+
+/**
+ * 限购规则模型
+ * 
+ * @property int $id 主键
+ * @property string $name 规则名称
+ * @property array $regions 限制区域
+ * @property bool $is_default 是否默认
+ * @property bool $status 是否启用
+ */
+class LimitPurchaseRuleModel extends Model
+{
+    /**
+     * 获取模型选项
+     * 
+     * @return array
+     */
+    protected function getOptions(): array
+    {
+        return [
+            'name' => 'limit_purchase_rule',
+            'type' => [
+                'regions' => 'json',
+                'is_default' => 'bool',
+                'status' => 'bool'
+            ]
+        ];
+    }
+}