Merge branch 'v5.0.0dev' of https://gitee.com/ZhongBangKeJi/CRMEB into v5.0.0dev

This commit is contained in:
From-wh 2023-05-11 10:11:49 +08:00
commit edcec0766f
12 changed files with 679 additions and 4 deletions

View File

@ -6,11 +6,18 @@
namespace app\adminapi\controller;
use crmeb\services\easywechat\orderShipping\MiniOrderService;
class Test
{
public function index()
{
$ship_list = [
[
'item_desc' => '蓝牙音乐手表 | Jeep智能表蓝牙'
]
];
MiniOrderService::shippingByTradeNo('cp179280225803173888', 3, $ship_list, 'oZEAhsy60rrq96hE86LmBb9v_Kes');
}
}

View File

@ -86,4 +86,20 @@ class StoreServiceDao extends BaseDao
return $this->getModel()->whereNotIn('uid', $where['notUid'])->update($data);
}
/**
* 统计数量
* @param array $where
* @param bool $search
* @return int
* @throws \ReflectionException
* @author 吴汐
* @email 442384644@qq.com
* @date 2023/05/10
*/
public function count($where = [], $search = true)
{
return $this->search($where, false)->when(isset($where['noId']), function ($query) use ($where) {
$query->whereNotIn('uid', $where['noId']);
})->count();
}
}

View File

@ -74,7 +74,7 @@ class ExportServices extends BaseServices
*/
public function exportOrderList($where)
{
$header = ['订单号', '收货人姓名', '收货人电话', '收货地址', '商品信息', '总价格', '实际支付', '支付状态', '支付时间', '订单状态', '下单时间', '用户备注'];
$header = ['订单号', '收货人姓名', '收货人电话', '收货地址', '商品信息', '总价格', '实际支付', '支付状态', '支付时间', '订单状态', '下单时间', '用户备注', '表单信息'];
$filename = '订单列表_' . date('YmdHis', time());
$export = $fileKey = [];
/** @var StoreOrderServices $orderServices */
@ -127,6 +127,11 @@ class ExportServices extends BaseServices
} else if ($item['paid'] == 1 && $item['refund_status'] == 2) {
$item['status_name'] = '已退款';
}
$custom_form = '';
foreach ($item['custom_form'] as $custom_form_value) {
$custom_form .= $custom_form_value['title'] . ':' . $custom_form_value['value'] . ';';
}
$goodsName = [];
foreach ($item['_info'] as $value) {
$_info = $value['cart_info'];
@ -157,6 +162,7 @@ class ExportServices extends BaseServices
'status_name' => $item['status_name'] ?? '未知状态',
'add_time' => $item['add_time'],
'mark' => $item['mark'],
'custom_form' => $custom_form,
];
$export[] = $one_data;
if ($i == 0) {

View File

@ -51,6 +51,16 @@ class AccessTokenServeService extends HttpService
*/
protected $apiHost = 'http://sms.crmeb.net/api/';
/**
* @var string
*/
protected $sandBoxApi = 'https://api_v2.crmeb.net/';
/**
* @var bool
*/
protected $sandBox = true;
/**
* 登录接口
*/
@ -166,6 +176,9 @@ class AccessTokenServeService extends HttpService
*/
public function get(string $apiUrl = '')
{
if ($this->sandBox) {
return $this->sandBoxApi . $apiUrl;
}
return $this->apiHost . $apiUrl;
}
}

View File

@ -14,10 +14,12 @@ namespace crmeb\services\easywechat;
use crmeb\services\easywechat\miniPayment\ServiceProvider;
use crmeb\services\easywechat\oauth2\wechat\WechatOauth2Provider;
use crmeb\services\easywechat\orderShipping\OrderClient;
use crmeb\services\easywechat\subscribe\ProgramProvider;
use crmeb\services\easywechat\v3pay\PayClient;
use crmeb\services\easywechat\wechatlive\ProgramProvider as LiveProgramProvider;
use crmeb\services\easywechat\v3pay\ServiceProvider as V3PayServiceProvider;
use crmeb\services\easywechat\orderShipping\ServiceProvider as OrderServiceProvider;
/**
@ -26,6 +28,7 @@ use crmeb\services\easywechat\v3pay\ServiceProvider as V3PayServiceProvider;
* @property LiveProgramProvider $wechat_live
* @property WechatOauth2Provider $oauth2
* @property PayClient $v3pay
* @property OrderClient $order_ship
*/
class Application extends \EasyWeChat\Foundation\Application
{
@ -40,6 +43,7 @@ class Application extends \EasyWeChat\Foundation\Application
ProgramProvider::class,
V3PayServiceProvider::class,
\crmeb\services\easywechat\Open3rd\ProgramProvider::class,
OrderServiceProvider::class
];
/**

View File

@ -0,0 +1,131 @@
<?php
namespace crmeb\services\easywechat\orderShipping;
use crmeb\exceptions\AdminException;
use EasyWeChat\Core\AbstractAPI;
use EasyWeChat\Core\AccessToken;
use EasyWeChat\Support\Collection;
class BaseOrder extends AbstractAPI
{
public $config;
const BASE_API = 'https://api.weixin.qq.com/';
const ORDER = 'wxa/sec/order/';
const EXPRESS = 'cgi-bin/express/delivery/open_msg/';
public function __construct(AccessToken $accessToken, $config)
{
parent::__construct($accessToken);
$this->config = $config;
}
private function resultHandle(Collection $result)
{
if (empty($result)) {
throw new AdminException('微信接口返回异常');
}
$res = $result->toArray();
if ($res['errcode'] == 0) {
return $res;
} else {
throw new AdminException("微信接口异常:code = {$res['errcode']} msg = {$res['errmsg']}");
}
}
/**
* 发货
* @param $params
* @return array
* @throws \EasyWeChat\Core\Exceptions\HttpException
*
* @date 2023/05/09
* @author yyw
*/
public function shipping($params)
{
return $this->resultHandle($this->parseJSON('POST', [self::BASE_API . self::ORDER . 'upload_shipping_info', json_encode($params)]));
}
/**
* 合单
* @param $params
* @return array
* @throws \EasyWeChat\Core\Exceptions\HttpException
*
* @date 2023/05/09
* @author yyw
*/
public function combinedShipping($params)
{
return $this->resultHandle($this->parseJSON('POST', [self::BASE_API . self::ORDER . 'upload_combined_shipping_info', json_encode($params)]));
}
/**
* 签收消息提醒
* @param $params
* @return array
* @throws \EasyWeChat\Core\Exceptions\HttpException
*
* @date 2023/05/09
* @author yyw
*/
public function notifyConfirm($params)
{
return $this->resultHandle($this->parseJSON('POST', [self::BASE_API . self::ORDER . 'notify_confirm_receive', json_encode($params)]));
}
/**
* 查询小程序是否已开通发货信息管理服务
* @return array
* @throws \EasyWeChat\Core\Exceptions\HttpException
*
* @date 2023/05/09
* @author yyw
*/
public function isManaged()
{
$params = [
'appid' => $this->config['config']['order_shipping']['appid']
];
return $this->resultHandle($this->parseJSON('POST', [self::BASE_API . self::ORDER . 'notify_confirm_receive', json_encode($params)]));
}
/**
* 设置跳转连接
* @param $path
* @return array
* @throws \EasyWeChat\Core\Exceptions\HttpException
*
* @date 2023/05/10
* @author yyw
*/
public function setMesJumpPath($path)
{
$params = [
'path' => $path
];
return $this->resultHandle($this->parseJSON('POST', [self::BASE_API . self::ORDER . 'set_msg_jump_path', json_encode($params)]));
}
/**
* 获取运力id列表get_delivery_list
* @return array
* @throws \EasyWeChat\Core\Exceptions\HttpException
*
* @date 2023/05/09
* @author yyw
*/
public function getDeliveryList()
{
return $this->resultHandle($this->parseJSON('POST', [self::BASE_API . self::EXPRESS . 'get_delivery_list']));
}
}

View File

@ -0,0 +1,133 @@
<?php
namespace crmeb\services\easywechat\orderShipping;
use crmeb\services\easywechat\Application;
use crmeb\services\SystemConfigService;
class MiniOrderService
{
/**
* @var Application
*/
protected static $instance;
/**
* @param array $config
* @return array[]
*
* @date 2023/05/09
* @author yyw
*/
protected static function options(array $config = [])
{
$payment = SystemConfigService::more(['routine_appId', 'routine_appsecret', 'pay_weixin_mchid', 'pay_new_weixin_open', 'pay_new_weixin_mchid']);
return [
'order_shipping' => [
'appid' => $payment['routine_appId'] ?? '',
'secret' => $payment['routine_appsecret'] ?? '',
'merchant_id' => empty($payment['pay_new_weixin_open']) ? trim($payment['pay_weixin_mchid']) : trim($payment['pay_new_weixin_mchid']),
]
];
}
/**
* 初始化
* @param bool $cache
* @return Application
*/
protected static function application($cache = false)
{
(self::$instance === null || $cache === true) && (self::$instance = new Application(self::options()));
return self::$instance;
}
protected static function order()
{
return self::application()->order_ship;
}
/**
* 上传订单
* @param string $out_trade_no
* @param int $logistics_type
* @param array $shipping_list
* @param string $payer_openid
* @param int $delivery_mode
* @param bool $is_all_delivered
* @return array
*
* @date 2023/05/09
* @author yyw
*/
public static function shippingByTradeNo(string $out_trade_no, int $logistics_type, array $shipping_list, string $payer_openid, int $delivery_mode = 1, bool $is_all_delivered = true)
{
return self::order()->shippingByTradeNo($out_trade_no, $logistics_type, $shipping_list, $payer_openid, $delivery_mode, $is_all_delivered);
}
/**
* 合单
* @param string $out_trade_no
* @param int $logistics_type
* @param array $sub_orders
* @param string $payer_openid
* @param int $delivery_mode
* @param bool $is_all_delivered
* @return array
* @throws \EasyWeChat\Core\Exceptions\HttpException
*
* @date 2023/05/10
* @author yyw
*/
public static function combinedShippingByTradeNo(string $out_trade_no, int $logistics_type, array $sub_orders, string $payer_openid, int $delivery_mode = 2, bool $is_all_delivered = false)
{
return self::order()->combinedShippingByTradeNo($out_trade_no, $logistics_type, $sub_orders, $payer_openid, $delivery_mode, $is_all_delivered);
}
/**
* 签收通知
* @param string $merchant_trade_no
* @param string $received_time
* @return array
*
* @date 2023/05/09
* @author yyw
*/
public static function notifyConfirmByTradeNo(string $merchant_trade_no, string $received_time)
{
return self::order()->notifyConfirmByTradeNo($merchant_trade_no, $received_time);
}
/**
* 判断是否开通
* @return bool
* @throws \EasyWeChat\Core\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
*
* @date 2023/05/09
* @author yyw
*/
public static function isManaged()
{
return self::order()->checkManaged();
}
/**
* 设置小修跳转路径
* @param $path
* @return array
* @throws \EasyWeChat\Core\Exceptions\HttpException
*
* @date 2023/05/10
* @author yyw
*/
public static function setMesJumpPath($path)
{
return self::order()->setMesJumpPath($path);
}
}

View File

@ -0,0 +1,276 @@
<?php
namespace crmeb\services\easywechat\orderShipping;
use crmeb\exceptions\AdminException;
use crmeb\services\CacheService;
use EasyWeChat\Core\AccessToken;
use EasyWeChat\Core\Exceptions\HttpException;
use think\facade\Cache;
class OrderClient extends BaseOrder
{
const redis_prefix = 'mimi_order';
public function __construct(AccessToken $accessToken, $config)
{
parent::__construct($accessToken, $config);
}
/**
* @var \Redis
*/
protected $redis;
/**
* @return object|\Redis|null
*
* @date 2023/05/10
* @author yyw
*/
protected function getRedis()
{
if (empty($this->redis)) {
$this->redis = Cache::store('redis')->handler();
}
return $this->redis;
}
/**
* 处理联系人
* @param array $contact
* @return array
*
* @date 2023/05/10
* @author yyw
*/
protected function handleContact(array $contact = []): array
{
if (isset($contact)) {
if (isset($contact['consignor_contact']) && $contact['consignor_contact']) {
$contact['consignor_contact'] = Utility::encryptTel($contact['consignor_contact']);
}
if (isset($contact['receiver_contact']) && $contact['receiver_contact']) {
$contact['receiver_contact'] = Utility::encryptTel($contact['receiver_contact']);
}
}
return $contact;
}
/**
* 发货
* @param string $out_trade_no
* @param int $logistics_type
* @param array $shipping_list
* @param string $payer_openid
* @param int $delivery_mode
* @param bool $is_all_delivered
* @return array
* @throws HttpException
*
* @date 2023/05/10
* @author yyw
*/
public function shippingByTradeNo(string $out_trade_no, int $logistics_type, array $shipping_list, string $payer_openid, int $delivery_mode = 1, bool $is_all_delivered = true)
{
if (!$this->checkManaged()) {
throw new AdminException('开通小程序订单管理服务后重试');
}
$params = [
'order_key' => [
'order_number_type' => 1,
'mchid' => $this->config['config']['order_shipping']['merchant_id'],
'out_trade_no' => $out_trade_no,
],
'logistics_type' => $logistics_type,
'delivery_mode' => $delivery_mode,
'upload_time' => date(DATE_RFC3339),
'payer' => [
'openid' => $payer_openid
]
];
if ($delivery_mode == 2) {
$params['is_all_delivered'] = $is_all_delivered;
}
foreach ($shipping_list as $shipping) {
$contact = $this->handleContact($shipping['contact'] ?? []);
$params['shipping_list'][] = [
'tracking_no' => $shipping['tracking_no'] ?? '',
'express_company' => isset($shipping['express_company']) ? $this->getDelivery($shipping['express_company']) : '',
'item_desc' => $shipping['item_desc'],
'contact' => $contact
];
}
return $this->shipping($params);
}
/**
* 合单
* @param string $out_trade_no
* @param int $logistics_type
* @param array $sub_orders
* @param string $payer_openid
* @param int $delivery_mode
* @param bool $is_all_delivered
* @return array
* @throws HttpException
*
* @date 2023/05/10
* @author yyw
*/
public function combinedShippingByTradeNo(string $out_trade_no, int $logistics_type, array $sub_orders, string $payer_openid, int $delivery_mode = 2, bool $is_all_delivered = false)
{
if (!$this->checkManaged()) {
throw new AdminException('开通小程序订单管理服务后重试');
}
$params = [
'order_key' => [
'order_number_type' => 1,
'mchid' => $this->config['order_shipping']['merchant_id'],
'out_trade_no' => $out_trade_no,
],
'upload_time' => date(DATE_RFC3339),
'payer' => [
'openid' => $payer_openid
]
];
foreach ($sub_orders as $order) {
$sub_order = [
'order_key' => [
'order_number_type' => 1,
'mchid' => $this->config['order_shipping']['merchant_id'],
'out_trade_no' => $order['out_trade_no'],
'logistics_type' => $logistics_type,
],
'delivery_mode' => $delivery_mode,
'is_all_delivered' => $is_all_delivered
];
foreach ($sub_orders['shipping_list'] as $shipping) {
$contact = $this->handleContact($shipping['contact'] ?? []);
$sub_order['shipping_list'][] = [
'tracking_no' => $shipping['tracking_no'] ?? '',
'express_company' => isset($shipping['express_company']) ? $this->getDelivery($shipping['express_company']) : '',
'item_desc' => $shipping['item_desc'],
'contact' => $contact
];
}
$params['sub_orders'][] = $sub_order;
}
return $this->combinedShipping($params);
}
/**
* 签收通知
* @param string $merchant_trade_no
* @param string $received_time
* @return array
* @throws HttpException
*
* @date 2023/05/10
* @author yyw
*/
public function notifyConfirmByTradeNo(string $merchant_trade_no, string $received_time)
{
$params = [
'merchant_id' => $this->config['payment']['merchant_id'],
'merchant_trade_no' => $merchant_trade_no,
'received_time' => $received_time
];
return $this->notifyConfirm($params);
}
/**
* 设置小程序管理服务开通状态
* @return bool
* @throws HttpException
*
* @date 2023/05/09
* @author yyw
*/
public function setManaged()
{
$res = $this->isManaged();
if ($res['is_trade_managed']) {
$key = self::redis_prefix . '_is_trade_managed';
$this->getRedis()->set($key, $res['is_trade_managed']);
return true;
} else {
return false;
}
}
/**
* @return bool
* @throws HttpException
*
* @date 2023/05/10
* @author yyw
*/
public function checkManaged()
{
$key = self::redis_prefix . '_is_trade_managed';
if ($this->getRedis()->exists($key)) {
return true;
} else {
return $this->setManaged();
}
}
/**
* 同步去微信物流列表
* @return array
* @throws HttpException
*
* @date 2023/05/10
* @author yyw
*/
public function setDeliveryList()
{
$list = $this->getDeliveryList();
if ($list) {
$key = self::redis_prefix . '_delivery_list';
$date = array_column($list, 'delivery_id', 'delivery_name');
// 创建缓存
$this->getRedis()->hMSet($key, $date);
return $date;
} else {
throw new AdminException('物流公司列表异常');
}
}
/**
* 获取物流公司编码
* @param $company_name
* @return array|mixed
* @throws HttpException
*
* @date 2023/05/10
* @author yyw
*/
public function getDelivery($company_name)
{
$key = self::redis_prefix . '_delivery_list';
if (!$this->getRedis()->exists($key)) {
$date = $this->setDeliveryList();
if (!isset($date[$company_name])) {
throw new AdminException('物流公司异常');
}
$express_company = $date[$company_name];
} else {
$express_company = $this->getRedis()->hMGet($key, $company_name);
}
return $express_company;
}
}

View File

@ -0,0 +1,44 @@
<?php
/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace crmeb\services\easywechat\orderShipping;
use EasyWeChat\Payment\Merchant;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use EasyWeChat\MiniProgram\AccessToken;
/**
* Class ServiceProvider.
*
* @package crmeb\services\easywechat\order_ship
* @package crmeb\services\easywechat\mini_express
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $pimple)
{
$pimple['mini_program.access_token'] = function ($pimple) {
return new AccessToken(
$pimple['config']['order_shipping']['app_id'],
$pimple['config']['order_shipping']['secret'],
$pimple['cache']
);
};
$pimple['order_ship'] = function ($pimple) {
return new OrderClient($pimple['access_token'], $pimple);
};
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace crmeb\services\easywechat\orderShipping;
use crmeb\exceptions\AdminException;
class Utility
{
/**
* @param int $padding
*/
private static function paddingModeLimitedCheck(int $padding): void
{
if (!($padding === OPENSSL_PKCS1_OAEP_PADDING || $padding === OPENSSL_PKCS1_PADDING)) {
throw new AdminException(sprintf("Doesn't supported padding mode(%d), here only support OPENSSL_PKCS1_OAEP_PADDING or OPENSSL_PKCS1_PADDING.", $padding));
}
}
/**
* 加密数据
* @param string $plaintext
* @param int $padding
* @return string
*/
public function encryptor(string $plaintext, int $padding = OPENSSL_PKCS1_OAEP_PADDING)
{
self::paddingModeLimitedCheck($padding);
if (!openssl_public_encrypt($plaintext, $encrypted, $this->getPublicKey(), $padding)) {
throw new AdminException('Encrypting the input $plaintext failed, please checking your $publicKey whether or nor correct.');
}
return base64_encode($encrypted);
}
public static function encryptTel($tel)
{
$new_tel = substr_replace($tel, '****', 3, 4);
return $new_tel;
}
}

View File

@ -165,6 +165,9 @@ class Express extends BaseExpress
if ($expressData['check_man'] == 1) $param['checkMan'] = $expressData['courier_name'];
if ($expressData['partner_name'] == 1) $param['partnerName'] = $expressData['customer_name'];
if ($expressData['is_code'] == 1) $param['code'] = $expressData['code_name'];
if (!$data['siid']) {
$param['print_type'] = 'IMAGE';
}
return $this->accessToken->httpRequest(self::EXPRESS_DUMP, $param, 'POST');
}

View File

@ -18,7 +18,7 @@ const resolve = (dir) => {
// 例如:https://www.foobar.com/my-app/
// 需要将它改为'/my-app/'
// iview-admin线上演示打包路径: https://file.iviewui.com/admin-dist/
// const BASE_URL = process.env.NODE_ENV === 'production' ? '/' : '/';
const BASE_URL = process.env.NODE_ENV === 'production' ? '/' : '/';
const env = process.env.NODE_ENV;
module.exports = {
// Project deployment base
@ -31,7 +31,7 @@ module.exports = {
outputDir: Setting.outputDir,
runtimeCompiler: true,
productionSourceMap: false, //关闭生产环境下的SourceMap映射文件
baseUrl: '/',
baseUrl: BASE_URL,
// tweak internal webpack configuration.
// see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
// 如果你不需要使用eslint,把lintOnSave设为false即可