Переглянути джерело

feat:将业务系统的结算操作拆分以适配计算服务的独立

brook 3 роки тому
батько
коміт
e1e2eb0be5

+ 15 - 0
backendApi/config/urlManagerRules.php

@@ -553,4 +553,19 @@ return [
             'GET user-login-export' => 'user-login-export',
         ],
     ],
+    [
+        'class' => 'yii\rest\UrlRule',
+        'pluralize' => false,
+        'controller' => 'v1/calc',
+        'extraPatterns' => [
+            'GET record-list/<periodNum>' => 'record-list',
+            'GET auto-calc/<periodNum>' => 'auto-calc',
+            'GET perf-order/<periodNum>' => 'perf-order',
+            'GET init-data/<periodNum>' => 'init-data',
+            'GET calc-perf-period/<periodNum>' => 'calc-perf-period',
+            'GET pull-perf-period/<periodNum>' => 'pull-perf-period',
+            'GET calc-bonus/<periodNum>' => 'calc-bonus',
+            'GET pull-bonus/<periodNum>' => 'pull-bonus',
+        ],
+    ],
 ];

+ 285 - 0
backendApi/modules/v1/controllers/CalcController.php

@@ -0,0 +1,285 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: leo
+ * Date: 2018/2/24
+ * Time: 下午12:48
+ */
+
+namespace backendApi\modules\v1\controllers;
+
+use common\helpers\bonus\Calc\CalcConsole;
+use common\helpers\bonus\Calc\PullCalcBonusData;
+use common\helpers\Form;
+use common\models\CalcRecord;
+use common\models\FlowBonus;
+use common\models\forms\PeriodForm;
+use common\models\Period;
+use Yii;
+
+class CalcController extends BaseController
+{
+    public $modelClass = FlowBonus::class;
+
+    public function behaviors()
+    {
+        $behaviors = parent::behaviors();
+        return $behaviors;
+    }
+
+    public function actionAutoCalc()
+    {
+        $periodNum = \Yii::$app->request->get('periodNum');
+        if (!$periodNum) {
+            return static::notice('期数不存在', 400);
+        }
+        if (Period::isProcessing($periodNum)) {
+            return static::notice('有操作正在进行中请稍后', 400);
+        }
+        //设置计算进行中标识
+        Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
+        //设置自动计算标识
+        Period::updatePeriodIsAutoExec($periodNum, Period::AUTO_EXEC_CALC);
+        $formModel           = new PeriodForm();
+        $formModel->scenario = 'perf';
+
+        if ($formModel->load(Yii::$app->request->get(), '') && $formModel->autoExec()) {
+            return static::notice('自动计算已开始,请等待');
+        } else {
+            return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+        }
+    }
+
+    /**
+     * 生成业绩单
+     * @return mixed
+     * @throws \yii\web\HttpException
+     */
+    public function actionPerfOrder()
+    {
+        $periodNum = \Yii::$app->request->get('periodNum');
+        if (!$periodNum) {
+            return static::notice('期数不存在', 400);
+        }
+        if (Period::isProcessing($periodNum)) {
+            return static::notice('有操作正在进行中请稍后', 400);
+        }
+        //设置手动计算标识
+        Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
+        //设置计算进行中标识
+        Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
+
+        $formModel           = new PeriodForm();
+        $formModel->scenario = 'perf';
+
+        if ($formModel->load(Yii::$app->request->get(), '') && $formModel->generatePerfOrder()) {
+            return static::notice('业绩单已开始生成,请等待');
+        } else {
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+        }
+    }
+
+
+    /**
+     * 推送基础数据
+     * @return mixed
+     * @throws \yii\web\HttpException
+     */
+    public function actionInitData()
+    {
+        $periodNum = \Yii::$app->request->get('periodNum');
+        if (!$periodNum) {
+            return static::notice('期数不存在', 400);
+        }
+        if (Period::isProcessing($periodNum)) {
+            return static::notice('有操作正在进行中请稍后', 400);
+        }
+        //设置手动计算标识
+        Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
+        //设置计算进行中标识
+        Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
+
+        $formModel           = new PeriodForm();
+        $formModel->scenario = 'perf';
+
+        if ($formModel->load(Yii::$app->request->get(), '') && $formModel->pushBaseData()) {
+            return static::notice('基础数据已开始推送,请等待');
+        } else {
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+        }
+    }
+
+
+    /**
+     * 通知计算系统生成期业绩和月业绩单
+     * @return mixed
+     * @throws \yii\web\HttpException
+     */
+    public function actionCalcPerfPeriod()
+    {
+        $periodNum = \Yii::$app->request->get('periodNum');
+        if (!$periodNum) {
+            return static::notice('期数不存在', 400);
+        }
+        if (Period::isProcessing($periodNum)) {
+            return static::notice('有操作正在进行中请稍后', 400);
+        }
+        //设置手动计算标识
+        Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
+        //设置计算进行中标识
+//        Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
+
+        $db     = CalcConsole::CALC_DB_NAME;
+        $period = \Yii::$app->$db->createCommand('SELECT * FROM AR_PERIOD where PERIOD_NUM=:PERIOD_NUM')
+            ->bindValue(':PERIOD_NUM', $periodNum)
+            ->queryOne();
+
+        if (empty($period)) {
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            return static::notice('计算系统中的第' . $periodNum . '期的业绩期信息不存在');
+        } else {
+            CalcRecord::record($periodNum, '已通知计算系统生成本期的期业绩');
+            \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['IS_PREPARE' => 1], 'PERIOD_NUM=' . $periodNum)->execute();
+            return static::notice('期业绩已于计算系统中开始计算,请等待');
+        }
+    }
+
+    /**
+     * 拉取期业绩和月业绩到业务系统
+     * @return mixed
+     * @throws \yii\web\HttpException
+     */
+    public function actionPullPerfPeriod()
+    {
+        $periodNum = \Yii::$app->request->get('periodNum');
+        if (!$periodNum) {
+            return static::notice('期数不存在', 400);
+        }
+        if (Period::isProcessing($periodNum)) {
+            return static::notice('有操作正在进行中请稍后', 400);
+        }
+        //设置手动计算标识
+        Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
+
+        $db     = CalcConsole::CALC_DB_NAME;
+        $period = \Yii::$app->$db->createCommand('SELECT * FROM AR_PERIOD where PERIOD_NUM=:PERIOD_NUM')
+            ->bindValue(':PERIOD_NUM', $periodNum)
+            ->queryOne();
+
+        if (empty($period)) {
+            return static::notice('计算系统中的第' . $periodNum . '期的业绩期信息不存在');
+        }
+        $formModel           = new PeriodForm();
+        $formModel->scenario = 'perf';
+
+        if (2 == $period['IS_PREPARE'] && 1 == $period['IS_PERFED']) {
+            //设置计算进行中标识
+            Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
+            if ($formModel->load(Yii::$app->request->get(), '') && $formModel->pullPeriod()) {
+                return static::notice('开始拉取期业绩数据,请等待');
+            } else {
+                Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+                return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+            }
+        } else {
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            return static::notice('业绩单还未全部生成,无法拉取期业绩数据,请稍后重试', 400);
+        }
+
+    }
+
+    /**
+     * 通知业务系统计算奖金
+     * @return mixed|void
+     * @throws \yii\web\HttpException
+     */
+    public function actionCalcBonus()
+    {
+        $periodNum = \Yii::$app->request->get('periodNum');
+        if (!$periodNum) {
+            return static::notice('期数不存在', 400);
+        }
+        if (Period::isProcessing($periodNum)) {
+            return static::notice('有操作正在进行中请稍后', 400);
+        }
+        //设置手动计算标识
+        Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
+        //设置计算进行中标识
+//        Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
+        $db     = CalcConsole::CALC_DB_NAME;
+        $period = \Yii::$app->$db->createCommand('SELECT * FROM AR_PERIOD where PERIOD_NUM=:PERIOD_NUM')
+            ->bindValue(':PERIOD_NUM', $periodNum)
+            ->queryOne();
+
+        $formModel           = new PeriodForm();
+        $formModel->scenario = 'calc';
+        if (empty($period)) {
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            return static::notice('计算系统中的第' . $periodNum . '期的业绩期信息不存在');
+        } else if (1 == $period['IS_PERFED'] && 2 == $period['IS_PREPARE']) {
+            Period::updateAll(['IS_CALCULATED' => 0], ['PERIOD_NUM' => $period['PERIOD_NUM']]);
+            CalcRecord::record($periodNum, '奖金已于计算系统中开始计算,请等待');
+            \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['IS_PREPARE' => 3], 'PERIOD_NUM=' . $periodNum)->execute();
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            return static::notice('本期奖金已于计算系统中开始计算,请等待');
+        } else {
+            return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+        }
+    }
+
+    public function actionPullBonus()
+    {
+        $periodNum = \Yii::$app->request->get('periodNum');
+        if (!$periodNum) {
+            return static::notice('期数不存在', 400);
+        }
+        if (Period::isProcessing($periodNum)) {
+            return static::notice('有操作正在进行中请稍后', 400);
+        }
+        //设置手动计算标识
+        Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
+
+        $db     = CalcConsole::CALC_DB_NAME;
+        $period = \Yii::$app->$db->createCommand('SELECT * FROM AR_PERIOD where PERIOD_NUM=:PERIOD_NUM')
+            ->bindValue(':PERIOD_NUM', $periodNum)
+            ->queryOne();
+
+        if (empty($period)) {
+            return static::notice('计算系统中的第' . $periodNum . '期的业绩期信息不存在');
+        }
+
+        $formModel           = new PeriodForm();
+        $formModel->scenario = 'calc';
+
+        if (4 == $period['IS_PREPARE'] && 1 == $period['IS_CALCULATED']) {
+            //设置计算进行中标识
+            Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
+            if ($formModel->load(Yii::$app->request->get(), '') && $formModel->pullBonus()) {
+                return static::notice('开始拉取奖金数据,请等待');
+            } else {
+                Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+                return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+            }
+        } else {
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            return static::notice('奖金数据还未全部生成,无法全部拉取,请稍后重试', 400);
+        }
+
+    }
+
+
+    public function actionRecordList()
+    {
+        $filter    = $this->filterCondition([
+            'periodNum' => 'PERIOD_NUM',
+        ]);
+        $condition = $filter['condition'];
+        $params    = $filter['params'];
+        $data      = CalcRecord::lists($condition, $params, ['orderBy' => 'CREATED_AT DESC , ID DESC']);
+        return static::notice($data);
+    }
+
+
+}

+ 227 - 3
backendEle/src/views/bonus/period.vue

@@ -67,7 +67,49 @@
         </el-table-column>
         <el-table-column fixed="right" label="Action" width="180"><!-- 操作 -->
           <template slot-scope="scope">
-            <el-dropdown size="small" trigger="click" v-if="scope.row.BUTTON_IS_CAN">
+            <el-button v-if="scope.row.IS_CAN_CLOSE && permission.hasPermission(`bonus/close-period`)" type="primary" class="button"
+                       @click.native="closeHandle(scope.row)">
+              Closure period<!-- 封期 -->
+            </el-button>
+            <el-button v-if="scope.row.IS_CAN_PERF" type="primary" class="button"
+                       @click="dialogTableVisible = true;currentPeriod = scope.row.PERIOD_NUM;getDialogData();">
+              操作</el-button>
+            <el-button @click.native="sentHandle(scope.row)" type="primary" class="button"
+                              v-if="scope.row.IS_CAN_SENT && permission.hasPermission(`bonus/send-period`)">
+              <!-- 挂网 -->spread a net
+            </el-button>
+
+            <el-dialog :title="`第${currentPeriod}期计算任务日志`" :visible.sync="dialogTableVisible" append-to-body width="60%">
+              <el-row><el-button class="button" type="primary" @click.native="getDialogData();dialogLoading=true;" >日志刷新</el-button></el-row>
+              <div class="flex">
+                <div>
+                  <el-table :data="dialogData" height="550" v-loading="dialogLoading">
+                    <el-table-column prop="CREATED_AT" label="操作时间" width="210">
+                      <template slot-scope="scope">
+                        {{tool.formatDate(scope.row.CREATED_AT)}}
+                      </template>
+                    </el-table-column>
+                    <el-table-column prop="TEXT" label="日志内容" width="400"></el-table-column>
+                  </el-table>
+                </div>
+                <div style="margin-left: 15px">
+                  <el-row>
+                    <el-button class="button" type="success" @click.native="autoCalcHandle(currentPeriod)"
+                     v-if="permission.hasPermission(`bonus/calc-period`)"
+                    >自动计算</el-button>
+                  </el-row>
+                  <el-row><el-button class="button" type="primary" @click.native="perfOrderHandle(currentPeriod)" >生成业绩单</el-button></el-row>
+                  <el-row><el-button class="button" type="primary" @click.native="initDataHandle(currentPeriod)" >初始化基础数据</el-button></el-row>
+                  <el-row><el-button class="button" type="primary" @click.native="perfPeriodHandle(currentPeriod)" >生成期业绩</el-button></el-row>
+                  <el-row><el-button class="button" type="primary" @click.native="pullPerfPeriodHandle(currentPeriod)" >拉取期业绩</el-button></el-row>
+                  <el-row><el-button class="button" type="primary" @click.native="calcBonusHandle(currentPeriod)" >计算奖金</el-button></el-row>
+                  <el-row><el-button class="button" type="primary" @click.native="pullBonusHandle(currentPeriod)" >拉取奖金数据</el-button></el-row>
+                </div>
+              </div>
+            </el-dialog>
+
+            <el-dropdown size="small" trigger="click" v-if=false>
+<!--            <el-dropdown size="small" trigger="click" v-if="scope.row.BUTTON_IS_CAN">-->
               <el-button type="primary" size="small" @click.stop="">
                 <!-- 操作该数据 -->Action<i class="el-icon-arrow-down el-icon--right"></i>
               </el-button>
@@ -148,6 +190,7 @@ export default {
   },
   data () {
     return {
+      currentPeriod: 0,
       tableData: null,
       loading: true,
       multipleSelection: [],
@@ -176,10 +219,158 @@ export default {
         'PERF_PERCENT': {},
         'CALC_PERCENT': {},
         'SENT_PERCENT': {}
-      }
-    }
+      },
+      dialogTableVisible: false,
+      dialogData: null,
+      currentDialogPage: 1,
+      dialogPageSize: 10,
+      dialogLoading: true,
+      form: {
+        name: '',
+        region: '',
+        date1: '',
+        date2: '',
+        delivery: false,
+        type: [],
+        resource: '',
+        desc: ''
+      },
+      formLabelWidth: '120px'
+    };
+
   },
   methods: {
+    autoCalcHandle(currentPeriod) {
+      this.$confirm('Confirm to perform settlement operation for the auto current period?', 'Hint', { // '确定对当前期进行结算操作?', '提示'
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        return network.getData(`calc/auto-calc/${currentPeriod}`)
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+        this.getDialogData(this.currentPage, this.pageSize)
+      }).catch(response => {
+      })
+    },
+    perfOrderHandle(currentPeriod) {
+      this.$confirm('Confirm to generate performance order for the current period?', 'Hint', { // '确定对当前期进行生成业绩单操作?', '提示'
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        return network.getData(`calc/perf-order/${currentPeriod}`)
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+        this.getDialogData(this.currentPage, this.pageSize)
+      }).catch(response => {
+      })
+    },
+    initDataHandle(currentPeriod) {
+      this.$confirm('Confirm to generate performance sheet for the current period?', 'Hint', { // '确定对当前期进行生成业绩单操作?', '提示'
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        return network.getData(`calc/init-data/${currentPeriod}`)
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+        this.getDialogData(this.currentPage, this.pageSize)
+      }).catch(response => {
+      })
+    },
+    perfPeriodHandle(currentPeriod) {
+      this.$confirm('Confirm to generate performance sheet for the current period?', 'Hint', { // '确定对当前期进行生成业绩单操作?', '提示'
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        return network.getData(`calc/calc-perf-period/${currentPeriod}`)
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+        this.getDialogData(this.currentPage, this.pageSize)
+      }).catch(response => {
+      })
+    },
+    pullPerfPeriodHandle(currentPeriod) {
+      this.$confirm('Confirm to pull performance sheet for the current period?', 'Hint', { // '确定对当前期进行生成业绩单操作?', '提示'
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        return network.getData(`calc/pull-perf-period/${currentPeriod}`)
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+        this.getDialogData(this.currentPage, this.pageSize)
+      }).catch(response => {
+      })
+    },
+    calcBonusHandle(currentPeriod) {
+      this.$confirm('Confirm to calculate bonus data for the current period?', 'Hint', { // '确定对当前期进行生成业绩单操作?', '提示'
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        return network.getData(`calc/calc-bonus/${currentPeriod}`)
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+        this.getDialogData(this.currentPage, this.pageSize)
+      }).catch(response => {
+      })
+    },
+    pullBonusHandle(currentPeriod) {
+      this.$confirm('Confirm to calculate bonus data for the current period?', 'Hint', { // '确定对当前期进行生成业绩单操作?', '提示'
+        confirmButtonText: 'confirm', // 确定
+        cancelButtonText: 'cancel', // 取消
+        type: 'warning'
+      }).then(() => {
+        return network.getData(`calc/pull-bonus/${currentPeriod}`)
+      }).then(response => {
+        this.$message({
+          message: response,
+          type: 'success'
+        })
+        this.getDialogData(this.currentPage, this.pageSize)
+      }).catch(response => {
+      })
+    },
+
+    getDialogData() {
+      let vueObj = this
+      let paramsData = {
+        page: this.currentDialogPage,
+        pageSize: this.dialogPageSize,
+      }
+      network.getData('calc/record-list/' + this.currentPeriod, paramsData)
+        .then(response => {
+          vueObj.dialogData = response.list
+          vueObj.dialogLoading = false;
+        }).catch(response => {
+        this.$message({
+          message: response,
+          type: 'fail'
+        })
+      })
+    },
+
     closeHandle (row) {
       this.$confirm('Confirm to manually seal the current period?', 'Hint', { // '确定对当前期进行手动封期操作?', '提示'
         confirmButtonText: 'confirm', // 确定
@@ -298,3 +489,36 @@ export default {
   }
 }
 </script>
+<style>
+.flex{
+  /*position: fixed;*/
+  /*top: calc(50vh - 200px);*/
+  /*left:calc(50vw - 200px);*/
+  display: flex;
+  /*width:400px;*/
+  /*height:400px;*/
+  /*background: rgb(190, 167, 154);*/
+}
+.lefti{
+  width: 100px;
+  background: chartreuse;
+
+}
+.item{
+  width: 100px;
+  height:40px;
+  line-height: 40px;
+  text-align: center;
+}
+.active{
+  background: coral;
+}
+.overfowwlosc{
+  /*width: 100px;*/
+  /*height: 100px;*/
+  overflow-y: scroll;
+}
+.button{
+  margin-bottom: 10px;
+}
+</style>

+ 3 - 1
common/components/SwooleAsyncTimer.php

@@ -12,6 +12,7 @@ use anlity\swooleAsyncTimer\SwooleAsyncTimerComponent;
 use backendApi\modules\v1\models\Admin;
 use common\components\Redis;
 use common\helpers\bonus\AutoClosePeriod;
+use common\helpers\bonus\Calc\CalcConsole;
 use common\helpers\Cache;
 use common\helpers\Date;
 use common\helpers\Tool;
@@ -50,7 +51,8 @@ class SwooleAsyncTimer extends SwooleAsyncTimerComponent implements SocketInterf
         AutoClosePeriod::instance()->autoClose();
         // 自动执行任务队列中的任务
         Queue::instance()->consumeTask();
-
+        // 实时监听计算系统修改的period表字段
+        CalcConsole::listenCalcPeriod();
         return true;
     }
 

+ 71 - 0
common/helpers/bonus/Calc/BaseBusiness.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace common\helpers\bonus\Calc;
+
+use common\models\Period;
+use common\traits\ResponseTrait;
+
+class BaseBusiness
+{
+    use ResponseTrait;
+
+    protected $_periodNum = 0;
+
+    protected $_calc_db_name = 'dbCalc';
+
+    const CALC_DB_NAME = 'dbCalc';
+
+    protected $_limit = 10000;
+
+
+    public function __construct($periodNum)
+    {
+        if (is_numeric($periodNum)) {
+            $this->_periodNum = $periodNum;
+            $periodObj        = Period::instance();
+//            $periodDataArr = $periodObj->setPeriodNum($periodNum);
+//            $this->_periodId = $periodDataArr['ID'];
+            $this->_isCalcMonth = $periodObj->isCalcMonth($periodNum);
+//            $this->_calcYear = $periodObj->getYear($periodNum);
+//            $this->_calcMonth     = $periodObj->getMonth($periodNum);
+            $this->_calcYearMonth = $periodObj->getYearMonth($periodNum);
+        } else {
+            //todo
+            return false;
+        }
+    }
+
+    /**
+     *  从结算系统拉取周期信息并更新
+     * @return int
+     */
+    public static function pullPeriodForUpdate($periodNum = 0): int
+    {
+        if (empty($periodNum) || !is_numeric($periodNum)) {
+            return 0;
+        }
+        $db     = self::CALC_DB_NAME;
+        $period = \Yii::$app->$db->createCommand("SELECT 
+                PERIOD_NUM,
+                IS_PERFED,
+                IS_CALCULATED,
+                IS_PERFING,
+                IS_CALCING,
+                PERF_PERCENT,
+                CALC_PERCENT,
+                PERF_STARTED_AT,
+                PERFED_AT,
+                CALCULATE_STARTED_AT,
+                CALCULATED_AT
+                FROM AR_PERIOD where PERIOD_NUM = $periodNum")->queryOne();
+
+        return Period::updateAll($period, ['PERIOD_NUM' => $periodNum]);
+    }
+
+    public function getCalcPeriod()
+    {
+        $db = $this->_calc_db_name;
+        return \Yii::$app->$db->createCommand("SELECT * FROM AR_PERIOD where PERIOD_NUM = $this->_periodNum")->queryOne();
+    }
+
+}

+ 33 - 0
common/helpers/bonus/Calc/BasePerfBusiness.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace common\helpers\bonus\Calc;
+
+
+use common\helpers\Date;
+use common\models\Period;
+
+class BasePerfBusiness extends BaseBusiness
+{
+    protected $_periodNum = 0;
+
+    protected $_calc_db_name = 'dbCalc';
+
+    protected $_limit = 10000;
+
+
+    public function __construct($periodNum)
+    {
+        parent::__construct($periodNum);
+    }
+
+    public function setCalcStatus($type)
+    {
+        if ($type == 'start') {
+            Period::updateAll(['IS_PERFING' => 1, 'IS_PERFED' => Period::PERF_NONE, 'PERF_STARTED_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
+        } elseif ($type == 'end') {
+            Period::updateAll(['IS_PERFING' => 0, 'IS_PERFED' => Period::PERF_FINISH, 'PERFED_AT' => Date::nowTime()], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
+        } elseif ($type == 'fail') {
+            Period::updateAll(['IS_PERFING' => 0, 'IS_PERFED' => Period::PERF_FAIL, 'PERFED_AT' => 0], 'PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum]);
+        }
+    }
+}

+ 84 - 0
common/helpers/bonus/Calc/CalcConsole.php

@@ -0,0 +1,84 @@
+<?php
+
+namespace common\helpers\bonus\Calc;
+
+
+use common\models\CalcRecord;
+use common\models\Period;
+
+class CalcConsole extends BaseBusiness
+{
+    const  CALC_DB_NAME = 'dbCalc';
+
+    public static function listenCalcPeriod()
+    {
+        $db        = self::CALC_DB_NAME;
+        $allPeriod = \Yii::$app->$db->createCommand("SELECT * FROM AR_PERIOD ")->queryAll();
+        $period    = [];
+        foreach ($allPeriod as $v) {
+            if ($v['IS_PREPARE'] > 0
+                //&& todo 补全状态
+            ) {
+                $period = $v;
+                break;
+            }
+        }
+
+        $businessPeriod = Period::find()->where(['PERIOD_NUM' => $period['PERIOD_NUM']])
+            ->asArray()->one();
+        //用户选择是否自动执行
+        $autoExec = $businessPeriod['AUTO_EXEC'] > 0;
+
+        if (2 == $period['IS_PREPARE'] && 1 == $period['IS_PERFED'] && $autoExec) {
+            //拉取期业绩
+            CalcRecord::record($period['PERIOD_NUM'], '第' . $period['PERIOD_NUM'] . '期的业绩数据已生成');
+            CalcRecord::record($period['PERIOD_NUM'], '开始获取第' . $period['PERIOD_NUM'] . '期的期业绩数据');
+
+            Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::IS_PROCESSING);
+            $res = (new PullPerfDataFromCalc($period['PERIOD_NUM']))->start();
+            if (200 == $res['code']) {
+                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
+                CalcRecord::record($period['PERIOD_NUM'], '第' . $period['PERIOD_NUM'] . '期的期业绩数据已获取');
+            } else {
+                //结束计算状态
+                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
+                CalcRecord::record($period['PERIOD_NUM'], '第' . $period['PERIOD_NUM'] . '期的期业绩数据获取失败,原因:' . $res['msg']);
+                return $res;
+            }
+
+            //自动执行且IS_PREPARE为3 直接开始结算流程
+            CalcRecord::record($period['PERIOD_NUM'], '开始计算第' . $period['PERIOD_NUM'] . '期的奖金');
+            //同步周期表的值到业务系统
+            self::pullPeriodForUpdate($period['PERIOD_NUM']);
+            return \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['IS_PREPARE' => 3], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $period['PERIOD_NUM']])->execute();
+
+        }
+
+        if (4 == $period['IS_PREPARE'] && 1 == $period['IS_CALCULATED'] && $autoExec) {
+            CalcRecord::record($period['PERIOD_NUM'], '第' . $period['PERIOD_NUM'] . '期的奖金已计算完成');
+            CalcRecord::record($period['PERIOD_NUM'], '开始拉取第' . $period['PERIOD_NUM'] . '期的奖金数据');
+
+            Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::IS_PROCESSING);
+            //自动执行且IS_CALCULATED为1 直接开始拉取结算数据
+            $res = (new PullCalcBonusData($period['PERIOD_NUM']))->start();
+            if (200 == $res['code']) {
+                CalcRecord::record($period['PERIOD_NUM'], '第' . $period['PERIOD_NUM'] . '期的奖金数据已全部拉取');
+                //自动执行完成 更新对应字段
+                //计算结束
+                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
+                //流程结束
+                //同步周期表的值到业务系统
+                self::pullPeriodForUpdate($period['PERIOD_NUM']);
+            } else {
+                //结束计算状态
+                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
+                CalcRecord::record($period['PERIOD_NUM'], '第' . $period['PERIOD_NUM'] . '期的奖金数据获取失败,原因:' . $res['msg']);
+                return $res;
+            }
+            return true;
+        }
+
+        return true;
+    }
+
+}

+ 441 - 0
common/helpers/bonus/Calc/GeneratePerfOrder.php

@@ -0,0 +1,441 @@
+<?php
+
+namespace common\helpers\bonus\Calc;
+
+use common\helpers\bonus\PerfCalc;
+use common\helpers\Cache;
+use common\helpers\Date;
+use common\helpers\snowflake\SnowFake;
+use common\models\DecOrder;
+use common\models\forms\DeclarationForm;
+use common\models\Order;
+use common\models\OrderDec;
+use common\models\OrderShop;
+use common\models\PerfOrder;
+
+
+class GeneratePerfOrder extends BasePerfBusiness
+{
+
+    /**
+     * @var array|mixed|\yii\db\ActiveRecord[]
+     */
+    private $_sysConfig;
+
+    protected $_limit = 2000;
+
+    public function __construct($periodNum)
+    {
+        parent::__construct($periodNum);
+        $this->_sysConfig = Cache::getSystemConfig();//系统配置 AR_CONFIG
+
+    }
+
+    const BASE_INFO_METHODS = [
+        //--- 业绩单表
+        'perfOrder' => ['separately' => false, 'table' => 'AR_PERF_ORDER', 'field' => [
+            'ID',
+            'SN',
+//            'PERF_TYPE',
+            'DEC_TYPE',
+            'USER_ID',
+            'PV',
+            'PERIOD_NUM',
+            'CALC_MONTH',
+            'DEC_USER_ID',
+            'CREATED_AT',
+            'ORDER_AMOUNT',
+            'PAY_TYPE',
+            'FROM_TABLE',
+        ]],
+    ];
+
+    public function start(): array
+    {
+//        $this->setCalcStatus('start');
+        try {
+            //清楚业务系统本地数据(业绩相关)
+            $this->clearPerfOrderBusinessData();
+            //生成业绩单
+            $this->perfOrder();
+            //todo 更新进度
+//            $this->setCalcStatus('end');
+            //如果自动结算 todo
+            if (boolval($this->_sysConfig['autoCalcPeriod']['VALUE'])) {
+//                $period = Period::instance();
+//                if($period->isLastSent($this->_periodNum)) {
+//                    $bonusCalc = BonusCalc::instance();
+//                    $asyncResult = $bonusCalc->calcStep($this->_periodNum);
+//                    if ($asyncResult) {
+//                        $bonusCalc->endCalcTask();
+//                    } else {
+//                        $bonusCalc->errorCalcTask();
+//                    }
+//                    return $asyncResult;
+//                }
+            }
+
+        } catch (\Exception $e) {
+            $this->setCalcStatus('fail');
+            return $this->fail('msg:' . $e->getMessage() . 'line:' . $e->getLine());
+        }
+        return $this->success();
+    }
+
+    public function clearPerfOrderBusinessData()
+    {
+        // 业绩单
+        PerfOrder::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+    }
+
+    public function perfOrder(): bool
+    {
+        $this->decOrder();
+        $this->orderDec();
+        $this->order();
+        $this->orderShop();
+        return true;
+    }
+
+    public function decOrder(): bool
+    {
+        $_offset = 0;
+        decOrder:
+        $allData = DecOrder::find()
+            ->select('PAID_WALLET,ID,DEC_SN,ORDER_SN,USER_ID,TYPE,TO_USER_ID,IS_ADMIN,DEC_AMOUNT,DEC_PV,PERIOD_NUM,CALC_MONTH,IS_DEL,P_CALC_MONTH,CREATED_AT,DEC_ID')
+            ->where("PERIOD_NUM=:PERIOD_NUM AND IS_DEL=0 AND TYPE='ZC'", [':PERIOD_NUM' => $this->_periodNum])
+            ->orderBy('CREATED_AT DESC,ID DESC')
+            ->limit($this->_limit)->offset($_offset * $this->_limit)
+            ->with([
+                'recName'        => function ($query) {
+                    $query->select('USER_ID,REC_UID')
+                        ->with([
+                            'recUserName' => function ($query) {
+                                $query->select('USER_ID,REC_UID,USER_NAME');
+                            },
+                            'recRealName' => function ($query) {
+                                $query->select('ID,REAL_NAME');
+                            }
+                        ]);
+                },
+                'userByToUserId' => function ($query) {
+                    $query->select('ID,LAST_DEC_LV,EMP_LV,STATUS,SUB_COM_ID,PROVINCE,CITY,COUNTY');
+                },
+                'userByUserId'   => function ($query) {
+                    $query->select('ID,LAST_DEC_LV,SUB_COM_ID,PROVINCE,CITY,COUNTY');
+                }
+            ])
+            ->asArray()->all();
+
+        $insertPerfOrderData = [];
+        if (!empty($allData)) {
+            foreach ($allData as $k => $data) {
+                $insertPerfOrderData[] = [
+                    'LAST_REC_USER_NAME' => $data['recName']['recUserName']['USER_NAME'] ?? '',
+                    'LAST_REC_REAL_NAME' => $data['recName']['recRealName']['REAL_NAME'] ?? '',
+                    'LAST_DEC_LV'        => $data['userByToUserId']['LAST_DEC_LV'] ?? '', //CalcCache 810
+                    'LAST_EMP_LV'        => $data['userByToUserId']['EMP_LV'] ?? '',
+                    'LAST_STATUS'        => $data['userByToUserId']['STATUS'] ?? '',
+                    'LAST_SUB_COM_ID'    => $data['userByToUserId']['SUB_COM_ID'] ?? '',
+                    'LAST_PROVINCE'      => $data['userByToUserId']['PROVINCE'] ?? '',
+                    'LAST_CITY'          => $data['userByToUserId']['CITY'] ?? '',
+                    'LAST_COUNTY'        => $data['userByToUserId']['COUNTY'] ?? '',
+
+                    'LAST_DEC_DEC_LV'     => $data['userByUserId']['LAST_DEC_LV'] ?? '',
+                    'LAST_DEC_SUB_COM_ID' => $data['userByUserId']['SUB_COM_ID'] ?? '',
+                    'LAST_DEC_PROVINCE'   => $data['userByUserId']['PROVINCE'] ?? '',
+                    'LAST_DEC_CITY'       => $data['userByUserId']['CITY'] ?? '',
+                    'LAST_DEC_COUNTY'     => $data['userByUserId']['COUNTY'] ?? '',
+
+                    'PAY_TYPE'         => $data['PAID_WALLET'],
+                    'FROM_TABLES'      => 'dec_order',
+                    'ID'               => SnowFake::instance()->generateId(),
+                    'SN'               => $data['ORDER_SN'],
+                    'DEC_SN'           => $data['DEC_SN'],
+                    'DEC_TYPE'         => strtoupper($data['TYPE']),
+                    'DEC_STATUS'       => PerfOrder::STATUS_NORMAL,
+                    'USER_ID'          => $data['TO_USER_ID'],
+                    'PV'               => $data['DEC_PV'],
+                    'DEC_AMOUNT'       => $data['DEC_AMOUNT'],
+                    'DEC_USER_ID'      => $data['USER_ID'],
+                    'PERIOD_NUM'       => $this->_periodNum,
+                    'CALC_MONTH'       => $this->_calcYearMonth,
+                    'P_CALC_MONTH'     => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
+                    'CREATED_AT'       => Date::nowTime(),
+                    'CLOSED_AT'        => 0,
+                    'ORDER_CREATED_AT' => $data['CREATED_AT'],
+                ];
+            }
+            $data    = null;
+            $_offset += 1;
+            $this->perfOrderInsert($insertPerfOrderData);
+            $insertPerfOrderData = null;
+            goto decOrder;
+        }
+        $allData = null;
+        return true;
+    }
+
+    public function orderDec(): bool
+    {
+        $_offset = 0;
+        orderDec:
+        $allData = OrderDec::find()->select(
+            'PAY_TYPE,CREATED_AT,ID,SN,USER_ID,ORDER_TYPE,ORDER_AMOUNT,PV,PAY_AMOUNT,PAY_PV,PERIOD_NUM,PAY_TYPE')
+            ->where("PERIOD_NUM=:PERIOD_NUM AND IS_DELETE=0 AND ORDER_TYPE=:ORDER_TYPE",
+                [':PERIOD_NUM' => $this->_periodNum, ':ORDER_TYPE' => 'ZC'])
+            ->orderBy('ID DESC')
+            ->limit($this->_limit)->offset($_offset * $this->_limit)
+            ->with([
+                'recName'      => function ($query) {
+                    $query->select('USER_ID,REC_UID')
+                        ->with([
+                            'recUserName' => function ($query) {
+                                $query->select('USER_ID,REC_UID,USER_NAME');
+                            },
+                            'recRealName' => function ($query) {
+                                $query->select('ID,REAL_NAME');
+                            }
+                        ]);
+                },
+                'userByUserId' => function ($query) {
+                    $query->select('ID,DEC_ID,LAST_DEC_LV,EMP_LV,STATUS,SUB_COM_ID,PROVINCE,CITY,COUNTY');
+                }
+            ])
+            ->asArray()->all();
+
+        $insertPerfOrderData = [];
+        if (!empty($allData)) {
+            foreach ($allData as $k => $data) {
+                $insertPerfOrderData[] = [
+                    'LAST_REC_USER_NAME' => $data['recName']['recUserName']['USER_NAME'] ?? '',
+                    'LAST_REC_REAL_NAME' => $data['recName']['recRealName']['REAL_NAME'] ?? '',
+                    'LAST_DEC_LV'        => $data['userByUserId']['LAST_DEC_LV'] ?? '', //CalcCache 810
+                    'LAST_EMP_LV'        => $data['userByUserId']['EMP_LV'] ?? '',
+                    'LAST_STATUS'        => $data['userByUserId']['STATUS'] ?? '',
+                    'LAST_SUB_COM_ID'    => $data['userByUserId']['SUB_COM_ID'] ?? '',
+                    'LAST_PROVINCE'      => $data['userByUserId']['PROVINCE'] ?? '',
+                    'LAST_CITY'          => $data['userByUserId']['CITY'] ?? '',
+                    'LAST_COUNTY'        => $data['userByUserId']['COUNTY'] ?? '',
+
+                    'LAST_DEC_DEC_LV'     => $data['userByUserId']['LAST_DEC_LV'] ?? '',
+                    'LAST_DEC_SUB_COM_ID' => $data['userByUserId']['SUB_COM_ID'] ?? '',
+                    'LAST_DEC_PROVINCE'   => $data['userByUserId']['PROVINCE'] ?? '',
+                    'LAST_DEC_CITY'       => $data['userByUserId']['CITY'] ?? '',
+                    'LAST_DEC_COUNTY'     => $data['userByUserId']['COUNTY'] ?? '',
+                    'DEC_USER_ID'         => $data['userByUserId']['DEC_ID'],
+
+                    'PAY_TYPE'         => $data['PAY_TYPE'],
+                    'FROM_TABLES'      => 'order_dec',
+                    'ID'               => SnowFake::instance()->generateId(),
+                    'SN'               => $data['SN'],
+                    'DEC_SN'           => $data['SN'],
+                    'DEC_TYPE'         => strtoupper($data['ORDER_TYPE']),
+                    'DEC_STATUS'       => PerfOrder::STATUS_NORMAL,
+                    'USER_ID'          => $data['USER_ID'],
+                    'PV'               => $data['PAY_PV'],
+                    'DEC_AMOUNT'       => $data['ORDER_AMOUNT'],
+                    'PERIOD_NUM'       => $this->_periodNum,
+                    'CALC_MONTH'       => $this->_calcYearMonth,
+                    'P_CALC_MONTH'     => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
+                    'CREATED_AT'       => Date::nowTime(),
+                    'CLOSED_AT'        => 0,
+                    'ORDER_CREATED_AT' => $data['CREATED_AT']
+                ];
+            }
+            $data    = null;
+            $_offset += 1;
+            $this->perfOrderInsert($insertPerfOrderData);
+            $insertPerfOrderData = null;
+            goto orderDec;
+        }
+        $allData = null;
+        return true;
+    }
+
+    public function order(): bool
+    {
+        $_offset = 0;
+        order:
+        $allData = Order::find()->select('ID,SN,DEC_SN,USER_ID,ORDER_TYPE,ORDER_AMOUNT,PAY_AMOUNT,PAY_PV,PAY_TYPE,PERIOD_NUM,STATUS,IS_DELETE,P_CALC_MONTH,CREATED_AT')
+            ->where("PERIOD_NUM=:PERIOD_NUM AND IS_DELETE=0 AND ORDER_TYPE=:ORDER_TYPE",
+                [':PERIOD_NUM' => $this->_periodNum, ':ORDER_TYPE' => DeclarationForm::TYPE_FX])
+            ->orderBy('CREATED_AT DESC,ID DESC')
+            ->limit($this->_limit)->offset($_offset * $this->_limit)
+            ->with([
+                'recName'      => function ($query) {
+                    $query->select('USER_ID,REC_UID')
+                        ->with([
+                            'recUserName' => function ($query) {
+                                $query->select('USER_ID,REC_UID,USER_NAME');
+                            },
+                            'recRealName' => function ($query) {
+                                $query->select('ID,REAL_NAME');
+                            }
+                        ]);
+                },
+                'userByUserId' => function ($query) {
+                    $query->select('ID,LAST_DEC_LV,EMP_LV,STATUS,SUB_COM_ID,PROVINCE,CITY,COUNTY');
+                }
+            ])
+            ->asArray()->all();
+
+        $insertPerfOrderData = [];
+        if (!empty($allData)) {
+            foreach ($allData as $k => $data) {
+                if ($data['PAY_TYPE'] === PerfCalc::ORDER_PAY_TYPE_CASH) {
+                    //111期开始由50%改为60%-by 2020-04-30修改
+                    $payPv = $data['PAY_PV'] * $this->_sysConfig['cashReconsumeBonusPercent']['VALUE'] / 100;
+                } else {
+                    $payPv = $data['PAY_PV'];
+                }
+
+                if ($payPv <= 0) continue;
+
+                $insertPerfOrderData[] = [
+                    'PV'                 => $payPv,
+                    'LAST_REC_USER_NAME' => $data['recName']['recUserName']['USER_NAME'] ?? '',
+                    'LAST_REC_REAL_NAME' => $data['recName']['recRealName']['REAL_NAME'] ?? '',
+                    'LAST_DEC_LV'        => $data['userByUserId']['LAST_DEC_LV'] ?? '', //CalcCache 810
+                    'LAST_EMP_LV'        => $data['userByUserId']['EMP_LV'] ?? '',
+                    'LAST_STATUS'        => $data['userByUserId']['STATUS'] ?? '',
+                    'LAST_SUB_COM_ID'    => $data['userByUserId']['SUB_COM_ID'] ?? '',
+                    'LAST_PROVINCE'      => $data['userByUserId']['PROVINCE'] ?? '',
+                    'LAST_CITY'          => $data['userByUserId']['CITY'] ?? '',
+                    'LAST_COUNTY'        => $data['userByUserId']['COUNTY'] ?? '',
+
+                    'LAST_DEC_DEC_LV'     => $data['userByUserId']['LAST_DEC_LV'] ?? '',
+                    'LAST_DEC_SUB_COM_ID' => $data['userByUserId']['SUB_COM_ID'] ?? '',
+                    'LAST_DEC_PROVINCE'   => $data['userByUserId']['PROVINCE'] ?? '',
+                    'LAST_DEC_CITY'       => $data['userByUserId']['CITY'] ?? '',
+                    'LAST_DEC_COUNTY'     => $data['userByUserId']['COUNTY'] ?? '',
+                    'DEC_USER_ID'         => $data['USER_ID'],
+
+                    'PAY_TYPE'         => $data['PAY_TYPE'],
+                    'FROM_TABLES'      => 'order',
+                    'ID'               => SnowFake::instance()->generateId(),
+                    'SN'               => $data['SN'],
+                    'DEC_SN'           => null,
+                    'DEC_TYPE'         => 'FX',
+                    'DEC_STATUS'       => PerfOrder::STATUS_NORMAL,
+                    'USER_ID'          => $data['USER_ID'],
+                    'DEC_AMOUNT'       => $data['PAY_AMOUNT'],
+                    'PERIOD_NUM'       => $this->_periodNum,
+                    'CALC_MONTH'       => $this->_calcYearMonth,
+                    'P_CALC_MONTH'     => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
+                    'CREATED_AT'       => Date::nowTime(),
+                    'CLOSED_AT'        => 0,
+                    'ORDER_CREATED_AT' => $data['CREATED_AT']
+                ];
+            }
+            $data    = null;
+            $_offset += 1;
+            $this->perfOrderInsert($insertPerfOrderData);
+            $insertPerfOrderData = null;
+            goto order;
+        }
+        $allData = null;
+        return true;
+    }
+
+    public function orderShop(): bool
+    {
+        $_offset = 0;
+        orderShop:
+        $allData = OrderShop::find()->select(
+            'ID,SN,DEC_SN,USER_ID,ORDER_TYPE,ORDER_AMOUNT,PAY_AMOUNT,PAY_PV,PAY_TYPE,
+            PERIOD_NUM,STATUS,IS_DELETE,P_CALC_MONTH,CREATED_AT')
+            ->where("PERIOD_NUM=:PERIOD_NUM AND IS_DELETE=0 AND ORDER_TYPE=:ORDER_TYPE",
+                [':PERIOD_NUM' => $this->_periodNum, ':ORDER_TYPE' => DeclarationForm::TYPE_FX])
+            ->orderBy('CREATED_AT DESC,ID DESC')
+            ->limit($this->_limit)->offset($_offset * $this->_limit)
+            ->with([
+                'recName'      => function ($query) {
+                    $query->select('USER_ID,REC_UID')
+                        ->with([
+                            'recUserName' => function ($query) {
+                                $query->select('USER_ID,REC_UID,USER_NAME');
+                            },
+                            'recRealName' => function ($query) {
+                                $query->select('ID,REAL_NAME');
+                            }
+                        ]);
+                },
+                'userByUserId' => function ($query) {
+                    $query->select('ID,LAST_DEC_LV,EMP_LV,STATUS,SUB_COM_ID,PROVINCE,CITY,COUNTY');
+                }
+            ])
+            ->asArray()->all();
+
+        $insertPerfOrderData = [];
+        if (!empty($allData)) {
+            foreach ($allData as $k => $data) {
+                if ($data['PAY_TYPE'] === PerfCalc::ORDER_PAY_TYPE_CASH) {
+                    //111期开始由50%改为60%-by 2020-04-30修改
+                    $payPv = $data['PAY_PV'] * $this->_sysConfig['cashReconsumeBonusPercent']['VALUE'] / 100;
+                } else {
+                    $payPv = $data['PAY_PV'];
+                }
+
+                if ($payPv <= 0) continue;
+
+                $insertPerfOrderData[] = [
+                    'PV'                 => $payPv,
+                    'LAST_REC_USER_NAME' => $data['recName']['recUserName']['USER_NAME'] ?? '',
+                    'LAST_REC_REAL_NAME' => $data['recName']['recRealName']['REAL_NAME'] ?? '',
+                    'LAST_DEC_LV'        => $data['userByUserId']['LAST_DEC_LV'] ?? '', //CalcCache 810
+                    'LAST_EMP_LV'        => $data['userByUserId']['EMP_LV'] ?? '',
+                    'LAST_STATUS'        => $data['userByUserId']['STATUS'] ?? 0,
+                    'LAST_SUB_COM_ID'    => $data['userByUserId']['SUB_COM_ID'] ?? '',
+                    'LAST_PROVINCE'      => $data['userByUserId']['PROVINCE'] ?? 0,
+                    'LAST_CITY'          => $data['userByUserId']['CITY'] ?? 0,
+                    'LAST_COUNTY'        => $data['userByUserId']['COUNTY'] ?? 0,
+
+                    'LAST_DEC_DEC_LV'     => $data['userByUserId']['LAST_DEC_LV'] ?? '',
+                    'LAST_DEC_SUB_COM_ID' => $data['userByUserId']['SUB_COM_ID'] ?? 0,
+                    'LAST_DEC_PROVINCE'   => $data['userByUserId']['PROVINCE'] ?? 0,
+                    'LAST_DEC_CITY'       => $data['userByUserId']['CITY'] ?? 0,
+                    'LAST_DEC_COUNTY'     => $data['userByUserId']['COUNTY'] ?? 0,
+
+                    'DEC_USER_ID' => $data['USER_ID'],
+
+                    'PAY_TYPE'         => $data['PAY_TYPE'],
+                    'FROM_TABLES'      => 'order_shop',
+                    'ID'               => SnowFake::instance()->generateId(),
+                    'SN'               => $data['SN'],
+                    'DEC_SN'           => null,
+                    'DEC_TYPE'         => 'FX',
+                    'DEC_STATUS'       => PerfOrder::STATUS_NORMAL,
+                    'USER_ID'          => $data['USER_ID'],
+                    'DEC_AMOUNT'       => $data['PAY_AMOUNT'],
+                    'PERIOD_NUM'       => $this->_periodNum,
+                    'CALC_MONTH'       => $this->_calcYearMonth,
+                    'P_CALC_MONTH'     => Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH),
+                    'CREATED_AT'       => Date::nowTime(),
+                    'CLOSED_AT'        => 0,
+                    'ORDER_CREATED_AT' => $data['CREATED_AT']
+                ];
+            }
+            $data    = null;
+            $_offset += 1;
+            $this->perfOrderInsert($insertPerfOrderData);
+            $insertPerfOrderData = null;
+            goto orderShop;
+        }
+        $allData = null;
+        return true;
+    }
+
+
+    /**
+     * @param $data
+     */
+    private function perfOrderInsert($data)
+    {
+        $table = self::BASE_INFO_METHODS['perfOrder']['table'] ?? '';
+//        $fieldArray = self::BASE_INFO_METHODS['perfOrder']['field'] ?? [];
+        $fieldArray = array_keys($data[0]);
+        return \Yii::$app->db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+    }
+}
+

+ 278 - 0
common/helpers/bonus/Calc/PullCalcBonusData.php

@@ -0,0 +1,278 @@
+<?php
+
+namespace common\helpers\bonus\Calc;
+
+use common\helpers\Date;
+use common\models\CalcBonus;
+use common\models\CalcBonusBD;
+use common\models\CalcBonusBS;
+use common\models\CalcBonusGarage;
+use common\models\CalcBonusGL;
+use common\models\CalcBonusGX;
+use common\models\CalcBonusQuarter;
+use common\models\CalcBonusQY;
+use common\models\CalcBonusStandard;
+use common\models\CalcBonusTG;
+use common\models\CalcBonusTourism;
+use common\models\CalcBonusVilla;
+use common\models\CalcBonusVIP;
+use common\models\CalcBonusYC;
+use common\models\CalcBonusYJ;
+use common\models\ScoreMonth;
+use common\models\User;
+use common\models\UserBonus;
+use common\models\UserInfo;
+
+class PullCalcBonusData extends BaseBusiness
+{
+    const BASE_INFO_METHODS = [
+        'calcBonus' => ['type' => 'separately', 'table' => 'AR_CALC_BONUS'],
+
+        'calcBonusBs'      => ['type' => 'same', 'table' => 'AR_CALC_BONUS_BS'],
+        'calcBonusQuarter' => ['type' => 'same', 'table' => 'AR_CALC_BONUS_QUARTER'],
+        'calcBonusTourism' => ['type' => 'same', 'table' => 'AR_CALC_BONUS_TOURISM'],
+
+        'calcBonusBd'     => ['type' => 'calc_month', 'table' => 'AR_CALC_BONUS_BD'],
+        'calcBonusQy'     => ['type' => 'calc_month', 'table' => 'AR_CALC_BONUS_QY'],
+        'calcBonusTg'     => ['type' => 'calc_month', 'table' => 'AR_CALC_BONUS_TG'],
+        'calcBonusVilla'  => ['type' => 'calc_month', 'table' => 'AR_CALC_BONUS_VILLA'],
+        'calcBonusGarage' => ['type' => 'calc_month', 'table' => 'AR_CALC_BONUS_GARAGE'],
+
+    ];
+
+    public function __construct($periodNum)
+    {
+        parent::__construct($periodNum);
+    }
+
+    public function start(): array
+    {
+        $db = $this->_calc_db_name;
+        try {
+            //清除本期原有数据,可多次拉取
+//            \Yii::$app->$db->createCommand()->delete($table, "PERIOD_NUM = $this->_periodNum")->execute();
+            $this->clearCalcTableData();
+
+            foreach (self::BASE_INFO_METHODS as $method => $info) {
+                $table = $info['table'];
+
+                if ('same' == $info['type']) {
+                    $_offset = 0;
+                    sameBonus:
+                    $offset = $_offset * $this->_limit;
+                    $data   = \Yii::$app->$db->createCommand("SELECT * from $table where PERIOD_NUM = $this->_periodNum limit $this->_limit offset $offset;")->queryAll();
+                    if (!empty($data)) {
+                        $fieldArray = array_keys($data[0]);
+                        $_offset    += 1;
+                        \Yii::$app->db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+                        $data = null;
+                        goto sameBonus;
+                    }
+                    $data = null;
+                    continue;
+                }
+
+                if ('calc_month' == $info['type']) {
+                    $_offset = 0;
+                    calcMonth:
+                    $offset = $_offset * $this->_limit;
+                    $data   = \Yii::$app->$db->createCommand("SELECT *,FROM_UNIXTIME(CREATED_AT,'%Y-%m-%d') as P_CALC_MONTH from $table where PERIOD_NUM = $this->_periodNum limit $this->_limit offset $offset;")->queryAll();
+                    if (!empty($data)) {
+                        $fieldArray = array_keys($data[0]);
+                        $_offset    += 1;
+                        \Yii::$app->db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+                        $data = null;
+                        goto calcMonth;
+                    }
+                    $data = null;
+                    continue;
+                }
+
+                if ('separately' == $info['type'] && method_exists($this, $method)) {
+                    $this->$method();
+                    continue;//必要,避免增加逻辑代码后忘记此处
+                }
+            }
+        } catch (\Exception $e) {
+            return $this->fail('msg:' . $e->getMessage() . 'line:' . $e->getLine());
+        }
+        //同步周期表的值到业务系统
+        self::pullPeriodForUpdate($this->_periodNum);
+        return $this->success();
+    }
+
+    public function calcBonus()
+    {
+        $repeatField = [
+//            'ORI_CAPPED_BONUS_QY' => ['CAPPED_BONUS_QY'],
+            'ORI_BONUS_QY'        => ['BONUS_QY'],
+
+            'ORI_BONUS_BS'      => ['BONUS_BS', 'REAL_BONUS_BS'],
+            'ORI_BONUS_QUARTER' => ['BONUS_QUARTER'],
+            'ORI_BONUS_TOURISM' => ['BONUS_TOURISM'],
+            'ORI_BONUS_GARAGE'  => ['BONUS_GARAGE'],
+            'ORI_BONUS_VILLA'   => ['BONUS_VILLA'],
+            'ORI_BONUS_BD'      => ['BONUS_BD'],
+            'ORI_BONUS_TG'      => ['BONUS_TG'],
+        ];
+
+        $db         = $this->_calc_db_name;
+        $table      = self::BASE_INFO_METHODS['calcBonus']['table'];
+        $pCalcMonth = Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH);
+
+        $calcPeriod   = $this->getCalcPeriod();
+        $calculatedAt = $calcPeriod['CALCULATED_AT'];
+
+        $_offset      = 0;
+        $this->_limit = 2000;
+        calcMonth:
+
+        $offset = $_offset * $this->_limit;
+
+        $data = \Yii::$app->$db->createCommand("SELECT
+            b.*,
+            1 as LAST_LOCATION,
+            $calculatedAt as CALCULATED_AT,
+            '$pCalcMonth' as P_CALC_MONTH
+            from $table as b 
+        where b.PERIOD_NUM = $this->_periodNum limit $this->_limit offset $offset;")->queryAll();
+
+        if (!empty($data)) {
+            $_offset += 1;
+            $data    = array_column($data, null, 'USER_ID');
+            $userIds = array_keys($data);
+            //获取用户信息相关的数据 在下方循环内以名为$user的变量整合
+            $userInfo = array_column(self::getUserInfoByUserIds($userIds), null, 'ID');
+            //获取用户积分相关的数据
+            $userPointsInfo = array_column(self::getUserPointsByUserIds($userIds), null, 'ID');
+
+            foreach ($data as $userId => $value) {
+                if (isset($userInfo[$userId])) {
+                    $user                       = $userInfo[$userId];
+                    $user['LAST_REC_USER_NAME'] = $user['recUserName']['USER_NAME'] ?? '';
+                    $user['LAST_REC_REAL_NAME'] = $user['recRealName']['REAL_NAME'] ?? '';
+                    $user['LAST_CON_USER_NAME'] = $user['conUserName']['USER_NAME'] ?? '';
+                    $user['LAST_CON_REAL_NAME'] = $user['conRealName']['REAL_NAME'] ?? '';
+                    $user['LAST_SYSTEM_ID']     = $user['LAST_SYSTEM_ID'] ?? '';
+                    $user['EXCHANGE_POINTS']    = $userPointsInfo[$userId] ?? 0;
+
+                    //将ori开头的奖金项目赋值到实发奖金字段
+                    $value = self::formatDataByParams($value, $repeatField);
+                    unset($user['ID'], $user['REC_UID'], $user['CON_UID'], $user['recUserName'], $user['recRealName'],
+                        $user['conUserName'], $user['conRealName'],
+                        $value['ORI_BONUS_TOURISM'], $value['ORI_BONUS_GARAGE'], $value['ORI_BONUS_VILLA']);
+
+                    //整合数据
+                    $data[$userId] = array_merge($value, $user);
+                }
+            }
+
+            $fieldArray = array_keys(current($data));
+            \Yii::$app->db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+            $userInfo = null;
+            $data     = null;
+            goto calcMonth;
+        }
+        $data = null;
+        return true;
+
+    }
+
+    /**
+     * @param array $userIds
+     * @return array|\yii\db\ActiveRecord[]
+     */
+    public static function getUserInfoByUserIds(array $userIds): array
+    {
+        if (empty($userIds)) {
+            return [];
+        }
+        return UserInfo::find()->alias('UI')
+            ->join('INNER JOIN', User::tableName() . ' AS U', 'U.ID = UI.USER_ID')
+            ->where(['in', 'U.ID', $userIds])
+            ->select('
+                U.ID,U.STATUS AS LAST_STATUS,U.MOBILE AS LAST_MOBILE,U.PERIOD_AT AS LAST_PERIOD_AT,U.CREATED_AT AS LAST_CREATED_AT,
+            U.SUB_COM_ID AS LAST_SUB_COM_ID,U.PROVINCE AS LAST_PROVINCE,U.CITY AS LAST_CITY,U.COUNTY AS LAST_COUNTY,
+            U.IS_DIRECT_SELLER AS LAST_IS_DIRECT_SELLER,UI.SYSTEM_ID AS LAST_SYSTEM_ID,UI.REC_UID,UI.CON_UID')
+            ->with([
+                'recUserName' => function ($query) {
+                    $query->select('USER_ID,USER_NAME');
+                },
+                'recRealName' => function ($query) {
+                    $query->select('ID,REAL_NAME');
+                },
+                'conUserName' => function ($query) {
+                    $query->select('USER_ID,USER_NAME');
+                },
+                'conRealName' => function ($query) {
+                    $query->select('ID,REAL_NAME');
+                }
+            ])
+            ->asArray()
+            ->all();
+    }
+
+    /**
+     * @param array $userIds
+     * @return array|\yii\db\ActiveRecord[]
+     */
+    public static function getUserPointsByUserIds(array $userIds): array
+    {
+        if (empty($userIds)) {
+            return [];
+        }
+        return UserBonus::find()->where(['in', 'USER_ID', $userIds])
+            ->select('EXCHANGE_POINTS,USER_ID')
+            ->asArray()
+            ->all();
+    }
+
+    /**
+     * @param array $data
+     * @param array $format
+     * @return array
+     */
+    public static function formatDataByParams(array $data, array $format): array
+    {
+        if (empty($data) || empty($format)) {
+            return [];
+        }
+        foreach ($data as $field => $v) {
+            if (isset($format[$field])) {
+                foreach ($format[$field] as $formatField) {
+                    $data[$formatField] = $v;
+                }
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * 清空相关表数据
+     */
+    public function clearCalcTableData()
+    {
+        // 奖金表
+        CalcBonus::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        CalcBonusBS::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        CalcBonusQuarter::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+
+        CalcBonusBD::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        CalcBonusQY::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        CalcBonusTG::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+//        CalcBonusYJ::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+//        CalcBonusGX::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+//        CalcBonusGL::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        // 月结时要清空的数据
+        if ($this->_isCalcMonth) {
+//            CalcBonusYC::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+//            CalcBonusVIP::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+//            CalcBonusStandard::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+//            ScoreMonth::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+            CalcBonusTourism::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+            CalcBonusGarage::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+            CalcBonusVilla::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        }
+    }
+
+}

+ 128 - 0
common/helpers/bonus/Calc/PullPerfDataFromCalc.php

@@ -0,0 +1,128 @@
+<?php
+
+namespace common\helpers\bonus\Calc;
+
+use common\helpers\Date;
+use common\models\PerfMonth;
+use common\models\PerfPeriod;
+use common\models\PerfStandard;
+
+/**
+ * 从计算服务拉取生成的业绩单
+ */
+class PullPerfDataFromCalc extends BasePerfBusiness
+{
+
+    public function __construct($periodNum)
+    {
+        parent::__construct($periodNum);
+    }
+
+    public function start(): array
+    {
+        try {
+            //验证状态值
+            if ($this->verify()) {
+                //清空业绩单相关数据
+                $this->clearPerfPeriod();
+                if ($this->_isCalcMonth) {
+                    //拉取月业绩
+                    $this->pullPerfMonth();
+                }
+                //拉取期业绩数据
+                $this->pullPerfPeriod();
+                //同步周期表的值到业务系统
+                self::pullPeriodForUpdate($this->_periodNum);
+
+                return $this->success();
+            } else {
+                return $this->fail('业绩单还未生成');
+            }
+        } catch (\Exception $e) {
+            return $this->fail('msg:' . $e->getMessage() . 'line:' . $e->getLine());
+        }
+    }
+
+    public function verify(): bool
+    {
+        $db   = $this->_calc_db_name;
+        $data = \Yii::$app->$db->createCommand("SELECT IS_PREPARE,IS_PERFED FROM AR_PERIOD where PERIOD_NUM = $this->_periodNum")->queryOne();;
+
+        if (
+            2 == $data['IS_PREPARE'] //计算业绩阶段
+            &&
+            1 == $data['IS_PERFED']  //1表示累计业绩已完成
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    public function clearPerfPeriod()
+    {
+        // 周业绩
+        PerfPeriod::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        // 业绩单
+//        PerfOrder::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        // 删除活跃用户
+        // PerfActiveUser::pageDeleteAll('PERIOD_NUM='.$this->_periodNum.'  AND IS_SENT=0 ');
+        // 月结时要清空的数据
+        if ($this->_isCalcMonth) {
+            // 月业绩表
+            PerfMonth::pageDeleteAll("CALC_MONTH='{$this->_calcYearMonth}'");
+            //达标业绩表
+//            PerfStandard::pageDeleteAll("CALC_MONTH='{$this->_calcYearMonth}'");
+        }
+    }
+
+    public function pullPerfMonth(): bool
+    {
+        $db      = $this->_calc_db_name;
+        $_offset = 0;
+
+        //        $pCalcMonth = date('Y-m-d', strtotime($formatOrderData['CREATED_AT']));
+        $pCalcMonth = Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH);
+
+        periodMonth:
+        $offset = $_offset * $this->_limit;
+        $data   = \Yii::$app->$db->createCommand("SELECT p.*,'$pCalcMonth' as P_CALC_MONTH ,u.DEC_LV as LAST_DEC_LV,u.EMP_LV as LAST_EMP_LV, ifnull(u.STATUS,0) as LAST_STATUS FROM AR_PERF_MONTH as p LEFT JOIN AR_USER AS u ON p.user_id = u.id
+            where p.CALC_MONTH = $this->_calcYearMonth limit $this->_limit offset $offset;")->queryAll();
+
+        if (!empty($data)) {
+            $fieldArray = array_keys($data[0]);
+            $_offset    += 1;
+            \Yii::$app->db->createCommand()->batchInsert('AR_PERF_MONTH', $fieldArray, $data)->execute();
+            $data = null;
+            goto periodMonth;
+        }
+        $data = null;
+
+        return true;
+    }
+
+    public function pullPerfPeriod(): bool
+    {
+        $db      = $this->_calc_db_name;
+        $_offset = 0;
+
+        //        $pCalcMonth = date('Y-m-d', strtotime($formatOrderData['CREATED_AT']));
+        $pCalcMonth = Date::ociToDate($this->_calcYearMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH);
+
+        period:
+        $offset = $_offset * $this->_limit;
+        $data   = \Yii::$app->$db->createCommand("SELECT p.*,'$pCalcMonth' as P_CALC_MONTH ,u.DEC_LV as LAST_DEC_LV,u.EMP_LV as LAST_EMP_LV, ifnull(u.STATUS,0) as LAST_STATUS FROM AR_PERF_PERIOD as p LEFT JOIN AR_USER AS u ON p.user_id = u.id where p.PERIOD_NUM = $this->_periodNum limit $this->_limit offset $offset;")->queryAll();
+
+        if (!empty($data)) {
+            $fieldArray = array_keys($data[0]);
+            $_offset    += 1;
+            \Yii::$app->db->createCommand()->batchInsert('AR_PERF_PERIOD', $fieldArray, $data)->execute();
+            $data = null;
+            goto period;
+        }
+        $data = null;
+
+        return true;
+    }
+
+
+}

+ 589 - 0
common/helpers/bonus/Calc/PushBaseDataToCalc.php

@@ -0,0 +1,589 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: leo
+ * Date: 2018/8/2
+ * Time: 上午10:38
+ */
+
+namespace common\helpers\bonus\Calc;
+
+use common\components\ActiveRecord;
+use common\models\CalcBonus;
+use common\models\CalcBonusBS;
+use common\models\CalcBonusQY;
+use common\models\EmployLevel;
+use common\models\forms\DeclarationForm;
+use common\models\PerfMonth;
+use common\models\PerfPeriod;
+use common\models\PerfStandard;
+use common\models\Period;
+
+class PushBaseDataToCalc extends BaseBusiness
+{
+    const BASE_INFO_METHODS = [
+        //--- 用户表
+        'user'             => ['separately' => true, 'table' => 'AR_USER', 'field' => [
+            'ID',
+            'USER_NAME',
+            'REAL_NAME',
+            'CREATED_AT',
+            'STATUS',
+            'DEC_LV',
+            'EMP_LV',
+            'CROWN_LV',
+            'IS_DEC',
+            'DEC_ID',
+            'DEC_ROLE_ID',
+            'LAST_DEC_LV',
+            'IS_STUDIO',
+            'LAST_CROWN_LV',
+            'DELETED',
+        ]],
+        //--- 用户信息表
+        'userInfo'         => ['table' => 'AR_USER_INFO', 'field' => [
+            'ID',
+            'USER_ID',
+            'USER_NAME',
+            'CON_UID',
+            'REC_UID',
+            'CREATED_AT',
+        ]],
+
+        //---配置表初始化数据见数据库是否开启管理奖,使用的是openbs进行控制
+        'config'           => ['table' => 'AR_CONFIG', 'field' => [
+            'CONFIG_NAME',
+            'TITLE',
+            'UNIT',
+            'INPUT_TYPE',
+            'OPTIONS',
+            'VALUE',
+            'TYPE',
+            'SORT',
+            'CREATED_AT',
+            'UPDATED_AT',
+        ]],
+        //--- 报单级别配置表
+        'declarationLevel' => ['table' => 'AR_DECLARATION_LEVEL', 'field' => [
+            'ID',
+            'LEVEL_NAME',
+            'PERF',
+            'QY_PERCENT',
+            'QY_TOUCH_CAP',
+            'QY_BIG_CAP',
+            'SORT',
+            'CREATED_AT',
+            'INCOME_CAP',
+        ]],
+        //--- 级别配置表
+        'employLevel'      => ['table' => 'AR_EMPLOY_LEVEL', 'field' => [
+            'ID',
+            'LEVEL_NAME',
+            'CREATED_AT',
+            'BS_PERCENT',
+            'LEVEL_SORT',
+            'TOURISM_PERCENT',
+            'GARAGE_PERCENT',
+            'ACHIEVE_PV',
+            'ACHIEVE_MEMBER_NUM',
+            'ACHIEVE_PERF_PV',
+        ], 'alias'                     => [
+            'LEVEL_SORT' => 'SORT',
+        ]],
+        //--- 报单中心级别配置表
+        'decRole'          => ['table' => 'AR_DEC_ROLE', 'field' => [
+            'ID',
+            'ROLE_NAME',
+            'FW_BONUS_PERCENT',
+            'SORT',
+            'CREATED_AT',
+        ]],
+        //--- 安置网络表
+        'userNetwork'      => ['table' => 'AR_USER_NETWORK_NEW', 'field' => [
+            'ID',
+            'USER_ID',
+            'PARENT_UID',
+//            'LOCATION_TAG',
+            'RELATIVE_LOCATION',
+//            'TOP_UID',
+//            'TOP_DEEP',
+//            'PARENT_UIDS',
+            'CREATED_AT',
+//            'UPDATED_AT',
+        ]],
+
+        //--- 星级配置表
+        'starCrownLevel'   => ['table' => 'AR_CROWN_LEVEL', 'field' => [
+            'ID',
+            'LEVEL_NAME',
+            'MIN_LEVEL_ID',
+            'LEVEL_SCORE',
+            'UPGRADE_SCORE',
+            'SORT',
+            'TOURISM_PERCENT',
+            'VILLA_PERCENT',
+            'GARAGE_PERCENT',
+            'CREATED_AT',
+        ]],
+        //--- 业绩单表
+        'perfOrder'        => ['separately' => true, 'table' => 'AR_PERF_ORDER', 'field' => [
+            'ID',
+            'SN',
+//            'PERF_TYPE',
+            'DEC_TYPE',
+            'USER_ID',
+            'PV',
+            'PERIOD_NUM',
+            'CALC_MONTH',
+            'DEC_USER_ID',
+            'CREATED_AT',
+            'ORDER_AMOUNT',
+            'PAY_TYPE',
+            'FROM_TABLES',
+            'PAY_TYPE'
+        ]],
+        //--- 用户结余业绩表
+        'userPerf'         => ['table' => 'AR_USER_PERF', 'field' => [
+            'ID',
+            'USER_ID',
+            'PV_PCS_ZC',
+            'PV_PCS_FX',
+            'PV_1L',
+            'PV_2L',
+            'PV_3L',
+            'PV_4L',
+            'PV_5L',
+            'CREATED_AT',
+            'PV_PSS',
+            'SURPLUS_1L',
+            'SURPLUS_1L_ZC',
+            'SURPLUS_1L_FX',
+            'SURPLUS_2L',
+            'SURPLUS_2L_ZC',
+            'SURPLUS_2L_FX',
+            'SURPLUS_3L',
+            'SURPLUS_3L_ZC',
+            'SURPLUS_3L_FX',
+            'SURPLUS_4L',
+            'SURPLUS_4L_ZC',
+            'SURPLUS_4L_FX',
+            'SURPLUS_5L',
+            'SURPLUS_5L_ZC',
+            'SURPLUS_5L_FX',
+            'PV_PSS_TOTAL',
+        ]],
+        //--- 团队奖明细表, 月节点的时候,需要将这个结算月的所有数据都同步过来
+        'calcBonusQy'      => ['separately' => true, 'table' => 'AR_CALC_BONUS_QY', 'field' => [
+            'ID',
+            'USER_ID',
+            'LAST_DEC_LV',
+            'LAST_EMP_LV',
+            'LAST_CROWN_LV',
+            'LAST_STATUS',
+            'AMOUNT',
+            'PERIOD_NUM',
+            'CALC_YEAR',
+            'CALC_MONTH',
+            'LOGS',
+            'CREATED_AT',
+            'ORI_BONUS',
+            'RECONSUME_POINTS',
+            'MANAGE_TAX',
+            'ORI_CAPPED_BONUS_QY',
+            'IS_ACTIVE',
+            'HOPE_CROWN_LV',
+            'HOPE_BONUS',
+        ]],
+        //--- 用户结算月累计复消积分表,此表为新表,不传数据即代表没有扣除复消积分。 统计此结算月扣除的复消费积分总数,根据calc_bonus表 sum 一下复消积分字段即可。
+        'deductReconsume'  => ['separately' => true, 'table' => 'AR_DEDUCT_RECONSUME', 'field' => [
+            'ID',
+            'USER_ID',
+            'CALC_MONTH',
+            'RECONSUME_POINTS_SUM',
+            'CREATED_AT',
+        ]],
+        //--- 期业绩表数据. 月节点的时候,计算月业绩,需要业务系统将ar_perf_period表此结算月的所有数据同步过来。
+        'perfPeriod'       => ['separately' => true, 'table' => 'AR_PERF_PERIOD', 'field' => [
+            'ID',
+            'USER_ID',
+            'FX_AMOUNT_CASH',
+            'PV_PCS',
+            'PV_PCS_ZC',
+            'PV_PCS_FX',
+            'PV_PCS_FX_CASH',
+            'PV_PCS_FX_POINT',
+            'PV_1L',
+            'PV_1L_TOUCH',
+            'PV_1L_ZC',
+            'PV_1L_FX',
+            'PV_2L',
+            'PV_2L_TOUCH',
+            'PV_2L_ZC',
+            'PV_2L_FX',
+            'PV_3L',
+            'PV_3L_TOUCH',
+            'PV_3L_ZC',
+            'PV_3L_FX',
+            'PV_4L',
+            'PV_4L_TOUCH',
+            'PV_4L_ZC',
+            'PV_4L_FX',
+            'PV_5L',
+            'PV_5L_TOUCH',
+            'PV_5L_ZC',
+            'PV_5L_FX',
+            'SURPLUS_1L',
+            'SURPLUS_1L_ZC',
+            'SURPLUS_1L_FX',
+            'SURPLUS_2L',
+            'SURPLUS_2L_ZC',
+            'SURPLUS_2L_FX',
+            'SURPLUS_3L',
+            'SURPLUS_3L_ZC',
+            'SURPLUS_3L_FX',
+            'SURPLUS_4L',
+            'SURPLUS_4L_ZC',
+            'SURPLUS_4L_FX',
+            'SURPLUS_5L',
+            'SURPLUS_5L_ZC',
+            'SURPLUS_5L_FX',
+            'PERIOD_NUM',
+            'CALC_MONTH',
+            'CREATED_AT',
+            'PV_PSS',
+        ]],
+        //--- 期数配置表
+        'period'           => ['separately' => true, 'table' => 'AR_PERIOD', 'field' => [
+            'ID',
+            'PERIOD_NUM',
+            'CALC_YEAR',
+            'CALC_MONTH',
+            'IS_MONTH',
+            'IS_PERFED',
+            'IS_CALCULATED',
+            'IS_PERFING',
+            'IS_CALCING',
+            'PERF_PERCENT',
+            'CALC_PERCENT',
+            'PERF_STARTED_AT',
+            'PERFED_AT',
+            'CALCULATE_STARTED_AT',
+            'CALCULATED_AT',
+            'CREATED_AT',
+        ]],
+        //--- 推荐网络表
+        'userRelation'     => ['table' => 'AR_USER_RELATION_NEW', 'field' => [
+            'ID',
+            'USER_ID',
+            'PARENT_UID',
+            'TOP_DEEP',
+            'CREATED_AT',
+        ]],
+    ];
+
+    public function __construct($periodNum)
+    {
+        parent::__construct($periodNum);
+    }
+
+    public function start(): array
+    {
+        try {
+            //清除业务系统本地数据(业绩相关)
+            $this->clearBusinessData();
+            //清理计算服务系统的基本数据表
+            $this->clearSyncCalcTableData();
+            //从业务系统推送数据到计算服务系统
+            $this->initializeBaseInfo();
+//            $db = $this->_calc_db_name;
+            //todo 更新进度
+//            \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['IS_PREPARE' => 1], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $this->_periodNum])->execute();
+        } catch (\Exception $e) {
+            return $this->fail('msg:' . $e->getMessage() . 'line:' . $e->getLine());
+        }
+        return $this->success();
+    }
+
+    public function clearBusinessData()
+    {
+        // 周业绩
+        PerfPeriod::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        // 业绩单
+//        PerfOrder::pageDeleteAll('PERIOD_NUM=' . $this->_periodNum);
+        // 删除活跃用户
+        // PerfActiveUser::pageDeleteAll('PERIOD_NUM='.$this->_periodNum.'  AND IS_SENT=0 ');
+        // 月结时要清空的数据
+        if ($this->_isCalcMonth) {
+            // 月业绩表
+            PerfMonth::pageDeleteAll("CALC_MONTH='{$this->_calcYearMonth}'");
+            //达标业绩表
+            PerfStandard::pageDeleteAll("CALC_MONTH='{$this->_calcYearMonth}'");
+        }
+    }
+
+    /**
+     * 清理配置中的数据表数据
+     * @return array
+     */
+    public function clearSyncCalcTableData(): array
+    {
+        $db  = $this->_calc_db_name;
+        $res = [];
+        foreach (self::BASE_INFO_METHODS as $method => $val) {
+            if (\Yii::$app->$db->createCommand("TRUNCATE TABLE {$val['table']}")->execute() !== false) {
+                $res[] = $val['table'];
+            }
+        }
+        return $res;
+    }
+
+    public function initializeBaseInfo()
+    {
+        $db = $this->_calc_db_name;
+
+        foreach (self::BASE_INFO_METHODS as $method => $val) {
+            $table      = self::BASE_INFO_METHODS[$method]['table'] ?? '';
+            $fieldArray = self::BASE_INFO_METHODS[$method]['field'] ?? [];
+
+            if (!empty($table) && !empty($fieldArray)) {
+                if (!isset($val['separately'])) {
+                    $_offset = 0;
+                    A:
+                    if (isset($val['alias'])) {
+                        //如果业务表字段和计算服务表不同,则配置alias
+                        foreach ($val['alias'] as $org => $v) {
+                            $index              = array_search($org, $fieldArray);
+                            $fieldArray[$index] = $v;
+                        }
+                    }
+                    $field = implode(',', $fieldArray);
+                    $model = 'common\models\\' . ucfirst($method);
+                    $data  = $model::find()->select($field)->limit($this->_limit)->offset($_offset * $this->_limit)->asArray()->all();
+                    if (!empty($data)) {
+                        $_offset += 1;
+                        \Yii::$app->$db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+                        $data = null;
+                        goto A;
+                    }
+                    $data = null;
+                } elseif (method_exists($this, $method) && $val['separately']) {
+                    $this->$method($table, $fieldArray, $db);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public function period($table, $fieldArray, $db)
+    {
+        $field = implode(',', $fieldArray);
+        $data  = Period::find()->select($field)
+            ->where('PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum])
+            ->asArray()->all();
+        if (!empty($data)) {
+            \Yii::$app->$db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+        }
+        return true;
+    }
+
+    public function user($table, $fieldArray, $db)
+    {
+        $field        = '`' . implode('`, `', $fieldArray) . '`,null as LAST_EMP_LV';
+        $fieldArray[] = 'LAST_EMP_LV';
+        $_offset      = 0;
+
+        $forwardMonthPeriod = Period::find()
+            ->where('IS_MONTH=1 AND PERIOD_NUM<:PERIOD_NUM', [':PERIOD_NUM' => $this->_periodNum])
+            ->orderBy('PERIOD_NUM DESC')->asArray()->one();
+
+        $forwardPeriodNum = $forwardMonthPeriod['PERIOD_NUM'];
+
+//        $employeeLevelIds = CalcBonusBS::findAllAsArray(
+//            'PERIOD_NUM=:PERIOD_NUM ', [':PERIOD_NUM' => $forwardPeriodNum]
+//        );
+
+        $employeeLevelIds = CalcBonusBS::find()->where(
+            'PERIOD_NUM=:PERIOD_NUM ', [':PERIOD_NUM' => $forwardPeriodNum]
+        )->select('USER_ID,LEVEL_ID')->asArray()->all();
+
+        if (!empty($employeeLevelIds)) {
+            $employeeLevelIds = array_column($employeeLevelIds, null, 'USER_ID'); // 当前蓝星奖计算(即管理奖) 的等级
+        }
+
+        $noLevelId = EmployLevel::NO_LEVEL_ID;
+
+        user:
+        $offset = $_offset * $this->_limit;
+        $data   = ActiveRecord::findBySql("SELECT $field from AR_USER limit $this->_limit offset $offset;")->asArray()->all();
+
+        if (!empty($data)) {
+            foreach ($data as $k => $v) {
+                $data[$k]['LAST_EMP_LV'] = $noLevelId;
+                if (isset($employeeLevelIds[$v['ID']])) {
+                    $data[$k]['LAST_EMP_LV'] = $employeeLevelIds[$v['ID']]['LEVEL_ID'] ?? 0;
+                }
+            }
+            $_offset += 1;
+            \Yii::$app->$db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+
+            $data = null;
+            goto user;
+        }
+        $data             = null;
+        $employeeLevelIds = null;
+
+        return true;
+    }
+
+    public function calcBonusQy($table, $fieldArray, $db): bool
+    {
+        if (!$this->_isCalcMonth) {
+            return true;
+        }
+        $periodNum     = $this->_periodNum;
+        $currentPeriod = Period::getInfoByPeriodNum($periodNum);
+        //判断是否月节点
+        if ($currentPeriod['IS_MONTH']) {
+            $periodNum = Period::getCurrentMonthPeriodByPeriodNum($periodNum);
+        }
+        $field = implode(',', $fieldArray);
+        $data  = CalcBonusQY::find()->select($field)->where(['PERIOD_NUM' => $periodNum])->asArray()->all();
+
+        \Yii::$app->$db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+        $data = null;
+        return true;
+    }
+
+    public function deductReconsume($table, $fieldArray, $db): bool
+    {
+        $data = CalcBonus::find()
+            ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
+            ->select(['ID', 'USER_ID', 'CALC_MONTH', 'SUM(RECONSUME_POINTS)', 'CREATED_AT'])
+            ->groupBy(['USER_ID'])->having(['>', 'SUM(RECONSUME_POINTS)', 0])
+            ->asArray()
+            ->all();
+        if (!empty($data)) {
+            \Yii::$app->$db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+
+        }
+        $data = null;
+        return true;
+    }
+
+    public function perfPeriod($table, $fieldArray, $db): bool
+    {
+        if (!$this->_isCalcMonth) {
+            return true;
+        }
+        $field = implode(',', $fieldArray);
+        $data  = PerfPeriod::find()
+            ->where('CALC_MONTH=:CALC_MONTH', [':CALC_MONTH' => $this->_calcYearMonth])
+            ->select($field)
+            ->asArray()
+            ->all();
+        if (!empty($data)) {
+            \Yii::$app->$db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+        }
+        $data = null;
+        return true;
+    }
+
+    public function perfOrder($table, $fieldArray, $db): bool
+    {
+        $_offset = 0;
+
+        perfOrder:
+        $offset = $_offset * $this->_limit;
+        $field  = "ID,SN,DEC_TYPE, USER_ID,PV,
+         PERIOD_NUM, CALC_MONTH,DEC_USER_ID,
+         CREATED_AT,DEC_AMOUNT as ORDER_AMOUNT,PAY_TYPE,FROM_TABLES,PAY_TYPE";
+
+        $data = ActiveRecord::findBySql("SELECT $field  from AR_PERF_ORDER where PERIOD_NUM =  $this->_periodNum limit $this->_limit offset $offset;")
+            ->asArray()->all();
+        if (!empty($data)) {
+            $_offset    += 1;
+            $fieldArray = array_keys(current($data));
+            \Yii::$app->$db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+            $data = null;
+            goto perfOrder;
+        }
+        $data = null;
+        return true;
+    }
+
+    protected function decOrder($calcMonth, $offset = 0): bool
+    {
+        $field = "ID,ORDER_SN as SN,'ZC' as PERF_TYPE, TO_USER_ID as USER_ID,DEC_PV as PV,
+         PERIOD_NUM, '$calcMonth' as CALC_MONTH,USER_ID as DEC_USER_ID,
+         CREATED_AT,DEC_AMOUNT as ORDER_AMOUNT, PAID_WALLET as PAY_TYPE,'AR_DEC_ORDER'";
+
+        $data = ActiveRecord::findBySql("SELECT $field from AR_DEC_ORDER;")
+            ->where("PERIOD_NUM=:PERIOD_NUM AND IS_DEL=0 AND TYPE='ZC'", [':PERIOD_NUM' => $this->_periodNum])->asArray()->all();
+
+        $this->perfOrderInsert($data);
+        $data = null;
+        return true;
+    }
+
+    protected function orderDec($calcMonth, $offset = 0): bool
+    {
+        $field = "od.ID,od.SN,'ZC' as TYPE,od.USER_ID,od.PAY_PV,
+         od.PERIOD_NUM, '$calcMonth' as CALC_MONTH,
+         u.DEC_ID as DEC_USER_ID,
+         od.CREATED_AT,od.PAY_AMOUNT as ORDER_AMOUNT, od.PAY_TYPE,'AR_ORDER_DEC'";
+
+        $data = ActiveRecord::findBySql("SELECT $field from AR_ORDER_DEC as od left join AR_USER as u on od.USER_ID=u.ID;")
+            ->where("PERIOD_NUM=:PERIOD_NUM AND IS_DEL=0 AND TYPE='ZC'", [':PERIOD_NUM' => $this->_periodNum])->asArray()->all();
+
+        $this->perfOrderInsert($data);
+        $data = null;
+        return true;
+    }
+
+    protected function order($calcMonth, $offset = 0): bool
+    {
+        $field = "od.ID,od.SN,'FX' as TYPE,od.USER_ID,od.PAY_PV,
+         od.PERIOD_NUM, '$calcMonth' as CALC_MONTH,
+         '' as DEC_USER_ID,
+         od.CREATED_AT,od.PAY_AMOUNT as ORDER_AMOUNT, od.PAY_TYPE,'AR_ORDER'";
+
+        $data = ActiveRecord::findBySql("SELECT $field from AR_ORDER as od;")
+            ->where("PERIOD_NUM=:PERIOD_NUM AND IS_DELETE=0 AND ORDER_TYPE=:ORDER_TYPE",
+                [':PERIOD_NUM' => $this->_periodNum, ':ORDER_TYPE' => DeclarationForm::TYPE_FX])
+            ->asArray()->all();
+
+        $this->perfOrderInsert($data);
+        $data = null;
+        return true;
+    }
+
+    protected function orderShop($calcMonth, $offset = 0): bool
+    {
+        $field = "od.ID,od.SN,'FX' as TYPE,od.USER_ID,od.PAY_PV,
+         od.PERIOD_NUM, '$calcMonth' as CALC_MONTH,
+         '' as DEC_USER_ID,
+         od.CREATED_AT,od.PAY_AMOUNT as ORDER_AMOUNT, od.PAY_TYPE,'AR_ORDER_SHOP'";
+
+        $data = ActiveRecord::findBySql("SELECT $field from AR_ORDER_SHOP as od;")
+            ->where("PERIOD_NUM=:PERIOD_NUM AND IS_DELETE=0 AND ORDER_TYPE=:ORDER_TYPE",
+                [':PERIOD_NUM' => $this->_periodNum, ':ORDER_TYPE' => DeclarationForm::TYPE_FX])
+            ->asArray()->all();
+
+        $this->perfOrderInsert($data);
+        $data = null;
+        return true;
+    }
+
+    /**
+     * @param $data
+     * @return void
+     */
+    private function perfOrderInsert($data): void
+    {
+        $db         = $this->_calc_db_name;
+        $table      = self::BASE_INFO_METHODS['perfOrder']['table'] ?? '';
+        $fieldArray = self::BASE_INFO_METHODS['perfOrder']['field'] ?? [];
+        \Yii::$app->$db->createCommand()->batchInsert($table, $fieldArray, $data)->execute();
+    }
+}

+ 60 - 0
common/models/CalcRecord.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace common\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "{{%CALC_RECORD}}".
+ *
+ * @property string $ID
+ * @property string $PERIOD_NUM 周期
+ * @property string $TEXT 日志
+ * @property int $CREATED_AT 创建时间
+ */
+class CalcRecord extends \common\components\ActiveRecord
+{
+    /**
+     * {@inheritdoc}
+     */
+    public static function tableName()
+    {
+        return '{{%CALC_RECORD}}';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rules()
+    {
+        return [
+            [['PERIOD_NUM', 'TEXT'], 'required'],
+            [['PERIOD_NUM'], 'number'],
+            [['TEXT'], 'string', 'max' => 2000],
+            [['ID'], 'unique'],
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function attributeLabels()
+    {
+        return [
+            'ID'         => 'ID',
+            'PERIOD_NUM' => '周期数',
+            'TEXT'       => '日志',
+            'CREATED_AT' => '创建时间',
+        ];
+    }
+
+    public static function record($periodNum, $text): bool
+    {
+        CalcRecord::insertOne([
+            'PERIOD_NUM' => $periodNum,
+            'TEXT'       => $text,
+            'CREATED_AT' => time(),
+        ]);
+        return true;
+    }
+}

+ 16 - 0
common/models/DecOrder.php

@@ -89,4 +89,20 @@ class DecOrder extends \common\components\ActiveRecord
             'DELETED_AT' => '删除时间',
         ];
     }
+
+    public function getRecName()
+    {
+        return $this->hasOne(UserInfo::class, ['USER_ID' => 'TO_USER_ID']);
+    }
+
+    public function getUserByToUserId()
+    {
+        return $this->hasOne(User::class, ['ID' => 'TO_USER_ID']);
+    }
+
+    public function getUserByUserId()
+    {
+        return $this->hasOne(User::class, ['ID' => 'USER_ID']);
+    }
+
 }

+ 10 - 0
common/models/Order.php

@@ -154,4 +154,14 @@ class Order extends \common\components\ActiveRecord
         $orders = Order::find()->where("USER_ID = :USER_ID AND PERIOD_NUM IN ($periodsStr)", [':USER_ID' => $userId]);
         return $orders;
     }
+
+    public function getRecName()
+    {
+        return $this->hasOne(UserInfo::class, ['USER_ID' => 'USER_ID']);
+    }
+
+    public function getUserByUserId()
+    {
+        return $this->hasOne(User::class, ['ID' => 'USER_ID']);
+    }
 }

+ 10 - 0
common/models/OrderDec.php

@@ -128,4 +128,14 @@ class OrderDec extends \common\components\ActiveRecord
             'WAREHOUSE' => '发货仓',
         ];
     }
+
+    public function getRecName()
+    {
+        return $this->hasOne(UserInfo::class, ['USER_ID' => 'USER_ID']);
+    }
+
+    public function getUserByUserId()
+    {
+        return $this->hasOne(User::class, ['ID' => 'USER_ID']);
+    }
 }

+ 10 - 0
common/models/OrderShop.php

@@ -128,4 +128,14 @@ class OrderShop extends \common\components\ActiveRecord
             'WAREHOUSE' => '发货仓',
         ];
     }
+
+    public function getRecName()
+    {
+        return $this->hasOne(UserInfo::class, ['USER_ID' => 'USER_ID']);
+    }
+
+    public function getUserByUserId()
+    {
+        return $this->hasOne(User::class, ['ID' => 'USER_ID']);
+    }
 }

+ 27 - 0
common/models/Period.php

@@ -56,6 +56,11 @@ class Period extends \common\components\ActiveRecord
 
     const SYSTEM_START_PERIOD_NUM = 100;
 
+    const AUTO_EXEC_CALC = 1;
+    const MANUAL_EXEC_CALC = 0;
+    const IS_PROCESSING = 1;
+    const NOT_PROCESSING = 0;
+
     public $nowPeriodArr = null;
     public $periodNum = null;
     public $periodArr = null;
@@ -848,4 +853,26 @@ class Period extends \common\components\ActiveRecord
 
         return $periods;
     }
+
+    public static function updatePeriodIsAutoExec($periodNum, $isAutoExec = 0): int
+    {
+        return Period::updateAll(['AUTO_EXEC' => $isAutoExec], ['PERIOD_NUM' => $periodNum]);
+    }
+
+    public static function updatePeriodIsProcessing($periodNum, $isProcessing = 1): int
+    {
+        return Period::updateAll(['IS_PROCESSING' => $isProcessing], ['PERIOD_NUM' => $periodNum]);
+    }
+
+    /**
+     * 是否计算操作中
+     * @param $periodNum
+     * @return bool
+     */
+    public static function isProcessing($periodNum = null): bool
+    {
+        $period = static::findOneAsArray(['PERIOD_NUM' => $periodNum]);
+        if ($period['IS_PROCESSING'] == self::IS_PROCESSING) return true;
+        return false;
+    }
 }

+ 20 - 0
common/models/UserInfo.php

@@ -248,4 +248,24 @@ class UserInfo extends \common\components\ActiveRecord
             ],
         ];
     }
+
+    public function getRecUserName()
+    {
+        return $this->hasOne(UserInfo::class, ['USER_ID' => 'REC_UID']);
+    }
+
+    public function getRecRealName()
+    {
+        return $this->hasOne(User::class, ['ID' => 'REC_UID']);
+    }
+
+    public function getConUserName()
+    {
+        return $this->hasOne(UserInfo::class, ['USER_ID' => 'CON_UID']);
+    }
+
+    public function getConRealName()
+    {
+        return $this->hasOne(User::class, ['ID' => 'CON_UID']);
+    }
 }

+ 135 - 0
common/models/forms/PeriodForm.php

@@ -366,4 +366,139 @@ class PeriodForm extends Model
 //        }
     }
 
+    /**
+     * 页面请求异步处理 自动计算
+     * @return string | null
+     */
+    public function autoExec()
+    {
+        if (!$this->validate()) {
+            return null;
+        }
+//        // 把正在结算标记为真
+        $model = $this->_periodModel;
+//        $model->IS_CALCING    = 1;
+//        $model->CALC_ADMIN_ID = \Yii::$app->user->id;
+//        if (!$model->save()) {
+//            $this->addError('calc', Form::formatErrorsForApi($model->getErrors()));
+//            return null;
+//        }
+        // 异步处理添加任务
+        $settings      = \Yii::$app->params['swooleAsyncTimer'];
+        $bonusSettings = \Yii::$app->params['swooleBonusConfig'];
+        $settings      = array_merge($settings, $bonusSettings);
+        $taskKey       = \Yii::$app->swooleAsyncTimer->asyncHandle('calc/auto-execr', \Yii::$app->request->get(), $settings);
+        if ($taskKey === false) {
+            $this->addError('send', '请求失败');
+            return null;
+        }
+        return $model;
+    }
+
+    /**
+     * 页面请求异步处理 生成业绩单
+     * @return string | null
+     */
+    public function generatePerfOrder()
+    {
+        if (!$this->validate()) {
+            return null;
+        }
+//        // 把正在结算标记为真
+        $model = $this->_periodModel;
+//        $model->IS_CALCING    = 1;
+//        $model->CALC_ADMIN_ID = \Yii::$app->user->id;
+//        if (!$model->save()) {
+//            $this->addError('calc', Form::formatErrorsForApi($model->getErrors()));
+//            return null;
+//        }
+        // 异步处理添加任务
+        $settings      = \Yii::$app->params['swooleAsyncTimer'];
+        $bonusSettings = \Yii::$app->params['swooleBonusConfig'];
+        $settings      = array_merge($settings, $bonusSettings);
+        $taskKey       = \Yii::$app->swooleAsyncTimer->asyncHandle('calc/perf-order', \Yii::$app->request->get(), $settings);
+        if ($taskKey === false) {
+            $this->addError('send', '请求失败');
+            return null;
+        }
+        return $model;
+    }
+
+
+    /**
+     * 页面请求异步处理 推送基础数据
+     * @return string | null
+     */
+    public function pushBaseData()
+    {
+        if (!$this->validate()) {
+            return null;
+        }
+//        // 把正在结算标记为真
+        $model = $this->_periodModel;
+//        $model->IS_CALCING    = 1;
+//        $model->CALC_ADMIN_ID = \Yii::$app->user->id;
+//        if (!$model->save()) {
+//            $this->addError('calc', Form::formatErrorsForApi($model->getErrors()));
+//            return null;
+//        }
+        // 异步处理添加任务
+        $settings      = \Yii::$app->params['swooleAsyncTimer'];
+        $bonusSettings = \Yii::$app->params['swooleBonusConfig'];
+        $settings      = array_merge($settings, $bonusSettings);
+        $taskKey       = \Yii::$app->swooleAsyncTimer->asyncHandle('calc/push-data', \Yii::$app->request->get(), $settings);
+        if ($taskKey === false) {
+            $this->addError('send', '请求失败');
+            return null;
+        }
+        return $model;
+    }
+    public function pullPeriod()
+    {
+        if (!$this->validate()) {
+            return null;
+        }
+//        // 把正在结算标记为真
+        $model = $this->_periodModel;
+//        $model->IS_CALCING    = 1;
+//        $model->CALC_ADMIN_ID = \Yii::$app->user->id;
+//        if (!$model->save()) {
+//            $this->addError('calc', Form::formatErrorsForApi($model->getErrors()));
+//            return null;
+//        }
+        // 异步处理添加任务
+        $settings      = \Yii::$app->params['swooleAsyncTimer'];
+        $bonusSettings = \Yii::$app->params['swooleBonusConfig'];
+        $settings      = array_merge($settings, $bonusSettings);
+        $taskKey       = \Yii::$app->swooleAsyncTimer->asyncHandle('calc/pull-period', \Yii::$app->request->get(), $settings);
+        if ($taskKey === false) {
+            $this->addError('send', '请求失败');
+            return null;
+        }
+        return $model;
+    }
+    public function pullBonus()
+    {
+        if (!$this->validate()) {
+            return null;
+        }
+//        // 把正在结算标记为真
+        $model = $this->_periodModel;
+//        $model->IS_CALCING    = 1;
+//        $model->CALC_ADMIN_ID = \Yii::$app->user->id;
+//        if (!$model->save()) {
+//            $this->addError('calc', Form::formatErrorsForApi($model->getErrors()));
+//            return null;
+//        }
+        // 异步处理添加任务
+        $settings      = \Yii::$app->params['swooleAsyncTimer'];
+        $bonusSettings = \Yii::$app->params['swooleBonusConfig'];
+        $settings      = array_merge($settings, $bonusSettings);
+        $taskKey       = \Yii::$app->swooleAsyncTimer->asyncHandle('calc/pull-bonus', \Yii::$app->request->get(), $settings);
+        if ($taskKey === false) {
+            $this->addError('send', '请求失败');
+            return null;
+        }
+        return $model;
+    }
 }

+ 24 - 0
common/traits/ResponseTrait.php

@@ -0,0 +1,24 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Ming
+ * Date: 2017/11/3
+ * Time: 13:09
+ */
+
+namespace common\traits;
+
+trait ResponseTrait
+{
+    public function success($data = null, $msg = null)
+    {
+        return ['code' => 200, 'data' => $data ?? [], 'msg' => $msg ?? 'success'];
+
+    }
+
+    public function fail($msg = null, $code = null)
+    {
+        return ['code' => $code ?? 500, 'data' => [], 'msg' => $msg ?? 'fail'];
+
+    }
+}

+ 143 - 0
console/controllers/CalcController.php

@@ -0,0 +1,143 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: leo
+ * Date: 2018/3/9
+ * Time: 上午11:56
+ */
+
+namespace console\controllers;
+
+use common\helpers\bonus\Calc\GeneratePerfOrder;
+use common\helpers\bonus\Calc\PullCalcBonusData;
+use common\helpers\bonus\Calc\PullPerfDataFromCalc;
+use common\helpers\bonus\Calc\PushBaseDataToCalc;
+use common\helpers\Cache;
+use common\models\CalcRecord;
+use common\models\Period;
+
+
+class CalcController extends BaseController
+{
+
+    public function actionPerfOrder($taskKey)
+    {
+        $params    = Cache::getAsyncParams($taskKey);
+        $periodNum = $params['periodNum'] ?? 0;
+        CalcRecord::record($periodNum, '业绩单生成中');
+        //生成业绩单 状态已修改
+        $res = (new GeneratePerfOrder($periodNum))->start();
+        //该期状态改为不在计算中
+//        Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+
+        if (200 == $res['code']) {
+            self::recordCalcAndProcessStatus($periodNum, '业绩单已生成');
+            \Yii::$app->swooleAsyncTimer->pushAsyncResultToAdmin($params['handleUserId'], "第{$params['periodNum']}期生成业绩单更新成功");
+            return true;
+        } else {
+            self::recordCalcAndProcessStatus($periodNum, '业绩单生成失败,原因:' . $res['msg'] ?? '');
+            \Yii::$app->swooleAsyncTimer->pushAsyncResultToAdmin($params['handleUserId'], "第{$params['periodNum']}期生成业绩单更新失败,原因:" . $res['msg'] ?? '', false);
+            return false;
+        }
+    }
+
+    //自动执行
+    public static function actionAutoExec($taskKey): bool
+    {
+        $params    = Cache::getAsyncParams($taskKey);
+        $periodNum = $params['periodNum'] ?? 0;
+        $db        = 'dbCalc';
+        //生成业绩单 状态已修改
+        CalcRecord::record($periodNum, '开始生成业绩单');
+        $res = (new GeneratePerfOrder($periodNum))->start();
+        if (200 == $res['code']) {
+            CalcRecord::record($periodNum, '业绩单已生成');
+        } else {
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            CalcRecord::record($periodNum, '第' . $periodNum . '期业绩单生成失败,原因:' . $res['msg']);
+            return false;
+        }
+
+        //推送基础数据
+        CalcRecord::record($periodNum, '开始推送基础数据');
+        $res = (new PushBaseDataToCalc($periodNum))->start();
+        if (200 == $res['code']) {
+            CalcRecord::record($periodNum, '基础数据完成推送');
+        } else {
+            Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+            CalcRecord::record($periodNum, '第' . $periodNum . '期基础数据推送失败,原因:' . $res['msg']);
+            return false;
+        }
+        //todo 完成需要修改状态
+
+        //通知结算系统生成期业绩
+        CalcRecord::record($periodNum, '开始生成' . $periodNum . '期的期业绩');
+        \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['IS_PREPARE' => 1], 'PERIOD_NUM=' . $periodNum)->execute();
+        //todo 完成需要修改状态
+
+        //自动监听 拉取期业绩 并计算奖金
+//        (new PullPerfDataFromCalc($periodNum))->start();
+        //通知结算系统计算奖金
+//        \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['IS_PREPARE' => 3], 'PERIOD_NUM=' . $periodNum)->execute();
+
+        //自动监听 计算完成 自动拉取奖金结果并修改状态
+//        (new PullCalcBonusData($period['PERIOD_NUM']))->start();
+        return true;
+    }
+
+    public static function recordCalcAndProcessStatus($periodNum, $text)
+    {
+        Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
+        CalcRecord::record($periodNum, $text);
+        return true;
+    }
+
+    public function actionPushData($taskKey): bool
+    {
+        $params    = Cache::getAsyncParams($taskKey);
+        $periodNum = $params['periodNum'] ?? 0;
+        CalcRecord::record($periodNum, '开始推送基础数据');
+        //推送数据 状态已修改
+        $res = (new PushBaseDataToCalc($periodNum))->start();
+        if (200 == $res['code']) {
+            self::recordCalcAndProcessStatus($periodNum, '基础数据已全部推送');
+            return true;
+        } else {
+            self::recordCalcAndProcessStatus($periodNum, '基础数据推送失败,原因:' . $res['msg'] ?? '');
+            return false;
+        }
+    }
+
+    public function actionPullPeriod($taskKey): bool
+    {
+        $params    = Cache::getAsyncParams($taskKey);
+        $periodNum = $params['periodNum'] ?? 0;
+        CalcRecord::record($periodNum, '开始拉取期业绩数据');
+        //推送数据 状态已修改
+        $res = (new PullPerfDataFromCalc($periodNum))->start();
+        if (200 == $res['code']) {
+            self::recordCalcAndProcessStatus($periodNum, '期业绩数据已全部拉取');
+            return true;
+        } else {
+            self::recordCalcAndProcessStatus($periodNum, '期业绩数据拉取失败,原因:' . $res['msg'] ?? '');
+            return false;
+        }
+    }
+
+    public function actionPullBonus($taskKey): bool
+    {
+        $params    = Cache::getAsyncParams($taskKey);
+        $periodNum = $params['periodNum'] ?? 0;
+        CalcRecord::record($periodNum, '开始拉取奖金数据');
+        //推送数据 状态已修改
+        $res = (new PullCalcBonusData($periodNum))->start();
+        if (200 == $res['code']) {
+            self::recordCalcAndProcessStatus($periodNum, '奖金数据已全部拉取');
+            return true;
+        } else {
+            self::recordCalcAndProcessStatus($periodNum, '奖金数据拉取失败,原因:' . $res['msg'] ?? '');
+            return false;
+        }
+    }
+
+}