样机小程序

This commit is contained in:
2025-12-08 17:37:34 +08:00
commit d19bb9046b
175 changed files with 19427 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
/runtime
/.idea
/.vscode
/vendor
*.log
.env
/tests/tmp
/tests/.phpunit.result.cache
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/webman/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\AgreementService;
class AgreementController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new AgreementService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
return $data;
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\BannerService;
class BannerController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new BannerService();
}
/**
* 轮播图模块
* @return \support\Response
*/
public function getBannerModule()
{
try {
$raw = $this->service->getBannerModule();
$data = $this->transformData($raw, method_exists($this, 'indexFormat')
? [$this, 'indexFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['image_url']) && !empty($data['image_url'])){
$ossservice = new OssService();
$data['image_url_pull'] = $ossservice->generatePrivateUrl($data['image_url']);
}
return $data;
}
}
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\MessageNoticeService;
class MessageNoticeController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new MessageNoticeService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,62 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostCategoryService;
use support\Request;
class PostCategoryController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostCategoryService();
}
/**
* 分类树
* @param Request $request
* @return \support\Response
*/
public function tree(Request $request)
{
try {
$params = $request->all();
$result = $this->service->getList($params);
return $this->success($result, '获取分类列表');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
$ossservice = new OssService();
if (!empty($data['icon_active'])){
$data['icon_active_pull'] = $ossservice->generatePrivateUrl($data['icon_active']);
}
if (!empty($data['icon_inactive'])){
$data['icon_inactive_pull'] = $ossservice->generatePrivateUrl($data['icon_inactive']);
}
if (!empty($data['icon_preset'])){
$data['icon_preset_pull'] = $ossservice->generatePrivateUrl($data['icon_preset']);
}
return $data;
}
}
@@ -0,0 +1,62 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\model\PostComment;
use app\common\model\User;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostCommentService;
class PostCommentController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostCommentService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['id'])){
$data['child'] = [];
$child = PostComment::where(['parent_id' => $data['id']])->first();
if(!empty($child)){
$child = $child->toArray();
}
$data['count'] = PostComment::where(['parent_id' => $data['id']])->count();
if (isset($child['user_id'])){
$ossservice = new OssService();
$child['user'] = User::where('id', $child['user_id'])->select(['nickname', 'avatar', 'openid'])->first();
if(!str_contains($child['user']['avatar'], 'http') && !empty($child['user']['avatar'])){
$child['user']['avatar_pull'] = $ossservice->generatePrivateUrl($child['user']['avatar']);
}
$child['created_at'] = date('Y-m-d H:i:s', strtotime($child['created_at']));
$child['updated_at'] = date('Y-m-d H:i:s', strtotime($child['updated_at']));
$data['child'][] = $child;
}
}
if (isset($data['user_id'])){
$ossservice = new OssService();
$data['user'] = User::where('id', $data['user_id'])->select(['nickname', 'avatar', 'openid'])->first();
if(!str_contains($data['user']['avatar'], 'http') && !empty($data['user']['avatar'])){
$data['user']['avatar_pull'] = $ossservice->generatePrivateUrl($data['user']['avatar']);
}
}
return $data;
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\model\PostCategory;
use app\common\model\PostMedia;
use app\common\model\User;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostService;
class PostController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['id']))
$data['media'] = PostMedia::where('post_id', $data['id'])->get();
if (isset($data['category_id']))
$data['category_name'] = PostCategory::where('id', $data['category_id'])->value('name')??'';
$ossservice = new OssService();
if (isset($data['id'])){
$data['media'] = PostMedia::where('post_id', $data['id'])->get()->toArray();
foreach ($data['media'] as &$media) {
$media['url_pull'] = $ossservice->generatePrivateUrl($media['url']);
}
}
if (isset($data['user_id'])){
$data['user'] = User::where('id', $data['user_id'])->first();
unset($data['user']['password']);
$data['user']['avatar_pull'] = $data['user']['avatar'];
if(!str_contains($data['user']['avatar'], 'http') && !empty($data['user']['avatar'])){
$data['user']['avatar_pull'] = $ossservice->generatePrivateUrl($data['user']['avatar']);
}
}
return $data;
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function showFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['id']))
$data['media'] = PostMedia::where('post_id', $data['id'])->get();
if (isset($data['category_id']))
$data['category_name'] = PostCategory::where('id', $data['category_id'])->value('name')??'';
$ossservice = new OssService();
if (isset($data['id'])){
$data['media'] = PostMedia::where('post_id', $data['id'])->get()->toArray();
foreach ($data['media'] as &$media) {
$media['url_pull'] = $ossservice->generatePrivateUrl($media['url']);
}
}
if (isset($data['user_id'])){
$data['user'] = User::where('id', $data['user_id'])->first();
unset($data['user']['password']);
$data['user']['avatar_pull'] = $data['user']['avatar'];
if(!str_contains($data['user']['avatar'], 'http') && !empty($data['user']['avatar'])){
$data['user']['avatar_pull'] = $ossservice->generatePrivateUrl($data['user']['avatar']);
}
}
return $data;
}
}
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostFavoriteService;
class PostFavoriteController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostFavoriteService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostLikeService;
class PostLikeController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostLikeService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostMediaService;
class PostMediaController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostMediaService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\admin\service\RoleService;
use app\common\traits\crud\CrudControllerTrait;
use support\Request;
class RoleController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new RoleService();
}
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
return $data;
}
public function menuResource(Request $request)
{
try {
// $data = $this->validate($this->service->getValidator(), $request->post(), 'allocation');
$data = $request->post();
$res = $this->service->menuResource($request, $data);
return $this->success($res);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\SkuAttributeService;
class SkuAttributeController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new SkuAttributeService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\SkuAttributeValueService;
class SkuAttributeValueController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new SkuAttributeValueService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\SkuService;
class SkuController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new SkuService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\admin\service\SystemsService;
class SystemsController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new SystemsService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
return $data;
}
}
+196
View File
@@ -0,0 +1,196 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\model\Corp;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\admin\service\UserService;
use app\crm\model\Customer;
use support\Request;
class UserController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new UserService();
}
/**
* 添加
*
* @param Request $request 请求对象
* @return mixed 返回更新成功的数据或信息
*/
public function store(Request $request)
{
try {
$data = $request->post();
if(mb_strlen($data['mobile']) < 11){
return $this->error('手机号错误');
}
$res = $this->service->serviceCreateUser($request, $data);
return $this->success([], '添加成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 更新
*
* @param Request $request 请求对象
* @return mixed 返回更新成功的数据或信息
*/
public function update(Request $request)
{
try {
$data = $request->post();
$res = $this->service->serviceUpdateUser($request, $data);
return $this->success([], '更新成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 修改密码
* @param Request $request
* @return \support\Response
* @throws \Random\RandomException
*/
public function savePassword(Request $request)
{
try {
$data = $request->all();
if(empty($data['password'])){
return $this->error('密码不能为空');
}
$result = $this->service->savePassword($request,$data);;
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 更新
*
* @param Request $request 请求对象
* @return mixed 返回更新成功的数据或信息
*/
public function edit(Request $request)
{
try {
$data = $request->post();
$data['id'] = $data['id']??$request->user->uid;
$res = $this->service->serviceUpdate($request, $data);
return $this->success([], '更新成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if(isset($data['records'])){
return $data;
}
$data['sex_txt'] = '';
if (isset($data['sex']) && $data['sex'] == 1){
$data['sex_txt'] = '男';
}elseif(isset($data['sex']) && $data['sex'] == 2){
$data['sex_txt'] = '女';
}
$ossservice = new OssService();
if(isset($data['avatar']) && !empty($data['avatar']) && strpos($data['avatar'], 'http') === false){
//$data['avatar_pull'] = $ossservice->generatePrivateUrl($data['avatar']);
$data['avatar_pull'] = '/proxy/ossurl?file='.$data['avatar'];
}else{
$data['avatar_pull'] = $data['avatar'];
}
switch ($data['status']){
case 1:
$data['status_txt'] = '正常';
break;
case 2:
$data['status_txt'] = '离职';
break;
default:
$data['status_txt'] = '';
break;
}
$data['role_ids'] = [];
$data['system_ids'] = [];
if (isset($data['roles']) && count($data['roles']) > 0) {
foreach ($data['roles'] as $role) {
$data['role_ids'][] = $role['id'];
}
}
if (isset($data['systems']) && count($data['systems']) > 0) {
foreach ($data['systems'] as $system) {
$data['system_ids'][] = $system['id'];
}
}
return $data;
}
/**
* 授权
*
* @param Request $request
* @return mixed
*/
public function authorize(Request $request)
{
try {
$data = $request->post();
$res = $this->service->authorize($request, $data);
return $this->success([], '分配成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 直接授权
*
* @param Request $request
* @return mixed
*/
public function updateResources(Request $request)
{
try {
$data = $request->post();
$res = $this->service->updateResources($request, $data);
return $this->success([], '分配成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function updateSign(Request $request)
{
try {
$data = $request->post();
$this->service->serviceUpdateSign($request, $data);
return $this->success([], '签名成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
+94
View File
@@ -0,0 +1,94 @@
<?php
namespace app\admin\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\User;
use support\Db;
class UserLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = User::class;
public function updateRoleAndSystem(array $data): bool
{
$roleIds = $data['role_ids'] ?? [];
$systemIds = $data['system_ids'] ?? [];
$userId = $data['user_id'] ?? 0;
Db::beginTransaction();
try {
Db::table('user_roles')->where('user_id', $userId)->delete();
if (count($roleIds) > 0) {
$insertDataRole = [];
foreach ($roleIds as $roleId) {
$insertDataRole[] = [
'role_id' => $roleId,
'user_id' => $userId
];
}
$res = Db::table('user_roles')->insert($insertDataRole);
var_dump($res);
}
Db::table('user_systems')->where('user_id', $userId)->delete();
if (count($systemIds) > 0) {
$insertDataSystem = [];
foreach ($systemIds as $systemId) {
$insertDataSystem[] = [
'system_id' => $systemId,
'user_id' => $userId
];
}
Db::table('user_systems')->insert($insertDataSystem);
}
Db::commit();
return true;
} catch (\Throwable $exception) {
Db::rollBack();
return false;
}
}
public function updateUserResources(array $data): bool
{
$userId = $data['user_id'] ?? 0;
$resources = $data['resources'] ?? [];
if (empty($userId)) {
return false;
}
Db::beginTransaction();
try {
// 删除原有资源关联
Db::table('user_resources')->where('user_id', $userId)->delete();
// 批量插入新角色资源关联
if (!empty($resources)) {
$insertData = [];
foreach ($resources as $resourceId) {
$insertData[] = [
'user_id' => $userId,
'resource_id' => $resourceId
];
}
Db::table('user_resources')->insert($insertData);
}
Db::commit();
return true;
} catch (\Throwable $exception) {
Db::rollBack();
return false;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace app\admin\service;
use app\common\logic\RoleLogic;
use app\common\validate\RoleValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Role;
use support\Db;
class RoleService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Role::class;
/**
* 验证器
*/
protected $validator = RoleValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 获取角色资源
*/
public function menuResource($request, $data)
{
return (new RoleLogic)->updateRoleResources($data);
}
public function getList()
{
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
return $this->logicListAll($config);
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace app\admin\service;
use app\common\validate\SystemsValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Systems;
class SystemsService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Systems::class;
/**
* 验证器
*/
protected $validator = SystemsValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
}
+180
View File
@@ -0,0 +1,180 @@
<?php
namespace app\admin\service;
use app\admin\logic\UserLogic;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\User;
use app\common\validate\UserValidate;
use support\exception\BusinessException;
use support\Request;
class UserService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = User::class;
/**
* 验证器
*/
protected $validator = UserValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/** 更新 */
public function serviceCreateUser(Request $request, array $data = [])
{
$config = $this->buildQueryConfig($request, 'create');
if(isset($data['avatar']) && empty($data['avatar'])){
$data['avatar'] = 'bsh_datax/user/20251021/a4ee815405a1506abeec1af0f738f70f_68f6e76514b66.jpg';
}
if(empty($data['password'])){
$data['password'] = password_hash('12345678', PASSWORD_DEFAULT);
}else{
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
}
$created = $this->logicCreate($data);
return $this->postProcess('Create', $data);
}
/** 更新 */
public function serviceUpdateUser(Request $request, array $data = [])
{
$data = $this->preProcess('Update', $data, $request);
$config = $this->buildQueryConfig($request, 'update');
$updated = $this->logicUpdateBy($config, $data);
return $this->postProcess('Update', $data);
}
/** 更新 */
public function serviceUpdate(Request $request, array $data = [])
{
unset($data['roles']);
unset($data['avatar_pull']);
$data = $this->preProcess('Update', $data, $request);
$config = $this->buildQueryConfig($request, 'edit');
$updated = $this->logicUpdateBy($config, $data);
return $this->postProcess('Update', $data);
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['where']['status'] = 1;
$config['order']['id'] = 'desc';
break;
case 'index':
// $config['where']['status'] = 1;
$config['order']['id'] = 'desc';
// 关键字
if (isset($p['keyword']) && $p['keyword']) {
$k = '%' . $p['keyword'] . '%';
$config['where']['name'] = ['like', $k];
}
if (isset($p['status']) && $p['status']) {
$config['where']['status'] = $p['status'];
}
if (isset($p['user_id']) && $p['user_id']) {
$config['where']['user_id'] = $p['user_id'];
}
if (isset($p['id']) && $p['id']) {
$config['where']['id'] = $p['id'];
}
if (isset($p['corp_id']) && $p['corp_id']) {
$config['where']['corp_id'] = $p['corp_id'];
}
$config['fields'] = ['id','sn', 'name', 'email', 'mobile', 'avatar', 'nickname', 'is_release', 'status', 'created_at'];
$config['with']['roles'][] = 'id';
$config['with']['roles'][] = 'name';
$config['with']['systems'][] = 'id';
$config['with']['systems'][] = 'name';
break;
case 'show':
$config['where']['id'] = $p['id'];
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
case 'savePassword':
$config['where']['id'] = $p['id'];
break;
case 'edit':
case 'updateSign':
$config['where']['id'] = $request->user->uid;
break;
}
return $config;
}
/**
* 授权
*
* @param Request $request
* @param array $data
* @return bool
*/
public function authorize(Request $request, array $data = [])
{
return (new UserLogic())->updateRoleAndSystem($data);
}
/**
* 更新用户资源权限
*
* @param Request $request
* @param array $data
* @return bool
*/
public function updateResources(Request $request, array $data = [])
{
return (new UserLogic())->updateUserResources($data);
}
/**
* 修改用户密码
*/
public function savePassword(Request $request, array $data)
{
$config = $this->buildQueryConfig($request, 'savePassword');
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
$user = $this->logicUpdateBy($config,$data);
return $user;
}
public function serviceUpdateSign(Request $request, array $data)
{
$config = $this->buildQueryConfig($request, 'updateSign');
$user = $this->logicUpdateBy($config,$data);
return $user;
}
}
@@ -0,0 +1,33 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\AgreementService;
class AgreementController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new AgreementService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace app\common\controller;
use app\common\service\AuthService;
use app\common\validate\UserValidate;
use support\Redis;
use support\Request;
class AuthController extends BaseController
{
/**
* 不需要头信息的方法
*/
protected $noNeedHeaderData = ['feishuAuth'];
protected $noNeedLogin = ['feishuScope', 'login', 'register', 'feishuAuth'];
// 调试
public function feishuAuth(Request $request)
{
try {
$code = $request->all();
return $this->success($code);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function feishuScope(Request $request)
{
try {
$result = (new AuthService())->feishuAuthorize($request->corp_abbr, $request->system_code);
return $this->success($result);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function login(Request $request)
{
try {
$data = $this->validate(new UserValidate(), $this->requestData($request), $request->login_type.'_login');
$result = (new AuthService())->login($data);
return $this->success($result, '登录成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function register(Request $request)
{
try {
$data = $this->validate(new UserValidate(), $this->requestData($request), $request->login_type.'_register');
$result = (new AuthService())->register($data);
return $this->success($result, '注册成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function refresh(Request $request)
{
try {
$data = $this->requestData($request);
if (!$data['refresh_token'] || !$data['system_code']) return $this->error('参数缺失', 422);
$result = (new AuthService())->refreshToken($data['refresh_token'], $data['system_code']);
return $this->success($result, '刷新成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage(), 401);
}
}
// public function perm(Request $request)
// {
// try {
// $data = $this->requestData($request);
// if (!$data['access_token'] || !$data['system_code']) return $this->error('参数缺失', 422);
// $result = (new AuthService())->logout($data['access_token'], $data['system_code']);
// return $this->success($result, '已登出');
// } catch (\Throwable $e) {}
// }
// public function verify(Request $request)
// {
// try {
// $token = $request->post('access_token') ?: $request->header('authorization');
// $systemCode = $request->post('system_code');
// if (!$token || !$systemCode) return $this->error('参数缺失', 422);
// $payload = (new AuthService())->verifyToken($token, $systemCode);
// return $this->success($payload, '验证成功');
// } catch (\Throwable $e) {
// return $this->error($e->getMessage() ?: '无效或过期的token', 401);
// }
// }
// public function logout(Request $request)
// {
// $token = $request->post('access_token') ?: $request->header('Authorization');
// $systemCode = $request->post('system_code') ?: $request->header('system_code');
// (new AuthService())->logout($token, $systemCode);
// return $this->success([], '已登出');
// }
// public function blockToken(Request $request)
// {
// $token = $request->post('token');
// $systemCode = $request->post('system_code');
// if (!$token || !$systemCode) return $this->error('参数缺失', 422);
// (new AuthService())->blockToken($token, $systemCode);
// return $this->success([], '已强制停用该token');
// }
}
@@ -0,0 +1,65 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\BannerService;
use support\Request;
class BannerController extends BaseController
{
use CrudControllerTrait;
protected $noNeedLogin = ['getAll'];
protected $noNeedHeaderData = ['getAll'];
protected $service;
public function __construct()
{
$this->service = new BannerService();
}
/**
* 轮播
* @param Request $request
* @return \support\Response
*/
public function getAll(Request $request)
{
try {
$params = $request->all();
$result = $this->service->getBanner($params);
foreach ($result as $key => $value) {
if (isset($value['image_url']) && !empty($value['image_url'])){
$ossservice = new OssService();
$result[$key]['image_url_pull'] = $ossservice->generatePrivateUrl($value['image_url']);
}
}
return $this->success($result, '获取轮播列表');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['image_url']) && !empty($data['image_url'])){
$ossservice = new OssService();
$data['image_url_pull'] = $ossservice->generatePrivateUrl($data['image_url']);
}
return $data;
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
namespace app\common\controller;
use support\exception\BusinessException;
use app\common\traits\ResponseTrait;
use app\common\traits\DataTransformer;
use think\Validate;
use support\Request;
class BaseController
{
/**
* 响应处理、数据格式化处理
*/
use ResponseTrait, DataTransformer;
/**
* 验证器
* @var Validate
*/
protected mixed $validator = null;
/**
* @var Model
*/
protected $model = null;
/**
* 不登录不鉴权
* @var array
*/
protected $noNeedLogin = [];
/**
* 登录不鉴权
* @var array
*/
protected $noNeedAuth = [];
/**
* 不用头部数据
* @var array
*/
protected $noNeedHeaderData = [];
/**
* 数据限制
* null 不做限制,任何管理员都可以查看该表的所有数据
* auth 管理员能看到自己以及自己的子管理员插入的数据
* personal 管理员只能看到自己插入的数据
* @var string
*/
protected $dataLimit = null;
/**
* 数据限制字段
*/
protected $dataLimitField = 'admin_id';
/**
* 验证数据
*
* @param Validate $validator 验证器实例
* @param array $data 待验证的数据
* @param string $scene 验证场景
* @return array 验证后的数据
* @throws BusinessException 验证失败时抛出业务异常
*/
public function validate(Validate $validator, array $data, string $scene = ''): array
{
// 如果指定了验证场景,并且验证器支持场景方法,则设置验证场景
if ($scene && method_exists($validator, 'scene')) {
$validator->scene($scene);
}
// 执行数据验证
if (!$validator->check($data)) {
// 验证失败,抛出业务异常,异常信息为验证器返回的错误信息
throw new BusinessException($validator->getError());
}
// 验证成功,返回原始数据
return $data;
}
/**
* 参数处理
*/
public function requestData(Request $request)
{
return $request->all() + $request->basic_data;
}
}
@@ -0,0 +1,73 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\model\Post;
use app\common\model\PostComment;
use app\common\model\PostMedia;
use app\common\model\User;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\MessageNoticeService;
use support\Request;
class MessageNoticeController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new MessageNoticeService();
}
/**
* 获取列表
*
* @param Request $request 请求对象
* @return mixed 返回分页数据及总数
*/
public function index(Request $request)
{
try {
$raw = $this->service->serviceSelfList($request);
$data = $this->transformData($raw, method_exists($this, 'indexFormat')
? [$this, 'indexFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['post_id'])){
$data['post'] = Post::where('id', $data['post_id'])->first();
$data['post']['media'] = PostMedia::where('post_id', $data['post_id'])->get();
}
if (isset($data['sender_user_id'])){
$ossservice = new OssService();
$data['sender_user'] = User::where('id', $data['sender_user_id'])->select(['nickname', 'avatar', 'openid'])->first();
if(!str_contains($data['sender_user']['avatar'], 'http') && !empty($data['sender_user']['avatar'])){
$data['sender_user']['avatar_pull'] = $ossservice->generatePrivateUrl($data['sender_user']['avatar']);
}
}
if (!empty($data['comment_id'])){
$data['comment'] = PostComment::where('id', $data['comment_id'])->first();
}
return $data;
}
}
@@ -0,0 +1,38 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostCategoryService;
class PostCategoryController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostCategoryService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (!empty($data['icon'])){
$ossservice = new OssService();
$data['icon_pull'] = $ossservice->generatePrivateUrl($data['icon']);
}
return $data;
}
}
@@ -0,0 +1,79 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\model\PostComment;
use app\common\model\User;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostCommentService;
use support\Request;
class PostCommentController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostCommentService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['id'])){
$data['child'] = [];
$child = PostComment::where(['parent_id' => $data['id']])->first();
if(!empty($child)){
$child = $child->toArray();
}
$data['count'] = PostComment::where(['parent_id' => $data['id']])->count();
if (isset($child['user_id'])){
$ossservice = new OssService();
$child['user'] = User::where('id', $child['user_id'])->select(['nickname', 'avatar', 'openid'])->first();
if(!str_contains($child['user']['avatar'], 'http') && !empty($child['user']['avatar'])){
$child['user']['avatar_pull'] = $ossservice->generatePrivateUrl($child['user']['avatar']);
}
$child['created_at'] = date('Y-m-d H:i:s', strtotime($child['created_at']));
$child['updated_at'] = date('Y-m-d H:i:s', strtotime($child['updated_at']));
$data['child'][] = $child;
}
}
if (isset($data['user_id'])){
$ossservice = new OssService();
$data['user'] = User::where('id', $data['user_id'])->select(['nickname', 'avatar', 'openid'])->first();
if(!str_contains($data['user']['avatar'], 'http') && !empty($data['user']['avatar'])){
$data['user']['avatar_pull'] = $ossservice->generatePrivateUrl($data['user']['avatar']);
}
}
return $data;
}
/**
* 删除
*
* @param Request $request 请求对象
* @return mixed 返回删除操作结果
*/
public function delete(Request $request)
{
try {
$count = $this->service->serviceDelete($request);
return $this->success([], "删除评论成功");
} catch (\Throwable $e) {
return $this->error($e->getMessage().$e->getLine().$e->getFile());
}
}
}
+199
View File
@@ -0,0 +1,199 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\model\Post;
use app\common\model\PostComment;
use app\common\model\PostFavorite;
use app\common\model\PostLike;
use app\common\model\PostMedia;
use app\common\model\User;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostService;
use support\Request;
class PostController extends BaseController
{
use CrudControllerTrait;
// protected $noNeedLogin = ['index'];
//
// protected $noNeedHeaderData = ['index'];
protected $service;
public function __construct()
{
$this->service = new PostService();
}
/**
* 获取列表
*
* @param Request $request 请求对象
* @return mixed 返回分页数据及总数
*/
public function index(Request $request)
{
try {
$raw = $this->service->serviceList($request);
$data = $this->transformData($raw, method_exists($this, 'indexFormat')
? [$this, 'indexFormat']
: null);
$records = $data['records'];
foreach ($records as &$record) {
if (!isset($record['id']))
break;
if(!empty($request->user) && !empty($request->user->uid)){
$where = [
'post_id' => $record['id'],
'user_id' => $request->user->uid
];
$record['is_like'] = PostLike::where($where)->count();
$record['is_favorite'] = PostFavorite::where($where)->count();
$record['is_comment'] = PostComment::where($where)->count();
}else{
$record['is_like'] = 0;
$record['is_favorite'] = 0;
$record['is_comment'] = 0;
}
$created_at = strtotime($record['created_at']);
$record['year'] = date('Y', $created_at);
$record['month'] = date('m', $created_at);
$record['day'] = date('d', $created_at);
}
$data['records'] = $records;
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage().$e->getFile().$e->getLine());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
$ossservice = new OssService();
if (isset($data['id'])){
$data['media'] = PostMedia::where('post_id', $data['id'])->get()->toArray();
foreach ($data['media'] as &$media) {
$media['url_pull'] = $ossservice->generatePrivateUrl($media['url']);
}
}
if (isset($data['user_id'])){
$data['user'] = User::where('id', $data['user_id'])->first();
unset($data['user']['password']);
$data['user']['avatar_pull'] = $data['user']['avatar'];
if(!str_contains($data['user']['avatar'], 'http') && !empty($data['user']['avatar'])){
$data['user']['avatar_pull'] = $ossservice->generatePrivateUrl($data['user']['avatar']);
}
}
return $data;
}
/**
* 获取评论,点赞,收藏数量
*
* @param Request $request 请求对象
* @param int|string $id 资源 ID
* @return mixed 返回资源详情数据
*/
public function interaction(Request $request)
{
try {
$param = $request->all();
$data = Post::where('id', $param['id'])->select(['like','favorite','comment'])->first();
if(!empty($request->user) && !empty($request->user->uid)){
$where = [
'post_id' => $data['id'],
'user_id' => $request->user->uid
];
$data['is_like'] = PostLike::where($where)->count();
$data['is_favorite'] = PostFavorite::where($where)->count();
$data['is_comment'] = PostComment::where($where)->count();
}else{
$data['is_like'] = 0;
$data['is_favorite'] = 0;
$data['is_comment'] = 0;
}
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage().$e->getFile().$e->getLine());
}
}
/**
* 获取详情
*
* @param Request $request 请求对象
* @param int|string $id 资源 ID
* @return mixed 返回资源详情数据
*/
public function show(Request $request)
{
try {
$raw = $this->service->serviceShow($request);
$data = $this->transformData($raw, method_exists($this, 'showFormat')
? [$this, 'showFormat']
: null);
if(!empty($request->user) && !empty($request->user->uid)){
$where = [
'post_id' => $data['id'],
'user_id' => $request->user->uid
];
$data['is_like'] = PostLike::where($where)->count();
$data['is_favorite'] = PostFavorite::where($where)->count();
$data['is_comment'] = PostComment::where($where)->count();
}else{
$data['is_like'] = 0;
$data['is_favorite'] = 0;
$data['is_comment'] = 0;
}
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage().$e->getFile().$e->getLine());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function showFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
$ossservice = new OssService();
if (isset($data['id'])){
$data['media'] = PostMedia::where('post_id', $data['id'])->get()->toArray();
foreach ($data['media'] as &$media) {
$media['url_pull'] = $ossservice->generatePrivateUrl($media['url']);
}
}
if (isset($data['user_id'])){
$data['user'] = User::where('id', $data['user_id'])->first();
unset($data['user']['password']);
$data['user']['avatar_pull'] = $data['user']['avatar'];
if(!str_contains($data['user']['avatar'], 'http') && !empty($data['user']['avatar'])){
$data['user']['avatar_pull'] = $ossservice->generatePrivateUrl($data['user']['avatar']);
}
}
return $data;
}
}
@@ -0,0 +1,100 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\model\Post;
use app\common\model\PostMedia;
use app\common\model\User;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostFavoriteService;
use support\Request;
class PostFavoriteController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostFavoriteService();
}
/**
* 获取列表
*
* @param Request $request 请求对象
* @return mixed 返回分页数据及总数
*/
public function self(Request $request)
{
try {
$raw = $this->service->serviceSelfList($request);
$data = $this->transformData($raw, method_exists($this, 'indexFormat')
? [$this, 'indexFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
if (isset($data['post_id'])){
$ossservice = new OssService();
$post = Post::where('id', $data['post_id'])->first()->toArray();
if (isset( $post['user_id'])){
$user = User::where('id', $post['user_id'])->first();
$postUser = [];
$postUser['name'] = $user['name'];
$postUser['nickname'] = $user['nickname'];
$postUser['avatar'] = $user['avatar'];
$postUser['avatar_pull'] = $user['avatar'];
if(!str_contains($postUser['avatar'], 'http') && !empty($postUser['avatar'])){
$postUser['avatar_pull'] = $ossservice->generatePrivateUrl($postUser['avatar']);
}
$post['user'] = $postUser;
}
$ossservice = new OssService();
if (isset($post['id'])){
$post['media'] = PostMedia::where('post_id', $post['id'])->get()->toArray();
foreach ($post['media'] as &$media) {
$media['url_pull'] = $ossservice->generatePrivateUrl($media['url']);
}
}
$data['post'] = $post;
}
return $data;
}
/**
* 删除
*
* @param Request $request 请求对象
* @return mixed 返回删除操作结果
*/
public function delete(Request $request)
{
try {
$count = $this->service->serviceDelete($request);
return $this->success([], "取消收藏成功");
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
@@ -0,0 +1,69 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostLikeService;
use support\Request;
class PostLikeController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostLikeService();
}
/**
* 获取列表
*
* @param Request $request 请求对象
* @return mixed 返回分页数据及总数
*/
public function index(Request $request)
{
try {
$raw = $this->service->serviceSelfList($request);
$data = $this->transformData($raw, method_exists($this, 'indexFormat')
? [$this, 'indexFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 删除
*
* @param Request $request 请求对象
* @return mixed 返回删除操作结果
*/
public function delete(Request $request)
{
try {
$count = $this->service->serviceDelete($request);
return $this->success([], "取消点赞成功");
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
return $data;
}
}
@@ -0,0 +1,33 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\PostMediaService;
class PostMediaController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new PostMediaService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,90 @@
<?php
namespace app\common\controller;
use app\admin\service\RoleService;
use app\common\service\BrandSeriesService;
use app\common\service\BrandService;
use app\common\service\SysAccountService;
use app\common\service\SysCurrencyService;
use app\common\service\SysDictItemService;
use app\common\service\SysDictService;
use app\common\service\SysPortService;
use app\common\service\VehColorService;
use app\common\service\VehModificationService;
use support\Request;
use app\common\service\CorpService;
use app\common\service\CorpFeishuAppService;
use app\common\service\SystemsService;
use app\common\service\UserService;
use support\Cache;
use app\common\service\PermService;
use app\common\service\UserGroupService;
class PresetController extends BaseController
{
protected $noNeedLogin = ['systems'];
protected $noNeedHeaderData = ['systems'];
public function systems()
{
try {
$result = (new SystemsService())->getList();
return $this->success($result, '获取系统列表');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function users()
{
try {
$result = (new UserService())->getList();
return $this->success($result, '获取用户列表');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function roles()
{
try {
$result = (new RoleService)->getList();
return $this->success($result, '获取角色列表');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function cacheClear()
{
try {
Cache::clear();
return $this->success([], '操作成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function menus(Request $request)
{
try {
$uid = $request->user->uid;
$systemCode = $request->system_code;
$permService = new PermService();
$menuTree = $permService->getMenuTree($uid, $systemCode);
$permissionCodes = $permService->getPermissionCodes($uid, $systemCode);
$result = [
'menu' => $menuTree,
'permissions' => $permissionCodes
];
return $this->success($result, '获取菜单权限');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
@@ -0,0 +1,82 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\ResourceService;
use support\Request;
class ResourceController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new ResourceService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function allFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
return $data;
}
/**
* 资源树形结构
*/
public function listTree(Request $request)
{
try {
$raw = $this->service->serviceListAll($request);
$data = $this->transformData($raw, method_exists($this, 'allFormat')
? [$this, 'allFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 菜单
*/
public function menuList(Request $request)
{
try {
$raw = $this->service->serviceMenuList($request);
$data = $this->transformData($raw, method_exists($this, 'allFormat')
? [$this, 'allFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 菜单
*/
public function menuTreeList(Request $request)
{
try {
$raw = $this->service->serviceMenuTreeList($request);
$data = $this->transformData($raw, method_exists($this, 'allFormat')
? [$this, 'allFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage().$e->getFile().$e->getLine());
}
}
}
@@ -0,0 +1,33 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\SkuAttributeService;
class SkuAttributeController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new SkuAttributeService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,33 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\SkuAttributeValueService;
class SkuAttributeValueController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new SkuAttributeValueService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\SkuService;
class SkuController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new SkuService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
+299
View File
@@ -0,0 +1,299 @@
<?php
namespace app\common\controller;
use app\common\service\OssService;
use app\common\service\UploadedFilesService;
use app\common\service\UploadService;
use app\common\traits\ResponseTrait;
use support\Log;
use support\Request;
use support\exception\BusinessException;
use Shopwwi\WebmanFilesystem\Facade\Storage;
class UploadController
{
use ResponseTrait;
protected $noNeedLogin = ['proxy'];
protected $noNeedHeaderData = ['proxy'];
public function upload(Request $request)
{
try {
$scene = $request->input('scene', 'image');
$file = $request->file('file');
if ($file && $file->isValid()) {
$fileName = md5($file->getUploadName() . time()).'.'.$file->getUploadExtension();
$pathUrl = '/bsh_sample/'.$scene.'/'.date('Ymd').'/'.$fileName;
$fullPathUrl = public_path() . $pathUrl;
$file->move($fullPathUrl);
$data = [
'domain' => getenv('MAIN_DOMAIN'),
'file_url' => $pathUrl,
'full_url' => getenv('MAIN_DOMAIN') . $pathUrl,
'scene' => $scene,
];
// 记录文件信息到数据库
try {
$uploadedFilesService = new UploadedFilesService();
$partition = $scene;
$category = $request->input('category', 'general');
$fileInfo = [
'file_name' => basename($fileName),
'file_path' => $pathUrl,
'file_size' => $file->getSize(),
'file_type' => $file->getUploadMimeType(),
'original_name' => $file->getUploadName(),
];
$recordResult = $uploadedFilesService->recordFileInfo($fileInfo, $request, $partition, $category);
$data['file_id'] = $recordResult['id'];
$data['partition'] = $partition;
$data['category'] = $category;
} catch (\Throwable $e) {
// 记录失败不影响上传结果,只记录日志
Log::error('记录文件信息失败: ' . $e->getMessage());
}
return $this->success($data, 'upload success');
}
return $this->error('file not found');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
// 上传单文件附件
public function uploadFile(Request $request)
{
try {
$scene = $request->input('scene');
$file = $request->file('file');
if (!$file || !$file->isValid()) {
return $this->error('上传失败');
}
if (empty($scene)) {
// return $this->error('上传场景不能为空');
$sceneUrl = '';
}else{
$sceneUrl = $scene . '/';
}
$originalName = $file->getUploadName();
$result = Storage::adapter('oss')
->path('bsh_sample/' . $sceneUrl . date('Ymd'))
->size(1024*1024*100)
->upload($file);
$ossservice = new OssService();
$data = [
'original_name' => $originalName,
'file_name' => $result->file_name,
'file_url' => $ossservice->generatePrivateUrl($result->file_name)
];
// 记录文件信息到数据库
$category = $request->input('category', 'general');
$res = $this->recordFileInfo($result,$request,$file,$scene,$category);
$data = array_merge($data, $res);
return $this->success($data);
}catch (\Throwable $e){
return $this->error($e->getMessage());
}
}
// 上传单文件图片
public function uploadImage(Request $request)
{
try {
$scene = $request->input('scene');
$file = $request->file('image');
if (!$file || !$file->isValid()) {
return $this->error('上传失败');
}
if (empty($scene)) {
// return $this->error('上传场景不能为空');
$sceneUrl = '';
}else{
$sceneUrl = $scene . '/';
}
$result = Storage::adapter('oss')
->path('bsh_sample/' . $sceneUrl . date('Ymd'))
->size(1024*1024*100)
->extYes(['image/jpeg','image/png'])
->extNo(['image/gif'])
->upload($file);
$ossservice = new OssService();
$data = [
'href' => $result->file_name,
'alt' => '',
'url' => $ossservice->generatePrivateUrl($result->file_name)
];
// 记录文件信息到数据库
$category = $request->input('category', 'general');
$res = $this->recordFileInfo($result,$request,$file,$scene,$category);
$data = array_merge($data, $res);
return json(['errno' => 0, 'data' => $data]);
// return $this->success($data);
} catch (\Throwable $e) {
return json(['errno' => 1, 'message' => $e->getMessage()]);
// return $this->error($e->getMessage());
}
}
public function recordFileInfo($result, $request, $file, $scene, $category)
{
try {
$uploadedFilesService = new UploadedFilesService();
$partition = $scene;
$fileInfo = [
'file_name' => basename($result->file_name),
'file_path' => $result->file_name, // OSS存储路径
'file_size' => $file->getSize(),
'file_type' => $file->getUploadMimeType(),
'original_name' => $file->getUploadName(),
];
$partition = !empty($partition)?$partition:'other';
$category = !empty($category)?$category:'other';
$recordResult = $uploadedFilesService->recordFileInfo($fileInfo, $request, $partition, $category);
$data = [];
$data['file_id'] = $recordResult['id'];
$data['partition'] = $partition;
$data['category'] = $category;
return $data;
} catch (\Throwable $e) {
// 记录失败不影响上传结果,只记录日志
Log::error('记录文件信息失败: ' . $e->getMessage());
return [];
}
}
/**
* 获取文件信息
*
* @param Request $request
* @return \support\Response
*/
public function getFileInfo(Request $request)
{
try {
$fileId = $request->input('id');
if (!$fileId) {
return $this->error('文件ID不能为空');
}
$uploadedFilesService = new UploadedFilesService();
$fileInfo = $uploadedFilesService->getFileInfo($fileId);
if (!$fileInfo) {
return $this->error('文件不存在');
}
return $this->success($fileInfo);
} catch (\Throwable $e) {
return $this->error('获取文件信息失败: ' . $e->getMessage());
}
}
/**
* 删除文件
*
* @param Request $request
* @return \support\Response
*/
public function deleteFile(Request $request)
{
try {
$fileId = $request->input('id');
$path = $request->input('path');
$uploadedFilesService = new UploadedFilesService();
if(!empty($path) && empty($fileId)){
$where = ['file_path' => $path];
$file = $uploadedFilesService->getFile($where);
$fileId = $file->id;
}elseif(empty($path) && !empty($fileId)){
$where = ['id' => $fileId];
$file = $uploadedFilesService->getFile($where);
$path = $file->file_path;
}
if (!$fileId) {
return $this->error('文件ID与路径不能为空');
}
// 删除 OSS 上的文件
$ossService = new OssService();
$ossService->deleteFile($path);
$result = $uploadedFilesService->deleteFile($fileId);
if ($result) {
return $this->success([], '文件删除成功');
} else {
return $this->error('文件删除失败');
}
} catch (\Throwable $e) {
return $this->error('文件删除失败: ' . $e->getMessage());
}
}
/**
* 获取分区配置
*
* @param Request $request
* @return \support\Response
*/
public function getPartitionConfig(Request $request)
{
try {
$uploadedFilesService = new UploadedFilesService();
$config = $uploadedFilesService->getPartitionConfig();
return $this->success($config);
} catch (\Throwable $e) {
return $this->error('获取分区配置失败: ' . $e->getMessage());
}
}
public function proxy(Request $request)
{
$filePath = $request->get('file'); // 原OSS文件路径
try {
// 生成签名URL
$signedUrl = (new \app\common\service\OssService())->generatePrivateUrl($filePath);
// 获取图片数据并返回
$imageData = file_get_contents($signedUrl);
// 根据文件类型设置Content-Type
$mime = mime_content_type('data://application/octet-stream;base64,'.base64_encode($imageData));
return response($imageData, 200, [
'Content-Type' => $mime,
'Cache-Control' => 'public, max-age=600' // 客户端缓存10分钟
]);
} catch (\Exception $e) {
return response('Image not found '.$e->getMessage(), 404);
}
}
}
+191
View File
@@ -0,0 +1,191 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\service\OssService;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\UserService;
use app\common\service\WechatService;
use support\Request;
use support\Response;
class UserController extends BaseController
{
protected $noNeedLogin = ['getQrUrl', 'getUserInfo', 'bindUser', 'miniappLogin'];
protected $noNeedHeaderData = ['getQrUrl', 'getUserInfo', 'bindUser', 'miniappLogin'];
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new UserService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
/**
* 获取微信二维码
*/
public function getQrUrl()
{
try {
$result = $this->service->makeQrUrl();
return $this->success($result);
} catch (\Throwable $th) {
return $this->error($th->getMessage());
}
}
/**
* 获取用户信息
*/
public function getUserInfo(Request $request)
{
try {
$params = $request->all();
$result = $this->service->userInfo($params['code'], $params['state']);
unset($result['password']);
if (!isset($result['avatar'])){
return $this->success($result);
}
$ossservice = new OssService();
$result['avatar_pull'] = $result['avatar'];
if(strpos($result['avatar'], 'http') === false && !empty($result['avatar'])){
$result['avatar_pull'] = $ossservice->generatePrivateUrl($result['avatar']);
}
return $this->success($result);
} catch (\Throwable $th) {
return $this->error($th->getMessage());
}
}
/**
* 用户信息(登录后调用)
*/
public function info(Request $request)
{
try {
$uid = $request->user->uid;
$result = (new UserService())->getUserInfo($uid);
return $this->success($result, '获取用户信息');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 绑定用户
*/
public function bindUser(Request $request)
{
try {
$params = $request->all();
$result = $this->service->bindUser($params);
unset($result['password']);
return $this->success($result);
} catch (\Throwable $th) {
return $this->error($th->getMessage());
}
}
/**
* 修改密码
* @param Request $request
* @return \support\Response
* @throws \Random\RandomException
*/
public function savePassword(Request $request)
{
try {
$uid = $request->user->uid;
$data = $request->all();
$result = (new UserService())->savePassword($uid,$data);;
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 修改名字与头像
* @param Request $request
* @return \support\Response
* @throws \Random\RandomException
*/
public function save(Request $request)
{
try {
$uid = $request->user->uid;
$data = $request->all();
$result = (new UserService())->save($uid,$data);;
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function wechatLogin($request)
{
try {
$params = $request->all();
$result = $this->service->wechatLogin($params['login_code'], $params['phone_code']);
return $this->success($result);
} catch (\Throwable $th) {
return $this->error($th->getMessage());
}
}
/**
* 小程序登录
*/
public function miniappLogin(Request $request): Response
{
try {
$result = $this->service->miniappLogin($request->all());
return $this->success($result);
} catch (\Throwable $th) {
return $this->error($th->getMessage());
}
}
/**
* 获取手机号
*/
public function miniappPhoneNumber(Request $request): Response
{
try {
$result = $this->service->miniappPhoneNumber($request->all(),$request);
return $this->success($result);
} catch (\Throwable $th) {
return $this->error($th->getMessage());
}
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\Agreement;
class AgreementLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Agreement::class;
}
+572
View File
@@ -0,0 +1,572 @@
<?php
namespace app\common\logic;
use app\common\model\Corp;
use app\common\model\CorpFeishuApp;
use app\common\model\Resource;
use app\common\model\UserTokenLog;
use app\common\model\Systems;
use app\common\model\User;
use app\common\model\UserSn;
use app\common\utils\Feishu;
use app\common\utils\Jwt;
use support\Redis;
use support\exception\BusinessException;
use support\Log;
class AuthLogic
{
/**
* 账号密码登录
*/
public function uPwdLogin(array $data): array
{
$user = $this->validateUser($data['account'], $data['password']);
$system = $this->validateSystem($data['system_code']);
$this->validateSystemPermission($user, $system);
return $this->generateTokensAndReturn($user, $system, $data);
}
/**
* 飞书登录
* 如果用户不存在则新增用户信息
*/
public function feishuLogin(array $data): array
{
$feishuUserInfo = $this->getFeishuUserInfo($data);
$user = $this->getOrCreateUser($feishuUserInfo);
$system = $this->validateSystem($data['system_code']);
if ($user->isLogin) {
$this->validateSystemPermission($user, $system);
}
return $this->generateTokensAndReturn($user, $system, $data);
}
/**
* 注册
*/
public function register(array $data)
{
try {
// 获取可用用户编号
$sn = $this->getAvailableUserSn();
$data['sn'] = $sn->user_sn;
// 对密码进行哈希处理
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
// 处理账户
$data = $data + normalizeAccount($data['account']);
// 创建用户记录
$user = $this->createUser($data);
// 更新用户编号的用户 ID
$this->updateUserId($user->id, $sn->user_sn);
$system = $this->validateSystem($data['system_code']);
return $this->generateTokensAndReturn($user, $system, $data);
} catch (\Throwable $e) {
throw $e;
}
}
/**
* 查找或创建飞书用户
*/
private function getOrCreateUser(array $feishuUserInfo)
{
try {
$phone = $this->sanitizePhone($feishuUserInfo['mobile'] ?? '');
$email = $feishuUserInfo['enterprise_email'];
// $user = User::where('corp_id', $feishuUserInfo['corp_id'])
// ->where(function($query) use ($phone, $email) {
// $query->where('phone', $phone)
// ->orWhere('email', $email);
// })
// ->first();
$userByPhone = User::where('corp_id', $feishuUserInfo['corp_id'])
->where('phone', $phone)
->where('phone', '!=', '')
->first();
$userByEmail = User::where('corp_id', $feishuUserInfo['corp_id'])
->where('email', $email)
->where('email', '!=', '')
->first();
$user = $userByPhone ?: $userByEmail;
if (!$user) {
// 获取编号
$userSn = $this->getAvailableUserSn();
// 创建用户
$user = User::create([
'name' => $feishuUserInfo['name'],
'avatar' => $feishuUserInfo['avatar_middle'],
'phone' => $this->sanitizePhone($feishuUserInfo['mobile'] ?? ''),
'email' => $feishuUserInfo['enterprise_email'],
'union_id' => $feishuUserInfo['union_id'],
'user_id' => $feishuUserInfo['user_id'],
'open_id' => $feishuUserInfo['open_id'],
'tenant_key' => $feishuUserInfo['tenant_key'],
'corp_id' => $feishuUserInfo['corp_id'],
'password' => password_hash($userSn->user_sn, PASSWORD_DEFAULT)
]);
// 更新使用
$this->updateUserId($user->id, $userSn->user_sn);
// 更新编号
if (!$user->update(['sn'=>$userSn->user_sn])) {
throw new BusinessException('编号绑定失败');
}
// 是否登陆
$user->isLogin = false;
$options = [
'corp_id' => $user->corp_id,
'user_id' => $user->id
];
\Webman\RedisQueue\Redis::send('perm-agt', $options);
} else {
$updated = [
'union_id' => $feishuUserInfo['union_id'],
'user_id' => $feishuUserInfo['user_id'],
'open_id' => $feishuUserInfo['open_id'],
'tenant_key' => $feishuUserInfo['tenant_key']
];
if ($phone && $phone == $user->phone) {
$updated['phone'] = $phone;
}
if ($phone && $phone == $user->phone) {
$updated['email'] = $email;
}
// 更新飞书信息
$user->update($updated);
// 是否登陆
$user->isLogin = true;
}
return $user;
} catch (\Throwable $e) {
throw $e;
}
}
/**
* 过滤和格式化手机号
*/
private function sanitizePhone($mobile)
{
return isset($mobile) ? str_replace('+86', '', $mobile) : '';
}
/**
* 生成并记录token,返回包含token的响应数据
*/
private function generateTokensAndReturn(User $user, Systems $system, array $data): array
{
$payload = $this->buildJwtPayload($user, $system);
$token = $this->generateAccessToken($payload);
$refreshToken = $this->generateRefreshToken($user->id, $data['system_code']);
$homeUrl = $this->getHome($system->id);
// 记录 token 到 Redis 和 MySQL
$this->recordUserToken($user->id, $token, $data['system_code']);
return [
'access_token' => $token,
'refresh_token' => $refreshToken,
'expires_in' => config('jwt.jwt_expiration'),
'system_code' => $data['system_code'],
'login_type' => $data['login_type'],
'home_url' => $homeUrl,
// 'payload' => $payload,
];
}
/**
* 获取飞书用户信息
*/
private function getFeishuUserInfo(array $data)
{
$certificate = $this->feishuAppInfo($data['corp_abbr'], $data['system_code']);
$feishu = new Feishu($certificate['app_id'], $certificate['app_secret'], $certificate['abbr']);
$userAccessToken = $feishu->userAccessToken($data['feishu_code'], $certificate['r_url']);
$userInfo = $feishu->userInfo($userAccessToken['access_token']);
if ($userInfo['code'] != 0) {
throw new BusinessException('授权失败,请重试');
}
return $userInfo['data'] + $data + $certificate;
}
/**
* 更新用户编号
*/
private function updateUserId($userId, $userSn)
{
$updated = UserSn::where('user_sn', $userSn)
->update(['user_id' => $userId]);
if ($updated === 0) {
throw new BusinessException('用户编号不存在或更新失败');
}
return $updated;
}
/**
* 记录用户token到 Redis 和 MySQL
*/
protected function recordUserToken(int $userId, string $token, string $systemCode)
{
$request = request();
$ip = $request ? $request->getRealIp() : '';
$ua = $request ? $request->header('user-agent', '') : '';
$now = time();
$expireAt = $now + config('jwt.jwt_expiration');
// Redis Set 用户token集合
Redis::sAdd("user_tokens:{$userId}:{$systemCode}", $token);
Redis::expire("user_tokens:{$userId}:{$systemCode}", config('jwt.jwt_expiration'));
// Redis Hash token元信息
$meta = [
'system_code' => $systemCode,
'login_ip' => $ip,
'user_agent' => $ua,
'login_time' => $now,
'expired_at' => $expireAt,
];
Redis::hMSet("user_token_meta:{$token}", $meta);
Redis::expire("user_token_meta:{$token}", config('jwt.jwt_expiration'));
// 可选:MySQL 持久化
try {
UserTokenLog::create([
'user_id' => $userId,
'token' => $token,
'system_code'=> $systemCode,
'login_ip' => $ip,
'user_agent' => $ua,
'login_time' => $now,
'expired_at' => $expireAt,
'is_valid' => 1,
]);
} catch (\Throwable $e) {
Log::error('Token log write failed: '.$e->getMessage());
}
}
/**
* 校验token有效性
*/
public function verifyToken(string $token, string $systemCode): array
{
if (!$systemCode) throw new BusinessException('缺少system_code', 422);
$payload = (new Jwt())->verifyToken($token);
// 校验黑名单
if (Redis::has("token_blacklist:{$token}")) {
throw new BusinessException('token已失效', 403);
}
// 强制校验aud(系统隔离)
if ($systemCode && $payload['aud'] != $systemCode && $payload['aud'] != ($payload['system_code'] ?? '')) {
throw new BusinessException('token不属于当前系统', 403);
}
return (array)$payload;
}
/**
* 获取飞书应用信息
*/
public function feishuAppInfo(string $corpAbbr, string $systemCode)
{
$corpInfo = $this->validateCorp($corpAbbr);
$systemInfo = $this->validateSystem($systemCode);
$fsAppInfo = $this->validateFeishuApp($corpInfo->id, $systemInfo->id);
return [
'app_id' => $fsAppInfo->app_id,
'app_secret' => $fsAppInfo->app_secret,
'r_url' => $fsAppInfo->r_url,
'scope' => $fsAppInfo->scopes,
'state' => $fsAppInfo->state,
'abbr' => $corpAbbr,
'corp_id' => $corpInfo->id,
'system_id' => $systemInfo->id
];
}
/**
* 验证用户信息
*/
private function validateUser(string $account, string $password)
{
// $user = User::byAccount($account)->first();
$user = User::byAccount($account)->first();
if (!$user || !password_verify($password, $user->password)) {
throw new BusinessException('账号或密码错误', 401);
}
if ($user->status != 1) {
throw new BusinessException('账号已禁用', 403);
}
return $user;
}
/**
* 验证系统信息
*/
private function validateSystem(string $systemCode)
{
$system = Systems::where('code', $systemCode)->first();
if (!$system) {
throw new BusinessException('系统不存在', 403);
}
return $system;
}
/**
* 验证公司信息
*/
private function validateCorp(string $corpAbbr)
{
$corp = Corp::where('abbr', $corpAbbr)->first();
if (!$corp) {
throw new BusinessException('公司不存在', 403);
}
return $corp;
}
/**
* 验证飞书应用信息
*/
private function validateFeishuApp(int $corpId, int $systemId)
{
$feishuAppInfo = CorpFeishuApp::where('corp_id', $corpId)->where('system_id', $systemId)->first();
if (!$feishuAppInfo) {
throw new BusinessException('飞书应用不存在', 403);
}
return $feishuAppInfo;
}
/**
* 验证用户系统权限
*/
private function validateSystemPermission(User $user, Systems $system)
{
if ($system->is_role) {
$systemIds = $user->systems->pluck('id')->toArray();
if (!in_array($system->id, $systemIds)) {
throw new BusinessException('无该系统权限', 403);
}
}
}
/**
* 获取可用的用户编号
*
* @return \app\common\model\UserSn 用户编号模型实例
* @throws BusinessException 当未找到适用编号时抛出
*/
private function getAvailableUserSn(): \app\common\model\UserSn
{
$userSnModel = new UserSn();
$sn = $userSnModel->where('user_id', '=', 0)->inRandomOrder()->first();
if (!$sn) {
Log::error('用户注册时未找到适用编号');
throw new BusinessException('未找到适用编号');
}
return $sn;
}
/**
* 创建用户记录
*
* @param array $data 包含用户信息的数组
* @return \app\common\model\User 用户模型实例
*/
private function createUser(array $data): \app\common\model\User
{
try {
return User::create($data);
} catch (\Throwable $e) {
Log::error('用户注册时创建用户记录失败', [
'data' => $data,
'error' => $e->getMessage()
]);
throw new BusinessException('用户注册失败,请稍后重试', 500);
}
}
/**
* JWT payload 构建
* 可扩展 payload 字段
*/
private function buildJwtPayload(User $user, ?Systems $system = null): array
{
$now = time();
$exp = $now + config('jwt.jwt_expiration');
$system_ids = $user->systems ? $user->systems->pluck('id')->toArray() : [];
$role_ids = $user->roles ? $user->roles->pluck('id')->toArray() : [];
return [
'sub' => 'user_' . $user->id,
'aud' => $system->code,
'iat' => $now,
'nbf' => $now,
'exp' => $exp,
'typ' => 'access',
'uid' => $user->id,
'uname' => $user->name,
'role_ids' => $role_ids,
'system_ids' => $system_ids
];
}
/**
* JWT签发
*/
private function generateAccessToken(array $payload)
{
return (new Jwt())->generateToken($payload);
}
/**
* 刷新令牌生成
*/
public function generateRefreshToken(int $userId, string $systemCode)
{
$refreshToken = bin2hex(random_bytes(32));
// user_id+system_code只允许一个refresh_token生效
Redis::set("refresh_token:{$userId}:{$systemCode}", $refreshToken, config('jwt.jwt_refresh_token_expiration'));
// 建立refresh_token到user_id+system_code映射,便于校验
Redis::set("refresh_token_map:{$refreshToken}:{$systemCode}", $userId, config('jwt.jwt_refresh_token_expiration'));
return $refreshToken;
}
/**
* 刷新token
* @param string $refreshToken
* @param string|null $systemCode 可选,指定系统
* @return array
*/
public function refreshToken(string $refreshToken, ?string $systemCode = null): array
{
if (!$systemCode) throw new BusinessException('缺少system_code', 422);
// 校验refresh_token
$userId = Redis::get("refresh_token_map:{$refreshToken}:{$systemCode}");
if (!$userId) {
throw new BusinessException('refresh_token无效或已过期', 401);
}
$validToken = Redis::get("refresh_token:{$userId}:{$systemCode}");
if ($validToken !== $refreshToken) {
throw new BusinessException('refresh_token无效或已过期', 401);
}
$user = User::find($userId);
if (!$user || $user->status != 1) {
throw new BusinessException('用户无效', 403);
}
$system = $this->validateSystem($systemCode);
$this->validateSystemPermission($user, $system);
$payload = $this->buildJwtPayload($user, $system);
$token = $this->generateAccessToken($payload);
$refreshToken = $this->generateRefreshToken($user->id, $systemCode);
$this->recordUserToken($user->id, $token, $systemCode);
return [
'access_token' => $token,
'refresh_token' => $refreshToken,
'expires_in' => config('jwt.jwt_expiration'),
// 'payload' => $payload,
];
}
/**
* 登出/失效token
*/
public function logout(?string $token, ?string $systemCode = null)
{
if ($token) {
Redis::set("token_blacklist:{$token}", 1, config('jwt.jwt_expiration'));
$payload = null;
try { $payload = (new Jwt())->verifyToken($token); } catch (\Throwable $e) {}
$userId = $payload['uid'] ?? null;
$aud = $payload['aud'] ?? null;
$sc = $systemCode ?? $aud;
if ($userId && $sc) {
Redis::sRem("user_tokens:{$userId}:{$sc}", $token);
}
Redis::del("user_token_meta:{$token}");
UserTokenLog::where('token', $token)->update(['is_valid'=>0]);
}
}
/**
* 强制拉黑token
*/
public function blockToken(string $token, ?string $systemCode = null)
{
if (!$token) {
throw new BusinessException('token不能为空', 422);
}
Redis::set("token_blacklist:{$token}", 1, config('jwt.jwt_expiration'));
$payload = null;
try { $payload = (new Jwt())->verifyToken($token); } catch (\Throwable $e) {}
$userId = $payload['uid'] ?? null;
$aud = $payload['aud'] ?? null;
$sc = $systemCode ?? $aud;
if ($userId && $sc) {
Redis::sRem("user_tokens:{$userId}:{$sc}", $token);
}
Redis::del("user_token_meta:{$token}");
UserTokenLog::where('token', $token)->update(['is_valid'=>0]);
}
public function getHome($systemId)
{
$info = Resource::where('system_id', $systemId)
->where('is_home', 1)
->first();
if (!$info) {
return '/dashboard/console';
}
// 递归拼接 URL
$url = $this->buildParentUrl($info);
return $url ?: '/dashboard/console';
}
/**
* 递归构建父级 URL
* @param Resource $resource
* @return string
*/
protected function buildParentUrl(Resource $resource)
{
$url = $resource->url ?? '';
// 如果存在父级,则递归拼接
if ($resource->parent_id != 0) {
$parent = Resource::find($resource->parent_id);
if ($parent) {
$parentUrl = $this->buildParentUrl($parent);
$url = $parentUrl . ($url ? '/' . ltrim($url, '/') : '');
}
}
return $url;
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\Banner;
class BannerLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Banner::class;
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\MessageNotice;
class MessageNoticeLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = MessageNotice::class;
}
+144
View File
@@ -0,0 +1,144 @@
<?php
namespace app\common\logic;
use app\common\model\User;
use app\common\model\Resource;
use app\common\model\Systems;
class PermLogic
{
/**
* 获取用户所有资源(合并角色+直接分配),并加入公共菜单,排序按sort字段
*/
public function getUserResources(int $userId, string $systemCode)
{
$system = Systems::where('code', $systemCode)->first();
// 获取用户及其关联角色资源和直接分配的资源
$user = User::with(['roles.resources', 'resources'])->find($userId);
if (!$user) {
return collect();
}
$all = collect();
// 遍历用户的所有角色,合并每个角色所拥有的资源
foreach ($user->roles as $role) {
$all = $all->merge(
// 过滤当前角色的资源列表
$role->resources->filter(function ($resource) use($system) {
// 资源过滤条件:
// 1. !$resource->no_auth:资源需要权限认证(排除公共资源)
// 2. 系统匹配条件:
// - $system->id == $resource->system_id:资源属于当前系统
// - $resource->system_id == 0:资源为全局公共资源(所有系统可用)
return !$resource->no_auth && ($system->id == $resource->system_id || $resource->system_id == 0);
})
);
}
// 合并用户直接分配的资源,排除不需要权限认证的资源
$all = $all->merge(
$user->resources->filter(function ($resource) {
// 只保留需要权限认证的资源(或公共资源)
return !$resource->no_auth;
})
);
// 加入公共菜单,公共菜单不管是否授权,所有人都能访问
$publicResources = Resource::where('no_auth', 1)
->orderBy('sort', 'asc') // 按 sort 字段升序排序
->get();
// 合并所有公共资源
$all = $all->merge($publicResources);
// 返回唯一的资源列表,并按 id 排序
// 使用 sortBy('sort') 来确保最终结果根据 'sort' 字段排序
return $all->unique('id')->values()->sortBy('sort');
}
/**
* 构建目录/菜单树
*
* @param \Illuminate\Support\Collection $resources 全部资源集合
* @param int $parentId 当前父级资源 ID
* @param string $parentPath 父级完整路径(用于递归)
* @return array
*/
public function buildTree($resources, int $parentId = 0, string $parentPath = ''): array
{
$tree = [];
foreach ($resources as $node) {
if ($node->parent_id !== $parentId) continue;
// 拼接完整路径
$currentPath = $parentPath;
if ($currentPath && substr($currentPath, -1) === '/') {
$currentPath = rtrim($currentPath, '/');
}
if (!empty($node->url)) {
if (!$currentPath) {
$currentPath = $node->url;
} else {
$url = ltrim($node->url, '/');
$currentPath = $currentPath . '/' . $url;
}
}
// 子节点递归
$children = [];
if ($node->type === 'directory') {
$children = $this->buildTree($resources, $node->id, $currentPath);
}
// 权限
$authList = $resources
->filter(fn($r) => $r->type === 'permission' && $r->parent_id === $node->id)
->map(fn($r) => [
'id' => $r->id,
'title' => $r->name,
'auth_mark' => $r->code,
])->values()->all();
// 判断是否有子级菜单(非权限)
$hasChildrenMenu = collect($resources)->where('parent_id', $node->id)
->whereIn('type', ['directory', 'menu'])->isNotEmpty();
$componentValue = $hasChildrenMenu ? '/index/index' : $currentPath;
$tree[] = [
'id' => $node->id,
'name' => $node->name,
'path' => $node->url,
'component' => $componentValue,
'meta' => [
'title' => $node->name,
'icon' => $node->icon,
'keepAlive' => $node->keep_alive == 1 ? true : false,
'authList' => $authList,
'openType' => $node->open_type,
'isHide' => $node->is_hide == 0 ? false : true,
'isHideTab' => $node->is_hide_tab == 0 ? false : true
],
'children' => $children,
];
}
return $tree;
}
/**
* 获取用户所有权限code
*/
public function getPermissionCodes(int $userId, string $systemCode): array
{
$resources = $this->getUserResources($userId, $systemCode);
return $resources
->filter(fn($res) => $res->type === 'permission')
->pluck('code')
->unique()
->values()
->all();
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\PostCategory;
class PostCategoryLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostCategory::class;
public function getList($where)
{
return PostCategory::where('status', 1)->where($where)->orderBy('sort','desc')->get();
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\PostComment;
class PostCommentLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostComment::class;
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\PostFavorite;
class PostFavoriteLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostFavorite::class;
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\PostLike;
class PostLikeLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostLike::class;
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\Post;
class PostLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Post::class;
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\PostMedia;
class PostMediaLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostMedia::class;
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\Resource;
use support\Request;
class ResourceLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Resource::class;
public function logicListAllResources(Request $request, array $config)
{
$data = $this->logicListAll($config);
if ($request->get('role_id')) {
$selected = (new RoleLogic())->getRoleResources($request->get('role_id'));
} else if ($request->get('user_id')) {
$selected = (new RoleLogic())->getUserResources($request->get('user_id'));
} else {
$selected = [];
}
foreach ($data as &$item) {
$item['selected'] = in_array($item['id'], $selected) ? true : false;
}
return $data;
}
}
+85
View File
@@ -0,0 +1,85 @@
<?php
namespace app\common\logic;
use support\Db;
class RoleLogic
{
/**
* 更新角色资源权限
*
* @param array $data 包含 role_id 和 resources 的数据
* @return bool 操作是否成功
*/
public function updateRoleResources(array $data): bool
{
$roleId = $data['role_id'] ?? 0;
$resources = $data['resources'] ?? [];
if (empty($roleId)) {
return false;
}
Db::beginTransaction();
try {
// 删除原有角色资源关联
Db::table('role_resources')->where('role_id', $roleId)->delete();
// 批量插入新角色资源关联
if (!empty($resources)) {
$insertData = [];
foreach ($resources as $resourceId) {
$insertData[] = [
'role_id' => $roleId,
'resource_id' => $resourceId
];
}
Db::table('role_resources')->insert($insertData);
}
Db::commit();
return true;
} catch (\Throwable $exception) {
Db::rollBack();
return false;
}
}
/**
* 获取角色资源
*
* @param int $roleId 角色ID
* @return array 资源ID列表
*/
public function getRoleResources(int $roleId): array
{
if (empty($roleId)) {
return [];
}
return Db::table('role_resources')
->where('role_id', $roleId)
->pluck('resource_id')
->toArray();
}
/**
* 获取用户资源
*
* @param int $userId 用户ID
* @return array 资源ID列表
*/
public function getUserResources(int $userId): array
{
if (empty($userId)) {
return [];
}
return Db::table('user_resources')
->where('user_id', $userId)
->pluck('resource_id')
->toArray();
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\SkuAttribute;
class SkuAttributeLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = SkuAttribute::class;
}
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\SkuAttributeValue;
class SkuAttributeValueLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = SkuAttributeValue::class;
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\Sku;
class SkuLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Sku::class;
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\Systems;
class SystemsLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Systems::class;
public function getList()
{
return Systems::where('status', 1)->orderBy('sort', 'asc')->get(['id', 'name', 'code', 'type', 'type']);
}
}
+169
View File
@@ -0,0 +1,169 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\UploadedFiles;
class UploadedFilesLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = UploadedFiles::class;
/**
* 根据分区获取文件列表
*
* @param string $partition
* @param int $limit
* @param int $page
* @return mixed
*/
public function getFilesByPartition(string $partition, int $limit = 15, int $page = 1)
{
$config = [
'model' => $this->modelClass,
'where' => ['partition' => $partition],
'order' => ['created_at' => 'desc', 'id' => 'desc']
];
return $this->logicList($config, $limit, $page);
}
/**
* 根据分类获取文件列表
*
* @param string $category
* @param int $limit
* @param int $page
* @return mixed
*/
public function getFilesByCategory(string $category, int $limit = 15, int $page = 1)
{
$config = [
'model' => $this->modelClass,
'where' => ['category' => $category],
'order' => ['created_at' => 'desc', 'id' => 'desc']
];
return $this->logicList($config, $limit, $page);
}
/**
* 根据文件类型获取文件列表
*
* @param string $fileType
* @param int $limit
* @param int $page
* @return mixed
*/
public function getFilesByType(string $fileType, int $limit = 15, int $page = 1)
{
$config = [
'model' => $this->modelClass,
'where' => ['file_type' => $fileType],
'order' => ['created_at' => 'desc', 'id' => 'desc']
];
return $this->logicList($config, $limit, $page);
}
/**
* 搜索文件
*
* @param string $keyword
* @param int $limit
* @param int $page
* @return mixed
*/
public function searchFiles(string $keyword, int $limit = 15, int $page = 1)
{
$config = [
'model' => $this->modelClass,
'where' => [
'or' => [
['file_name', 'like', '%' . $keyword . '%'],
['original_name', 'like', '%' . $keyword . '%'],
['category', 'like', '%' . $keyword . '%']
]
],
'order' => ['created_at' => 'desc', 'id' => 'desc']
];
return $this->logicList($config, $limit, $page);
}
/**
* 获取文件统计信息
*
* @return array
*/
public function getFileStatistics(): array
{
$model = new $this->modelClass();
$totalFiles = $model->count();
$totalSize = $model->sum('file_size');
$partitionStats = $model->selectRaw('partition, COUNT(*) as count, SUM(file_size) as size')
->groupBy('partition')
->get()
->toArray();
$categoryStats = $model->selectRaw('category, COUNT(*) as count, SUM(file_size) as size')
->groupBy('category')
->get()
->toArray();
$typeStats = $model->selectRaw('file_type, COUNT(*) as count, SUM(file_size) as size')
->groupBy('file_type')
->get()
->toArray();
return [
'total_files' => $totalFiles,
'total_size' => $totalSize,
'partition_stats' => $partitionStats,
'category_stats' => $categoryStats,
'type_stats' => $typeStats
];
}
/**
* 清理过期文件
*
* @param int $days 保留天数
* @return int 清理的文件数量
*/
public function cleanExpiredFiles(int $days = 30): int
{
$expiredTime = time() - ($days * 24 * 60 * 60);
$config = [
'model' => $this->modelClass,
'where' => [
['created_at', '<', $expiredTime]
]
];
$files = $this->logicListAll($config);
$count = 0;
foreach ($files as $file) {
// 删除物理文件
$fullPath = public_path() . $file->file_path;
if (file_exists($fullPath)) {
unlink($fullPath);
}
// 删除数据库记录
$this->logicDeleteBy(['where' => ['id' => $file->id]]);
$count++;
}
return $count;
}
}
+207
View File
@@ -0,0 +1,207 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\User;
use app\common\service\WechatService;
use support\exception\BusinessException;
class UserLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = User::class;
/**
* 构建 JWT 令牌有效载荷
*
* @param User $user
* @return array
*/
public function buildJwtPayload(User $user): array
{
$now = time();
return [
'sub' => 'user_' . $user->id,
'aud' => 'bsh.yw.hnzhwlkj.cn',
'iat' => $now,
'nbf' => $now,
'exp' => $now + config('jwt.jwt_expiration'),
'typ' => 'access',
'user_id' => $user->id,
'name' => $user->name,
'meta' => $user->toArray(),
'uid' => $user->id,
];
}
/**
* 用户信息
*/
public function userInfo(int $uid)
{
$user = User::find($uid);
if (!$user) {
throw new BusinessException('账户不存在', 404);
}
if ($user->status != 1) {
throw new BusinessException('账号已禁用', 403);
}
return $user;
}
/**
* 检测用户是否存在
*
* @param string $openid
* @param string $unionid
* @return User|bool
*/
public function checkUser(string $openid, string $unionid): User|bool
{
$uinfo = User::where('status', 1)
->where('openid', $openid)
->where('unionid', $unionid)
->first();
return $uinfo === null ? false : $uinfo;
}
/**
* 绑定用户
*
* @param array $data
* @return User|bool
*/
public function bindUser(array $data): User|bool
{
$uinfo = User::where('status', 1)
->where('name', $data['name'])
->where('mobile', $data['mobile'])
->where('sn', $data['sn'])
->first();
if ($uinfo !== null) {
$uinfo->update([
'openid' => $data['openid'],
'unionid' => $data['unionid'],
'nickname' => $data['nickname'],
'avatar' => $data['avatar'],
'password' => password_hash($data['password'], PASSWORD_DEFAULT)
]);
return $uinfo;
}
return false;
}
/**
* 创建用户
*
* @param array $data
* @return User
*/
public function createUser(array $data): User
{
return User::create($data);
}
/**
* 获取微信access_token
*
* @param string $code
* @param string $state
* @return array
*/
public function getAccessToken(string $code, string $state): array
{
$wechat = new WechatService();
return $wechat->wechatWebAccessToken($code, $state);
}
/**
* 获取微信用户信息
*
* @param array $token
* @return array
*/
public function getWechatUserInfo(array $token)
{
$wechat = new WechatService();
return $wechat->wechatWebUserInfo($token);
}
/**
* 获取用户列表
*/
public function getList()
{
$list = User::where('status', 1)->orderBy('id', 'desc')->get();
$arr = [];
foreach ($list->toArray() as $item) {
$arr[] = [
'name' => $item['name'],
'id' => $item['id'],
];
}
return $arr;
}
/**
* 修改密码
*/
public function savePassword(int $uid, array $data)
{
$password = password_hash($data['password_new'], PASSWORD_DEFAULT);
$res = User::where('id',$uid)->update(['password' => $password]);
return $res;
}
//---------------------------------------------小程序------------------------------------------------------------
/**
* 获取微信小程序access_token
*
* @param string $code
* @param string $state
* @return array
*/
public function getMiniAccessToken(): array
{
$wechat = new WechatService();
return $wechat->getMiniAccessToken();
}
/**
* 获取 openid / session_key
*
* @param string $code
* @param string $state
* @return array
*/
public function miniCode2Session($loginCode): array
{
$wechat = new WechatService();
return $wechat->miniCode2Session($loginCode);
}
/**
* 获取 openid / session_key
*
* @param string $code
* @param string $state
* @return array
*/
public function getMiniPhoneNumber($access_token, $phone_code): array
{
$wechat = new WechatService();
return $wechat->getMiniPhoneNumber($access_token, $phone_code);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class Agreement extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'agreement';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'title', 'type', 'content', 'sort', 'status', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class Banner extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'banner';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = [];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class BannerModule extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'banner_module';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = [];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class MessageNotice extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'message_notice';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'receiver_user_id', 'sender_user_id', 'post_id', 'comment_id', 'type', 'content', 'is_read', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class Post extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'post';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'user_id', 'category_id', 'content', 'location_name', 'latitude', 'longitude', 'status', 'sort', 'like', 'favorite', 'comment', 'browse', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class PostCategory extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'post_category';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'parent_id', 'icon_active', 'icon_inactive', 'icon_preset', 'name', 'level', 'path', 'sort', 'status', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class PostComment extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'post_comment';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'post_id', 'user_id', 'parent_id', 'root_id', 'content', 'status', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class PostFavorite extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'post_favorite';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'post_id', 'user_id', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class PostLike extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'post_like';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'post_id', 'user_id', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class PostMedia extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'post_media';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'post_id', 'media_type', 'url', 'sort', 'updated_at', 'created_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class Resource extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'resources';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'system_id', 'name', 'desc', 'type', 'code', 'parent_id', 'url', 'icon', 'open_type', 'is_hide', 'is_hide_tab', 'no_auth', 'is_home', 'instance', 'sort', 'status', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+66
View File
@@ -0,0 +1,66 @@
<?php
namespace app\common\model;
use support\Model;
/**
* roles 角色表
* @property integer $id 角色ID(主键)
* @property string $name 角色名称
* @property string $desc 角色描述
* @property string $created_at
* @property string $updated_at
*/
class Role extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'roles';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
protected $fillable = [
'system_id',
'name',
'desc',
'sort',
'status'
];
// 角色下所有用户
public function users()
{
return $this->belongsToMany(User::class, 'user_roles', 'role_id', 'user_id');
}
// 角色拥有的资源(权限点、菜单)
public function resources()
{
return $this->belongsToMany(Resource::class, 'role_resources', 'role_id', 'resource_id');
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class Sku extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sku';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = [];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class SkuAttribute extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sku_attribute_definition';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = [];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class SkuAttributeValue extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'sku_attribute_value';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = [];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class Systems extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'systems';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'code', 'name', 'type', 'desc', 'icon', 'domain_url', 'is_role', 'sort', 'status', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace app\common\model;
use support\Model;
class UploadedFiles extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'uploaded_files';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = [
'id',
'file_name',
'file_path',
'file_size',
'file_type',
'category',
'original_name',
'extension',
'partition',
'upload_ip',
'user_agent',
'created_at',
'updated_at'
];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+75
View File
@@ -0,0 +1,75 @@
<?php
namespace app\common\model;
use support\Model;
class User extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'user';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'password', 'name', 'email', 'mobile', 'avatar', 'sn', 'status', 'is_release', 'nickname', 'openid', 'unionid', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
// 用户所有角色
public function roles()
{
return $this->belongsToMany(Role::class, 'user_roles', 'user_id', 'role_id');
}
// 用户直接拥有的资源(用于特殊补权)
public function resources()
{
return $this->belongsToMany(Resource::class, 'user_resources', 'user_id', 'resource_id')->orderBy('sort', 'asc');
}
// 用户可访问的系统
public function systems()
{
return $this->belongsToMany(Systems::class, 'user_systems', 'user_id', 'system_id');
}
// 账号查询的作用域
public function scopeByAccount($query, $account)
{
return $query->where('sn', $account)
->orWhere('mobile', $account)
->orWhere('email', $account);
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
namespace app\common\service;
use app\common\validate\AgreementValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Agreement;
use support\Request;
class AgreementService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Agreement::class;
/**
* 验证器
*/
protected $validator = AgreementValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['where']['status'] = 1;
$config['order']['sort'] = 'desc';
$config['order']['id'] = 'desc';
break;
case 'index':
$config['where']['status'] = 1;
$config['order']['sort'] = 'desc';
$config['order']['id'] = 'desc';
break;
case 'show':
if (isset($p['type']) && !empty($p['type'])) {
$config['where']['type'] = $p['type'];
}
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
namespace app\common\service;
use app\common\logic\AuthLogic;
use app\common\model\User;
use app\common\utils\Feishu;
use support\Db;
class AuthService
{
/**
* 飞书授权
*/
public function feishuAuthorize(string $corpAbbr, string $systemCcode)
{
$logic = new AuthLogic();
$certificate = $logic->feishuAppInfo($corpAbbr, $systemCcode);
$feishu = new Feishu($certificate['app_id'], $certificate['app_secret'], $certificate['abbr']);
return ['auth_url' => $feishu->authorize($certificate)];
}
/**
* 登录,支持多类型
*/
public function login(array $data): array
{
$loginType = $data['login_type'] ?? 'upwd';
$logic = new AuthLogic();
return match ($loginType) {
default => $logic->uPwdLogin($data),
};
}
/**
* 用户注册
*/
public function register(array $data)
{
$logic = new AuthLogic();
try {
Db::beginTransaction();
$result = $logic->register($data);
Db::commit();
return $result;
} catch (\Throwable $e) {
Db::rollback();
throw $e;
}
}
/**
* 校验token
*/
public function verifyToken(string $token, string $systemCode): array
{
return (new AuthLogic())->verifyToken($token, $systemCode);
}
/**
* 刷新token
* @param string $refreshToken
* @param string|null $systemCode
*/
public function refreshToken(string $refreshToken, string $systemCode): array
{
return (new AuthLogic())->refreshToken($refreshToken, $systemCode);
}
/**
* 登出(失效token)
*/
public function logout(?string $token, string $systemCode): void
{
(new AuthLogic())->logout($token, $systemCode);
}
/**
* 强制拉黑token
*/
public function blockToken(string $token, string $systemCode): void
{
(new AuthLogic())->blockToken($token, $systemCode);
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace app\common\service;
use app\common\model\BannerModule;
use app\common\validate\BannerValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Banner;
class BannerService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Banner::class;
/**
* 验证器
*/
protected $validator = BannerValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
public function getBanner($params){
$where = [];
if(isset($params['module'])){
$where['module'] = $params['module'];
}
return Banner::where('status','1')->where($where)->get();
}
public function getBannerModule(){
return BannerModule::where('status','1')->get();
}
}
+119
View File
@@ -0,0 +1,119 @@
<?php
namespace app\common\service;
use app\common\model\Corp;
use app\common\model\Systems;
use support\Redis;
use support\exception\BusinessException;
class BasicDataService
{
// 定义Redis键名前缀常量
const LOGIN_TYPE_KEY = 'bsh_info_login_types';
const SYSTEM_CODE_KEY = 'bsh_info_system_codes';
/**
* 检查并缓存独立的基础参数
*/
public function checkAndCacheBaseParams($loginType, $systemCode)
{
if (!$loginType && !$systemCode) {
throw new BusinessException('header缺少必要参数');
}
// 检查并缓存登录类型
if (!$this->checkParamInCache(self::LOGIN_TYPE_KEY, $loginType)) {
$this->cacheLoginTypes();
if (!$this->checkParamInCache(self::LOGIN_TYPE_KEY, $loginType)) {
throw new BusinessException('无效的登录方式');
}
}
// 检查并缓存系统码
if (!$this->checkParamInCache(self::SYSTEM_CODE_KEY, $systemCode)) {
$this->cacheSystemCodes();
if (!$this->checkParamInCache(self::SYSTEM_CODE_KEY, $systemCode)) {
throw new BusinessException('无效的系统码');
}
}
return true;
}
/**
* 检查参数是否在Redis集合中
*/
private function checkParamInCache($cacheKey, $param)
{
return Redis::sIsMember($cacheKey, $param);
}
/**
* 缓存所有登录类型到集合
*/
private function cacheLoginTypes()
{
$cacheKey = self::LOGIN_TYPE_KEY;
// 如果集合已存在,直接返回
// if (Redis::exists($cacheKey)) {
// return;
// }
$loginTypes = $this->getAllLoginTypes();
if (empty($loginTypes)) {
throw new BusinessException('未获取到登录类型数据');
}
// 批量添加值到集合
Redis::sAdd($cacheKey, ...$loginTypes);
Redis::expire($cacheKey, 3600); // 1小时有效期
}
/**
* 缓存所有系统码到集合
*/
private function cacheSystemCodes()
{
$cacheKey = self::SYSTEM_CODE_KEY;
// 如果集合已存在,直接返回
// if (Redis::exists($cacheKey)) {
// return;
// }
$systemCodes = $this->getAllSystemCodes();
if (empty($systemCodes)) {
throw new BusinessException('未获取到系统码数据');
}
// 批量添加值到集合
Redis::sAdd($cacheKey, ...$systemCodes);
Redis::expire($cacheKey, 3600); // 1小时有效期
}
/**
* 获取所有有效的登录类型
*/
private function getAllLoginTypes()
{
return ['upwd', 'feishu', 'wechat', 'qrcode']; // 示例数据
}
/**
* 获取所有有效的系统码
*/
private function getAllSystemCodes()
{
return Systems::pluck('code')->toArray();
}
/**
* 获取所有有效的公司标识
*/
private function getAllCorpAbbrs()
{
return Corp::pluck('abbr')->toArray();
}
}
@@ -0,0 +1,92 @@
<?php
namespace app\common\service;
use app\common\validate\MessageNoticeValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\MessageNotice;
use support\Request;
class MessageNoticeService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = MessageNotice::class;
/**
* 验证器
*/
protected $validator = MessageNoticeValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/** 分页列表 */
public function serviceSelfList(Request $request, string $scene = 'self')
{
$result = $this->cqyProcess(ucfirst($scene), $request);
if ($result === false) {
$config = $this->buildQueryConfig($request, $scene);
$limit = (int)$request->get('size', 15);
$page = (int)$request->get('current', 1);
$result = $this->logicList($config, $limit, $page);
}
return $this->postProcess(ucfirst($scene), $result);
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['order']['id'] = 'desc';
break;
case 'self':
$config['where']['receiver_user_id'] = $request->user->uid;
$config['order']['id'] = 'desc';
break;
case 'index':
$config['order']['id'] = 'desc';
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
/**
* 添加后置处理
*/
public function beforeServiceSelf($data, Request $request)
{
MessageNotice::where('receiver_user_id', $request->user->uid)->update(['is_read' => 1]);
return $data;
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
namespace app\common\service;
use OSS\OssClient;
use OSS\Core\OssException;
use support\exception\BusinessException;
class OssService
{
private $accessKeyId;
private $accessKeySecret;
private $bucket;
private $endpoint;
private $domain;
private $ossClient;
public function __construct()
{
$this->accessKeyId = getenv('OSS_ACCESSID');
$this->accessKeySecret = getenv('OSS_ACCESSSECRET');
$this->bucket = getenv('OSS_BUCKET');
$this->endpoint = getenv('OSS_ENDPOINT');
$this->domain = getenv('OSS_DOMAIN');
$this->ossClient = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);
}
/**
* 上传图片到 OSS
* @param string $filePath 本地文件路径
* @param string $objectKey OSS上存储的对象名称
* @return string 图片的 URL
*/
public function uploadImage($filePath, $objectKey)
{
try {
// 上传文件到 OSS
$this->ossClient->uploadFile($this->bucket, $objectKey, $filePath);
// 构造图片的私有 URL
$url = $this->generatePrivateUrl($objectKey);
return $url;
} catch (OssException $e) {
// 错误处理
throw new \RuntimeException('OSS Error: ' . $e->getMessage());
} catch (\Throwable $th) {
// 错误处理
throw new BusinessException('Error: ' . $th->getMessage());
}
}
/**
* 生成图片的私有访问 URL
* @param string $objectKey
* @return string
*/
public function generatePrivateUrl($objectKey, $expireTime = 3600)
{
// 生成 OSS 私有 URL(有效期为 3600 秒)
$originalUrl = $this->ossClient->signUrl($this->bucket, $objectKey, $expireTime);
// // 使用正则表达式替换
$originalUrl = preg_replace(
'#^https?://[^/]+#',
'https://static.hnzhwlkj.cn',
$originalUrl
);
return $originalUrl;
}
/**
* 删除 OSS 上的文件
* @param string $objectKey OSS上存储的对象名称
* @return bool 删除是否成功
*/
public function deleteFile($objectKey)
{
try {
// 删除 OSS 上的文件
$this->ossClient->deleteObject($this->bucket, $objectKey);
return true;
} catch (OssException $e) {
// 错误处理
throw new \RuntimeException('OSS Delete Error: ' . $e->getMessage());
} catch (\Throwable $th) {
// 错误处理
throw new BusinessException('Delete Error: ' . $th->getMessage());
}
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace app\common\service;
use app\common\logic\PermLogic;
use support\Cache;
class PermService
{
protected $logic;
public function __construct()
{
$this->logic = (new PermLogic());
}
// 用户资源缓存key
protected static function cacheKey(int $userId, string $systemCode): string
{
return "user_resources_{$userId}_{$systemCode}";
}
/**
* 获取用户所有资源(合并角色+直接分配),结果缓存
*/
public function getUserResources(int $userId, string $systemCode)
{
$cacheKey = self::cacheKey($userId, $systemCode);
$cached = Cache::get($cacheKey);
if ($cached !== null) {
return $cached;
}
// 调用 Logic 层获取资源
$resources = $this->logic->getUserResources($userId, $systemCode);
// 缓存结果
Cache::set($cacheKey, $resources, 3600);
return $resources;
}
/**
* 构建目录/菜单树,节点包含权限与打开方式
*/
public function getMenuTree(int $userId, string $systemCode): array
{
// 调用 Logic 层处理菜单构建
$resources = $this->getUserResources($userId, $systemCode);
return $this->logic->buildTree($resources);
}
/**
* 获取用户所有权限code
*/
public function getPermissionCodes(int $userId, string $systemCode): array
{
return $this->logic->getPermissionCodes($userId, $systemCode);
}
/**
* 清除资源相关缓存
*/
public function clearCache(int $userId, string $systemCode): void
{
Cache::delete(self::cacheKey($userId, $systemCode));
}
}
@@ -0,0 +1,89 @@
<?php
namespace app\common\service;
use app\common\logic\PostCategoryLogic;
use app\common\model\NavItems;
use app\common\validate\PostCategoryValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\PostCategory;
use support\Request;
class PostCategoryService
{
use CrudServiceTrait;
protected $logic;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostCategory::class;
/**
* 验证器
*/
protected $validator = PostCategoryValidate::class;
public function __construct()
{
$this->logic = (new PostCategoryLogic());
}
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 获取系统list
*/
public function getList($params)
{
$where = [];
if(isset($params['parent_id']) && $params['parent_id']) {
$where['parent_id'] = $params['parent_id'];
}else{
$where['parent_id'] = 0;
}
$result = $this->logic->getList($where)->toArray();
foreach ($result as $key => $value) {
$where = [];
$where['parent_id'] = $value['id'];
$result[$key]['children'] = $this->logic->getList($where)->toArray();
foreach ( $result[$key]['children'] as $k => $v) {
$where = [];
$where['parent_id'] = $v['id'];
$result[$key]['children'][$k]['children'] = $this->logic->getList($where)->toArray();
}
}
return $result;
}
/**
* 添加前置处理
*/
public function beforeServiceStore($data, Request $request)
{
$brand_id = $data['parent_id'] ?? 0;
if($brand_id == 0) {
$data['level'] = 1;
$data['path'] = '';
return $data;
}
$parent = PostCategory::where('id', $data['parent_id'])->first();
$parent['path'] = !empty($parent['path'])?:'/';
$data['level'] = $parent['level']+1;
$data['path'] = $parent['path'].$parent['id'].'/';
if($data['level'] > 3) {
throw new \Exception("分类层级不能超过三层");
}
return $data;
}
}
+157
View File
@@ -0,0 +1,157 @@
<?php
namespace app\common\service;
use app\common\model\MessageNotice;
use app\common\model\Post;
use app\common\validate\PostCommentValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\PostComment;
use support\Redis;
use support\Request;
class PostCommentService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostComment::class;
/**
* 验证器
*/
protected $validator = PostCommentValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['where']['status'] = 1;
$config['order']['id'] = 'desc';
break;
case 'index':
$config['where']['status'] = 1;
$config['order']['id'] = 'desc';
// 帖子
if (isset($p['post_id']) && !empty($p['post_id'])) {
$config['where']['post_id'] = $p['post_id'];
}
// 用户
if (isset($p['user_id']) && !empty($p['user_id'])) {
$config['where']['user_id'] = $p['user_id'];
}
// 只查询根评论
if (isset($p['parent_id']) && $p['parent_id'] == 0) {
$config['where']['parent_id'] = 0;
}
// 根评论ID
if (isset($p['parent_id']) && !empty($p['parent_id'])) {
$config['where']['root_id'] = $p['parent_id'];
}
// 根评论ID
if (isset($p['received_id']) && !empty($p['received_id'])) {
$config['where']['id'] = ['not in', $p['received_id']];
}
// 根评论ID
if (isset($p['status']) && !empty($p['status'])) {
$config['where']['status'] = $p['status'];
}
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
/**
* 添加前置处理
*/
public function beforeServiceStore($data, Request $request)
{
$data['user_id'] = $request->user->uid;;
$data['root_id'] = 0;
if(!empty($data['parent_id'])){
$data['root_id'] = PostComment::where('id', $data['parent_id'])->value('root_id');
if($data['root_id'] == 0){
$data['root_id'] = $data['parent_id'];
}
}
return $data;
}
/**
* 添加后置处理
*/
public function afterServiceStore($res)
{
$data = [];
$data['post_id'] = $res['post_id'];
$data['receiver_user_id'] = Post::where(['id' => $data['post_id']])->value('user_id');
$data['sender_user_id'] = $res['user_id'];
$data['comment_id'] = $res['id'];
$data['type'] = 3;
MessageNotice::create($data);
$post = Post::where(['id' => $data['post_id']])->first();
$post['comment'] = $post['comment']+1;
$post->save();
}
/**
* 添加后置处理
*/
public function serviceDelete(Request $request)
{
$param = $request->all();
if (empty($param['id'])) {
throw new \Exception("参数错误");
}
$where = [];
$where['id'] = $param['id'];
$comment = PostComment::where($where)->first();
$post = Post::where(['id' => $comment['post_id']])->first();
$post['comment'] = $post['comment']-1;
$post->save();
$count = $comment->delete();
return $count;
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
namespace app\common\service;
use app\common\model\MessageNotice;
use app\common\model\Post;
use app\common\model\PostLike;
use app\common\validate\PostFavoriteValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\PostFavorite;
use support\Request;
class PostFavoriteService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostFavorite::class;
/**
* 验证器
*/
protected $validator = PostFavoriteValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/** 分页列表 */
public function serviceSelfList(Request $request, string $scene = 'self')
{
$result = $this->cqyProcess(ucfirst($scene), $request);
if ($result === false) {
$config = $this->buildQueryConfig($request, $scene);
$limit = (int)$request->get('size', 15);
$page = (int)$request->get('current', 1);
$result = $this->logicList($config, $limit, $page);
}
return $this->postProcess(ucfirst($scene), $result);
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['order']['id'] = 'desc';
break;
case 'self':
$config['where']['user_id'] = $request->user->uid;
$config['order']['id'] = 'desc';
break;
case 'index':
$config['order']['id'] = 'desc';
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
/**
* 添加前置处理
*/
public function beforeServiceStore($data, Request $request)
{
$data['user_id'] = $request->user->uid;;
return $data;
}
/**
* 添加后置处理
*/
public function afterServiceStore($res)
{
$data = [];
$data['post_id'] = $res['post_id'];
$data['receiver_user_id'] = Post::where(['id' => $data['post_id']])->value('user_id');
$data['sender_user_id'] = $res['user_id'];
$data['type'] = 2;
MessageNotice::create($data);
$post = Post::where(['id' => $data['post_id']])->first();
$post['favorite'] = $post['favorite']+1;
$post->save();
}
/**
* 添加后置处理
*/
public function serviceDelete(Request $request)
{
$param = $request->all();
if (empty($param['post_id']) && empty($param['user_id'])) {
throw new \Exception("参数错误");
}
$where = [];
$where['post_id'] = $param['post_id'];
$where['user_id'] = $request->user->uid;
$count = PostFavorite::where($where)->delete();
if ($count) {
$post = Post::where(['id' => $param['post_id']])->first();
$post['favorite'] = $post['favorite']-1;
$post['favorite'] = $post['favorite']<0?0:$post['favorite'];
$post->save();
}
return $count;
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
namespace app\common\service;
use app\common\model\MessageNotice;
use app\common\model\Post;
use app\common\model\PostMedia;
use app\common\validate\PostLikeValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\PostLike;
use support\Redis;
use support\Request;
class PostLikeService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostLike::class;
/**
* 验证器
*/
protected $validator = PostLikeValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/** 分页列表 */
public function serviceSelfList(Request $request, string $scene = 'self')
{
$result = $this->cqyProcess(ucfirst($scene), $request);
if ($result === false) {
$config = $this->buildQueryConfig($request, $scene);
$limit = (int)$request->get('size', 15);
$page = (int)$request->get('current', 1);
$result = $this->logicList($config, $limit, $page);
}
return $this->postProcess(ucfirst($scene), $result);
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['order']['id'] = 'desc';
break;
case 'self':
$config['where']['user_id'] = $request->user->uid;
$config['order']['id'] = 'desc';
break;
case 'index':
$config['order']['id'] = 'desc';
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
/**
* 添加前置处理
*/
public function beforeServiceStore($data, Request $request)
{
$data['user_id'] = $request->user->uid;;
return $data;
}
/**
* 添加后置处理
*/
public function afterServiceStore($res)
{
$data = [];
$data['post_id'] = $res['post_id'];
$data['receiver_user_id'] = Post::where(['id' => $data['post_id']])->value('user_id');
$data['sender_user_id'] = $res['user_id'];
$data['type'] = 1;
MessageNotice::create($data);
$post = Post::where(['id' => $data['post_id']])->first();
$post['like'] = $post['like']+1;
$post->save();
}
/**
* 添加后置处理
*/
public function serviceDelete(Request $request)
{
$param = $request->all();
if (empty($param['post_id']) && empty($param['user_id'])) {
throw new \Exception("参数错误");
}
$where = [];
$where['post_id'] = $param['post_id'];
$where['user_id'] = $request->user->uid;
$count = PostLike::where($where)->delete();
if ($count) {
$post = Post::where(['id' => $param['post_id']])->first();
$post['like'] = $post['like']-1;
$post['like'] = $post['like']<0?0:$post['like'];
$post->save();
}
return $count;
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace app\common\service;
use app\common\validate\PostMediaValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\PostMedia;
class PostMediaService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = PostMedia::class;
/**
* 验证器
*/
protected $validator = PostMediaValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
}
+200
View File
@@ -0,0 +1,200 @@
<?php
namespace app\common\service;
use app\common\model\PostMedia;
use app\common\model\User;
use app\common\validate\PostValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Post;
use support\exception\BusinessException;
use support\Redis;
use support\Request;
class PostService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Post::class;
/**
* 验证器
*/
protected $validator = PostValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['where']['status'] = 1;
$config['order']['sort'] = 'desc';
$config['order']['id'] = 'desc';
break;
case 'index':
if (isset($p['category_id']) && $p['category_id']) {
$config['where']['category_id'] = $p['category_id'];
}
if (isset($p['scene']) && $p['scene']=='self') {
if(!empty($request->user) && !empty($request->user->uid)){
$config['where']['user_id'] = $request->user->uid;
}else{
$config['where']['user_id'] = -1;
}
}
if(isset($p['year']) && $p['year']){
$from = strtotime($p['year'].'-01-01');
$to = strtotime($p['year'].'-12-31 23:59:59');
$config['where']['created_at'] = [
'between', [$from, $to]
];
}
if (isset($p['keyword']) && $p['keyword']) {
$config['where']['content'] = ['like', '%'.$p['keyword'].'%'];
}
$config['where']['status'] = 1;
$config['order']['sort'] = 'desc';
if (isset($p['favorite']) && $p['favorite']) {
$config['order']['favorite'] = 'desc';
} elseif (isset($p['browse']) && $p['browse']) {
$config['order']['browse'] = 'desc';
} else {
$config['order']['id'] = 'desc';
}
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
/**
* 添加前置处理
*/
public function beforeServiceStore($data, Request $request)
{
$data['user_id'] = $request->user->uid;;
// $user = User::where('id', $data['user_id'])->first();
// if($user->is_release == 0){
// throw new BusinessException('无发布权限');
// }
$media = serialize($data['media']);
redis::set('PostMedia_'.$data['user_id'],$media,30);
unset($data['media']);
return $data;
}
/**
* 添加后置处理
*/
public function afterServiceStore($res)
{
$media = redis::get('PostMedia_'.$res['user_id']);
$media = unserialize($media);
if(!is_array($media)){
$media = explode(',',$media);
}
foreach ($media as $v) {
PostMedia::create([
'post_id' => $res->id,
'media_type' => $this->getMediaTypeByUrl($v),
'url' => $v,
]);
}
}
/**
* 添加前置处理
*/
public function beforeServiceUpdate($data, Request $request)
{
$data['user_id'] = $request->user->uid;;
$media = serialize($data['media']);
redis::set('PostMedia_'.$data['user_id'],$media,30);
unset($data['media']);
return $data;
}
/**
* 添加后置处理
*/
public function afterServiceUpdate($res)
{
PostMedia::where('post_id', $res->id)->delete();
$media = redis::get('PostMedia_'.$res['user_id']);
$media = unserialize($media);
foreach ($media as $v) {
PostMedia::create([
'post_id' => $res->id,
'media_type' => $this->getMediaTypeByUrl($v['url']),
'url' => $v['url'],
]);
}
}
/**
* 添加前置处理
*/
public function beforeServiceShow($data, Request $request)
{
$post = Post::find($data['id']);
if(!$post){
$post->browse = $post->browse+1;
$post->save();
}
return $data;
}
function getMediaTypeByUrl(string $url): int
{
// 提取后缀
$ext = strtolower(pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION));
// 图片类型
$imageExt = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'];
// 视频类型
$videoExt = ['mp4', 'mov', 'avi', 'wmv', 'flv', 'mkv'];
if (in_array($ext, $imageExt)) {
return 1; // 图片
}
if (in_array($ext, $videoExt)) {
return 2; // 视频
}
return 0; // 未知类型
}
}
+130
View File
@@ -0,0 +1,130 @@
<?php
namespace app\common\service;
use app\common\logic\ResourceLogic;
use app\common\validate\ResourceValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Resource;
use app\common\utils\Tree;
use support\Request;
class ResourceService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Resource::class;
/**
* 验证器
*/
protected $validator = ResourceValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['where']['system_id'] = ['in', [0, $p['system_id']]];
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
break;
case 'menuList':
$config['where']['status'] = 1;
$config['where']['type'] = ['!=', 'permission'];
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
break;
case 'menuTreeList':
$config['where']['system_id'] = ['in', [0, $p['system_id']]];
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
break;
case 'index':
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
public function serviceMenuList(Request $request)
{
$config = $this->buildQueryConfig($request, 'menuList');
$result = $this->logicListAll($config);
return $this->postProcess('MenuList', $result);
}
public function serviceMenuTreeList(Request $request)
{
$config = $this->buildQueryConfig($request, 'menuTreeList');
$result = (new ResourceLogic())->logicListAllResources($request, $config);
return $this->postProcess('MenuTreeList', $result);
}
protected function afterServiceDefault($data)
{
$data = Tree::buildMenuTreeWithReference($data->toArray());
return $data;
}
protected function afterServiceMenuList($data)
{
$data = Tree::buildMenuTree($data->toArray());
return $data;
}
protected function afterServiceMenuTreeList($data)
{
$data = Tree::buildMenuTree($data->toArray());
return $data;
}
public function beforeServiceStore($data, Request $request)
{
$data['parent_id'] = $data['parent_id'] != '' ? $data['parent_id'] : 0;
$data['system_id'] = $data['system_id'] != '' ? $data['system_id'] : 0;
return $data;
}
public function beforeServiceUpdate($data, Request $request)
{
$data['parent_id'] = $data['parent_id'] != '' ? $data['parent_id'] : 0;
$data['system_id'] = $data['system_id'] != '' ? $data['system_id'] : 0;
return $data;
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace app\common\service;
use app\common\logic\RoleLogic;
use app\common\validate\RoleValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Role;
use support\Db;
class RoleService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Role::class;
/**
* 验证器
*/
protected $validator = RoleValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 获取角色资源
*/
public function menuResource($request, $data)
{
return (new RoleLogic)->updateRoleResources($data);
}
public function getList()
{
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
return $this->logicListAll($config);
}
}
@@ -0,0 +1,31 @@
<?php
namespace app\common\service;
use app\common\validate\SkuAttributeValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\SkuAttribute;
class SkuAttributeService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = SkuAttribute::class;
/**
* 验证器
*/
protected $validator = SkuAttributeValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
}
@@ -0,0 +1,31 @@
<?php
namespace app\common\service;
use app\common\validate\SkuAttributeValueValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\SkuAttributeValue;
class SkuAttributeValueService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = SkuAttributeValue::class;
/**
* 验证器
*/
protected $validator = SkuAttributeValueValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace app\common\service;
use app\common\validate\SkuValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Sku;
class SkuService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Sku::class;
/**
* 验证器
*/
protected $validator = SkuValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace app\common\service;
use app\common\logic\SystemsLogic;
class SystemsService
{
protected $logic;
public function __construct()
{
$this->logic = (new SystemsLogic());
}
/**
* 获取系统list
*/
public function getList()
{
return $this->logic->getList();
}
}
+177
View File
@@ -0,0 +1,177 @@
<?php
namespace app\common\service;
use support\exception\BusinessException;
use support\Log;
use support\Request;
class UploadService
{
/**
* 从服务器本地路径上传文件到 OSS
*
* @param Request $request
* - path: 本地文件绝对路径
* - scene: 业务分区/场景
* - category: 文件分类(可选)
* - original_name: 原始文件名(可选)
*/
public function uploadLocalFile($param)
{
try {
$filePath = $param['path'];
$scene = $param['scene'];
$category = $param['category']??'general';
$providedOriginalName = $param['original_name']??'';
if (empty($filePath)) {
Log::error('本地文件路径不能为空',$param);
throw new BusinessException('本地文件路径不能为空');
}
if (!file_exists($filePath) || !is_readable($filePath)) {
Log::error('本地文件不存在或不可读',$param);
throw new BusinessException('本地文件不存在或不可读');
}
if (empty($scene)) {
Log::error('上传场景不能为空',$param);
throw new BusinessException('上传场景不能为空');
}
$basename = basename($filePath);
$originalName = $providedOriginalName ?: $basename;
$extensionFromName = pathinfo($originalName, PATHINFO_EXTENSION);
$extensionFromPath = pathinfo($basename, PATHINFO_EXTENSION);
$extension = $extensionFromName ?: $extensionFromPath;
$mime = mime_content_type($filePath) ?: 'application/octet-stream';
$size = filesize($filePath) ?: 0;
$sceneUrl = $scene ? ($scene . '/') : '';
$dir = 'bsh_sample/' . $sceneUrl . date('Ymd') . '/';
// 使用传入的 original_name 命名(缺少扩展名时补上推断的扩展名),并做简单安全处理
$safeBaseName = preg_replace('/[\\\\\/]+/', '_', $originalName);
if ($extension && strtolower(pathinfo($safeBaseName, PATHINFO_EXTENSION)) !== strtolower($extension)) {
// 若提供的名称没有扩展名或扩展名不一致,则以推断扩展名为准
$safeBaseName = pathinfo($safeBaseName, PATHINFO_FILENAME) . '.' . $extension;
} elseif (!$extension && !pathinfo($safeBaseName, PATHINFO_EXTENSION)) {
// 实在没有扩展名信息时,保持原名
}
// 重名检测:同一目录、同名且同类型则在文件名后追加序号
$uploadedFilesService = new UploadedFilesService();
$candidateName = $safeBaseName;
$nameBody = pathinfo($safeBaseName, PATHINFO_FILENAME);
$nameExt = pathinfo($safeBaseName, PATHINFO_EXTENSION);
$counter = 1;
while (true) {
$candidatePath = $dir . $candidateName;
$existing = $uploadedFilesService->findFile(['file_path' => $candidatePath]);
if (!$existing) {
break;
}
// 若存在同路径同名,且类型也相同,则追加序号
if (isset($existing->file_type) && strtolower($existing->file_type) === strtolower($mime)) {
$suffix = ' (' . $counter . ')';
$candidateName = $nameBody . $suffix . ($nameExt !== '' ? ('.' . $nameExt) : '');
$counter++;
continue;
}
// 类型不同则允许同名,结束循环
break;
}
$objectKey = $dir . $candidateName;
$ossService = new OssService();
$ossService->uploadImage($filePath, $objectKey);
$privateUrl = $ossService->generatePrivateUrl($objectKey);
// 记录文件信息到数据库
$uploadedFilesService = new UploadedFilesService();
$fileInfo = [
'file_name' => basename($objectKey),
'file_path' => $objectKey,
'file_size' => $size,
'file_type' => $mime,
'original_name' => $originalName,
];
$recordResult = $uploadedFilesService->recordFileInfo($fileInfo, null, $scene, $category);
$data = [
'original_name' => $originalName,
'file_name' => $objectKey,
'file_url' => $privateUrl,
'file_id' => $recordResult['id'] ?? null,
'partition' => $scene,
'category' => $category,
];
return $data;
} catch (\Throwable $e) {
Log::error($e->getMessage(),$param);
throw new BusinessException($e->getMessage());
}
}
/**
* 删除 OSS 上的文件
*
* @param array $param
* - object_key: OSS 对象键名-oss路径(必需)
* - file_id: 数据库文件记录ID(可选,用于删除数据库记录)
* @return bool 删除是否成功
*
* 示例
* $uploadService = new UploadService();
*
* 只删除 OSS 文件
* $uploadService->deleteOssFile([
* 'object_key' => 'vehicle_album/documents/20241201/abc123.pdf'
* ]);
*
* 同时删除 OSS 文件和数据库记录
* $uploadService->deleteOssFile([
* 'object_key' => 'vehicle_album/images/20241201/abc123.jpg',
* 'file_id' => 123
* ]);
*/
public function deleteOssFile($objectKey,$fileId = null)
{
try {
if (empty($objectKey)) {
Log::error('OSS对象键名不能为空', $objectKey);
throw new BusinessException('OSS对象键名不能为空');
}
$uploadedFilesService = new UploadedFilesService();
// 如果提供了文件ID,同时删除数据库记录
if ($fileId) {
$uploadedFilesService->deleteFile($fileId);
}else{
$uploadedFilesService = new UploadedFilesService();
if (empty($fileId)) {
$where = ['file_path' => $objectKey];
$file = $uploadedFilesService->getFile($where);
$fileId = $file->id;
}
if(!empty($fileId)){
$uploadedFilesService->deleteFile($fileId);
}
}
// 删除 OSS 上的文件
$ossService = new OssService();
$ossService->deleteFile($objectKey);
Log::info('OSS文件删除成功', ['object_key' => $objectKey, 'file_id' => $fileId]);
return true;
} catch (\Throwable $e) {
Log::error('删除OSS文件失败: ' . $e->getMessage());
throw new BusinessException('删除OSS文件失败: ' . $e->getMessage());
}
}
}
+238
View File
@@ -0,0 +1,238 @@
<?php
namespace app\common\service;
use app\common\validate\UploadedFilesValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\UploadedFiles;
use support\Request;
use support\exception\BusinessException;
class UploadedFilesService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = UploadedFiles::class;
/**
* 文件上传分区配置
*/
protected array $uploadPartitions = [
'image' => [
'path' => 'images',
'max_size' => 5 * 1024 * 1024, // 5MB
'allowed_types' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp']
],
'document' => [
'path' => 'documents',
'max_size' => 10 * 1024 * 1024, // 10MB
'allowed_types' => ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
'extensions' => ['pdf', 'doc', 'docx']
],
'audio' => [
'path' => 'audios',
'max_size' => 20 * 1024 * 1024, // 20MB
'allowed_types' => ['audio/mp3', 'audio/wav', 'audio/ogg'],
'extensions' => ['mp3', 'wav', 'ogg']
],
'other' => [
'path' => 'others',
'max_size' => 5 * 1024 * 1024, // 5MB
'allowed_types' => [],
'extensions' => []
]
];
/**
* 记录文件信息到数据库
*
* @param array $fileInfo 文件信息
* @param Request|null $request 请求对象(可选)
* @param string $partition 分区名称
* @param string $category 文件分类
* @return array
* @throws BusinessException
*/
public function recordFileInfo(array $fileInfo, ?Request $request = null, string $partition = 'other', string $category = 'general'): array
{
// 验证必需的文件信息
$requiredFields = ['file_name', 'file_path', 'file_size', 'file_type'];
foreach ($requiredFields as $field) {
if (!isset($fileInfo[$field])) {
throw new BusinessException("缺少必需的文件信息字段: {$field}");
}
}
// 获取文件扩展名
$fileExtension = pathinfo($fileInfo['file_name'], PATHINFO_EXTENSION);
// 记录到数据库
$uploadIp = $request ? $request->getRealIp() : '0.0.0.0';
$userAgent = $request ? $request->header('User-Agent', '') : '';
$fileData = [
'file_name' => $fileInfo['file_name'],
'file_path' => $fileInfo['file_path'],
'file_size' => $fileInfo['file_size'],
'file_type' => $fileInfo['file_type'],
'category' => $category,
'original_name' => $fileInfo['original_name'] ?? '',
'extension' => $fileExtension,
'partition' => $partition,
'upload_ip' => $uploadIp,
'user_agent' => $userAgent,
];
$uploadedFile = $this->logicCreate($fileData);
return [
'id' => $uploadedFile->id,
'file_name' => $fileInfo['file_name'],
'file_path' => $fileInfo['file_path'],
'file_url' => getenv('MAIN_DOMAIN') . $fileInfo['file_path'],
'file_size' => $fileInfo['file_size'],
'file_type' => $fileInfo['file_type'],
'category' => $category,
'partition' => $partition,
'original_name' => $fileInfo['original_name'] ?? '',
'extension' => $fileExtension,
];
}
/**
* 获取文件信息
*
* @param int $fileId
* @return array|null
*/
public function getFileInfo(int $fileId): ?array
{
$file = $this->logicShow(['where' => ['id' => $fileId]]);
if (!$file) {
return null;
}
return [
'id' => $file->id,
'file_name' => $file->file_name,
'file_path' => $file->file_path,
'file_url' => getenv('MAIN_DOMAIN') . $file->file_path,
'file_size' => $file->file_size,
'file_type' => $file->file_type,
'category' => $file->category,
'partition' => $file->partition ?? 'other',
'original_name' => $file->original_name ?? '',
'extension' => $file->extension ?? '',
'created_at' => $file->created_at,
];
}
/**
* 获取文件
*
* @param string $path
* @return bool|\Illuminate\Database\Eloquent\Model
*/
public function getFile(array $where)
{
$config['model'] = $this->modelClass;
$config['where'] = $where;
$file = $this->logicShow($config);
if (!$file) {
return false;
}
return $file;
}
/**
* 获取文件
*
* @param string $path
* @return bool|\Illuminate\Database\Eloquent\Model
*/
public function findFile(array $where)
{
$file = UploadedFiles::where(['file_path' => $where['file_path']]);
if (!$file) {
return false;
}
return $file;
}
/**
* 删除文件
*
* @param int $fileId
* @return bool
*/
public function deleteFile(int $fileId): bool
{
$config['model'] = $this->modelClass;
$config['where'] = ['id' => $fileId];
$file = $this->logicShow($config);
if (!$file) {
return false;
}
// 删除数据库记录
$config =[
'model' => $this->modelClass,
'where' => ['id' => $fileId],
];
return $this->logicDeleteBy($config);
}
/**
* 获取分区配置
*
* @return array
*/
public function getPartitionConfig(): array
{
return $this->uploadPartitions;
}
/**
* 生成文件名
*
* @param $file
* @return string
*/
protected function generateFileName($file): string
{
$extension = $file->getUploadExtension();
$timestamp = time();
$random = substr(md5(uniqid()), 0, 8);
return $timestamp . '_' . $random . '.' . $extension;
}
/**
* 格式化文件大小
*
* @param int $bytes
* @return string
*/
protected function formatFileSize(int $bytes): string
{
$units = ['B', 'KB', 'MB', 'GB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, 2) . ' ' . $units[$pow];
}
}
+315
View File
@@ -0,0 +1,315 @@
<?php
namespace app\common\service;
use app\common\logic\AuthLogic;
use app\common\validate\UserValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\User;
use app\common\logic\UserLogic;
use app\common\utils\Jwt;
use support\exception\BusinessException;
use support\Request;
class UserService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = User::class;
protected $logic;
// 字符集配置
protected static $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
public function __construct()
{
$this->logic = (new UserLogic());
}
/**
* 验证器
*/
protected $validator = UserValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 获取微信二维码
*/
public function makeQrUrl()
{
$wechat = new WechatService();
return $wechat->makeQrUrl();
}
/**
* 获取用户信息
*/
public function userInfo(string $code, string $state)
{
$logic = new UserLogic();
$accessToken = $logic->getAccessToken($code, $state);
// $accessToken['openid'] = 'op9DT6xBzVF05dypmQ2JsM5DP6yw';
// $accessToken['unionid'] = 'o04nT6-St_2MZR6VRduYEwiicmiA';
$userInfo = $logic->checkUser($accessToken['openid'], $accessToken['unionid']);
if ($userInfo === false) {
return [
'is_bind' => false,
'access_token' => $accessToken['access_token'],
'openid' => $accessToken['openid'],
'unionid' => $accessToken['unionid'],
];
}
$jwt = new Jwt();
$token = $jwt->generateToken($logic->buildJwtPayload($userInfo));
$refreshToken = (new AuthLogic())->generateRefreshToken($userInfo->id, 'board');
return array_merge($userInfo->toArray(), ['access_token' => $token, 'is_bind' => true, 'refresh_token'=> $refreshToken, 'expires_in' => config('jwt.jwt_expiration')]);
}
/**
* 获取用户信息(包括菜单和权限)
*/
public function getUserInfo(int $uid)
{
$user = $this->logic->userInfo($uid);
$ossservice = new OssService();
$user['avatar_pull'] = $user->avatar;
if(strpos($user->avatar, 'http') === false && !empty($user->avatar)){
$user['avatar_pull'] = $ossservice->generatePrivateUrl($user->avatar);
// $user['avatar_pull'] = '/proxy/ossurl?file='.$user->avatar;
}
return [
'user' => [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'avatar_pull' => $user['avatar_pull'],
'mobile' => $user->mobile,
'email' => $user->email,
'nickname' => $user->nickname,
'status' => $user->status,
'openid' => $user->openid,
'unionid' => $user->unionid,
],
];
}
/**
* 绑定用户
*
* @param array $data
* @return array
*/
public function bindUser(array $data)
{
$logic = new UserLogic();
$wechat = [
'access_token' => $data['access_token'],
'openid' => $data['openid'],
];
$wechatUserInfo = $logic->getWechatUserInfo($wechat);
$uData = [
'nickname' => $wechatUserInfo['nickname'],
'avatar' => $wechatUserInfo['headimgurl'],
'unionid' => $wechatUserInfo['unionid'],
'openid' => $wechatUserInfo['openid'],
'name' => $data['name'],
'mobile' => $data['mobile'],
'sn' => $data['sn'],
'password' => $data['password'],
];
$userinfo = $logic->bindUser($uData);
if ($userinfo === false) {
throw new BusinessException('未查到用户信息');
}
$jwt = new Jwt();
$refreshToken = (new AuthLogic())->generateRefreshToken($userinfo->id, 'board');
return array_merge($userinfo->toArray(), [
'access_token' => $jwt->generateToken($logic->buildJwtPayload($userinfo)),
'is_bind' => true,
'refresh_token'=> $refreshToken,
'expires_in' => config('jwt.jwt_expiration')
]);
}
/**
* 获取用户列表
*/
public function getList()
{
return $this->logic->getList();
}
/**
* 修改用户密码
*/
public function savePassword(int $uid, array $data)
{
// 对密码进行哈希处理
$password = $data['password'];
$user = User::find($uid);
if (!password_verify($password, $user->password)) {
throw new BusinessException('密码错误');
}
$user = $this->logic->savePassword($uid,$data);
return $user;
}
/**
* 修改用户密码
*/
public function save(int $uid, array $data)
{
// 对密码进行哈希处理
$user = User::find($uid);
$keysToKeep = ['name', 'nickname', 'avatar'];
$data = array_intersect_key($data, array_flip($keysToKeep));
$user = $user->update($data);
return $user;
}
/**
* 获取微信小程序用户信息(登录及获取手机号)
*/
public function wechatLogin(string $loginCode, string $phoneCode)
{
$logic = new UserLogic();
//$accessToken = $logic->getAccessToken($code, $state);
$session = $logic->miniCode2Session($loginCode);
if (!isset($session['openid'])) {
throw new BusinessException('登录失败');
// return json(['code'=>0, 'msg'=>'登录失败', 'data'=>$session]);
}
$openid = $session['openid'];
$tokenData = $logic->getMiniAccessToken();
$access_token = $tokenData['access_token'];
// 换手机号
$phoneData = $logic->getMiniPhoneNumber($access_token, $phoneCode);
if (!isset($phoneData['phone_info']['phoneNumber'])) {
throw new BusinessException('获取手机号失败');
// return json(['code'=>0, 'msg'=>'获取手机号失败', 'data'=>$phoneData]);
}
$phone = $phoneData['phone_info']['phoneNumber'];
$user = User::firstOrCreate(['phone' => $phone], [
'openid' => $openid
]);
// $userInfo = $logic->checkUser($accessToken['openid'], $accessToken['unionid']);
//
// if ($userInfo === false) {
// return [
// 'is_bind' => false,
// 'access_token' => $accessToken['access_token'],
// 'openid' => $accessToken['openid'],
// 'unionid' => $accessToken['unionid'],
// ];
// }
$jwt = new Jwt();
$JwtPayload = $logic->buildJwtPayload($user);
$JwtPayload['exp'] = $JwtPayload['exp'] + (86400 * 30);
$token = $jwt->generateToken($JwtPayload);
$refreshToken = (new AuthLogic())->generateRefreshToken($user->id, 'info');
return array_merge($user->toArray(), ['access_token' => $token, 'is_bind' => true, 'refresh_token'=> $refreshToken, 'expires_in' => config('jwt.jwt_expiration')]);
}
/**
* 小程序登录
*/
public function miniappLogin(array $data)
{
$logic = new UserLogic();
$wechatService = new WechatService();
$wxinfo = $wechatService->xcxOpenid($data['code']);
$name = '微信用户_' . self::generate(8);
// 用户信息
$user = User::firstOrCreate(
['openid' => $wxinfo['openid']],
[
'nickname' => $name,
'unionid' => $wxinfo['unionid'],
'name' => $name,
'avatar' => 'bsh_datax/user/20251021/a4ee815405a1506abeec1af0f738f70f_68f6e76514b66.jpg'
]
);
$jwt = new Jwt();
$JwtPayload = $logic->buildJwtPayload($user);
$JwtPayload['exp'] = $JwtPayload['exp'] + (86400 * 30);
$token = $jwt->generateToken($JwtPayload);
$refreshToken = (new AuthLogic())->generateRefreshToken($user->id, 'info');
return array_merge($user->toArray(), ['token' => $token, 'is_bind' => true, 'refresh_token'=> $refreshToken, 'expires_in' => config('jwt.jwt_expiration')]);
}
/**
* 获取手机号
*/
public function miniappPhoneNumber(array $data,Request $request)
{
// 手机号
$wechatService = new WechatService();
$wxinfo = $wechatService->xcxPhoneNumber($data['phone_code']);
$uinfo = User::where('id', $request->user->uid)->first();
$uinfo->mobile = $wxinfo['phone_info']['phoneNumber'];
$uinfo->save();
return $wxinfo;
}
/**
* 生成随机字符串编码
*
* @param int $length 字符串长度
* @param bool $uppercaseOnly 是否只使用大写字母
* @return string
*/
public static function generate(int $length = 8, bool $uppercaseOnly = false): string
{
$characters = self::$characters;
if ($uppercaseOnly) {
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
}
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[random_int(0, $charactersLength - 1)];
}
return $randomString;
}
}
+236
View File
@@ -0,0 +1,236 @@
<?php
namespace app\common\service;
use app\common\model\User;
use EasyWeChat\MiniApp\Application as xcxApplication;
use EasyWeChat\OpenPlatform\Application;
use support\Redis;
use GuzzleHttp\Client;
use support\exception\BusinessException;
class WechatService
{
protected array $web;
protected array $mini;
protected array $login;
protected $app;
public function __construct()
{
$cfg = config('wechat');
$this->web = $cfg['web'];
$this->mini = $cfg['mini'];
$this->login = $cfg['login'];
}
/**
* 获取微信二维码
*/
public function makeQrUrl()
{
try {
$state = bin2hex(random_bytes(16));
$ttl = (int)($this->login['state_ttl'] ?? 300);
// 记录 state
Redis::setEx("wx:login:state:$state", $ttl, json_encode([
'redirect' => $this->web['redirect'],
'ts' => time(),
], JSON_UNESCAPED_UNICODE));
// 组装跳转地址
$qrUrl = sprintf(
'https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=%s&scope=%s&state=%s#wechat_redirect',
$this->web['app_id'],
urlencode($this->web['redirect']),
'code',
'snsapi_login',
$state,
);
$data = [
'state' => $state,
'qr_url' => $qrUrl
];
return $data;
} catch (\Throwable $th) {
// var_dump($th->getFile().'|'.$th->getLine().'|'.$th->getMessage());
throw new BusinessException('获取微信二维码失败');
}
}
/**
* 获取微信access_token
*/
public function wechatWebAccessToken(string $code, string $state): array
{
try {
// 校验 state
$stateJson = Redis::get("wx:login:state:$state");
if (!$stateJson) {
throw new \RuntimeException('state 已过期或无效');
}
// 拼接地址
$tokenUrl = sprintf(
'https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code',
$this->web['app_id'],
$this->web['app_secret'],
$code
);
// code 换 token
$token = $this->httpGetJson($tokenUrl);
if (!isset($token['access_token'], $token['openid'])) {
throw new \RuntimeException('换取 access_token 失败:'. json_encode($token, JSON_UNESCAPED_UNICODE));
}
return $token;
} catch (\Throwable $th) {
throw new BusinessException('获取 access_token 失败');
}
}
/**
* 获取微信用户信息
*
* @param array $token
* @return array
*/
public function wechatWebUserInfo(array $accessToken): array
{
try {
// 拉取用户信息
return $this->httpGetJson(sprintf(
'https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s',
$accessToken['access_token'],
$accessToken['openid']
));
} catch (\Throwable $th) {
// var_dump($th->getFile().'|'.$th->getLine().'|'.$th->getMessage());
throw new BusinessException('获取用户信息失败');
}
}
/**
* tools - httpGetJson
*/
protected function httpGetJson(string $url): array
{
$client = new Client([
'verify' => false, // 禁用 SSL 验证
'timeout' => 10,
]);
$json = $client->get($url)->getBody()->getContents();
return json_decode($json, true) ?: [];
}
//----------------------------小程序登录----------------------------------------
/**
* 获取 openid / session_key
* @param $code
* @return mixed
*/
public function miniCode2Session($code)
{
$appid = $this->mini['app_id'];
$secret = $this->mini['app_secret'];
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$secret&js_code=$code&grant_type=authorization_code";
return json_decode(file_get_contents($url), true);
}
/**
* 获取 access_token
* @return mixed
*/
public function getMiniAccessToken()
{
$appid = $this->mini['app_id'];
$secret = $this->mini['app_secret'];
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appid&secret=$secret";
return json_decode(file_get_contents($url), true);
}
/**
* 调用微信接口取手机号
* @param $access_token
* @param $phone_code
* @return mixed
*/
public function getMiniPhoneNumber($access_token, $phone_code)
{
$url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={$access_token}";
$data = json_encode([
'code' => $phone_code
]);
$options = [
'http' => [
'header' => "Content-type: application/json",
'method' => 'POST',
'content' => $data,
],
];
return json_decode(file_get_contents($url, false, stream_context_create($options)), true);
}
/**
* 小程序获取openid
* @param string $code
* @return array
* @throws BusinessException
*/
public function xcxOpenid(string $code): array
{
$config = [
'app_id' => getenv('WECHAT_MINI_APPID'),
'secret' => getenv('WECHAT_MINI_SECRET'),
'response_type' => 'array',
'log' => [
'level' => 'debug',
'file' => runtime_path().'/mzl/wechat.log',
]
];
// 创建微信小程序对象
$wechat = new xcxApplication($config);
// 获取 session_key 和 openid
return $wechat->getUtils()->codeToSession($code);
}
/**
* 小程序获取手机号
* @param string $phone_code
* @return mixed
* @throws BusinessException
*/
public function xcxPhoneNumber(string $phone_code)
{
$config = [
'app_id' => getenv('WECHAT_MINI_APPID'),
'secret' => getenv('WECHAT_MINI_SECRET'),
'response_type' => 'array',
'log' => [
'level' => 'debug',
'file' => runtime_path().'/mzl/wechat.log',
]
];
// 创建微信小程序对象
$wechat = new xcxApplication($config);
// getPhoneNumber
$data = ['code' => $phone_code];
$result = $wechat->getClient()->postJson('wxa/business/getuserphonenumber', $data);
if (isset($result['phone_info'])) {
// 业务处理 $result['phone_info']['phoneNumber']
return $result;
} else {
throw new BusinessException($result['errmsg']);
}
}
}
+152
View File
@@ -0,0 +1,152 @@
<?php
namespace app\common\traits;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
/**
* 数据转换器 Trait
* 用于将原始数据(如模型、集合、分页等)统一转换为数组格式,
* 并支持字段级与结果级的灵活后置加工。
*/
trait DataTransformer
{
/**
* 核心转换方法
*
* @param mixed $rawData 原始数据:Model、Collection、Paginator、数组等
* @param array $opts 可选配置:
* - 'fieldFormatter' => callable 字段名格式化回调,如 fn($f)=>snake_case($f)
* - 'postFormatter' => callable 结果级加工回调,接收整个数组并返回
*
* @return array 转换并加工后的纯 PHP 数组
*/
protected function transformData(
mixed $rawData,
?callable $postFormatter = null,
?callable $fieldFormatter = null
): array {
try {
$fieldFormatter = $fieldFormatter ?: fn($field) => $field;
if ($rawData instanceof LengthAwarePaginator) {
$arr = $this->formatPaginated($rawData, $postFormatter, $fieldFormatter);
} elseif ($rawData instanceof Model) {
$arr = $this->formatModel($rawData, $fieldFormatter);
} elseif ($rawData instanceof Collection) {
$arr = $this->formatCollection($rawData, $fieldFormatter);
} elseif (is_array($rawData)) {
$arr = $this->formatArray($rawData, $fieldFormatter);
} else {
$arr = (array)$rawData;
}
if (is_callable($postFormatter)) {
$arr = $postFormatter($arr);
}
return $arr;
} catch (\Throwable $e) {
return [
'error' => true,
'message' => $e->getMessage(),
// 'message' => $e->getMessage()."\n".$e->getLine()."\n".$e->getFile(),
'code' => $e->getCode(),
];
}
}
/**
* 格式化分页数据:转换每条记录
*
* @param LengthAwarePaginator $p
* @param callable $fmt
* @return array{records: array, total: int, current: int, size: int}
*/
protected function formatPaginated(
LengthAwarePaginator $p,
?callable $postFormatter,
callable $fmt
): array {
$records = $p->getCollection()
->map(fn($item) => $this->transformData($item, $postFormatter, $fmt))
->toArray();
return [
'records' => $records,
'total' => $p->total(),
'current' => $p->currentPage(),
'size' => $p->perPage(),
];
}
/**
* 格式化 Eloquent 模型:主表属性 + 关联递归
*
* @param Model $model
* @param callable $fmt
* @return array
*/
protected function formatModel(
Model $model,
callable $fmt
): array {
$out = [];
// 1) 主表字段
foreach ($model->getAttributes() as $key => $value) {
$out[$fmt($key)] = $value;
}
// 2) 关联递归
foreach ($model->getRelations() as $rel => $relData) {
if ($relData instanceof Model) {
$out[$rel] = $this->formatModel($relData, $fmt);
} elseif ($relData instanceof Collection) {
$out[$rel] = $relData
->map(fn($item) => $this->transformData($item, null, $fmt))
->toArray();
} else {
$out[$rel] = $relData;
}
}
return $out;
}
/**
* 格式化集合数据:对每个元素递归调用 transformData
*
* @param Collection $c
* @param callable $fmt
* @return array
*/
protected function formatCollection(
Collection $c,
callable $fmt
): array {
return $c
->map(fn($item) => $this->transformData($item, null, $fmt))
->toArray();
}
/**
* 格式化普通数组:递归处理可能出现的模型或集合
*
* @param array $arr
* @param callable $fmt
* @return array
*/
protected function formatArray(
array $arr,
callable $fmt
): array {
return array_map(function($item) use ($fmt) {
if (is_array($item)
|| $item instanceof Model
|| $item instanceof Collection
|| $item instanceof LengthAwarePaginator
) {
return $this->transformData($item, null, $fmt);
}
return $item;
}, $arr);
}
}

Some files were not shown because too many files have changed in this diff Show More