14 Commits

Author SHA1 Message Date
18338781771 ac35fccbbf Merge branch 'master' into develop 2025-07-21 09:46:26 +08:00
18338781771 fa4ade4b35 版权信息修改 2025-07-21 09:45:07 +08:00
18338781771 1ccaced113 1 2025-07-16 13:58:51 +08:00
18338781771 bfd458aeb7 1 2025-07-16 10:35:07 +08:00
18338781771 d42167bf14 1 2025-07-11 14:04:16 +08:00
18338781771 7711b76ac3 1 2025-06-24 13:43:45 +08:00
18338781771 c9674a6ad9 1 2025-06-23 14:06:39 +08:00
18338781771 7ab9151e4a 1 2025-06-23 13:46:11 +08:00
18338781771 a8041c4785 1 2025-06-23 13:44:55 +08:00
18338781771 0d59666ca4 Merge branch 'feature/admin' into develop
# Conflicts:
#	config/middleware.php
2025-04-18 15:42:55 +08:00
18338781771 c3d9527532 后端管理 2025-04-18 15:19:16 +08:00
18338781771 c954218088 日报 2025-04-17 10:14:01 +08:00
18338781771 88dc849bd5 日报 2025-04-11 11:32:41 +08:00
18338781771 00c8bb9c50 1 2025-04-09 16:26:44 +08:00
37 changed files with 2037 additions and 74 deletions
+5 -1
View File
@@ -19,4 +19,8 @@ JWT_EXPIRATION = 2592000
WEBMAN_LISTEN = 'http://0.0.0.0:9007'
WEBMAN_COUNT = 8
RPC_DOMAIN = http://219.157.255.213:25015
# 公共服务
RPC_DOMAIN = http://219.157.255.213:25015
# 前端域名
WEB_DOMAIN = http://219.157.255.213:8085
Generated
-20
View File
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/webman/." isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/webman/./app" isTestSource="false" packagePrefix="app\" />
<sourceFolder url="file://$MODULE_DIR$/webman/./app/view/components" isTestSource="false" packagePrefix="app\View\Components\" />
<excludeFolder url="file://$MODULE_DIR$/webman/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/webman/vendor/monolog/monolog" />
<excludeFolder url="file://$MODULE_DIR$/webman/vendor/nikic/fast-route" />
<excludeFolder url="file://$MODULE_DIR$/webman/vendor/psr/container" />
<excludeFolder url="file://$MODULE_DIR$/webman/vendor/psr/log" />
<excludeFolder url="file://$MODULE_DIR$/webman/vendor/workerman/coroutine" />
<excludeFolder url="file://$MODULE_DIR$/webman/vendor/workerman/webman-framework" />
<excludeFolder url="file://$MODULE_DIR$/webman/vendor/workerman/workerman" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+208
View File
@@ -0,0 +1,208 @@
<?php
namespace app\admin\controller;
use app\admin\model\Admin;
use app\util\Util;
use Illuminate\Support\Facades\Validator;
use support\Request;
use app\model\User;
use app\admin\model\Role;
class AdminController
{
public function list(Request $request,)
{
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$admin = Admin::with('roles')->paginate($perPage, ['*'], 'page', $page);
return Util::success($admin);
}
// 获取用户角色列表
public function roles(Request $request, $id)
{
$admin = Admin::with('roles')->findOrFail($id);
$allRoles = Role::all();
$res = [
'admin' => $admin,
'all_roles' => $allRoles,
'assigned_roles' => $admin->roles->pluck('id')->toArray()
];
return Util::success($res);
}
// 更新用户角色
public function updateRoles(Request $request, $id)
{
// $request->validate([
// 'roles' => 'required|array',
// 'roles.*' => 'exists:roles,id'
// ]);
// 验证规则
$rules = [
'roles' => 'required|array',
'roles.*' => 'exists:roles,id'
];
// 自定义错误消息(可选)
$messages = [
'roles.required' => '角色不能为空',
'roles.exists' => '角色id不存在',
];
// 执行验证
$validator = Validator::make($request->all(), $rules, $messages);
$admin = Admin::findOrFail($id);
$res = $admin->roles()->sync($request->input('roles'));
return Util::success($res,'角色更新成功');
}
// 创建管理员
public function create(Request $request)
{
$rules = [
'username' => ['required', 'string', 'min:5', 'regex:/^[a-zA-Z0-9\-\_\.]+$/'],
'password' => 'required|string|min:6',
'nickname' => 'required|string',
'email' => 'nullable|email',
'status' => 'in:0,1'
];
$messages = [
'username.required' => '账号不能为空',
'username.min' => '账号长度不能少于5位',
'username.regex' => '账号只能包含字母、数字、下划线、连字符和点号',
'password.required' => '密码不能为空',
'password.min' => '密码长度不能少于6位',
'nickname.required' => '姓名不能为空',
'email.email' => '邮箱格式不正确',
'status.in' => '状态值不正确'
];
$validator = Validator::make($request->all(), $rules, $messages);
if ($validator->fails()) {
return Util::fail('',$validator->errors()->first());
}
// 检查用户名是否已存在
if (Admin::where('username', $request->input('username'))->exists()) {
return Util::fail('','账号已存在');
}
// 检查邮箱是否已存在
if ($request->input('email') && Admin::where('email', $request->input('email'))->exists()) {
return Util::fail('','邮箱已存在');
}
$admin = new Admin();
$admin->username = $request->input('username');
$admin->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
$admin->nickname = $request->input('nickname');
$admin->email = $request->input('email', null);
$admin->status = $request->input('status', 1);
$admin->save();
if ($request->input('roles')) {
$res = $admin->roles()->sync($request->input('roles'));
}
return Util::success($admin, '管理员创建成功');
}
// 修改管理员信息
public function update(Request $request, $id)
{
$admin = Admin::findOrFail($id);
$rules = [
'password' => ['string', 'min:6', 'regex:/^[a-zA-Z0-9\-\_\.]+$/'],
'nickname' => 'string',
'email' => 'email',
'status' => 'in:0,1',
'old_password' => 'required_with:password|string',
'avatar' => 'string',
'mobile' => 'string'
];
$messages = [
'password.min' => '密码长度不能少于6位',
'password.regex' => '密码只能包含字母、数字、下划线、连字符和点号',
'email.email' => '邮箱格式不正确',
'status.in' => '状态值不正确',
'old_password.required_with' => '请提供旧密码',
'old_password.string' => '旧密码格式不正确'
];
$validator = Validator::make($request->all(), $rules, $messages);
if ($validator->fails()) {
return Util::fail('',$validator->errors()->first());
}
// 检查邮箱是否已存在(排除当前管理员)
if ($request->input('email') && Admin::where('email', $request->input('email'))->where('id', '!=', $id)->exists()) {
return Util::fail('','邮箱已存在');
}
// 准备要更新的数据
$updateData = $request->only(['nickname', 'email', 'status', 'avatar', 'mobile']);
// 如果修改密码,验证旧密码
if ($request->input('password')) {
if (!$request->input('old_password')) {
return Util::fail('','请提供旧密码');
}
if (!password_verify($request->input('old_password'), $admin->password)) {
return Util::fail('','旧密码不正确');
}
$updateData['password'] = password_hash($request->input('password'), PASSWORD_DEFAULT);
}
// 只填充前端传递的字段
$admin->fill($updateData);
$admin->save();
return Util::success($admin, '管理员信息更新成功');
}
// 禁用/启用管理员
public function toggleStatus(Request $request, $id)
{
$admin = Admin::findOrFail($id);
$admin->status = $admin->status == 1 ? 0 : 1;
$admin->save();
$statusText = $admin->status == 1 ? '启用' : '禁用';
return Util::success($admin, "管理员{$statusText}成功");
}
// 重置管理员密码
public function resetPassword(Request $request, $id)
{
$rules = [
'new_password' => 'required|string|min:6',
// 'confirm_password' => 'required|string|same:new_password'
];
$messages = [
'new_password.required' => '新密码不能为空',
'new_password.min' => '新密码长度不能少于6位',
// 'confirm_password.required' => '确认密码不能为空',
// 'confirm_password.same' => '两次输入的密码不一致'
];
$validator = Validator::make($request->all(), $rules, $messages);
if ($validator->fails()) {
return Util::fail('',$validator->errors()->first());
}
$admin = Admin::findOrFail($id);
$admin->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
$admin->save();
return Util::success(null, '密码重置成功');
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace app\admin\controller;
use app\admin\model\Admin;
use app\admin\model\Menu;
use app\util\Util;
use support\Redis;
use support\Request;
use app\util\JwtUtil;
class AuthController
{
/**
* 不需要登录的方法
*/
protected $noNeedLogin = ['login'];
public function login(Request $request)
{
$username = Util::getParam($request, 'username');
if (Util::isEmpty($username)) {
return Util::fail([], trans('required_not_null'));
}
$password = Util::getParam($request, 'password');
if (Util::isEmpty($password)) {
return Util::fail([], trans('required_not_null'));
}
$admin = Admin::where('status', 1)
->where('username', $username)
->first();
if (!$admin) {
return Util::fail(['user' => $admin], trans('user_none'));
}
$userPassword = $admin->password;
$user = $admin->toArray();
//加密
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
//验证密码
if (!password_verify($password, $userPassword)) {
return Util::fail([], trans('password_error'));
}
$userId = $user['id']; // 实际从数据库获取
Redis::set('admin_' . $userId, $user);
//密码验证成功后加密生成 Token
$token = JwtUtil::generateToken('admin_'.$userId);
$result = $user;
unset($result['password']);
$result['token'] = $token;
return Util::success($result, trans('login_success'));
}
public function menus(Request $request)
{
$user = $request->user;
$menus = Menu::with(['child.child' => function($query) {
$query->where('status', 1)->orderBy('sort');
}])
->where('parent_id', 0)
->where('status', 1)
->orderBy('sort')
->get()
->filter(function($menu) use ($user) {
if (empty($menu->permission_slug)) {
return true;
}
return $user->hasPermission($menu->permission_slug);
});
return Util::success($menus);
}
}
@@ -0,0 +1,49 @@
<?php
namespace app\admin\controller;
use app\model\Customer;
use app\model\User;
use app\util\Util;
use support\Request;
class CustomerController
{
/**
* 客户列表
* @param Request $request
* @return \support\Response
*/
public function list(Request $request)
{
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$where = [];
$user_id = $request->input('user_id');
if(empty($user_id) || $user_id == 0){
$where[] = ['user_id', '=', $request->input('user_id')];
}
$user = Customer::with('user')->where($where)->paginate($perPage, ['*'], 'page', $page);
$user = $user->toArray();
foreach ($user['data'] as &$item) {
unset($item['password']);
}
return Util::success($user);
}
public function follow(Request $request)
{
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$where = [];
if($request->input('user_id')){
$where[] = ['user_id', '=', $request->input('user_id')];
}
$user = Customer::with('user')->where($where)->paginate($perPage, ['*'], 'page', $page);
$user = $user->toArray();
foreach ($user['data'] as &$item) {
unset($item['password']);
}
return Util::success($user);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
namespace app\admin\controller;
use app\admin\model\Permission;
use app\admin\model\Role;
use app\util\Util;
use Illuminate\Support\Facades\Validator;
use support\Request;
class RoleController
{
public function all(Request $request,)
{
$admin = Role::with('permissions')->where([['status',1]])->get();
return Util::success($admin);
}
/*
* 角色列表
*/
public function list(Request $request,)
{
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$admin = Role::with('permissions')->paginate($perPage, ['*'], 'page', $page);
return Util::success($admin);
}
// 获取角色权限列表
public function permissions(Request $request, $id)
{
$role = Role::with('permissions')->findOrFail($id);
$allPermissions = Permission::all();
return json([
'code' => 0,
'data' => [
'role' => $role,
'all_permissions' => $allPermissions,
'assigned_permissions' => $role->permissions->pluck('id')->toArray()
]
]);
}
// 更新角色权限
public function updatePermissions(Request $request, $id)
{
// 验证规则
$rules = [
'permissions' => 'required|array',
'permissions.*' => 'exists:permissions,id'
];
// 自定义错误消息(可选)
$messages = [
'permissions.required' => '角色不能为空',
'permissions.exists' => '角色id不存在',
];
// 执行验证
$validator = Validator::make($request->all(), $rules, $messages);
$role = Role::with('permissions')->findOrFail($id);
$role->permissions()->sync($request->input('permissions'));
return Util::success($role,'角色更新成功');
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace app\admin\controller;
use app\model\User;
use app\util\Util;
use support\Request;
class UserController
{
public function list(Request $request,)
{
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$user = User::paginate($perPage, ['*'], 'page', $page);
$user = $user->toArray();
foreach ($user['data'] as &$item) {
unset($item['password']);
}
return Util::success($user);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace app\admin\controller;
use app\admin\model\Menu;
use support\Request;
class menus
{
public function menus(Request $request)
{
$user = $request->user();
$menus = Menu::with('children')
->where('parent_id', 0)
->where('status', 1)
->orderBy('sort')
->get()
->filter(function($menu) use ($user) {
if (empty($menu->permission_slug)) {
return true;
}
return $user->hasPermission($menu->permission_slug);
});
return json($menus);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\admin\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthMiddleware implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
try {
$token = $this->getTokenFromRequest($request);
if (!$token) {
return $this->unauthorized('缺少访问令牌');
}
if (!$user = auth('api')->setToken($token)->user()) {
return $this->unauthorized('用户不存在');
}
// 将用户信息存入请求
$request->user = $user;
} catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return $this->unauthorized('令牌已过期');
} catch (JWTException $e) {
return $this->unauthorized('令牌无效');
}
return $handler($request);
}
private function getTokenFromRequest(Request $request)
{
$header = $request->header('Authorization', '');
if (preg_match('/Bearer\s+(\S+)/', $header, $matches)) {
return $matches[1];
}
return $request->input('token');
}
private function unauthorized($msg)
{
return json(['code' => 401, 'msg' => $msg]);
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace app\admin\middleware;
use app\util\Util;
use ReflectionClass;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class PermissionCheck implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
$user = $request->user;
// $route = $request->route;
$route = $request->path();
// 通过反射获取控制器哪些方法不需要登录
$controller = new ReflectionClass($request->controller);
$noNeedLogin = $controller->getDefaultProperties()['noNeedLogin'] ?? [];
$isLogin = !in_array($request->action, $noNeedLogin);
$permission = substr($route,7);
$is_auth = str_starts_with($permission, 'auth');
$permissions = $user?$user->permissions():true;
if($isLogin && $permission && (empty($permissions) && !$is_auth) && !$user->isPermission($permission)){
return Util::fail('','无权访问该资源',401);
}
// // 检查角色
// $requiredRoles = $request->route->param('roles', []);
// if (!empty($requiredRoles) && !$user->hasAnyRole($requiredRoles)) {
// return $this->forbidden('需要特定角色才能访问');
// }
//
// // 检查权限
// $requiredPermission = $route->param('permission');
// if ($requiredPermission && !$user->hasPermission($requiredPermission)) {
// return $this->forbidden('无权访问该资源');
// }
return $handler($request);
}
private function forbidden($msg)
{
return json(['code' => 403, 'msg' => $msg]);
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
namespace app\admin\model;
use support\Model;
class Admin extends Model
{
protected $table = 'admins';
protected $hidden = ['password'];
public function roles()
{
return $this->belongsToMany(Role::class, 'admin_roles', 'user_id', 'role_id');
}
public function hasRole($role)
{
return $this->roles()->where('name', $role)->exists();
}
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
public function hasAnyRole($roles)
{
return $this->roles()->whereIn('name', (array)$roles)->exists();
}
public function hasPermission($permission)
{
return $this->permissions()->contains('slug', $permission);
}
public function isPermission($permission)
{
$permissions = $this->permissions();
return in_array($permission, array_column($permissions->toArray(), 'slug'));
}
public function permissions()
{
return $this->roles()
->with('permissions')
->get()
->pluck('permissions')
->flatten()
->unique('id');
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace app\admin\model;
use Illuminate\Database\Eloquent\Model;
class Menu extends Model
{
protected $table = 'menus';
public function child()
{
return $this->hasMany(Menu::class, 'parent_id')->orderBy('sort');
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace app\admin\model;
use Illuminate\Database\Eloquent\Model;
class Permission extends Model
{
protected $table = 'permissions';
public function roles()
{
return $this->belongsToMany(Role::class, 'role_permissions', 'permission_id', 'role_id');
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace app\admin\model;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
protected $table = 'roles';
public function users()
{
return $this->belongsToMany(Admin::class, 'user_roles', 'role_id', 'user_id');
}
public function permissions()
{
return $this->belongsToMany(Permission::class, 'role_permissions', 'role_id', 'permission_id');
}
}
+10 -10
View File
@@ -47,17 +47,17 @@ class AreaController
{
$list = RedisHelper::get("rpc_country");
$list = null;
if(!$list){
$res = HttpClient::get(config('const.rpc_domain') . '/country/all');
if ($res['code'] != 200 && $res['data']['code'] != 0){
Log::info('register_rpc',$res);
return Util::fail($res, trans('register_fail'));
}
$list = $res['data']['data']['data'];
RedisHelper::set("rpc_country", $list, 600);
}
// if(!$list){
// $res = HttpClient::get(config('const.rpc_domain') . '/country/all');
// if ($res['code'] != 200 && $res['data']['code'] != 0){
// Log::info('register_rpc',$res);
// return Util::fail($res, trans('register_fail'));
// }
// $list = $res['data']['data']['data'];
// RedisHelper::set("rpc_country", $list, 600);
// }
// $list = Country::get()->toArray();
$list = Country::get()->toArray();
$language = 'en_addr';
if($request->user['language'] == 'zh_CN'){
$language = 'cn_addr';
+107
View File
@@ -0,0 +1,107 @@
<?php
namespace app\controller;
use app\middleware\DataTracker;
use app\model\Daily;
use app\model\FeishuUser;
use app\model\User;
use app\util\Util;
use config\constants\Constants;
use support\Log;
use support\Request;
use support\exception\BusinessException;
class DailyController
{
/**
* 日报列表
* @return \support\Response
*/
public function list(Request $request)
{
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
//id
$userId = $request->user['id'];
if(Util::isEmpty($userId)){
return Util::fail([],'user_id'.trans('not_null'));
}
$where = [];
//判断领导
$mast = config('const.daily_group_2');
if ($userId == $mast['master']) {
$where['leader_id'] = $mast['master'];
}else{
$where['user_id'] = $userId;
}
// 执行分页查询
$data = Daily::with('user')->where($where)
->orderBy('id', 'desc')
->paginate($perPage, ['*'], 'page', $page);
return Util::success($data,trans('success'));
}
/**
* 日报列表
* @return \support\Response
*/
public function detail(Request $request)
{
//日报id
$id = Util::getParam($request,'id');
if(Util::isEmpty($id)){
return Util::fail([],'id'.trans('not_null'));
}
$where = [];
$where['id'] = $id;
// 执行分页查询
$data = Daily::with('user')->where($where)
->first();
$data['self'] = 0;
return Util::success($data,trans('success'));
}
/**
* 日报编辑
* @param Request $request
* @return \support\Response
*/
public function save(Request $request)
{
$id = Util::getParam($request, 'id');
$text = Util::getParam($request, 'text');
if (empty($id)) {
return Util::fail([], '日报id' . trans('not_null'));
}
if (Util::isEmpty($text)) {
return Util::fail([], 'text' . trans('not_null'));
}
$data = [
'text' => $text,
'remark' => Util::getParam($request, 'remark', '')
];
$res = Daily::where(['id' => $id])->update($data);
if ($res) {
return Util::success($res,trans('save_success'));
} else {
return Util::fail($res,trans('save_fail'));
}
}
}
+85 -9
View File
@@ -141,17 +141,18 @@ class FeishuController
/**
* @param $receiveId openid
* @param $title 推送标题
* @param $content 推送内容
* @param $href 详情链接
* @param $receiveId //openid
* @param $title //推送标题
* @param $content //推送内容
* @param $href //详情链接
* @param $msgType = 'post'; //卡片消息:interactive,富文本消息:post
* @return \support\Response
*/
public static function send($config='xinyouhe',$receiveId='ou_e33bb3017ada6c4781398a68e55a4696',$title='ceshi',$content='测试',$href='http://219.157.255.213:8085/pages/index/mine')
public static function send($config='xinyouhe',$receiveId='ou_e33bb3017ada6c4781398a68e55a4696',$content = '' , $msgType = 'post')
{
// 获取凭证
$certificate = config('feishu.'.$config);
Log::info('feishu send', [$certificate,'config'=>$config,'receiveId' => $receiveId, 'title' => $title, 'content' => $content, 'href' => $href]);
Log::info('feishu send', [$certificate,'config'=>$config,'receiveId' => $receiveId, 'content' => $content]);
// 飞书服务
$class = new \app\service\FeishuService($certificate['appId'], $certificate['appSecret'], $certificate['state']);
@@ -160,7 +161,7 @@ class FeishuController
// $receiveId = 'ou_a3b97543acbf1b28d978356bb08f85dd';
// 富文本 类型
$content = "{
/*$content = "{
\"zh_cn\":{
\"title\":\"$title\",
\"content\":[
@@ -179,7 +180,7 @@ class FeishuController
}]
]
}
}";
}";*/
// 卡片 类型
/*$content = "{
@@ -212,13 +213,88 @@ class FeishuController
}
}";*/
// $msgType = 'interactive';
$msgType = 'post'; //卡片消息:interactive,富文本消息:post
// $msgType = 'post'; //卡片消息:interactive,富文本消息:post
$receiveIdtype = 'open_id';
$result = $class->sendMessages($receiveId, $content, $msgType, $receiveIdtype);
return json($result);
}
/**
* 富文本消息
* @param $title
* @param $content
* @param $href
* @return string
*/
public static function postMsg($title,$content,$href)
{
// 富文本 类型
$content = "{
\"zh_cn\":{
\"title\":\"$title\",
\"content\":[
[{
\"tag\":\"text\",
\"text\":\"$content\",
\"style\":[\"bold\"]
}],
[{
\"tag\":\"hr\"
}],
[{
\"tag\":\"a\",
\"href\":\"$href\",
\"text\":\"查看详情\"
}]
]
}
}";
return $content;
}
/**
* 卡片消息
* @param $title
* @param $content
* @param $href
* @return string
*/
public static function interactiveMsg($title,$content,$href)
{
// 富文本 类型
$content = "{
\"config\":{
\"wide_screen_mode\":true
},
\"elements\":[{
\"tag\":\"div\",
\"text\":{
\"content\":\"$content \",
\"tag\":\"lark_md\"
}},{
\"actions\":[{
\"tag\":\"button\",
\"text\":{
\"content\":\"立即查看\",
\"tag\":\"plain_text\"
},
\"type\":\"primary\",
\"url\":\"$href\"
}],
\"tag\":\"action\"
}],
\"header\":{
\"template\":\"turquoise\",
\"title\":{
\"content\":\"📚$title\",
\"tag\":\"plain_text\"
}
}
}";
return $content;
}
public function show()
{
return '<div>查看详情 - 示例页面</div>';
+7 -4
View File
@@ -123,10 +123,13 @@ class FollowController
if(Util::isEmpty($type)){
return Util::fail([],'type'.trans('not_null'));
}
$picture = Util::getParam($request,'picture');
if(Util::isEmpty($picture)){
return Util::fail([],trans('picture_not_null'));
}
$picture = Util::getParam($request,'picture');
$audio_text = Util::getParam($request,'audio_text');
$remark = Util::getParam($request,'remark');
if(Util::isEmpty($picture) && Util::isEmpty($audio_text) && Util::isEmpty($remark)){
return Util::fail([],trans('follow_not_null'));
}
$data = [
'user_id'=>$request->user['id'],
+3 -2
View File
@@ -47,11 +47,12 @@ class UserController
$userPassword = $user['password'];
//加密
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
//验证密码
// if (!password_verify($userPassword, $hashedPassword)) {
if ($userPassword == $hashedPassword) {
// if ($userPassword == $hashedPassword) {
if (!password_verify($password, $userPassword)) {
locale($language);
return Util::fail([], trans('password_error'));
}
+17 -6
View File
@@ -2,6 +2,7 @@
namespace app\middleware;
use app\admin\model\Admin;
use app\model\User;
use app\util\JwtUtil;
use app\util\Util;
@@ -77,17 +78,27 @@ class AuthMiddleware implements MiddlewareInterface
}
$userId = $decoded->sub;
$user = User::select(['id','name','sex','role','avatar','email','password','language','created_at'])
->find($userId);
if(!$user){
return Util::fail(['user'=>$user],trans('user_none'));
if(str_starts_with($userId,'admin_')){
$userId = substr($userId,6);
$user = Admin::find($userId);
if(!$user){
return Util::fail(['user'=>$user],trans('user_none'));
}
// $user = $user->toArray();
}else{
$user = User::select(['id','name','sex','role','avatar','email','password','language','created_at'])
->find($userId);
if(!$user){
return Util::fail(['user'=>$user],trans('user_none'));
}
$user = $user->toArray();
locale($user['language']);
}
$user = $user->toArray();
// 将用户ID存入请求对象,供后续使用
// $request->user = ['id' => $userId];
$request->user = $user;
locale($user['language']);
return $handler($request);
} catch (BusinessException $e) {
return json(['code' => 401, 'msg' => $e->getMessage()]);
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace app\middleware;
use app\model\RequestTracks;
class DataTracker
{
// 不需要埋点的路径
protected $excludePaths = [
'/user/login'
];
public function process($request, \Closure $next)
{
if ($this->shouldSkipTracking($request)) {
return $next($request);
}
// 前置处理
$this->logRequestData($request);
$response = $next($request);
// 后置处理
// $this->logResponseData($response);
return $response;
}
protected function shouldSkipTracking($request)
{
$currentPath = $request->path();
$eventTracking = config('const.event_tracking');
// 检查是否在排除列表中
// foreach ($this->excludePaths as $path) {
// if (str_starts_with($currentPath, $path)) {
// return true;
// }
// }
if (in_array($currentPath, $eventTracking)) {
return false;
}
return true;
}
protected function logRequestData($request)
{
$trackData = [
'app_id' => 'crm/api',
'user_id' => $request->user['id']??0,
'ip' => $request->getRealIp(),
'method' => $request->method(),
'path' => $request->path(),
'params' => json_encode($request->all()),
'header' => json_encode($request->header()),
'uuid' => $this->generateTraceId(),
'user_agent' => $request->header('user-agent'),
'referer' => $request->header('referer'),
];
$requestTrack = RequestTracks::create($trackData);
}
protected function generateTraceId()
{
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
}
+6
View File
@@ -128,4 +128,10 @@ class Customer extends Model
{
return $this->hasMany(CustomerHis::class, 'customer_id', 'id');
}
// 定义反向多对一关联
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace app\model;
use DateTimeInterface;
use Illuminate\Support\Carbon;
use support\Model;
class Daily extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'daily';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
//设置黑名单
protected $guarded = ['id','created_at','updated_at'];
// 指定日期字段存储格式为时间戳
protected $dateFormat = 'U';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
// protected $dates = ['deleted_at'];
protected $casts = [
'updated_at' => 'datetime:Y-m-d H:i:s', // 指定序列化格式
'created_at' => 'datetime:Y-m-d H:i:s', // 指定序列化格式
];
/**
* 重新格式化时间
* @param DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date): string
{
return $date->format(Carbon::parse($date)->toDateTimeString());
}
// 定义反向多对一关联
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
}
+10 -6
View File
@@ -38,8 +38,6 @@ class Follow extends Model
*/
public $timestamps = true;
protected $dates = ['created_at', 'updated_at', 'follow_time'];
protected $casts = [
'updated_at' => 'datetime:Y-m-d H:i:s', // 指定序列化格式
'created_at' => 'datetime:Y-m-d H:i:s', // 指定序列化格式
@@ -51,10 +49,16 @@ class Follow extends Model
public static function getFollow($follow,$domain = 'https://vehicle-album.oss-cn-beijing.aliyuncs.com')
{
$follow['type_txt'] = Constants::FOLLOW[$follow['type']];
$follow['firstFollowtime'] = $follow['follow_time'];
$ossservice = new OssService();
$oss_picture = $ossservice->generatePrivateUrl($follow['picture'],86400);
$follow['oss_picture'] = $oss_picture;
$follow['firstFollowtime'] = $follow['follow_time'];
if (isset($follow['picture']) && !empty($follow['picture'])) {
$ossservice = new OssService();
$oss_picture = $ossservice->generatePrivateUrl($follow['picture'],86400);
$follow['oss_picture'] = $oss_picture;
}else {
$follow['oss_picture'] = '';
}
$follow['created_at'] = date('Y-m-d H:i:s',strtotime($follow['created_at'])+(3600*8));
$follow['updated_at'] = date('Y-m-d H:i:s',strtotime($follow['updated_at'])+(3600*8));
// $follow = Util::formatDate($follow);
return $follow;
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace app\model;
use DateTimeInterface;
use Illuminate\Support\Carbon;
use support\Model;
class RequestTracks extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'request_tracks';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
//设置黑名单
protected $guarded = ['id','created_at'];
// 指定日期字段存储格式为时间戳
protected $dateFormat = 'U';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $casts = [
'created_at' => 'datetime:Y-m-d H:i:s', // 指定序列化格式
];
/**
* 重新格式化时间
* @param DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date): string
{
return $date->format(Carbon::parse($date)->toDateTimeString());
}
// 禁用 updated_at 的自动更新
public function setUpdatedAt($value)
{
return $this; // 不做任何操作
}
}
+6
View File
@@ -50,4 +50,10 @@ class User extends Model
->toArray();
return $user;
}
// 定义与 Customer 的一对多关系
public function customer()
{
return $this->hasMany(Customer::class, 'user_id', 'id');
}
}
+99 -11
View File
@@ -5,7 +5,9 @@ namespace app\process;
use app\controller\FeishuController;
use app\model\Customer;
use app\model\CustomerHis;
use app\model\Daily;
use app\model\FeishuUser;
use app\model\RequestTracks;
use app\model\User;
use support\Log;
use Webman\App;
@@ -17,9 +19,9 @@ class Task
public function onWorkerStart()
{
// 每半小时执行一次
new Crontab('0 */30 * * * *', function(){
new Crontab('0 */30 * * * *', function () {
echo date('Y-m-d H:i:s') . "\n";
Log::info('send follow start',[time()]);
Log::info('send follow start', [time()]);
// FeishuController::send('xinyouhe','ou_e33bb3017ada6c4781398a68e55a4696', '跟进提醒', '您有新的跟进提醒', '');
// $customer = Customer::where(['follow_next', '<=', time() + 3600])
// ->where(['follow_next', '>', time()])
@@ -55,28 +57,29 @@ class Task
$feishuUser = FeishuUser::where(['user_id' => $item->user_id])->first();
if ($feishuUser) {
// 获取凭证
$certificate = config('feishu.'.$feishuUser->sign);
$certificate = config('feishu.' . $feishuUser->sign);
// 使用parse_url解析URL
$parsedUrl = parse_url($certificate['rUrl']);
// 获取域名和端口
$host = $parsedUrl['host'];
$port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';
$url = 'http://'. $host .$port. '/pages/customer/detile?id='.$item->id;
$url = 'http://' . $host . $port . '/pages/customer/detile?id=' . $item->id;
$item->warn = 1;
$item->update();
Log::info('send',['open_id'=>$item['open_id'],'url'=>$url]);
FeishuController::send($feishuUser->sign, $feishuUser->open_id, '跟进提醒', '您有新的跟进提醒', $url);
Log::info('send', ['open_id' => $item['open_id'], 'url' => $url]);
$content = FeishuController::postMsg('跟进提醒', '您有新的跟进提醒', $url);
FeishuController::send($feishuUser->sign, $feishuUser->open_id, $content, 'post');
}
}
Log::info('send follow start',[time()]);
Log::info('send follow start', [time()]);
});
// 每小时执行一次
new Crontab('0 10 * * * *', function(){
//回收站客户转向公海 每小时执行一次
new Crontab('0 10 * * * *', function () {
echo date('Y-m-d H:i:s') . "\n";
Log::info('send high seas start',[time()]);
Log::info('send high seas start', [time()]);
$where = [
['deleted_at', '<=', time() - 3600],
@@ -92,7 +95,92 @@ class Task
]);
}
Log::info('send high seas end',[time()]);
Log::info('send high seas end', [time()]);
});
//总结日报, 每天执行一次
new Crontab('0 30 17 * * *', function () {
echo date('Y-m-d H:i:s') . " start daily \n";
Log::info('start daily', [time()]);
$where[] = ["user_id", ">", 0];
$where = [
['created_at', '<=', time() - (3600 * 24)],
];
$eventTracking = config('const.event_tracking');
$requestTracks = RequestTracks::selectRaw('user_id,path, COUNT(*) as count',)
// select('*',' COUNT(*) as count')
->where($where)
->whereIn('path', $eventTracking)
->groupBy('user_id', 'path')
// ->orderBy('user_id', 'asc')
->get();
$statistics = [];
$users = [];
$content = '';
foreach ($requestTracks as $item) {
$statistics[$item->user_id][$item->path] = $item->count;
$users[] = $item->user_id;
}
foreach ($users as $userId) {
$data = [
'user_id' => $userId,
'customer_claim' => $statistics[$userId]['customer/claim'] ?? 0,
'customer_save' => $statistics[$userId]['customer/save'] ?? 0,
'follow_add' => $statistics[$userId]['follow/save'] ?? 0,
'customer_collect' => $statistics[$userId]['customer/collect'] ?? 0,
'content' => '',
];
//判断领导
$mast = config('const.daily_group_2');
if (in_array($userId, $mast['member'])) {
$data['leader_id'] = $mast['master'];
}else{
$data['leader_id'] = 5;
}
$content .= date('Y-m-d') . "\n";
$data['content'] .= '领取客户' . ($statistics[$userId]['customer/claim'] ?? 0) . '位\n';
$data['content'] .= '创建或修改客户' . ($statistics[$userId]['customer/save'] ?? 0) . '位\n';
$data['content'] .= '添加跟进' . ($statistics[$userId]['follow/save'] ?? 0) . '次\n';
$data['content'] .= '询盘跟单客户' . ($statistics[$userId]['customer/collect'] ?? 0) . '位\n';
$content .= $data['content'];
$daily = Daily::create($data);
$content .= '请及时查看,30分钟后将向主管发送';
$feishuUser = FeishuUser::where(['user_id' => $userId])->first();
if ($feishuUser) {
$url = config('const.web_domain') . '/pages/mine/workreportdetil?id=' . $daily->id;
$content = FeishuController::postMsg(date('Y-m-d') . '日报', $content, $url);
FeishuController::send($feishuUser->sign, $feishuUser->open_id, $content, 'post');
}
}
Log::info('send high seas end', [time()]);
});
//向领导发送日报 每天执行一次
new Crontab('0 0 18 * * *', function () {
echo date('Y-m-d H:i:s') . "\n";
Log::info('send daily to leader', [time()]);
$where = [];
$where[] = ['send', '=', 0];
$where[] = ['created_at', '>=', strtotime(date('Y-m-d 00:00:00'))];
$daily = Daily::select('leader_id')->where($where)->groupBy('leader_id')->get();
foreach ($daily as $item) {
$feishuUser = FeishuUser::where(['user_id' => $item->leader_id])->first();
if ($feishuUser) {
$url = config('const.web_domain') . '/pages/mine/workreport';
$content = FeishuController::postMsg(date('Y-m-d') . '日报已汇总', '请点击查看', $url);
FeishuController::send($feishuUser->sign, $feishuUser->open_id, $content, 'post');
}
}
});
}
+1 -1
View File
@@ -97,7 +97,7 @@
</ul>
<div class="footer">
<p>© 2024 河南晋海科技环保有限公司. 保留所有权利。</p>
<p>© 2024 河南新友合科技环保有限公司. 保留所有权利。</p>
</div>
</div>
</body>
+1 -1
View File
@@ -90,7 +90,7 @@
<p>2. 如因本协议产生争议,双方应友好协商解决;协商不成的,任何一方可向 管辖地 有管辖权的人民法院提起诉讼。</p>
<div class="footer">
<p>© 2024 河南晋海科技环保有限公司. 保留所有权利。</p>
<p>© 2024 河南新友合科技有限公司. 保留所有权利。</p>
</div>
</div>
</body>
+3 -1
View File
@@ -41,7 +41,9 @@
"vlucas/phpdotenv": "^5.6",
"guzzlehttp/guzzle": "^7.9",
"webman/cache": "^2.1",
"workerman/crontab": "^1.0"
"workerman/crontab": "^1.0",
"illuminate/validation": "^11.44",
"illuminate/container": "^11.44"
},
"suggest": {
"ext-event": "For better performance. "
Generated
+781 -2
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a1bdf8bacc564776f13b4f4634c1b4c9",
"content-hash": "611cb43c328188aa7f01631e69f47567",
"packages": [
{
"name": "aliyuncs/oss-sdk-php",
@@ -277,6 +277,162 @@
],
"time": "2024-02-18T20:23:39+00:00"
},
{
"name": "doctrine/lexer",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
"reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^8.1"
},
"require-dev": {
"doctrine/coding-standard": "^12",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10.5",
"psalm/plugin-phpunit": "^0.18.3",
"vimeo/psalm": "^5.21"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Lexer\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
"keywords": [
"annotations",
"docblock",
"lexer",
"parser",
"php"
],
"support": {
"issues": "https://github.com/doctrine/lexer/issues",
"source": "https://github.com/doctrine/lexer/tree/3.0.1"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
"type": "tidelift"
}
],
"time": "2024-02-05T11:56:58+00:00"
},
{
"name": "egulias/email-validator",
"version": "4.0.3",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
"reference": "b115554301161fa21467629f1e1391c1936de517"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517",
"reference": "b115554301161fa21467629f1e1391c1936de517",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"doctrine/lexer": "^2.0 || ^3.0",
"php": ">=8.1",
"symfony/polyfill-intl-idn": "^1.26"
},
"require-dev": {
"phpunit/phpunit": "^10.2",
"vimeo/psalm": "^5.12"
},
"suggest": {
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Egulias\\EmailValidator\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Eduardo Gulias Davis"
}
],
"description": "A library for validating emails against several RFCs",
"homepage": "https://github.com/egulias/EmailValidator",
"keywords": [
"email",
"emailvalidation",
"emailvalidator",
"validation",
"validator"
],
"support": {
"issues": "https://github.com/egulias/EmailValidator/issues",
"source": "https://github.com/egulias/EmailValidator/tree/4.0.3"
},
"funding": [
{
"url": "https://github.com/egulias",
"type": "github"
}
],
"time": "2024-12-27T00:36:43+00:00"
},
{
"name": "firebase/php-jwt",
"version": "v6.11.0",
@@ -957,7 +1113,13 @@
"type": "zip",
"url": "https://api.github.com/repos/illuminate/container/zipball/4dcc3fcbab92e734fc0c08d9cfd97f5a1aef18ca",
"reference": "4dcc3fcbab92e734fc0c08d9cfd97f5a1aef18ca",
"shasum": ""
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"illuminate/contracts": "^11.0",
@@ -1168,6 +1330,79 @@
},
"time": "2025-01-31T17:44:22+00:00"
},
{
"name": "illuminate/filesystem",
"version": "v11.44.2",
"source": {
"type": "git",
"url": "https://github.com/illuminate/filesystem.git",
"reference": "c79a54ebc61ad4e1665ca7610f276ff8a92f48f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/filesystem/zipball/c79a54ebc61ad4e1665ca7610f276ff8a92f48f7",
"reference": "c79a54ebc61ad4e1665ca7610f276ff8a92f48f7",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"illuminate/collections": "^11.0",
"illuminate/contracts": "^11.0",
"illuminate/macroable": "^11.0",
"illuminate/support": "^11.0",
"php": "^8.2",
"symfony/finder": "^7.0.3"
},
"suggest": {
"ext-fileinfo": "Required to use the Filesystem class.",
"ext-ftp": "Required to use the Flysystem FTP driver.",
"ext-hash": "Required to use the Filesystem class.",
"illuminate/http": "Required for handling uploaded files (^7.0).",
"league/flysystem": "Required to use the Flysystem local driver (^3.25.1).",
"league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).",
"league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).",
"league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).",
"psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
"symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).",
"symfony/mime": "Required to enable support for guessing extensions (^7.0)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "11.x-dev"
}
},
"autoload": {
"files": [
"functions.php"
],
"psr-4": {
"Illuminate\\Filesystem\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "The Illuminate Filesystem package.",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2025-02-19T20:17:00+00:00"
},
{
"name": "illuminate/macroable",
"version": "v11.44.2",
@@ -1443,6 +1678,130 @@
},
"time": "2025-02-23T14:20:22+00:00"
},
{
"name": "illuminate/translation",
"version": "v11.44.2",
"source": {
"type": "git",
"url": "https://github.com/illuminate/translation.git",
"reference": "de64584470b1d4bfc30f2c291585b873ce844ecf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/translation/zipball/de64584470b1d4bfc30f2c291585b873ce844ecf",
"reference": "de64584470b1d4bfc30f2c291585b873ce844ecf",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"illuminate/collections": "^11.0",
"illuminate/contracts": "^11.0",
"illuminate/filesystem": "^11.0",
"illuminate/macroable": "^11.0",
"illuminate/support": "^11.0",
"php": "^8.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "11.x-dev"
}
},
"autoload": {
"psr-4": {
"Illuminate\\Translation\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "The Illuminate Translation package.",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2025-02-14T15:47:05+00:00"
},
{
"name": "illuminate/validation",
"version": "v11.44.2",
"source": {
"type": "git",
"url": "https://github.com/illuminate/validation.git",
"reference": "0abcf721a46d3c2a94f225dce82f3c00070f6121"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/validation/zipball/0abcf721a46d3c2a94f225dce82f3c00070f6121",
"reference": "0abcf721a46d3c2a94f225dce82f3c00070f6121",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"brick/math": "^0.9.3|^0.10.2|^0.11|^0.12",
"egulias/email-validator": "^3.2.5|^4.0",
"ext-filter": "*",
"ext-mbstring": "*",
"illuminate/collections": "^11.0",
"illuminate/container": "^11.0",
"illuminate/contracts": "^11.0",
"illuminate/macroable": "^11.0",
"illuminate/support": "^11.0",
"illuminate/translation": "^11.0",
"php": "^8.2",
"symfony/http-foundation": "^7.0",
"symfony/mime": "^7.0"
},
"suggest": {
"illuminate/database": "Required to use the database presence verifier (^11.0)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "11.x-dev"
}
},
"autoload": {
"psr-4": {
"Illuminate\\Validation\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "The Illuminate Validation package.",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2025-03-11T08:51:12+00:00"
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.3",
@@ -2953,6 +3312,250 @@
],
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/finder",
"version": "v7.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/6de263e5868b9a137602dd1e33e4d48bfae99c49",
"reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=8.2"
},
"require-dev": {
"symfony/filesystem": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/finder/tree/v7.2.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-10-23T06:56:12+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v7.2.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "62d1a43796ca3fea3f83a8470dfe63a4af3bc588"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/62d1a43796ca3fea3f83a8470dfe63a4af3bc588",
"reference": "62d1a43796ca3fea3f83a8470dfe63a4af3bc588",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/polyfill-mbstring": "~1.1",
"symfony/polyfill-php83": "^1.27"
},
"conflict": {
"doctrine/dbal": "<3.6",
"symfony/cache": "<6.4.12|>=7.0,<7.1.5"
},
"require-dev": {
"doctrine/dbal": "^3.6|^4",
"predis/predis": "^1.1|^2.0",
"symfony/cache": "^6.4.12|^7.1.5",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/expression-language": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/mime": "^6.4|^7.0",
"symfony/rate-limiter": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpFoundation\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v7.2.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-12-30T19:00:17+00:00"
},
{
"name": "symfony/mime",
"version": "v7.2.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/2fc3b4bd67e4747e45195bc4c98bea4628476204",
"reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=8.2",
"symfony/polyfill-intl-idn": "^1.10",
"symfony/polyfill-mbstring": "^1.0"
},
"conflict": {
"egulias/email-validator": "~3.0.0",
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/mailer": "<6.4",
"symfony/serializer": "<6.4.3|>7.0,<7.0.3"
},
"require-dev": {
"egulias/email-validator": "^2.1.10|^3.1|^4",
"league/html-to-markdown": "^5.0",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/property-access": "^6.4|^7.0",
"symfony/property-info": "^6.4|^7.0",
"symfony/serializer": "^6.4.3|^7.0.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Mime\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Allows manipulating MIME messages",
"homepage": "https://symfony.com",
"keywords": [
"mime",
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.2.3"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-01-27T11:08:17+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.31.0",
@@ -3038,6 +3641,182 @@
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "c36586dcf89a12315939e00ec9b4474adcb1d773"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773",
"reference": "c36586dcf89a12315939e00ec9b4474adcb1d773",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=7.2",
"symfony/polyfill-intl-normalizer": "^1.10"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Trevor Rowbotham",
"email": "trevor.rowbotham@pm.me"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"idn",
"intl",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "3833d7255cc303546435cb650316bff708a1c75c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
"reference": "3833d7255cc303546435cb650316bff708a1c75c",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=7.2"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's Normalizer class and related functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"intl",
"normalizer",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.31.0",
+18
View File
@@ -12,6 +12,24 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
// 初始化 Laravel 容器
use Illuminate\Container\Container;
use Illuminate\Support\Facades\Facade;
$container = Container::getInstance();
// 将容器设置为 Facade 的根
Facade::setFacadeApplication($container);
// 注册验证器服务(关键步骤)
$container->singleton('validator', function () {
return new \Illuminate\Validation\Factory(
new \Illuminate\Translation\Translator(
new \Illuminate\Translation\ArrayLoader(), 'en' // 默认语言
)
);
});
return [
support\bootstrap\Session::class,
];
+12
View File
@@ -2,4 +2,16 @@
return [
'rpc_domain' => getenv('RPC_DOMAIN'),
'event_tracking' => [
'/follow/save',
'/customer/claim',
'/customer/save',
'/customer/collect',
'/user/login',
],
'web_domain' => getenv('WEB_DOMAIN'),
'daily_group_2' => [
'master' => 28, //组长
'member' => [22, 23, 28, 34, 44, 46, 77, 87] //组员
],
];
+8
View File
@@ -17,5 +17,13 @@ return [
'' => [
app\middleware\CrossDomain::class, // 全局跨域中间件
app\middleware\AuthMiddleware::class, // 鉴权中间件
app\middleware\DataTracker::class, //埋点
app\admin\middleware\PermissionCheck::class, // 后端权限
],
// 路由中间件
'route' => [
'track' => app\middleware\DataTracker::class,
]
];
+1
View File
@@ -22,6 +22,7 @@ return [
'param_error' => ' param error',
'required_not_null' => ' required not_ ull',
'picture_not_null' => ' picture not null',
'follow_not_null' => ' The image and follow-up content cannot be empty at the same time',
'contacts_not_null' => ' Contact person, email, and phone cannot be empty at the same time',
'car_type_not_null' => ' preference_not_null',
'channel_not_null' => ' channel not null',
+1
View File
@@ -22,6 +22,7 @@ return [
'param_error' => ' Ошибка параметра',
'required_not_null' => ' Обязательное заполнение не может быть пустым.',
'picture_not_null' => ' Фотографии не могут быть пустыми',
'follow_not_null' => ' Изображение и последующий контент не могут быть пустыми одновременно.X',
'contacts_not_null' => ' Контакты, почтовый ящик, телефон не может быть одновременно пустым',
'car_type_not_null' => ' Предпочтения не могут быть пустыми',
'channel_not_null' => ' Каналы не могут быть пустыми',
+1
View File
@@ -22,6 +22,7 @@ return [
'param_error' => ' 参数错误',
'required_not_null' => ' 必填项不能为空',
'picture_not_null' => ' 图片不能为空',
'follow_not_null' => ' 图片与跟进内容不能同时为空',
'contacts_not_null' => ' 联系人,邮箱,电话不能同时为空',
'car_type_not_null' => ' 偏好不能为空',
'channel_not_null' => ' 渠道不能为空',