2023-08-10 06:59:52 +00:00
< ? php
// +----------------------------------------------------------------------
// | likeshop开源商城系统
// +----------------------------------------------------------------------
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
// | gitee下载: https://gitee.com/likeshop_gitee
// | github下载: https://github.com/likeshop-github
// | 访问官网: https://www.likeshop.cn
// | 访问社区: https://home.likeshop.cn
// | 访问手册: http://doc.likeshop.cn
// | 微信公众号: likeshop技术社区
// | likeshop系列产品在gitee、github等公开渠道开源版本可免费商用, 未经许可不能去除前后端官方版权标识
// | likeshop系列产品收费版本务必购买商业授权, 购买去版权授权后, 方可去除前后端官方版权标识
// | 禁止对系统程序代码以任何目的,任何形式的再发布
// | likeshop团队版权所有并拥有最终解释权
// +----------------------------------------------------------------------
// | author: likeshop.cn.team
// +----------------------------------------------------------------------
namespace app\admin\controller\goods ;
use app\common\basics\AdminBase ;
use app\common\enum\GoodsEnum ;
use app\common\model\goods\GoodsBrand ;
use app\common\model\goods\GoodsUnit ;
use app\common\model\Freight ;
use app\common\model\goods\Supplier ;
use app\common\model\goods\Goods as GoodsModel ;
2023-10-12 03:27:45 +00:00
use app\common\model\shop\Shop ;
2023-08-10 06:59:52 +00:00
use app\common\server\JsonServer ;
use app\admin\logic\goods\CategoryLogic as MallCategoryLogic ;
use app\admin\logic\goods\GoodsLogic ;
use app\admin\logic\goods\ColumnLogic ;
use app\shop\logic\goods\CategoryLogic as ShopCategoryLogic ;
2023-10-12 03:27:45 +00:00
use PhpOffice\PhpSpreadsheet\IOFactory ;
2023-08-10 06:59:52 +00:00
use think\exception\ValidateException ;
use app\admin\validate\goods\GoodsValidate ;
2023-10-09 03:13:50 +00:00
use think\facade\Db ;
2023-10-12 07:45:14 +00:00
use think\facade\Log ;
2023-10-12 03:27:45 +00:00
use think\response\Json ;
2023-08-10 06:59:52 +00:00
/**
* 商品管理
* Class Goods
*/
class Goods extends AdminBase
{
/**
* Notes : 列表
*/
public function lists ()
{
if ( $this -> request -> isAjax ()) {
$get = $this -> request -> get ();
return JsonServer :: success ( '' , GoodsLogic :: lists ( $get ));
}
$cate_list = MallCategoryLogic :: categoryTreeeTree ();
$statistics = GoodsLogic :: statistics ();
$column_list = ColumnLogic :: getList ();
return view ( '' , [
'statistics' => $statistics ,
'cate_list' => $cate_list ,
'column_list' => $column_list ,
'goods_type' => GoodsEnum :: getTypeDesc ()
]);
}
/**
* 查看
*/
public function view ()
{
$goods_id = $this -> request -> get ( 'goods_id' );
$shop_id = GoodsModel :: where ( 'id' , $goods_id ) -> value ( 'shop_id' );
return view ( 'goods/goods/add' , [
'category_lists' => json_encode ( MallCategoryLogic :: getAllTree (), JSON_UNESCAPED_UNICODE ),
'shop_category_lists' => json_encode ( ShopCategoryLogic :: listAll ( $shop_id ), JSON_UNESCAPED_UNICODE ),
'brand_lists' => json_encode ( GoodsBrand :: getNameColumn (), JSON_UNESCAPED_UNICODE ),
'supplier_lists' => json_encode ( Supplier :: getNameColumn (), JSON_UNESCAPED_UNICODE ),
'unit_lists' => json_encode ( GoodsUnit :: getNameColumn (), JSON_UNESCAPED_UNICODE ),
'freight_lists' => json_encode ( Freight :: getNameColumn ( $shop_id ), JSON_UNESCAPED_UNICODE ),
'info' => json_encode ( GoodsLogic :: info ( $goods_id ), JSON_UNESCAPED_UNICODE )
]);
}
/**
* 违规重审
*/
public function reAudit ()
{
if ( $this -> request -> isAjax ()) {
try {
$params = $this -> request -> post ();
validate ( GoodsValidate :: class ) -> scene ( 're_audit' ) -> check ( $params );
} catch ( ValidateException $e ) {
return JsonServer :: error ( $e -> getMessage ());
}
$result = GoodsLogic :: reAudit ( $params );
if ( $result ) {
return JsonServer :: success ( '保存成功' );
}
return JsonServer :: error ( '保存失败' );
}
$goods_id = $this -> request -> get ( 'goods_id' , '' , 'intval' );
return view ( 're_audit' , [
'goods_id' => $goods_id
]);
}
/**
* 商品设置
*/
public function setInfo ()
{
if ( $this -> request -> isAjax ()) {
try {
$params = $this -> request -> post ();
validate ( GoodsValidate :: class ) -> scene ( 'set_info' ) -> check ( $params );
} catch ( ValidateException $e ) {
return JsonServer :: error ( $e -> getMessage ());
}
$result = GoodsLogic :: setInfo ( $params );
if ( $result ) {
return JsonServer :: success ( '设置成功' );
}
return JsonServer :: error ( '设置失败' );
}
$goods_id = $this -> request -> get ( 'goods_id' , '' , 'intval' );
$goods_detail = GoodsModel :: find ( $goods_id );
$goods_detail [ 'column_ids' ] = $goods_detail [ 'column_ids' ] ? explode ( ',' , $goods_detail [ 'column_ids' ]) : [];
$goods_detail [ 'column_ids' ] = json_encode ( $goods_detail [ 'column_ids' ]);
$column_list = ColumnLogic :: getList ();
return view ( 'set_info' , [
'goods_id' => $goods_id ,
'column_list' => json_encode ( $column_list ),
'goods_detail' => $goods_detail
]);
}
/**
* 审核
*/
public function audit ()
{
if ( $this -> request -> isAjax ()) {
try {
$params = $this -> request -> post ();
validate ( GoodsValidate :: class ) -> scene ( 'audit' ) -> check ( $params );
} catch ( ValidateException $e ) {
return JsonServer :: error ( $e -> getMessage ());
}
$result = GoodsLogic :: audit ( $params );
if ( $result ) {
return JsonServer :: success ( '操作完成' );
}
return JsonServer :: error ( '操作失败' );
}
$goods_id = $this -> request -> get ( 'goods_id' , '' , 'intval' );
return view ( 'audit' , [
'goods_id' => $goods_id
]);
}
public function totalCount ()
{
if ( $this -> request -> isAjax ()) {
return JsonServer :: success ( '获取成功' , GoodsLogic :: statistics ());
}
2023-10-12 03:27:45 +00:00
return false ;
2023-08-10 06:59:52 +00:00
}
2023-10-09 03:13:50 +00:00
2023-10-12 03:27:45 +00:00
public function export () : Json
2023-10-09 03:13:50 +00:00
{
set_time_limit ( 0 );
ini_set ( 'memory_limit' , '1024M' );
if ( $this -> request -> isAjax ()) {
try {
2023-10-09 09:45:56 +00:00
$ids = input ( 'ids/a' , []);
2023-10-09 03:13:50 +00:00
$shopName = input ( 'post.shop_name' , '' );
$goodsName = input ( 'post.goods_name' , '' );
$goodsType = input ( 'post.goods_type' , '' );
$platformCateId = input ( 'post.platform_cate_id' , 0 );
$writer = new \XLSXWriter ();
$fieldsArr = [
2023-10-09 09:45:56 +00:00
'ID' => 'string' ,
'商品名称' => 'string' ,
2023-10-09 09:53:18 +00:00
'商品编码' => 'string' ,
2023-10-09 09:45:56 +00:00
'商家名称' => 'string' ,
'单位' => 'string' ,
'品牌' => 'string' ,
'一级分类' => 'string' ,
'二级分类' => 'string' ,
'三级分类' => 'string' ,
'图片' => 'string' ,
'详情' => 'string' ,
'状态' => 'string' ,
'实际销量' => 'string' ,
'价格' => 'string' ,
'库存' => 'string' ,
'审核状态' => 'string' ,
'自定义参数' => 'string' ,
// '商品规格' => 'string'
2023-10-09 03:13:50 +00:00
];
$writer -> writeSheetHeader ( 'Sheet1' , $fieldsArr );
$where = [];
2023-10-09 09:45:56 +00:00
if ( ! empty ( $ids )) {
$where [] = [ 'g.id' , 'in' , $ids ];
} else {
if ( ! empty ( $shopName ) || $shopName == '0' ) {
$where [] = [ 's.name' , 'like' , '%' . $shopName . '%' ];
}
if ( ! empty ( $goodsName ) || $goodsName == '0' ) {
$where [] = [ 'g.name' , 'like' , '%' . $goodsName . '%' ];
}
if ( ! empty ( $platformCateId )) {
$where [] = [ 'g.first_cate_id|g.second_cate_id|g.third_cate_id' , '=' , $platformCateId ];
}
2023-10-09 03:13:50 +00:00
2023-10-09 09:45:56 +00:00
if ( ! empty ( $goodsType ) || $goodsType == '0' ) {
$where [] = [ 'g.type' , '=' , $goodsType ];
}
2023-10-09 03:13:50 +00:00
}
$total = GoodsModel :: alias ( 'g' )
-> leftJoin ( 'shop s' , 'g.shop_id = s.id' )
2023-10-09 09:45:56 +00:00
// ->leftJoin('goods_item gi', 'gi.goods_id = g.id') // 先注释 不关联查询sku 否则非常慢
2023-10-09 03:13:50 +00:00
-> where ( $where ) -> count ();
2023-10-09 09:53:18 +00:00
if ( $total > 100000 ) {
return JsonServer :: error ( '导出条数不能超过10W条' );
2023-10-09 03:13:50 +00:00
}
if ( $total == 0 ) {
return JsonServer :: error ( '没有符合条件的数据' );
}
2023-10-09 09:53:18 +00:00
$fieldRaw = " g.id as id, g.name as name,g.code,s.name as shop_name,gu.name as unit_name,gb.name as brand_name,
2023-10-09 09:45:56 +00:00
gc1 . name as first_cate_name , gc2 . name as second_cate_name , gc3 . name as third_cate_name ,
g . image , g . content , g . status , g . sales_actual , g . max_price , g . min_price , g . stock , g . audit_status , g . custom_params
" ;
// 先注释 不关联查询sku 否则非常慢
// $fieldRaw .= " ,GROUP_CONCAT(CONCAT('规格名称:',gi.spec_value_str,' 价格:',gi.price, ' 库存:',gi.stock) ORDER BY gi.spec_value_str SEPARATOR ';') AS sku_list";
2023-10-09 03:13:50 +00:00
$query = GoodsModel :: alias ( 'g' )
-> leftJoin ( 'shop s' , 'g.shop_id = s.id' )
2023-10-09 09:45:56 +00:00
-> leftJoin ( 'goods_unit gu' , 'g.unit_id = gu.id' )
-> leftJoin ( 'goods_brand gb' , 'g.brand_id = gb.id' )
-> leftJoin ( 'goods_category gc1' , 'g.first_cate_id = gc1.id' )
-> leftJoin ( 'goods_category gc2' , 'g.second_cate_id = gc2.id' )
-> leftJoin ( 'goods_category gc3' , 'g.third_cate_id = gc3.id' );
// ->leftJoin('goods_item gi', 'gi.goods_id = g.id');// 先注释 不关联查询sku 否则非常慢
2023-10-09 03:13:50 +00:00
$cursor = $query
-> where ( $where )
2023-10-09 09:53:18 +00:00
-> limit ( 100000 )
2023-10-09 03:13:50 +00:00
-> fieldRaw ( $fieldRaw )
-> group ( 'g.id' )
-> cursor ();
foreach ( $cursor as $k => $item ) {
$row = [];
2023-10-09 09:45:56 +00:00
$row [] = ( string ) $item [ 'id' ];
2023-10-09 03:13:50 +00:00
$row [] = $item [ 'name' ];
2023-10-09 09:53:18 +00:00
$row [] = $item [ 'code' ];
2023-10-09 09:45:56 +00:00
$row [] = $item [ 'shop_name' ];
$row [] = $item [ 'unit_name' ];
$row [] = $item [ 'brand_name' ];
$row [] = $item [ 'first_cate_name' ];
$row [] = $item [ 'second_cate_name' ];
$row [] = $item [ 'third_cate_name' ];
$row [] = $item [ 'image' ];
$row [] = $item [ 'content' ];
$status = '' ;
switch ( $item [ 'status' ])
{
case 1 :
$status = '上架中' ;
break ;
case 0 :
$status = '仓库中' ;
break ;
}
$row [] = $status ;
$row [] = $item [ 'sales_actual' ];
$price = $item [ 'min_price' ];
if ( $item [ 'max_price' ] != $item [ 'min_price' ]) {
$price = sprintf ( '%s - %s' , $item [ 'min_price' ], $item [ 'max_price' ]);
}
$row [] = $price ;
$row [] = $item [ 'stock' ];
$auditStatus = '' ;
switch ( $item [ 'audit_status' ])
{
case 1 :
$auditStatus = '审核通过' ;
break ;
case 2 :
$auditStatus = '审核失败' ;
break ;
case 0 :
$auditStatus = '待审核' ;
break ;
}
$row [] = $auditStatus ;
$row [] = $item [ 'custom_params' ];
// $row[] = $item['sku_list'];// 先注释 不关联查询sku 否则非常慢
$writer -> writeSheetRow ( 'Sheet1' , $row );
2023-10-09 03:13:50 +00:00
}
2023-10-09 09:45:56 +00:00
$src = 'uploads/export/goods_' . time () . '.xlsx' ;
$writer -> writeToFile ( $src );
// $domain = request()->domain();
return JsonServer :: success ( '资料整理完成 等待下载' , [ 'url' => '/admin/goods.Goods/download?url=' . $src ]);
2023-10-09 03:13:50 +00:00
} catch ( \Exception $e ) {
return JsonServer :: error ( $e -> getMessage ());
}
}
return JsonServer :: success ( '导出完成 本次使用内存#' . floor (( memory_get_peak_usage ()) / 1024 / 1024 ) . " MB " , input ( 'post.' ));
}
2023-10-09 09:45:56 +00:00
public function download ()
{
$url = input ( 'url' );
$fullPath = public_path () . $url ;
if ( file_exists ( $fullPath )) {
return download ( $fullPath , sprintf ( '商品导出_%s.xlsx' , date ( 'Y_m_d_H_i_s' )));
}
echo '文件不存在' ; exit ;
}
2023-10-12 03:27:45 +00:00
// 导入商品
public function import ()
{
if ( $this -> request -> isAjax ()){
set_time_limit ( 0 );
ini_set ( 'memory_limit' , '1024M' );
2023-10-13 03:49:23 +00:00
$begin = microtime ( true );
$log = [
'0010-begin' => $begin
];
2023-10-12 03:27:45 +00:00
$shopId = input ( 'shop_id' );
if ( empty ( $shopId )) {
return JsonServer :: error ( '请先选择商家' );
}
$file = request () -> file ( 'file' );
if ( ! $file ) {
return JsonServer :: error ( '文件不存在' );
}
if ( $file -> getSize () > 100 * 1024 * 1024 ) {
return JsonServer :: error ( '导入文件不要超过100M' );
}
$filePath = $file -> getPathname ();
// 导入Excel文件
$spreadsheet = IOFactory :: load ( $filePath );
$worksheet = $spreadsheet -> getActiveSheet ();
if ( $worksheet === null ) {
return JsonServer :: error ( '不是有效的excel文件' );
}
// 获取最大行数
$highestRow = $worksheet -> getHighestRow ();
// 每N条记录处理一次批量导入
$batchSize = 500 ;
$dataAll = [];
$totalCount = 0 ;
$batchCode = []; // 同批次商品编码
2023-10-13 03:49:23 +00:00
$time1 = microtime ( true );
$log [ '0020-before_foreach' ] = sprintf ( '循环前 时间:%s 总耗时:%s秒' , $time1 , $time1 - $begin );
2023-10-12 03:27:45 +00:00
// 读取数据并批量导入
for ( $row = 2 ; $row <= $highestRow ; $row ++ ) {
$data = [];
$data [ 'name' ] = ( string ) $worksheet -> getCell ( 'A' . $row ) -> getValue ();
if ( empty ( $data [ 'name' ])) {
continue ; //不存在就跳转到下一条
}
$data [ 'code' ] = ( string ) $worksheet -> getCell ( 'B' . $row ) -> getValue ();
if ( empty ( $data [ 'code' ])) {
continue ; //不存在就跳转到下一条
}
$batchCode [] = $data [ 'code' ];
$data [ 'unit' ] = ( string ) $worksheet -> getCell ( 'C' . $row ) -> getValue ();
$data [ 'brand' ] = ( string ) $worksheet -> getCell ( 'D' . $row ) -> getValue ();
$data [ 'first_cate' ] = ( string ) $worksheet -> getCell ( 'E' . $row ) -> getValue ();
$data [ 'second_cate' ] = ( string ) $worksheet -> getCell ( 'F' . $row ) -> getValue ();
$data [ 'third_cate' ] = ( string ) $worksheet -> getCell ( 'G' . $row ) -> getValue ();
$data [ 'image' ] = ( string ) $worksheet -> getCell ( 'H' . $row ) -> getValue ();
$data [ 'images' ] = ( string ) $worksheet -> getCell ( 'I' . $row ) -> getValue ();
$data [ 'content' ] = ( string ) $worksheet -> getCell ( 'J' . $row ) -> getValue ();
$data [ 'price' ] = ( string ) $worksheet -> getCell ( 'K' . $row ) -> getValue ();
$data [ 'stock' ] = ( string ) $worksheet -> getCell ( 'L' . $row ) -> getValue ();
2023-10-12 07:45:14 +00:00
$data [ 'custom_params' ] = ( string ) $worksheet -> getCell ( 'M' . $row ) -> getValue ();
2023-10-12 03:27:45 +00:00
$dataAll [ $data [ 'code' ]] = $data ;
// // 每N条记录进行一次批量插入操作, 可以调整batchSize的值
2023-10-12 07:45:14 +00:00
// Log::write(sprintf('excel row:%s total:%s', $row, $totalCount));
2023-10-12 03:27:45 +00:00
if ( $totalCount % $batchSize == 0 ) {
// 已存在的商品编码数组
2023-10-12 07:45:14 +00:00
GoodsModel :: batchInsert ( $shopId , $dataAll , $batchCode );
2023-10-13 03:49:23 +00:00
$time2 = microtime ( true );
$log [ '0030-after_batch_insert' ] = sprintf ( '批次插入 第%s条记录时 时间:%s 总耗时:%s秒' , $row , $time2 , $time2 - $begin );
2023-10-12 03:27:45 +00:00
$dataAll = []; //清空该批次
$batchCode = []; // 清空该批次商品编码
}
$totalCount ++ ; // 记录已处理的行数
}
2023-10-12 07:45:14 +00:00
// Log::write(sprintf('循环结束 row:%s total:%s', json_encode($needInsertGoods), $totalCount));
2023-10-12 03:27:45 +00:00
// 最后一批次导入
2023-10-12 07:45:14 +00:00
if ( ! empty ( $dataAll )) {
// Log::write('最后一批次导入');
GoodsModel :: batchInsert ( $shopId , $dataAll , $batchCode );
2023-10-13 03:49:23 +00:00
$time3 = microtime ( true );
$log [ '0040-last_batch_insert' ] = sprintf ( '最后一批次插入 时间:%s 总耗时:%s秒' , $time3 , $time3 - $begin );
2023-10-12 03:27:45 +00:00
}
// 以上 基础商品记录已插入
// SKU相关完善 包含SKU、规格、规格值
// 启动事务
Db :: startTrans ();
try {
2023-10-13 03:49:23 +00:00
$time40 = microtime ( true );
$log [ '0050-after_sku' ] = sprintf ( '规格前 时间:%s 耗时:%s秒' , $time40 , $time40 - $begin );
2023-10-12 03:27:45 +00:00
GoodsModel :: batchHandleSku (); //批量插入规格名
2023-10-13 03:49:23 +00:00
$time41 = microtime ( true );
$log [ '0051-after_sku' ] = sprintf ( '规格后 时间:%s 耗时:%s秒' , $time41 , $time41 - $time40 );
2023-10-12 03:27:45 +00:00
Db :: commit ();
} catch ( \Exception $e ) {
2023-10-13 03:49:23 +00:00
return JsonServer :: error ( $e -> getMessage ());
2023-10-12 03:27:45 +00:00
// 回滚事务
Db :: rollback ();
}
2023-10-13 03:49:23 +00:00
$time5 = microtime ( true );
$log [ '0060-before_relation' ] = sprintf ( '规格后关联前 时间:%s 总耗时:%s秒' , $time5 , $time5 - $begin );
2023-10-12 03:27:45 +00:00
GoodsModel :: handleImages (); //处理商品组图 插入到组图表
2023-10-13 03:49:23 +00:00
$time51 = microtime ( true );
$log [ '0061-after_image' ] = sprintf ( '处理组图 时间:%s 耗时:%s秒' , $time51 , $time51 - $time5 );
2023-10-12 03:27:45 +00:00
GoodsModel :: handleBrand (); //处理商品品牌 插入到品牌表 未更新商品表中的brand_id
2023-10-13 03:49:23 +00:00
$time52 = microtime ( true );
$log [ '0062-after_image' ] = sprintf ( '处理品牌 时间:%s 耗时:%s秒' , $time52 , $time52 - $time51 );
2023-10-12 03:27:45 +00:00
GoodsModel :: handleUnit (); //处理商品单位 插入到单位表 未更新商品表中的unit_id
2023-10-13 03:49:23 +00:00
$time53 = microtime ( true );
$log [ '0063-after_image' ] = sprintf ( '处理单位 时间:%s 耗时:%s秒' , $time53 , $time53 - $time52 );
2023-10-12 03:27:45 +00:00
GoodsModel :: handleCategory (); //处理商品分类 插入到分类表 未更新商品表中的分类ID
2023-10-13 03:49:23 +00:00
$time6 = microtime ( true );
$log [ '0070-after_relation' ] = sprintf ( '处理分类 时间:%s 总耗时:%s秒' , $time6 , $time6 - $time53 );
2023-10-12 03:27:45 +00:00
GoodsModel :: handleSelfInfo (); //处理商品自身数据的更新 更新分类ID、品牌ID、单位ID
2023-10-13 03:49:23 +00:00
$done = microtime ( true );
$log [ '0080-done' ] = sprintf ( '处理自身关联 时间:%s 耗时:%s秒' , $done , $done - $time6 );
$log [ '0090-done' ] = sprintf ( '完成 时间:%s 总耗时:%s秒' , $done , $done - $begin );
2023-10-12 03:27:45 +00:00
2023-10-13 03:49:23 +00:00
return JsonServer :: success ( '导入成功' , $log );
2023-10-12 03:27:45 +00:00
}
$shopList = Shop :: where ( 'del' , 0 ) -> field ( 'id,name' ) -> select ();
return view ( '' , [
'shop_list' => json_encode ( $shopList )
]);
}
2023-10-12 09:04:06 +00:00
public function importTemplate ()
{
$fullPath = public_path () . 'static/admin/images/import_demo.xlsx' ;
if ( file_exists ( $fullPath )) {
return download ( $fullPath , '商品导入模版.xlsx' );
}
echo '文件不存在' ; exit ;
}
2023-08-10 06:59:52 +00:00
}