mirror of
https://gitee.com/ShopeX/ECShopX
synced 2026-08-10 21:31:57 +08:00
4.6.2
This commit is contained in:
@@ -28,6 +28,11 @@ use SystemLinkBundle\Services\ThirdSettingService;
|
||||
use CompanysBundle\Services\ShopsService;
|
||||
use CompanysBundle\Services\Shops\WxShopsService;
|
||||
use DistributionBundle\Services\DistributorService;
|
||||
use PromotionsBundle\Services\SmsDriver\ShopexSmsClient;
|
||||
use ThirdPartyBundle\Services\SaasCertCentre\CertClient;
|
||||
use ThirdPartyBundle\Services\SaasCertCentre\CertService;
|
||||
use ThirdPartyBundle\Services\AppsCenter\AppsCenterService;
|
||||
use CompanysBundle\Ego\PrismEgo;
|
||||
|
||||
class Companys extends BaseController
|
||||
{
|
||||
@@ -635,5 +640,108 @@ class Companys extends BaseController
|
||||
return $this->response->array($result);
|
||||
}
|
||||
|
||||
|
||||
public function loginUsercenter(Request $request)
|
||||
{
|
||||
$companyId = app('auth')->user()->get('company_id');
|
||||
|
||||
$code = $request->get('code');
|
||||
if (!$code) {
|
||||
throw new ResourceException('缺少授权code');
|
||||
}
|
||||
|
||||
$prismEgo = new PrismEgo();
|
||||
$res = $prismEgo->getToken($code);
|
||||
app('log')->debug($res);
|
||||
$res['access_token'] = $res['access_token'] ?? '';
|
||||
$res['data'] = $res['data'] ?? [];
|
||||
|
||||
if (!$res['access_token'] || !$res['data']) {
|
||||
throw new ResourceException('登录失败');
|
||||
}
|
||||
|
||||
//保存 access_token 和 refresh_token
|
||||
$prismData = $res['data'];
|
||||
$accessToken = $res['access_token'];
|
||||
$expiresIn = $res['expires_in'];
|
||||
$refreshToken = $res['refresh_token'];
|
||||
$refreshExpires = $res['refresh_expires'];
|
||||
$shopexSmsClient = new ShopexSmsClient($companyId, $prismData['passport_uid']);
|
||||
$shopexSmsClient->setAccessToken($accessToken, $expiresIn);
|
||||
$shopexSmsClient->setRefreshToken($refreshToken, $refreshExpires);
|
||||
|
||||
$this->companysService->updateInfo(['company_id' => $companyId], [
|
||||
'passport_uid' => $prismData['passport_uid'],
|
||||
'eid' => $prismData['eid'],
|
||||
]);
|
||||
|
||||
// 获取shopex证书
|
||||
$certService = new CertService(new CertClient($companyId, $prismData['passport_uid']));
|
||||
$certService->getAouthCert();
|
||||
|
||||
return $this->response->array(['status' => true]);
|
||||
}
|
||||
|
||||
public function getUsercenterOuthorizeurl()
|
||||
{
|
||||
$callback = rtrim(config('common.shop_admin_url'), '/') . '/setting/ShangPai/usercenter';
|
||||
$data = array(
|
||||
'response_type' => 'code',
|
||||
'client_id' => config('common.prism_key'),
|
||||
'redirect_uri' => $callback,
|
||||
'view' => 'oauth_usercenter'
|
||||
);
|
||||
$query = http_build_query($data);
|
||||
$shopexUrl = config('common.openapi_shopex_url');
|
||||
$url = "{$shopexUrl}/oauth/authorize?{$query}";
|
||||
return $this->response->array(['url' => $url]);
|
||||
}
|
||||
|
||||
public function getAppcenterGoods()
|
||||
{
|
||||
$appsCenterService = new AppsCenterService();
|
||||
$data = $appsCenterService->fetchEmbedGoods();
|
||||
$data['open_base_url'] = rtrim(config('common.appcenter_base_url'), '/');
|
||||
|
||||
return $this->response->array($data);
|
||||
}
|
||||
|
||||
public function getAppcenterUrl()
|
||||
{
|
||||
$companyId = app('auth')->user()->get('company_id');
|
||||
$shopexUid = $this->companysService->getPassportUidByCompanyId($companyId);
|
||||
if (!$shopexUid) {
|
||||
throw new ResourceException('缺少 Shopex ID,请先完成用户中心登录');
|
||||
}
|
||||
|
||||
$certService = new CertService(false, $companyId, $shopexUid);
|
||||
$certSetting = $certService->getCertSetting();
|
||||
$nodeId = trim((string) ($certSetting['node_id'] ?? ''));
|
||||
$token = trim((string) ($certSetting['token'] ?? ''));
|
||||
if (!$nodeId) {
|
||||
throw new ResourceException('缺少节点号,请先完成证书绑定');
|
||||
}
|
||||
if (!$token) {
|
||||
throw new ResourceException('缺少证书 token,请先完成证书绑定');
|
||||
}
|
||||
|
||||
$appsCenterService = new AppsCenterService();
|
||||
$callback = $appsCenterService->buildAppcenterUrl([
|
||||
'channel' => config('common.appcenter_channel'),
|
||||
'shopexid' => $shopexUid,
|
||||
'sys_node_id' => $nodeId,
|
||||
'callback' => rtrim(config('common.shop_admin_url'), '/'),
|
||||
], $token);
|
||||
|
||||
$data = array(
|
||||
'response_type' => 'code',
|
||||
'client_id' => config('common.prism_key'),
|
||||
'redirect_uri' => $callback,
|
||||
'view' => 'oauth_usercenter'
|
||||
);
|
||||
$query = http_build_query($data);
|
||||
$shopexUrl = config('common.openapi_shopex_url');
|
||||
$url = "{$shopexUrl}/oauth/authorize?{$query}";
|
||||
|
||||
return $this->response->array(['url' => $url]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,10 +253,12 @@ class PickupLocation extends Controller
|
||||
unset($filter['distributor_id']);
|
||||
}
|
||||
|
||||
// if (isset($params['rel_distributor_id']) && $params['rel_distributor_id']) {
|
||||
// $filter['rel_distributor_id'] = $params['rel_distributor_id'];
|
||||
// unset($filter['distributor_id']); //总部
|
||||
// }
|
||||
if (isset($params['rel_distributor_id']) && $params['rel_distributor_id']) {
|
||||
$filter['rel_distributor_id'] = $params['rel_distributor_id'];
|
||||
if ($operatorType != 'distributor') {
|
||||
unset($filter['distributor_id']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['name']) && $params['name']) {
|
||||
$filter['name|contains'] = $params['name'];
|
||||
@@ -436,17 +438,29 @@ class PickupLocation extends Controller
|
||||
throw new ResourceException($error);
|
||||
}
|
||||
|
||||
$filter['company_id'] = app('auth')->user()->get('company_id');
|
||||
$companyId = app('auth')->user()->get('company_id');
|
||||
$operatorType = app('auth')->user()->get('operator_type');
|
||||
$filter['distributor_id'] = 0;
|
||||
$distributorId = 0;
|
||||
if ($operatorType == 'distributor') { //店铺端
|
||||
$filter['distributor_id'] = $request->get('distributor_id');
|
||||
$distributorId = $request->get('distributor_id');
|
||||
}
|
||||
$filter['id'] = $params['id'];
|
||||
$filter['rel_distributor_id'] = $params['rel_distributor_id'];
|
||||
|
||||
$pickupLocationService = new PickupLocationService();
|
||||
$pickupLocationService->updateBy($filter, ['rel_distributor_id' => 0]);
|
||||
$ids = is_array($params['id']) ? $params['id'] : [$params['id']];
|
||||
foreach ($ids as $id) {
|
||||
$filter = [
|
||||
'company_id' => $companyId,
|
||||
'id' => $id,
|
||||
'rel_distributor_id' => $params['rel_distributor_id'],
|
||||
];
|
||||
if ($operatorType == 'distributor') {
|
||||
$filter['distributor_id'] = $distributorId;
|
||||
}
|
||||
$affected = $pickupLocationService->updateBy($filter, ['rel_distributor_id' => 0]);
|
||||
if (!$affected) {
|
||||
throw new ResourceException(trans('DistributionBundle/Services/PickupLocationService.pickup_location_not_exist'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->response->array(['status' => true]);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ use LaravelDoctrine\Extensions\SoftDeletes\SoftDeletes;
|
||||
* @ORM\Table(name="theme_pc_template", options={"comment":"pc页面装修"},
|
||||
* indexes={
|
||||
* @ORM\Index(name="idx_company_id", columns={"company_id"}),
|
||||
* @ORM\Index(name="idx_company_distributor", columns={"company_id", "distributor_id"}),
|
||||
* },)
|
||||
* @ORM\Entity(repositoryClass="ThemeBundle\Repositories\ThemePcTemplateRepository")
|
||||
*/
|
||||
@@ -50,6 +51,13 @@ class ThemePcTemplate
|
||||
*/
|
||||
private $company_id;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @ORM\Column(name="distributor_id", type="integer", options={"comment":"店铺ID", "default":0})
|
||||
*/
|
||||
private $distributor_id = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
@@ -136,6 +144,30 @@ class ThemePcTemplate
|
||||
return $this->company_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set distributorId.
|
||||
*
|
||||
* @param int $distributorId
|
||||
*
|
||||
* @return ThemePcTemplate
|
||||
*/
|
||||
public function setDistributorId($distributorId = 0)
|
||||
{
|
||||
$this->distributor_id = (int)$distributorId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get distributorId.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDistributorId()
|
||||
{
|
||||
return $this->distributor_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set templateTitle.
|
||||
*
|
||||
|
||||
@@ -57,7 +57,7 @@ class ThemePcTemplateContent
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="name", type="string", length=20, options={"comment":"配置名称"})
|
||||
* @ORM\Column(name="name", type="string", length=64, options={"comment":"配置名称"})
|
||||
*/
|
||||
private $name;
|
||||
|
||||
|
||||
127
src/ThemeBundle/Entities/WebMenu.php
Normal file
127
src/ThemeBundle/Entities/WebMenu.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Entities;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Web 端商城 — 菜单主表
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="ThemeBundle\Repositories\WebMenuRepository")
|
||||
* @ORM\Table(
|
||||
* name="web_menus",
|
||||
* options={"comment":"Web端商城-菜单主表"},
|
||||
* uniqueConstraints={
|
||||
* @ORM\UniqueConstraint(name="uk_company_key", columns={"company_id", "key"})
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class WebMenu
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer", options={"unsigned":true})
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="company_id", type="integer", options={"unsigned":true})
|
||||
*/
|
||||
private $companyId;
|
||||
|
||||
/** @ORM\Column(type="string", length=100) */
|
||||
private $name;
|
||||
|
||||
/** @ORM\Column(name="`key`", type="string", length=100) */
|
||||
private $key;
|
||||
|
||||
/** @ORM\Column(type="smallint", options={"default":1}) */
|
||||
private $status = 1;
|
||||
|
||||
/** @ORM\Column(name="created_at", type="datetime") */
|
||||
private $createdAt;
|
||||
|
||||
/** @ORM\Column(name="updated_at", type="datetime") */
|
||||
private $updatedAt;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setCompanyId(int $companyId): self
|
||||
{
|
||||
$this->companyId = $companyId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCompanyId(): int
|
||||
{
|
||||
return (int) $this->companyId;
|
||||
}
|
||||
|
||||
public function setName(string $name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setKey(string $key): self
|
||||
{
|
||||
$this->key = $key;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setStatus(int $status): self
|
||||
{
|
||||
$this->status = $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus(): int
|
||||
{
|
||||
return (int) $this->status;
|
||||
}
|
||||
|
||||
public function setCreatedAt(\DateTimeInterface $createdAt): self
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
|
||||
{
|
||||
$this->updatedAt = $updatedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
}
|
||||
217
src/ThemeBundle/Entities/WebMenuItem.php
Normal file
217
src/ThemeBundle/Entities/WebMenuItem.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Entities;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Web 端商城 — 菜单项表
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="ThemeBundle\Repositories\WebMenuItemRepository")
|
||||
* @ORM\Table(
|
||||
* name="web_menu_items",
|
||||
* options={"comment":"Web端商城-菜单项表"},
|
||||
* indexes={
|
||||
* @ORM\Index(name="idx_menu_id", columns={"menu_id"}),
|
||||
* @ORM\Index(name="idx_company_id", columns={"company_id"}),
|
||||
* @ORM\Index(name="idx_parent_id", columns={"parent_id"})
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class WebMenuItem
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer", options={"unsigned":true})
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/** @ORM\Column(name="menu_id", type="integer", options={"unsigned":true}) */
|
||||
private $menuId;
|
||||
|
||||
/** @ORM\Column(name="company_id", type="integer", options={"unsigned":true}) */
|
||||
private $companyId;
|
||||
|
||||
/** @ORM\Column(name="parent_id", type="integer", options={"unsigned":true, "default":0}) */
|
||||
private $parentId = 0;
|
||||
|
||||
/** @ORM\Column(type="string", length=100) */
|
||||
private $name;
|
||||
|
||||
/** @ORM\Column(name="image_url", type="string", length=500, nullable=true) */
|
||||
private $imageUrl;
|
||||
|
||||
/** @ORM\Column(name="link_type", type="string", length=50, options={"default":"url"}) */
|
||||
private $linkType = 'url';
|
||||
|
||||
/** @ORM\Column(name="link_value", type="string", length=500, nullable=true) */
|
||||
private $linkValue;
|
||||
|
||||
/** @ORM\Column(name="link_extra", type="text", nullable=true) */
|
||||
private $linkExtra;
|
||||
|
||||
/** @ORM\Column(type="integer", options={"default":0}) */
|
||||
private $sort = 0;
|
||||
|
||||
/** @ORM\Column(type="smallint", options={"default":1}) */
|
||||
private $status = 1;
|
||||
|
||||
/** @ORM\Column(name="created_at", type="datetime") */
|
||||
private $createdAt;
|
||||
|
||||
/** @ORM\Column(name="updated_at", type="datetime") */
|
||||
private $updatedAt;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setMenuId(int $menuId): self
|
||||
{
|
||||
$this->menuId = $menuId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMenuId(): int
|
||||
{
|
||||
return (int) $this->menuId;
|
||||
}
|
||||
|
||||
public function setCompanyId(int $companyId): self
|
||||
{
|
||||
$this->companyId = $companyId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCompanyId(): int
|
||||
{
|
||||
return (int) $this->companyId;
|
||||
}
|
||||
|
||||
public function setParentId(int $parentId): self
|
||||
{
|
||||
$this->parentId = $parentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParentId(): int
|
||||
{
|
||||
return (int) $this->parentId;
|
||||
}
|
||||
|
||||
public function setName(string $name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setImageUrl(?string $imageUrl): self
|
||||
{
|
||||
$this->imageUrl = $imageUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImageUrl(): ?string
|
||||
{
|
||||
return $this->imageUrl;
|
||||
}
|
||||
|
||||
public function setLinkType(string $linkType): self
|
||||
{
|
||||
$this->linkType = $linkType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLinkType(): string
|
||||
{
|
||||
return $this->linkType;
|
||||
}
|
||||
|
||||
public function setLinkValue(?string $linkValue): self
|
||||
{
|
||||
$this->linkValue = $linkValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLinkValue(): ?string
|
||||
{
|
||||
return $this->linkValue;
|
||||
}
|
||||
|
||||
public function setLinkExtra(?string $linkExtra): self
|
||||
{
|
||||
$this->linkExtra = $linkExtra;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLinkExtra(): ?string
|
||||
{
|
||||
return $this->linkExtra;
|
||||
}
|
||||
|
||||
public function setSort(int $sort): self
|
||||
{
|
||||
$this->sort = $sort;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSort(): int
|
||||
{
|
||||
return (int) $this->sort;
|
||||
}
|
||||
|
||||
public function setStatus(int $status): self
|
||||
{
|
||||
$this->status = $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus(): int
|
||||
{
|
||||
return (int) $this->status;
|
||||
}
|
||||
|
||||
public function setCreatedAt(\DateTimeInterface $createdAt): self
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
|
||||
{
|
||||
$this->updatedAt = $updatedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedAt(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -108,8 +108,8 @@ class OpenScreenAd extends Controller
|
||||
'show_time' => ['required|in:always,first', '请选择曝光时间'],
|
||||
'waiting_time' => ['required', '请设置秒数'],
|
||||
// 'app' => ['required', '请选择应用端'],
|
||||
'start_time' => ['required', '请选择展示开始时间'],
|
||||
'end_time' => ['required', '请选择展示结束时间'],
|
||||
// 'start_time' => ['required', '请选择展示开始时间'],
|
||||
// 'end_time' => ['required', '请选择展示结束时间'],
|
||||
];
|
||||
$error = validator_params($params, $rules);
|
||||
if ($error) {
|
||||
|
||||
@@ -47,6 +47,13 @@ class PcTemplate extends Controller
|
||||
* type="string",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="distributor_id",
|
||||
* in="query",
|
||||
* description="店铺ID,不传默认0",
|
||||
* required=false,
|
||||
* type="integer",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="page_no",
|
||||
* in="query",
|
||||
* description="页号",
|
||||
@@ -76,6 +83,7 @@ class PcTemplate extends Controller
|
||||
* @SWG\Items(
|
||||
* type="object",
|
||||
* @SWG\Property(property="company_id", type="int"),
|
||||
* @SWG\Property(property="distributor_id", type="int"),
|
||||
* @SWG\Property(property="created", type="string"),
|
||||
* @SWG\Property(property="deleted_at", type="string"),
|
||||
* @SWG\Property(property="page_type", type="string"),
|
||||
@@ -102,6 +110,7 @@ class PcTemplate extends Controller
|
||||
$page_no = $request->input('page_no', 1);
|
||||
$page_size = $request->input('page_size', 20);
|
||||
$status = $request->input('status');
|
||||
$distributor_id = $request->input('distributor_id');
|
||||
|
||||
$params = [
|
||||
'company_id' => $company_id,
|
||||
@@ -110,6 +119,9 @@ class PcTemplate extends Controller
|
||||
'page_size' => $page_size,
|
||||
'status' => $status,
|
||||
];
|
||||
if ($distributor_id !== null) {
|
||||
$params['distributor_id'] = (int)$distributor_id;
|
||||
}
|
||||
|
||||
$theme_pc_template_services = new ThemePcTemplateServices();
|
||||
$result = $theme_pc_template_services->lists($params);
|
||||
@@ -160,6 +172,13 @@ class PcTemplate extends Controller
|
||||
* type="string",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="distributor_id",
|
||||
* in="formData",
|
||||
* description="店铺ID,不传默认0",
|
||||
* required=false,
|
||||
* type="integer",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="status",
|
||||
* in="formData",
|
||||
* description="是否启用",
|
||||
@@ -176,6 +195,7 @@ class PcTemplate extends Controller
|
||||
* @SWG\Items(
|
||||
* type="object",
|
||||
* @SWG\Property(property="company_id", type="int"),
|
||||
* @SWG\Property(property="distributor_id", type="int"),
|
||||
* @SWG\Property(property="created", type="string"),
|
||||
* @SWG\Property(property="deleted_at", type="string"),
|
||||
* @SWG\Property(property="page_type", type="string"),
|
||||
@@ -201,9 +221,11 @@ class PcTemplate extends Controller
|
||||
$page_type = $request->input('page_type');
|
||||
$version = $request->input('version', 'v1.0.1');
|
||||
$status = $request->input('status', 2);
|
||||
$distributor_id = $request->input('distributor_id', 0);
|
||||
|
||||
$params = [
|
||||
'company_id' => $company_id,
|
||||
'distributor_id' => (int)$distributor_id,
|
||||
'template_title' => $template_title,
|
||||
'template_description' => $template_description,
|
||||
'page_type' => $page_type,
|
||||
@@ -213,7 +235,7 @@ class PcTemplate extends Controller
|
||||
$rules = [
|
||||
'template_title' => ['required', '缺少页面名称'],
|
||||
'template_description' => ['required', '缺少页面描述'],
|
||||
'page_type' => ['required|in:index,custom', '缺少页面类型'],
|
||||
'page_type' => ['required|in:index,custom,product_list', '缺少页面类型'],
|
||||
'version' => ['required', '缺少版本号']
|
||||
];
|
||||
$error = validator_params($params, $rules);
|
||||
@@ -277,6 +299,13 @@ class PcTemplate extends Controller
|
||||
* type="string",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="distributor_id",
|
||||
* in="formData",
|
||||
* description="店铺ID,不传不更新",
|
||||
* required=false,
|
||||
* type="integer",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="status",
|
||||
* in="formData",
|
||||
* description="是否启用",
|
||||
@@ -293,6 +322,7 @@ class PcTemplate extends Controller
|
||||
* @SWG\Items(
|
||||
* type="object",
|
||||
* @SWG\Property(property="company_id", type="int"),
|
||||
* @SWG\Property(property="distributor_id", type="int"),
|
||||
* @SWG\Property(property="created", type="string"),
|
||||
* @SWG\Property(property="deleted_at", type="string"),
|
||||
* @SWG\Property(property="page_type", type="string"),
|
||||
@@ -317,6 +347,7 @@ class PcTemplate extends Controller
|
||||
$template_description = $request->input('template_description');
|
||||
$page_type = $request->input('page_type');
|
||||
$status = $request->input('status');
|
||||
$distributor_id = $request->input('distributor_id');
|
||||
|
||||
$params = [
|
||||
'company_id' => $company_id,
|
||||
@@ -326,6 +357,9 @@ class PcTemplate extends Controller
|
||||
'page_type' => $page_type,
|
||||
'status' => $status
|
||||
];
|
||||
if ($distributor_id !== null) {
|
||||
$params['distributor_id'] = (int)$distributor_id;
|
||||
}
|
||||
$rules = [
|
||||
'theme_pc_template_id' => ['required', '缺少theme_pc_template_id'],
|
||||
];
|
||||
@@ -581,6 +615,30 @@ class PcTemplate extends Controller
|
||||
return $this->response->array($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 PC 模板装修内容
|
||||
*/
|
||||
public function getDecorationContent(Request $request)
|
||||
{
|
||||
$company_id = app('auth')->user()->get('company_id');
|
||||
$page_name = $request->input('page_name', 'page');
|
||||
$theme_pc_template_id = $request->input('theme_pc_template_id');
|
||||
$page_type = $request->input('page_type');
|
||||
$distributor_id = (int)$request->input('distributor_id', 0);
|
||||
|
||||
$params = [
|
||||
'company_id' => $company_id,
|
||||
'page_name' => $page_name,
|
||||
'theme_pc_template_id' => $theme_pc_template_id,
|
||||
'page_type' => $page_type,
|
||||
'distributor_id' => $distributor_id,
|
||||
];
|
||||
$service = new ThemePcTemplateContentServices();
|
||||
$result = $service->decorationContent($params);
|
||||
|
||||
return $this->response->array($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Post(
|
||||
* path="/pctemplate/saveTemplateContent",
|
||||
@@ -651,9 +709,15 @@ class PcTemplate extends Controller
|
||||
$result = app('redis')->connection('companys')->get('pc_login_page:'.$companyId);
|
||||
if (!$result) {
|
||||
$result['logo'] = '';
|
||||
$result['logo_light'] = '';
|
||||
$result['logo_dark'] = '';
|
||||
$result['background'] = '';
|
||||
} else {
|
||||
$result = json_decode($result, true);
|
||||
$result['logo_light'] = $result['logo_light'] ?? ($result['logo'] ?? '');
|
||||
$result['logo_dark'] = $result['logo_dark'] ?? ($result['logo'] ?? '');
|
||||
$result['logo'] = $result['logo'] ?? ($result['logo_light'] ?? '');
|
||||
$result['background'] = $result['background'] ?? '';
|
||||
}
|
||||
return $this->response->array($result);
|
||||
}
|
||||
@@ -662,9 +726,13 @@ class PcTemplate extends Controller
|
||||
{
|
||||
$companyId = app('auth')->user()->get('company_id');
|
||||
// 整理参数
|
||||
$logoLight = $request->input('logo_light', $request->input('logo', ''));
|
||||
$logoDark = $request->input('logo_dark', $request->input('logo', ''));
|
||||
$params = [
|
||||
'logo' => $request->input('logo'),
|
||||
'background' => $request->input('background'),
|
||||
'logo' => $request->input('logo', $logoLight),
|
||||
'logo_light' => $logoLight,
|
||||
'logo_dark' => $logoDark,
|
||||
'background' => $request->input('background', ''),
|
||||
];
|
||||
app('redis')->connection('companys')->set('pc_login_page:'.$companyId, json_encode($params));
|
||||
return $this->response->array(['status' => true]);
|
||||
|
||||
106
src/ThemeBundle/Http/Api/V1/Action/WebMenuAction.php
Normal file
106
src/ThemeBundle/Http/Api/V1/Action/WebMenuAction.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Http\Api\V1\Action;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use ThemeBundle\Entities\WebMenu;
|
||||
use ThemeBundle\Entities\WebMenuItem;
|
||||
use ThemeBundle\Services\WebMenuService;
|
||||
use ThemeBundle\Transformers\WebMenuItemTransformer;
|
||||
use ThemeBundle\Transformers\WebMenuTransformer;
|
||||
|
||||
class WebMenuAction extends Controller
|
||||
{
|
||||
/**
|
||||
* GET /web-menus
|
||||
* 列表接口:{ "data": { "total_count": N, "list": [...] } }(与 Wiki 接口响应格式一致)
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$page = (int) $request->query('page', 1);
|
||||
$pageSize = (int) $request->query('page_size', 20);
|
||||
$name = $request->query('name');
|
||||
$name = is_string($name) ? trim($name) : null;
|
||||
if ($name === '') {
|
||||
$name = null;
|
||||
}
|
||||
$service = new WebMenuService();
|
||||
$result = $service->listMenus($companyId, $page, $pageSize, $name);
|
||||
$transformer = new WebMenuTransformer();
|
||||
$em = app('registry')->getManager('default');
|
||||
$itemRepo = $em->getRepository(WebMenuItem::class);
|
||||
$menuIds = array_map(fn ($m) => $m->getId(), $result['list']);
|
||||
$counts = $itemRepo->countItemsByMenuIds($companyId, $menuIds);
|
||||
$topLevelNames = $itemRepo->findTopLevelItemNamesGroupedByMenuIds($companyId, $menuIds);
|
||||
$list = [];
|
||||
foreach ($result['list'] as $m) {
|
||||
$row = $transformer->transform($m);
|
||||
$mid = $m->getId();
|
||||
$row['items_count'] = $counts[$mid] ?? 0;
|
||||
$row['top_level_item_names'] = $topLevelNames[$mid] ?? '';
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
return $this->response->array([
|
||||
'total_count' => $result['total'],
|
||||
'list' => $list,
|
||||
]);
|
||||
}
|
||||
|
||||
/** POST /web-menus */
|
||||
public function store(Request $request)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$service = new WebMenuService();
|
||||
$menu = $service->createMenu($companyId, $request->only(['name', 'key', 'status']));
|
||||
|
||||
return $this->response->item($menu, new WebMenuTransformer());
|
||||
}
|
||||
|
||||
/** GET /web-menus/{id} */
|
||||
public function show(Request $request, $id)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$menuId = (int) $id;
|
||||
$service = new WebMenuService();
|
||||
$em = app('registry')->getManager('default');
|
||||
$menu = $em->getRepository(WebMenu::class)->findOneByIdAndCompany($menuId, $companyId);
|
||||
if (!$menu) {
|
||||
return $this->response->errorNotFound('菜单不存在');
|
||||
}
|
||||
$items = $em->getRepository(WebMenuItem::class)->findAllByMenu($menuId, $companyId);
|
||||
$tree = $service->buildTree($items);
|
||||
$itemTr = new WebMenuItemTransformer();
|
||||
|
||||
/** 详情:{ "data": { id, name, key, status, items: [...] } } */
|
||||
return $this->response->array(array_merge(
|
||||
(new WebMenuTransformer())->transform($menu),
|
||||
['items' => array_map([$itemTr, 'transform'], $tree)]
|
||||
));
|
||||
}
|
||||
|
||||
/** PUT /web-menus/{id} */
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$service = new WebMenuService();
|
||||
$menu = $service->updateMenu((int) $id, $companyId, $request->only(['name', 'key', 'status']));
|
||||
|
||||
return $this->response->item($menu, new WebMenuTransformer());
|
||||
}
|
||||
|
||||
/** DELETE /web-menus/{id} */
|
||||
public function destroy(Request $request, $id)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$service = new WebMenuService();
|
||||
$service->deleteMenu((int) $id, $companyId);
|
||||
|
||||
return $this->response->noContent();
|
||||
}
|
||||
}
|
||||
62
src/ThemeBundle/Http/Api/V1/Action/WebMenuItemAction.php
Normal file
62
src/ThemeBundle/Http/Api/V1/Action/WebMenuItemAction.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Http\Api\V1\Action;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use ThemeBundle\Services\WebMenuService;
|
||||
use ThemeBundle\Transformers\WebMenuItemFlatTransformer;
|
||||
|
||||
class WebMenuItemAction extends Controller
|
||||
{
|
||||
/** POST /web-menus/{id}/items */
|
||||
public function store(Request $request, $id)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$service = new WebMenuService();
|
||||
$item = $service->createItem((int) $id, $companyId, $request->only([
|
||||
'name', 'image_url', 'link_type', 'link_value', 'link_extra', 'sort', 'status', 'parent_id',
|
||||
]));
|
||||
|
||||
return $this->response->item($item, new WebMenuItemFlatTransformer());
|
||||
}
|
||||
|
||||
/** PUT /web-menus/{id}/items/{itemId} */
|
||||
public function update(Request $request, $id, $itemId)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$service = new WebMenuService();
|
||||
$item = $service->updateItem((int) $itemId, (int) $id, $companyId, $request->only([
|
||||
'name', 'image_url', 'link_type', 'link_value', 'link_extra', 'sort', 'status', 'parent_id',
|
||||
]));
|
||||
|
||||
return $this->response->item($item, new WebMenuItemFlatTransformer());
|
||||
}
|
||||
|
||||
/** DELETE /web-menus/{id}/items/{itemId} */
|
||||
public function destroy(Request $request, $id, $itemId)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$service = new WebMenuService();
|
||||
$service->deleteItem((int) $itemId, (int) $id, $companyId);
|
||||
|
||||
return $this->response->noContent();
|
||||
}
|
||||
|
||||
/** PUT /web-menus/{id}/items/sort */
|
||||
public function batchSort(Request $request, $id)
|
||||
{
|
||||
$companyId = (int) app('auth')->user()->get('company_id');
|
||||
$sorts = $request->input('sorts', $request->input('items', []));
|
||||
if (!is_array($sorts)) {
|
||||
$sorts = [];
|
||||
}
|
||||
$service = new WebMenuService();
|
||||
$service->batchUpdateSort((int) $id, $companyId, $sorts);
|
||||
|
||||
return $this->response->noContent();
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ use TdksetBundle\Services\TdkGlobalService;
|
||||
use ThemeBundle\Services\PagesTemplateServices;
|
||||
use ThemeBundle\Services\PagesTemplateSetServices;
|
||||
use DistributionBundle\Entities\Distributor;
|
||||
use EmployeePurchaseBundle\Services\ActivityItemsService;
|
||||
use GoodsBundle\Services\ItemsService;
|
||||
|
||||
class PagesTemplate extends Controller
|
||||
@@ -72,6 +73,13 @@ class PagesTemplate extends Controller
|
||||
* required=true,
|
||||
* type="integer",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="e_activity_id",
|
||||
* in="query",
|
||||
* description="内购活动 ID;传入时按活动店铺查商品并附加 employee_purchase_activity_items 活动价",
|
||||
* required=false,
|
||||
* type="integer",
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="成功返回结构",
|
||||
@@ -101,9 +109,10 @@ class PagesTemplate extends Controller
|
||||
public function getWidgetItems(Request $request)
|
||||
{
|
||||
$authInfo = $request->get('auth');
|
||||
$params = $request->all('regionauth_id', 'distributor_id', 'data_type', 'data_value', 'num', 'page', 'pageSize', 'sort_gte');
|
||||
$params = $request->all('regionauth_id', 'distributor_id', 'data_type', 'data_value', 'num', 'page', 'pageSize', 'sort_gte', 'e_activity_id');
|
||||
$params['company_id'] = $authInfo['company_id'];
|
||||
$params['user_id'] = $authInfo['user_id'] ?? 0;
|
||||
$eActivityId = (int) ($params['e_activity_id'] ?? 0);
|
||||
$pages_template_services = new PagesTemplateServices();
|
||||
$result = $pages_template_services->getWidgetItems($params);
|
||||
|
||||
@@ -113,6 +122,12 @@ class PagesTemplate extends Controller
|
||||
if ($result['data']) {
|
||||
$result['data'] = $itemsService->applyMultiSpecTotalStoreForItemList($result['data']);
|
||||
}
|
||||
if ($eActivityId > 0 && $result['data']) {
|
||||
$activityItemsService = new ActivityItemsService();
|
||||
$wrapped = ['list' => $result['data']];
|
||||
$wrapped = $activityItemsService->getItemsListActityPrice($wrapped, $eActivityId, (int) $params['company_id']);
|
||||
$result['data'] = $wrapped['list'];
|
||||
}
|
||||
if ($result['data']) {
|
||||
$result['data'] = $itemsService->getItemsListMemberPrice($result['data'], $authInfo['user_id'], $params['company_id']);
|
||||
}
|
||||
|
||||
@@ -24,80 +24,6 @@ use ThemeBundle\Services\ThemePcTemplateContentServices;
|
||||
|
||||
class PcTemplate extends Controller
|
||||
{
|
||||
/**
|
||||
* @SWG\Get(
|
||||
* path="/h5app/wxapp/pctemplate/getHeaderOrFooter",
|
||||
* summary="获取pc模板头尾部",
|
||||
* tags={"模板"},
|
||||
* description="获取pc模板头尾部",
|
||||
* operationId="getHeaderOrFooter",
|
||||
* @SWG\Parameter(
|
||||
* name="Authorization",
|
||||
* in="header",
|
||||
* description="JWT验证token",
|
||||
* required=true,
|
||||
* type="string",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="page_name",
|
||||
* in="query",
|
||||
* description="页面名称",
|
||||
* required=true,
|
||||
* type="string",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="company_id",
|
||||
* in="query",
|
||||
* description="公司编号",
|
||||
* required=true,
|
||||
* type="integer",
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="成功返回结构",
|
||||
* @SWG\Schema(
|
||||
* @SWG\Property(
|
||||
* property="data",
|
||||
* type="array",
|
||||
* @SWG\Items(
|
||||
* type="object",
|
||||
* @SWG\Property(property="company_id", type="int"),
|
||||
* @SWG\Property(property="created", type="string"),
|
||||
* @SWG\Property(property="name", type="string"),
|
||||
* @SWG\Property(property="params", type="string"),
|
||||
* @SWG\Property(property="theme_pc_template_content_id", type="int"),
|
||||
* @SWG\Property(property="theme_pc_template_id", type="int"),
|
||||
* @SWG\Property(property="updated", type="string"),
|
||||
* )
|
||||
* ),
|
||||
* ),
|
||||
* ),
|
||||
* @SWG\Response( response="default", description="错误返回结构", @SWG\Schema( type="array", @SWG\Items(ref="#/definitions/ThemeErrorRespones") ) )
|
||||
* )
|
||||
*/
|
||||
public function getHeaderOrFooter(Request $request)
|
||||
{
|
||||
$company_id = $request->get('company_id');
|
||||
$page_name = $request->input('page_name');
|
||||
$params = [
|
||||
'company_id' => $company_id,
|
||||
'page_name' => $page_name,
|
||||
];
|
||||
$rules = [
|
||||
'company_id' => ['required', '缺少company_id'],
|
||||
'page_name' => ['required', '缺少page_name'],
|
||||
];
|
||||
$error = validator_params($params, $rules);
|
||||
if ($error) {
|
||||
throw new ResourceException($error);
|
||||
}
|
||||
|
||||
$service = new ThemePcTemplateContentServices();
|
||||
$result = $service->detail($params);
|
||||
|
||||
return $this->response->array($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Get(
|
||||
* path="/h5app/wxapp/pctemplate/getTemplateContent",
|
||||
@@ -113,11 +39,18 @@ class PcTemplate extends Controller
|
||||
* type="string",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="theme_pc_template_id",
|
||||
* name="page_type",
|
||||
* in="query",
|
||||
* description="主题PC模板ID",
|
||||
* description="页面类型 home/header/footer/custom/product_list",
|
||||
* required=true,
|
||||
* type="integer",
|
||||
* type="string",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="page_id",
|
||||
* in="query",
|
||||
* description="页面ID,自定义页传模板ID",
|
||||
* required=false,
|
||||
* type="string",
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* name="company_id",
|
||||
@@ -132,16 +65,10 @@ class PcTemplate extends Controller
|
||||
* @SWG\Schema(
|
||||
* @SWG\Property(
|
||||
* property="data",
|
||||
* type="array",
|
||||
* @SWG\Items(
|
||||
* type="array",
|
||||
* @SWG\Items(
|
||||
* type="object",
|
||||
* @SWG\Property(property="config", type="string"),
|
||||
* @SWG\Property(property="name", type="string"),
|
||||
* )
|
||||
* ),
|
||||
* ),
|
||||
* type="object",
|
||||
* @SWG\Property(property="id", type="integer"),
|
||||
* @SWG\Property(property="name", type="string"),
|
||||
* @SWG\Property(property="config", type="string")
|
||||
* ),
|
||||
* ),
|
||||
* @SWG\Response( response="default", description="错误返回结构", @SWG\Schema( type="array", @SWG\Items(ref="#/definitions/ThemeErrorRespones") ) )
|
||||
@@ -151,17 +78,19 @@ class PcTemplate extends Controller
|
||||
{
|
||||
$authInfo = $request->get('auth');
|
||||
$company_id = $request->get('company_id');
|
||||
$page_type = $request->get('page_type', 'index');
|
||||
$theme_pc_template_id = $request->input('theme_pc_template_id', '');
|
||||
$page_type = $request->get('page_type', 'home');
|
||||
$page_id = $request->input('page_id', '');
|
||||
|
||||
$params = [
|
||||
'company_id' => $company_id,
|
||||
'page_type' => $page_type,
|
||||
'page_id' => $page_id,
|
||||
'user_id' => $authInfo['user_id'] ?? 0,
|
||||
'theme_pc_template_id' => $theme_pc_template_id,
|
||||
];
|
||||
$rules = [
|
||||
'company_id' => ['required', '缺少company_id'],
|
||||
'page_type' => ['required|in:home,header,footer,custom,product_list', '缺少或错误的page_type'],
|
||||
'page_id' => ['required_if:page_type,custom', '缺少page_id'],
|
||||
];
|
||||
$error = validator_params($params, $rules);
|
||||
if ($error) {
|
||||
@@ -169,7 +98,7 @@ class PcTemplate extends Controller
|
||||
}
|
||||
|
||||
$service = new ThemePcTemplateContentServices();
|
||||
$result = $service->templateContent($params);
|
||||
$result = $service->decorationContent($params);
|
||||
|
||||
return $this->response->array($result);
|
||||
}
|
||||
@@ -181,9 +110,15 @@ class PcTemplate extends Controller
|
||||
$result = app('redis')->connection('companys')->get('pc_login_page:'.$companyId);
|
||||
if (!$result) {
|
||||
$result['logo'] = '';
|
||||
$result['logo_light'] = '';
|
||||
$result['logo_dark'] = '';
|
||||
$result['background'] = '';
|
||||
} else {
|
||||
$result = json_decode($result, true);
|
||||
$result['logo_light'] = $result['logo_light'] ?? ($result['logo'] ?? '');
|
||||
$result['logo_dark'] = $result['logo_dark'] ?? ($result['logo'] ?? '');
|
||||
$result['logo'] = $result['logo'] ?? ($result['logo_light'] ?? '');
|
||||
$result['background'] = $result['background'] ?? '';
|
||||
}
|
||||
return $this->response->array($result);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Http\FrontApi\V1\Action;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use ThemeBundle\Entities\WebMenu;
|
||||
use ThemeBundle\Entities\WebMenuItem;
|
||||
use ThemeBundle\Services\WebMenuService;
|
||||
use ThemeBundle\Transformers\WebMenuItemTransformer;
|
||||
|
||||
class WebMenuFrontAction extends Controller
|
||||
{
|
||||
/** GET /h5app/web/menus/{key} */
|
||||
public function show(Request $request, string $key)
|
||||
{
|
||||
$companyId = $this->resolveCompanyId($request);
|
||||
if ($companyId <= 0) {
|
||||
return $this->response->errorUnauthorized('缺少 company 上下文');
|
||||
}
|
||||
|
||||
$em = app('registry')->getManager('default');
|
||||
/** @var \ThemeBundle\Repositories\WebMenuRepository $menuRepo */
|
||||
$menuRepo = $em->getRepository(WebMenu::class);
|
||||
$menu = $menuRepo->findActiveByCompanyAndKey($companyId, $key);
|
||||
if (!$menu) {
|
||||
return $this->response->errorNotFound('菜单不存在');
|
||||
}
|
||||
|
||||
return $this->renderMenuTree($companyId, $menu);
|
||||
}
|
||||
|
||||
/** GET /h5app/web/menus/id/{id} */
|
||||
public function showById(Request $request, int $id)
|
||||
{
|
||||
$companyId = $this->resolveCompanyId($request);
|
||||
if ($companyId <= 0) {
|
||||
return $this->response->errorUnauthorized('缺少 company 上下文');
|
||||
}
|
||||
|
||||
$em = app('registry')->getManager('default');
|
||||
/** @var \ThemeBundle\Repositories\WebMenuRepository $menuRepo */
|
||||
$menuRepo = $em->getRepository(WebMenu::class);
|
||||
$menu = $menuRepo->findActiveByCompanyAndId($companyId, $id);
|
||||
if (!$menu) {
|
||||
return $this->response->errorNotFound('菜单不存在');
|
||||
}
|
||||
|
||||
return $this->renderMenuTree($companyId, $menu);
|
||||
}
|
||||
|
||||
private function resolveCompanyId(Request $request): int
|
||||
{
|
||||
$auth = $request->get('auth');
|
||||
if (!is_array($auth) || empty($auth['company_id'])) {
|
||||
$auth = $request->attributes->get('auth', []);
|
||||
}
|
||||
|
||||
return (int) ($auth['company_id'] ?? 0);
|
||||
}
|
||||
|
||||
private function renderMenuTree(int $companyId, WebMenu $menu)
|
||||
{
|
||||
$em = app('registry')->getManager('default');
|
||||
/** @var \ThemeBundle\Repositories\WebMenuItemRepository $itemRepo */
|
||||
$itemRepo = $em->getRepository(WebMenuItem::class);
|
||||
$items = $itemRepo->findActiveByMenu($menu->getId(), $companyId);
|
||||
$service = new WebMenuService();
|
||||
$tree = $service->buildTree($items);
|
||||
$tr = new WebMenuItemTransformer();
|
||||
|
||||
return $this->response->array([
|
||||
'id' => $menu->getId(),
|
||||
'name' => $menu->getName(),
|
||||
'key' => $menu->getKey(),
|
||||
'items' => array_map([$tr, 'transformFront'], $tree),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ use Dingo\Api\Exception\ResourceException;
|
||||
class ThemePcTemplateRepository extends EntityRepository
|
||||
{
|
||||
public $table = "theme_pc_template";
|
||||
public $cols = ['theme_pc_template_id','company_id','template_title','template_description','page_type','status','version','created','updated','deleted_at'];
|
||||
public $cols = ['theme_pc_template_id','company_id','distributor_id','template_title','template_description','page_type','status','version','created','updated','deleted_at'];
|
||||
|
||||
public $module = 'theme_pc_template'; // 多语言对应的模块
|
||||
public $primaryKey = 'theme_pc_template_id'; // 主键,对应data_id
|
||||
@@ -159,7 +159,7 @@ class ThemePcTemplateRepository extends EntityRepository
|
||||
private function setColumnNamesData($entity, $params)
|
||||
{
|
||||
foreach ($this->cols as $col) {
|
||||
if (isset($params[$col])) {
|
||||
if (array_key_exists($col, $params)) {
|
||||
$fun = "set". str_replace(" ", "", ucwords(str_replace("_", " ", $col)));
|
||||
if (method_exists($entity, $fun)) {
|
||||
$entity->$fun($params[$col]);
|
||||
|
||||
135
src/ThemeBundle/Repositories/WebMenuItemRepository.php
Normal file
135
src/ThemeBundle/Repositories/WebMenuItemRepository.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Repositories;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use ThemeBundle\Entities\WebMenuItem;
|
||||
|
||||
class WebMenuItemRepository extends EntityRepository
|
||||
{
|
||||
/**
|
||||
* 前台:仅启用项
|
||||
*
|
||||
* @return WebMenuItem[]
|
||||
*/
|
||||
public function findActiveByMenu(int $menuId, int $companyId): array
|
||||
{
|
||||
return $this->createQueryBuilder('i')
|
||||
->where('i.menuId = :mid AND i.companyId = :cid AND i.status = 1')
|
||||
->setParameter('mid', $menuId)
|
||||
->setParameter('cid', $companyId)
|
||||
->orderBy('i.parentId', 'ASC')
|
||||
->addOrderBy('i.sort', 'ASC')
|
||||
->addOrderBy('i.id', 'ASC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台:含禁用项
|
||||
*
|
||||
* @return WebMenuItem[]
|
||||
*/
|
||||
public function findAllByMenu(int $menuId, int $companyId): array
|
||||
{
|
||||
return $this->createQueryBuilder('i')
|
||||
->where('i.menuId = :mid AND i.companyId = :cid')
|
||||
->setParameter('mid', $menuId)
|
||||
->setParameter('cid', $companyId)
|
||||
->orderBy('i.parentId', 'ASC')
|
||||
->addOrderBy('i.sort', 'ASC')
|
||||
->addOrderBy('i.id', 'ASC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function findOneByIdMenuCompany(int $itemId, int $menuId, int $companyId): ?WebMenuItem
|
||||
{
|
||||
return $this->findOneBy([
|
||||
'id' => $itemId,
|
||||
'menuId' => $menuId,
|
||||
'companyId' => $companyId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量统计各菜单下的菜单项数量(含子项)
|
||||
*
|
||||
* @param int[] $menuIds
|
||||
*
|
||||
* @return array<int, int> menu_id => count
|
||||
*/
|
||||
public function countItemsByMenuIds(int $companyId, array $menuIds): array
|
||||
{
|
||||
$menuIds = array_values(array_unique(array_filter(array_map('intval', $menuIds))));
|
||||
if ($menuIds === []) {
|
||||
return [];
|
||||
}
|
||||
$qb = $this->createQueryBuilder('i')
|
||||
->select('i.menuId AS menuId')
|
||||
->addSelect('COUNT(i.id) AS cnt')
|
||||
->where('i.companyId = :cid')
|
||||
->andWhere('i.menuId IN (:mids)')
|
||||
->setParameter('cid', $companyId)
|
||||
->setParameter('mids', $menuIds)
|
||||
->groupBy('i.menuId');
|
||||
|
||||
$out = array_fill_keys($menuIds, 0);
|
||||
foreach ($qb->getQuery()->getScalarResult() as $row) {
|
||||
$mid = (int) ($row['menuId'] ?? $row['menu_id'] ?? 0);
|
||||
$cnt = (int) ($row['cnt'] ?? 0);
|
||||
if ($mid > 0) {
|
||||
$out[$mid] = $cnt;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询各菜单下「一级」菜单项名称(parent_id = 0),按 sort、id 排序后供列表展示
|
||||
*
|
||||
* @param int[] $menuIds
|
||||
*
|
||||
* @return array<int, string> menu_id => 名称以英文逗号连接
|
||||
*/
|
||||
public function findTopLevelItemNamesGroupedByMenuIds(int $companyId, array $menuIds): array
|
||||
{
|
||||
$menuIds = array_values(array_unique(array_filter(array_map('intval', $menuIds))));
|
||||
if ($menuIds === []) {
|
||||
return [];
|
||||
}
|
||||
$qb = $this->createQueryBuilder('i')
|
||||
->select('i.menuId AS menuId')
|
||||
->addSelect('i.name AS name')
|
||||
->where('i.companyId = :cid')
|
||||
->andWhere('i.menuId IN (:mids)')
|
||||
->andWhere('i.parentId = 0')
|
||||
->setParameter('cid', $companyId)
|
||||
->setParameter('mids', $menuIds)
|
||||
->orderBy('i.menuId', 'ASC')
|
||||
->addOrderBy('i.sort', 'ASC')
|
||||
->addOrderBy('i.id', 'ASC');
|
||||
|
||||
$grouped = [];
|
||||
foreach ($menuIds as $mid) {
|
||||
$grouped[$mid] = [];
|
||||
}
|
||||
foreach ($qb->getQuery()->getScalarResult() as $row) {
|
||||
$mid = (int) ($row['menuId'] ?? $row['menu_id'] ?? 0);
|
||||
$name = isset($row['name']) ? trim((string) $row['name']) : '';
|
||||
if ($mid > 0 && $name !== '') {
|
||||
$grouped[$mid][] = $name;
|
||||
}
|
||||
}
|
||||
$out = [];
|
||||
foreach ($grouped as $mid => $names) {
|
||||
$out[$mid] = $names === [] ? '' : implode(',', $names);
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
85
src/ThemeBundle/Repositories/WebMenuRepository.php
Normal file
85
src/ThemeBundle/Repositories/WebMenuRepository.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Repositories;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use ThemeBundle\Entities\WebMenu;
|
||||
|
||||
class WebMenuRepository extends EntityRepository
|
||||
{
|
||||
public function findActiveByCompanyAndKey(int $companyId, string $key): ?WebMenu
|
||||
{
|
||||
return $this->findOneBy([
|
||||
'companyId' => $companyId,
|
||||
'key' => $key,
|
||||
'status' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
public function findOneByIdAndCompany(int $id, int $companyId): ?WebMenu
|
||||
{
|
||||
return $this->findOneBy(['id' => $id, 'companyId' => $companyId]);
|
||||
}
|
||||
|
||||
public function findActiveByCompanyAndId(int $companyId, int $id): ?WebMenu
|
||||
{
|
||||
return $this->findOneBy([
|
||||
'id' => $id,
|
||||
'companyId' => $companyId,
|
||||
'status' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WebMenu[]
|
||||
*/
|
||||
public function findPageByCompany(int $companyId, int $page = 1, int $pageSize = 20, ?string $name = null): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('m')
|
||||
->where('m.companyId = :cid')
|
||||
->setParameter('cid', $companyId);
|
||||
$this->applyNameFilter($qb, $name);
|
||||
|
||||
return $qb->orderBy('m.id', 'DESC')
|
||||
->setFirstResult(($page - 1) * $pageSize)
|
||||
->setMaxResults($pageSize)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function countByCompany(int $companyId, ?string $name = null): int
|
||||
{
|
||||
$qb = $this->createQueryBuilder('m')
|
||||
->select('COUNT(m.id)')
|
||||
->where('m.companyId = :cid')
|
||||
->setParameter('cid', $companyId);
|
||||
$this->applyNameFilter($qb, $name);
|
||||
|
||||
return (int) $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
private function applyNameFilter($qb, ?string $name): void
|
||||
{
|
||||
$name = $name !== null ? trim($name) : '';
|
||||
if ($name !== '') {
|
||||
$qb->andWhere('m.name LIKE :menuName')->setParameter('menuName', '%' . $name . '%');
|
||||
}
|
||||
}
|
||||
|
||||
public function existsDuplicateKey(int $companyId, string $key, ?int $excludeId = null): bool
|
||||
{
|
||||
$qb = $this->createQueryBuilder('m')
|
||||
->select('COUNT(m.id)')
|
||||
->where('m.companyId = :cid AND m.key = :k')
|
||||
->setParameter('cid', $companyId)
|
||||
->setParameter('k', $key);
|
||||
if ($excludeId !== null) {
|
||||
$qb->andWhere('m.id != :eid')->setParameter('eid', $excludeId);
|
||||
}
|
||||
|
||||
return (int) $qb->getQuery()->getSingleScalarResult() > 0;
|
||||
}
|
||||
}
|
||||
@@ -1533,6 +1533,18 @@ class PagesTemplateServices
|
||||
|
||||
public function getWidgetItems($params)
|
||||
{
|
||||
$eActivityId = (int) ($params['e_activity_id'] ?? 0);
|
||||
if ($eActivityId > 0 && !empty($params['company_id'])) {
|
||||
$activitiesService = new ActivitiesService();
|
||||
$activityInfo = $activitiesService->entityRepository->getInfo([
|
||||
'id' => $eActivityId,
|
||||
'company_id' => $params['company_id'],
|
||||
]);
|
||||
if (!empty($activityInfo['distributor_id'])) {
|
||||
$params['distributor_id'] = (int) $activityInfo['distributor_id'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($params['data_type'] != 'sales') {
|
||||
if (!isset($params['data_value']) || !$params['data_value']) {
|
||||
return [
|
||||
|
||||
@@ -86,6 +86,93 @@ class ThemePcTemplateContentServices
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 PC 模板装修内容
|
||||
*/
|
||||
public function decorationContent($params)
|
||||
{
|
||||
$page_type = $params['page_type'] ?? '';
|
||||
if (in_array($page_type, ['header', 'footer'])) {
|
||||
return $this->formatDecorationContent($this->detail([
|
||||
'company_id' => $params['company_id'],
|
||||
'page_name' => $page_type,
|
||||
]));
|
||||
}
|
||||
|
||||
return $this->pageDecorationDetail($params);
|
||||
}
|
||||
|
||||
private function pageDecorationDetail($params)
|
||||
{
|
||||
$company_id = $params['company_id'];
|
||||
$theme_pc_template_id = $params['theme_pc_template_id'] ?? '';
|
||||
$distributor_id = $params['distributor_id'] ?? 0;
|
||||
$page_type = $params['page_type'] ?? 'index';
|
||||
$page_id = $params['page_id'] ?? '';
|
||||
$filter = [
|
||||
'company_id' => $company_id,
|
||||
];
|
||||
|
||||
if (!empty($theme_pc_template_id)) {
|
||||
$filter['theme_pc_template_id'] = $theme_pc_template_id;
|
||||
} else if ($page_type == 'custom' && !empty($page_id)) {
|
||||
$filter['theme_pc_template_id'] = $page_id;
|
||||
} else {
|
||||
$filter['page_type'] = $page_type == 'home' ? 'index' : $page_type;
|
||||
$filter['status'] = 1;
|
||||
$filter['distributor_id'] = (int)$distributor_id;
|
||||
}
|
||||
|
||||
$theme_pc_template_info = $this->themePcTemplateRepository->getInfo($filter);
|
||||
if (empty($theme_pc_template_info)) {
|
||||
return $this->emptyDecorationContent();
|
||||
}
|
||||
|
||||
$_filter = [
|
||||
'theme_pc_template_id' => $theme_pc_template_info['theme_pc_template_id']
|
||||
];
|
||||
$list = $this->themePcTemplateContentRepository->getLists($_filter, '*', 1, -1, ['theme_pc_template_content_id' => 'ASC']);
|
||||
if (empty($list)) {
|
||||
return $this->emptyDecorationContent();
|
||||
}
|
||||
|
||||
$target = null;
|
||||
foreach ($list as $value) {
|
||||
if (strpos((string)($value['params'] ?? ''), 'ECX_SP_WEB_DECORATION_DSL_V1') !== false) {
|
||||
$target = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($target === null) {
|
||||
$target = $list[0];
|
||||
}
|
||||
|
||||
return $this->formatDecorationContent($target);
|
||||
}
|
||||
|
||||
private function formatDecorationContent($row)
|
||||
{
|
||||
if (empty($row) || !is_array($row)) {
|
||||
return $this->emptyDecorationContent();
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $row['theme_pc_template_content_id'] ?? $row['id'] ?? 0,
|
||||
'name' => $row['name'] ?? '',
|
||||
'config' => $row['params'] ?? $row['config'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
private function emptyDecorationContent()
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'config' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模版内容
|
||||
*/
|
||||
@@ -93,6 +180,7 @@ class ThemePcTemplateContentServices
|
||||
{
|
||||
$company_id = $params['company_id'];
|
||||
$theme_pc_template_id = $params['theme_pc_template_id'];
|
||||
$distributor_id = $params['distributor_id'] ?? 0;
|
||||
$filter = [
|
||||
'company_id' => $company_id,
|
||||
];
|
||||
@@ -101,6 +189,7 @@ class ThemePcTemplateContentServices
|
||||
} else {
|
||||
$filter['page_type'] = $params['page_type'];
|
||||
$filter['status'] = 1;
|
||||
$filter['distributor_id'] = (int)$distributor_id;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
@@ -40,13 +40,15 @@ class ThemePcTemplateServices
|
||||
public function lists($params)
|
||||
{
|
||||
$company_id = $params['company_id'];
|
||||
$distributor_id = $params['distributor_id'] ?? 0;
|
||||
$page_type = $params['page_type'];
|
||||
$page_no = $params['page_no'];
|
||||
$page_size = $params['page_size'];
|
||||
$status = $params['status'];
|
||||
|
||||
$filter = [
|
||||
'company_id' => $company_id
|
||||
'company_id' => $company_id,
|
||||
'distributor_id' => (int)$distributor_id
|
||||
];
|
||||
if (!empty($page_type)) {
|
||||
$filter['page_type'] = $page_type;
|
||||
@@ -69,12 +71,13 @@ class ThemePcTemplateServices
|
||||
* @param int|null $exclude_template_id 排除的模板ID(编辑时使用)
|
||||
* @throws ResourceException
|
||||
*/
|
||||
private function checkIndexTemplateStatus($company_id, $page_type, $status, $exclude_template_id = null)
|
||||
private function checkIndexTemplateStatus($company_id, $page_type, $status, $exclude_template_id = null, $distributor_id = 0)
|
||||
{
|
||||
// 只检查首页且启用状态的情况
|
||||
if ($page_type == 'index' && $status == 1) {
|
||||
$filter = [
|
||||
'company_id' => $company_id,
|
||||
'distributor_id' => (int)$distributor_id,
|
||||
'page_type' => 'index',
|
||||
'status' => 1,
|
||||
];
|
||||
@@ -100,9 +103,13 @@ class ThemePcTemplateServices
|
||||
$this->checkIndexTemplateStatus(
|
||||
$params['company_id'],
|
||||
$params['page_type'] ?? '',
|
||||
$params['status'] ?? 2
|
||||
$params['status'] ?? 2,
|
||||
null,
|
||||
$params['distributor_id'] ?? 0
|
||||
);
|
||||
|
||||
$params['distributor_id'] = $params['distributor_id'] ?? 0;
|
||||
|
||||
$result = $this->themePcTemplateRepository->create($params);
|
||||
|
||||
return $result;
|
||||
@@ -130,6 +137,9 @@ class ThemePcTemplateServices
|
||||
// 确定 page_type:优先使用传入的值,否则使用数据库中的值
|
||||
$page_type = $params['page_type'] ?? $pc_template_info['page_type'];
|
||||
$status = $params['status'] ?? null;
|
||||
$distributor_id = array_key_exists('distributor_id', $params)
|
||||
? (int)$params['distributor_id']
|
||||
: (int)($pc_template_info['distributor_id'] ?? 0);
|
||||
|
||||
// 检查首页模板启用状态
|
||||
if (!empty($status)) {
|
||||
@@ -137,7 +147,8 @@ class ThemePcTemplateServices
|
||||
$company_id,
|
||||
$page_type,
|
||||
$status,
|
||||
$theme_pc_template_id
|
||||
$theme_pc_template_id,
|
||||
$distributor_id
|
||||
);
|
||||
}
|
||||
|
||||
@@ -161,6 +172,10 @@ class ThemePcTemplateServices
|
||||
$data['page_type'] = $params['page_type'];
|
||||
}
|
||||
|
||||
if (array_key_exists('distributor_id', $params)) {
|
||||
$data['distributor_id'] = (int)$params['distributor_id'];
|
||||
}
|
||||
|
||||
$result = $this->themePcTemplateRepository->updateOneBy($filter, $data);
|
||||
$conn->commit();
|
||||
} catch (\Exception $e) {
|
||||
|
||||
338
src/ThemeBundle/Services/WebMenuService.php
Normal file
338
src/ThemeBundle/Services/WebMenuService.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Services;
|
||||
|
||||
use Dingo\Api\Exception\ResourceException;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use ThemeBundle\Entities\WebMenu;
|
||||
use ThemeBundle\Entities\WebMenuItem;
|
||||
use ThemeBundle\Repositories\WebMenuItemRepository;
|
||||
use ThemeBundle\Repositories\WebMenuRepository;
|
||||
|
||||
class WebMenuService
|
||||
{
|
||||
private EntityManager $em;
|
||||
|
||||
private WebMenuRepository $menuRepo;
|
||||
|
||||
private WebMenuItemRepository $itemRepo;
|
||||
|
||||
public function __construct(?EntityManager $em = null)
|
||||
{
|
||||
$this->em = $em ?? app('registry')->getManager('default');
|
||||
$this->menuRepo = $this->em->getRepository(WebMenu::class);
|
||||
$this->itemRepo = $this->em->getRepository(WebMenuItem::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{list: WebMenu[], total: int, page: int, page_size: int}
|
||||
*/
|
||||
public function listMenus(int $companyId, int $page = 1, int $pageSize = 20, ?string $name = null): array
|
||||
{
|
||||
$page = max(1, $page);
|
||||
$pageSize = min(100, max(1, $pageSize));
|
||||
$name = $name !== null ? trim($name) : null;
|
||||
if ($name === '') {
|
||||
$name = null;
|
||||
}
|
||||
$list = $this->menuRepo->findPageByCompany($companyId, $page, $pageSize, $name);
|
||||
$total = $this->menuRepo->countByCompany($companyId, $name);
|
||||
|
||||
return [
|
||||
'list' => $list,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'page_size' => $pageSize,
|
||||
];
|
||||
}
|
||||
|
||||
public function createMenu(int $companyId, array $data): WebMenu
|
||||
{
|
||||
$name = trim((string) ($data['name'] ?? ''));
|
||||
$key = trim((string) ($data['key'] ?? ''));
|
||||
if ($name === '' || $key === '') {
|
||||
throw new ResourceException('name 与 key 不能为空');
|
||||
}
|
||||
if ($this->menuRepo->existsDuplicateKey($companyId, $key)) {
|
||||
throw new ResourceException('同一店铺下 key 已存在');
|
||||
}
|
||||
$now = new \DateTimeImmutable();
|
||||
$menu = new WebMenu();
|
||||
$menu->setCompanyId($companyId);
|
||||
$menu->setName($name);
|
||||
$menu->setKey($key);
|
||||
$menu->setStatus(isset($data['status']) ? (int) $data['status'] : 1);
|
||||
$menu->setCreatedAt($now);
|
||||
$menu->setUpdatedAt($now);
|
||||
$this->em->persist($menu);
|
||||
$this->em->flush();
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
public function updateMenu(int $id, int $companyId, array $data): WebMenu
|
||||
{
|
||||
$menu = $this->menuRepo->findOneByIdAndCompany($id, $companyId);
|
||||
if (!$menu) {
|
||||
throw new ResourceException('菜单不存在');
|
||||
}
|
||||
if (isset($data['name'])) {
|
||||
$name = trim((string) $data['name']);
|
||||
if ($name === '') {
|
||||
throw new ResourceException('name 不能为空');
|
||||
}
|
||||
$menu->setName($name);
|
||||
}
|
||||
if (isset($data['key'])) {
|
||||
$key = trim((string) $data['key']);
|
||||
if ($key === '') {
|
||||
throw new ResourceException('key 不能为空');
|
||||
}
|
||||
if ($this->menuRepo->existsDuplicateKey($companyId, $key, $id)) {
|
||||
throw new ResourceException('同一店铺下 key 已存在');
|
||||
}
|
||||
$menu->setKey($key);
|
||||
}
|
||||
if (isset($data['status'])) {
|
||||
$menu->setStatus((int) $data['status']);
|
||||
}
|
||||
$menu->setUpdatedAt(new \DateTimeImmutable());
|
||||
$this->em->flush();
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
public function deleteMenu(int $id, int $companyId): void
|
||||
{
|
||||
$menu = $this->menuRepo->findOneByIdAndCompany($id, $companyId);
|
||||
if (!$menu) {
|
||||
throw new ResourceException('菜单不存在');
|
||||
}
|
||||
$this->em->createQuery(
|
||||
'DELETE ThemeBundle\Entities\WebMenuItem i WHERE i.menuId = :mid AND i.companyId = :cid'
|
||||
)->setParameter('mid', $id)->setParameter('cid', $companyId)->execute();
|
||||
$this->em->remove($menu);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function createItem(int $menuId, int $companyId, array $data): WebMenuItem
|
||||
{
|
||||
$menu = $this->menuRepo->findOneByIdAndCompany($menuId, $companyId);
|
||||
if (!$menu) {
|
||||
throw new ResourceException('菜单不存在');
|
||||
}
|
||||
$name = trim((string) ($data['name'] ?? ''));
|
||||
if ($name === '') {
|
||||
throw new ResourceException('菜单项名称不能为空');
|
||||
}
|
||||
$parentId = isset($data['parent_id']) ? (int) $data['parent_id'] : 0;
|
||||
if ($parentId > 0) {
|
||||
$parent = $this->itemRepo->findOneByIdMenuCompany($parentId, $menuId, $companyId);
|
||||
if (!$parent) {
|
||||
throw new ResourceException('父级菜单项不存在');
|
||||
}
|
||||
}
|
||||
$now = new \DateTimeImmutable();
|
||||
$item = new WebMenuItem();
|
||||
$item->setMenuId($menuId);
|
||||
$item->setCompanyId($companyId);
|
||||
$item->setParentId($parentId);
|
||||
$item->setName($name);
|
||||
$imageUrl = $data['image_url'] ?? null;
|
||||
$item->setImageUrl($imageUrl !== null && $imageUrl !== '' ? trim((string) $imageUrl) : null);
|
||||
$item->setLinkType(trim((string) ($data['link_type'] ?? 'url')) ?: 'url');
|
||||
$linkValue = $data['link_value'] ?? null;
|
||||
$item->setLinkValue($linkValue !== null && $linkValue !== '' ? (string) $linkValue : null);
|
||||
$linkExtra = $data['link_extra'] ?? null;
|
||||
$item->setLinkExtra($this->encodeLinkExtra($linkExtra));
|
||||
$item->setSort(isset($data['sort']) ? (int) $data['sort'] : 0);
|
||||
$item->setStatus(isset($data['status']) ? (int) $data['status'] : 1);
|
||||
$item->setCreatedAt($now);
|
||||
$item->setUpdatedAt($now);
|
||||
$this->em->persist($item);
|
||||
$this->em->flush();
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function updateItem(int $itemId, int $menuId, int $companyId, array $data): WebMenuItem
|
||||
{
|
||||
$item = $this->itemRepo->findOneByIdMenuCompany($itemId, $menuId, $companyId);
|
||||
if (!$item) {
|
||||
throw new ResourceException('菜单项不存在');
|
||||
}
|
||||
if (isset($data['parent_id'])) {
|
||||
$parentId = (int) $data['parent_id'];
|
||||
if ($parentId === $itemId) {
|
||||
throw new ResourceException('parent_id 不能指向自身');
|
||||
}
|
||||
if ($parentId > 0) {
|
||||
$parent = $this->itemRepo->findOneByIdMenuCompany($parentId, $menuId, $companyId);
|
||||
if (!$parent) {
|
||||
throw new ResourceException('父级菜单项不存在');
|
||||
}
|
||||
}
|
||||
$item->setParentId($parentId);
|
||||
}
|
||||
if (isset($data['name'])) {
|
||||
$name = trim((string) $data['name']);
|
||||
if ($name === '') {
|
||||
throw new ResourceException('菜单项名称不能为空');
|
||||
}
|
||||
$item->setName($name);
|
||||
}
|
||||
if (array_key_exists('image_url', $data)) {
|
||||
$imageUrl = $data['image_url'];
|
||||
$item->setImageUrl($imageUrl !== null && $imageUrl !== '' ? trim((string) $imageUrl) : null);
|
||||
}
|
||||
if (array_key_exists('link_type', $data)) {
|
||||
$item->setLinkType(trim((string) $data['link_type']) ?: 'url');
|
||||
}
|
||||
if (array_key_exists('link_value', $data)) {
|
||||
$v = $data['link_value'];
|
||||
$item->setLinkValue($v !== null && $v !== '' ? (string) $v : null);
|
||||
}
|
||||
if (array_key_exists('link_extra', $data)) {
|
||||
$item->setLinkExtra($this->encodeLinkExtra($data['link_extra']));
|
||||
}
|
||||
if (isset($data['sort'])) {
|
||||
$item->setSort((int) $data['sort']);
|
||||
}
|
||||
if (isset($data['status'])) {
|
||||
$item->setStatus((int) $data['status']);
|
||||
}
|
||||
$item->setUpdatedAt(new \DateTimeImmutable());
|
||||
$this->em->flush();
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function deleteItem(int $itemId, int $menuId, int $companyId): void
|
||||
{
|
||||
$item = $this->itemRepo->findOneByIdMenuCompany($itemId, $menuId, $companyId);
|
||||
if (!$item) {
|
||||
throw new ResourceException('菜单项不存在');
|
||||
}
|
||||
$all = $this->itemRepo->findAllByMenu($menuId, $companyId);
|
||||
$ids = $this->collectDescendantIds($itemId, $all);
|
||||
foreach ($ids as $id) {
|
||||
$e = $this->itemRepo->find($id);
|
||||
if ($e) {
|
||||
$this->em->remove($e);
|
||||
}
|
||||
}
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebMenuItem[] $allItems
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function collectDescendantIds(int $rootId, array $allItems): array
|
||||
{
|
||||
$byParent = [];
|
||||
foreach ($allItems as $i) {
|
||||
$pid = $i->getParentId();
|
||||
if (!isset($byParent[$pid])) {
|
||||
$byParent[$pid] = [];
|
||||
}
|
||||
$byParent[$pid][] = $i->getId();
|
||||
}
|
||||
$ids = [];
|
||||
$stack = [$rootId];
|
||||
while ($stack) {
|
||||
$id = array_pop($stack);
|
||||
$ids[] = $id;
|
||||
if (!empty($byParent[$id])) {
|
||||
foreach ($byParent[$id] as $childId) {
|
||||
$stack[] = $childId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
private function encodeLinkExtra($linkExtra): ?string
|
||||
{
|
||||
if ($linkExtra === null || $linkExtra === '') {
|
||||
return null;
|
||||
}
|
||||
if (is_string($linkExtra)) {
|
||||
return trim($linkExtra) !== '' ? $linkExtra : null;
|
||||
}
|
||||
if (!is_array($linkExtra) && !is_object($linkExtra)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$encoded = json_encode($linkExtra, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
return $encoded !== false ? $encoded : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{id:int, sort:int}>|array<int|string, int> $sorts
|
||||
*/
|
||||
public function batchUpdateSort(int $menuId, int $companyId, array $sorts): void
|
||||
{
|
||||
$menu = $this->menuRepo->findOneByIdAndCompany($menuId, $companyId);
|
||||
if (!$menu) {
|
||||
throw new ResourceException('菜单不存在');
|
||||
}
|
||||
if ($sorts === []) {
|
||||
return;
|
||||
}
|
||||
$pairs = [];
|
||||
if (isset($sorts[0]) && is_array($sorts[0])) {
|
||||
foreach ($sorts as $row) {
|
||||
if (!isset($row['id'])) {
|
||||
continue;
|
||||
}
|
||||
$pairs[(int) $row['id']] = (int) ($row['sort'] ?? 0);
|
||||
}
|
||||
} else {
|
||||
foreach ($sorts as $id => $sort) {
|
||||
if (is_int($id) || ctype_digit((string) $id)) {
|
||||
$pairs[(int) $id] = (int) $sort;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($pairs as $itemId => $sort) {
|
||||
$item = $this->itemRepo->findOneByIdMenuCompany($itemId, $menuId, $companyId);
|
||||
if ($item) {
|
||||
$item->setSort($sort);
|
||||
$item->setUpdatedAt(new \DateTimeImmutable());
|
||||
}
|
||||
}
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebMenuItem[] $items
|
||||
*
|
||||
* @return array<int, array{item: WebMenuItem, children: array}>
|
||||
*/
|
||||
public function buildTree(array $items): array
|
||||
{
|
||||
$indexed = [];
|
||||
foreach ($items as $item) {
|
||||
$indexed[$item->getId()] = ['item' => $item, 'children' => []];
|
||||
}
|
||||
$roots = [];
|
||||
foreach ($indexed as $id => &$node) {
|
||||
$pid = $node['item']->getParentId();
|
||||
if ($pid === 0) {
|
||||
$roots[] = &$node;
|
||||
} elseif (isset($indexed[$pid])) {
|
||||
$indexed[$pid]['children'][] = &$node;
|
||||
}
|
||||
}
|
||||
unset($node);
|
||||
|
||||
return $roots;
|
||||
}
|
||||
}
|
||||
32
src/ThemeBundle/Transformers/WebMenuItemFlatTransformer.php
Normal file
32
src/ThemeBundle/Transformers/WebMenuItemFlatTransformer.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Transformers;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
use ThemeBundle\Entities\WebMenuItem;
|
||||
|
||||
class WebMenuItemFlatTransformer extends TransformerAbstract
|
||||
{
|
||||
public function transform(WebMenuItem $item): array
|
||||
{
|
||||
$decodedExtra = json_decode((string) $item->getLinkExtra(), true);
|
||||
|
||||
return [
|
||||
'id' => $item->getId(),
|
||||
'menu_id' => $item->getMenuId(),
|
||||
'parent_id' => $item->getParentId(),
|
||||
'name' => $item->getName(),
|
||||
'image_url' => $item->getImageUrl(),
|
||||
'link_type' => $item->getLinkType(),
|
||||
'link_value' => $item->getLinkValue(),
|
||||
'link_extra' => is_array($decodedExtra) ? $decodedExtra : [],
|
||||
'sort' => $item->getSort(),
|
||||
'status' => $item->getStatus(),
|
||||
'created_at' => $item->getCreatedAt() ? $item->getCreatedAt()->format('Y-m-d H:i:s') : null,
|
||||
'updated_at' => $item->getUpdatedAt() ? $item->getUpdatedAt()->format('Y-m-d H:i:s') : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
66
src/ThemeBundle/Transformers/WebMenuItemTransformer.php
Normal file
66
src/ThemeBundle/Transformers/WebMenuItemTransformer.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Transformers;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
use ThemeBundle\Entities\WebMenuItem;
|
||||
|
||||
class WebMenuItemTransformer extends TransformerAbstract
|
||||
{
|
||||
/**
|
||||
* @param array{item: \ThemeBundle\Entities\WebMenuItem, children: array<int, mixed>} $node
|
||||
*/
|
||||
public function transform(array $node): array
|
||||
{
|
||||
$item = $node['item'];
|
||||
$children = $node['children'] ?? [];
|
||||
|
||||
return [
|
||||
'id' => $item->getId(),
|
||||
'parent_id' => $item->getParentId(),
|
||||
'name' => $item->getName(),
|
||||
'image_url' => $item->getImageUrl(),
|
||||
'link_type' => $item->getLinkType(),
|
||||
'link_value' => $item->getLinkValue(),
|
||||
'link_extra' => $this->decodeLinkExtra($item),
|
||||
'sort' => $item->getSort(),
|
||||
'status' => $item->getStatus(),
|
||||
'children' => array_map([$this, 'transform'], $children),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{item: \ThemeBundle\Entities\WebMenuItem, children: array<int, mixed>} $node
|
||||
*/
|
||||
public function transformFront(array $node): array
|
||||
{
|
||||
$item = $node['item'];
|
||||
$children = $node['children'] ?? [];
|
||||
|
||||
return [
|
||||
'id' => $item->getId(),
|
||||
'name' => $item->getName(),
|
||||
'image_url' => $item->getImageUrl(),
|
||||
'link_type' => $item->getLinkType(),
|
||||
'link_value' => $item->getLinkValue(),
|
||||
'link_extra' => $this->decodeLinkExtra($item),
|
||||
'sort' => $item->getSort(),
|
||||
'children' => array_map([$this, 'transformFront'], $children),
|
||||
];
|
||||
}
|
||||
|
||||
private function decodeLinkExtra(WebMenuItem $item): array
|
||||
{
|
||||
$raw = $item->getLinkExtra();
|
||||
if (!$raw) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($raw, true);
|
||||
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
}
|
||||
24
src/ThemeBundle/Transformers/WebMenuTransformer.php
Normal file
24
src/ThemeBundle/Transformers/WebMenuTransformer.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*/
|
||||
|
||||
namespace ThemeBundle\Transformers;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
use ThemeBundle\Entities\WebMenu;
|
||||
|
||||
class WebMenuTransformer extends TransformerAbstract
|
||||
{
|
||||
public function transform(WebMenu $menu): array
|
||||
{
|
||||
return [
|
||||
'id' => $menu->getId(),
|
||||
'name' => $menu->getName(),
|
||||
'key' => $menu->getKey(),
|
||||
'status' => $menu->getStatus(),
|
||||
'created_at' => $menu->getCreatedAt() ? $menu->getCreatedAt()->format('Y-m-d H:i:s') : null,
|
||||
'updated_at' => $menu->getUpdatedAt() ? $menu->getUpdatedAt()->format('Y-m-d H:i:s') : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2019-2026 ShopeX
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
namespace ThirdPartyBundle\Services\AppsCenter;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Dingo\Api\Exception\ResourceException;
|
||||
|
||||
class AppsCenterService
|
||||
{
|
||||
public function fetchEmbedGoods($channel = null)
|
||||
{
|
||||
$channel = $channel ?: config('common.appcenter_channel', 'ecshopx');
|
||||
$baseUrl = rtrim(config('common.appcenter_base_url'), '/');
|
||||
$url = $baseUrl . '/appcenter/open/embed/' . rawurlencode($channel) . '/goods';
|
||||
|
||||
$client = new Client(['timeout' => 15, 'http_errors' => false]);
|
||||
$response = $client->get($url);
|
||||
$body = json_decode((string) $response->getBody(), true);
|
||||
if (!is_array($body)) {
|
||||
throw new ResourceException('获取应用中心商品失败');
|
||||
}
|
||||
if (($body['status'] ?? '') !== 'succ') {
|
||||
throw new ResourceException($body['error'] ?: '获取应用中心商品失败');
|
||||
}
|
||||
|
||||
return $body['data'] ?? [];
|
||||
}
|
||||
|
||||
public function buildAppcenterUrl(array $params, $token)
|
||||
{
|
||||
$channel = $params['channel'];
|
||||
$baseUrl = rtrim(config('common.appcenter_base_url'), '/');
|
||||
$base = $baseUrl . '/appcenter/embed/' . rawurlencode($channel);
|
||||
|
||||
$query = [
|
||||
'shopexid' => (string) $params['shopexid'],
|
||||
'sys_node_id' => (string) $params['sys_node_id'],
|
||||
'callback' => (string) $params['callback'],
|
||||
'embed' => '1',
|
||||
'nonce' => bin2hex(random_bytes(8)),
|
||||
'timestamp' => (string) time(),
|
||||
];
|
||||
|
||||
$query['sign'] = $this->signAppcenterParams($channel, $query, $token);
|
||||
|
||||
return $base . '?' . http_build_query($query);
|
||||
}
|
||||
|
||||
public function signAppcenterParams($channel, array $query, $token)
|
||||
{
|
||||
$signData = $query;
|
||||
$signData['channel'] = $channel;
|
||||
unset($signData['sign']);
|
||||
ksort($signData, SORT_STRING);
|
||||
|
||||
$lines = [];
|
||||
foreach ($signData as $key => $value) {
|
||||
$lines[] = $key . '=' . rawurlencode((string) $value);
|
||||
}
|
||||
|
||||
$canonicalString = implode("\n", $lines);
|
||||
|
||||
return hash_hmac('sha256', $canonicalString, $token);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user