Files
amb_wechatapp/application/admin/controller/app/physical/Goods.php
T

876 lines
28 KiB
PHP

<?php
namespace app\admin\controller\app\physical;
use app\common\controller\Backend;
use think\Db;
/**
* 商品管理
*/
class Goods extends Backend
{
protected $model = null;
protected $skuModel = null;
protected $skuPriceModel = null;
public function _initialize()
{
parent::_initialize();
$this->model = new \app\admin\model\app\physical\Goods;
$this->skuModel = new \app\admin\model\app\physical\Sku;
$this->skuPriceModel = new \app\admin\model\app\physical\SkuPrice;
}
/**
* 查看
*/
public function index()
{
$this->request->filter(['strip_tags', 'trim']);
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$query = $this->model
->where($where)
->where([
'uniacid'=>UNIACID
]);
// 处理分类搜索
$hasCategoryId = $this->request->has('category_id');
if ($hasCategoryId) {
$categoryIds = $this->request->post('category_id/a', []);
// 如果是空数组,返回空结果
if (empty($categoryIds)) {
$query->where('1=0');
} else {
// 获取所有分类及其子分类
$allCategoryIds = [];
foreach ($categoryIds as $categoryId) {
$childIds = $this->getCategoryChildIds($categoryId);
$allCategoryIds = array_merge($allCategoryIds, $childIds);
}
$allCategoryIds = array_unique($allCategoryIds);
$query->where(function ($q) use ($allCategoryIds) {
foreach ($allCategoryIds as $cid) {
$q->whereOrRaw("find_in_set($cid, category_id)");
}
});
}
}
$list = $query->order($sort, $order)->paginate($limit);
// 获取商品ID列表
$goodsIds = [];
foreach ($list as $item) {
$goodsIds[] = $item['id'];
}
// 批量获取SKU价格数据
$skuPrices = [];
if (!empty($goodsIds)) {
$skuPriceList = $this->skuPriceModel
->where('goods_id', 'in', $goodsIds)
->where('uniacid', UNIACID)
->select();
foreach ($skuPriceList as $skuPrice) {
$skuPrices[$skuPrice['goods_id']][] = $skuPrice;
}
}
// 处理列表数据
$rows = [];
foreach ($list as $item) {
$item = $item->toArray();
$goodsId = $item['id'];
// 获取该商品的SKU价格列表
$goodsSkuPrices = $skuPrices[$goodsId] ?? [];
// 计算总库存(只计算status=1的规格)
$totalStock = 0;
foreach ($goodsSkuPrices as $skuPrice) {
if (isset($skuPrice['status']) && $skuPrice['status'] == 1) {
$totalStock += $skuPrice['stock'];
}
}
$item['total_stock'] = $totalStock;
// SKU价格列表(用于编辑)
$item['sku_price_list'] = $goodsSkuPrices;
// 价格范围(从商品表获取价格,如果为空则从SKU计算)
$item['price'] = $item['price'] ?? 0;
if (!empty($goodsSkuPrices)) {
$prices = array_column($goodsSkuPrices, 'price');
$item['price_min'] = min($prices);
$item['price_max'] = max($prices);
} else {
$item['price_min'] = $item['price'];
$item['price_max'] = $item['price'];
}
$rows[] = $item;
}
$result = array("total" => $list->total(), "rows" => $rows,'attr'=>[
'status'=>$this->model->getStatusList(),
'specType'=>$this->model->getSpecTypeList(),
'isHidden'=>$this->model->getIsHiddenList()
]);
return $this->success("获取成功",$result);
}
/**
* 获取分类及其所有子分类ID
* @param int|array $categoryId 分类ID或分类ID数组
* @return array
*/
protected function getCategoryChildIds($categoryId)
{
$categoryIds = [];
// 支持数组或单个ID
$categoryIdArray = is_array($categoryId) ? $categoryId : [$categoryId];
foreach ($categoryIdArray as $cid) {
$categoryIds[] = $cid;
// 递归获取所有子分类
$this->getChildCategoryIdsById($cid, $categoryIds);
}
return array_unique($categoryIds);
}
/**
* 递归获取子分类ID
* @param int $parentId
* @param array $categoryIds
*/
protected function getChildCategoryIdsById($parentId, &$categoryIds)
{
$categoryModel = new \app\admin\model\app\physical\Category();
$children = $categoryModel
->where('parent_id', $parentId)
->where('uniacid', UNIACID)
->column('id');
foreach ($children as $childId) {
$categoryIds[] = $childId;
$this->getChildCategoryIdsById($childId, $categoryIds);
}
}
/**
* 递归获取子分类ID(兼容旧代码)
* @param object $model
* @param int $parentId
* @param array $categoryIds
*/
protected function getChildCategoryIds($model, $parentId, &$categoryIds)
{
$children = $model
->where('parent_id', $parentId)
->where('uniacid', UNIACID)
->column('id');
foreach ($children as $childId) {
$categoryIds[] = $childId;
$this->getChildCategoryIds($model, $childId, $categoryIds);
}
}
/**
* 表单验证
*/
protected function validateGoods($params, $skuList = [], $specType = 'single')
{
// 验证发货时间
if (!isset($params['delivery_time'])) {
$this->error('发货时间不能为空');
}
// 验证单次限购
if (isset($params['single_limit'])) {
if (!is_numeric($params['single_limit'])) {
$this->error('单次限购数量必须是数字');
}
if ($params['single_limit'] < 0) {
$this->error('单次限购数量不能小于0');
}
}
// 验证终身限购
if (isset($params['lifetime_limit'])) {
if (!is_numeric($params['lifetime_limit'])) {
$this->error('终身限购数量必须是数字');
}
if ($params['lifetime_limit'] < 0) {
$this->error('终身限购数量不能小于0');
}
}
// 多规格验证
if ($specType == 'multi' && !empty($skuList)) {
$skuPrices = $skuList['sku_prices'] ?? [];
$skuNames = $skuList['sku_names'] ?? [];
// 验证每个规格都有规格值
if (!empty($skuNames)) {
foreach ($skuNames as $index => $skuName) {
if (empty($skuName['name'])) {
$this->error('规格名称不能为空');
}
if (empty($skuName['values']) || !is_array($skuName['values'])) {
$this->error('规格【' . $skuName['name'] . '】必须有规格值');
}
}
}
// 验证SKU价格
if (!empty($skuPrices)) {
foreach ($skuPrices as $index => $skuPrice) {
if (!isset($skuPrice['price']) || $skuPrice['price'] === '' || $skuPrice['price'] === null) {
$this->error('规格【' . ($skuPrice['sku_text'] ?? '第' . ($index + 1) . '个') . '】价格不能为空');
}
if (!is_numeric($skuPrice['price']) || $skuPrice['price'] < 0) {
$this->error('规格【' . ($skuPrice['sku_text'] ?? '第' . ($index + 1) . '个') . '】价格必须大于等于0');
}
}
}
}
}
/**
* 添加
*/
public function add()
{
$params = $this->request->param('row/a');
if (empty($params)) {
$this->error(__('Parameter %s can not be empty', ''));
}
$params = $this->preExcludeFields($params);
$specType = $params['spec_type'] ?? 'single';
$skus = $params['skus'] ?? [];
$skuList = $params['sku_list'] ?? [];
unset($params['skus'], $params['sku_list']);
// 表单验证
$this->validateGoods($params, $skuList, $specType);
// 处理轮播图
$carousel = $params['carousel'] ?? [];
if (is_array($carousel)) {
if (count($carousel) > 10) {
$this->error('轮播图最多只能上传10张');
}
$params['carousel'] = json_encode($carousel);
}
// 处理分类ID
if (isset($params['category_id']) && is_array($params['category_id'])) {
$params['category_id'] = implode(',', $params['category_id']);
}
$params['createtime'] = time();
$params['updatetime'] = time();
$params['uniacid'] = UNIACID;
// 设置限购字段默认值
if (!isset($params['single_limit'])) {
$params['single_limit'] = 0;
}
if (!isset($params['lifetime_limit'])) {
$params['lifetime_limit'] = 0;
}
Db::startTrans();
try {
$result = $this->model->allowField(true)->save($params);
if ($result && !empty($skuList)) {
$this->saveSkuData($this->model->id, $skuList, $specType);
// 计算并更新商品价格
$this->updateGoodsPrice($this->model->id, $specType);
// 计算并更新商品库存
$this->updateGoodsStock($this->model->id, $specType);
}
Db::commit();
} catch (ValidateException|PDOException|Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result !== false) {
$this->success("添加成功");
} else {
$this->error(__('No rows were inserted'));
}
}
/**
* 编辑
*/
public function edit($ids = null)
{
$params = $this->request->param('row/a');
if (empty($params)) {
$this->error(__('Parameter %s can not be empty', ''));
}
$params = $this->preExcludeFields($params);
$specType = $params['spec_type'] ?? 'single';
$skuList = $params['sku_list'] ?? [];
unset($params['sku_list']);
// 表单验证
$this->validateGoods($params, $skuList, $specType);
// 处理轮播图
$carousel = $params['carousel'] ?? [];
if (is_array($carousel)) {
if (count($carousel) > 10) {
$this->error('轮播图最多只能上传10张');
}
$params['carousel'] = json_encode($carousel);
}
// 处理分类ID
if (isset($params['category_id']) && is_array($params['category_id'])) {
$params['category_id'] = implode(',', $params['category_id']);
}
// 设置限购字段默认值
if (!isset($params['single_limit'])) {
$params['single_limit'] = 0;
}
if (!isset($params['lifetime_limit'])) {
$params['lifetime_limit'] = 0;
}
$params['updatetime'] = time();
Db::startTrans();
try {
$row = $this->model->where('id', $ids)->where('uniacid', UNIACID)->find();
if (!$row) {
$this->error(__('No Results were found'));
}
$result = $row->allowField(true)->save($params);
if ($result && !empty($skuList)) {
$this->saveSkuData($ids, $skuList, $specType);
// 计算并更新商品价格
$this->updateGoodsPrice($ids, $specType);
// 计算并更新商品库存
$this->updateGoodsStock($ids, $specType);
}
Db::commit();
} catch (ValidateException|PDOException|Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result !== false) {
$this->success("更新成功");
} else {
$this->error(__('No rows were updated'));
}
}
/**
* 生成唯一SKU编码
* 格式: SP + 13位随机字符
*/
protected function generateSkuSn()
{
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
do {
$sn = 'SP';
for ($i = 0; $i < 13; $i++) {
$sn .= $chars[mt_rand(0, strlen($chars) - 1)];
}
// 检查是否已存在
$exists = $this->skuPriceModel->where('sn', $sn)->find();
} while ($exists);
return $sn;
}
/**
* 保存规格和SKU数据
* @param int $goodsId 商品ID
* @param array $skuData SKU数据
* @param string $specType 规格类型:single=单规格,multi=多规格
*/
protected function saveSkuData($goodsId, $skuData, $specType = 'multi')
{
if ($specType == 'single') {
// 单规格处理
$this->saveSingleSku($goodsId, $skuData);
} else {
// 多规格处理
$this->saveMultiSku($goodsId, $skuData);
}
}
/**
* 保存单规格SKU
*/
protected function saveSingleSku($goodsId, $skuData)
{
$skuPrices = $skuData['sku_prices'] ?? [];
if (empty($skuPrices) || !isset($skuPrices[0])) {
// 如果没有提供单规格数据,直接删除所有规格数据
$this->skuModel->where('goods_id', $goodsId)->where('uniacid', UNIACID)->delete();
$this->skuPriceModel->where('goods_id', $goodsId)->where('uniacid', UNIACID)->delete();
return;
}
$skuPrice = $skuPrices[0];
// 查询是否已存在SKU价格记录
$existingSkuPrice = $this->skuPriceModel
->where('goods_id', $goodsId)
->where('uniacid', UNIACID)
->order('id', 'asc')
->find();
// 删除多余的规格项(多规格转单规格时)
$this->skuModel->where('goods_id', $goodsId)->where('uniacid', UNIACID)->delete();
// 准备单规格数据
$data = [
'goods_id' => $goodsId,
'uniacid' => UNIACID,
'goods_sku_ids' => null,
'goods_sku_text' => null,
'image' => $skuPrice['image'] ?? '',
'stock' => $skuPrice['stock'] ?? 0,
'stock_warning' => $skuPrice['stock_warning'] ?? null,
'sales' => $skuPrice['sales'] ?? 0,
'sn' => $skuPrice['sn'] ?? '',
'weight' => $skuPrice['weight'] ?? 0,
'volume' => $skuPrice['volume'] ?? 0,
'cost_price' => $skuPrice['cost_price'] ?? 0,
'original_price' => $skuPrice['original_price'] ?? 0,
'price' => $skuPrice['price'] ?? 0,
'status' => $skuPrice['status'] ?? 1,
'weigh' => $skuPrice['weigh'] ?? 0,
'updatetime' => time()
];
if ($existingSkuPrice) {
// 更新现有记录
// 删除该商品的其他SKU价格记录(防止多规格改单规格遗留数据)
$this->skuPriceModel
->where('goods_id', $goodsId)
->where('uniacid', UNIACID)
->where('id', '<>', $existingSkuPrice['id'])
->delete();
// 更新记录(不更新销量,销量是累计的)
unset($data['sales']);
$this->skuPriceModel->where('id', $existingSkuPrice['id'])->update($data);
} else {
// 新增记录
$data['createtime'] = time();
// 自动生成SKU编码(如果为空)
if (empty($data['sn'])) {
$data['sn'] = $this->generateSkuSn();
}
// 先删除所有旧的SKU价格记录
$this->skuPriceModel->where('goods_id', $goodsId)->where('uniacid', UNIACID)->delete();
$this->skuPriceModel->insert($data);
}
}
/**
* 保存多规格SKU
*/
protected function saveMultiSku($goodsId, $skuData)
{
// 先删除所有旧的规格数据
$this->skuModel->where('goods_id', $goodsId)->where('uniacid', UNIACID)->delete();
$this->skuPriceModel->where('goods_id', $goodsId)->where('uniacid', UNIACID)->delete();
if (empty($skuData)) {
return;
}
$skuNames = $skuData['sku_names'] ?? [];
$skuPrices = $skuData['sku_prices'] ?? [];
$skuIdMap = [];
// 保存规格名称和规格值
if (!empty($skuNames)) {
foreach ($skuNames as $skuName) {
$specId = $this->skuModel->insertGetId([
'goods_id' => $goodsId,
'uniacid' => UNIACID,
'name' => $skuName['name'] ?? '',
'parent_id' => 0,
'weigh' => $skuName['weigh'] ?? 0,
'createtime' => time(),
'updatetime' => time()
]);
if (!empty($skuName['values'])) {
foreach ($skuName['values'] as $index => $value) {
$childId = $this->skuModel->insertGetId([
'goods_id' => $goodsId,
'uniacid' => UNIACID,
'name' => $value,
'parent_id' => $specId,
'weigh' => $index,
'createtime' => time(),
'updatetime' => time()
]);
$skuIdMap[$skuName['name']][$value] = $childId;
}
}
}
}
// 保存SKU价格
if (!empty($skuPrices)) {
foreach ($skuPrices as $skuPrice) {
$skuIds = $skuPrice['sku_ids'] ?? [];
$skuText = $skuPrice['sku_text'] ?? '';
$skuIdStr = '';
if (is_array($skuIds)) {
$skuIdStr = implode(',', $skuIds);
}
// 自动生成SKU编码(如果为空)
$sn = $skuPrice['sn'] ?? '';
if (empty($sn)) {
$sn = $this->generateSkuSn();
}
$this->skuPriceModel->insert([
'goods_id' => $goodsId,
'uniacid' => UNIACID,
'goods_sku_ids' => $skuIdStr,
'goods_sku_text' => $skuText,
'image' => $skuPrice['image'] ?? '',
'stock' => $skuPrice['stock'] ?? 0,
'stock_warning' => $skuPrice['stock_warning'] ?? null,
'sales' => $skuPrice['sales'] ?? 0,
'sn' => $sn,
'weight' => $skuPrice['weight'] ?? 0,
'volume' => $skuPrice['volume'] ?? 0,
'cost_price' => $skuPrice['cost_price'] ?? 0,
'original_price' => $skuPrice['original_price'] ?? 0,
'price' => $skuPrice['price'] ?? 0,
'status' => $skuPrice['status'] ?? 1,
'weigh' => $skuPrice['weigh'] ?? 0,
'createtime' => time(),
'updatetime' => time()
]);
}
}
}
/**
* 详情
*/
public function detail($ids = null)
{
$row = $this->model->where('id', $ids)->where('uniacid', UNIACID)->find();
if (!$row) {
$this->error(__('No Results were found'));
}
// 处理分类ID
if (isset($row['category_id']) && !empty($row['category_id'])) {
$row['category_id'] = explode(',', $row['category_id']);
// 转换为整数数组
$row['category_id'] = array_map('intval', $row['category_id']);
} else {
$row['category_id'] = [];
}
$specType = $row['spec_type'] ?? 'single';
if ($specType == 'single') {
// 单规格商品,从sku_price表读取第一条记录
$skuPrice = $this->skuPriceModel
->where('goods_id', $ids)
->where('uniacid', UNIACID)
->order('id', 'asc')
->find();
$row['sku_tree'] = [];
if ($skuPrice) {
$row['sku_prices'] = [$skuPrice];
} else {
// 如果没有找到sku_price记录,返回空结构
$row['sku_prices'] = [];
}
} else {
// 多规格商品,从sku表读取数据
$skuTree = $this->skuModel->getTree($ids);
$skuPrices = $this->skuPriceModel->where('goods_id', $ids)->where('uniacid', UNIACID)->select();
$row['sku_tree'] = $skuTree;
$row['sku_prices'] = $skuPrices;
}
$this->success("获取成功", $row);
}
/**
* 更新商品价格
* 单规格:取单一价格
* 多规格:取最低价格
* @param int $goodsId 商品ID
* @param string $specType 规格类型:single=单规格,multi=多规格
*/
protected function updateGoodsPrice($goodsId, $specType = 'single')
{
$price = 0;
if ($specType == 'single') {
// 单规格:取第一个SKU的价格
$skuPrice = $this->skuPriceModel
->where('goods_id', $goodsId)
->where('uniacid', UNIACID)
->order('id', 'asc')
->find();
if ($skuPrice) {
$price = $skuPrice['price'] ?? 0;
}
} else {
// 多规格:取最低价格
$minPrice = $this->skuPriceModel
->where('goods_id', $goodsId)
->where('uniacid', UNIACID)
->min('price');
$price = $minPrice ?? 0;
}
// 更新商品价格
$this->model->where('id', $goodsId)->update([
'price' => $price,
'updatetime' => time()
]);
}
/**
* 更新商品库存
* 单规格:取单一库存(status=1)
* 多规格:取所有规格库存之和(status=1)
* @param int $goodsId 商品ID
* @param string $specType 规格类型:single=单规格,multi=多规格
*/
protected function updateGoodsStock($goodsId, $specType = 'single')
{
if (empty($goodsId)) {
return;
}
$stock = 0;
if ($specType == 'single') {
$skuPrice = $this->skuPriceModel
->where('goods_id', $goodsId)
->where('uniacid', UNIACID)
->where('status', 1)
->order('id', 'asc')
->find();
if ($skuPrice) {
$stock = $skuPrice['stock'] ?? 0;
}
} else {
$totalStock = $this->skuPriceModel
->where('goods_id', $goodsId)
->where('uniacid', UNIACID)
->where('status', 1)
->sum('stock');
$stock = $totalStock ?? 0;
}
// 更新商品库存
$this->model->where('id', $goodsId)->where('uniacid', UNIACID)->update([
'stock' => $stock,
'updatetime' => time()
]);
}
/**
* 删除
*/
public function del($ids = "")
{
if ($ids) {
Db::startTrans();
try {
$count = $this->model->where('id', 'in', $ids)->where('uniacid', UNIACID)->delete();
$this->skuModel->where('goods_id', 'in', $ids)->where('uniacid', UNIACID)->delete();
$this->skuPriceModel->where('goods_id', 'in', $ids)->where('uniacid', UNIACID)->delete();
Db::commit();
} catch (PDOException|Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($count) {
$this->success("删除成功");
} else {
$this->error(__('No rows were deleted'));
}
}
$this->error(__('Parameter %s can not be empty', 'ids'));
}
/**
* 批量更新
*/
public function multi($ids = "")
{
$ids = $ids ? $ids : $this->request->param("ids");
if ($ids) {
if ($this->request->has('params')) {
parse_str($this->request->post("params"), $values);
$values = $this->preExcludeFields($values);
$values['updatetime'] = time();
$result = $this->model->where('id', 'in', $ids)->where('uniacid', UNIACID)->update($values);
if ($result) {
$this->success("操作成功");
} else {
$this->error(__('No rows were updated'));
}
}
}
$this->error(__('Parameter %s can not be empty', 'ids'));
}
/**
* 批量更新SKU价格和库存
* 用于列表页快速编辑
*/
public function updateSkuPrice()
{
$params = $this->request->param();
$goodsId = $params['goods_id'] ?? 0;
$skuPrices = $params['sku_prices'] ?? [];
if (empty($goodsId)) {
$this->error('商品ID不能为空');
}
if (empty($skuPrices) || !is_array($skuPrices)) {
$this->error('SKU价格数据不能为空');
}
Db::startTrans();
try {
foreach ($skuPrices as $skuPrice) {
$skuPriceId = $skuPrice['id'] ?? 0;
if (empty($skuPriceId)) {
continue;
}
$updateData = [];
// 更新价格
if (isset($skuPrice['price'])) {
$updateData['price'] = floatval($skuPrice['price']);
}
// 更新库存(支持增减)
if (isset($skuPrice['stock_change'])) {
$stockChange = intval($skuPrice['stock_change']);
if ($stockChange > 0) {
// 增加库存
$this->skuPriceModel->where('id', $skuPriceId)
->where('uniacid', UNIACID)
->setInc('stock', $stockChange);
} elseif ($stockChange < 0) {
// 减少库存
$this->skuPriceModel->where('id', $skuPriceId)
->where('uniacid', UNIACID)
->setDec('stock', abs($stockChange));
}
}
// 直接设置库存
if (isset($skuPrice['stock'])) {
$updateData['stock'] = intval($skuPrice['stock']);
}
// 更新其他字段
if (!empty($updateData)) {
$updateData['updatetime'] = time();
$this->skuPriceModel->where('id', $skuPriceId)
->where('uniacid', UNIACID)
->update($updateData);
}
}
// 更新商品价格
$goods = $this->model->where('id', $goodsId)->where('uniacid', UNIACID)->find();
if ($goods) {
$specType = $goods['spec_type'] ?? 'single';
$this->updateGoodsPrice($goodsId, $specType);
}
Db::commit();
$this->success('更新成功');
} catch (\Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
}
/**
* 获取状态列表
*/
public function getStatusList()
{
$this->success("获取成功", $this->model->getStatusList());
}
/**
* 获取规格类型列表
*/
public function getSpecTypeList()
{
$this->success("获取成功", $this->model->getSpecTypeList());
}
/**
* 获取隐藏状态列表
*/
public function getIsHiddenList()
{
$this->success("获取成功", $this->model->getIsHiddenList());
}
}