feat: 添加产品购买链接相关接口

This commit is contained in:
2025-02-18 18:09:11 +08:00
parent 3581687a1d
commit 5655fd978b
5 changed files with 452 additions and 0 deletions

View File

@@ -0,0 +1,339 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ProductModel;
use app\admin\model\v1\ProductPurchaseLinkModel;
use app\admin\model\v1\ProductPurchaseLinkPlatformModel;
use app\admin\validate\v1\ProductPurchaseLinkValidate;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use think\facade\Db;
class ProductPurchaseLink
{
/**
* 获取购买平台
*/
public function platforms()
{
$platforms = ProductPurchaseLinkPlatformModel::withoutField([
'language_id',
'desc',
'sort',
'created_at'
])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', $platforms);
}
/**
* 购买链接分布数据
*/
public function index()
{
$params = request()->param([
'name' => '',
'spu' => '',
'page/d' => 1,
'size/d' => 10
]);
$links = ProductModel::alias('pd')
->field([
'pd.id',
'pd.spu',
'pd.name',
'CASE WHEN pd.is_show = 0 THEN "已下架" WHEN pd.is_show = 1 THEN "已上架" END' => 'is_show',
'pf.id' => 'platform_id',
'pf.platform' => 'platform_name',
'min(pl.id)' => 'link_id',
'pl.link'
])
->leftJoin('product_purchase_link pl', 'pl.product_id = pd.id')
->leftJoin('product_purchase_platform pf', 'pf.id = pl.platform_id')
->where(function ($query) use ($params) {
$query->where('pd.is_show', '=', 1);
if (!empty($params['name'])) {
$query->where('pd.name', 'like', '%' . $params['name'] . '%');
}
if (!empty($params['spu'])) {
$query->where('pd.spu', 'like', '%' . $params['spu'] . '%');
}
})
->where('pd.language_id', '=', request()->lang_id)
->group('pd.id')
->order(['pl.sort' => 'asc', 'pl.id' => 'desc', 'pd.id' => 'desc'])
->paginate([
'list_rows' => $params['size'],
'page' => $params['page'],
]);
if ($links->isEmpty()) {
return success('获取成功', []);
}
$others = ProductPurchaseLinkModel::alias('pl')
->field([
'pl.product_id',
'pl.platform_id',
'pf.platform' => 'platform_name',
'pl.id' => 'link_id',
'pl.link'
])
->join('product_purchase_platform pf', 'pf.id = pl.platform_id')
->where('pl.language_id', '=', request()->lang_id)
->where('pl.product_id', 'in', array_column($links->items(), 'id'))
->where('pl.id', 'not in', array_column($links->items(), 'link_id'))
->select();
if (!$others->isEmpty()) {
$others_map = [];
$others_arr = $others->toArray();
foreach ($others_arr as $other) {
$product_id = $other['product_id'];
unset($other['product_id']);
$others_map[$product_id][] = $other;
}
$links->each(function ($item) use ($others_map) {
if (!empty($others_map[$item['id']])) {
$item['rowspan'] = $others_map[$item['id']];
}
return $item;
});
}
return success('获取成功', $links);
}
/**
* 导入
*/
public function import()
{
$file = request()->file('file');
if ($file->getSize() > 20 * 1024 * 1024) {
return error('上传文件不能超过20M');
}
$reader = IOFactory::createReader('Xlsx');
// 读取文件
$spreadsheet = $reader->load($file->getRealPath());
// 获取活动sheet
$sheet = $spreadsheet->getActiveSheet();
// 获取行数
$rows = $sheet->getHighestRow();
// 从表格获取数据
$xlsx_data = [];
for ($i=2; $i <= $rows; $i++) {
$xlsx_data[$i] = [
'spu' => $sheet->getCell('A' . $i)->getValue(),
'platform' => $sheet->getCell('B' . $i)->getValue(),
'link' => $sheet->getCell('C' . $i)->getValue(),
];
}
$platforms_name = array_unique(array_column($xlsx_data, 'platform'));
$platforms_map = ProductPurchaseLinkPlatformModel::language(request()->lang_id)
->where('platform', 'in', $platforms_name)
->column('id', 'platform');
$data = [];
$errors = [];
$chunks = array_chunk($xlsx_data, 500, true);
// 分组验证每行,并组装数据
foreach ($chunks as $chunk) {
$spus = array_unique(array_column($chunk, 'spu'));
$products_map = ProductModel::language(request()->lang_id)
->where('spu', 'in', $spus)
->column('id', 'spu');
$items = [];
foreach ($chunk as $r => $it) {
if (empty($platforms_map[$it['platform']])) {
$errors[] = sprintf("第%d行平台名称错误", $r);
continue;
}
if (empty($products_map[$it['spu']])) {
$errors[] = sprintf("第%d行型号不存在", $r);
continue;
}
$items[] = [
'language_id' => request()->lang_id,
'product_id' => $products_map[$it['spu']],
'platform_id' => $platforms_map[$it['platform']],
'link' => $it['link'],
'sort' => 0,
];
}
if (!empty($items)) {
$data[] = $items;
}
}
// 组装sql并执行
if (!empty($data)) {
$link_model = new ProductPurchaseLinkModel();
$sql = sprintf(
'REPLACE INTO %s (id, language_id, product_id, platform_id, link, sort) VALUES ',
$link_model->getTable(),
);
foreach ($data as $items) {
$products_id = array_unique(array_column($items, 'product_id'));
$links = $link_model->field([
'id',
'product_id',
'platform_id'
])
->where('product_id', 'in', $products_id)
->select();
$links_map = [];
if (!$links->isEmpty()) {
foreach ($links as $link) {
$links_map[$link['product_id'] . '_' . $link['platform_id']] = $link['id'];
}
}
foreach ($items as $item) {
$item['id'] = null;
if (!empty($links_map[$item['product_id'] . '_' . $item['platform_id']])) {
$item['id'] = $links_map[$item['product_id'] . '_' . $item['platform_id']];
}
$sql .= sprintf(
'(%d, %d, %d, %d, "%s", %d),',
$item['id'],
$item['language_id'],
$item['product_id'],
$item['platform_id'],
$item['link'],
$item['sort']
);
}
}
Db::execute(rtrim($sql, ','));
}
if (!empty($errors)) {
return error(implode(";\n", $errors));
}
return success('导入成功');
}
/**
* 导出
*/
public function export()
{
$schema = [
'spu' => '型号',
'platform' => '平台',
'link' => '链接'
];
// 获取导出数据
$data = $this->getExportLinkData();
// 获取Spreadsheet对象
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写入表头
$title = array_values($schema);
$title_col = 'A';
foreach ($title as $value) {
// 单元格内容写入
$sheet->setCellValue($title_col . '1', $value);
$title_col++;
}
// 写入数据
$row = 2;
$keys = array_keys($schema);
foreach ($data as $item) {
$data_col = 'A';
foreach ($keys as $key) {
$sheet->setCellValue($data_col . $row, $item[$key] ?? '');
$data_col++;
}
$row++;
}
flush();
ob_flush();
$filename = date('YmdHms');
header('Access-Control-Expose-Headers: Content-Disposition');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; Charset=UTF-8');
header('Content-Disposition: attachment;filename=' . $filename . '.xlsx');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output');
}
private function getExportLinkData()
{
$params = request()->param([
'name' => '',
'spu' => ''
]);
return ProductModel::alias('pd')
->field([
'pd.spu',
'pf.platform',
'pl.link'
])
->leftJoin('product_purchase_link pl', 'pl.product_id = pd.id')
->leftJoin('product_purchase_platform pf', 'pf.id = pl.platform_id')
->where(function ($query) use ($params) {
$query->where('pd.is_show', '=', 1);
if (!empty($params['name'])) {
$query->where('pd.name', 'like', '%' . $params['name'] . '%');
}
if (!empty($params['spu'])) {
$query->where('pd.spu', 'like', '%' . $params['spu'] . '%');
}
})
->where('pd.language_id', '=', request()->lang_id)
->order(['pl.sort' => 'asc', 'pl.id' => 'desc', 'pd.id' => 'desc'])
->select();
}
/**
* 更新购买链接
*/
public function update()
{
$id = request()->param('id');
$put = request()->put([
'link',
'platform_id'
]);
$data = [
'id' => $id,
'link' => $put['link'],
'platform_id' => $put['platform_id']
];
$validate = new ProductPurchaseLinkValidate;
if (!$validate->scene('update')->check($data)) {
return error($validate->getError());
}
$link = ProductPurchaseLinkModel::bypk($data['id'])->find();
if (empty($link)) {
return error('请确认操作对象是否存在');
}
if (!$link->save($data)) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductPurchaseLinkBaseModel;
/**
* 产品 - 购买链接模型
* @mixin \think\Model
*/
class ProductPurchaseLinkModel extends ProductPurchaseLinkBaseModel
{
// 关联产品
public function product()
{
return $this->belongsTo(ProductModel::class, 'product_id', 'id');
}
// 关联平台
public function platform()
{
return $this->belongsTo(ProductPurchaseLinkPlatformModel::class, 'platform_id', 'id');
}
// 根据语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductPurchasePlatformBaseModel;
/**
* 产品 - 采购链接平台模型
* @mixin \think\Model
*/
class ProductPurchaseLinkPlatformModel extends ProductPurchasePlatformBaseModel
{
// 根据语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
}

View File

@@ -184,6 +184,24 @@ Route::group('v1', function () {
Route::delete('delete/:id', 'ProductCategory/delete');
});
// 产品购买链接
Route::group('buypass', function () {
// 购买链接平台列表
Route::get('platforms', 'ProductPurchaseLink/platforms');
// 购买链接列表
Route::get('index', 'ProductPurchaseLink/index');
// 购买链接导入
Route::post('import', 'ProductPurchaseLink/import');
// 购买链接导出
Route::get('export', 'ProductPurchaseLink/export');
// 购买链接更新
Route::put('update/:id', 'ProductPurchaseLink/update');
});
// 产品回收站
Route::group('trash', function () {
// 产品回收站列表

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace app\admin\validate\v1;
use think\Validate;
class ProductPurchaseLinkValidate extends Validate
{
/**
* 定义验证规则
* 格式:'字段名' => ['规则1','规则2'...]
*
* @var array
*/
protected $rule = [
'id' => 'require|integer',
'link' => 'url|max:255',
'platform_id' => 'integer'
];
/**
* 定义错误信息
* 格式:'字段名.规则名' => '错误信息'
*
* @var array
*/
protected $message = [
'id.require' => 'id不能为空',
'id.integer' => 'id字段类型错误',
'link.url' => '链接格式不正确',
'link.max' => '链接不能超过255个字符',
'platform_id.integer' => '平台id类型错误'
];
/**
* 更新场景
*/
public function sceneUpdate()
{
return $this->only(['id', 'link', 'platform_id'])->remove('id', 'require');
}
}