25 Commits

Author SHA1 Message Date
18338781771 69ae53f545 客户线索重复检查 2025-08-29 18:49:42 +08:00
18338781771 1500e39bb0 获取客户时间 2025-08-25 15:05:49 +08:00
18338781771 a2b01ad9a1 头像处理 2025-07-31 15:55:33 +08:00
18338781771 7052f8128a 公海数据隔离 2025-07-31 09:41:42 +08:00
18338781771 e78a1189ad 1 2025-07-30 18:23:14 +08:00
18338781771 c8fdbd5773 修改密码 2025-07-30 18:20:41 +08:00
18338781771 4e3616e973 重置密码 2025-07-30 18:13:24 +08:00
18338781771 e4aebe8ca7 商机 2025-07-29 09:34:13 +08:00
18338781771 aac0d4be6a 1 2025-07-28 18:04:33 +08:00
18338781771 601c299c2b 1 2025-07-28 18:00:52 +08:00
18338781771 4ef9784559 oss配置 2025-07-28 16:35:35 +08:00
18338781771 4080bfca42 脱敏优化 2025-07-25 16:59:26 +08:00
18338781771 8ef4abded4 1 2025-07-25 16:38:53 +08:00
18338781771 a43cdd9ba1 1 2025-07-25 15:49:41 +08:00
18338781771 e84be2be04 公海数据脱敏 2025-07-25 15:31:30 +08:00
18338781771 494e66488a 登录注册 2025-07-24 17:26:30 +08:00
18338781771 99d5c0c81d 1 2025-07-24 16:40:15 +08:00
18338781771 060c1a89c1 客户转让 2025-07-24 15:49:04 +08:00
18338781771 1c1b1dab23 客户转让 2025-07-24 15:41:25 +08:00
18338781771 c04f60c7e4 1 2025-07-24 15:07:11 +08:00
18338781771 20b394de65 渠道 2025-07-24 15:04:47 +08:00
18338781771 ea88566112 渠道 2025-07-24 15:03:30 +08:00
18338781771 a327b56d1b 获取用户 2025-07-24 14:57:35 +08:00
18338781771 ed479e026f 国家筛选 2025-07-24 13:42:17 +08:00
18338781771 ef66ecbbef 使用fdesk库 2025-07-23 16:55:18 +08:00
48 changed files with 1454 additions and 2093 deletions
+1 -5
View File
@@ -19,8 +19,4 @@ JWT_EXPIRATION = 2592000
WEBMAN_LISTEN = 'http://0.0.0.0:9007'
WEBMAN_COUNT = 8
# 公共服务
RPC_DOMAIN = http://219.157.255.213:25015
# 前端域名
WEB_DOMAIN = http://219.157.255.213:8085
RPC_DOMAIN = http://219.157.255.213:25015
-208
View File
@@ -1,208 +0,0 @@
<?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
@@ -1,79 +0,0 @@
<?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);
}
}
@@ -1,49 +0,0 @@
<?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
@@ -1,69 +0,0 @@
<?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
@@ -1,23 +0,0 @@
<?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
@@ -1,27 +0,0 @@
<?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
@@ -1,49 +0,0 @@
<?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
@@ -1,51 +0,0 @@
<?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
@@ -1,54 +0,0 @@
<?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
@@ -1,15 +0,0 @@
<?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
@@ -1,15 +0,0 @@
<?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
@@ -1,20 +0,0 @@
<?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');
}
}
+7 -4
View File
@@ -58,13 +58,16 @@ class AreaController
// }
$list = Country::get()->toArray();
$language = 'en_addr';
if($request->user['language'] == 'zh_CN'){
$language = 'cn_addr';
$name = 'en_addr';
//$language = $request->user['language'];
//$language = 'zh_CN';
$language = $request->language;
if($language == 'zh_CN'){
$name = 'cn_addr';
}
foreach ($list as &$row) {
$row['name'] = $row[$language];
$row['name'] = $row[$name];
}
return Util::success($list);
}
+5 -2
View File
@@ -17,11 +17,14 @@ class CarTypeController
{
$list = CarType::getAll();
if($request->user['language'] != 'zh_CN'){
//$language = $request->user['language'];
//$language = 'zh_CN';
$language = $request->language;
if($language == 'en'){
foreach ($list as &$value) {
$value['name'] = $value['name_en'];
}
}elseif ($request->user['language'] == 'ru'){
}elseif ($language == 'ru'){
foreach ($list as &$value) {
$value['name'] = $value['name_ru'];
}
+150 -42
View File
@@ -3,13 +3,16 @@
namespace app\controller;
use app\model\Channel;
use app\model\Corp;
use app\model\Customer;
use app\model\CustomerHis;
use app\model\Follow;
use app\model\InquiryLog;
use app\model\User;
use app\service\UserService;
use app\util\Util;
use config\constants\Constants;
use Illuminate\Database\Eloquent\Builder;
use support\Log;
use support\Request;
@@ -31,9 +34,13 @@ class CustomerController
{
$grade = [];
$gradeConstants = Constants::GRADE_EN;
if($request->user['language'] == 'zh_CN'){
//$language = $request->user['language'];
//$language = 'zh_CN';
$language = $request->language;
if($language == 'zh_CN'){
$gradeConstants = Constants::GRADE;
}elseif ($request->user['language'] == 'ru'){
}elseif ($language == 'ru'){
$gradeConstants = Constants::GRADE_RU;
}
foreach ($gradeConstants as $key => $value) {
@@ -54,9 +61,12 @@ class CustomerController
{
$grade = [];
$magnitudeConstants = Constants::MAGNITUDE_EN;
if($request->user['language'] == 'zh_CN'){
//$language = $request->user['language'];
//$language = 'zh_CN';
$language = $request->language;
if($language == 'zh_CN'){
$magnitudeConstants = Constants::MAGNITUDE;
}elseif ($request->user['language'] == 'ru'){
}elseif ($language == 'ru'){
$magnitudeConstants = Constants::MAGNITUDE_RU;
}
foreach ($magnitudeConstants as $key => $value) {
@@ -77,15 +87,18 @@ class CustomerController
{
$list = Channel::getAll();
if($request->user['language'] != 'zh_CN'){
foreach ($list as &$value) {
$value['name'] = $value['name_en'];
}
}elseif ($request->user['language'] == 'ru'){
foreach ($list as &$value) {
$value['name'] = $value['name_ru'];
}
}
//$language = $request->user['language'];
//$language = 'zh_CN';
// $language = $request->language;
// if($language != 'zh_CN'){
// foreach ($list as &$value) {
// $value['name'] = $value['name_en'];
// }
// }elseif ($language == 'ru'){
// foreach ($list as &$value) {
// $value['name'] = $value['name_ru'];
// }
// }
return Util::success($list);
@@ -140,6 +153,7 @@ class CustomerController
$keyword = $request->input('keyword', ''); //follow_last
$follow_last = $request->input('follow_last', ''); //最后跟进:1:当天跟进客户,2:近三天跟进客户,3:近七天跟进客户
$tagId = $request->input('tag_id', ''); //打有某个标签的客户
$countryId = $request->input('country_id', ''); //打有某个标签的客户
$seas = $request->input('seas', ''); //公海客户
$where = [];
@@ -154,6 +168,10 @@ class CustomerController
}else{
$where[] = ['user_id', $userId];
}
$where[] = ['status', 1];
if (!empty($countryId)){
$where[] = ['country_id', $countryId];
}
//验证权限
// if ($request->user['role'] != 1) {
// $where[] =['user_id',$userId] ;
@@ -178,14 +196,20 @@ class CustomerController
// 执行分页查询 //关联标签tags //关联标签及分组tags.group
$query = Customer::with('tags')->where($where)
->orderBy('customer.id', 'desc');
->orderBy('crm_customer.id', 'desc');
if(!empty($tagId)){
// $query->with(['tags' => function($query) use ($tagId){
//查询有$tagId 里任意一个标签的客户
// $query->whereHas('tags', function($query) use ($tagId){
// $query->where('tag.id', $tagId);
// }]);
$query->whereHas('tags', function($query) use ($tagId){
$query->where('tag.id', $tagId);
});
// });
//查询有$tagId 里所有标签的客户
$tagId = explode(',', $tagId);
foreach ($tagId as $id) {
$query->whereHas('tags', function($query) use ($id){
$query->where('tag_id', $id);
});
}
}
if (!Util::isEmpty($keyword)) {
$query->where(function ($query) use ($keyword) {
@@ -198,16 +222,31 @@ class CustomerController
}
$recycle = Util::getParam($request,'recycle', '');
if ($recycle == 'recycle' || $seas == 'seas') {
if ($recycle == 'recycle') {
$query->onlyTrashed();
}
if ($seas == 'seas') {
$user = User::where('id', $userId)->first();
$corpId = $user?$user->corp_id:0;
if($corpId == 1){
$query->whereIn('channel_id', [1,2,3,4,5,6,7,8]);
} elseif($corpId == 3) {
$query->whereIn('channel_id', [9,10,11]);
} else{
$query->whereIn('channel_id', [0]);
}
$query->withTrashed();
}
$data = $query->paginate($perPage, ['*'], 'page', $page);
foreach ($data->items() as &$value) {
$value = Customer::getCustomer($value, $request);
if($seas == 'seas'){
$value['seas_time'] = time()-strtotime($value['deleted_at']);
$value['phone'] = Util::mask($value['phone']);
$value['email'] = Util::mask($value['email']);
$value['WhatsApp'] = Util::mask($value['WhatsApp']);
$value['seas_time'] = time()-(empty($value['deleted_at'])?strtotime($value['updated_at']):strtotime($value['deleted_at']));
}
}
@@ -281,10 +320,10 @@ class CustomerController
if (Util::isEmpty($country_id)) {
// return Util::fail([],'country_id'.trans('not_null'));
}
$car_type_id = Util::getParam($request, 'car_type_id');
if (Util::isEmpty($car_type_id)) {
return Util::fail([], trans('car_type_not_null'));
}
// $car_type_id = Util::getParam($request, 'car_type_id');
// if (Util::isEmpty($car_type_id)) {
// return Util::fail([], trans('car_type_not_null'));
// }
$contacts = Util::getParam($request, 'contacts');
$email = Util::getParam($request, 'email');
$phone = Util::getParam($request, 'phone');
@@ -292,16 +331,19 @@ class CustomerController
return Util::fail([], trans('contacts_not_null'));
}
$user = User::where(['id' => $request->user['id']])->first();
$whatsApp = Util::getParam($request, 'WhatsApp', '');
$data = [
'user_id' => $request->user['id'],
'corp_id' => $user->corp_id,
'grade' => Util::getParam($request, 'grade', 0),
'channel_id' => $channel_id,
'country_id' => $country_id,
'car_type_id' => $car_type_id,
// 'car_type_id' => $car_type_id,
'magnitude' => Util::getParam($request, 'magnitude', 0),
'email' => $email,
'phone' => $phone,
'WhatsApp' => Util::getParam($request, 'WhatsApp', ''),
'WhatsApp' => $whatsApp,
'contacts' => $contacts,
'describe' => Util::getParam($request, 'describe'),
'lat' => Util::getParam($request, 'lat', 0),
@@ -311,6 +353,16 @@ class CustomerController
];
if (Util::isEmpty($id)) {
$data['enter_user_id'] = $request->user->uid;
$distinct = [
'email' => $email,
'phone' => $phone,
'whatsapp' => $whatsApp,
];
$dis = $this->distinct($distinct, $user->corp_id);
if($dis){
return Util::fail([], '客户线索重复,已经有人在跟进');
}
$res = Customer::create($data);
} else {
unset($data['grade']);
@@ -327,6 +379,26 @@ class CustomerController
}
}
public function distinct($params, $corpId){
$customer = Customer::where('corp_id', $corpId)
->where((function (Builder $query) use ($params) {
if(!empty($params['email']))
$query->Orwhere('email', '=', $params['email']);
if(!empty($params['phone']))
$query->Orwhere('phone', '=', $params['phone']);
if(!empty($params['whatsApp']))
$query->Orwhere('whatsApp', '=', $params['whatsApp']);
return $query;
}))
->first();
if (!empty($customer)) {
return true;
}
return false;
}
/**
* 领取公海客户
* @param Request $request
@@ -339,8 +411,11 @@ class CustomerController
if (Util::isEmpty($customerId)) {
return Util::fail([], trans('customer_id').trans('not_null'));
}
$user = User::where(['id' => $userId])->first();
$data = [
'user_id' => $request->user['id']
'user_id' => $request->user['id'],
'obtain' => time(),
'corp_id' => $user->corp_id,
];
$customer= Customer::withTrashed()->find($customerId);
@@ -357,6 +432,7 @@ class CustomerController
'user_his' => 0,
'user_id' => $userId,
]);
self::inquiryUpdate($customer,$request->user['id']);
if ($res) {
return Util::success($res, trans('success'));
@@ -366,6 +442,36 @@ class CustomerController
}
/**
* 询单修改
*/
public static function inquiryUpdate($customer,$userId = null)
{
if(empty($customer->sn) || $customer->type!=1){
return false;
}
$inquiryLog = InquiryLog::where('order_no', $customer->sn)->first();
if(empty($inquiryLog)){
return false;
}
$userId = $userId??$customer->user_id;
if ($userId == 0){
$inquiryLog->recipient = 0;
$inquiryLog->t_time = time();
$inquiryLog->status = 1;
$inquiryLog->r_time = time();
$inquiryLog->save();
}else {
$inquiryLog->recipient = $customer->user_id;
$inquiryLog->t_time = time();
$inquiryLog->save();
}
return true;
}
/**
* 批量转让
* @param Request $request
@@ -374,25 +480,21 @@ class CustomerController
public function cede(Request $request)
{
$customerId = Util::getParam($request, 'customer_id');
$user_sn = Util::getParam($request, 'user_sn');
$userId = $request->user['id'];
if (Util::isEmpty($user_sn)) {
return Util::fail([], 'ID'.trans('not_null'));
$userId = Util::getParam($request, 'user_id');
$selfId = $request->user['id'];
if (Util::isEmpty($userId)) {
return Util::fail([], 'user_id'.trans('not_null'));
}
if (Util::isEmpty($customerId)) {
return Util::fail([], 'customer_id'.trans('not_null'));
}
$user = User::where(['sn' => $user_sn])->first();
$user = User::where(['id' => $userId])->first();
if (Util::isEmpty($user)) {
return Util::fail(null, trans('user_none'));
}
if(Util::isEmpty($customerId)){
$data = [
'user_id' => $userId
];
$query= Customer::where($data);
}else{
$query= Customer::whereIn('id',explode(',',$customerId));
}
$query= Customer::where('user_id',$selfId)->where('id',$customerId);
$customer = $query->get();
foreach ($customer as $item) {
@@ -402,8 +504,14 @@ class CustomerController
'user_his' => $item->user_id,
'user_id' => $user['id'],
]);
self::inquiryUpdate($customer,$user->id);
}
$res = $query->update(['user_id' => $user->id]);
$data = [
'user_id' => $user->id,
'obtain' => time(),
];
$res = $query->update($data);
if ($res) {
-107
View File
@@ -1,107 +0,0 @@
<?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'));
}
}
}
+9 -85
View File
@@ -141,18 +141,17 @@ class FeishuController
/**
* @param $receiveId //openid
* @param $title //推送标题
* @param $content //推送内容
* @param $href //详情链接
* @param $msgType = 'post'; //卡片消息:interactive,富文本消息:post
* @param $receiveId openid
* @param $title 推送标题
* @param $content 推送内容
* @param $href 详情链接
* @return \support\Response
*/
public static function send($config='xinyouhe',$receiveId='ou_e33bb3017ada6c4781398a68e55a4696',$content = '' , $msgType = 'post')
public static function send($config='xinyouhe',$receiveId='ou_e33bb3017ada6c4781398a68e55a4696',$title='ceshi',$content='测试',$href='http://219.157.255.213:8085/pages/index/mine')
{
// 获取凭证
$certificate = config('feishu.'.$config);
Log::info('feishu send', [$certificate,'config'=>$config,'receiveId' => $receiveId, 'content' => $content]);
Log::info('feishu send', [$certificate,'config'=>$config,'receiveId' => $receiveId, 'title' => $title, 'content' => $content, 'href' => $href]);
// 飞书服务
$class = new \app\service\FeishuService($certificate['appId'], $certificate['appSecret'], $certificate['state']);
@@ -161,7 +160,7 @@ class FeishuController
// $receiveId = 'ou_a3b97543acbf1b28d978356bb08f85dd';
// 富文本 类型
/*$content = "{
$content = "{
\"zh_cn\":{
\"title\":\"$title\",
\"content\":[
@@ -180,7 +179,7 @@ class FeishuController
}]
]
}
}";*/
}";
// 卡片 类型
/*$content = "{
@@ -213,88 +212,13 @@ 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>';
+1 -3
View File
@@ -55,9 +55,7 @@ class FollowController
if(Util::isEmpty($userId)){
return Util::fail(['user_id'=>$userId],trans('user_none'));
}
if($request->user['role']!=1){
$where['user_id'] = $userId;
}
$where['user_id'] = $userId;
//跟进id
$id = Util::getParam($request,'id');
+3 -1
View File
@@ -16,7 +16,9 @@ class TagGroupController
{
$list = TagGroup::with('tags')->get();
$languageArr = Constants::LANGUAGE;
$language = $request->user['language'];
// $language = $request->user['language'];
//$language = 'zh_CN';
$language = $request->language;
foreach ($list as &$item) {
$item['names'] = unserialize($item['names']);
if (in_array($language, $languageArr)) {
+101 -41
View File
@@ -2,16 +2,19 @@
namespace app\controller;
use app\model\Corp;
use app\model\Country;
use app\model\FeishuUser;
use app\model\User;
use app\model\UserSn;
use app\service\AuthService;
use app\service\MailService;
use app\service\UserService;
use app\util\HttpClient;
use app\util\JwtUtil;
use app\util\Util;
use config\constants\Constants;
use support\exception\BusinessException;
use support\Log;
use support\Redis;
use support\Request;
@@ -21,24 +24,40 @@ class UserController
/**
* 不需要登录的方法
*/
protected $noNeedLogin = ['login', 'sendCode', 'register', 'step'];
protected $noNeedLogin = ['login', 'sendCode', 'register', 'step', 'savePassword'];
public function login(Request $request)
{
$email = Util::getParam($request, 'email');
$corpAbbr = Util::getParam($request, 'abbr');
$language = Util::getParam($request, 'language');
if (Util::isEmpty($email)) {
return Util::fail([], trans('required_not_null'));
}
if (Util::isEmpty($corpAbbr)) {
return Util::fail([], 'abbr'.trans('not_null'));
}
$password = Util::getParam($request, 'password');
if (Util::isEmpty($password)) {
return Util::fail([], trans('required_not_null'));
}
$user = User::select(['id', 'name', 'sex', 'role', 'avatar', 'email', 'password'])
->where('status', 0)
->where('email', $request->post('email'))
// 获取企业ID
$corpInfo = Corp::where('abbr', $corpAbbr)->first();
if (!$corpInfo) {
return Util::fail([], '公司不存在');
}
$corpId = $corpInfo->id;
$user = User::where('status', 0)
// ->where('email', $request->post('email'))
->where('corp_id', $corpId)
->OrWhere(['email' => $request->post('email'),'phone' => $request->post('email')])
->first();
// $builder = User::where('status', 0)
// ->where('email', $request->post('email'));
// $sql = str_replace(['?'], ['\'%s\''], $builder->toSql());
// $fullSql = vsprintf($sql, $builder->getBindings());
if (!$user) {
return Util::fail(['user' => $user], trans('user_none'));
}
@@ -52,10 +71,15 @@ class UserController
//验证密码
// if (!password_verify($userPassword, $hashedPassword)) {
// if ($userPassword == $hashedPassword) {
if (!password_verify($password, $userPassword)) {
locale($language);
return Util::fail([], trans('password_error'));
}
if ($user['status'] != 1) {
return Util::fail([], '账号已禁用',403);
}
$userId = $user['id']; // 实际从数据库获取
@@ -66,17 +90,29 @@ class UserController
$result = $user;
unset($result['password']);
$result['token'] = $token;
$result['feishu_openid'] = FeishuUser::where('user_id', $userId)->value('open_id');
// $result['feishu_openid'] = FeishuUser::where('user_id', $userId)->value('open_id');
$result['feishu_openid'] = $result['openid']??'';
if (!Util::isEmpty($language)) {
User::where('id', $userId)
->update(['language' => $language]);
// User::where('id', $userId)
// ->update(['language' => $language]);
locale($language);
}
return Util::success($result, trans('login_success'));
}
public function loginNew(Request $request)
{
try {
$authService = new AuthService();
$result = $authService->uPwdLogin($request->all());
return Util::success($result, '登录成功');
} catch (\Throwable $e) {
Util::fail([],$e->getMessage());
}
}
/**
* 发送邮箱验证码
* @param Request $request
@@ -115,12 +151,13 @@ class UserController
*/
public function register(Request $request)
{
$email = Util::getParam($request, 'email', 'asdf');
$email = Util::getParam($request, 'email', '');
$code = Util::getParam($request, 'code');
$sex = Util::getParam($request, 'sex', 0);
$name = Util::getParam($request, 'name', '');
$password = Util::getParam($request, 'password');
$mobile = Util::getParam($request, 'mobile');
$corpAbbr = Util::getParam($request, 'abbr');
$id = Util::getParam($request, 'id');
$language = Util::getParam($request, 'language');
@@ -144,7 +181,7 @@ class UserController
if ($code != $verification_code) {
return Util::fail([], trans('code_error'));
}
if(strlen($password) < 6) {
if(strlen($password) < 8) {
return Util::fail([], trans('password_short'));
}
@@ -152,36 +189,33 @@ class UserController
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$data = [
'mobile' => $mobile,
'phone' => $mobile,
'email' => $email,
'name' => '用户_'.rand(111111, 999999),
'sex' => $sex,
'role' => 1,
'password' => $hashedPassword,
'status' => 0,
'avatar' => 'lingke_crm/logo.png'
'status' => 1,
'avatar' => ''
];
if (Util::isEmpty($id)) {
$option = ['email' => $email];
$userService = new UserService;
$res = $userService->createOrquery($option);
Log::info('register_rpc',$res);
// return Util::fail($res, trans('register_fail'));
if ($res['code'] != 200 && $res['data']['code'] != 0){
return Util::fail($res, trans('register_fail'));
}
$data['sn'] = $res['data']['data']['sn'];
$data['id'] = $res['data']['data']['uid'];
$res = User::firstOrCreate(['sn' => $data['sn']],$data);
$res['id'] = $data['id'];
} else {
$res = User::where('id', $id)
->update($data);
// Log::info('update', ['res' => $res]);
(!$res) ?: $res = User::find($id)->toArray();
// 获取可用用户编号
$userSnModel = new UserSn();
$sn = $userSnModel->where('user_id', '=', 0)->inRandomOrder()->first();
if (!$sn) {
Log::error('用户注册时未找到适用编号');
return Util::fail([], '未找到适用编号');
}
$data['sn'] = $sn->user_sn;
// 获取企业ID
$corpInfo = Corp::where('abbr', $corpAbbr)->first();
if (!$corpInfo) {
return Util::fail([], '公司不存在');
}
$data['corp_id'] = $corpInfo->id;
$res = User::create($data);
unset($res['password']);
locale($language);
@@ -222,10 +256,11 @@ class UserController
->first();
$user->toArray();
unset($user->password);
$user['feishu_openid'] = FeishuUser::where('user_id', $userId)->value('open_id');
// $user['feishu_openid'] = FeishuUser::where('user_id', $userId)->value('open_id');
$user['feishu_openid'] = $user['openid']??'';
$user['country_name'] = Country::where('id', $user['country_id'])->value('cn_addr');
$ossservice = new \app\service\OssService();
$user['avatar_url'] = $user['avatar'] ? $ossservice->generatePrivateUrl($user['avatar']) : '';
$user['avatar_url'] = $user['avatar'];
$user['socialize_pic_url'] = $user['socialize_pic'] ? $ossservice->generatePrivateUrl($user['socialize_pic']) : '';
// 重构数组
@@ -234,6 +269,21 @@ class UserController
return Util::success($user, trans('success'));
}
public function getUsers(Request $request)
{
$userId = $request->user['id'];
$abbr = $request->input('abbr');
if(empty($abbr)){
return Util::fail([], 'abbr'.trans('not_null'));
}
$corpId = Corp::where('abbr', $abbr)->value('id');
$where[] = ['corp_id' , $corpId];
$where[] = ['status' , 1];
$user = User::select('id','name')->where($where)
->get();
return Util::success($user, trans('success'));
}
/**
* 修改
* @param Request $request
@@ -288,16 +338,24 @@ class UserController
$email = Util::getParam($request, 'email');
$code = Util::getParam($request, 'code');
$verification_code = Redis::get("verification_code:{$email}");
$id = $request->user['id'];
// $id = $request->user->uid;
if ($code != $verification_code) {
return Util::fail([], trans('code_error'));
}
$res = User::where('id', $id)
// $res = User::where('id', $id)
// ->first();
// if($res['email'] != $email) {
// return Util::fail([], trans('email_consistent'));
// }
$user = User::where('status', 0)
// ->where('corp_id', $corpId)
->OrWhere(['email' => $request->post('email'),'phone' => $request->post('email')])
->first();
if($res['email'] != $email) {
return Util::fail([], trans('email_consistent'));
if(!$user) {
return Util::fail([], trans('user_none'));
}
//加密
@@ -307,9 +365,11 @@ class UserController
$data = [
'password' => $hashedPassword
];
$user->password = $hashedPassword;
$res = $user->save();
$res = User::where('id', $id)
->update($data);
// $res = User::where('id', $id)
// ->update($data);
if ($res) {
return Util::success($res, trans('save_success'));
@@ -335,7 +395,7 @@ class UserController
return Util::fail([], trans('param_error'));
}
$data = [
'language' => $language,
// 'language' => $language,
];
$res = User::where('id', $id)
+8 -17
View File
@@ -2,7 +2,6 @@
namespace app\middleware;
use app\admin\model\Admin;
use app\model\User;
use app\util\JwtUtil;
use app\util\Util;
@@ -78,27 +77,19 @@ class AuthMiddleware implements MiddlewareInterface
}
$userId = $decoded->sub;
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::find($userId);
if(!$user){
return Util::fail(['user'=>$userId],trans('user_none'));
}
$user = $user->toArray();
// 将用户ID存入请求对象,供后续使用
// $request->user = ['id' => $userId];
$request->user = $user;
$request->language = $request->header('fdesk-language');
// locale($user['language']);
locale($request->language);
return $handler($request);
} catch (BusinessException $e) {
return json(['code' => 401, 'msg' => $e->getMessage()]);
-78
View File
@@ -1,78 +0,0 @@
<?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)
);
}
}
+2 -2
View File
@@ -11,7 +11,7 @@ class Channel extends Model
*
* @var string
*/
protected $table = 'channel';
protected $table = 'inquiry_channel';
/**
* The primary key associated with the table.
@@ -42,7 +42,7 @@ class Channel extends Model
public static function getAll()
{
$list = self::get()->select('id','name','name_en','status')->where('status',0)->toArray();
$list = self::get()->select('id','name','name_en','status')->where('status',1)->toArray();
return $list;
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace app\model;
use app\util\Util;
use config\constants\Constants;
use DateTimeInterface;
use Illuminate\Support\Carbon;
use support\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Corp extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'corp';
/**
* 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;
}
+7 -9
View File
@@ -17,7 +17,7 @@ class Customer extends Model
*
* @var string
*/
protected $table = 'customer';
protected $table = 'crm_customer';
/**
* The primary key associated with the table.
@@ -67,8 +67,11 @@ class Customer extends Model
*/
public static function getCustomer($customer, $request, $tag = false)
{
$language = $request->user['language'] ?: 'en';
//$language = $request->user['language'] ?: 'en';
//$language = 'zh_CN';
$language = $request->language;
$customer['grade_txt'] = Util::language($language, Constants::GRADE_EN[$customer['grade']], Constants::GRADE[$customer['grade']], Constants::GRADE_RU[$customer['grade']]);
$customer['magnitude'] = ($customer['magnitude']==0)?1:$customer['magnitude'];
$customer['magnitude_txt'] = Util::language($language, Constants::MAGNITUDE_EN[$customer['magnitude']], Constants::MAGNITUDE[$customer['magnitude']], Constants::MAGNITUDE_RU[$customer['magnitude']]);
$customer['sex_txt'] = Util::language($language, Constants::SEX_EN[$customer['sex']], Constants::SEX[$customer['sex']], Constants::SEX_RU[$customer['sex']]);
@@ -97,6 +100,7 @@ class Customer extends Model
$customer = Util::formatDate($customer,['follow_last']);
$nameLang = $language=='en'?'name_en':($language=='ru'?'name_ru':'name');
$nameLang = 'name';
$countryLang = $language=='en'?'en_addr':'cn_addr';
$customer['car_type_name'] = empty($customer['car_type_id'])?'':CarType::where(['id'=>$customer['car_type_id']])->value($nameLang);
$customer['country_name'] = empty($customer['country_id'])?'':Country::where(['id'=>$customer['country_id']])->value($countryLang);
@@ -118,7 +122,7 @@ class Customer extends Model
*/
public function tags()
{
return $this->belongsToMany(Tag::class, 'customer_tag', 'customer_id', 'tag_id')
return $this->belongsToMany(Tag::class, 'crm_customer_tag', 'customer_id', 'tag_id')
->withTimestamps(); // 如果需要记录关联时间
}
@@ -128,10 +132,4 @@ class Customer extends Model
{
return $this->hasMany(CustomerHis::class, 'customer_id', 'id');
}
// 定义反向多对一关联
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ class CustomerHis extends Model
*
* @var string
*/
protected $table = 'customer_his';
protected $table = 'crm_customer_his';
/**
* The primary key associated with the table.
+1 -1
View File
@@ -14,7 +14,7 @@ class CustomerTag extends Model
*
* @var string
*/
protected $table = 'customer_tag';
protected $table = 'crm_customer_tag';
/**
* The primary key associated with the table.
-61
View File
@@ -1,61 +0,0 @@
<?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');
}
}
+3 -3
View File
@@ -16,7 +16,7 @@ class Follow extends Model
*
* @var string
*/
protected $table = 'follow';
protected $table = 'crm_follow';
/**
* The primary key associated with the table.
@@ -57,8 +57,8 @@ class Follow extends Model
}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['created_at'] = (!empty($follow['created_at']))?date('Y-m-d H:i:s',strtotime($follow['created_at'])+(3600*8)):'';
$follow['updated_at'] = (!empty($follow['updated_at']))?date('Y-m-d H:i:s',strtotime($follow['updated_at'])+(3600*8)):'';
// $follow = Util::formatDate($follow);
return $follow;
}
+74
View File
@@ -0,0 +1,74 @@
<?php
namespace app\model;
use support\Model;
class InquiryLog extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'inquiry_log';
/**
* 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', 'order_no', 'enter_user_id', 'recipient',
'quality', 'channel', 'email', 'tel', 'whatsapp',
'country', 'contacts', 'content', 'status',
'r_time', 't_time', 'created_at', 'updated_at'
];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
public function enter()
{
return $this->hasOne(User::class, 'id', 'enter_user_id');
}
// public function recipientInfo()
// {
// return $this->hasOne(User::class, 'id', 'recipient')->where('status', 1);
// }
public function recipientInfo()
{
return $this->hasOne(User::class, 'id', 'recipient');
}
public function countryInfo()
{
return $this->hasOne(Country::class, 'id', 'country');
}
}
-57
View File
@@ -1,57 +0,0 @@
<?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; // 不做任何操作
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ class Tag extends Model
*
* @var string
*/
protected $table = 'tag';
protected $table = 'crm_tag';
/**
* The primary key associated with the table.
+1 -1
View File
@@ -17,7 +17,7 @@ class TagGroup extends Model
*
* @var string
*/
protected $table = 'tag_group';
protected $table = 'crm_tag_group';
/**
* The primary key associated with the table.
-6
View File
@@ -50,10 +50,4 @@ class User extends Model
->toArray();
return $user;
}
// 定义与 Customer 的一对多关系
public function customer()
{
return $this->hasMany(Customer::class, 'user_id', 'id');
}
}
+5 -149
View File
@@ -5,9 +5,7 @@ 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;
@@ -18,68 +16,11 @@ class Task
public function onWorkerStart()
{
// 每半小时执行一次
new Crontab('0 */30 * * * *', function () {
// 每小时执行一次
new Crontab('0 10 * * * *', function(){
echo date('Y-m-d H:i:s') . "\n";
Log::info('send follow start', [time()]);
// FeishuController::send('xinyouhe','ou_e33bb3017ada6c4781398a68e55a4696', '跟进提醒', '您有新的跟进提醒', '');
// $customer = Customer::where(['follow_next', '<=', time() + 3600])
// ->where(['follow_next', '>', time()])
// ->where(['warn', '<', 1])
// ->get();
// foreach ($customer as $item) {
// $feishuUser = FeishuUser::where(['user_id' => $item->user_id])->first();
// if ($feishuUser) {
// // 获取凭证
// $certificate = config($this->config);
// // 使用parse_url解析URL
// $parsedUrl = parse_url($certificate['rUrl']);
// // 获取域名和端口
// $host = $parsedUrl['host'];
// $port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';
// $url = $host .$port. '/pages/customer/detile?id='.$customer->id;
// $item->warn = 1;
// $item->update();
// $user = User::first()->where('id', $item->user_id);
// Log::info('send',['open_id'=>$item['open_id'],'url'=>$url]);
// // FeishuController::send('xinyouhe','ou_e33bb3017ada6c4781398a68e55a4696', '跟进提醒', '您有新的跟进提醒', $url);
// FeishuController::send($user->sign,$feishuUser->open_id, '跟进提醒', '您有新的跟进提醒', $url);
// }
// }
$where = [
['follow_next', '<=', time() + 3600],
['follow_next', '>', time()],
['warn', '<', 1]
];
$customer = Customer::where($where)->get();
foreach ($customer as $item) {
$feishuUser = FeishuUser::where(['user_id' => $item->user_id])->first();
if ($feishuUser) {
// 获取凭证
$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;
$item->warn = 1;
$item->update();
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()]);
});
//回收站客户转向公海 每小时执行一次
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],
@@ -95,92 +36,7 @@ class Task
]);
}
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');
}
}
Log::info('send high seas end',[time()]);
});
}
+4 -4
View File
@@ -15,10 +15,10 @@ class OssService
public function __construct()
{
$this->accessKeyId = 'LTAI5tAGiH7bkVkmrniXnfkA';
$this->accessKeySecret = 'nqklJBft6t3S0A4ZvxF4ci9ni91kmm';
$this->bucket = 'vehicle-album';
$this->endpoint = 'https://oss-cn-beijing.aliyuncs.com';
$this->accessKeyId = getenv('OSS_ACCESSID');
$this->accessKeySecret = getenv('OSS_ACCESSSECRET');
$this->bucket = getenv('OSS_BUCKET');
$this->endpoint = getenv('OSS_ENDPOINT');
$this->ossClient = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint);
}
+8
View File
@@ -118,4 +118,12 @@ class Util
return $en;
}
public static function mask($str)
{
if(empty($str)){
return '';
}
return substr($str, 0, 3) . '****' . substr($str, -4);
}
}
+2 -4
View File
@@ -36,14 +36,12 @@
"firebase/php-jwt": "^6.11",
"illuminate/database": "^11.44",
"phpmailer/phpmailer": "^6.9",
"shopwwi/webman-filesystem": "^2.0",
"iidestiny/flysystem-oss": "^4",
"vlucas/phpdotenv": "^5.6",
"guzzlehttp/guzzle": "^7.9",
"webman/cache": "^2.1",
"workerman/crontab": "^1.0",
"illuminate/validation": "^11.44",
"illuminate/container": "^11.44"
"shopwwi/webman-filesystem": "1.1",
"shopwwi/filesystem-oss": "^1.0"
},
"suggest": {
"ext-event": "For better performance. "
Generated
+990 -693
View File
File diff suppressed because it is too large Load Diff
-18
View File
@@ -12,24 +12,6 @@
* @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,16 +2,4 @@
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] //组员
],
];
+3 -3
View File
@@ -37,9 +37,9 @@ class Constants
];
//客户重要性
public const CUSTOMER_MAGNITUDE_INITIAL = 0; // 金牌商机
public const CUSTOMER_MAGNITUDE_COMMUNICATE = 1; // 银牌商机
public const CUSTOMER_MAGNITUDE_PLAN = 2; // 铜牌商机
public const CUSTOMER_MAGNITUDE_INITIAL = 1; // 金牌商机
public const CUSTOMER_MAGNITUDE_COMMUNICATE = 2; // 银牌商机
public const CUSTOMER_MAGNITUDE_PLAN = 3; // 铜牌商机
public const MAGNITUDE = [
self::CUSTOMER_MAGNITUDE_INITIAL => '金牌商机',
-8
View File
@@ -17,13 +17,5 @@ return [
'' => [
app\middleware\CrossDomain::class, // 全局跨域中间件
app\middleware\AuthMiddleware::class, // 鉴权中间件
app\middleware\DataTracker::class, //埋点
app\admin\middleware\PermissionCheck::class, // 后端权限
],
// 路由中间件
'route' => [
'track' => app\middleware\DataTracker::class,
]
];
+23 -13
View File
@@ -1,4 +1,17 @@
<?php
/**
*-------------------------------------------------------------------------p*
* 配置文件
*-------------------------------------------------------------------------h*
* @copyright Copyright (c) 2015-2022 Shopwwi Inc. (http://www.shopwwi.com)
*-------------------------------------------------------------------------c*
* @license http://www.shopwwi.com s h o p w w i . c o m
*-------------------------------------------------------------------------e*
* @link http://www.shopwwi.com by 象讯科技 phcent.com
*-------------------------------------------------------------------------n*
* @since shopwwi象讯·PHP商城系统Pro
*-------------------------------------------------------------------------t*
*/
return [
'enable' => true,
@@ -64,21 +77,18 @@ return [
],
'oss' => [
'driver' => \Shopwwi\WebmanFilesystem\Adapter\AliyunOssAdapterFactory::class,
// 'accessId' => 'OSS_ACCESS_ID',
// 'accessSecret' => 'OSS_ACCESS_SECRET',
// 'bucket' => 'OSS_BUCKET',
// 'endpoint' => 'OSS_ENDPOINT',
// 'url' => '', // 静态文件访问域名
'accessKey' => 'LTAI5tAGiH7bkVkmrniXnfkA',
'secretKey' => 'nqklJBft6t3S0A4ZvxF4ci9ni91kmm',
'bucket' => 'vehicle-album',
'endpoint' => 'https://oss-cn-beijing.aliyuncs.com',
'url' => '', // 静态文件访问域名
// 'accessKey' => getenv('OSS_ACCESSID'),
// 'secretKey' => getenv('OSS_ACCESSSECRET'),
// 'bucket' => getenv('OSS_BUCKET'),
// 'endpoint' => getenv('OSS_ENDPOINT'),
'accessId' => getenv('OSS_ACCESSID'),
'accessSecret' => getenv('OSS_ACCESSSECRET'),
'bucket' => getenv('OSS_BUCKET'),
'endpoint' => getenv('OSS_ENDPOINT'),
'url' => '' // 静态文件访问域名
// 'timeout' => 3600,
// 'connectTimeout' => 10,
'isCName' => false,
'prefix' => '',
// 'isCName' => false,
// 'token' => null,
// 'proxy' => null,
],
+1 -1
View File
@@ -28,7 +28,7 @@ return [
'channel_not_null' => ' channel not null',
'email_consistent' => ' Inconsistent email addresses',
'sign_not_null' => ' No affiliated company has been set',
'password_short' => ' Password cannot be less than 6 characters',
'password_short' => ' Password cannot be less than 8 characters',
'id_empty' => ' ID cannot be empty',
'customer_id' => ' customer ID',
'tag_group_id' => ' tag group ID',
+1 -1
View File
@@ -28,7 +28,7 @@ return [
'channel_not_null' => ' Каналы не могут быть пустыми',
'email_consistent' => ' Почтовый ящик не совпадает',
'sign_not_null' => ' Компания не создана',
'password_short' => ' Пароль не может быть меньше 6.',
'password_short' => ' Пароль не может быть меньше 8.',
'id_empty' => ' ID не может быть пустым',
'customer_id' => ' Идентификатор клиента',
'tag_group_id' => ' Идентификатор группы меток',
+1 -1
View File
@@ -28,7 +28,7 @@ return [
'channel_not_null' => ' 渠道不能为空',
'email_consistent' => ' 邮箱不一致',
'sign_not_null' => ' 未设置所属公司',
'password_short' => ' 密码不能少于6位',
'password_short' => ' 密码不能少于8位',
'id_empty' => ' id不能为空',
'customer_id' => ' 客户ID',
'tag_group_id' => ' 标签组ID',