Initial commit: queue workspace

Made-with: Cursor
This commit is contained in:
apple
2026-03-21 02:55:24 +08:00
commit 78de918c37
12388 changed files with 1840126 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\product\sku;
use app\dao\product\sku\StoreProductAttrResultDao;
use app\services\BaseServices;
use app\services\product\product\StoreProductServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
/**
* Class StoreProductAttrResultService
* @package app\services\product\sku
* @mixin StoreProductAttrResultDao
*/
class StoreProductAttrResultServices extends BaseServices
{
/**
* @var StoreProductAttrResultDao
*/
#[Inject]
protected StoreProductAttrResultDao $dao;
/**
* 获取属性规格
* @param array $where
* @return mixed
*/
public function getResult(array $where)
{
return json_decode($this->dao->value($where, 'result'), true);
}
/**
* 删除属性
* @param int $id
* @param int $type
* @return bool
*/
public function del(int $id, int $type)
{
return $this->dao->del($id, $type);
}
/**
* 设置属性
* @param array $data
* @param int $id
* @param int $type
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function setResult(array $data, int $id, int $type)
{
$result = $this->dao->get(['product_id' => $id, 'type' => $type]);
if ($result) {
$res = $this->dao->update($result['id'], ['result' => json_encode($data), 'change_time' => time()]);
} else {
$res = $this->dao->save(['product_id' => $id, 'result' => json_encode($data), 'change_time' => time(), 'type' => $type]);
}
if (!$res) throw new AdminException('规格保存失败');
return true;
}
/**
* 无属性添加默认属性
* @param int $id
* @param int $type
* @param array $productInfo
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function checkProductAttr(int $id, int $type = 0, array $productInfo = []):bool
{
if (!$id) {
return false;
}
try {
/** @var StoreProductServices $storeProductServices */
$storeProductServices = app()->make(StoreProductServices::class);
if (!$productInfo) {
$productInfo = $storeProductServices->getCacheProductInfo($id);
}
if (!$productInfo) {
return false;
}
if ($this->getResult(['product_id' => $id, 'type' => $type])) {
return true;
}
$attr = [
[
'value' => '规格',
'detailValue' => '',
'attrHidden' => '',
'detail' => ['默认']
]
];
$detail[0] = [
'value1' => '默认',
'detail' => ['规格' => '默认'],
'pic' => $productInfo['image'],
'price' => $productInfo['price'],
'settle_price' => $productInfo['settle_price'] ?? 0,
'cost' => $productInfo['cost'],
'ot_price' => $productInfo['ot_price'],
'stock' => $productInfo['stock'],
'bar_code' => '',
'weight' => 0,
'volume' => 0,
'brokerage' => 0,
'brokerage_two' => 0,
'code' => 0,
'write_times' => 1,
'write_valid' => 1,
'days' => 0,
'section_time' => []
];
/** @var StoreProductAttrServices $storeProductAttrServices */
$storeProductAttrServices = app()->make(StoreProductAttrServices::class);
$skuList = $storeProductAttrServices->validateProductAttr($attr, $detail, $id);
$storeProductAttrServices->saveProductAttr($skuList, $id, 0);
$storeProductServices->update($id, ['spec_type' => 0]);
} catch (\Throwable $e) {
}
return true;
}
}

View File

@@ -0,0 +1,606 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\product\sku;
use app\dao\product\sku\StoreProductAttrDao;
use app\services\BaseServices;
use app\services\order\StoreCartServices;
use app\services\product\product\StoreProductServices;
use app\services\user\channel\ChannelMerchantServices;
use crmeb\exceptions\AdminException;
use crmeb\services\CacheService;
use crmeb\traits\OptionTrait;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class StoreProductAttrService
* @package app\services\product\sku
* @mixin StoreProductAttrDao
*/
class StoreProductAttrServices extends BaseServices
{
use OptionTrait;
/**
* @var StoreProductAttrDao
*/
#[Inject]
protected StoreProductAttrDao $dao;
/**
* 生成规格唯一值
* @param int $id
* @param string $sku
* @return false|string
*/
public function createAttrUnique(int $id, string $sku)
{
return substr(md5($id . $sku . uniqid(true)), 12, 8);
}
/**
* 根据根据前端规格数据获取sku
* @param array $value
* @param string
*/
public function getSku(array $value)
{
$sku = '';
if ($value) {
$detail = [];
$count = count($value['detail'] ?? []);
for ($i = 1; $i <= $count; $i++) {
$detail[] = trim($value['value' . $i]);
}
$sku = implode(',', $detail);
}
return $sku;
}
/**
* 添加商品属性数据判断
* @param array $attrList
* @param array $valueList
* @param int $productId
* @param int $type
* @param int $is_vip
* @param int $validate
* @return array
*/
public function validateProductAttr(array $attrList, array $valueList, int $productId, int $type = 0, int $is_vip = 0, int $validate = 1)
{
$attrValueList = [];
$attrNameList = [];
foreach ($attrList as $index => $attr) {
if (!isset($attr['value'])) {
throw new AdminException('请输入规则名称!');
}
$attr['value'] = trim($attr['value']);
if (!isset($attr['value'])) {
throw new AdminException('请输入规则名称!!');
}
if (!isset($attr['detail']) || !count($attr['detail'])) {
throw new AdminException('请输入属性名称!');
}
foreach ($attr['detail'] as $k => $attrValue) {
if (is_string($attrValue)) {
$attrValueTemp = $attrValue;
$attrValue = [];
$attrValue['value'] = $attrValueTemp;
$attrValue['pic'] = '';
} else {
$attrValue['value'] = trim($attrValue['value']);
$attrValue['pic'] = isset($attr['add_pic']) && $attr['add_pic'] ? trim($attrValue['pic']) : '';
}
if (empty($attrValue['value'])) {
throw new AdminException('请输入正确的属性');
}
$attr['detail'][$k] = $attrValue;
$attrValueList[] = $attrValue;
}
$attrNameList[] = $attr['value'];
$attrList[$index] = $attr;
}
$result = ['attr' => $attrList, 'value' => $valueList];
$attrCount = count($attrList);
foreach ($valueList as $index => $value) {
if (!isset($value['detail']) || count($value['detail']) != $attrCount) {
throw new AdminException('请填写正确的商品信息');
}
if (!isset($value['price']) || !is_numeric($value['price']) || floatval($value['price']) != $value['price']) {
throw new AdminException('请填写正确的商品价格');
}
if ($type == 4) {
if (!isset($value['integral']) || !is_numeric($value['integral']) || floatval($value['integral']) != $value['integral']) {
throw new AdminException('请填写正确的商品积分价格');
}
}
if (isset($value['price']) && $value['price'] <= 0 && isset($value['integral']) && $value['integral'] <= 0) {
throw new AdminException('积分商品兑换积分和价格不能同时为空');
}
if (!isset($value['stock']) || !is_numeric($value['stock']) || intval($value['stock']) != $value['stock']) {
throw new AdminException('请填写正确的商品库存');
}
//供应商结算价 不用成本价
if (!($value['settle_price'] ?? 0) && (!isset($value['cost']) || !is_numeric($value['cost']) || floatval($value['cost']) != $value['cost'])) {
throw new AdminException('请填写正确的商品成本价格');
}
if ($validate && (!isset($value['pic']) || empty($value['pic']))) {
throw new AdminException('请上传商品规格图片');
}
if ($is_vip && (!isset($value['vip_price']) || !$value['vip_price'])) {
throw new AdminException('会员价格不能为0');
}
foreach ($value['detail'] as $attrName => $attrValue) {
//如果attrName 存在空格 则这个规格key 会出现两次
unset($valueList[$index]['detail'][$attrName]);
$attrName = trim($attrName);
$attrValue = trim($attrValue);
if (!in_array($attrName, $attrNameList, true)) {
throw new AdminException($attrName . '规则不存在');
}
if (!in_array($attrValue, array_column($attrValueList, 'value'), true)) {
throw new AdminException($attrName . '属性不存在');
}
if (empty($attrName)) {
throw new AdminException('请输入正确的属性');
}
$valueList[$index]['detail'][$attrName] = $attrValue;
}
}
$attrGroup = [];
$valueGroup = [];
foreach ($attrList as $k => $value) {
$attrGroup[] = [
'product_id' => $productId,
'attr_name' => $value['value'],
'attr_values' => $value['detail'],
'type' => $type
];
}
/** @var StoreProductAttrValueServices $storeProductAttrValueServices */
$storeProductAttrValueServices = app()->make(StoreProductAttrValueServices::class);
$skuArray = $storeProductAttrValueServices->getSkuArray(['product_id' => $productId, 'type' => $type], 'unique,vip_price,level_price,brokerage_two,brokerage,channel_level_price', 'suk');
$store_product_id = $this->getItem('store_product_id', 0);
$productSkuArray = [];
if ($store_product_id) {
$productSkuArray = $storeProductAttrValueServices->getSkuArray(['product_id' => $store_product_id, 'type' => 0], '*', 'suk');
}
$sort = 0;
$inventoryData = [];
foreach ($valueList as $k => $value) {
$sku = implode(',', array_map(fn($key) => $value['detail'][$key], array_column($attrList, 'value')));
$stock = $value['stock'];
if (isset($value['pm'])) {
//pm是 1 是增加 0 是减少
if ($value['pm'] == 1) {
$stock = (int)$value['stock'] + (int)$value['inventory'];
} else {
$stock = (int)$value['stock'] - (int)$value['inventory'];
if ($stock < 0) {
throw new AdminException('出库数量不能大于库存');
}
}
if ($value['inventory'] > 0) {
$inventoryData[$k]['pm'] = $value['pm'];
$inventoryData[$k]['inventory'] = $value['inventory'];
}
} else {
if ($value['stock'] > 0) {
$inventoryData[$k]['inventory'] = $value['stock'];
$inventoryData[$k]['pm'] = 1;
}
}
$valueGroup[$sku] = [
'product_id' => $productId,
'suk' => $sku,
'price' => $value['price'],
'integral' => $value['integral'] ?? 0,
'settle_price' => $value['settle_price'] ?? 0.00,//供应商结算价
'cost' => ($value['settle_price'] ?? 0.00) ?: $value['cost'],//供应端:去除成本价
'ot_price' => $value['ot_price'],
'stock' => $stock,
'unique' => $skuArray[$sku]['unique'] ?? $this->createAttrUnique($productId, $sku),
'image' => $value['pic'],
'bar_code' => $value['bar_code'] ?? '',
'weight' => $value['weight'] ?? 0,
'volume' => $value['volume'] ?? 0,
'brokerage' => $value['brokerage'] ?? 0,
'brokerage_two' => $value['brokerage_two'] ?? 0,
'type' => $type,
'quota' => $value['quota'] ?? 0,
'quota_show' => $value['quota'] ?? 0,
'vip_price' => $value['vip_price'] ?? 0,
'level_price' => $value['level_price'] ?? 0,
'channel_level_price' => $value['channel_level_price'] ?? 0,
'code' => $value['code'] ?? '',
'product_type' => $value['product_type'] ?? 0,
'is_default_select' => $value['is_default_select'] ?? 0,
'is_show' => $value['is_show'] ?? 1,
'virtual_list' => $productSkuArray[$sku]['virtual_list'] ?? $value['virtual_list'] ?? [],
'disk_info' => $productSkuArray[$sku]['disk_info'] ?? $value['disk_info'] ?? '',
'write_times' => $productSkuArray[$sku]['write_times'] ?? $value['write_times'] ?? 1,
'write_valid' => $productSkuArray[$sku]['write_valid'] ?? $value['write_valid'] ?? 1,
'write_days' => $productSkuArray[$sku]['write_days'] ?? $value['write_days'] ?? $value['days'] ?? 0,
'write_start' => ($productSkuArray[$sku]['section_time'][0] ?? $value['section_time'][0] ?? '') ? strtotime($productSkuArray[$sku]['section_time'][0] ?? $value['section_time'][0] ?? '') : 0,
'write_end' => ($productSkuArray[$sku]['section_time'][1] ?? $value['section_time'][1] ?? '') ? strtotime($productSkuArray[$sku]['section_time'][1] ?? $value['section_time'][1] ?? '') : 0,
'sort' => $sort
];
if (isset($inventoryData[$k]['inventory']) && $inventoryData[$k]['inventory'] > 0) {
$inventoryData[$k]['unique'] = $valueGroup[$sku]['unique'];
$inventoryData[$k]['product_id'] = $productId;
}
$sort = $sort + 1;
}
if (!count($attrGroup) || !count($valueGroup)) {
throw new AdminException('请设置至少一个属性!');
}
return compact('result', 'attrGroup', 'valueGroup', 'inventoryData');
}
/**
* 保存商品规格
* @param array $data
* @param int $id
* @param int $type
* @return bool|mixed|\think\Collection
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function saveProductAttr(array $data, int $id, int $type = 0)
{
$this->setAttr($data['attrGroup'], $id, $type);
/** @var StoreProductAttrResultServices $storeProductAttrResultServices */
$storeProductAttrResultServices = app()->make(StoreProductAttrResultServices::class);
$storeProductAttrResultServices->setResult($data['result'], $id, $type);
/** @var StoreProductAttrValueServices $storeProductAttrValueServices */
$storeProductAttrValueServices = app()->make(StoreProductAttrValueServices::class);
$valueGroup = $data['valueGroup'] ?? [];
$updateSuks = array_column($valueGroup, 'suk');
$oldSuks = [];
$oldAttrValue = $storeProductAttrValueServices->getSkuArray(['product_id' => $id, 'type' => $type], '*', 'suk');
if ($oldAttrValue) $oldSuks = array_column($oldAttrValue, 'suk');
$delSuks = array_merge(array_diff($oldSuks, $updateSuks));
$dataAll = [];
$res1 = $res2 = $res3 = true;
foreach ($valueGroup as $item) {
$item['virtual_list'] = json_encode($item['virtual_list']);
$item['level_price'] = json_encode($item['level_price']);
$item['channel_level_price'] = json_encode($item['channel_level_price']);
if ($oldSuks && in_array($item['suk'], $oldSuks) && isset($oldAttrValue[$item['suk']])) {
$attrId = $oldAttrValue[$item['suk']]['id'];
unset($item['suk'], $item['unique']);
$res1 = $res1 && $storeProductAttrValueServices->update($attrId, $item);
} else {
$dataAll[] = $item;
}
}
if ($delSuks) {
$res2 = $storeProductAttrValueServices->del($id, $type, $delSuks);
}
if ($dataAll) {
$res3 = $storeProductAttrValueServices->saveAll($dataAll);
}
if ($res1 && $res2 && $res3) {
return $valueGroup;
} else {
throw new AdminException('商品规格信息保存失败');
}
}
/**
* 获取商品规格
* @param array $where
* @return array
*/
public function getProductAttr(array $where)
{
return $this->dao->getProductAttr($where);
}
/**
* 获取商品规格详情
* @param int $id
* @param int $uid
* @param int $cartNum //是否查询购物车数量
* @param int $type //活动类型 attr_value表
* @param int $productId
* @param array $productInfo
* @param int $discount //限时折扣
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getProductAttrDetailCache(int $id, int $uid, int $cartNum = 0, int $type = 0, int $productId = 0, array $productInfo = [], int $discount = -1, int $is_channel = 0)
{
$attrDetail = $this->dao->cacheTag()->remember('attr_' . $id . '_' . $type . '_' . $productId, function () use ($productId, $id, $type) {
$attrDetail = $this->dao->getProductAttr(['product_id' => $id, 'type' => $type]);
if (!$attrDetail && $type > 0 && $productId) {//活动商品未获取到规格信息
$attrDetail = $this->dao->getProductAttr(['product_id' => $productId, 'type' => 0]);
}
return $attrDetail;
}, 600);
/** @var StoreProductAttrValueServices $storeProductAttrValueService */
$storeProductAttrValueService = app()->make(StoreProductAttrValueServices::class);
$_values = $this->dao->cacheTag()->remember('attr_value_' . $id . '_' . $type, function () use ($storeProductAttrValueService, $id, $type) {
return $storeProductAttrValueService->getProductAttrValue(['product_id' => $id, 'type' => $type]);
}, 600);
$_values = $storeProductAttrValueService->getProductAttrValue(['product_id' => $id, 'type' => $type]);
if ($productId == 0) {
$productId = $id;
}
/** @var StoreProductServices $storeProductService */
$storeProductService = app()->make(StoreProductServices::class);
$vip_price = true;
if (!$storeProductService->vipIsOpen(!!($productInfo['is_vip'] ?? 0))) $vip_price = false;
$cartNumList = [];
$activityAttr = [];
if ($cartNum) {
/** @var StoreCartServices $storeCartService */
$storeCartService = app()->make(StoreCartServices::class);
$unique = array_column($_values, 'unique');
$cartNumList = $storeCartService->cacheTag('Cart_Nums_' . $uid)->remember(md5(json_encode($unique)),
function () use ($storeCartService, $unique, $id, $uid) {
return $storeCartService->getUserCartNums($unique, $id, $uid);
}
, 600);
}
$values = [];
$field = $type ? 'stock,price' : 'stock';
$storeProducts = $this->dao->cacheTag()->remember('attr_sku_' . $productId . '_' . $type, function () use ($storeProductAttrValueService, $productId, $field) {
return $storeProductAttrValueService->getSkuArray(['product_id' => $productId, 'type' => 0], $field, 'suk');
});
$channelDiscount = false;
if ($is_channel) {
$channelDiscountInfo = app()->make(ChannelMerchantServices::class)->isChannel($uid, true);
$channelDiscount = $channelDiscountInfo ? $channelDiscountInfo['discount'] : false;
if ($channelDiscount === false) {
throw new ValidateException('您不是采购商');
}
}
foreach ($_values as $value) {
if ($cartNum) {
$value['cart_num'] = $cartNumList[$value['unique']] ?? 0;
}
if (!$vip_price) $value['vip_price'] = 0;
$value['product_stock'] = $storeProducts[$value['suk']]['stock'] ?? 0;
if ($discount != -1) $value['price'] = bcmul((string)$value['price'], (string)bcdiv((string)$discount, '100', 2), 2);
if ($type) {
$value['product_price'] = $storeProducts[$value['suk']]['price'] ?? 0;
$attrs = explode(',', $value['suk']);
$count = count($attrs);
for ($i = 0; $i < $count; $i++) {
$activityAttr[$i][] = $attrs[$i];
}
}
if ($channelDiscount) {
if ($productInfo['channel_type'] == 1) {
$value['channel_price'] = bcmul($value['price'], $channelDiscount, 2);
} else {
foreach (json_decode($value['channel_level_price'], true) as $channel_level_price) {
if (isset($channelDiscountInfo) && $channel_level_price['id'] == $channelDiscountInfo['channel_type']) {
$value['channel_price'] = $channel_level_price['price'];
}
}
}
}
$value['small_image'] = get_thumb_water($value['image']);
$value['stock'] = $value['is_show'] == 1 ? $value['stock'] : 0;
$value['stock'] = max($value['stock'], 0);
$values[$value['suk']] = $value;
}
$attrResult = app()->make(StoreProductAttrResultServices::class)->getResult(['product_id' => $id, 'type' => $type]);
$attrPics = [];
foreach ($attrResult['attr'] as $resultAttr) {
foreach ($resultAttr['detail'] as $detail) {
if (is_string($detail)) {
$detail = [
'value' => $detail,
'pic' => '',
];
}
$attrPics[$resultAttr['value']][$detail['value']] = $detail['pic'] ?? '';
}
}
foreach ($attrDetail as $k => $v) {
$attr = $v['attr_values'];
//活动商品只展示参与活动sku
if ($type && $activityAttr && $a = array_merge(array_intersect($v['attr_values'], $activityAttr[$k]))) {
$attrDetail[$k]['attr_values'] = $a;
$attr = $a;
}
$is_pic = 0;
foreach ($attr as $kk => $vv) {
$attrDetail[$k]['attr_value'][$kk]['attr'] = $vv;
$attrDetail[$k]['attr_value'][$kk]['check'] = false;
$attrDetail[$k]['attr_value'][$kk]['pic'] = $attrPics[$v['attr_name']][$vv] ?? '';
if ($attrDetail[$k]['attr_value'][$kk]['pic']) {
$is_pic = 1;
}
}
$attrDetail[$k]['is_pic'] = $is_pic;
}
return [$attrDetail, $values];
}
/**
* 获取商品规格详情
* @param int $id
* @param int $uid
* @param int $cartNum //是否查询购物车数量
* @param int $type //活动类型 attr_value表
* @param int $productId
* @param array $productInfo
* @param int $discount //限时折扣
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getProductAttrDetail(int $id, int $uid, int $cartNum = 0, int $type = 0, int $productId = 0, array $productInfo = [], int $discount = -1, $is_channel = 0)
{
$attrDetail = $this->dao->getProductAttr(['product_id' => $id, 'type' => $type]);
if (!$attrDetail && $type > 0 && $productId) {//活动商品未获取到规格信息
$attrDetail = $this->dao->getProductAttr(['product_id' => $productId, 'type' => 0]);
}
/** @var StoreProductAttrValueServices $storeProductAttrValueService */
$storeProductAttrValueService = app()->make(StoreProductAttrValueServices::class);
$_values = $storeProductAttrValueService->getProductAttrValue(['product_id' => $id, 'type' => $type]);
if ($productId == 0) $productId = $id;
/** @var StoreProductServices $storeProductService */
$storeProductService = app()->make(StoreProductServices::class);
if (!$productInfo) {
$productInfo = $storeProductService->get($productId, ['is_vip']);
}
$vip_price = true;
if (!$storeProductService->vipIsOpen(!!$productInfo['is_vip'])) $vip_price = false;
$cartNumList = [];
$activityAttr = [];
if ($cartNum) {
/** @var StoreCartServices $storeCartService */
$storeCartService = app()->make(StoreCartServices::class);
//真实用户
if ($uid) {
$cartNumList = $storeCartService->getUserCartNums(array_column($_values, 'unique'), $id, $uid);
} else {
//虚拟用户
$touristUid = $this->getItem('touristUid');
if ($touristUid) {
$cartNumList = $storeCartService->getUserCartNums(array_column($_values, 'unique'), $id, $touristUid, 'tourist_uid');
}
}
}
$values = [];
$field = $type ? 'stock,price' : 'stock';
$channelDiscount = false;
if ($is_channel && $uid) {
$channelDiscount = app()->make(ChannelMerchantServices::class)->isChannel($uid, true);
$channelDiscount = $channelDiscount ? $channelDiscount['discount'] : false;
if ($channelDiscount === false) {
throw new ValidateException('请选择采购商用户');
}
}
$storeProducts = $storeProductAttrValueService->getSkuArray(['product_id' => $productId, 'type' => 0], $field, 'suk');
foreach ($_values as $value) {
if ($cartNum) {
$value['cart_num'] = $uid || $touristUid ? ($cartNumList[$value['unique']] ?? 0) : 0;
}
if (!$vip_price) $value['vip_price'] = 0;
$value['product_stock'] = $storeProducts[$value['suk']]['stock'] ?? 0;
if ($channelDiscount) {
$value['channel_price'] = bcmul($value['price'], $channelDiscount, 2);
} else {
if ($discount != -1) $value['price'] = bcmul((string)$value['price'], (string)bcdiv((string)$discount, '100', 2), 2);
}
if ($type) {
$value['product_price'] = $storeProducts[$value['suk']]['price'] ?? 0;
$attrs = explode(',', $value['suk']);
$count = count($attrs);
for ($i = 0; $i < $count; $i++) {
$activityAttr[$i][] = $attrs[$i];
}
if ($value['product_stock'] <= 0) {//原商品规格无库存
$value['stock'] = 0;
}
}
$value['small_image'] = get_thumb_water($value['image']);
$values[$value['suk']] = $value;
}
$attrResult = app()->make(StoreProductAttrResultServices::class)->getResult(['product_id' => $id, 'type' => $type]);
$attrPics = [];
if (isset($attrResult['attr'])) {
foreach ($attrResult['attr'] as $resultAttr) {
foreach ($resultAttr['detail'] as $detail) {
if (is_string($detail)) {
$detail = [
'value' => $detail,
'pic' => '',
];
}
$attrPics[$resultAttr['value']][$detail['value']] = $detail['pic'] ?? '';
}
}
}
foreach ($attrDetail as $k => $v) {
$attr = $v['attr_values'];
//活动商品只展示参与活动sku
if ($type && $activityAttr && $a = array_merge(array_intersect($v['attr_values'], $activityAttr[$k]))) {
$attrDetail[$k]['attr_values'] = $a;
$attr = $a;
}
$is_pic = 0;
foreach ($attr as $kk => $vv) {
$attrDetail[$k]['attr_value'][$kk]['attr'] = $vv;
$attrDetail[$k]['attr_value'][$kk]['check'] = false;
$attrDetail[$k]['attr_value'][$kk]['pic'] = $attrPics[$v['attr_name']][$vv] ?? '';
if ($attrDetail[$k]['attr_value'][$kk]['pic']) {
$is_pic = 1;
}
}
$attrDetail[$k]['is_pic'] = $is_pic;
}
return [$attrDetail, $values];
}
/**
* 删除一条数据
* @param int $id
* @param int $type
*/
public function del(int $id, int $type)
{
$this->dao->del($id, $type);
}
/**
* 设置规格
* @param array $data
* @param int $id
* @param int $type
* @return bool
* @throws \Exception
*/
public function setAttr(array $data, int $id, int $type)
{
foreach ($data as &$item) {
$item['attr_values'] = array_column($item['attr_values'], 'value');
}
if ($data) {
$this->dao->del($id, $type);
$res = $this->dao->saveAll($data);
if (!$res) throw new AdminException('规格保存失败');
}
return true;
}
}

View File

@@ -0,0 +1,647 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\product\sku;
use app\dao\product\sku\StoreProductAttrValueDao;
use app\jobs\product\ProductStockTips;
use app\jobs\product\ProductStockValueTips;
use app\model\product\stock\StockRecord;
use app\services\activity\bargain\StoreBargainServices;
use app\services\activity\combination\StoreCombinationServices;
use app\services\activity\discounts\StoreDiscountsServices;
use app\services\activity\integral\StoreIntegralServices;
use app\services\activity\seckill\StoreSeckillServices;
use app\services\BaseServices;
use app\services\product\product\StoreProductStockRecordServices;
use app\webscoket\SocketPush;
use crmeb\exceptions\AdminException;
use app\services\product\product\StoreProductServices;
use crmeb\services\CacheService;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class StoreProductAttrValueService
* @package app\services\product\sku
* @mixin StoreProductAttrValueDao
*/
class StoreProductAttrValueServices extends BaseServices
{
/**
* @var StoreProductAttrValueDao
*/
#[Inject]
protected StoreProductAttrValueDao $dao;
/**
* 获取单规格规格
* @param array $where
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOne(array $where, string $field = '*')
{
return $this->dao->getOne($where, $field);
}
/**
* 获取商品规格信息
* @param int $id
* @param int $pid
* @param int $type
* @return array
*/
public function attrList(int $id, int $pid, int $type = 0)
{
/** @var StoreProductAttrResultServices $storeProductAttrResultServices */
$storeProductAttrResultServices = app()->make(StoreProductAttrResultServices::class);
$result = $storeProductAttrResultServices->value(['product_id' => $id, 'type' => $type], 'result');
$items = json_decode($result, true)['attr'];
$productAttr = $this->getAttr($items, $pid, 0);
$activityAttr = $this->getAttr($items, $id, $type);
foreach ($productAttr as $pk => &$pv) {
if ($type == 3) {
$sv['ot_price'] = isset($pv['_checked']) && $pv['_checked'] ? $pv['ot_price'] : $pv['price'];
}
foreach ($activityAttr as &$sv) {
if ($pv['detail'] == $sv['detail']) {
if ($type == 3) {
$sv['ot_price'] = isset($sv['_checked']) && $sv['_checked'] ? $sv['ot_price'] : $sv['price'];
}
$productAttr[$pk] = $sv;
}
}
$productAttr[$pk]['detail'] = json_decode($productAttr[$pk]['detail']);
}
$attrs['items'] = $items;
$attrs['value'] = $productAttr;
foreach ($items as $key => $item) {
$header[] = ['title' => $item['value'], 'key' => 'value' . ($key + 1), 'align' => 'center', 'minWidth' => 80];
}
$header[] = ['title' => '图片', 'slot' => 'pic', 'align' => 'center', 'minWidth' => 120];
$header[] = ['title' => '成本价', 'key' => 'cost', 'align' => 'center', 'minWidth' => 80];
if ($type == 3) {
$header[] = ['title' => '日常售价', 'key' => 'ot_price', 'align' => 'center', 'minWidth' => 80];
} elseif ($type == 4) {
$header[] = ['title' => '金额', 'key' => 'price', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
} else {
$header[] = ['title' => '划线价', 'key' => 'ot_price', 'align' => 'center', 'minWidth' => 80];
}
if ($type == 1) {
$header[] = ['title' => '秒杀价', 'key' => 'price', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
} elseif ($type == 2) {
$header[] = ['title' => '砍价起始金额', 'slot' => 'price', 'align' => 'center', 'minWidth' => 80];
$header[] = ['title' => '砍价最低价', 'slot' => 'min_price', 'align' => 'center', 'minWidth' => 80];
} elseif ($type == 3) {
$header[] = ['title' => '拼团价', 'key' => 'price', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
} elseif ($type == 4) {
$header[] = ['title' => '兑换积分', 'key' => 'integral', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
}
$header[] = ['title' => '库存', 'key' => 'stock', 'align' => 'center', 'minWidth' => 80];
if (in_array($type, [1, 3, 4])) {
$header[] = ['title' => $type == 4 ? '兑换次数' : '限量', 'key' => 'quota', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
} else {
$header[] = ['title' => '限量', 'slot' => 'quota', 'align' => 'center', 'minWidth' => 80];
}
$header[] = ['title' => '重量(KG)', 'key' => 'weight', 'align' => 'center', 'minWidth' => 80];
$header[] = ['title' => '体积(m³)', 'key' => 'volume', 'align' => 'center', 'minWidth' => 80];
$header[] = ['title' => '商品条形码', 'key' => 'bar_code', 'align' => 'center', 'minWidth' => 80];
$header[] = ['title' => '商品编号', 'key' => 'code', 'align' => 'center', 'minWidth' => 80];
$attrs['header'] = $header;
return $attrs;
}
/**
* 获取规格
* @param $attr
* @param $id
* @param $type
* @return array
*/
public function getattr($attr, $id, $type)
{
foreach ($attr as $k => $v) {
foreach ($v['detail'] as $key => $item) {
if (isset($item['value'])) {
$attr[$k]['detail'][$key] = $item['value'];
}
}
}
$value = attr_format($attr)[1];
$valueNew = [];
$count = 0;
if ($type == 2) {
/** @var StoreBargainServices $storeBargainServices */
$storeBargainServices = app()->make(StoreBargainServices::class);
$min_price = $storeBargainServices->value(['id' => $id], 'min_price');
} else {
$min_price = 0;
}
$sukValueArr = $this->getSkuArray(['product_id' => $id, 'type' => $type], 'bar_code,code,cost,price,ot_price,stock,image as pic,weight,volume,brokerage,brokerage_two,quota,quota_show,settle_price,integral', 'suk');
foreach ($value as $key => $item) {
$detail = $item['detail'];
// sort($item['detail'], SORT_STRING);
$suk = implode(',', $item['detail']);
$sukValue = $sukValueArr[$suk] ?? [];
if ($sukValue) {
foreach (array_values($detail) as $k => $v) {
$valueNew[$count]['value' . ($k + 1)] = $v;
}
$valueNew[$count]['detail'] = json_encode($detail);
$valueNew[$count]['pic'] = $sukValue['pic'] ?? '';
$valueNew[$count]['price'] = $sukValue['price'] ? floatval($sukValue['price']) : 0;
$valueNew[$count]['integral'] = $sukValue['integral'] ?: 0;
$valueNew[$count]['settle_price'] = $sukValue['cost'] ? floatval($sukValue['settle_price']) : 0;
$valueNew[$count]['cost'] = $sukValue['cost'] ? floatval($sukValue['cost']) : 0;
$valueNew[$count]['ot_price'] = isset($sukValue['ot_price']) ? floatval($sukValue['ot_price']) : 0;
$valueNew[$count]['stock'] = $sukValue['stock'] ? intval($sukValue['stock']) : 0;
$valueNew[$count]['quota'] = isset($sukValue['quota_show']) && $sukValue['quota_show'] ? intval($sukValue['quota_show']) : 0;
$valueNew[$count]['code'] = $sukValue['code'] ?? '';
$valueNew[$count]['bar_code'] = $sukValue['bar_code'] ?? '';
$valueNew[$count]['weight'] = $sukValue['weight'] ? floatval($sukValue['weight']) : 0;
$valueNew[$count]['volume'] = $sukValue['volume'] ? floatval($sukValue['volume']) : 0;
$valueNew[$count]['brokerage'] = $sukValue['brokerage'] ? floatval($sukValue['brokerage']) : 0;
$valueNew[$count]['brokerage_two'] = $sukValue['brokerage_two'] ? floatval($sukValue['brokerage_two']) : 0;
switch ($type) {
case 1://秒杀
$valueNew[$count]['_checked'] = true;
break;
case 2://砍价
$valueNew[$count]['min_price'] = $min_price ? floatval($min_price) : 0;
$valueNew[$count]['opt'] = true;
break;
case 3://拼团
$valueNew[$count]['_checked'] = true;
break;
case 4://积分
$valueNew[$count]['integral'] = isset($sukValue['integral']) ? floatval($sukValue['integral']) : 0;
$valueNew[$count]['_checked'] = true;
break;
default:
$valueNew[$count]['_checked'] = false;
$valueNew[$count]['opt'] = false;
break;
}
$count++;
}
}
return $valueNew;
}
/**
* 根据活动商品unique查看原商品unique
* @param string $unique
* @param int $activity_id
* @param int $type
* @param array|string[] $field
* @return array|mixed|string|\think\Model|null
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getUniqueByActivityUnique(string $unique, int $activity_id, int $type = 1, array $field = ['unique'])
{
if ($type == 0) return $unique;
$attrValue = $this->dao->get(['unique' => $unique, 'product_id' => $activity_id, 'type' => $type], ['id', 'suk', 'product_id']);
if (!$attrValue) {
return '';
}
switch ($type) {
case 1://秒杀
/** @var StoreSeckillServices $activityServices */
$activityServices = app()->make(StoreSeckillServices::class);
break;
case 2://砍价
/** @var StoreBargainServices $activityServices */
$activityServices = app()->make(StoreBargainServices::class);
break;
case 3://拼团
/** @var StoreCombinationServices $activityServices */
$activityServices = app()->make(StoreCombinationServices::class);
break;
case 4://积分
/** @var StoreIntegralServices $activityServices */
$activityServices = app()->make(StoreIntegralServices::class);
break;
case 5://套餐
/** @var StoreDiscountsServices $activityServices */
$activityServices = app()->make(StoreDiscountsServices::class);
break;
default:
/** @var StoreProductServices $activityServices */
$activityServices = app()->make(StoreProductServices::class);
break;
}
$product_id = $activityServices->value(['id' => $activity_id], 'product_id');
if (!$product_id) {
return '';
}
if (count($field) == 1) {
return $this->dao->value(['suk' => $attrValue['suk'], 'product_id' => $product_id, 'type' => 0], $field[0] ?? 'unique');
} else {
return $this->dao->get(['suk' => $attrValue['suk'], 'product_id' => $product_id, 'type' => 0], $field);
}
}
/**
* 删除一条数据
* @param int $id
* @param int $type
* @param array $suk
* @return bool
*/
public function del(int $id, int $type, array $suk = [])
{
return $this->dao->del($id, $type, $suk);
}
/**
* 批量保存
* @param array $data
*/
public function saveAll(array $data)
{
$res = $this->dao->saveAll($data);
if (!$res) throw new AdminException('规格保存失败');
return $res;
}
/**
* 获取sku
* @param array $where
* @param string $field
* @param string $key
* @return array
*/
public function getSkuArray(array $where, string $field = 'id,unique,bar_code,cost,price,integral,ot_price,stock,image as pic,weight,volume,brokerage,brokerage_two,quota,product_id,code,settle_price,level_price,vip_price,channel_level_price', string $key = 'suk')
{
return $this->dao->getColumn($where, $field, $key, false, 'sort asc,id asc');
}
/**
* 交易排行榜
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function purchaseRanking()
{
$dlist = $this->dao->attrValue();
/** @var StoreProductServices $proServices */
$proServices = app()->make(StoreProductServices::class);
$slist = $proServices->getProductLimit(['is_del' => 0], 20, 'id as product_id,store_name,sales * price as val');
$data = array_merge($dlist, $slist);
$last_names = array_column($data, 'val');
array_multisort($last_names, SORT_DESC, $data);
$list = array_splice($data, 0, 20);
return $list;
}
/**
* 获取商品的属性数量
* @param $product_id
* @param $unique
* @param $type
* @return int
*/
public function getAttrvalueCount($product_id, $unique, $type)
{
return $this->dao->count(['product_id' => $product_id, 'unique' => $unique, 'type' => $type]);
}
/**
* 获取唯一值下的库存
* @param string $unique
* @return int
*/
public function uniqueByStock(string $unique)
{
if (!$unique) return 0;
return $this->dao->uniqueByStock($unique);
}
/**
* 减销量,加库存
* @param $productId
* @param $unique
* @param $num
* @param int $type
* @return mixed
*/
public function decProductAttrStock($productId, $unique, $num, $type = 0)
{
$res = $this->dao->decStockIncSales([
'product_id' => $productId,
'unique' => $unique,
'type' => $type
], $num);
if ($res) {
$this->workSendStock($productId, $unique, $type);
}
return $res;
}
/**
* 减少销量增加库存
* @param $productId
* @param $unique
* @param $num
* @return bool
*/
public function incProductAttrStock(int $productId, string $unique, int $num, int $type = 0)
{
return $this->dao->incStockDecSales(['unique' => $unique, 'product_id' => $productId, 'type' => $type], $num);
}
/**
* 库存预警消息提醒
* @param int $productId
* @param string $unique
* @param int $type
*/
public function workSendStock(int $productId, string $unique, int $type)
{
ProductStockValueTips::dispatch([$productId, $unique, $type]);
}
/**
* 更新sum_stock
* @param array $uniques
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function updateSumStock(array $uniques)
{
$stockSumData = [];
$this->dao->getList(['unique' => $uniques])->map(function ($item) use ($stockSumData) {
if (isset($stockSumData[$item->unique])) {
$data['sum_stock'] = $item->stock + $stockSumData[$item->unique];
} else {
$data['sum_stock'] = $item->stock;
}
$this->dao->update(['product_id' => $item['product_id'], 'unique' => $item['unique'], 'type' => $item['type']], $data);
});
}
/**
* 批量快速修改商品规格库存
* @param int $id
* @param array $data
* @return int|string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function saveProductAttrsStock(int $id, array $data, int $admin_id = 0)
{
/** @var StoreProductServices $productServices */
$productServices = app()->make(StoreProductServices::class);
$product = $productServices->get($id);
if (!$product) {
throw new ValidateException('商品不存在');
}
$attrs = $this->dao->getProductAttrValue(['product_id' => $id, 'type' => 0]);
if ($attrs) $attrs = array_combine(array_column($attrs, 'unique'), $attrs);
$dataAll = $update = [];
$stock = 0;
$time = time();
$inInventory = [];
$outInventory = [];
foreach ($data as $attr) {
if (!isset($attrs[$attr['unique']])) continue;
if ($attr['pm']) {
if ($attr['stock']) {
$inInventory[] = [
'pm' => $attr['pm'],
'unique' => $attr['unique'],
'inventory' => $attr['stock'],
'product_id' => $id
];
}
$stock = bcadd((string)$stock, (string)$attr['stock'], 0);
$update['stock'] = bcadd((string)$attrs[$attr['unique']]['stock'], (string)$attr['stock'], 0);
$update['sum_stock'] = bcadd((string)$attrs[$attr['unique']]['sum_stock'], (string)$attr['stock'], 0);
} else {
$stock = bcsub((string)$stock, (string)$attr['stock'], 0);
$update['stock'] = bcsub((string)$attrs[$attr['unique']]['stock'], (string)$attr['stock'], 0);
if ($attr['stock']) {
$_outInventory = [
'pm' => $attr['pm'],
'unique' => $attr['unique'],
'product_id' => $id
];
if ($update['stock'] > 0) {
$_outInventory['inventory'] = $attr['stock'];
} else {
$_outInventory['inventory'] = $attrs[$attr['unique']]['stock'];
}
$outInventory[] = $_outInventory;
}
$update['sum_stock'] = bcsub((string)$attrs[$attr['unique']]['sum_stock'], (string)$attr['stock'], 0);
}
// $update['stock'] = $update['stock'] > 0 ? $update['stock'] : 0;
$this->dao->update(['id' => $attrs[$attr['unique']]['id']], $update);
$dataAll[] = [
'product_id' => $id,
'unique' => $attr['unique'],
'cost_price' => $attrs[$attr['unique']]['cost'] ?? 0,
'number' => $attr['stock'],
'pm' => $attr['pm'] ? 1 : 0,
'add_time' => $time,
];
}
$product_stock = $stock ? bcadd((string)$product['stock'], (string)$stock, 0) : bcsub((string)$product['stock'], (string)$stock, 0);
// $product_stock = $product_stock > 0 ? $product_stock : 0;
//修改商品库存
$productServices->update($id, ['stock' => $product_stock]);
//添加库存记录$product_stock
if ($dataAll) {
/** @var StoreProductStockRecordServices $storeProductStockRecordServces */
$storeProductStockRecordServces = app()->make(StoreProductStockRecordServices::class);
$storeProductStockRecordServces->saveAll($dataAll);
}
if ($admin_id) {
if (count($inInventory) > 0) {
event('product.stock.record', [StockRecord::STOCK_TYPE_OTHER_IN, StockRecord::TYPE_IN, $inInventory, $admin_id, '']);
}
if (count($outInventory) > 0) {
event('product.stock.record', [StockRecord::STOCK_TYPE_OTHER_OUT, StockRecord::TYPE_OUT, $outInventory, $admin_id, '']);
}
}
//清除缓存
$productServices->cacheTag()->clear();
/** @var StoreProductAttrServices $attrService */
$attrService = app()->make(StoreProductAttrServices::class);
$attrService->cacheTag()->clear();
//警戒库存
$productServices->workSendStock($id);
return $product_stock;
}
/**
* 查询库存预警产品ids
* @param array $where
* @return array
*/
public function getGroupId(array $where)
{
$res1 = [];
$res2 = $this->dao->getGroupData('product_id', 'product_id', $where);
foreach ($res2 as $id) {
$res1[] = $id['product_id'];
}
return $res1;
}
/**
* 批量修改库存价格
* @param $id
* @param $data
* @return true
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2024/1/25 10:01
*/
public function updateAttrs($id, $data)
{
/** @var StoreProductServices $productServices */
$productServices = app()->make(StoreProductServices::class);
$product = $productServices->get($id);
if (!$product) {
throw new ValidateException('商品不存在');
}
$attrs = $this->dao->getProductAttrValue(['product_id' => $id, 'type' => 0]);
$data = array_combine(array_column($data, 'unique'), $data);
$dataAll = $update = [];
$product_stock = $product_price = $product_ot_price = $product_cost = 0;
$time = time();
foreach ($attrs as $item) {
$attr = $data[$item['unique']] ?? [];
if ($attr) {
$this->update($item['id'], [
'price' => $attr['price'],
'stock' => $attr['stock'],
'sum_stock' => $attr['stock'],
'cost' => $attr['cost'],
'ot_price' => $attr['ot_price']
]);
$number = bcsub((string)$attr['stock'], (string)$item['stock'], 0);
$dataAll[] = [
'product_id' => $id,
'unique' => $attr['unique'],
'cost_price' => $attr['cost'] ?? 0,
'number' => abs($number),
'pm' => $number > 0 ? 1 : 0,
'add_time' => $time,
];
}
$product_array = $attr ?: $item;
// 计算商品库存
$product_stock = bcadd((string)$product_stock, (string)$product_array['stock'], 0);
// 更新商品价格
$product_price = max($product_price, $product_array['price']);
// 更新商品原价
$product_ot_price = max($product_ot_price, $product_array['ot_price']);
// 更新商品成本
$product_cost = max($product_cost, $product_array['cost']);
}
// 修改商品库存等信息
$productServices->update($id, [
'stock' => $product_stock,
'price' => $product_price,
'ot_price' => $product_ot_price,
'cost' => $product_cost,
]);
if ($dataAll) {
/** @var StoreProductStockRecordServices $storeProductStockRecordServces */
$storeProductStockRecordServces = app()->make(StoreProductStockRecordServices::class);
$storeProductStockRecordServces->saveAll($dataAll);
}
// 清除缓存
$productServices->cacheTag()->clear();
/** @var StoreProductAttrServices $attrService */
$attrService = app()->make(StoreProductAttrServices::class);
$attrService->cacheTag()->clear();
return true;
}
/**
* 购买普通商品扣除活动的总库存显示
* @param $productId
* @param $unique
* @param $num
* @return bool
* @author wuhaotian
* @email 442384644@qq.com
* @date 2025/9/18
*/
public function decMarketingProductStock($productId, $unique, $num)
{
try {
$sku = $this->dao->value(['product_id' => $productId, 'unique' => $unique], 'suk');
$seckillIds = app()->make(StoreSeckillServices::class)->getColumn(['product_id' => $productId, 'is_del' => 0], 'id');
$bargainIds = app()->make(StoreBargainServices::class)->getColumn(['product_id' => $productId, 'is_del' => 0], 'id');
$combinationIds = app()->make(StoreCombinationServices::class)->getColumn(['product_id' => $productId, 'is_del' => 0], 'id');
if (count($seckillIds)) {
$this->dao->decUpdate([['product_id', 'in', $seckillIds], ['suk', '=', $sku], ['type', '=', 1]], 'stock', $num);
}
if (count($bargainIds)) {
$this->dao->decUpdate([['product_id', 'in', $bargainIds], ['suk', '=', $sku], ['type', '=', 2]], 'stock', $num);
}
if (count($combinationIds)) {
$this->dao->decUpdate([['product_id', 'in', $combinationIds], ['suk', '=', $sku], ['type', '=', 3]], 'stock', $num);
}
} catch (\Exception $e) {
}
return true;
}
/**
* 查询规格列表
* @param array $where
* @return array
* @throws \ReflectionException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2025/9/18 16:42
*/
public function getAttrValueList(array $where = [])
{
[$page, $limit] = $this->getPageValue();
$query = $this->dao->joinAttrSearch($where);
$count = $query->count();
$list = $query->field('a.*,p.store_name')->page($page, $limit)->order('p.sort desc,p.id desc')->select()->toArray();
// $this->dao->getAttrList($where,'*', $page, $limit,'id desc',['product']);
return compact('list', 'count');
}
}

View File

@@ -0,0 +1,174 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\product\sku;
use app\dao\product\sku\StoreProductRuleDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
/**
* Class StoreProductRuleService
* @package app\services\product\sku
* @mixin StoreProductRuleDao
*/
class StoreProductRuleServices extends BaseServices
{
/**
* @var StoreProductRuleDao
*/
#[Inject]
protected StoreProductRuleDao $dao;
/**
* 获取规格模板
* @param int $type
* @param int $relation_id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getRule(int $type = 0, int $relation_id = 0 )
{
$list = $this->dao->getList(['type' => $type, 'relation_id' => $relation_id]);
foreach ($list as &$item) {
$attr_name = $attr_value = [];
if ($item['rule_value']) {
$item['rule_value'] = $specs = json_decode($item['rule_value'], true);
foreach ($item['rule_value'] as $ruleKey => $ruleItem) {
foreach ($ruleItem['detail'] as $valueKey => $valueItem) {
if (is_string($valueItem)) {
$item['rule_value'][$ruleKey]['detail'][$valueKey] = ['value' => $valueItem, 'pic' => ''];
$item['rule_value'][$ruleKey]['add_pic'] = 0;
}
}
}
if ($specs) {
foreach ($specs as $key => $value) {
$attr_name[] = $value['value'];
$attr_value[] = implode(',', $value['detail']);
}
} else {
$attr_name[] = '';
$attr_value[] = '';
}
$item['attr_name'] = implode(',', $attr_name);
$item['attr_value'] = $attr_value;
}
}
return $list;
}
/**
* 获取商品规格列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where = [])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $page, $limit);
foreach ($list as &$item) {
$attr_name = $attr_value = [];
if ($item['rule_value']) {
$specs = json_decode($item['rule_value'], true);
if ($specs) {
foreach ($specs as $key => $value) {
$attr_name[] = $value['value'];
$attr_value[] = implode(',', $value['detail']);
}
} else {
$attr_name[] = '';
$attr_value[] = '';
}
$item['attr_name'] = implode(',', $attr_name);
$item['attr_value'] = $attr_value;
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 保存数据
* @param int $id
* @param array $data
* @param int $type
* @param int $relation_id
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function save(int $id, array $data, int $type = 0, int $relation_id = 0)
{
$data['type'] = $type;
$data['relation_id'] = $relation_id;
$data['rule_value'] = json_encode($data['spec']);
unset($data['spec']);
$rule = $this->dao->getOne(['rule_name' => $data['rule_name'], 'type' => $type, 'relation_id' => $relation_id]);
if ($id) {
if ($rule && $rule['id'] != $id) {
throw new AdminException('分类名称已存在');
}
$res = $this->dao->update($id, $data);
} else {
if ($rule) {
throw new AdminException('分类名称已存在');
}
$res = $this->dao->save($data);
}
if (!$res) throw new AdminException('保存失败');
}
/**
* 获取一条数据
* @param int $id
* @return array
*/
public function getInfo(int $id)
{
$info = $this->dao->get($id);
$info['spec'] = json_decode($info['rule_value'], true);
return compact('info');
}
/**
* 删除数据
* @param string $ids
*/
public function del(string $ids)
{
if ($ids == '') throw new AdminException('请至少选择一条数据');
$this->dao->del($ids);
}
/**
* @param array $where
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getProductRuleList(array $where, $field = "*")
{
return $this->dao->getProductRuleList($where, $field);
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace app\services\product\sku;
use app\dao\product\sku\StoreProductVirtualDao;
use app\services\BaseServices;
use app\services\product\product\StoreProductServices;
use think\annotation\Inject;
/**
* 规格卡密信息
* Class StoreProductVirtualServices
* @package app\services\product\sku
* @mixin StoreProductVirtualDao
*/
class StoreProductVirtualServices extends BaseServices
{
/**
* @var StoreProductVirtualDao
*/
#[Inject]
protected StoreProductVirtualDao $dao;
/**
* 规格中获取卡密列表
* @param $unique
* @param $product_id
* @return array
*/
public function getArr($unique, $product_id)
{
$res = $this->dao->getColumn(['attr_unique' => $unique, 'product_id' => $product_id], 'card_no,card_pwd');
$data = [];
foreach ($res as $item) {
$data[] = ['key' => $item['card_no'], 'value' => $item['card_pwd']];
}
return $data;
}
/**
* 获取订单发送卡密列表
* @param array $where
* @param int $limit
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOrderCardList(array $where, int $limit = 1)
{
return $this->dao->getList($where, '*', 0, $limit);
}
/**
* 保存商品规格(虚拟卡密信息)
* @param int $id
* @param array $valueGroup
* @param int $store_id
* @return bool
*/
public function saveProductVirtual(int $id, array $valueGroup, int $store_id = 0)
{
/** @var StoreProductAttrValueServices $storeProductAttrValueServices */
$storeProductAttrValueServices = app()->make(StoreProductAttrValueServices::class);
/** @var StoreProductServices $storeProductServices */
$storeProductServices = app()->make(StoreProductServices::class);
$cardList = $this->dao->getList(['product_id' => $id, 'store_id' => $store_id], "card_no");
$cardList = empty($cardList) ? [] : array_column($cardList, "card_no");
$count = 0;
foreach ($valueGroup as $item) {
if (isset($item['product_type']) && $item['product_type'] == 1) {
$num = 0;
if (isset($item['disk_info']) && $item['disk_info']) {
$num = $item['stock'];
$count += $num;
} elseif (isset($item['virtual_list']) && count($item['virtual_list'])) {
$num = $this->dao->count(['store_id' => $store_id, 'product_id' => $id, 'attr_unique' => $item['unique'], 'uid' => 0]) ?? 0;
$count += $num;
$data = [];
foreach ($item['virtual_list'] as $items) {
if (!in_array($items['key'], $cardList)) {
$data[] = [
'product_id' => $id,
'attr_unique' => $item['unique'],
'card_no' => $items['key'],
'card_pwd' => $items['value'],
'card_unique' => md5($item['unique'] . ',' . $items['key'] . ',' . $items['value'])
];
$num++;
$count++;
}
}
}
if (!empty($data)) {
$this->dao->saveAll($data);
}
$storeProductAttrValueServices->update(['product_id' => $id, 'unique' => $item['unique']], ['stock' => $num]);
}
}
$storeProductServices->update($id, ['stock' => $count]);
unset($item);
return true;
}
}