初始化项目
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service;
|
||||
|
||||
use app\api\model\Cart as CartModel;
|
||||
use app\api\model\Goods as GoodsModel;
|
||||
use app\api\service\User as UserService;
|
||||
use app\common\library\helper;
|
||||
use app\common\service\BaseService;
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 服务类: 购物车
|
||||
* Class Cart
|
||||
* @package app\api\service
|
||||
*/
|
||||
class Cart extends BaseService
|
||||
{
|
||||
/**
|
||||
* 购物车商品列表(用于购物车页面)
|
||||
* @param array $cartIds 购物车记录ID集
|
||||
* @param bool $isGoodsGradeMoney 是否设置商品会员价
|
||||
* @return array|\think\Collection
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getList(array $cartIds = [], bool $isGoodsGradeMoney = true)
|
||||
{
|
||||
// 购物车列表
|
||||
$cartList = $this->getCartList($cartIds);
|
||||
// 整理商品ID集
|
||||
$goodsIds = helper::getArrayColumn($cartList, 'goods_id');
|
||||
if (empty($goodsIds)) return [];
|
||||
// 获取商品列表
|
||||
$goodsList = $this->getGoodsListByIds($goodsIds, $isGoodsGradeMoney);
|
||||
// 整理购物车商品列表
|
||||
foreach ($cartList as $cartIdx => $item) {
|
||||
// 查找商品, 商品不存在则删除该购物车记录
|
||||
$result = $this->findGoods($goodsList, $item, $isGoodsGradeMoney);
|
||||
if ($result !== false) {
|
||||
$cartList[$cartIdx]['goods'] = $result;
|
||||
} else {
|
||||
$this->clear([$item['id']]);
|
||||
unset($cartList[$cartIdx]);
|
||||
}
|
||||
}
|
||||
return $cartList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取购物车商品列表(用于订单结算台)
|
||||
* @param array $cartIds 购物车记录ID集
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getOrderGoodsList(array $cartIds = []): array
|
||||
{
|
||||
// 购物车列表
|
||||
$cartList = $this->getList($cartIds, false);
|
||||
// 订单商品列表
|
||||
$goodsList = [];
|
||||
foreach ($cartList as $item) {
|
||||
// 商品记录
|
||||
$goods = $item['goods'];
|
||||
// 商品单价
|
||||
$goods['goods_price'] = $goods['skuInfo']['goods_price'];
|
||||
// 商品购买数量
|
||||
$goods['total_num'] = $item['goods_num'];
|
||||
// 商品SKU索引
|
||||
$goods['goods_sku_id'] = $item['goods_sku_id'];
|
||||
// 商品购买总金额
|
||||
$goods['total_price'] = helper::bcmul($goods['goods_price'], $item['goods_num']);
|
||||
$goodsList[] = $goods;
|
||||
}
|
||||
return $goodsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检索查询商品
|
||||
* @param mixed $goodsList 商品列表
|
||||
* @param CartModel $item 购物车记录
|
||||
* @param bool $isGoodsGradeMoney 是否设置商品会员价
|
||||
* @return false|mixed
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function findGoods($goodsList, CartModel $item, bool $isGoodsGradeMoney = true)
|
||||
{
|
||||
// 查找商品记录
|
||||
$goodsInfo = helper::getArrayItemByColumn($goodsList, 'goods_id', $item['goods_id']);
|
||||
if (empty($goodsInfo)) {
|
||||
return false;
|
||||
}
|
||||
// 获取当前选择的商品SKU信息
|
||||
$goodsInfo['skuInfo'] = GoodsModel::getSkuInfo($goodsInfo, $item['goods_sku_id'], $isGoodsGradeMoney);
|
||||
if (empty($goodsInfo['skuInfo'])) {
|
||||
return false;
|
||||
}
|
||||
// 商品封面 (优先sku封面)
|
||||
$goodsInfo['goods_image'] = $goodsInfo['skuInfo']['goods_image'] ?: $goodsInfo['goods_image'];
|
||||
// 这里需要用到clone, 因对象是引用传递 后面的值会覆盖前面的
|
||||
return clone $goodsInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除购物车中指定记录
|
||||
* @param array $cartIds
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
*/
|
||||
public function clear(array $cartIds = []): bool
|
||||
{
|
||||
$model = new CartModel;
|
||||
return $model->clear($cartIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商品ID集获取商品列表
|
||||
* @param array $goodsIds
|
||||
* @param bool $isGoodsGradeMoney 是否设置会员折扣价
|
||||
* @return mixed
|
||||
*/
|
||||
private function getGoodsListByIds(array $goodsIds, bool $isGoodsGradeMoney = true)
|
||||
{
|
||||
$model = new GoodsModel;
|
||||
return $model->isGoodsGradeMoney($isGoodsGradeMoney)
|
||||
->getListByIdsFromApi($goodsIds)
|
||||
->hidden(GoodsModel::getHidden(['goods_images']));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的购物车记录
|
||||
* @param array $cartIds 购物车记录ID集
|
||||
* @return \think\Collection
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getCartList(array $cartIds = []): \think\Collection
|
||||
{
|
||||
// 当前用户ID
|
||||
$userId = UserService::getCurrentLoginUserId();
|
||||
// 购物车记录模型
|
||||
$model = new CartModel;
|
||||
// 检索查询条件
|
||||
$filter = [];
|
||||
if (!empty($cartIds)) {
|
||||
$filter[] = ['id', 'in', $cartIds];
|
||||
}
|
||||
// 查询列表记录
|
||||
return $model->where($filter)
|
||||
->where('user_id', '=', $userId)
|
||||
->where('is_delete', '=', 0)
|
||||
->select();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service;
|
||||
|
||||
use app\api\model\GoodsSku as GoodsSkuModel;
|
||||
use app\common\service\Goods as GoodsService;
|
||||
|
||||
/**
|
||||
* 商品服务类
|
||||
* Class Goods
|
||||
* @package app\api\service
|
||||
*/
|
||||
class Goods extends GoodsService
|
||||
{
|
||||
/**
|
||||
* 获取商品的指定的某个SKU信息
|
||||
* @param int $goodsId
|
||||
* @param string $goodsSkuId
|
||||
* @return GoodsSkuModel|array|null
|
||||
*/
|
||||
public static function getSkuInfo(int $goodsId, string $goodsSkuId)
|
||||
{
|
||||
$detail = GoodsSkuModel::detail($goodsId, $goodsSkuId);
|
||||
if (!empty($detail['image'])) {
|
||||
$detail['goods_image'] = $detail['image']['preview_url'];
|
||||
}
|
||||
return $detail;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service;
|
||||
|
||||
use app\api\service\User as UserService;
|
||||
use app\api\model\wxapp\Setting as WxappSettingModel;
|
||||
use app\common\enum\OrderType as OrderTypeEnum;
|
||||
use app\common\enum\order\PayType as OrderPayTypeEnum;
|
||||
use app\common\service\BaseService;
|
||||
use app\common\library\wechat\WxPay;
|
||||
use app\common\library\leshua\LsPay;
|
||||
use app\common\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 订单支付服务类
|
||||
* Class Payment
|
||||
* @package app\api\service
|
||||
*/
|
||||
class Payment extends BaseService
|
||||
{
|
||||
/**
|
||||
* 构建订单支付参数
|
||||
* @param $order
|
||||
* @param $payType
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function orderPayment($order, $payType): array
|
||||
{
|
||||
if ($payType == OrderPayTypeEnum::WECHAT) {
|
||||
return self::wechat(
|
||||
$order['order_id'],
|
||||
$order['order_no'],
|
||||
$order['pay_price'],
|
||||
OrderTypeEnum::ORDER
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建微信支付
|
||||
* @param $orderId
|
||||
* @param $orderNo
|
||||
* @param $payPrice
|
||||
* @param int $orderType
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function wechat(
|
||||
$orderId,
|
||||
$orderNo,
|
||||
$payPrice,
|
||||
int $orderType = OrderTypeEnum::ORDER
|
||||
): array
|
||||
{
|
||||
// 获取当前用户信息
|
||||
$userInfo = UserService::getCurrentLoginUser(true);
|
||||
// 获取第三方用户信息(微信)
|
||||
$oauth = UserService::getOauth($userInfo['user_id'], 'MP-WEIXIN');
|
||||
empty($oauth) && throwError('没有找到第三方用户信息oauth');
|
||||
// 统一下单API
|
||||
$WxPay = new WxPay(static::getWxConfig());
|
||||
return $WxPay->unifiedorder($orderNo, $oauth['oauth_id'], $payPrice, $orderType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信支付配置
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private static function getWxConfig(): array
|
||||
{
|
||||
$storeId = getStoreId();
|
||||
return WxappSettingModel::getWxappConfig($storeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建乐刷支付
|
||||
* @param $orderId
|
||||
* @param $orderNo
|
||||
* @param $payPrice
|
||||
* @param int $orderType
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function leshua(
|
||||
$orderId,
|
||||
$orderNo,
|
||||
$payPrice,
|
||||
int $orderType = OrderTypeEnum::ORDER
|
||||
): array
|
||||
{
|
||||
// 获取当前用户信息
|
||||
$userInfo = UserService::getCurrentLoginUser(true);
|
||||
// 获取第三方用户信息(微信)
|
||||
$oauth = UserService::getOauth($userInfo['user_id'], 'MP-WEIXIN');
|
||||
empty($oauth) && throwError('没有找到第三方用户信息oauth');
|
||||
// 统一下单API
|
||||
$LsPay = new LsPay(static::getWxConfig());
|
||||
return $LsPay->unifiedorder($orderNo, $oauth['oauth_id'], $payPrice, $orderType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service;
|
||||
|
||||
use app\common\library\helper;
|
||||
use app\common\service\BaseService;
|
||||
use app\api\model\Store as StoreModel;
|
||||
use app\api\model\Setting as SettingModel;
|
||||
use app\api\model\h5\Setting as H5SettingModel;
|
||||
use app\common\enum\Setting as SettingEnum;
|
||||
|
||||
/**
|
||||
* 服务类:商城设置
|
||||
* Class Setting
|
||||
* @package app\api\service
|
||||
*/
|
||||
class Setting extends BaseService
|
||||
{
|
||||
/**
|
||||
* 商城公共设置
|
||||
* 这里的商城设置仅暴露可公开的设置项 例如分类页模板、积分名称
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getPublic(): array
|
||||
{
|
||||
$data = [];
|
||||
//分类页模板设置
|
||||
$data[SettingEnum::PAGE_CATEGORY_TEMPLATE] = $this->getCatTplStyle();
|
||||
// 积分设置
|
||||
$data[SettingEnum::POINTS] = $this->getPoints();
|
||||
// 充值设置
|
||||
$data[SettingEnum::RECHARGE] = $this->getRecharge();
|
||||
// 分享设置
|
||||
$data[SettingEnum::SHARE] = $this->getShare();
|
||||
// 兑换设置
|
||||
$data[SettingEnum::EXCHANGE] = $this->getExchange();
|
||||
// 注册设置
|
||||
$data[SettingEnum::REGISTER] = $this->getRegister();
|
||||
// 店铺设置
|
||||
$data['store'] = StoreModel::getInfo();
|
||||
// 其他设置
|
||||
$data['_other'] = $this->getOtherSetting();
|
||||
// 商城设置
|
||||
$data['trade'] = $this->getTrade();
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取其他设置
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getOtherSetting(): array
|
||||
{
|
||||
// H5端访问地址
|
||||
$data['h5Url'] = H5SettingModel::getH5Url();
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 积分设置 (积分名称、积分描述)
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getPoints(): array
|
||||
{
|
||||
$values = SettingModel::getItem(SettingEnum::POINTS);
|
||||
return helper::pick($values, ['points_name', 'describe']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 积分设置 (积分名称、积分描述)
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getRecharge(): array
|
||||
{
|
||||
$values = SettingModel::getItem(SettingEnum::RECHARGE);
|
||||
return helper::pick($values, ['is_entrance', 'is_custom', 'describe']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分享设置 (分享开关、分享描述)
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getShare(): array
|
||||
{
|
||||
$values = SettingModel::getItem(SettingEnum::SHARE);
|
||||
return helper::pick($values, ['is_entrance', 'describe']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 兑换设置 (兑换开关、兑换描述)
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getExchange(): array
|
||||
{
|
||||
$values = SettingModel::getItem(SettingEnum::EXCHANGE);
|
||||
return helper::pick($values, ['is_entrance', 'describe']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册设置 (默认登录方式、是否开启微信小程序授权登录)
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getRegister(): array
|
||||
{
|
||||
$values = SettingModel::getItem(SettingEnum::REGISTER);
|
||||
return helper::pick($values, ['registerMethod', 'isOauthMpweixin', 'isManualBind']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 商城设置 (开放省份、客服手机号)
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getTrade(): array
|
||||
{
|
||||
$values = SettingModel::getItem(SettingEnum::TRADE);
|
||||
return helper::pick($values, ['province_list', 'phone']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类页模板设置
|
||||
* @return array|mixed
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getCatTplStyle()
|
||||
{
|
||||
return SettingModel::getItem(SettingEnum::PAGE_CATEGORY_TEMPLATE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service;
|
||||
|
||||
use app\api\model\User as UserModel;
|
||||
use app\api\model\UserOauth as UserOauthModel;
|
||||
use app\common\service\BaseService;
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 用户服务类
|
||||
* Class User
|
||||
* @package app\api\service
|
||||
*/
|
||||
class User extends BaseService
|
||||
{
|
||||
// 当前登录的会员信息
|
||||
private static $currentLoginUser;
|
||||
|
||||
/**
|
||||
* 获取当前登录的用户信息 (快捷)
|
||||
* 可在api应用中的任意模块中调用此方法(controller model service)
|
||||
* 已登录情况下返回用户信息, 未登录返回false
|
||||
* @param bool $isForce 是否强制验证登录, 如果未登录将抛错
|
||||
* @return false|UserModel
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static function getCurrentLoginUser(bool $isForce = false)
|
||||
{
|
||||
$service = new static;
|
||||
if (empty(static::$currentLoginUser)) {
|
||||
static::$currentLoginUser = $service->getLoginUser();
|
||||
if (empty(static::$currentLoginUser)) {
|
||||
$isForce && throwError($service->getError(), config('status.not_logged'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return static::$currentLoginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录的用户ID
|
||||
* getCurrentLoginUser方法的二次封装
|
||||
* @return mixed
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static function getCurrentLoginUserId()
|
||||
{
|
||||
$userInfo = static::getCurrentLoginUser(true);
|
||||
return $userInfo['user_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第三方用户信息
|
||||
* @param int $userId 用户ID
|
||||
* @param string $oauthType 第三方登陆类型
|
||||
* @return array|\think\Model|null
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getOauth(int $userId, $oauthType = 'MP-WEIXIN')
|
||||
{
|
||||
return UserOauthModel::getOauth($userId, $oauthType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否已登录
|
||||
* @param bool $isForce 是否强制验证登录, 如果未登录将抛错
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static function isLogin(bool $isForce = false)
|
||||
{
|
||||
return !empty(static::getCurrentLoginUser($isForce));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录的用户信息
|
||||
* @return UserModel|array|false|null
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function getLoginUser()
|
||||
{
|
||||
// 获取用户认证Token
|
||||
if (!$token = $this->getToken()) {
|
||||
return false;
|
||||
}
|
||||
// 获取用户信息
|
||||
if (!$user = UserModel::getUserByToken($token)) {
|
||||
$this->error = '没有找到用户信息';
|
||||
return false;
|
||||
}
|
||||
$referrer_id = (int)$this->request->get('referrer_id', 0);
|
||||
// 绑定推荐人ID
|
||||
if($referrer_id > 0 && empty($user['referrer_id'])){
|
||||
$user->save(['referrer_id' => $referrer_id]);
|
||||
/*$referrer = UserModel::detail($referrer_id);
|
||||
if($referrer) {
|
||||
$user->save(['referrer_id' => $referrer_id]);
|
||||
}*/
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户认证Token
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function getToken()
|
||||
{
|
||||
// 获取请求中的token
|
||||
$token = $this->request->header('Access-Token');
|
||||
// 调试模式下可通过param
|
||||
if (empty($token) && is_debug()) {
|
||||
$token = $this->request->param('Access-Token');
|
||||
}
|
||||
// 不存在token报错
|
||||
if (empty($token)) {
|
||||
$this->error = '缺少必要的参数token, 请先登录';
|
||||
return false;
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\coupon;
|
||||
|
||||
use app\common\enum\coupon\ApplyRange as ApplyRangeEnum;
|
||||
use app\common\enum\coupon\CouponType as CouponTypeEnum;
|
||||
use app\common\service\BaseService;
|
||||
use app\common\library\helper;
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 订单优惠券折扣服务类
|
||||
* Class GoodsDeduct
|
||||
* @package app\api\service\coupon
|
||||
*/
|
||||
class GoodsDeduct extends BaseService
|
||||
{
|
||||
// 实际抵扣金额
|
||||
private $actualReducedMoney;
|
||||
|
||||
// 订单商品列表
|
||||
private $goodsList = [];
|
||||
|
||||
// 优惠券信息
|
||||
private $couponInfo = [];
|
||||
|
||||
// 实际参与优惠券折扣的商品记录
|
||||
private $rangeGoodsList = [];
|
||||
|
||||
// 设置订单商品列表
|
||||
public function setGoodsList(iterable $goodsList): GoodsDeduct
|
||||
{
|
||||
$this->goodsList = $goodsList;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// 设置优惠券信息
|
||||
public function setCouponInfo(array $couponInfo): GoodsDeduct
|
||||
{
|
||||
$this->couponInfo = $couponInfo;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算优惠券抵扣金额
|
||||
* @return GoodsDeduct
|
||||
* @throws BaseException
|
||||
*/
|
||||
public function setGoodsCouponMoney(): GoodsDeduct
|
||||
{
|
||||
// 验证当前类属性
|
||||
$this->checkAttribute();
|
||||
// 设置实际参与优惠券折扣的商品记录
|
||||
$this->setRangeGoodsList();
|
||||
// 计算实际抵扣的金额
|
||||
$this->setActualReducedMoney();
|
||||
// 实际抵扣金额为0
|
||||
if ($this->actualReducedMoney > 0) {
|
||||
// 计算商品的价格权重
|
||||
$this->setGoodsListWeight();
|
||||
// 计算商品优惠券抵扣金额
|
||||
$this->setGoodsListCouponMoney();
|
||||
// 总抵扣金额 (已分配的)
|
||||
$assignedCouponMoney = helper::getArrayColumnSum($this->rangeGoodsList, 'coupon_money');
|
||||
// 分配剩余的抵扣金额
|
||||
$this->setGoodsListCouponMoneyFill($assignedCouponMoney);
|
||||
$this->setGoodsListCouponMoneyDiff($assignedCouponMoney);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// 获取实际参与优惠券折扣的商品记录
|
||||
public function getRangeGoodsList(): array
|
||||
{
|
||||
return $this->rangeGoodsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置实际参与优惠券折扣的商品记录
|
||||
* @return void
|
||||
*/
|
||||
private function setRangeGoodsList()
|
||||
{
|
||||
$this->rangeGoodsList = [];
|
||||
foreach ($this->goodsList as $goods) {
|
||||
$goods['total_price'] *= 100;
|
||||
$goodsKey = "{$goods['goods_id']}-{$goods['goods_sku_id']}";
|
||||
switch ($this->couponInfo['apply_range']) {
|
||||
case ApplyRangeEnum::ALL:
|
||||
$this->rangeGoodsList[$goodsKey] = $goods;
|
||||
break;
|
||||
case ApplyRangeEnum::SOME:
|
||||
if (in_array($goods['goods_id'], $this->couponInfo['apply_range_config']['applyGoodsIds'])) {
|
||||
$this->rangeGoodsList[$goodsKey] = $goods;
|
||||
}
|
||||
break;
|
||||
case ApplyRangeEnum::EXCLUDE:
|
||||
if (!in_array($goods['goods_id'], $this->couponInfo['apply_range_config']['excludedGoodsIds'])) {
|
||||
$this->rangeGoodsList[$goodsKey] = $goods;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证当前类属性
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function checkAttribute()
|
||||
{
|
||||
if (empty($this->goodsList) || empty($this->couponInfo)) {
|
||||
throwError('goodsList or couponInfo not found.');
|
||||
}
|
||||
}
|
||||
|
||||
public function getActualReducedMoney()
|
||||
{
|
||||
return $this->actualReducedMoney;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算实际抵扣的金额
|
||||
*/
|
||||
private function setActualReducedMoney()
|
||||
{
|
||||
// 获取当前订单商品总额
|
||||
$orderTotalPrice = $this->getOrderTotalPrice();
|
||||
// 计算最大抵扣金额
|
||||
$reducedMoney = 0;
|
||||
foreach ($this->rangeGoodsList as $goods) {
|
||||
$reducedMoney += $goods['total_price'];
|
||||
}
|
||||
// 计算打折金额
|
||||
if ($this->couponInfo['coupon_type'] == CouponTypeEnum::DISCOUNT) {
|
||||
$reducePrice = $reducedMoney - ($reducedMoney * ($this->couponInfo['discount'] / 10));
|
||||
} else {
|
||||
$reducePrice = $this->couponInfo['reduce_price'] * 100;
|
||||
}
|
||||
// 优惠券最大允许抵扣到一分钱,所以此处判断抵扣金额大于等于订单金额时,减去一分钱
|
||||
$this->actualReducedMoney = ($reducePrice >= $orderTotalPrice) ? $orderTotalPrice - 1 : $reducePrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前订单商品总额
|
||||
* @return float|int
|
||||
*/
|
||||
private function getOrderTotalPrice()
|
||||
{
|
||||
$orderTotalPrice = 0;
|
||||
foreach ($this->goodsList as $goods) {
|
||||
$orderTotalPrice += ($goods['total_price'] * 100);
|
||||
}
|
||||
return $orderTotalPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算商品抵扣的权重(占比)
|
||||
*/
|
||||
private function setGoodsListWeight()
|
||||
{
|
||||
$orderTotalPrice = helper::getArrayColumnSum($this->rangeGoodsList, 'total_price');
|
||||
foreach ($this->rangeGoodsList as &$goods) {
|
||||
$goods['weight'] = round($goods['total_price'] / $orderTotalPrice, 6);
|
||||
}
|
||||
array_sort($this->rangeGoodsList, 'weight', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算商品抵扣的金额
|
||||
*/
|
||||
private function setGoodsListCouponMoney(): void
|
||||
{
|
||||
foreach ($this->rangeGoodsList as &$goods) {
|
||||
$goods['coupon_money'] = helper::bcmul($this->actualReducedMoney, $goods['weight'], 0);
|
||||
}
|
||||
}
|
||||
|
||||
private function setGoodsListCouponMoneyFill($assignedCouponMoney): void
|
||||
{
|
||||
if ($assignedCouponMoney === 0) {
|
||||
$temReducedMoney = $this->actualReducedMoney;
|
||||
foreach ($this->rangeGoodsList as &$goods) {
|
||||
if ($temReducedMoney === 0) break;
|
||||
$goods['coupon_money'] = 1;
|
||||
$temReducedMoney--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setGoodsListCouponMoneyDiff($assignedCouponMoney): void
|
||||
{
|
||||
$tempDiff = $this->actualReducedMoney - $assignedCouponMoney;
|
||||
foreach ($this->rangeGoodsList as &$goods) {
|
||||
if ($tempDiff < 1) break;
|
||||
$goods['coupon_money']++ && $tempDiff--;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\main\order;
|
||||
|
||||
use app\common\service\BaseService;
|
||||
|
||||
/**
|
||||
* 普通订单支付成功后的回调
|
||||
* Class PaySuccess
|
||||
* @package app\api\service\main\order
|
||||
*/
|
||||
class PaySuccess extends BaseService
|
||||
{
|
||||
/**
|
||||
* 回调方法
|
||||
* @param $order
|
||||
* @return bool
|
||||
*/
|
||||
public function onPaySuccess($order)
|
||||
{
|
||||
// 暂无业务逻辑
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,952 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\order;
|
||||
|
||||
use app\api\model\Order as OrderModel;
|
||||
|
||||
use app\api\model\User as UserModel;
|
||||
use app\api\model\Merchant as MerchantModel;
|
||||
use app\api\model\Goods as GoodsModel;
|
||||
use app\api\model\Setting as SettingModel;
|
||||
use app\api\model\UserCoupon as UserCouponModel;
|
||||
|
||||
use app\api\service\User as UserService;
|
||||
use app\api\service\Payment as PaymentService;
|
||||
use app\api\service\user\Grade as UserGradeService;
|
||||
use app\api\service\coupon\GoodsDeduct as GoodsDeductService;
|
||||
use app\api\service\points\GoodsDeduct as PointsDeductService;
|
||||
use app\api\service\order\source\checkout\Factory as CheckoutFactory;
|
||||
|
||||
use app\common\enum\Setting as SettingEnum;
|
||||
use app\common\enum\order\PayType as OrderPayTypeEnum;
|
||||
use app\common\enum\order\OrderStatus as OrderStatusEnum;
|
||||
use app\common\enum\order\OrderSource as OrderSourceEnum;
|
||||
use app\common\enum\order\DeliveryType as DeliveryTypeEnum;
|
||||
use app\common\lock\RedisLock;
|
||||
use app\common\service\BaseService;
|
||||
use app\common\service\delivery\Express as ExpressService;
|
||||
use app\common\service\goods\source\Factory as StockFactory;
|
||||
use app\common\library\helper;
|
||||
use app\common\service\Order as OrderService;
|
||||
use app\store\model\Company as CompanyModel;
|
||||
use cores\exception\BaseException;
|
||||
use think\facade\Db;
|
||||
use app\common\service\SybOil;
|
||||
use app\common\model\OrderGoods as OrderGoodsModel;
|
||||
|
||||
/**
|
||||
* 订单结算台服务类
|
||||
* Class Checkout
|
||||
* @package app\api\service\order
|
||||
*/
|
||||
class Checkout extends BaseService
|
||||
{
|
||||
/* $model OrderModel 订单模型 */
|
||||
public $model;
|
||||
|
||||
/* @var UserModel $user 当前用户信息 */
|
||||
private $user;
|
||||
|
||||
// 订单结算商品列表
|
||||
private $goodsList = [];
|
||||
|
||||
/**
|
||||
* 订单结算api参数
|
||||
* @var array
|
||||
*/
|
||||
private $param = [
|
||||
'delivery' => null, // 配送方式
|
||||
'couponId' => 0, // 用户的优惠券ID
|
||||
'isUsePoints' => 0, // 是否使用积分抵扣
|
||||
'remark' => '', // 买家留言
|
||||
'payType' => OrderPayTypeEnum::WECHAT, // 支付方式
|
||||
];
|
||||
|
||||
/**
|
||||
* 订单结算的规则
|
||||
* @var array
|
||||
*/
|
||||
private $checkoutRule = [
|
||||
'isUserGrade' => true, // 会员等级折扣
|
||||
'isCoupon' => true, // 优惠券抵扣
|
||||
'isUsePoints' => true, // 是否使用积分抵扣
|
||||
];
|
||||
|
||||
/**
|
||||
* 订单来源
|
||||
* @var array
|
||||
*/
|
||||
private $orderSource = [
|
||||
'source' => OrderSourceEnum::MAIN,
|
||||
'source_id' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* 订单结算数据
|
||||
* @var array
|
||||
*/
|
||||
private $orderData = [];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* Checkout constructor.
|
||||
* @throws BaseException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->user = UserService::getCurrentLoginUser(true);
|
||||
$this->model = new OrderModel;
|
||||
$this->storeId = $this->getStoreId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置结算台请求的参数
|
||||
* @param $param
|
||||
* @return array
|
||||
*/
|
||||
public function setParam($param): array
|
||||
{
|
||||
$this->param = array_merge($this->param, $param);
|
||||
return $this->getParam();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取结算台请求的参数
|
||||
* @return array
|
||||
*/
|
||||
public function getParam(): array
|
||||
{
|
||||
return $this->param;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单结算的规则
|
||||
* @param $data
|
||||
* @return $this
|
||||
*/
|
||||
public function setCheckoutRule($data): Checkout
|
||||
{
|
||||
$this->checkoutRule = array_merge($this->checkoutRule, $data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置订单来源(普通订单)
|
||||
* @param $data
|
||||
* @return $this
|
||||
*/
|
||||
public function setOrderSource($data): Checkout
|
||||
{
|
||||
$this->orderSource = array_merge($this->orderSource, $data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单确认-结算台
|
||||
* @param $goodsList
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function onCheckout($goodsList): array
|
||||
{
|
||||
// 订单确认-立即购买
|
||||
$this->goodsList = $goodsList;
|
||||
return $this->checkout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单结算台
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function checkout(): array
|
||||
{
|
||||
// 整理订单数据
|
||||
$this->orderData = $this->getOrderData();
|
||||
// 验证商品状态, 是否允许购买
|
||||
$this->validateGoodsList();
|
||||
// 订单商品总数量
|
||||
$orderTotalNum = (int)helper::getArrayColumnSum($this->goodsList, 'total_num');
|
||||
// 设置订单商品会员折扣价
|
||||
$this->setOrderGoodsGradeMoney();
|
||||
// 设置订单商品总金额(不含优惠折扣)
|
||||
$this->setOrderTotalPrice();
|
||||
// 当前用户可用的优惠券列表
|
||||
$couponList = $this->getUserCouponList((float)$this->orderData['orderTotalPrice']);
|
||||
// 计算优惠券抵扣
|
||||
$this->setOrderCouponMoney($couponList, (int)$this->param['couponId']);
|
||||
// 计算可用积分抵扣
|
||||
$this->setOrderPoints();
|
||||
// 计算订单商品的实际付款金额
|
||||
$this->setOrderGoodsPayPrice();
|
||||
// 设置默认配送方式
|
||||
if (!$this->param['delivery']) {
|
||||
$deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
|
||||
$this->param['delivery'] = current($deliveryType);
|
||||
}
|
||||
// 处理配送方式
|
||||
if ($this->param['delivery'] == DeliveryTypeEnum::EXPRESS) {
|
||||
$this->setOrderExpress();
|
||||
}
|
||||
// 计算订单最终金额
|
||||
$this->setOrderPayPrice();
|
||||
// 计算订单积分赠送数量
|
||||
$this->setOrderPointsBonus();
|
||||
// 返回订单数据
|
||||
return array_merge([
|
||||
'goodsList' => $this->goodsList, // 商品信息
|
||||
'orderTotalNum' => $orderTotalNum, // 商品总数量
|
||||
'couponList' => array_values($couponList), // 优惠券列表
|
||||
'hasError' => $this->hasError(),
|
||||
'errorMsg' => $this->getError(),
|
||||
], $this->orderData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单可用积分抵扣
|
||||
* @return void
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function setOrderPoints(): void
|
||||
{
|
||||
// 设置默认的商品积分抵扣信息
|
||||
$this->setDefaultGoodsPoints();
|
||||
// 积分设置
|
||||
$setting = SettingModel::getItem('points');
|
||||
// 条件:后台开启下单使用积分抵扣
|
||||
if (!$setting['is_shopping_discount'] || !$this->checkoutRule['isUsePoints']) {
|
||||
return;
|
||||
}
|
||||
// 条件:订单金额满足[?]元
|
||||
if (helper::bccomp($setting['discount']['full_order_price'], $this->orderData['orderTotalPrice']) === 1) {
|
||||
return;
|
||||
}
|
||||
// 计算订单商品最多可抵扣的积分数量
|
||||
$this->setOrderGoodsMaxPointsNum();
|
||||
// 订单最多可抵扣的积分总数量
|
||||
$maxPointsNumCount = (int)helper::getArrayColumnSum($this->goodsList, 'max_points_num');
|
||||
// 实际可抵扣的积分数量
|
||||
$actualPointsNum = min($maxPointsNumCount, $this->user['points']);
|
||||
if ($actualPointsNum < 1) {
|
||||
return;
|
||||
}
|
||||
// 计算订单商品实际抵扣的积分数量和金额
|
||||
$GoodsDeduct = new PointsDeductService($this->goodsList);
|
||||
$GoodsDeduct->setGoodsPoints($maxPointsNumCount, $actualPointsNum);
|
||||
// 积分抵扣总金额
|
||||
$orderPointsMoney = helper::getArrayColumnSum($this->goodsList, 'points_money');
|
||||
$this->orderData['pointsMoney'] = helper::number2($orderPointsMoney);
|
||||
// 积分抵扣总数量
|
||||
$this->orderData['pointsNum'] = $actualPointsNum;
|
||||
// 允许积分抵扣
|
||||
$this->orderData['isAllowPoints'] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单商品最多可抵扣的积分数量
|
||||
* @return void
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function setOrderGoodsMaxPointsNum(): void
|
||||
{
|
||||
// 积分设置
|
||||
$setting = SettingModel::getItem('points');
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
// 商品不允许积分抵扣
|
||||
if (!$goods['is_points_discount']) continue;
|
||||
// 积分抵扣比例
|
||||
$deductionRatio = helper::bcdiv($setting['discount']['max_money_ratio'], 100);
|
||||
// 最多可抵扣的金额
|
||||
$totalPayPrice = helper::bcsub($goods['total_price'], $goods['coupon_money']);
|
||||
$maxPointsMoney = helper::bcmul($totalPayPrice, $deductionRatio);
|
||||
// 最多可抵扣的积分数量
|
||||
$goods['max_points_num'] = helper::bcdiv($maxPointsMoney, $setting['discount']['discount_ratio'], 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认的商品积分抵扣信息
|
||||
* @return void
|
||||
*/
|
||||
private function setDefaultGoodsPoints(): void
|
||||
{
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
// 最多可抵扣的积分数量
|
||||
$goods['max_points_num'] = 0;
|
||||
// 实际抵扣的积分数量
|
||||
$goods['pointsNum'] = 0;
|
||||
// 实际抵扣的金额
|
||||
$goods['points_money'] = 0.00;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 整理订单数据(结算台初始化)
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getOrderData(): array
|
||||
{
|
||||
// 整理当前订单商品类型
|
||||
$types = helper::getArrayColumn($this->goodsList, 'oilCardType');
|
||||
// 系统支持的配送方式 (后台设置)
|
||||
$deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
|
||||
return [
|
||||
// 当前配送类型
|
||||
'delivery' => $this->param['delivery'] > 0 ? $this->param['delivery'] : $deliveryType[0],
|
||||
// 默认地址
|
||||
'address' => $this->user['address_default'],
|
||||
// 是否存在收货地址
|
||||
'existAddress' => $this->user['address_id'] > 0,
|
||||
// 配送费用
|
||||
'expressPrice' => 0.00,
|
||||
// 当前用户收货城市是否存在配送规则中
|
||||
'isIntraRegion' => true,
|
||||
// 是否允许使用积分抵扣
|
||||
'isAllowPoints' => false,
|
||||
// 是否使用积分抵扣
|
||||
'isUsePoints' => $this->param['isUsePoints'],
|
||||
// 积分抵扣金额
|
||||
'pointsMoney' => 0.00,
|
||||
// 赠送的积分数量
|
||||
'pointsBonus' => 0,
|
||||
// 支付方式
|
||||
'payType' => $this->param['payType'],
|
||||
// 订单类型
|
||||
'order_type' => $types[0] ?? '',
|
||||
// 系统设置 TODO: 废弃
|
||||
'setting' => $this->getSetting(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单页面中使用到的系统设置
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getSetting(): array
|
||||
{
|
||||
// 系统支持的配送方式 (后台设置)
|
||||
$deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
|
||||
// 积分设置
|
||||
$pointsSetting = SettingModel::getItem(SettingEnum::POINTS);
|
||||
return [
|
||||
'deliveryType' => $deliveryType, // 支持的配送方式
|
||||
'points_name' => $pointsSetting['points_name'], // 积分名称
|
||||
'points_describe' => $pointsSetting['describe'], // 积分说明
|
||||
];
|
||||
}
|
||||
|
||||
// 获取订单结算时的个人信息
|
||||
public function getPersonal(): array
|
||||
{
|
||||
return [
|
||||
'user_id' => $this->user['user_id'],
|
||||
'balance' => $this->user['balance'],
|
||||
'points' => $this->user['points'],
|
||||
'address_id' => $this->user['address_id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前用户可用的优惠券列表
|
||||
* @param float $orderTotalPrice 总金额
|
||||
* @return array|mixed
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
private function getUserCouponList(float $orderTotalPrice)
|
||||
{
|
||||
// 是否开启优惠券折扣
|
||||
if (!$this->checkoutRule['isCoupon']) {
|
||||
return [];
|
||||
}
|
||||
// 整理当前订单所有商品ID集
|
||||
$orderGoodsIds = helper::getArrayColumn($this->goodsList, 'goods_id');
|
||||
// 当前用户可用的优惠券列表
|
||||
$couponList = UserCouponModel::getUserCouponList($this->user['user_id'], $orderTotalPrice);
|
||||
// 判断当前优惠券是否满足订单使用条件 (优惠券适用范围)
|
||||
return UserCouponModel::couponListApplyRange($couponList, $orderGoodsIds);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证订单商品的状态
|
||||
* @return void
|
||||
*/
|
||||
private function validateGoodsList(): void
|
||||
{
|
||||
$Checkout = CheckoutFactory::getFactory(
|
||||
$this->user,
|
||||
$this->goodsList,
|
||||
$this->orderSource['source'],
|
||||
$this->orderData
|
||||
);
|
||||
$status = $Checkout->validateGoodsList();
|
||||
$status == false && $this->setError($Checkout->getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置订单的商品总金额(不含优惠折扣)
|
||||
*/
|
||||
private function setOrderTotalPrice()
|
||||
{
|
||||
// 订单商品的总金额(不含优惠券折扣)
|
||||
$this->orderData['orderTotalPrice'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_price'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置订单的实际支付金额(含配送费)
|
||||
*/
|
||||
private function setOrderPayPrice()
|
||||
{
|
||||
// 订单金额(含优惠折扣)
|
||||
$this->orderData['orderPrice'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_pay_price'));
|
||||
// 订单实付款金额(订单金额 + 运费)
|
||||
$this->orderData['orderPayPrice'] = helper::number2(helper::bcadd($this->orderData['orderPrice'], $this->orderData['expressPrice']));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单积分赠送数量
|
||||
* @return void
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function setOrderPointsBonus(): void
|
||||
{
|
||||
// 初始化商品积分赠送数量
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
$goods['points_bonus'] = 0;
|
||||
}
|
||||
// 积分设置
|
||||
$setting = SettingModel::getItem('points');
|
||||
// 条件:后台开启开启购物送积分
|
||||
if (!$setting['is_shopping_gift']) {
|
||||
return;
|
||||
}
|
||||
// 设置商品积分赠送数量
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
// 积分赠送比例
|
||||
$ratio = helper::bcdiv($setting['gift_ratio'], 100);
|
||||
// 计算抵扣积分数量
|
||||
$goods['points_bonus'] = !$goods['is_points_gift'] ? 0 : helper::bcmul($goods['total_pay_price'], $ratio, 0);
|
||||
}
|
||||
// 订单积分赠送数量
|
||||
$this->orderData['pointsBonus'] = (int)helper::getArrayColumnSum($this->goodsList, 'points_bonus');
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单商品的实际付款金额
|
||||
* @return void
|
||||
*/
|
||||
private function setOrderGoodsPayPrice(): void
|
||||
{
|
||||
// 商品总价 - 优惠抵扣
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
// 减去优惠券抵扣金额
|
||||
$value = helper::bcsub($goods['total_price'], $goods['coupon_money']);
|
||||
// 减去积分抵扣金额
|
||||
if ($this->orderData['isAllowPoints'] && $this->orderData['isUsePoints']) {
|
||||
$value = helper::bcsub($value, $goods['points_money']);
|
||||
}
|
||||
$goods['total_pay_price'] = helper::number2($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置订单商品会员折扣价
|
||||
* @return void
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function setOrderGoodsGradeMoney(): void
|
||||
{
|
||||
// 设置默认数据
|
||||
helper::setDataAttribute($this->goodsList, [
|
||||
// 标记参与会员折扣
|
||||
'is_user_grade' => false,
|
||||
// 会员等级抵扣的金额
|
||||
'grade_ratio' => 0,
|
||||
// 会员折扣的商品单价
|
||||
'grade_goods_price' => 0.00,
|
||||
// 会员折扣的总额差
|
||||
'grade_total_money' => 0.00,
|
||||
], true);
|
||||
|
||||
// 是否开启会员等级折扣
|
||||
if (!$this->checkoutRule['isUserGrade']) {
|
||||
return;
|
||||
}
|
||||
// 获取当前登录用户的会员等级信息
|
||||
$gradeInfo = UserGradeService::getCurrentGradeInfo();
|
||||
// 判断商品是否参与会员折扣
|
||||
if (empty($gradeInfo)) {
|
||||
return;
|
||||
}
|
||||
// 计算抵扣金额
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
// 判断商品是否参与会员折扣
|
||||
if (!$goods['is_enable_grade']) {
|
||||
continue;
|
||||
}
|
||||
// 折扣比例
|
||||
$discountRatio = $gradeInfo['equity']['discount'];
|
||||
// 商品单独设置了会员折扣
|
||||
if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$this->user['grade_id']])) {
|
||||
$discountRatio = $goods['alone_grade_equity'][$gradeInfo['grade_id']];
|
||||
}
|
||||
if (empty($discountRatio)) {
|
||||
continue;
|
||||
}
|
||||
// 会员折扣后的商品总金额
|
||||
$gradeTotalPrice = UserGradeService::getDiscountPrice($goods['total_price'], $discountRatio);
|
||||
helper::setDataAttribute($goods, [
|
||||
'is_user_grade' => true,
|
||||
'grade_ratio' => $discountRatio,
|
||||
'grade_goods_price' => UserGradeService::getDiscountPrice($goods['goods_price'], $discountRatio),
|
||||
'grade_total_money' => helper::bcsub($goods['total_price'], $gradeTotalPrice),
|
||||
'total_price' => $gradeTotalPrice,
|
||||
], false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置订单优惠券抵扣信息
|
||||
* @param array $couponList 当前用户可用的优惠券列表
|
||||
* @param int $userCouponId 当前选择的优惠券ID
|
||||
* @return void
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function setOrderCouponMoney(array $couponList, int $userCouponId): void
|
||||
{
|
||||
// 默认选择优惠券
|
||||
if (empty($userCouponId)) {
|
||||
foreach ($couponList as $item) {
|
||||
if (!empty($item['is_apply'])) { // 如果 is_apply 为 true 或非空
|
||||
$userCouponId = $item['user_coupon_id'] ?? 0;
|
||||
break; // 找到第一个就退出
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置默认数据:订单信息
|
||||
helper::setDataAttribute($this->orderData, [
|
||||
'couponId' => 0, // 用户的优惠券ID
|
||||
'couponMoney' => 0, // 优惠券抵扣金额
|
||||
], false);
|
||||
// 设置默认数据:订单商品列表
|
||||
helper::setDataAttribute($this->goodsList, [
|
||||
'coupon_money' => 0, // 优惠券抵扣金额
|
||||
], true);
|
||||
// 验证选择的优惠券ID是否合法
|
||||
if (!$this->verifyOrderCouponId($userCouponId, $couponList)) {
|
||||
return;
|
||||
}
|
||||
// 获取优惠券信息
|
||||
$couponInfo = $this->getCouponInfo($userCouponId, $couponList);
|
||||
// 计算订单商品优惠券抵扣金额
|
||||
$goodsListTemp = helper::getArrayColumns($this->goodsList, ['goods_id', 'goods_sku_id', 'total_price']);
|
||||
$CouponMoney = new GoodsDeductService;
|
||||
$rangeGoodsList = $CouponMoney->setGoodsList($goodsListTemp)
|
||||
->setCouponInfo($couponInfo)
|
||||
->setGoodsCouponMoney()
|
||||
->getRangeGoodsList();
|
||||
// 分配订单商品优惠券抵扣金额
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
$goodsKey = "{$goods['goods_id']}-{$goods['goods_sku_id']}";
|
||||
if (isset($rangeGoodsList[$goodsKey])) {
|
||||
$goods['coupon_money'] = helper::bcdiv($rangeGoodsList[$goodsKey]['coupon_money'], 100);
|
||||
}
|
||||
}
|
||||
|
||||
// 记录订单优惠券信息
|
||||
$this->orderData['couponId'] = $userCouponId;
|
||||
$this->orderData['couponMoney'] = helper::number2(helper::bcdiv($CouponMoney->getActualReducedMoney(), 100));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户选择的优惠券ID是否合法
|
||||
* @param int $userCouponId
|
||||
* @param $couponList
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function verifyOrderCouponId(int $userCouponId, $couponList): bool
|
||||
{
|
||||
// 是否开启优惠券折扣
|
||||
if (!$this->checkoutRule['isCoupon']) {
|
||||
return false;
|
||||
}
|
||||
// 如果没有可用的优惠券,直接返回
|
||||
if ($userCouponId <= 0 || empty($couponList)) {
|
||||
return false;
|
||||
}
|
||||
// 判断优惠券是否存在
|
||||
$couponInfo = $this->getCouponInfo($userCouponId, $couponList);
|
||||
if (!$couponInfo) {
|
||||
throwError('未找到优惠券信息');
|
||||
}
|
||||
// 判断优惠券适用范围是否合法
|
||||
if (!$couponInfo['is_apply']) {
|
||||
throwError($couponInfo['not_apply_info']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定的优惠券信息
|
||||
* @param int $userCouponId 优惠券ID
|
||||
* @param array $couponList 优惠券列表
|
||||
* @return false|mixed
|
||||
*/
|
||||
private function getCouponInfo(int $userCouponId, array $couponList)
|
||||
{
|
||||
return helper::getArrayItemByColumn($couponList, 'user_coupon_id', $userCouponId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单配送-快递配送
|
||||
* @return void
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function setOrderExpress(): void
|
||||
{
|
||||
// 设置默认数据:配送费用
|
||||
helper::setDataAttribute($this->goodsList, [
|
||||
'expressPrice' => 0,
|
||||
], true);
|
||||
// 当前用户收货城市id
|
||||
$cityId = $this->user['address_default'] ? (int)$this->user['address_default']['city_id'] : 0;
|
||||
// 初始化配送服务类
|
||||
$ExpressService = new ExpressService($cityId, $this->goodsList);
|
||||
// 验证商品是否在配送范围
|
||||
$isIntraRegion = $ExpressService->isIntraRegion();
|
||||
if ($cityId > 0 && $isIntraRegion == false) {
|
||||
$notInRuleGoodsName = $ExpressService->getNotInRuleGoodsName();
|
||||
$this->setError("很抱歉,您的收货地址不在商品 [{$notInRuleGoodsName}] 的配送范围内");
|
||||
}
|
||||
// 订单总运费金额
|
||||
$this->orderData['isIntraRegion'] = $isIntraRegion;
|
||||
$this->orderData['expressPrice'] = $ExpressService->getDeliveryFee();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新订单
|
||||
* @param array $order 订单信息
|
||||
* @return bool
|
||||
*/
|
||||
public function createOrder(array $order): bool
|
||||
{
|
||||
// 表单验证
|
||||
if (!$this->validateOrderForm($order)) {
|
||||
return false;
|
||||
}
|
||||
// 创建新的订单
|
||||
$status = $this->model->transaction(function () use ($order) {
|
||||
// 创建订单事件
|
||||
if ($this->createOrderEvent($order)) {
|
||||
// 余额支付标记订单已支付
|
||||
if ($order['payType'] == OrderPayTypeEnum::BALANCE && !$this->model->onPaymentByBalance($this->model['order_no'])) {
|
||||
throwError($this->error);
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// 抛出异常以触发事务回滚
|
||||
throwError($this->error);
|
||||
}
|
||||
});
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建订单事件
|
||||
* @param $order
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function createOrderEvent($order): bool
|
||||
{
|
||||
// 新增订单记录
|
||||
$status = $this->add($order, $this->param['remark']);
|
||||
/*if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
|
||||
// 记录收货地址
|
||||
$this->saveOrderAddress($order['address']);
|
||||
}*/
|
||||
// 保存订单商品信息
|
||||
$goodsStatus = $this->saveOrderGoods($order);
|
||||
|
||||
// 更新商品库存 (针对下单减库存的商品)
|
||||
$this->updateGoodsStockNum($order);
|
||||
// 设置优惠券使用状态
|
||||
$order['couponId'] > 0 && UserCouponModel::setIsUse((int)$order['couponId']);
|
||||
// 积分抵扣情况下扣除用户积分
|
||||
if ($order['isAllowPoints'] && $order['isUsePoints'] && $order['pointsNum'] > 0) {
|
||||
$describe = "用户消费:{$this->model['order_no']}";
|
||||
UserModel::setIncPoints($this->user['user_id'], -$order['pointsNum'], $describe);
|
||||
}
|
||||
// 获取订单详情
|
||||
// $detail = OrderModel::getUserOrderDetail((int)$this->model['order_id']);
|
||||
|
||||
return $status && $goodsStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建支付请求的参数
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function onOrderPayment(): array
|
||||
{
|
||||
return PaymentService::orderPayment($this->model, $this->param['payType']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单验证 (订单提交)
|
||||
* @param array $order 订单信息
|
||||
* @return bool
|
||||
*/
|
||||
private function validateOrderForm(array $order): bool
|
||||
{
|
||||
/*if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
|
||||
if (empty($order['address'])) {
|
||||
$this->error = '您还没有选择配送地址';
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
// 余额支付时判断用户余额是否足够
|
||||
/*if ($order['payType'] != OrderPayTypeEnum::BALANCE) {
|
||||
$this->error = '目前仅支持余额支付';
|
||||
return false;
|
||||
}*/
|
||||
// 余额支付时判断用户余额是否足够
|
||||
if ($order['payType'] == OrderPayTypeEnum::BALANCE) {
|
||||
if ($this->user['balance'] < $order['orderPayPrice']) {
|
||||
$this->error = '您的余额不足,无法使用余额支付';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前订单是否存在和使用积分抵扣
|
||||
* @param $order
|
||||
* @return bool
|
||||
*/
|
||||
private function isExistPointsDeduction($order): bool
|
||||
{
|
||||
return $order['isAllowPoints'] && $order['isUsePoints'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增订单记录
|
||||
* @param $order
|
||||
* @param string $remark
|
||||
* @return bool|false
|
||||
*/
|
||||
private function add($order, string $remark = ''): bool
|
||||
{
|
||||
// 当前订单是否存在和使用积分抵扣
|
||||
$isExistPointsDeduction = $this->isExistPointsDeduction($order);
|
||||
// 订单数据
|
||||
$data = [
|
||||
'user_id' => $this->user['user_id'],
|
||||
'order_no' => $this->model->orderNo(),
|
||||
'order_type' => $order['order_type'],
|
||||
'total_price' => $order['orderTotalPrice'],
|
||||
'order_price' => $order['orderPrice'],
|
||||
'coupon_id' => $order['couponId'],
|
||||
'coupon_money' => $order['couponMoney'],
|
||||
'points_money' => $isExistPointsDeduction ? $order['pointsMoney'] : 0.00,
|
||||
'points_num' => $isExistPointsDeduction ? $order['pointsNum'] : 0,
|
||||
'pay_price' => $order['orderPayPrice'],
|
||||
'delivery_type' => $order['delivery'],
|
||||
'pay_type' => $order['payType'],
|
||||
'buyer_remark' => trim($remark),
|
||||
'order_source' => $this->orderSource['source'],
|
||||
'order_source_id' => $this->orderSource['source_id'],
|
||||
'points_bonus' => $order['pointsBonus'],
|
||||
'order_status' => OrderStatusEnum::NORMAL,
|
||||
'platform' => getPlatform(),
|
||||
'store_id' => $this->storeId,
|
||||
];
|
||||
if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
|
||||
$data['express_price'] = $order['expressPrice'];
|
||||
}
|
||||
// 计算返佣
|
||||
if($this->user['referrer_id'] > 0) {
|
||||
// 分销设置
|
||||
$setting = SettingModel::getItem('promotion');
|
||||
// 查询一级返佣
|
||||
$spread = MerchantModel::detail($this->user['referrer_id']);
|
||||
$data['spread_uid'] = $spread['merchant_id'];
|
||||
$data['one_brokerage_ratio'] = round($setting['one_brokerage_ratio']/100,2);
|
||||
$data['one_brokerage'] = round($data['pay_price']*$data['one_brokerage_ratio'],2);
|
||||
// 查询二级返佣
|
||||
if($spread['parent_merchant_id'] > 0) {
|
||||
$spreadTwo = MerchantModel::detail($spread['parent_merchant_id']);
|
||||
$data['spread_two_uid'] = $spreadTwo['merchant_id'];
|
||||
$data['two_brokerage_ratio'] = round($setting['two_brokerage_ratio']/100,2);
|
||||
$data['two_brokerage'] = round($data['pay_price']*$data['two_brokerage_ratio'],2);
|
||||
}
|
||||
}
|
||||
// 保存订单记录
|
||||
return $this->model->save($data);
|
||||
}
|
||||
|
||||
public static function createOrderNo(): string
|
||||
{
|
||||
// 获取当前时间戳到毫秒(13位)
|
||||
$time = (int)(microtime(true) * 1000); // 返回形如 1716544103986
|
||||
|
||||
// 拼接一个 4 位随机数
|
||||
$rand = mt_rand(1000, 9999);
|
||||
|
||||
return (string)($time . $rand); // 拼接成纯数字字符串
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存订单商品信息
|
||||
* @param $order
|
||||
* @return bool
|
||||
*/
|
||||
private function saveOrderGoods($order): bool
|
||||
{
|
||||
// 当前订单是否存在和使用积分抵扣
|
||||
$isExistPointsDeduction = $this->isExistPointsDeduction($order);
|
||||
// 订单商品列表
|
||||
$goodsList = [];
|
||||
// 查询一级返佣
|
||||
$spread = MerchantModel::detail($this->user['referrer_id']);
|
||||
$spread_uid = $spread ? $spread['merchant_id'] : 0;
|
||||
$spread_two_uid = 0;
|
||||
// 查询二级返佣
|
||||
if($spread && $spread['parent_merchant_id'] > 0) {
|
||||
$spreadTwo = MerchantModel::detail($spread['parent_merchant_id']);
|
||||
$spread_two_uid = $spreadTwo ? $spreadTwo['merchant_id'] : 0;
|
||||
}
|
||||
// 分销设置
|
||||
$setting = SettingModel::getItem('promotion');
|
||||
// 一级分销
|
||||
$one_brokerage_ratio = round($setting['one_brokerage_ratio']/100,2);
|
||||
// 二级分销
|
||||
$two_brokerage_ratio = round($setting['two_brokerage_ratio']/100,2);
|
||||
foreach ($order['goodsList'] as $goods) {
|
||||
/* @var GoodsModel $goods */
|
||||
$item = [
|
||||
'oilCardNo' => 'OIL' . self::createOrderNo(),
|
||||
'user_id' => $this->user['user_id'],
|
||||
'spread_uid' => $spread_uid,
|
||||
'spread_two_uid' => $spread_two_uid,
|
||||
'one_brokerage_ratio' => $one_brokerage_ratio,
|
||||
'one_brokerage' => ($goods['total_price'] > 0 && $one_brokerage_ratio > 0) ? round($goods['total_price'] * $one_brokerage_ratio, 2) : 0,
|
||||
'two_brokerage_ratio' => $two_brokerage_ratio,
|
||||
'two_brokerage' => ($goods['total_price'] > 0 && $two_brokerage_ratio > 0) ? round($goods['total_price'] * $two_brokerage_ratio,2) : 0,
|
||||
'province' => $goods['province'],
|
||||
'store_id' => $this->storeId,
|
||||
'goods_id' => $goods['goods_id'],
|
||||
'goods_name' => $goods['goods_name'],
|
||||
'goods_no' => $goods['goods_no'] ?: '',
|
||||
'image_id' => $this->getGoodsImageId($goods),
|
||||
'deduct_stock_type' => $goods['deduct_stock_type'],
|
||||
'spec_type' => $goods['spec_type'],
|
||||
'goods_sku_id' => $goods['skuInfo']['goods_sku_id'],
|
||||
'goods_props' => $goods['skuInfo']['goods_props'] ?: '',
|
||||
'content' => $goods['content'] ?? '',
|
||||
'goods_sku_no' => $goods['skuInfo']['goods_sku_no'] ?: '',
|
||||
'goods_price' => $goods['skuInfo']['goods_price'],
|
||||
'line_price' => $goods['skuInfo']['line_price'],
|
||||
'goods_weight' => $goods['skuInfo']['goods_weight'],
|
||||
'is_user_grade' => (int)$goods['is_user_grade'],
|
||||
'grade_ratio' => $goods['grade_ratio'],
|
||||
'grade_goods_price' => $goods['grade_goods_price'],
|
||||
'grade_total_money' => $goods['grade_total_money'],
|
||||
'coupon_money' => $goods['coupon_money'],
|
||||
'points_money' => $isExistPointsDeduction ? $goods['points_money'] : 0.00,
|
||||
'points_num' => $isExistPointsDeduction ? $goods['points_num'] : 0,
|
||||
'points_bonus' => $goods['points_bonus'],
|
||||
'total_num' => $goods['total_num'],
|
||||
'total_price' => $goods['total_price'],
|
||||
'total_pay_price' => $goods['total_pay_price'],
|
||||
'oilCardType' => $goods['oilCardType'],
|
||||
'expiration_time' => strtotime("+15 days")
|
||||
];
|
||||
// 记录订单商品来源id
|
||||
$item['goods_source_id'] = isset($goods['goods_source_id']) ? $goods['goods_source_id'] : 0;
|
||||
$goodsList[] = $item;
|
||||
}
|
||||
$this->model->goods()->saveAll($goodsList) !== false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单商品的封面图(优先sku封面图)
|
||||
* @param $goods
|
||||
* @return int
|
||||
*/
|
||||
private function getGoodsImageId($goods): int
|
||||
{
|
||||
return $goods['skuInfo']['image_id'] ?: (int)current($goods['goods_images'])['file_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新商品库存 (针对下单减库存的商品)
|
||||
* @param $order
|
||||
* @return void
|
||||
*/
|
||||
private function updateGoodsStockNum($order): void
|
||||
{
|
||||
StockFactory::getFactory($this->model['order_source'])->updateGoodsStock($order['goodsList']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录收货地址
|
||||
* @param $address
|
||||
* @return void
|
||||
*/
|
||||
private function saveOrderAddress($address): void
|
||||
{
|
||||
$this->model->address()->save([
|
||||
'user_id' => $this->user['user_id'],
|
||||
'store_id' => $this->storeId,
|
||||
'name' => $address['name'],
|
||||
'phone' => $address['phone'],
|
||||
'province_id' => $address['province_id'],
|
||||
'city_id' => $address['city_id'],
|
||||
'region_id' => $address['region_id'],
|
||||
'detail' => $address['detail'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\order;
|
||||
|
||||
use app\api\model\Goods as GoodsModel;
|
||||
use app\common\library\helper;
|
||||
use app\common\lock\RedisLock;
|
||||
use app\common\model\Coupon;
|
||||
use app\common\service\SybOil;
|
||||
use app\common\service\IdingjuOil;
|
||||
use app\store\model\Company as CompanyModel;
|
||||
use app\store\model\Coupon as CouponModel;
|
||||
use app\store\model\GoodsCoupon as GoodsCouponModel;
|
||||
use app\common\model\UserCoupon as UserCouponModel;
|
||||
use think\facade\Db;
|
||||
use think\facade\Event;
|
||||
use app\common\service\BaseService;
|
||||
use app\api\model\User as UserModel;
|
||||
use app\api\model\Order as OrderModel;
|
||||
use app\api\model\user\BalanceLog as BalanceLogModel;
|
||||
use app\common\service\goods\source\Factory as StockFactory;
|
||||
use app\common\enum\OrderType as OrderTypeEnum;
|
||||
use app\common\enum\order\PayStatus as PayStatusEnum;
|
||||
use app\common\enum\order\PayType as OrderPayTypeEnum;
|
||||
use app\common\enum\user\balanceLog\Scene as SceneEnum;
|
||||
|
||||
/**
|
||||
* 订单支付成功服务类
|
||||
* Class PaySuccess
|
||||
* @package app\api\service\order
|
||||
*/
|
||||
class PaySuccess extends BaseService
|
||||
{
|
||||
// 订单模型
|
||||
public $model;
|
||||
|
||||
// 当前用户信息
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* PaySuccess constructor.
|
||||
* @param $orderNo
|
||||
*/
|
||||
public function __construct($orderNo)
|
||||
{
|
||||
parent::__construct();
|
||||
// 实例化订单模型
|
||||
$this->model = OrderModel::getPayDetail($orderNo);
|
||||
// 获取用户信息
|
||||
$this->user = UserModel::detail($this->model['user_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单详情
|
||||
* @return OrderModel|null
|
||||
*/
|
||||
public function getOrderInfo()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单支付成功业务处理
|
||||
* @param $payType
|
||||
* @param array $payData
|
||||
* @return bool
|
||||
*/
|
||||
public function onPaySuccess($payType, $payData = [])
|
||||
{
|
||||
if (empty($this->model)) {
|
||||
$this->error = '未找到该订单信息';
|
||||
return false;
|
||||
}
|
||||
// 更新付款状态
|
||||
$status = $this->updatePayStatus($payType, $payData);
|
||||
// 订单支付成功事件
|
||||
if ($status == true) {
|
||||
Event::trigger('OrderPaySuccess', ['order' => $this->model, 'orderType' => OrderTypeEnum::ORDER]);
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新付款状态
|
||||
* @param $payType
|
||||
* @param array $payData
|
||||
* @return bool
|
||||
*/
|
||||
private function updatePayStatus($payType, $payData = [])
|
||||
{
|
||||
// 验证余额支付时用户余额是否满足
|
||||
if ($payType == OrderPayTypeEnum::BALANCE) {
|
||||
if ($this->user['balance'] < $this->model['pay_price']) {
|
||||
$this->error = '用户余额不足,无法使用余额支付';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 获取订单详情
|
||||
$detail = OrderModel::getUserOrderDetail((int)$this->model['order_id']);
|
||||
// 触发分油接口
|
||||
$shareStatus = $this->shareOil($detail);
|
||||
if (!$shareStatus) {
|
||||
// 记录日志
|
||||
log_record(['name' => '分油失败', [
|
||||
'order_id' => $this->model['order_id'],
|
||||
]]);
|
||||
// return false;
|
||||
}
|
||||
// 事务处理
|
||||
$this->model->transaction(function () use ($payType, $payData, $detail) {
|
||||
// 更新订单状态
|
||||
$this->updateOrderInfo($payType, $payData);
|
||||
// 累积用户总消费金额
|
||||
UserModel::setIncPayMoney($detail['user_id'], (float)$this->model['pay_price']);
|
||||
// 记录订单支付信息
|
||||
$this->updatePayInfo($payType);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public function processUserOrders($companyId) {
|
||||
$redisLock = new RedisLock('user_order_lock');
|
||||
$maxAttempts = 10; // 最大尝试次数
|
||||
$attempt = 0;
|
||||
$sleepTime = 500000; // 每次尝试失败后的等待时间(微秒),500000微秒 = 0.5秒
|
||||
|
||||
while ($attempt < $maxAttempts) {
|
||||
$lockAcquired = $redisLock->acquireLock();
|
||||
if ($lockAcquired) {
|
||||
break;
|
||||
}
|
||||
|
||||
$attempt++;
|
||||
usleep($sleepTime); // 等待一段时间再尝试
|
||||
}
|
||||
|
||||
if (!$lockAcquired) {
|
||||
throwError('网络繁忙请稍后');
|
||||
}
|
||||
|
||||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||||
// 查找一个未被调用的用户
|
||||
$account = Db::name('account')
|
||||
->alias('a')
|
||||
->join('company c', 'a.company_id = c.company_id')
|
||||
->where('a.last_called_at', '<', $todayStart)
|
||||
->where('a.is_delete', '=', 0)
|
||||
->where('a.state', '=', 0)
|
||||
->where('a.company_id', '=', $companyId)
|
||||
->where('c.status', '=', 10)
|
||||
->field('a.*')
|
||||
->limit(1)
|
||||
->find();
|
||||
if (!$account) {
|
||||
throwError("没有可用的用户");
|
||||
}
|
||||
// 更新用户的 `last_called_at`
|
||||
Db::name('account')
|
||||
->where('account_id', $account['account_id'])
|
||||
->update(['last_called_at' => time(), 'state' => 1]);
|
||||
|
||||
$redisLock->releaseLock();
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分油
|
||||
* @param $order
|
||||
* @return bool
|
||||
*/
|
||||
// private function shareOil($order): bool
|
||||
// {
|
||||
// $SybOil = new SybOil();
|
||||
// foreach ($order->goods as $goods) {
|
||||
// /* @var GoodsModel $goods */
|
||||
// if($goods['oilCardType']) {
|
||||
// $data['amount'] = $goods['total_price']; // 金额
|
||||
// try {
|
||||
// // 查找一个未被调用的用户
|
||||
// $account = $this->processUserOrders();
|
||||
// } catch (\Throwable $e) {
|
||||
// $this->error = $e->getMessage();
|
||||
// return false;
|
||||
// }
|
||||
// $company = CompanyModel::detail($account['company_id']);
|
||||
// // 查询公司余额
|
||||
// if ($data['amount'] > $company['balance']) {
|
||||
// $this->error = '公司余额不足';
|
||||
// return false;
|
||||
// }
|
||||
// // 对接平台接口
|
||||
// $res = $SybOil->oilOilCardShare(['goodsNo'=>$goods['goods_no'],'amount'=>$data['amount'],'userNo'=>$account['userNo'],'mobile'=>$account['mobile'],'saleOrderNo'=>$this->model['order_no'],'oilCardType'=>$goods['oilCardType']], $company);
|
||||
// //$res = json_decode('{"code":"00","message":"请求成功","errCode":null,"errMessage":null,"data":{"oilCardNo":null,"amount":100,"balance":null,"shareNo":"CDV2408021928160001"}}', true);
|
||||
// if ($res['code'] !== '00') {
|
||||
// $this->error = ($res['errMessage'] ?? $res['message']);
|
||||
// return false;
|
||||
// }
|
||||
// $item['account_id'] = $account['account_id'];
|
||||
// $item['userNo'] = $account['userNo'];
|
||||
// $item['mobile'] = $account['mobile'];
|
||||
// $item['company_id'] = $company['company_id'];
|
||||
// $item['oilCardNo'] = $goods['oilCardType'] == 'zsy_ticket' ? $res['data']['shareNo'] : $res['data']['oilCardNo'];
|
||||
// $item['shareNo'] = $res['data']['shareNo'] ?? '';
|
||||
// $item['oil_time'] = time();
|
||||
// $item['oil_status'] = 1;
|
||||
// $goods->save($item);
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
/**
|
||||
* @param $goodsNo
|
||||
* @return string
|
||||
*/
|
||||
public function getCompany($goodsNo): string
|
||||
{
|
||||
switch ($goodsNo) {
|
||||
case 'djgdface100':
|
||||
case 'djgdface200':
|
||||
case 'djgdface300':
|
||||
return 'umQZhIHKofyA';
|
||||
case 'jmgxyyface100':
|
||||
case 'jmgxyyface200':
|
||||
case 'jmgxyyface300':
|
||||
|
||||
case 'jmgzface100':
|
||||
case 'jmgzface200':
|
||||
case 'jmgzface300':
|
||||
|
||||
case 'jmfjface100':
|
||||
case 'jmfjface200':
|
||||
case 'jmfjface300':
|
||||
return 'HpWqXIgYvTBg';
|
||||
case 'face003':
|
||||
return 'NKJBgaaRNdiL'; // 测试参数
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
private function shareOil($order): bool
|
||||
{
|
||||
$IdingjuOil = new IdingjuOil();
|
||||
foreach ($order->goods as $goods) {
|
||||
/* @var GoodsModel $goods */
|
||||
if($goods['oilCardType'] == 'zsy_ticket') {
|
||||
$data['amount'] = $goods['total_price']; // 金额
|
||||
$company = CompanyModel::detail(['orgNo' => $this->getCompany($goods['goods_no'])]);
|
||||
if (!$company) {
|
||||
$this->error = '没有可用参数';
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// 查找一个未被调用的用户
|
||||
$account = $this->processUserOrders($company['company_id']);
|
||||
} catch (\Throwable $e) {
|
||||
$this->error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 查询公司余额
|
||||
/*if ($data['amount'] > $company['balance']) {
|
||||
$this->error = '公司余额不足';
|
||||
return false;
|
||||
}*/
|
||||
// 对接平台接口
|
||||
$res = $IdingjuOil->oilOilCardShare([
|
||||
'goodsNo'=>$goods['goods_no'],
|
||||
'amount'=>$data['amount'],
|
||||
'userNo'=>$account['userNo'],
|
||||
'mobile'=>$account['mobile'],
|
||||
'saleOrderNo'=>$goods['oilCardNo'],
|
||||
'oilCardType'=>$goods['oilCardType']
|
||||
], $company);
|
||||
//$res = json_decode('{"code":"00","message":"请求成功","errCode":null,"errMessage":null,"data":{"oilCardNo":null,"amount":100,"balance":null,"shareNo":"CDV2408021928160001"}}', true);
|
||||
if ($res['code'] != 1) {
|
||||
$this->error = ($res['errMessage'] ?? $res['message']);
|
||||
return false;
|
||||
}
|
||||
|
||||
$item['account_id'] = $account['account_id'];
|
||||
$item['userNo'] = $account['userNo'];
|
||||
$item['mobile'] = $account['mobile'];
|
||||
$item['company_id'] = $company['company_id'];
|
||||
$item['shareNo'] = $res['data']['trade_no'] ?? '';
|
||||
$item['qrCode'] = $res['data']['useUrl'] ?? '';
|
||||
$item['oil_status_zsy'] = $res['data']['state'] ?? 0;
|
||||
$item['oil_time'] = time();
|
||||
$item['oil_status'] = $item['oil_status_zsy'] == 2 ? 1 : 0; // 已发券 为1
|
||||
$goods->save($item);
|
||||
} elseif($goods['oilCardType'] == 'coupon'){
|
||||
// 发放优惠券
|
||||
$coupons = GoodsCouponModel::getCouponList($goods['goods_id'])->toArray();
|
||||
if($coupons && is_array($coupons)) {
|
||||
foreach ($coupons as $item) {
|
||||
$coupon = CouponModel::detail($item['coupon_id']);
|
||||
for ($i=0;$i<$item['send_num'];$i++){
|
||||
$userCouponModel = new UserCouponModel();
|
||||
$userCouponModel->add($goods['user_id'], $coupon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订单记录
|
||||
* @param int $payType
|
||||
* @param array $payData
|
||||
* @return false|int
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function updateOrderInfo(int $payType, array $payData)
|
||||
{
|
||||
// 更新商品库存、销量
|
||||
StockFactory::getFactory($this->model['order_source'])->updateStockSales($this->model['goods']);
|
||||
// 整理订单信息
|
||||
$order = [
|
||||
'pay_type' => $payType,
|
||||
'pay_status' => PayStatusEnum::SUCCESS,
|
||||
'pay_time' => time()
|
||||
];
|
||||
if ($payType == OrderPayTypeEnum::WECHAT) {
|
||||
$order['transaction_id'] = $payData['transaction_id'];
|
||||
}
|
||||
// 更新订单状态
|
||||
return $this->model->save($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录订单支付信息
|
||||
* @param int $payType
|
||||
*/
|
||||
private function updatePayInfo(int $payType)
|
||||
{
|
||||
// 余额支付
|
||||
if ($payType == OrderPayTypeEnum::BALANCE) {
|
||||
// 更新用户余额
|
||||
UserModel::setDecBalance((int)$this->user['user_id'], (float)$this->model['pay_price']);
|
||||
// 新增余额变动记录
|
||||
BalanceLogModel::add(SceneEnum::CONSUME, [
|
||||
'user_id' => (int)$this->user['user_id'],
|
||||
'money' => -$this->model['pay_price'],
|
||||
], ['order_no' => $this->model['order_no']]);
|
||||
}
|
||||
// 微信支付
|
||||
if ($payType == OrderPayTypeEnum::WECHAT) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\order\source;
|
||||
|
||||
use app\common\service\BaseService;
|
||||
use app\common\enum\order\OrderStatus as OrderStatusEnum;
|
||||
use app\common\enum\order\PayStatus as OrderPayStatusEnum;
|
||||
|
||||
abstract class Basics extends BaseService
|
||||
{
|
||||
/**
|
||||
* 判断订单是否允许付款
|
||||
* @param $order
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function checkOrderStatusOnPay($order);
|
||||
|
||||
/**
|
||||
* 判断商品状态、库存 (未付款订单)
|
||||
* @param $goodsList
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function checkGoodsStatusOnPay($goodsList);
|
||||
|
||||
/**
|
||||
* 判断订单状态(公共)
|
||||
* @param $order
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkOrderStatusOnPayCommon($order)
|
||||
{
|
||||
|
||||
// 判断订单状态
|
||||
if (
|
||||
$order['order_status'] != OrderStatusEnum::NORMAL
|
||||
|| $order['pay_status'] != OrderPayStatusEnum::PENDING
|
||||
) {
|
||||
$this->error = '很抱歉,当前订单不合法,无法支付';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\order\source;
|
||||
|
||||
use app\common\service\BaseService;
|
||||
use app\common\enum\order\OrderSource as OrderSourceEnum;
|
||||
|
||||
/**
|
||||
* 商品库存工厂类
|
||||
* Class Factory
|
||||
* @package app\common\service\stock
|
||||
*/
|
||||
class Factory extends BaseService
|
||||
{
|
||||
// 订单来源的结算台服务类
|
||||
private static $class = [
|
||||
OrderSourceEnum::MAIN => 'Main',
|
||||
];
|
||||
|
||||
/**
|
||||
* 根据订单来源获取商品库存类
|
||||
* @param int $orderSource
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getFactory($orderSource = OrderSourceEnum::MAIN)
|
||||
{
|
||||
static $classObj = [];
|
||||
if (!isset($classObj[$orderSource])) {
|
||||
$className = __NAMESPACE__ . '\\' . static::$class[$orderSource];
|
||||
$classObj[$orderSource] = new $className();
|
||||
}
|
||||
return $classObj[$orderSource];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\order\source;
|
||||
|
||||
use app\api\model\GoodsSku as GoodsSkuModel;
|
||||
|
||||
/**
|
||||
* 订单来源-普通订单扩展类
|
||||
* Class Main
|
||||
* @package app\api\service\order\source
|
||||
*/
|
||||
class Main extends Basics
|
||||
{
|
||||
/**
|
||||
* 判断订单是否允许付款
|
||||
* @param $order
|
||||
* @return bool
|
||||
*/
|
||||
public function checkOrderStatusOnPay($order)
|
||||
{
|
||||
// 判断订单状态
|
||||
if (!$this->checkOrderStatusOnPayCommon($order)) {
|
||||
return false;
|
||||
}
|
||||
// 判断商品状态、库存
|
||||
if (!$this->checkGoodsStatusOnPay($order['goods'])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断商品状态、库存 (未付款订单)
|
||||
* @param $goodsList
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkGoodsStatusOnPay($goodsList)
|
||||
{
|
||||
foreach ($goodsList as $goods) {
|
||||
// 判断商品是否下架
|
||||
if (
|
||||
empty($goods['goods'])
|
||||
|| $goods['goods']['status'] != 10
|
||||
) {
|
||||
$this->error = "很抱歉,商品 [{$goods['goods_name']}] 已下架";
|
||||
return false;
|
||||
}
|
||||
// 获取商品的sku信息
|
||||
$goodsSku = $this->getOrderGoodsSku($goods['goods_id'], $goods['goods_sku_id']);
|
||||
// sku已不存在
|
||||
if (empty($goodsSku)) {
|
||||
$this->error = "很抱歉,商品 [{$goods['goods_name']}] sku已不存在,请重新下单";
|
||||
return false;
|
||||
}
|
||||
// 付款减库存
|
||||
if ($goods['deduct_stock_type'] == 20 && $goods['total_num'] > $goodsSku['stock_num']) {
|
||||
$this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定的商品sku信息
|
||||
* @param $goodsId
|
||||
* @param $goodsSkuId
|
||||
* @return GoodsSkuModel|array|null
|
||||
*/
|
||||
private function getOrderGoodsSku($goodsId, $goodsSkuId)
|
||||
{
|
||||
return GoodsSkuModel::detail($goodsId, $goodsSkuId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\order\source\checkout;
|
||||
|
||||
use app\common\service\BaseService;
|
||||
use app\api\model\User as UserModel;
|
||||
|
||||
/**
|
||||
* 订单结算台扩展基类
|
||||
* Class Basics
|
||||
* @package app\api\service\order\source\checkout
|
||||
*/
|
||||
abstract class Basics extends BaseService
|
||||
{
|
||||
/* @var UserModel $user 当前用户信息 */
|
||||
protected $user;
|
||||
|
||||
// 订单结算商品列表
|
||||
protected $goodsList = [];
|
||||
|
||||
// 订单结算主数据
|
||||
protected $orderData = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* Checkout constructor.
|
||||
* @param UserModel $user
|
||||
* @param array $goodsList
|
||||
* @param array $orderData
|
||||
*/
|
||||
public function __construct($user, $goodsList, $orderData)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->user = $user;
|
||||
$this->goodsList = $goodsList;
|
||||
$this->orderData = $orderData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证商品列表
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function validateGoodsList();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\order\source\checkout;
|
||||
|
||||
use app\common\service\BaseService;
|
||||
use app\common\enum\order\OrderSource as OrderSourceEnum;
|
||||
|
||||
/**
|
||||
* 订单结算台扩展工厂类
|
||||
* Class Factory
|
||||
* @package app\common\service\stock
|
||||
*/
|
||||
class Factory extends BaseService
|
||||
{
|
||||
// 订单来源的结算台服务类
|
||||
private static $class = [
|
||||
OrderSourceEnum::MAIN => Main::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* 根据订单来源获取商品库存类
|
||||
* @param $user
|
||||
* @param $goodsList
|
||||
* @param int $orderSource
|
||||
* @param array $orderData
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getFactory($user, $goodsList, $orderSource = OrderSourceEnum::MAIN, $orderData = [])
|
||||
{
|
||||
return new static::$class[$orderSource]($user, $goodsList, $orderData);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\order\source\checkout;
|
||||
|
||||
use app\common\enum\order\PayType as OrderPayTypeEnum;
|
||||
/**
|
||||
* 订单结算台-普通商品扩展类
|
||||
* Class Main
|
||||
* @package app\api\service\order\source\checkout
|
||||
*/
|
||||
class Main extends Basics
|
||||
{
|
||||
/**
|
||||
* 验证商品列表
|
||||
* @return bool
|
||||
*/
|
||||
public function validateGoodsList()
|
||||
{
|
||||
$order = $this->orderData;
|
||||
foreach ($this->goodsList as $goods) {
|
||||
// 判断商品是否下架
|
||||
if ($goods['status'] != 10) {
|
||||
$this->error = "很抱歉,商品 [{$goods['goods_name']}] 已下架";
|
||||
return false;
|
||||
}
|
||||
// 判断商品库存
|
||||
if ($goods['total_num'] > $goods['skuInfo']['stock_num']) {
|
||||
$this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足";
|
||||
return false;
|
||||
}
|
||||
// 判断购买优惠券不能使用 余额支付
|
||||
if (isset($order['payType']) && $order['payType'] == OrderPayTypeEnum::BALANCE && $goods['oilCardType'] == 'coupon'){
|
||||
$this->error = "本商品不可使用余额支付";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\passport;
|
||||
|
||||
use app\common\service\BaseService;
|
||||
use yiovo\captcha\facade\CaptchaApi;
|
||||
|
||||
class Captcha extends BaseService
|
||||
{
|
||||
/**
|
||||
* 图形验证码
|
||||
* @return array
|
||||
*/
|
||||
public function create(): array
|
||||
{
|
||||
$data = CaptchaApi::create();
|
||||
return [
|
||||
'base64' => str_replace("\r\n", '', $data['base64']),
|
||||
'key' => $data['key'],
|
||||
'md5' => $data['md5']
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\passport;
|
||||
|
||||
use think\facade\Cache;
|
||||
use yiovo\captcha\facade\CaptchaApi;
|
||||
use app\api\model\{User as UserModel, Setting as SettingModel};
|
||||
use app\api\service\{user\Oauth as OauthService, user\Avatar as AvatarService, passport\Party as PartyService};
|
||||
use app\api\validate\passport\Login as ValidateLogin;
|
||||
use app\common\service\BaseService;
|
||||
use app\common\enum\Setting as SettingEnum;
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 服务类:用户登录
|
||||
* Class Login
|
||||
* @package app\api\service\passport
|
||||
*/
|
||||
class Login extends BaseService
|
||||
{
|
||||
/**
|
||||
* 用户信息 (登录成功后才记录)
|
||||
* @var UserModel|null $userInfo
|
||||
*/
|
||||
private $userInfo;
|
||||
|
||||
// 用于生成token的自定义盐
|
||||
const TOKEN_SALT = 'user_salt';
|
||||
|
||||
/**
|
||||
* 执行用户登录
|
||||
* @param array $data
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
* @throws \think\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function login(array $data): bool
|
||||
{
|
||||
// 数据验证
|
||||
$this->validate($data);
|
||||
// 自动登录注册
|
||||
$this->register($data);
|
||||
// 保存第三方用户信息
|
||||
$this->createUserOauth($this->getUserId(), $data['isParty'], $data['partyData']);
|
||||
// 记录登录态
|
||||
return $this->setSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷登录:微信小程序用户
|
||||
* @param array $form
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function loginMpWx(array $form): bool
|
||||
{
|
||||
// 获取微信小程序登录态(session)
|
||||
$wxSession = PartyService::getMpWxSession($form['partyData']['code']);
|
||||
|
||||
// 判断openid是否存在
|
||||
$userId = OauthService::getUserIdByOauthId($wxSession['openid'], 'MP-WEIXIN');
|
||||
// 获取用户信息
|
||||
$userInfo = !empty($userId) ? UserModel::detail($userId) : null;
|
||||
|
||||
// 用户信息存在, 更新登录信息
|
||||
if (!empty($userInfo)) {
|
||||
// 更新用户登录信息
|
||||
$this->updateUser($userInfo, true, $form['partyData']);
|
||||
// 记录登录态
|
||||
return $this->setSession();
|
||||
}
|
||||
|
||||
// 用户信息不存在 => 注册新用户 或者 跳转到绑定手机号页
|
||||
$setting = SettingModel::getItem(SettingEnum::REGISTER);
|
||||
// 后台设置了需强制绑定手机号, 返回前端isBindMobile, 跳转到手机号验证页
|
||||
if ($setting['isForceBindMpweixin']) {
|
||||
throwError('当前用户未绑定手机号', null, ['isBindMobile' => true]);
|
||||
}
|
||||
// 后台未开启强制绑定手机号, 直接保存新用户
|
||||
if (!$setting['isForceBindMpweixin']) {
|
||||
// 用户不存在: 创建一个新用户
|
||||
$this->createUser('', true, $form['partyData']);
|
||||
// 保存第三方用户信息
|
||||
$this->createUserOauth($this->getUserId(), true, $form['partyData']);
|
||||
}
|
||||
// 记录登录态
|
||||
return $this->setSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷登录:微信小程序用户
|
||||
* @param array $form
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function loginMpWxMobile(array $form): bool
|
||||
{
|
||||
// 获取微信小程序登录态(session)
|
||||
$wxSession = PartyService::getMpWxSession($form['code']);
|
||||
// 解密encryptedData -> 拿到手机号
|
||||
$wxData = OauthService::wxDecryptData($wxSession['session_key'], $form['encryptedData'], $form['iv']);
|
||||
// 整理登录注册数据
|
||||
$loginData = [
|
||||
'mobile' => $wxData['purePhoneNumber'],
|
||||
'isParty' => $form['isParty'],
|
||||
'partyData' => $form['partyData'],
|
||||
];
|
||||
// 自动登录注册
|
||||
$this->register($loginData);
|
||||
// 保存第三方用户信息
|
||||
$this->createUserOauth($this->getUserId(), $loginData['isParty'], $loginData['partyData']);
|
||||
// 记录登录态
|
||||
return $this->setSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存oauth信息(第三方用户信息)
|
||||
* @param int $userId 用户ID
|
||||
* @param bool $isParty 是否为第三方用户
|
||||
* @param array $partyData 第三方用户数据
|
||||
* @return void
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function createUserOauth(int $userId, bool $isParty, array $partyData = []): void
|
||||
{
|
||||
if ($isParty) {
|
||||
$Oauth = new PartyService;
|
||||
$Oauth->createUserOauth($userId, $partyData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前登录的用户信息
|
||||
*/
|
||||
public function getUserInfo(): ?UserModel
|
||||
{
|
||||
return $this->userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前登录的用户ID
|
||||
* @return int
|
||||
*/
|
||||
private function getUserId(): int
|
||||
{
|
||||
return (int)$this->getUserInfo()['user_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动登录注册
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws \think\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function register(array $data): void
|
||||
{
|
||||
// 查询用户是否已存在
|
||||
// 用户存在: 更新用户登录信息
|
||||
$userInfo = UserModel::detail(['mobile' => $data['mobile']]);
|
||||
if ($userInfo) {
|
||||
$this->updateUser($userInfo, $data['isParty'], $data['partyData']);
|
||||
return;
|
||||
}
|
||||
// 用户不存在: 创建一个新用户
|
||||
$this->createUser($data['mobile'], $data['isParty'], $data['partyData']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户
|
||||
* @param string $mobile 手机号
|
||||
* @param bool $isParty 是否存在第三方用户信息
|
||||
* @param array $partyData 用户信息(第三方)
|
||||
* @return void
|
||||
* @throws \think\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function createUser(string $mobile, bool $isParty, array $partyData = []): void
|
||||
{
|
||||
// 用户信息
|
||||
$data = [
|
||||
'mobile' => $mobile,
|
||||
'nick_name' => !empty($mobile) ? hide_mobile($mobile) : '',
|
||||
'platform' => getPlatform(),
|
||||
'last_login_time' => time(),
|
||||
'store_id' => $this->storeId
|
||||
];
|
||||
// 写入用户信息(第三方)
|
||||
if ($isParty === true && !empty($partyData)) {
|
||||
$partyUserInfo = PartyService::partyUserInfo($partyData, true);
|
||||
$data = array_merge($data, $partyUserInfo);
|
||||
}
|
||||
// 新增用户记录
|
||||
$model = new UserModel;
|
||||
$model->save($data);
|
||||
// 将微信用户昵称添加编号便于后台管理, 例如:微信用户_10001
|
||||
if ($data['nick_name'] === '微信用户') {
|
||||
$model->save(['nick_name' => "微信用户_{$model['user_id']}"]);
|
||||
}
|
||||
// 记录用户信息
|
||||
$this->userInfo = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户登录信息
|
||||
* @param UserModel $userInfo
|
||||
* @param bool $isParty 是否存在第三方用户信息
|
||||
* @param array $partyData 用户信息(第三方)
|
||||
* @return void
|
||||
*/
|
||||
private function updateUser(UserModel $userInfo, bool $isParty, array $partyData = []): void
|
||||
{
|
||||
// 用户信息
|
||||
$data = [
|
||||
'last_login_time' => time(),
|
||||
'store_id' => $this->storeId
|
||||
];
|
||||
// 写入用户信息(第三方)
|
||||
// 如果不需要每次登录都更新微信用户头像昵称, 下面4行代码可以屏蔽掉
|
||||
// if ($isParty === true && !empty($partyData)) {
|
||||
// $partyUserInfo = PartyService::partyUserInfo($partyData, true);
|
||||
// $data = array_merge($data, $partyUserInfo);
|
||||
// }
|
||||
// 更新用户记录
|
||||
$status = $userInfo->save($data) !== false;
|
||||
// 记录用户信息
|
||||
$this->userInfo = $userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录态
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function setSession(): bool
|
||||
{
|
||||
empty($this->userInfo) && throwError('未找到用户信息');
|
||||
// 登录的token
|
||||
$token = $this->getToken($this->getUserId());
|
||||
// 记录缓存, 30天
|
||||
Cache::set($token, [
|
||||
'user' => $this->userInfo,
|
||||
'store_id' => $this->storeId,
|
||||
'is_login' => true,
|
||||
], 86400 * 30);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据验证
|
||||
* @param array $data
|
||||
* @return void
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function validate(array $data): void
|
||||
{
|
||||
// 数据验证
|
||||
$validate = new ValidateLogin;
|
||||
if (!$validate->check($data)) {
|
||||
throwError($validate->getError());
|
||||
}
|
||||
// 验证短信验证码是否匹配
|
||||
if (!CaptchaApi::checkSms($data['smsCode'], $data['mobile'])) {
|
||||
throwError('短信验证码不正确');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录的token
|
||||
* @param int $userId
|
||||
* @return string
|
||||
*/
|
||||
public function getToken(int $userId): string
|
||||
{
|
||||
static $token = '';
|
||||
if (empty($token)) {
|
||||
$token = $this->makeToken($userId);
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成用户认证的token
|
||||
* @param int $userId
|
||||
* @return string
|
||||
*/
|
||||
private function makeToken(int $userId): string
|
||||
{
|
||||
$storeId = $this->storeId;
|
||||
// 生成一个不会重复的随机字符串
|
||||
$guid = get_guid_v4();
|
||||
// 当前时间戳 (精确到毫秒)
|
||||
$timeStamp = microtime(true);
|
||||
// 自定义一个盐
|
||||
$salt = self::TOKEN_SALT;
|
||||
return md5("{$storeId}_{$timeStamp}_{$userId}_{$guid}_{$salt}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\passport;
|
||||
|
||||
use app\api\model\UserOauth as UserOauthModel;
|
||||
use app\api\service\user\Oauth as OauthService;
|
||||
use app\api\service\user\Avatar as AvatarService;
|
||||
use app\common\service\BaseService;
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 第三方用户注册登录服务
|
||||
* Class Party
|
||||
* @package app\api\service\passport
|
||||
*/
|
||||
class Party extends BaseService
|
||||
{
|
||||
/**
|
||||
* 保存用户的第三方认证信息
|
||||
* @param int $userId 用户ID
|
||||
* @param array $partyData 第三方登录信息
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function createUserOauth(int $userId, array $partyData = []): bool
|
||||
{
|
||||
try {
|
||||
// 获取oauthId和unionId
|
||||
$oauthInfo = $this->getOauthInfo($partyData);
|
||||
} catch (BaseException $e) {
|
||||
// isBack参数代表需重新获取code, 前端拿到该参数进行页面返回
|
||||
throwError($e->getMessage(), null, ['isBack' => true]);
|
||||
}
|
||||
// 是否存在第三方用户
|
||||
$oauthId = UserOauthModel::getOauthIdByUserId($userId, $partyData['oauth']);
|
||||
// 如果不存在oauth则写入
|
||||
if (empty($oauthId)) {
|
||||
return (new UserOauthModel)->add([
|
||||
'user_id' => $userId,
|
||||
'oauth_type' => $partyData['oauth'],
|
||||
'oauth_id' => $oauthInfo['oauth_id'],
|
||||
'unionid' => $oauthInfo['unionid'] ?? '', // unionid可以不存在
|
||||
'store_id' => $this->storeId
|
||||
]);
|
||||
}
|
||||
// 如果存在第三方用户, 需判断oauthId是否相同
|
||||
if ($oauthId != $oauthInfo['oauth_id']) {
|
||||
// isBack参数代表需重新获取code, 前端拿到该参数进行页面返回
|
||||
throwError('很抱歉,当前手机号已绑定其他微信号', null, ['isBack' => true]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信小程序登录态(session)
|
||||
* 这里支持静态变量缓存, 用于实现第二次调用该方法时直接返回已获得的session
|
||||
* @param string $code
|
||||
* @return array|false
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getMpWxSession(string $code)
|
||||
{
|
||||
static $session;
|
||||
if (empty($session)) {
|
||||
try {
|
||||
// 微信小程序通过code获取session
|
||||
$session = OauthService::wxCode2Session($code);
|
||||
} catch (BaseException $e) {
|
||||
// showError参数表示让前端显示错误
|
||||
throwError($e->getMessage());
|
||||
}
|
||||
}
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方用户信息
|
||||
* @param array $partyData 第三方用户信息
|
||||
* @param bool $isGetAvatarUrl 是否下载头像
|
||||
* @return array
|
||||
* @throws BaseException
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public static function partyUserInfo(array $partyData, bool $isGetAvatarUrl = true): array
|
||||
{
|
||||
$partyUserInfo = $partyData['userInfo'];
|
||||
$data = [
|
||||
'nick_name' => $partyUserInfo['nickName'],
|
||||
'gender' => $partyUserInfo['gender']
|
||||
];
|
||||
// 下载用户头像
|
||||
if ($isGetAvatarUrl) {
|
||||
$data['avatar_id'] = static::partyAvatar($partyUserInfo['avatarUrl']);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载第三方头像并写入文件库
|
||||
* @param string $avatarUrl
|
||||
* @return int
|
||||
* @throws BaseException
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
private static function partyAvatar(string $avatarUrl): int
|
||||
{
|
||||
$Avatar = new AvatarService;
|
||||
$fileId = $Avatar->party($avatarUrl);
|
||||
return $fileId ? $fileId : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第三方用户session信息 (openid、unionid、session_key等)
|
||||
* @param array $partyData
|
||||
* @return array|null
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function getOauthInfo(array $partyData): ?array
|
||||
{
|
||||
if ($partyData['oauth'] === 'MP-WEIXIN') {
|
||||
$wxSession = static::getMpWxSession($partyData['code']);
|
||||
return ['oauth_id' => $wxSession['openid'], 'unionid' => $wxSession['unionid'] ?? null];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\passport;
|
||||
|
||||
use yiovo\cache\facade\Cache;
|
||||
use yiovo\captcha\facade\CaptchaApi;
|
||||
use app\api\validate\passport\SmsCaptcha as ValidateSmsCaptcha;
|
||||
use app\common\service\BaseService;
|
||||
use app\common\service\Message as MessageService;
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 服务类:发送短信验证码
|
||||
* Class SmsCaptcha
|
||||
* @package app\api\service\passport
|
||||
*/
|
||||
class SmsCaptcha extends BaseService
|
||||
{
|
||||
// 最大发送次数,默认10次
|
||||
protected $sendTimes = 10;
|
||||
|
||||
// 发送限制间隔时间,默认24小时
|
||||
protected $safeTime = 86400;
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
* @param array $data
|
||||
* @return bool
|
||||
* @throws BaseException
|
||||
*/
|
||||
public function handle(array $data): bool
|
||||
{
|
||||
// 数据验证
|
||||
$this->validate($data);
|
||||
// 执行发送短信
|
||||
if (!$this->sendCaptcha($data['mobile'])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行发送短信
|
||||
* @param string $mobile
|
||||
* @return bool
|
||||
*/
|
||||
private function sendCaptcha(string $mobile): bool
|
||||
{
|
||||
// 缓存发送记录并判断次数
|
||||
if (!$this->record($mobile)) {
|
||||
return false;
|
||||
}
|
||||
// 生成验证码
|
||||
$smsCaptcha = CaptchaApi::createSMS($mobile);
|
||||
// 发送短信
|
||||
MessageService::send('passport.captcha', [
|
||||
'code' => $smsCaptcha['code'],
|
||||
'mobile' => $smsCaptcha['key']
|
||||
], $this->storeId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录短信验证码发送记录并判断是否超出发送限制
|
||||
* @param string $mobile
|
||||
* @return bool
|
||||
*/
|
||||
private function record(string $mobile): bool
|
||||
{
|
||||
// 获取发送记录缓存
|
||||
$record = Cache::get("sendCaptchaSMS.$mobile");
|
||||
// 写入缓存:记录剩余发送次数
|
||||
if (empty($record)) {
|
||||
Cache::set("sendCaptchaSMS.$mobile", ['times' => $this->sendTimes - 1], $this->safeTime);
|
||||
return true;
|
||||
}
|
||||
// 判断发送次数是否合法
|
||||
if ($record['times'] <= 0) {
|
||||
$this->error = '很抱歉,已超出今日最大发送次数限制';
|
||||
return false;
|
||||
}
|
||||
// 发送次数递减
|
||||
Cache::update("sendCaptchaSMS.$mobile", ['times' => $record['times'] - 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据验证
|
||||
* @param array $data
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function validate(array $data)
|
||||
{
|
||||
// 数据验证
|
||||
$validate = new ValidateSmsCaptcha;
|
||||
if (!$validate->check($data)) {
|
||||
throwError($validate->getError());
|
||||
}
|
||||
// 验证图形验证码
|
||||
if (!CaptchaApi::check($data['captchaCode'], $data['captchaKey'])) {
|
||||
throwError('很抱歉,图形验证码不正确');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\points;
|
||||
|
||||
use app\common\library\helper;
|
||||
use app\api\model\Setting as SettingModel;
|
||||
use app\common\service\BaseService;
|
||||
|
||||
/**
|
||||
* 订单积分抵扣服务类
|
||||
* Class GoodsDeduct
|
||||
* @package app\api\service\points
|
||||
*/
|
||||
class GoodsDeduct extends BaseService
|
||||
{
|
||||
private $goodsList;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* GoodsDeduct constructor.
|
||||
* @param $goodsList
|
||||
*/
|
||||
public function __construct($goodsList)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->goodsList = $goodsList;
|
||||
}
|
||||
|
||||
public function setGoodsPoints($maxPointsNumCount, $actualPointsNum)
|
||||
{
|
||||
// 计算实际积分抵扣数量
|
||||
$this->setGoodsListPointsNum($maxPointsNumCount, $actualPointsNum);
|
||||
// 总抵扣数量
|
||||
$totalPointsNum = (int)helper::getArrayColumnSum($this->goodsList, 'points_num');
|
||||
// 填充余数
|
||||
$this->setGoodsListPointsNumFill($actualPointsNum, $totalPointsNum);
|
||||
$this->setGoodsListPointsNumDiff($actualPointsNum, $totalPointsNum);
|
||||
// 计算实际积分抵扣金额
|
||||
$this->setGoodsListPointsMoney();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算实际积分抵扣数量
|
||||
* @param $maxPointsNumCount
|
||||
* @param $actualPointsNum
|
||||
*/
|
||||
private function setGoodsListPointsNum($maxPointsNumCount, $actualPointsNum)
|
||||
{
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
if (!$goods['is_points_discount']) continue;
|
||||
$goods['points_num'] = floor($goods['max_points_num'] / $maxPointsNumCount * $actualPointsNum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算实际积分抵扣金额
|
||||
*/
|
||||
private function setGoodsListPointsMoney()
|
||||
{
|
||||
$setting = SettingModel::getItem('points');
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
if (!$goods['is_points_discount']) continue;
|
||||
$goods['points_money'] = helper::bcmul($goods['points_num'], $setting['discount']['discount_ratio']);
|
||||
}
|
||||
}
|
||||
|
||||
private function setGoodsListPointsNumFill($actualPointsNum, $totalPointsNum)
|
||||
{
|
||||
if ($totalPointsNum === 0) {
|
||||
$temReducedMoney = $actualPointsNum;
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
if (!$goods['is_points_discount']) continue;
|
||||
if ($temReducedMoney === 0) break;
|
||||
$goods['points_num'] = 1;
|
||||
$temReducedMoney--;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function setGoodsListPointsNumDiff($actualPointsNum, $totalPointsNum)
|
||||
{
|
||||
$tempDiff = $actualPointsNum - $totalPointsNum;
|
||||
foreach ($this->goodsList as &$goods) {
|
||||
if (!$goods['is_points_discount']) continue;
|
||||
if ($tempDiff < 1) break;
|
||||
$goods['points_num'] = $goods['points_num'] + 1;
|
||||
$tempDiff--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\recharge;
|
||||
|
||||
use app\common\service\BaseService;
|
||||
use app\api\model\User as UserModel;
|
||||
use app\api\model\recharge\Order as OrderModel;
|
||||
use app\api\model\user\BalanceLog as BalanceLogModel;
|
||||
use app\common\enum\order\PayType as OrderPayTypeEnum;
|
||||
use app\common\enum\user\balanceLog\Scene as SceneEnum;
|
||||
use app\common\enum\recharge\order\PayStatus as PayStatusEnum;
|
||||
|
||||
class PaySuccess extends BaseService
|
||||
{
|
||||
// 订单模型
|
||||
public $model;
|
||||
|
||||
// 当前用户信息
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* PaySuccess constructor.
|
||||
* @param $orderNo
|
||||
*/
|
||||
public function __construct($orderNo)
|
||||
{
|
||||
parent::__construct();
|
||||
// 实例化订单模型
|
||||
$this->model = OrderModel::getPayDetail($orderNo);
|
||||
// 获取用户信息
|
||||
$this->user = UserModel::detail($this->model['user_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单详情
|
||||
* @return OrderModel|null
|
||||
*/
|
||||
public function getOrderInfo()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单支付成功业务处理
|
||||
* @param int $payType 支付类型
|
||||
* @param array $payData 支付回调数据
|
||||
* @return bool
|
||||
*/
|
||||
public function onPaySuccess(int $payType, $payData)
|
||||
{
|
||||
return $this->model->transaction(function () use ($payData) {
|
||||
// 更新订单状态
|
||||
$this->model->save([
|
||||
'pay_status' => PayStatusEnum::SUCCESS,
|
||||
'pay_time' => time(),
|
||||
'transaction_id' => $payData['transaction_id']
|
||||
]);
|
||||
// 累积用户余额
|
||||
UserModel::setIncBalance((int)$this->user['user_id'], (float)$this->model['actual_money']);
|
||||
// 用户余额变动明细
|
||||
BalanceLogModel::add(SceneEnum::RECHARGE, [
|
||||
'user_id' => $this->user['user_id'],
|
||||
'money' => $this->model['actual_money'],
|
||||
'store_id' => $this->getStoreId(),
|
||||
], ['order_no' => $this->model['order_no']]);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\user;
|
||||
|
||||
use app\common\service\BaseService;
|
||||
use app\api\model\Setting as SettingModel;
|
||||
use app\api\model\UploadFile as UploadFileModel;
|
||||
use app\common\enum\file\FileType as FileTypeEnum;
|
||||
use app\common\library\{Download, storage\Driver as Storage};
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 服务类: 用户头像
|
||||
* Class Avatar
|
||||
* @package app\api\service\user
|
||||
*/
|
||||
class Avatar extends BaseService
|
||||
{
|
||||
// 存储配置
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* Avatar constructor.
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
// 存储配置
|
||||
$this->config = SettingModel::getItem('storage');
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载第三方头像并返回文件记录ID
|
||||
* @param string $avatarUrl
|
||||
* @return int
|
||||
* @throws BaseException
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function party(string $avatarUrl): int
|
||||
{
|
||||
// 下载网络图片
|
||||
$filePath = $this->download($avatarUrl);
|
||||
// 上传到本地
|
||||
$fileInfo = $this->upload($filePath);
|
||||
// 新增文件记录
|
||||
return $this->record($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片上传接口
|
||||
* @param string $filePath
|
||||
* @return false|mixed
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function upload(string $filePath = '')
|
||||
{
|
||||
// 实例化存储驱动
|
||||
$storage = new Storage($this->config);
|
||||
// 设置上传文件的信息
|
||||
$storage->setUploadFileByReal($filePath)
|
||||
->setRootDirName((string)$this->storeId)
|
||||
->setValidationScene('image')
|
||||
->upload();
|
||||
// 执行文件上传
|
||||
if (!$storage->upload()) {
|
||||
throwError('图片上传失败:' . $storage->getError());
|
||||
}
|
||||
// 文件信息
|
||||
return $storage->getSaveFileInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加文件库记录
|
||||
* @param array $fileInfo
|
||||
* @return int
|
||||
*/
|
||||
private function record(array $fileInfo): int
|
||||
{
|
||||
$model = new UploadFileModel;
|
||||
$model->add($fileInfo, FileTypeEnum::IMAGE, 0);
|
||||
return (int)$model['file_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载网络图片
|
||||
* @param string $avatarUrl
|
||||
* @return string
|
||||
* @throws BaseException
|
||||
*/
|
||||
private function download(string $avatarUrl): string
|
||||
{
|
||||
$Download = new Download;
|
||||
return $Download->saveTempImage($this->storeId, $avatarUrl, 'avatar');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\user;
|
||||
|
||||
use app\common\library\helper;
|
||||
use app\common\service\BaseService;
|
||||
use app\api\service\User as UserService;
|
||||
use app\api\model\user\Grade as UserGradeModel;
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 服务类: 会员等级
|
||||
* Class Grade
|
||||
* @package app\api\service\user
|
||||
*/
|
||||
class Grade extends BaseService
|
||||
{
|
||||
/**
|
||||
* 获取当前登录用户的会员等级信息
|
||||
* @param bool $isForce 是否强制验证登录, 如果未登录将抛错
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static function getCurrentGradeInfo(bool $isForce = false)
|
||||
{
|
||||
// 当前登录的用户信息
|
||||
$userInfo = UserService::getCurrentLoginUser($isForce);
|
||||
if (empty($userInfo) || empty($userInfo['grade_id'])) {
|
||||
return false;
|
||||
}
|
||||
// 获取会员等级信息
|
||||
$gradeInfo = UserGradeModel::detail($userInfo['grade_id']);
|
||||
if (empty($gradeInfo) || $gradeInfo['is_delete'] || empty($gradeInfo['status'])) {
|
||||
return false;
|
||||
}
|
||||
return $gradeInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取和计算折扣后的价格
|
||||
* @param $originalPrice
|
||||
* @param $discountRatio
|
||||
* @return string
|
||||
*/
|
||||
public static function getDiscountPrice($originalPrice, $discountRatio): string
|
||||
{
|
||||
// 使用高精度方法计算等级折扣; 因bcmach不支持四舍五入, 所以精确计算到3位数, 使用round四舍五入
|
||||
$discountPrice = helper::bcmul($originalPrice, $discountRatio / 10, 3);
|
||||
$discountPrice = round((float)$discountPrice, 2);
|
||||
return helper::number2($discountPrice, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 萤火科技 <admin@yiovo.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\api\service\user;
|
||||
|
||||
use app\api\model\UserOauth as UserOauthModel;
|
||||
use app\api\model\wxapp\Setting as WxappSettingModel;
|
||||
use app\common\service\BaseService;
|
||||
use app\common\library\wechat\WxUser;
|
||||
use app\common\library\wechat\ErrorCode;
|
||||
use app\common\library\wechat\WXBizDataCrypt;
|
||||
use cores\exception\BaseException;
|
||||
|
||||
/**
|
||||
* 服务类: 第三方用户服务类
|
||||
* Class Avatar
|
||||
* @package app\api\service\user
|
||||
*/
|
||||
class Oauth extends BaseService
|
||||
{
|
||||
/**
|
||||
* 微信小程序通过code获取session (openid session_key unionid)
|
||||
* @param string $code
|
||||
* @return array|false
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function wxCode2Session(string $code)
|
||||
{
|
||||
// 获取当前小程序信息
|
||||
$wxConfig = static::getMpWxConfig();
|
||||
// 微信登录 (获取session_key)
|
||||
$WxUser = new WxUser($wxConfig['app_id'], $wxConfig['app_secret']);
|
||||
$result = $WxUser->jscode2session($code);
|
||||
!$result && throwError($WxUser->getError());
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密微信的加密数据encryptedData
|
||||
* @param string $sessionKey
|
||||
* @param string $encryptedData
|
||||
* @param string $iv
|
||||
* @return mixed
|
||||
* @throws BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
* @throws \cores\exception\BaseException
|
||||
*/
|
||||
public static function wxDecryptData(string $sessionKey, string $encryptedData, string $iv)
|
||||
{
|
||||
// 获取当前小程序信息
|
||||
$wxConfig = static::getMpWxConfig();
|
||||
// 微信数据解密
|
||||
$WXBizDataCrypt = new WXBizDataCrypt($wxConfig['app_id'], $sessionKey);
|
||||
$content = null;
|
||||
$code = $WXBizDataCrypt->decryptData($encryptedData, $iv, $content);
|
||||
if ($code !== ErrorCode::$OK) {
|
||||
throwError('微信数据 encryptedData 解密失败');
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信小程序配置项
|
||||
* @return array
|
||||
* @throws \cores\exception\BaseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private static function getMpWxConfig(): array
|
||||
{
|
||||
$wxConfig = WxappSettingModel::getWxappConfig();
|
||||
if (empty($wxConfig['app_id']) || empty($wxConfig['app_secret'])) {
|
||||
throwError('请到后台小程序设置填写AppID和AppSecret参数');
|
||||
}
|
||||
return $wxConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据openid获取用户ID
|
||||
* @param string $oauthId 第三方用户唯一标识 (openid)
|
||||
* @param string $oauthType 第三方登陆类型
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getUserIdByOauthId(string $oauthId, string $oauthType)
|
||||
{
|
||||
return UserOauthModel::getUserIdByOauthId($oauthId, $oauthType);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user