Browse Source

第一次

yingzi 1 year ago
commit
5f1245c96e
100 changed files with 15799 additions and 0 deletions
  1. 4 0
      .gitignore
  2. 1 0
      .htaccess
  3. 1 0
      .user.ini
  4. 7 0
      404.html
  5. BIN
      Jobs/.DS_Store
  6. 181 0
      Jobs/.ENV.php
  7. 71 0
      Jobs/Cache/AllTableNameCache.Class.php
  8. 220 0
      Jobs/Cache/CustomerCache.Class.php
  9. 45 0
      Jobs/Cache/FinanceCache.Class.php
  10. 395 0
      Jobs/Cache/OverviewCache.Class.php
  11. BIN
      Jobs/Config/.DS_Store
  12. 351 0
      Jobs/Config/Config.php
  13. 42 0
      Jobs/Controller/BaseController.Class.php
  14. 81 0
      Jobs/Controller/CAddJob.Class.php
  15. 14 0
      Jobs/Controller/CBase.Class.php
  16. 195 0
      Jobs/Controller/CESinit.Class.php
  17. 22 0
      Jobs/Controller/CJobObject.Class.php
  18. 38 0
      Jobs/Controller/CJobs.Class.php
  19. 647 0
      Jobs/Controller/Test.Class.php
  20. 309 0
      Jobs/Dao/BaseDao.Class.php
  21. 60 0
      Jobs/Dao/DCoupon.Class.php
  22. 49 0
      Jobs/Dao/DGoodsBasic.Class.php
  23. 35 0
      Jobs/Dao/DLog.Class.php
  24. 35 0
      Jobs/Dao/DLoginLog.Class.php
  25. 35 0
      Jobs/Dao/DPriceLog.Class.php
  26. 57 0
      Jobs/Dao/DShop.Class.php
  27. 58 0
      Jobs/Dao/DUserCoupon.Class.php
  28. 42 0
      Jobs/Dao/Finance/DPay.Class.php
  29. 42 0
      Jobs/Dao/Finance/DPayReceiptIndex.Class.php
  30. 42 0
      Jobs/Dao/Finance/DReceive.Class.php
  31. 41 0
      Jobs/Dao/Finance/DReceiveReceiptIndex.Class.php
  32. 40 0
      Jobs/Dao/Message/DMessage.Class.php
  33. 59 0
      Jobs/Dao/Order/DOrder.Class.php
  34. 41 0
      Jobs/Dao/Order/DOrderCoupon.Class.php
  35. 57 0
      Jobs/Dao/Order/DOrderGoods.Class.php
  36. 45 0
      Jobs/Dao/Order/DOrderIndex.Class.php
  37. 48 0
      Jobs/Dao/Order/DOrderReceive.Class.php
  38. 56 0
      Jobs/Dao/Purchase/DPurchase.Class.php
  39. 57 0
      Jobs/Dao/Purchase/DPurchaseOut.Class.php
  40. 50 0
      Jobs/Dao/Stock/DInventoryBatch.Class.php
  41. 53 0
      Jobs/Dao/Stock/DInventoryDetails.Class.php
  42. 58 0
      Jobs/Dao/Stock/DInventoryIn.Class.php
  43. 54 0
      Jobs/Dao/Stock/DInventoryInDetails.Class.php
  44. 60 0
      Jobs/Dao/Stock/DInventoryOut.Class.php
  45. 49 0
      Jobs/Dao/Stock/DWarehouse.Class.php
  46. 154 0
      Jobs/Model/MConsole.Class.php
  47. 114 0
      Jobs/Model/MJobs.Class.php
  48. 441 0
      Jobs/Model/MProcess.Class.php
  49. 25 0
      Jobs/Model/MQueue.Class.php
  50. 85 0
      Jobs/Model/MTopic/Enterprise/MEnterprise.Class.php
  51. 252 0
      Jobs/Model/MTopic/Finance/MPayable.Class.php
  52. 488 0
      Jobs/Model/MTopic/Finance/MReceivable.Class.php
  53. 46 0
      Jobs/Model/MTopic/MErp.Class.php
  54. 149 0
      Jobs/Model/MTopic/Market/MCoupon.Class.php
  55. 57 0
      Jobs/Model/MTopic/Market/MGrantCoupon.Class.php
  56. 55 0
      Jobs/Model/MTopic/Market/MOnlinePayCoupon.Class.php
  57. 52 0
      Jobs/Model/MTopic/Market/MRegisterCoupon.Class.php
  58. 95 0
      Jobs/Model/MTopic/Message/MMessage.Class.php
  59. 132 0
      Jobs/Model/MTopic/Order/MCommissionOrder.Class.php
  60. 33 0
      Jobs/Model/MTopic/Order/MDelayedCancelOrder.Class.php
  61. 125 0
      Jobs/Model/MTopic/Order/MOrder.Class.php
  62. 217 0
      Jobs/Model/MTopic/Order/MOrderStatistics.Class.php
  63. 246 0
      Jobs/Model/MTopic/Push/MPush.Class.php
  64. 107 0
      Jobs/Model/Middleware.Class.php
  65. 55 0
      Jobs/Model/Queue/MBaseTopicQueue.Class.php
  66. 114 0
      Jobs/Model/Queue/MRedisTopicQueue.Class.php
  67. 48 0
      Jobs/Model/Queue/MTopicQueueInterface.Class.php
  68. 33 0
      Jobs/Model/Test/K3Curl.Class.php
  69. 165 0
      Jobs/Model/Test/K3Handle.Class.php
  70. 54 0
      Jobs/Model/Test/K3login.Class.php
  71. BIN
      Jobs/Public/.DS_Store
  72. 25 0
      Jobs/Public/www/index.php
  73. 23 0
      Jobs/start.php
  74. 27 0
      Mall/Bootstrap.php
  75. 28 0
      Mall/Framework/Cache/AbstractStorage.Class.php
  76. 609 0
      Mall/Framework/Cache/Redis.Class.php
  77. 52 0
      Mall/Framework/Cache/StorageInterface.Class.php
  78. 1188 0
      Mall/Framework/Common/Functions.php
  79. 63 0
      Mall/Framework/Core/Aes.Class.php
  80. 53 0
      Mall/Framework/Core/AutoLoad.Class.php
  81. 202 0
      Mall/Framework/Core/BaseImg.Class.php
  82. 36 0
      Mall/Framework/Core/Cache.Class.php
  83. 139 0
      Mall/Framework/Core/Config.Class.php
  84. 75 0
      Mall/Framework/Core/Cookie.Class.php
  85. 33 0
      Mall/Framework/Core/Db.Class.php
  86. 96 0
      Mall/Framework/Core/ErrorCode.Class.php
  87. 414 0
      Mall/Framework/Core/File.Class.php
  88. 1831 0
      Mall/Framework/Core/FirePHP.Class.php
  89. 382 0
      Mall/Framework/Core/Jwt.Class.php
  90. 215 0
      Mall/Framework/Core/Logs.Class.php
  91. 105 0
      Mall/Framework/Core/RedisQueue.Class.php
  92. 407 0
      Mall/Framework/Core/Request.Class.php
  93. 93 0
      Mall/Framework/Core/ResultWrapper.Class.php
  94. 300 0
      Mall/Framework/Core/Search.Class.php
  95. 148 0
      Mall/Framework/Core/SendMail.Class.php
  96. 73 0
      Mall/Framework/Core/SendSms.Class.php
  97. 180 0
      Mall/Framework/Core/Session.Class.php
  98. 1070 0
      Mall/Framework/Core/SqlHelper.Class.php
  99. 710 0
      Mall/Framework/Core/StatusCode.Class.php
  100. 33 0
      Mall/Framework/Core/Swoole.Class.php

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+nbproject/
+log/
+Jobs/log/
+./Jobs/log/

+ 1 - 0
.htaccess

@@ -0,0 +1 @@
+ 

+ 1 - 0
.user.ini

@@ -0,0 +1 @@
+open_basedir=/www/wwwroot/queueqnys.liuniukj.com/:/tmp/

+ 7 - 0
404.html

@@ -0,0 +1,7 @@
+<html>
+<head><title>404 Not Found</title></head>
+<body>
+<center><h1>404 Not Found</h1></center>
+<hr><center>nginx</center>
+</body>
+</html>

BIN
Jobs/.DS_Store


+ 181 - 0
Jobs/.ENV.php

@@ -0,0 +1,181 @@
+<?php
+/**
+ * 基础常量
+ */
+// 项目根目录
+define('PROJECT_PATH', __DIR__);
+// 项目名称
+define('PROJECT_NAME', 'Jobs');
+// 项目域名
+define('PROJECT_DOMAIN', 'queueqnys');
+// 项目在权限系统编号
+define('PROJECT_ACL_NO', '3');
+// 默认控制器
+define('DEFAULT_CLASS_CONTROLLER_NAME', 'index');
+// 默认方法名
+define('DEFAULT_CLASS_ACTION_NAME', 'index');
+// 默认模块名
+define('DEFAULT_CLASS_MODULE_NAME', 'index');
+
+/**
+ * 域名
+ */
+// 根域名
+define('DOMAIN', 'mallguang.top');
+/* 域名后缀 */
+define('DOMAIN_SUFFIX', '.' . DOMAIN);
+// 项目域名
+define('DOMAIN_PROJECT', strtolower(PROJECT_NAME) . DOMAIN_SUFFIX);
+// 配置项目资源域名
+define('DOMAIN_PROJECT_IMG', strtolower(PROJECT_NAME). '.res' . DOMAIN_SUFFIX);
+// 上传附件资源域名
+define('DOMAIN_UPLOAD', 'upload' . DOMAIN_SUFFIX);
+
+/**
+ * URL
+ * 必须以 / 结尾
+ */
+// http协议
+define('AGREEMENT', 'http://');
+// https协议
+define('AGREEMENTSSL', 'https://');
+// 项目地址
+define('URL_PROJECT', AGREEMENT . DOMAIN_PROJECT . '/');
+// 项目资源地址
+define('URL_PROJECT_RES',  AGREEMENT . DOMAIN_PROJECT_IMG . '/');
+// 阿里云请求主域名
+define('URL_ALI_SERVICE', AGREEMENT . 'ali' . DOMAIN_SUFFIX);
+// 上传附件资源域名
+define('URL_UPLOAD', AGREEMENT . DOMAIN_UPLOAD . '/');
+// Mobile Web分享
+define('URL_SHARE_MOBILE', AGREEMENT . 'share' . DOMAIN_SUFFIX . '/');
+// android 下载地址
+define('URL_ANDROID_DOWNLOAD', 'http://a.app.qq.com/o/simple.jsp?pkgname=com.maoguang.catmall');
+// ios 下载地址
+define('URL_IOS_DOWNLOAD', 'http://www.mallguang.com');
+// 队列处理地址  dev队列 对应请求域名
+define('QIANNIAO_QUEUE', 'http://queueqnys.liuniukj.com');
+// 项目正式域名
+define('URL_API', 'https://apiqnys.liuniukj.com');
+// 项目开发环境域名
+define('DEV_URL_API', 'https://devapiqnys.liuniukj.com');
+
+/**
+ * Smarty
+ * 模板引擎配置
+ */
+// 模板存放目录
+define('SMARTY_TEMPLATE_DIR', PROJECT_PATH . DS . 'View' . DS . 'Templates');
+// 模板编译目录
+define('SMARTY_COMPILE_DIR', PROJECT_PATH . DS . 'Storage' . DS . 'Views');
+// 模板缓存目录
+define('SMARTY_CACHE_DIR', PROJECT_PATH . DS . 'Storage' . DS . 'Views');
+// 模板后缀
+define('SMARTY_TEMPLATE_TYPE', '.html');
+// 模板缓存是否开启
+define('SMARTY_IS_CACHE', FALSE);
+
+
+/**
+ * DB
+ * 数据库引擎配置
+ */
+// 数据库引擎名称,首字母大写
+define('DB_DIRVER', 'Pdo');
+// 数据库服务IP地址
+define('DB_HOST', '47.104.177.139');
+// 数据库服务端口号
+define('DB_PORT', '3306');
+// 数据库服务用户名
+define('DB_USERNAME', 'mallguang');
+// 数据库服务密码
+define('DB_PASSWORD', 'zxcvbnm');
+// 数据库名称
+define('DB_NAME', 'shop');
+// 数据库字符编码
+define('DB_CHARSET', 'utf-8');
+
+/**
+ * Cookie
+ */
+define('COOKIE_PREFIX', 'mallguang_');
+define('COOKIE_PATH', '/');
+define('COOKIE_DOMAIN', DOMAIN_SUFFIX);
+
+/**
+ * Mail
+ */
+/**
+ * 邮件发送方式
+ * 1.使用PHP的mail函数发送
+ * 2.通过 SOCKET 连接 SMTP 服务器发送(支持 ESMTP 验证, 推荐方法)
+ * 3.邮件发送方式1. 使用PHP的mail函数发送2.通过SOCKET 连接 SMTP 服务器发送(支持 ESMTP 验证, 推荐方法)
+ */
+define('MAIL_MAILER', 2);
+/**
+ * 分隔符
+ * 使用 CRLF 作为分隔符(常用, SMTP方式默认分割符)
+ * 使用 LF 作为分隔符(一些Unix主机使用mail函数时需用LF替代CRLF)
+ * 使用 CR 作为分隔符(通常为 Mac 主机, 不常用)
+ */
+define('MAIL_DELIMITER', 1);
+// 字符集
+define('MAIL_CHARSET', 'utf-8');
+// 发件人邮箱
+define('MAIL_FROM', 'qlxbaa@163.com');
+// 是否需要鉴权
+define('MAIL_SIGN', FALSE);
+// Smtp 服务地址
+define('MAIL_SMTP_HOST', 'smtp.163.com');
+// Smtp 服务端口
+define('MAIL_SMTP_PORT', 25);
+// Smtp 身份验证
+define('MAIL_SMTP_AUTH', 1);
+// Smtp 用户名
+define('MAIL_SMTP_USERNAME', 'qlxbaa@163.com');
+// Smtp 密码
+define('MAIL_SMTP_PASSWORD', '8879288aaa');
+// 内容是否Base64
+define('MAIL_CONTENT_BASE64', FALSE);
+
+/**
+ * Session
+ */
+// Session引擎
+define('SESSION_SAVE_HANDLER', 'redis');
+// 存放地址
+define('SESSION_SAVE_PATH',  'tcp://127.0.0.1:6379?auth=&databases=2&weight=1&timeout=1&prefix=BackendSession:');
+// 数据在服务器端储存有效期
+define('SESSION_GC_MAXLIFETIME', 3600);
+// 客户端的缓存方式,有nocache,private,private_no_expire,publice
+define('SESSION_CACHE_LIMITER', 'nocache');
+// cookie 值生存周期
+define('SESSION_CACHE_EXPIRE', 60);
+// cookie 域名
+define('SESSION_COOKIE_DOMAIN', DOMAIN_SUFFIX);
+// cookie 路径
+define('SESSION_COOKIE_PATH', '/');
+// cookie 数据存储有效期
+define('SESSION_COOKIE_LIFETIME', 0);
+
+/** 附加常量 **/
+
+/**
+ * 时间戳
+ */
+define('TIME', time());
+
+/**
+ * 站点加密SECRET
+ */
+define('SITE_SECRET', '884N7GpYjPM/}rmx,LCxGJP3si?D&4');
+
+/**
+ * Upload File Path
+ */
+define('UPLOAD_FILE_PATH',  PROJECT_PATH . DS.'Public/upload/file/');
+
+/**
+ * default member avatar path
+ */
+define('DEFAULT_MEMBER_AVATAR', URL_UPLOAD.'defaultavatar.png');

+ 71 - 0
Jobs/Cache/AllTableNameCache.Class.php

@@ -0,0 +1,71 @@
+<?php
+namespace Jobs\Cache;
+
+use Mall\Framework\Factory;
+
+class AllTableNameCache
+{
+    /**
+     * 所有表名缓存
+     *
+     * @var
+     */
+    static $allTableName = 'all_tablename';
+
+    /**
+     * 判断所有表名缓存是否存在
+     *
+     * @param string $databaseName 库名
+     *
+     * @return bool
+     */
+    static function allTableNameCacheIsExists($databaseName){
+        $result = Factory::cache('default')->has(self::$allTableName.'::'.$databaseName);
+        return $result;
+    }
+
+    /**
+     * 缓存制定库下的所有表名
+     *
+     * @param string $dataBaseName      当前数据库库名
+     * @param array  $allTableNameData  当前库中所有表名数据
+     */
+    static function allTableNameCache($databaseName, $allTableNameData)
+    {
+        if(empty($allTableNameData)){
+            return false;
+        }
+
+        $pipe =  Factory::cache('default')->multi();
+        foreach ($allTableNameData as $key => $value){
+            $writeCache = Factory::cache('default')->sadd(self::$allTableName.'::'.$databaseName, $value['TABLE_NAME']);
+        }
+        $pipe->exec();
+    }
+
+    /**
+     * 判断是否存在这张表
+     *
+     * @param string $dataBaseName    当前数据库库名
+     * @param string $tableName       表名
+     *
+     * @return bool
+     */
+    static function TableIsExists($databaseName, $tableName)
+    {
+        $result =  Factory::cache('default')->sismember(self::$allTableName.'::'.$databaseName, $tableName);
+        return $result;
+    }
+
+    /**
+     * 添加新表到表名缓存中
+     * @param string $dataBaseName    当前数据库库名
+     * @param string $tableName       表名
+     *
+     * @return bool
+     */
+    static function addNewTableName($databaseName, $tableName){
+        $writeCache = Factory::cache('default')->sadd(self::$allTableName.'::'.$databaseName, $tableName);
+        return $writeCache;
+    }
+}

+ 220 - 0
Jobs/Cache/CustomerCache.Class.php

@@ -0,0 +1,220 @@
+<?php
+/**
+ * 客户统计部分埋点
+ * Created by PhpStorm.
+ * User: wxj
+ * Date: 2019/11/14
+ * Time: 11:58
+ */
+
+namespace Jobs\Cache;
+
+use Mall\Framework\Core\ResultWrapper;
+use Mall\Framework\Factory;
+
+class CustomerCache
+{
+    private $cache;
+    protected $key = '';
+    protected $registerSmsCountKey = 'todayCreateOrderCustomer::'; //今日下单用户key
+    protected $customerExpire = 10;//过期时间为一天
+    protected $allCustomerNumKey = 'allCustomerNum';//全部客户数量key
+    protected $lastMonthNewCustomerKey = 'lastMonthNewCustomer';//新增客户数量:最近30天新增,且没有成功付款的客户 key
+    protected $interestCustomerKey = 'interestCustomer';//兴趣人群数量:近7天有加购行为,但没有成功付款的客户
+
+    public function __construct()
+    {
+        $this->key = $this->registerSmsCountKey . date('Ymd', time());
+        $this->cache = Factory::cache('user');
+    }
+
+    /***************************** 今日下单用户相关 start **********************************/
+
+    /**
+     * 下单时调用此方法,添加一条客户下单信息  todayCreateOrderCustomer::20190101:0001:0002:0003
+     * @param $enterpriseId
+     * @param $provinceCode
+     * @param $cityCode
+     * @param $districtCode
+     * @param $customerId
+     * @param $shopId
+     * @return bool|ResultWrapper
+     */
+    public function cacheCustomerOrderInfo($enterpriseId, $provinceCode = null, $cityCode = null, $districtCode = null, $customerId, $shopId = null)
+    {
+        if (empty($enterpriseId) || empty($customerId)) {
+            return false;
+        }
+        $this->key = $this->key . '::enterprise_' . $enterpriseId;
+        $this->cache->zincrby($this->key, 1, $customerId);//存全国
+        $this->cache->expire($this->key, $this->customerExpire);
+
+        if (!empty($provinceCode)) {
+            $key = $this->key . '::areaCode_' . $provinceCode;
+            $this->cache->zincrby($key, 1, $customerId);//存省
+            $this->cache->expire($key, $this->customerExpire);
+        }
+
+        if (!empty($cityCode)) {
+            $key = $this->key . '::areaCode_' . $cityCode;
+            $this->cache->zincrby($key, 1, $customerId);//存市
+            $this->cache->expire($key, $this->customerExpire);
+        }
+
+        if (!empty($districtCode)) {
+            $key = $this->key . '::areaCode_' . $districtCode;
+            $this->cache->zincrby($key, 1, $customerId);//存区
+            $this->cache->expire($key, $this->customerExpire);
+        }
+
+        if (!empty($shopId)) {
+            $key = $this->key . '::shopId_' . $shopId;
+            $this->cache->zincrby($key, 1, $customerId);
+            $this->cache->expire($key, $this->customerExpire);
+        }
+        return true;
+    }
+
+    /**
+     * 今日下单用户统计,按地区搜索
+     * @param $enterpriseId 企业id
+     * code不传则查的是全国的客户下单信息
+     * @param $code
+     * @param $shopId
+     * @return array
+     */
+    public function getTodayCustomerOrderInfo($enterpriseId, $code = null, $shopId = null)
+    {
+        $key = $this->key . '::enterprise_' . $enterpriseId;
+        if (!empty($code)) {
+            $key .= '::areaCode_' . $code;
+        }
+        if (!empty($shopId)) {
+            $key .= '::shopId_' . $shopId;
+        }
+        $result = $this->cache->zrange($key, 0, -1);
+        return $result ? $result : [];
+    }
+
+    /***************************** 今日下单用户相关 end **********************************/
+
+    /***************************** 全部客户数量相关 start **********************************/
+
+    /**
+     * 增加客户数量:每次客户注册调用此方法
+     * @param $enterpriseId
+     * @return bool
+     */
+    public function incrCustomerNum($enterpriseId)
+    {
+        $result = $this->cache->incr($this->allCustomerNumKey . '::' . $enterpriseId);
+        return $result ? true : false;
+    }
+
+    /**
+     * 获取当前客户总数
+     * @param $enterpriseId
+     * @return bool
+     */
+    public function getAllCustomerNum($enterpriseId)
+    {
+        return $this->cache->get($this->allCustomerNumKey . '::' . $enterpriseId);
+    }
+
+    /***************************** 全部客户数量相关 end **********************************/
+
+
+    /***************************** 新增客户数量相关 start **********************************/
+
+    /**
+     * 新增客户 customerId  score存时间戳   注册用户时调用此方法
+     * @param $customerId
+     * @param $enterpriseId
+     * @return bool
+     */
+    public function incrCustomer($customerId, $enterpriseId)
+    {
+        $result = $this->cache->zadd($this->lastMonthNewCustomerKey . '::' . $enterpriseId, time(), $customerId);
+        return $result ? true : false;
+    }
+
+    /**
+     * 删除所有企业注册超过30天的用户
+     * @return bool   true代表有数据被删除  false相反
+     */
+    public function delCustomerOfOneMonthAgo()
+    {
+        //获取所有key  ['lastMonthNewCustomer::12','lastMonthNewCustomer::11']
+        $keysList = $this->cache->keys($this->lastMonthNewCustomerKey . '*');
+        $oneMonthAgoTimestamp = strtotime("-30 day");
+        foreach ($keysList as $value) {
+            $keyArr = explode('::', $value);
+            $result = $this->cache->zremrangebyscore($this->lastMonthNewCustomerKey . '::' . $keyArr[2], 0, $oneMonthAgoTimestamp);
+            if (!$result) {
+                return false;//没有数据可删除也返回false
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 客户下单后删除客户信息
+     * @param $customerId
+     * @param $enterpriseId
+     * @return bool
+     */
+    public function delCustomerAfterPlaceOrder($customerId, $enterpriseId)
+    {
+        $result = $this->cache->zrem($this->lastMonthNewCustomerKey . '::' . $enterpriseId, $customerId);
+        return $result ? true : false;
+    }
+    /***************************** 新增客户数量相关 end **********************************/
+
+    /***************************** 兴趣人群数量 : 近7天加购行为 start **********************************/
+    /**
+     * 加入购物车 customerId  score存时间戳   注册用户时调用此方法
+     * @param $customerId
+     * @param $enterpriseId
+     * @return bool
+     */
+    public function incrInterestCustomer($customerId, $enterpriseId)
+    {
+        $result = $this->cache->zadd($this->interestCustomerKey . '::' . $enterpriseId, time(), $customerId);
+        return $result ? true : false;
+    }
+
+    /**
+     * 删除所有企业加购超过7天的用户
+     * @return bool   true代表有数据被删除  false相反
+     */
+    public function delInterestCustomerOfSevenDaysAgo()
+    {
+        //获取所有key  ['lastMonthNewCustomer::12','lastMonthNewCustomer::11']
+        $keysList = $this->cache->keys($this->interestCustomerKey . '*');
+        $SeveralDaysAgoTimestamp = strtotime("-7 day");
+        foreach ($keysList as $value) {
+            $keyArr = explode('::', $value);
+            $result = $this->cache->zremrangebyscore($this->interestCustomerKey . '::' . $keyArr[2], 0, $SeveralDaysAgoTimestamp);
+            if (!$result) {
+                return false;//没有数据可删除也返回false
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 客户成功付款后删除信息
+     * @param $customerId
+     * @param $enterpriseId
+     * @return bool
+     */
+    public function delInterestCustomerAfterPay($customerId, $enterpriseId)
+    {
+        $result = $this->cache->zrem($this->interestCustomerKey . '::' . $enterpriseId, $customerId);
+        return $result ? true : false;
+    }
+
+    /***************************** 近7天加购行为 end **********************************/
+
+
+}

+ 45 - 0
Jobs/Cache/FinanceCache.Class.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace Jobs\Cache;
+
+use http\Exception;
+use Mall\Framework\Factory;
+use Mall\Framework\Core\ResultWrapper;
+use Mall\Framework\Core\ErrorCode;
+
+class FinanceCache
+{
+    private $cache;
+    protected $salesOutReceiveKey = 'salesOutReceive';//销售出库生成应收,缓存应收单的id
+    private $existPayEnterpriseKey = 'existPayEnterprise'; // 存在应付单的企业标识
+
+    public function __construct()
+    {
+        $this->cache = Factory::cache('finance');
+    }
+
+    /**
+     * 销售出库生成应收单,应收单自动审核  缓存应收单id和时间
+     * @param $enterpriseId
+     * @param $id
+     * @param $createTime
+     * @return array|mixed
+     */
+    public function saveSalesOutReceive($enterpriseId, $id, $createTime)
+    {
+        $this->cache->zadd($this->salesOutReceiveKey.'::'.$enterpriseId, $createTime, $id);
+    }
+
+    public function delSalesOutReceive($enterpriseId, $id)
+    {
+        $this->cache->zrem($this->salesOutReceiveKey.'::'.$enterpriseId, $id);
+    }
+
+    /**
+     * 缓存一下那个企业有应付单生成,便于计划任务进行自动切换企业,做自动审核
+     */
+    public function cacheExistPayEnterprise($enterpriseId)
+    {
+        return $this->cache->sadd($this->existPayEnterpriseKey, $enterpriseId);
+    }
+}

+ 395 - 0
Jobs/Cache/OverviewCache.Class.php

@@ -0,0 +1,395 @@
+<?php
+/**
+ * 首页概况缓存
+ * Created by PhpStorm.
+ * User: wxj
+ * Date: 2019/11/5
+ * Time: 10:14
+ */
+
+namespace Jobs\Cache;
+
+use http\Exception;
+use Mall\Framework\Factory;
+use Mall\Framework\Core\ResultWrapper;
+use Mall\Framework\Core\ErrorCode;
+
+class OverviewCache
+{
+    private $cache;
+    private $expireTime = 86400;
+    private $orderDay = 7;//近7天订单趋势
+    protected $aggregateStatisticsKey = 'aggregateStatistics';//总数统计 区分企业
+    protected $businessOverviewEnterpriseKey = 'businessOverviewEnterprise';//经营概况_日期_企业Id_(全店)
+    protected $businessOverviewEnterpriseShopKey = 'businessOverviewEnterpriseShop';//经营概况_日期_企业Id_商铺id
+    protected $rankingEnterpriseKey = 'rankingEnterprise';//销量排行榜_日期_企业Id_(全店)
+    protected $rankingEnterpriseShopKey = 'rankingEnterpriseShop';//销量排行榜_日期_企业Id_商铺id
+    protected $salesMoneyRankingEnterpriseKey = 'salesMoneyRankingEnterprise';//销额排行榜_日期_企业Id_(全店)
+    protected $salesMoneyRankingEnterpriseShopKey = 'salesMoneyRankingEnterpriseShop';//销额排行榜_日期_企业Id_商铺id
+    protected $orderTrendKey = 'orderTrend';//近7天订单趋势_企业Id
+
+    public function __construct()
+    {
+        $this->cache = Factory::cache('finance');
+    }
+
+    /**
+     * 总数统计
+     * @param $enterpriseId
+     * @param string $key
+     * @param $change_num
+     * @return ResultWrapper
+     */
+    public function saveAggregateStatistics($enterpriseId, $key = 'totalShouldReceive', $change_num)
+    {
+        if (empty($key)) {
+            return ResultWrapper::fail('要缓存的数据为空', ErrorCode::$paramError);
+        }
+
+        //获取Key的过期时间,大于0的话就不再设置了
+        $Key = $this->aggregateStatisticsKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        $ttl = $this->cache->ttl($Key);
+
+        $old = self::getAggregateStatistics($enterpriseId,$key);
+        $this->cache->hset($Key , $key, $change_num + $old);
+        //如果键没有设置,那么设置过期时间
+        if ($ttl == -2) {
+            $this->cache->expire($Key, $this->expireTime);
+        }
+    }
+
+    /**
+     * 获取总数统计
+     * @param $enterpriseId
+     * @param $key
+     * @return array
+     */
+    public function getAggregateStatistics($enterpriseId, $key = 'totalShouldReceive')
+    {
+        $Key = $this->aggregateStatisticsKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        $result = $this->cache->hget($Key, $key);
+        return $result ? $result : 0;
+    }
+
+    /**
+     * 删除总数统计
+     * @param $enterpriseId
+     * @param $key
+     * @return array
+     */
+    public function delAggregateStatistics($enterpriseId, $key = 'totalShouldReceive')
+    {
+        $Key = $this->aggregateStatisticsKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        return $this->cache->hdel($Key, $key);
+    }
+
+    /**************************经营概况 start**********************************/
+    /**
+     * 经营概况
+     * @param $enterpriseId
+     * @param string $key
+     * @param $change_num
+     * @param null $shopId
+     * @return ResultWrapper
+     */
+    public function saveBusinessOverview($enterpriseId, $key = 'orderTotalMoney', $change_num, $shopId = null)
+    {
+        if (empty($key)) {
+            return ResultWrapper::fail('要缓存的数据为空', ErrorCode::$paramError);
+        }
+
+        //获取Key的过期时间,大于0的话就不再设置了
+        $Key = $this->businessOverviewEnterpriseKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->businessOverviewEnterpriseShopKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId . '::ShopId_' . $shopId;
+        }
+
+        $ttl = $this->cache->ttl($Key);
+
+        $old = self::getBusinessOverview($enterpriseId, $key, $shopId);
+        $this->cache->hset($Key, $key, $old + $change_num);
+
+        //如果键没有设置,那么设置过期时间
+        if ($ttl == -2) {
+            $this->cache->expire($Key, $this->expireTime);
+        }
+    }
+
+    /**
+     * 获取经营概况数字
+     * @param $enterpriseId
+     * @param string $key
+     * @param null $shopId
+     * @return ResultWrapper
+     */
+    public function getBusinessOverview($enterpriseId, $key = 'totalShouldReceive', $shopId = null)
+    {
+        if (empty($key)) {
+            return ResultWrapper::fail('要获取的key不存在', ErrorCode::$paramError);
+        }
+
+        //获取Key的过期时间,大于0的话就不再设置了
+        $Key = $this->businessOverviewEnterpriseKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->businessOverviewEnterpriseShopKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId . '::ShopId_' . $shopId;
+        }
+
+        $result = $this->cache->hget($Key, $key);
+        return $result ? $result : 0;
+    }
+
+    /**
+     * 删除经营概况统计
+     * @param $enterpriseId
+     * @param string $key
+     * @param null $shopId
+     * @return mixed
+     */
+    public function delBusinessOverview($enterpriseId, $key = 'totalShouldReceive', $shopId = null)
+    {
+        //获取Key的过期时间,大于0的话就不再设置了
+        $Key = $this->businessOverviewEnterpriseKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->businessOverviewEnterpriseShopKey . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId . '::ShopId_' . $shopId;
+        }
+        return $this->cache->hdel($Key, $key);
+    }
+    /**************************经营概况 end**********************************/
+
+    /**************************销量排行榜 start**********************************/
+
+    /**
+     * 销量排行榜
+     * @param $enterpriseId
+     * @param string $suffix
+     * @param string $id
+     * @param $change_num
+     * @param null $shopId
+     * @return ResultWrapper
+     */
+    public function saveRanking($enterpriseId, $suffix = 'categoryRanking', $id, $change_num, $shopId = null)
+    {
+        if (empty($suffix) || empty($id)) {
+            return ResultWrapper::fail('要缓存的数据为空', ErrorCode::$paramError);
+        }
+
+        //获取Key的过期时间,大于0的话就不再设置了
+        $Key = $this->rankingEnterpriseKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->rankingEnterpriseShopKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId.'::ShopId_'. $shopId;
+        }
+
+        $ttl = $this->cache->ttl($Key);
+
+        $old = $this->cache->zscore($Key, $id);
+        $old = $old ? $old : 0;
+        $change_num = bcmul($change_num,100);
+        $this->cache->zAdd($Key, bcadd($old,$change_num), $id);
+
+        //如果键没有设置,那么设置过期时间
+        if ($ttl == -2) {
+            $this->cache->expire($Key, $this->expireTime);
+        }
+    }
+
+    /**
+     * 获取排行榜,按score从大到小排行
+     * @param $enterpriseId
+     * @param string $suffix
+     * @param $id
+     * @param $change_num
+     * @param null $shopId
+     * @return ResultWrapper
+     */
+    public function getRanking($enterpriseId, $suffix = 'categoryRanking', $shopId = null)
+    {
+        if (empty($suffix)) {
+            return ResultWrapper::fail('要缓存的数据为空', ErrorCode::$paramError);
+        }
+
+        //获取Key的过期时间,大于0的话就不再设置了
+        $Key = $this->rankingEnterpriseKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->rankingEnterpriseShopKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId.'::ShopId_'. $shopId;
+        }
+
+        $result = $this->cache->zRevRange($Key, 0, -1, true);
+        if(!empty($result)) {
+            foreach ($result as $key=>$value) {
+                if ($value) {
+                    $result[$key] = bcdiv($value,100,2);
+                }
+            }
+        }
+        return $result ? $result : [];
+    }
+
+    /**
+     * 删除排行榜 key
+     * @param $enterpriseId
+     * @param string $suffix
+     * @param null $shopId
+     * @return mixed
+     */
+    public function delRanking($enterpriseId, $suffix = 'categoryRanking', $shopId = null)
+    {
+        $Key = $this->rankingEnterpriseKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->rankingEnterpriseShopKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId.'::ShopId_'. $shopId;
+        }
+        return $this->cache->del($Key);
+    }
+
+    /**************************销量排行榜 end**********************************/
+
+    /**************************销额排行榜 start**********************************/
+
+    /**
+     * 销额排行榜
+     * @param $enterpriseId
+     * @param string $suffix
+     * @param string $id
+     * @param $change_num
+     * @param null $shopId
+     * @return ResultWrapper
+     */
+    public function saveSalesMoneyRanking($enterpriseId, $suffix = 'categoryRanking', $id, $change_num, $shopId = null)
+    {
+        if (empty($suffix) || empty($id)) {
+            return ResultWrapper::fail('要缓存的数据为空', ErrorCode::$paramError);
+        }
+
+        //获取Key的过期时间,大于0的话就不再设置了
+        $Key = $this->salesMoneyRankingEnterpriseKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->salesMoneyRankingEnterpriseShopKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId.'::ShopId_'. $shopId;
+        }
+
+        $ttl = $this->cache->ttl($Key);
+
+        $old = $this->cache->zscore($Key, $id);
+        $old = $old ? $old : 0;
+        $change_num = bcmul($change_num,100);
+        $this->cache->zAdd($Key, bcadd($old, $change_num), $id);
+
+        //如果键没有设置,那么设置过期时间
+        if ($ttl == -2) {
+            $this->cache->expire($Key, $this->expireTime);
+        }
+    }
+
+    /**
+     * 获取排行榜,按score从大到小排行
+     * @param $enterpriseId
+     * @param string $suffix
+     * @param $id
+     * @param $change_num
+     * @param null $shopId
+     * @return ResultWrapper
+     */
+    public function getSalesMoneyRanking($enterpriseId, $suffix = 'categoryRanking', $shopId = null)
+    {
+        if (empty($suffix)) {
+            return ResultWrapper::fail('要缓存的数据为空', ErrorCode::$paramError);
+        }
+
+        //获取Key的过期时间,大于0的话就不再设置了
+        $Key = $this->salesMoneyRankingEnterpriseKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->salesMoneyRankingEnterpriseShopKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId.'::ShopId_'. $shopId;
+        }
+
+        $result = $this->cache->zRevRange($Key, 0, -1, true);
+        if(!empty($result)) {
+            foreach ($result as $key=>$value) {
+                if ($value) {
+                    $result[$key] = bcdiv($value,100,2);
+                }
+            }
+        }
+        return $result ? $result : [];
+    }
+
+    /**
+     * 删除排行榜 key
+     * @param $enterpriseId
+     * @param string $suffix
+     * @param null $shopId
+     * @return mixed
+     */
+    public function delSalesMoneyRanking($enterpriseId, $suffix = 'categoryRanking', $shopId = null)
+    {
+        $Key = $this->salesMoneyRankingEnterpriseKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId;
+        if (!empty($shopId)) {
+            $Key = $this->salesMoneyRankingEnterpriseShopKey . '::' . $suffix . '::' . date('Y-m-d') . '::EnterpriseId_' . $enterpriseId.'::ShopId_'. $shopId;
+        }
+        return $this->cache->del($Key);
+    }
+
+    /**************************销额排行榜 end**********************************/
+
+
+    /**************************近7天订单趋势 start**********************************/
+    /**
+     * 近7天订单趋势   订单审核后调用此方法
+     * @param $enterpriseId
+     * @param $orderMoney
+     * @param $goodsNum 商品数量
+     * @return void
+     */
+    public function saveOrderTrend($enterpriseId, $orderMoney, $goodsNum)
+    {
+        $Key = $this->orderTrendKey. '::EnterpriseId_' . $enterpriseId;
+
+        //删除前30天的值
+        $keys = $this->cache->hkeys($Key);
+        if(!empty($keys)) {
+            foreach ($keys as $k) {
+                if(strtotime($k) <= strtotime('-30 days')) {
+                    $this->cache->hdel($Key, $k);
+                }
+            }
+        }
+
+        //获取今天的值
+        $oldValue = $this->cache->hget($Key, date('Y-m-d'));
+        if(!empty($oldValue)) {
+            $oldValue = json_decode($oldValue, true);
+        }
+        $newValue = [
+            'orderMoney'=> $orderMoney + (isset($oldValue['orderMoney']) ? $oldValue['orderMoney'] : 0),
+            'goodsNum'=> $goodsNum + (isset($oldValue['goodsNum']) ? $oldValue['goodsNum'] : 0),
+            'orderNum'=> 1 + (isset($oldValue['orderNum']) ? $oldValue['orderNum'] : 0),
+        ];
+        $this->cache->hset($Key, date('Y-m-d'), json_encode($newValue));
+    }
+
+    /**
+     * 获取近7天订单趋势
+     * @param $enterpriseId
+     * @return ResultWrapper
+     */
+    public function getOrderTrend($enterpriseId)
+    {
+        $Key = $this->orderTrendKey. '::EnterpriseId_' . $enterpriseId;
+        $result = [];
+        for($i=6; $i>=0; $i--) {
+            $date = date('Y-m-d',strtotime("-$i days"));
+            $value = $this->cache->hget($Key, $date);
+
+            $value = empty($value) ? [] : json_decode($value, true);
+
+            $result[] = [
+                'date'       => $date,
+                'orderMoney' => isset($value['orderMoney']) ? $value['orderMoney'] : 0,
+                'goodsNum'   => isset($value['goodsNum']) ? $value['goodsNum'] : 0,
+                'orderNum'   => isset($value['orderNum']) ? $value['orderNum'] : 0,
+            ];
+        }
+
+        return $result;
+    }
+
+    /**************************近7天订单趋势 end**********************************/
+
+}

BIN
Jobs/Config/.DS_Store


+ 351 - 0
Jobs/Config/Config.php

@@ -0,0 +1,351 @@
+<?php
+return [
+    // 内置项目配置
+    'app_name'          => PROJECT_NAME,
+    'app_path'          => PROJECT_PATH,
+
+    // 自定义项目配置
+    //'logPath'           => PROJECT_PATH . '/log', // 项目日志目录
+    'logPath'           => PROJECT_PATH . '/log/crontab/'.date("Ymd"), // 项目日志目录
+    'logSaveFileApp'    => 'application.log', // 默认日志文件名称
+    'logSaveFileWorker' => 'crontab.log', // 进程启动相关log存储名字
+    'pidPath'           => PROJECT_PATH . '/log',
+    'sleep'             => 2, // 队列没消息时,暂停秒数
+    'queueMaxNum'       => 10, // 队列达到一定长度,启动动态子进程个数发和送消息提醒
+    'excuteTime'        => 3600, // 子进程最长执行时间,防止内存泄漏
+    'queueTickTimer'    => 1000 * 10, //一定时间间隔(毫秒)检查队列长度;默认10秒钟
+    'messageTickTimer'  => 1000 * 180, //一定时间间隔(毫秒)发送消息提醒;默认3分钟
+    'processName'       => ':swooleTopicQueue', // 设置进程名, 方便管理, 默认值 swooleTopicQueue
+
+
+    //job任务相关
+    'job'               => [
+        'topics' => [
+            ['name' => 'MyJob', 'workerMinNum' => 1, 'workerMaxNum' => 5],
+            //['name'=> 'MyJob2', 'workerMinNum'=>1, 'workerMaxNum'=>5],
+            //['name'=> 'MyJob3', 'workerMinNum'=>1, 'workerMaxNum'=>5],
+        ],
+        // redis
+        'queue'  => [
+            'class'    => 'Jobs\Model\Queue\MRedisTopicQueue',
+            'host'     => '127.0.0.1',
+            'port'     => 6379,
+            'password' => '',
+            'database' => 3,
+        ],
+
+        // rabbitmq
+        // 'queue'   => [
+        //     'class'         => '\Kcloze\Jobs\Queue\RabbitmqTopicQueue',
+        //     'host'          => '192.168.9.24',
+        //     'user'          => 'phpadmin',
+        //     'pass'          => 'phpadmin',
+        //     'port'          => '5671',
+        //     'vhost'         => 'php',
+        //     'exchange'      => 'php.amqp.ext',
+        // ],
+    ],
+
+    //框架类型及装载类
+    'framework'         => [
+        //可以自定义,但是该类必须继承\Kcloze\Jobs\Action\BaseAction
+        'class' => 'Jobs\Controller\CJobs',
+    ],
+
+    'message' => [
+        'class' => '\Kcloze\Jobs\Message\DingMessage',
+        'token' => '***your-dingding-token***',
+    ],
+
+    'db' => [
+        'default' => [
+            'driver' => 'mysql',
+            'host' => '127.0.0.1',
+            'port' => '3306',
+            'username' => 'root',
+            'password' => '0d6047adbbae53c7',
+            'dbname' => 'qnys_devanfei',
+            'prefix' => 'qianniao',
+            'pconnect' => 0,
+            'charset' => 'utf8',
+        ],
+        'stock' => [
+            'driver' => 'mysql',
+            'host' => '127.0.0.1',
+            'port' => '3306',
+            'username' => 'root',
+            'password' => '0d6047adbbae53c7',
+            'dbname' => 'qnys_devstock',
+            'prefix' => 'qianniao',
+            'pconnect' => 0,
+            'charset' => 'utf8',
+        ],
+        'price'   => [
+            'driver'   => 'mysql',
+            'host'     => '127.0.0.1',
+            'port'     => '3306',
+             'username' => 'root',
+            'password' => '0d6047adbbae53c7',
+            'dbname'   => 'qnys_devprice',
+            'prefix'   => 'qianniao',
+            'pconnect' => 0,
+            'charset'  => 'utf8',
+        ],
+        'log' => [
+            'driver' => 'mysql',
+            'host' => '127.0.0.1',
+            'port' => '3306',
+             'username' => 'root',
+            'password' => '0d6047adbbae53c7',
+            'dbname' => 'qnys_devlog',
+            'prefix' => 'qianniao',
+            'pconnect' => 0,
+            'charset' => 'utf8',
+        ],
+        'finance' => [
+            'driver' => 'mysql',
+            'host' => '127.0.0.1',
+            'port' => '3306',
+            'username' => 'root',
+            'password' => '0d6047adbbae53c7',
+            'dbname' => 'qnys_devfinan',
+            'prefix' => 'qianniao',
+            'pconnect' => 0,
+            'charset' => 'utf8',
+        ],
+    ],
+    'cache' => [
+        'default' => [
+            'storage' => 'redis',
+            'host' => '127.0.0.1',
+            'port' => '6379',
+            'auth' => '',
+            'prefix' => 'new_',
+            'database' => '0',
+        ],
+        'mapping' => [
+            'storage' => 'redis',
+            'host' => '127.0.0.1',
+            'port' => '6379',
+            'auth' => '',
+            'prefix' => 'new_',
+            'database' => '1',//用于缓存地区
+        ],
+        'user' => [
+            'storage' => 'redis',
+            'host' => '127.0.0.1',
+            'port' => '6379',
+            'auth' => '',
+            'prefix' => 'new_',
+            'database' => '2',
+        ],
+        'finance'    => [
+            'storage'  => 'redis',
+            'host'     => '127.0.0.1',
+            'port'     => '6379',
+            'auth'     => '',
+            'prefix'   => 'new_',
+            'database' => '3',
+        ],
+        'activity'  => [
+            'storage'  => 'redis',
+            'host'     => '127.0.0.1',
+            'port'     => '6379',
+            'auth'     => '',
+            'prefix'   => 'new_',
+            'database' => '5',
+        ],//活动
+    ],
+
+
+    'dataResources' => [
+        'ThirdPartyService' => [
+            'host'     => '',
+            'port'     => '9508',
+            'time_out' => -1,
+        ],
+    ],
+
+    'search' => [
+        'goods_search' =>[
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_goods_search',
+            'type' => 'goods',
+        ],
+        'customer_search' =>[
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_customer_search',
+            'type' => 'customer',
+        ],
+        'goods_basic_search' =>[
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_goodsbasic_search',
+            'type' => 'goodsBasic',
+        ],
+        'shop_search' =>[
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_shop_search',
+            'type' => 'shop',
+        ],
+        'order_search' =>[
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_order_search',
+            'type' => 'order',
+        ],
+        'priceAdjustmentSheet_search' => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'online_priceadjustmentsheet_search',
+            'type'     => 'priceAdjustmentSheet',
+        ],
+        'customerPriceAdjustmentSheet_search' => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'online_customer_price_adjustment_sheet_search',
+            'type'     => 'customerPriceAdjustmentSheet',
+        ],
+        'inventory_details_search' => [
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_inventory_details_search',
+            'type' => 'inventory_details',
+        ],
+        'purchase_details_search' => [
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_purchase_details_search',
+            'type' => 'purchase_details',
+        ],
+        'order_out_search' => [
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_order_out_search',
+            'type' => 'order_out',
+        ],
+        'inventory_out_search' => [
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_inventory_out_search',
+            'type' => 'inventory_out',
+        ],
+        'inventory_in_search' => [
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_inventory_in_search',
+            'type' => 'inventory_in',
+        ],
+        'inventory_batch_search' => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'online_inventory_batch_search',
+            'type'     => 'inventory_batch',
+        ],
+        'should_receive_receipt_search' => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'dev_should_receive_receipt_search',
+            'type'     => 'should_receive_receipt',
+        ],
+        'should_pay_receipt_search' => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'dev_should_pay_receipt_search',
+            'type'     => 'should_pay_receipt',
+        ],
+        'activity_search' => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'online_activity_search',
+            'type'     => 'activity',
+        ],
+        'login_log' =>[
+            'protocol' => 'http',
+            'servers' => [
+                '127.0.0.1:80'
+            ],
+            'timeout' => null,
+            'index' => 'online_login_log',
+            'type' => 'login',
+        ],
+        'logs' => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'dev_logs',
+            'type'     => 'logs',
+        ],
+        'received_receipt_search'  => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'online_received_receipt_search',
+            'type'     => 'received_receipt',
+        ],
+        'user_coupon_search' => [
+            'protocol' => 'http',
+            'servers'  => [
+                '127.0.0.1:80'
+            ],
+            'timeout'  => null,
+            'index'    => 'online_user_coupon_search',
+            'type'     => 'user_coupon',
+        ],
+    ]
+];

+ 42 - 0
Jobs/Controller/BaseController.Class.php

@@ -0,0 +1,42 @@
+<?php
+namespace Jobs\Controller;
+
+use Mall\Framework\Factory;
+
+class BaseController
+{
+
+    /** @var  \Mall\Framework\Core\Request $request */
+    protected $request;
+
+    /** @var  \Mall\Framework\Core\Cache $cache */
+    protected $cache;
+
+    public function __construct($isCheckAcl = true)
+    {
+        $this->request = Factory::request();
+        //$this->cache = Factory::cache();
+    }
+
+
+    public function sendOutput($data, $errorcode = 0)
+    {
+        $errorcode = intval($errorcode);
+
+        if ($errorcode) {
+            $sendMessage = [
+                'state' => false,
+                'data' => $data,
+                'errorcode' => $errorcode
+            ];
+        } else {
+            $sendMessage = [
+                'state' => true,
+                'data' => $data,
+                'errorcode' => $errorcode
+            ];
+        }
+        exit(json_encode($sendMessage, JSON_UNESCAPED_UNICODE));
+    }
+
+}

+ 81 - 0
Jobs/Controller/CAddJob.Class.php

@@ -0,0 +1,81 @@
+<?php
+namespace Jobs\Controller;
+
+use Mall\Framework\Factory;
+use Mall\Framework\Core\ErrorCode;
+
+use Jobs\Controller\CJobObject;
+use Jobs\Controller\BaseController;
+use Jobs\Model\MQueue;
+use think\Error;
+
+class CAddJob extends BaseController
+{
+    private $objMQueue;
+
+    public function __construct()
+    {
+        parent::__construct();
+
+        $config = Factory::config()->all();
+        $this->config      = $config;
+        $this->logger      = Factory::logs($this->config['logPath'] ?:'', $this->config['logSaveFileApp'] ?:'', $this->config['app_name'] ?:'');
+
+        $this->objMQueue = MQueue::getQueue($this->config['job']['queue'], $this->logger);
+        if(!is_object($this->objMQueue)){
+            exit('初始化队列失败');
+        }
+        $this->objMQueue->setTopics($this->config['job']['topics']);
+    }
+
+    /**
+     * 统一的添加队列任务入口
+     */
+    public function add()
+    {
+
+        /*$params['topicName'] = 'MyJob';                       // 推送到那个队列主题中
+        $params['topicClass'] = 'Jobs\Model\MTopic\Log\MLog'; // 往队列中添加得任务脚本类文件
+        $params['topicMethon'] = 'push';                     // 需要触发的类文件中的入口函数
+        $params['topicMethonParams'] = [
+            'userCenterId' => 1,
+            'enterpriseId' => 15,
+            'createTime'   => time(),//操作时间
+            'no' => 'Y12455f4545', // 来源:
+            'actionType'=> '新增订单', // 操作类型:登录/注册
+            'operationData' => [
+                "goodsId"=>12,
+                "name"=>"商品名称"
+            ], //本次操作请求的数据(可空) json格式
+        ];    // 携带过去的参数*/
+
+        //接收参数
+        $params['topicName'] = $this->request->param('topicName');
+        $params['topicClass'] = $this->request->param('topicClass');
+        $params['topicMethon'] = $this->request->param('topicMethon');
+        $params['topicMethonParams'] = $this->request->param('topicMethonParams');
+
+        foreach ($params as $key => $value){
+            if(empty($value)){
+                parent::sendOutput('参数'.$key.'值为空', ErrorCode::$paramError);
+            }
+        }
+
+        $jobExtras = [                              // 指定在队列中的执行优先级(redis版本队列不支持,带上就行)
+            'delay' => mt_rand(0, 100) * 1000,
+            'priority' => 1,
+        ];
+
+        if(!is_array($params['topicMethonParams'])){
+            $params['topicMethonParams'] = json_decode(htmlspecialchars_decode($params['topicMethonParams'], ENT_QUOTES), true);
+        }
+
+        $job    = new CJobObject($params['topicName'], $params['topicClass'], $params['topicMethon'], $params['topicMethonParams'], $jobExtras);
+        $result = $this->objMQueue->push($params['topicName'], $job);
+        if($result) {
+            parent::sendOutput('添加队列成功');
+        }
+        parent::sendOutput('添加队列失败',ErrorCode::$redisWriteError);
+    }
+
+}

+ 14 - 0
Jobs/Controller/CBase.Class.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace Jobs\Controller;
+
+abstract class CBase
+{
+    public function init()
+    {
+    }
+
+    public function start($JobObject)
+    {
+    }
+}

+ 195 - 0
Jobs/Controller/CESinit.Class.php

@@ -0,0 +1,195 @@
+<?php
+/**
+ * ElasticSearch 初始化脚本
+ * Created by PhpStorm.
+ * User: phperstar
+ * Date: 2019/2/23
+ * Time: 2:04 PM
+ */
+
+namespace Jobs\Controller;
+
+use Mall\Framework\Factory;
+use Mall\Framework\Core\Request;
+
+class CESinit
+{
+    /**
+     * Search Demo
+     */
+    public function search()
+    {
+        $data = Request::params();
+        $c = $data['b'] ?: 1;
+
+        switch ($c) {
+            // 添加wms日志索引库
+            case 1 :
+                $mappings = array(
+                    'mappings' => array(
+                        "_default_" => [
+                            "properties" => [
+                                'userId' => ['type' => 'integer'],
+                                "userName" => [
+                                    "type" => "text",
+                                    "store" => false,
+                                    "analyzer" => "ik_max_word",
+                                    "search_analyzer" => "ik_max_word",
+                                    "boost" => 10
+                                ],
+                                'object' => ['type' => 'keyword'],
+                                'actionType' => ['type' => 'keyword'],
+                                "logContent" => [
+                                    "type" => "text",
+                                    "store" => false,
+                                    "analyzer" => "ik_max_word",
+                                    "search_analyzer" => "ik_max_word",
+                                    "boost" => 5
+                                ],
+                                'time' => ['type' => 'integer'],
+                            ]
+                        ]
+                    )
+                );
+
+                $searchServer = Factory::search('wms');
+                $result = $searchServer->createBase('dev_wms_operation_log', $mappings);
+                if ($result && !isset($result['error'])) {
+                    echo 'dev_wms_operation_log init done.';
+                    return true;
+                } else {
+                    print_r($result['error']);
+                    return false;
+                }
+
+                break;
+            // 删除wms索引库
+            case 2:
+                $searchServer = Factory::search('wms');
+                $searchServer->setIndex('dev_wms_operation_log');
+                $a = $searchServer->deleteBase('dev_wms_operation_log');
+                print_r($a);
+                break;
+            // 添加oms订单状态日志索引库
+            case 3 :
+                $mappings = array(
+                    'mappings' => array(
+                        "_default_" => [
+                            "properties" => [
+                                'orderNo' => ['type' => 'keyword'],
+                                'status' => ['type' => 'integer'],
+                                'operationUserName' => ['type' => 'keyword'],
+                                'time' => ['type' => 'integer'],
+                            ]
+                        ]
+                    )
+                );
+
+                $searchServer = Factory::search('wms');
+                $result = $searchServer->createBase('dev_oms_orderstatus_log', $mappings);
+                if ($result && !isset($result['error'])) {
+                    echo 'dev_oms_orderstatus_log init done.';
+                    return true;
+                } else {
+                    print_r($result['error']);
+                    return false;
+                }
+
+                break;
+            // 删除oms订单状态索引库
+            case 4:
+                $searchServer = Factory::search('wms');
+                $searchServer->setIndex('dev_oms_orderstatus_log');
+                $a = $searchServer->deleteBase('dev_oms_orderstatus_log');
+                print_r($a);
+                break;
+            // 添加capital日志索引库
+            case 5 :
+                $mappings = array(
+                    'mappings' => array(
+                        "_default_" => [
+                            "properties" => [
+                                'userId' => ['type' => 'integer'],
+                                "userName" => [
+                                    "type" => "text",
+                                ],
+                                'object' => ['type' => 'keyword'],
+                                'actionType' => ['type' => 'keyword'],
+                                "logContent" => [
+                                    "type" => "text",
+                                    "store" => false,
+                                    "analyzer" => "ik_max_word",
+                                    "search_analyzer" => "ik_max_word",
+                                    "boost" => 5
+                                ],
+                                'time' => ['type' => 'integer'],
+                            ]
+                        ]
+                    )
+                );
+
+                $searchServer = Factory::search('capital');
+                $result = $searchServer->createBase('dev_capital_operation_log', $mappings);
+                if ($result && !isset($result['error'])) {
+                    echo 'dev_capital_operation_log init done.';
+                    return true;
+                } else {
+                    print_r($result['error']);
+                    return false;
+                }
+
+                break;
+            // 删除wms索引库
+            case 6:
+                $searchServer = Factory::search('capital');
+                $searchServer->setIndex('dev_capital_operation_log');
+                $a = $searchServer->deleteBase('dev_capital_operation_log');
+                print_r($a);
+                break;
+            // 添加oms日志索引库
+            case 7 :
+                $mappings = array(
+                    'mappings' => array(
+                        "_default_" => [
+                            "properties" => [
+                                'userId' => ['type' => 'integer'],
+                                "userName" => [
+                                    "type" => "text",
+                                ],
+                                'object' => ['type' => 'keyword'],
+                                'actionType' => ['type' => 'keyword'],
+                                "logContent" => [
+                                    "type" => "text",
+                                    "store" => false,
+                                    "analyzer" => "ik_max_word",
+                                    "search_analyzer" => "ik_max_word",
+                                    "boost" => 5
+                                ],
+                                'time' => ['type' => 'integer'],
+                            ]
+                        ]
+                    )
+                );
+
+                $searchServer = Factory::search('oms');
+                $result = $searchServer->createBase('dev_oms_operation_log', $mappings);
+                if ($result && !isset($result['error'])) {
+                    echo 'dev_oms_operation_log init done.';
+                    return true;
+                } else {
+                    print_r($result['error']);
+                    return false;
+                }
+
+                break;
+            // 删除wms索引库
+            case 8:
+                $searchServer = Factory::search('oms');
+                $searchServer->setIndex('dev_oms_operation_log');
+                $a = $searchServer->deleteBase('dev_oms_operation_log');
+                print_r($a);
+                break;
+
+        }
+    }
+}

+ 22 - 0
Jobs/Controller/CJobObject.Class.php

@@ -0,0 +1,22 @@
+<?php
+namespace Jobs\Controller;
+
+class CJobObject
+{
+    public $uuid        =''; //job uuid
+    public $topic       =''; //job 队列名
+    public $jobClass    =''; //job 执行类
+    public $jobMethod   =''; //job 执行方法
+    public $jobParams   =[]; //job参数
+    public $jobExtras   =[]; //附件信息,delay/expiration/priority等
+
+    public function __construct( $topic, $jobClass, $jobMethod, array $jobParams=[], array $jobExtras=[])
+    {
+        $this->uuid       =uniqid($topic) . '.' . microtime(true);
+        $this->topic      =$topic;
+        $this->jobClass   =$jobClass;
+        $this->jobMethod  =$jobMethod;
+        $this->jobParams  =$jobParams;
+        $this->jobExtras  =$jobExtras;
+    }
+}

+ 38 - 0
Jobs/Controller/CJobs.Class.php

@@ -0,0 +1,38 @@
+<?php
+namespace Jobs\Controller;
+
+use Mall\Framework\Factory;
+use Jobs\Controller\CBase;
+
+class CJobs extends CBase
+{
+    private $logger=null;
+
+    public function __construct($config)
+    {
+        $this->config      = $config;
+        $this->logger      = Factory::logs($this->config['logPath'] ?:'', $this->config['logSaveFileApp'] ?:'', $this->config['app_name'] ?:'');
+    }
+
+    public function start($JobObject)
+    {
+        $jobClass =$JobObject->jobClass;
+        $jobMethod=$JobObject->jobMethod;
+        $jobParams=$JobObject->jobParams;
+        try {
+            $obj      =new $jobClass();
+            if (is_object($obj) && method_exists($obj, $jobMethod)) {
+                $result = call_user_func([$obj, $jobMethod], $jobParams);
+                $this->logger->log('topic run result: ' . $result, 'error');
+            } else {
+                $this->logger->log('Action obj not find: ' . json_encode($JobObject), 'error');
+            }
+        } catch (\Throwable $e) {
+            catchError($this->logger, $e);
+        } catch (\Exception $e) {
+            catchError($this->logger, $e);
+        }
+
+        $this->logger->log('Action has been done, action content: ' . json_encode($JobObject));
+    }
+}

+ 647 - 0
Jobs/Controller/Test.Class.php

@@ -0,0 +1,647 @@
+<?php
+/**
+ * 测试队列任务脚本入口文件
+ * Created by PhpStorm.
+ * User: phperstar
+ * Date: 2018/6/10
+ * Time: 下午4:49
+ */
+
+namespace Jobs\Controller;
+
+
+use Jobs\Dao\DCloudUsers;
+use Jobs\Dao\DUsersTestify;
+use Jobs\Model\MTopic\Push\MPush;
+use Jobs\Model\Middleware;
+
+use Jobs\Dao\DCloudUsersCategory;
+use Jobs\Dao\DUsersCategory;
+use Jobs\Dao\DGoodsSku;
+use Jobs\Dao\DGoods;
+use Jobs\Model\Test\K3Handle;
+use Jobs\Dao\DOrder;
+
+use Jobs\Model\MTopic\Finance\MReceivable;
+
+use Mall\Framework\Db\Db;
+
+class Test
+{
+    /**
+     * php ./Public/www/index.php --c=Test --a=Test
+     */
+    public function test()
+    {
+        $objMReceivable = new MReceivable();
+        $params = [
+            'userCenterId' => 35,
+            'enterpriseId' => 64,
+            'id'      => 9,
+            'type'         => 5,
+        ];
+        $objMReceivable->createReceivable($params);
+    }
+
+
+
+    public function testk3()
+    {
+        //K/3 Cloud 业务站点地址
+        $cloudUrl = "http://123.206.51.139/k3cloud/";
+
+        //登陆参数
+        $data = array(
+            '597be128163613',//帐套Id
+            '秦统业',//用户名
+            'Cwb20141222',//密码
+            2052//语言标识
+        );
+
+        //定义记录Cloud服务端返回的Session
+        $cookie_jar = tempnam('./tmp','CloudSession');
+        $post_content = self::create_postdata($data);
+
+        //$result = invoke_login($cloudUrl,$post_content,$cookie_jar);
+        $loginurl = $cloudUrl.'Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc';
+        $result = self::invoke_post($loginurl,$post_content,$cookie_jar,TRUE);
+
+        header("Content-type: text/html; charset=gb2312");
+        //echo '<pre>';print_r('登陆请求数据:');
+        //echo '<pre>';print_r($post_content);
+
+        //echo '<pre>';print_r('登陆返回结果:');
+        //echo '<pre>';print_r($result);
+
+
+
+
+
+        unset($post_content);
+        unset($result);
+        unset($data);
+        /*$data = array(
+            'FormId'=>'STK_Inventory',//业务对象表单Id(必录)
+            'TopRowCount'=>0, // 最多允许查询的数量,0或者不要此属性表示不限制
+            'Limit'=>10,
+            'StartRow'=>0,
+            'OrderString'=>'FID ASC',
+            'FieldKeys'=>'FMaterialId,FMaterialId.FNumber,FStockName,FMaterialName,FBaseQty,FStockStatusId,FStockOrgId.FNumber',//字段keys,字符串类型用逗号分隔,比如"key1,key2..."(必录)
+            'FilterString'=>"FMaterialId=147946",//过滤(非必录)
+        );*/
+
+        /*$k3GoodsInfo = '{"FormId":"STK_Inventory","FieldKeys":"FMaterialId.FNumber,FMaterialName,FStockUnitId.FName,FBaseQty",
+                                    "FilterString":"FMaterialId=147946 and ( FStockName = \'菏泽G1库\' or FStockName = \'绿源冷库\' or FStockName = \'绿源冷库3号库\' )",
+                                    "OrderString":"","TopRowCount":"0","StartRow":"0","Limit":"100"}';*/
+
+        $data = array(
+            'FormId'=>'STK_Inventory',//业务对象表单Id(必录)
+            'TopRowCount'=>0, // 最多允许查询的数量,0或者不要此属性表示不限制
+            'Limit'=>100,
+            'StartRow'=>0,
+            'OrderString'=>'',
+            'FieldKeys'=>'FMaterialId,FMaterialId.FNumber,FStockName,FMaterialName,FBaseQty,FStockStatusId,FStockOrgId.FNumber',//字段keys,字符串类型用逗号分隔,比如"key1,key2..."(必录)
+            'FilterString'=>"FMaterialId.FNumber ='0602070004' and (FStockName = '菏泽G1库' or FStockName = '绿源冷库' or FStockName = '绿源冷库3号库') and FStockOrgId.FNumber='101' ",//过滤(非必录)
+        );
+
+
+        //定义记录Cloud服务端返回的Session
+        $post_content = self::create_postdata([$data]);
+
+        $url = $cloudUrl.'Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc';
+        $result = self::invoke_post($url,$post_content,$cookie_jar,false);
+
+        header("Content-type: text/html; charset=gb2312");
+        //echo '<pre>';print_r('及时库存请求数据:');
+        //echo '<pre>';print_r($post_content);
+
+        //echo '<pre>';print_r('及时库存返回结果:');
+        echo '<pre>';var_dump(json_decode($result, true));
+
+        $k3Weight = 0.00;
+        $resultArray = json_decode($result, true);
+        if(!empty($resultArray)){
+            foreach ($resultArray as $key => $value){
+                $k3Weight += $value[4];
+            }
+        }
+        var_dump($k3Weight);
+        return $k3Weight;
+    }
+
+    public function testk4()
+    {
+        //K/3 Cloud 业务站点地址
+        $cloudUrl = "http://123.206.51.139/k3cloud/";
+
+        //登陆参数
+        $data = array(
+            '597be128163613',//帐套Id
+            '秦统业',//用户名
+            'Cwb20141222',//密码
+            2052//语言标识
+        );
+
+        //定义记录Cloud服务端返回的Session
+        $cookie_jar = tempnam('./tmp','CloudSession');
+        $post_content = self::create_postdata($data);
+
+        //$result = invoke_login($cloudUrl,$post_content,$cookie_jar);
+        $loginurl = $cloudUrl.'Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc';
+        $result = self::invoke_post($loginurl,$post_content,$cookie_jar,true);
+
+        header("Content-type: text/html; charset=gb2312");
+        echo '<pre>';print_r('登陆请求数据:');
+        echo '<pre>';print_r($post_content);
+
+        echo '<pre>';print_r('登陆返回结果:');
+        echo '<pre>';print_r($result);
+
+
+
+
+
+        unset($post_content);
+        unset($result);
+        unset($data);
+        /*$data = array(
+            'FormId'=>'STK_Inventory',//业务对象表单Id(必录)
+            'TopRowCount'=>0, // 最多允许查询的数量,0或者不要此属性表示不限制
+            'Limit'=>10,
+            'StartRow'=>0,
+            'OrderString'=>'FID ASC',
+            'FieldKeys'=>'FMaterialId,FMaterialId.FNumber,FStockName,FMaterialName,FBaseQty,FStockStatusId,FStockOrgId.FNumber',//字段keys,字符串类型用逗号分隔,比如"key1,key2..."(必录)
+            'FilterString'=>"FMaterialId=147946",//过滤(非必录)
+        );*/
+
+        /*$k3GoodsInfo = '{"FormId":"STK_Inventory","FieldKeys":"FMaterialId.FNumber,FMaterialName,FStockUnitId.FName,FBaseQty",
+                                    "FilterString":"FMaterialId=147946 and ( FStockName = \'菏泽G1库\' or FStockName = \'绿源冷库\' or FStockName = \'绿源冷库3号库\' )",
+                                    "OrderString":"","TopRowCount":"0","StartRow":"0","Limit":"100"}';*/
+
+
+        $data = '{"Model":{"FNumber":"xyc2218","FCreateOrgId":{"FNumber":"101"},"FUseOrgId":{"FNumber":"101"},"FName":"u5218u5e05uff08u4ebfu6210u98dfu54c1u5185u90e8u5458u5de5uff09","FTEL":"18114640037","FADDRESS":"u5c71u4e1cu7701u6d4eu5357u5e02u5386u4e0bu533au6d4bu8bd5u5730u5740","FSALDEPTID":{"FNumber":"0020"},"FSALGROUPID":{"FNumber":"16"},"FSELLER":{"FNumber":"0020"},"FCustTypeId":{"FNumber":"KHLB001_SYS"},"FTRADINGCURRID":{"FNumber":"PRE001"},"F_PAEZ_Assistant3":{"FNumber":"0101"},"F_PAEZ_Assistant5":{"FNumber":"01"}},"Creator":"u4ebfu6210u5546u57ce"}';
+
+        //定义记录Cloud服务端返回的Session
+        $post_content = self::create_postdata(["BD_Customer",$data]);
+
+        $url = $cloudUrl.'Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save.common.kdsvc';
+        $result = self::invoke_post($url,$post_content,$cookie_jar,false);
+
+        //header("Content-type: text/html; charset=gb2312");
+        //echo '<pre>';print_r('及时库存请求数据:');
+        echo '<pre>';print_r($post_content);
+
+        //echo '<pre>';print_r('及时库存返回结果:');
+        //file_put_contents('/tmp/11.log',var_export($result,true),FILE_APPEND);
+        V($result);
+
+    }
+
+
+    // 采购入库单
+    public function testk5()
+    {
+        //K/3 Cloud 业务站点地址
+        $cloudUrl = "http://123.206.51.139/k3cloud/";
+
+        //登陆参数
+        $data = array(
+            '597be128163613',//帐套Id
+            '秦统业',//用户名
+            'Cwb20141222',//密码
+            2052//语言标识
+        );
+
+        //定义记录Cloud服务端返回的Session
+        $cookie_jar = tempnam('./tmp','CloudSession');
+        $post_content = self::create_postdata($data);
+
+        //$result = invoke_login($cloudUrl,$post_content,$cookie_jar);
+        $loginurl = $cloudUrl.'Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc';
+        $result = self::invoke_post($loginurl,$post_content,$cookie_jar,TRUE);
+
+        header("Content-type: text/html; charset=gb2312");
+        //echo '<pre>';print_r('登陆请求数据:');
+        //echo '<pre>';print_r($post_content);
+
+        //echo '<pre>';print_r('登陆返回结果:');
+        //echo '<pre>';print_r($result);
+
+
+
+
+
+        unset($post_content);
+        unset($result);
+        unset($data);
+        /*$data = array(
+            'FormId'=>'STK_Inventory',//业务对象表单Id(必录)
+            'TopRowCount'=>0, // 最多允许查询的数量,0或者不要此属性表示不限制
+            'Limit'=>10,
+            'StartRow'=>0,
+            'OrderString'=>'FID ASC',
+            'FieldKeys'=>'FMaterialId,FMaterialId.FNumber,FStockName,FMaterialName,FBaseQty,FStockStatusId,FStockOrgId.FNumber',//字段keys,字符串类型用逗号分隔,比如"key1,key2..."(必录)
+            'FilterString'=>"FMaterialId=147946",//过滤(非必录)
+        );*/
+
+        /*$k3GoodsInfo = '{"FormId":"STK_Inventory","FieldKeys":"FMaterialId.FNumber,FMaterialName,FStockUnitId.FName,FBaseQty",
+                                    "FilterString":"FMaterialId=147946 and ( FStockName = \'菏泽G1库\' or FStockName = \'绿源冷库\' or FStockName = \'绿源冷库3号库\' )",
+                                    "OrderString":"","TopRowCount":"0","StartRow":"0","Limit":"100"}';*/
+
+        $data = array(
+            'FormId'=>'STK_InStock',//业务对象表单Id(必录)
+            'TopRowCount'=>0, // 最多允许查询的数量,0或者不要此属性表示不限制
+            'Limit'=>1,
+            'StartRow'=>0,
+            'OrderString'=>'FBillNo desc',
+            'FieldKeys'=>'FMaterialId.FNumber,FID,FDate,FBillNo,FDocumentStatus,FMaterialId,FStockStatusid,FMaterialName',//字段keys,字符串类型用逗号分隔,比如"key1,key2..."(必录)
+            'FilterString'=>"FMaterialId.FNumber ='01110101010001',FDocumentStatus = 'c'",//过滤(非必录)
+        );
+
+
+        //定义记录Cloud服务端返回的Session
+        $post_content = self::create_postdata([$data]);
+
+        $url = $cloudUrl.'Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc';
+        $result = self::invoke_post($url,$post_content,$cookie_jar,false);
+
+        header("Content-type: text/html; charset=gb2312");
+        //echo '<pre>';print_r('及时库存请求数据:');
+        //echo '<pre>';print_r($post_content);
+
+        //echo '<pre>';print_r('及时库存返回结果:');
+        V(json_decode($result, true));
+    }
+
+
+    // 销售退货单
+    public function testk6()
+    {
+        //K/3 Cloud 业务站点地址
+        $cloudUrl = "http://123.206.51.139/k3cloud/";
+
+        //登陆参数
+        $data = array(
+            '597be128163613',//帐套Id
+            '秦统业',//用户名
+            'Cwb20141222',//密码
+            2052//语言标识
+        );
+
+        //定义记录Cloud服务端返回的Session
+        $cookie_jar = tempnam('./tmp','CloudSession');
+        $post_content = self::create_postdata($data);
+
+        //$result = invoke_login($cloudUrl,$post_content,$cookie_jar);
+        $loginurl = $cloudUrl.'Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc';
+        $result = self::invoke_post($loginurl,$post_content,$cookie_jar,TRUE);
+
+        header("Content-type: text/html; charset=gb2312");
+        //echo '<pre>';print_r('登陆请求数据:');
+        //echo '<pre>';print_r($post_content);
+
+        //echo '<pre>';print_r('登陆返回结果:');
+        //echo '<pre>';print_r($result);
+
+
+
+
+
+        unset($post_content);
+        unset($result);
+        unset($data);
+        /*$data = array(
+            'FormId'=>'STK_Inventory',//业务对象表单Id(必录)
+            'TopRowCount'=>0, // 最多允许查询的数量,0或者不要此属性表示不限制
+            'Limit'=>10,
+            'StartRow'=>0,
+            'OrderString'=>'FID ASC',
+            'FieldKeys'=>'FMaterialId,FMaterialId.FNumber,FStockName,FMaterialName,FBaseQty,FStockStatusId,FStockOrgId.FNumber',//字段keys,字符串类型用逗号分隔,比如"key1,key2..."(必录)
+            'FilterString'=>"FMaterialId=147946",//过滤(非必录)
+        );*/
+
+        /*$k3GoodsInfo = '{"FormId":"STK_Inventory","FieldKeys":"FMaterialId.FNumber,FMaterialName,FStockUnitId.FName,FBaseQty",
+                                    "FilterString":"FMaterialId=147946 and ( FStockName = \'菏泽G1库\' or FStockName = \'绿源冷库\' or FStockName = \'绿源冷库3号库\' )",
+                                    "OrderString":"","TopRowCount":"0","StartRow":"0","Limit":"100"}';*/
+
+        $data = array(
+            'FormId'=>'STK_InStock',//业务对象表单Id(必录)
+            'TopRowCount'=>0, // 最多允许查询的数量,0或者不要此属性表示不限制
+            'Limit'=>1,
+            'StartRow'=>0,
+            'OrderString'=>'FBillNo desc',
+            'FieldKeys'=>'FMaterialId.FNumber,FID,FDate,FBillNo,FDocumentStatus,FMaterialId,FStockStatusid,FMaterialName',//字段keys,字符串类型用逗号分隔,比如"key1,key2..."(必录)
+            'FilterString'=>"FMaterialId.FNumber ='01110101010001',FDocumentStatus = 'c'",//过滤(非必录)
+        );
+
+
+        //定义记录Cloud服务端返回的Session
+        $post_content = self::create_postdata([$data]);
+
+        $url = $cloudUrl.'Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc';
+        $result = self::invoke_post($url,$post_content,$cookie_jar,false);
+
+        header("Content-type: text/html; charset=gb2312");
+        //echo '<pre>';print_r('及时库存请求数据:');
+        //echo '<pre>';print_r($post_content);
+
+        //echo '<pre>';print_r('及时库存返回结果:');
+        V(json_decode($result, true));
+    }
+
+
+
+
+
+
+
+
+
+    //构造Web API请求格式
+    public function create_postdata($args) {
+        $postdata = array(
+            'format'=>1,
+            'useragent'=>'ApiClient',
+            'rid'=>self::create_guid(),
+            'parameters'=>$args,
+            'timestamp'=>date('Y-m-d'),
+            'v'=>'1.0'
+        );
+        return json_encode($postdata);
+    }
+
+    public function create_guid() {
+        $charid = strtoupper(md5(uniqid(mt_rand(), true)));
+        $hyphen = chr(45);// "-"
+        $uuid = chr(123)// "{"
+            .substr($charid, 0, 8).$hyphen
+            .substr($charid, 8, 4).$hyphen
+            .substr($charid,12, 4).$hyphen
+            .substr($charid,16, 4).$hyphen
+            .substr($charid,20,12)
+            .chr(125);// "}"
+        return $uuid;
+    }
+
+    public function invoke_post($url,$post_content,$cookie_jar,$isLogin)
+    {
+        $ch = curl_init($url);
+
+        $this_header = array(
+            'Content-Type: application/json',
+            'Content-Length: '.strlen($post_content)
+        );
+
+        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $this_header);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_content);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        if($isLogin){
+            curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar);
+        }
+        else{
+            curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
+        }
+        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
+
+        $result = curl_exec($ch);
+        curl_close($ch);
+
+        return $result;
+    }
+
+
+    public static function s_put_txt()
+    {
+
+        $objDOrder = new DOrder('pifabbc');
+        $orderData = $objDOrder->query('SELECT order_id,buyer_id,order_no,order_status,created_at,pay_money,payment_type from ns_order');
+        foreach ($orderData as $key => $value){
+            $buyerids[] = $value['buyer_id'];
+            $orderData[$value['order_id']] = $value;
+        }
+
+
+        $objDusersCategory = new DUsersCategory('pifabbc');
+        $infos = $objDusersCategory ->query('SELECT
+                     t.id,
+                     t.user_id as user_id,
+                    t.contact_phone,
+                    t.legal_person,
+                    r.referee_name,
+                    g.group_name,
+                    c.id as category_id,
+                    c.category_name,
+                    c.pid,
+                    t.check_status,
+                    t.company_address,
+                    t.detail_address,
+                    t.erp_no
+                FROM
+                    cloud_users_testify AS t,
+                    cloud_referee AS r,
+                    cloud_referee_group AS g,
+                    cloud_users_category AS c
+                WHERE
+                    t.referrer_no = r.referee_no
+                    AND t.referrer_group_no = g.group_no
+                    AND t.category_id = c.id and t.user_id in ('.implode(',', $buyerids).')');
+
+        if (!empty($infos)){
+            foreach ($infos as $key => $value) {
+                if ($value['pid'] != 0) {
+                    $data = self::s_handle($value);
+                }
+                $info = array_merge($value, $data);
+                $data_file = implode(',', $info);
+
+                $userData[$value['user_id']] = $data_file;
+            }
+        }
+
+        foreach ($orderData as $key => $value){
+            $content = $userData[$value['user_id']].','.implode(',', $value);
+            file_put_contents('order.txt',$content . PHP_EOL,FILE_APPEND) ;
+            unset($content);
+        }
+
+
+//        $data_file = implode(',', $file);
+//
+//        file_put_contents('cat.txt', $data_file);
+    }
+
+    public static function s_handle(&$info_value)
+    {
+        $objDusersCategory = new DUsersCategory();
+        $id = $info_value['pid'];
+        $detail = $objDusersCategory->get($id);
+        if (!empty($detail)) {
+            $info_value[] = $detail['category_name'];
+            $info_value['pid'] = $detail['pid'];
+            self::s_handle($info_value);
+        }
+        return $info_value;
+    }
+
+    //php ./Jobs/Public/www/index.php --c=Test --a=userErpReset
+    public static function userErpReset(){
+        $objectDCloudUsers = new DCloudUsers();
+        $objectDUsersTestify = new DUsersTestify();
+        $fileName = 'k3id.txt';
+        /*
+ * 逐行读取TXT文件
+ */
+            $file = fopen($fileName,'r');
+
+            $content = array();
+            if(!$file){
+                return 'file open fail';
+            }else{
+                $i = 0;
+                while (!feof($file)){
+                    $content = mb_convert_encoding(fgets($file),"UTF-8","GBK,ASCII,ANSI,UTF-8");
+//print_r($content);
+
+                    $data = explode(',',$content);
+
+                    $info = $objectDUsersTestify -> get(['erp_no' => $data[1]]);
+
+                    if (empty($info)){
+                        echo 0;
+                        continue;
+                    }else{
+                        $res = $objectDUsersTestify ->update(['k3_id'=>$data[0]],['erp_no' => $data[1]]);
+
+                        echo $res;
+
+                    }
+
+
+
+                }
+//                fclose($file);
+//                $content = array_filter($content); //数组去空
+            }
+
+//            return $content;
+
+
+
+
+    }
+
+    //php ./Jobs/Public/www/index.php --c=Test --a=resetErpNo
+    public static function resetErpNo(){
+        $log ='';
+        $objectDCloudUsers = new DCloudUsers();
+        $objectDUsersTestify = new DUsersTestify();
+        $sql = "select *from cloud_users_testify WHERE  SUBSTR(erp_no,1,3) !='xyc'";
+        $data = $objectDUsersTestify ->query($sql);
+
+        $log .= 'ID,客户新编号,客户旧编号,客户推荐人编码,客户推荐人组织编码,客户推荐人电话'.PHP_EOL;
+        $K3Handle = new K3Handle();
+        foreach($data as $value){
+
+            $id = $value['id'];
+//V($value);
+
+            if(!$value['erp_no']){
+                continue;
+            }
+            $erp_no = 'xyc'.$value['user_id'];
+            $res = $objectDUsersTestify -> update(['erp_no'=>$erp_no],['id'=>$id]);
+            $res = $K3Handle ->store($value,$erp_no);
+            if ($res['code'] !=200){
+continue;
+            }
+            $log .= $id.','.$erp_no.','.$value['erp_no']?:'空'.','.$value['referrer_no'].','.$value['referrer_group_no'].','.$value['referrer_phone'].PHP_EOL;
+        }
+        file_put_contents('update_erp_no.txt',$log,FILE_APPEND);
+    }
+
+
+
+
+//php ./Jobs/Public/www/index.php --c=Test --a=export
+    public static function export(){
+
+       $objDOrder = new DOrder('pifabbc');
+         $orderData = $objDOrder->query('SELECT sum(pay_money) as order_money ,count(order_id) as total_count,buyer_id  from ns_order where order_status <>5 group by buyer_id');
+         $a =[];
+        foreach ($orderData as $key => $value){
+        //$buyerids[] = $value['buyer_id'];
+        $a[$value['buyer_id']] = $value;
+        }
+        $objDusersCategory = new DUsersCategory('pifabbc');
+        $infos = $objDusersCategory ->query('SELECT
+                             t.id,
+                             t.user_id as user_id,
+                            t.contact_phone,
+                            t.legal_person,
+                            r.referee_name,
+                            g.group_name,
+                            c.id as category_id,
+                            c.category_name,
+                            c.pid,
+                            t.check_status,
+                            t.company_address,
+                            t.detail_address,
+                            t.erp_no
+                        FROM
+                            cloud_users_testify AS t,
+                            cloud_referee AS r,
+                            cloud_referee_group AS g,
+                            cloud_users_category AS c
+                        WHERE
+                            t.referrer_no = r.referee_no
+                            AND t.referrer_group_no = g.group_no
+                            AND t.category_id = c.id and t.user_id ');
+        $user =[];
+
+
+        if (!empty($infos)){
+            foreach ($infos as $key => $value) {
+                if ($value['pid'] != 0) {
+                    $data = self::s_handle($value);
+                }
+                $user[$value['user_id']] = $data;
+            }
+        }
+
+        foreach ($user as $key => $value){
+            echo 1 .'//';
+            echo ($key);
+//            $orderMoney = $objDOrder->query("SELECT sum('pay_money') as order_money ,count('order_id') as total_count from ns_order where order_status <>5 and buyer_id = $key");
+            $lastTime = $objDOrder->query("SELECT created_at from ns_order where order_status <>5 and buyer_id = $key ORDER by  order_id DESC limit 0,1");
+
+            if (isset($a[$key])){
+                $content = implode(',', $value).','.'总金额:'.$a[$key]['order_money']?:0;
+                $content .= ',订单数:'.$a[$key]['total_count']?:0 ;
+                $content .= ',最后下单时间:'.$lastTime[0]['created_at']?:0 ;
+            }else{
+                $content = implode(',', $value);
+            }
+    file_put_contents('export.txt',$content . PHP_EOL,FILE_APPEND) ;
+    unset($content);
+
+        }
+    }
+
+    public function ce(){
+        $objDOrder = new DOrder('pifabbc');
+
+        $lastTime = $objDOrder->query("SELECT created_at from ns_order where order_status <>5 and buyer_id = 4739 ORDER by  order_id DESC limit 0,1");
+        V($lastTime);
+    }
+
+    public function jpush()
+    {
+        $swResult = Middleware::getInstance('ThirdPartyService')->sendSwoole('Jpush', 'broadcastPush', ['alias'=>['7581'],'content'=>'优鲜供应链新版即将发布']);
+        if( !$swResult['state'] ){
+            V( $swResult['data']);
+        }else{
+            V($swResult['data']);
+        }
+    }
+}

+ 309 - 0
Jobs/Dao/BaseDao.Class.php

@@ -0,0 +1,309 @@
+<?php
+
+namespace Jobs\Dao;
+
+use Mall\Framework\Core\SqlHelper;
+use Mall\Framework\Factory;
+
+use Jobs\Cache\AllTableNameCache;
+
+class BaseDao extends SqlHelper
+{
+    /**
+     * @var \Mall\Framework\SearchClient\Client
+     */
+    private $search;
+
+    /**
+     * 当前操作的数据库配置标识
+     * @var $serviceDB
+     */
+    private $serviceDB;
+
+    /**
+     * 参考文章: https://www.php.cn/php-weizijiaocheng-403412.html
+     * 开启事务数量统计
+     * BaseDao constructor.
+     * @param string $serviceDB
+     */
+    private $transactions;
+
+    public function __construct($serviceDB = 'default')
+    {
+        $this->serviceDB = $serviceDB;
+
+        parent::__construct($serviceDB);
+
+        //$this->setSearchIndex($serviceDB);
+    }
+
+
+    /**
+     * 设置搜索引擎配置项
+     *
+     * @param string $serviceDB
+     *
+     * @throws \Exception
+     * @return \Mall\Framework\SearchClient\Client
+     */
+    public function setSearchIndex($serviceDB)
+    {
+        $this->search = Factory::search($serviceDB);
+
+        return $this->search;
+    }
+
+    /**
+     * 计算分表表名
+     *
+     * @param string $prefix 表名前缀
+     * @param int $id 索引表id
+     * @param int $pNumber 分割数量
+     *
+     * @return string
+     */
+    public function getTableName($prefix, $id, $pNumber = 500000)
+    {
+        $prefix = trim($prefix, '_') . '_';
+        $tableName = strtolower($prefix . ceil($id / $pNumber));
+
+        return $tableName;
+    }
+
+    /**
+     * 切换Dao层操作的表
+     * 主要用作切换分表使用
+     *
+     * @param $tableName
+     *
+     * @throws \Exception
+     */
+    public function setTable($tableName)
+    {
+        $this->_table = $tableName;
+
+        $databaseName = Factory::config()->get('db')[$this->serviceDB]['dbname'];
+
+        // 当前使用库所有表名缓存如果不存在自动更新
+        if (!AllTableNameCache::allTableNameCacheIsExists($databaseName)) {
+            $tables = $this->db->select("SELECT TABLE_NAME FROM INFORMATION_SCHEMA. TABLES WHERE TABLE_SCHEMA = '{$databaseName}';") ?: [];
+            if (!empty($tables)) {
+                AllTableNameCache::allTableNameCache($databaseName, $tables);
+            }
+        }
+        var_dump("xxxxaaaa");
+var_dump($databaseName);
+var_dump($tableName);
+
+        // 判断切换的表是否存在,不存在自动创建
+        if (!AllTableNameCache::TableIsExists($databaseName, $tableName)) {
+            $result = explode('_', $tableName);
+            if ($result && !empty($result)) {
+                $tablePrefix = '';
+                for ($i = 0, $c = count($result); $i < ($c - 1); $i++) {
+                    if(!is_numeric($result[$i])){
+                        $tablePrefix .= $result[$i] . '_';
+                    }
+                }
+
+                $dbresult = $this->db->query("CREATE TABLE {$tableName} LIKE {$tablePrefix}1");
+                if ($dbresult === false) {
+                    throw new \Exception($tableName . '分表创建错误. ErrorInfo: ' . var_export($this->db->error(), true));
+                }
+            } else {
+                throw new \Exception($tableName . '不是一个正确得表名');
+            }
+            AllTableNameCache::addNewTableName($databaseName, $tableName);
+        }
+    }
+
+    /**
+     * 添加数据
+     *
+     * @param array $params
+     *
+     * @return bool|int
+     */
+    public function insert($params = array(), $multiple = false)
+    {
+        return parent::insert($params, $multiple);
+    }
+
+    /**
+     * replace方式添加数据
+     *
+     * @param array $params
+     *
+     * @return bool|int
+     */
+    public function replace($params = array(), $multiple = false)
+    {
+        return parent::replace($params, $multiple);
+    }
+
+    /**
+     * 更新数据
+     *
+     * @param array $data
+     * @param null $where
+     * @param null $limit
+     * @param null $order
+     *
+     * @return int
+     */
+    public function update($data = array(), $where = null, $limit = null, $order = null)
+    {
+
+        return parent::update($data, $where, $limit, $order);
+    }
+
+    /**
+     * 删除数据
+     *
+     * @param null $where
+     * @param null $limit
+     * @param null $order
+     * @param array $data
+     *
+     * @return int
+     */
+    public function delete($where = null, $limit = null, $order = null, $data = array())
+    {
+        return parent::delete($where, $limit, $order, $data);
+    }
+
+    /**
+     * 执行sql
+     */
+    public function query($sql)
+    {
+        return parent::query($sql);
+    }
+
+    /**
+     * 获取Sql操作错误
+     *
+     * @return string
+     */
+    public function error()
+    {
+        return parent::error();
+    }
+
+    /**
+     * 过虑字段
+     *
+     * @param array $params 要筛选的数据
+     *
+     * @return array
+     */
+    public function getTablesFields($params)
+    {
+        $fields = [];
+
+        foreach ($this->_fields as $field) {
+            if (array_key_exists($field, $params)) {
+                $fields[$field] = $params[$field];
+            }
+        }
+
+        return $fields;
+    }
+
+    /**
+     * 根据DSL查询文档
+     *
+     * @param $query
+     *
+     * @return array
+     */
+    public function getSearchQueryDsl($query)
+    {
+        return $this->search->search($query);
+    }
+
+    /**
+     * 根据索引的主ID查询文档
+     *
+     * @param int $id 索引的DocumentId
+     *
+     * @return array
+     */
+    public function getSearchIndexDocument($id)
+    {
+        $id = intval($id);
+        return $this->search->get($id);
+    }
+
+    /**
+     * 添加或更新ES索引
+     *
+     * @param array $data 创建索引数据
+     * @param int $id 创建索引的DocumentId
+     *
+     * @return array
+     */
+    public function addUpSearchIndexDocument($data, $id)
+    {
+        return $this->search->index($data, $id);
+    }
+
+    /**
+     * 局部更新索引内容
+     * @param array $data 要更新的数据
+     * @param int $id 要更新的文档下的数据id
+     * @return array
+     */
+    public function esupdateTypeFieldVaule($data, $id)
+    {
+        return $this->search->updateFieldVaule($data, $id);
+    }
+
+    /**
+     * 删除索引下面的指定文档
+     */
+    public function esdeleteTypeDocument($id)
+    {
+        return $this->search->delete($id);
+    }
+
+
+    /**
+     * 开启事务
+     * @param bool $foreign_key_checks
+     * @return mixed
+     */
+    public function beginTransaction($foreign_key_checks = false)
+    {
+        ++$this->transactions;
+        if ($this->transactions == 1){
+            return parent::beginTransaction($foreign_key_checks = false);
+        }
+    }
+
+    /**
+     * 提交事务
+     * @return mixed
+     */
+    public function commit()
+    {
+        if($this->transactions == 1){
+            return parent::commit();
+        }
+        --$this->transactions;
+    }
+
+    /**
+     * 回滚事务
+     * @return mixed
+     */
+    public function rollBack()
+    {
+        if($this->transactions == 1){
+            $this->transactions = 0;
+            return parent::rollBack();
+        }else{
+            --$this->transactions;
+        }
+    }
+}

+ 60 - 0
Jobs/Dao/DCoupon.Class.php

@@ -0,0 +1,60 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/12/16
+ * Time: 15:55
+ */
+
+namespace Jobs\Dao;
+
+
+class DCoupon extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'coupon';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '优惠券id',
+            "startTime", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '固定时间-开始时间',
+            "endTime", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '固定时间-结束时间',
+            "name", //varchar(255) NOT NULL DEFAULT '' COMMENT '优惠券名称',
+            "couponType", //tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '优惠券类型(10满减券)',
+            "reducePrice", //decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '满减券-减免金额(面值)',
+            "minPrice", //decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '最低消费金额 0.00不限金额',
+            "remark", //varchar(255) NOT NULL DEFAULT '' COMMENT '使用说明',
+            "grantType", //tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '发放方式(10主动领取 20定向发放 30注册领取 40在线支付赠送)',
+            "totalNum", //int(11) NOT NULL DEFAULT '0' COMMENT '发放总数量(-1为不限制)',
+            "allowNum", //int(11) NOT NULL DEFAULT '0' COMMENT '每人限制领取数量(-1为不限制)',
+            "customerSourceId", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '领取范围',
+            "mustCondition", //decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '(40在线支付赠送)-满X金额发放 0.00所有在线支付不限金额可领取这个优惠券',
+            "grantStartTime", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '发放开始时间',
+            "grantEndTime", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '发放结束时间',
+            "useShop", //varchar(255) NOT NULL DEFAULT '' COMMENT '使用店铺 为空时所有店铺都能使用',
+            "applyRange", //tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '适用范围(10全部商品 20指定分类 30指定品牌)',
+            "categoryCollect", //varchar(255) NOT NULL DEFAULT '' COMMENT '(20指定分类)-分类集合',
+            "brandCollect", //varchar(255) NOT NULL DEFAULT '' COMMENT '(30指定品牌)-品牌集合',
+            "goodsCollect",
+            "receiveNum", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '已领取数量',
+            "sort", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越大越靠前)',
+            "deleteStatus", //tinyint(3) unsigned NOT NULL DEFAULT '5' COMMENT '软删除(5未删除 4已删除)',
+            "auditStatus", //tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '审核状态(1待审 2审核通过 3未通过 4审核中)',
+            "createTime", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime", //int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
+            "extends", //json DEFAULT NULL COMMENT '拓展字段',
+            "grantStatus",//tinyint(3) unsigned NOT NULL DEFAULT '5' COMMENT 'grantType==20发放状态(4未发放 5已发放)',
+            "customerIds",//varchar(255) NOT NULL DEFAULT '' COMMENT '领取客户 为空时所有客户都能领取',
+            "isMutex",//tinyint(3) unsigned NOT NULL DEFAULT '5' COMMENT '是否互斥(5是 4否)',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 49 - 0
Jobs/Dao/DGoodsBasic.Class.php

@@ -0,0 +1,49 @@
+<?php
+/**
+ * 商品基础数据Dao
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/10/30
+ * Time: 14:27
+ */
+
+namespace Jobs\Dao;
+
+class DGoodsBasic extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'goods_basic';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT,
+            "images", //json DEFAULT NULL COMMENT '商品相册',
+            "title", //varchar(255) NOT NULL DEFAULT '' COMMENT '商品名称',
+            "code", //varchar(255) NOT NULL DEFAULT '' COMMENT '商品编码',
+            "barCode", //varchar(255) NOT NULL DEFAULT '' COMMENT '商品条码',
+            "categoryId", //int(10) NOT NULL default '0' COMMENT '商品分类id',
+            "categoryPath", //varchar(50) NOT NULL default '' COMMENT '商品分类路径',
+            "expireTime", //int(10) NOT NULL default '0' COMMENT '过期时间',
+            "brandId", //int(10) NOT NULL default '0' COMMENT '品牌id',
+            'describe', // 商品描述
+            "tag", //varchar(255) NOT NULL DEFAULT '' COMMENT '关键词',
+            "link", //varchar(255) NOT NULL DEFAULT '' COMMENT 'link',
+            "description", //text NOT NULL COMMENT '商品详情',
+            "noSalesShop", //json DEFAULT NULL COMMENT '禁止销售店铺',
+            "extends", //json DEFAULT NULL COMMENT '拓展字段',
+            "enableStatus", //tinyint(3) NOT NULL DEFAULT '5' COMMENT '商品状态  默认5 上线 6下线',
+            "deleteStatus", //tinyint(3) NOT NULL DEFAULT '5' COMMENT '是否删除 4 删除 5正常',
+            "createTime", //int(10) NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime", //int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 35 - 0
Jobs/Dao/DLog.Class.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: wxj
+ * Date: 2019/11/28
+ * Time: 10:20
+ */
+namespace Jobs\Dao;
+
+use Jobs\Dao\BaseDao;
+
+class DLog extends BaseDao
+{
+    public function __construct($serviceDB = 'log')
+    {
+        $this->_table = 'log_2019_1';
+        $this->_primary = 'id';
+        $this->_fields = [
+          'id',//int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
+          'enterpriseId',//int(11) NOT NULL COMMENT '企业id',
+          'userCenterId',//int(11) DEFAULT NULL COMMENT 'userCenterId',
+          'createTime',//int(11) DEFAULT NULL COMMENT '创建时间',
+          'no',//varchar(225) DEFAULT NULL COMMENT '编码或id',
+          'actionType',//varchar(225) DEFAULT NULL COMMENT '操作类型',
+          'operationData',//json DEFAULT NULL COMMENT '操作数据',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+        ];
+        $this->_update_autofill = [
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 35 - 0
Jobs/Dao/DLoginLog.Class.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: wxj
+ * Date: 2019/11/28
+ * Time: 10:20
+ */
+namespace Jobs\Dao;
+
+use Jobs\Dao\BaseDao;
+
+class DLoginLog extends BaseDao
+{
+    public function __construct($serviceDB = 'log')
+    {
+        $this->_table = 'login_log_2019_12';
+        $this->_primary = 'id';
+        $this->_fields = [
+              'userCenterId',//int(11) DEFAULT NULL COMMENT '会员id',
+              'enterpriseId',//int(11) DEFAULT NULL COMMENT '企业id',
+              'mobile',//varchar(255) DEFAULT NULL COMMENT '注册手机号码',
+              'createTime',//varchar(20) DEFAULT NULL COMMENT '操作时间',
+              'source',//varchar(50) DEFAULT NULL COMMENT '来源'
+              'actionType',//varchar(255) DEFAULT NULL COMMENT '操作类型',
+              'operationData',//json DEFAULT NULL COMMENT '提交数据',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+        ];
+        $this->_update_autofill = [
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 35 - 0
Jobs/Dao/DPriceLog.Class.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2020/1/3
+ * Time: 16:10
+ */
+
+namespace Jobs\Dao;
+
+class DPriceLog extends BaseDao
+{
+    public function __construct($serviceDB = 'log')
+    {
+        $this->_table = 'price_log';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id",//int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
+            "enterpriseId",//int(11) NOT NULL COMMENT '企业id',
+            "userCenterId",//int(11) DEFAULT NULL COMMENT 'userCenterId',
+            "createTime",//int(11) DEFAULT NULL COMMENT '创建时间',
+            "no",//varchar(225) DEFAULT NULL COMMENT '编码或id',
+            "actionType",//varchar(225) DEFAULT NULL COMMENT '操作类型',
+            "operationData",//json DEFAULT NULL COMMENT '操作数据',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+
+        ];
+        $this->_update_autofill = [
+
+        ];
+        parent::__construct($serviceDB);
+    }
+}

+ 57 - 0
Jobs/Dao/DShop.Class.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 商铺管理模块
+ * Created by PhpStorm.
+ * User: wxj
+ * Date: 2019/10/31
+ * Time: 15:02
+ */
+
+namespace Jobs\Dao;
+
+/**
+ * Class DShop
+ * @package JinDouYun\Dao\Shop
+ */
+class DShop extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'shop';
+        $this->_primary = 'id';
+        $this->_fields = [
+            'id',//int(11) NOT NULL AUTO_INCREMENT COMMENT '商铺id',
+            'enterpriseId',// int(11) NOT NULL COMMENT '企业实体id',
+            'costType',
+            'name',// varchar(255) DEFAULT NULL COMMENT '商铺名称',
+            'logo',//varchar(255) NOT NULL COMMENT '商铺logo',
+            'shopCode',//varchar(255) NOT NULL COMMENT '商铺仓库编码',
+            'shopType',//int(10) DEFAULT NULL COMMENT '商铺类型',
+            'openTime',// json DEFAULT NULL COMMENT '营业时间',
+            'contactName',// varchar(50) NOT NULL COMMENT '联系人',
+            'mobile',//char(11) NOT NULL COMMENT '联系电话',
+            'deleteStatus',//tinyint(4) DEFAULT '5' COMMENT '删除状态(5未删除 4已删除)',
+            'createTime',//int(10) DEFAULT NULL COMMENT '创建时间',
+            'updateTime',//int(10) DEFAULT NULL COMMENT '修改时间',
+            'enableStatus',//tinyint(4) DEFAULT '5' COMMENT '启用状态(4未启用 5已启用)',
+            'provinceCode',//varchar(20) DEFAULT NULL COMMENT '省编码',
+            'cityCode',//varchar(20) DEFAULT NULL COMMENT '市编码',
+            'districtCode',//varchar(20) DEFAULT NULL COMMENT '区编码',
+            'address',//varchar(100) DEFAULT NULL COMMENT '详细地址',
+            'latitude',// varchar(255) DEFAULT NULL COMMENT '纬度',
+            'longitude',// varchar(255) DEFAULT NULL COMMENT '经度',
+            'salesArea',// json DEFAULT NULL COMMENT '销售区域',
+            'shopPartnerId',// int(11) DEFAULT NULL COMMENT '合伙人id',
+            'warehouseId',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 58 - 0
Jobs/Dao/DUserCoupon.Class.php

@@ -0,0 +1,58 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/12/16
+ * Time: 15:41
+ */
+
+namespace Jobs\Dao;
+
+
+class DUserCoupon extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'user_coupon';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id",//int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
+            "couponId",//int(11) unsigned NOT NULL COMMENT '优惠券id',
+            "name",//varchar(255) NOT NULL DEFAULT '' COMMENT '优惠券名称',
+            "remark",//varchar(255) NOT NULL DEFAULT '' COMMENT '使用说明',
+            "couponType",//tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '优惠券类型(10满减券)',
+            "reducePrice",//decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '满减券-减免金额(面值)',
+            "minPrice",//decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '最低消费金额 0.00不限金额',
+            "startTime",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '有效期开始时间',
+            "endTime",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '有效期结束时间',
+            "applyRange",//tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '适用范围(10全部商品 20指定分类 30指定品牌)',
+            "categoryCollect",//varchar(255) NOT NULL DEFAULT '' COMMENT '(20指定分类)-分类集合',
+            "brandCollect",//varchar(255) NOT NULL DEFAULT '' COMMENT '(30指定品牌)-品牌集合',
+            "goodsCollect",
+            "isExpire",//tinyint(3) unsigned NOT NULL DEFAULT '4' COMMENT '是否过期(4未过期 5已过期)',
+            "isUse",//tinyint(3) unsigned NOT NULL DEFAULT '4' COMMENT '是否已使用(4未使用 5已使用)',
+            "useTime",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '使用时间',
+            "userId",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id',
+            "customerId",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '客户id',
+            "shopId",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '领取的店铺id',
+            "useShop",//varchar(255) NOT NULL DEFAULT '' COMMENT '使用范围 为空时所有店铺都能使用',
+            "source",//tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '优惠券来源(10主动领取 20定向发放 30注册领取 40在线支付赠送)',
+            "createTime",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
+            "orderNo",//char(21) NOT NULL DEFAULT '' COMMENT '使用单号',
+            "orderId",//int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id',
+            "extends",//json DEFAULT NULL COMMENT '拓展字段',
+            "customerIds",//varchar(255) NOT NULL DEFAULT '' COMMENT '领取客户 为空时所有客户都能领取',
+            "isMutex",//tinyint(3) unsigned NOT NULL DEFAULT '5' COMMENT '是否互斥(5是 4否)',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 42 - 0
Jobs/Dao/Finance/DPay.Class.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Jobs\Dao\Finance;
+
+use Jobs\Dao\BaseDao;
+
+class DPay extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'qianniao_pay_receipt_1_201901';
+        $this->_primary = 'id';
+        $this->_fields = [
+            'id',//int(11) NOT NULL AUTO_INCREMENT COMMENT '应付单据id',
+            'customerId',//int(11) DEFAULT NULL COMMENT '客户Id',
+            'customerName',//varchar(50) DEFAULT NULL COMMENT '客户名称',
+            'no',//char(25) DEFAULT NULL COMMENT '应付单据编号',
+            'sourceNo',//char(25) DEFAULT NULL COMMENT '源订单号',
+            'purchaseId',
+            'createTime',//int(11) DEFAULT NULL COMMENT '创建时间',
+            'updateTime',//int(11) DEFAULT NULL COMMENT '修改时间',
+            'financeTypeId',//int(10) DEFAULT NULL COMMENT '财务类型id',
+            'financeType',//varchar(50) DEFAULT NULL COMMENT '财务类型名称',
+            'discountMoney',//float(10,2) DEFAULT NULL COMMENT '优惠金额',
+            'payMoney',//float(10,2) DEFAULT NULL COMMENT '实际应付金额',
+            'shopId',//int(11) DEFAULT NULL COMMENT '商铺Id',
+            'shopName',//varchar(50) DEFAULT NULL COMMENT '商铺名称',
+            'deleteStatus',//tinyint(1) DEFAULT NULL COMMENT '删除状态',
+            'auditStatus',//tinyint(1) NOT NULL COMMENT '审核状态(1未审核  2已审核)',
+            'receiptTypeId',//tinyint(2) DEFAULT NULL COMMENT '应付单据类型',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 42 - 0
Jobs/Dao/Finance/DPayReceiptIndex.Class.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * 应付单索引Dao类
+ * Created by PhpStorm.
+ * User: wxj
+ * Date: 2019/10/30
+ * Time: 14:17
+ */
+
+namespace Jobs\Dao\Finance;
+
+use Jobs\Dao\BaseDao;
+
+class DPayReceiptIndex extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'qianniao_pay_receipt_index_1';
+        $this->_primary = 'id';
+        $this->_fields = [
+            'id',//int(11) NOT NULL AUTO_INCREMENT COMMENT '应收索引表id',
+            'payReceiptId',//int(11) NOT NULL COMMENT '应付单据id',
+            'createTime',//int(11) NOT NULL COMMENT '创建时间',
+            'updateTime',//int(11) NOT NULL COMMENT '修改时间',
+            'shopId',//int(11) DEFAULT NULL COMMENT '商铺id',
+            'financeTypeId',//tinyint(2) DEFAULT NULL COMMENT '财务类型id',
+            'financeType',//varchar(50) DEFAULT NULL COMMENT '财务类型名称',
+            'auditStatus',//tinyint(1) DEFAULT '1' COMMENT '应付单据状态(1未审核 2已审核)',
+            'customerId',//int(11) DEFAULT NULL COMMENT '客户id',
+            'sourceNo',//char(25) DEFAULT NULL COMMENT '源订单号',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 42 - 0
Jobs/Dao/Finance/DReceive.Class.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Jobs\Dao\Finance;
+
+use Jobs\Dao\BaseDao;
+
+class DReceive extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'receive_receipt_1_201901';
+        $this->_primary = 'id';
+        $this->_fields = [
+            'id',//int(11) NOT NULL AUTO_INCREMENT COMMENT '应收单据id',
+            'customerId',//int(11) DEFAULT NULL COMMENT '客户Id',
+            'customerName',//varchar(50) DEFAULT NULL COMMENT '客户名称',
+            'no',//varchar(50) DEFAULT NULL COMMENT '单据编号',
+            'orderId',//int(11) DEFAULT NULL COMMENT '订单id',
+            'sourceNo',//varchar(50) DEFAULT NULL COMMENT '订单号',
+            'createTime',//int(11) DEFAULT NULL COMMENT '创建时间',
+            'updateTime',//int(11) DEFAULT NULL COMMENT '修改时间',
+            'financeTypeId',//int(10) DEFAULT NULL COMMENT '财务类型id',
+            'financeType',//varchar(255) DEFAULT NULL COMMENT '财务类型名称',
+            'discountMoney',//float(10,2) DEFAULT NULL COMMENT '优惠金额',
+            'receiveMoney',//float(10,2) DEFAULT NULL COMMENT '实际应收金额',
+            'shopId',//int(11) DEFAULT NULL COMMENT '商铺Id',
+            'shopName',//varchar(50) DEFAULT NULL COMMENT '商铺名称',
+            'deleteStatus',//tinyint(1) DEFAULT NULL COMMENT '删除状态',
+            'auditStatus',//tinyint(2) DEFAULT NULL COMMENT '单据状态',
+            'receiptType',//tinyint(2) DEFAULT NULL COMMENT '单据类型',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 41 - 0
Jobs/Dao/Finance/DReceiveReceiptIndex.Class.php

@@ -0,0 +1,41 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: wxj
+ * Date: 2019/10/30
+ * Time: 14:17
+ */
+
+namespace Jobs\Dao\Finance;
+
+use Jobs\Dao\BaseDao;
+
+class DReceiveReceiptIndex extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'receive_receipt_index_1';
+        $this->_primary = 'id';
+        $this->_fields = [
+            'id',//int(11) NOT NULL AUTO_INCREMENT COMMENT '应收索引表id',
+            'receiveReceiptId',//int(11) NOT NULL COMMENT '应收单据id',
+            'createTime',//int(11) NOT NULL COMMENT '创建时间',
+            'updateTime',//int(11) NOT NULL COMMENT '修改时间',
+            'shopId',//int(11) DEFAULT NULL COMMENT '商铺id',
+            'financeTypeId',//tinyint(2) DEFAULT NULL COMMENT '财务类型id',
+            'financeType',//varchar(50) DEFAULT NULL COMMENT '财务类型名称',
+            'auditStatus',//tinyint(1) DEFAULT NULL COMMENT '单据状态',
+            'customerId',//int(11) DEFAULT NULL COMMENT '客户id',
+            'sourceNo',//varchar(50) DEFAULT NULL COMMENT '订单编号',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 40 - 0
Jobs/Dao/Message/DMessage.Class.php

@@ -0,0 +1,40 @@
+<?php
+/**
+ * 消息Dao
+ * Created by PhpStorm.
+ * User: 小威
+ * Date: 2019/04/03
+ * Time: 12:20
+ */
+
+namespace Jobs\Dao\Message;
+
+use Jobs\Dao\BaseDao;
+
+class DMessage extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'message';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT COMMENT 'id',
+            "sendId", //int(10) NOT NULL DEFAULT '0' COMMENT '发送人id 0为系统发送',
+            "receiveId", //int(10) NOT NULL COMMENT '接收人id',
+            "title", //varchar(255) NOT NULL COMMENT '标题',
+            "content", //text NOT NULL COMMENT '内容',
+            "receiveStatus", //tinyint(3) NOT NULL COMMENT '已读状态 4:未读 5:已读',
+            "type", //tinyint(3) NOT NULL COMMENT '类型 1:库存不足消息',
+            "deleteStatus",
+            "sendTime", //int(10) NOT NULL COMMENT '发送时间',
+            "receiveTime", //int(10) DEFAULT NULL COMMENT '接收时间',
+            "createTime", //int(10) NOT NULL COMMENT '创建时间',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 59 - 0
Jobs/Dao/Order/DOrder.Class.php

@@ -0,0 +1,59 @@
+<?php
+/**
+ * 订单Dao
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/10/31
+ * Time: 15:49
+ */
+
+namespace Jobs\Dao\Order;
+
+use Jobs\Dao\BaseDao;
+
+class DOrder extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'order';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT,
+            "no", //char(20) NOT NULL COMMENT '订单编号',
+            "shopId", //int(10) NOT NULL DEFAULT '0' COMMENT '店铺id',
+            "shopName",
+            "outerTradeNo", //varchar(255) NOT NULL DEFAULT '' COMMENT '外部流水号(支付宝/微信返回的流水号)',
+            "totalMoney", //decimal(12,2) DEFAULT '0.00' COMMENT '总金额',
+            "payAmount", //decimal(12,2) DEFAULT '0.00' COMMENT '实付金额',
+            "buyTotal", //int(10) NOT NULL DEFAULT '0' COMMENT '购买总数',
+            "customerName", //varchar(50) NOT NULL COMMENT '客户姓名',
+            "customerId", //int(10) NOT NULL COMMENT '客户id',
+            "payStatus", //tinyint(3) NOT NULL DEFAULT '1' COMMENT '支付状态  默认1 未支付 2已支付',
+            "orderStatus", //tinyint(3) NOT NULL DEFAULT '2' COMMENT '订单状态 默认 2待支付 3代发货 4待收货 5已完成 6已关闭',
+            "payType", //tinyint(3) DEFAULT NULL COMMENT '支付方式 1微信 2支付宝 3货到付款 4上门自提',
+            "deliveryType", //tinyint(3) DEFAULT NULL COMMENT '配送方式 1商品配送 2上门自提',
+            "source", //tinyint(3) DEFAULT NULL COMMENT '订单来源 默认1 ios 2android 3小程序 4后台创建',
+            "remark", //varchar(255) NOT NULL DEFAULT '' COMMENT '订单备注',
+            "extends", //json DEFAULT NULL COMMENT '拓展字段',
+            "deleteStatus", //tinyint(3) NOT NULL DEFAULT '5' COMMENT '是否删除  默认5 正常 4删除',
+            "auditStatus", //tinyint(3) NOT NULL DEFAULT '1' COMMENT '审核状态 默认1 待审 2审核通过 3 审核未通过 4 审核中',
+            "payTime", //int(10) NOT NULL DEFAULT '0' COMMENT '付款时间',
+            "createTime", //int(10) NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime", //int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
+            "preferentialActivityId", //int(10) DEFAULT '0' COMMENT '店铺优惠活动id',
+            "salesManId", //int(10) DEFAULT '0' COMMENT '业务员id',
+            "salesManName", //varchar(255) DEFAULT NULL COMMENT '业务员名称',
+            "customerType", //tinyint(3) DEFAULT NULL COMMENT '客户类型',
+            "userCenterId", //int(10) DEFAULT NULL COMMENT 'userCenterId',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 41 - 0
Jobs/Dao/Order/DOrderCoupon.Class.php

@@ -0,0 +1,41 @@
+<?php
+/**
+ * 订单优惠表Dao
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/10/31
+ * Time: 15:58
+ */
+
+namespace JJobs\Dao\Order;
+
+use Jobs\Dao\BaseDao;
+
+class DOrderCoupon extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'order_coupon';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id",//int(10) NOT NULL AUTO_INCREMENT,
+            "orderId",//int(10) NOT NULL DEFAULT '0' COMMENT '订单Id',
+            "goodsId",//int(10) NOT NULL DEFAULT '0' COMMENT '商品id',
+            "shopId",// int(10) NOT NULL DEFAULT '0' COMMENT '店铺id',
+            "couponAmount",//decimal(12,2) default '0.00' COMMENT '商品优惠金额',
+            "couponInfo",//json DEFAULT NULL COMMENT '优惠信息',
+            "extends",//json DEFAULT NULL COMMENT '拓展字段',
+            "createTime",//int(10) NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime",//int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 57 - 0
Jobs/Dao/Order/DOrderGoods.Class.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 订单商品详情Dao
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/10/31
+ * Time: 16:04
+ */
+
+namespace Jobs\Dao\Order;
+
+use Jobs\Dao\BaseDao;
+
+class DOrderGoods extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'order_goods';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT,
+            "no", //char(20) NOT NULL COMMENT '订单编号',
+            "orderId", //int(10) NOT NULL DEFAULT '0' COMMENT '订单Id',
+            "goodsCode", //varchar(255) NOT NULL DEFAULT '' COMMENT '商品编号',
+            "goodsId", //int(10) NOT NULL DEFAULT '0' COMMENT '商品Id',
+            "goodsName", //varchar(50) NOT NULL DEFAULT '' COMMENT '商品名称',
+            "shopId", //int(10) NOT NULL DEFAULT '0' COMMENT '商铺Id',
+            "shopName", //varchar(50) NOT NULL DEFAULT '' COMMENT '商铺名称',
+            "originPrice", //decimal(12,2) DEFAULT '0.00' COMMENT '原始单价',
+            "price", //decimal(12,2) DEFAULT '0.00' COMMENT '商品单价',
+            "buyNum", //int(10) NOT NULL DEFAULT '0' COMMENT '购买数量',
+            "preferential", //decimal(12,2) DEFAULT '0.00' COMMENT '每/优惠',
+            "totalMoney", //decimal(12,2) DEFAULT '0.00' COMMENT '小计金额',
+            "deliverNum", //int(10) NOT NULL DEFAULT '0' COMMENT '发货数量',
+            "extends", //json DEFAULT NULL COMMENT '拓展字段',
+            "deleteStatus", //tinyint(3) NOT NULL DEFAULT '5' COMMENT '是否删除  默认5 正常 4删除',
+            "returnStatus", //tinyint(3) DEFAULT '0' COMMENT '退货状态 0:未退货 1:已退货',
+            "createTime", //int(10) NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime", //int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
+            "preferentialActivityId", //int(10) DEFAULT '0' COMMENT '店铺优惠活动id',
+            "skuId", //int(10) DEFAULT NULL COMMENT 'skuid',
+            "goodsBasicId", //int(10) DEFAULT NULL COMMENT 'goodsBasicId',
+            "unitName", //varchar(50) DEFAULT NULL COMMENT '单位名称',
+            "conversion", //decimal(10,2) DEFAULT NULL COMMENT '换算比率',
+            "outCostPrice", //decimal(10,2) DEFAULT NULL COMMENT '成本',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 45 - 0
Jobs/Dao/Order/DOrderIndex.Class.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/11/23
+ * Time: 9:22
+ */
+
+namespace Jobs\Dao\Order;
+
+use Jobs\Dao\BaseDao;
+
+class DOrderIndex extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'order_index';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
+            "orderId", //int(11) NOT NULL COMMENT '订单id',
+            "userCenterId", //int(11) NOT NULL COMMENT 'userCenterId',
+            "shopId", //int(11) NOT NULL COMMENT 'shopId',
+            "createTime", //int(10) DEFAULT NULL COMMENT '创建时间',
+            "updateTime", //int(10) DEFAULT NULL COMMENT '更新时间',
+            "deleteStatus", //tinyint(3) DEFAULT '5' COMMENT '删除状态',
+            "auditStatus", //tinyint(3) DEFAULT NULL COMMENT '审核状态',
+            "salesManId", //int(10) DEFAULT NULL COMMENT '业务员id',
+            "payStatus", //tinyint(3) DEFAULT '4' COMMENT '支付状态  默认4 未支付 5已支付',
+            "orderStatus", //tinyint(3) DEFAULT NULL COMMENT '订单状态 默认 2待支付 3代发货 4待收货 5已完成 6已关闭',
+            "outStatus",
+            "returnStatus",
+            "deliveryType", //tinyint(3) DEFAULT NULL COMMENT '配送方式 1商品配送 2上门自提',
+            "payType",//支付方式
+            "customerId",//客户id
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+        ];
+        $this->_update_autofill = [
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 48 - 0
Jobs/Dao/Order/DOrderReceive.Class.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * 订单收货信息Dao
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/10/31
+ * Time: 16:01
+ */
+
+namespace Jobs\Dao\Order;
+
+use Jobs\Dao\BaseDao;
+
+class DOrderReceive extends BaseDao
+{
+    public function __construct($serviceDB = 'default')
+    {
+        $this->_table = 'order_receive';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT,
+            "no", //bigint(20) NOT NULL COMMENT '订单编号',
+            "orderId", //int(10) NOT NULL DEFAULT '0' COMMENT '订单Id',
+            "customerId", //int(10) NOT NULL DEFAULT '0' COMMENT '客户Id',
+            "customerCode", //varchar(255) NOT NULL DEFAULT '' COMMENT '客户编码',
+            "realName", //varchar(255) NOT NULL DEFAULT '' COMMENT '收货人姓名',
+            "mobile", //char(11) NOT NULL DEFAULT '' COMMENT '收货人电话',
+            "address", //varchar(255) NOT NULL DEFAULT '' COMMENT '收货详细地址',
+            "provinceCode", //int(10) NOT NULL DEFAULT '0' COMMENT '省份编码',
+            "cityCode", //int(10) NOT NULL DEFAULT '0' COMMENT '城市编码',
+            "districtCode", //int(10) NOT NULL DEFAULT '0' COMMENT '区编码',
+            "remark", //varchar(255) NOT NULL DEFAULT '' COMMENT '收货备注',
+            "extends", //json DEFAULT NULL COMMENT '拓展字段',
+            "deleteStatus", //tinyint(3) NOT NULL DEFAULT '5' COMMENT '是否删除  默认5 正常 4删除',
+            "createTime", //int(10) NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime", //int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 56 - 0
Jobs/Dao/Purchase/DPurchase.Class.php

@@ -0,0 +1,56 @@
+<?php
+/**
+ * 采购Dao
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/11/11
+ * Time: 17:46
+ */
+
+namespace Jobs\Dao\Purchase;
+
+use Jobs\Dao\BaseDao;
+
+class DPurchase extends BaseDao
+{
+    public function __construct($serviceDB = 'stock')
+    {
+        $this->_table = 'purchase';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT,
+            "no", //char(20) NOT NULL COMMENT '采购单号',
+            "shopId", //int(10) NOT NULL DEFAULT '0' COMMENT '采购门店id',
+            "shopName", //varchar(255) NOT NULL DEFAULT '' COMMENT '门店名称',
+            "supplierId", //int(10) NOT NULL DEFAULT '0' COMMENT '供应商Id',
+            "supplierName", //varchar(255) NOT NULL DEFAULT '' COMMENT '供应商名称',
+            "buyerId", //int(10) NOT NULL DEFAULT '0' COMMENT '采购人员Id',
+            "buyerName", //varchar(255) NOT NULL DEFAULT '' COMMENT '采购人员名称',
+            "remark", //varchar(255) DEFAULT '' COMMENT '备注',
+            "operatorId", //int(10) NOT NULL DEFAULT '0' COMMENT '制单人员Id',
+            "operatorName", //varchar(255) NOT NULL DEFAULT '' COMMENT '制单人名',
+            "purchaseAmount", //decimal(15,4) DEFAULT '0.0000' COMMENT '采购金额',
+            "couponAmount", //decimal(15,4) DEFAULT '0.0000' COMMENT '优惠金额',
+            "otherAmount", //decimal(15,4) DEFAULT '0.0000' COMMENT '其他金额',
+            "goodsNum", //decimal(20,8) NOT NULL COMMENT '采购商品数量',
+            "extends", //json DEFAULT NULL COMMENT '拓展字段',
+            "orderStatus", //tinyint(3) DEFAULT '4' COMMENT '单据入库状态 4:未入库 5:已入库',
+            "auditStatus", //tinyint(3) NOT NULL DEFAULT '1' COMMENT '审核状态  默认1 待审 2审核通过 3审核未通过 4审核中',
+            "deleteStatus", //tinyint(3) NOT NULL DEFAULT '5' COMMENT '是否删除 默认5 正常 4删除',
+            "auditId", //int(10) NOT NULL DEFAULT '0' COMMENT '审核人员Id',
+            "auditName", //varchar(255) NOT NULL DEFAULT '' COMMENT '审核人员名册',
+            "auditTime", //int(10) NOT NULL DEFAULT '0' COMMENT '审核时间',
+            "createTime", //int(10) NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime", //int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 57 - 0
Jobs/Dao/Purchase/DPurchaseOut.Class.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 采购退货单Dao
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/11/11
+ * Time: 17:46
+ */
+
+namespace Jobs\Dao\Purchase;
+
+use Jobs\Dao\BaseDao;
+
+class DPurchaseOut extends BaseDao
+{
+    public function __construct($serviceDB = 'stock')
+    {
+        $this->_table = 'purchase_out';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT,
+            "purchaseId", //int(10) NOT NULL DEFAULT '0' COMMENT '采购单id',
+            "purchaseNo", //bigint(20) NOT NULL COMMENT '采购订单号',
+            "no", //bigint(20) NOT NULL COMMENT '采购退货单号',
+            "shopId", //int(10) NOT NULL DEFAULT '0' COMMENT '门店id',
+            "shopName", //varchar(255) NOT NULL DEFAULT '' COMMENT '门店名称',
+            "supplierId", //int(10) NOT NULL DEFAULT '0' COMMENT '供应商Id',
+            "supplierName", //varchar(255) NOT NULL DEFAULT '' COMMENT '供应商名称',
+            "buyerId", //int(10) NOT NULL DEFAULT '0' COMMENT '采购人员Id',
+            "buyerName", //varchar(255) NOT NULL DEFAULT '' COMMENT '采购人员名称',
+            "remark", //varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
+            "operatorId", //int(10) NOT NULL DEFAULT '0' COMMENT '制单人员Id',
+            "operatorName", //varchar(255) NOT NULL DEFAULT '' COMMENT '制单人名',
+            "purchaseAmount", //decimal(12,4) default '0.0000' COMMENT '采购退货金额',
+            "couponAmount", //decimal(12,4) default '0.0000' COMMENT '优惠金额',
+            "otherAmount", //decimal(12,4) default '0.0000' COMMENT '其他金额',
+            "extends", //json DEFAULT NULL COMMENT '拓展字段',
+            "orderStatus",
+            "auditStatus", //tinyint(3) NOT NULL DEFAULT '1' COMMENT '审核状态  默认1 待审 2审核通过 3审核未通过 4审核中',
+            "deleteStatus", //tinyint(3) NOT NULL DEFAULT '5' COMMENT '是否删除 默认5 正常 4删除',
+            "auditId", //int(10) NOT NULL DEFAULT '0' COMMENT '审核人员Id',
+            "auditName", //varchar(255) NOT NULL DEFAULT '' COMMENT '审核人员名册',
+            "auditTime", //int(10) NOT NULL DEFAULT '0' COMMENT '审核时间',
+            "createTime", //int(10) NOT NULL DEFAULT '0' COMMENT '创建时间',
+            "updateTime", //int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 50 - 0
Jobs/Dao/Stock/DInventoryBatch.Class.php

@@ -0,0 +1,50 @@
+<?php
+/**
+ * 库存批次管理Dao
+ * Created by PhpStorm.
+ * User: phperstar
+ * Date: 2019/12/10
+ * Time: 10:00
+ */
+
+namespace Jobs\Dao\Stock;
+
+
+use Jobs\Dao\BaseDao;
+
+class DInventoryBatch extends BaseDao
+{
+    public function __construct($serviceDB = 'stock')
+    {
+        $this->_table = 'inventory_batch';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT COMMENT '自增id',
+            "originId",
+            "originNo",
+            "warehouseId", //int(10) NOT NULL COMMENT '仓库id',
+            "materielId", //int(10) NOT NULL COMMENT '物料ID',
+            "materielIdCode", //varchar(255) DEFAULT NULL COMMENT '物料编码',
+            "sourceNo", // 来源单号
+            "skuId", //单位id
+            'batchNo', // 批次编号
+            'num', // 批次可用数量
+            "averageCost", //成本均价
+            'batchCost',//批次成本
+            'productionData',//生产日期
+            'batchStatus',
+            "updateTime", //int(10) DEFAULT NULL COMMENT '修改时间',
+            "createTime", //int(10) DEFAULT NULL COMMENT '创建时间',
+            "extend", //json DEFAULT NULL COMMENT '扩展字段',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time(),
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 53 - 0
Jobs/Dao/Stock/DInventoryDetails.Class.php

@@ -0,0 +1,53 @@
+<?php
+/**
+ * 库存流水管理Dao
+ * Created by PhpStorm.
+ * User: 小威
+ * Date: 2019/11/12
+ * Time: 10:00
+ */
+
+namespace Jobs\Dao\Stock;
+
+use Jobs\Dao\BaseDao;
+
+class DInventoryDetails extends BaseDao
+{
+    public function __construct($serviceDB = 'stock')
+    {
+        $this->_table = 'inventory_details';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT COMMENT '自增id',
+            "warehouseId", //int(10) NOT NULL COMMENT '仓库id',
+            "originId", //int(10) NOT NULL COMMENT '业务来源id',
+            "originNo", //char(22) NOT NULL COMMENT '业务来源no',
+            "materielId", //int(10) NOT NULL COMMENT '物料id',
+            "materielCode", //varchar(255) NOT NULL COMMENT '物料编码',
+            "materielName", //varchar(255) NOT NULL COMMENT '物料名称',
+            "sourceNo", //char(20) NOT NULL COMMENT '来源单号',
+            "source", //tinyint(3) NOT NULL COMMENT '单据来源标识',
+            "operatorId", //int(10) NOT NULL COMMENT '操作人id',
+            "operatorName", //varchar(50) NOT NULL COMMENT '操作人姓名',
+            "skuId", //int(10) NOT NULL COMMENT '单位id',
+            "inventoryNum", //decimal(20,8) NOT NULL COMMENT '库存数',
+            "inventoryChangeNum", //decimal(20,8) NOT NULL COMMENT '变动后库存数量',
+            "batch", //json NOT NULL COMMENT '批次信息',
+            "costType", //tinyint(3) NOT NULL DEFAULT '1' COMMENT '计算成本方式 1:移动加权 2:个别计价',
+            "averageCost", //decimal(15,4) NOT NULL COMMENT '均价成本',
+            "actionType", //tinyint(3) NOT NULL COMMENT '出入库操作标识 5:入库 4:出库',
+            "createTime", //int(10) NOT NULL COMMENT '创建时间',
+            "updateTime", //int(10) NOT NULL COMMENT '更新时间',
+            "extend", //json DEFAULT NULL COMMENT '扩展字段',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 58 - 0
Jobs/Dao/Stock/DInventoryIn.Class.php

@@ -0,0 +1,58 @@
+<?php
+/**
+ * 入库管理Dao
+ * Created by PhpStorm.
+ * Date: 2019/11/11
+ * Time: 18:30
+ */
+
+namespace Jobs\Dao\Stock;
+
+use Jobs\Dao\BaseDao;
+
+class DInventoryIn extends BaseDao
+{
+    public function __construct($serviceDB = 'stock')
+    {
+        $this->_table = 'inventory_in';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT COMMENT '主键自增',
+            "no", //char(25) NOT NULL COMMENT '入库单号',
+            "sourceId", //int(10) NOT NULL COMMENT '来源id',
+            "sourceNo", //char(25) NOT NULL COMMENT '来源单号',
+            "originNo", //char(25) NOT NULL COMMENT '源头单号',
+            "merchantId", // int(10) NOT NULL DEFAULT '0' COMMENT '商户id',
+            "originId", //int(10) DEFAULT NULL COMMENT '源头id',
+            "materielNum", //int(10) DEFAULT NULL COMMENT '入库商品数量',
+            "amount", //decimal(15,4) DEFAULT NULL COMMENT '入库总金额',
+            "costAllocationType", //tinyint(1) DEFAULT NULL COMMENT '成本分摊类型',
+            "costAllocation", //decimal(15,4) DEFAULT '0.0000' COMMENT '成本分摊费用',
+            "shopId", //int(10) DEFAULT '0' COMMENT '商铺id',
+            "warehouseId", //int(10) DEFAULT NULL COMMENT '仓库id',
+            "warehouseName", //varchar(255) DEFAULT NULL COMMENT '仓库名称',
+            "operatorId", //int(10) DEFAULT NULL COMMENT '操作人id',
+            "operatorName", //varchar(255) DEFAULT NULL COMMENT '操作人姓名',
+            "auditId", //int(10) DEFAULT NULL COMMENT '审核人id',
+            "auditName", //varchar(20) DEFAULT NULL COMMENT '审核人姓名',
+            "remark", //varchar(255) DEFAULT NULL COMMENT '备注',
+            "deleteStatus", //tinyint(3) DEFAULT NULL COMMENT '删除状态 5:正常 4:删除',
+            "source", //tinyint(3) DEFAULT NULL COMMENT '订单来源(入库类型)',
+            "type", //tinyint(3) DEFAULT NULL COMMENT '单据类型',
+            "auditStatus", //tinyint(3) DEFAULT NULL COMMENT '审核状态 1:未审核 2:已审核',
+            "auditTime", //int(10) DEFAULT NULL COMMENT '审核日期',
+            "createTime", //int(10) DEFAULT NULL COMMENT '创建时间/制单时间',
+            "updateTime", //int(10) DEFAULT NULL COMMENT '修改时间',
+            "extend", //json DEFAULT NULL COMMENT '拓展字段',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 54 - 0
Jobs/Dao/Stock/DInventoryInDetails.Class.php

@@ -0,0 +1,54 @@
+<?php
+/**
+ * 入库详情管理Dao
+ * Created by PhpStorm.
+ * User: 小威
+ * Date: 2019/11/11
+ * Time: 14:00
+ */
+
+namespace Jobs\Dao\Stock;
+
+use Jobs\Dao\BaseDao;
+
+class DInventoryInDetails extends BaseDao
+{
+    public function __construct($serviceDB = 'stock')
+    {
+        $this->_table = 'inventory_in_details';
+        $this->_primary = 'id';
+        $this->_fields = [
+            'id',//int(10) NOT NULL AUTO_INCREMENT COMMENT '自增id',
+            'linkId',//int(10) DEFAULT NULL COMMENT '入库id',
+            'linkNo',//char(25) DEFAULT NULL COMMENT '入库单号',
+            'InWarehouse',//json DEFAULT NULL COMMENT '入库仓库',
+            'materielId',//int(10) DEFAULT NULL COMMENT '物料id',
+            'materielName',//varchar(255) DEFAULT NULL COMMENT '物料名称',
+            'materielCode',//varchar(20) DEFAULT NULL COMMENT '物料编号',
+            'skuId',//int(10) DEFAULT NULL COMMENT '单位id',
+            'unitName',//varchar(255) NOT NULL COMMENT '单位名称',
+            'skuName',//varchar(255) NOT NULL COMMENT '属性名称',
+            'skuStorage',//varchar(255) DEFAULT NULL COMMENT '货架编码',
+            'num',//decimal(20,8) unsigned zerofill DEFAULT '000000000000.00000000' COMMENT '数量',
+            'inNum',//decimal(20,8) DEFAULT '0.00000000' COMMENT '入库数量',
+            'otherNum',//decimal(20,8) DEFAULT '0.00000000' COMMENT '其他单位数量',
+            'unitPrice',//decimal(15,4) DEFAULT NULL COMMENT '单价',
+            'totalPrice',//decimal(15,4) DEFAULT NULL COMMENT '总价',
+            'inCost',//decimal(15,4) DEFAULT '0.0000' COMMENT '入库成本',
+            'inCostTotal',//decimal(15,4) DEFAULT '0.0000' COMMENT '入库总成本',
+            'productionData',//int(10) DEFAULT '0' COMMENT '生产日期',
+            'createTime',//int(10) DEFAULT NULL COMMENT '创建时间',
+            'updateTime',//int(10) DEFAULT NULL COMMENT '修改时间',
+            'extend',//json DEFAULT NULL COMMENT '扩展字段',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 60 - 0
Jobs/Dao/Stock/DInventoryOut.Class.php

@@ -0,0 +1,60 @@
+<?php
+/**
+ * 出库管理Dao
+ * Created by PhpStorm.
+ * Date: 2019/11/11
+ * Time: 14:00
+ */
+namespace Jobs\Dao\Stock;
+
+use Jobs\Dao\BaseDao;
+
+class DInventoryOut extends BaseDao
+{
+    public function __construct($serviceDB = 'stock')
+    {
+        $this->_table = 'inventory_out';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT COMMENT '主键自增',
+            "no", //char(25) NOT NULL COMMENT '出库单号',
+            "sourceId", //int(10) NOT NULL COMMENT '来源Id',
+            "sourceNo", //char(25) NOT NULL COMMENT '来源单号',
+            "originNo", //char(25) NOT NULL COMMENT '源头单号',
+            "originId", //int(10) NOT NULL COMMENT '源头id',
+            "materielNum", //int(10) DEFAULT NULL COMMENT '出库商品数量',
+            "amount", //decimal(15,4) DEFAULT NULL COMMENT '出库总金额',
+            "shopId", //int(10) NOT NULL COMMENT '商铺ID',
+            "shopName", //varchar(50) NOT NULL COMMENT '商铺名称',
+            "operatorId", //int(10) DEFAULT NULL COMMENT '操作人id',
+            "operatorName", //varchar(255) DEFAULT NULL COMMENT '操作人姓名',
+            "auditId", //int(10) DEFAULT NULL COMMENT '审核人id',
+            "auditName", //varchar(30) DEFAULT NULL COMMENT '审核人姓名',
+            "customerId", //int(10) DEFAULT NULL COMMENT '客户id',
+            "customerName", //varchar(20) DEFAULT NULL COMMENT '客户姓名',
+            "customerCode", //varchar(255) DEFAULT NULL COMMENT '客户编码',
+            "customerMobile", //char(11) DEFAULT NULL COMMENT '客户电话',
+            "remark", //varchar(255) DEFAULT NULL COMMENT '备注',
+            "source", //tinyint(3) DEFAULT NULL COMMENT '订单来源(出库类型)',
+            "deliveryType", //tinyint(3) DEFAULT NULL COMMENT '配送方式',
+            "type", //tinyint(3) DEFAULT NULL COMMENT '单据类型',
+            "deleteStatus", //tinyint(3) DEFAULT NULL COMMENT '删除状态 5:正常 4:删除',
+            "outStatus", //tinyint(1) DEFAULT NULL COMMENT '出库状态 1:待出库 2:全部出库 3:部分出库',
+            "auditStatus", //tinyint(3) DEFAULT NULL COMMENT '审核状态1 :未审核 2:已审核',
+            "auditTime", //int(10) DEFAULT NULL COMMENT '审核日期',
+            "createTime", //int(10) DEFAULT NULL COMMENT '创建时间/制单时间',
+            "updateTime", //int(10) DEFAULT NULL COMMENT '修改时间',
+            "extend", //json DEFAULT NULL COMMENT '拓展字段',
+            "serialNum", //varchar(20) DEFAULT '0' COMMENT '序号',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 49 - 0
Jobs/Dao/Stock/DWarehouse.Class.php

@@ -0,0 +1,49 @@
+<?php
+/**
+ * 仓库管理Dao
+ * Created by PhpStorm.
+ * User: 小威
+ * Date: 2019/11/08
+ * Time: 14:30
+ */
+
+namespace Jobs\Dao\Stock;
+
+use Jobs\Dao\BaseDao;
+
+class DWarehouse extends BaseDao
+{
+    public function __construct($serviceDB = 'stock')
+    {
+        $this->_table = 'warehouse';
+        $this->_primary = 'id';
+        $this->_fields = [
+            "id", //int(10) NOT NULL AUTO_INCREMENT COMMENT '自增id',
+            "shopId", //int(10) DEFAULT NULL COMMENT '商铺Id',
+            "warehouseCode", //varchar(50) DEFAULT NULL COMMENT '仓库编码',
+            "warehouseName", //varchar(255) DEFAULT NULL COMMENT '仓库名称',
+            "contactName", //varchar(255) DEFAULT NULL COMMENT '联系人姓名',
+            "contactMobile", //char(11) DEFAULT NULL COMMENT '联系方式/手机号',
+            "provinceCode", //int(10) DEFAULT NULL COMMENT '省份编码',
+            "cityCode", //int(10) DEFAULT NULL COMMENT '城市编码',
+            "districtCode", //int(10) DEFAULT NULL COMMENT '区县编码',
+            "contactAddress", //varchar(255) DEFAULT NULL COMMENT '仓库地址',
+            "remarks", //varchar(255) DEFAULT NULL COMMENT '备注',
+            "beginningStatus", // tinyint(3) NOT NULL DEFAULT '5' COMMENT '期初状态 5:正常 4:禁用',
+            "enableStatus", //tinyint(3) DEFAULT '5' COMMENT '启用状态 4:未启用 5:启用',
+            "deleteStatus", //tinyint(3) DEFAULT '5' COMMENT '删除状态 4:删除 5:正常',
+            "updateTime", //int(10) DEFAULT NULL COMMENT '更新时间',
+            "createTime", //int(10) DEFAULT NULL COMMENT '创建时间',
+            "extend", //json DEFAULT NULL COMMENT '扩展字段',
+        ];
+        $this->_readonly = ['id'];
+        $this->_create_autofill = [
+            'createTime' => time()
+        ];
+        $this->_update_autofill = [
+            'updateTime' => time()
+        ];
+
+        parent::__construct($serviceDB);
+    }
+}

+ 154 - 0
Jobs/Model/MConsole.Class.php

@@ -0,0 +1,154 @@
+<?php
+namespace Jobs\Model;
+
+use Mall\Framework\Factory;
+use Jobs\Model\MProcess;
+
+class MConsole
+{
+    public $logger    = null;
+    private $config   = [];
+
+    public function __construct($config)
+    {
+        $this->config = $config;
+        $this->logger  = Factory::logs($this->config['logPath'] ?:'', $this->config['logSaveFileApp'] ?:'', $this->config['app_name'] ?:'');
+    }
+
+    public function run()
+    {
+        $this->runOpt();
+    }
+
+    public function runOpt()
+    {
+        global $argv;
+        if (empty($argv[1])) {
+            $this->printHelpMessage();
+            exit();
+        }
+        $opt=$argv[1];
+        switch ($opt) {
+            case 'start':
+                $this->start($this->config);
+                break;
+            case 'stop':
+                $this->sendSignal();
+                break;
+            case 'status':
+                $this->sendSignal(SIGUSR2);
+                break;
+            case 'exit':
+                $this->kill();
+                break;
+            case 'restart':
+                $this->restart();
+                break;
+            case 'help':
+                $this->printHelpMessage();
+                break;
+
+            default:
+                $this->printHelpMessage();
+                break;
+        }
+    }
+
+    public function restart()
+    {
+        $this->logger->log('restarting...');
+        $this->kill();
+        sleep(3);
+        $this->start($this->config);
+    }
+
+    public function kill()
+    {
+        $this->sendSignal(SIGTERM);
+    }
+
+    /**
+     * 启动进程
+     */
+    public function start($config)
+    {
+        $process = new MProcess($config);
+        $process->start();
+    }
+
+    /**
+     *  给主进程发送信号:
+     *  SIGUSR1 自定义信号,让子进程平滑退出
+     *  SIGUSR2 自定义信号2,显示进程状态
+     *  SIGTERM 程序终止,让子进程强制退出.
+     *
+     * @param [type] $signal
+     */
+    public function sendSignal($signal=SIGUSR1)
+    {
+        $this->logger->log($signal . (SIGUSR1 == $signal) ? ' smooth to exit...' : ' force to exit...');
+
+        if (isset($this->config['pidPath']) && !empty($this->config['pidPath'])) {
+            $masterPidFile=$this->config['pidPath'] . '/master.pid';
+            $pidStatusFile=$this->config['pidPath'] . '/status.info';
+        } else {
+            die('config pidPath must be set!' . PHP_EOL);
+        }
+
+        if (file_exists($masterPidFile)) {
+            $pid   =file_get_contents($masterPidFile);
+            if ($pid && !@\Swoole\Process::kill($pid, 0)) {
+                exit('service is not running' . PHP_EOL);
+            }
+            if (@\Swoole\Process::kill($pid, $signal)) {
+                $this->logger->log('[master pid: ' . $pid . '] has been received  signal' . $signal);
+                sleep(1);
+                //如果是SIGUSR2信号,显示swoole-jobs状态信息
+                if (SIGUSR2 == $signal) {
+                    $statusStr=file_get_contents($pidStatusFile);
+
+                    echo $statusStr ? $statusStr : 'sorry,show status fail.';
+                    exit;
+                }
+            }
+            $this->logger->log('[master pid: ' . $pid . '] has been received signal fail');
+        } else {
+            exit('service is not running' . PHP_EOL);
+        }
+    }
+
+    /**
+     * 帮助手册
+     */
+    public function printHelpMessage()
+    {
+        $msg=<<<'EOF'
+NAME
+      php swoole-jobs - manage swoole-jobs
+
+SYNOPSIS
+      php swoole-jobs command [options]
+          Manage swoole-jobs daemons.
+
+WORKFLOWS
+
+      help [command]
+      Show this help, or workflow help for command.
+
+      restart
+      Stop, then start swoole-jobs master and workers.
+
+      start
+      Start swoole-jobs master and workers.
+
+      stop
+      Wait all running workers smooth exit, please check swoole-jobs status for a while.
+
+      exit
+      Kill all running workers and master PIDs.
+
+
+EOF;
+        echo $msg;
+    }
+}

+ 114 - 0
Jobs/Model/MJobs.Class.php

@@ -0,0 +1,114 @@
+<?php
+namespace Jobs\Model;
+
+use Mall\Framework\Factory;
+
+use Jobs\Model\MQueue;
+use Jobs\Model\MProcess;
+
+class MJobs
+{
+    public $logger              = null;
+    public $queue               = null;
+    public $sleep               = 2; //单个topic如果没有任务,该进程暂停秒数,不能低于1秒,数值太小无用进程会频繁拉起
+    public $config              = [];
+
+    private $pidInfoFile        = ''; // 主进程pid信息文件路径
+
+    public function __construct($pidInfoFile, $config)
+    {
+        $this->config      = $config;
+        $this->pidInfoFile = $pidInfoFile;
+        $this->sleep       = $this->config['sleep'] ?: $this->sleep;
+        $this->logger      = Factory::logs($this->config['logPath'] ?:'', $this->config['logSaveFileApp'] ?:'', $this->config['app_name'] ?:'');
+    }
+
+
+    public function run($topic='')
+    {
+        if ($topic) {
+            $this->queue = MQueue::getQueue($this->config['job']['queue'], $this->logger);
+            if (empty($this->queue)) {
+                sleep($this->sleep);
+
+                return;
+            }
+            $this->queue->setTopics($this->config['job']['topics'] ?: []);
+
+            $len = $this->queue->len($topic);
+            $this->logger->log($topic . ' pop len: ' . $len, 'info');
+            if ($len > 0) {
+                //循环拿出队列消息
+                while ($data = $this->queue->pop($topic)) {
+                    //主进程状态不是running状态,退出循环
+                    if (MProcess::STATUS_RUNNING != $this->getMasterData('status')) {
+                        break;
+                    }
+                    $this->logger->log('pop data: ' . print_r($data, true), 'info');
+                    if (!empty($data) && is_object($data)) {
+                        $beginTime=microtime(true);
+                        // 根据自己的业务需求改写此方法
+                        $jobObject               =  $this->loadObject($data);
+                        $baseAction              =  $this->loadFrameworkAction();
+                        $baseAction->start($jobObject);
+                        $endTime=microtime(true);
+                        $this->logger->log('job id ' . $jobObject->uuid . ' done, spend time: ' . ($endTime - $beginTime), 'info');
+                        unset($jobObject, $baseAction);
+                    } else {
+                        $this->logger->log('pop error data: ' . print_r($data, true), 'error');
+                    }
+                    // if ($this->queue->len($topic) <= 0) {
+                    //     break;
+                    // }
+                }
+            } else {
+                $this->logger->log($topic . ' no work to do!', 'info');
+                sleep($this->sleep);
+                //$this->logger->log('sleep ' . $this->sleep . ' second!', 'info');
+            }
+            $this->queue->close();
+        } else {
+            $this->logger->log('All topic no work to do!', 'info');
+        }
+    }
+
+    /**
+     * 获取主进程状态
+     * @param string $key
+     * @return mixed|null
+     */
+    private function getMasterData($key='')
+    {
+        $data=unserialize(file_get_contents($this->pidInfoFile));
+        if ($key) {
+            return $data[$key] ?: null;
+        }
+
+        return $data;
+    }
+
+    //实例化job对象
+    private function loadObject($data)
+    {
+        if (is_object($data)) {
+            return $data;
+        }
+
+        return fasle;
+    }
+
+    //根据配置装入不同的框架
+    private function loadFrameworkAction()
+    {
+        $classFramework=$this->config['framework']['class'] ?: 'Jobs\Controller\CJobs';
+        try {
+            $action = new $classFramework($this->config);
+        } catch (\Throwable $e) {
+            Utils::catchError($this->logger, $e);
+        } catch (\Exception $e) {
+            Utils::catchError($this->logger, $e);
+        }
+
+        return $action;
+    }
+}

+ 441 - 0
Jobs/Model/MProcess.Class.php

@@ -0,0 +1,441 @@
+<?php
+namespace Jobs\Model;
+
+use Mall\Framework\Factory;
+
+use Jobs\Model\MJobs;
+
+class MProcess
+{
+    const CHILD_PROCESS_CAN_RESTART                    ='staticWorker'; //子进程可以重启,进程个数固定
+    const CHILD_PROCESS_CAN_NOT_RESTART                ='dynamicWorker'; //子进程不可以重启,进程个数根据队列堵塞情况动态分配
+    const STATUS_RUNNING                               ='runnning'; //主进程running状态
+    const STATUS_WAIT                                  ='wait'; //主进程wait状态
+    const STATUS_STOP                                  ='stop'; //主进程stop状态
+    const APP_NAME                                     ='jobs'; //app name
+    const STATUS_HSET_KEY_HASH                         ='status'; //status hash名
+
+    public $processName                   = ':swooleProcessTopicQueueJob'; // 进程重命名, 方便 shell 脚本管理
+    public $workers                       = [];
+
+    private $version                      = '2.5';
+    private $excuteTime                   =3600; //子进程最长执行时间,单位:秒
+    private $queueMaxNum                  =10; //队列达到一定长度,启动动态子进程个数发和送消息提醒
+    private $queueTickTimer               =1000 * 10; //一定时间间隔(毫秒)检查队列长度;默认10秒钟
+    private $messageTickTimer             =1000 * 180; //一定时间间隔(毫秒)发送消息提醒;默认3分钟
+    private $message                      =[]; //提醒消息内容
+    private $workerNum                    =0; //固定分配的子进程个数
+    private $dynamicWorkerNum             =[]; //动态(不能重启)子进程计数,最大数为每个topic配置workerMaxNum,它的个数是动态变化的
+    private $workersInfo                  =[];
+    private $ppid;
+    private $config                       = [];
+    private $pidFile                      = 'master.pid'; //pid存放文件
+    private $pidInfoFile                  = 'master.info'; //pid 序列化信息
+    private $pidStatusFile                = 'status.info'; //pid status信息
+    private $status                       = '';
+    private $logger                       = null;
+    private $queue                        = null;
+    private $topics                       = null;
+    private $beginTime                    = '';
+    private $logSaveFileWorker            = 'workers.log';
+
+    public function __construct($config)
+    {
+        $this->config                    = $config;
+        $this->logger                    = Factory::logs($this->config['logPath'] ?:'', $this->config['logSaveFileApp'] ?:'', $this->config['app_name'] ?:'');
+        $this->topics                    = $this->config['job']['topics'] ?: [];
+        $this->processName               = $this->config['processName'] ?: $this->processName;
+        $this->excuteTime                = $this->config['excuteTime'] ?: $this->excuteTime;
+        $this->queueMaxNum               = $this->config['queueMaxNum'] ?: $this->queueMaxNum;
+        $this->queueTickTimer            = $this->config['queueTickTimer'] ?: $this->queueTickTimer;
+        $this->messageTickTimer          = $this->config['messageTickTimer'] ?: $this->messageTickTimer;
+        $this->logSaveFileWorker         = $this->config['logSaveFileWorker'] ?: $this->logSaveFileWorker;
+
+        $this->beginTime=time();
+        //该变量需要在多进程共享
+        $this->status=self::STATUS_RUNNING;
+
+        if (isset($this->config['pidPath']) && !empty($this->config['pidPath'])) {
+            if (!is_dir($this->config['pidPath'])) {
+                mkdir($this->config['pidPath'], '0777', true);
+            }
+            $this->pidFile      =$this->config['pidPath'] . '/' . $this->pidFile;
+            $this->pidInfoFile  =$this->config['pidPath'] . '/' . $this->pidInfoFile;
+            $this->pidStatusFile=$this->config['pidPath'] . '/' . $this->pidStatusFile;
+        } else {
+            die('config pidPath must be set!' . PHP_EOL);
+        }
+
+        /*
+         * master.pid 文件记录 master 进程 pid, 方便之后进程管理
+         * 请管理好此文件位置, 使用 systemd 管理进程时会用到此文件
+         * 判断文件是否存在,并判断进程是否在运行
+         */
+        if (file_exists($this->pidFile)) {
+            $pid=$this->getMasterData('pid');
+
+            // $signo=0,可以检测进程是否存在,不会发送默认SIGTERM信号,终止进程
+            if ($pid && @\Swoole\Process::kill($pid, 0)) {
+                die('已有进程运行中,请先结束或重启' . PHP_EOL);
+            }
+        }
+
+        // 使当前进程蜕变为一个守护进程。
+        \Swoole\Process::daemon();
+        $this->ppid    = getmypid();
+        $data['pid']   =$this->ppid;
+        $data['status']=$this->status;
+        $this->saveMasterData($data);
+        $this->setProcessName(self::APP_NAME . ' master ' . $this->ppid . $this->processName);
+        //报错解决:must be forked outside the coroutine
+        swoole_async_set(['enable_coroutine' => false]);//关闭内置协程
+    }
+
+    /**
+     * 设置进程名
+     * @param mixed $name
+     */
+    private function setProcessName($name)
+    {
+        $updateName = 0;
+        // 低版本Linux内核和Mac OSX不支持进程重命名
+        if (function_exists('cli_set_process_title') && PHP_OS != 'Darwin'){
+            if(cli_set_process_title($name)){ // 按照官网文档优先使用内置函数,php >= 5.5
+                $updateName = 1;
+            }
+        }else if (function_exists('swoole_set_process_name') && PHP_OS != 'Darwin' && !$updateName) {
+            swoole_set_process_name($name); // swoole >= 1.6.3
+        }
+    }
+
+    /**
+     * 保存守护进程数据
+     * @param array $data
+     */
+    private function saveMasterData($data=[])
+    {
+        isset($data['pid']) && file_put_contents($this->pidFile, $data['pid']);
+        file_put_contents($this->pidInfoFile, serialize($data));
+    }
+
+    /**
+     * 读取守护进程数据
+     * @param string $key
+     * @return mixed|null
+     */
+    private function getMasterData($key='')
+    {
+        $data=unserialize(file_get_contents($this->pidInfoFile));
+        if ($key) {
+            return $data[$key] ?: null;
+        }
+
+        return $data;
+    }
+
+    /**
+     * 按照配置开启每个topic进程
+     */
+    public function start()
+    {
+        $topics = $this->topics;
+        $this->logger->log('topics: ' . json_encode($topics));
+
+        if ($topics) {
+            //遍历topic任务列表
+            foreach ((array) $topics as  $topic) {
+                if (isset($topic['workerMinNum']) && isset($topic['name'])) {
+                    //每个topic开启最少个进程消费队列
+                    for ($i = 0; $i < $topic['workerMinNum']; ++$i) {
+                        $this->reserveQueue($i, $topic['name'], self::CHILD_PROCESS_CAN_RESTART);
+                    }
+                }
+            }
+        }
+
+        $this->registSignal();
+        $this->registTimer();
+    }
+
+    //增加定时器,检查队列积压情况;
+    public function registTimer()
+    {
+        \Swoole\Timer::tick($this->queueTickTimer, function ($timerId) {
+            $topics = $this->topics;
+            $this->status  =$this->getMasterData('status');
+            if (empty($this->workers) && self::STATUS_WAIT == $this->status) {
+                $this->exitMaster();
+            }
+            $this->queue   = MQueue::getQueue($this->config['job']['queue'], $this->logger);
+            if (empty($this->queue)) {
+                $this->logger->log('queue connection is lost', 'info', $this->logSaveFileWorker);
+
+                return;
+            }
+            $this->queue->setTopics($topics);
+
+            if ($topics && self::STATUS_RUNNING == $this->status) {
+                //遍历topic任务列表
+                foreach ((array) $topics as  $topic) {
+                    if (empty($topic['name'])) {
+                        continue;
+                    }
+                    $this->dynamicWorkerNum[$topic['name']]=$this->dynamicWorkerNum[$topic['name']] ?? 0;
+                    $topic['workerMaxNum']                       =$topic['workerMaxNum'] ?? 0;
+                    try {
+                        $len=$this->queue->len($topic['name']);
+                        $this->logger->log('topic: ' . $topic['name'] . ' ' . $this->status . ' len: ' . $len, 'info', $this->logSaveFileWorker);
+                    } catch (\Throwable $e) {
+                        $this->logger->log('queueError' . $e->getMessage(), 'error', $this->logSaveFileWorker);
+                    } catch (\Exception $e) {
+                        $this->logger->log('queueError: ' . $e->getMessage(), 'error', $this->logSaveFileWorker);
+                    }
+                    $this->status=$this->getMasterData('status');
+                    //消息提醒:消息体收集
+                    if ($len > $this->queueMaxNum && count($this->message) <= count($topics)) {
+                        $this->message[]= strtr('Time:{time} Pid:{pid} ProName:{pname} Topic:{topic} Message:{message}', [
+                            '{time}'   => date('Y-m-d H:i:s'),
+                            '{pid}'    => $this->ppid,
+                            '{pname}'  => $this->processName,
+                            '{topic}'  => $topic['name'],
+                            '{message}'=> '积压消息个数:' . $len . PHP_EOL,
+                        ]);
+                    }
+
+                    if ($topic['workerMaxNum'] > $topic['workerMinNum'] && self::STATUS_RUNNING == $this->status && $len > $this->queueMaxNum && $this->dynamicWorkerNum[$topic['name']] < $topic['workerMaxNum']) {
+                        $max=$topic['workerMaxNum'] - $this->dynamicWorkerNum[$topic['name']];
+                        for ($i=0; $i < $max; ++$i) {
+                            //队列堆积达到一定数据,拉起一次性子进程,这类进程不会自动重启[没必要]
+                            $this->reserveQueue($this->dynamicWorkerNum[$topic['name']], $topic['name'], self::CHILD_PROCESS_CAN_NOT_RESTART);
+                            ++$this->dynamicWorkerNum[$topic['name']];
+                            $this->logger->log('topic: ' . $topic['name'] . ' ' . $this->status . ' len: ' . $len . ' for: ' . $i . ' ' . $max, 'info', $this->logSaveFileWorker);
+                        }
+                    }
+                }
+            }
+            $this->queue->close();
+        });
+        //积压队列提醒
+        \Swoole\Timer::tick($this->messageTickTimer, function ($timerId) {
+            !empty($this->message) && $this->logger->log('Warning Message: ' . implode('', $this->message), 'warning', $this->logSaveFileWorker);
+            if ($this->message && isset($this->config['message'])) {
+                //$message =Message::getMessage($this->config['message']);
+                //$message->send(implode('', $this->message), $this->config['message']['token']);
+            }
+            //重置message,防止message不断变长
+            $this->message=[];
+        });
+    }
+
+    //退出主进程
+    private function exitMaster()
+    {
+        @unlink($this->pidFile);
+        @unlink($this->pidInfoFile);
+        $this->logger->log('Time: ' . microtime(true) . '主进程' . $this->ppid . '退出', 'info', $this->logSaveFileWorker);
+
+        sleep(1);
+        exit();
+    }
+
+    /**
+     * 注册信号
+     */
+    public function registSignal()
+    {
+        //终止进程信号
+        \Swoole\Process::signal(SIGTERM, function ($signo) {
+            $this->killWorkersAndExitMaster();
+        });
+        //
+        \Swoole\Process::signal(SIGKILL, function ($signo) {
+            $this->killWorkersAndExitMaster();
+        });
+        //平滑退出
+        \Swoole\Process::signal(SIGUSR1, function ($signo) {
+            $this->waitWorkers();
+        });
+        //记录进程状态
+        \Swoole\Process::signal(SIGUSR2, function ($signo) {
+            $this->logger->log('[master pid: ' . $this->ppid . '] has been received  signal' . $signo);
+            $result=$this->showStatus();
+            $this->saveSwooleJobsStatus($result);
+            //echo $result;
+        });
+
+        // 在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程
+        \Swoole\Process::signal(SIGCHLD, function ($signo) {
+            while (true) {
+                try {
+                    $ret = \Swoole\Process::wait(false);
+                } catch (\Exception $e) {
+                    $this->logger->log('signoError: ' . $signo . $e->getMessage(), 'error', $this->logSaveFileWorker);
+                }
+                if ($ret) {
+                    $pid           = $ret['pid'];
+                    $childProcess = $this->workers[$pid];
+                    $topic = $this->workersInfo[$pid]['topic'] ?: '';
+                    $this->status=$this->getMasterData('status');
+                    if(empty($this->dynamicWorkerNum) || empty($this->dynamicWorkerNum[$topic])){
+                        $topicCanNotRestartNum = null;
+                    }else{
+                        $topicCanNotRestartNum =  $this->dynamicWorkerNum[$topic];
+                    }
+                    // $topicCanNotRestartNum =  $this->dynamicWorkerNum[$topic] ?: 'null';
+                    $this->logger->log(self::CHILD_PROCESS_CAN_RESTART . '---' . $topic . '***' . $topicCanNotRestartNum . '***' . $this->status . '***' . $this->workersInfo[$pid]['type'] . '***' . $pid, 'info', $this->logSaveFileWorker);
+                    $this->logger->log($pid . ',' . $this->status . ',' . self::STATUS_RUNNING . ',' . $this->workersInfo[$pid]['type'] . ',' . self::CHILD_PROCESS_CAN_RESTART, 'info', $this->logSaveFileWorker);
+
+                    //主进程状态为running并且该子进程是可以重启的
+                    if (self::STATUS_RUNNING == $this->status && $this->workersInfo[$pid]['type'] == self::CHILD_PROCESS_CAN_RESTART) {
+                        try {
+                            //子进程重启可能失败,必须启动成功之后,再往下执行;最多尝试30次
+                            for ($i=0; $i < 30; ++$i) {
+                                var_dump("aaaa");
+                                $newPid = $childProcess->start();
+                                var_dump("xxxxxx".$newPid);
+                                if ($newPid > 0) {
+                                    break;
+                                }
+                                sleep(1);
+                            }
+                            if (!$newPid) {
+                                $this->logger->log('静态子进程重启失败,问题有点严重,平滑退出子进程,主进程会跟着退出', 'error', $this->logSaveFileWorker);
+                                $this->waitWorkers();
+                                //$this->reserveQueue(0, $topic);
+                                continue;
+                            }
+
+                            $this->workers[$newPid] = $childProcess;
+                            $this->workersInfo[$newPid]['type'] = self::CHILD_PROCESS_CAN_RESTART;
+                            $this->workersInfo[$newPid]['topic'] = $topic;
+                            ++$this->workerNum;
+                            $this->logger->log("Worker Restart, kill_signal={$ret['signal']} PID=" . $newPid, 'info', $this->logSaveFileWorker);
+                        } catch (\Throwable $e) {
+                            $this->logger->log('restartErrorThrow' . $e->getMessage(), 'error', $this->logSaveFileWorker);
+                        } catch (\Exception $e) {
+                            $this->logger->log('restartError: ' . $e->getMessage(), 'error', $this->logSaveFileWorker);
+                        }
+                    }
+                    //某个topic动态变化的子进程,退出之后个数减少一个
+                    if ($this->workersInfo[$pid]['type'] == self::CHILD_PROCESS_CAN_NOT_RESTART) {
+                        --$this->dynamicWorkerNum[$topic];
+                    }
+                    $this->logger->log("Worker Exit, kill_signal={$ret['signal']} PID=" . $pid, 'info', $this->logSaveFileWorker);
+                    unset($this->workers[$pid], $this->workersInfo[$pid]);
+                    --$this->workerNum;
+
+                    $this->logger->log('Worker count: ' . count($this->workers) . '==' . $this->workerNum, 'info', $this->logSaveFileWorker);
+                    //如果$this->workers为空,且主进程状态为wait,说明所有子进程安全退出,这个时候主进程退出
+                    if (empty($this->workers) && self::STATUS_WAIT == $this->status) {
+                        $this->logger->log('主进程收到所有信号子进程的退出信号,子进程安全退出完成', 'info', $this->logSaveFileWorker);
+                        $this->exitMaster();
+                    }
+                } else {
+                    break;
+                }
+            }
+        });
+    }
+
+    private function saveSwooleJobsStatus($data)
+    {
+        file_put_contents($this->pidStatusFile, $data);
+    }
+
+    private function showStatus()
+    {
+        $statusStr  ='-------------------------------------' . $this->processName . ' status--------------------------------------------' . PHP_EOL;
+        $statusStr .= 'PHP version:' . PHP_VERSION . '      swoole-jobs version: ' . $this->version . PHP_EOL;
+        $statusStr .= 'start time : ' . date('Y-m-d H:i:s', $this->beginTime) . '   run ' . floor((time() - $this->beginTime) / (24 * 60 * 60)) . ' days ' . floor(((time() - $this->beginTime) % (24 * 60 * 60)) / (60 * 60)) . ' hours   ' . PHP_EOL;
+        //$statusStr .= Utils::getSysLoadAvg() . '   memory use:' . Utils::getServerMemoryUsage() . PHP_EOL;
+        $statusStr .= '|-- Master pid ' . $this->ppid . ' status: ' . $this->status . ' Worker num: ' . count($this->workers) . PHP_EOL;
+        if ($this->workers) {
+            foreach ($this->workers as $pid => $value) {
+                $type =$this->workersInfo[$pid]['type'];
+                $topic=$this->workersInfo[$pid]['topic'];
+
+                $statusStr .= '    |---- Worker pid:  ' . $pid . ' ' . $type . ' ' . $topic . PHP_EOL;
+            }
+        }
+
+        return $statusStr;
+    }
+
+    //平滑等待子进程退出之后,再退出主进程
+    private function waitWorkers()
+    {
+        $data['pid']   =$this->ppid;
+        $data['status']=self::STATUS_WAIT;
+        $this->saveMasterData($data);
+        $this->status = self::STATUS_WAIT;
+        $this->logger->log('master status: ' . $this->status, 'info', $this->logSaveFileWorker);
+    }
+
+    //强制杀死子进程并退出主进程
+    private function killWorkersAndExitMaster()
+    {
+        //修改主进程状态为stop
+        $this->status   =self::STATUS_STOP;
+        if ($this->workers) {
+            foreach ($this->workers as $pid => $worker) {
+                //强制杀workers子进程
+                \Swoole\Process::kill($pid);
+                unset($this->workers[$pid]);
+                $this->logger->log('主进程收到退出信号,[' . $pid . ']子进程跟着退出', 'info', $this->logSaveFileWorker);
+                $this->logger->log('Worker count: ' . count($this->workers), 'info', $this->logSaveFileWorker);
+            }
+        }
+        $this->exitMaster();
+    }
+
+    /**
+     * fork子进程消费队列.
+     *
+     * @param [type] $num   子进程编号
+     * @param [type] $topic topic名称
+     * @param string $type  是否会重启 canRestart|unRestart
+     */
+    public function reserveQueue($num, $topic, $type=self::CHILD_PROCESS_CAN_RESTART)
+    {
+        $reserveProcess = new \Swoole\Process(function ($worker) use ($num, $topic, $type) {
+            $this->checkMpid($worker);
+            $beginTime=microtime(true);
+            try {
+                //设置进程名字
+                $this->setProcessName($type . ' ' . $topic . ' job ' . $num . ' ' . $this->processName);
+                $jobs  = new MJobs($this->pidInfoFile, $this->config);
+                do {
+                    $jobs->run($topic);
+                    $this->status=$this->getMasterData('status');
+                    $where = (self::STATUS_RUNNING == $this->status) && (self::CHILD_PROCESS_CAN_RESTART == $type ? time() < ($beginTime + $this->excuteTime) : false);
+                } while ($where);
+            } catch (\Throwable $e) {
+                catchError($this->logger, $e);
+            } catch (\Exception $e) {
+                catchError($this->logger, $e);
+            }
+
+            $endTime=microtime(true);
+            $this->logger->log($topic . ' worker id: ' . $num . ' is done!!! Timing: ' . ($endTime - $beginTime), 'info', $this->logSaveFileWorker);
+            unset($num, $topic, $type);
+        });
+        $pid                                        = $reserveProcess->start();
+        $this->workers[$pid]                        = $reserveProcess;
+        $this->workersInfo[$pid]['type']            = $type;
+        $this->workersInfo[$pid]['topic']           = $topic;
+        $this->logger->log('topic: ' . $topic . ' ' . $type . ' worker id: ' . $num . ' pid: ' . $pid . ' is start...', 'info', $this->logSaveFileWorker);
+        ++$this->workerNum;
+    }
+
+    /**
+     * 主进程如果不存在了,子进程退出
+     **/
+    private function checkMpid(&$worker)
+    {
+        if (!@\Swoole\Process::kill($this->ppid, 0)) {
+            $worker->exit();
+            $this->logger->log("Master process exited, I [{$worker['pid']}] also quit", 'info', $this->logSaveFileWorker);
+        }
+    }
+
+}

+ 25 - 0
Jobs/Model/MQueue.Class.php

@@ -0,0 +1,25 @@
+<?php
+namespace Jobs\Model;
+
+class MQueue
+{
+    public static function getQueue(array $config, $logger)
+    {
+        $classQueue = $config['class'] ?: 'Jobs\Model\Queue\MRedisTopicQueue';
+        if (is_callable([$classQueue, 'getConnection'])) {
+            //最多尝试连接3次
+            for ($i=0; $i < 3; $i++) {
+                $connection = $classQueue::getConnection($config, $logger);
+                if ($connection && is_object($connection)) {
+                    break;
+                }else{
+                    $logger->log("connect...,retry=".($i+1), 'error');
+                }
+            }
+
+            return $connection;
+        }
+        echo 'you must add queue config' . PHP_EOL;
+        exit;
+    }
+}

+ 85 - 0
Jobs/Model/MTopic/Enterprise/MEnterprise.Class.php

@@ -0,0 +1,85 @@
+<?php
+/**
+ * 企业
+ * Created by PhpStorm.
+ * User: 小威
+ * Date: 2020/06/30
+ * Time: 18:41
+ */
+
+namespace Jobs\Model\MTopic\Enterprise;
+
+
+use Mall\Framework\Core\ErrorCode;
+use Mall\Framework\Core\ResultWrapper;
+use Mall\Framework\Core\StatusCode;
+
+class MEnterprise
+{
+    private $enterpriseId;
+
+    private $userCenterId;
+
+    private $objDMessage;
+
+    /**
+     * MGrantCoupon constructor.
+     * @throws \Exception
+     */
+    public function __construct()
+    {
+        echo '企业创建自动创建数据:' . date('Y-d-d H:i:s') . ':' . PHP_EOL;
+    }
+
+    /**
+     * 自动创建
+     * @param $params
+     * @return bool
+     * @throws \Exception
+     */
+    public function autoCreate($params)
+    {
+        echo '开始执行自动创建队列方法**************************************************************************************************************************';
+        var_dump($params);
+        $project_domain = $params['data']['PROJECT_DOMAIN'];
+        unset($params['data']['PROJECT_DOMAIN']);
+        if(empty($params['data']['enterpriseId'])){
+            echo '企业id参数错误'.PHP_EOL;
+            return false;
+        }
+        if(empty($params['data']['userCenterId'])){
+            echo '用户id参数错误'.PHP_EOL;
+            return false;
+        }
+        if(empty($params['data']['Authorization'])){
+            echo 'Authorization参数错误'.PHP_EOL;
+            return false;
+        }
+
+        switch ($project_domain){
+            case 'api':
+                $ip = URL_API.'/Enterprise/UserEnterprise/autoCreateData';
+                break;
+            default:
+                $ip = DEV_URL_API.'/Enterprise/UserEnterprise/autoCreateData';
+        }
+
+        $postData = [
+            'enterpriseId' => $params['data']['enterpriseId'],
+            'userCenterId' => $params['data']['userCenterId']
+        ];
+        $post = json_encode($postData);
+        $curl_option = [
+            CURLOPT_HTTPHEADER => [
+                'Content-Type:text/plain',
+                'Authorization:'.$params['data']['Authorization'],
+            ],
+        ];
+        $result = request($ip, $post, 40, false,$curl_option);
+        print_r($result);
+        echo '成功' . PHP_EOL;
+        return true;
+    }
+
+
+}

+ 252 - 0
Jobs/Model/MTopic/Finance/MPayable.Class.php

@@ -0,0 +1,252 @@
+<?php
+/**
+ * 财务应付生成和检测脚本
+ * Created by PhpStorm.
+ * User: phperstar
+ * Date: 2019/12/19
+ * Time: 10:38 AM
+ */
+namespace Jobs\Model\MTopic\Finance;
+
+use Mall\Framework\Core\StatusCode;
+
+use Jobs\Cache\FinanceCache;
+
+use Jobs\Dao\Purchase\DPurchase;
+use Jobs\Dao\Purchase\DPurchaseOut;
+use Jobs\Dao\Stock\DInventoryOut;
+use Jobs\Dao\Stock\DInventoryInDetails;
+use Jobs\Dao\Finance\DPay;
+use Jobs\Dao\Finance\DPayReceiptIndex;
+
+
+class MPayable
+{
+    private $objDPurchase;
+    private $objDPurchaseOut;
+    private $objDInventoryOut;
+    private $objDPay;
+    private $objDPayReceiptIndex;
+
+    public function __construct()
+    {
+        $this->objDPurchase = new DPurchase('stock');
+        $this->objDPurchaseOut = new DPurchaseOut('stock');
+        $this->objDInventoryOut = new DInventoryOut('stock');
+        $this->objDPay = new DPay('finance');
+        $this->objDPayReceiptIndex = new DPayReceiptIndex('finance');
+        //$this->objDPay->setSearchIndex('should_pay_receipt_search')->setType('should_pay_receipt');
+    }
+
+    /**
+     * 检测销售出库单是否都生成应收单了
+     */
+    public function checkSaleOutCreateReceivable()
+    {
+
+    }
+
+    /**
+     * 采购入库单生成应付单
+     */
+    public function createPayable($params)
+    {
+        if (empty($params)) {
+            echo '请求创建应付单参数为空'.PHP_EOL;
+            return false;
+        }
+
+        if (empty($params['type'])) {
+            echo '生成应付单:type字段错误,请提供单据类型'.PHP_EOL;
+            return false;
+        }
+
+        $payableExtraData = [];
+        $purchaseData = [];
+        $payableData = [];
+        //采购入库生成应付
+        if($params['type'] == StatusCode::$orderType['purchaseIn']) {
+            // 查询采购单信息
+            $this->objDPurchase->setTable($this->objDPurchase->get_Table() . '_' . $params['enterpriseId']);
+            $purchaseData = $this->objDPurchase->get(['id'=>$params['id']]);
+            if($purchaseData === false){
+                echo 'sql错误'.$this->objDPurchase->error().PHP_EOL;
+                return false;
+            }
+
+            if (empty($purchaseData)) {
+                echo '采购订单id:' . $params['id'] . '的数据为空'.PHP_EOL;
+                return false;
+            }
+
+            // 获取入库金额生成应付
+            $objDInventoryInDetails = new DInventoryInDetails('stock');
+            $objDInventoryInDetails->setTable('qianniao_inventory_in_details_'.$params['enterpriseId']);
+            $inDetailsData = $objDInventoryInDetails->select(['linkId' => $params['inventoryInId']]);
+            if($inDetailsData === false){
+                echo '查询采购订单对应的入库单详情数据错误'.$objDInventoryInDetails->error().PHP_EOL;
+                return false;
+            }
+            if( empty($inDetailsData) ){
+                echo '采购订单'.$params['id'].'对应的入库单详情数据为空'.PHP_EOL;
+                return false;
+            }
+
+            $payMoney = 0;
+            foreach ($inDetailsData as $key => $value){
+                $payMoney = bcadd($payMoney, bcmul($value['inNum'], $value['unitPrice']), 2);
+            }
+
+            $payableExtraData = [
+                'supplierId' => $purchaseData['supplierId'],
+                'supplierName' => $purchaseData['supplierName'],
+                'sourceNo' => $purchaseData['no'],
+                //'payMoney' => $payMoney, // 应付 = 实际采购总成本-均摊费用
+                'payMoney' => bcadd(bcsub($purchaseData['purchaseAmount'],$purchaseData['couponAmount'],2),$purchaseData['otherAmount'],2),
+                'discountMoney' => $purchaseData['couponAmount'],
+                'receiptTypeId' => StatusCode::$orderType['purchaseOrder'],
+                'purchaseId' => $purchaseData['id'],
+                'notOffsetMoney' => $payMoney,
+            ];
+            // 创建应付单数据
+            $payableData = [
+                'warehouseId' => $purchaseData['warehouseId'],
+                'warehouseName' => $purchaseData['warehouseName'],
+                'financeTypeId' => StatusCode::$orderType['purchaseReturn'],
+                'financeType'   => '采购单',
+                'auditStatus' => StatusCode::$auditStatus['auditing'],
+                'createTime'  => time(),
+                'updateTime'  => time(),
+            ];
+        }
+
+        //采购退货出库生成应付
+        if($params['type'] == StatusCode::$orderType['purchaseReturnOut']) {
+            // 查询采购退货出库单信息
+            $this->objDInventoryOut->setTable($this->objDInventoryOut->get_Table() . '_' . $params['enterpriseId']);
+            $inventoryOutData = $this->objDInventoryOut->get(['id'=>$params['id']]);
+            if($inventoryOutData === false){
+                echo 'sql错误'.$this->objDInventoryOut->error().PHP_EOL;
+                return false;
+            }
+
+            if (empty($inventoryOutData)) {
+                echo '采购退货出库单id:' . $params['id'] . '的数据为空'.PHP_EOL;
+                return false;
+            }
+
+            //查出源采购退货单对应的供应商Id和名称
+            $this->objDPurchaseOut->setTable($this->objDPurchaseOut->get_Table() . '_' . $params['enterpriseId']);
+            $purchaseData = $this->objDPurchaseOut->get(['no'=>$inventoryOutData['sourceNo']]);
+            if($purchaseData === false){
+                echo 'sql错误'.$this->objDPurchase->error().PHP_EOL;
+                return false;
+            }
+
+            if (empty($purchaseData)) {
+                echo '采购退货单no:' . $inventoryOutData['originNo'] . '的数据为空'.PHP_EOL;
+                return false;
+            }
+
+            $payableExtraData = [
+                'supplierId' => $purchaseData['supplierId'],
+                'supplierName' => $purchaseData['supplierName'],
+                'sourceNo' => $purchaseData['originNo'],//采购订单编号
+                'payMoney' => '-'.bcadd($purchaseData['purchaseAmount'], $purchaseData['otherAmount'],4),//实际应付金额
+                'discountMoney' => 0,
+                'receiptTypeId' => StatusCode::$orderType['purchaseReturn'],
+                'purchaseId' => $purchaseData['originId'],
+                'notOffsetMoney'=>'-'.bcadd($purchaseData['purchaseAmount'], $purchaseData['otherAmount'],4),//实际应付金额
+            ];
+            // 创建应付单数据
+            $payableData = [
+                'warehouseId' => $purchaseData['warehouseId'],
+                'warehouseName' => $purchaseData['warehouseName'],
+                'financeTypeId' => StatusCode::$orderType['purchaseReturn'],
+                'financeType'   => '采购退款单',
+                'auditStatus' => StatusCode::$auditStatus['auditing'],
+                'createTime'  => time(),
+                'updateTime'  => time(),
+            ];
+        }
+
+        /*foreach ($payableData as $key => $value) {
+            if (empty($value) && $value !== 0) {
+                echo $key . '参数错误';
+                return false;
+            }
+        }*/
+        $payableData = array_merge($payableData, $payableExtraData);
+
+        // 添加应付单
+        $this->objDPay->beginTransaction();
+        $this->objDPay->setTable('qianniao_pay_receipt_' . $params['enterpriseId'] . '_' . date('Y') . '_' . ceil(date('m') / 3));
+
+        //添加应付单no编号
+        $dbResult =  $this->objDPay->get('createTime >='.strtotime(date('Ymd'.'0:0:0')), 'no', 'createTime desc');
+        if ($dbResult === false) {
+            echo '数据库查询失败'.$this->objDPay->error().PHP_EOL;
+            return false;
+        }
+        if(empty($dbResult)){
+            $payableData['no'] = createSerialNumberByDate('');
+        }else{
+            $payableData['no'] = createSerialNumberByDate($dbResult['no']);
+        }
+
+        //索引表数据
+        $indexData = [
+            'payReceiptId' => 0,
+            'supplierId'       => $payableData['supplierId'],
+            'sourceNo'         => $payableData['sourceNo'],
+            'auditStatus'      => $payableData['auditStatus'],
+            'financeTypeId'    => $payableData['financeTypeId'],
+            'financeType'      => $payableData['financeType'],
+            'warehouseId'      => $payableData['warehouseId'],
+            'createTime'       => $payableData['createTime'],
+            'updateTime'       => $payableData['updateTime'],
+
+        ];
+        $this->objDPayReceiptIndex->setTable('qianniao_pay_receipt_index_' . $params['enterpriseId']);
+        $payReceiptId = $this->objDPayReceiptIndex->insert($indexData);
+        if ($payReceiptId === false) {
+            $this->objDPay->rollBack();
+            echo 'sql错误'.$this->objDPayReceiptIndex->error().PHP_EOL;
+            return false;
+        }
+
+        $payableData['id'] = $payReceiptId;
+        $payableId = $this->objDPay->insert($payableData);
+        if ($payableId === false) {
+            $this->objDPay->rollBack();
+            echo 'sql错误'.$this->objDPay->error().PHP_EOL;
+            return false;
+        }
+
+
+        $this->objDPay->commit();
+
+        /*
+        $_id = self::createEsDocumentId($payableId, $params['enterpriseId']);
+        $esData = $payableData;
+        $esData['id'] = $payableId;
+        $esData['enterpriseId'] = $params['enterpriseId'];
+        $result = $this->objDPay->addUpSearchIndexDocument($esData, $_id);
+        if (isset($result['_shards']) && isset($result['_shards']['successful']) && $result['_shards']['successful'] == 1) {
+            //echo "es操作成功";die;
+        }*/
+
+        $objFinanceCache = new FinanceCache();
+        $objFinanceCache->cacheExistPayEnterprise($params['enterpriseId']);
+
+        echo '添加应付单成功'.PHP_EOL;
+        return true;
+    }
+
+    private function createEsDocumentId($payId, $enterpriseId)
+    {
+        $t = date('Y') . '_' . ceil(date('m') / 3);
+        return 'EnterpriseId_' . $enterpriseId . '_'. $t .'_payId_' . $payId;
+    }
+
+}

+ 488 - 0
Jobs/Model/MTopic/Finance/MReceivable.Class.php

@@ -0,0 +1,488 @@
+<?php
+/**
+ * 财务应收生成和检测脚本
+ * Created by PhpStorm.
+ * User: phperstar
+ * Date: 2019/12/19
+ * Time: 10:38 AM
+ */
+
+namespace Jobs\Model\MTopic\Finance;
+
+use Jobs\Dao\DShop;
+use Mall\Framework\Factory;
+use Jobs\Cache\OverviewCache;
+use Jobs\Dao\Purchase\DPurchase;
+use Jobs\Dao\Stock\DInventoryBatch;
+use Jobs\Dao\Stock\DWarehouse;
+use Mall\Framework\Core\StatusCode;
+use Jobs\Dao\Stock\DInventoryDetails;
+use Jobs\Dao\Order\DOrder;
+use Jobs\Dao\Stock\DInventoryIn;
+use Jobs\Dao\Stock\DInventoryOut;
+use Jobs\Dao\Finance\DReceiveReceiptIndex;
+use Jobs\Dao\Finance\DReceive;
+use Jobs\Cache\FinanceCache;
+
+
+class MReceivable
+{
+    private $objDOrder;
+    private $objDInventoryIn;
+    private $objDReceive;
+    private $objDReceiveReceiptIndex;
+    private $objFinanceCache;
+    private $objDInventoryDetails;
+    private $objDWarehouse;
+    private $enterpriseId;
+    private $objOverviewCache;
+    private $objDInventoryBatch;
+    private $objDPurchase;
+    private $objDShop;
+
+    private $cutTable = 200000; // 订单表切割基数
+
+    public function __construct()
+    {
+        $this->objDOrder = new DOrder('default');
+        $this->objDInventoryIn = new DInventoryIn('stock');
+        $this->objDReceive = new DReceive('finance');
+        $this->objDReceiveReceiptIndex = new DReceiveReceiptIndex('finance');
+        $this->objFinanceCache = new FinanceCache();
+        $this->objDInventoryDetails = new DInventoryDetails();
+        $this->objDWarehouse = new DWarehouse();
+        $this->objOverviewCache = new OverviewCache();
+        $this->objDInventoryBatch = new DInventoryBatch();
+        $this->objDPurchase = new DPurchase();
+        $this->objDShop = new DShop();
+
+        $this->objDReceive->setSearchIndex('should_receive_receipt_search')->setType('should_receive_receipt');
+
+    }
+
+    /**
+     * @param $params
+     */
+    public function checkExistCreate($params)
+    {
+        echo '检查应收单开始'.PHP_EOL;
+        print_r($params);
+        self::checkSaleOutCreateReceivable($params['enterpriseId']);
+        echo '检查成功'.PHP_EOL;
+    }
+
+    /**
+     * 检测销售出库单是否都生成应收单了
+     */
+    public function checkSaleOutCreateReceivable($enterpriseId)
+    {
+        $financeDbName = Factory::config()->get('db.finance.dbname');
+        $stockDbName = Factory::config()->get('db.stock.dbname');
+        $defaultDbName = Factory::config()->get('db.default.dbname');
+
+        if (!$financeDbName || !$stockDbName || !$defaultDbName) {
+            echo "dbname获取错误";
+            return false;
+        }
+
+        //查出该企业下 审核通过但未生成应收单的销售出库单
+        $sql = 'select a.originId,a.customerId,a.type,b.userCenterId,a.id from `'. $stockDbName .'`.`qianniao_inventory_out_'. $enterpriseId .'` as a JOIN `'. $defaultDbName .'`.qianniao_customer_'. $enterpriseId .' AS b ON a.customerId=b.id AND a.sourceNo NOT IN (select sourceNo from `'. $financeDbName .'`.`qianniao_receive_receipt_index_'. $enterpriseId .'`) and a.auditStatus = '. StatusCode::$auditStatus['auditPass'].' AND a.type ='.StatusCode::$orderType['saleOut'];
+        $salesOutData = $this->objDOrder->query($sql);
+        if (!empty($salesOutData)) {
+            foreach ($salesOutData as $salesOut) {
+                $result = self::createReceivable([
+                    'userCenterId' => $salesOut['userCenterId'],
+                    'enterpriseId' => $enterpriseId,
+                    'id'           => $salesOut['id'],//订单id
+                    'type'         => StatusCode::$orderType['saleOut'],
+                    'checkOldData'  => false
+                ]);
+                print_r('订单'.$salesOut['originId'].'生成应收'.$result);
+            }
+        }
+
+
+        //查出该企业下 审核通过但未生成应收单的销售出库单
+        $sql = 'select a.id,a.type,b.userCenterId from `'. $stockDbName .'`.`qianniao_inventory_in_'. $enterpriseId .'` as a JOIN `'. $defaultDbName .'`.qianniao_order_index_'. $enterpriseId .' AS b ON a.originId=b.id AND a.originNo NOT IN (select sourceNo from `'. $financeDbName .'`.`qianniao_receive_receipt_index_'. $enterpriseId .'`) and a.auditStatus = '. StatusCode::$auditStatus['auditPass'].' AND a.type ='.StatusCode::$orderType['saleReturnIn'];
+        $saleReturnInData = $this->objDOrder->query($sql);
+        if (!empty($saleReturnInData)) {
+            foreach ($saleReturnInData as $saleReturnIn) {
+                $result = self::createReceivable([
+                    'userCenterId' => $saleReturnIn['userCenterId'],
+                    'enterpriseId' => $enterpriseId,
+                    'id'           => $saleReturnIn['id'],//入库单Id
+                    'type'         => StatusCode::$orderType['saleReturnIn'],
+                    'checkOldData'  => false
+                ]);
+                print_r('入库单'.$saleReturnIn['id'].'生成应收'.$result);
+            }
+        }
+    }
+
+    /**
+     * 销售出库单生成应收单
+     * @param $params
+     * @return bool
+     * @throws \Exception
+     */
+    public function createReceivable($params)
+    {
+        // 参数校验
+        if (empty($params)) {
+            echo '请求创建应收单参数为空' . PHP_EOL;
+            return false;
+        }
+
+        foreach ($params as $key => $value){
+            if(empty($value)){
+                echo $key.'参数错误' . PHP_EOL;
+                return false;
+            }
+        }
+        $this->enterpriseId = $params['enterpriseId'];
+
+        $receiveExtraData = [];
+        $orderData = [];
+        $receiveData = [];
+        //销售出库生成应收
+        if ($params['type'] == StatusCode::$orderType['saleOut']) {
+            // 查询销售出库单数据
+            $objDInventoryOut  = new DInventoryOut('stock');
+            $objDInventoryOut->setTable('qianniao_inventory_out_'.$params['enterpriseId']);
+            $inventoryOutData = $objDInventoryOut->get(['id'=>$params['id']]);
+            if($inventoryOutData === false){
+                echo 'sql错误'.$objDInventoryOut->error().PHP_EOL;
+                return false;
+            }
+
+            if (empty($inventoryOutData)) {
+                echo '销售出库单id:' . $params['id'] . '的数据为空'.PHP_EOL;
+                return false;
+            }
+
+            // 查询订单信息
+            $tableName = $this->objDOrder->getTableName('qianniao_order_' . $params['enterpriseId'], $params['userCenterId'], $this->cutTable);
+            $this->objDOrder->setTable($tableName);
+            $orderData = $this->objDOrder->get(['id' => $inventoryOutData['originId']]);
+            if ($orderData === false) {
+                echo 'sql错误' . $this->objDOrder->error() . PHP_EOL;
+                return false;
+            }
+
+            if (empty($orderData)) {
+                echo '源销售订单id:' . $inventoryOutData['originId'] . '的数据为空' . PHP_EOL;
+                return false;
+            }
+
+            // 统计首页概况今日毛利
+            self::getInfo($orderData);
+
+            // 组装应收单数据
+            $receiveExtraData = [
+                'orderId'       => $orderData['id'], // 销售出库单对应的应收单订单id为销售订单id
+                'customerId'    => $orderData['customerId'],
+                'customerName'  => $orderData['customerName'],
+                'sourceNo'      => $orderData['no'],
+                'receiveMoney'  => $inventoryOutData['amount'],
+                'notOffsetMoney'=> $inventoryOutData['amount'],
+                'discountMoney' => 0,
+                'receiptTypeId' => StatusCode::$orderType['saleOrder'],
+
+            ];
+            // 创建应收单数据
+            $receiveData = [
+                'shopId'        => $orderData['shopId'],
+                'shopName'      => $orderData['shopName'],
+                'financeTypeId' => StatusCode::$orderType['saleOut'],
+                'financeType'   => '销售单',
+                'auditStatus'   => StatusCode::$auditStatus['auditing'],
+                'createTime'    => time(),
+                'updateTime'    => time(),
+            ];
+        }
+
+
+        //销售退货入库生成应收
+        if ($params['type'] == StatusCode::$orderType['saleReturnIn']) {
+            // 查询销售退货入库单信息
+            $this->objDInventoryIn->setTable('qianniao_inventory_in_' . $params['enterpriseId']);
+            $inventoryInData = $this->objDInventoryIn->get(['id' => $params['id']]);
+            if ($inventoryInData === false) {
+                echo 'sql错误' . $this->objDInventoryIn->error() . PHP_EOL;
+                return false;
+            }
+
+            if (empty($inventoryInData)) {
+                echo '销售退货-入库单id:' . $params['id'] . '的数据为空' . PHP_EOL;
+                return false;
+            }
+
+            //源销售订单的信息
+            $tableName = $this->objDOrder->getTableName('qianniao_order_' . $params['enterpriseId'], $params['userCenterId'], $this->cutTable);
+            $this->objDOrder->setTable($tableName);
+            $orderData = $this->objDOrder->get(['id' => $inventoryInData['originId']]);
+            if ($orderData === false) {
+                echo 'sql错误' . $this->objDOrder->error() . PHP_EOL;
+                return false;
+            }
+
+            if (empty($orderData)) {
+                echo '源销售订单id:' . $inventoryInData['originId'] . '的数据为空' . PHP_EOL;
+                return false;
+            }
+
+            $receiveExtraData = [
+                'orderId'       => $inventoryInData['sourceId'], // 销售退货应收订单id为销售退货单id
+                'sourceNo'      => $inventoryInData['sourceNo'],
+                'customerId'    => $orderData['customerId'],
+                'customerName'  => $orderData['customerName'],
+                'receiveMoney'  => '-' . $inventoryInData['amount'],
+                'discountMoney' => 0.00,
+                'receiptTypeId' => StatusCode::$orderType['saleReturn'],
+                'notOffsetMoney'=>'-' . $inventoryInData['amount'],
+            ];
+            // 创建应收单数据
+            $receiveData = [
+                'shopId'        => $orderData['shopId'],
+                'shopName'      => $orderData['shopName'],
+                'financeTypeId' => StatusCode::$orderType['saleOut'],
+                'financeType'   => '销售退款单',
+                'auditStatus'   => StatusCode::$auditStatus['auditing'],
+                'createTime'    => time(),
+                'updateTime'    => time(),
+            ];
+        }
+        
+
+        $receiveData = array_merge($receiveData, $receiveExtraData);
+        // 添加应收单
+        $beginTransactionStatus = $this->objDReceive->beginTransaction();
+        $this->objDReceive->setTable('qianniao_receive_receipt_' . $params['enterpriseId'] . '_' . date('Y') . '_' . ceil(date('m') / 3));
+        $dbResult =  $this->objDReceive->get('createTime >='.strtotime(date('Ymd'.'0:0:0')), 'no', 'createTime desc');
+        if ($dbResult === false) {
+            echo 'sql错误' . $this->objDReceive->error() . PHP_EOL;
+            return false;
+        }
+        if(empty($dbResult)){
+            $receiveData['no'] =  createSerialNumberByDate('');
+        }else{
+            $receiveData['no'] =  createSerialNumberByDate($dbResult['no']);
+        }
+
+        //索引表数据
+        $indexData = [
+            'receiveReceiptId' => 0,
+            'customerId'       => $receiveData['customerId'],
+            'sourceNo'         => $receiveData['sourceNo'],
+            'auditStatus'      => $receiveData['auditStatus'],
+            'financeTypeId'    => $receiveData['financeTypeId'],
+            'financeType'      => $receiveData['financeType'],
+            'shopId'           => $receiveData['shopId'],
+            'createTime'       => $receiveData['createTime'],
+            'updateTime'       => $receiveData['updateTime'],
+        ];
+        $this->objDReceiveReceiptIndex->setTable('qianniao_receive_receipt_index_' . $params['enterpriseId']);
+        $receiveReceiptId = $this->objDReceiveReceiptIndex->insert($indexData);
+        if ($receiveReceiptId === false) {
+            $this->objDReceive->rollBack();
+            echo 'sql错误' . $this->objDReceiveReceiptIndex->error() . PHP_EOL;
+            return false;
+        }
+
+        //判断收款金额是否为负数,核销状态直接给5
+        $receiveData['offsetStatus'] = ($receiveData['receiveMoney']<0) ? 5 : 4;
+        $receiveData['id'] = $receiveReceiptId;
+        $dbResult = $this->objDReceive->insert($receiveData);
+        if ($dbResult === false) {
+            $this->objDReceive->rollBack();
+            echo 'sql错误' . $this->objDReceive->error() . PHP_EOL;
+            return false;
+        }
+
+        if($beginTransactionStatus){
+            $this->objDReceive->commit();
+        }
+
+        echo '数据库添加应收单成功' . PHP_EOL;
+
+        /*
+        $_id = self::createEsDocumentId($ReceiveId, $params['enterpriseId']);
+        $esData = $receiveData;
+        $esData['id'] = $ReceiveId;
+        $esData['enterpriseId'] = $params['enterpriseId'];
+        $result = $this->objDReceive->addUpSearchIndexDocument($esData, $_id);
+        if (isset($result['_shards']) && isset($result['_shards']['successful']) && $result['_shards']['successful'] == 1) {
+            echo "es操作成功";//die;
+        }else{
+            echo "es操作失败";
+            file_put_contents('/apps/logs/elasticsearch.log',date('Y-m-d H:i:s').'生成应收es错误,错误原因'.var_export($result,true).PHP_EOL,FILE_APPEND);
+        }
+
+        echo 'es添加应收单成功' . PHP_EOL;*/
+
+        // 添加应收队列缓存,计划任务自动审核应收用
+        $this->objFinanceCache->saveSalesOutReceive($params['enterpriseId'], $receiveReceiptId, $receiveData['createTime']);
+
+
+        if(!isset($params['checkOldData'])) {
+            echo '检测销售出库单是否都生成应收单了';
+            self::checkSaleOutCreateReceivable($params['enterpriseId']);
+        }
+
+        return true;
+    }
+
+    private function createEsDocumentId($receiveId, $enterpriseId)
+    {
+        $t = date('Y') . '_' . ceil(date('m') / 3);
+        return 'EnterpriseId_' . $enterpriseId . '_' . $t . '_receiveId_' . $receiveId;
+    }
+
+    /**
+     * @param $orderData
+     * @throws \Exception
+     */
+    private function getInfo($orderData)
+    {
+        // 查询订单对应店铺绑定的仓库id
+        $this->objDShop->setTable('qianniao_shop_1');
+        $warehouseId = $this->objDShop->get_field('warehouseId',['id'=>$orderData['shopId']]);
+
+        // 获取当前订单的shopId对应的仓库id
+        $this->objDWarehouse->setTable('qianniao_warehouse_' . $this->enterpriseId);
+        if ($warehouseId === false || empty($warehouseId)) {
+            echo '商铺id'. $orderData['shopId'] .'获取仓库id时出错' . $this->objDWarehouse->error() . PHP_EOL;
+            return false;
+        }
+        $warehouseId = strpos($warehouseId,',') ? explode(',', $warehouseId) : [$warehouseId];
+        $orderData['warehouseId'] = $warehouseId;
+
+        // 查询当前订单的流水记录
+        $this->objDPurchase->setTable('qianniao_purchase_' . $this->enterpriseId);//切换分表
+        $inventoryDetails = [];
+        foreach($warehouseId as $wid){
+            self::setDetailsTable($this->enterpriseId, $wid, 'qianniao_inventory_details');//切换分表
+            self::setBatchTable($this->enterpriseId, $wid, 'qianniao_inventory_batch');//切换分表
+            $dbResult = $this->objDInventoryDetails->select(['actionType' => StatusCode::$delete, 'originId' => $orderData['id']]);
+            if ($dbResult === false) {
+                echo '库存流水查询出错' . $this->objDInventoryDetails->error() . PHP_EOL;
+                return false;
+            }
+            if(!empty($dbResult)){
+                $inventoryDetails = array_merge($inventoryDetails, $dbResult);
+            }
+        }
+        if (empty($inventoryDetails)) {
+            echo '库存流水数据为空' . PHP_EOL;
+            return false;
+        }
+
+
+        self::todayGrossProfit($inventoryDetails, $orderData);
+        //self::supplierRanking($inventoryDetails, $orderData);
+    }
+
+
+
+    /**
+     * 缓存订单毛利
+     * @param $inventoryDetails
+     * @param $orderData
+     */
+    private function todayGrossProfit($inventoryDetails, $orderData)
+    {
+        $averageCostTotal = 0;
+        $batchCostTotal = 0;
+        foreach ($inventoryDetails as $detail) {
+            switch ($detail['costType']) {
+                case StatusCode::$costType['mwa']://平均值
+                    $averageCostTotal += bcmul($detail['inventoryNum'], $detail['averageCost'], 2);
+                    break;
+                case StatusCode::$costType['sp']://批次成本
+                    $batch = json_decode($detail['batch'], true);
+                    foreach ($batch as $val) {
+                        $batchCostTotal += bcmul($val['num'], $val['batchCost'], 2);
+                    }
+                    break;
+            }
+        }
+
+        //TODO(这里暂时不回写订单商品的出库成本)
+        $costTotal = bcadd($averageCostTotal, $batchCostTotal, 2);//此订单的总成本
+        $grossProfit = bcsub($orderData['payAmount'], $costTotal, 2);
+        $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'todayGrossProfit', $grossProfit);//订单毛利
+        $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'todayGrossProfit', $grossProfit, $orderData['shopId']);
+        echo '订单' . $orderData['no'] . ':毛利缓存成功' . PHP_EOL;
+    }
+
+    /**
+     * @param $inventoryDetails
+     * @param $orderData
+     */
+    private function supplierRanking($inventoryDetails, $orderData)
+    {
+        $batchNo = [];
+        foreach ($inventoryDetails as $detail) {
+            $batch = json_decode($detail['batch'], true);
+            foreach ($batch as $val) {
+                $batchNo[] = $val['batch'];
+            }
+        }
+        $fields = 'p.supplierId,p.supplierName,p.warehouseId,b.materielId,b.skuId,b.batchNo';
+        $sql = 'SELECT ' . $fields . ' FROM ' . $this->objDInventoryBatch->get_Table() . ' AS b LEFT JOIN ' . $this->objDPurchase->get_Table() . ' AS p ON b.originId=p.id WHERE b.batchNo IN(' . implode(',', $batchNo) . ')';
+        $supplier = $this->objDInventoryBatch->query($sql);
+        if ($supplier === false) {
+            echo '查询订单商品所属供应商时出错' . $this->objDInventoryBatch->error() . PHP_EOL;
+            die;
+        }
+        if (empty($supplier)) {
+            echo '查询订单商品对应供应商数据为空' . PHP_EOL;
+            die;
+        }
+
+        $allSupplier = [];
+        foreach ($supplier as $val) {
+            $allSupplier[$val['batchNo']] = $val;
+        }
+        foreach ($inventoryDetails as &$detail) {
+            $batch = json_decode($detail['batch'], true);
+            foreach ($batch as &$val) {
+                $val['supplierId'] = isset($allSupplier[$val['batch']]['supplierId']) ? $allSupplier[$val['batch']]['supplierId'] : 0;
+                $this->objOverviewCache->saveRanking($this->enterpriseId, 'supplierRanking', $val['supplierId'], $val['num']);//销量
+                $this->objOverviewCache->saveRanking($this->enterpriseId, 'supplierRanking', $val['supplierId'], $val['num'], $orderData['shopId']);
+
+                $this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'supplierRanking', $val['supplierId'], $orderData['payAmount']);//销额
+                $this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'supplierRanking', $val['supplierId'], $orderData['payAmount'], $orderData['shopId']);
+            }
+
+        }
+        echo '出库供应商数据统计成功' . PHP_EOL;
+    }
+
+    /**
+     * 库存批次按照企业商铺分表
+     * @param $enterpriseId
+     * @param $warehouseId
+     * @param $tableName
+     * @throws \Exception
+     */
+    public function setBatchTable($enterpriseId, $warehouseId, $tableName)
+    {
+        $tableName = $tableName . '_' . $enterpriseId . '_' . $warehouseId;
+        $this->objDInventoryBatch->setTable($tableName);
+    }
+
+
+    /**
+     * 库存流水按照仓库+季度分表
+     * @param $enterpriseId
+     * @param $warehouseId
+     * @param $tableName
+     * @throws \Exception
+     */
+    private function setDetailsTable($enterpriseId, $warehouseId, $tableName)
+    {
+        $tableName = $tableName . '_' . $enterpriseId . '_' . $warehouseId . '_' . substr(date('Y'), -2) . ceil(date('m') / 3);
+        $this->objDInventoryDetails->setTable($tableName);
+    }
+}

+ 46 - 0
Jobs/Model/MTopic/MErp.Class.php

@@ -0,0 +1,46 @@
+<?php
+/**
+ * erp相关队列业务脚本
+ */
+namespace Jobs\Model\MTopic;
+
+class MErp
+{
+    /**
+     * 锁定库存
+     * @param string $callback_url 回调地址
+     * @param string $callback_params 回调参数
+     */
+    public function lockStock($params)
+    {
+        if(!$params){
+            echo '参数值有空值'.PHP_EOL;
+            return false;
+        }
+        $callback_url = $params['callback_url'];
+        $authorization = $params['token'];
+        unset($params['callback_url']);
+        unset($params['token']);
+
+        $i = 1;
+        do{
+            $curl_option = [
+                CURLOPT_HTTPHEADER => [
+                    'Content-Type:text/plain',
+                    'Authorization:'.$authorization,
+                ],
+            ];
+            $result = request($callback_url, json_encode($params['data']), 10, $curl_option);
+            if($result['httpcode'] == 200){
+                echo '请求业务脚本成功'.PHP_EOL;
+                return true;
+            }
+
+            $i ++;
+            sleep(1);
+        }while($result['httpcode'] != 200 && $i <= 3);
+
+        echo '请求业务脚本失败'.PHP_EOL;
+        return false;
+    }
+}

+ 149 - 0
Jobs/Model/MTopic/Market/MCoupon.Class.php

@@ -0,0 +1,149 @@
+<?php
+/**
+ * 优惠券公共方法
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/12/24
+ * Time: 9:46
+ */
+
+namespace Jobs\Model\MTopic\Market;
+
+use Jobs\Dao\DCoupon;
+use Jobs\Dao\DUserCoupon;
+use Mall\Framework\Core\StatusCode;
+
+trait MCoupon
+{
+    /**
+     * @param DCoupon $object
+     * @param $selectParams
+     * @param null $extends
+     * @return bool
+     */
+    public function selectCoupon(DCoupon $object, $selectParams, $extends = null)
+    {
+        $fields = 'id,isMutex,customerIds,startTime,endTime,name,couponType,reducePrice,minPrice,remark,grantType,totalNum,allowNum,customerSourceId,useShop,mustCondition,grantStartTime,grantEndTime,applyRange,categoryCollect,brandCollect,receiveNum,goodsCollect';
+        $nowTime = time();
+        $sql = 'SELECT ' . $fields . ' FROM qianniao_coupon_' . $this->onlineEnterpriseId . ' WHERE deleteStatus=' . StatusCode::$standard . ' AND enableStatus='.StatusCode::$standard.' AND auditStatus=' . StatusCode::$auditStatus['auditPass'] . ' AND grantStartTime<' . $nowTime . ' AND grantEndTime> ' . $nowTime . ' AND totalNum>receiveNum AND grantType=' . $selectParams['grantType'].' AND couponType='.StatusCode::$couponType['commonly'];
+
+        if (!empty($extends)) {
+            $sql .= $extends;
+        }
+
+        $dbResult = $object->query($sql);
+        if ($dbResult === false) {
+            echo '优惠券查询错误' . $object->error() . PHP_EOL;
+            exit;
+        }
+        if (empty($dbResult)) {
+            echo '没有可领取的优惠券' . PHP_EOL;
+            exit;
+        }
+        return $dbResult;
+    }
+
+    /**
+     * 验证优惠券,自己可以领取那些
+     * @param DUserCoupon $object
+     * @param $coupon
+     * @param $params
+     * @return mixed
+     */
+    public function checkCoupon(DUserCoupon $object, $coupon, $params)
+    {
+        foreach ($coupon as $key => $val) {
+            $coupon[$key]['allowNum'] = $val['allowNum'];
+            if ($val['receiveNum'] >= $val['totalNum']){
+                echo '优惠券'.$val['id'].'已经领取完了'.PHP_EOL;
+                unset($coupon[$key]);
+                continue;
+            }
+            $dbResult = $object->count(['couponId' => $val['id'], 'userId' => $params['userCenterId']]);
+            if ($dbResult === false) {
+                echo '领取记录查询失败' . $object->error() . PHP_EOL;
+                exit;
+            }
+            if ($dbResult >= $val['allowNum']){
+                echo '优惠券'.$val['id'].'已经领取了'.$dbResult.'张,每人限领取'.$val['allowNum'].'张'.PHP_EOL;
+                unset($coupon[$key]);
+                continue;
+            }
+
+            //剩余数量不足每人领取数量
+            if ($val['receiveNum']+$val['allowNum'] > $val['totalNum']){
+                $rec = $val['totalNum'] - $val['receiveNum'];
+                $coupon[$key]['allowNum'] = $rec;
+            }
+        }
+
+        if (empty($coupon)) {
+            echo '优惠券都领取过了' . PHP_EOL;
+            exit;
+        }
+        return $coupon;
+    }
+
+    /**
+     * 添加领取记录
+     * @param DUserCoupon $object
+     * @param $coupon
+     * @param $params
+     */
+    public function add(DUserCoupon $object, $coupon, $params)
+    {
+        $insert = [];
+        foreach ($coupon as $key => $val) {
+            for ($i = 0;$i < $val['allowNum'];$i++){
+                $insert[] = [
+                    'couponId'        => $val['id'],
+                    'name'            => $val['name'],
+                    'remark'          => $val['remark'],
+                    'couponType'      => $val['couponType'],
+                    'reducePrice'     => $val['reducePrice'],
+                    'minPrice'        => $val['minPrice'],
+                    'startTime'       => $val['startTime'],
+                    'endTime'         => $val['endTime'],
+                    'applyRange'      => $val['applyRange'],
+                    'categoryCollect' => $val['categoryCollect'],
+                    'brandCollect'    => $val['brandCollect'],
+                    'goodsCollect'    => $val['goodsCollect'],
+                    'isExpire'        => StatusCode::$delete,
+                    'isUse'           => StatusCode::$standard,
+                    'userId'          => $params['userCenterId'],
+                    'useShop'         => $val['useShop'],
+                    'customerId'      => $params['customerId'],
+                    'source'          => $val['grantType'],
+                    'createTime'      => time(),
+                    'updateTime'      => time(),
+                    'customerIds'     => $val['customerIds'],
+                    'isMutex'         => isset($val['isMutex']) ? $val['isMutex'] : StatusCode::$standard,
+                ];
+            }
+        }
+        $dbResult = $object->insert($insert, true);
+        if ($dbResult === false) {
+            echo '操作数据库失败' . $object->error() . PHP_EOL;
+            exit();
+        }
+        echo '优惠券发送成功' . PHP_EOL;
+    }
+
+    /**
+     * @param DCoupon $object
+     * @param $coupon
+     * @param $params
+     */
+    public function receiveNumIncr(DCoupon $object, $coupon, $params,$step=1)
+    {
+        foreach ($coupon as $val) {
+            $sql = 'UPDATE qianniao_coupon_' . $this->onlineEnterpriseId . ' SET receiveNum=receiveNum+' . $val['allowNum'] . ' WHERE id=' . $val['id'];
+            $dbResult = $object->query($sql);
+            if ($dbResult === false) {
+                echo '操作数据库失败' . $object->error() . PHP_EOL;
+                exit();
+            }
+            echo '增加' . $val['id'] . '领取数量成功' . PHP_EOL;
+        }
+    }
+}

+ 57 - 0
Jobs/Model/MTopic/Market/MGrantCoupon.Class.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/12/24
+ * Time: 14:43
+ */
+
+namespace Jobs\Model\MTopic\Market;
+
+use Jobs\Dao\DCoupon;
+use Jobs\Dao\DUserCoupon;
+use Mall\Framework\Core\StatusCode;
+
+class MGrantCoupon
+{
+    use MCoupon;
+
+    private $objDUserCoupon;
+
+    private $objDCoupon;
+
+    private $onlineEnterpriseId;
+
+    /**
+     * MGrantCoupon constructor.
+     * @throws \Exception
+     */
+    public function __construct()
+    {
+        echo '定向发放优惠券:' . date('Y-d-d H:i:s') . ': ';
+        $this->objDUserCoupon = new DUserCoupon();
+        $this->objDCoupon = new DCoupon();
+        $this->objDUserCoupon->setSearchIndex('user_coupon_search')->setType('user_coupon');;
+    }
+
+    /**
+     * @param $params
+     * @throws \Exception
+     */
+    public function push($params)
+    {
+        $this->onlineEnterpriseId = $params['enterpriseId'];
+        // 计算分表
+        $this->objDUserCoupon->setTable($this->objDUserCoupon->get_Table() . '_' . $params['enterpriseId']);
+        $this->objDCoupon->setTable($this->objDCoupon->get_Table() . '_' . $params['enterpriseId']);
+        $extends = '';
+        if (!empty($params['customerId'])) {
+            $extends = ' AND FIND_IN_SET(' . $params['customerId'] . ',customerIds)';
+        }
+        $coupon = self::selectCoupon($this->objDCoupon, ['grantType' => StatusCode::$grantType['grant']], $extends);
+        $checkCoupon = self::checkCoupon($this->objDUserCoupon, $coupon, $params);
+        self::add($this->objDUserCoupon, $checkCoupon, $params);
+        self::receiveNumIncr($this->objDCoupon, $checkCoupon, $params);
+    }
+
+}

+ 55 - 0
Jobs/Model/MTopic/Market/MOnlinePayCoupon.Class.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * 在线支付发放优惠券
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/12/24
+ * Time: 9:43
+ */
+
+namespace Jobs\Model\MTopic\Market;
+
+use Mall\Framework\Core\StatusCode;
+use Jobs\Dao\DCoupon;
+use Jobs\Dao\DUserCoupon;
+
+class MOnlinePayCoupon
+{
+    use MCoupon;
+
+    private $objDUserCoupon;
+
+    private $objDCoupon;
+
+    private $onlineEnterpriseId;
+
+    /**
+     * MUserCoupon constructor.
+     * @throws \Exception
+     */
+    public function __construct()
+    {
+        echo '在线支付发放优惠券:'.date('Y-d-d H:i:s').': ';
+        $this->objDUserCoupon = new DUserCoupon();
+        $this->objDCoupon = new DCoupon();
+        $this->objDUserCoupon->setSearchIndex('user_coupon_search')->setType('user_coupon');;
+    }
+
+    /**
+     * @param $params
+     * @return bool
+     * @throws \Exception
+     */
+    public function push($params)
+    {
+        $this->onlineEnterpriseId = $params['enterpriseId'];
+        // 计算分表
+        $this->objDUserCoupon->setTable($this->objDUserCoupon->get_Table() . '_' . $params['enterpriseId']);
+        $this->objDCoupon->setTable($this->objDCoupon->get_Table() . '_' . $params['enterpriseId']);
+        $extends=' AND mustCondition<=' . $params['payAmount'];
+        $coupon = self::selectCoupon($this->objDCoupon,['grantType'=>StatusCode::$grantType['onlinePay']],$extends);
+        $checkCoupon = self::checkCoupon($this->objDUserCoupon,$coupon,$params);
+        self::add($this->objDUserCoupon,$checkCoupon,$params);
+        self::receiveNumIncr($this->objDCoupon,$checkCoupon,$params);
+    }
+}

+ 52 - 0
Jobs/Model/MTopic/Market/MRegisterCoupon.Class.php

@@ -0,0 +1,52 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2019/12/23
+ * Time: 16:21
+ */
+
+namespace Jobs\Model\MTopic\Market;
+
+use Jobs\Dao\DCoupon;
+use Jobs\Dao\DUserCoupon;
+use Mall\Framework\Core\StatusCode;
+
+class MRegisterCoupon
+{
+    use MCoupon;
+
+    private $objDUserCoupon;
+
+    private $objDCoupon;
+
+    private $onlineEnterpriseId;
+
+    /**
+     * MUserCoupon constructor.
+     * @throws \Exception
+     */
+    public function __construct()
+    {
+        echo '注册发放优惠券:'.date('Y-d-d H:i:s').': ';
+        $this->objDUserCoupon = new DUserCoupon();
+        $this->objDCoupon = new DCoupon();
+        $this->objDUserCoupon->setSearchIndex('user_coupon_search')->setType('user_coupon');;
+    }
+
+    /**
+     * @param $params
+     * @throws \Exception
+     */
+    public function push($params)
+    {
+        $this->onlineEnterpriseId = $params['enterpriseId'];
+        // 计算分表
+        $this->objDUserCoupon->setTable($this->objDUserCoupon->get_Table() . '_' . $params['enterpriseId']);
+        $this->objDCoupon->setTable($this->objDCoupon->get_Table() . '_' . $params['enterpriseId']);
+        $coupon = self::selectCoupon($this->objDCoupon,['grantType'=>StatusCode::$grantType['register']]);
+        $checkCoupon = self::checkCoupon($this->objDUserCoupon,$coupon,$params);
+        self::add($this->objDUserCoupon,$checkCoupon,$params);
+        self::receiveNumIncr($this->objDCoupon,$checkCoupon,$params);
+    }
+}

+ 95 - 0
Jobs/Model/MTopic/Message/MMessage.Class.php

@@ -0,0 +1,95 @@
+<?php
+/**
+ * 消息
+ * Created by PhpStorm.
+ * User: 小威
+ * Date: 2020/04/03
+ * Time: 12:20
+ */
+
+namespace Jobs\Model\MTopic\Message;
+
+
+use Mall\Framework\Core\ErrorCode;
+use Mall\Framework\Core\ResultWrapper;
+use Mall\Framework\Core\StatusCode;
+
+use Jobs\Dao\BaseDao;
+use Jobs\Dao\Message\DMessage;
+
+class MMessage
+{
+    private $enterpriseId;
+
+    private $userCenterId;
+
+    private $objDMessage;
+
+    /**
+     * MGrantCoupon constructor.
+     * @throws \Exception
+     */
+    public function __construct()
+    {
+        $this->objDMessage = new DMessage();
+        echo '库存不足预警消息:' . date('Y-d-d H:i:s') . ':' . PHP_EOL;
+    }
+
+    /**
+     * 库存不足预警
+     * @param $params
+     * @return bool
+     * @throws \Exception
+     */
+    public function inventoryNumWarning($params)
+    {
+        echo '开始执行消息提醒队列方法**************************************************************************************************************************';
+        var_dump($params);
+        $data = $params['data']['data'];
+        if(empty($params['data']['enterpriseId'])){
+            echo '企业id参数错误'.PHP_EOL;
+            return false;
+        }
+        $enterpriseId = $params['data']['enterpriseId'];
+        $this->objDMessage->setTable($this->objDMessage->get_Table().'_'.$enterpriseId);
+        //查询企业管理员
+        $sql = 'select userCenterId from qianniao_user_bind_enterprise where enterpriseId = '.$enterpriseId.' and roleType = '.StatusCode::$roleType['admin'];
+        $dbResult = $this->objDMessage->query($sql);
+        if($dbResult === false){
+            echo $this->objDMessage->error() . PHP_EOL;
+            return false;
+        }
+        if(empty($dbResult)){
+            echo '企业管理员查询失败' . PHP_EOL;
+            return false;
+        }
+        $adminId = $dbResult[0]['userCenterId'];
+
+        $insertData = [];
+        foreach($data as $value){
+            $insertData[] = [
+                'sendId' => 0,//0为系统自动发送
+                'receiveId' => $adminId,
+                'title' => '库存不足',
+                'content' => $value.'库存不足',
+                'type' => StatusCode::$messageType['inventoryNull'],
+                'receiveStatus' => StatusCode::$delete,
+                'deleteStatus' => StatusCode::$standard,
+                'sendTime' => time(),
+                'createTime' => time(),
+                'receiveTime' => null,
+            ];
+        }
+
+        unset($dbResult);
+        $dbResult = $this->objDMessage->insert($insertData, true);
+        if($dbResult === false){
+            echo $this->objDMessage->error() . PHP_EOL;
+            return false;
+        }
+        echo '添加成功' . PHP_EOL;
+        return true;
+    }
+
+
+}

+ 132 - 0
Jobs/Model/MTopic/Order/MCommissionOrder.Class.php

@@ -0,0 +1,132 @@
+<?php
+
+
+namespace Jobs\Model\MTopic\Order;
+
+
+class MCommissionOrder
+{
+    public function __construct()
+    {
+        echo '订单分销开始执行:' . date('Y-m-d H:i:s') . ':' . PHP_EOL;
+    }
+
+    /**
+     * Doc: (des="")
+     * User: XMing
+     * Date: 2020/7/28
+     * Time: 4:52 下午
+     * @param $params
+     * @return boolean
+     */
+    public function push($params)
+    {
+        echo PHP_EOL;
+        $project_domain = $params['PROJECT_DOMAIN'];
+        switch ($project_domain){
+            case 'api':
+                $ip = URL_API.'/Commission/Commission/createCommission';
+                break;
+            default:
+                $ip = DEV_URL_API.'/Commission/Commission/createCommission';
+        }
+
+        $postData = [
+            'orderId' => $params['orderId'],
+            'customerId' => $params['customerId']
+        ];
+        $post = json_encode($postData);
+        $curl_option = [
+            CURLOPT_HTTPHEADER => [
+                'Content-Type:text/plain',
+                'Authorization:'.$params['Authorization'],
+                'Token:'.$params['Token']
+            ],
+        ];
+        $result = request($ip, $post, 40, false,$curl_option);
+        print_r($result);
+        echo '成功' . PHP_EOL;
+        return true;
+    }
+
+    /**
+     * Doc: (des="订单完成")
+     * User: XMing
+     * Date: 2020/7/30
+     * Time: 6:54 下午
+     * @param $params
+     * @return bool
+     */
+    public function updateBalance($params)
+    {
+        echo PHP_EOL;
+        $project_domain = $params['PROJECT_DOMAIN'];
+        switch ($project_domain){
+            case 'api':
+                $ip = URL_API.'/Commission/Commission/updateBalance';
+                break;
+            default:
+                $ip = DEV_URL_API.'/Commission/Commission/updateBalance';
+        }
+
+        $postData = [
+            'orderId' => $params['orderId'],
+        ];
+        $post = json_encode($postData);
+        $curl_option = [
+            CURLOPT_HTTPHEADER => [
+                'Content-Type:text/plain',
+                'Authorization:'.$params['Authorization'],
+                'Token:'.$params['Token']
+            ],
+        ];
+        $result = request($ip, $post, 40, false,$curl_option);
+        echo '成功' . PHP_EOL;
+        return true;
+    }
+
+    /**
+     * Doc: (des="取消订单")
+     * User: XMing
+     * Date: 2020/7/31
+     * Time: 6:18 下午
+     * @param $params
+     * @return bool
+     */
+    public function cancelSubCommission($params)
+    {
+        echo PHP_EOL;
+        $project_domain = $params['PROJECT_DOMAIN'];
+        switch ($project_domain){
+            case 'api':
+                $ip = URL_API.'/Commission/Commission/cancelSubCommission';
+                break;
+            default:
+                $ip = DEV_URL_API.'/Commission/Commission/cancelSubCommission';
+        }
+
+        $postData = [
+            'orderId' => $params['orderId'],
+        ];
+        $post = json_encode($postData);
+        $curl_option = [
+            CURLOPT_HTTPHEADER => [
+                'Content-Type:text/plain',
+                'Authorization:'.$params['Authorization'],
+                'Token:'.$params['Token']
+            ],
+        ];
+        $result = request($ip, $post, 40, false,$curl_option);
+        print_r($result);
+        echo '成功' . PHP_EOL;
+        return true;
+    }
+
+
+
+    public function __destruct()
+    {
+        // TODO: Implement __destruct() method.
+        echo '订单分销执行结束:' . date('Y-m-d H:i:s') . ':' . PHP_EOL;
+    }
+}

+ 33 - 0
Jobs/Model/MTopic/Order/MDelayedCancelOrder.Class.php

@@ -0,0 +1,33 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2020/4/15
+ * Time: 17:06
+ */
+
+namespace Jobs\Model\MTopic\Order;
+
+
+class MDelayedCancelOrder
+{
+    /**
+     * @var int
+     */
+    private static $ms = 10 * 1000;
+
+    public function __construct()
+    {
+        echo '订单延时自动取消:' . date('Y-m-d H:i:s') . ':' . PHP_EOL;
+    }
+
+
+    public function push($params)
+    {
+        $taskId = \Swoole\Timer::after(self::$ms, function () use ($params) {
+            print_r($params);
+            echo date('Y-m-d H:i:s') . '订单延时自动取消10s' . PHP_EOL;
+        });
+        echo '定时器id:' . $taskId . PHP_EOL;
+    }
+}

+ 125 - 0
Jobs/Model/MTopic/Order/MOrder.Class.php

@@ -0,0 +1,125 @@
+<?php
+/**
+ * 订单
+ * Created by PhpStorm.
+ * User: 小威
+ * Date: 2020/03/14
+ * Time: 17:03
+ */
+
+namespace Jobs\Model\MTopic\Order;
+
+use Jobs\Dao\BaseDao;
+use Jobs\Dao\Order\DOrder;
+use Jobs\Dao\Order\DOrderGoods;
+use Mall\Framework\Core\ErrorCode;
+use Mall\Framework\Core\ResultWrapper;
+use Mall\Framework\Core\StatusCode;
+
+class MOrder
+{
+    private $enterpriseId;
+
+    private $userCenterId;
+
+    private $objDOrder;
+
+    private $objDOrderGoods;
+
+    private $cutTable = 200000;
+
+    /**
+     * MGrantCoupon constructor.
+     * @throws \Exception
+     */
+    public function __construct()
+    {
+        $this->objDOrder = new DOrder();
+        $this->objDOrderGoods = new DOrderGoods();
+        echo '编辑订单信息:' . date('Y-d-d H:i:s') . ':' . PHP_EOL;
+    }
+
+    /**
+     * 切换分表
+     * @param $enterpriseId
+     * @param $userCenterId
+     * @throws \Exception
+     */
+    public function orderSubTable($enterpriseId, $userCenterId)
+    {
+        $tableName = $this->objDOrder->getTableName($this->objDOrder->get_Table() . '_' . $enterpriseId, $userCenterId, $this->cutTable);
+        $this->objDOrder->setTable($tableName);
+        $tableName = $this->objDOrderGoods->getTableName($this->objDOrderGoods->get_Table() . '_' . $enterpriseId, $userCenterId, $this->cutTable);
+        $this->objDOrderGoods->setTable($tableName);
+    }
+
+    /**
+     * 回写订单库存成本
+     * @param $params
+     * @return ResultWrapper
+     * @throws \Exception
+     */
+    public function backOrderOutCostPrise($params)
+    {
+        var_dump($params);
+        $params = $params['data'];
+        //调用分表
+        self::orderSubTable($params['enterpriseId'], $params['userCenterId']);
+        //查询订单
+        $dbResult = $this->objDOrder->get(['id'=>$params['originId'], 'deleteStatus' => StatusCode::$standard]);
+        if($dbResult === false){
+            echo $this->objDOrder->error() . PHP_EOL;
+            return false;
+        }
+        if(empty($dbResult)){
+            echo '订单不存在' . PHP_EOL;
+            return false;
+        }
+        unset($dbResult);
+        //查询详情
+        $dbResult = $this->objDOrderGoods->get(['orderId' => $params['originId'], 'deleteStatus' => StatusCode::$standard, 'returnStatus' => StatusCode::$orderReturn['notReturn']]);
+        if($dbResult === false){
+            echo $this->objDOrderGoods->error() . PHP_EOL;
+            return false;
+        }
+        if(empty($dbResult)){
+            echo '商品数据为空' . PHP_EOL;
+            return false;
+        }
+        unset($dbResult);
+        foreach($params['stock'] as $skuId => $value){
+            //判断是那种计算成本方式
+            if($params['costType'] == StatusCode::$costType['mwa']){
+                //移动加权
+                $costPrise = $value['averageCost'];
+            }elseif($params['costType'] == StatusCode::$costType['sp']){
+                //批次成本
+                //计算平均值
+                $prise = 0;
+                foreach($value['batch'] as $v){
+                    $prise = bcadd($prise,$v['batchCost'],4);
+                }
+                $count = count($value['batch']);
+                $costPrise = bcdiv($prise ,$count,4);
+            }else{
+                echo '成本计算参数错误' . PHP_EOL;
+                return false;
+            }
+            echo '$skuId='.$skuId.'  --  '.'成本方式'.$params['costType'].', 成本单价'.$costPrise.PHP_EOL;
+            //修改订单详情
+            print_r(['outCostPrice' => $costPrise, 'updateTime' => time()]);
+            print_r(['id' => $params['originId'], 'skuId' => $skuId]);
+            print_r($this->objDOrderGoods->get_Table());
+            $dbResult = $this->objDOrderGoods->update(['outCostPrice' => $costPrise, 'updateTime' => time()], ['orderId' => $params['originId'], 'skuId' => $skuId]);
+            //var_dump($dbResult);
+            if($dbResult === false){
+                echo $this->objDOrderGoods->error() . PHP_EOL;
+                return false;
+            }
+        }
+        echo '修改成功' . PHP_EOL;
+        return true;
+    }
+
+
+}

+ 217 - 0
Jobs/Model/MTopic/Order/MOrderStatistics.Class.php

@@ -0,0 +1,217 @@
+<?php
+/**
+ * 订单,后台首页缓存数据统计
+ * Created by PhpStorm.
+ * User: XiaoMing
+ * Date: 2020/1/6
+ * Time: 17:23
+ */
+
+namespace Jobs\Model\MTopic\Order;
+
+use Jobs\Cache\CustomerCache;
+use Jobs\Cache\OverviewCache;
+use Jobs\Dao\BaseDao;
+use Jobs\Dao\DGoodsBasic;
+use Jobs\Dao\Order\DOrder;
+use Jobs\Dao\Order\DOrderGoods;
+use Jobs\Dao\Order\DOrderReceive;
+
+class MOrderStatistics
+{
+    private $enterpriseId;
+
+    private $userCenterId;
+
+    private $objOverviewCache;
+
+    private $objCustomerCache;
+
+    private $objDGoodsBasic;
+
+    private $objDOrder;
+
+    private $objDOrderGoods;
+
+    private $objDOrderReceive;
+
+    private $cutTable = 200000;
+
+    /**
+     * MGrantCoupon constructor.
+     * @throws \Exception
+     */
+    public function __construct()
+    {
+        $this->objCustomerCache = new CustomerCache();
+        $this->objOverviewCache = new OverviewCache();
+        $this->objDGoodsBasic = new DGoodsBasic();
+        $this->objDOrder = new DOrder();
+        $this->objDOrderGoods = new DOrderGoods();
+        $this->objDOrderReceive = new DOrderReceive();
+        echo '统计订单信息:' . date('Y-d-d H:i:s') . ':' . PHP_EOL;
+    }
+
+    /**
+     * 订单统计项
+     * @param $params
+     * @throws \Exception
+     */
+    public function push($params)
+    {
+        if (empty($params['no'])) die('订单数据为空');
+        $this->enterpriseId = $params['enterpriseId'];
+        $this->userCenterId = $params['userCenterId'];
+        self::orderSubTable($this->objDOrder);//切换订单分表
+        self::orderSubTable($this->objDOrderGoods);//切换订单商品分表
+        self::orderSubTable($this->objDOrderReceive);//切换订单收货分表
+        $formatData = self::getOrderInfo($params);//获取订单信息
+
+        switch ($params['noticeType']) {
+            case 'create'://创建订单
+                $allOrder = $formatData['orderData']['masterOrder'];
+                foreach ($allOrder as $order) {
+                    $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'orderTotalMoney', $order['payAmount']);//下单金额
+                    $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'orderTotalMoney', $order['payAmount'], $order['shopId']);
+                }
+                break;
+            case 'audit'://确认订单
+                if(isset($formatData['orderData']['masterOrder'][0]['createTime']) && $formatData['orderData']['masterOrder'][0]['createTime'] < strtotime(date("Y-m-d",time()))){
+                    echo '这不是今天的订单, 不缓存数据___________________________________________________________________________________';
+                    break;
+                }
+                echo '进入审核订单-------------------------------------------------------------------------------------------------------';
+                //var_dump($formatData);
+                $allOrder = $formatData['orderData']['masterOrder'][0];//订单主数据
+                $allOrderGoods = self::getBasicInfo($formatData['orderData']['orderGoods']);//订单商品数据
+                $allOrderReceive = $formatData['orderData']['orderReceive'][0];//订单收货信息
+
+                $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'orderConfirmedMoney', $allOrder['payAmount']);//营业额
+                $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'orderConfirmedMoney', $allOrder['payAmount'], $allOrder['shopId']);
+                $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'todayConfirmedOrderNum', 1);//今日订单
+                $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'todayConfirmedOrderNum', 1, $allOrder['shopId']);
+                $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'orderNumOfNotOutOfStock', 1);//未出库订单
+                $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'orderNumOfNotOutOfStock', '1', $allOrder['shopId']);
+                $this->objCustomerCache->cacheCustomerOrderInfo($this->enterpriseId, $allOrderReceive['provinceCode'], $allOrderReceive['cityCode'], $allOrderReceive['districtCode'], $allOrder['customerId']);//下单客户数
+                $this->objCustomerCache->cacheCustomerOrderInfo($this->enterpriseId, $allOrderReceive['provinceCode'], $allOrderReceive['cityCode'], $allOrderReceive['districtCode'], $allOrder['customerId'], $allOrder['shopId']);//下单客户数
+
+                $this->objOverviewCache->saveOrderTrend($this->enterpriseId, $allOrder['payAmount'], $allOrder['buyTotal']);//趋势图订单数量
+                foreach ($allOrderGoods as $goods) {
+                    //商品的大分类
+                    $this->objOverviewCache->saveRanking($this->enterpriseId, 'categoryRanking', $goods['parentCategoryId'], $goods['buyNum']);//销量
+                    $this->objOverviewCache->saveRanking($this->enterpriseId, 'categoryRanking', $goods['parentCategoryId'], $goods['buyNum'], $allOrder['shopId']);
+                    $this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'categoryRanking', $goods['parentCategoryId'], $goods['totalMoney']);//销额
+                    $this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'categoryRanking', $goods['parentCategoryId'], $goods['totalMoney'], $allOrder['shopId']);
+
+                    //查询商品
+                    $this->objOverviewCache->saveRanking($this->enterpriseId, 'goodsRanking', $goods['goodsBasicId'], $goods['buyNum']);//销量
+                    $this->objOverviewCache->saveRanking($this->enterpriseId, 'goodsRanking', $goods['goodsBasicId'], $goods['buyNum'], $allOrder['shopId']);
+                    $this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'goodsRanking', $goods['goodsBasicId'], $goods['totalMoney']);//销额
+                    $this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'goodsRanking', $goods['goodsBasicId'], $goods['totalMoney'], $allOrder['shopId']);
+
+                    //客户
+                    $this->objOverviewCache->saveRanking($this->enterpriseId, 'customerRanking', $allOrder['customerId'], $goods['buyNum']);//销量
+                    $this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'customerRanking', $allOrder['customerId'], $goods['totalMoney']);//销售额
+                }
+
+                //供应商
+                //$this->objOverviewCache->saveRanking($this->enterpriseId, 'supplierRanking', $allOrder['supplierId'], 1);//销量
+                //$this->objOverviewCache->saveRanking($this->enterpriseId, 'supplierRanking', $allOrder['supplierId'], 1, $allOrder['shopId']);
+                //$this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'supplierRanking', $allOrder['supplierId'], $allOrder['payAmount']);//销额
+                //$this->objOverviewCache->saveSalesMoneyRanking($this->enterpriseId, 'supplierRanking', $allOrder['supplierId'], $allOrder['payAmount'], $allOrder['shopId']);
+
+                //TODO
+                $this->objCustomerCache->delCustomerAfterPlaceOrder($allOrder['customerId'], $this->enterpriseId);
+                break;
+            case 'close'://取消订单
+                $allOrder = $formatData['orderData']['masterOrder'][0];
+                $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'todayCanceledOrder', 1);//取消订单数
+                $this->objOverviewCache->saveBusinessOverview($this->enterpriseId, 'todayCanceledOrder', 1, $allOrder['shopId']);
+                break;
+        }
+        echo '统计订单信息成功' . PHP_EOL;
+    }
+
+    /**
+     * @param $allOrderGoods
+     * @return mixed
+     * @throws \Exception
+     */
+    private function getBasicInfo($allOrderGoods)
+    {
+        $this->objDGoodsBasic->setTable($this->objDGoodsBasic->get_Table() . '_' . $this->enterpriseId);
+        $dbResult = $this->objDGoodsBasic->select(['id' => array_column($allOrderGoods, 'goodsBasicId')], 'categoryPath,brandId,id');
+        if ($dbResult === false) {
+            echo '查询商品信息时发生错误' . $this->objDGoodsBasic->error() . PHP_EOL;
+            die;
+        }
+        if (empty($dbResult)) echo '查询商品信息为空' . PHP_EOL;
+
+        $allGoodsInfo = [];
+        foreach ($dbResult as &$val) {
+            $categoryArr = explode(',', $val['categoryPath']);
+            $val['parentCategoryId'] = array_shift($categoryArr);
+            $allGoodsInfo[$val['id']] = $val;
+        }
+
+        foreach ($allOrderGoods as &$goods) {
+            $goods['parentCategoryId'] = isset($allGoodsInfo[$goods['goodsBasicId']]['parentCategoryId']) ? $allGoodsInfo[$goods['goodsBasicId']]['parentCategoryId'] : 0;
+            $goods['brandId'] = isset($allGoodsInfo[$goods['goodsBasicId']]['brandId']) ? $allGoodsInfo[$goods['goodsBasicId']]['brandId'] : '';
+        }
+        return $allOrderGoods;
+    }
+
+    /**
+     * 获取订单信息
+     * @param $data
+     * @return mixed
+     */
+    private function getOrderInfo($data)
+    {
+        $no = $data['no'];//订单编号
+        $orderMaster = $this->objDOrder->select(['no' => $no]);
+        if ($orderMaster === false) {
+            echo '获取订单数据时出错' . $this->objDOrder->error() . PHP_EOL;
+            die;
+        }
+        if (empty($orderMaster)) {
+            echo '获取订单数据为空' . PHP_EOL;
+            die;
+        }
+        $data['orderData']['masterOrder'] = $orderMaster;
+        $orderGoods = $this->objDOrderGoods->select(['no' => $no]);
+        if ($orderGoods === false) {
+            echo '获取订单商品数据时出错' . $this->objDOrderGoods->error() . PHP_EOL;
+            die;
+        }
+        if (empty($orderGoods)) {
+            echo '获取订单商品数据为空' . PHP_EOL;
+            die;
+        }
+        $data['orderData']['orderGoods'] = $orderGoods;
+        $orderReceive = $this->objDOrderReceive->select(['no' => $no]);
+        if ($orderReceive === false) {
+            echo '获取订单收货数据时出错' . $this->objDOrderReceive->error() . PHP_EOL;
+            die;
+        }
+        if (empty($orderReceive)) {
+            echo '获取订单收货数据为空' . PHP_EOL;
+            die;
+        }
+        $data['orderData']['orderReceive'] = $orderReceive;
+//        echo '订单数据:';
+//        print_r($data);
+        return $data;
+    }
+
+    /**
+     * 切换分表
+     * @param BaseDao $object
+     * @throws \Exception
+     */
+    public function orderSubTable($object)
+    {
+        $tableName = $object->getTableName($object->get_Table() . '_' . $this->enterpriseId, $this->userCenterId, $this->cutTable);
+        $object->setTable($tableName);
+    }
+}

+ 246 - 0
Jobs/Model/MTopic/Push/MPush.Class.php

@@ -0,0 +1,246 @@
+<?php
+/**
+ * 亿成推送队列任务
+ * Created by PhpStorm.
+ * User: phperstar
+ * Date: 2018/6/10
+ * Time: 下午3:38
+ */
+namespace Jobs\Model\MTopic\Push;
+
+use Jobs\Model\Middleware;
+
+use Jobs\Dao\DMessageTemplate;
+use Jobs\Dao\DTmsMessageTemplate;
+use Jobs\Dao\DUserMessage;
+
+class MPush
+{
+
+    private $objDMessageTemplate;
+    public function __construct()
+    {
+
+    }
+
+    /**
+     * 推送消息
+     * @param array $params 推送任务参数
+     * @param string $callback_params 回调参数
+     */
+    public  function push($params)
+    {
+        self::initDao($params['push_source']);
+
+        // 1.读取推送任务模板
+        $template_id = intval($params['push_template_id']);
+        if(!$template_id){
+            echo '未指定推送模板id'.PHP_EOL;
+            return false;
+        }
+
+        $template_data = self::getMessageTemplateById($template_id);
+        if(empty($template_data)){
+            echo '指定的推送消息不存在'.PHP_EOL.$this->objDMessageTemplate->error();
+            return false;
+        }
+
+        if($template_data['status'] != 1){
+            echo '指定的推送消息未启用'.PHP_EOL;
+            return false;
+        }
+
+        // 2.获取推送的内容
+        $message_content = self::paseTemplateContent($template_data['template_content'], $params['push_template_params']);
+
+        if($message_content){
+            // 3.开启推送
+            self::pushMessage($message_content, $template_data, $params);
+        }
+
+    }
+
+    /*
+     * 初始化库
+     */
+    public function initDao($push_source)
+    {
+        switch ($push_source){
+            case 'wuliu':
+                $this->objDMessageTemplate = new DTmsMessageTemplate('wuliu');
+                break;
+            case 'pifabbc':
+                $this->objDMessageTemplate = new DMessageTemplate('newcloud');
+                break;
+            default:
+                $this->objDMessageTemplate = new DMessageTemplate('cloud');
+                break;
+        }
+
+    }
+
+    /**
+     * 获取推送模板数据
+     * @param int $template_id 模板id
+     */
+    public function getMessageTemplateById($template_id)
+    {
+        return $this->objDMessageTemplate->get($template_id);
+    }
+
+    /**
+     * 渲染模板消息中的参数
+     * @param string $template_content 模板内容
+     * @param array  $template_params  模板参数
+     * @return string
+     */
+    public function paseTemplateContent($template_content, $template_params)
+    {
+        // 比对模板参数数量和传输过来的替换的参数数量是否一致
+        preg_match_all('/<{(.*?)}>/', $template_content, $matchs);
+
+        if(count($matchs[0]) != count($template_params)){
+            var_dump($matchs);
+            echo '要替换的模板参数数量和实际模板参数数量不一致'.PHP_EOL;
+            return false;
+        }
+
+        foreach ($template_params as $key => $value){
+            if(empty($value) && $value != ''){
+                echo '模板变量'.$key.'的值为空'.PHP_EOL;
+                return false;
+            }
+            if(!in_array($key,$matchs[1])){
+                echo '传输过来的模板变量'.$key.'不在模板中'.PHP_EOL;
+                return false;
+            }
+        }
+
+        return str_replace($matchs[0], $template_params, $template_content);
+    }
+
+    /**
+     * 开启推送
+     * @param string $message_content 推送内容
+     * @param int    $push_to_userId  推送给的用户
+     * @param array  $message_template_data 推送任务模板数据
+     */
+    public function pushMessage($message_content, $message_template_data, $params)
+    {
+        if(empty($message_content)){
+            echo '推送内容为空'.PHP_EOL;
+            return false;
+        }
+
+        $message_template_data['send_type'] = json_decode($message_template_data['send_type'],true);
+
+        $appPushData = [
+            'push_source' => $params['push_source'],
+            'pushPlatForm' => $params['pushPlatForm'],
+            'pushType' => $params['pushType'],
+            'alias' => $params['alias'],
+            'content'  => $message_content,
+            'apns_production' => $params['apns_production'],
+            'extras' => isset($params['extras']) ? $params['extras'] : [],
+        ];
+
+        foreach ($message_template_data['send_type'] as $key => $value){
+            switch($value){
+                case 1: // app 推送消息
+                    self::appPush($appPushData);
+                    break;
+                case 2: // 站内消息
+                    self::siteMessagePush($message_content, $params['push_to_userId'], $params['push_source'], $message_template_data, $params['order_id']);
+                    break;
+            }
+        }
+
+        echo '推送完成'.PHP_EOL;
+        return true;
+    }
+
+    /**
+     * 极光app推送方式
+     */
+    public function appPush($appPushData)
+    {
+
+        /*  老版请求云端
+        $i = 1;
+        do{
+            $url = 'http://devcloud.91youxian.net/v2/baseinfo/aurora/jpush?message='.$message_content.'&user_id='.$push_to_userId;
+            $result = request($url, null, 5);
+            if($result['httpcode'] != 200){
+                file_put_contents('/tmp/jipush.log',date('Y-m-d H:i:s').var_export($result,true).PHP_EOL,FILE_APPEND);
+
+                echo '请求极光推送失败'.PHP_EOL;
+            }
+
+            $i ++;
+            sleep(1);
+        }while($result['httpcode'] != 200 && $i <= 3);*/
+
+        $i = 1;
+        do{
+            switch ($appPushData['pushType']){
+                case 'all':
+                    $swResult = Middleware::getInstance('ThirdPartyService')->sendSwoole('Jpush', 'broadcastPush', $appPushData);
+                    break;
+                case 'alias':
+                    $swResult = Middleware::getInstance('ThirdPartyService')->sendSwoole('Jpush', 'aliasPush', $appPushData);
+                    break;
+            }
+
+            if( !$swResult['state'] ){
+                file_put_contents('/tmp/jipush.log',date('Y-m-d H:i:s').var_export($swResult['data'],true).PHP_EOL,FILE_APPEND);
+
+                echo '请求极光推送失败'.PHP_EOL;
+            }else{
+                $jpushResult = $swResult['data'];
+            }
+
+            $i ++;
+            sleep(1);
+        }while( !$swResult['state'] && $i <= 3);
+
+        echo '请求极光推送完成'.json_encode($jpushResult).PHP_EOL;
+        return true;
+    }
+    
+    /**
+     * 站内信推送方式
+     */
+    public function siteMessagePush($message_content, $push_to_userId, $push_source, $message_template_data, $order_id)
+    {
+        if($push_source == 'pifabbc'){
+            $objDUserMessage = new DUserMessage('pifabbc');
+        }else{
+            $objDUserMessage = new DUserMessage('bbc');
+        }
+
+        if($message_template_data['type'] == 1){
+            $order_id = 0;
+        }
+
+        $data = [
+            'message_name' => $message_template_data['template_name'],
+            'message_content' => $message_content,
+            'message_from'    => 1,
+            'message_to'      => $push_to_userId,
+            'type'            => $message_template_data['type'],
+            'create_time'     => time(),
+            'order_id'        => $order_id,
+        ];
+
+        $result = $objDUserMessage->insert($data);
+        if(!$result){
+            echo '操作数据库失败'.PHP_EOL;
+            return false;
+        }
+
+        echo '站内消息推送完成'.PHP_EOL;
+        return true;
+    }
+
+
+}

+ 107 - 0
Jobs/Model/Middleware.Class.php

@@ -0,0 +1,107 @@
+<?php
+namespace Jobs\Model;
+
+use Mall\Framework\Core\Config;
+use Mall\Framework\Core\ErrorCode;
+use Mall\Framework\Factory;
+
+class Middleware
+{
+
+    static private $instance;
+
+    private $resourceConfig;
+    private $Swoole_Client;
+
+    public function __construct($serviceName)
+    {
+        $this->resourceConfig = Config::getInstance()->get('dataResources');
+
+        if (!empty($this->resourceConfig)) {
+            $this->Swoole_Client = Factory::swoole($this->resourceConfig[$serviceName]);
+        }
+    }
+
+    /**
+     * 单例模式
+     *
+     * @param string $serviceName 服务名称
+     *
+     * @return object
+     *
+     * @throws \Exception
+     */
+    static public function getInstance($serviceName)
+    {
+        /*$serviceNameIndex = md5($serviceName);
+        if (!isset(self::$instance[$serviceNameIndex])) {
+            self::$instance[$serviceNameIndex] = new self($serviceName);
+        }
+
+        if (is_null(self::$instance[$serviceNameIndex]->resourceConfig)) {
+            throw new \Exception('instance resourceConfig is null');
+        }
+        return self::$instance[$serviceNameIndex];*/
+        return new self($serviceName);
+    }
+
+    /**
+     * 发送swoole请求 目前统一封装为json消息体传输
+     *
+     * @param string $controller 控制器
+     * @param string $action 方法
+     * @param array $params 需要的参数
+     *
+     * @return mixed
+     */
+    public function sendSwoole($controller, $action, array $params = [])
+    {
+        //请求串号
+        $serid = self::getRequestId();
+
+        $msg = $this->structureMsg($controller, $action, $params, $serid);
+        $result = $this->Swoole_Client->sendMsg($msg, $serid);
+
+        if (!empty($result)) {
+            return $result;
+        } else {
+            //todo 日志记录返回为空时得请求消息体
+
+            return [
+                'state' => false,
+                'data' => 'swoole未返回消息',
+                'errorcode' => ErrorCode::$swooleRecvError
+            ];
+        }
+    }
+
+    /**
+     * 生成请求串号
+     * @return int
+     */
+    static function getRequestId()
+    {
+        $us = strstr(microtime(), ' ', true);
+        return intval(strval($us * 1000 * 1000) . rand(100, 999));
+    }
+
+    /**
+     * 构造发送消息体
+     *
+     * @param string $controller 控制器
+     * @param string $action 方法
+     * @param array $params 需要的参数
+     * @param int   $serid  请求串号
+     *
+     * @return string
+     */
+    private function structureMsg($controller, $action, $params = [], $serid,  $uid = 0)
+    {
+        $data = json_encode([
+            'contorller' => ucfirst($controller),
+            'action' => $action,
+            'params' => $params
+        ], JSON_UNESCAPED_UNICODE);
+        return pack('NNN', strlen($data), $uid, $serid) . $data;
+    }
+}

+ 55 - 0
Jobs/Model/Queue/MBaseTopicQueue.Class.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace Jobs\Model\Queue;
+
+use Jobs\Model\Queue\MTopicQueueInterface;
+
+abstract class MBaseTopicQueue implements MTopicQueueInterface
+{
+    //队列优先级
+    const HIGH_LEVEL_1=1;
+    const HIGH_LEVEL_2=2;
+    const HIGH_LEVEL_3=3;
+    const HIGH_LEVEL_4=4;
+    const HIGH_LEVEL_5=5;
+
+    public $topics = [];
+    public $queue  = null;
+
+    public static function getConnection(array $config, $logger)
+    {
+    }
+
+    public function getTopics()
+    {
+        //根据key大到小排序,并保持索引关系
+        arsort($this->topics);
+
+        return array_values($this->topics);
+    }
+
+    public function setTopics(array $topics)
+    {
+        $this->topics = $topics;
+    }
+
+    public function push($topic, $job)
+    {
+    }
+
+    public function pop($topic)
+    {
+    }
+
+    public function len($topic)
+    {
+    }
+
+    public function close()
+    {
+    }
+
+    public function isConnected()
+    {
+    }
+}

+ 114 - 0
Jobs/Model/Queue/MRedisTopicQueue.Class.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace Jobs\Model\Queue;
+
+use Jobs\Model\Queue\MBaseTopicQueue;
+
+class MRedisTopicQueue extends MBaseTopicQueue
+{
+    private $logger =null;
+
+    /**
+     * RedisTopicQueue constructor.
+     * 使用依赖注入的方式.
+     *
+     * @param \Redis $redis
+     */
+    public function __construct(\Redis $redis, $logger)
+    {
+        $this->queue   = $redis;
+        $this->logger  = $logger;
+    }
+
+    public static function getConnection(array $config, $logger)
+    {
+        try {
+            $redis = new \Redis();
+            $redis->connect($config['host'], $config['port']);
+
+            if (isset($config['password']) && !empty($config['password'])) {
+                $redis->auth($config['password']);
+            }
+            if (isset($config['database']) && !empty($config['database'])) {
+                $redis->select($config['database']);
+            }
+
+        } catch (\Throwable $e) {
+            catchError($logger, $e);
+
+            return false;
+        } catch (\Exception $e) {
+            catchError($logger, $e);
+
+            return false;
+        }
+        $connection = new self($redis, $logger);
+
+        return $connection;
+    }
+
+    /**
+     * Push message to queue.
+     *
+     * @param [type]    $topic
+     * @param object $job
+     *
+     * @return string
+     */
+    public function push($topic, $job)
+    {
+        if (!$this->isConnected()) {
+            return '';
+        }
+
+        $this->queue->lPush($topic, serialize($job));
+
+        return $job->uuid ?: '';
+    }
+
+    public function pop($topic)
+    {
+        if (!$this->isConnected()) {
+            return;
+        }
+
+        $result = $this->queue->lPop($topic);
+
+        return !empty($result) ? unserialize($result) : null;
+    }
+
+    public function len($topic)
+    {
+        if (!$this->isConnected()) {
+            return 0;
+        }
+
+        return (int) $this->queue->lLen($topic) ?: 0;
+    }
+
+    public function close()
+    {
+        if (!$this->isConnected()) {
+            return;
+        }
+
+        $this->queue->close();
+    }
+
+    public function isConnected()
+    {
+        try {
+            $this->queue->ping();
+        } catch (\Throwable $e) {
+            catchError($this->logger, $e);
+
+            return false;
+        } catch (\Exception $e) {
+            catchError($this->logger, $e);
+
+            return false;
+        }
+
+        return true;
+    }
+}

+ 48 - 0
Jobs/Model/Queue/MTopicQueueInterface.Class.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace Jobs\Model\Queue;
+
+interface MTopicQueueInterface
+{
+    public static function getConnection(array $config, $logger);
+
+    /**
+     * @return array a array of topics
+     */
+    public function getTopics();
+
+    /**
+     * @param array $topics
+     */
+    public function setTopics(array $topics);
+
+    /**
+     * 推送队列,返回jobid字符串.
+     *
+     * @param [type]    $topic
+     * @param JobObject $job
+     *
+     * @return string
+     */
+    public function push($topic, $job);
+
+    /**
+     * 从队列拿消息.
+     *
+     * @param [type] $topic
+     *
+     * @return array
+     */
+    public function pop($topic);
+
+    /**
+     * @param $topic
+     *
+     * @return int
+     */
+    public function len($topic);
+
+    public function close();
+
+    public function isConnected();
+}

+ 33 - 0
Jobs/Model/Test/K3Curl.Class.php

@@ -0,0 +1,33 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ASUS
+ * Date: 2018/7/18
+ * Time: 10:53
+ */
+
+namespace Jobs\Model\Test;
+class K3Curl
+{
+    public static function MyCurlPost($url, $post_content, $cookie_jar, $isLogin)
+    {
+        $ch = curl_init($url);
+        $this_header = array(
+            'Content-Type: application/json',
+            'Content-Length: ' . strlen($post_content)
+        );
+        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $this_header);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_content);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        if ($isLogin) {
+            curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar);
+        } else {
+            curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
+        }
+        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
+        $result = curl_exec($ch);
+        curl_close($ch);
+        return $result;
+    }
+}

+ 165 - 0
Jobs/Model/Test/K3Handle.Class.php

@@ -0,0 +1,165 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ASUS
+ * Date: 2018/7/18
+ * Time: 11:00
+ */
+
+namespace Jobs\Model\Test;
+
+use Jobs\Dao\DCloudUsers;
+use Jobs\Dao\DUsersCategory;
+use Jobs\Dao\DUsersTestify;
+class K3Handle
+{
+    public function store($data,$erp_no)
+    {
+        $param = [];
+        $DGusersCategory = new DUsersCategory();
+        $param['user_id'] = $data['user_id'];
+        $param['company_name'] = $data['company_name']?:'';
+        $param['referrer_phone'] = $data['referrer_phone']?:'';
+        $param['legal_person'] = $data['legal_person']?:'';
+        $param['contact_phone'] = $data['contact_phone']?:'';
+        $param['detail_address'] = $data['company_address']?:''.$data['detail_address']?:'';
+        $param['category_id'] = $data['category_id']?:0;
+
+        if ($param['category_id']){
+            $res_data =$DGusersCategory ->get($param['category_id']);
+        }
+        $param['erp_no'] = $erp_no;
+
+        //$param['referrer_name'] = $data['referrer_name'];
+        $param['referrer_no'] = $data['referrer_no'];
+        $param['referrer_group_no'] = $data['referrer_group_no'];
+        $DGusers = new DCloudUsers();
+        if (isset($res_data)&&!empty($res_data)) {
+            $param['category_no'] = $res_data['category_no'];
+
+            //$UserCat = new UsersCategoryModel[);
+            //取出分类
+            if ($param['category_no']) {
+                $cateInfo = $DGusersCategory->get(['category_no' => $param['category_no']]);
+                $id = $cateInfo['pid'];
+                $cateInfo2 = $DGusersCategory->get($id);
+                if ($cateInfo2) {
+                    $id = $cateInfo2['pid'];
+                    $cateInfo1 = $DGusersCategory  -> get($id);
+                    if ($cateInfo1) {
+                        $category_no2 = $cateInfo2['category_no'];
+                        $category_no1 = $cateInfo1['category_no'];
+                        $category_no3 = $param['category_no'];
+                    } else {
+                        $category_no1 = $cateInfo2['category_no'];
+                        $category_no2 = $param['category_no'];
+                        $category_no3 = '';
+                    }
+                } else {
+                    $category_no1 = $param['category_no'];
+                    $category_no2 = '';
+                    $category_no3 = '';
+                }
+            }
+        }else{
+
+            $res_uesr =$DGusers ->get($param['user_id']);
+            if (!empty($res_uesr)){
+                return $response = [
+                    'code' =>500,
+                ];
+                file_put_contents('K3_sync_empty_erp_no.log','法人;客户手机号;联系电话,详细地址'.PHP_EOL.$param['legal_person'] .';'.$res_uesr['phone'].';'.$param['contact_phone'].';'.$param['detail_address'].PHP_EOL,FILE_APPEND);
+            }else{
+                return $response = [
+                    'code' =>500,
+                ];
+            }
+
+        }
+
+        if ($param['referrer_group_no']){
+            $k3Result = $this ->syncCustomer(isset($category_no1)?$category_no1:'', $param, isset($category_no2)?$category_no2:'', isset($category_no3)?$category_no3:'',$data['id']);
+            if ($k3Result['code']!=200){
+                return ['code' =>500,'msg'=>$k3Result['msg']];
+            }
+        }else{
+            $res_uesr =$DGusers ->get($param['user_id']);
+            file_put_contents('K3_sync_empty_referrer_no.log','法人;客户手机号;联系电话,详细地址'.PHP_EOL.$param['legal_person'] .';'.$res_uesr['phone'].';'.$param['contact_phone'].';'.$param['detail_address'].PHP_EOL,FILE_APPEND);
+
+            return ['code' =>500];
+        }
+
+    }
+
+    public function syncCustomer($category_no1,$param,$category_no2,$category_no3, $id=0)
+    {
+        $objectDusersTestify = new DUsersTestify();
+        $cookie_jar = './k3.ini';
+        $data = array(
+            //业务对象表单Id(必录)
+            'Model'=>[
+                'FNumber' => $param['erp_no'],
+                'FCreateOrgId'=>['FNumber' => '101'], //
+                'FUseOrgId'=>['FNumber' => '101'],
+                'FName'=>$param['legal_person'],
+                'FADDRESS'=>$param['detail_address'],
+                'FTEL'=>$param['contact_phone'],
+                'FSALDEPTID'=>['FNumber' =>$param['referrer_no']],
+                'FSALGROUPID'=>['FNumber' =>$param['referrer_group_no']],
+                'FSELLER'=>['FNumber' =>$param['referrer_no']],
+                'FCustTypeId'=>['FNumber' =>'KHLB001_SYS'],
+                'FTRADINGCURRID'=>['FNumber' =>'PRE001'],
+                'F_PAEZ_Assistant5'=>['FNumber' =>$category_no1],
+                'F_PAEZ_Assistant6'=>['FNumber' =>$category_no2],
+                'F_PAEZ_Assistant3'=>['FNumber' =>$category_no3],
+            ],
+            'Creator' => '亿成商城'
+        );
+
+        //V($data);
+        //定义记录Cloud服务端返回的Session
+        $K3login = new K3Login();
+        $K3Curl = new K3Curl();
+        $post_content = $K3login->create_postdata(['BD_Customer',$data]);
+
+//        $url = 'http://192.168.0.135/k3cloud/'.'Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save.common.kdsvc';
+        $url = 'http://123.206.51.139/k3cloud/'.'Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save.common.kdsvc';
+        $res = $K3login->loginIfNeed();
+        //V($post_content);
+        //V($res);
+        $resultJsonSave =$K3Curl->MyCurlPost($url, $post_content, $cookie_jar, false);
+        //V($resultJsonSave);
+        file_put_contents('K3_sync_users_testify.log',date('Y-m-d H:i:s').'发送数据'.$post_content.PHP_EOL.'返回的数据:'.var_export($resultJsonSave ,true).PHP_EOL,FILE_APPEND);
+        $resultArray = json_decode($resultJsonSave, true);
+        //V($resultArray);
+        //判断是否已经有该客户
+        if ($resultArray['Result']['ResponseStatus']['IsSuccess']) {
+            $K3_id = $resultArray['Result']['Id'];
+            if ($K3_id){
+                $res = $objectDusersTestify->update(['k3_id' =>$K3_id],['id'=>$id]);
+            }
+            $response = array(
+                'code' => 200,
+                'msg' => 'K3保存成功',
+                'info' => 'K3保存成功',
+            );
+            return $response;
+        }else{
+            return  array(
+                'code' => 500,
+                'msg' => $resultArray['Result']['ResponseStatus']['Errors'][0]['Message'],
+                'info' => 'K3保存成功',
+            );
+        }
+//        if (!strpos($resultArray['Result']['ResponseStatus']['Errors'][0]['Message'], '客户,组织内编码唯一')) {
+//            $response = array(
+//                'code' => 500,
+//                'msg' => $resultArray['Result']['ResponseStatus']['Errors'][0]['Message'],
+//                'info' => 'K3:' . $resultArray['Result']['ResponseStatus']['Errors'][0]['Message'],
+//            );
+//            return $response;
+//        }
+
+
+        }
+    }

+ 54 - 0
Jobs/Model/Test/K3login.Class.php

@@ -0,0 +1,54 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ASUS
+ * Date: 2018/7/18
+ * Time: 10:50
+ */
+
+namespace Jobs\Model\Test;
+
+
+class K3Login
+{
+
+    public function loginIfNeed()
+    {
+        $cookie_jar = './k3.ini';
+        $K3Curl = new K3Curl();
+        $data = array('597be128163613', '秦统业', 'Cwb20141222', '2052');        // 线上
+//        $data = array('5b499257f851c8', 'liuxin', '123456', '2052');        // 线上
+
+        $post_content = $this ->create_postdata($data);
+
+        $loginurl = 'http://123.206.51.139/k3cloud/' . 'Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc';
+//        $loginurl = 'http://192.168.0.135/k3cloud/' . 'Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc';
+        $loginResult = $K3Curl -> MyCurlPost($loginurl, $post_content, $cookie_jar, TRUE);
+        return $loginResult;
+    }
+    public function create_postdata($args){
+        $postdata = array(
+            'format' => 1,
+            'useragent' => 'ApiClient',
+            'rid' => self::create_guid(),
+            'parameters' => $args,
+            'timestamp' => time(),
+            'v' => '1.0'
+        );
+        return json_encode($postdata);
+    }
+
+    public static function create_guid()
+    {
+        $charid = strtoupper(md5(uniqid(mt_rand(), true)));
+        $hyphen = chr(45);// "-"
+        $uuid = chr(123)// "{"
+            . substr($charid, 0, 8) . $hyphen
+            . substr($charid, 8, 4) . $hyphen
+            . substr($charid, 12, 4) . $hyphen
+            . substr($charid, 16, 4) . $hyphen
+            . substr($charid, 20, 12)
+            . chr(125);// "}"
+        return $uuid;
+    }
+}

BIN
Jobs/Public/.DS_Store


+ 25 - 0
Jobs/Public/www/index.php

@@ -0,0 +1,25 @@
+<?php
+use Mall\Framework\Factory;
+use Mall\Framework\Core\Config;
+
+require_once __DIR__. DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .'..' . DIRECTORY_SEPARATOR .'..' . DIRECTORY_SEPARATOR . 'Mall' . DIRECTORY_SEPARATOR . 'Bootstrap.php';
+// 加载项目全局变量
+require_once dirname(dirname(dirname(__FILE__))) .DS. '.ENV.php';
+
+//switch ($_SERVER['HTTP_ENV']){
+//    case 'beta':
+//        $configPath = PROJECT_PATH . DS . 'Config' . DS . 'BetaConfig.php';
+//        break;
+//    case 'master':
+//        $configPath = PROJECT_PATH . DS . 'Config' . DS . 'Config.php';
+//        break;
+//    default:
+//        $configPath = PROJECT_PATH . DS . 'Config' . DS . 'DevConfig.php';
+//}
+$configPath = PROJECT_PATH . DS . 'Config' . DS . 'Config.php';
+
+Config::load($configPath);
+
+
+// 加载框架入口文件
+Factory::request()->dispatch(false,false);

+ 23 - 0
Jobs/start.php

@@ -0,0 +1,23 @@
+<?php
+use Mall\Framework\Core\Config;
+use Jobs\Model\MConsole;
+
+require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .  'Mall' . DIRECTORY_SEPARATOR . 'Bootstrap.php';
+
+// 加载项目全局变量
+require_once dirname(__FILE__) .DS. '.ENV.php';
+
+
+$configPath = __DIR__ . DS . 'Config' . DS . 'Config.php';
+
+if (!file_exists($configPath)) {
+    exit($configPath . 'config file is not found');
+}
+
+Config::load($configPath);
+$config = Config::getInstance()->all();
+
+$console = new MConsole($config);
+$console->run();
+//【影子:报错添加】
+Swoole\Event::wait();

+ 27 - 0
Mall/Bootstrap.php

@@ -0,0 +1,27 @@
+<?php
+define('DS', DIRECTORY_SEPARATOR);
+define('ROOT_PATH', __DIR__);
+define('FRAMEWORK_PATH', ROOT_PATH . DS . 'Framework');
+define('FRAMEWORK_NAME', 'Mall');
+define('PACKAGE_EOF', '--END--');
+define('PACKAGE_MAXLENG', 2465792);
+define('PACKAGE_BODY_OFFSET', 12);
+
+# 默认将显示错误关闭
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+ini_set('xdebug.remote_enable', 0);
+
+# 记录所有错误到日志文件
+define('LOG_ERROR', TRUE);
+
+# 是否开启队列日志
+define('QUEUE_RUNLOG', true);
+
+# 设置默认时区
+date_default_timezone_set('PRC');
+
+require_once FRAMEWORK_PATH . DS . 'Common' . DS . 'Functions.php';
+require_once FRAMEWORK_PATH . DS . 'Core' . DS . 'AutoLoad.Class.php';
+
+spl_autoload_register(array('Mall\Framework\Core\AutoLoad', '_autoLoad'));

+ 28 - 0
Mall/Framework/Cache/AbstractStorage.Class.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace Mall\Framework\Cache;
+
+abstract class AbstractStorage implements StorageInterface
+{
+	protected $prefix = 'mallguang::';
+
+    protected $options = array();
+
+    protected $ttl = 0;
+
+	public function __construct($options = null)
+	{
+        if (!is_array($options)) {
+            throw new \Exception('Cache options cannot be found');
+        }
+
+        $this->options = $options;
+        $prefix = FALSE;
+
+        if ($this->options['prefix']) {
+            $prefix = str_replace(':', '', $this->options['prefix']) . '::';
+        }
+
+        $this->prefix = $prefix ?: $this->prefix;
+    }
+}

+ 609 - 0
Mall/Framework/Cache/Redis.Class.php

@@ -0,0 +1,609 @@
+<?php
+
+namespace Mall\Framework\Cache;
+
+use Redis as RedisSource;
+use Mall\Framework\Cache\AbstractStorage as Storage;
+
+class Redis extends Storage
+{
+
+    /**
+     * @var RedisSource|array
+     */
+    protected $resource;
+
+
+    public function __construct($options)
+    {
+        parent::__construct($options);
+
+        //判断redis扩展是否已经安装
+        if(!extension_loaded('redis')){
+            throw new \Exception('Redis extension is not found');
+        }
+
+        $this->resource = new RedisSource();
+        $port = $this->options['port'] ?: 6379;
+
+        if (!$this->resource->connect($this->options['host'], $port, 3)) {
+            throw new \Exception(sprintf(
+                'Cannot connect to redis server on %s:%d',
+                $this->options['host'], $this->options['port']
+            ));
+        }
+        if ($this->options['auth'] && !$this->resource->auth($this->options['auth'])) {
+            throw new \Exception(sprintf(
+                'Auth failed on %s:%d, auth: %s',
+                $this->options['host'], $this->options['port'], $this->options['auth']
+            ));
+        }
+
+        if (isset($this->options['database']) && !$this->resource->select($this->options['database'])) {
+            throw new \Exception(sprintf(
+                'Select Database failed on %s:%d, auth: %s, database:%d',
+                $this->options['host'], $this->options['port'], $this->options['auth'], $this->options['database']
+            ));
+        }
+
+        $this->resource->setOption(RedisSource::OPT_PREFIX, 'Mall_');
+
+    }
+
+    public function set($key, $value = null, $ttl = null)
+    {
+        $key = $this->prefix . $key;
+        $ttl = $ttl ?: 0;
+        $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
+
+        if ($ttl > 0) {
+            $ret = $this->resource->setex($key, $ttl, $value);
+        } else {
+            $ret = $this->resource->set($key, $value);
+        }
+
+        return $ret;
+    }
+
+    public function get($key)
+    {
+        $key = $this->prefix . $key;
+
+        $value = $this->resource->get($key);
+        $jsonData = json_decode($value, true);
+
+        return ($jsonData === NULL) ? $value : $jsonData;
+    }
+
+    public function incr($key)
+    {
+        $key = $this->prefix . $key;
+
+        return $this->resource->incr($key);
+    }
+
+    /**
+     * 将 key 所储存的值加上增量 increment
+     * @param $key
+     * @return int
+     */
+    public function incrby($key, $increment)
+    {
+        $key = $this->prefix . $key;
+
+        return $this->resource->incrBy($key, $increment);
+    }
+
+    public function del($key)
+    {
+        if(is_array($key)){
+            foreach ($key as $k => $v){
+                $key[$k] = $this->prefix . $v;
+            }
+        }else{
+            $key = $this->prefix . $key;
+        }
+        $ret = $this->resource->del($key);
+        return $ret;
+    }
+
+    public function has($key)
+    {
+        $key = $this->prefix . $key;
+
+        $ret = $this->resource->exists($key);
+
+        return $ret;
+    }
+
+    public function delete($key)
+    {
+        $key = $this->prefix . $key;
+
+        $ret = $this->resource->delete($key) > 0;
+
+        return $ret;
+    }
+
+    public function push($key, $value, $type = 'end')
+    {
+        $key = $this->prefix . $key;
+        $ret = FALSE;
+
+        if ($type == 'end') {
+            $ret = $this->resource->rpush($key, $value);
+        } else if ($type == 'start') {
+            $ret = $this->resource->lpush($key, $value);
+        }
+
+        return $ret;
+    }
+
+    public function pop($key, $type = 'start')
+    {
+        $key = $this->prefix . $key;
+        $ret =FALSE;
+
+        if ($type == 'end') {
+            $ret = $this->resource->rpop($key);
+        } else if ($type == 'start') {
+            $ret = $this->resource->lpop($key);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
+     * @param $key
+     * @param $start
+     * @param $stop
+     * @return array
+     */
+    public function lrange($key, $start, $stop)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->lRange($key, $start, $stop);
+    }
+
+    /**
+     * 返回列表 key 中,下标为 index 的元素
+     * @param $key
+     * @param $start
+     * @param $stop
+     * @return array
+     */
+    public function lindex($key, $index)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->lIndex($key, $index);
+    }
+
+    /**
+     * 根据参数 count 的值,移除列表中与参数 value 相等的元素
+     */
+    public function lrem($key, $value, $count = 0)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->lRem($key, $value, $count);
+    }
+
+    /**
+     * @param $key
+     * @param array['field1'=>'value', 'field2'=>'value'] $values
+     * @return int
+     */
+    public function hmset($key, $values){
+        $key = $this->prefix . $key;
+
+        return $this->resource->hMSet($key, $values);
+    }
+
+    /**
+     * @param $key
+     * @param  array['field1', 'field2'] $value
+     *
+     * @return int
+     */
+    public function hmget($key, $fields){
+        $key = $this->prefix . $key;
+        return $this->resource->hMGet($key, $fields);
+    }
+
+    public function hset($key, $field, $value){
+        $key = $this->prefix . $key;
+        return $this->resource->hSet($key, $field, $value);
+    }
+
+    public function hget($key, $field){
+        $key = $this->prefix . $key;
+        return $this->resource->hGet($key, $field);
+    }
+
+    public function hdel($key, $field){
+        $key = $this->prefix . $key;
+        return $this->resource->hDel($key, $field);
+    }
+
+    public function hkeys($key){
+        $key = $this->prefix . $key;
+        return $this->resource->hKeys($key);
+    }
+
+    public function hvalues($key){
+        $key = $this->prefix . $key;
+        return $this->resource->hVals($key);
+    }
+
+    /**
+     * @param $key
+     * @param $field
+     * @return bool
+     */
+    public function hExists($key, $field)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->hExists($key, $field);
+    }
+
+    public function hIncrBy($key, $field,$value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->hIncrBy($key, $field,$value);
+    }
+
+    public function hIncrByFloat($key, $field,$value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->hIncrByFloat($key, $field,$value);
+    }
+
+    /**
+     * 集合
+     *
+     * @param $key
+     * @param $value
+     *
+     * @return bool
+     */
+    public function sadd($key, $value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->sAdd($key, $value);
+    }
+
+    /**
+     * 集合sadd统计
+     *
+     * @param $key
+     *
+     * @return int
+     */
+    public function scard($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->sCard($key);
+    }
+
+    /**
+     * 移除集 key 中的一个或多个成员,不存在的成员将被忽略。
+     */
+    public function srem($key, $value){
+        $key = $this->prefix . $key;
+        return $this->resource->sRem($key, $value);
+    }
+
+    /**
+     * 随机取集 key 中的一个或多个成员,不存在的成员将被忽略。
+     * @param $value 获取的条数,默认1
+     */
+    public function srandmember($key, $value=1){
+        $key = $this->prefix . $key;
+        return $this->resource->sRandMember($key,$value);
+    }
+
+    /**
+     * 设置客户端
+     */
+    public function setOption($option, $value)
+    {
+        return $this->resource->setOption($option, $value);
+    }
+
+    /**
+     * 迭代查询
+     * @params string $key 集合名
+     * @params string $keyword 要查询的关键词
+     */
+    public function scan($key, $keyword)
+    {
+        $key = $this->prefix . $key;
+        $iterator = null;
+        while ($members = $this->resource->sScan($key, $iterator, $keyword)) {
+            foreach ($members as $member) {
+                echo $member . PHP_EOL;
+            }
+        }
+    }
+
+
+    /**
+     * 有序集合
+     *
+     * @param $key
+     * @param $value
+     *
+     * @return bool
+     */
+    public function zadd($key, $score , $value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zAdd($key, $score, $value);
+    }
+
+    /**
+     * 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员
+     */
+    public function zrangebyscore($key, $start, $end, array $options = array()){
+        $key = $this->prefix . $key;
+        return $this->resource->zRangeByScore($key, $start, $end, $options);
+    }
+
+    /**
+     * 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。
+     */
+    public function zrem($key, $value){
+        $key = $this->prefix . $key;
+        return $this->resource->zRem($key, $value);
+    }
+
+    /**
+     * 除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员
+     */
+    public function zremrangebyscore($key, $start, $end){
+        $key = $this->prefix . $key;
+        return $this->resource->zRemRangeByScore($key, $start, $end);
+    }
+
+    /**
+     * 判断member元素是否是集合Key的成员
+     *
+     * @param $key
+     * @param $value
+     *
+     * @return bool
+     */
+    public function sismember($key, $value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->sIsMember($key, $value);
+    }
+
+    /**
+     * 返回集合中的所有成员
+     *
+     * @param $key
+     *
+     * @return bool
+     */
+    public function smembers($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->sMembers($key);
+    }
+
+    /**
+     * 返回集合之间的差集
+     *
+     * @param string $key1
+     * @param string $key2
+     *
+     * @return object
+     */
+    public function sdiff($key1, $key2)
+    {
+        $key1 = $this->prefix . $key1;
+        $key2 = $this->prefix . $key2;
+
+        return $this->resource->sDiff($key1, $key2);
+    }
+
+    /**
+     * 返回集合之间的交集
+     *
+     * @param string $key1
+     * @param string $key2
+     *
+     * @return object
+     */
+    public function sinter($key1, $key2)
+    {
+        $key1 = $this->prefix . $key1;
+        $key2 = $this->prefix . $key2;
+
+        return $this->resource->sInter($key1, $key2);
+    }
+
+    /**
+     * 计算集合之间的交集并写入一个新的key中
+     *
+     * @param string $deskey  要存入的新key
+     * @param string $key1
+     * @param string $key2
+     *
+     * @return int 新集合得数量
+     */
+    public function sinterStore($deskey, $key1, $key2)
+    {
+        $deskey = $this->prefix .$deskey;
+        $key1 = $this->prefix . $key1;
+        $key2 = $this->prefix . $key2;
+
+        return $this->resource->sInterStore($deskey, $key1, $key2);
+    }
+
+    /**
+     * 返回集合之间的并集
+     *
+     * @param string $key1
+     * @param string $key2
+     *
+     * @return object
+     */
+    public function sunion($key1, $key2)
+    {
+        $key1 = $this->prefix . $key1;
+        $key2 = $this->prefix . $key2;
+
+        return $this->resource->sUnion($key1, $key2);
+    }
+
+    /**
+     * 事物支持
+     */
+    public function multi()
+    {
+        return $this->resource->multi();
+    }
+
+    /**
+     * 执行事务
+     */
+    public function exec()
+    {
+        return $this->resource->exec();
+    }
+
+    /**
+     * 返回整数为列表键长度
+     *
+     * @param string $key
+     *
+     * @return int
+     */
+    public function llen($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->llen($key);
+    }
+
+    /**
+     * 有序集合中对指定成员的分数加上增量 increment
+     *
+     * @param $key
+     * @param $value
+     * @param $member
+     *
+     * @return float
+     */
+    public function zincrby($key, $value, $member)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zIncrBy($key, $value, $member);
+    }
+
+    /**
+     * 返回有序集中,成员的分数值
+     *
+     * @param $key
+     * @param $member
+     *
+     * @return float
+     */
+    public function zscore($key, $member)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zScore($key, $member);
+    }
+
+    /**
+     * 获取有序集合的成员数
+     *
+     * @param $key
+     *
+     * @return int
+     */
+    public function zcard($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zCard($key);
+    }
+
+    /**
+     * 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量
+     *
+     * @return int
+     */
+    public function zcount($key, $min, $max)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zCount($key, $min, $max);
+    }
+
+    /**
+     * 返回有序集 key 中,指定区间内的成员。
+     *
+     * @param $key
+     * @param $start
+     * @param $end
+     *
+     * @return array
+     */
+    public function zrange($key, $start, $end, $withscores = null)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zRange($key, $start, $end, $withscores);
+    }
+
+    /**
+     * 返回有序集 key 中,指定区间内的成员,按score从大到小的顺序
+     *
+     * @param $key
+     * @param $start
+     * @param $end
+     *
+     * @return array
+     */
+    public function zRevRange($key, $start, $end, $withscores = null)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zRevRange($key, $start, $end, $withscores);
+    }
+
+    /**
+     * 获取所有的keys
+     */
+    public function keys($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->keys($key);
+    }
+
+    /**
+     * 获取配置项的值
+     */
+    public function getOption($name)
+    {
+        return $this->resource->getOption($name);
+    }
+
+    /**
+     * 设置key的生存时间
+     */
+    public function expire($key, $time)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->expire($key, $time);
+    }
+
+    /**
+     * 获取键到期的剩余时间(秒)
+     * @param $key
+     * @return bool|int
+     */
+    public function ttl($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->ttl($key);
+    }
+}

+ 52 - 0
Mall/Framework/Cache/StorageInterface.Class.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Mall\Framework\Cache;
+
+interface StorageInterface
+{
+	/**
+	 * @param string $key
+     *
+	 * @return mixed
+	 */
+	public function get($key);
+
+    /**
+     * @param $key
+     * @param null $value
+     * @param null $ttl
+     *
+     * @return mixed
+     */
+    public function set($key, $value = null, $ttl = null);
+
+    /**
+     * @param string $key
+     *
+     * @return bool
+     */
+    public function has($key);
+
+    /**
+     * @param string $key
+     *
+     * @return bool
+     */
+    public function delete($key);
+
+    /**
+     * @param string $key
+     * @param mixed $value
+     * @param string $type start | end
+     *
+     * @return int|bool The new value on success, false on failure
+     */
+    public function push($key, $value, $type = 'end');
+    /**
+     * @param string $key
+     * @param string $type start | end
+     * @return int|bool The new value on success, false on failure
+     */
+    public function pop($key, $type = 'start');
+
+}

+ 1188 - 0
Mall/Framework/Common/Functions.php

@@ -0,0 +1,1188 @@
+<?php
+
+function getArrayItem(array $array,$key = 0, $defaultValue = '')
+{
+    if (!is_array($array)){
+        return $defaultValue;
+    }
+    return (isset($array[$key]) && !empty($array[$key])) ? $array[$key] : $defaultValue;
+}
+
+function loadFile($filepath, $return = false)
+{
+    if (file_exists($filepath)) {
+        if ($return) {
+            return include $filepath;
+        } else {
+            include $filepath;
+        }
+    } else {
+        throw new \Exception($filepath . ' file cannot be found');
+    }
+}
+
+function xssCheck()
+{
+    if (isset($_SERVER['REQUEST_URI'])) {
+        $temp = strtoupper(urldecode(urldecode($_SERVER['REQUEST_URI'])));
+        if (strpos($temp, '<') !== false || strpos($temp, '"') !== false || strpos($temp, 'CONTENT-TRANSFER-ENCODING') !== false) {
+            throw new \Exception('request_tainting' . $temp);
+        }
+    }
+
+    return true;
+}
+
+/**
+ * @param $module string 服务模块名
+ * @throws Exception
+ */
+function loadServiceConfig($module)
+{
+    if (!$module) {
+        throw new \Exception('module not null');
+    }
+
+    $serviceConfig = ROOT_PATH . DS . '..' . DS . 'Service' . DS . ucfirst($module) . DS . 'Config.php';
+
+    return loadFile($serviceConfig, true);
+}
+
+/**
+ * 写入php错误日志
+ *
+ * @param string $errno 错误编号
+ * @param string $errmsg 错误信息
+ * @param string $filename 错误文件
+ * @param string $linenum 错误行数
+ * @param mixed $vars 错误参数
+ *
+ */
+function php_error_log($errno, $errmsg, $filename, $linenum, $vars)
+{
+    if (!defined('E_STRICT')) define('E_STRICT', 2048);
+
+    $errortype = array(
+        E_ERROR           => 'Error',
+        E_WARNING         => 'Warning',
+        E_PARSE           => 'Parsing Error',
+        E_NOTICE          => 'Notice',
+        E_CORE_ERROR      => 'Core Error',
+        E_CORE_WARNING    => 'Core Warning',
+        E_COMPILE_ERROR   => 'Compile Error',
+        E_COMPILE_WARNING => 'Compile Warning',
+        E_USER_ERROR      => 'User Error',
+        E_USER_WARNING    => 'User Warning',
+        E_USER_NOTICE     => 'User Notice',
+        E_STRICT          => 'Runtime Notice'
+    );
+    $user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE);
+    $filename = str_replace("\\", '/', $filename);
+    $dt = date('Y-m-d H:i:s');
+
+    $err = "#####" . $dt . "\n";
+    $err .= "```" . "\n";
+    $err .= "datetime     :" . $dt . "\n";
+    $err .= "errornum     :" . $errno . "\n";
+    $err .= "errormsg     :" . $errortype[$errno] . "\n";
+    $err .= "errortype    :" . $errmsg . "\n";
+    $err .= "scriptname   :" . $filename . "\n";
+    if (in_array($errno, $user_errors)) {
+        $err .= "scriptname   :" . wddx_serialize_value($vars, "Variables") . "\n";
+    }
+    $err .= "scriptlinenum:" . $linenum . "\n";
+    $err .= "```\n";
+
+    $logDir = PROJECT_PATH . DS . 'Storage' . DS . 'Logs' . DS . 'SysError' . DS;
+    $logFile = $logDir . date('m-d') . '.md';
+    if (!is_dir($logDir)) {
+        @mkdir($logDir);
+    }
+
+    @error_log($err, 3, $logFile);
+    @chmod($logFile, 0777);
+}
+
+
+function shutdown_function()
+{
+    $e = error_get_last();
+    if (isset($e['type'])) {
+        if (in_array($e['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR))) {
+            php_error_log($e['type'], $e['message'], $e['file'], $e['line'], NULL);
+        }
+    }
+}
+
+
+if (defined('LOG_ERROR') && LOG_ERROR) {
+    register_shutdown_function('shutdown_function');
+}
+
+function isMobile($mobile)
+{
+    return preg_match('#^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,6,7,8,3,5]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$#', $mobile)
+        ? true
+        : false;
+}
+
+/**电子邮箱格式校验*/
+function isEmail($email)
+{
+    $pattern = "/^([0-9A-Za-z\\-_\\.]+)@([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)$/i";
+
+    return preg_match_all($pattern, $email) ? true : false;
+}
+
+
+/**
+ * 利用curl模拟浏览器发送请求
+ *
+ * @param string $url 请求的URL
+ * @param array|string $post post数据
+ * @param int $timeout 执行超时时间
+ * @param boolean $sendcookie 是否发送当前cookie
+ * @param array $options 可选的CURL参数
+ * @return array
+ */
+function request($url, $post = null, $timeout = 40, $sendcookie = true, $options = array(), $ssl = false, $sslDate = array(), $useragent = false, $proxy = false, $http_build_query = true)
+{
+    if (empty($useragent)) {
+        if (isset($_SERVER['HTTP_USER_AGENT'])) {
+            $useragent = $_SERVER['HTTP_USER_AGENT'];
+        } else {
+            $useragent = 'internalloginuseragent';
+        }
+    }
+
+    if ($proxy) {
+        $ip_long = array(
+            array('607649792', '608174079'), //36.56.0.0-36.63.255.255
+            array('1038614528', '1039007743'), //61.232.0.0-61.237.255.255
+            array('1783627776', '1784676351'), //106.80.0.0-106.95.255.255
+            array('2035023872', '2035154943'), //121.76.0.0-121.77.255.255
+            array('2078801920', '2079064063'), //123.232.0.0-123.235.255.255
+            array('-1950089216', '-1948778497'), //139.196.0.0-139.215.255.255
+            array('-1425539072', '-1425014785'), //171.8.0.0-171.15.255.255
+            array('-1236271104', '-1235419137'), //182.80.0.0-182.92.255.255
+            array('-770113536', '-768606209'), //210.25.0.0-210.47.255.255
+            array('-569376768', '-564133889'), //222.16.0.0-222.95.255.255
+        );
+
+        $rand_key = mt_rand(0, 9);
+        $ip = long2ip(mt_rand($ip_long[$rand_key][0], $ip_long[$rand_key][1]));//随机生成国内某个ip
+
+        $result = getNewProxyId();
+        if ($result['error'] != '') {
+            echo $result['error'];
+        } else {
+            $rand_ip = $result['ip'];
+        }
+
+
+        $baseHeader = array(
+            "CLIENT-IP:{$ip}",
+            "X-FORWARDED-FOR:{$ip}",
+        );
+
+        if (isset($options[CURLOPT_HTTPHEADER])) {
+            $options[CURLOPT_HTTPHEADER] = array_merge($options[CURLOPT_HTTPHEADER], $baseHeader);
+        }
+    }
+
+    $ch = curl_init($url);
+    curl_setopt($ch, CURLOPT_HEADER, 0);
+    curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
+    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 35);
+    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : 40);
+
+    if (defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V4')) {
+        curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+    }
+
+    if ($sendcookie) {
+        $cookie = '';
+        foreach ($_COOKIE as $key => $val) {
+            $cookie .= rawurlencode($key) . '=' . rawurlencode($val) . ';';
+        }
+        curl_setopt($ch, CURLOPT_COOKIE, $cookie);
+    }
+    if ($post) {
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, (is_array($post) && $http_build_query) ? http_build_query($post) : $post);
+    }
+
+    if (!ini_get('safe_mode') && ini_get('open_basedir') == '') {
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+    }
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+
+    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);  //检查证书中是否设置域名且是否与提供的主机名匹配
+    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  //是否只信任CA颁发的证书
+    curl_setopt($ch, CURLOPT_SSLVERSION, 1);  //CURL_SSLVERSION_TLSv1
+    if ($ssl) {
+        /*curl_setopt($ch, CURLOPT_SSLCERT, '/apps/jindouyun/Public/upload/apiclient_cert.pem');
+        curl_setopt($ch, CURLOPT_SSLKEY, '/apps/jindouyun/Public/upload/apiclient_key.pem');*/
+        curl_setopt($ch, CURLOPT_SSLCERT, $sslDate['cert']);
+        curl_setopt($ch, CURLOPT_SSLKEY, $sslDate['key']);
+    }
+
+    foreach ($options as $key => $value) {
+        curl_setopt($ch, $key, $value);
+    }
+
+    if ($proxy && isset($rand_ip)) {
+        curl_setopt($ch, CURLOPT_PROXY, 'http://' . $rand_ip);
+    }
+
+
+    $ret = curl_exec($ch);
+    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+    $content_length = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
+    if (!$content_length) $content_length = curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD);
+    $info = curl_getinfo($ch);
+    $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
+    $errorMsg = curl_error($ch);
+    curl_close($ch);
+    return array(
+        'httpcode'       => $httpcode,
+        'info'           => $info,
+        'errorMsg'       => $errorMsg,
+        'content_length' => $content_length,
+        'content_type'   => $content_type,
+        'content'        => $ret
+    );
+}
+
+/**
+ * 获取最新的代理ip
+ * 代理测试工具 http://proxies.site-digger.com/proxy-test/
+ **/
+function getNewProxyId()
+{
+    //$url = 'http://webapi.http.zhimacangku.com/getip?num=1&type=2&pro=&city=0&yys=0&port=1&time=1&ts=1&ys=1&cs=1&lb=1&sb=0&pb=4&mr=1&regions=';
+    $url = 'http://http.tiqu.qingjuhe.cn/getip?num=1&type=2&pack=35299&port=1&lb=1&pb=4&regions=';
+    $data = request($url);
+    if ($data['httpcode'] == 200) {
+        $content = json_decode($data['content'], true);
+        if ($content['success'] == 'true') {
+            foreach ($content['data'] as $key => $value) {
+                $rand_ip = $value['ip'] . ':' . $value['port'];
+            }
+        } else {
+            $errorMessage = '获取代理ip失败' . $content['code'] . $content['msg'] . PHP_EOL;
+        }
+    }
+
+    if (isset($rand_ip)) {
+        return [
+            'ip'    => $rand_ip,
+            'error' => '',
+        ];
+    } else {
+        return [
+            'ip'    => '',
+            'error' => $errorMessage,
+        ];
+    }
+}
+
+/**
+ * 获取当前毫秒时间戳
+ */
+function msectime()
+{
+    list($msec, $sec) = explode(' ', microtime());
+    return (int)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
+}
+
+
+/**
+ * 断点调试输出方法
+ *
+ * @param mixed $msg 输出的内容
+ *
+ * @return string
+ */
+function V($msg)
+{
+    echo "<pre>";
+    var_export($msg);
+    exit();
+}
+
+/**
+ * 校验日期格式是否正确
+ *
+ * @param string $date 日期
+ * @param array $formats 需要检验的格式数组
+ *
+ * @return bool|int
+ */
+function checkDateIsValid($date, $formats = ["Y-m-d", "Y/m/d", "Y-m-d H:i:s", "Y/m/d H:i:s"])
+{
+    $unixTime = strtotime($date);
+    if (!$unixTime) {
+        return false;
+    }
+
+    foreach ($formats as $format) {
+        if (date($format, $unixTime) == $date) {
+            return $unixTime;
+        }
+    }
+
+    return false;
+}
+
+/**
+ * 二维数组根据某个字段排序
+ *
+ * @param array $needArray 需要排序的二维数组
+ * @param string $field 根据的字段排序
+ * @param array $sort 排序规则ASC 升序 | DESC 降序
+ *
+ * @return array;
+ */
+function arrayMultiSort(array $needArray, $field, $sort = 'ASC')
+{
+    $rule = [];
+    $arrSort = [];
+
+    $sort = strtoupper($sort);
+    $rule['direction'] = 'SORT_' . $sort;
+    $rule['field'] = $field;
+
+    foreach ($needArray AS $uniqid => $row) {
+        foreach ($row AS $key => $value) {
+            $arrSort[$key][$uniqid] = $value;
+        }
+    }
+    if ($rule['direction']) {
+        array_multisort($arrSort[$rule['field']], constant($rule['direction']), $needArray);
+    }
+
+    return $needArray;
+}
+
+/**
+ * 利用firephp输出调试到header
+ *
+ * 需要安装:
+ * firefox:FireBug | chrome:FirePHP for Chrome
+ *
+ * @param mixed $message 要输出的信息
+ * @param bool $showtime 是否显示执行时间
+ */
+function console($message, $showtime = false)
+{
+    static $lasttime = TIME;
+    static $_getInstance;
+
+    $thistime = microtime(true);
+    $usedtime = $thistime - $lasttime;
+    $lasttime = $thistime;
+
+    $label = $showtime ? sprintf("%09.5fs", $usedtime) : NULL;
+
+    if (is_null($_getInstance)) {
+        $_getInstance = \Mall\Framework\Core\FirePHP::getInstance(true);
+    }
+
+    $_getInstance->info($message, $label);
+}
+
+/**
+ * 检测数组的值不允许为空
+ *
+ * @param array $params 需要检测的数组
+ * @param array $checkKey 需要检测的key默认全部检测
+ * @param array $excludeKey 过虑不检测的Key
+ *
+ * @return string
+ */
+function checkNotEmpty($params, $excludeKey = [], $checkKey = [])
+{
+    $newParams = [];
+    if ($checkKey && is_array($checkKey)) {
+        foreach ($checkKey as $key) {
+            if (isset($params[$key])) {
+                $newParams[$key] = $params[$key];
+            }
+        }
+    }
+
+    if ($newParams) {
+        $params = $newParams;
+    }
+
+    if (is_array($params)) {
+        foreach ($params as $k => $v) {
+            if (in_array($k, $excludeKey, true)) {
+                continue;
+            }
+
+            if (is_array($v)) {
+                checkNotEmpty($v);
+            }
+
+            if (empty($v)) {
+                return $k . ' 不允许为空';
+            }
+        }
+    }
+}
+
+/**
+ * 友好格式化时间
+ * @param int $timestamp 时间
+ * @param array $formats
+ * @return string
+ */
+function formatDateTime($timestamp, $formats = null)
+{
+    if ($formats == null) {
+        $formats = array(
+            'DAY'           => '%s天前',
+            'DAY_HOUR'      => '%s天%s小时前',
+            'HOUR'          => '%s小时',
+            'HOUR_MINUTE'   => '%s小时%s分前',
+            'MINUTE'        => '%s分钟前',
+            'MINUTE_SECOND' => '%s分钟%s秒前',
+            'SECOND'        => '%s秒前',
+        );
+    }
+
+    /* 计算出时间差 */
+    $seconds = time() - $timestamp;
+    $minutes = floor($seconds / 60);
+    $hours = floor($minutes / 60);
+    $days = floor($hours / 24);
+    if ($days > 0 && $days < 31) {
+        $diffFormat = 'DAY';
+    } elseif ($days == 0) {
+        $diffFormat = ($hours > 0) ? 'HOUR' : 'MINUTE';
+        if ($diffFormat == 'HOUR') {
+            $diffFormat .= ($minutes > 0 && ($minutes - $hours * 60) > 0) ? '_MINUTE' : '';
+        } else {
+            $diffFormat = (($seconds - $minutes * 60) > 0 && $minutes > 0)
+                ? $diffFormat . '_SECOND' : 'SECOND';
+        }
+    } else {
+        $diffFormat = 'TURE_DATE_TIME';//超出30天, 正常时间显示
+    }
+
+    $dateDiff = null;
+    switch ($diffFormat) {
+        case 'DAY':
+            $dateDiff = sprintf($formats[$diffFormat], $days);
+            break;
+        case 'DAY_HOUR':
+            $dateDiff = sprintf($formats[$diffFormat], $days, $hours - $days * 60);
+            break;
+        case 'HOUR':
+            $dateDiff = sprintf($formats[$diffFormat], $hours);
+            break;
+        case 'HOUR_MINUTE':
+            $dateDiff = sprintf($formats[$diffFormat], $hours, $minutes - $hours * 60);
+            break;
+        case 'MINUTE':
+            $dateDiff = sprintf($formats[$diffFormat], $minutes);
+            break;
+        case 'MINUTE_SECOND':
+            $dateDiff = sprintf($formats[$diffFormat], $minutes, $seconds - $minutes * 60);
+            break;
+        case 'SECOND':
+            $dateDiff = sprintf($formats[$diffFormat], $seconds);
+            break;
+        default:
+            $dateDiff = date('Y-m-d H:i:s',$timestamp);
+    }
+    return $dateDiff;
+}
+
+/**
+ * 数组交叉排序并合并
+ *
+ * @param array $needo
+ * @param array $needt
+ *
+ * @return array
+ */
+function sortCross(array $needo = [], array $needt = [])
+{
+    $newData = [];
+    $needoCount = count($needo);
+    $needtCount = count($needt);
+    $size = $needoCount > $needtCount ? $needoCount : $needtCount;
+
+    for ($i = 0; $i < $size; $i++) {
+        if (isset($needo[$i])) {
+            array_push($newData, $needo[$i]);
+        }
+        if (isset($needt[$i])) {
+            array_push($newData, $needt[$i]);
+        }
+    }
+
+    return $newData;
+}
+
+/**
+ * 二维数组去重
+ *
+ * @param $list
+ * @param $key
+ *
+ * @return array
+ */
+function assocUnique($list, $key)
+{
+    $arr = array();
+    for ($i = 0; $i < count($list); $i++) {
+        if (isset($list[$i][$key])) {
+            if (!isset($arr[$list[$i][$key]])) {
+                $arr[$list[$i][$key]] = $list[$i];
+            }
+        }
+    }
+    return array_values($arr);
+}
+
+/**
+ * 为相对路径的缩略图加绝对地址
+ *
+ * @param string|array $thumbs 图片集合
+ * @param string $key 图片地址的Key
+ *
+ * @return array
+ */
+function absoluteImg($thumbs, $key = '')
+{
+    if ($thumbs) {
+        if (is_array($thumbs)) {
+            foreach ($thumbs as &$t) {
+                if ($key) {
+                    $value = $t[$key];
+                } else {
+                    $value = $t;
+                }
+                if (!$value) {
+                    continue;
+                }
+                if (!filter_var($value, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)) {
+                    if ($key) {
+                        $t[$key] = URL_UPLOAD . $value;
+                    } else {
+                        $t = URL_UPLOAD . $value;
+                    }
+                }
+            }
+        }
+
+        if (is_string($thumbs)) {
+            if (!filter_var($thumbs, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)) {
+                $thumbs = URL_UPLOAD . $thumbs;
+            }
+        }
+    }
+
+    return $thumbs;
+}
+
+/**
+ * 对内容字段进行 int 整形转换
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+function contentIntValFormat(array $params)
+{
+    $intVal = [
+        'id',
+        'appid',
+        'has_thumb',
+        'catid',
+        'created',
+        'createdby',
+        'published',
+        'publishedby',
+        'modified',
+        'modifiedby',
+        'digg',
+        'pv',
+        'virtual_pv',
+        'status',
+        'indexid',
+        'contentid',
+        'pagecount',
+        'weight',
+        'commend',
+        'noexpiration',
+        'commend_pv',
+        'iscount'
+    ];
+    foreach ($params as $k => $param) {
+        if (in_array($k, $intVal, true)) {
+            $params[$k] = intval($param);
+        }
+    }
+
+    return $params;
+}
+
+/**
+ * 根据用户年月日算出星座
+ *
+ * @param string $dob 出生年月 1990-12-18
+ *
+ * @return string
+ */
+function constellation($dob)
+{
+    $dob = date("m-d", strtotime($dob));
+    list($month, $day) = explode("-", $dob);
+
+    $constellation = '';
+    if (($month == 3 || $month == 4) && ($day > 22 || $day < 21)) {
+        $constellation = "Aries";
+    } else if (($month == 4 || $month == 5) && ($day > 22 || $day < 22)) {
+        $constellation = "Taurus";
+    } else if (($month == 5 || $month == 6) && ($day > 23 || $day < 22)) {
+        $constellation = "Gemini";
+    } else if (($month == 6 || $month == 7) && ($day > 23 || $day < 23)) {
+        $constellation = "Cancer";
+    } else if (($month == 7 || $month == 8) && ($day > 24 || $day < 22)) {
+        $constellation = "Leo";
+    } else if (($month == 8 || $month == 9) && ($day > 23 || $day < 24)) {
+        $constellation = "Virgo";
+    } elseif (($month == 9 || $month == 10) && ($day > 25 || $day < 24)) {
+        $constellation = "Libra";
+    } else if (($month == 10 || $month == 11) && ($day > 25 || $day < 23)) {
+        $constellation = "Scorpio";
+    } else if (($month == 11 || $month == 12) && ($day > 24 || $day < 23)) {
+        $constellation = "Sagittarius";
+    } else if (($month == 12 || $month == 1) && ($day > 24 || $day < 21)) {
+        $constellation = "Cpricorn";
+    } else if (($month == 1 || $month == 2) && ($day > 22 || $day < 20)) {
+        $constellation = "Aquarius";
+    } else if (($month == 2 || $month == 3) && ($day > 21 || $day < 21)) {
+        $constellation = "Pisces";
+    }
+
+    return $constellation;
+}
+
+/**
+ * 随机生成短信验证码
+ * @param int $length 验证码长度
+ * @return int
+ */
+function generate_code($length = 6)
+{
+    return rand(pow(10, ($length - 1)), pow(10, $length) - 1);
+}
+
+/**
+ * 计算两点地理坐标之间的距离
+ * @param  string $longitude1 起点经度
+ * @param  string $latitude1 起点纬度
+ * @param  string $longitude2 终点经度
+ * @param  string $latitude2 终点纬度
+ * @param  int $unit 单位 1:米 2:公里
+ * @param  int $decimal 精度 保留小数位数
+ * @return string
+ */
+function getDistance($longitude1, $latitude1, $longitude2, $latitude2, $unit = 2, $decimal = 2)
+{
+
+    $EARTH_RADIUS = 6370.996; // 地球半径系数
+    $PI = 3.1415926;
+
+    $radLat1 = $latitude1 * $PI / 180.0;
+    $radLat2 = $latitude2 * $PI / 180.0;
+
+    $radLng1 = $longitude1 * $PI / 180.0;
+    $radLng2 = $longitude2 * $PI / 180.0;
+
+    $a = $radLat1 - $radLat2;
+    $b = $radLng1 - $radLng2;
+
+    $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
+    $distance = $distance * $EARTH_RADIUS * 1000;
+
+    if ($unit == 2) {
+        $distance = $distance / 1000;
+    }
+
+    return round($distance, $decimal);
+
+}
+
+/**
+ * @param int $month 月份
+ * @param int $year 年
+ *
+ * @return mixed
+ */
+function getMonthBeginToEnd($month, $year = 0)
+{
+    $beginDate = 0;
+    $endDate = 0;
+
+    if ($month = intval($month)) {
+        if (!$year) {
+            $year = date('Y', time());
+        }
+
+        $beginDate = strtotime("{$year}-{$month}-01");
+        $endDate = strtotime(date('Y-m-t', $beginDate));
+    }
+
+    return [
+        'beginTime' => $beginDate,
+        'endTime'   => $endDate
+    ];
+}
+
+/**
+ * 格式化商品价格
+ *
+ * @access  public
+ * @param   float $price 商品价格
+ * @return  string
+ */
+function priceFormat($price)
+{
+    return number_format($price, 2, '.', '');
+}
+
+/**
+ * 数组转xml内容
+ *
+ * @param   array $arr 数组
+ * @return  string
+ */
+function arrayToXml($arr)
+{
+    $xml = '';
+    foreach ($arr as $key => $value) {
+        if (is_string($value)) {
+            $xml .= '<' . $key . '><![CDATA[' . $value . ']]></' . $key . '>';
+        } else {
+            $xml .= '<' . $key . '>' . $value . '</' . $key . '>';
+        }
+    }
+    return $xml;
+}
+
+//数组转xml
+function arrayToWeiXinXml($arr)
+{
+    $xml = '<xml>';
+    foreach($arr as $key => $value){
+        if (is_string($value)) {
+            $xml .= '<' . $key . '><![CDATA[' . $value . ']]></' . $key . '>';
+        } else {
+            $xml .= '<' . $key . '>' . $value . '</' . $key . '>';
+        }
+    }
+    $xml .='</xml>';
+    return $xml;
+}
+
+/**
+ * xml转json格式数据
+ *
+ * @param string $xml xml格式文件内容
+ * @return string
+ */
+function XML2JSON($xml)
+{
+
+    function normalizeSimpleXML($obj, &$result)
+    {
+        $data = $obj;
+        if (is_object($data)) {
+            $data = get_object_vars($data);
+        }
+        if (is_array($data)) {
+            foreach ($data as $key => $value) {
+                $res = null;
+                normalizeSimpleXML($value, $res);
+                if (($key == '@attributes') && ($key)) {
+                    $result = $res;
+                } else {
+                    $result[$key] = $res;
+                }
+            }
+        } else {
+            $result = $data;
+        }
+    }
+
+    normalizeSimpleXML(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), $result);
+    return json_encode($result, JSON_UNESCAPED_UNICODE);
+}
+
+/**
+ * 分页方法
+ */
+function pageToOffset($page = 1, $pageSize = 10)
+{
+    return [
+        'limit'  => $pageSize,
+        'offset' => ($page - 1) * $pageSize,
+    ];
+}
+
+/**
+ * PHP截取UTF-8字符串,解决半字符问题。
+ * 英文、数字(半角)为1字节(8位),中文(全角)为2字节
+ * 取出的字符串, 当$len小于等于0时, 会返回整个字符串
+ * @param string $str 源字符串
+ * @param int $len 左边的子串的长度
+ */
+function utf_substr($str, $len)
+{
+    $new_str = [];
+    for ($i = 0; $i < $len; $i++) {
+        $temp_str = substr($str, 0, 1);
+        if (ord($temp_str) > 127) {
+            if ($i < $len) {
+                $new_str[] = substr($str, 0, 3);
+                $str = substr($str, 3);
+            }
+        } else {
+            $new_str[] = substr($str, 0, 1);
+            $str = substr($str, 1);
+        }
+    }
+    return join($new_str);
+}
+
+
+function isCardNo($vStr)
+{
+    $vCity = array(
+        '11', '12', '13', '14', '15', '21', '22',
+        '23', '31', '32', '33', '34', '35', '36',
+        '37', '41', '42', '43', '44', '45', '46',
+        '50', '51', '52', '53', '54', '61', '62',
+        '63', '64', '65', '71', '81', '82', '91'
+    );
+    if (!preg_match('/^([\d]{17}[xX\d]|[\d]{15})$/', $vStr)) return false;
+    if (!in_array(substr($vStr, 0, 2), $vCity)) return false;
+    $vStr = preg_replace('/[xX]$/i', 'a', $vStr);
+    $vLength = strlen($vStr);
+    if ($vLength == 18) {
+        $vBirthday = substr($vStr, 6, 4) . '-' . substr($vStr, 10, 2) . '-' . substr($vStr, 12, 2);
+    } else {
+        $vBirthday = '19' . substr($vStr, 6, 2) . '-' . substr($vStr, 8, 2) . '-' . substr($vStr, 10, 2);
+    }
+    if (date('Y-m-d', strtotime($vBirthday)) != $vBirthday) return false;
+    if ($vLength == 18) {
+        $vSum = 0;
+        for ($i = 17; $i >= 0; $i--) {
+            $vSubStr = substr($vStr, 17 - $i, 1);
+            $vSum += (pow(2, $i) % 11) * (($vSubStr == 'a') ? 10 : intval($vSubStr, 11));
+        }
+        if ($vSum % 11 != 1) return false;
+    }
+    return true;
+}
+
+/**
+ * try_catch 统一写入日志方法
+ * @param object $logger Factory::logs
+ * @param $exception  捕获的异常内容
+ */
+function catchError($logger, $exception)
+{
+    $error = '错误类型:' . get_class($exception) . PHP_EOL;
+    $error .= '错误代码:' . $exception->getCode() . PHP_EOL;
+    $error .= '错误信息:' . $exception->getMessage() . PHP_EOL;
+    $error .= '错误堆栈:' . $exception->getTraceAsString() . PHP_EOL;
+
+    $logger && $logger->log($error, 'error');
+}
+
+/**
+ * 资金元单位转化为分单位
+ * @param float 元
+ * @return int 分
+ */
+function yuanToFen($yuan)
+{
+    $fen = strval($yuan * 100);
+    return intval($fen);
+}
+
+/**
+ * 资金分单位转化为元单位
+ * @param int 分
+ * @return float 元
+ */
+function fenToYuan($fen)
+{
+    $fen = $fen / 100;
+    return $fen;
+}
+
+
+/**
+ * 千克转为克
+ * @param string|float
+ * @return int
+ */
+function kgTog($kg)
+{
+    $g = strval($kg * 1000);
+    return $g = intval($g);
+}
+
+
+/**
+ * 克转为千克
+ * @param string|float
+ * @return int
+ */
+
+function gToKg($kg)
+{
+    $kg = $kg / 1000;
+    return $kg;
+}
+
+
+/**
+ * 元/千克 转为 分/克 并保留8位有效数字
+ * @param string|float
+ * @return int
+ */
+function kgYuanTogFenDanWei($KgYuan)
+{
+    $gFen = $KgYuan / 10;
+    return $gFen;
+}
+
+function floatDiv($param)
+{
+    return $param / 100000000;
+}
+
+function floatMul($param)
+{
+    return $param * 100000000;
+}
+
+
+/**
+ * 分/克 转为 元/千克
+ * @param string|float
+ * @return int
+ */
+function gFenToKgYuanDanWei($gFen)
+{
+    $kgYuan = $gFen * 10;
+    return $kgYuan;
+}
+
+/**
+ * 获取当前的时间(毫秒)
+ * @return float
+ */
+function getMillisecond()
+{
+    list($t1, $t2) = explode(' ', microtime());
+    return (float)sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
+}
+
+
+/**
+ * 多维数组转树状结构
+ *
+ * @param $tree
+ * @param int $rootId
+ * @return array
+ */
+function arr2tree($tree, $rootId = 0)
+{
+
+    $return = [];
+    foreach ($tree as $leaf) {
+        if ($leaf['pid'] == $rootId) {
+            foreach ($tree as $subleaf) {
+                if ($subleaf['pid'] == $leaf['id']) {
+                    $leaf['children'] = arr2tree($tree, $leaf['id']);
+                    break;
+                }
+            }
+            $return[] = $leaf;
+        }
+    }
+
+    return $return;
+}
+
+/**
+ * <生成订单号>
+ * 规则说明: 来源(pc/ios/android)1位 + 订单类型标示(海淘/门票)1位 + 时间戳转日期(20171107)8位 + 用户id(2位) + 毫秒时间提取(4位)
+ * @param $orderFrom
+ * @param $orderTypeId
+ * @param $userId
+ * @return int
+ */
+function createOrderSn($orderFrom, $orderTypeId, $userId)
+{
+    $no = $orderFrom . $orderTypeId . date('Ymd') . substr(strval($userId + 100), -2) . substr(microtime(), 2, 4) . sprintf('%02d', rand(1000, 9999));
+
+    return $no;
+}
+
+/**
+ * 按照日期生成流水号
+ * @param string $maxNo 当天最后一条数据编号
+ */
+function createSerialNumberByDate($maxNo = '',$dateLength = 8, $lenght = 4)
+{
+    $number = 0;
+    if( !empty($maxNo) ){
+        $todayLastNo = explode('-',$maxNo);
+        $number = intval($todayLastNo[1]);
+    }
+    $number = $number+1;
+    switch ($dateLength){
+        case 12: // 根据年月日时分生成流水号
+            $date = date('YmdHi',time());
+            break;
+        case 14: // 根据年月日时分秒生成流水号
+            $date = date('YmdHis',time());
+            break;
+        default: // 根据年月日生成流水号
+            $date = date('Ymd',time());
+    }
+
+    return $date.'-'.str_pad($number,$lenght,'0',STR_PAD_LEFT);
+}
+
+/**
+ * 生成编码
+ * @param $prefix 编码前缀 例如:采购入库单 CGRK
+ * @param $id 自动id
+ * @param int $length 编码长度
+ * @return string
+ */
+function createCode($prefix, $id, $length = 6)
+{
+    $idLength = strlen((string)$id);
+    $leaveLength = $length - $idLength;
+    $zeroStr = "";
+    if ($leaveLength > 0) {
+        $zeroStr = str_repeat('0', $leaveLength);
+    }
+    return strtoupper($prefix) . $zeroStr . $id;
+}
+
+/**
+ * 随机生成16位字符串
+ * @return string 生成的字符串
+ */
+function getRandomStr($lenght = 16)
+{
+
+    $str = "";
+    $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
+    $max = strlen($str_pol) - 1;
+    for ($i = 0; $i < $lenght; $i++) {
+        $str .= $str_pol[mt_rand(0, $max)];
+    }
+    return $str;
+}
+
+
+/*
+ * uuid 唯一序列码
+ */
+
+function uuid($prefix = '')
+{
+    $chars = md5(uniqid(mt_rand(), true));
+    $uuid  = substr($chars,0,8) . '-';
+    $uuid .= substr($chars,8,4) . '-';
+    $uuid .= substr($chars,12,4) . '-';
+    $uuid .= substr($chars,16,4) . '-';
+    $uuid .= substr($chars,20,12);
+    return $prefix . $uuid;
+}
+
+/**
+ * 获取 当天,昨天,本周,本月,本年时间戳
+ */
+
+function gettimestamp($name = 'today',$param = ''){
+    switch ($name){
+        case 'today'://今天
+            $timeamp['start'] = strtotime(date('Y-m-d'));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+        case 'yesterday'://昨天
+            $timeamp['start'] = strtotime(date('Y-m-d',strtotime('-1 day')));
+            $timeamp['end'] = strtotime(date('Y-m-d'));
+            break;
+        case 'beforyesterday'://前天
+            $timeamp['start'] = strtotime(date('Y-m-d',strtotime('-2 day')));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('-1 day')));
+            break;
+        case 'beforweek'://本周
+            $timeamp['start'] = strtotime(date("Y-m-d H:i:s",mktime(0, 0 , 0,date("m"),date("d")-date("w")+1,date("Y"))));
+            $timeamp['end'] = strtotime(date("Y-m-d H:i:s",mktime(23,59,59,date("m"),date("d")-date("w")+7,date("Y"))));
+            break;
+        case 'nowmonth'://本月
+            $timeamp['start'] = strtotime(date('Y-m-01'));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+        case 'permonth'://上月
+            $timeamp['start'] = strtotime(date('Y-m-01',strtotime('-1 month')));
+            $timeamp['end'] = strtotime(date('Y-m-01'));
+            break;
+        case 'preweek'://上周 注意我们是从周一开始算
+            $timeamp['start'] = strtotime(date('Y-m-d',strtotime('-2 week Monday')));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('-1 week Monday +1 day')));
+            break;
+        case 'nowweek'://本周
+            $timeamp['start'] = strtotime(date('Y-m-d',strtotime('-1 week Monday')));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+        case 'preday'://近三个月
+//            $timeamp['start'] = strtotime(date('Y-m-d'),strtotime($param.' day'));
+            $timeamp['start'] = strtotime("-0 year -3 month -0 day");
+            $timeamp['end'] = strtotime(date('Y-m-d'));
+            break;
+        case 'nextday'://30
+            $timeamp['start'] = strtotime(date('Y-m-d'));
+            $timeamp['end'] = strtotime(date('Y-m-d'),strtotime($param.' day'));
+            break;
+        case 'preyear'://去年
+            $timeamp['start'] = strtotime(date('Y-01-01',strtotime('-1 year')));
+            $timeamp['end'] = strtotime(date('Y-12-31',strtotime('-1 year')));
+            break;
+        case 'nowyear'://今年
+            $timeamp['start'] = strtotime(date('Y-01-01'));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+        case 'quarter'://季度
+            $quarter = empty($param) ? ceil((date('n'))/3) : $param;//获取当前季度
+            $timeamp['start'] = mktime(0, 0, 0,$quarter*3-2,1,date('Y'));
+            $timeamp['end'] = mktime(0, 0, 0,$quarter*3+1,1,date('Y'));
+//            $timeamp['end'] = mktime(23,59,59,$quarter*3,date('t',mktime(0, 0 , 0,$quarter*3,1,date("Y"))),date('Y'));
+            break;
+        default:
+            $timeamp['start'] = strtotime(date('Y-m-d'));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+    }
+    return $timeamp;
+}
+

+ 63 - 0
Mall/Framework/Core/Aes.Class.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Aes
+{
+    private $_iv = "0102030405060708";
+
+    private $_encryptKey;
+
+    private static $_instance;
+
+    public function __construct ($encryptKey = '')
+    {
+        $this->_encryptKey = $encryptKey ?: defined('SITE_SECRET') ?: 'h7WPzPi87}MC7AxM6Jz)L?9%NXy*jB';
+    }
+
+    public static function getInstance($encryptKey = [])
+    {
+        if (!self::$_instance instanceof self) {
+            self::$_instance = new self($encryptKey);
+        }
+
+        return self::$_instance;
+    }
+
+    public function encrypt($encryptStr)
+    {
+        $localIV = $this->_iv;
+        $encryptKey = $this->_encryptKey;
+
+        $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, $localIV);
+
+        mcrypt_generic_init($module, $encryptKey, $localIV);
+
+        $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
+        $pad = $block - (strlen($encryptStr) % $block);
+        $encryptStr .= str_repeat(chr($pad), $pad);
+
+        $encrypted = mcrypt_generic($module, $encryptStr);
+
+        mcrypt_generic_deinit($module);
+        mcrypt_module_close($module);
+
+        return base64_encode($encrypted);
+
+    }
+
+    public function decrypt($encryptStr)
+    {
+        $localIV = $this->_iv;
+        $encryptKey = $this->_encryptKey;
+
+        $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, $localIV);
+
+        mcrypt_generic_init($module, $encryptKey, $localIV);
+
+        $encryptedData = base64_decode($encryptStr);
+        $encryptedData = mdecrypt_generic($module, $encryptedData);
+
+        return $encryptedData;
+    }
+}

+ 53 - 0
Mall/Framework/Core/AutoLoad.Class.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class AutoLoad
+{
+    static function _autoLoad($class)
+    {
+        //var_dump($class);
+        //xssCheck();
+
+        if(function_exists('apcu_fetch') && ini_get('apc.enabled')){
+            if ($file_path = apcu_fetch('autoload_'.PROJECT_DOMAIN.$class)) {
+                loadFile($file_path);
+                return true;
+            }
+        }
+
+        $class_path = str_replace('\\', '/', $class) . '.Class.php';
+
+        // util工具类特殊处理
+        if(strpos($class_path, 'Util') === 0){
+            $real_path = DS.$class_path;
+        }else{
+            $real_path = substr($class_path, strpos($class_path, '/'));
+        }
+
+
+        switch($real_path){
+            case strpos($class_path, FRAMEWORK_NAME.'/') !== FALSE:
+                $file_path = ROOT_PATH . $real_path;
+                break;
+            case strpos($class_path, 'Smarty') !== FALSE:
+                return false;
+                break;
+            case strpos($class_path, 'Service') !== FALSE:
+                if(!defined('SWOOLESERVER_NAME')){
+                    throw new \Exception('请去swoole服务项目启动脚本配置服务目录名称,重启生效!!!');
+                }
+                $file_path = ROOT_PATH . DS . '..' . DS . SWOOLESERVER_NAME . $real_path;
+                break;
+            default:
+                $file_path = PROJECT_PATH . $real_path;
+        }
+
+        if(function_exists('apcu_fetch') && ini_get('apc.enabled')){
+            apcu_add('autoload_'.PROJECT_DOMAIN.$class, $file_path);
+        }
+        loadFile($file_path);
+    }
+
+
+}

+ 202 - 0
Mall/Framework/Core/BaseImg.Class.php

@@ -0,0 +1,202 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class BaseImg
+{
+    // 上传文件名
+    protected $saveName;
+    
+    // 上传Base64文件
+    protected $file;
+
+    // 上传的文件正则匹配后的信息
+    protected $info;
+
+    // 错误信息
+    private $error = '';
+
+    protected static $_instance;
+
+    private function __construct(){}
+
+    public static function getInstance()
+    {
+        $key = md5('baseImg64');
+
+        if (!isset(self::$_instance[$key])) {
+            self::$_instance[$key] = new self();
+        }
+
+        return self::$_instance[$key];
+    }
+
+    /**
+     * @param string $file base64的图片
+     * @param  bool $replace 同名文件是否覆盖
+     * @param string $dirName 制定目录名称
+     *
+     * @return bool|File
+     */
+    public function move($file, $replace = true, $dirName = '')
+    {
+        $this->file = $file;
+        $path = UPLOAD_FILE_PATH;
+
+        // 检测合法性
+        if (!$this->baseIsValid()) {
+            $this->error = '非法上传文件';
+            return false;
+        }
+
+        // 验证上传
+        if (!$this->baseCheckImg($this->info[2])) {
+            $this->error = '非法图像文件';
+            return false;
+        }
+
+        $path = rtrim($path, DS) . DS;
+        // 文件保存命名规则
+        $saveName = $this->baseBuildSaveName($dirName);
+        $filename = $path . $saveName;
+
+        // 检测目录
+        if (false === $this->checkPath(dirname($filename))) {
+            return false;
+        }
+
+        /* 不覆盖同名文件 */
+        if (!$replace && is_file($filename)) {
+            $this->error = '存在同名文件' . $filename;
+            return false;
+        }
+
+        if(!file_put_contents($filename, base64_decode(str_replace($this->info[1], '', $file)))){
+            $this->error = '图像上传失败';
+            return false;
+        }
+
+        // 返回 File对象实例
+        $this->setSaveName($saveName);
+        return $this;
+    }
+
+    protected function baseIsValid()
+    {
+        preg_match('/^(data:\s*image\/(\w+);base64,)/', $this->file, $result);
+
+        $this->info = $result;
+
+        return $result ? true : false;
+    }
+
+    protected function baseCheckImg($extension)
+    {
+        if (!in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf'], true)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    protected function baseBuildSaveName($dirName = '')
+    {
+        if($dirName){
+            return $dirName .DS. date('Ymd') . DS . md5(microtime(true)) . '.' . $this->info[2];
+        }
+        return date('Ymd') . DS . md5(microtime(true)) . '.' . $this->info[2];
+    }
+
+    /**
+     * 检查目录是否可写
+     * @param  string $path 目录
+     * @return boolean
+     */
+    protected function checkPath($path)
+    {
+        if (is_dir($path)) {
+            return true;
+        }
+
+        if (mkdir($path, 0755, true)) {
+            return true;
+        } else {
+            $this->error = "目录 {$path} 创建失败!";
+            return false;
+        }
+    }
+
+    /**
+     * 获取上传文件的文件名
+     * @return string
+     */
+    public function getSaveName()
+    {
+        return $this->saveName;
+    }
+
+    /**
+     * 设置上传文件的保存文件名
+     * @param  string $saveName
+     * @return $this
+     */
+    public function setSaveName($saveName)
+    {
+        $this->saveName = $saveName;
+        return $this;
+    }
+
+    /**
+     * 获取错误信息
+     * @return mixed
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    public function baseImgMove($thumbs, $selfUploadUrl = '')
+    {
+        $actionImg = [];
+
+        if (!empty($thumbs)) {
+            if (is_array($thumbs) && !empty($thumbs)) {
+                foreach ($thumbs as $thumb) {
+                    if ($thumb) {
+                        if ($path = self::_baseImgValidate($thumb, $selfUploadUrl)) {
+                            $actionImg[] = $path;
+                        }
+                    }
+                }
+            } else {
+                if (is_string($thumbs)) {
+                    if ($path = self::_baseImgValidate($thumbs, $selfUploadUrl)) {
+                        $actionImg[] = $path;
+                    }
+                }
+            }
+        }
+
+        return json_encode($actionImg);
+    }
+
+    private function _baseImgValidate($thumb, $selfUploadUrl)
+    {
+        if(filter_var($thumb, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)) {
+            preg_match('#^'.preg_quote($selfUploadUrl).'#', $thumb, $myThumb);
+            if ($myThumb) {
+                $actionImg = str_replace($selfUploadUrl, '', $thumb);
+            } else {
+                $actionImg = $thumb;
+            }
+        } else {
+            if ($file = self::move($thumb)) {
+                $actionImg = $file->getSaveName();
+            } else {
+                $actionImg = $thumb;
+            }
+        }
+
+        return $actionImg ? $actionImg : false;
+    }
+}

+ 36 - 0
Mall/Framework/Core/Cache.Class.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Cache {
+
+    protected static $_instance;
+
+    private function __construct(){}
+
+    public function connect($storage, $options=array())
+    {
+        $cache = FALSE;
+        $class = 'Mall\\Framework\\Cache\\'.ucwords(strtolower($storage));
+
+        if(class_exists($class)) {
+            $cache = new $class($options);
+        }
+
+        return $cache;
+    }
+
+    public static function getInstance($options = [])
+    {
+        $options = $options ?: Config::getInstance()->get('cache');
+        $storage = $options['storage'] ?: 'Redis';
+        $key = $storage . md5(implode(',', $options));
+
+        if (!isset(self::$_instance[$key])) {
+            $obj = new self();
+            self::$_instance[$key] = $obj->connect($storage, $options);
+        }
+
+        return self::$_instance[$key];
+    }
+}

+ 139 - 0
Mall/Framework/Core/Config.Class.php

@@ -0,0 +1,139 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Config {
+
+    static private $instance;
+
+    /**
+     * 所有app的配置集
+     * array(
+     * 		md5($app_conf_path) => array(
+     *
+     * 		),
+     * 		... ,
+     * );
+     * @var array
+     */
+    static protected $appConfigs = array();
+
+    /**
+     *
+     * 当前app的配置
+     * @var array
+     */
+    protected $appConfig;
+
+    /**
+     *
+     * 不允许直接 new
+     */
+    private function __construct() {}
+
+    /**
+     *
+     * 载入配置,如果成功,返回instance
+     * @param string $app_conf_path 配置文件的路径
+     */
+    static public function load($app_conf_path) {
+        $app_conf_path = realpath($app_conf_path);
+        $app_conf_key = md5($app_conf_path);
+        if (!isset(self::$appConfigs[$app_conf_key])) {
+
+
+            $config = include($app_conf_path);
+
+            if (!is_array($config)) {
+                throw new \Exception("load app_conf_path fail: {$app_conf_path}");
+            }
+            # 将conf的path存进来
+            $config['conf_path'] = $app_conf_path;
+
+            self::$appConfigs[$app_conf_key] = $config;
+        }
+
+        if (is_null(self::$instance)) {
+            self::$instance = new self();
+        }
+
+        self::$instance->appConfig = self::$appConfigs[$app_conf_key];
+
+        return self::$instance;
+    }
+
+    /**
+     *
+     * 单例模式
+     */
+    static public function getInstance() {
+        if (is_null(self::$instance)) {
+            self::$instance = new self();
+    }
+
+        if (is_null(self::$instance->appConfig)) {
+
+            throw new \Exception('instance appconfig is null, pleace run Mall\Core\Config::load !');
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     *
+     * 获取指定配置项的值
+     * @param string $key
+     * @return mixed
+     */
+    public function get($key) {
+        if (empty($key)) {
+            return false;
+        }
+
+        $keys = explode('.', $key);
+
+        $value = $this->appConfig;
+
+        foreach ($keys as $tmpKey) {
+            if (!isset($value[$tmpKey])) {
+                return false;
+            }
+            $value = $value[$tmpKey];
+        }
+        return $value;
+    }
+
+    /**
+     *
+     * 获取指定配置项下的得某一项
+     * @param string $configKey  配置文件下的某一项
+     * @param string $appointKey 指定项下的某一个节点
+     * @return mixed
+     */
+    public function getAppoint($configKey, $appointKey) {
+        if (!$configKey) {
+            return false;
+        }
+
+        $allConfig = $this->appConfig;
+
+        if(empty($allConfig[$configKey])){
+            return [];
+        }
+
+        if(isset($allConfig[$configKey][$appointKey])){
+            return $allConfig[$configKey][$appointKey];
+        }else{
+            return $allConfig[$configKey];
+        }
+    }
+
+    /**
+     *
+     * 获取所有app的配置集
+     * @return array
+     */
+     public function all() {
+        return $this->appConfig;
+    }
+}

+ 75 - 0
Mall/Framework/Core/Cookie.Class.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Cookie
+{
+    private $prefix = '';
+
+    private $path = '/';
+
+    private $domain = '';
+
+    private static $_instance;
+
+    public function __clone()
+    {
+        trigger_error('Clone is not allow!', E_USER_ERROR);
+    }
+
+    public static function getInstance($options = [])
+    {
+        $options = $options ?: Config::getInstance()->get('cookie');
+
+        if (!self::$_instance instanceof self) {
+            self::$_instance = new self;
+            self::$_instance->setOptions($options);
+        }
+
+        return self::$_instance;
+    }
+
+    public function setOptions($options)
+    {
+        $this->prefix = $options['prefix'];
+        $this->path = $options['path'];
+        $this->domain = $options['domain'];
+    }
+
+    /**
+     * 设置一个cookie值
+     *
+     * @param string $var
+     * @param mixed $value
+     * @param int $time 存活时间
+     */
+    public function set($var, $value = null, $time = 0)
+    {
+        if (is_null($value)) {
+            $time = time() - 3600;
+        } elseif ($time > 0 && $time < 31536000) {
+            $time += time();
+        }
+
+        $s = $_SERVER['SERVER_PORT'] == '443' ? 1 : 0;
+        $var = $this->prefix . $var;
+        $_COOKIE[$var] = $value;
+        if (is_array($value)) {
+            foreach ($value as $k => $v) {
+                setcookie($var . '[' . $k . ']', $v, $time, $this->path, $this->domain, $s);
+            }
+        } else {
+            setcookie($var, $value, $time, $this->path, $this->domain, $s);
+        }
+    }
+
+    /**
+     * @param $var
+     * @return bool
+     */
+    public function get($var)
+    {
+        $var = $this->prefix . $var;
+        return isset($_COOKIE[$var]) ? $_COOKIE[$var] : false;
+    }
+}

+ 33 - 0
Mall/Framework/Core/Db.Class.php

@@ -0,0 +1,33 @@
+<?php
+namespace Mall\Framework\Core;
+
+class Db
+{
+
+    static protected $instance;
+
+    static public function getInstance($options = [])
+    {
+        $options = $options ?: Config::getInstance()->get('db');
+        $options['driver'] =  strtolower($options['driver']);
+
+        $key = $options['driver'] . md5(implode(',', $options));
+        if (!isset(self::$instance[$key])) {
+            switch ($options['driver'])
+            {
+                case 'mssql':
+                    $class = 'Mall\\Framework\\Db\\DbMssql';
+                    break;
+                case 'oci':
+                    $class = 'Mall\\Framework\\Db\\DbOci';
+                    break;
+                default:
+                    $class = 'Mall\\Framework\\Db\\Db';
+                    break;
+            }
+            self::$instance[$key] = new $class($options);
+        }
+
+        return self::$instance[$key];
+    }
+}

+ 96 - 0
Mall/Framework/Core/ErrorCode.Class.php

@@ -0,0 +1,96 @@
+<?php
+/**
+ * 统一的错误编码 方便Service 和 其它项目之间Swoole传输判断
+ */
+namespace Mall\Framework\Core;
+
+class ErrorCode
+{
+    /** @var int $notShowError 后端错误,前端不用展示*/
+    public static $notShowError = 9999;
+
+
+    /** @var int $paramError 参数错误 */
+    public static $paramError = 1001;
+
+    /** @var int $userNotFount 用户不存在 */
+    public static $userNotFount = 1002;
+
+    /** @var int $notAllowAccess 非法的请求 */
+    public static $notAllowAccess = 1003;
+
+    /** @var int $mobileCodeFail 验证码错误 */
+    public static $mobileCodeFail = 1004;
+
+    /** @var int $dberror 数据库操作错误 */
+    public static $dberror = 1005;
+
+    /** @var int $accountfail 登录帐号密码错误 */
+    public static $accountfail = 1006;
+
+    /** @var int $swooleRecvError swoole服务未返回消息 */
+    public static $swooleRecvError = 1007;
+
+    /** @var int $loginedexpire  登陆过期 */
+    public static $loginedexpire = 1008;
+
+    /** @var int $redisWriteError  redis写入失败 */
+    public static $redisWriteError = 1009;
+
+    /** @var int $notHaveAclAccess 没有权限访问 */
+    public static $notHaveAclAccess = 1010;
+
+    /** @var int $signfail 签名错误 */
+    public static $signfail = 1011;
+
+    /** @var int $mobileishaved 注册手机号已经存在 */
+    public static $mobileishaved = 1012;
+
+    /** @var int $uploadEroor 上传操作失败 */
+    public static $uploadEroor = 1013;
+
+    /** @var int $configEroor 配置文件错误 */
+    public static $configEroor = 1014;
+
+    /** @var int $apiNotResult 接口返回数据错误 */
+    public static $apiNotResult = 1015;
+
+    /** @var int $serviceError 服务错误 */
+    public static $serviceError = 1016;
+
+    /** @var int $notAllowToken 非法或无效的Token */
+    public static $notAllowToken = 1017;
+
+    /** @var int $contentNotExists 资讯内容不存在 */
+    public static $contentNotExists = 1018;
+
+    /** @var int $nickNameIsHaved 昵称已经注册过了 */
+    public static $nickNameIsHaved = 1019;
+
+    /** @var int $actionIsDo 重复得操作 */
+    public static $actionIsDo = 1020;
+
+    /** @var int $weixinPayError 微信支付接口返回错误 */
+    public static $weixinPayError = 1021;
+
+    /** @var int $invalidAccess 库存不足 */
+    public static $invalidAccess = 1021;
+    /** @var int $invalidAccess 销售单重复提交 */
+    public static $resubmit = 1022;
+
+    /** @var int $invalidAccess 商品被删除 */
+    public static $goodsDelete = 1023;
+
+    /** @var int $invalidAccess 初始密码登录 */
+    public static $defaultPassword = 1024;
+
+    public static $OK = 0;
+
+    public static $IllegalAesKey = 900004;
+    public static $ValidateSignatureError = 900005;
+    public static $ComputeSignatureError = 900006;
+    public static $EncryptAESError = 900007;
+    public static $DecryptAESError = 900008;
+    public static $ValidateSuiteKeyError = 900010;
+
+}

+ 414 - 0
Mall/Framework/Core/File.Class.php

@@ -0,0 +1,414 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use SplFileObject;
+
+class File extends SplFileObject
+{
+    /**
+     * 错误信息
+     * @var string
+     */
+    private $error = '';
+    // 当前完整文件名
+    protected $filename;
+    // 上传文件名
+    protected $saveName;
+    // 文件上传命名规则
+    protected $rule = 'date';
+    // 文件上传验证规则
+    protected $validate = [];
+    // 单元测试
+    protected $isTest;
+    // 上传文件信息
+    protected $info;
+    // 文件hash信息
+    protected $hash = [];
+
+    protected static $_instance;
+
+    public static function getInstance($filename = '', $mode = 'r')
+    {
+        $key = md5('file');
+
+        if (!isset(self::$_instance[$key])) {
+            self::$_instance[$key] = new self($filename, $mode);
+        }
+
+        return self::$_instance[$key];
+    }
+
+
+    public function __construct($filename = '', $mode = 'r')
+    {
+        parent::__construct($filename, $mode);
+        $this->filename = $this->getRealPath() ?: $this->getPathname();
+    }
+
+    /**
+     * 是否测试
+     * @param  bool $test 是否测试
+     * @return $this
+     */
+    public function isTest($test = false)
+    {
+        $this->isTest = $test;
+        return $this;
+    }
+
+    /**
+     * 设置上传信息
+     * @param  array $info 上传文件信息
+     * @return $this
+     */
+    public function setUploadInfo($info)
+    {
+        $this->info = $info;
+        return $this;
+    }
+
+    /**
+     * 获取上传文件的信息
+     * @param  string $name
+     * @return array|string
+     */
+    public function getInfo($name = '')
+    {
+        return isset($this->info[$name]) ? $this->info[$name] : $this->info;
+    }
+
+    /**
+     * 获取上传文件的文件名
+     * @return string
+     */
+    public function getSaveName()
+    {
+        return $this->saveName;
+    }
+
+    /**
+     * 设置上传文件的保存文件名
+     * @param  string $saveName
+     * @return $this
+     */
+    public function setSaveName($saveName)
+    {
+        $this->saveName = $saveName;
+        return $this;
+    }
+
+    /**
+     * 获取文件的哈希散列值
+     * @return $string
+     */
+    public function hash($type = 'sha1')
+    {
+        if (!isset($this->hash[$type])) {
+            $this->hash[$type] = hash_file($type, $this->filename);
+        }
+        return $this->hash[$type];
+    }
+
+    /**
+     * 检查目录是否可写
+     * @param  string $path 目录
+     * @return boolean
+     */
+    protected function checkPath($path)
+    {
+        if (is_dir($path)) {
+            return true;
+        }
+
+        if (mkdir($path, 0777, true)) {
+            return true;
+        } else {
+            $this->error = "目录 {$path} 创建失败!";
+            return false;
+        }
+    }
+
+    /**
+     * 获取文件类型信息
+     * @return string
+     */
+    public function getMime()
+    {
+        $finfo = finfo_open(FILEINFO_MIME_TYPE);
+        return finfo_file($finfo, $this->filename);
+    }
+
+    /**
+     * 设置文件的命名规则
+     * @param  string $rule 文件命名规则
+     * @return $this
+     */
+    public function rule($rule)
+    {
+        $this->rule = $rule;
+        return $this;
+    }
+
+    /**
+     * 设置上传文件的验证规则
+     * @param  array $rule 验证规则
+     * @return $this
+     */
+    public function validate($rule = [])
+    {
+        $this->validate = $rule;
+        return $this;
+    }
+
+    /**
+     * 检测是否合法的上传文件
+     * @return bool
+     */
+    public function isValid()
+    {
+        if ($this->isTest) {
+            return is_file($this->filename);
+        }
+        return is_uploaded_file($this->filename);
+    }
+
+    /**
+     * 检测上传文件
+     * @param  array $rule 验证规则
+     * @return bool
+     */
+    public function check($rule = [])
+    {
+        $rule = $rule ?: $this->validate;
+
+        /* 检查文件大小 */
+        if (isset($rule['size']) && !$this->checkSize($rule['size'])) {
+            $this->error = '上传文件大小不符!';
+            return false;
+        }
+        /* 检查文件Mime类型 */
+        if (isset($rule['type']) && !$this->checkMime($rule['type'])) {
+            $this->error = '上传文件MIME类型不允许!';
+            return false;
+        }
+        /* 检查文件后缀 */
+        if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) {
+            $this->error = '上传文件后缀不允许';
+            return false;
+        }
+
+        /* 检查图像文件 */
+        if (!$this->checkImg()) {
+            $this->error = '非法图像文件!';
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 检测上传文件后缀
+     * @param  array|string $ext 允许后缀
+     * @return bool
+     */
+    public function checkExt($ext)
+    {
+        if (is_string($ext)) {
+            $ext = explode(',', $ext);
+        }
+        $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
+        if (!in_array($extension, $ext)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 检测图像文件
+     * @return bool
+     */
+    public function checkImg()
+    {
+        $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
+        /* 对图像文件进行严格检测 */
+        if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6])) {
+            return false;
+        }
+        return true;
+    }
+
+    // 判断图像类型
+    protected function getImageType($image)
+    {
+        if (function_exists('exif_imagetype')) {
+            return exif_imagetype($image);
+        } else {
+            $info = getimagesize($image);
+            return $info[2];
+        }
+    }
+
+    /**
+     * 检测上传文件大小
+     * @param  integer $size 最大大小
+     * @return bool
+     */
+    public function checkSize($size)
+    {
+        if ($this->getSize() > $size) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 检测上传文件类型
+     * @param  array|string $mime 允许类型
+     * @return bool
+     */
+    public function checkMime($mime)
+    {
+        if (is_string($mime)) {
+            $mime = explode(',', $mime);
+        }
+        if (!in_array(strtolower($this->getMime()), $mime)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 移动文件
+     //* @param  string $path 保存路径
+     * @param  string|bool $savename 保存的文件名 默认自动生成
+     * @param  boolean $replace 同名文件是否覆盖
+     * @return false|File false-失败 否则返回File实例
+     */
+    public function move($savename = true, $replace = true)
+    {
+        $path = UPLOAD_FILE_PATH;
+        // 文件上传失败,捕获错误代码
+        if (!empty($this->info['error'])) {
+            $this->error($this->info['error']);
+            return false;
+        }
+
+        // 检测合法性
+        if (!$this->isValid()) {
+            $this->error = '非法上传文件';
+            return false;
+        }
+
+        // 验证上传
+        if (!$this->check()) {
+            return false;
+        }
+        $path = rtrim($path, DS) . DS;
+        // 文件保存命名规则
+        $saveName = $this->buildSaveName($savename);
+        $filename = $path . $saveName;
+
+        // 检测目录
+        if (false === $this->checkPath(dirname($filename))) {
+            return false;
+        }
+
+        /* 不覆盖同名文件 */
+        if (!$replace && is_file($filename)) {
+            $this->error = '存在同名文件' . $filename;
+            return false;
+        }
+
+        /* 移动文件 */
+        if ($this->isTest) {
+            rename($this->filename, $filename);
+        } elseif (!move_uploaded_file($this->filename, $filename)) {
+            $this->error = '文件上传保存错误!';
+            return false;
+        }
+        // 返回 File对象实例
+        //$file = new self($filename);
+        $this->setSaveName($saveName);
+        $this->setUploadInfo($this->info);
+        return $this;
+    }
+
+    /**
+     * 获取保存文件名
+     * @param  string|bool $savename 保存的文件名 默认自动生成
+     * @return string
+     */
+    protected function buildSaveName($savename)
+    {
+        if (true === $savename) {
+            // 自动生成文件名
+            if ($this->rule instanceof \Closure) {
+                $savename = call_user_func_array($this->rule, [$this]);
+            } else {
+                switch ($this->rule) {
+                    case 'date':
+                        $savename = date('Ymd') . DS . md5(microtime(true));
+                        break;
+                    default:
+                        if (in_array($this->rule, hash_algos())) {
+                            $hash = $this->hash($this->rule);
+                            $savename = substr($hash, 0, 2) . DS . substr($hash, 2);
+                        } elseif (is_callable($this->rule)) {
+                            $savename = call_user_func($this->rule);
+                        } else {
+                            $savename = date('Ymd') . DS . md5(microtime(true));
+                        }
+                }
+            }
+        } elseif ('' === $savename) {
+            $savename = $this->getInfo('name');
+        }
+        if (!strpos($savename, '.')) {
+            $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
+        }
+        return $savename;
+    }
+
+    /**
+     * 获取错误代码信息
+     * @param int $errorNo  错误号
+     */
+    private function error($errorNo)
+    {
+        switch ($errorNo) {
+            case 1:
+            case 2:
+                $this->error = '上传文件大小超过了最大值!';
+                break;
+            case 3:
+                $this->error = '文件只有部分被上传!';
+                break;
+            case 4:
+                $this->error = '没有文件被上传!';
+                break;
+            case 6:
+                $this->error = '找不到临时文件夹!';
+                break;
+            case 7:
+                $this->error = '文件写入失败!';
+                break;
+            default:
+                $this->error = '未知上传错误!';
+        }
+    }
+
+    /**
+     * 获取错误信息
+     * @return mixed
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    public function __call($method, $args)
+    {
+        return $this->hash($method);
+    }
+}

+ 1831 - 0
Mall/Framework/Core/FirePHP.Class.php

@@ -0,0 +1,1831 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+// Authors:
+// - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
+// - qbbr, Sokolov Innokenty <sokolov.innokenty@gmail.com>, Copyright 2011, New BSD License
+// - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT Licensea
+
+/**
+ * *** BEGIN LICENSE BLOCK *****
+ *
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ *
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ***** END LICENSE BLOCK *****
+ *
+ * @copyright       Copyright (C) 2007+ Christoph Dorn
+ * @author          Christoph Dorn <christoph@christophdorn.com>
+ * @license         [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ * @package         FirePHPCore
+ */
+
+/**
+ * @see http://code.google.com/p/firephp/issues/detail?id=112
+ */
+if (!defined('E_STRICT')) {
+    define('E_STRICT', 2048);
+}
+if (!defined('E_RECOVERABLE_ERROR')) {
+    define('E_RECOVERABLE_ERROR', 4096);
+}
+if (!defined('E_DEPRECATED')) {
+    define('E_DEPRECATED', 8192);
+}
+if (!defined('E_USER_DEPRECATED')) {
+    define('E_USER_DEPRECATED', 16384);
+}
+
+/**
+ * Sends the given data to the FirePHP Firefox Extension.
+ * The data can be displayed in the Firebug Console or in the
+ * "Server" request tab.
+ *
+ * For more information see: http://www.firephp.org/
+ *
+ * @copyright       Copyright (C) 2007+ Christoph Dorn
+ * @author          Christoph Dorn <christoph@christophdorn.com>
+ * @license         [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ * @package         FirePHPCore
+ */
+class FirePHP {
+
+    /**
+     * FirePHP version
+     *
+     * @var string
+     */
+    const VERSION = '0.3';    // @pinf replace '0.3' with '%%VERSION%%'
+
+    /**
+     * Firebug LOG level
+     *
+     * Logs a message to firebug console.
+     *
+     * @var string
+     */
+    const LOG = 'LOG';
+
+    /**
+     * Firebug INFO level
+     *
+     * Logs a message to firebug console and displays an info icon before the message.
+     *
+     * @var string
+     */
+    const INFO = 'INFO';
+
+    /**
+     * Firebug WARN level
+     *
+     * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
+     *
+     * @var string
+     */
+    const WARN = 'WARN';
+
+    /**
+     * Firebug ERROR level
+     *
+     * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
+     *
+     * @var string
+     */
+    const ERROR = 'ERROR';
+
+    /**
+     * Dumps a variable to firebug's server panel
+     *
+     * @var string
+     */
+    const DUMP = 'DUMP';
+
+    /**
+     * Displays a stack trace in firebug console
+     *
+     * @var string
+     */
+    const TRACE = 'TRACE';
+
+    /**
+     * Displays an exception in firebug console
+     *
+     * Increments the firebug error count.
+     *
+     * @var string
+     */
+    const EXCEPTION = 'EXCEPTION';
+
+    /**
+     * Displays an table in firebug console
+     *
+     * @var string
+     */
+    const TABLE = 'TABLE';
+
+    /**
+     * Starts a group in firebug console
+     *
+     * @var string
+     */
+    const GROUP_START = 'GROUP_START';
+
+    /**
+     * Ends a group in firebug console
+     *
+     * @var string
+     */
+    const GROUP_END = 'GROUP_END';
+
+    /**
+     * Singleton instance of FirePHP
+     *
+     * @var FirePHP
+     */
+    protected static $instance = null;
+
+    /**
+     * Flag whether we are logging from within the exception handler
+     *
+     * @var boolean
+     */
+    protected $inExceptionHandler = false;
+
+    /**
+     * Flag whether to throw PHP errors that have been converted to ErrorExceptions
+     *
+     * @var boolean
+     */
+    protected $throwErrorExceptions = true;
+
+    /**
+     * Flag whether to convert PHP assertion errors to Exceptions
+     *
+     * @var boolean
+     */
+    protected $convertAssertionErrorsToExceptions = true;
+
+    /**
+     * Flag whether to throw PHP assertion errors that have been converted to Exceptions
+     *
+     * @var boolean
+     */
+    protected $throwAssertionExceptions = false;
+
+    /**
+     * Wildfire protocol message index
+     *
+     * @var integer
+     */
+    protected $messageIndex = 1;
+
+    /**
+     * Options for the library
+     *
+     * @var array
+     */
+    protected $options = array('maxDepth' => 10,
+        'maxObjectDepth' => 5,
+        'maxArrayDepth' => 5,
+        'useNativeJsonEncode' => true,
+        'includeLineNumbers' => true);
+
+    /**
+     * Filters used to exclude object members when encoding
+     *
+     * @var array
+     */
+    protected $objectFilters = array(
+        'firephp' => array('objectStack', 'instance', 'json_objectStack'),
+        'firephp_test_class' => array('objectStack', 'instance', 'json_objectStack')
+    );
+
+    /**
+     * A stack of objects used to detect recursion during object encoding
+     *
+     * @var object
+     */
+    protected $objectStack = array();
+
+    /**
+     * Flag to enable/disable logging
+     *
+     * @var boolean
+     */
+    protected $enabled = true;
+
+    /**
+     * The insight console to log to if applicable
+     *
+     * @var object
+     */
+    protected $logToInsightConsole = null;
+
+    /**
+     * When the object gets serialized only include specific object members.
+     *
+     * @return array
+     */
+    public function __sleep()
+    {
+        return array('options', 'objectFilters', 'enabled');
+    }
+
+    /**
+     * Gets singleton instance of FirePHP
+     *
+     * @param boolean $autoCreate
+     * @return FirePHP
+     */
+    public static function getInstance($autoCreate = false)
+    {
+        if ($autoCreate === true && !self::$instance) {
+            self::init();
+        }
+        return self::$instance;
+    }
+
+    /**
+     * Creates FirePHP object and stores it for singleton access
+     *
+     * @return FirePHP
+     */
+    public static function init()
+    {
+        return self::setInstance(new self());
+    }
+
+    /**
+     * Set the instance of the FirePHP singleton
+     *
+     * @param FirePHP $instance The FirePHP object instance
+     * @return FirePHP
+     */
+    public static function setInstance($instance)
+    {
+        return self::$instance = $instance;
+    }
+
+    /**
+     * Set an Insight console to direct all logging calls to
+     *
+     * @param object $console The console object to log to
+     * @return void
+     */
+    public function setLogToInsightConsole($console)
+    {
+        if (is_string($console)) {
+            if (get_class($this) != 'FirePHP_Insight' && !is_subclass_of($this, 'FirePHP_Insight')) {
+                throw new Exception('FirePHP instance not an instance or subclass of FirePHP_Insight!');
+            }
+            $this->logToInsightConsole = $this->to('request')->console($console);
+        } else {
+            $this->logToInsightConsole = $console;
+        }
+    }
+
+    /**
+     * Enable and disable logging to Firebug
+     *
+     * @param boolean $enabled TRUE to enable, FALSE to disable
+     * @return void
+     */
+    public function setEnabled($enabled)
+    {
+        $this->enabled = $enabled;
+    }
+
+    /**
+     * Check if logging is enabled
+     *
+     * @return boolean TRUE if enabled
+     */
+    public function getEnabled()
+    {
+        return $this->enabled;
+    }
+
+    /**
+     * Specify a filter to be used when encoding an object
+     *
+     * Filters are used to exclude object members.
+     *
+     * @param string $class The class name of the object
+     * @param array $filter An array of members to exclude
+     * @return void
+     */
+    public function setObjectFilter($class, $filter)
+    {
+        $this->objectFilters[strtolower($class)] = $filter;
+    }
+
+    /**
+     * Set some options for the library
+     *
+     * Options:
+     *  - maxDepth: The maximum depth to traverse (default: 10)
+     *  - maxObjectDepth: The maximum depth to traverse objects (default: 5)
+     *  - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
+     *  - useNativeJsonEncode: If true will use json_encode() (default: true)
+     *  - includeLineNumbers: If true will include line numbers and filenames (default: true)
+     *
+     * @param array $options The options to be set
+     * @return void
+     */
+    public function setOptions($options)
+    {
+        $this->options = array_merge($this->options, $options);
+    }
+
+    /**
+     * Get options from the library
+     *
+     * @return array The currently set options
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Set an option for the library
+     *
+     * @param string $name
+     * @param mixed $value
+     * @return void
+     * @throws Exception
+     */
+    public function setOption($name, $value)
+    {
+        if (!isset($this->options[$name])) {
+            throw $this->newException('Unknown option: ' . $name);
+        }
+        $this->options[$name] = $value;
+    }
+
+    /**
+     * Get an option from the library
+     *
+     * @param string $name
+     * @return mixed
+     * @throws Exception
+     */
+    public function getOption($name)
+    {
+        if (!isset($this->options[$name])) {
+            throw $this->newException('Unknown option: ' . $name);
+        }
+        return $this->options[$name];
+    }
+
+    /**
+     * Register FirePHP as your error handler
+     *
+     * Will throw exceptions for each php error.
+     *
+     * @return mixed Returns a string containing the previously defined error handler (if any)
+     */
+    public function registerErrorHandler($throwErrorExceptions = false)
+    {
+        //NOTE: The following errors will not be caught by this error handler:
+        //      E_ERROR, E_PARSE, E_CORE_ERROR,
+        //      E_CORE_WARNING, E_COMPILE_ERROR,
+        //      E_COMPILE_WARNING, E_STRICT
+
+        $this->throwErrorExceptions = $throwErrorExceptions;
+
+        return set_error_handler(array($this, 'errorHandler'));
+    }
+
+    /**
+     * FirePHP's error handler
+     *
+     * Throws exception for each php error that will occur.
+     *
+     * @param integer $errno
+     * @param string $errstr
+     * @param string $errfile
+     * @param integer $errline
+     * @param array $errcontext
+     */
+    public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
+    {
+        // Don't throw exception if error reporting is switched off
+        if (error_reporting() == 0) {
+            return;
+        }
+        // Only throw exceptions for errors we are asking for
+        if (error_reporting() & $errno) {
+
+            $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
+            if ($this->throwErrorExceptions) {
+                throw $exception;
+            } else {
+                $this->fb($exception);
+            }
+        }
+    }
+
+    /**
+     * Register FirePHP as your exception handler
+     *
+     * @return mixed Returns the name of the previously defined exception handler,
+     *               or NULL on error.
+     *               If no previous handler was defined, NULL is also returned.
+     */
+    public function registerExceptionHandler()
+    {
+        return set_exception_handler(array($this, 'exceptionHandler'));
+    }
+
+    /**
+     * FirePHP's exception handler
+     *
+     * Logs all exceptions to your firebug console and then stops the script.
+     *
+     * @param Exception $exception
+     * @throws Exception
+     */
+    function exceptionHandler($exception)
+    {
+        $this->inExceptionHandler = true;
+
+        header('HTTP/1.1 500 Internal Server Error');
+
+        try {
+            $this->fb($exception);
+        } catch (Exception $e) {
+            echo 'We had an exception: ' . $e;
+        }
+
+        $this->inExceptionHandler = false;
+    }
+
+    /**
+     * Register FirePHP driver as your assert callback
+     *
+     * @param boolean $convertAssertionErrorsToExceptions
+     * @param boolean $throwAssertionExceptions
+     * @return mixed Returns the original setting or FALSE on errors
+     */
+    public function registerAssertionHandler($convertAssertionErrorsToExceptions = true, $throwAssertionExceptions = false)
+    {
+        $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions;
+        $this->throwAssertionExceptions = $throwAssertionExceptions;
+
+        if ($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) {
+            throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!');
+        }
+
+        return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
+    }
+
+    /**
+     * FirePHP's assertion handler
+     *
+     * Logs all assertions to your firebug console and then stops the script.
+     *
+     * @param string $file File source of assertion
+     * @param integer $line Line source of assertion
+     * @param mixed $code Assertion code
+     */
+    public function assertionHandler($file, $line, $code)
+    {
+        if ($this->convertAssertionErrorsToExceptions) {
+
+            $exception = new ErrorException('Assertion Failed - Code[ ' . $code . ' ]', 0, null, $file, $line);
+
+            if ($this->throwAssertionExceptions) {
+                throw $exception;
+            } else {
+                $this->fb($exception);
+            }
+
+        } else {
+            $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File' => $file, 'Line' => $line));
+        }
+    }
+
+    /**
+     * Start a group for following messages.
+     *
+     * Options:
+     *   Collapsed: [true|false]
+     *   Color:     [#RRGGBB|ColorName]
+     *
+     * @param string $name
+     * @param array $options OPTIONAL Instructions on how to log the group
+     * @return true
+     * @throws Exception
+     */
+    public function group($name, $options = null)
+    {
+
+        if (!$name) {
+            throw $this->newException('You must specify a label for the group!');
+        }
+
+        if ($options) {
+            if (!is_array($options)) {
+                throw $this->newException('Options must be defined as an array!');
+            }
+            if (array_key_exists('Collapsed', $options)) {
+                $options['Collapsed'] = ($options['Collapsed']) ? 'true' : 'false';
+            }
+        }
+
+        return $this->fb(null, $name, FirePHP::GROUP_START, $options);
+    }
+
+    /**
+     * Ends a group you have started before
+     *
+     * @return true
+     * @throws Exception
+     */
+    public function groupEnd()
+    {
+        return $this->fb(null, null, FirePHP::GROUP_END);
+    }
+
+    /**
+     * Log object with label to firebug console
+     *
+     * @see FirePHP::LOG
+     * @param mixes $object
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function log($object, $label = null, $options = array())
+    {
+        return $this->fb($object, $label, FirePHP::LOG, $options);
+    }
+
+    /**
+     * Log object with label to firebug console
+     *
+     * @see FirePHP::INFO
+     * @param mixes $object
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function info($object, $label = null, $options = array())
+    {
+        return $this->fb($object, $label, FirePHP::INFO, $options);
+    }
+
+    /**
+     * Log object with label to firebug console
+     *
+     * @see FirePHP::WARN
+     * @param mixes $object
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function warn($object, $label = null, $options = array())
+    {
+        return $this->fb($object, $label, FirePHP::WARN, $options);
+    }
+
+    /**
+     * Log object with label to firebug console
+     *
+     * @see FirePHP::ERROR
+     * @param mixes $object
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function error($object, $label = null, $options = array())
+    {
+        return $this->fb($object, $label, FirePHP::ERROR, $options);
+    }
+
+    /**
+     * Dumps key and variable to firebug server panel
+     *
+     * @see FirePHP::DUMP
+     * @param string $key
+     * @param mixed $variable
+     * @return true
+     * @throws Exception
+     */
+    public function dump($key, $variable, $options = array())
+    {
+        if (!is_string($key)) {
+            throw $this->newException('Key passed to dump() is not a string');
+        }
+        if (strlen($key) > 100) {
+            throw $this->newException('Key passed to dump() is longer than 100 characters');
+        }
+        if (!preg_match_all('/^[a-zA-Z0-9-_\.:]*$/', $key, $m)) {
+            throw $this->newException('Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]');
+        }
+        return $this->fb($variable, $key, FirePHP::DUMP, $options);
+    }
+
+    /**
+     * Log a trace in the firebug console
+     *
+     * @see FirePHP::TRACE
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function trace($label)
+    {
+        return $this->fb($label, FirePHP::TRACE);
+    }
+
+    /**
+     * Log a table in the firebug console
+     *
+     * @see FirePHP::TABLE
+     * @param string $label
+     * @param string $table
+     * @return true
+     * @throws Exception
+     */
+    public function table($label, $table, $options = array())
+    {
+        return $this->fb($table, $label, FirePHP::TABLE, $options);
+    }
+
+    /**
+     * Insight API wrapper
+     *
+     * @see Insight_Helper::to()
+     */
+    public static function to()
+    {
+        $instance = self::getInstance();
+        if (!method_exists($instance, '_to')) {
+            throw new Exception('FirePHP::to() implementation not loaded');
+        }
+        $args = func_get_args();
+        return call_user_func_array(array($instance, '_to'), $args);
+    }
+
+    /**
+     * Insight API wrapper
+     *
+     * @see Insight_Helper::plugin()
+     */
+    public static function plugin()
+    {
+        $instance = self::getInstance();
+        if (!method_exists($instance, '_plugin')) {
+            throw new Exception('FirePHP::plugin() implementation not loaded');
+        }
+        $args = func_get_args();
+        return call_user_func_array(array($instance, '_plugin'), $args);
+    }
+
+    /**
+     * Check if FirePHP is installed on client
+     *
+     * @return boolean
+     */
+    public function detectClientExtension()
+    {
+        // Check if FirePHP is installed on client via User-Agent header
+        if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si', $this->getUserAgent(), $m) &&
+            version_compare($m[1][0], '0.0.6', '>=')) {
+            return true;
+        } else
+            // Check if FirePHP is installed on client via X-FirePHP-Version header
+            if (@preg_match_all('/^([\.\d]*)$/si', $this->getRequestHeader('X-FirePHP-Version'), $m) &&
+                version_compare($m[1][0], '0.0.6', '>=')) {
+                return true;
+            }
+        return false;
+    }
+
+    /**
+     * Log varible to Firebug
+     *
+     * @see http://www.firephp.org/Wiki/Reference/Fb
+     * @param mixed $object The variable to be logged
+     * @return boolean Return TRUE if message was added to headers, FALSE otherwise
+     * @throws Exception
+     */
+    public function fb($object)
+    {
+        if ($this instanceof FirePHP_Insight && method_exists($this, '_logUpgradeClientMessage')) {
+            if (!FirePHP_Insight::$upgradeClientMessageLogged) { // avoid infinite recursion as _logUpgradeClientMessage() logs a message
+                $this->_logUpgradeClientMessage();
+            }
+        }
+
+        static $insightGroupStack = array();
+
+        if (!$this->getEnabled()) {
+            return false;
+        }
+
+        if ($this->headersSent($filename, $linenum)) {
+            // If we are logging from within the exception handler we cannot throw another exception
+            if ($this->inExceptionHandler) {
+                // Simply echo the error out to the page
+                echo '<div style="border: 2px solid red; font-family: Arial; font-size: 12px; background-color: lightgray; padding: 5px;"><span style="color: red; font-weight: bold;">FirePHP ERROR:</span> Headers already sent in <b>' . $filename . '</b> on line <b>' . $linenum . '</b>. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.</div>';
+            } else {
+                throw $this->newException('Headers already sent in ' . $filename . ' on line ' . $linenum . '. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
+            }
+        }
+
+        $type = null;
+        $label = null;
+        $options = array();
+
+        if (func_num_args() == 1) {
+        } else if (func_num_args() == 2) {
+            switch (func_get_arg(1)) {
+                case self::LOG:
+                case self::INFO:
+                case self::WARN:
+                case self::ERROR:
+                case self::DUMP:
+                case self::TRACE:
+                case self::EXCEPTION:
+                case self::TABLE:
+                case self::GROUP_START:
+                case self::GROUP_END:
+                    $type = func_get_arg(1);
+                    break;
+                default:
+                    $label = func_get_arg(1);
+                    break;
+            }
+        } else if (func_num_args() == 3) {
+            $type = func_get_arg(2);
+            $label = func_get_arg(1);
+        } else if (func_num_args() == 4) {
+            $type = func_get_arg(2);
+            $label = func_get_arg(1);
+            $options = func_get_arg(3);
+        } else {
+            throw $this->newException('Wrong number of arguments to fb() function!');
+        }
+
+        if ($this->logToInsightConsole !== null && (get_class($this) == 'FirePHP_Insight' || is_subclass_of($this, 'FirePHP_Insight'))) {
+            $trace = debug_backtrace();
+            if (!$trace) return false;
+            for ($i = 0; $i < sizeof($trace); $i++) {
+                if (isset($trace[$i]['class'])) {
+                    if ($trace[$i]['class'] == 'FirePHP' || $trace[$i]['class'] == 'FB') {
+                        continue;
+                    }
+                }
+                if (isset($trace[$i]['file'])) {
+                    $path = $this->_standardizePath($trace[$i]['file']);
+                    if (substr($path, -18, 18) == 'FirePHPCore/fb.php' || substr($path, -29, 29) == 'FirePHPCore/FirePHP.php') {
+                        continue;
+                    }
+                }
+                if (isset($trace[$i]['function']) && $trace[$i]['function'] == 'fb' &&
+                    isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                    continue;
+                }
+                if (isset($trace[$i]['class']) && $trace[$i]['class'] == 'FB' &&
+                    isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                    continue;
+                }
+                break;
+            }
+            // adjust trace offset
+            $msg = $this->logToInsightConsole->option('encoder.trace.offsetAdjustment', $i);
+
+            if ($object instanceof Exception) {
+                $type = self::EXCEPTION;
+            }
+            if ($label && $type != self::TABLE && $type != self::GROUP_START) {
+                $msg = $msg->label($label);
+            }
+            switch ($type) {
+                case self::DUMP:
+                case self::LOG:
+                    return $msg->log($object);
+                case self::INFO:
+                    return $msg->info($object);
+                case self::WARN:
+                    return $msg->warn($object);
+                case self::ERROR:
+                    return $msg->error($object);
+                case self::TRACE:
+                    return $msg->trace($object);
+                case self::EXCEPTION:
+                    return $this->plugin('error')->handleException($object, $msg);
+                case self::TABLE:
+                    if (isset($object[0]) && !is_string($object[0]) && $label) {
+                        $object = array($label, $object);
+                    }
+                    return $msg->table($object[0], array_slice($object[1], 1), $object[1][0]);
+                case self::GROUP_START:
+                    $insightGroupStack[] = $msg->group(md5($label))->open();
+                    return $msg->log($label);
+                case self::GROUP_END:
+                    if (count($insightGroupStack) == 0) {
+                        throw new Error('Too many groupEnd() as opposed to group() calls!');
+                    }
+                    $group = array_pop($insightGroupStack);
+                    return $group->close();
+                default:
+                    return $msg->log($object);
+            }
+        }
+
+        if (!$this->detectClientExtension()) {
+            return false;
+        }
+
+        $meta = array();
+        $skipFinalObjectEncode = false;
+
+        if ($object instanceof Exception) {
+
+            $meta['file'] = $this->_escapeTraceFile($object->getFile());
+            $meta['line'] = $object->getLine();
+
+            $trace = $object->getTrace();
+            if ($object instanceof ErrorException
+                && isset($trace[0]['function'])
+                && $trace[0]['function'] == 'errorHandler'
+                && isset($trace[0]['class'])
+                && $trace[0]['class'] == 'FirePHP') {
+
+                $severity = false;
+                switch ($object->getSeverity()) {
+                    case E_WARNING:
+                        $severity = 'E_WARNING';
+                        break;
+
+                    case E_NOTICE:
+                        $severity = 'E_NOTICE';
+                        break;
+
+                    case E_USER_ERROR:
+                        $severity = 'E_USER_ERROR';
+                        break;
+
+                    case E_USER_WARNING:
+                        $severity = 'E_USER_WARNING';
+                        break;
+
+                    case E_USER_NOTICE:
+                        $severity = 'E_USER_NOTICE';
+                        break;
+
+                    case E_STRICT:
+                        $severity = 'E_STRICT';
+                        break;
+
+                    case E_RECOVERABLE_ERROR:
+                        $severity = 'E_RECOVERABLE_ERROR';
+                        break;
+
+                    case E_DEPRECATED:
+                        $severity = 'E_DEPRECATED';
+                        break;
+
+                    case E_USER_DEPRECATED:
+                        $severity = 'E_USER_DEPRECATED';
+                        break;
+                }
+
+                $object = array('Class' => get_class($object),
+                    'Message' => $severity . ': ' . $object->getMessage(),
+                    'File' => $this->_escapeTraceFile($object->getFile()),
+                    'Line' => $object->getLine(),
+                    'Type' => 'trigger',
+                    'Trace' => $this->_escapeTrace(array_splice($trace, 2)));
+                $skipFinalObjectEncode = true;
+            } else {
+                $object = array('Class' => get_class($object),
+                    'Message' => $object->getMessage(),
+                    'File' => $this->_escapeTraceFile($object->getFile()),
+                    'Line' => $object->getLine(),
+                    'Type' => 'throw',
+                    'Trace' => $this->_escapeTrace($trace));
+                $skipFinalObjectEncode = true;
+            }
+            $type = self::EXCEPTION;
+
+        } else if ($type == self::TRACE) {
+
+            $trace = debug_backtrace();
+            if (!$trace) return false;
+            for ($i = 0; $i < sizeof($trace); $i++) {
+
+                if (isset($trace[$i]['class'])
+                    && isset($trace[$i]['file'])
+                    && ($trace[$i]['class'] == 'FirePHP'
+                        || $trace[$i]['class'] == 'FB')
+                    && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
+                        || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.php')) {
+                    /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
+                } else
+                    if (isset($trace[$i]['class'])
+                        && isset($trace[$i+1]['file'])
+                        && $trace[$i]['class'] == 'FirePHP'
+                        && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                        /* Skip fb() */
+                    } else
+                        if ($trace[$i]['function'] == 'fb'
+                            || $trace[$i]['function'] == 'trace'
+                            || $trace[$i]['function'] == 'send') {
+
+                            $object = array('Class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : '',
+                                'Type' => isset($trace[$i]['type']) ? $trace[$i]['type'] : '',
+                                'Function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : '',
+                                'Message' => $trace[$i]['args'][0],
+                                'File' => isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '',
+                                'Line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : '',
+                                'Args' => isset($trace[$i]['args']) ? $this->encodeObject($trace[$i]['args']) : '',
+                                'Trace' => $this->_escapeTrace(array_splice($trace, $i + 1)));
+
+                            $skipFinalObjectEncode = true;
+                            $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
+                            $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
+                            break;
+                        }
+            }
+
+        } else
+            if ($type == self::TABLE) {
+
+                if (isset($object[0]) && is_string($object[0])) {
+                    $object[1] = $this->encodeTable($object[1]);
+                } else {
+                    $object = $this->encodeTable($object);
+                }
+
+                $skipFinalObjectEncode = true;
+
+            } else if ($type == self::GROUP_START) {
+
+                if (!$label) {
+                    throw $this->newException('You must specify a label for the group!');
+                }
+
+            } else {
+                if ($type === null) {
+                    $type = self::LOG;
+                }
+            }
+
+        if ($this->options['includeLineNumbers']) {
+            if (!isset($meta['file']) || !isset($meta['line'])) {
+
+                $trace = debug_backtrace();
+                for ($i = 0; $trace && $i < sizeof($trace); $i++) {
+
+                    if (isset($trace[$i]['class'])
+                        && isset($trace[$i]['file'])
+                        && ($trace[$i]['class'] == 'FirePHP'
+                            || $trace[$i]['class'] == 'FB')
+                        && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
+                            || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.php')) {
+                        /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
+                    } else
+                        if (isset($trace[$i]['class'])
+                            && isset($trace[$i + 1]['file'])
+                            && $trace[$i]['class'] == 'FirePHP'
+                            && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                            /* Skip fb() */
+                        } else
+                            if (isset($trace[$i]['file'])
+                                && substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                                /* Skip FB::fb() */
+                            } else {
+                                $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
+                                $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
+                                break;
+                            }
+                }
+            }
+        } else {
+            unset($meta['file']);
+            unset($meta['line']);
+        }
+
+        $this->setHeader('X-Wf-Protocol-1', 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
+        $this->setHeader('X-Wf-1-Plugin-1', 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/' . self::VERSION);
+
+        $structureIndex = 1;
+        if ($type == self::DUMP) {
+            $structureIndex = 2;
+            $this->setHeader('X-Wf-1-Structure-2', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
+        } else {
+            $this->setHeader('X-Wf-1-Structure-1', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
+        }
+
+        if ($type == self::DUMP) {
+            $msg = '{"' . $label . '":' . $this->jsonEncode($object, $skipFinalObjectEncode) . '}';
+        } else {
+            $msgMeta = $options;
+            $msgMeta['Type'] = $type;
+            if ($label !== null) {
+                $msgMeta['Label'] = $label;
+            }
+            if (isset($meta['file']) && !isset($msgMeta['File'])) {
+                $msgMeta['File'] = $meta['file'];
+            }
+            if (isset($meta['line']) && !isset($msgMeta['Line'])) {
+                $msgMeta['Line'] = $meta['line'];
+            }
+            $msg = '[' . $this->jsonEncode($msgMeta) . ',' . $this->jsonEncode($object, $skipFinalObjectEncode) . ']';
+        }
+
+        $parts = explode("\n", chunk_split($msg, 5000, "\n"));
+
+        for ($i = 0; $i < count($parts); $i++) {
+
+            $part = $parts[$i];
+            if ($part) {
+
+                if (count($parts) > 2) {
+                    // Message needs to be split into multiple parts
+                    $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
+                        (($i == 0) ? strlen($msg) : '')
+                        . '|' . $part . '|'
+                        . (($i < count($parts) - 2) ? '\\' : ''));
+                } else {
+                    $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
+                        strlen($part) . '|' . $part . '|');
+                }
+
+                $this->messageIndex++;
+
+                if ($this->messageIndex > 99999) {
+                    throw $this->newException('Maximum number (99,999) of messages reached!');
+                }
+            }
+        }
+
+        $this->setHeader('X-Wf-1-Index', $this->messageIndex - 1);
+
+        return true;
+    }
+
+    /**
+     * Standardizes path for windows systems.
+     *
+     * @param string $path
+     * @return string
+     */
+    protected function _standardizePath($path)
+    {
+        return preg_replace('/\\\\+/', '/', $path);
+    }
+
+    /**
+     * Escape trace path for windows systems
+     *
+     * @param array $trace
+     * @return array
+     */
+    protected function _escapeTrace($trace)
+    {
+        if (!$trace) return $trace;
+        for ($i = 0; $i < sizeof($trace); $i++) {
+            if (isset($trace[$i]['file'])) {
+                $trace[$i]['file'] = $this->_escapeTraceFile($trace[$i]['file']);
+            }
+            if (isset($trace[$i]['args'])) {
+                $trace[$i]['args'] = $this->encodeObject($trace[$i]['args']);
+            }
+        }
+        return $trace;
+    }
+
+    /**
+     * Escape file information of trace for windows systems
+     *
+     * @param string $file
+     * @return string
+     */
+    protected function _escapeTraceFile($file)
+    {
+        /* Check if we have a windows filepath */
+        if (strpos($file, '\\')) {
+            /* First strip down to single \ */
+
+            $file = preg_replace('/\\\\+/', '\\', $file);
+
+            return $file;
+        }
+        return $file;
+    }
+
+    /**
+     * Check if headers have already been sent
+     *
+     * @param string $filename
+     * @param integer $linenum
+     */
+    protected function headersSent(&$filename, &$linenum)
+    {
+        return headers_sent($filename, $linenum);
+    }
+
+    /**
+     * Send header
+     *
+     * @param string $name
+     * @param string $value
+     */
+    protected function setHeader($name, $value)
+    {
+        return header($name . ': ' . $value);
+    }
+
+    /**
+     * Get user agent
+     *
+     * @return string|false
+     */
+    protected function getUserAgent()
+    {
+        if (!isset($_SERVER['HTTP_USER_AGENT'])) return false;
+        return $_SERVER['HTTP_USER_AGENT'];
+    }
+
+    /**
+     * Get all request headers
+     *
+     * @return array
+     */
+    public static function getAllRequestHeaders()
+    {
+        static $_cachedHeaders = false;
+        if ($_cachedHeaders !== false) {
+            return $_cachedHeaders;
+        }
+        $headers = array();
+        if (function_exists('getallheaders')) {
+            foreach (getallheaders() as $name => $value) {
+                $headers[strtolower($name)] = $value;
+            }
+        } else {
+            foreach ($_SERVER as $name => $value) {
+                if (substr($name, 0, 5) == 'HTTP_') {
+                    $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
+                }
+            }
+        }
+        return $_cachedHeaders = $headers;
+    }
+
+    /**
+     * Get a request header
+     *
+     * @return string|false
+     */
+    protected function getRequestHeader($name)
+    {
+        $headers = self::getAllRequestHeaders();
+        if (isset($headers[strtolower($name)])) {
+            return $headers[strtolower($name)];
+        }
+        return false;
+    }
+
+    /**
+     * Returns a new exception
+     *
+     * @param string $message
+     * @return Exception
+     */
+    protected function newException($message)
+    {
+        return new Exception($message);
+    }
+
+    /**
+     * Encode an object into a JSON string
+     *
+     * Uses PHP's jeson_encode() if available
+     *
+     * @param object $object The object to be encoded
+     * @param boolean $skipObjectEncode
+     * @return string The JSON string
+     */
+    public function jsonEncode($object, $skipObjectEncode = false)
+    {
+        if (!$skipObjectEncode) {
+            $object = $this->encodeObject($object);
+        }
+
+        if (function_exists('json_encode')
+            && $this->options['useNativeJsonEncode'] != false) {
+
+            return json_encode($object);
+        } else {
+            return $this->json_encode($object);
+        }
+    }
+
+    /**
+     * Encodes a table by encoding each row and column with encodeObject()
+     *
+     * @param array $table The table to be encoded
+     * @return array
+     */
+    protected function encodeTable($table)
+    {
+        if (!$table) return $table;
+
+        $newTable = array();
+        foreach ($table as $row) {
+
+            if (is_array($row)) {
+                $newRow = array();
+
+                foreach ($row as $item) {
+                    $newRow[] = $this->encodeObject($item);
+                }
+
+                $newTable[] = $newRow;
+            }
+        }
+
+        return $newTable;
+    }
+
+    /**
+     * Encodes an object including members with
+     * protected and private visibility
+     *
+     * @param object $object The object to be encoded
+     * @param integer $Depth The current traversal depth
+     * @return array All members of the object
+     */
+    protected function encodeObject($object, $objectDepth = 1, $arrayDepth = 1, $maxDepth = 1)
+    {
+        if ($maxDepth > $this->options['maxDepth']) {
+            return '** Max Depth (' . $this->options['maxDepth'] . ') **';
+        }
+
+        $return = array();
+
+        if (is_resource($object)) {
+
+            return '** ' . (string) $object . ' **';
+
+        } else if (is_object($object)) {
+
+            if ($objectDepth > $this->options['maxObjectDepth']) {
+                return '** Max Object Depth (' . $this->options['maxObjectDepth'] . ') **';
+            }
+
+            foreach ($this->objectStack as $refVal) {
+                if ($refVal === $object) {
+                    return '** Recursion (' . get_class($object) . ') **';
+                }
+            }
+            array_push($this->objectStack, $object);
+
+            $return['__className'] = $class = get_class($object);
+            $classLower = strtolower($class);
+
+            $reflectionClass = new ReflectionClass($class);
+            $properties = array();
+            foreach ($reflectionClass->getProperties() as $property) {
+                $properties[$property->getName()] = $property;
+            }
+
+            $members = (array)$object;
+
+            foreach ($properties as $plainName => $property) {
+
+                $name = $rawName = $plainName;
+                if ($property->isStatic()) {
+                    $name = 'static:' . $name;
+                }
+                if ($property->isPublic()) {
+                    $name = 'public:' . $name;
+                } else if ($property->isPrivate()) {
+                    $name = 'private:' . $name;
+                    $rawName = "\0" . $class . "\0" . $rawName;
+                } else if ($property->isProtected()) {
+                    $name = 'protected:' . $name;
+                    $rawName = "\0" . '*' . "\0" . $rawName;
+                }
+
+                if (!(isset($this->objectFilters[$classLower])
+                    && is_array($this->objectFilters[$classLower])
+                    && in_array($plainName, $this->objectFilters[$classLower]))) {
+
+                    if (array_key_exists($rawName, $members) && !$property->isStatic()) {
+                        $return[$name] = $this->encodeObject($members[$rawName], $objectDepth + 1, 1, $maxDepth + 1);
+                    } else {
+                        if (method_exists($property, 'setAccessible')) {
+                            $property->setAccessible(true);
+                            $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
+                        } else
+                            if ($property->isPublic()) {
+                                $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
+                            } else {
+                                $return[$name] = '** Need PHP 5.3 to get value **';
+                            }
+                    }
+                } else {
+                    $return[$name] = '** Excluded by Filter **';
+                }
+            }
+
+            // Include all members that are not defined in the class
+            // but exist in the object
+            foreach ($members as $rawName => $value) {
+
+                $name = $rawName;
+
+                if ($name{0} == "\0") {
+                    $parts = explode("\0", $name);
+                    $name = $parts[2];
+                }
+
+                $plainName = $name;
+
+                if (!isset($properties[$name])) {
+                    $name = 'undeclared:' . $name;
+
+                    if (!(isset($this->objectFilters[$classLower])
+                        && is_array($this->objectFilters[$classLower])
+                        && in_array($plainName, $this->objectFilters[$classLower]))) {
+
+                        $return[$name] = $this->encodeObject($value, $objectDepth + 1, 1, $maxDepth + 1);
+                    } else {
+                        $return[$name] = '** Excluded by Filter **';
+                    }
+                }
+            }
+
+            array_pop($this->objectStack);
+
+        } elseif (is_array($object)) {
+
+            if ($arrayDepth > $this->options['maxArrayDepth']) {
+                return '** Max Array Depth (' . $this->options['maxArrayDepth'] . ') **';
+            }
+
+            foreach ($object as $key => $val) {
+
+                // Encoding the $GLOBALS PHP array causes an infinite loop
+                // if the recursion is not reset here as it contains
+                // a reference to itself. This is the only way I have come up
+                // with to stop infinite recursion in this case.
+                if ($key == 'GLOBALS'
+                    && is_array($val)
+                    && array_key_exists('GLOBALS', $val)) {
+                    $val['GLOBALS'] = '** Recursion (GLOBALS) **';
+                }
+
+                if (!$this->is_utf8($key)) {
+                    $key = utf8_encode($key);
+                }
+
+                $return[$key] = $this->encodeObject($val, 1, $arrayDepth + 1, $maxDepth + 1);
+            }
+        } else {
+            if ($this->is_utf8($object)) {
+                return $object;
+            } else {
+                return utf8_encode($object);
+            }
+        }
+        return $return;
+    }
+
+    /**
+     * Returns true if $string is valid UTF-8 and false otherwise.
+     *
+     * @param mixed $str String to be tested
+     * @return boolean
+     */
+    protected function is_utf8($str)
+    {
+        if (function_exists('mb_detect_encoding')) {
+            return (
+                mb_detect_encoding($str, 'UTF-8', true) == 'UTF-8' &&
+                ($str === null || $this->jsonEncode($str, true) !== 'null')
+            );
+        }
+        $c = 0;
+        $b = 0;
+        $bits = 0;
+        $len = strlen($str);
+        for ($i = 0; $i < $len; $i++) {
+            $c = ord($str[$i]);
+            if ($c > 128) {
+                if (($c >= 254)) return false;
+                elseif ($c >= 252) $bits = 6;
+                elseif ($c >= 248) $bits = 5;
+                elseif ($c >= 240) $bits = 4;
+                elseif ($c >= 224) $bits = 3;
+                elseif ($c >= 192) $bits = 2;
+                else return false;
+                if (($i + $bits) > $len) return false;
+                while($bits > 1) {
+                    $i++;
+                    $b = ord($str[$i]);
+                    if ($b < 128 || $b > 191) return false;
+                    $bits--;
+                }
+            }
+        }
+        return ($str === null || $this->jsonEncode($str, true) !== 'null');
+    }
+
+    /**
+     * Converts to and from JSON format.
+     *
+     * JSON (JavaScript Object Notation) is a lightweight data-interchange
+     * format. It is easy for humans to read and write. It is easy for machines
+     * to parse and generate. It is based on a subset of the JavaScript
+     * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
+     * This feature can also be found in  Python. JSON is a text format that is
+     * completely language independent but uses conventions that are familiar
+     * to programmers of the C-family of languages, including C, C++, C#, Java,
+     * JavaScript, Perl, TCL, and many others. These properties make JSON an
+     * ideal data-interchange language.
+     *
+     * This package provides a simple encoder and decoder for JSON notation. It
+     * is intended for use with client-side Javascript applications that make
+     * use of HTTPRequest to perform server communication functions - data can
+     * be encoded into JSON notation for use in a client-side javascript, or
+     * decoded from incoming Javascript requests. JSON format is native to
+     * Javascript, and can be directly eval()'ed with no further parsing
+     * overhead
+     *
+     * All strings should be in ASCII or UTF-8 format!
+     *
+     * LICENSE: Redistribution and use in source and binary forms, with or
+     * without modification, are permitted provided that the following
+     * conditions are met: Redistributions of source code must retain the
+     * above copyright notice, this list of conditions and the following
+     * disclaimer. Redistributions in binary form must reproduce the above
+     * copyright notice, this list of conditions and the following disclaimer
+     * in the documentation and/or other materials provided with the
+     * distribution.
+     *
+     * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+     * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+     * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+     * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+     * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+     * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+     * DAMAGE.
+     *
+     * @category
+     * @package     Services_JSON
+     * @author      Michal Migurski <mike-json@teczno.com>
+     * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
+     * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
+     * @author      Christoph Dorn <christoph@christophdorn.com>
+     * @copyright   2005 Michal Migurski
+     * @version     CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
+     * @license     http://www.opensource.org/licenses/bsd-license.php
+     * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+     */
+
+
+    /**
+     * Keep a list of objects as we descend into the array so we can detect recursion.
+     */
+    private $json_objectStack = array();
+
+
+    /**
+     * convert a string from one UTF-8 char to one UTF-16 char
+     *
+     * Normally should be handled by mb_convert_encoding, but
+     * provides a slower PHP-only method for installations
+     * that lack the multibye string extension.
+     *
+     * @param    string  $utf8   UTF-8 character
+     * @return   string  UTF-16 character
+     * @access   private
+     */
+    private function json_utf82utf16($utf8)
+    {
+        // oh please oh please oh please oh please oh please
+        if (function_exists('mb_convert_encoding')) {
+            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
+        }
+
+        switch (strlen($utf8)) {
+            case 1:
+                // this case should never be reached, because we are in ASCII range
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return $utf8;
+
+            case 2:
+                // return a UTF-16 character from a 2-byte UTF-8 char
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr(0x07 & (ord($utf8{0}) >> 2))
+                . chr((0xC0 & (ord($utf8{0}) << 6))
+                    | (0x3F & ord($utf8{1})));
+
+            case 3:
+                // return a UTF-16 character from a 3-byte UTF-8 char
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr((0xF0 & (ord($utf8{0}) << 4))
+                    | (0x0F & (ord($utf8{1}) >> 2)))
+                . chr((0xC0 & (ord($utf8{1}) << 6))
+                    | (0x7F & ord($utf8{2})));
+        }
+
+        // ignoring UTF-32 for now, sorry
+        return '';
+    }
+
+    /**
+     * encodes an arbitrary variable into JSON format
+     *
+     * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
+     *                           see argument 1 to Services_JSON() above for array-parsing behavior.
+     *                           if var is a strng, note that encode() always expects it
+     *                           to be in ASCII or UTF-8 format!
+     *
+     * @return   mixed   JSON string representation of input var or an error if a problem occurs
+     * @access   public
+     */
+    private function json_encode($var)
+    {
+        if (is_object($var)) {
+            if (in_array($var, $this->json_objectStack)) {
+                return '"** Recursion **"';
+            }
+        }
+
+        switch (gettype($var)) {
+            case 'boolean':
+                return $var ? 'true' : 'false';
+
+            case 'NULL':
+                return 'null';
+
+            case 'integer':
+                return (int) $var;
+
+            case 'double':
+            case 'float':
+                return (float) $var;
+
+            case 'string':
+                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+                $ascii = '';
+                $strlen_var = strlen($var);
+
+                /*
+                 * Iterate over every character in the string,
+                 * escaping with a slash or encoding to UTF-8 where necessary
+                 */
+                for ($c = 0; $c < $strlen_var; ++$c) {
+
+                    $ord_var_c = ord($var{$c});
+
+                    switch (true) {
+                        case $ord_var_c == 0x08:
+                            $ascii .= '\b';
+                            break;
+                        case $ord_var_c == 0x09:
+                            $ascii .= '\t';
+                            break;
+                        case $ord_var_c == 0x0A:
+                            $ascii .= '\n';
+                            break;
+                        case $ord_var_c == 0x0C:
+                            $ascii .= '\f';
+                            break;
+                        case $ord_var_c == 0x0D:
+                            $ascii .= '\r';
+                            break;
+
+                        case $ord_var_c == 0x22:
+                        case $ord_var_c == 0x2F:
+                        case $ord_var_c == 0x5C:
+                            // double quote, slash, slosh
+                            $ascii .= '\\' . $var{$c};
+                            break;
+
+                        case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+                            // characters U-00000000 - U-0000007F (same as ASCII)
+                            $ascii .= $var{$c};
+                            break;
+
+                        case (($ord_var_c & 0xE0) == 0xC0):
+                            // characters U-00000080 - U-000007FF, mask 110XXXXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
+                            $c += 1;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xF0) == 0xE0):
+                            // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                ord($var{$c + 1}),
+                                ord($var{$c + 2}));
+                            $c += 2;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xF8) == 0xF0):
+                            // characters U-00010000 - U-001FFFFF, mask 11110XXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                ord($var{$c + 1}),
+                                ord($var{$c + 2}),
+                                ord($var{$c + 3}));
+                            $c += 3;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xFC) == 0xF8):
+                            // characters U-00200000 - U-03FFFFFF, mask 111110XX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                ord($var{$c + 1}),
+                                ord($var{$c + 2}),
+                                ord($var{$c + 3}),
+                                ord($var{$c + 4}));
+                            $c += 4;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xFE) == 0xFC):
+                            // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                ord($var{$c + 1}),
+                                ord($var{$c + 2}),
+                                ord($var{$c + 3}),
+                                ord($var{$c + 4}),
+                                ord($var{$c + 5}));
+                            $c += 5;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+                    }
+                }
+
+                return '"' . $ascii . '"';
+
+            case 'array':
+                /*
+                 * As per JSON spec if any array key is not an integer
+                 * we must treat the the whole array as an object. We
+                 * also try to catch a sparsely populated associative
+                 * array with numeric keys here because some JS engines
+                 * will create an array with empty indexes up to
+                 * max_index which can cause memory issues and because
+                 * the keys, which may be relevant, will be remapped
+                 * otherwise.
+                 *
+                 * As per the ECMA and JSON specification an object may
+                 * have any string as a property. Unfortunately due to
+                 * a hole in the ECMA specification if the key is a
+                 * ECMA reserved word or starts with a digit the
+                 * parameter is only accessible using ECMAScript's
+                 * bracket notation.
+                 */
+
+                // treat as a JSON object
+                if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+
+                    $this->json_objectStack[] = $var;
+
+                    $properties = array_map(array($this, 'json_name_value'),
+                        array_keys($var),
+                        array_values($var));
+
+                    array_pop($this->json_objectStack);
+
+                    foreach ($properties as $property) {
+                        if ($property instanceof Exception) {
+                            return $property;
+                        }
+                    }
+
+                    return '{' . join(',', $properties) . '}';
+                }
+
+                $this->json_objectStack[] = $var;
+
+                // treat it like a regular array
+                $elements = array_map(array($this, 'json_encode'), $var);
+
+                array_pop($this->json_objectStack);
+
+                foreach ($elements as $element) {
+                    if ($element instanceof Exception) {
+                        return $element;
+                    }
+                }
+
+                return '[' . join(',', $elements) . ']';
+
+            case 'object':
+                $vars = self::encodeObject($var);
+
+                $this->json_objectStack[] = $var;
+
+                $properties = array_map(array($this, 'json_name_value'),
+                    array_keys($vars),
+                    array_values($vars));
+
+                array_pop($this->json_objectStack);
+
+                foreach ($properties as $property) {
+                    if ($property instanceof Exception) {
+                        return $property;
+                    }
+                }
+
+                return '{' . join(',', $properties) . '}';
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * array-walking function for use in generating JSON-formatted name-value pairs
+     *
+     * @param    string  $name   name of key to use
+     * @param    mixed   $value  reference to an array element to be encoded
+     *
+     * @return   string  JSON-formatted name-value pair, like '"name":value'
+     * @access   private
+     */
+    private function json_name_value($name, $value)
+    {
+        // Encoding the $GLOBALS PHP array causes an infinite loop
+        // if the recursion is not reset here as it contains
+        // a reference to itself. This is the only way I have come up
+        // with to stop infinite recursion in this case.
+        if ($name == 'GLOBALS'
+            && is_array($value)
+            && array_key_exists('GLOBALS', $value)) {
+            $value['GLOBALS'] = '** Recursion **';
+        }
+
+        $encodedValue = $this->json_encode($value);
+
+        if ($encodedValue instanceof Exception) {
+            return $encodedValue;
+        }
+
+        return $this->json_encode(strval($name)) . ':' . $encodedValue;
+    }
+
+    /**
+     * @deprecated
+     */
+    public function setProcessorUrl($URL)
+    {
+        trigger_error('The FirePHP::setProcessorUrl() method is no longer supported', E_USER_DEPRECATED);
+    }
+
+    /**
+     * @deprecated
+     */
+    public function setRendererUrl($URL)
+    {
+        trigger_error('The FirePHP::setRendererUrl() method is no longer supported', E_USER_DEPRECATED);
+    }
+}

+ 382 - 0
Mall/Framework/Core/Jwt.Class.php

@@ -0,0 +1,382 @@
+<?php
+namespace Mall\Framework\Core;
+
+use \DomainException;
+use \InvalidArgumentException;
+use \UnexpectedValueException;
+use \DateTime;
+
+/**
+ * JSON Web Token implementation, based on this spec:
+ * https://tools.ietf.org/html/rfc7519
+ *
+ * PHP version 5
+ *
+ * @category Authentication
+ * @package  Authentication_JWT
+ * @author   Neuman Vong <neuman@twilio.com>
+ * @author   Anant Narayanan <anant@php.net>
+ * @license  http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
+ * @link     https://github.com/firebase/php-jwt
+ */
+class Jwt
+{
+
+    /**
+     * When checking nbf, iat or expiration times,
+     * we want to provide some extra leeway time to
+     * account for clock skew.
+     */
+    public static $leeway = 0;
+
+    /**
+     * Allow the current timestamp to be specified.
+     * Useful for fixing a value within unit testing.
+     *
+     * Will default to PHP time() value if null.
+     */
+    public static $timestamp = null;
+
+    public static $supported_algs = array(
+        'HS256' => array('hash_hmac', 'SHA256'),
+        'HS512' => array('hash_hmac', 'SHA512'),
+        'HS384' => array('hash_hmac', 'SHA384'),
+        'RS256' => array('openssl', 'SHA256'),
+        'RS384' => array('openssl', 'SHA384'),
+        'RS512' => array('openssl', 'SHA512'),
+    );
+
+    /**
+     * Decodes a JWT string into a PHP object.
+     *
+     * @param string        $jwt            The JWT
+     * @param string|array  $key            The key, or map of keys.
+     *                                      If the algorithm used is asymmetric, this is the public key
+     * @param array         $allowed_algs   List of supported verification algorithms
+     *                                      Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+     *
+     * @return object The JWT's payload as a PHP object
+     *
+     * @throws UnexpectedValueException     Provided JWT was invalid
+     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
+     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
+     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
+     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
+     *
+     * @uses jsonDecode
+     * @uses urlsafeB64Decode
+     */
+    public static function decode($jwt, $key, $allowed_algs = array())
+    {
+        $timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;
+
+        if (empty($key)) {
+            throw new InvalidArgumentException('Key may not be empty');
+        }
+        if (!is_array($allowed_algs)) {
+            throw new InvalidArgumentException('Algorithm not allowed');
+        }
+        $tks = explode('.', $jwt);
+        if (count($tks) != 3) {
+            throw new UnexpectedValueException('Wrong number of segments');
+        }
+        list($headb64, $bodyb64, $cryptob64) = $tks;
+        if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
+            throw new UnexpectedValueException('Invalid header encoding');
+        }
+        if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
+            throw new UnexpectedValueException('Invalid claims encoding');
+        }
+        if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
+            throw new UnexpectedValueException('Invalid signature encoding');
+        }
+        if (empty($header->alg)) {
+            throw new UnexpectedValueException('Empty algorithm');
+        }
+        if (empty(static::$supported_algs[$header->alg])) {
+            throw new UnexpectedValueException('Algorithm not supported');
+        }
+        if (!in_array($header->alg, $allowed_algs)) {
+            throw new UnexpectedValueException('Algorithm not allowed');
+        }
+        if (is_array($key) || $key instanceof \ArrayAccess) {
+            if (isset($header->kid)) {
+                if (!isset($key[$header->kid])) {
+                    throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
+                }
+                $key = $key[$header->kid];
+            } else {
+                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
+            }
+        }
+
+        // Check the signature
+        if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
+            throw new UnexpectedValueException('Signature verification failed');
+        }
+
+        // Check if the nbf if it is defined. This is the time that the
+        // token can actually be used. If it's not yet that time, abort.
+        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
+            throw new UnexpectedValueException(
+                'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
+            );
+        }
+
+        // Check that this token has been created before 'now'. This prevents
+        // using tokens that have been created for later use (and haven't
+        // correctly used the nbf claim).
+        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
+            throw new UnexpectedValueException(
+                'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
+            );
+        }
+
+        // Check if this token has expired.
+        if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
+            throw new UnexpectedValueException('Expired token');
+        }
+
+        return $payload;
+    }
+
+    /**
+     * Converts and signs a PHP object or array into a JWT string.
+     *
+     * @param object|array  $payload    PHP object or array
+     * @param string        $key        The secret key.
+     *                                  If the algorithm used is asymmetric, this is the private key
+     * @param string        $alg        The signing algorithm.
+     *                                  Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+     * @param mixed         $keyId
+     * @param array         $head       An array with header elements to attach
+     *
+     * @return string A signed JWT
+     *
+     * @uses jsonEncode
+     * @uses urlsafeB64Encode
+     */
+    public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
+    {
+        $header = array('typ' => 'JWT', 'alg' => $alg);
+        if ($keyId !== null) {
+            $header['kid'] = $keyId;
+        }
+        if ( isset($head) && is_array($head) ) {
+            $header = array_merge($head, $header);
+        }
+        $segments = array();
+        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
+        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
+        $signing_input = implode('.', $segments);
+
+        $signature = static::sign($signing_input, $key, $alg);
+        $segments[] = static::urlsafeB64Encode($signature);
+
+        return implode('.', $segments);
+    }
+
+    /**
+     * Sign a string with a given key and algorithm.
+     *
+     * @param string            $msg    The message to sign
+     * @param string|resource   $key    The secret key
+     * @param string            $alg    The signing algorithm.
+     *                                  Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+     *
+     * @return string An encrypted message
+     *
+     * @throws DomainException Unsupported algorithm was specified
+     */
+    public static function sign($msg, $key, $alg = 'HS256')
+    {
+        if (empty(static::$supported_algs[$alg])) {
+            throw new DomainException('Algorithm not supported');
+        }
+        list($function, $algorithm) = static::$supported_algs[$alg];
+        switch($function) {
+            case 'hash_hmac':
+                return hash_hmac($algorithm, $msg, $key, true);
+            case 'openssl':
+                $signature = '';
+                $success = openssl_sign($msg, $signature, $key, $algorithm);
+                if (!$success) {
+                    throw new DomainException("OpenSSL unable to sign data");
+                } else {
+                    return $signature;
+                }
+        }
+    }
+
+    /**
+     * Verify a signature with the message, key and method. Not all methods
+     * are symmetric, so we must have a separate verify and sign method.
+     *
+     * @param string            $msg        The original message (header and body)
+     * @param string            $signature  The original signature
+     * @param string|resource   $key        For HS*, a string key works. for RS*, must be a resource of an openssl public key
+     * @param string            $alg        The algorithm
+     *
+     * @return bool
+     *
+     * @throws DomainException Invalid Algorithm or OpenSSL failure
+     */
+    private static function verify($msg, $signature, $key, $alg)
+    {
+        if (empty(static::$supported_algs[$alg])) {
+            throw new DomainException('Algorithm not supported');
+        }
+
+        list($function, $algorithm) = static::$supported_algs[$alg];
+        switch($function) {
+            case 'openssl':
+                $success = openssl_verify($msg, $signature, $key, $algorithm);
+                if ($success === 1) {
+                    return true;
+                } elseif ($success === 0) {
+                    return false;
+                }
+                // returns 1 on success, 0 on failure, -1 on error.
+                throw new DomainException(
+                    'OpenSSL error: ' . openssl_error_string()
+                );
+            case 'hash_hmac':
+            default:
+                $hash = hash_hmac($algorithm, $msg, $key, true);
+                if (function_exists('hash_equals')) {
+                    return hash_equals($signature, $hash);
+                }
+                $len = min(static::safeStrlen($signature), static::safeStrlen($hash));
+
+                $status = 0;
+                for ($i = 0; $i < $len; $i++) {
+                    $status |= (ord($signature[$i]) ^ ord($hash[$i]));
+                }
+                $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
+
+                return ($status === 0);
+        }
+    }
+
+    /**
+     * Decode a JSON string into a PHP object.
+     *
+     * @param string $input JSON string
+     *
+     * @return object Object representation of JSON string
+     *
+     * @throws DomainException Provided string was invalid JSON
+     */
+    public static function jsonDecode($input)
+    {
+        if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
+            /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
+             * to specify that large ints (like Steam Transaction IDs) should be treated as
+             * strings, rather than the PHP default behaviour of converting them to floats.
+             */
+            $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
+        } else {
+            /** Not all servers will support that, however, so for older versions we must
+             * manually detect large ints in the JSON string and quote them (thus converting
+             *them to strings) before decoding, hence the preg_replace() call.
+             */
+            $max_int_length = strlen((string) PHP_INT_MAX) - 1;
+            $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
+            $obj = json_decode($json_without_bigints);
+        }
+
+        if (function_exists('json_last_error') && $errno = json_last_error()) {
+            static::handleJsonError($errno);
+        } elseif ($obj === null && $input !== 'null') {
+            throw new DomainException('Null result with non-null input');
+        }
+        return $obj;
+    }
+
+    /**
+     * Encode a PHP object into a JSON string.
+     *
+     * @param object|array $input A PHP object or array
+     *
+     * @return string JSON representation of the PHP object or array
+     *
+     * @throws DomainException Provided object could not be encoded to valid JSON
+     */
+    public static function jsonEncode($input)
+    {
+        $json = json_encode($input);
+        if (function_exists('json_last_error') && $errno = json_last_error()) {
+            static::handleJsonError($errno);
+        } elseif ($json === 'null' && $input !== null) {
+            throw new DomainException('Null result with non-null input');
+        }
+        return $json;
+    }
+
+    /**
+     * Decode a string with URL-safe Base64.
+     *
+     * @param string $input A Base64 encoded string
+     *
+     * @return string A decoded string
+     */
+    public static function urlsafeB64Decode($input)
+    {
+        $remainder = strlen($input) % 4;
+        if ($remainder) {
+            $padlen = 4 - $remainder;
+            $input .= str_repeat('=', $padlen);
+        }
+        return base64_decode(strtr($input, '-_', '+/'));
+    }
+
+    /**
+     * Encode a string with URL-safe Base64.
+     *
+     * @param string $input The string you want encoded
+     *
+     * @return string The base64 encode of what you passed in
+     */
+    public static function urlsafeB64Encode($input)
+    {
+        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
+    }
+
+    /**
+     * Helper method to create a JSON error.
+     *
+     * @param int $errno An error number from json_last_error()
+     *
+     * @return void
+     */
+    private static function handleJsonError($errno)
+    {
+        $messages = array(
+            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
+            JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
+            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
+            JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
+            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
+        );
+        throw new DomainException(
+            isset($messages[$errno])
+            ? $messages[$errno]
+            : 'Unknown JSON error: ' . $errno
+        );
+    }
+
+    /**
+     * Get the number of bytes in cryptographic strings.
+     *
+     * @param string
+     *
+     * @return int
+     */
+    private static function safeStrlen($str)
+    {
+        if (function_exists('mb_strlen')) {
+            return mb_strlen($str, '8bit');
+        }
+        return strlen($str);
+    }
+}

+ 215 - 0
Mall/Framework/Core/Logs.Class.php

@@ -0,0 +1,215 @@
+<?php
+namespace Mall\Framework\Core;
+
+class Logs
+{
+    const LEVEL_TRACE          = 'trace';
+    const LEVEL_WARNING        = 'warning';
+    const LEVEL_ERROR          = 'error';
+    const LEVEL_INFO           = 'info';
+    const LEVEL_PROFILE        = 'profile';
+    const MAX_LOGS             = 10000;
+
+    public $rotateByCopy       = true;
+    public $maxLogFiles        = 5;
+    public $maxFileSize        = 100; // in MB
+
+    //系统日志标识
+    private $logSystem       = 'swoole-jobs';
+
+    private $logPath      = '';
+    //单个类型log
+    private $logs                 = [];
+    private $logCount             = 0;
+    //默认log文件存储名
+    private $logSaveFileApp       = 'application.log';
+
+    private static $instance=null;
+
+    public function __construct($logPath, $logSaveFileApp='', $logSystem = '')
+    {
+        if (empty($logPath)) {
+            die('config logPath must be set!' . PHP_EOL);
+        }
+        if (!is_dir($logPath)) {
+            mkdir($logPath, '0777', true);
+        }
+        $this->logPath = $logPath;
+        if ($logSaveFileApp) {
+            $this->logSaveFileApp = $logSaveFileApp;
+        }
+
+        $logSystem && $this->logSystem = $logSystem;
+    }
+
+
+    static public function getInstance($logPath='', $logSaveFileApp='', $logSystem = '')
+    {
+        $key = md5('logs');
+
+        if (isset(self::$instance[$key]) && self::$instance[$key] !== null) {
+            return self::$instance[$key];
+        }
+        self::$instance[$key] = new self($logPath, $logSaveFileApp, $logSystem);
+
+        return self::$instance[$key];
+    }
+
+    /**
+     * 格式化日志信息.
+     *
+     * @param mixed $message
+     * @param mixed $level
+     * @param mixed $category
+     * @param mixed $time
+     */
+    public function formatLogMessage($message, $level, $category, $time)
+    {
+        return @date('Y/m/d H:i:s', $time) . "$this->logSystem [$level] [$category] \n $message \n";
+    }
+
+    /**
+     * 日志分类处理.
+     *
+     * @param mixed $message
+     * @param mixed $level
+     * @param mixed $category
+     * @param mixed $flush
+     */
+    public function log($message, $level = 'info', $category = '', $flush = true)
+    {
+        if( defined('QUEUE_RUNLOG') && !LOG_ERROR ){
+            return;
+        }
+
+        if (empty($category)) {
+            $category=$this->logSaveFileApp;
+        }
+        $this->logs[$category][]      = [$message, $level, $category, microtime(true)];
+        $this->logCount++;
+        if ($this->logCount >= self::MAX_LOGS || true == $flush) {
+            $this->flush($category);
+        }
+    }
+
+    /**
+     * 日志分类处理.
+     */
+    public function processLogs()
+    {
+        $logsAll=[];
+        foreach ((array) $this->logs as $key => $logs) {
+            $logsAll[$key] = '';
+            foreach ((array) $logs as $log) {
+                $logsAll[$key] .= $this->formatLogMessage($log[0], $log[1], $log[2], $log[3]);
+            }
+        }
+
+        return $logsAll;
+    }
+
+    /**
+     * 写日志到文件.
+     */
+    public function flush()
+    {
+        if ($this->logCount <= 0) {
+            return false;
+        }
+        $logsAll = $this->processLogs();
+        $this->write($logsAll);
+        $this->logs     = [];
+        $this->logCount = 0;
+    }
+
+    /**
+     * [write 根据日志类型写到不同的日志文件].
+     *
+     * @param $logsAll
+     *
+     * @throws \Exception
+     */
+    public function write($logsAll)
+    {
+        if (empty($logsAll)) {
+            return;
+        }
+        //$this->logPath = ROOT_PATH . 'src/runtime/';
+        if (!is_dir($this->logPath)) {
+            self::mkdir($this->logPath, [], true);
+        }
+        foreach ($logsAll as $key => $value) {
+            if (empty($key)) {
+                continue;
+            }
+            $fileName = $this->logPath . '/' . $key;
+
+            if (($fp = @fopen($fileName, 'a')) === false) {
+                throw new \Exception("Unable to append to log file: {$fileName}");
+            }
+            @flock($fp, LOCK_EX);
+
+            if (@filesize($fileName) > $this->maxFileSize * 1024 * 1024) {
+                $this->rotateFiles($fileName);
+            }
+            @fwrite($fp, $value);
+            @flock($fp, LOCK_UN);
+            @fclose($fp);
+        }
+    }
+
+    /**
+     * Rotates log files.
+     *
+     * @param mixed $file
+     */
+    protected function rotateFiles($file)
+    {
+        for ($i = $this->maxLogFiles; $i >= 0; --$i) {
+            // $i == 0 is the original log file
+            $rotateFile = $file . ($i === 0 ? '' : '.' . $i);
+            //var_dump($rotateFile);
+            if (is_file($rotateFile)) {
+                // suppress errors because it's possible multiple processes enter into this section
+                if ($i === $this->maxLogFiles) {
+                    @unlink($rotateFile);
+                } else {
+                    if ($this->rotateByCopy) {
+                        @copy($rotateFile, $file . '.' . ($i + 1));
+                        if ($fp = @fopen($rotateFile, 'a')) {
+                            @ftruncate($fp, 0);
+                            @fclose($fp);
+                        }
+                    } else {
+                        @rename($rotateFile, $file . '.' . ($i + 1));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Shared environment safe version of mkdir. Supports recursive creation.
+     * For avoidance of umask side-effects chmod is used.
+     *
+     * @param string $dst       path to be created
+     * @param array  $options   newDirMode element used, must contain access bitmask
+     * @param bool   $recursive whether to create directory structure recursive if parent dirs do not exist
+     *
+     * @return bool result of mkdir
+     *
+     * @see mkdir
+     */
+    private static function mkdir($dst, array $options, $recursive)
+    {
+        $prevDir = dirname($dst);
+        if ($recursive && !is_dir($dst) && !is_dir($prevDir)) {
+            self::mkdir(dirname($dst), $options, true);
+        }
+        $mode = isset($options['newDirMode']) ? $options['newDirMode'] : 0777;
+        $res  = mkdir($dst, $mode, $recursive);
+        @chmod($dst, $mode);
+
+        return $res;
+    }
+}

+ 105 - 0
Mall/Framework/Core/RedisQueue.Class.php

@@ -0,0 +1,105 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Cache\Redis;
+
+class RedisQueue
+{
+    public static $prefix = 'queue::';
+    public static $queueName = 'public_queue';
+
+    protected static $_instance;
+
+    /** @var Redis $redis */
+    protected static $redis;
+
+    /**
+     * @param Redis $redis      redis实例
+     * @param string $queueName 队列名称
+     *
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    public static function getInstance(Redis $redis ,$queueName = 'public_queue')
+    {
+        if ($redis instanceof \Redis) {
+            throw new \Exception('Redis driver not null');
+        }
+
+        self::$redis = $redis;
+        self::setQueueName($queueName);
+
+        $key = md5(self::$queueName);
+        if (!isset(self::$_instance[$key])) {
+            self::$_instance[$key] = new self();
+        }
+
+        return self::$_instance[$key];
+    }
+
+    public static function setQueueName($queueName)
+    {
+        if ($queueName) {
+            self::$queueName = self::$prefix . $queueName;
+        }
+    }
+
+    /**
+     * 添加队列
+     *
+     * @param string $value 存储的队列
+     *
+     * @return bool
+     */
+    public static function push($value)
+    {
+        return self::$redis->push(self::$queueName, $value, 'start');
+    }
+
+    /**
+     * 从队列取出数据
+     *
+     * @param int $number 数量
+     *
+     * @return array
+     */
+    public static function pop($number = 1)
+    {
+        $result = [];
+
+        for ($i = 0; $i < $number; $i++) {
+            if ($value = self::$redis->pop(self::$queueName, 'end')) {
+                $result[] = $value;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 删除队列
+     */
+    public static function flushQueue()
+    {
+        return self::$redis->delete(self::$queueName);
+    }
+
+
+    /**
+     * 返回队列的长度
+     */
+    public function llen()
+    {
+        return self::$redis->llen(self::$queueName);
+    }
+
+    /**
+     * 查看队列的 key 是否存在
+     */
+    public function has()
+    {
+        return self::$redis->has(self::$queueName);
+    }
+}

+ 407 - 0
Mall/Framework/Core/Request.Class.php

@@ -0,0 +1,407 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Request
+{
+    /**
+     *
+     * 存放当前请求的参数
+     * @var array
+     */
+    protected $params;
+
+    static protected $instance;
+
+    private function __construct()
+    {
+    }
+
+    static public function getInstance()
+    {
+        if (is_null(self::$instance)) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * 获取请求的参数集。
+     * 支持http访问的参数 以及 命令行下访问的参数
+     * demo1:php test.php -p3 -t=abc --opt=valopt --opt2 valopt2
+     * demo2 http://test.kimiss.com/index.php?c=xxx&a=ddd
+     *
+     * @param boolean $returnRawJson 是否将json参数作为独立key(rawjson)返回
+     * @throws
+     * @return array
+     */
+    public static function params($returnRawJson = false)
+    {
+        if (self::isCLI()) {
+            return self::cliParams();
+        } else if( strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== FALSE || strpos($_SERVER['CONTENT_TYPE'], 'text/plain') !== FALSE || strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== FALSE){
+            $request_body = file_get_contents("php://input");
+            if(!empty($request_body)){
+                $request_body = json_decode($request_body, true);
+                if(!$request_body){
+                    throw new \Exception("参数格式异常");
+                }
+                if($returnRawJson){
+                    return array_merge($_REQUEST, ['rawjson'=> $request_body]);
+                }
+
+                return array_merge($_REQUEST, $request_body);
+            }
+            return $_REQUEST;
+        } else{
+            return $_REQUEST;
+        }
+    }
+
+    /**
+     *
+     * 获取命令行下传递进来的参数
+     * 只支持以 - 或 -- 开头的参数
+     * demo:php test.php -p3 -t=abc --opt=valopt --opt2 valopt2
+     * @return array
+     */
+    private static function cliParams()
+    {
+        $result = array();
+        $params = $GLOBALS['argv'];
+
+        array_shift($params);
+        do {
+            $tmpEachResult = array_shift($params);
+            if (!$tmpEachResult) {
+                break;
+            }
+            $p = $tmpEachResult;
+            if ($p[0] == '-') {
+                $pname = substr($p, 1);
+                $value = false;
+                if ($pname[0] == '-') {// 长选项 (--<param>)
+                    $pname = substr($pname, 1);
+                    if (strpos($p, '=') !== false) {
+                        // value specified inline (--<param>=<value>)
+                        list($pname, $value) = explode('=', substr($p, 2), 2);
+                    }
+                } else {// 短选项
+                    if (strpos($p, '=') !== false) {
+                        // value specified inline (-<param>=<value>)
+                        list($pname, $value) = explode('=', substr($p, 1), 2);
+                    } else if (strlen($p) > 1) {
+                        $pname = substr($p, 1, 1);
+                        $value = substr($p, 2);
+                    }
+                }
+                # 如果上面没有取到值,并且下一个不是以-开头的,则下一个值为当前参数的值
+                $nextparm = current($params);
+                if ($value === false
+                    && $nextparm !== false
+                    && $nextparm[0] != '-'
+                ) {
+                    $value = array_shift($params);
+                }
+                $result[$pname] = (string)$value;// 将 false转为空串,以便与http访问时对参数的处理一致
+            } else {
+                # 不是以-指定开始的参数,一律丢弃
+                //$result[] = $p;
+            }
+        } while (true);
+
+        return $result;
+    }
+
+
+    /**
+     * 分发请求,调用Controller以及对应的action
+     *
+     * @param boolean $enableNginxRwite  是否启用nginx rewrite功能支持pathinfo访问方式
+     * @param boolean $enableModule 是否启用控制器分模块方式
+     * @throws
+     * @return object
+     */
+    public function dispatch($enableNginxRwite = true, $enableModule = false)
+    {
+
+        // 如果未启用nginx rewrite,通过此处简单路由功能匹配
+        if( !$enableNginxRwite && !self::isCLI()){
+            $requestUrl = $_SERVER['REQUEST_URI'];
+            if(strpos($requestUrl, '?') !== false){
+                $requestUrl = substr($requestUrl, 0, strpos($requestUrl, '?'));
+            }
+            if(!empty($requestUrl)){
+                $requestUrl = explode('/', $requestUrl);
+
+                if(!empty($requestUrl)){
+                    if($enableModule){
+                        $_REQUEST['m'] = $requestUrl[1];
+                        $_REQUEST['c'] = $requestUrl[2];
+                        $_REQUEST['a'] = $requestUrl[3];
+                        if(isset($requestUrl[4])){
+                            $_REQUEST['request_id'] = intval($requestUrl[4]);
+                        }
+                    }else{
+                        $_REQUEST['c'] = $requestUrl[1];
+                        $_REQUEST['a'] = $requestUrl[2];
+                        if(isset($requestUrl[3])){
+                            $_REQUEST['request_id'] = intval($requestUrl[3]);
+                        }
+                    }
+                }
+            }
+        }
+
+        self::$instance->params = self::params();
+
+        $controllerName = self::getController($enableModule);
+        if (!class_exists($controllerName)) {
+            throw new \Exception("controller: {$controllerName} not exists!");
+        }
+
+        $action = self::getAction();
+        $controller = new $controllerName();
+        $controller->$action();
+//
+//        if (method_exists($controller, 'display')) {
+//            $controller->display();
+//        }
+    }
+
+    public function getModule()
+    {
+        $m = self::param('m') ?: DEFAULT_CLASS_MODULE_NAME;
+        return ucfirst(trim($m, '_'));
+    }
+
+    public function getModuleType()
+    {
+        $t = self::param('t') ?: '';
+        if ($t) {
+            return ucfirst(trim($t, '_'));
+        }
+    }
+
+    public function getController($enableModule)
+    {
+        $c = self::param('c');
+        $c = trim($c, '_');
+        //默认给一个控制器名
+        if (!$c) {
+            $c = DEFAULT_CLASS_CONTROLLER_NAME;
+        }
+
+        $arr_class_path = array_map(function ($tmpV) {
+            return ucfirst($tmpV);
+        }, explode('_', $c));
+        $c = join('\\', $arr_class_path);
+        $NS = Config::getInstance()->get('app_name');
+        $controller = "{$NS}\\Controller\\";
+        if ($t = self::getModuleType()) {
+            $controller .= $t . "\\";
+        }
+
+        if (($m = self::getModule()) && $enableModule) {
+            $controller .= $m . "\\";
+        }
+        $controller .= "{$c}";
+        return $controller;
+    }
+
+    public function getAction()
+    {
+        $action = self::param('a');
+        //默认给一个方法名
+        if (!$action) {
+            $action = DEFAULT_CLASS_ACTION_NAME;
+        }
+        return $action;
+    }
+
+    /**
+     * 获取指定的参数值
+     * @param $name
+     * @return array|bool|string
+     */
+    public function param($name)
+    {
+        if(empty($this->params)){
+            $this->params = self::params();
+        }
+
+        if (isset($this->params[$name])) {
+            return self::paramFilter($this->params[$name]);
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取raw中json格式参数值
+     */
+    public function getRawJson()
+    {
+        if(empty($this->params['rawjson'])){
+            $this->params = self::params(true);
+        }
+
+        if (isset($this->params['rawjson'])) {
+            return self::paramFilter($this->params['rawjson']);
+        }
+
+        return false;
+    }
+
+    /**
+     * 是否POST请求
+     *
+     * @return boolean
+     */
+    static public function isPost()
+    {
+        return $_SERVER['REQUEST_METHOD'] == 'POST' ? true : false;
+    }
+
+    /**
+     * 是否GET请求
+     *
+     * @return boolean
+     */
+    static public function isGet()
+    {
+        return $_SERVER['REQUEST_METHOD'] == 'GET' ? true : false;
+    }
+
+    /**
+     * 是否处于命令行下
+     *
+     * @return Boolean
+     */
+    static public function isCLI()
+    {
+        if (php_sapi_name() == "cli" || empty($_SERVER['PHP_SELF'])) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 获取客户端真实Ip
+     * @return string
+     */
+    static public function get_onlineip()
+    {
+        $onlineip = '';
+
+        if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
+            $onlineip = getenv('HTTP_CLIENT_IP');
+        } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
+            $onlineip = getenv('HTTP_X_FORWARDED_FOR');
+        } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
+            $onlineip = getenv('REMOTE_ADDR');
+        } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
+            $onlineip = $_SERVER['REMOTE_ADDR'];
+        }
+
+        return $onlineip;
+    }
+
+
+    /**
+     * 获取$_SERVER下面的方法
+     */
+    function getServerParam($param)
+    {
+        if (isset($_SERVER[$param])) {
+            return self::paramFilter($_SERVER[$param]);
+        }
+
+        return '';
+    }
+
+    /**
+     * 全局的接收参数过滤
+     */
+    private function paramFilter($param)
+    {
+        if (is_array($param)) {
+            foreach ($param as $key => $value) {
+                if(is_array($value)){
+                    self::paramFilter($value);
+                    continue;
+                }
+                if(!is_numeric($value)){
+                    $param[$key] = htmlspecialchars(trim($value), ENT_QUOTES, 'UTF-8');
+                }
+            }
+        } else if (!empty($param) && !is_numeric($param)) {
+            $param = htmlspecialchars(trim($param), ENT_QUOTES, 'UTF-8');
+        }
+
+        return $param;
+    }
+
+    /**
+     * 获取上传的文件信息
+     *
+     * @access public
+     * @param string|array $name 名称
+     *
+     * @return null|array| \Mall\Framework\Core\File
+     */
+    public function file($name = '')
+    {
+        $files = isset($_FILES) ? $_FILES : [];
+        if (is_array($name)) {
+            return $files = array_merge($files, $name);
+        }
+        if (!empty($files)) {
+            // 处理上传文件
+            $array = [];
+            foreach ($files as $key => $file) {
+                if (is_array($file['name'])) {
+                    $item = [];
+                    $keys = array_keys($file);
+                    $count = count($file['name']);
+                    for ($i = 0; $i < $count; $i++) {
+                        if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) {
+                            continue;
+                        }
+                        $temp['key'] = $key;
+                        foreach ($keys as $_key) {
+                            $temp[$_key] = $file[$_key][$i];
+                        }
+                        $item[] = (new File($temp['tmp_name']))->setUploadInfo($temp);
+                    }
+                    $array[$key] = $item;
+                } else {
+                    if ($file instanceof File) {
+                        $array[$key] = $file;
+                    } else {
+                        if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) {
+                            continue;
+                        }
+                        $array[$key] = (new File($file['tmp_name']))->setUploadInfo($file);
+                    }
+                }
+            }
+            if (strpos($name, '.')) {
+                list($name, $sub) = explode('.', $name);
+            }
+            if ('' === $name) {
+                // 获取全部文件
+                return $array;
+            } elseif (isset($sub) && isset($array[$name][$sub])) {
+                return $array[$name][$sub];
+            } elseif (isset($array[$name])) {
+                return $array[$name];
+            }
+        }
+
+        return;
+    }
+
+}

+ 93 - 0
Mall/Framework/Core/ResultWrapper.Class.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+/**
+ * 内部结果传递类
+ * 目的:标准化方法执行后返回值的表述。
+ *
+ */
+class ResultWrapper
+{
+    /** @var Boolean $status 存放处理状态 */
+    private $status;
+
+    /** @var mixed $data 存放处理状态 */
+    private $data;
+
+    /** @var int $errorCode 错误编码 */
+    private $errorCode;
+
+    /**
+     * 构造函数私有,这个类不允许从外部实例化
+     */
+    private function __construct()
+    {
+    }
+
+
+    /**
+     * 表示处理成功
+     *
+     * @param mixed $data
+     *
+     * @return ResultWrapper
+     */
+    public static function success($data = null)
+    {
+        $objResultWrapper = new self();
+
+        $objResultWrapper->status = true;
+        $objResultWrapper->data = $data;
+
+        return $objResultWrapper;
+    }
+
+    /**
+     * 表示处理失败
+     *
+     * @param mixed $data
+     *
+     * @return ResultWrapper
+     */
+    public static function fail($data = null, $errorCode)
+    {
+        $objResultWrapper = new self();
+
+        $objResultWrapper->status = false;
+        $objResultWrapper->data = $data;
+        $objResultWrapper->errorCode = $errorCode;
+
+        return $objResultWrapper;
+    }
+
+    /**
+     * 判断处理是否成功
+     *
+     * @return Boolean
+     */
+    public function isSuccess()
+    {
+        return $this->status === true;
+    }
+
+    /**
+     * 获取数据
+     * 
+     * @return mixed
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * 获取错误编码
+     *
+     * @return int
+     */
+    public function getErrorCode()
+    {
+        return $this->errorCode;
+    }
+}

+ 300 - 0
Mall/Framework/Core/Search.Class.php

@@ -0,0 +1,300 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\SearchClient\Client;
+use Mall\Framework\SearchClient\Transport;
+use Exception;
+
+class Search
+{
+    /**
+     * @var array
+     */
+    private $config;
+
+    /**
+     * SearchClient 客户端
+     *
+     * @var Client
+     */
+    private $connection = null;
+
+    /**
+     * @var int
+     */
+    private $serverId;
+
+    /**
+     * @var array
+     */
+    private static $badServerIds = array();
+
+    /**
+     * @var object
+     */
+    private static $_instance;
+
+    /**
+     * 构造函数
+     *
+     * @param $config
+     */
+    public function __construct(array $config)
+    {
+        $this->config = $config;
+    }
+
+    /**
+     * 从服务器组中获取一个可用的ES服务器信息
+     *
+     * @return array
+     * @throws Exception
+     */
+    private function getServer()
+    {
+        $config = $this->config;
+
+        $serverId = null;
+        if (empty(self::$badServerIds)) {
+            $serverId = array_rand($config['servers']);
+        } else {
+            foreach ($config['servers'] as $id => $val) {
+                if (in_array($id, self::$badServerIds)) {
+                    continue;
+                }
+                $serverId = $id;
+                break;
+            }
+        }
+
+        if ($serverId === null) {
+            throw new Exception('服务暂时不可用,请稍后再试!');
+        }
+
+        $this->serverId = $serverId;
+
+        return array(
+            'protocol' => $config['protocol'],
+            'timeout' => $config['timeout'],
+            'index' => $config['index'],
+            'type' => $config['type'],
+            'servers' => $config['servers'][$serverId]
+        );
+    }
+
+    /**
+     * 设置Index索引
+     *
+     * @param $index
+     * @return Search|null
+     */
+    public function setIndex($index)
+    {
+        $this->config['index'] = $index;
+        return is_object($this->getConnection()->setIndex($index)) ? $this : null;
+    }
+
+    /**
+     * 设置Type
+     *
+     * @param $type
+     * @return Search|null
+     */
+    public function setType($type)
+    {
+        $this->config['type'] = $type;
+        return is_object($this->getConnection()->setType($type)) ? $this : null;
+    }
+
+    /**
+     * 创建Index索引
+     *
+     * @param string $index 索引名称
+     * @param array $mappings options
+     * @return mixed
+     */
+    public function createBase($index, $mappings)
+    {
+        return $this->getConnection()->createBase($index, $mappings);
+    }
+
+    /**
+     * 删除Index索引
+     *
+     * @param string $index 索引名称
+     * @return mixed
+     */
+    public function deleteBase($index)
+    {
+        return $this->getConnection()->deleteBase($index);
+    }
+
+    /**
+     * 根据 ID 获取文档
+     *
+     * @param $id
+     * @param bool|false $verbose
+     * @return array
+     */
+    public function get($id, $verbose = false)
+    {
+        $this->checkIndexType();
+
+        return $this->getConnection()->get($id, $verbose);
+    }
+
+    /**
+     * 创建索引
+     *
+     * @param string $document 文档格式
+     * @param string $id 文档ID
+     * @param array $options 自定义选项
+     * @return mixed
+     */
+    public function index($document, $id, $options = array())
+    {
+        $this->checkIndexType();
+
+        $this->log('index', sprintf(
+            'domain:[%s] uri:[%s] contentid:[%s] %s %s',
+            empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
+            empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
+            $id,
+            var_export($document, true),
+            var_export($options, true)
+        ));
+
+        return $this->getConnection()->index($document, $id, $options);
+    }
+
+    /**
+     * 更新索引
+     *
+     * @param string $document 文档
+     * @param string $id 文档ID
+     * @param array $options 自定义选项
+     * @return mixed
+     */
+    public function update($document, $id, $options = array())
+    {
+        $this->checkIndexType();
+
+        $this->log('update', sprintf(
+            'domain:[%s] uri:[%s] contentid:[%s] %s %s',
+            empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
+            empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
+            $id,
+            var_export($document, true),
+            var_export($options, true)
+        ));
+
+        return $this->getConnection()->index($document, $id, $options);
+    }
+
+    /**
+     * 删除索引
+     *
+     * @param string $id 索引ID
+     * @return null
+     */
+    public function delete($id)
+    {
+        if (!$id) return null;
+
+        $this->checkIndexType();
+
+        $this->log('delete', sprintf(
+            'domain:[%s] uri:[%s] contentid:[%s]',
+            empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
+            empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
+            $id
+        ));
+
+        return $this->getConnection()->delete($id);
+    }
+
+    /**
+     * 搜索接口
+     *
+     * @param array $query DSL查询格式
+     * @param array $options 选项
+     * @return array
+     * @throws Exception
+     */
+    public function search($query = array(), array $options = array())
+    {
+        try {
+            $client = $this->getConnection()->search($query, $options);
+           
+            return empty($client) ? array() : $client;
+        } catch (Transport\HTTPException $e) {
+            self::$badServerIds[] = $this->serverId;
+            $this->connection = Client::connection($this->getServer());
+            return $this->search($query, $options);
+        }
+    }
+
+    /**
+     * 搜索服务链接
+     *
+     * @return Client
+     * @throws Exception
+     */
+    public function getConnection()
+    {
+        $key = md5(json_encode($this->config));
+
+        if (!isset(self::$_instance[$key]) || !self::$_instance[$key] instanceof Client) {
+            if ($this->connection) {
+                return $this->connection;
+            }
+            $connection = Client::connection($this->getServer());
+            $connection->setIndex($this->config['index']);
+            $connection->setType($this->config['type']);
+
+            self::$_instance[$key] = $this->connection = $connection;
+        }
+
+        return self::$_instance[$key];
+    }
+
+    private function checkIndexType()
+    {
+        $connection = $this->getConnection();
+
+        $index = $connection->getIndex();
+        if (empty($index)) {
+            throw new \RuntimeException('Invalid search index');
+        }
+
+        $type = $connection->getType();
+        if (empty($type)) {
+            throw new \RuntimeException('Invalid search index type');
+        }
+
+        return true;
+    }
+
+    protected function log($type, $message)
+    {
+        if (!defined('DEBUG_MODE') || DEBUG_MODE != 1) {
+            return;
+        }
+
+        $temp_dir = sys_get_temp_dir();
+        if (!is_writable($temp_dir)) {
+            return;
+        }
+
+        $log_file = sprintf('%s/search_%s.log', $temp_dir, $type);
+        $log = sprintf(
+            '%s index[%s] type[%s] %s',
+            date('r'),
+            $this->config['index'],
+            $this->config['type'],
+            $message
+        );
+        file_put_contents($log_file, $log . PHP_EOL, FILE_APPEND);
+    }
+}

+ 148 - 0
Mall/Framework/Core/SendMail.Class.php

@@ -0,0 +1,148 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Mail\Mail;
+
+class SendMail {
+
+    private static $_instance;
+
+    protected $error;
+
+    static public function getInstance()
+    {
+        $key = md5('sendmail');
+
+        if (!self::$_instance[$key] instanceof self) {
+            self::$_instance[$key] = new self;
+        }
+
+        return self::$_instance[$key];
+    }
+
+    public function mail($title, $email, $html, $sign=null)
+    {
+        $key = md5('mail');
+
+        if (!self::$_instance[$key] instanceof self) {
+            $options = Config::getInstance()->get('mail');
+            self::$_instance[$key] = new Mail($options['mailer'], $options['delimiter'], $options['charset'], $options['from'], $options['sign'], $options['smtp_host'], $options['smtp_port'], $options['smtp_auth'], $options['smtp_username'], $options['smtp_password']);
+        }
+
+        self::$_instance[$key]->execute($email, $title, $html);
+        if ($this->error = self::$_instance[$key]->error()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * 邮箱验证码
+     * @param $nickname
+     * @param $code
+     * @return string
+     */
+    public function emailCode($nickname, $code)
+    {
+        return "
+             <div style='width:700px;height:240px;padding:0 30px;color:#444;font-size:15px;padding-top:20px;'>
+                <p style='font-size: 16px;'>
+                    亲爱的{$nickname}:
+                </p>
+                <div style='width:630px;margin-top:20px;color:#444;word-wrap:break-word;'>
+                    <p style='margin-top:20px;'>您的验证码为:{$code}</p>
+                </div>
+            </div>
+        ";
+    }
+
+    /**
+     * 重置密码
+     * @param $nickname
+     * @param $url
+     * @return string
+     */
+    public function userResetPw($nickname, $url)
+    {
+        return "
+             <div style='width:700px;height:240px;padding:0 30px;color:#444;font-size:15px;padding-top:10px;'>
+                <h4 style='font-size: 15px; padding:0; margin:0;'>亲爱的 {$nickname},您好:</h4>
+                <p style='font-size: 14px; margin: 3px 0; padding-left:30px;'>您的账号正在进行找回密码操作!</p>
+                <div style='width:630px;margin-top:20px; padding-left:30px;color:#444;word-wrap:break-word;'>
+                    <p style='text-align: center;width:630px;'>
+                        <a href='" . $url . "' style='margin-left:220px;padding: 6px; color:white;text-decoration: none;width:130px;height:30px;display:block;background: #65A8FF;border-radius:10px;'>
+                            <span style='vertical-align: middle;'>点击重置密码</span>
+                        </a>
+                    </p>
+                    <div style='border-left: 5px solid #007AFF;padding-left:10px; font-size: 14px;'>
+                        <p style='margin:0; padding:0;'>如果您点击上述链接无效,请将下面的链接复制到浏览器地址栏中访问:</p>
+                        <p>{$url}</p>
+                    </div>
+                    <p style='color:grey;margin:0;'>
+                        <span>为保障您的账号安全性,以上链接有效期为 3 天。</span><br/>
+                    </p>
+                </div>
+            </div>
+        ";
+    }
+
+    public function html($content, $sitename, $info)
+    {
+        $logo = $logo = PROJECT_IMG_DOMAIN . "assets/mail/img/logo.png";
+        $copyright = $copyright = "Copyright © 2017 北京猫逛科技有限公司";
+        $sitename = $sitename ?: '北京猫逛科技有限公司';
+
+
+        return
+            "<html xmlns='http://www.w3.org/1999/xhtml'>
+            <head>
+                <meta http-equiv='Content-Type' content='text/html; charset=3DUTF-8'/>
+                <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
+                <title>{$sitename}</title>
+            </head>
+            <body style='margin:0;padding:0;'>
+            <table width='900' cellpadding='0' cellspacing='0' border='0'>
+                <tr>
+                    <td style='width:900px;background-color:#f5f3f0;'>
+                        <div style='padding:50px 100px;'>
+                            <div style='width:700px;min-height:600px; _height:600px;background-color:#fff;font-family:微软雅黑;overflow:hidden;'>
+                                <div style='width:600px;height:80px;'>
+                                    <div style='float:left;'>
+                                        <img src='{$logo}' width='245' height='30' style='padding:25px 0 0 30px;' />
+                                    </div>
+                                    <div style='padding:32px 0 0 280px;'>
+                                        <span>这里是占位符</span>
+                                    </div>
+                                </div>
+                                <div style='width:700px;height:200px;padding:0 30px;'>
+                                    <div style='width:640px;height:200px;background-image:url(http://www{$domainSuffix}/assets/mail/img/mail.png);'>
+                                       <div style='width:545px;height:200px;padding-top:70px;margin-left:95px;overflow:hidden;'>
+                                            <span style='padding-left:130px;font-size:32px;color:#fff;font-weight:bold;white-space:nowrap;'>{$sitename}</span>
+            <br />
+            <span style='padding-left:132px;font-size:18px;color:#feebeb;'>{$info}</span>
+            </div>
+            </div>
+            </div>
+            {$content}
+            <div style='width:700px;height:30px;padding-left:500px;margin-top:20px;;color:#444;'>
+                <span>{$sitename}团队</span>
+            </div>
+            </div>
+            <div style='text-align:center;color:#444;font-size:12px;font-family:微软雅黑;margin-top:20px;padding-top:20px;'>
+                {$copyright}
+            </div>
+            </div>
+            </td>
+            </tr>
+            </table>
+            </body>
+            </html>";
+    }
+}

+ 73 - 0
Mall/Framework/Core/SendSms.Class.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Core\ResultWrapper;
+use Mall\Framework\SearchClient\Exception;
+
+class SendSms {
+
+    private static $_instance;
+
+    protected $error;
+
+    static public function getInstance()
+    {
+        $key = md5('sendSms');
+
+        if (!self::$_instance[$key] instanceof self) {
+            self::$_instance[$key] = new self;
+        }
+
+        return self::$_instance[$key];
+    }
+
+
+    /**
+     * 发送短信消息方法
+     * 官方文档地址: https://help.aliyun.com/document_detail/55451.html?spm=5176.doc55288.6.556.rZK9dj
+     * 业务限流       https://help.aliyun.com/knowledge_detail/57710.html?spm=5176.doc55451.6.583.sHFzrz
+     *
+     * @param string $mobile 接收短信的手机号
+     * @param string $signname  短信签名
+     * @param string $templatecode  短信模板编号
+     * @param string $templateparam 短信模板替换变量
+     * @param string $source        发送短信业务来源
+     */
+    public function send($mobile, $signname, $templatecode, $templateparam, $source = '')
+    {
+        $options = Config::getInstance()->get('sms');
+
+        if(empty($options)){
+            throw  new \ErrorException('短信配置错误');
+        }
+
+        $post = [
+            'phonenumbers' => $mobile,
+            'signname' => $signname,
+            'templatecode' => $templatecode,
+            'templateparam' => $templateparam,
+        ];
+        $result = request($options['api_url'], $post);
+        if( $result['httpcode'] == 200 && !empty($result['content'])){
+            $resultContent = json_decode($result['content'],true);
+            if($resultContent['state']){
+                return ResultWrapper::success('发送成功');
+            }else{
+                return ResultWrapper::fail($resultContent['data'], ErrorCode::$apiNotResult);
+            }
+        }else{
+            if(!empty($result['content'])){
+                file_put_contents('/tmp/sendSms.log',date('Y-m-d H:i:s').var_export($result['content'],true).PHP_EOL,FILE_APPEND);
+                return ResultWrapper::fail('请求接口失败,错误原因请留意日志', ErrorCode::$apiNotResult);
+            }else{
+                return ResultWrapper::fail('发送失败,接口未返回消息', ErrorCode::$apiNotResult);
+            }
+        }
+    }
+
+    public function getError()
+    {
+        return $this->error;
+    }
+}

+ 180 - 0
Mall/Framework/Core/Session.Class.php

@@ -0,0 +1,180 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Session\SaveHandler\SaveHandlerInterface;
+use Mall\Framework\Session\SaveHandler\RedisSession;
+
+class Session
+{
+    private static $_instance;
+
+    protected static $defaultOptions = array(
+        'save_path'               => null,
+        'name'                    => null, /* this should be set to a unique value for each application */
+        'save_handler'            => null,
+        //'auto_start'            => null, /* intentionally excluded (see manual) */
+        'gc_probability'          => null,
+        'gc_divisor'              => null,
+        'gc_maxlifetime'          => null,
+        'serialize_handler'       => null,
+        'cookie_lifetime'         => null,
+        'cookie_path'             => null,
+        'cookie_domain'           => null,
+        'cookie_secure'           => null,
+        'cookie_httponly'         => null,
+        'use_cookies'             => null,
+        'use_only_cookies'        => 'on',
+        'referer_check'           => null,
+        'entropy_file'            => null,
+        'entropy_length'          => null,
+        'cache_limiter'           => null,
+        'cache_expire'            => null,
+        'use_trans_sid'           => null,
+        'bug_compat_42'           => null,
+        'bug_compat_warn'         => null,
+        'hash_function'           => null,
+        'hash_bits_per_character' => null
+    );
+
+    static public function getInstance()
+    {
+        $key = 'SessionRedis';
+
+        if (!isset(self::$_instance[$key])) {
+            $obj = new self();
+            $obj->connect();
+
+            self::$_instance[$key] = $obj;
+        }
+
+        return self::$_instance[$key];
+    }
+
+    public function connect()
+    {
+        $saveHandler = new RedisSession();
+
+        $this->registerSaveHandler($saveHandler);
+
+        session_start();
+    }
+
+    public static function setOptions($options)
+    {
+        if (!is_array($options)) {
+            throw new \Exception(sprintf(
+                'Parameter provided to %s must be an array or Traversable',
+                __METHOD__
+            ));
+        }
+
+        // set the options the user has requested to set
+        foreach ($options as $name => $value) {
+
+            $name = strtolower($name);
+
+            // set the ini based values
+            if (array_key_exists($name, self::$defaultOptions)) {
+                ini_set("session.$name", $value);
+            }
+        }
+    }
+
+    public function set($name, $value)
+    {
+        if (!self::sessionExists()) {
+            return FALSE;
+        }
+
+        $_SESSION[$name] = $value;
+    }
+
+    public function get($name)
+    {
+        if (!self::sessionExists()) {
+            return FALSE;
+        }
+
+        if (isset($_SESSION[$name])) {
+
+            return $_SESSION[$name];
+
+        }
+
+        return FALSE;
+    }
+
+    /**
+     * Does a session exist and is it currently active?
+     *
+     * @return bool
+     */
+    public static function sessionExists()
+    {
+        $sid = defined('SID') ? constant('SID') : false;
+        if ($sid !== false && self::getId()) {
+            return true;
+        }
+        if (headers_sent()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Set session ID
+     *
+     * @param  string $id
+     */
+    public static function setId($id)
+    {
+        if (self::sessionExists()) {
+            throw new \Exception (
+                'Session has already been started, to change the session ID call regenerateId()'
+            );
+        }
+
+        session_id($id);
+    }
+
+    /**
+     * Get session ID
+     *
+     * @return string
+     */
+    public static function getId()
+    {
+        return session_id();
+    }
+
+    /**
+     * session destory
+     */
+    public function destory()
+    {
+        if (!self::sessionExists()) {
+            return FALSE;
+        }
+
+        session_destroy();
+    }
+
+    /**
+	 * Register Save Handler with ext/session
+	 *
+	 * @param Mall\Framework\SaveHandler\SaveHandlerInterface $saveHandler
+	 * @return bool
+	 */
+	protected static function registerSaveHandler(SaveHandlerInterface $saveHandler)
+	{
+		return session_set_save_handler(
+			array($saveHandler, 'open'),
+			array($saveHandler, 'close'),
+			array($saveHandler, 'read'),
+			array($saveHandler, 'write'),
+			array($saveHandler, 'destroy'),
+			array($saveHandler, 'gc')
+		);
+	}
+}

+ 1070 - 0
Mall/Framework/Core/SqlHelper.Class.php

@@ -0,0 +1,1070 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Factory;
+
+abstract class SqlHelper
+{
+    const FETCH_ASSOC = \PDO::FETCH_ASSOC;
+    const FETCH_OBJ = \PDO::FETCH_OBJ;
+
+    /**
+     * 数据库连接
+     *
+     * @var db
+     */
+    protected $db;
+
+    /**
+     * 数据库名称
+     *
+     * @var string
+     */
+    protected $_table = null;
+
+    /**
+     * 数据库查询主键
+     *
+     * @var string
+     */
+    protected $_primary = null;
+
+    /**
+     * 数据表字段
+     *
+     * @var array
+     */
+    protected $_fields = array();
+
+    /**
+     * 只读字段
+     *
+     * @var array
+     */
+    protected $_readonly = array();
+
+    /**
+     * 自动填充字段定义
+     *
+     * @var array
+     */
+    protected $_create_autofill = array();
+
+    /**
+     * 自动更新字段定义
+     *
+     * @var array
+     */
+    protected $_update_autofill = array();
+
+    /**
+     * 自动过滤字段定义
+     *
+     * @var array
+     */
+    protected $_filters_input = array();
+
+    /**
+     * 自动过滤字段定义
+     *
+     * @var array
+     */
+    protected $_filters_output = array();
+
+    /**
+     * 插入时,字段验证定义
+     *
+     * @var array
+     */
+    protected $_validators = array();
+
+    /**
+     * 插入时,准备好的数据
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * select 查询join where limit 等定义
+     *
+     * @var array
+     */
+    protected $_options = array();
+
+    /**
+     * 数据获取方式
+     *
+     * @var int
+     */
+    protected $_fetch_style = self::FETCH_ASSOC;
+
+    /**
+     * 用户ID
+     *
+     * @var int
+     */
+    public $_userid = null;
+
+    /**
+     * 用户名称
+     *
+     * @var string
+     */
+    public $_username = null;
+
+    /**
+     * 用户组ID
+     *
+     * @var int
+     */
+    public $_groupid = null;
+
+    /**
+     * 用户角色ID
+     *
+     * @var int
+     */
+    public $_roleid = null;
+
+    /**
+     * 错误信息
+     *
+     * @var mixed
+     */
+    public $error = null;
+
+    /**
+     * mysql 操作符
+     * @var array
+     */
+    public static $operator = ['=','!=','>=','<=','like','in','>','<'];
+
+    /**
+     * 初始化数据库,用户定义
+     *
+     */
+    function __construct($serviceDB)
+    {
+        $this->db = Factory::db($serviceDB);
+
+        // 表前缀自动拼接
+        $tablePrefix = $this->db->prefix();
+        if( !empty($tablePrefix) ){
+            $this->_table = $tablePrefix .'_'.$this->_table;
+        }
+    }
+
+    public function __set($name, $value)
+    {
+        $this->_data[$name] = $value;
+    }
+
+    public function __get($name)
+    {
+        return isset($this->_data[$name]) ? $this->_data[$name] : null;
+    }
+
+    public function __isset($name)
+    {
+        return isset($this->_data[$name]);
+    }
+
+    public function __unset($name)
+    {
+        unset($this->_data[$name]);
+    }
+
+    public function __call($method, $args)
+    {
+        if (in_array($method, array('field', 'where', 'order', 'limit', 'offset', 'having', 'group', 'distinct', 'data'), true)) {
+            $this->_options[$method] = $args[0];
+            return $this;
+        } elseif (in_array($method, array('sum', 'min', 'max', 'avg'), true)) {
+            $field = isset($args[0]) ? $args[0] : '*';
+            return $this->get_field($method . '(' . $field . ') AS `count`');
+        } elseif (preg_match("/^(get|gets|delete)_by_(.*)$/", $method, $matches)) {
+            $field = $matches[2];
+            if (in_array($field, $this->_fields, true)) {
+                array_unshift($args, $field);
+                return call_user_func_array(array($this, $matches[1] . '_by'), $args);
+            }
+        }
+        return;
+    }
+
+    /**
+     * 设置查询读取模式
+     *
+     * @param int $style
+     */
+    public function set_fetch_style($style)
+    {
+        $this->_fetch_style = $style;
+    }
+
+    /**
+     * 查询数据
+     *
+     * @param string|array $where
+     * @param string|array $fields
+     * @param string|array $order
+     * @param int $limit
+     * @param int $offset
+     * @param array $data
+     * @param boolean $multiple
+     * @return array|boolean
+     */
+    function select($where = null, $fields = '*', $order = null, $limit = null, $offset = null, $data = array(), $multiple = true, $isExport = false , $join = null)
+    {
+        $having = $group = NULL;
+        if (!empty($this->_options)) {
+            $fields = isset($this->_options['field']) ? $this->_options['field'] : $fields;
+            $fields = isset($this->_options['distinct']) ? "distinct " . $this->_options['distinct'] : $fields;
+            $where = isset($this->_options['where']) ? $this->_options['where'] : $where;
+            $having = isset($this->_options['having']) ? $this->_options['having'] : null;
+            $order = isset($this->_options['order']) ? $this->_options['order'] : $order;
+            $group = isset($this->_options['group']) ? $this->_options['group'] : null;
+            $limit = isset($this->_options['limit']) ? $this->_options['limit'] : $limit;
+            $offset = isset($this->_options['offset']) ? $this->_options['offset'] : $offset;
+            $this->_options = array();
+        }
+        if (is_array($fields)) $fields = '`' . implode('`,`', $fields) . '`';
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+        if (!$this->_before_select($where)) return false;
+        if ($join){
+            $sql = "SELECT $fields FROM `$this->_table` as a ".$join;
+        }else{
+            $sql = "SELECT $fields FROM `$this->_table` ";
+        }
+
+        if ($where && is_string($where)) $sql .= " WHERE $where ";
+        if ($order) $sql .= " ORDER BY $order ";
+        if ($group) $sql .= " GROUP BY $group ";
+        if ($having) $sql .= " HAVING $having ";
+        if (is_null($limit) && !$multiple) $sql .= " LIMIT 1 ";
+        $method = $multiple ? 'select' : 'get';
+        if($isExport){
+            $method = 'exportSelect';
+        }
+//         var_dump($sql);
+        $result = is_null($limit)
+            ? $this->db->$method($sql, $data, $this->_fetch_style)
+            : $this->db->limit($sql, $limit, $offset, $data, $this->_fetch_style);
+        if ($result === false || empty($result)) {
+            if ($this->db->error() && $this->db->errno() != '00000') {
+                $this->error = $this->db->error();
+                return false;
+            }
+
+            return [];
+        } else {
+            $this->_data = $result;
+            $this->_after_select($result, $multiple);
+            return $result;
+        }
+    }
+
+    /**
+     * 查询前执行
+     *
+     * @param array|string $where
+     * @return boolean
+     */
+    protected function _before_select(&$where)
+    {
+        return true;
+    }
+
+    /**
+     * 查询后执行
+     *
+     * @param array $result 查询后的结果
+     * @param boolean $multiple 是否多条数据
+     */
+    protected function _after_select(&$result, $multiple = true)
+    {
+    }
+
+    /**
+     * 分页查询
+     *
+     * @param array|string $where
+     * @param array|string $fields
+     * @param string $order
+     * @param int $page
+     * @param int $size
+     * @param array $data
+     * @return array
+     */
+    public function page($where = null, $fields = '*', $order = null, $page = 1, $size = 20, $data = array())
+    {
+        $offset = ($page - 1) * $size;
+        return $this->select($where, $fields, $order, $size, $offset, $data, true);
+    }
+
+    /**
+     * 获得单条数据
+     *
+     * @param array|string $where
+     * @param array|string $fields
+     * @param string $order
+     * @return array
+     */
+    public function get($where = null, $fields = '*', $order = null)
+    {
+        return $this->select($where, $fields, $order, null, null, array(), false);
+    }
+
+    /**
+     * 根据字段值获得单条数据
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param array|string $fields
+     * @param string $order
+     * @return array
+     */
+    public function get_by($field, $value, $fields = '*', $order = null)
+    {
+        return $this->select("`$field`=?", $fields, $order, null, null, array($value), false);
+    }
+
+    /**
+     * 根据字段值查询多条数据
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param array|string $fields
+     * @param string $order
+     * @param int $limit
+     * @param int $offset
+     * @return array
+     */
+    public function gets_by($field, $value, $fields = '*', $order = null, $limit = null, $offset = 0)
+    {
+        return $this->select("`$field`=?", $fields, $order, $limit, $offset, array($value), true);
+    }
+
+    /**
+     * 根据字段值查询多条数据
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param array|string $fields
+     * @param string $order
+     * @param int $limit
+     * @param int $offset
+     * @return object
+     */
+    public function exportSelect($where = null, $fields = '*', $order = null, $limit = null, $value = [], $offset = 0)
+    {
+        return $this->select($where, $fields, $order, $limit, $offset, $value, true, true);
+    }
+
+
+    /**
+     * 获取单条字段值
+     *
+     * @param string $field
+     * @param array|string $where
+     * @param array $data
+     * @return mixed
+     */
+    public function get_field($field, $where = null, $data = array())
+    {
+        $r = $this->select($where, $field, null, null, null, $data, false);
+        if(empty($r)){
+            return $r;
+        }
+        return array_shift($r);
+    }
+
+    /**
+     * 获取字段集合
+     *
+     * @param string $field
+     * @param array|string $where
+     * @param array $data
+     * @return array
+     */
+    public function gets_field($field, $where = null, $data = array())
+    {
+        $result = array();
+        $data = $this->select($where, $field, null, null, null, $data, true);
+        foreach ($data as $r) {
+            $result[] = array_shift($r);
+        }
+        return $result;
+    }
+
+    /**
+     * 获取总数
+     *
+     * @param array|string $where
+     * @param array $data
+     * @return int
+     */
+    public function count($where = null, $data = array(), $join = null)
+    {
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+        if (!empty($where)) $where = " WHERE $where";
+        if ($join){
+            $sql = "SELECT count(*) as `count` FROM `$this->_table` as a $join $where";
+        }else{
+            $sql = "SELECT count(*) as `count` FROM `$this->_table` $where";
+        }
+//        var_dump($sql);
+        $r = $this->db->get($sql, $data);
+        return $r ? $r['count'] : false;
+    }
+
+    /**
+     * 获取主健定义
+     *
+     * @return string
+     */
+    public function primary()
+    {
+        return isset($this->_primary) ? $this->_primary : $this->db->get_primary($this->_table);
+    }
+
+    /**
+     * 是否存在符合条件的记录
+     *
+     * @param array|string $field
+     * @param mixed $value
+     * @return boolean
+     */
+    public function exists($field, $value)
+    {
+        return $this->db->get("SELECT `$field` FROM `$this->_table` WHERE `$field`=?", array($value)) ? true : false;
+    }
+
+    /**
+     * 插入数据
+     *
+     * @param array $data
+     * @param boolean $multiple 是否批量插入
+     * @return int|boolean
+     */
+    protected function insert($data = array(), $multiple = false)
+    {
+        $this->_data($data);
+        if (!$this->_before_insert($data)) return false;
+
+        $this->_create_autofill($data, $multiple);
+
+        if($multiple){
+            foreach ($data as $key => $value){
+                $insertData[] = array_values($value);
+            }
+            $id = $this->db->insert("INSERT INTO `$this->_table` (`" . implode('`,`', array_keys($data[0])) . "`) VALUES(" . implode(',', array_fill(0, count($data[0]), '?')) . ")", $insertData, $multiple);
+        }else{
+            $id = $this->db->insert("INSERT INTO `$this->_table` (`" . implode('`,`', array_keys($data)) . "`) VALUES(" . implode(',', array_fill(0, count($data), '?')) . ")", array_values($data), $multiple);
+        }
+        if ($id === false) {
+            $this->error = $this->db->error();
+            return $this->error;
+        } else {
+            $this->_after_insert($data);
+            return $id;
+        }
+    }
+
+
+    /**
+     * 替换方式插入数据
+     *
+     * @param array $data
+     * @return int|boolean
+     */
+    protected function replace($data = array(), $multiple = false)
+    {
+        $this->_data($data);
+
+        if (!$this->_before_insert($data)) return false;
+
+        $this->_create_autofill($data, $multiple);
+
+        if($multiple){
+            foreach ($data as $key => $value){
+                $insertData[] = array_values($value);
+            }
+
+            $id = $this->db->insert("REPLACE INTO `$this->_table` (`" . implode('`,`', array_keys($data[0])) . "`) VALUES(" . implode(',', array_fill(0, count($data[0]), '?')) . ")", $insertData, $multiple);
+        }else{
+            $id = $this->db->insert("REPLACE INTO `$this->_table` (`" . implode('`,`', array_keys($data)) . "`) VALUES(" . implode(',', array_fill(0, count($data), '?')) . ")", array_values($data));
+        }
+
+        if ($id === false) {
+            $this->error = $this->db->error();
+            return false;
+        } else {
+            $this->_after_insert($data);
+            return $id;
+        }
+    }
+
+    /**
+     * 插入之前执行
+     *
+     * @param array $data
+     * @return boolean
+     */
+    protected function _before_insert(&$data)
+    {
+        return true;
+    }
+
+    /**
+     * 插入之后执行
+     *
+     * @param array $data
+     */
+    protected function _after_insert(&$data)
+    {
+    }
+
+    /**
+     * 根据ID拷贝一份
+     *
+     * @param int $id
+     * @param array $data
+     * @return int/boolean
+     */
+    public function copy_by_id($id, $data = array())
+    {
+        $r = $this->db->get("SELECT * FROM `$this->_table` WHERE `$this->_primary`=?", array($id));
+        if (!$r) return false;
+        unset($r[$this->_primary]);
+        if ($data) $r = array_merge($r, $data);
+        return $this->insert($r);
+    }
+
+    /**
+     * 更新
+     *
+     * @param array $data
+     * @param array /string $where
+     * @param int $limit
+     * @param array /string $order
+     * @return int/boolean
+     */
+    protected function update($data = array(), $where = null, $limit = null, $order = null)
+    {
+
+        if (!empty($this->_options)) {
+            $where = isset($this->_options['where']) ? $this->_options['where'] : $where;
+            $order = isset($this->_options['order']) ? $this->_options['order'] : $order;
+            $limit = isset($this->_options['limit']) ? $this->_options['limit'] : $limit;
+            $offset = isset($this->_options['offset']) ? $this->_options['offset'] : $offset;
+            $this->_options = array();
+        }
+
+        $this->_data($data);
+
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+
+        if (!$this->_before_update($data, $where)) return false;
+
+        $this->_update_autofill($data);
+
+        $this->_readonly($data);
+
+
+        $sql = "UPDATE `$this->_table` SET `" . implode('`=?,`', array_keys($data)) . "`=?";
+        if ($where) $sql .= " WHERE $where ";
+        if ($order) $sql .= " ORDER BY $order ";
+        if ($limit) $sql .= " LIMIT $limit ";
+        
+        $result = $this->db->update($sql, array_values($data));
+
+        if ($result === FALSE) {
+            $this->error = $this->db->error();
+            return false;
+        } else {
+            $this->_after_update($data, $where);
+            return $result;
+        }
+    }
+
+    /**
+ * query方法
+ */
+    protected function query($sql)
+    {
+        if(!empty($sql)){
+            $result = $this->db->query($sql);
+            if($result === false){
+                $this->error = $this->db->error();
+                return false;
+            }else{
+                return $result;
+            }
+        }else{
+            $this->error = 'sql is null';
+            return false;
+        }
+    }
+
+    /**
+     * query方法
+     */
+    protected function exportQuery($sql)
+    {
+        if(!empty($sql)){
+            $result = $this->db->exportQuery($sql);
+            if($result === false){
+                $this->error = $this->db->error();
+                return false;
+            }else{
+                return $result;
+            }
+        }else{
+            $this->error = 'sql is null';
+            return false;
+        }
+    }
+
+    /**
+     * 更新记录之前执行
+     *
+     * @param array $data
+     * @param array|string $where
+     * @return boolean
+     */
+    protected function _before_update(&$data, $where)
+    {
+        return true;
+    }
+
+    /**
+     * 更新记录之后执行
+     *
+     * @param array $data
+     * @param array|string $where
+     */
+    protected function _after_update(&$data, $where)
+    {
+    }
+
+    /**
+     * 更新一个字段值
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param string /array $where
+     * @return int/boolean
+     */
+    public function set_field($field, $value, $where = null)
+    {
+        return $this->update(array($field => $value), $where);
+    }
+
+    /**
+     * 递增一个int字段值
+     *
+     * @param string $field
+     * @param string /array $where
+     * @param int $step
+     * @param array $data
+     * @return int/boolean
+     */
+    public function set_inc($field, $where = null, $step = 1, $data = array())
+    {
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+        if(empty($step)){
+            $step = 0;
+        }
+        $sql = "UPDATE " .$this->_table." SET `$field`=IFNULL(`$field`,0)+".$step." WHERE $where";
+        return $this->db->update($sql, $data);
+    }
+
+    /**
+     * 递减一个int字段值
+     *
+     * @param string $field
+     * @param array /string $where
+     * @param int $step
+     * @param array $data
+     * @return int/boolean
+     */
+    public function set_dec($field, $where = null, $step = 1, $data = array())
+    {
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+        if(empty($step)){
+            $step = 0;
+        }
+        $sql = "UPDATE ".$this->_table." SET `$field`=IFNULL(`$field`,0)-".$step." WHERE $where";
+        return $this->db->update($sql, $data);
+    }
+
+    /**
+     * 删除
+     *
+     * @param array /string $where
+     * @param int $limit
+     * @param array /string $order
+     * @param array $data
+     * @return int/boolean
+     */
+    protected function delete($where = null, $limit = null, $order = null, $data = array())
+    {
+        if (!empty($this->_options)) {
+            $where = isset($this->_options['where']) ? $this->_options['where'] : $where;
+            $order = isset($this->_options['order']) ? $this->_options['order'] : $order;
+            $limit = isset($this->_options['limit']) ? $this->_options['limit'] : $limit;
+            $offset = isset($this->_options['offset']) ? $this->_options['offset'] : $offset;
+            $this->_options = array();
+        }
+
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+
+        if (!$this->_before_delete($where)) return false;
+
+        $sql = "DELETE FROM `$this->_table`";
+        $asql = $sql;
+        if ($where) $sql .= " WHERE $where ";
+        if ($limit) {
+            if ($order) $sql .= " ORDER BY $order ";
+            $sql .= " LIMIT $limit ";
+        }
+        if ($sql == $asql) {
+            return false;
+        }
+
+        $result = $this->db->delete($sql, $data);
+        if ($result === FALSE) {
+            $this->error = $this->db->error();
+            return false;
+        } else {
+            $this->_after_delete($where);
+            return $result;
+        }
+    }
+
+    /**
+     * 删除前执行
+     *
+     * @param array|string $where
+     * @return boolean
+     */
+    protected function _before_delete(&$where)
+    {
+        return true;
+    }
+
+    /**
+     * 删除后执行
+     *
+     * @param array|string $where
+     */
+    protected function _after_delete(&$where)
+    {
+    }
+
+    /**
+     * 根据字段值查询
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param int $limit
+     * @param array|string $order
+     * @return int|boolean
+     */
+    public function delete_by($field, $value, $limit = null, $order = null)
+    {
+        return $this->delete("`$field`=?", $limit, $order, array($value));
+    }
+
+
+    /**
+     * 获得一个字段的类型
+     *
+     * @param string $field
+     * @return string
+     */
+    protected function _fieldtype($field)
+    {
+
+        static $fields;
+        if (is_null($fields) || !isset($fields[$this->_table])) {
+            if ($data = $this->db->list_fields($this->_table)) {
+                foreach ($data as $k => $v) {
+                    $fields[$v['Field']] = $v;
+                }
+            }
+        }
+        return isset($fields[$field]) && isset($fields[$field]['Type']) ? $fields[$field]['Type'] : '';
+
+    }
+
+
+    private function _create_autofill(& $data, $multiple = false)
+    {
+        if (empty($this->_create_autofill)) return true;
+        if($multiple){
+            foreach ($this->_create_autofill as $field => $val) {
+                foreach ($data as $key => $value){
+                    if (!isset($data[$key][$field])) $data[$key][$field] = $val;
+                }
+            }
+        }else{
+            foreach ($this->_create_autofill as $field => $val) {
+                if (!isset($data[$field])) $data[$field] = $val;
+            }
+        }
+    }
+
+    private function _update_autofill(& $data)
+    {
+        if (empty($this->_update_autofill)) return true;
+        foreach ($this->_update_autofill as $field => $val) {
+            if (!isset($data[$field])) $data[$field] = $val;
+        }
+    }
+
+    private function _readonly(& $data)
+    {
+        if (empty($this->_readonly)) return true;
+        foreach ($this->_readonly as $field => $val) {
+            if (isset($data[$field])) unset($data[$field]);
+        }
+    }
+
+    private function checkVaule($where){
+        if(is_string($where)){
+            return addslashes($where);
+        }
+        return $where;
+    }
+
+
+    private function _where(& $where)
+    {
+        if (empty($where) && isset($this->_data[$this->_primary])) $where = $this->_data[$this->_primary];
+        if (empty($where)) return null;
+
+        if (is_numeric($where)) {
+            $where = "`$this->_primary`=$where";
+        } elseif (is_array($where)) {
+            $condition = array();
+            foreach ($where as $key => $value){
+                if($key === 0 && is_numeric($value)){
+                    $where = array_map(array(__CLASS__,'checkVaule'), $where);
+                    $ids = is_numeric($where[0]) ? implode(',', $where) : "'" . implode("','", $where) . "'";
+                    $where = "`$this->_primary` IN($ids)";
+                    break;
+                }else{
+                    if(is_numeric($key) && count($value) == 3){
+//                        if(!in_array($value[0],$this->_fields)){
+//                            $this->error = $value[0].'字段未被映射';
+//                            return false;
+//                        }
+
+                        if(!in_array($value[1], self::$operator)){
+                            $this->error = $value[1].'查询不支持';
+                            return false;
+                        }
+
+                        if(is_array($value[2])){
+                            $sqlIn = '';
+                            $len = count($value[2]);
+                            foreach ($value[2] as $k => $v){
+                                if(!is_numeric($v)){
+                                    $sqlIn .="'".$v."'";
+                                }else{
+                                    $sqlIn .= $v;
+                                }
+
+                                if($len >1 && $k+1 != $len){
+                                    $sqlIn .= ',';
+                                }
+                            }
+                            $condition[] = "`$value[0]` $value[1] (".$sqlIn.")";
+                        }else{
+                            if (strstr($value[0], '|')){
+                                $str = explode('|', $value[0]);
+                                $condition[] = "$str[0] $value[1] '".$this->checkVaule($value[2])."' OR "."$str[1] $value[1] '".$this->checkVaule($value[2])."'";
+                            }else{
+                                if ($value[1] == 'in'){
+                                    $condition[] = "$value[0] $value[1] (".$this->checkVaule($value[2]).")";
+                                }else{
+                                    $condition[] = "$value[0] $value[1] '".$this->checkVaule($value[2])."'";
+                                }
+                            }
+                        }
+                        $condition;
+                    }else{
+                        $where = array_map(array(__CLASS__,'checkVaule'), $where);
+//                        if (in_array($key, $this->_fields)) {
+                            if(is_array($value)){
+                                $sqlIn = '';
+                                $len = count($value);
+                                foreach ($value as $k => $v){
+                                    if(!is_numeric($v)){
+                                        $sqlIn .="'".$v."'";
+                                    }else{
+                                        $sqlIn .= $v;
+                                    }
+
+                                    if($len >1 && $k+1 != $len){
+                                        $sqlIn .= ',';
+                                    }
+                                }
+                                $condition[] = "$key in(".$sqlIn.")";
+                            }else{
+                                if (strstr($key, '|')){
+                                    $str = explode('|', $key);
+                                    $condition[] =  "$str[0] ='$value'"."' OR "."$str[0] ='$value'";
+                                }else{
+                                    $condition[] = "$key ='$value'";
+                                }
+                            }
+//                        }else{
+//                            $this->error = $key.'字段未被映射';
+//                            return false;
+//                        }
+                    }
+                }
+            }
+            if(!empty($condition)){
+                $where = implode(' AND ', $condition);
+            }
+        } elseif (preg_match("/^[0-9a-z\'\"\,\s]+$/i", $where)) {
+            $where = strpos($where, ',') === false ? "`$this->_primary`='$where'" : "`$this->_primary` IN($where)";
+        }
+
+        $notice_flags = array('select', 'delete', 'insert', 'drop', 'update', 'benchmark', 'database');
+        $quote = strpos($where, '"') === true ? '"' : "'";
+        foreach ($notice_flags as $w) {
+            $where = preg_replace('#([\s\(\)\!])' . $w . '([\s\(\)\*])#i', "$1" . substr($w, 0, 1) . $quote . '/**/' . $quote . substr($w, 1) . "$2", $where);
+        }
+        return $where;
+    }
+
+    private function _data(& $data)
+    {
+        if (empty($data)) {
+            if (!empty($this->_options['data'])) {
+                $data = $this->_options['data'];
+            } elseif (!empty($this->_data)) {
+                $data = $this->_data;
+            } elseif (!empty($_POST)) {
+                $data = $_POST;
+            }
+        }
+    }
+
+    /**
+     * 过滤不需要的字段
+     *
+     * @param array $data 输入数据
+     * @param array $keys 需要的字段定义
+     * @return array
+     */
+    protected function filter_array($data, $keys)
+    {
+        // 合并附加字段
+        $fields = $this->_get_extra_fileds();
+        if ($fields) {
+            $fields = array_filter(array_keys($fields));
+            if ($fields) {
+                $keys = array_unique(array_merge($keys, $fields));
+            }
+        }
+
+        foreach ($data as $field => $v) {
+            if (!in_array($field, $keys)) unset($data[$field]);
+        }
+        return $data;
+    }
+
+    /**
+     * 获取模型操作中的错误,并返回字符串类型的错误
+     *
+     * @return string
+     */
+    public function error()
+    {
+        $error = $this->error;
+        if (empty($error)) {
+            $error = $this->db->error();
+        }
+        if (empty($error)) {
+            return '';
+        } else {
+            if (is_array($error)) {
+                $error = implode("*****", $error);
+            }
+            return $error;
+        }
+    }
+
+    /**
+     * 切换分表
+     * @param $tablename
+     */
+    public function get_Table()
+    {
+       return $this->_table;
+    }
+
+
+    /**
+     * 切换分表
+     * @param $tablename
+     */
+    public function set_Table($tablename)
+    {
+        $this->_table = $tablename;
+    }
+
+    /**
+     * 开启事务
+     * @param bool $foreign_key_checks
+     * @return mixed
+     */
+    public function beginTransaction($foreign_key_checks = false)
+    {
+        return $this->db->beginTransaction($foreign_key_checks)?true:false;
+    }
+
+    /**
+     * 提交事务
+     * @return mixed
+     */
+    public function commit()
+    {
+        return $this->db->commit();
+    }
+
+    /**
+     * 回滚事务
+     * @return mixed
+     */
+    public function rollBack()
+    {
+        return $this->db->rollBack();
+    }
+}

+ 710 - 0
Mall/Framework/Core/StatusCode.Class.php

@@ -0,0 +1,710 @@
+<?php
+/**
+ * 统一的内容状态码
+ * 原则上按照数字顺序申请,可以提前占位,体现分组
+ * !!!不允许出现重复状态码!!!
+ */
+
+namespace Mall\Framework\Core;
+
+class StatusCode
+{
+    /**
+     * 公用模块状态码
+     *
+     * 数值范围 1-100
+     */
+
+    /** @var int $partion 部分 */
+    public static $partion = 3;
+
+    /** @var int $delete 删除 */
+    public static $delete = 4;
+
+    /** @var int $standard 正常 */
+    public static $standard = 5;
+
+    /** @var int $offline 下线 */
+    public static $offline = 6;
+
+    /**
+     * XX模块状态码
+     *
+     * 数值范围 101-200
+     */
+
+
+    /**
+     * XX模块状态码
+     *
+     * 数值范围 201-300
+     */
+
+    /**
+     * 系统设置类型type
+     */
+    public static $settingType = [
+        'applets' => 1,  // 小程序设置
+        'message' => 2,  // 模板消息设置
+        'class'   => 3,  // 分类设置
+    ];
+
+    public static $adminSettingType = [
+        'pay'         => 1,  // 支付
+        'delivery'    => 2,  // 配送
+        'financeType' => 3,  // 财务类型
+        'unit'        => 4,  //属性
+        'printTemplate'    => 5,//打印模版,
+        'role'        => 6,//角色
+        'process'    => 7,//流程,
+        'notice'    => 8,//语音提示
+    ];
+
+    public static $systemFinanceType = [
+        'saleReturnReceipt'     => 1,//销售退货退款
+        'saleReceipt'           => 2,//销售收款
+        'purchaseReturnReceipt' => 3,//采购退货收款
+        'purchasePrepaid'       => 4,//采购预付
+        'purchaseReceipt'       => 5,//采购付款
+        'depositsReceived'      => 6,//预存收款
+        'expenseSingleIn'       => 7,//费用单收款
+        'expenseSingleOut'      => 8,//费用单支出
+        1      => '销售退货退款',//费用单支出
+        2      => '销售收款',//费用单支出
+        3      => '采购退货收款',//费用单支出
+        4      => '采购预付',//费用单支出
+        5      => '采购付款',//费用单支出
+        6      => '预存收款',//费用单支出
+        7      => '费用单收款',//费用单支出
+        8      => '费用单支出',//费用单支出
+    ];
+
+    public static $messageType = [
+        'inventoryNull' => 1,//库存不足消息
+    ];
+
+
+    /**
+     * 通用审核状态
+     */
+    public static $auditStatus = [
+        'completion'   => 0, //待补全
+        'auditing'     => 1,  // 待审
+        'auditPass'    => 2,  // 审核通过
+        'auditNotPass' => 3, //审核未通过
+        'auditIng'     => 4, // 审核中
+        0              => '待补全',
+        1              => '待审核',
+        2              => '审核通过',
+        3              => '审核未通过',
+        4              => '审核中',
+    ];
+
+    /**
+     * 分销层级
+     */
+    public static $level = [
+        'off' => 0, //分销开关
+        'one' => 1, //第一级
+        'two' => 2, //第二级
+        'three' => 3, //第三级
+    ];
+
+    public static $subConditions = [
+        'oneCheck' => 1, //首次点击链接
+        'oneOrder' => 2, //首次下单
+        'onePay' => 3, //首次付款
+];
+
+
+    /** @var array $userActionID 用户操作行为 */
+    public static $userActionID = [
+        'favour'      => 1,// 点赞
+        'tread'       => 2, // 踩--不喜欢
+        'report'      => 3, // 举报
+        'fllow'       => 4, // 关注
+        'avorites'    => 5, //收藏
+        'notAvorites' => 6, // 取消收藏
+        'notFollow'   => 7,  //取消关注
+        'notTread'    => 8,//取消踩--不喜欢
+        'notFavour'   => 9, //取消点赞
+    ];
+
+    /** @var array $type 价格类型 */
+    public static $priceType = [
+        'salePrice' => 1,//销售价
+        'costPrice' => 2,// 成本价
+        'qtPrice'   => 3,// 其他价
+    ];
+    /** @var array $type 运费类型 */
+    public static $freightType = [
+        'enterCharge' => 1,//销售价
+        'pourCharge'  => 2,// 成本价
+    ];
+
+    /** @var array $userActionObj 用户操作对象  此处标识修改需谨慎多处关联 */
+    public static $userActionObj = [
+        /** @var int $actionContent 用户操作对象-文章 */
+        'actionContent'        => 1,
+        /** @var int $actionUser 用户操作对象-用户 */
+        'actionUser'           => 2,
+        /** @var int $actionComment 用户操作对象-评论 */
+        'actionComment'        => 3,
+        /** @var int $actionMall 用户操作对象-商场 -店铺 */
+        'actionMallStore'      => 4,
+        /** @var int $actionMall 用户操作对象-商场 -商品 */
+        'actionMallShop'       => 5,
+        /** @var int $actionSea 用户操作对象-海淘--店铺 */
+        'actionSeaStore'       => 6,
+        /** @var int $actionSea 用户操作对象-海淘--商品 */
+        'actionSeaShop'        => 7,
+        /** @var int $actionMall 用户操作对象-商场 */
+        'actionMall'           => 8,
+        /** @var int $actionMall 用户操作对象-旅游景点 */
+        'actionScenic'         => 9,
+        /** @var int $actionPromotion 用户操作对象-消费打折信息 */
+        'actionPromotion'      => 10,
+
+
+        /** @var int $actionArVocalConcert 用户操作对象-休闲--演唱会a */
+        'actionArVocalConcert' => 24,
+        /** @var int $actionArTheatre 用户操作对象-休闲--剧场a */
+        'actionArTheatre'      => 25,
+        /** @var int $actionArTusicale 用户操作对象-休闲--音乐会a */
+        'actionArMusicale'     => 26,
+        /** @var int $actionArCompete 用户操作对象-休闲--比赛a */
+        'actionArCompete'      => 27,
+        /** @var int $actionArVariety 用户操作对象-休闲--综艺a */
+        'actionArVariety'      => 28,
+        /** @var int $actionArder 用户操作对象-休闲--博物馆p */
+        'actionArMuseum'       => 29,
+        /** @var int $actionArSport 用户操作对象-休闲--健身p */
+        'actionArSport'        => 30,
+        /** @var int $actionArCoffee 用户操作对象-休闲--咖啡p */
+        'actionArCoffee'       => 31,
+        /** @var int $actionMall 用户操作对象-其他 */
+        'actionArOther'        => 32,
+        /** @var int $actionArCoffee 用户操作对象-休闲--下午茶p */
+        'actionArTea'          => 33,
+        /** @var int $actionArCoffee 用户操作对象-休闲--酒吧p */
+        'actionArWine'         => 34,
+        /** @var int $actionArCoffee 用户操作对象-休闲--足疗p */
+        'actionArFoot'         => 35,
+        /** @var int $actionArCoffee 用户操作对象-休闲--轰趴p */
+        'actionArParty'        => 36,
+        /** @var int $actionArCoffee 用户操作对象-休闲--瑜伽p */
+        'actionArYoga'         => 37,
+        /** @var int $actionArCoffee 用户操作对象-休闲--舞蹈p */
+        'actionArDance'        => 38,
+    ];
+
+    /**
+     * 成本计算标识
+     */
+    public static $costType = [
+        'mwa' => 1,//移动加权平均法
+        'sp'  => 2,//个别计价法
+    ];
+
+    /**
+     * 成本标识名称
+     */
+    public static $costTypeTitle = [
+        1 => '移动加权平均法',
+        2 => '个别计价法',
+    ];
+    /**
+     * 采购订单 状态码
+     * @var array
+     *  1:创建2:审核中3:已审核4:重新审核
+     */
+    public static $purchaseOrder = [
+        'create'   => 1,
+        'auditing' => 2,
+        'audited'  => 3,
+        'reAudite' => 4
+    ];
+
+    /**
+     * 销售出库是否已出库状态
+     * @var array
+     */
+    public static $outWarehouseStatus = [
+        'shopPut'    => 1, //未出库商品推送出库单,状态未出库
+        'erpOut'     => 2, //已出库 erp自己创建的销售出库
+        'shopPutOut' => 2, //已出库 商城推的订单
+    ];
+
+    /**
+     * 销售订单来源
+     * @var array
+     */
+    public static $orderFrom = [
+        'shopOrder'  => 1, //商城订单
+        'stockOrder' => 2, //库存服务自建订单
+    ];
+    /**
+     *锁库存与解库存标识
+     * @var array
+     */
+    public static $lockType = [
+        'lock'   => 1, //未出库商品推送出库单,状态未出库
+        'unlock' => 2, //已出库 erp自己创建的销售出库
+    ];
+    /**
+     * 销售退货方式
+     * @var array
+     */
+    public static $sellReturnMoney = [
+        'returnMoney'    => 1, //销售退货退资金
+        'notReturnMoney' => 2, //销售退货不退资金
+    ];
+    /*
+     * 订单状态
+     */
+    public static $orderShippingStatus = [
+        'out'        => 1, //已经提交出库
+        'get'        => 2, //已收货
+        'returnAll'  => 3, //全部退货
+        'returnPart' => 4, //部分退货
+    ];
+    /*
+     * 订单操作来源
+     */
+    public static $actionType = [
+        'shop'  => 0, //商城
+        'stock' => 1, //库存服务
+        'tms'   => 2, //已收货
+    ];
+
+    /**
+     * 定价系统 - 销售价格生效区域类型
+     */
+    public static $salePriceAreaType = [
+        'nationalUnifiedPrice' => 1, // 全国统一价格
+        'areaPrice'            => 2, // 大区和片区价格
+    ];
+
+    /**
+     * 定价系统 - 销售价格类型
+     */
+    public static $salePriceType = [
+        'salePrice'    => 1, // 普通市场价
+        'seckillPrice' => 2, // 活动秒杀价
+    ];
+
+    /**
+     * 定价系统 - 权限标识
+     */
+    public static $aclType = [
+        'edit'   => 1, // 编辑
+        'cat'    => 2, // 查看
+        'noCast' => 3, // 去除成本
+    ];
+
+    /**
+     * 支付方式
+     */
+    public static $payType = [
+        'wxPay'     => 1, //微信
+        'aliPay'    => 2, //支付宝
+        'cashPay'   => 3, //货到付款
+        'bankLoans' => 4, //银行打款
+        'cash'      => 5,//现金支付
+        'other'     => 6,//其他
+        'balance'   => 7, // 余额支付
+        'byte'      => 8, // 字节跳动
+        'cashBefore'=> 9, // 先款后货
+        1 => '微信支付',
+        2 => '支付宝',
+        3 => '货到付款',
+        4 => '银行打款',
+        5 => '现金支付',
+        6 => '其它',
+        7 => '余额支付',
+        8 => '字节跳动',
+        9 => '先款后货'
+    ];
+
+    /**
+     * 配送方式
+     */
+    public static $deliveryType = [
+        'goodsDelivery' => 1, //商品配送
+        'selfMention'   => 2, //上门自提
+        'logistics'     => 3, //物流
+        'logisticsLines'   => 4, // 物流专线
+        'logisticsVehicle' => 5, // 物流专车
+        1               => '快递',
+        2               => '上门自提',
+        3               => '物流',
+        4               => '物流专线',
+        5               => '物流专车',
+    ];
+
+    /**
+     * 业务来源
+     */
+    public static $source = [
+        'ios'               => 1, //ios
+        'android'           => 2,//安卓
+        'miniProgram'       => 3,//小程序
+        'manage'            => 4, //后台创建
+        'H5'                => 5, //H5页面
+        'Pc'                => 6, //Pc页面
+        'manageMiniProgram' => 7,//微信小程序后台管理端
+        'ByteProgram'       => 8,//字节小程序
+        'weiXinBrowser'     => 9, // 微信浏览器
+        1                   => '苹果',
+        2                   => '安卓',
+        3                   => '小程序',
+        4                   => '后台创建',
+        5                   => 'H5页面',
+        6                   => 'PC页面',
+        7                   => '微信小程序后台管理',
+        8                   => '字节小程序',
+        9                   => '微信浏览器'
+    ];
+
+    /**
+     * 订单状态
+     */
+    public static $orderStatus = [
+        //'create'       => 1, //创建
+        'waitPay'      => 2, //等待支付
+        'waitDelivery' => 3, //待发货
+        'waitReceive'  => 4, //待收货
+        'finish'       => 5, //已完成
+        'close'        => 6, //已关闭
+        'cancelIng'    => 7, // 待取消
+        2              => '等待支付',
+        3              => '待发货',
+        4              => '待收货',
+        5              => '已完成',
+        6              => '已关闭',
+        7              => '待取消', // 申请取消订单,等待审核
+    ];
+
+    /**
+     * 订单退货状态
+     */
+    public static $orderReturn = [
+        'notReturn'  => 0, //未退货
+        'partReturn' => 1, //部分退货 (详情里用是已退货)
+        'allReturn'  => 2, //全部退货 (详情里用是已退货)
+    ];
+
+    /**
+     * 单据状态
+     */
+    public static $documentStatus = [
+        'create'         => 1, //已创建
+        'audit'          => 2, //已审核
+        'inventoryAudit' => 3, //库存已审核
+        'return'         => 9, //已退货 (单据详情)
+        'returnAll'      => 7, //全部退货 (主单据)
+        'returnPart'     => 8, //部分退货 (主单据)
+    ];
+
+    /**
+     * 订单退货状态
+     */
+    public static $orderPickStatus = [
+        'notPicking'        =>  1,
+        'picking'           =>  2,
+        'finishPicking'     =>  3,
+        1                   =>  '未拣货',
+        2                   =>  '拣货中',
+        3                   =>  '拣货完成',
+    ];
+
+    /**
+     * 客户账号状态
+     */
+    public static $customerStatus = [
+        'enable' => 1, //已审核
+        'delete' => 2, //待审核
+    ];
+
+    /**
+     * 客户类型
+     */
+    public static $customerType = [
+        'customer' => 5, //企业客户
+        'user'     => 4, //企业用户
+    ];
+
+    /**
+     * 单据类型
+     * @var array
+     */
+    public static $orderType = [
+        'saleOrder'                   => 1,//销售订单
+        'purchaseOrder'               => 2,//采购订单
+        'purchaseIn'                  => 3,//采购入库
+        'purchaseReturn'              => 4,//采购退货
+        'saleOut'                     => 5,//销售出库
+        'saleReturn'                  => 6,//销售退货
+        'allocate'                    => 7,//调拨库存
+        'allocateOut'                 => 8,//调拨出库
+        'batch'                       => 9,//批次编号
+        'priceAdjustment'             => 10,//商品调价单
+        'customerPriceAdjustment'     => 11,//客户调价单
+        'customerTypePriceAdjustment' => 11,//客户类型调价单
+        'allocateIn'                  => 12,//调拨入库
+        'stocktaking'                 => 13,//盘点库存
+        'purchaseReturnOut'           => 14,//采购退货出库
+        'saleReturnIn'                => 15,//销售退货入库
+        'reportLoss'                  => 27,//库存报损
+        'merchantPurchase'            => 29,//商户采购单
+        'merchantPurchaseIn'          => 30,//商户入库单
+
+
+        /*财务*/
+        'shouldReceive'               => 16,//应收单
+        'received'                    => 17,//收款单
+        'shouldPay'                   => 18,//应付单
+        'payed'                       => 19,//付款单
+        'accountTransfer'             => 20,//资金转账
+        'ExpenseSingle'               => 28, //费用单
+
+        //单据
+        'warehouseBeginning'          => 21,//仓库期初
+
+        //会员卡
+        'vipCard'                     => 22,
+
+        //销售单(销售单管理)
+        'salesSlip'                   => 23,//销售单
+        'cashierOrder'                => 24,//收银台订单
+        'storeWithdraw'               => 25,//商户提现
+        'supplierOfferPrice'          => 26,//供应商报价单
+    ];
+
+    /**
+     * 单据编码前缀
+     * @var array
+     */
+    public static $noPrefix = [
+        1  => 'XSDD',// 1 销售订单
+        2  => 'CGDD',// 2 采购订单
+        3  => 'CGRK',// 3 采购入库
+        4  => 'CGTH',// 4 采购退货
+        5  => 'XSCK',// 5 销售出库
+        6  => 'TH',// 6 销售退货
+        7  => 'DBKC',// 7 调拨库存
+        8  => 'DBCK',// 8 调拨出库
+        12 => 'DBRK',//12 调拨入库
+        13 => 'PDKC',//13 盘点库存
+        14 => 'CGTHCK',//14 采购退货出库
+        15 => 'XSTHRK',//15 销售退货入库
+        16 => 'YS', // 16 应收单
+        17 => 'SK', // 17 收款单
+        18 => 'YF', // 18 应付单
+        19 => 'FK', // 19 付款单
+        20 => 'TK', // 20 退款单
+        26 => 'RFQS',// 26 供应商报价单
+        27 => 'BSD',//27 报损单
+        28 => 'FY', // 28 费用单
+        29 =>'SHCG',//商户采购单
+        30 => 'SHRK',//商户入库单
+        31 => 'JHD',//拣货单
+        32 => 'DBD',//调拨单
+        33 => 'BCD',//报损单
+    ];
+
+
+    /**
+     * 编码前缀
+     * @var array
+     */
+    public static $code = [
+        'brand'      => ['prefix' => 'BRAND', 'length' => 6],//品牌
+        'customer'   => ['prefix' => 'CUSTOMER', 'length' => 6],//客户
+        'category'   => ['prefix' => 'CATEGORY', 'length' => 6],//分类
+        'goodsBasic' => ['prefix' => 'MATERIEL', 'length' => 6],//基础物料code
+        'shop'       => ['prefix' => 'SHOP', 'length' => 6],//商铺
+        'supplier'   => ['prefix' => 'SUPPLIER', 'length' => 6],//供应商
+        'staff'      => ['prefix' => 'STAFF', 'length' => 6],//员工
+        'warehouse'  => ['prefix' => 'WAREHOUSE', 'length' => 6],//仓库
+        'account'    => ['prefix' => 'ACCOUNT', 'length' => 6],//账户编码
+    ];
+
+    /**
+     * 角色类型
+     * @var array
+     */
+    public static $roleType = [
+        'admin'    => 1,//后台管理员
+        'staff'    => 2,//员工
+        'customer' => 3,//客户
+        'merchant' => 4,//商户
+        'supplier' => 5,//供应商
+    ];
+
+    /**
+     * 数据域类型
+     * @var array
+     */
+    public static $dataFieldType = [
+        'person' => 1,//个人
+        'staff'  => 2,//员工
+        'all'    => 3,//所有
+    ];
+
+    /**
+     * 小程序模板审核状态
+     */
+    public static $weixinTemplateStatus = [
+        'commit'   => 1, // 提交代码
+        'auditing' => 2, // 提交审核
+    ];
+
+    /*** @var array 模版状态********* */
+    public static $wxAuditStatus = [
+        'pass'        => 0,//审核成功
+        'refuse'      => 1,//审核拒绝
+        'inAudit'     => 2,//审核中
+        'withdrawn'   => 3,//已撤回
+        'postponed'   => 4,//延后
+        'noSubmit'    => 5,//提交审核
+        'beenRelease' => 6,//提交代码
+        'beingUsed'   => 7,//正在使用
+    ];
+
+    /*** @var array 优惠券类型 */
+    public static $couponType = [
+        'commonly'  => 10,// 普通优惠券
+        'vipCoupon' => 20,// 会员卡优惠券
+    ];
+
+    /**@var array 发放方式****** */
+    public static $grantType = [
+        'receive'   => 10,//主动领取
+        'grant'     => 20,//定向发放
+        'register'  => 30,//注册领取
+        'onlinePay' => 40,//在线支付领取
+    ];
+    /***@var array 商品适用范围* */
+    public static $applyRange = [
+        'allGoods'        => 10,//全部商品
+        'appointCategory' => 20,//指定分类
+        'appointBrand'    => 30,//指定品牌
+        'goodsCollect'    => 40,//指定商品
+    ];
+
+    /***************活动类型*********/
+    public static $activityType = [
+        'specialOffer' => 10,//特价
+        'secKill'      => 20,//秒杀
+    ];
+
+    /************************/
+    public static $delivery = [
+        '包邮',
+        '自提'
+    ];
+
+    /******@var array $templateType 模版类型 * */
+    public static $templateType = [
+        'ordinary'  => 1,//普通模版
+        'universal' => 2,//万能模版
+    ];
+
+    /***@var array $pageType 页面类型** */
+    public static $pageType = [
+        'home'    => 1,//首页,
+        'special' => 2,//专题
+    ];
+
+    /*** @var array $specType 规格类型 */
+    public static $specType = [
+        'single'   => 1,//单规格
+        'multiple' => 2,//多规格
+    ];
+
+    /*** @var array 系统默认角色id */
+    public static $roleId = [
+        'cashier' => 1,//收银员工,
+        'guide'   => 2,//导购员
+    ];
+
+    public static $expressType = [
+        'free' => 1,
+        'rule' => 2,
+        'unify' => 3
+    ];
+
+    //匿名用户UID
+    public static $noneUserCenter = 99999;
+
+    //匿名客户id
+    public static $noneCustomer = 99999;
+
+    /**
+     * @var array 流程类型
+     */
+    public static $processType = [
+        'sales' => 1,//订货流程
+        'retSales' => 2,//退货流程
+    ];
+
+    /**
+     * @var array 小程序主体类型
+     */
+    public static $principalType = [
+        'personal' => 0,
+        'enterprise' => 1,
+        'media' => 2,
+        'gov' => 3,
+        'other' => 4,
+    ];
+
+    /**
+     * @var array 业务模式
+     */
+    public static $modelType = [
+        'B2C' => 1,
+        'B2B' => 2,
+        'multi' => 3,//多商户
+    ];
+    
+    /**
+     * @var array 标签颜色
+     */
+    public static $colourType = [
+        1                   => '蓝色',
+        2                   => '绿色',
+        3                   => '粉色',
+        4                   => '橙色',
+    ];
+
+    /**
+     * 财务账户类型
+     */
+    public static $financeAccountType = [
+        'ali' => 1, // 支付宝账户
+        'wx' => 2, // 微信账户
+        'bank' => 3, //银行卡账户
+        'ordinary' => 4, // 普通账户
+    ];
+
+    /**
+     * 出库状态
+     */
+    public static $outStatus = [
+        'notOut' => 4, // 未出库
+        'allOut' => 5, // 全部出库
+        'notAllOut' => 6, // 部分出库
+    ];
+
+    /**
+     * 支付状态
+     */
+    public static $payStatus = [
+        'pay' => 4,
+        'notPay' => 5,
+        'refund' => 6,
+        'partRefund'=>7,
+    ];
+}

+ 33 - 0
Mall/Framework/Core/Swoole.Class.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Swoole
+{
+
+    static protected $instance;
+
+    static public function getInstance($options = [], $driver = 'Client')
+    {
+        $driver = $driver ?: 'Client';
+
+        if (!is_array($options) && !$options) {
+            throw new \Exception('Need default Swoole ' . $driver . ' config');
+        }
+
+        /*$key = $driver . md5(implode(',', $options)) . $driver;
+
+        if (!isset(self::$instance[$key])) {
+            $class = 'Mall\\Framework\\Swoole\\' . ucwords(strtolower($driver));
+            if(class_exists($class)) {
+                self::$instance[$key] = new $class($options);
+            }
+        }
+        return self::$instance[$key];
+        */
+
+
+        $class = 'Mall\\Framework\\Swoole\\' . ucwords($driver);
+        return new $class($options);
+    }
+}

Some files were not shown because too many files changed in this diff