chore: 添加静态资源文件和版本配置文件

添加大量图片、字体、音频等静态资源文件,包括用户界面图片、模板文件和安装界面素材
添加系统版本配置文件(.version)和常量定义文件(.constant)
更新供应商依赖文件,包括Symfony VarDumper组件和相关许可证文件
添加验证码配置文件(captcha.php)和.gitignore规则
This commit is contained in:
2026-03-06 20:14:23 +08:00
commit c9a98f342c
4485 changed files with 490986 additions and 0 deletions

View File

@@ -0,0 +1,508 @@
<?php
namespace app\services\system;
use app\services\BaseServices;
use app\services\product\product\StoreProductServices;
use app\services\product\sku\StoreProductAttrValueServices;
use app\services\serve\ServeServices;
class AiServices extends BaseServices
{
/**
* @var ServeServices
*/
protected ServeServices $services;
/**
* 构造方法
* @param ServeServices $serveServices 服务实例
*/
public function __construct(ServeServices $serveServices)
{
$this->services = $serveServices;
}
/**
* AI类型分发处理
* @param array $data 请求数据
* @return array|string
*/
public function AiType($data)
{
switch ($data['type']) {
case 'product_name':
$result = $this->productName($data);
break;
case 'product_info':
$result = $this->productInfo($data);
break;
case 'share_content':
$result = $this->shareContent($data);
break;
case 'product_attr':
$result = $this->productAttr($data);
break;
case 'product_specs':
$result = $this->productSpecs($data);
break;
case 'product_reply':
$result = $this->productReply($data);
break;
case 'community':
$result = $this->community($data);
break;
case 'community_title':
$result = $this->communityTitle($data);
break;
case 'community_content':
$result = $this->communityContent($data);
break;
case 'article_title':
$result = $this->articleTitle($data);
break;
case 'article_content':
$result = $this->articleContent($data);
break;
default:
return '暂不支持该类型';
}
return $result;
}
/**
* AI生成商品名称
* @param array $data 请求数据
* @return array
*/
public function productName($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 生成3条差异化标题,每条30-50字,符合电商平台SEO规则
• 必须包含:核心关键词(前10字)+核心卖点(材质/功能)+附加价值(促销/场景/人群)
• 禁止:重复词、违禁词、模糊描述(如“优质”“多功能”)
2. 优化维度:
▶ 标题1(流量型):关键词堆砌+高频搜索词,例:
“2024新款男士运动鞋透气防滑跑步鞋轻便百搭男鞋网面休闲鞋耐磨减震”
▶ 标题2(转化型):强促销+紧迫感,例:
“【限时5折】男士运动鞋春秋季飞织透气跑步鞋学生潮流男鞋次日达退货运费险”
▶ 标题3(场景型):痛点解决方案,例:
“健身房必备!男士爆款运动鞋防滑减震跑步鞋宽脚掌透气训练鞋久穿不累”
3. 输出格式:仅返回JSON,键名固定为t1/t2/t3(节省字符):
{
"t1": "标题1(关键词+功能)",
"t2": "标题2(促销+服务)",
"t3": "标题3(场景+痛点)"
}
4. 特殊要求:
• 若标题含促销,需标注具体力度(如“7折”而非“促销中”)
• 人群定向需明确(如“中老年”“宝妈”而非“通用”)
• 材质/规格必须具体(如“新疆棉”“20000mAh”)
';
$userContent = $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return array_values(json_decode($result, true));
}
/**
* AI生成商品简介
* @param array $data 请求数据
* @return array
*/
public function productInfo($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 生成3条差异化商品简介,每条不少于80字,符合电商平台SEO规则
• 必须包含:核心关键词(前10字)+核心卖点(材质/功能)+附加价值(促销/场景/人群)
• 禁止:重复词、违禁词、模糊描述(如“优质”“多功能”)
2. 优化维度:
▶ 标题1(流量型):关键词堆砌+高频搜索词,例:
“2024新款男士运动鞋透气防滑跑步鞋轻便百搭男鞋网面休闲鞋耐磨减震”
▶ 标题2(转化型):强促销+紧迫感,例:
“【限时5折】男士运动鞋春秋季飞织透气跑步鞋学生潮流男鞋次日达退货运费险”
▶ 标题3(场景型):痛点解决方案,例:
“健身房必备!男士爆款运动鞋防滑减震跑步鞋宽脚掌透气训练鞋久穿不累”
3. 输出格式:仅返回JSON,键名固定为t1/t2/t3(节省字符):
{
"t1": "标题1(关键词+功能)",
"t2": "标题2(促销+服务)",
"t3": "标题3(场景+痛点)"
}
4. 特殊要求:
• 若标题含促销,需标注具体力度(如“7折”而非“促销中”)
• 人群定向需明确(如“中老年”“宝妈”而非“通用”)
• 材质/规格必须具体(如“新疆棉”“20000mAh”)
';
$userContent = $data['store_name'] . $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return array_values(json_decode($result, true));
}
/**
* AI生成推广文案
* @param array $data 请求数据
* @return array
*/
public function shareContent($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 生成3条差异化推广文案,每条不少于100字,符合各平台投放规范
• 必须包含:核心卖点(前15字)+用户痛点+行动号召
• 禁止:虚假宣传、绝对化用语(如"最佳""第一")、模糊表述
2. 优化维度:
▶ 文案1(痛点刺激型):直击用户痛点+解决方案,例:
"跑步膝盖疼?新一代缓震科技运动鞋,采用XX减震材料,降低关节冲击力30%,立即体验→"
▶ 文案2(场景共鸣型):构建使用场景+情感共鸣,例:
"加班到深夜的你值得更好!人体工学办公椅,8小时久坐不腰酸,今日下单享免费安装→"
▶ 文案3(数据实证型):量化效果+权威背书,例:
"92%用户回购的爆款面膜!中科院认证XX成分,实测28天淡化细纹45%,限时买二送一→"
3. 输出格式:仅返回JSON,键名固定为c1/c2/c3:
{
"c1": "文案1(痛点型)",
"c2": "文案2(场景型)",
"c3": "文案3(数据型)"
}
4. 特殊要求:
• 痛点描述需具体(如"久坐腰酸"而非"不舒服")
• 数据必须真实可查(如"实验室实测""用户调研数据")
• 行动号召明确(如"立即咨询""限时特惠"需标注截止时间)
';
$userContent = $data['store_name'] . $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return array_values(json_decode($result, true));
}
/**
* AI生成商品规格属性
* @param array $data 请求数据
* @return array
*/
public function productAttr($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 自动识别商品类目并生成完整规格参数
• 参数值必须为具体可选项(非范围值)
• 包含:基础参数+技术参数+功能参数+扩展参数
• 最多三个规格类型,每个规格最多10个值
2. 输出格式:仅返回JSON,键名固定为value/detail:
{
"rule_name": "商品类目"
"spec": [
{
"value": "规格名称1",
"detail": [
"规格值1-1",
"规格值1-2"
]
},
{
"value": "规格名称2",
"detail": [
"规格值2-1",
"规格值2-2"
]
},
...
]
}
';
$userContent = $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return json_decode($result, true);
}
/**
* AI生成商品参数模板
* @param array $data 请求数据
* @return array
*/
public function productSpecs($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 自动识别商品类目并生成完整规格参数
• 参数值必须为具体可选项(非范围值)
• 包含:基础参数+技术参数+功能参数+扩展参数
• 生成最少5个specs参数,name和value都是字符串,sort为0
2. 输出格式:仅返回JSON:
{
"name": "参数模板名称",
"specs": [
{
"name": "参数名称1",
"value": "参数值1",
"sort": 0,
},
{
"name": "参数名称2",
"value": "参数值2",
"sort": 0,
}
...
]
}
';
$userContent = $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return json_decode($result, true);
}
/**
* AI生成商品评价
* @param array $data 请求数据
* @return array
*/
public function productReply($data)
{
$productInfo = app()->make(StoreProductServices::class)->get($data['product_id']);
$sku = app()->make(StoreProductAttrValueServices::class)->value(['product_id' => $data['product_id'], 'unique' => $data['unique']], 'suk');
$date = date('Y-m-d H:i:s');
$systemContent = '
请严格按此规范执行:
1. 输出格式:仅返回JSON,一条数据也按照多条数据的格式返回:
{
[
"product_id": ' . $data['product_id'] . ',
"unique": ' . $data['unique'] . ',
"store_name": ' . $productInfo['store_name'] . ',
"product_image": ' . $productInfo['image'] . ',
"delivery_score": 5,
"service_score": 5,
"product_score": 5,
"reply_score": "3",
"add_time": ' . $date . ',
"nickname": "随机生成的用户名",
"avatar": "在https://pro.crmeb.net/uploads/thumb_water/0e41012b7d7df9404ca456df1c7625c6.png,https://pro.crmeb.net/uploads/thumb_water/b0c0dc1d88c00870e16a2e21ca53b71a.png,https://pro.crmeb.net/uploads/thumb_water/037bb2585a07753bf9115d7f16bf5fe3.png,https://pro.crmeb.net/uploads/thumb_water/34dad9517ed6fcc7fdd4c035abad098c.png中随机选择一个",
"comment": "评价内容"
]
...
}
2. 核心指令:
• 根据提示词生成10条差异化好评,每条不少于80字
• 必须包含:使用体验+具体优点+场景描述
• 禁止:重复内容、虚假评价、模糊表述
• 评价内容生成到comment字段,用户随机昵称生成到nickname字段,其余固定
• 生成3条评价
3. 评价维度(根据提示词自动分配):
• 质量评价(占比40%):材质/做工/耐用性
• 功能评价(占比30%):实际使用效果
• 服务评价(占比20%):物流/包装/售后
• 场景评价(占比10%):特定使用场景体验
4. 特殊要求:
• 优先体现提示词相关内容
• 使用真实体验式口吻(如"收到后使用了半个月...")
• 包含具体细节(如"缝线很密实""按键反应灵敏")
• 每条评价需有明确侧重点
';
$userContent = '商品名称:' . $data['store_name'] . ',商品规格:' . $sku . ',提示词:' . $data['message'] . ', 评价维度:质量、功能、服务、场景';
$result = $this->services->ai()->chat($systemContent, $userContent);
var_dump($result);
return json_decode($result, true);
}
/**
* AI生成社区内容
* @param array $data 请求数据
* @return array
*/
public function community($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 生成3条内容
• title标题要准确概括核心内容,不超过20个汉字,避免夸张表述
• content内容要包含核心内容,内容的类型自适应(描述/心得/观点等),并且符合各平台规范,不少于200字,避免重复内容
• topic_name话题名称要和内容相关,话题名称不超过10个汉字
• 禁止使用网络流行语和过度口语化表达
• 保持语言自然流畅,符合中文表达习惯
2. 内容要求:
▶ 标题规范:
• 准确概括核心内容
• 不超过20个汉字
• 避免夸张表述
▶ 正文规范:
• 首段:自然引入主题(50-80字)
• 主体:根据内容类型自由展开
• 结尾:恰当收束(30-50字)
3. 输出格式:仅返回JSON:
{
[
"title": "生成标题",
"content": "生成正文内容",
"topic_name": "话题名称",
]
...
}
4. 特殊要求:
• 描述类:需包含五感细节(视觉/听觉/触觉等)
• 心得类:需包含真实使用体验
• 观点类:需包含合理逻辑推导
• 禁止使用网络流行语和过度口语化表达
';
$userContent = $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return json_decode($result, true);
}
/**
* AI生成社区标题
* @param array $data 请求数据
* @return array
*/
public function communityTitle($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 生成3条差异化标题,每条不超过20字,需准确概括文章内容
• 必须包含:核心主题(前10字)+内容亮点+情感基调
• 禁止:标题党、无关词、模糊表述(如"精彩""感人")
2. 优化维度:
▶ 标题1(流量型):关键词聚焦+热点结合,例:"三月樱花拍摄指南:手机出片的5个构图技巧"
▶ 标题2(共鸣型):情感触动+场景代入,例:"老城咖啡馆记事:藏在拿铁里的人生百态"
▶ 标题3(悬念型):问题引发+价值提示,例:"为何这间民宿让90%客人续住?丽江庭院设计秘密"
3. 输出格式:仅返回JSON:
{
"t1": "标题1(关键词型)",
"t2": "标题2(情感型)",
"t3": "标题3(悬念型)"
}
4. 特殊要求:
• 情感基调需明确(如"怀旧""治愈"而非"好看")
• 内容亮点需具体(如"手机摄影技巧""庭院设计细节")
• 文化类内容需标注地域/时期(如"唐代服饰""江南民居")
';
$userContent = $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return array_values(json_decode($result, true));
}
/**
* AI优化社区内容
* @param array $data 请求数据
* @return array
*/
public function communityContent($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 生成3条差异化优化版本,每条保持原篇幅±10%
• 每条需体现不同优化方向(结构/语言/情感)
• 禁止:改变核心事实、虚构内容
2. 优化维度:
▶ 版本1(结构优化型):
重组段落逻辑
增强过渡衔接
突出核心论点
▶ 版本2(语言润色型):
提升表达精准度
丰富修辞手法
统一语言风格
▶ 版本3(情感强化型):
深化细节描写
增强场景代入感
把控情感节奏
3. 输出格式:仅返回JSON:
{
"v1": "结构优化版本",
"v2": "语言润色版本",
"v3": "情感强化版本"
}
4. 特殊要求:
• 技术类:专业术语需验证
• 文学类:保留原修辞特色
• 商业类:符合广告法规定
';
$userContent = $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return array_values(json_decode($result, true));
}
/**
* AI生成文章标题
* @param array $data 请求数据
* @return array
*/
public function articleTitle($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 生成3条差异化标题,每条不超过20字,需准确概括文章内容
• 必须包含:核心主题(前10字)+内容亮点+情感基调
• 禁止:标题党、无关词、模糊表述(如"精彩""感人")
2. 优化维度:
▶ 标题1(流量型):关键词聚焦+热点结合,例:"三月樱花拍摄指南:手机出片的5个构图技巧"
▶ 标题2(共鸣型):情感触动+场景代入,例:"老城咖啡馆记事:藏在拿铁里的人生百态"
▶ 标题3(悬念型):问题引发+价值提示,例:"为何这间民宿让90%客人续住?丽江庭院设计秘密"
3. 输出格式:仅返回JSON:
{
"t1": "标题1(关键词型)",
"t2": "标题2(情感型)",
"t3": "标题3(悬念型)"
}
4. 特殊要求:
• 情感基调需明确(如"怀旧""治愈"而非"好看")
• 内容亮点需具体(如"手机摄影技巧""庭院设计细节")
• 文化类内容需标注地域/时期(如"唐代服饰""江南民居")
';
$userContent = $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return array_values(json_decode($result, true));
}
/**
* AI优化文章内容
* @param array $data 请求数据
* @return array
*/
public function articleContent($data)
{
$systemContent = '
请严格按此规范执行:
1. 核心指令:
• 生成3条差异化优化版本,每条保持原篇幅±10%
• 每条需体现不同优化方向(结构/语言/情感)
• 禁止:改变核心事实、虚构内容
2. 优化维度:
▶ 版本1(结构优化型):
重组段落逻辑
增强过渡衔接
突出核心论点
▶ 版本2(语言润色型):
提升表达精准度
丰富修辞手法
统一语言风格
▶ 版本3(情感强化型):
深化细节描写
增强场景代入感
把控情感节奏
3. 输出格式:仅返回JSON:
{
"v1": "结构优化版本",
"v2": "语言润色版本",
"v3": "情感强化版本"
}
4. 特殊要求:
• 技术类:专业术语需验证
• 文学类:保留原修辞特色
• 商业类:符合广告法规定
';
$userContent = $data['message'];
$result = $this->services->ai()->chat($systemContent, $userContent);
return array_values(json_decode($result, true));
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace app\services\system;
use app\dao\system\AppVersionDao;
use app\services\BaseServices;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\facade\Route as Url;
class AppVersionServices extends BaseServices
{
/**
* @var AppVersionDao
*/
#[Inject]
protected AppVersionDao $dao;
/**
* 获取APP版本列表
* @param array $where 查询条件
* @return array
*/
public function versionList($where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->versionList($where, $page, $limit);
foreach ($list as &$item) {
$item['add_time'] = date('Y-m-d H:i:s', $item['add_time']);
}
$count = $this->dao->versionCount($where);
return compact('list', 'count');
}
/**
* 创建版本表单
* @param int $id 版本ID
* @return array
*/
public function createForm($id = 0)
{
if ($id) {
$info = $this->dao->get($id);
}
$field[] = Form::hidden('id', $info['id'] ?? 0);
$field[] = Form::input('version', '版本号:', $info['version'] ?? '')->col(24);
$field[] = Form::radio('platform', '平台类型:', $info['platform'] ?? 1)->options([['label' => 'Android', 'value' => 1], ['label' => 'IOS', 'value' => 2]]);
$field[] = Form::input('info', '版本介绍:', $info['info'] ?? '')->type('textarea');
$field[] = Form::input('url', '下载链接:', $info['url'] ?? '')->appendRule('suffix', [
'type' => 'div',
'class' => 'tips-info',
'domProps' => ['innerHTML' => '填写下载链接,Android的为压缩包的url地址,点击升级会自动下载压缩包替换安装,例如:域名/xxx.zip;IOS的为应用商店链接地址,直接跳转AppStore,例如:itms-apps://itunes.apple.com/cn/app/id1234567890']
]);
$field[] = Form::radio('is_force', '强制:', $info['is_force'] ?? 1)->options([['label' => '开启', 'value' => 1], ['label' => '关闭', 'value' => 0]]);
$field[] = Form::radio('is_new', '是否最新:', $info['is_new'] ?? 1)->options([['label' => '是', 'value' => 1], ['label' => '否', 'value' => 0]]);
return create_form('添加版本信息', $field, Url::buildUrl('/app_version/version_save'), 'POST');
}
/**
* 保存版本信息
* @param int $id 版本ID
* @param array $data 版本数据
* @return bool|mixed
*/
public function versionSave($id, $data)
{
if ($id) {
return $this->transaction(function () use ($data, $id) {
if ($data['is_new']) {
$this->dao->update(['platform' => $data['platform']], ['is_new' => 0]);
}
return $this->dao->update($id, $data);
});
} else {
$data['is_del'] = 0;
$data['add_time'] = time();
return $this->transaction(function () use ($data) {
$this->dao->update(['platform' => $data['platform']], ['is_new' => 0]);
return $this->dao->save($data);
});
}
}
/**
* 获取最新版本信息
* @param int $platform 平台类型(1:Android 2:IOS)
* @return array
*/
public function getNewInfo($platform)
{
$res = $this->dao->get(['platform' => $platform, 'is_new' => 1]);
if ($res) {
$res = $res->toArray();
$res['time'] = date('Y-m-d H:i:s', $res['add_time']);
return $res;
} else {
return [];
}
}
}

View File

@@ -0,0 +1,188 @@
<?php
namespace app\services\system;
use app\dao\system\CapitalFlowDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
/**
* 资金流水
* Class SystemFileServices
* @package app\services\system
* @mixin CapitalFlowDao
*/
class CapitalFlowServices extends BaseServices
{
protected array $typeName = [
1 => '商城购物',
2 => '商城购物退款',
3 => '用户充值',
4 => '用户充值退款',
5 => '抽奖中奖',
6 => '佣金提现',
7 => '购买会员',
8 => '线下支付',
];
/**
* @var CapitalFlowDao
*/
#[Inject]
protected CapitalFlowDao $dao;
/**
* 添加资金流水
* @param $orderInfo
* @param string $type
*/
public function setFlow($data, $type = '')
{
$data['flow_id'] = 'ZJ' . date('Ymdhis', time()) . rand('1000', '9999');
switch ($type) {
case 'order':
$data['trading_type'] = 1;
break;
case 'refund':
$data['price'] = bcmul('-1', $data['price'], 2);
$data['trading_type'] = 2;
break;
case 'recharge':
$data['trading_type'] = 3;
break;
case 'refund_recharge':
$data['price'] = bcmul('-1', $data['price'], 2);
$data['trading_type'] = 4;
break;
case 'luck':
$data['price'] = bcmul('-1', $data['price'], 2);
$data['trading_type'] = 5;
break;
case 'extract':
$data['price'] = bcmul('-1', $data['price'], 2);
$data['trading_type'] = 6;
break;
case 'pay_member':
$data['trading_type'] = 7;
break;
case 'offline_scan':
$data['trading_type'] = 8;
break;
default:
break;
}
$data['add_time'] = time();
$this->dao->save($data);
}
/**
* 获取资金流水
* @param $where
* @return array
*/
public function getFlowList($where)
{
$export = $where['export'] ?? 0;
unset($where['export']);
[$page, $limit] = $this->getPageValue();
$status = ['支付订单', '订单退款', '充值订单', '充值退款', '抽奖红包', '佣金提现', '购买会员', '线下收银'];
$pay_type = ['weixin' => '微信支付', 'routine' => '小程序', 'alipay' => '支付宝', 'offline' => '线下支付', 'bank' => '银行'];
$list = $this->dao->getList($where, '*', $page, $limit);
foreach ($list as &$item) {
$item['add_time'] = date('Y-m-d H:i:s', $item['add_time']);
$item['trading_type'] = $status[$item['trading_type']];
$item['pay_type'] = $pay_type[$item['pay_type']] ?? '';
}
$count = $this->dao->count($where);
if ($export) {
$fileKey = ['flow_id', 'order_id', 'nickname', 'phone', 'price', 'trading_type', 'pay_type', 'add_time', 'mark'];
$header = ['交易单号', '关联订单', '用户', '电话', '金额', '订单类型', '支付类型', '交易时间', '备注'];
$fileName = '账单导出' . date('YmdHis') . rand(1000, 9999);
return compact('list', 'fileKey', 'header', 'fileName');
} else {
return compact('list', 'count', 'status');
}
}
/**
* 添加备注
* @param $id
* @param $data
* @return bool
*/
public function setMark($id, $data)
{
$res = $this->dao->update($id, $data);
if ($res) {
return true;
} else {
throw new AdminException('备注失败');
}
}
/**
* 获取账单记录
* @param $where
* @return array
*/
public function getFlowRecord($where)
{
[$page, $limit] = $this->getPageValue();
$data = $this->dao->getRecordList($where, $page, $limit);
$i = 1;
foreach ($data['list'] as &$item) {
$item['id'] = $i;
$i++;
$item['entry_price'] = bcadd($item['income_price'], $item['exp_price'], 2);
switch ($where['type']) {
case "day" :
$item['title'] = "日账单";
$item['add_time'] = date('Y-m-d', $item['add_time']);
break;
case "week" :
$item['title'] = "周账单";
$item['add_time'] = '第' . $item['day'] . '周(' . date('m', $item['add_time']) . '月)';
break;
case "month" :
$item['title'] = "月账单";
$item['add_time'] = date('Y-m', $item['add_time']);
break;
}
}
return $data;
}
/**
* 用户资金记录
* @param int $uid
* @param array $data
* @return array
*/
public function userCapitalList(int $uid, array $data)
{
$where = [];
$where['uid'] = $uid;
$where['trading_type'] = [1, 7];
if ((isset($data['start']) && $data['start']) || (isset($data['stop']) && $data['stop'])) {
$where['time'] = [$data['start'], $data['stop']];
}
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, '*', $page, $limit);
$times = [];
if ($list) {
$typeName = $this->typeName;
foreach ($list as &$item) {
$item['time_key'] = $item['add_time'] ? date('Y-m', (int)$item['add_time']) : '';
$item['day'] = $item['add_time'] ? date('Y-m-d', (int)$item['add_time']) : '';
$item['add_time'] = $item['add_time'] ? date('Y/m/d H:i', (int)$item['add_time']) : '';
$item['type'] = $item['trading_type'];
$item['type_name'] = $typeName[$item['type'] ?? ''] ?? '未知类型';
$item['title'] = $item['type_name'];
}
$times = array_merge(array_unique(array_column($list, 'time_key')));
}
return ['list' => $list, 'time' => $times];
}
}

View File

@@ -0,0 +1,481 @@
<?php
namespace app\services\system;
use app\dao\system\PrintDocumentDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use crmeb\services\printer\Printer;
use think\annotation\Inject;
class PrintDocumentServices extends BaseServices
{
/**
* @var PrintDocumentDao
*/
#[Inject]
protected PrintDocumentDao $dao;
/**
* 小票打印机列表
* @param $where
* @return array
* @author wuhaotian
* @email 442384644@qq.com
* @date 2024/8/21
*/
public function printList($where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->printList($where, $page, $limit);
foreach ($list as &$item) {
$item['add_time'] = date('Y-m-d H:i:s', $item['add_time']);
}
$count = $this->dao->printCount($where);
return compact('list', 'count');
}
/**
* 小票打印机表单
* @param $id
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author wuhaotian
* @email 442384644@qq.com
* @date 2024/8/21
*/
public function printForm($id, $supplier_id = 0)
{
$info = $this->dao->get($id) ?? [];
if ($info) $info = $info->toArray();
$field[] = Form::input('print_name', '打印机名称:', $info['print_name'] ?? '')->required('请输入打印机名称')->info('打印机名称');
$field[] = Form::radio('type', '平台选择:', $info['type'] ?? 1)
->options([['label' => '易联云', 'value' => 1], ['label' => '飞鹅云', 'value' => 2]])
->appendControl(1, [
Form::input('yly_user_id', '用户ID:', $info['yly_user_id'] ?? '')->required('请输入用户ID')->info('易联云开发者ID'),
Form::input('yly_app_id', '应用ID:', $info['yly_app_id'] ?? '')->required('请输入应用ID')->info('易联应用ID'),
Form::input('yly_app_secret', '应用密钥:', $info['yly_app_secret'] ?? '')->required('请输入应用密钥')->info('易联应用密钥'),
Form::input('yly_sn', '终端号', $info['yly_sn'] ?? '')->required('请输入终端号')->info('易联云打印机终端号,打印机型号:易联云打印机 K4无线版'),
]
)->appendControl(2, [
Form::input('fey_user', '飞鹅云USER:', $info['fey_user'] ?? '')->required('请输入飞鹅云USER')->info('飞鹅云后台注册账号'),
Form::input('fey_ukey', '飞鹅云UYEK:', $info['fey_ukey'] ?? '')->required('请输入飞鹅云UYEK')->info('飞鹅云后台注册账号后生成的UKEY 【备注:这不是填打印机的KEY】'),
Form::input('fey_sn', '飞鹅云SN:', $info['fey_sn'] ?? '')->required('请输入飞鹅云SN')->info('打印机标签上的编号,必须要在管理后台里添加打印机或调用API接口添加之后,才能调用API'),
]
);
$field[] = Form::number('times', '打印联数:', $info['times'] ?? 1)->min(0)->required('请输入打印联数')->info('打印机单次打印张数');
$field[] = Form::radio('print_type', '打印时机:', $info['print_type'] ?? 1)->options([['label' => '支付后打印', 'value' => 1], ['label' => '下单后打印', 'value' => 2]]);
$field[] = Form::switches('status', '打印开关:', $info['status'] ?? 1)->falseValue(0)->trueValue(1)->openStr('打开')->closeStr('关闭')->size('large');
return create_form('小票打印机', $field, $this->url('/print/save/' . $id), 'POST');
}
/**
* 保存小票打印机
* @param $id
* @param $data
* @return bool
* @author wuhaotian
* @email 442384644@qq.com
* @date 2024/8/21
*/
public function printSave($id, $data)
{
if ($id) {
$this->dao->update($id, $data);
} else {
$data['add_time'] = time();
$this->dao->save($data);
}
return true;
}
/**
* 修改打印机状态
* @param $id
* @param $status
* @return bool
* @author wuhaotian
* @email 442384644@qq.com
* @date 2024/8/21
*/
public function printSetStatus($id, $status)
{
$this->dao->update($id, ['status' => $status]);
return true;
}
/**
* 删除打印机
* @param $id
* @return bool
* @author wuhaotian
* @email 442384644@qq.com
* @date 2024/8/21
*/
public function printDel($id)
{
$this->dao->update($id, ['is_del' => 1]);
return true;
}
/**
* 获取打印内容配置
* @param int $id 打印机ID
* @return array
*/
public function getPrintContent($id)
{
$print_content = $this->dao->value(['id' => $id], 'print_content');
return $print_content ? json_decode($print_content, true) : [];
}
/**
* 保存打印内容配置
* @param int $id 打印机ID
* @param array $data 打印内容配置
* @return bool
*/
public function savePrintContent($id, $data)
{
$print_content = json_encode($data);
$this->dao->update($id, ['print_content' => $print_content]);
return true;
}
/**
* 开始打印订单小票
* @param array $order 订单信息
* @param array $product 商品信息
* @param int $print_type 打印时机(1:支付后 2:下单后)
* @param int $supplier_id 供应商ID
* @return void
* @throws AdminException
*/
public function startPrint($order, $product, $print_type = 1, $supplier_id = 0)
{
$list = $this->dao->printList(['status' => 1, 'print_type' => $print_type, 'supplier_id' => $supplier_id]);
foreach ($list as $item) {
if ($item['type'] == 1) { //易联云
$name = 'yi_lian_yun';
$configData = [
'partner' => $item['yly_user_id'],
'clientId' => $item['yly_app_id'],
'apiKey' => $item['yly_app_secret'],
'terminal' => $item['yly_sn']
];
$print_content = json_decode($item['print_content'], true);
if (is_null($print_content) || !count($print_content)) throw new AdminException('请先配置打印内容');
$content = $this->ylyContent($print_content, $order, $product, $item['times'], $print_type);
} else { //飞鹅云
$name = 'fei_e_yun';
$configData = [
'feyUser' => $item['fey_user'],
'feyUkey' => $item['fey_ukey'],
'feySn' => $item['fey_sn']
];
$print_content = json_decode($item['print_content'], true);
if (is_null($print_content) || !count($print_content)) throw new AdminException('请先配置打印内容');
$content = $this->feyContent($print_content, $order, $product, $print_type);
}
$printer = new Printer($name, $configData);
$printer->setPrinterContent($content, $item['times'])->startPrinter();
}
}
/**
* 生成易联云打印内容
* @param array $printContent 打印配置
* @param array $orderInfo 订单信息
* @param array $product 商品信息
* @param int $times 打印联数
* @param int $print_type 打印时机
* @return string
*/
public function ylyContent($printContent, $orderInfo, $product, $times, $print_type)
{
$goodsStr = '<table><tr><td>名称</td><td>单价</td><td>数量</td><td>金额</td></tr>';
foreach ($product as $item) {
$goodsStr .= '<tr><td><FH2><FW2>----------------</FW2></FH2></td></tr>';
$goodsStr .= '<tr>';
$price = $item['sum_price'];
$num = $item['cart_num'];
$prices = bcmul((string)$item['cart_num'], (string)$item['sum_price'], 2);
$goodsStr .= "<td>{$item['productInfo']['store_name']} | {$item['productInfo']['attrInfo']['suk']}</td><td>{$price}</td><td>{$num}</td><td>{$prices}</td>";
$goodsStr .= '</tr>';
if (in_array(1, $printContent['goods'])) {
$goodsStr .= '<tr>';
$goodsStr .= "<td>规格编码:{$item['productInfo']['attrInfo']['code']}</td>";
$goodsStr .= '</tr>';
}
unset($price, $num, $prices);
}
$goodsStr .= '</table>';
$total_price = bcadd($orderInfo['total_price'], $orderInfo['pay_postage'], 2);
$addTime = date('Y-m-d H:i:s', $orderInfo['add_time']);
$payTime = isset($orderInfo['pay_time']) ? date('Y-m-d H:i:s', $orderInfo['pay_time']) : '';
$printTime = date('Y-m-d H:i:s', time());
$content = '';
$content .= '<MN>' . $times . '</MN>';
if ($printContent['header']) {
$content .= '<FS2><center>' . sys_config('site_name') . '</center></FS2>';
$content .= '<FH2><FW2>----------------</FW2></FH2>';
}
if ($printContent['delivery']) {
if ($orderInfo['shipping_type'] == 1) {
$content .= "配送方式:商家配送 \r";
} else {
$content .= "配送方式:门店自提 \r";
}
$content .= '客户姓名: ' . $orderInfo['real_name'] . " \r";
$content .= '客户电话: ' . $orderInfo['user_phone'] . " \r";
if ($orderInfo['shipping_type'] == 1) $content .= '收货地址: ' . $orderInfo['user_address'] . " \r";
$content .= '<FH2><FW2>----------------</FW2></FH2>';
}
if ($printContent['buyer_remarks']) {
$content .= '买家备注: ' . $orderInfo['mark'] . " \r";
$content .= '<FH2><FW2>----------------</FW2></FH2>';
}
if (in_array(0, $printContent['goods'])) {
$content .= '*************商品***************';
$content .= " \r";
$content .= $goodsStr;
$content .= "********************************\r";
$content .= '<FH2><FW2>----------------</FW2></FH2>';
}
if ($printContent['freight']) {
$content .= '<RA>邮费:' . $orderInfo['pay_postage'] . '元</RA>';
$content .= '<RA>合计:' . $total_price . '元</RA>';
$content .= '<FH2><FW2>----------------</FW2></FH2>';
}
if ($printContent['preferential']) {
$discount_price = bcsub(bcadd($orderInfo['total_price'], $orderInfo['pay_postage'], 2), bcadd($orderInfo['deduction_price'], $orderInfo['pay_price'], 2), 2);
$content .= '<RA>优惠:-' . $discount_price . '元</RA>';
$content .= '<RA>抵扣:-' . $orderInfo['deduction_price'] . '元</RA>';
$content .= '<FH2><FW2>----------------</FW2></FH2>';
}
if (in_array(0, $printContent['pay'])) {
if ($print_type == 1) {
$content .= match ($orderInfo['pay_type']) {
'weixin' => '<RA>支付方式:微信支付</RA>',
'alipay' => '<RA>支付方式:支付宝支付</RA>',
'yue' => '<RA>支付方式:余额支付</RA>',
'offline' => '<RA>支付方式:线下支付</RA>',
default => '<RA>支付方式:暂无</RA>',
};
} else {
$content .= '<RA>支付方式:暂无</RA>';
}
}
if (in_array(1, $printContent['pay'])) {
$content .= '<RA>实际支付:' . $orderInfo['pay_price'] . '元</RA>';
}
if (count($printContent['pay'])) {
$content .= '<FH2><FW2>----------------</FW2></FH2>';
}
if (in_array(0, $printContent['order'])) {
$content .= '订单编号:' . $orderInfo['order_id'] . "\r";
}
if (in_array(1, $printContent['order'])) {
$content .= '下单时间:' . $addTime . "\r";
}
if (in_array(2, $printContent['order'])) {
$content .= '支付时间:' . $payTime . "\r";
}
if (in_array(3, $printContent['order'])) {
$content .= '打印时间:' . $printTime . "\r";
}
$content .= '<FH2><FW2>----------------</FW2></FH2>';
if ($printContent['code'] && $printContent['code_url']) {
$content .= '<QR>' . sys_config('site_url') . $printContent['code_url'] . '</QR>';
$content .= " \r";
}
if ($printContent['show_notice']) {
$content .= '<center>' . $printContent['notice_content'] . '</center>';
$content .= " \r";
}
return $content;
}
/**
* 生成飞鹅云打印内容
* @param array $printContent 打印配置
* @param array $orderInfo 订单信息
* @param array $product 商品信息
* @param int $print_type 打印时机
* @return string
*/
public function feyContent($printContent, $orderInfo, $product, $print_type)
{
$printTime = date('Y-m-d H:i:s', time());
$addTime = date('Y-m-d H:i:s', $orderInfo['add_time']);
$payTime = isset($orderInfo['pay_time']) ? date('Y-m-d H:i:s', $orderInfo['pay_time']) : '';
$content = '';
if ($printContent['header']) {
$content .= '<CB>' . sys_config('site_name') . '</CB><BR>';
$content .= '--------------------------------<BR>';
}
if ($printContent['delivery']) {
if ($orderInfo['shipping_type'] == 1) {
$content .= '配送方式:商家配送<BR>';
} else {
$content .= '配送方式:门店自提<BR>';
}
$content .= '客户姓名: ' . $orderInfo['real_name'] . '<BR>';
$content .= '客户电话: ' . $orderInfo['user_phone'] . '<BR>';
if ($orderInfo['shipping_type'] == 1) $content .= '收货地址:' . $orderInfo['user_address'] . '<BR>';
$content .= '--------------------------------<BR>';
}
if ($printContent['buyer_remarks']) {
$content .= '买家备注:' . $orderInfo['mark'] . '<BR>';
$content .= '--------------------------------<BR>';
}
if (in_array(0, $printContent['goods'])) {
$content .= '<BR>';
$content .= '**************商品**************<BR>';
$content .= '<BR>';
$content .= '名称 单价 数量 金额<BR>';
foreach ($product as $item) {
$content .= '--------------------------------<BR>';
$name = $item['productInfo']['store_name'] . " | " . $item['productInfo']['attrInfo']['suk'];
$price = $item['sum_price'];
$num = $item['cart_num'];
$prices = bcmul((string)$item['cart_num'], (string)$item['sum_price'], 2);
$kw3 = '';
$kw1 = '';
$kw2 = '';
$kw4 = '';
$str = $name;
$blankNum = 14;//名称控制为14个字节
$lan = mb_strlen($str, 'utf-8');
$m = 0;
$j = 1;
$blankNum++;
$result = array();
if (strlen($price) < 6) {
$k1 = 6 - strlen($price);
for ($q = 0; $q < $k1; $q++) {
$kw1 .= ' ';
}
$price = $price . $kw1;
}
if (strlen($num) < 3) {
$k2 = 3 - strlen($num);
for ($q = 0; $q < $k2; $q++) {
$kw2 .= ' ';
}
$num = $num . $kw2;
}
if (strlen($prices) < 6) {
$k3 = 6 - strlen($prices);
for ($q = 0; $q < $k3; $q++) {
$kw4 .= ' ';
}
$prices = $prices . $kw4;
}
for ($i = 0; $i < $lan; $i++) {
$new = mb_substr($str, $m, $j, 'utf-8');
$j++;
if (mb_strwidth($new, 'utf-8') < $blankNum) {
if ($m + $j > $lan) {
$m = $m + $j;
$tail = $new;
$lenght = iconv("UTF-8", "GBK//IGNORE", $new);
$k = 14 - strlen($lenght);
for ($q = 0; $q < $k; $q++) {
$kw3 .= ' ';
}
if ($m == $j) {
$tail .= $kw3 . ' ' . $price . ' ' . $num . ' ' . $prices;
} else {
$tail .= $kw3 . '<BR>';
}
break;
} else {
$next_new = mb_substr($str, $m, $j, 'utf-8');
if (mb_strwidth($next_new, 'utf-8') < $blankNum) {
continue;
} else {
$m = $i + 1;
$result[] = $new;
$j = 1;
}
}
}
}
$head = '';
foreach ($result as $key => $value) {
if ($key < 1) {
$v_lenght = iconv("UTF-8", "GBK//IGNORE", $value);
$v_lenght = strlen($v_lenght);
if ($v_lenght == 13) $value = $value . " ";
$head .= $value . ' ' . $price . ' ' . $num . ' ' . $prices;
} else {
$head .= $value . '<BR>';
}
}
$content .= $head . $tail;
if (in_array(1, $printContent['goods'])) {
$content .= '规格编码:' . $item['productInfo']['attrInfo']['code'] . '<BR>';
}
unset($price);
}
$content .= '<BR>';
$content .= '********************************<BR>';
}
if ($printContent['freight']) {
$content .= '<BR>';
$content .= '--------------------------------<BR>';
$content .= '<RIGHT>邮费:' . number_format($orderInfo['pay_postage'], 2) . '元</RIGHT><BR>';
$total_price = bcadd($orderInfo['total_price'], $orderInfo['pay_postage'], 2);
$content .= '<RIGHT>合计:' . number_format($total_price, 2) . '元</RIGHT>';
$content .= '--------------------------------<BR>';
}
if ($printContent['preferential']) {
$discount_price = bcsub(bcadd($orderInfo['total_price'], $orderInfo['pay_postage'], 2), bcadd($orderInfo['deduction_price'], $orderInfo['pay_price'], 2), 2);
$content .= '<RIGHT>优惠:-' . number_format($discount_price, 2) . '元</RIGHT><BR>';
$content .= '<RIGHT>抵扣:-' . number_format($orderInfo['deduction_price'], 2) . '元</RIGHT>';
$content .= '--------------------------------<BR>';
}
if (in_array(0, $printContent['pay'])) {
if ($print_type == 1) {
$content .= match ($orderInfo['pay_type']) {
'weixin' => '<RIGHT>支付方式:微信支付</RIGHT><BR>',
'alipay' => '<RIGHT>支付方式:支付宝支付</RIGHT><BR>',
'yue' => '<RIGHT>支付方式:余额支付</RIGHT><BR>',
'offline' => '<RIGHT>支付方式:线下支付</RIGHT><BR>',
default => '<RIGHT>支付方式:暂无</RIGHT><BR>',
};
} else {
$content .= '<RIGHT>支付方式:暂无</RIGHT><BR>';
}
$content .= '<RIGHT>实际支付:' . number_format($orderInfo['pay_price'], 2) . '元</RIGHT>';
$content .= '--------------------------------<BR>';
}
if (in_array(0, $printContent['order'])) {
$content .= '订单编号:' . $orderInfo['order_id'] . '<BR>';
}
if (in_array(1, $printContent['order'])) {
$content .= '下单时间: ' . $addTime . '<BR>';
}
if (in_array(2, $printContent['order'])) {
$content .= '付款时间: ' . $payTime . '<BR>';
}
if (in_array(3, $printContent['order'])) {
$content .= '打印时间: ' . $printTime . '<BR>';
}
$content .= '--------------------------------<BR>';
$content .= '<BR>';
if ($printContent['code'] && $printContent['code_url']) {
$content .= '<QR>' . sys_config('site_url') . $printContent['code_url'] . '</QR>';
}
if ($printContent['show_notice']) {
$content .= '<C>' . $printContent['notice_content'] . '</C>';
}
return $content;
}
}

View File

@@ -0,0 +1,48 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\HttpService;
/**
* 商业授权
* Class SystemAuthServices
* @package app\services\system
*/
class SystemAuthServices extends BaseServices
{
/**
* 申请授权
* @param array $data
* @param $headerData
* @return bool
*/
public function authApply(array $data, $headerData)
{
$res = HttpService::postRequest('http://authorize.crmeb.net/api/auth_apply', $data, $headerData);
if ($res === false) {
throw new AdminException('申请失败,服务器没有响应!');
}
$res = json_decode($res, true);
if (isset($res['status'])) {
if ($res['status'] == 400) {
throw new AdminException($res['msg'] ?? "申请失败");
} else {
return true;
}
}
}
}

View File

@@ -0,0 +1,173 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system;
use app\services\BaseServices;
use app\services\community\CommunityRelevanceServices;
use app\services\product\product\StoreDescriptionServices;
use app\services\product\product\StoreProductCouponServices;
use app\services\product\product\StoreProductRelationServices;
use app\services\product\product\StoreProductReplyServices;
use app\services\product\product\StoreProductServices;
use app\services\product\sku\StoreProductAttrResultServices;
use app\services\product\sku\StoreProductAttrServices;
use app\services\product\sku\StoreProductAttrValueServices;
use app\services\user\UserRelationServices;
use crmeb\exceptions\AdminException;
use think\facade\Config;
use think\facade\Db;
/**
* 清除数据
* Class SystemClearServices
* @package app\services\system
*/
class SystemClearServices extends BaseServices
{
/**
* 清除表数据
* @param string|array $table_name
* @param $status
*/
public function clearData($table_name, bool $status)
{
$prefix = config('database.connections.' . config('database.default'))['prefix'];
if (is_string($table_name)) {
$clearData = [$table_name];
} else {
$clearData = $table_name;
}
foreach ($clearData as $name) {
if ($status) {
Db::execute('TRUNCATE TABLE ' . $prefix . $name);
} else {
Db::execute('DELETE FROM' . $prefix . $name);
}
}
}
/**
* 递归删除文件,只能删除 public/uploads下的文件
* @param $dirName
* @param bool $subdir
*/
public function delDirAndFile(string $dirName, $subdir = true)
{
if (strstr($dirName, 'public/uploads') === false) {
return true;
}
if ($handle = @opendir("$dirName")) {
while (false !== ($item = readdir($handle))) {
if ($item != "." && $item != "..") {
if (is_dir("$dirName/$item"))
$this->delDirAndFile("$dirName/$item", false);
else
@unlink("$dirName/$item");
}
}
closedir($handle);
if (!$subdir) @rmdir($dirName);
}
}
/**
* 替换域名
* @param string $url
* @return mixed
*/
public function replaceSiteUrl(string $url)
{
$siteUrl = sys_config('site_url');
$siteUrlJosn = str_replace('://', ':\\\/\\\/', $siteUrl);
$valueJosn = str_replace('://', ':\\\/\\\/', $url);
$prefix = Config::get('database.connections.' . Config::get('database.default') . '.prefix');
$sql = [
"UPDATE `{$prefix}system_attachment` SET `att_dir` = replace(att_dir ,'{$siteUrl}','{$url}'),`satt_dir` = replace(satt_dir ,'{$siteUrl}','{$url}')",
"UPDATE `{$prefix}store_product` SET `image` = replace(image ,'{$siteUrl}','{$url}'),`slider_image` = replace(slider_image ,'{$siteUrlJosn}','{$valueJosn}')",
"UPDATE `{$prefix}store_product_attr_value` SET `image` = replace(image ,'{$siteUrl}','{$url}')",
"UPDATE `{$prefix}store_seckill` SET `image` = replace(image ,'{$siteUrl}','{$url}'),`images` = replace(images,'{$siteUrlJosn}','{$valueJosn}')",
"UPDATE `{$prefix}store_combination` SET `image` = replace(image ,'{$siteUrl}','{$url}'),`images` = replace(images,'{$siteUrlJosn}','{$valueJosn}')",
"UPDATE `{$prefix}store_bargain` SET `image` = replace(image ,'{$siteUrl}','{$url}'),`images` = replace(images,'{$siteUrlJosn}','{$valueJosn}')",
"UPDATE `{$prefix}system_config` SET `value` = replace(value ,'{$siteUrlJosn}','{$valueJosn}')",
"UPDATE `{$prefix}article_category` SET `image` = replace(`image` ,'{$siteUrl}','{$url}')",
"UPDATE `{$prefix}article` SET `image_input` = replace(`image_input` ,'{$siteUrl}','{$url}')",
"UPDATE `{$prefix}article_content` SET `content` = replace(`content` ,'{$siteUrl}','{$url}')",
"UPDATE `{$prefix}store_product_category` SET `pic` = replace(`pic` ,'{$siteUrl}','{$url}')",
"UPDATE `{$prefix}system_group_data` SET `value` = replace(value ,'{$siteUrlJosn}','{$valueJosn}')",
"UPDATE `{$prefix}store_product_description` SET `description`= replace(description,'{$siteUrl}','{$url}')"
];
return $this->transaction(function () use ($sql) {
try {
foreach ($sql as $item) {
Db::execute($item);
}
} catch (\Throwable $e) {
throw new AdminException('替换失败,失败原因:' . $e->getMessage());
}
});
}
/**
* 回收站商品彻底删除
* @param array $ids 商品ID数组
* @return bool
*/
public function recycleProduct(array $ids)
{
$product = app()->make(StoreProductServices::class);
//清除规格表数据
/** @var StoreProductAttrServices $ProductAttr */
$productAttr = app()->make(StoreProductAttrServices::class);
$productAttr->delete([['product_id', 'in', $ids], ['type', '=', '0']]);
/** @var StoreProductAttrResultServices $productAttrResult */
$productAttrResult = app()->make(StoreProductAttrResultServices::class);
$productAttrResult->delete([['product_id', 'in', $ids], ['type', '=', '0']]);
/** @var StoreProductAttrValueServices $productAttrValue */
$productAttrValue = app()->make(StoreProductAttrValueServices::class);
$productAttrValue->delete([['product_id', 'in', $ids], ['type', '=', '0']]);
//删除商品详情
/** @var StoreDescriptionServices $productDescription */
$productDescription = app()->make(StoreDescriptionServices::class);
$productDescription->delete([['product_id', 'in', $ids], ['type', '=', '0']]);
//删除商品关联数据
/** @var StoreProductRelationServices $productRelation */
$productRelation = app()->make(StoreProductRelationServices::class);
$productRelation->delete([['product_id', 'in', $ids]]);
//删除商品关联优惠券数据
/** @var StoreProductCouponServices $productCoupon */
$productCoupon = app()->make(StoreProductCouponServices::class);
$productCoupon->delete([['product_id', 'in', $ids]]);
//删除商品收藏记录
/** @var UserRelationServices $productRelation */
$productRelation = app()->make(UserRelationServices::class);
$productRelation->delete([['relation_id', 'in', $ids], ['category', '=', UserRelationServices::CATEGORY_PRODUCT]]);
//删除商品的评论
/** @var StoreProductReplyServices $productReply */
$productReply = app()->make(StoreProductReplyServices::class);
$productReply->delete([['product_id', 'in', $ids]]);
//删除帖子里对应商品
$communityRelevanceServices = app()->make(CommunityRelevanceServices::class);
$communityRelevanceServices->search(['right_id' => $ids, 'type' => CommunityRelevanceServices::TYPE_COMMUNITY_PRODUCT])->delete();
$product->search(['id' => $ids])->delete();
return true;
}
}

View File

@@ -0,0 +1,124 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system;
use app\services\BaseServices;
use crmeb\services\MysqlBackupService;
use think\facade\Db;
use think\facade\Env;
/**
* 数据库备份
* Class SystemDatabackupServices
* @package app\services\system
*/
class SystemDatabackupServices extends BaseServices
{
/**
*
* @var MysqlBackupService
*/
protected $dbBackup;
/**
* 构造方法
* SystemDatabackupServices constructor.
*/
public function __construct()
{
$this->dbBackup = app()->make(MysqlBackupService::class, [[
//数据库备份卷大小
'compress' => 1,
//数据库备份文件是否启用压缩 0不压缩 1 压缩
'level' => 5,
]]);
}
/**
* 获取数据库列表
* @return array
* @throws \think\db\exception\BindParamException
*/
public function getDataList()
{
$list = $this->dbBackup->dataList();
$count = count($list);
return compact('list', 'count');
}
/**
* 获取表详情
* @param string $tablename
* @return array
*/
public function getRead(string $tablename)
{
$database = Env::get("database.database");
$list = Db::query("select * from information_schema.columns where table_name = '" . $tablename . "' and table_schema = '" . $database . "'");
$count = count($list);
foreach ($list as $key => $f) {
$list[$key]['EXTRA'] = ($f['EXTRA'] == 'auto_increment' ? '是' : ' ');
}
return compact('list', 'count');
}
/**
* @return MysqlBackupService
*/
public function getDbBackup()
{
return $this->dbBackup;
}
/**
* 备份表
* @param string $tables
* @return string
* @throws \think\db\exception\BindParamException
*/
public function backup(string $tables)
{
$tables = explode(',', $tables);
$data = '';
ini_set ("memory_limit","-1");
foreach ($tables as $t) {
$res = $this->dbBackup->backup($t, 0);
if ($res == false && $res != 0) {
$data .= $t . '|';
}
}
return $data;
}
/**
* 获取备份列表
* @return array
*/
public function getBackup()
{
$files = $this->dbBackup->fileList();
$data = [];
foreach ($files as $key => $t) {
$data[$key]['filename'] = $t['filename'];
$data[$key]['part'] = $t['part'];
$data[$key]['size'] = $t['size'] . 'B';
$data[$key]['compress'] = $t['compress'];
$data[$key]['backtime'] = $key;
$data[$key]['time'] = $t['time'];
}
krsort($data);//根据时间降序
return ['count' => count($data), 'list' => array_values($data)];
}
}

View File

@@ -0,0 +1,76 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system;
use app\dao\system\SystemMenusRelevanceDao;
use app\services\BaseServices;
use crmeb\services\CacheService;
use think\annotation\Inject;
/**
* 权限菜单关联
* Class SystemMenusRelevanceServices
* @package app\services\system
* @mixin SystemMenusRelevanceDao
*/
class SystemMenusRelevanceServices extends BaseServices
{
/**
* @var SystemMenusRelevanceDao
*/
#[Inject]
protected SystemMenusRelevanceDao $dao;
/**
* 添加关键词
* @param $menuId
* @param $data
* @return true
* @throws \ReflectionException
* User: liusl
* DateTime: 2024/12/25 上午10:04
*/
public function updateData($menuId, $data)
{
$haveData = $this->search(['menu_id' => $menuId])->column('keyword');
$delData = array_diff($haveData, $data);
$newData = array_diff($data, $haveData);
if ($delData) {
$this->dao->delete(['menu_id' => $menuId, 'keyword' => $delData]);
}
if ($newData) {
$newData = array_map(function ($item) use ($menuId) {
return ['menu_id' => $menuId, 'keyword' => $item];
}, $newData);
$this->dao->saveAll($newData);
}
CacheService::redisHandler('system_menus')->clear();
return true;
}
/**
* 获取关键词
* @param $menuId
* @return array
* @throws \ReflectionException
* User: liusl
* DateTime: 2024/12/25 上午10:04
*/
public function getKeyword($menuId): array
{
return $this->search(['menu_id' => $menuId])->column('keyword');
}
}

View File

@@ -0,0 +1,374 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system;
use app\dao\system\SystemMenusDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use crmeb\utils\Arr;
use think\annotation\Inject;
/**
* 权限菜单
* Class SystemMenusServices
* @package app\services\system
* @mixin SystemMenusDao
*/
class SystemMenusServices extends BaseServices
{
/**
* @var SystemMenusDao
*/
#[Inject]
protected SystemMenusDao $dao;
/**
* 获取菜单没有被修改器修改的数据
* @param $menusList
* @return array
*/
public function getMenusData($menusList)
{
$data = [];
foreach ($menusList as $item) {
// $item['expand'] = true;
$item['selected'] = false;
$item['title'] = $item['menu_name'];
$data[] = $item->getData();
}
return $data;
}
/**
* 获取后台权限菜单和权限
* @param $rouleId
* @param int $level
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getMenusList($rouleId, int $level, int $type = 1)
{
/** @var SystemRoleServices $systemRoleServices */
$systemRoleServices = app()->make(SystemRoleServices::class);
$rules = $systemRoleServices->getRoleArray(['status' => 1, 'id' => $rouleId], 'rules');
$rulesStr = Arr::unique($rules);
$menusList = $this->dao->getMenusRoule(['type' => $type, 'route' => $level ? $rulesStr : '']);
$unique = $this->dao->getMenusUnique(['type' => $type, 'unique' => $level ? $rulesStr : '']);
return [Arr::getMenuIviewList($this->getMenusData($menusList)), $unique];
}
/**
* 获取后台菜单树型结构列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where)
{
$menusList = $this->dao->getMenusList($where);
$menusList = $this->getMenusData($menusList);
return get_tree_children($menusList);
}
/**
* 获取form表单所需要的所要的菜单列表
* @return array[]
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
protected function getFormSelectMenus()
{
$menuList = $this->dao->getMenusRoule(['is_del' => 0], ['id', 'pid', 'menu_name']);
$list = get_tree_children($this->getMenusData($menuList), '0', 'pid', 'id');
$menus = [['value' => 0, 'label' => '顶级按钮']];
foreach ($list as $menu) {
$menus[] = ['value' => $menu['id'], 'label' => $menu['html'] . $menu['menu_name']];
}
return $menus;
}
/**
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
protected function getFormCascaderMenus(int $value = 0, int $type = 1)
{
$menuList = $this->dao->getMenusRoule(['is_del' => 0, 'type' => $type], ['id as value', 'pid', 'menu_name as label']);
$menuList = $this->getMenusData($menuList);
if ($value) {
$data = get_tree_value($menuList, $value);
} else {
$data = [];
}
return [get_tree_children($menuList, 'children', 'value'), array_reverse($data)];
}
/**
* 创建权限规格生表单
* @param array $formData
* @return mixed
* @throws \FormBuilder\Exception\FormBuilderException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function createMenusForm(array $formData = [], int $type = 1)
{
$field[] = Form::hidden('type', $type);
$field[] = Form::input('menu_name', '按钮名称', $formData['menu_name'] ?? '')->required('按钮名称必填');
// $field[] = Form::select('pid', '父级id', $formData['pid'] ?? 0)->setOptions($this->getFormSelectMenus())->filterable(1);
$field[] = Form::input('menu_path', '路由名称', $formData['menu_path'] ?? '')->placeholder('请输入前台跳转路由地址')->required('请填写前台路由地址');
$field[] = Form::input('unique_auth', '权限标识', $formData['unique_auth'] ?? '')->placeholder('不填写则后台自动生成');
$params = $formData['params'] ?? '';
$field[] = Form::input('params', '参数', is_array($params) ? '' : $params)->placeholder('举例:a/123/b/234');
$field[] = Form::frameInput('icon', '图标', $this->url('admin/widget.widgets/icon', ['fodder' => 'icon']), $formData['icon'] ?? '')->icon('md-add')->height('500px');
$field[] = Form::number('sort', '排序', (int)($formData['sort'] ?? 0))->min(0);
$field[] = Form::radio('auth_type', '类型', $formData['auth_type'] ?? 1)->options([['value' => 2, 'label' => '接口'], ['value' => 1, 'label' => '菜单(菜单只显示三级)']]);
$field[] = Form::radio('is_show', '状态', $formData['is_show'] ?? 1)->options([['value' => 0, 'label' => '关闭'], ['value' => 1, 'label' => '开启']]);
$field[] = Form::radio('is_show_path', '是否为前端隐藏菜单', $formData['is_show_path'] ?? 0)->options([['value' => 1, 'label' => '是'], ['value' => 0, 'label' => '否']]);
[$menuList, $data] = $this->getFormCascaderMenus((int)($formData['pid'] ?? 0), $type);
$field[] = Form::cascader('menu_list', '父级id', $data)->data($menuList)->filterable(true);
return $field;
}
/**
* 新增权限表单
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function createMenus(int $type = 1)
{
return create_form('添加权限', $this->createMenusForm([], $type), $this->url('/setting/save'));
}
/**
* 修改权限菜单
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function updateMenus(int $id)
{
$menusInfo = $this->dao->get($id);
if (!$menusInfo) {
throw new AdminException('数据不存在');
}
$menusInfo = $menusInfo->getData();
return create_form('修改权限', $this->createMenusForm($menusInfo, $menusInfo['type'] ?? 1), $this->url('/setting/update/' . $id), 'PUT');
}
/**
* 获取一条数据
* @param int $id
* @return mixed
*/
public function find(int $id)
{
$menusInfo = $this->dao->get($id);
if (!$menusInfo) {
throw new AdminException('数据不存在');
}
$menu = $menusInfo->getData();
$menu['pid'] = (int)$menu['pid'];
$menu['auth_type'] = (int)$menu['auth_type'];
$menu['is_header'] = (int)$menu['is_header'];
$menu['is_show'] = (int)$menu['is_show'];
$menu['is_show_path'] = (int)$menu['is_show_path'];
if (!$menu['path']) {
[$menuList, $data] = $this->getFormCascaderMenus($menu['pid']);
$menu['path'] = $data;
} else {
$menu['path'] = explode('/', $menu['path']);
if (is_array($menu['path'])) {
$menu['path'] = array_map(function ($item) {
return (int)$item;
}, $menu['path']);
}
}
return $menu;
}
/**
* 删除菜单
* @param int $id
* @return mixed
*/
public function delete(int $id)
{
if ($this->dao->count(['pid' => $id])) {
throw new AdminException('请先删除改菜单下的子菜单');
}
return $this->dao->delete($id);
}
/**
* 获取添加身份规格
* @param $roles
* @param int $type
* @param int $is_show
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getMenus($roles, int $type = 1, int $is_show = 1): array
{
$field = ['menu_name', 'pid', 'id'];
$where = ['is_del' => 0, 'type' => $type];
if ($is_show) $where['is_show'] = 1;
if (!$roles) {
$menus = $this->dao->getMenusRoule($where, $field);
} else {
/** @var SystemRoleServices $service */
$service = app()->make(SystemRoleServices::class);
//拼接有长度限制
// $ids = $service->value([['id', 'in', $roles]], 'GROUP_CONCAT(rules) as ids');
$roles = is_string($roles) ? explode(',', $roles) : $roles;
$ids = $service->getRoleIds($roles);
$menus = $this->dao->getMenusRoule(['rule' => $ids] + $where, $field);
}
return $this->tidyMenuTier(false, $menus);
}
/**
* 组合菜单数据
* @param bool $adminFilter
* @param $menusList
* @param int $pid
* @param array $navList
* @return array
*/
public function tidyMenuTier(bool $adminFilter = false, $menusList = [], int $pid = 0, array $navList = []): array
{
foreach ($menusList as $k => $menu) {
$menu = $menu->getData();
$menu['title'] = $menu['menu_name'];
unset($menu['menu_name']);
if ($menu['pid'] == $pid) {
unset($menusList[$k]);
$menu['children'] = $this->tidyMenuTier($adminFilter, $menusList, $menu['id']);
if ($pid == 0 && !count($menu['children'])) continue;
if ($menu['children']) $menu['expand'] = true;
$navList[] = $menu;
}
}
return $navList;
}
/**
* 获取菜单选择列表
* @param int $type 菜单类型
* @param string $keyword 搜索关键词
* @return array
*/
public function getSelectList(int $type = 1, string $keyword = '')
{
// 获取搜索列表
$_list = $this->dao->getSearchList($type, $keyword);
$list = [];
$ids = [];
foreach ($_list as $key => $v) {
$menu = json_decode($v, true);
$pid = $v->getData('pid');
$menu['p_id'] = $pid;
$list[$key] = $menu;
$path = explode('/', $menu['path']);
$ids = array_merge($ids, $path);
}
// 去除重复的id
$ids = array_filter(array_unique($ids));
// 合并PID列表查询
$pidList = [];
if ($ids) {
$_pidList = $this->dao->search(['id' => $ids])->select();
foreach ($_pidList as $key => $v) {
$menu = json_decode($v, true);
$pid = $v->getData('pid');
$menu['p_id'] = $pid;
$pidList[$key] = $menu;
}
}
// 合并pidList到原始列表
$list = array_merge($list, $pidList);
// 获取符合条件的counts(当前显示的有效菜单项)
$counts = $this->dao->getColumn([
'is_show' => 1,
'auth_type' => 1,
'is_del' => 0,
'is_show_path' => 0,
'keywords' => $keyword
], 'pid', '', true);
// 获取p_counts(菜单路径中有效的PID)
$p_counts = $this->dao->getColumn([
'id' => $ids
], 'pid', '', true);
// 合并counts和p_counts
$counts = array_merge($counts, $p_counts);
// 对菜单列表进行排序处理
$sortList = $this->sortListTier($list);
// 返回最终结果
$data = [];
foreach ($sortList as $item) {
if (!in_array($item['id'], $counts)) {
$data[$item['id']] = $item;
$data[$item['id']]['menu_name'] = isset($item['p_name']) && $item['p_name'] ? $item['p_name'] : $item['menu_name'];
$data[$item['id']]['type'] = 0;
}
}
return $data;
}
/**
* 递归排序菜单层级并拼接父级名称
* @param array $menuData 菜单数据引用
* @param int $pid 父级ID
* @param string $p_name 父级名称
* @return array
*/
public function sortListTier(&$menuData, $pid = 0, $p_name = '')
{
foreach ($menuData as &$menu) {
if ($menu['p_id'] == $pid) {
// 拼接父级名称
$menu['p_name'] = $p_name ? $p_name . '-' . $menu['menu_name'] : $menu['menu_name'];
// 递归查找子菜单
$this->sortListTier($menuData, $menu['id'], $menu['p_name']);
}
}
return $menuData;
}
}

View File

@@ -0,0 +1,188 @@
<?php
namespace app\services\system;
use app\dao\system\SystemRecommendDao;
use app\services\BaseServices;
use app\services\order\StoreCartServices;
use app\services\product\product\StoreProductLogServices;
use app\services\product\product\StoreProductServices;
use app\services\user\UserRelationServices;
use crmeb\exceptions\AdminException;
use think\annotation\Inject;
class SystemRecommendServices extends BaseServices
{
/**
* @var SystemRecommendDao
*/
#[Inject]
protected SystemRecommendDao $dao;
/**
* 获取推荐配置列表
* @param array $where 查询条件
* @return array
*/
public function getRecommendList($where = [])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->recommendList($where, $page, $limit, 'id,name,title,pic,type,update_time,status', 'id asc');
foreach ($list as &$item) {
$item['update_time'] = date('Y-m-d H:i:s', $item['update_time']);
}
$count = $this->dao->recommendCount($where);
return compact('list', 'count');
}
/**
* 获取推荐配置详情
* @param int $id 推荐ID
* @return array
*/
public function getRecommendInfo($id)
{
$info = $this->dao->getOne(['id' => $id]);
if ($info) {
$info = $info->toArray();
$info['specify_recommend_content'] = json_decode($info['specify_recommend_content'], true);
$info['personality_recommend_content'] = json_decode($info['personality_recommend_content'], true);
$info['sort_recommend_content'] = json_decode($info['sort_recommend_content'], true);
} else {
$info = [];
}
return $info;
}
/**
* 编辑推荐标题
* @param int $id 推荐ID
* @param string $title 标题
* @return bool
*/
public function editRecommendTitle($id, $title)
{
$this->dao->update($id, ['title' => $title]);
return true;
}
/**
* 保存推荐配置
* @param int $id 推荐ID
* @param array $data 配置数据
* @return bool
* @throws AdminException
*/
public function saveRecommend($id, $data)
{
$data['specify_recommend_content'] = json_encode($data['specify_recommend_content']);
$data['personality_recommend_content'] = json_encode($data['personality_recommend_content']);
$data['sort_recommend_content'] = json_encode($data['sort_recommend_content']);
$data['update_time'] = time();
if ($id) {
$res = $this->dao->update($id, $data);
} else {
$res = $this->dao->save($data);
}
if (!$res) {
throw new AdminException('保存失败');
}
return true;
}
/**
* 设置推荐状态
* @param int $id 推荐ID
* @param int $status 状态
* @return bool
*/
public function setRecommendStatus($id, $status)
{
$data = [
'status' => $status,
'update_time' => time(),
];
return $this->dao->update((int)$id, $data);
}
/**
* 获取推荐商品ID列表
* @param int $type 推荐类型
* @param int $uid 用户ID
* @return array
*/
public function getRecommendProductIds($type, $uid = 0)
{
$info = $this->dao->getOne(['type' => $type]);
if (!$info || !$info['status']) return ['为您推荐', []];
$info = $info->toArray();
$specifyProductIds = $personalityProductIds = $sortProductIds = [];
$info['specify_recommend_content'] = json_decode($info['specify_recommend_content'], true);
$info['personality_recommend_content'] = json_decode($info['personality_recommend_content'], true);
$info['sort_recommend_content'] = json_decode($info['sort_recommend_content'], true);
if ($info['specify_recommend_status'] == 1) {
$specifyProductIds = array_column($info['specify_recommend_content'], 'product_id');
}
if ($uid != 0 && $info['personality_recommend_status'] == 1) {
$productLogServices = app()->make(StoreProductLogServices::class);
foreach ($info['personality_recommend_content'] as $item) {
switch ($item['value']) {
case 1:
$cartProductIds = $productLogServices->getColumn([['uid', '=', $uid], ['type', '=', 'cart'], ['add_time', '>', strtotime('-30 days')]], 'product_id');
$personalityProductIds = array_merge($personalityProductIds, array_diff($cartProductIds, $personalityProductIds));
break;
case 2:
$relationProductIds = $productLogServices->getColumn([['uid', '=', $uid], ['type', '=', 'collect'], ['add_time', '>', strtotime('-30 days')]], 'product_id');
$personalityProductIds = array_merge($personalityProductIds, array_diff($relationProductIds, $personalityProductIds));
break;
case 3:
$visitProductIds = $productLogServices->getColumn([['uid', '=', $uid], ['type', '=', 'visit'], ['add_time', '>', strtotime('-30 days')]], 'product_id');
$personalityProductIds = array_merge($personalityProductIds, array_diff($visitProductIds, $personalityProductIds));
break;
case 4:
$payProductIds = $productLogServices->getColumn([['uid', '=', $uid], ['type', '=', 'pay'], ['add_time', '>', strtotime('-30 days')]], 'product_id');
$personalityProductIds = array_merge($personalityProductIds, array_diff($payProductIds, $personalityProductIds));
break;
}
}
}
if ($info['sort_recommend_status'] == 1) {
$productLogServices = app()->make(StoreProductLogServices::class);
$productServices = app()->make(StoreProductServices::class);
foreach ($info['sort_recommend_content'] as $item) {
switch ($item['value']) {
case 1:
$salesProductIds = [];
$salesProductList = $productLogServices->getProductIds('pay', 'product_id, COUNT(*) as pay_count', 'pay_count desc', 20);
if (count($salesProductList)) $salesProductIds = array_column($salesProductList, 'product_id');
$sortProductIds = array_merge($sortProductIds, array_diff($salesProductIds, $sortProductIds));
break;
case 2:
$payPriceProductIds = [];
$payPriceProductList = $productLogServices->getProductIds('pay', 'product_id, SUM(pay_price) as total_pay_price', 'total_pay_price desc', 20);
if (count($payPriceProductList)) $payPriceProductIds = array_column($payPriceProductList, 'product_id');
$sortProductIds = array_merge($sortProductIds, array_diff($payPriceProductIds, $sortProductIds));
break;
case 3:
$addTimeProductIds = $productServices->getProductIds(['is_show' => 1, 'is_del' => 0, 'is_verify' => 1], 'product_id, add_time', 'add_time desc', 20);
$sortProductIds = array_merge($sortProductIds, array_diff($addTimeProductIds, $sortProductIds));
break;
case 4:
$starProductIds = $productServices->getProductIds(['is_show' => 1, 'is_del' => 0, 'is_verify' => 1], 'product_id, star', 'star desc, add_time desc', 20);
$sortProductIds = array_merge($sortProductIds, array_diff($starProductIds, $sortProductIds));
break;
case 5:
$visitProductIds = [];
$visitProductList = $productLogServices->getProductIds('visit', 'product_id, COUNT(*) as visit_count', 'visit_count desc', 20);
if (count($visitProductList)) $visitProductIds = array_column($visitProductList, 'product_id');
$sortProductIds = array_merge($sortProductIds, array_diff($visitProductIds, $sortProductIds));
break;
}
}
}
$productIds = array_merge($specifyProductIds, array_diff($personalityProductIds, $specifyProductIds));
$productIds = array_merge($productIds, array_diff($sortProductIds, $productIds));
return [$info['title'], $productIds];
}
}

View File

@@ -0,0 +1,182 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system;
use app\Request;
use app\services\BaseServices;
use app\dao\system\SystemRoleDao;
use app\services\store\SystemStoreStaffServices;
use crmeb\exceptions\AuthException;
use crmeb\utils\ApiErrorCode;
use crmeb\services\CacheService;
use think\annotation\Inject;
/**
* Class SystemRoleServices
* @package app\services\system
* @mixin SystemRoleDao
*/
class SystemRoleServices extends BaseServices
{
/**
* 当前管理员权限缓存前缀
*/
const ADMIN_RULES_LEVEL = 'Admin_rules_level_';
/**
* @var SystemRoleDao
*/
#[Inject]
protected SystemRoleDao $dao;
/**
* 获取权限
* @return mixed
*/
public function getRoleArray(array $where = [], string $field = '', string $key = '')
{
return $this->dao->getRoule($where, $field, $key);
}
/**
* 获取表单所需的权限名称列表
* @param int $level
* @param int $type
* @param int $relation_id
* @return array
*/
public function getRoleFormSelect(int $level, int $type = 0, int $relation_id = 0)
{
$list = $this->getRoleArray(['level' => $level, 'type' => $type, 'relation_id' => $relation_id, 'status' => 1]);
$options = [];
foreach ($list as $id => $roleName) {
$options[] = ['label' => $roleName, 'value' => $id];
}
return $options;
}
/**
* 身份管理列表
* @param array $where
* @return array
*/
public function getRoleList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getRouleList($where, $page, $limit);
$count = $this->dao->count($where);
/** @var SystemMenusServices $service */
$service = app()->make(SystemMenusServices::class);
foreach ($list as &$item) {
$item['rules'] = implode(',', array_merge($service->column(['id' => $item['rules']], 'menu_name', 'id')));
}
return compact('count', 'list');
}
/**
* 后台验证权限
* @param Request $request
*/
public function verifiAuth(Request $request)
{
$rule = str_replace('adminapi/', '', trim(strtolower($request->rule()->getRule())));
if (in_array($rule, ['setting/admin/logout', 'menuslist'])) {
return true;
}
$method = trim(strtolower($request->method()));
$auth = $this->getAllRoles(2);
//验证访问接口是否存在
if (!in_array($method . '@@' . $rule, array_map(function ($item) {
return trim(strtolower($item['methods'])) . '@@' . trim(strtolower(str_replace(' ', '', $item['api_url'])));
}, $auth))) {
// throw new AuthException(ApiErrorCode::ERR_AUTH);
return true;
}
$auth = $this->getRolesByAuth($request->adminInfo()['roles'], 2);
//验证访问接口是否有权限
if ($auth && empty(array_filter($auth, function ($item) use ($rule, $method) {
if (trim(strtolower($item['api_url'])) === $rule && $method === trim(strtolower($item['methods'])))
return true;
}))) {
// throw new AuthException(ApiErrorCode::ERR_AUTH);
throw new AuthException(ApiErrorCode::ERR_AUTH);
}
}
/**
* 获取所有权限
* @param int $auth_type
* @param int $type
* @param string $cachePrefix
* @return array|bool|mixed|null
*/
public function getAllRoles(int $auth_type = 1, int $type = 1, string $cachePrefix = self::ADMIN_RULES_LEVEL)
{
$cacheName = md5($cachePrefix . '_' . $auth_type . '_' . $type . '_ALl');
return CacheService::redisHandler('system_menus')->remember($cacheName, function () use ($auth_type, $type) {
/** @var SystemMenusServices $menusService */
$menusService = app()->make(SystemMenusServices::class);
return $menusService->getColumn([['auth_type', '=', $auth_type], ['type', '=', $type]], 'api_url,methods');
});
}
/**
* 获取指定权限
* @param array $rules
* @param int $auth_type
* @param int $type
* @param string $cachePrefix
* @return array|bool|mixed|null
*/
public function getRolesByAuth(array $rules, int $auth_type = 1, int $type = 1, string $cachePrefix = self::ADMIN_RULES_LEVEL)
{
if (empty($rules)) return [];
$cacheName = md5($cachePrefix . '_' . $auth_type . '_' . $type . '_' . implode('_', $rules));
return CacheService::redisHandler('system_menus')->remember($cacheName, function () use ($rules, $auth_type, $type) {
/** @var SystemMenusServices $menusService */
$menusService = app()->make(SystemMenusServices::class);
return $menusService->getColumn([['id', 'IN', $this->getRoleIds($rules)], ['auth_type', '=', $auth_type], ['type', '=', $type]], 'api_url,methods');
});
}
/**
* 获取权限id
* @param array $rules
* @return array
*/
public function getRoleIds(array $rules)
{
$rules = $this->dao->getColumn([['id', 'IN', $rules], ['status', '=', '1']], 'rules', 'id');
return array_unique(explode(',', implode(',', $rules)));
}
/**
* 门店角色状态更改改变角色下店员、管理员状态
* @param int $store_id
* @param int $role_id
* @param $status
* @return mixed
*/
public function setStaffStatus(int $store_id, int $role_id, $status)
{
/** @var SystemStoreStaffServices $storeStaffServices */
$storeStaffServices = app()->make(SystemStoreStaffServices::class);
if ($status) {
return $storeStaffServices->update(['store_id' => $store_id, 'roles' => $role_id, 'is_del' => 0, 'status' => 0], ['status' => 1]);
} else {
return $storeStaffServices->update(['store_id' => $store_id, 'roles' => $role_id, 'status' => 1], ['status' => 0]);
}
}
}

View File

@@ -0,0 +1,110 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system;
use app\services\BaseServices;
use app\dao\system\SystemSignRewardDao;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\facade\Route as Url;
/**
* Class SystemSignRewardServices
* @package app\services\system
* @mixin SystemSignRewardDao
*/
class SystemSignRewardServices extends BaseServices
{
/**
* @var SystemSignRewardDao
*/
#[Inject]
protected SystemSignRewardDao $dao;
/**
* 签到奖励列表
* @param $type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList($type = 0)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->selectList(['type' => $type], '*', $page, $limit, 'days asc');
$count = $this->dao->count(['type' => $type]);
return compact('list', 'count');
}
/**
* 新增修改签到奖励表单
* @param $id
* @param $type
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function rewardsForm($id = 0, $type = 0)
{
$info = $this->dao->get($id);
if ($info) $type = $info['type'];
$form[] = Form::hidden('type', $type);
$form[] = Form::number('days', $type == 1 ? '累积签到天数:' : '连续签到天数:', (int)($info['days'] ?? 0))->max(sys_config('sign_mode') == 1 ? 7 : 30);
$form[] = Form::number('point', '赠送积分:', (int)($info['point'] ?? 0))->max(999)->min(0);
$form[] = Form::number('exp', '赠送经验:', (int)($info['exp'] ?? 0))->max(999)->min(0);
return create_form($type == 1 ? '累积签到奖励' : '连续签到奖励', $form, Url::buildUrl('/setting/sign/save_rewards/' . $id), 'POST');
}
/**
* 保存签到奖励
* @param $id
* @param $data
* @return bool
* @throws \think\db\exception\DbException
*/
public function saveRewards($id, $data)
{
if ($id) {
$this->dao->update($id, $data);
} else {
if ($this->dao->count(['type' => $data['type'], 'days' => $data['days']])) {
throw new AdminException('签到奖励已存在');
} else {
$this->dao->save($data);
}
}
return true;
}
/**
* 获取累积或者连续签到奖励数据
* @param $type
* @param $days
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSignRewards($type, $days)
{
$info = $this->dao->get(['type' => $type, 'days' => $days]);
if ($info) return [true, $info['point'], $info['exp']];
return [false, 0, 0];
}
}

View File

@@ -0,0 +1,200 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system;
use app\dao\system\SystemUserApplyDao;
use app\services\BaseServices;
use app\dao\system\SystemRoleDao;
use app\services\supplier\SystemSupplierServices;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\exception\ValidateException;
use think\facade\Route as Url;
/**
* Class SystemUserApplyServices
* @package app\services\system
* @mixin SystemRoleDao
*/
class SystemUserApplyServices extends BaseServices
{
/**
* @var string[]
*/
protected array $status_name = [
0 => '未处理',
1 => '已审核',
2 => '未通过',
];
/**
* @var SystemUserApplyDao
*/
#[Inject]
protected SystemUserApplyDao $dao;
/**
* 所有申请记录列表
* @param array $where
* @param string $field
* @return array
*/
public function getApplyList(array $where, string $field = '*')
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $field, ['user'], $page, $limit);
foreach ($list as &$item) {
$item['nickname'] = $item['user']['nickname'] ?? '';
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', (int)$item['add_time']) : '';
$item['status_name'] = $this->status_name[$item['status']] ?? '未处理';
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 用户申请记录
* @param int $uid
* @param int $type
* @return array
*/
public function getUserApply(int $uid, int $type = 2)
{
[$page, $limit] = $this->getPageValue();
$where = ['uid' => $uid, 'type' => $type, 'is_del' => 0];
$list = $this->dao->getList($where, '*', ['user'], $page, $limit);
foreach ($list as &$item) {
$item['nickname'] = $item['user']['nickname'] ?? '';
$item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', (int)$item['add_time']) : '';
}
return $list;
}
/**
* 添加申请
* @param int $id
* @param int $uid
* @param $data
* @param int $type
* @return int|mixed
*/
public function saveApply(int $id, int $uid, $data, int $type = 2)
{
$data['uid'] = $uid;
$data['add_time'] = time();
$data['type'] = $type;
if ($id) {
$data['status'] = 0;
$data['status_time'] = 0;
$data['fail_msg'] = '';
$this->dao->update($id, $data);
} else {
$res = $this->dao->save($data);
if (!$res) {
throw new ValidateException('保存失败,请稍后再试');
}
$id = $res->id;
}
return $id;
}
/**
* 审核表单
* @param int $id
* @return mixed
*/
public function verifyForm(int $id)
{
$info = $this->dao->get($id);
if (!$info) {
throw new ValidateException('申请记录不存在');
}
$f = [];
$f[] = Form::radio('status', '审核状态:', 1)->options([['value' => 1, 'label' => '通过'], ['value' => 2, 'label' => '拒绝']])->appendControl(2, [
Form::textarea('fail_msg', '拒绝原因:')->required('请输入拒绝原因')
]);
return create_form('供应商申请审核', $f, Url::buildUrl('/supplier/apply/verify/' . $id), 'post');
}
/**
* 备注表单
* @param int $id
* @return mixed
*/
public function markForm(int $id)
{
$info = $this->dao->get($id);
if (!$info) {
throw new ValidateException('申请记录不存在');
}
$info = $info->toArray();
$f = [];
$f[] = Form::textarea('mark', '备注:', $info['mark'])->required('请输入备注信息');
return create_form('供应商申请备注', $f, Url::buildUrl('/supplier/apply/mark/' . $id), 'post');
}
/**
* 审核申请记录
* @param int $id
* @param array $data
* @param int $type
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function verifyApply(int $id, array $data, int $type = 2)
{
$info = $this->dao->get($id);
if (!$info) {
throw new ValidateException('申请记录不存在');
}
$info = $info->toArray();
if ($info['status'] != 0) {
throw new ValidateException('请不要重复审核');
}
if (!isset($data['status']) || !in_array($data['status'], [1, 2])) {
throw new ValidateException('审核状态异常,请稍后重试');
}
$res = $this->transaction(function () use ($id, $info, $data, $type) {
$result = [];
if ($data['status'] == 1) {//通过
switch ($type) {
case 2://供应商
/** @var SystemSupplierServices $systemSupplierServices */
$systemSupplierServices = app()->make(SystemSupplierServices::class);
$result = $systemSupplierServices->verifyAgreeCreate($id, $info);
break;
}
}
$data['relation_id'] = $result['id'] ?? 0;
$data['status_time'] = time();
$res = $this->dao->update($id, $data);
if (!$res) {
throw new ValidateException('审核保存失败');
}
return $info;
});
event('supplier.verify', [$res, $data['status']]);
return $res;
}
}

View File

@@ -0,0 +1,146 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\admin;
use app\dao\system\admin\AdminAuthDao;
use app\services\BaseServices;
use app\services\other\CacheServices;
use app\services\supplier\SystemSupplierServices;
use crmeb\exceptions\AuthException;
use crmeb\services\CacheService;
use crmeb\utils\ApiErrorCode;
use crmeb\utils\JwtAuth;
use Firebase\JWT\ExpiredException;
use Psr\SimpleCache\InvalidArgumentException;
use think\annotation\Inject;
/**
* admin授权service
* Class AdminAuthServices
* @package app\services\system\admin
* @mixin AdminAuthDao
*/
class AdminAuthServices extends BaseServices
{
/**
* @var AdminAuthDao
*/
#[Inject]
protected AdminAuthDao $dao;
/**
* 获取Admin授权信息
* @param string $token
* @return array
* @throws InvalidArgumentException
*/
public function parseToken(string $token): array
{
/** @var CacheService $cacheService */
$cacheService = app()->make(CacheService::class);
if (!$token || $token === 'undefined') {
throw new AuthException(ApiErrorCode::ERR_LOGIN);
}
/** @var JwtAuth $jwtAuth */
$jwtAuth = app()->make(JwtAuth::class);
//设置解析token
[$id, $type, $auth] = $jwtAuth->parseToken($token);
//检测token是否过期
$md5Token = md5($token);
if (!$cacheService->hasToken($md5Token) || !($cacheToken = $cacheService->getTokenBucket($md5Token))) {
$this->authFailAfter($id, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN);
}
//是否超出有效次数
if (isset($cacheToken['invalidNum']) && $cacheToken['invalidNum'] >= 3) {
if (!request()->isCli()) {
$cacheService->clearToken($md5Token);
}
$this->authFailAfter($id, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
//验证token
try {
$jwtAuth->verifyToken();
$cacheService->setTokenBucket($md5Token, $cacheToken, $cacheToken['exp']);
} catch (ExpiredException $e) {
$cacheToken['invalidNum'] = isset($cacheToken['invalidNum']) ? $cacheToken['invalidNum']++ : 1;
$cacheService->setTokenBucket($md5Token, $cacheToken, $cacheToken['exp']);
} catch (\Throwable $e) {
if (!request()->isCli()) {
$cacheService->clearToken($md5Token);
}
$this->authFailAfter($id, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID);
}
//获取管理员信息
$adminInfo = $this->dao->get($id);
if (!$adminInfo || !$adminInfo->id || $adminInfo->status == 0 || $adminInfo->is_del == 1) {
if (!request()->isCli()) {
$cacheService->clearToken($md5Token);
}
$this->authFailAfter($id, $type);
throw new AuthException(ApiErrorCode::ERR_LOGIN_STATUS);
}
//修改密码后token立刻过期
if ($auth !== md5($adminInfo['pwd'])) {
throw new AuthException(ApiErrorCode::ERR_LOGIN_STATUS);
}
if($adminInfo['admin_type'] == 4){
$adminInfo = app()->make(SystemSupplierServices::class)->getOne(['id' => (int)$adminInfo->relation_id, 'is_del' => 0], '*', ['admin']);
if (!$adminInfo || !$adminInfo->account || $adminInfo->admin_is_del) {
if (!request()->isCli()) {
$cacheService->clearToken($md5Token);
}
throw new AuthException(ApiErrorCode::ERR_LOGIN_STATUS);
}
}
$adminInfo->type = $type;
return $adminInfo->hidden(['pwd', 'is_del', 'status'])->toArray();
}
/**
* token验证失败后事件
*/
protected function authFailAfter($id, $type)
{
try {
$postData = request()->post();
$rule = trim(strtolower(request()->rule()->getRule()));
$method = trim(strtolower(request()->method()));
//添加商品退出后事件
if ($rule === 'product/product/<id>' && $method === 'post') {
$this->saveProduct($id, $postData);
}
} catch (\Throwable $e) {
}
}
/**
* 保存提交数据
* @param $adminId
* @param $postData
*/
protected function saveProduct($adminId, $postData)
{
/** @var CacheServices $cacheService */
$cacheService = app()->make(CacheServices::class);
$cacheService->setDbCache($adminId . '_product_data', $postData, 68400);
}
}

View File

@@ -0,0 +1,152 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\admin;
use app\services\BaseServices;
use crmeb\services\CacheService;
use think\exception\ValidateException;
/**
* 密码、登录次数验证
* Class AdminAuthServices
* @package app\services\system\admin
*/
class LoginAuthServices extends BaseServices
{
/**
* 验证登录账号是否需要锁定
* @param string $account
* @param string $type
* @return bool
*/
public function checkErrorLock(string $account, string $type = 'admin')
{
$loginErrorNum = $this->getErrorNum($account, $type);
if ($loginErrorNum >= (int)sys_config('system_login_error_num', 3)) {
throw new ValidateException('您输入的错误次数较多,已被暂时锁定,请稍后再试');
}
return true;
}
/**
* 获取登录错误次数
* @param string $account
* @param string $type
* @return int
*/
public function getErrorNum(string $account, string $type = 'admin')
{
$errorKey = 'system_' . $type . '_login_error_num_' . $account;
/** @var CacheService $cacheServices */
$cacheServices = app()->make(CacheService::class);
return (int)$cacheServices->get($errorKey);
}
/**
* 设置登录错误次数缓存
* @param string $account
* @param string $type
* @return bool
*/
public function setErrorNum(string $account, string $type = 'admin')
{
$errorKey = 'system_' . $type . '_login_error_num_' . $account;
$lockTime = (int)sys_config('system_login_lock_time', 5);
/** @var CacheService $cacheServices */
$cacheServices = app()->make(CacheService::class);
$loginErrorNum = $this->getErrorNum($account, $type);
$cacheServices::set($errorKey, $loginErrorNum + 1, $lockTime);
return true;
}
/**
* 验证输入密码
* @param string $password
* @return bool
*/
public function validatePassword(string $password)
{
$type = (int)sys_config('system_password_type', 3);
$length = (int)sys_config('system_password_length', 3);
if (!$this->checkPassword($password, $type, $length)) {
throw new ValidateException($this->getMessage($type, $length));
}
return true;
}
/**
* 验证登录密码
* @param string $password
* @param int $type
* @param int $length
* @return false|int
*/
function checkPassword(string $password, int $type = 3, int $length = 6)
{
switch ($type) {
case 1:// 纯数字
$regex = '/^\d{' . $length . ',}$/';
break;
case 2:// 纯字母
$regex = '/^[A-Za-z]{' . $length . ',}$/';
break;
case 3:// 数字 + 纯字母
$regex = '/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{' . $length . ',}$/';
break;
case 4:// 数字 + 纯字母 + 特殊符号
// 使用 [\W_] 匹配所有特殊字符和下划线
$regex = '/^(?=.*[A-Za-z])(?=.*\d)(?=.*[\W_])[A-Za-z\d\W_]{' . $length . ',}$/';
break;
default:
$regex = '/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{' . $length . ',}$/';
break;
}
return preg_match($regex, $password);
}
/**
* 获取验证密码提示语
* @param int $type
* @param int $length
* @return string
*/
public function getMessage(int $type = 3, int $length = 6)
{
switch ($type) {
case 1://纯数字
$desc = '纯数字';
break;
case 2://纯字母
$desc = '纯字母';
break;
case 3://数字+纯字母
$desc = '数字+纯字母';
break;
case 4://数字+纯字母+特殊符号
$desc = '数字+纯字母+特殊符号';
break;
default:
$desc = '数字+纯字母';
break;
}
return '密码必须由:' . $desc . '组成,最小' . $length . '位';
}
}

View File

@@ -0,0 +1,479 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\admin;
use app\jobs\notice\SocketPushJob;
use app\services\BaseServices;
use app\services\order\StoreOrderServices;
use app\services\product\product\StoreProductReplyServices;
use app\services\product\product\StoreProductServices;
use app\services\user\UserExtractServices;
use crmeb\exceptions\AdminException;
use app\dao\system\admin\SystemAdminDao;
use app\services\system\SystemMenusServices;
use crmeb\services\CacheService;
use crmeb\services\FormBuilder;
use app\services\system\SystemRoleServices;
use crmeb\services\SystemConfigService;
use think\annotation\Inject;
use think\facade\Cache;
use function Symfony\Component\Translation\t;
/**
* 管理员service
* Class SystemAdminServices
* @package app\services\system\admin
* @mixin SystemAdminDao
*/
class SystemAdminServices extends BaseServices
{
/**
* form表单创建
* @var FormBuilder
*/
#[Inject]
protected FormBuilder $builder;
/**
* @var SystemAdminDao
*/
#[Inject]
protected SystemAdminDao $dao;
/**
* 管理员登陆
* @param string $account
* @param string $password
* @param bool $is_mobile
* @param int $adminType
* @return array|\think\Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function verifyLogin(string $account, string $password, bool $is_mobile = false, int $adminType = 1, bool $is_secure = false, bool $is_admin = false)
{
$key = $adminType == 1 ? 'login_captcha_' . $account : 'supplier_login_captcha_' . $account;
/** @var LoginAuthServices $loginAuthServices */
$loginAuthServices = app()->make(LoginAuthServices::class);
//验证是否锁定
$loginAuthServices->checkErrorLock($account, $adminType);
if ($is_mobile) {
$adminInfo = $this->dao->phoneByAdmin($account, $adminType);
} else {
$adminInfo = $this->dao->accountByAdmin($account, $adminType);
}
if (!$adminInfo) {
Cache::inc($key);
$loginAuthServices->setErrorNum($account, $adminType);
throw new AdminException('账号密码错误!');
}
if (!$adminInfo->status) {
Cache::inc($key);
$loginAuthServices->setErrorNum($account, $adminType);
throw new AdminException('您已被禁止登录!');
}
if (!$is_mobile && !password_verify($password, $adminInfo->pwd) && !$is_admin) {
Cache::inc($key);
$loginAuthServices->setErrorNum($account, $adminType);
throw new AdminException('账号或密码错误,请重新输入');
}
if (!$is_secure) {
$adminInfo->last_time = time();
$adminInfo->last_ip = app('request')->ip();
$adminInfo->login_count++;
$adminInfo->save();
}
return $adminInfo;
}
/**
* 安全验证获取手机号
* @param string $account
* @param string $password
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* User: liusl
* DateTime: 2024/8/2 10:32
*/
public function secure(string $account, string $password)
{
$adminInfo = $this->verifyLogin($account, $password, false, 1, true);
$phone = $adminInfo->phone;
if (!$phone) {
throw new AdminException('请先绑定手机号');
}
$phone = substr_replace($phone, '****', 3, 4);
$secure_key = 'admin_secure_' . md5($adminInfo->id);
CacheService::set($secure_key, $adminInfo->phone);
return compact('phone', 'secure_key');
}
/**
* 后台登陆获取菜单获取token
* @param string $account
* @param string $password
* @param string $type
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function login(string $account, string $password, string $type, bool $is_mobile = false, bool $is_admin = false)
{
$admin_type = $type == 'admin' ? 1 : 4;
$adminInfo = $this->verifyLogin($account, $password, $is_mobile, $admin_type, false, $is_admin);
$tokenInfo = $this->createToken($adminInfo->id, $type, $adminInfo['pwd']);
/** @var SystemMenusServices $services */
$services = app()->make(SystemMenusServices::class);
[$menus, $uniqueAuth] = $services->getMenusList($adminInfo->roles, (int)$adminInfo['level'], $admin_type);
$data = SystemConfigService::more(['site_logo', 'site_logo_square', 'new_order_audio_link']);
return [
'token' => $tokenInfo['token'],
'expires_time' => $tokenInfo['params']['exp'],
'menus' => $menus,
'unique_auth' => $uniqueAuth,
'user_info' => [
'id' => $adminInfo['id'],
'account' => $adminInfo['account'],
'head_pic' => $adminInfo['head_pic'],
'division_id' => $adminInfo['division_id'],
],
'logo' => $data['site_logo'],
'logo_square' => $data['site_logo_square'],
'version' => get_crmeb_version(),
'newOrderAudioLink' => get_file_link($data['new_order_audio_link'])
];
}
/**
* 获取登陆前的login等信息
* @return array
*/
public function getLoginInfo()
{
$data = SystemConfigService::more(['admin_login_slide', 'site_logo_square', 'site_logo', 'login_logo']);
return [
'slide' => sys_config('admin_login_slide') ?? [],
'system_secure_type' => (int)sys_config('system_secure_type') ?? 0,
'logo_square' => $data['site_logo_square'] ?? '',//透明
'logo_rectangle' => $data['site_logo'] ?? '',//方形
'login_logo' => $data['login_logo'] ?? '',//登陆
'version' => get_crmeb_version(),
'upload_file_size_max' => config('upload.filesize'),//文件上传大小kb
];
}
/**
* 管理员列表
* @param array $where
* @return array
*/
public function getAdminList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $page, $limit);
$count = $this->dao->count($where);
/** @var SystemRoleServices $service */
$service = app()->make(SystemRoleServices::class);
$allRole = $service->getRoleArray(['type' => 0]);
foreach ($list as &$item) {
if ($item['roles']) {
$roles = [];
foreach ($item['roles'] as $id) {
if (isset($allRole[$id])) $roles[] = $allRole[$id];
}
if ($roles) {
$item['roles'] = implode(',', $roles);
} else {
$item['roles'] = '';
}
}
$item['_add_time'] = date('Y-m-d H:i:s', $item['add_time']);
$item['_last_time'] = $item['last_time'] ? date('Y-m-d H:i:s', $item['last_time']) : '';
}
return compact('list', 'count');
}
/**
* 创建管理员表单
* @param int $level
* @param array $formData
* @return mixed
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createAdminForm(int $level, array $formData = [], $supplierId = 0)
{
if (!$level) {
$imgUrl = $supplierId ? '/supplier/widget.images/index' : '/admin/widget.images/index';
$f[] = $this->builder->frameImage('head_pic', '头像:', $this->url($imgUrl, ['fodder' => 'head_pic'], true), $formData['head_pic'] ?? '')->icon('ios-add')->width('960px')->height('505px')->modal(['footer-hide' => true]);
}
$f[] = $this->builder->input('account', '管理员账号:', $formData['account'] ?? '')->required('请填写管理员账号');
if ($formData) {
$f[] = $this->builder->input('pwd', '管理员密码:')->type('password')->placeholder('不修改密码请留空');
$f[] = $this->builder->input('conf_pwd', '确认密码:')->type('password')->placeholder('不修改密码请留空');
} else {
$f[] = $this->builder->input('pwd', '管理员密码:')->type('password')->required('请填写管理员密码');
$f[] = $this->builder->input('conf_pwd', '确认密码:')->type('password')->required('请输入确认密码');
}
$f[] = $this->builder->input('real_name', '管理员姓名:', $formData['real_name'] ?? '')->required('请输入管理员姓名');
$f[] = $this->builder->input('phone', '管理员电话:', $formData['phone'] ?? '')->required('请输入管理员电话');
/** @var SystemRoleServices $service */
$service = app()->make(SystemRoleServices::class);
$options = $service->getRoleFormSelect($level);
$roles = [];
if ($formData && ($formData['roles'] ?? [])) {
foreach ($formData['roles'] as $role) {
$roles[] = (int)$role;
}
}
if ($level) {
$f[] = $this->builder->select('roles', '管理员身份:', $roles)->setOptions(FormBuilder::setOptions($options))->multiple(true)->required('请选择管理员身份');
}
$f[] = $this->builder->radio('status', '状态:', $formData['status'] ?? 1)->options([['label' => '开启', 'value' => 1], ['label' => '关闭', 'value' => 0]]);
return $f;
}
/**
* 添加管理员form表单获取
* @param int $level
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createForm(int $level, string $url = '/setting/admin', int $supplierId = 0)
{
return create_form('管理员添加', $this->createAdminForm($level, [], $supplierId), $this->url($url));
}
/**
* 创建管理员
* @param array $data
* @return bool
*/
public function create(array $data)
{
if ($data['conf_pwd'] != $data['pwd']) {
throw new AdminException('两次输入的密码不相同');
}
unset($data['conf_pwd']);
if ($this->dao->count(['account' => $data['account'], 'admin_type' => $data['admin_type'] ?? 1, 'is_del' => 0])) {
throw new AdminException('管理员账号已存在');
}
if ($this->dao->count(['phone' => $data['phone'], 'admin_type' => $data['admin_type'] ?? 1, 'is_del' => 0])) {
throw new AdminException('管理员电话已存在');
}
$data['pwd'] = $this->passwordHash($data['pwd']);
$data['add_time'] = time();
$data['roles'] = implode(',', $data['roles']);
return $this->transaction(function () use ($data) {
if ($this->dao->save($data)) {
return true;
} else {
throw new AdminException('添加失败');
}
});
}
/**
* 修改管理员表单
* @param int $level
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function updateForm(int $level, int $id, string $url = '/setting/admin/', $supplierId = 0)
{
$adminInfo = $this->dao->get($id);
if (!$adminInfo) {
throw new AdminException('管理员不存在!');
}
if ($adminInfo->is_del) {
throw new AdminException('管理员已经删除');
}
return create_form('管理员修改', $this->createAdminForm($level, $adminInfo->toArray(), $supplierId), $this->url($url . $id), 'PUT');
}
/**
* 修改管理员
* @param int $id
* @param array $data
* @return bool
*/
public function save(int $id, array $data)
{
if (!$adminInfo = $this->dao->get($id)) {
throw new AdminException('管理员不存在,无法修改');
}
if ($adminInfo->is_del) {
throw new AdminException('管理员已经删除');
}
//修改密码
if ($data['pwd']) {
if (!$data['conf_pwd']) {
throw new AdminException('请输入确认密码');
}
if ($data['conf_pwd'] != $data['pwd']) {
throw new AdminException('上次输入的密码不相同');
}
$adminInfo->pwd = $this->passwordHash($data['pwd']);
}
//修改账号
if (isset($data['account']) && $data['account'] != $adminInfo->account && $this->dao->isAccountUsable($data['account'], $id)) {
throw new AdminException('管理员账号已存在');
}
if (isset($data['phone']) && $data['phone'] != $adminInfo->phone && $this->dao->count(['phone' => $data['phone'], 'admin_type' => 1, 'is_del' => 0])) {
throw new AdminException('管理员电话已存在');
}
if (isset($data['roles'])) {
$adminInfo->roles = implode(',', $data['roles']);
}
$adminInfo->real_name = $data['real_name'] ?? $adminInfo->real_name;
$adminInfo->phone = $data['phone'] ?? $adminInfo->phone;
$adminInfo->account = $data['account'] ?? $adminInfo->account;
$adminInfo->head_pic = $data['head_pic'] ?? $adminInfo->head_pic;
$adminInfo->status = $data['status'];
if ($adminInfo->save()) {
return true;
} else {
return false;
}
}
/**
* 修改当前管理员信息
* @param int $id
* @param array $data
* @return bool
*/
public function updateAdmin(int $id, array $data)
{
$adminInfo = $this->dao->get($id);
if (!$adminInfo)
throw new AdminException('管理员信息未查到');
if ($adminInfo->is_del) {
throw new AdminException('管理员已经删除');
}
if ($data['head_pic'] != '') {
$adminInfo->head_pic = $data['head_pic'];
} elseif ($data['real_name'] != '') {
$adminInfo->real_name = $data['real_name'];
} elseif ($data['pwd'] != '') {
if (!password_verify($data['pwd'], $adminInfo['pwd']))
throw new AdminException('原始密码错误');
if (!$data['new_pwd'])
throw new AdminException('请输入新密码');
if (!$data['conf_pwd'])
throw new AdminException('请输入确认密码');
if ($data['new_pwd'] != $data['conf_pwd'])
throw new AdminException('两次输入的密码不一致');
$adminInfo->pwd = $this->passwordHash($data['new_pwd']);
} elseif ($data['phone'] != '') {
$verifyCode = CacheService::get('code_' . $data['phone']);
if (!$verifyCode)
throw new AdminException('请先获取验证码');
$verifyCode = substr($verifyCode, 0, 6);
if ($verifyCode != $data['code']) {
CacheService::delete('code_' . $data['phone']);
throw new AdminException('验证码错误');
}
$adminInfo->phone = $data['phone'];
}
if ($adminInfo->save()) {
return true;
} else {
return false;
}
}
/**
* 后台订单下单,评论,支付成功,后台消息提醒
*/
public function adminNewPush()
{
try {
/** @var StoreOrderServices $orderServices */
$orderServices = app()->make(StoreOrderServices::class);
$data['ordernum'] = $orderServices->count(['is_del' => 0, 'status' => 1, 'shipping_type' => 1]);
/** @var StoreProductServices $productServices */
$productServices = app()->make(StoreProductServices::class);
$data['inventory'] = $productServices->count(['type' => 5]);
/** @var StoreProductReplyServices $replyServices */
$replyServices = app()->make(StoreProductReplyServices::class);
$data['commentnum'] = $replyServices->count(['is_reply' => 0]);
/** @var UserExtractServices $extractServices */
$extractServices = app()->make(UserExtractServices::class);
$data['reflectnum'] = $extractServices->getCount(['status' => 0]);//提现
$data['msgcount'] = intval($data['ordernum']) + intval($data['inventory']) + intval($data['commentnum']) + intval($data['reflectnum']);
SocketPushJob::dispatch(['', 'ADMIN_NEW_PUSH', $data, 'admin']);
} catch (\Exception $e) {
}
}
/**
* 短信修改密码
* @param $phone
* @param $newPwd
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function resetPwd($phone, $newPwd)
{
$adminInfo = $this->dao->phoneByAdmin($phone);
if ($adminInfo) {
$adminInfo->pwd = $this->passwordHash($newPwd);
$adminInfo->save();
return true;
} else {
throw new AdminException('管理员不存在,请检查手机号码');
}
}
/**
* 获取供应商接收通知管理员
* @param int $supplier_id
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getNotifySupplierList(int $supplier_id, string $field = '*')
{
$where = [
'relation_id' => $supplier_id,
'status' => 1,
'is_del' => 0
];
$list = $this->dao->getList($where, 0, 0, $field);
return $list;
}
}

View File

@@ -0,0 +1,196 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\system\attachment;
use app\services\BaseServices;
use app\dao\system\attachment\SystemAttachmentCategoryDao;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
use think\facade\Route as Url;
/**
*
* Class SystemAttachmentCategoryServices
* @package app\services\attachment
* @mixin SystemAttachmentCategoryDao
*/
class SystemAttachmentCategoryServices extends BaseServices
{
/**
* @var SystemAttachmentCategoryDao
*/
#[Inject]
protected SystemAttachmentCategoryDao $dao;
/**
* 获取分类列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getAll(array $where)
{
$list = $this->dao->getList($where);
foreach ($list as &$item) {
$item['title'] = $item['name'];
$item['children'] = [];
if ($where['name'] == '' && $this->dao->count(['pid' => $item['id'], 'file_type' => $where['file_type']])) $item['loading'] = false;
}
return compact('list');
}
/**
* 格式化列表
* @param $menusList
* @param int $pid
* @param array $navList
* @return array
*/
public function tidyMenuTier($menusList, $pid = 0, $navList = [])
{
foreach ($menusList as $k => $menu) {
$menu['title'] = $menu['name'];
if ($menu['pid'] == $pid) {
unset($menusList[$k]);
$menu['children'] = $this->tidyMenuTier($menusList, $menu['id']);
if ($menu['children']) $menu['expand'] = true;
$navList[] = $menu;
}
}
return $navList;
}
/**
* 创建新增表单
* @param $pid
* @param int $type
* @param int $relationId
* @param int $file_type
* @return mixed
*/
public function createForm($pid, int $type = 1, int $relationId = 0, int $file_type = 1)
{
return create_form('添加分类', $this->form(['pid' => $pid], $type, $relationId, $file_type), Url::buildUrl('/file/category'), 'POST');
}
/**
* 创建编辑表单
* @param int $id
* @param int $type
* @param int $relationId
* @param int $file_type
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function editForm(int $id, int $type = 1, int $relationId = 0, int $file_type = 1)
{
$info = $this->dao->get($id);
return create_form('编辑分类', $this->form($info, $type, $relationId, $file_type), Url::buildUrl('/file/category/' . $id), 'PUT');
}
/**
* 生成表单参数
* @param $info
* @param int $type
* @param int $relationId
* @param int $file_type
* @return array
*/
public function form($info = [], int $type = 1, int $relationId = 0, int $file_type = 1)
{
return [
Form::hidden('file_type', $file_type),
Form::select('pid', '上级分类', (int)($info['pid'] ?? ''))->setOptions($this->getCateList(['pid' => 0, 'type' => $type, 'relation_id' => $relationId, 'file_type' => $file_type]))->filterable(true),
Form::input('name', '分类名称', $info['name'] ?? '')->maxlength(10),
];
}
/**
* 获取分类列表(添加修改)
* @param array $where
* @return mixed
*/
public function getCateList(array $where)
{
$list = $this->dao->getList($where);
$options = [['value' => 0, 'label' => '所有分类']];
foreach ($list as $id => $cateName) {
$options[] = ['label' => $cateName['name'], 'value' => $cateName['id']];
}
return $options;
}
/**
* 保存新建的资源
* @param array $data
*/
public function save(array $data)
{
if ($this->dao->getOne(['name' => $data['name'], 'relation_id' => $data['relation_id'] ?? 0])) {
throw new AdminException('该分类已经存在');
}
$res = $this->dao->save($data);
if (!$res) throw new AdminException('新增失败!');
return $res;
}
/**
* 保存修改的资源
* @param int $id
* @param array $data
*/
public function update(int $id, array $data)
{
$attachment = $this->dao->getOne(['name' => $data['name'], 'relation_id' => $data['relation_id'] ?? 0]);
if ($attachment && $attachment['id'] != $id) {
throw new AdminException('该分类已经存在');
}
$res = $this->dao->update($id, $data);
if (!$res) throw new AdminException('编辑失败!');
}
/**
* 删除分类
* @param int $id
*/
public function del(int $id)
{
$count = $this->dao->getCount(['pid' => $id]);
if ($count) {
throw new AdminException('请先删除下级分类!');
} else {
$res = $this->dao->delete($id);
if (!$res) throw new AdminException('请先删除下级分类!');
}
}
/**
* 获取一条数据
* @param $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getOne($where)
{
return $this->dao->getOne($where);
}
}

View File

@@ -0,0 +1,472 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\system\attachment;
use app\services\BaseServices;
use app\dao\system\attachment\SystemAttachmentDao;
use app\services\system\config\SystemStorageServices;
use crmeb\exceptions\AdminException;
use crmeb\exceptions\UploadException;
use crmeb\services\DownloadImageService;
use crmeb\services\UploadService;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class SystemAttachmentServices
* @package app\services\attachment
* @mixin SystemAttachmentDao
*/
class SystemAttachmentServices extends BaseServices
{
/**
* @var SystemAttachmentDao
*/
#[Inject]
protected SystemAttachmentDao $dao;
/**
* 获取单个资源
* @param array $where
* @param string $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getInfo(array $where, string $field = '*')
{
return $this->dao->getOne($where, $field);
}
/**
* 获取图片列表
* @param array $where
* @return array
*/
public function getImageList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $page, $limit);
if ($list) {
$site_url = sys_config('site_url');
foreach ($list as &$item) {
if ($item['file_type'] == 1) {
if ($site_url) {
$item['satt_dir'] = (strpos($item['satt_dir'], $site_url) !== false || strstr($item['satt_dir'], 'http') !== false) ? $item['satt_dir'] : $site_url . $item['satt_dir'];
$item['att_dir'] = (strpos($item['att_dir'], $site_url) !== false || strstr($item['att_dir'], 'http') !== false) ? $item['satt_dir'] : $site_url . $item['att_dir'];
}
}
$item['att_size'] = formatFileSize($item['att_size']);
$item['time'] = date('Y-m-d H:i:s', $item['time']);
}
$list = get_thumb_water($list, 'mid', ['satt_dir']);
}
$where['module_type'] = 1;
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 删除图片
* @param string $ids
*/
public function del(string $ids)
{
$ids = explode(',', $ids);
if (empty($ids)) throw new AdminException('请选择要删除的图片');
foreach ($ids as $v) {
$attinfo = $this->dao->get((int)$v);
if ($attinfo) {
try {
$upload = UploadService::init($attinfo['image_type']);
if ($attinfo['image_type'] == 1) {
$fileArr = parse_url($attinfo['att_dir']);
$filePath = $fileArr['path'] ?? $attinfo['att_dir'];
if (strpos($filePath, '/') == 0) {
$filePath = substr($filePath, 1);
}
if ($filePath) $upload->delete($filePath);
} else {
if ($attinfo['name']) $upload->delete($attinfo['name']);
}
} catch (\Throwable $e) {
}
$this->dao->delete((int)$v);
}
}
}
/**
* 图片上传
* @param int $pid
* @param string $file
* @param int $upload_type
* @param int $type
* @return mixed
*/
public function upload(int $pid, string $file, int $upload_type, int $type)
{
if ($upload_type == 0) {
$upload_type = (int)sys_config('upload_type', 1);
}
try {
$path = make_path('attach', 2, true);
$upload = UploadService::init($upload_type);
$res = $upload->to($path)->setAuthThumb((bool)sys_config('image_thumb_status'))->validate()->move($file);
if ($res === false) {
throw new UploadException($upload->getError());
} else {
$fileInfo = $upload->getUploadInfo();
//保存附件记录
if ($type == 0) $this->saveAttachment($fileInfo, $pid, $type, 0, 1, $upload_type);
return ['real_name' => $res->realName, 'src' => $res->filePath];
}
} catch (\Exception $e) {
throw new UploadException($e->getMessage());
}
}
/**
* @param array $data
* @return \crmeb\basic\BaseModel
*/
public function move(array $data)
{
$res = $this->dao->move($data);
if (!$res) throw new AdminException('移动失败或不能重复移动到同一分类下');
}
/**
* 添加信息
* @param array $data
*/
public function save(array $data)
{
$this->dao->save($data);
}
/**
* 添加附件记录
* @param $name
* @param $att_size
* @param $att_type
* @param $att_dir
* @param string $satt_dir
* @param int $pid
* @param int $imageType
* @param int $time
* @return SystemAttachment
*/
public function attachmentAdd($name, $att_size, $att_type, $att_dir, $satt_dir = '', $pid = 0, $imageType = 1, $time = 0, $module_type = 1, $file_type = 1)
{
$data['name'] = $name;
$data['att_dir'] = $att_dir;
$data['satt_dir'] = $satt_dir;
$data['att_size'] = $att_size;
$data['att_type'] = $att_type;
$data['image_type'] = $imageType;
$data['module_type'] = $module_type;
$data['time'] = $time ? $time : time();
$data['pid'] = $pid;
$data['file_type'] = $file_type;
if (!$this->dao->save($data)) {
throw new ValidateException('添加附件失败');
}
return true;
}
/**
* 推广名片生成
* @param $name
*/
public function getLikeNameList($name)
{
return $this->dao->getLikeNameList(['like_name' => $name], 0, 0);
}
/**
* 清除昨日海报
* @return bool
* @throws \Exception
*/
public function emptyYesterdayAttachment()
{
try {
$list = $this->dao->getYesterday();
foreach ($list as $key => $item) {
$upload = UploadService::init((int)$item['image_type']);
if ($item['image_type'] == 1) {
$att_dir = $item['att_dir'];
if ($att_dir && strstr($att_dir, 'uploads') !== false) {
if (strstr($att_dir, 'http') === false) $upload->delete($att_dir); else {
$filedir = substr($att_dir, strpos($att_dir, 'uploads'));
if ($filedir) $upload->delete($filedir);
}
}
} else {
if ($item['name']) $upload->delete($item['name']);
}
}
$this->dao->delYesterday();
return true;
} catch (\Exception $e) {
$this->dao->delYesterday();
return true;
}
}
/**
* 门店图片上传
* @param int $pid
* @param string $file
* @param int $relationId
* @param int $type
* @return mixed
*/
public function storeUpload(int $pid, string $file, int $relationId, int $type = 2, int $upload_type = 0, string $uploadToken = '')
{
try {
if ($upload_type == 0) {
$upload_type = (int)sys_config('upload_type', 1);
}
$path = make_path('attach/' . ($type == 4 ? 'supplier' : ($type == 2 ? 'store' : '')), 2, true);
$upload = UploadService::init($upload_type);
$res = $upload->to($path)->validate()->setAuthThumb(true)->move($file);
if ($res === false) {
throw new UploadException($upload->getError());
} else {
$fileInfo = $upload->getUploadInfo();
//保存附件记录
$this->saveAttachment($fileInfo, $pid, $type, $relationId, 1, $upload_type, $uploadToken);
return ['real_name' => $res->realName, 'src' => $res->filePath];
}
} catch (\Exception $e) {
throw new UploadException($e->getMessage());
}
}
/**
* 视频分片上传
* @param $data
* @param $file
* @param int $type
* @param int $relation_id
* @return mixed
*/
public function videoUpload($data, $file, int $type = 1, int $relation_id = 0)
{
$data['chunkNumber'] = (int)$data['chunkNumber'];
$public_dir = app()->getRootPath() . 'public';
$store_dir = '';
switch ($type) {
case 2:
$store_dir = 'store' . DIRECTORY_SEPARATOR;
break;
case 4:
$store_dir = 'supplier' . DIRECTORY_SEPARATOR;
break;
}
$dir = '/uploads/video/' . $store_dir . date('Y') . DIRECTORY_SEPARATOR . date('m') . DIRECTORY_SEPARATOR . date('d');
$all_dir = $public_dir . $dir;
if (!is_dir($all_dir)) mkdir($all_dir, 0777, true);
$filename = $all_dir . '/' . $data['filename'] . '__' . $data['chunkNumber'];
move_uploaded_file($file->getPathName(), $filename);
$res['code'] = 0;
$res['msg'] = 'error';
$res['file_path'] = '';
if ($data['chunkNumber'] == $data['totalChunks']) {
$blob = '';
for ($i = 1; $i <= $data['totalChunks']; $i++) {
$blob .= file_get_contents($all_dir . '/' . $data['filename'] . '__' . $i);
}
file_put_contents($all_dir . '/' . $data['filename'], $blob);
for ($i = 1; $i <= $data['totalChunks']; $i++) {
@unlink($all_dir . '/' . $data['filename'] . '__' . $i);
}
if (file_exists($all_dir . '/' . $data['filename'])) {
$res['code'] = 2;
$res['msg'] = 'success';
$res['file_path'] = $dir . '/' . $data['filename'];
}
} else {
if (file_exists($all_dir . '/' . $data['filename'] . '__' . $data['chunkNumber'])) {
$res['code'] = 1;
$res['msg'] = 'waiting';
$res['file_path'] = '';
}
}
$res['name'] = $res['dir'] = $res['file_path'];
if (strpos($res['file_path'], 'http') === false) {
$res['dir'] = $res['file_path'] = sys_config('site_url') . $res['file_path'];
}
if ($res['code'] == 2) {
$res['size'] = getFileHeaders($res['file_path'])['size'] ?? 0;
$this->saveAttachment($res, (int)($data['pid'] ?? 0), $type, $relation_id, 2);
}
return $res;
}
/**
* 云端上传的视频保存记录
* @param array $data
* @param int $type
* @param int $relation_id
* @param int $upload_type
* @return bool
*/
public function saveOssVideoAttachment(array $data, int $type = 1, int $relation_id = 0, int $upload_type = 1)
{
$fileInfo = [];
$fileInfo['name'] = $data['path'];
$fileInfo['cover_image'] = $data['cover_image'];
if (!$fileInfo['cover_image'] && $upload_type != 1) {//云端视频
$res = UploadService::init($upload_type)->videoCoverImage($data['path'], 'mid');
$fileInfo['cover_image'] = $res['mid'] ?? '';
}
$fileInfo['size'] = getFileHeaders($data['path'])['size'] ?? 0;
if ($upload_type != 1) {
$config = app()->make(SystemStorageServices::class)->getConfig($upload_type);
$uploadUrl = $config['domain'];
$parsedUrl = parse_url($fileInfo['name']);
$path = $parsedUrl['path'] ?? '';
$fileInfo['name'] = rtrim($uploadUrl, '/') . $path;
}
$fileInfo['real_name'] = $fileInfo['name'];
$this->saveAttachment($fileInfo, (int)$data['pid'], $type, $relation_id, 2, $upload_type);
return true;
}
/**
* 保存附件信息
* @param $fileInfo
* @param int $pid
* @param int $type
* @param int $relation_id
* @param int $file_type
* @param int $upload_type
* @return bool
*/
public function saveAttachment($fileInfo, int $pid = 0, int $type = 1, int $relation_id = 0, int $file_type = 1, int $upload_type = 1)
{
$fileType = pathinfo($fileInfo['name'], PATHINFO_EXTENSION);
if ($fileInfo && !in_array($fileType, ['xlsx', 'xls'])) {
$data['file_type'] = $file_type;
$data['type'] = $type == 0 ? 1 : $type;
$data['relation_id'] = $relation_id;
$data['name'] = $fileInfo['name'];
$data['real_name'] = $fileInfo['real_name'] ?? $fileInfo['name'] ?? '';
$data['att_dir'] = $fileInfo['dir'] ?? $fileInfo['name'] ?? '';
if ($data['att_dir'] && strpos($data['att_dir'], 'http') === false) {
$data['att_dir'] = sys_config('site_url') . $data['att_dir'];
}
$data['satt_dir'] = $fileInfo['thumb_path'] ?? $fileInfo['cover_image'] ?? '';
if ($file_type == 2) {
if (!$data['satt_dir']) {//视频 默认封面
$data['satt_dir'] = sys_config('site_url') . '/statics/images/video_default_cover.png';
}
if ($data['real_name'] && strpos($data['real_name'], '/') !== false) {
$nameArr = explode('/', $data['real_name']);
$data['real_name'] = end($nameArr) ?? $data['real_name'];
}
}
$data['att_size'] = $fileInfo['size'] ?? '';
$data['att_type'] = $fileInfo['type'] ?? '';
$data['image_type'] = $upload_type;
$data['module_type'] = 1;
$data['time'] = $fileInfo['time'] ?? time();
$data['pid'] = $pid;
$this->dao->save($data);
}
return true;
}
/**
* 网络图片上传
* @param $data
* @return bool
* @throws \Exception
*/
public function onlineUpload(array $data, int $type = 1, int $relation_id = 0)
{
//生成附件目录
if (make_path('attach', 3, true) === '') {
throw new AdminException('无法创建文件夹,请检查您的上传目录权限');
}
//上传图片
/** @var SystemAttachmentServices $systemAttachmentService */
$systemAttachmentService = app()->make(SystemAttachmentServices::class);
$siteUrl = sys_config('site_url');
foreach ($data['images'] as $image) {
try {
$uploadValue = app()->make(DownloadImageService::class)->thumb(true)->downloadOnlineImage($image);
} catch (\Throwable $e) {
throw new AdminException('文件下载失败,请更换链接');
}
if (is_array($uploadValue)) {
//TODO 拼接图片地址
if ($uploadValue['image_type'] == 1) {
$imagePath = $siteUrl . $uploadValue['path'];
} else {
$imagePath = $uploadValue['path'];
}
//写入数据库
if (!$uploadValue['is_exists']) {
$systemAttachmentService->save([
'type' => $type == 0 ? 1 : $type,
'relation_id' => $relation_id,
'name' => $uploadValue['name'],
'real_name' => $uploadValue['name'],
'att_dir' => $imagePath,
'satt_dir' => $imagePath,
'att_size' => $uploadValue['size'],
'att_type' => $uploadValue['mime'],
'image_type' => $uploadValue['image_type'],
'module_type' => 1,
'time' => time(),
'pid' => $data['pid']
]);
}
}
}
return true;
}
/**
* 删除门店、供应商附件
* @param array $where
* @param int $type
* @param int $relation_id
* @return bool
*/
public function delAttachment(array $where = [], int $type = 0, int $relation_id = 0)
{
$where['type'] = $type;
$where['relation_id'] = $relation_id;
//删除附件
$attachments = $this->dao->getColumn($where, 'att_id');
if ($attachments) {
$idsArr = array_chunk($attachments, 100);
foreach ($idsArr as $ids) {
$this->del(implode(',', $ids));
}
}
return true;
}
}

View File

@@ -0,0 +1,130 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\config;
use app\dao\store\StoreConfigDao;
use app\services\BaseServices;
use crmeb\services\SystemConfigService;
/**
* Class ConfigServices
* @package app\services\system\config
* @mixin StoreConfigDao
*/
class ConfigServices extends BaseServices
{
//打印机类型
const PRINTER_NAME = [
1 => 'yi_lian_yun',
2 => 'fei_e_yun',
];
//平台打印机配置
const PRINTER_KEY = [
'switch' => 'pay_success_printing_switch',
'print_type' => 'print_type',
'printing_client_id', 'printing_api_key', 'develop_id', 'terminal_number',
'fey_user', 'fey_ukey', 'fey_sn',
];
//配置字段对应
const PRINTER_CONFIG_KEY = [
'yi_lian_yun' => ['printing_client_id' => 'clientId', 'printing_api_key' => 'apiKey', 'develop_id' => 'partner', 'terminal_number' => 'terminal'],
'fei_e_yun' => ['fey_user' => 'feyUser', 'fey_ukey' => 'feyUkey', 'fey_sn' => 'feySn'],
];
//门店打印机配置
const STORE_PRINTER_KEY = [
'switch' => 'store_pay_success_printing_switch',
'print_type' => 'store_print_type',
'store_printing_client_id', 'store_printing_api_key', 'store_develop_id', 'store_terminal_number',
'store_fey_user', 'store_fey_ukey', 'store_fey_sn',
];
//门店配置字段对应
const STORE_PRINTER_CONFIG_KEY = [
'yi_lian_yun' => ['store_printing_client_id' => 'clientId', 'store_printing_api_key' => 'apiKey', 'store_develop_id' => 'partner', 'store_terminal_number' => 'terminal'],
'fei_e_yun' => ['store_fey_user' => 'feyUser', 'store_fey_ukey' => 'feyUkey', 'store_fey_sn' => 'feySn'],
];
//供应商打印机配置
const SUPPLIER_PRINTER_KEY = [
'switch' => 'store_pay_success_printing_switch',
'print_type' => 'store_print_type',
'store_printing_client_id', 'store_printing_api_key', 'store_develop_id', 'store_terminal_number',
'store_fey_user', 'store_fey_ukey', 'store_fey_sn',
];
//供应商配置字段对应
const SUPPLIER_PRINTER_CONFIG_KEY = [
'yi_lian_yun' => ['store_printing_client_id' => 'clientId', 'store_printing_api_key' => 'apiKey', 'store_develop_id' => 'partner', 'store_terminal_number' => 'terminal'],
'fei_e_yun' => ['store_fey_user' => 'feyUser', 'store_fey_ukey' => 'feyUkey', 'store_fey_sn' => 'feySn'],
];
const CONFIG_TYPE = [
'printing_deploy' => self::PRINTER_KEY,
'store_printing_deploy' => self::STORE_PRINTER_KEY,
'supplier_printing_deploy' => self::SUPPLIER_PRINTER_KEY
];
/**
* StoreConfigServices constructor.
* @param StoreConfigDao $dao
*/
public function __construct(StoreConfigDao $dao)
{
$this->dao = $dao;
}
/**
* 获取小票打印配置
* @param int $type
* @param int $relation_id
* @return array
*/
public function getPrintingConfig(int $type = 0, int $relation_id = 0) : array
{
/** @var SystemConfigService $configService */
$configService = app('sysConfig');
switch ($type) {
case 0://平台
$keys = self::PRINTER_KEY;
$configKey = self::PRINTER_CONFIG_KEY;
break;
case 2://供应商
$keys = self::SUPPLIER_PRINTER_KEY;
$configKey = self::SUPPLIER_PRINTER_CONFIG_KEY;
$configService->setSupplier($relation_id);
break;
default:
$keys = self::PRINTER_KEY;
$configKey = self::PRINTER_CONFIG_KEY;
break;
}
$key = array_values($keys);
$config = $configService->more($key);
$switch = $config[$keys['switch']] ?? 0;
$printType = $config[$keys['print_type']] ?? 1;
$name = self::PRINTER_NAME[$printType] ?? 'yi_lian_yun';
$configKey = $configKey[$name];
$configData = [];
foreach ($config as $key => $value) {
if (isset($configKey[$key])) {
$configData[$configKey[$key]] = $value;
}
}
return [$switch, $name, $configData];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\config;
use app\dao\system\config\SystemConfigTabDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
/**
* 系统配置分类
* Class SystemConfigTabServices
* @package app\services\system\config
* @mixin SystemConfigTabDao
*/
class SystemConfigTabServices extends BaseServices
{
/**
* @var SystemConfigTabDao
*/
#[Inject]
protected SystemConfigTabDao $dao;
/**
* 系统设置头部分类读取
* @param int $pid
* @param int $is_store
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getConfigTab(int $pid, int $is_store = 0)
{
$list = $this->dao->getConfigTabAll(['status' => 1, 'pid' => $pid, 'is_store' => $is_store], ['id', 'id as value', 'title as label', 'pid', 'icon', 'type'], $pid ? [] : [['type', '=', '0']]);
return get_tree_children($list);
}
/**
* 获取配置分类列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getConfgTabList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getConfgTabList($where, $page, $limit);
$count = $this->dao->count($where);
$menusValue = [];
foreach ($list as $item) {
$menusValue[] = $item->getData();
}
$list = get_tree_children($menusValue);
return compact('list', 'count');
}
/**
* 获取配置分类选择下拉树
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getSelectForm()
{
$menuList = $this->dao->getConfigTabAll([], ['id as value', 'pid', 'title as label']);
$menus = [['value' => 0, 'label' => '顶级按钮']];
$list = get_tree_children($menuList, 'children', 'value');
$menus = array_merge($menus, $list);
// foreach ($list as $menu) {
// $menus[] = ['value' => $menu['id'], 'label' => $menu['html'] . $menu['title']];
// }
return $menus;
}
/**
* 创建form表单
* @param array $formData
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function createConfigTabForm(array $formData = [])
{
$pid[] = isset($formData['pid']) ? $formData['pid'] : 0;
// $form[] = Form::radio('is_store', '类型', $formData['is_store'] ?? 0)->options([['value' => 0, 'label' => '总后台'], ['value' => 1, 'label' => '门店后台']]);
$form[] = Form::cascader('pid', '父级分类:', $pid)->data($this->getSelectForm())->changeOnSelect(true);
$form[] = Form::input('title', '分类名称:', $formData['title'] ?? '');
$form[] = Form::input('eng_title', '分类字段英文:', $formData['eng_title'] ?? '');
$form[] = Form::frameInput('icon', '图标:', $this->url('admin/widget.widgets/icon', ['fodder' => 'icon'], true), $formData['icon'] ?? '')->icon('ios-ionic')->height('435px');
$form[] = Form::radio('type', '类型:', $formData['type'] ?? 0)->options([
['value' => 0, 'label' => '系统'],
['value' => 3, 'label' => '其它']
]);
$form[] = Form::radio('status', '状态:', $formData['status'] ?? 1)->options([['value' => 1, 'label' => '显示'], ['value' => 2, 'label' => '隐藏']]);
$form[] = Form::number('sort', '排序:', (int)($formData['sort'] ?? 0))->min(0);
return $form;
}
/**
* 添加配置分类表单
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createForm()
{
return create_form('添加配置分类', $this->createConfigTabForm(), $this->url('/setting/config_class'));
}
/**
* 修改配置分类表单
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function updateForm(int $id)
{
$configTabInfo = $this->dao->get($id);
if (!$configTabInfo) {
throw new AdminException('没有查到数据,无法修改!');
}
return create_form('编辑配置分类', $this->createConfigTabForm($configTabInfo->toArray()), $this->url('/setting/config_class/' . $id), 'PUT');
}
}

View File

@@ -0,0 +1,385 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\config;
use app\dao\system\config\SystemGroupDataDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\FormBuilder as Form;
use think\annotation\Inject;
/**
* 组合数据数据集
* Class SystemGroupDataServices
* @package app\services\system\config
* @mixin SystemGroupDataDao
*/
class SystemGroupDataServices extends BaseServices
{
/**
* @var SystemGroupDataDao
*/
#[Inject]
protected SystemGroupDataDao $dao;
/**
* @param int $id
* @return mixed|string
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author 等风来
* @email 136327134@qq.com
* @date 2022/11/3
*/
public function cacheInfoValue(int $id)
{
$groupData = $this->cacheRemember($id, function () use ($id) {
$res = $this->dao->get($id);
return $res ? $res->toArray() : null;
});
return $groupData['value'] ?? '';
}
/**
* 获取某个配置下的数据从新组合成新得数据返回
* @param string $configName
* @param int $limit
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getConfigNameValue(string $configName, int $limit = 0)
{
/** @var SystemGroupServices $systemGroupServices */
$systemGroupServices = app()->make(SystemGroupServices::class);
$value = $this->dao->getGroupDate((int)$systemGroupServices->getConfigNameId($configName), $limit);
$data = [];
foreach ($value as $key => $item) {
$data[$key]["id"] = $item["id"];
if (isset($item['status'])) $data[$key]["status"] = $item["status"];
$fields = json_decode($item["value"], true) ?: [];
foreach ($fields as $index => $field) {
if ($field['type'] == 'upload') {
$data[$key][$index] = set_file_url($field['value']);
} else {
$data[$key][$index] = $field["value"];
}
}
}
return $data;
}
/**
* 获取组合数据列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getGroupDataList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getGroupDataList($where, $page, $limit);
$count = $this->dao->count($where);
$type = '';
$gid = (int)$where['gid'];
/** @var SystemGroupServices $services */
$services = app()->make(SystemGroupServices::class);
$group = $services->getOne(['id' => $gid], 'id,config_name,fields');
$header = json_decode($group['fields'], true) ?? [];
$title = [];
$param = [];
foreach ($header as $item) {
if ($group['config_name'] == 'order_details_images' && $item['title'] == 'order_status') {
$status = str_replace("\r\n", "\n", $item["param"]);//防止不兼容
$status = explode("\n", $status);
if (is_array($status) && !empty($status)) {
foreach ($status as $index => $v) {
$vl = explode('=>', $v);
if (isset($vl[0]) && isset($vl[1])) {
$param[$vl[0]] = $vl[1];
}
}
}
}
if ($item['type'] == 'upload' || $item['type'] == 'uploads') {
$title[$item['title']] = [];
$type = $item['title'];
} else {
$title[$item['title']] = '';
}
}
foreach ($list as $key => $value) {
$list[$key] = array_merge($value, $title);
$infos = json_decode($value['value'], true) ?: [];
foreach ($infos as $index => $info) {
if ($group['config_name'] == 'user_recharge_quota' && $index == 'is_welfare') {
$info['value'] = $info['value'] == 1 ? '是' : '否';
}
if ($group['config_name'] == 'order_details_images' && $index == 'order_status') {
$list[$key][$index] = ($param[$info['value']] ?? '') . '/' . $info['value'];
} else {
if ($info['type'] == 'upload') {
$list[$key][$index] = [set_file_url($info['value'])];
} elseif ($info['type'] == 'checkbox') {
$list[$key][$index] = implode(",", $info["value"]);
} else {
$list[$key][$index] = $info['value'];
}
}
}
unset($list[$key]['value']);
}
return compact('list', 'count', 'type', 'gid');
}
/**
* 根据gid判断出是否能再次添加组合数据
* @param int $gid
* @param int $count
* @param string $key
* @return bool
*/
public function isGroupGidSave(int $gid, int $count, string $key): bool
{
/** @var SystemGroupServices $services */
$services = app()->make(SystemGroupServices::class);
$configName = $services->value(['id' => $gid], 'config_name');
if ($configName == $key) {
return $this->dao->count(['gid' => $gid]) >= $count;
} else {
return false;
}
}
/**
* 创建表单
* @param int $gid
* @param array $groupData
* @return mixed
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createGroupForm(int $gid, array $groupData = [])
{
$groupDataValue = isset($groupData["value"]) ? json_decode($groupData["value"], true) : [];
/** @var SystemGroupServices $services */
$services = app()->make(SystemGroupServices::class);
$configName = $services->value(['id' => $gid], 'config_name');
$fields = $services->getValueFields($gid);
$f[] = Form::hidden('gid', $gid);
foreach ($fields as $key => $value) {
$info = [];
if (isset($value["param"])) {
$value["param"] = str_replace("\r\n", "\n", $value["param"]);//防止不兼容
$params = explode("\n", $value["param"]);
if (is_array($params) && !empty($params)) {
foreach ($params as $index => $v) {
$vl = explode('=>', $v);
if (isset($vl[0]) && isset($vl[1])) {
$info[$index]["value"] = $vl[0];
$info[$index]["label"] = $vl[1];
}
}
}
}
$fvalue = isset($groupDataValue[$value['title']]['value']) ? $groupDataValue[$value['title']]['value'] : '';
switch ($value["type"]) {
case 'input':
if (($gid == 55 && $value['title'] == 'sign_num') || in_array($configName, ['user_recharge_quota'])) {
if ('user_recharge_quota' === $configName) {
$f[] = Form::number($value["title"], $value["name"] . ':', $fvalue)->min(0)->max(999999)->required();
} else {
$f[] = Form::number($value["title"], $value["name"] . ':', (int)$fvalue)->precision(0);
}
} else {
$f[] = Form::input($value["title"], $value["name"] . ':', $fvalue);
}
break;
case 'textarea':
$f[] = Form::input($value["title"], $value["name"] . ':', $fvalue)->type('textarea')->placeholder($value['param']);
break;
case 'radio':
$f[] = Form::radio($value["title"], $value["name"] . ':', $fvalue ?: $info[0]["value"] ?? '')->options($info);
break;
case 'checkbox':
$f[] = Form::checkbox($value["title"], $value["name"] . ':', $fvalue ?: $info[0] ?? '')->options($info);
break;
case 'select':
$f[] = Form::select($value["title"], $value["name"] . ':', $fvalue !== '' ? $fvalue : $info[0] ?? '')->options($info)->multiple(false);
break;
case 'upload':
if (!empty($fvalue)) {
$image = is_string($fvalue) ? $fvalue : $fvalue[0];
} else {
$image = '';
}
$f[] = Form::frameImage($value["title"], $value["name"] . ':', $this->url('admin/widget.images/index', ['fodder' => $value["title"], 'big' => 1], true), $image)->icon('ios-image')->width('960px')->height('505px')->modal(['footer-hide' => true]);
break;
case 'uploads':
if ($fvalue) {
if (is_string($fvalue)) $fvalue = [$fvalue];
$images = !empty($fvalue) ? $fvalue : [];
} else {
$images = [];
}
$f[] = Form::frameImages($value["title"], $value["name"] . ':', $this->url('admin/widget.images/index', ['fodder' => $value["title"], 'big' => 1, 'type' => 'many', 'maxLength' => 5], true), $images)->maxLength(5)->icon('ios-images')->width('960px')->height('505px')->modal(['footer-hide' => true])->spin(0);
break;
default:
$f[] = Form::input($value["title"], $value["name"] . ':', $fvalue);
break;
}
}
$f[] = Form::number('sort', '排序:', (int)($groupData["sort"] ?? 1))->min(0);
$f[] = Form::radio('status', '状态:', (int)($groupData["status"] ?? 1))->options([['value' => 1, 'label' => '显示'], ['value' => 0, 'label' => '隐藏']]);
return $f;
}
/**
* 获取添加组合数据表单
* @param int $gid
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function createForm(int $gid)
{
return create_form('添加数据', $this->createGroupForm($gid), $this->url('/setting/group_data'));
}
/**
* 获取修改组合数据表单
* @param int $gid
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function updateForm(int $gid, int $id)
{
$groupData = $this->dao->get($id);
if (!$groupData) {
throw new AdminException('修改失败未查到数据!');
}
return create_form('编辑数据', $this->createGroupForm($gid, $groupData->toArray()), $this->url('/setting/group_data/' . $id), 'PUT');
}
/**
* 根据id获取当前记录中的数据
* @param $id
* @return mixed
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function getDateValue($id)
{
$value = $this->dao->get($id);
$data = [];
if ($value) {
$data["id"] = $value["id"];
$data["status"] = $value["status"];
$fields = json_decode($value["value"], true);
foreach ($fields as $index => $field) {
$data[$index] = $field["value"];
}
}
return $data;
}
/**
* 根据id获取数据
* @param array $ids
* @param string $field
*/
public function getGroupDataColumn(array $ids)
{
$systemGroup = [];
if (!empty($ids)) {
$systemGroupData = $this->dao->idByGroupList($ids);
if (!empty($systemGroupData))
$systemGroup = array_combine(array_column($systemGroupData, 'id'), $systemGroupData);
}
return $systemGroup;
}
/**
* 添加数据配置
* @param $data
*/
public function saveAll(array $data)
{
return $this->dao->saveAll($data);
}
/**
* 根据gid删除数据
* @param int $gid
* @return mixed
*/
public function delGroupDate(int $gid)
{
return $this->dao->delGroupDate($gid);
}
/**
* 批量保存组合数据
* @param array $params 数据参数
* @param string $config_name 配置名称
* @return bool
* @throws AdminException
*/
public function saveAllData(array $params, string $config_name)
{
/** @var SystemGroupServices $systemGroupServices */
$systemGroupServices = app()->make(SystemGroupServices::class);
$gid = $systemGroupServices->value(['config_name' => $config_name], 'id');
$group = $systemGroupServices->getOne(['id' => $gid], 'id,config_name,fields');
$fields = json_decode($group['fields'], true) ?? [];
$this->transaction(function () use ($gid, $params, $fields) {
$this->delGroupDate($gid);
$data = [];
$sort = count($params);
foreach ($params as $k => $v) {
$value = [];
foreach ($v as $key => $param) {
foreach ($fields as $index => $field) {
if ($key == $field["title"]) {
if ($param == "")
throw new AdminException($field["name"] . "不能为空!");
else {
$value[$key]["type"] = $field["type"];
$value[$key]["value"] = $param;
}
}
}
}
$data[$k] = [
"gid" => $gid,
"add_time" => time(),
"value" => json_encode($value),
"sort" => $sort,
"status" => $v["status"] ?? 1
];
$sort--;
}
$this->dao->saveAll($data);
});
\crmeb\services\GroupDataService::clear();
return true;
}
}

View File

@@ -0,0 +1,93 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\config;
use app\dao\system\config\SystemGroupDao;
use app\services\BaseServices;
use think\annotation\Inject;
/**
* 组合数据
* Class SystemGroupServices
* @package app\services\system\config
* @mixin SystemGroupDao
*/
class SystemGroupServices extends BaseServices
{
/**
* @var SystemGroupDao
*/
#[Inject]
protected SystemGroupDao $dao;
/**
* 获取组合数据列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getGroupList(array $where, array $field = ['*'])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getGroupList($where, $field, $page, $limit);
$count = $this->dao->count($where);
foreach ($list as $key => $value) {
if (isset($value['fields'])) {
$list[$key]['typelist'] = $value['fields'];
unset($list[$key]['fields']);
}
}
return compact('list', 'count');
}
/**
* 获取组合数据tab下的header头部
* @param int $id
* @return array
*/
public function getGroupDataTabHeader(int $id)
{
$data = $this->getValueFields($id);
$header = [];
foreach ($data as $key => $item) {
if ($item['type'] == 'upload' || $item['type'] == 'uploads') {
$header[$key]['key'] = $item['title'];
$header[$key]['minWidth'] = 60;
$header[$key]['type'] = 'img';
} elseif ($item['title'] == 'url' || $item['title'] == 'wap_url' || $item['title'] == 'link' || $item['title'] == 'wap_link') {
$header[$key]['key'] = $item['title'];
$header[$key]['minWidth'] = 200;
} else {
$header[$key]['key'] = $item['title'];
$header[$key]['minWidth'] = 100;
}
$header[$key]['title'] = $item['name'];
}
array_unshift($header, ['key' => 'id', 'title' => '编号', 'width' => 55]);
array_push($header, ['slot' => 'status', 'title' => '是否可用', 'minWidth' => 80], ['key' => 'sort', 'title' => '排序', 'minWidth' => 80], ['slot' => 'action', 'fixed' => 'right', 'title' => '操作', 'minWidth' => 120]);
return compact('header');
}
/**
* 获取组合数据fields字段
* @param int $id
* @return array|mixed
*/
public function getValueFields(int $id)
{
return json_decode($this->dao->value(['id' => $id], 'fields'), true) ?: [];
}
}

View File

@@ -0,0 +1,597 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\config;
use app\dao\system\config\SystemStorageDao;
use app\services\BaseServices;
use crmeb\services\FormBuilder;
use crmeb\services\UploadService;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* Class SystemStorageServices
* @package app\services\system\config
* @mixin SystemStorageDao
*/
class SystemStorageServices extends BaseServices
{
/**
* @var SystemStorageDao
*/
#[Inject]
protected SystemStorageDao $dao;
/**
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getList(array $where)
{
[$page, $limit] = $this->getPageValue();
$config = $this->getStorageConfig((int)$where['type']);
$where['access_key'] = $config['accessKey'];
$list = $this->dao->getStorageList($where, ['*'], $page, $limit);
foreach ($list as &$item) {
$item['_add_time'] = date('Y-m-d H:i:s', $item['add_time']);
$item['_update_time'] = date('Y-m-d H:i:s', $item['update_time']);
$service = UploadService::init($item['type']);
$region = $service->getRegion();
foreach ($region as $value) {
if (strstr($item['region'], $value['value'])) {
$item['_region'] = $value['label'];
}
}
if (!$item['cname']) {
$item['cname'] = str_replace(['http://', 'https://'], '', $item['domain']);
}
}
$count = $this->dao->getCount($where);
return compact('list', 'count');
}
/**
* @param int $type
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function getFormStorage(int $type)
{
$upload = UploadService::init($type);
$config = $this->getStorageConfig($type);
$ruleConfig = [];
if (!$config['accessKey']) {
$ruleConfig = [
FormBuilder::input('accessKey', 'AccessKeyId:', $config['accessKey'] ?? '')->required(),
FormBuilder::input('secretKey', 'AccessKeySecret:', $config['secretKey'] ?? '')->required(),
];
}
if ($type === 4 && isset($config['appid']) && !$config['appid']) {
$ruleConfig[] = FormBuilder::input('appid', 'APPID', $config['appid'] ?? '')->required();
}
$rule = [
FormBuilder::input('name', '空间名称')->required(),
FormBuilder::select('region', '空间区域')->options($upload->getRegion())->required(),
FormBuilder::radio('acl', '读写权限', 'public-read')->options([
['label' => '公共读(推荐)', 'value' => 'public-read'],
['label' => '公共读写', 'value' => 'public-read-write'],
])->required(),
];
$rule = array_merge($ruleConfig, $rule);
return create_form('添加云空间', $rule, '/setting/config/storage/' . $type);
}
/**
* @param int $type
* @return array
*/
public function getStorageConfig(int $type)
{
$config = [
'accessKey' => '',
'secretKey' => ''
];
switch ($type) {
case 2://七牛
$config = [
'accessKey' => sys_config('qiniu_accessKey', ''),
'secretKey' => sys_config('qiniu_secretKey', ''),
];
break;
case 3:// oss 阿里云
$config = [
'accessKey' => sys_config('accessKey', ''),
'secretKey' => sys_config('secretKey', ''),
];
break;
case 4:// cos 腾讯云
$config = [
'accessKey' => sys_config('tengxun_accessKey', ''),
'secretKey' => sys_config('tengxun_secretKey', ''),
'appid' => sys_config('tengxun_appid', ''),
];
break;
case 5:// cos 京东云
$config = [
'accessKey' => sys_config('jd_accessKey', ''),
'secretKey' => sys_config('jd_secretKey', ''),
'storageRegion' => sys_config('jd_storage_region'),
];
break;
case 6:// cos 华为云
$config = [
'accessKey' => sys_config('hw_accessKey', ''),
'secretKey' => sys_config('hw_secretKey', ''),
];
break;
case 7:// cos 天翼云
$config = [
'accessKey' => sys_config('ty_accessKey', ''),
'secretKey' => sys_config('ty_secretKey', ''),
];
break;
}
return $config;
}
/**
* @param int $type
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function getFormStorageConfig(int $type)
{
$config = $this->getStorageConfig($type);
$rule = [
FormBuilder::hidden('type', $type),
FormBuilder::input('accessKey', 'AccessKeyId:', $config['accessKey'] ?? '')->required(),
FormBuilder::input('secretKey', 'AccessKeySecret:', $config['secretKey'] ?? '')->required(),
];
if ($type === 4) {
$rule[] = FormBuilder::input('appid', 'APPID', $config['appid'] ?? '')->required();
}
if ($type === 5) {
$rule[] = FormBuilder::input('storageRegion', 'Region', $config['storageRegion'] ?? '')->required();
}
return create_form('配置信息', $rule, '/setting/config/storage/config');
}
/**
* 删除空间
* @param int $id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function deleteStorage(int $id)
{
$storageInfo = $this->dao->get(['is_delete' => 0, 'id' => $id]);
if (!$storageInfo) {
throw new ValidateException('删除的云存储不存在');
}
if ($storageInfo->status) {
throw new ValidateException('云存储正在使用中,需要启动其他空间才能删除');
}
try {
$upload = UploadService::init($storageInfo->type);
$res = $upload->deleteBucket($storageInfo->name, $storageInfo->region);
if (false === $res) {
throw new ValidateException($upload->getError());
}
} catch (\Throwable $e) {
if ($e->getMessage() == 'The specified bucket does not exist.') {
$storageInfo->is_delete = 1;
$storageInfo->save();
return true;
}
throw new ValidateException($e->getMessage());
}
$storageInfo->is_delete = 1;
$storageInfo->save();
return true;
}
/**
* 保存云存储配置
* @param int $type 存储类型(2:七牛 3:阿里云 4:腾讯云 5:京东云 6:华为云 7:天翼云)
* @param array $data 配置数据
* @return bool|void
*/
public function saveConfig(int $type, array $data)
{
//保存配置信息
if (1 !== $type) {
$accessKey = $secretKey = $appid = $storageRegion = '';
if (isset($data['accessKey']) && isset($data['secretKey']) && $data['accessKey'] && $data['secretKey']) {
$accessKey = $data['accessKey'];
$secretKey = $data['secretKey'];
unset($data['accessKey'], $data['secretKey']);
}
if (isset($data['appid']) && $data['appid']) {
$appid = $data['appid'];
unset($data['appid']);
}
if (isset($data['storageRegion']) && $data['storageRegion']) {
$storageRegion = $data['storageRegion'];
unset($data['storageRegion']);
}
if (!$accessKey || !$secretKey) {
return true;
}
/** @var SystemConfigServices $make */
$make = app()->make(SystemConfigServices::class);
switch ($type) {
case 2://七牛
$make->update('qiniu_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
$make->update('qiniu_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
break;
case 3:// oss 阿里云
$make->update('accessKey', ['value' => json_encode($accessKey)], 'menu_name');
$make->update('secretKey', ['value' => json_encode($secretKey)], 'menu_name');
break;
case 4:// cos 腾讯云
$make->update('tengxun_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
$make->update('tengxun_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
$make->update('tengxun_appid', ['value' => json_encode($appid)], 'menu_name');
break;
case 5:// oss 京东云
$make->update('jd_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
$make->update('jd_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
$make->update('jd_storage_region', ['value' => json_encode($storageRegion)], 'menu_name');
break;
case 6:// oss 华为云
$make->update('hw_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
$make->update('hw_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
break;
case 7:// oss 天翼云
$make->update('ty_accessKey', ['value' => json_encode($accessKey)], 'menu_name');
$make->update('ty_secretKey', ['value' => json_encode($secretKey)], 'menu_name');
break;
}
\crmeb\services\SystemConfigService::clear();
}
}
/**
* 保存云存储
* @param int $type
* @param array $data
* @return mixed
*/
public function saveStorage(int $type, array $data)
{
//保存配置信息
$this->saveConfig($type, $data);
$name = $data['name'];
switch ($type) {
case 3://阿里云oss
$data['region'] = $this->getReagionHost($type, $data['region']);
break;
case 4://腾讯云cos
$name = $data['name'] . '-' . sys_config('tengxun_appid');
break;
}
if ($this->dao->count(['type' => $type, 'name' => $name])) {
throw new ValidateException('云空间名称不能重复');
}
//保存云存储
$data['type'] = $type;
$upload = UploadService::init($type);
$res = $upload->createBucket($data['name'], $data['region'], $data['acl']);
if (false === $res) {
throw new ValidateException($upload->getError());
}
$data['domain'] = $this->getDomain($type, $data['name'], $data['region'], sys_config('tengxun_appid'));
if (2 === $type) {
$domianList = $upload->getDomian($data['name']);
$data['domain'] = $domianList[count($domianList) - 1];
} else {
$data['cname'] = $data['domain'];
}
$data['name'] = $name;
$data['add_time'] = time();
$data['update_time'] = time();
$config = $this->getStorageConfig($type);
$data['access_key'] = $config['accessKey'];
return $this->dao->save($data);
}
/**
* 同步云储存桶
* @param int $type
* @return bool
*/
public function synchronization(int $type)
{
$data = [];
switch ($type) {
case 2://七牛
$config = $this->getStorageConfig($type);
$upload = UploadService::init($type);
$list = $upload->listbuckets();
if (false === $list) {
throw new ValidateException('同步失败,失败原因:' . $upload->getError());
}
foreach ($list as $item) {
if (!$this->dao->count(['name' => $item['id'], 'is_delete' => 0, 'access_key' => $config['accessKey']])) {
$data[] = [
'type' => $type,
'access_key' => $config['accessKey'],
'name' => $item['id'],
'region' => $item['region'],
'acl' => $item['private'] == 0 ? 'public-read' : 'private',
'status' => 0,
'is_delete' => 0,
'add_time' => time(),
'update_time' => time()
];
}
}
break;
case 3:// oss 阿里云
$upload = UploadService::init($type);
$list = $upload->listbuckets();
$config = $this->getStorageConfig($type);
foreach ($list as $item) {
if (!$this->dao->count(['name' => $item['name'], 'is_delete' => 0, 'access_key' => $config['accessKey']])) {
$region = $this->getReagionHost($type, $item['location']);
$data[] = [
'type' => $type,
'access_key' => $config['accessKey'],
'name' => $item['name'],
'region' => $region,
'acl' => 'public-read',
'domain' => $this->getDomain($type, $item['name'], $region),
'status' => 0,
'is_delete' => 0,
'add_time' => strtotime($item['createTime']),
'update_time' => time()
];
}
}
break;
case 4:// cos 腾讯云
$upload = UploadService::init($type);
$list = $upload->listbuckets();
if (!empty($list['Name'])) {
$newList = $list;
$list = [];
$list[] = $newList;
}
$config = $this->getStorageConfig($type);
foreach ($list as $item) {
if (($id = $this->dao->value(['name' => $item['Name'], 'is_delete' => 0, 'access_key' => $config['accessKey']], 'id'))) {
$this->dao->update($id, [
'update_time' => time(),
'region' => $item['Location'],
'name' => $item['Name'],
'domain' => sys_config('tengxun_appid') ? $this->getDomain($type, $item['Name'], $item['Location']) : '',
'cname' => str_replace('https://','',sys_config('tengxun_appid') ? $this->getDomain($type, $item['Name'], $item['Location']) : ''),
]);
} else {
$data[] = [
'type' => $type,
'access_key' => $config['accessKey'],
'name' => $item['Name'],
'region' => $item['Location'],
'acl' => 'public-read',
'status' => 0,
'domain' => sys_config('tengxun_appid') ? $this->getDomain($type, $item['Name'], $item['Location']) : '',
'cname' => str_replace('https://','',sys_config('tengxun_appid') ? $this->getDomain($type, $item['Name'], $item['Location']) : ''),
'is_delete' => 0,
'add_time' => strtotime($item['CreationDate']),
'update_time' => time()
];
}
}
break;
case 5:// cos 京东云
$upload = UploadService::init($type);
$list = $upload->listbuckets();
$config = $this->getStorageConfig($type);
$location = $config['storageRegion'] ?? 'cn-north-1';
foreach ($list as $item) {
if (!$this->dao->count(['name' => $item['Name'], 'access_key' => $config['accessKey']])) {
$data[] = [
'type' => $type,
'access_key' => $config['accessKey'],
'name' => $item['Name'],
'region' => $location,
'acl' => 'public-read',
'status' => 0,
'domain' => $this->getDomain($type, $item['Name'], $location),
'is_delete' => 0,
'add_time' => time(),
'update_time' => time()
];
}
}
break;
case 6:// cos 华为云
case 7:// cos 天翼云
$upload = UploadService::init($type);
$list = $upload->listbuckets();
if (!empty($list['Name'])) {
$newList = $list;
$list = [];
$list[] = $newList;
}
$config = $this->getStorageConfig($type);
foreach ($list as $item) {
if (!$this->dao->count(['name' => $item['Name'], 'access_key' => $config['accessKey']])) {
$data[] = [
'type' => $type,
'access_key' => $config['accessKey'],
'name' => $item['Name'],
'region' => $item['Location'],
'acl' => 'public-read',
'status' => 0,
'domain' => $this->getDomain($type, $item['Name'], $item['Location']),
'is_delete' => 0,
'add_time' => strtotime($item['CreationDate']),
'update_time' => time()
];
}
}
break;
}
if ($data) {
$this->dao->saveAll($data);
}
return true;
}
/**
* @param int $type
* @param string $reagion
* @return mixed|string
*/
public function getReagionHost(int $type, string $reagion)
{
$upload = UploadService::init($type);
$reagionList = $upload->getRegion();
foreach ($reagionList as $item) {
if (strstr($item['value'], $reagion) !== false) {
return $item['value'];
}
}
return '';
}
/**
* 获取域名
* @param int $type
* @param string $name
* @param string $reagion
* @param string $appid
* @return string
*/
public function getDomain(int $type, string $name, string $reagion, string $appid = '')
{
$domainName = '';
switch ($type) {
case 3:// oss 阿里云
$domainName = 'https://' . $name . '.' . $reagion;
break;
case 4:// cos 腾讯云
$domainName = 'https://' . $name . ($appid ? '-' . $appid : '') . '.cos.' . $reagion . '.myqcloud.com';
break;
case 5:// cos 京东云
$domainName = 'https://' . $name . '.s3.' . $reagion . '.jdcloud-oss.com';
break;
case 6:// cos 华为云
$domainName = 'https://' . $name . '.obs.' . $reagion . '.myhuaweicloud.com';
break;
case 7:// cos 天翼云
$domainName = 'https://' . $name . '.obs.' . $reagion . '.ctyun.cn';
break;
}
return $domainName;
}
/**
* 获取云存储配置
* @param int $type
* @return array|string[]
*/
public function getConfig(int $type)
{
$res = ['name' => '', 'region' => '', 'domain' => '', 'cname' => ''];
try {
$config = $this->dao->get(['type' => $type, 'status' => 1, 'is_delete' => 0]);
if ($config) {
return ['name' => $config->name, 'region' => $config->region, 'domain' => $config->domain, 'cname' => $config->cname];
}
} catch (\Throwable $e) {
}
return $res;
}
/**
* 获取修改域名表单
* @param int $id
* @return array
* @throws \FormBuilder\Exception\FormBuilderException
*/
public function getUpdateDomainForm(int $id)
{
$domain = $this->dao->value(['id' => $id], 'domain');
$rule = [
FormBuilder::input('domain', '空间域名', $domain),
];
return create_form('修改空间域名', $rule, '/setting/config/storage/domain/' . $id);
}
/**
* 修改域名并绑定
* @param int $id
* @param string $domain
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function updateDomain(int $id, string $domain, array $data = [])
{
$info = $this->dao->get($id);
if (!$info) {
throw new ValidateException('没有查询到数据');
}
if ($info->domain != $domain) {
if (4 === ((int)$info->type)) {
$info->domain = $domain;
return $info->save();
}
$info->domain = $domain;
$upload = UploadService::init($info->type);
//是否添加过域名不存在需要绑定域名
$domainList = $upload->getDomian($info->name, $info->region);
$domainParse = parse_url($domain);
if (!in_array($domainParse['host'], $domainList)) {
//绑定域名到云储存桶
$res = $upload->bindDomian($info->name, $domain, $info->region);
if (false === $res) {
throw new ValidateException($upload->getError());
}
}
//七牛云需要通过接口获取cname
if (2 === ((int)$info->type)) {
$resDomain = $upload->getDomianInfo($domain);
$info->cname = $resDomain['cname'] ?? '';
}
return $info->save();
}
return true;
}
}

View File

@@ -0,0 +1,86 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\system\form;
use app\dao\system\form\SystemFormDataDao;
use app\services\BaseServices;
use app\dao\diy\DiyDao;
use think\annotation\Inject;
use think\exception\ValidateException;
/**
* 系统表单
* Class SystemFormDataServices
* @package app\services\system\form
* @mixin DiyDao
*/
class SystemFormDataServices extends BaseServices
{
/**
* @var SystemFormDataDao
*/
#[Inject]
protected SystemFormDataDao $dao;
/**
* 获取表单收集数据列表
* @param int $id
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getFormDataList(int $id = 0, array $where = [])
{
$where['is_del'] = 0;
if ($id) $where['system_form_id'] = $id;
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, ['*'], $page, $limit, ['user', 'systemForm']);
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**
* 保存系统表单收集数据
* @param array $form
* @param int $type
* @return bool
*/
public function setFormData(array $form, int $type = 1)
{
if (!$form) {
throw new ValidateException('缺少表单收集数据');
}
/** @var SystemFormServices $systemFormServices */
$systemFormServices = app()->make(SystemFormServices::class);
$form['value'] = $systemFormServices->handleForm($form['value'] ?? []);
$form['value'] = json_encode($form['value']);
$data = ['type' => $type, 'add_time' => time()];
switch ($type) {
case 1://订单
$data = array_merge($data, $form);
break;
}
if ($data) {
$this->dao->save($data);
}
return true;
}
}

View File

@@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\services\system\form;
use app\dao\system\form\SystemFormDao;
use app\services\BaseServices;
use app\dao\diy\DiyDao;
use think\annotation\Inject;
/**
* 系统表单
* Class SystemFormServices
* @package app\services\diy
* @mixin DiyDao
*/
class SystemFormServices extends BaseServices
{
/**
* form类型
* @var string[]
*/
protected $formType = [
'checkboxs' => '多选框',
'citys' => '城市',
'dates' => '日期',
'dateranges' => '日期范围',
'radios' => '单选框',
'selects' => '下拉框',
'texts' => '文本框',
'times' => '时间',
'timeranges' => '时间范围',
'uploadPicture' => '图片'
];
/**
* @var SystemFormDao
*/
#[Inject]
protected SystemFormDao $dao;
/**
* 获取系统表单
* @param array $where
* @param array $field
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getFormList(array $where = [], array $field = ['*'])
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getFormList($where, $field, $page, $limit);
$count = $this->dao->count($where + ['is_del' => 0]);
return compact('list', 'count');
}
/**
* 处理表单数据
* @param array $form
* @return array
*/
public function handleForm(array $form)
{
$info = [];
if ($form) {
$infoOne = [];
foreach ($form as $item) {
$infoOne['id'] = $item['id'] ?? '';
$infoOne['type'] = $item['name'] ?? '';
$infoOne['name'] = $this->formType[$infoOne['type']] ?? '';
$infoOne['title'] = $item['titleConfig']['value'] ?? '';
$infoOne['tip'] = $item['tipConfig']['value'] ?? '';
$infoOne['list'] = [];
$infoOne['require'] = $item['titleShow']['val'] ?? false;
switch ($item['name']) {
case 'checkboxs':
case 'radios':
case 'selects':
$infoOne['list'] = $item['wordsConfig']['list'] ?? [];
break;
}
$infoOne['value'] = $item['value'] ?? '';
$info[] = $infoOne;
}
}
return $info;
}
}

View File

@@ -0,0 +1,81 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\log;
use app\services\BaseServices;
/**
* Class ClearServices
* @package app\services\system\log
*/
class ClearServices extends BaseServices
{
/** 递归删除文件
* @param $dirName
* @param bool $subdir
*/
protected function delDirAndFile($dirName)
{
$list = glob($dirName . '*');
foreach ($list as $file) {
if (is_dir($file))
$this->delDirAndFile($file . DS);
else
@unlink($file);
}
@rmdir($dirName);
}
/**
* 删除日志
*/
public function deleteLog()
{
$root = app()->getRootPath() . 'runtime' . DS;
$this->delDirAndFile($root . 'admin' . DS . 'log' . DS);
$this->delDirAndFile($root . 'api' . DS . 'log' . DS);
$this->delDirAndFile($root . 'log' . DS);
}
/**
* 刷新数据缓存
*/
public function refresCache()
{
$root = app()->getRootPath() . 'runtime' . DS;
$adminRoute = $root . 'admin';
$apiRoute = $root . 'api';
$cacheRoute = $root . 'cache';
$cache = [];
if (is_dir($adminRoute))
$cache[$adminRoute] = scandir($adminRoute);
if (is_dir($apiRoute))
$cache[$apiRoute] = scandir($apiRoute);
if (is_dir($cacheRoute))
$cache[$cacheRoute] = scandir($cacheRoute);
foreach ($cache as $p => $list) {
foreach ($list as $file) {
if (!in_array($file, ['.', '..', 'log', 'schema', 'admin.php'])) {
$path = $p . DS . $file;
if (is_file($path)) {
@unlink($path);
} else {
$this->delDirAndFile($path . DS);
}
}
}
}
}
}

View File

@@ -0,0 +1,255 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\log;
use app\dao\system\log\SystemFileDao;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use crmeb\services\CacheService;
use crmeb\services\FileService as FileClass;
use think\annotation\Inject;
/**
* 文件校验
* Class SystemFileServices
* @package app\services\system\log
* @mixin SystemFileDao
*/
class SystemFileServices extends BaseServices
{
/**
* @var SystemFileDao
*/
#[Inject]
protected SystemFileDao $dao;
/**
* 获取文件校验列表
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getFileList()
{
$rootPath = app()->getRootPath();
$key = 'system_file_app_crmeb_public';
$arr = CacheService::get(md5($key));
if (!$arr) {
$app = $this->getDir($rootPath . 'app');
$extend = $this->getDir($rootPath . 'crmeb');
$arr = array_merge($app, $extend);
CacheService::set(md5($key), $arr, 3600 * 24);
}
$fileAll = [];//本地文件
$cha = [];//不同的文件
$len = strlen($rootPath);
$fileKey = 'system_file_app_msyql';
$file = CacheService::get(md5($fileKey));
if (!$file) {
$file = $this->dao->getAll();//数据库中的文件
CacheService::set(md5($fileKey), $file, 3600 * 24);
}
if (empty($file)) {
foreach ($arr as $k => $v) {
$update_time = stat($v);
$fileAll[$k]['cthash'] = md5_file($v);
$fileAll[$k]['filename'] = substr($v, $len);
$fileAll[$k]['atime'] = $update_time['atime'];
$fileAll[$k]['mtime'] = $update_time['mtime'];
$fileAll[$k]['ctime'] = $update_time['ctime'];
}
$data_num = array_chunk($fileAll, 100);
$res = true;
$res = $this->transaction(function () use ($data_num, $res) {
foreach ($data_num as $k => $v) {
$res = $res && $this->dao->saveAll($v);
}
return $res;
});
if ($res) {
$cha = [];//不同的文件
} else {
$cha = $fileAll;
}
} else {
$file = array_combine(array_column($file, 'filename'), $file);
$insertData = [];
foreach ($arr as $ko => $vo) {
$update_time = stat($vo);
$cthash = md5_file($vo);
$fileName = substr($vo, $len);
$data = [
'filename' => $fileName,
'cthash' => $cthash,
'atime' => $update_time['atime'] ?? 0,
'mtime' => $update_time['mtime'] ?? 0,
'ctime' => $update_time['ctime'] ?? 0,
];
if (isset($file[$fileName])) {
$data ['type'] = $file[$fileName]['cthash'] != $cthash ? '已修改' : '新增的';
unset($file[$fileName]);
} else {//新增
$insertData[] = $data;
$data['type'] = '新增的';
}
$data['atime'] = (int)$data['atime'] ? date('Y-m-d H:i:s', (int)$data['atime']) : '';
$data['mtime'] = (int)$data['mtime'] ? date('Y-m-d H:i:s', (int)$data['mtime']) : '';
$data['ctime'] = (int)$data['ctime'] ? date('Y-m-d H:i:s', (int)$data['ctime']) : '';
$cha[] = $data;
}
if ($insertData) {
$this->dao->saveAll($insertData);
CacheService::delete(md5($fileKey));
}
foreach ($file as $k => $v) {
$cha[] = [
'filename' => $v['filename'],
'cthash' => $v['cthash'],
'atime' => (int)$v['atime'] ? date('Y-m-d H:i:s', (int)$v['atime']) : '',
'mtime' => (int)$v['mtime'] ? date('Y-m-d H:i:s', (int)$v['mtime']) : '',
'ctime' => (int)$v['ctime'] ? date('Y-m-d H:i:s', (int)$v['ctime']) : '',
'type' => '已删除',
];
}
}
$ctime = array_column($cha, 'ctime');
array_multisort($ctime, SORT_DESC, $cha);
return $cha;
}
/**
* 获取文件夹中的文件 包括子文件
* @param $dir
* @return array
*/
public function getDir($dir)
{
$data = [];
$this->searchDir($dir, $data);
return $data;
}
/**
* 获取文件夹中的文件 包括子文件 不能直接用 直接使用 $this->getDir()方法 P156
* @param $path
* @param $data
*/
public function searchDir($path, &$data)
{
if (is_dir($path) && !strpos($path, 'uploads')) {
$files = scandir($path);
foreach ($files as $file) {
if ($file != '.' && $file != '..') {
$this->searchDir($path . '/' . $file, $data);
}
}
}
if (is_file($path)) {
$data[] = $path;
}
}
/**
* 打开目录获取文件列表
* @return array
*/
public function opendir()
{
$fileAll = array('dir' => [], 'file' => []);
//根目录
$rootdir = app()->getRootPath();
// return $rootdir;
//当前目录
$request_dir = app('request')->param('dir');
//防止查看站点以外的目录
if (strpos($request_dir, $rootdir) === false) {
$request_dir = $rootdir;
}
//判断是否是返回上级
if (app('request')->param('superior') && !empty($request_dir)) {
if (strpos(dirname($request_dir), $rootdir) !== false) {
$dir = dirname($request_dir);
} else {
$dir = $rootdir;
}
} else {
$dir = !empty($request_dir) ? $request_dir : $rootdir;
$dir = rtrim($dir, DS) . DS . app('request')->param('filedir');
}
$list = scandir($dir);
foreach ($list as $key => $v) {
if ($v != '.' && $v != '..') {
if (is_dir($dir . DS . $v)) {
$fileAll['dir'][] = FileClass::listInfo($dir . DS . $v);
}
if (is_file($dir . DS . $v)) {
$fileAll['file'][] = FileClass::listInfo($dir . DS . $v);
}
}
}
//兼容windows
$uname = php_uname('s');
if (strstr($uname, 'Windows') !== false) {
$dir = ltrim($dir, '\\');
$rootdir = str_replace('\\', '\\\\', $rootdir);
}
$list = array_merge($fileAll['dir'], $fileAll['file']);
foreach ($list as $key => $value) {
$list[$key]['real_path'] = str_replace($rootdir, '', $value['pathname']);
$list[$key]['mtime'] = date('Y-m-d H:i:s', $value['mtime']);
}
return compact('dir', 'list');
}
/**
* 读取文件内容
* @param string $filepath 文件路径
* @return array
*/
public function openfile($filepath)
{
$content = FileClass::readFile($filepath);//防止页面内嵌textarea标签
$ext = FileClass::getExt($filepath);
$extarray = [
'js' => 'text/javascript'
, 'php' => 'text/x-php'
, 'html' => 'text/html'
, 'sql' => 'text/x-mysql'
, 'css' => 'text/x-scss'];
$mode = empty($extarray[$ext]) ? '' : $extarray[$ext];
return compact('content', 'mode', 'filepath');
}
/**
* 保存文件内容
* @param string $filepath 文件路径
* @param string $comment 文件内容
* @return bool
* @throws AdminException
*/
public function savefile($filepath, $comment)
{
//兼容windows
$uname = php_uname('s');
if (strstr($uname, 'Windows') !== false)
$filepath = ltrim(str_replace('/', DS, $filepath), '.');
if (!FileClass::isWritable($filepath)) {
throw new AdminException('没有权限');
}
return FileClass::writeFile($filepath, $comment);
}
}

View File

@@ -0,0 +1,91 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\log;
use app\dao\system\log\SystemLogDao;
use app\services\BaseServices;
use app\services\system\admin\SystemAdminServices;
use app\services\system\SystemMenusServices;
use think\annotation\Inject;
/**
* 系统日志
* Class SystemLogServices
* @package app\services\system\log
* @mixin SystemLogDao
*/
class SystemLogServices extends BaseServices
{
/**
* @var SystemLogDao
*/
#[Inject]
protected SystemLogDao $dao;
/**
* 记录访问日志
* @param int $adminId
* @param string $adminName
* @param string $module
* @param string $rule
* @param string $ip
* @param string $type
* @return bool
*/
public function recordAdminLog(int $adminId, string $adminName, string $module, string $rule, string $ip, string $type)
{
/** @var SystemMenusServices $service */
$service = app()->make(SystemMenusServices::class);
$data = [
'method' => $module,
'add_time' => time(),
'admin_name' => $adminName,
'path' => $rule,
'page' => $service->getVisitName($rule) ?: '未知',
'ip' => $ip,
'type' => $type
];
if ($type == 'store') {
$data['store_id'] = $adminId;
} else {
$data['admin_id'] = $adminId;
}
if ($this->dao->save($data)) {
return true;
} else {
return false;
}
}
/**
* 获取系统日志列表
* @param array $where
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getLogList(array $where, int $level)
{
[$page, $limit] = $this->getPageValue();
if (!$where['admin_id']) {
/** @var SystemAdminServices $service */
$service = app()->make(SystemAdminServices::class);
$where['admin_id'] = $service->getAdminIds($level);
}
$list = $this->dao->getLogList($where, $page, $limit);
$count = $this->dao->count($where);
return compact('list', 'count');
}
}

View File

@@ -0,0 +1,366 @@
<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2026 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace app\services\system\timer;
use app\services\BaseServices;
use crmeb\exceptions\AdminException;
use app\dao\system\timer\SystemTimerDao;
use think\annotation\Inject;
/**
* 定时器service
* Class SystemTimerServices
* @package app\services\system\timer
* @mixin SystemTimerDao
*/
class SystemTimerServices extends BaseServices
{
/**
* @var array|string[]
*/
protected array $taskName = [
'auto_cancel' => '自动取消订单',
'auto_take' => '自动确认收货',
'auto_comment' => '自动好评',
'auto_clear_integral' => '自动清空用户积分',
'auto_off_user_svip' => '自动取消用户到期svip',
'auto_agent' => '自动解绑上下级',
'auto_clear_poster' => '自动清除昨日海报',
'auto_sms_code' => '更新短信状态',
'auto_live' => '自动更新直播产品状态和直播间状态',
'auto_pink' => '拼团状态自动更新',
'auto_show' => '自动上下架商品',
'auto_channel' => '渠道码定时任务',
'auto_moment' => '定时创建发送朋友圈任务',
'auto_group_task' => '定时发送群发任务',
'auto_seckill' => '定时清理秒杀数据过期的数据缓存',
'rebate_points_orders' => '未支付积分订单退积分',
'reminder_unverified_remind' => '次卡商品未核销短信提醒',
'sign_remind_time' => '用户签到提醒',
'auto_presale_product' => '预售商品到期处理数据',
'auto_card_code' => '清理到期礼品卡',
'holiday_gift_push_task'=>'节日有礼赠送礼品',
];
/**
* @var SystemTimerDao
*/
#[Inject]
protected SystemTimerDao $dao;
/**
* 获取定时任务名称
* @return array|string[]
*/
public function getTasKName()
{
return $this->taskName;
}
/**
* 获取定时器列表
* @param array $where
* @return array
*/
public function getTimerList(array $where)
{
[$page, $limit] = $this->getPageValue();
$list = $this->dao->getList($where, $page, $limit);
foreach ($list as $key => &$item) {
$item['last_execution_time'] = $item['last_execution_time'] > 0 ? date('Y-m-d H:i:s', $item['last_execution_time']) : '';
switch ($item['type']) {
case 1:
$item['execution_cycle'] = '每隔' . $item['cycle'] . '分钟执行';
break;
case 2:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每' . $arr[0] . '小时, 第' . $arr[1] . '分钟 执行';
break;
case 3:
$item['execution_cycle'] = '每小时, 第' . $item['cycle'] . '分钟执行';
break;
case 4:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每天, ' . $arr[0] . '点' . $arr[1] . '分 执行';
break;
case 5:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每隔' . $arr[0] . '天, ' . $arr[1] . '点' . $arr[2] . '分 执行';
break;
case 6:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每周' . $arr[0] . ', ' . $arr[1] . '点' . $arr[2] . '分执行';
break;
case 7:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每月, ' . $arr[0] . '日 ' . $arr[1] . '点' . $arr[2] . '分执行';
break;
case 8:
$arr = explode('/', $item['cycle']);
$item['execution_cycle'] = '每年' . $arr[0] . '月' . $arr[1] . '日 ' . $arr[2] . '点' . $arr[3] . '分执行';
break;
default:
$item['execution_cycle'] = '周期有误!';
break;
}
}
$count = $this->dao->count($where);
return compact('list', 'count');
}
/**设置或更新定时任务缓存
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function setAllTimerCache()
{
$where['is_del'] = 0;
$list = $this->dao->getList($where, 0, 0);
foreach ($list as $key => &$value) {
if (!$value['update_execution_time']) {
$update_time = time();
$value['update_execution_time'] = $update_time;
$this->dao->update($value['id'], ['update_execution_time' => $update_time]);
}
}
$this->dao->cacheCreate($list);
}
/**
* 设置定时器状态
* @param $id
* @param $is_show
*/
public function setShow(int $id, int $is_show)
{
$this->dao->update($id, ['is_open' => $is_show]);
}
/**
* 删除定时器
* @param string $id
*/
public function del(int $id)
{
if (!$id) throw new AdminException('请选择要删除的定时任务');
$this->dao->delete((int)$id);
}
/**
* 新增数据
* @param $data
*/
public function createData($data)
{
if ($this->dao->be(['mark' => $data['mark'], 'name' => $data['name']])) {
throw new AdminException('该定时任务已经存在');
}
$data['add_time'] = time();
$data['update_execution_time'] = time();
$res = $this->dao->save($data);
if (!$res) throw new AdminException('添加失败');
return $res;
}
/**
* 保存修改数据
* @param $id
* @param $data
*/
public function editData($id, $data)
{
if (!$this->dao->be(['id' => $id])) {
throw new AdminException('该定时任务不存在');
}
$data['update_execution_time'] = time();
$res = $this->dao->update($id, $data);
if (!$res) throw new AdminException('修改失败');
return $res;
}
/**更新缓存
* @param $id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function updateOneTimerCache($id)
{
if (!$id) throw new AdminException('参数错误');
$timer = $this->dao->get($id);
$this->dao->cacheUpdate($timer->toArray());
return true;
}
/**删除缓存
* @param $id
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function delOneTimerCache($id)
{
if (!$id) throw new AdminException('参数错误');
$this->dao->cacheDelById($id);
return true;
}
/**获取单条定时器数据
* @param $id
* @return void
*/
public function getOneTimer($id)
{
if (!$id) throw new AdminException('请选择要删除的定时任务');
$timer = $this->dao->get($id);
if (!$timer) throw new AdminException('该定时任务不存在');
return $timer->toArray();
}
/**获取下次执行时间
* @param $type
* @param $cycle
* @param $time
* @param $start_time
* @return array
*/
public function getTimerCycleTime($type, $cycle, $time, $start_time = 0)
{
if (!$time) $time = time();
switch ($type) {
case 1: // N分钟
$cycle_time = 60 * $cycle;
$i = $time - $start_time;
$more = $i % $cycle_time;
if ($more == 0) {
$cycle_time = $time;
} else {
$cycle_time = $time - $more + $cycle_time;
}
break;
case 2: //N小时
$arr = explode('/', $cycle);
$todaystart = strtotime(date('Y-m-d H' . ':00:00', $time));
$start_time = strtotime(date('Y-m-d H' . ':00:00', $start_time));
if ($arr[0] <= 0) {
$cycle_time = 60 * $arr[1];
$i = $time - $start_time;
$more = $i % $cycle_time;
if ($more == 0) {
$cycle_time = $time;
} else {
$cycle_time = $time - $more + $cycle_time;
}
} else {
$h = ($todaystart - $start_time) / 3600;
$h = floor($h);
$more = $h % $arr[0];
if ($more == 0) {
$cycle_time = 60 * $arr[1];
$cycle_time1 = $todaystart + $cycle_time;
if ($cycle_time1 < $time) {
$cycle_time = 60 * 60 * $arr[0] + 60 * $arr[1];
}
} else {
$sh = $arr[0] - $more;
$cycle_time = 60 * 60 * $sh + 60 * $arr[1];
}
$cycle_time = $todaystart + $cycle_time;
}
break;
case 3: //每小时
$cycle_time = strtotime(date('Y-m-d ' . 'H:' . $cycle . ':00', $time));
break;
case 4: //每天
$arr = explode('/', $cycle);
$cycle_time = strtotime(date('Y-m-d ' . $arr[0] . ':' . $arr[1] . ':00', $time));
break;
case 5: //N天
$arr = explode('/', $cycle);
$todaystart = strtotime(date('Y-m-d ' . '00:00:00', $time));
$start_time = strtotime(date('Y-m-d ' . '00:00:00', $start_time));
$d = ($todaystart - $start_time) / 86400;
$d = floor($d);
$more = $d % $arr[0];
if ($more == 0) {
$cycle_time = 60 * 60 * $arr[1] + 60 * $arr[2];
$cycle_time1 = $todaystart + $cycle_time;
if ($cycle_time1 < $time) {
$cycle_time = 60 * 60 * 24 * $more + 60 * 60 * $arr[1] + 60 * $arr[2];
}
} else {
$sd = $arr[0] - $more;
$cycle_time = 60 * 60 * 24 * $sd + 60 * 60 * $arr[1] + 60 * $arr[2];
}
$cycle_time = $todaystart + $cycle_time;
break;
case 6: //每星期
$arr = explode('/', $cycle);
$todaystart = strtotime(date('Y-m-d ' . '00:00:00', $time));
$w = date("w");
if ($w > $arr[0]) {
$d = 7 - $w + $arr[0];
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
} else if ($w == $arr[0]) {
$to_time = 60 * 60 * $arr[1] + 60 * $arr[2];
$to_time = $todaystart + $to_time;
if ($time > $to_time) {
$d = 7 - $w + $arr[0];
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
} else {
$cycle_time = $to_time;
}
} else {
$d = $arr[0] - $w;
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
}
break;
case 7: //每月
$arr = explode('/', $cycle);
$current_d = date("d");
$firstDate = date('Y-m-01', $time);
$max_d = date('d', strtotime("$firstDate + 1 month -1 day"));
$todaystart = strtotime(date('Y-m-d ' . '00:00:00', $time));
if ($current_d > $arr[0]) {
$d = $max_d - $current_d + $arr[0];
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
} elseif ($current_d == $arr[0]) {
$to_time = 60 * 60 * $arr[1] + 60 * $arr[2];
$to_time = $todaystart + $to_time;
if ($time > $to_time) {
$d = $max_d - $current_d + $arr[0];
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
} else {
$cycle_time = $to_time;
}
} else {
$d = $arr[0] - $current_d;
$cycle_time = $todaystart + 60 * 60 * 24 * $d + 60 * 60 * $arr[1] + 60 * $arr[2];
}
break;
case 8: //每年
$arr = explode('/', $cycle);
$cycle_time = strtotime(date('Y-' . $arr[0] . '-' . $arr[1] . ' ' . $arr[2] . ':' . $arr[3] . ':00', $time));
break;
default:
$cycle_time = 0;
break;
}
return ['cycle_time' => $cycle_time];
}
}