Merge branch 'develop' into feature/crmAdmin0717

# Conflicts:
#	app/admin/controller/RoleController.php
#	app/common/controller/AuthController.php
#	app/common/logic/AuthLogic.php
#	app/common/logic/PermLogic.php
#	app/common/model/User.php
#	app/common/service/PermService.php
#	app/common/validate/RoleValidate.php
#	app/controller/IndexController.php
#	app/functions.php
#	app/ticket/controller/ConversationController.php
#	app/ticket/controller/TicketController.php
#	app/ticket/logic/TicketLogic.php
#	app/ticket/service/CategorieService.php
#	app/ticket/service/TicketService.php
#	app/view/index/qrcode.html
#	composer.json
#	composer.lock
#	config/middleware.php
#	config/route.php
#	config/whitelist.php
This commit is contained in:
2025-07-17 10:07:42 +08:00
38 changed files with 1854 additions and 379 deletions
+3
View File
@@ -0,0 +1,3 @@
User ← user_roles → Role ← role_resources → Resource (type=menu/button)
↘︎
role_permissions → Permission
@@ -1,36 +0,0 @@
<?php
namespace app\common\controller;
use support\Request;
use app\common\service\CorpService;
use app\common\service\CorpFeishuAppService;
class PresetController extends BaseController
{
protected $noNeedLogin = ['corps', 'feishuApp'];
public function corps()
{
try {
$result = (new CorpService())->getList();
return $this->success($result, '获取公司列表');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
public function feishuApp(Request $request)
{
try {
$corpId = $request->get('corp_id');
$result = (new CorpFeishuAppService())->getFeishuApp($corpId);
return $this->success($result, '获取飞书应用信息');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
-86
View File
@@ -1,86 +0,0 @@
<?php
namespace app\common\logic;
use Illuminate\Database\Eloquent\Model;
use support\exception\BusinessException;
class BaseLogic
{
/**
* 获取列表
*/
public function getList(array $params, string $modelClass)
{
$query = $modelClass::query();
// 你可以在此处添加分页、排序等功能
if (isset($params['page']) && isset($params['per_page'])) {
$query->paginate($params['per_page'], ['*'], 'page', $params['page']);
}
return $query->get();
}
/**
* 获取单个记录
*/
public function getDetail(int $id, string $modelClass)
{
$record = $modelClass::find($id);
if (!$record) {
throw new BusinessException("记录不存在", 404);
}
return $record;
}
/**
* 创建记录
*/
public function create(array $data, string $modelClass)
{
$model = new $modelClass;
$model->fill($data); // 根据字段填充数据
try {
$model->save();
} catch (\Throwable $e) {
throw new BusinessException("创建记录失败: " . $e->getMessage(), 500);
}
return $model;
}
/**
* 更新记录
*/
public function update(array $data, string $modelClass)
{
$record = $modelClass::find($data['id']);
if (!$record) {
throw new BusinessException("记录不存在", 404);
}
$record->update($data);
return $record;
}
/**
* 删除记录
*/
public function delete(int $id, string $modelClass)
{
$record = $modelClass::find($id);
if (!$record) {
throw new BusinessException("记录不存在", 404);
}
try {
$record->delete();
} catch (\Throwable $e) {
throw new BusinessException("删除记录失败: " . $e->getMessage(), 500);
}
return true;
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
namespace app\common\logic;
use app\common\model\CorpFeishuApp;
class CorpFeishuAppLogic
{
public function getFeishuApp($corpId)
{
return CorpFeishuApp::where('corp_id', $corpId)->first(['id', 'corp_id', 'name', 'app_id', 'r_url', 'state']);
}
}
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace app\common\logic;
use app\common\model\Corp;
class CorpLogic
{
public function getList()
{
return Corp::where('status', 1)->orderBy('sort', 'asc')->get(['id', 'name', 'abbr', 'tenant_key']);
}
}
+1 -1
View File
@@ -53,6 +53,6 @@ class Role extends Model
// 角色拥有的资源(权限点、菜单)
public function resources()
{
return $this->belongsToMany(Resource::class, 'role_resources', 'role_id', 'resource_id')->orderBy('sort', 'asc');
return $this->belongsToMany(Resource::class, 'role_resources', 'role_id', 'resource_id');
}
}
-69
View File
@@ -1,69 +0,0 @@
<?php
namespace app\common\model;
use support\Model;
/**
* user_feishu_app 用户-飞书应用
* @property integer $id ID(主键)
* @property integer $uid 用户ID
* @property integer $corp_id 公司ID
* @property string $name 名称
* @property string $mobile 手机
* @property string $union_id ISV的唯一标识
* @property string $user_id 用户 user_id
* @property string $open_id 应用内的唯一标识
* @property string $tenant_key 当前企业标识
* @property integer $sort 排序
* @property integer $status 状态
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class UserFeishuApp_bak extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'user_feishu_app';
/**
* 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 = [
'uid',
'corp_id',
'name',
'mobile',
'union_id',
'user_id',
'open_id',
'tenant_key',
'sort',
'status'
];
}
-47
View File
@@ -1,47 +0,0 @@
<?php
namespace app\common\model;
use support\Model;
/**
* user_roles 用户-角色关联
* @property integer $user_id 用户ID(主键)
* @property integer $role_id 角色ID(主键)
*/
class UserRoles extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'user_roles';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = null;
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
protected $fillable = [
'user_id',
'role_id'
];
}
-53
View File
@@ -1,53 +0,0 @@
<?php
namespace app\common\model;
use support\Model;
/**
* user_systems 用户-系统关联表:描述用户可访问哪些系统
* @property integer $user_id 用户ID(主键)
* @property integer $system_id 系统ID(主键)
* @property integer $created_at 创建时间
*/
class UserSystems extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'user_systems';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = null;
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
// protected $dateFormat = 'U';
// const UPDATED_AT = false;
protected $fillable = [
'user_id',
'system_id',
'created_at'
];
}
+1 -10
View File
@@ -31,7 +31,7 @@ class AuthService
return match ($loginType) {
'feishu' => $logic->feishuLogin($data),
// 'qrcode' => $logic->qrCodeLogin($data),
'qrcode' => $logic->qrCodeLogin($data),
default => $logic->uPwdLogin($data),
};
}
@@ -86,13 +86,4 @@ class AuthService
{
(new AuthLogic())->blockToken($token, $systemCode);
}
// public function perms()
// {
// $permService = new PermService();
// $menuTree = $permService->getMenuTree($uid);
// $permissionCodes = $permService->getPermissionCodes($uid);
// return (new PermService())->getMenuTree($this->userId)
// }
}
@@ -1,21 +0,0 @@
<?php
namespace app\common\service;
use app\common\logic\CorpFeishuAppLogic;
class CorpFeishuAppService
{
protected $logic;
public function __construct()
{
$this->logic = (new CorpFeishuAppLogic());
}
public function getFeishuApp(int $corpId)
{
return $this->logic->getFeishuApp($corpId);
}
}
-24
View File
@@ -1,24 +0,0 @@
<?php
namespace app\common\service;
use app\common\logic\CorpLogic;
class CorpService
{
protected $logic;
public function __construct()
{
$this->logic = (new CorpLogic());
}
/**
* 获取公司list
*/
public function getList()
{
return $this->logic->getList();
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace app\crm\controller;
use support\Request;
use support\Response;
use support\exception\BusinessException;
use app\common\traits\ResponseTrait;
use think\Validate;
abstract class BaseController
{
use ResponseTrait;
protected mixed $validator = null;
/**
* 验证数据
*
* @param Validate $validator 验证器实例
* @param array $data 待验证的数据
* @param string $scene 验证场景
* @return array 验证后的数据
* @throws BusinessException 验证失败时抛出业务异常
*/
public function validate(Validate $validator, array $data, string $scene = ''): array
{
// 如果指定了验证场景,并且验证器支持场景方法,则设置验证场景
if ($scene && method_exists($validator, 'scene')) {
$validator->scene($scene);
}
// 执行数据验证
if (!$validator->check($data)) {
// 验证失败,抛出业务异常,异常信息为验证器返回的错误信息
throw new BusinessException($validator->getError());
}
// 验证成功,返回原始数据
return $data;
}
public function page($result){
$current = $result->currentPage();
$total = $result->total();
$size = $result->perPage();
$result = $result->toArray();
$result['current'] = $current;
$result['total'] = $total;
$result['size'] = $size;
return $result;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace app\controller;
use app\crm\controller\BaseController;
use app\crm\model\CarType;
use app\crm\model\Customer;
use app\crm\utils\Util;
use PDO;
use support\Db;
use support\Redis;
use support\Request;
class CarTypeController extends BaseController
{
public function getAll(Request $request)
{
$list = CarType::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'];
}
}
return $this->success($list);
}
}
+519
View File
@@ -0,0 +1,519 @@
<?php
namespace app\crm\controller;
use app\common\model\User;
use app\crm\model\Channel;
use app\crm\model\Country;
use app\crm\model\Customer;
use app\crm\model\CustomerHis;
use app\crm\model\Follow;
use app\crm\service\CustomerService;
use app\crm\utils\Util;
use config\constants\Constants;
use support\Request;
class CustomerController extends BaseController
{
/**
* 客户分级
* @param Request $request
* @return \support\Response
*/
public function getGrade(Request $request)
{
$grade = [];
$gradeConstants = Constants::GRADE_EN;
if('zh_CN' == 'zh_CN'){
$gradeConstants = Constants::GRADE_ZH;
}elseif ('zh_CN' == 'ru'){
$gradeConstants = Constants::GRADE_RU;
}
foreach ($gradeConstants as $key => $value) {
$grade[] = [
'id' => $key,
'name' => $value,
];
}
return $this->success($grade);
}
/**
* 商机
* @param Request $request
* @return \support\Response
*/
public function getMagnitude(Request $request)
{
$grade = [];
$magnitudeConstants = Constants::MAGNITUDE_EN;
if('zh_CN' == 'zh_CN'){
$magnitudeConstants = Constants::MAGNITUDE;
}elseif ('zh_CN' == 'ru'){
$magnitudeConstants = Constants::MAGNITUDE_RU;
}
foreach ($magnitudeConstants as $key => $value) {
$grade[] = [
'id' => $key,
'name' => $value,
];
}
return $this->success($grade);
}
/**
* 客户渠道
* @param Request $request
* @return \support\Response
*/
public function getChannel(Request $request)
{
$list = Channel::getAll();
if('zh_CN' != 'zh_CN'){
foreach ($list as &$value) {
$value['name'] = $value['name_en'];
}
}elseif ('zh_CN' == 'ru'){
foreach ($list as &$value) {
$value['name'] = $value['name_ru'];
}
}
return $this->success($list);
}
/**
* 国家
* @param Request $request
* @return \support\Response
*/
public function getCountry(Request $request)
{
$list = Country::getAll();
if('zh_CN' != 'zh_CN'){
foreach ($list as &$value) {
$value['name'] = $value['name_en'];
}
}elseif ('zh_CN' == 'ru'){
foreach ($list as &$value) {
$value['name'] = $value['name_ru'];
}
}
return $this->success($list);
}
/**
* 待跟进客户列表
* @param Request $request
* @return \support\Response
*/
public function waitFollow(Request $request)
{
$userId = $request->user->uid;
$filter = Util::getParam($request, 'filter');
$page = $request->input('page', 1);
$perPage = $request->input('per_page', 10);
$where = [
['user_id', '=', $userId],
['corp_abbr', '=', $request->basic_data['corp_abbr']],
['follow_next', '<>', 0],
];
if($filter == 0){
$where[] = ['follow_next', '<=', time() + 3600 * 24];
} elseif ($filter == 1) {
$where[] = ['follow_next', '<=', time() + 3600 * 24 * 3];
} elseif ($filter == 2) {
$where[] = ['follow_next', '<=', time() + 3600 * 24 * 7];
}
$customer = Customer::where($where)
->whereNotNull('follow_next')
->orderBy('follow_next')
->paginate($perPage, ['*'], 'page', $page);
foreach ($customer as &$item) {
$item['follow_remaining'] = $item->follow_next-time();
$item['follow_next'] = date('Y-m-d H:i:s',$item->follow_next);
}
return $this->success($this->page($customer));
}
/**
* 客户列表
* @param Request $request
* @return \support\Response
*/
public function list(Request $request)
{
try {
$param = $request->all();
$param['user_id'] = $request->user->uid;
$param['corp_abbr'] = $request->basic_data['corp_abbr'];
$result = (new CustomerService())->list($param);
return $this->success($this->page($result));
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 客户恢复
* @param Request $request
* @return \support\Response
*/
public function restore(Request $request)
{
$id = Util::getParam($request,'id', '');
if (Util::isEmpty($id)) {
return $this->error(trans('id_empty'));
}
$userId = $request->user->uid;
$where = [
'id' => $id,
'user_id' => $userId,
];
$res = Customer::where($where)->restore();
if($res){
return $this->success($res, trans('success'));
}else{
return $this->error(trans('fail'));
}
}
/**
* 客户详情
* @param Request $request
* @return \support\Response
*/
public function detail(Request $request)
{
$id = Util::getParam($request, 'id');
if (Util::isEmpty($id)) {
return $this->error('id' . trans('not_null'));
}
// $res = Customer::with('tags')->select('id', 'user_id', 'grade', 'channel_id', 'car_type_id', 'country_id', 'magnitude', 'email', 'phone', 'WhatsApp', 'contacts', 'describe', 'address', 'lat', 'lng', 'follow_next', 'follow_remark', 'sex')
// ->where('id', $id)
// ->first();
$res = (new CustomerService)->detail($request->all());
if ($res) {
$res->toArray();
} else {
return $this->error('记录不存在');
}
$res = Customer::getCustomer($res, $request, true);
return $this->success($res);
}
/**
* 客户录入
* @param Request $request
* @return \support\Response
*/
public function save(Request $request)
{
$id = Util::getParam($request, 'id');
$channel_id = Util::getParam($request, 'channel_id');
if (Util::isEmpty($channel_id)) {
return $this->error(trans('channel_not_null'));
}
$country_id = Util::getParam($request, 'country_id', 0);
if (Util::isEmpty($country_id)) {
// return $this->error('country_id'.trans('not_null'));
}
$car_type_id = Util::getParam($request, 'car_type_id');
if (Util::isEmpty($car_type_id)) {
return $this->error(trans('car_type_not_null'));
}
$contacts = Util::getParam($request, 'contacts');
$email = Util::getParam($request, 'email');
$phone = Util::getParam($request, 'phone');
if (Util::isEmpty($contacts) && Util::isEmpty($email) && Util::isEmpty($phone)) {
return $this->error(trans('contacts_not_null'));
}
$data = [
'user_id' => $request->user->uid,
'corp_abbr' => $request->basic_data['corp_abbr'],
'grade' => Util::getParam($request, 'grade', 0),
'channel_id' => $channel_id,
'country_id' => $country_id,
'car_type_id' => $car_type_id,
'magnitude' => Util::getParam($request, 'magnitude', 0),
'email' => $email,
'phone' => $phone,
'WhatsApp' => Util::getParam($request, 'WhatsApp', ''),
'contacts' => $contacts,
'describe' => Util::getParam($request, 'describe'),
'lat' => Util::getParam($request, 'lat', 0),
'lng' => Util::getParam($request, 'lng', 0),
'address' => Util::getParam($request, 'address', ''),
'sex' => Util::getParam($request, 'sex', 0),
];
if (Util::isEmpty($id)) {
$res = Customer::create($data);
} else {
unset($data['grade']);
$res = Customer::where('id', $id)
->update($data);
// Log::info('update', ['res' => $res]);
(!$res) ?: $res = Customer::find($id)->toArray();
}
if ($res) {
return $this->success($res, trans('save_success'));
} else {
return $this->error(trans('save_fail'));
}
}
/**
* 更新下次跟进时间
* @param Request $request
* @return \support\Response
*/
public function followNext(Request $request)
{
$id = Util::getParam($request, 'id');
if (Util::isEmpty($id)) {
return $this->error('id' . trans('not_null'));
}
$data = [
'follow_next' => Util::getParam($request, 'follow_next', 0),
'follow_remark' => Util::getParam($request, 'follow_remark', 0),
'warn' => 0,
];
$res = Customer::where('id', $id)
->update($data);
if ($res) {
return $this->success($res, trans('save_success'));
} else {
return $this->error(trans('save_fail'));
}
}
/**
* 删除客户
* @param Request $request
* @return \support\Response
*/
public function delete(Request $request)
{
$id = Util::getParam($request, 'id');
$force = Util::getParam($request, 'force','');
$userId = $request->user->uid;
$where = [
'id' => $id,
'user_id' => $userId,
];
$customer = Customer::where($where)->withTrashed()->first();
if(empty($customer)){
return $this->error( trans('fail'));
}
if($force == 'force'){
$res = $customer->forceDelete();
}else{
$res = $customer->delete();
}
if ($res) {
return $this->success($res, trans('success'));
} else {
return $this->error(trans('fail'));
}
}
/**
* 统计客户
* @param Request $request
* @return \support\Response
*/
public function statistics(Request $request)
{
$created = Util::getParam($request, 'created');
if (Util::isEmpty($created)) {
return $this->error('created' . trans('not_null'));
}
$userId = $request->user->uid;
//$created_at = $request->user['created_at'];
$created_at = User::find($userId)->created_at;
$day = ceil((time() - $created_at + 1) / 60 / 60 / 24);
$where[] = ['user_id', '=', $userId];
$where[] = ['corp_abbr', '=', $request->basic_data['corp_abbr']];
$start = strtotime($created . '-01');
$year = date('Y', $start);
$month = date('n', $start); //获取月份
$end = mktime(00, 00, 00, $month + 1, 1, $year);
$where[] = ['created_at', '>=', $start];
$where[] = ['created_at', '<', $end];
$customer = Customer::where($where)->count();
//初步接触
$whereGrade = $where;
$whereGrade[] = ['grade', '<=', 2];
$customerInit = Customer::where($whereGrade)->count();
//跟进中
$customeroFllow = Customer::where($where)->whereIn('grade', [3, 4])->count();
//已成交
$whereGrade = $where;
$whereGrade[] = ['grade', '=', 5];
$customerDeal = Customer::where($whereGrade)->count();
//拜访数
$whereVisit = $where;
$whereVisit[] = ['type', '=', 0];
$visit = Follow::where($whereVisit)->distinct('customer_id')->count();
//跟进数
$whereVisit = $where;
$whereVisit[] = ['type', '=', 1];
$follow = Follow::where($whereVisit)->distinct('customer_id')->count();
$res = [
'customer' => $customer, //客户数
'visit' => $visit, //拜访
'follow' => $follow, //跟进
'day' => $day, //注册天数
'customer_init' => $customerInit, //初步接触
'customero_fllow' => $customeroFllow, //跟进中
'customer_deal' => $customerDeal, //已成交
];
if ($res) {
return $this->success($res, trans('success'));
} else {
return $this->error(trans('fail'));
}
}
/**
* 领取公海客户
* @param Request $request
* @return \support\Response
*/
public function claim(Request $request)
{
$customerId = Util::getParam($request, 'customer_id');
$userId = $request->user->uid;
if (Util::isEmpty($customerId)) {
return $this->error(trans('customer_id').trans('not_null'));
}
$data = [
'user_id' => $request->user->uid
];
$customer= Customer::withTrashed()->find($customerId);
if(empty($customer) || $customer->user_id != 0){
return $this->error( trans('claim_fail'));
}
$customer->restore();
$res = $customer->update($data);
//记录客户关系变化
$his = CustomerHis::create([
'customer_id' => $customerId,
'user_his' => 0,
'user_id' => $userId,
]);
if ($res) {
return $this->success($res, trans('success'));
} else {
return $this->error(trans('fail'));
}
}
/**
* 批量转让
* @param Request $request
* @return \support\Response
*/
public function cede(Request $request)
{
$customerId = Util::getParam($request, 'customer_id');
$user_sn = Util::getParam($request, 'user_sn');
$userId = $request->user->uid;
if (Util::isEmpty($user_sn)) {
return $this->error('ID'.trans('not_null'));
}
$user = User::where(['sn' => $user_sn])->first();
if (Util::isEmpty($user)) {
return $this->error( trans('user_none'));
}
if(Util::isEmpty($customerId)){
$data = [
'user_id' => $userId
];
$query= Customer::where($data);
}else{
$query= Customer::whereIn('id',explode(',',$customerId));
}
$customer = $query->get();
foreach ($customer as $item) {
//记录客户关系变化
$his = CustomerHis::create([
'customer_id' => $item->id,
'user_his' => $item->user_id,
'user_id' => $user['id'],
]);
}
$res = $query->update(['user_id' => $user->id]);
if ($res) {
return $this->success($res, trans('success'));
} else {
return $this->error(trans('fail'));
}
}
/**
* 跟进状态
* @param Request $request
* @return \support\Response
*/
public function grade(Request $request)
{
$id = Util::getParam($request, 'id');
$grade = Util::getParam($request, 'grade');
if (Util::isEmpty($id)) {
return $this->error( 'id' . trans('not_null'));
}
if (Util::isEmpty($id)) {
return $this->error( 'grade' . trans('not_null'));
}
$data = [
'grade' => $grade,
];
$res = Customer::where('id', $id)
->update($data);
if ($res) {
return $this->success($res, trans('save_success'));
} else {
return $this->error(trans('save_fail'));
}
}
}
+141
View File
@@ -0,0 +1,141 @@
<?php
namespace app\crm\controller;
use app\crm\model\Customer;
use app\crm\model\Follow;
use app\crm\service\CustomerService;
use app\crm\service\FollowService;
use app\crm\utils\Util;
use app\crm\validate\FollowValidate;
use config\constants\Constants;
use support\Request;
class FollowController extends BaseController
{
/**
* 客户分级
* @param Request $request
* @return \support\Response
*/
public function getFollowType(Request $request)
{
$grade = [];
foreach (Constants::FOLLOW as $key => $value) {
$grade[] = [
'status' => $key,
'text' => $value,
];
}
return $this->success($grade);
}
/**
* 跟进列表
* @param Request $request
* @return \support\Response
*/
public function list(Request $request)
{
try {
$data = $this->validate(new FollowValidate(), $request->get(), 'list');
$request->setGet('corp_abbr', $request->basic_data['corp_abbr']);
$result = (new FollowService())->list($request->all(), $request->user->uid);
return $this->success($result);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 跟进详情
* @param Request $request
* @return \support\Response
*/
public function detail(Request $request)
{
//跟进id
$id = Util::getParam($request, 'id');
if (Util::isEmpty($id)) {
return $this->error('id' . trans('not_null'));
}
$res = Follow::select('id', 'user_id', 'customer_id', 'type', 'picture', 'remark', 'audio', 'audio_text', 'lat', 'lng', 'address')
->where('id', $id)
->first();
if ($res) {
$res->toArray();
} else {
return $this->error('记录不存在');
}
$res = FollowService::getFollow($res);
return $this->success($res);
}
/**
* 客户录入
* @param Request $request
* @return \support\Response
*/
public function save(Request $request)
{
$customer_id = Util::getParam($request, 'customer_id');
if (Util::isEmpty($customer_id)) {
return $this->error('customer_id' . trans('not_null'));
}
$type = Util::getParam($request, 'type');
if (Util::isEmpty($type)) {
return $this->error('type' . trans('not_null'));
}
$picture = Util::getParam($request, 'picture');
if (Util::isEmpty($picture)) {
return $this->error(trans('picture_not_null'));
}
$data = [
'user_id' => $request->user->uid,
'corp_abbr' => $request->basic_data['corp_abbr'],
'customer_id' => $customer_id,
'type' => $type,
'picture' => $picture,
'remark' => Util::getParam($request, 'remark', ''),
'audio' => Util::getParam($request, 'audio', ''),
'audio_text' => Util::getParam($request, 'audio_text', ''),
'lat' => Util::getParam($request, 'lat', 0),
'lng' => Util::getParam($request, 'lng', 0),
'address' => Util::getParam($request, 'address'),
'follow_time' => strtotime(Util::getParam($request, 'firstFollowtime', 0)),
];
$res = Follow::create($data);
$customer = Customer::find($customer_id);
$customer->follow_last = strtotime($res->created_at);
$customer->follow_next = null;
$customer->warn = 0;
$customer->save();
$follow_next = Util::getParam($request, 'follow_next', 0);
$follow_remark = Util::getParam($request, 'follow_remark', 0);
if ($res && (!empty($follow_next))) {
$data = [
'follow_next' => $follow_next,
'follow_remark' => $follow_remark,
'warn' => 0,
];
$res = Customer::where('id', $customer_id)
->update($data);
}
if ($res) {
return $this->success($res, trans('save_success'));
} else {
return $this->error(trans('save_fail'));
}
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace app\crm\controller;
use app\crm\controller\BaseController;
use app\crm\model\CustomerTag;
use app\crm\model\TagGroup;
use app\crm\utils\Util;
use config\constants\Constants;
use support\Request;
class TagGroupController extends BaseController
{
// 列表
public function list(Request $request)
{
$list = TagGroup::with('tags')->get();
$languageArr = Constants::LANGUAGE;
$language = $request->user['language'];
foreach ($list as &$item) {
$item['names'] = unserialize($item['names']);
if (in_array($language, $languageArr)) {
$item['name'] = $item['names'][$language];
}
foreach ($item['tags'] as &$tag) {
$tag['names'] = unserialize($tag['names']);
if (in_array($language, $languageArr)) {
$tag['name'] = $tag['names'][$language];
}
}
}
return $this->success($list);
}
/**
* 更新客户标签关系
* @param Request $request
* @return \support\Response
*/
public function updatePivot(Request $request)
{
$customerId = Util::getParam($request, 'customer_id');
$groupId = Util::getParam($request, 'group_id');
$tagId = Util::getParam($request, 'tag_id');
if (Util::isEmpty($customerId)) {
return $this->error( trans('customer_id') . trans('not_null'));
}
if (Util::isEmpty($groupId)) {
return $this->error( trans('tag_group_id') . trans('not_null'));
}
if(empty($tagId)){
$res = CustomerTag::where(['customer_id' => $customerId, 'group_id' => $groupId])->delete();
}else{
$res = CustomerTag::updateOrCreate(['customer_id' => $customerId, 'group_id' => $groupId], ['tag_id' => $tagId]);
}
if($res){
return $this->success($res, trans('success'));
}else{
return $this->error( trans('fail'));
}
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace app\crm\logic;
use app\crm\model\Customer;
class CustomerLogic
{
public function find(int $id)
{
$customer = Customer::find($id);
return $customer;
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace app\crm\logic;
class FollowLogic
{
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace app\crm\model;
use support\Model;
class CarType extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'car_type';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
public static function getAll()
{
$list = self::get()->select('id','name','name_en','status')->where('status',0)->toArray();
return $list;
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace app\crm\model;
use support\Model;
class Channel extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'inquiry_channel';
/**
* 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 $casts = [
// 'updated_at' => 'datetime:Y-m-d H:i:s', // 指定序列化格式
// 'created_at' => 'datetime:Y-m-d H:i:s', // 指定序列化格式
'updated_at' => 'timestamp', // 指定序列化格式
'created_at' => 'timestamp', // 指定序列化格式
];
public static function getAll()
{
$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\crm\model;
use support\Model;
class Country extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'country';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
//设置黑名单
protected $guarded = ['id'];
// 指定日期字段存储格式为时间戳
protected $dateFormat = 'U';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
public static function getAll()
{
$list = self::select('id','cn_addr','en_addr')->get();
return $list;
}
}
+137
View File
@@ -0,0 +1,137 @@
<?php
namespace app\crm\model;
use app\crm\utils\Util;
use config\constants\Constants;
use DateTimeInterface;
use Illuminate\Support\Carbon;
use support\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use app\common\model\User;
class Customer extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'crm_customer';
/**
* 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;
use SoftDeletes;
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());
}
/**
* @param $customer
* @param $request
* @param $tag 是否返回客户标签
* @return mixed
*/
public static function getCustomer($customer, $request, $tag = false)
{
$language = 'zh_CN' ?: 'en';
$customer['grade_txt'] = Util::language($language, Constants::GRADE_EN[$customer['grade']], Constants::GRADE_ZH[$customer['grade']], Constants::GRADE_RU[$customer['grade']]);
$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']]);
if ($customer['grade']<=2) {
$customer['follow'] = 0;
$customer['follow_txt'] = Util::language($language,'Following','跟进中','Продолжение');
}elseif ($customer['grade']<5) {
$customer['follow'] = 1;
$customer['follow_txt'] = Util::language($language,'transacted','已成交','Транзакция завершена');
}else{
$customer['follow'] = 2;
$customer['follow_txt'] = Util::language($language,'lost','已流失','Потерянный');
}
$where = [
'customer_id' => $customer['id']
];
$begin_follow = Follow::select('id','created_at','updated_at')
->where($where)
->min('created_at');
$customer['begin_follow'] = Util::timeTotDate($begin_follow);
// $customer['created_at'] = Util::timeTotDate($customer['created_at']);
// $customer['updated_at'] = Util::timeTotDate($customer['updated_at']);
$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);
$customer['channel_name'] = empty($customer['channel_id'])?'':Channel::where(['id'=>$customer['channel_id']])->value($nameLang);
// $customer['last_follow'] = $last_follow;
if ($tag) {
foreach ($customer['tags'] as &$tag) {
$tag['names'] = unserialize($tag['names']);
$tag['name'] = $tag['names'][$language];
}
}
return $customer;
}
/**
* 客户的标签
*/
public function tags()
{
return $this->belongsToMany(Tag::class, 'crm_customer_tag', 'customer_id', 'tag_id')
->withTimestamps(); // 如果需要记录关联时间
}
// 定义与 变更历史 的一对多关系
public function his()
{
return $this->hasMany(CustomerHis::class, 'customer_id', 'id');
}
// 定义反向多对一关联
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
namespace app\crm\model;
use app\crm\utils\Util;
use config\constants\Constants;
use DateTimeInterface;
use Illuminate\Support\Carbon;
use support\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class CustomerHis extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'crm_customer_his';
/**
* 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', // 指定序列化格式
// 'created_at' => 'timestamp', // 指定序列化格式
];
/**
* 重新格式化时间
* @param DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date): string
{
return $date->format(Carbon::parse($date)->toDateTimeString());
}
// 定义反向多对一关联
public function customer()
{
return $this->belongsTo(Customer::class, 'customer_id', 'id');
}
// 禁用 updated_at 的自动更新
public function setUpdatedAt($value)
{
return $this; // 不做任何操作
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace app\crm\model;
use app\crm\utils\Util;
use support\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class CustomerTag extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'crm_customer_tag';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
//设置黑名单
protected $guarded = ['id', 'created_at'];
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace app\crm\model;
use app\service\OssService;
use app\crm\utils\Util;
use config\constants\Constants;
use support\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Follow extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'crm_follow';
/**
* 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 = ['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', // 指定序列化格式
'follow_time' => 'datetime:Y-m-d H:i:s', // 指定序列化格式
// 'updated_at' => 'timestamp', // 指定序列化格式
// 'created_at' => 'timestamp', // 指定序列化格式
];
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace app\crm\model;
use app\util\Util;
use config\constants\Constants;
use DateTimeInterface;
use Illuminate\Support\Carbon;
use support\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Tag extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'crm_tag';
/**
* 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;
use SoftDeletes;
protected $dates = ['deleted_at'];
// 定义反向多对一关联
public function group()
{
return $this->belongsTo(TagGroup::class, 'group_id', 'id');
}
public function customers()
{
return $this->belongsToMany(Customer::class, 'crm_customer_tag', 'tag_id', 'customer_id');
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace app\crm\model;
use app\crm\utils\Util;
use config\constants\Constants;
use DateTimeInterface;
use Illuminate\Support\Carbon;
use support\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class TagGroup extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'crm_tag_group';
/**
* 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;
use SoftDeletes;
protected $dates = ['deleted_at'];
// 定义与 Tag 的一对多关系
public function tags()
{
return $this->hasMany(Tag::class, 'group_id', 'id');
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace app\crm\service;
use app\crm\logic\CustomerLogic;
use app\crm\model\Customer;
class CustomerService
{
public function list($params)
{
// $page = $request->input('page', 1);
// $perPage = $request->input('per_page', 10);
$perPage = $params['size'] ?? 10;
$page = $params['current'] ?? 1;
$where = [];
$user_id =$params['user_id'];
if(empty($user_id) || $user_id == 0){
$where[] = ['user_id', '=', $user_id];
}
$user = Customer::where($where)->paginate($perPage, ['*'], 'page', $page);
// $user = Customer::with('user')->where($where)->paginate($perPage, ['*'], 'page', $page);
// $user = $user->toArray();
// foreach ($user['data'] as &$item) {
// unset($item['password']);
// }
return $user;
}
public function detail($params)
{
$id =$params['id'];
$customer = Customer::with('tags')->select('id', 'user_id', 'grade', 'channel_id', 'car_type_id', 'country_id', 'magnitude', 'email', 'phone', 'WhatsApp', 'contacts', 'describe', 'address', 'lat', 'lng', 'follow_next', 'follow_remark', 'sex')
->where('id', $id)
->first();
return $customer;
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace app\crm\service;
use app\common\service\OssService;
use app\crm\model\Follow;
use app\crm\utils\Util;
use config\constants\Constants;
class FollowService
{
public function list($param, $userId)
{
// $page = $request->input('page', 1);
// $perPage = $request->input('per_page', 10);
//客户id
$customer_id = $param['customer_id'];
$corp_abbr = $param['corp_abbr'];
$where = [];
$where['customer_id'] = $customer_id;
$where['corp_abbr'] = $corp_abbr;
//用户角色
//跟进id
$id = $param['id'] ?? null;
if (!Util::isEmpty($id)) {
$where['id'] = $id;
}
// 执行分页查询
$data = Follow::where($where)
->orderBy('id', 'desc')->get();
// ->paginate($perPage, ['*'], 'page', $page);
// $host = $request->host();
// $url = 'http:'.$request->url();
foreach ($data as &$value) {
$value = self::getFollow($value);
}
return $data;
}
public static function getFollow($follow)
{
$follow['type_txt'] = Constants::FOLLOW[$follow['type']];
$follow['firstFollowtime'] = $follow['follow_time'];
$oss_picture = '';
if (!empty($follow['picture'])) {
$ossservice = new OssService();
$oss_picture = $ossservice->generatePrivateUrl($follow['picture'], 86400);
}
$follow['oss_picture'] = $oss_picture;
// $follow = Util::formatDate($follow);
return $follow;
}
}
+121
View File
@@ -0,0 +1,121 @@
<?php
namespace app\crm\utils;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use support\exception\BusinessException;
use support\Log;
use support\Request;
class Util
{
/**
* 参数是否为空
*/
public static function isEmpty($param)
{
if(!empty($param) || $param == 0){
$result = false;
}else{
$result = true;
}
return $result;
}
/**
* 参数是否为空
*/
public static function getParam(Request $request,$param,$default='')
{
$param = $request->input($param,$default);
if(empty($param) && $default!==''){
$param = $default;
}
return $param;
}
/**
* 失败
*/
public static function fail($data = [],$message = '', $code = -1)
{
$result = [
'code' => $code,
'message' => $message,
'data' => $data
];
return json($result);
}
/**
* 成功
*/
public static function success($data = [],$message = '', $code = 0)
{
$result = [
'code' => $code,
'message' => $message,
'data' => $data
];
return json($result);
}
/**
* 分页查询返回
*/
public static function page($data,$message = '', $code = 0)
{
$result = [
'code' => $code,
'message' => $message
];
$result['data'] = [
'list' => $data->items(), // 当前页数据
'total' => $data->total(), // 总记录数
'current_page' => $data->currentPage(),
'per_page' => $data->perPage(),
'last_page' => $data->lastPage()
];
return json($result);
}
/**
* 日期格式转化
*/
public static function timeTotDate($time = 0, $format = 'Y-m-d H:i:s')
{
if (empty($time)) {
return '';
}
return date($format, $time);
}
/**
* 格式化创建时间
*/
public static function formatDate($value,$key = ['created_at','updated_at'])
{
foreach ($key as $k){
$value[$k]= self::timeTotDate($value[$k]);
}
return $value;
}
/**
* 格式化创建时间
*/
public static function language($language,$en,$zh_CN,$ru)
{
if ($language == 'zh_CN'){
return $zh_CN;
}elseif ($language == 'ru'){
return $ru;
}
return $en;
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace app\crm\validate;
use think\Validate;
class FollowValidate extends Validate
{
protected $rule = [
'customer_id' => 'require',
];
protected $message = [
'customer_id.require' => '用户id不能为空',
];
protected $scene = [
'list' => ['customer_id'],
];
}
+100
View File
@@ -34,4 +34,104 @@ class Constants
self::TICKET_STATUS['REJECTED'] => 'Закрыть',
];
//客户分级 USTOMER_GRADE
public const GRADE = [
'INITIAL' => 0, // 初步接触
'COMMUNICATE' => 1, // 需求沟通
'PLAN' => 2, // 方案推进
'DEAL' => 3, // 成交阶段
'SALES' => 4, // 售后阶段
'LOSS' => 5, // 停滞或流失
];
public const GRADE_ZH = [
self::GRADE['INITIAL'] => '初步接触',
self::GRADE['COMMUNICATE'] => '需求沟通',
self::GRADE['PLAN'] => '方案推进',
self::GRADE['DEAL'] => '成交阶段',
self::GRADE['SALES'] => '售后阶段',
self::GRADE['LOSS'] => '停滞或流失',
];
public const GRADE_EN = [
self::GRADE['INITIAL'] => 'Initial contact',
self::GRADE['COMMUNICATE'] => 'Demand Chat',
self::GRADE['PLAN'] => 'promotion of the plan',
self::GRADE['DEAL'] => 'Transaction Stage',
self::GRADE['SALES'] => 'After Sales',
self::GRADE['LOSS'] => 'Stagnation or loss',
];
public const GRADE_RU = [
self::GRADE['INITIAL'] => 'Первоначальные контакты',
self::GRADE['COMMUNICATE'] => 'Потребности в общении',
self::GRADE['PLAN'] => 'Продвижение программы',
self::GRADE['DEAL'] => 'Этап сделки',
self::GRADE['SALES'] => 'Послепродажный этап',
self::GRADE['LOSS'] => 'Застой или потеря',
];
//客户重要性
public const CUSTOMER_MAGNITUDE_INITIAL = 0; // 金牌商机
public const CUSTOMER_MAGNITUDE_COMMUNICATE = 1; // 银牌商机
public const CUSTOMER_MAGNITUDE_PLAN = 2; // 铜牌商机
public const MAGNITUDE = [
self::CUSTOMER_MAGNITUDE_INITIAL => '金牌商机',
self::CUSTOMER_MAGNITUDE_COMMUNICATE => '银牌商机',
self::CUSTOMER_MAGNITUDE_PLAN => '铜牌商机',
];
public const MAGNITUDE_EN = [
self::CUSTOMER_MAGNITUDE_INITIAL => 'Gold Medal',
self::CUSTOMER_MAGNITUDE_COMMUNICATE => 'Silver Medal',
self::CUSTOMER_MAGNITUDE_PLAN => 'Bronze Medal',
];
public const MAGNITUDE_RU = [
self::CUSTOMER_MAGNITUDE_INITIAL => 'Золотой бизнес',
self::CUSTOMER_MAGNITUDE_COMMUNICATE => 'Серебряный бизнес',
self::CUSTOMER_MAGNITUDE_PLAN => 'Бронзовый бизнес.',
];
//跟进类型
public const CUSTOMER_FOLLOW_VISIT = 0; // 线下拜访
public const CUSTOMER_FOLLOW_UP = 1; // 线上跟进
public const FOLLOW = [
self::CUSTOMER_FOLLOW_VISIT => '线下拜访',
self::CUSTOMER_FOLLOW_UP => '线上跟进',
];
public const FOLLOW_EN = [
self::CUSTOMER_FOLLOW_VISIT => 'Visit',
self::CUSTOMER_FOLLOW_UP => 'On line',
];
//跟进类型
public const CUSTOMER_SEX_UNKNOWN = 0; // 未知
public const CUSTOMER_SEX_MAN = 1; // 男
public const CUSTOMER_SEX_WOMAN = 2; // 女
public const SEX = [
self::CUSTOMER_SEX_UNKNOWN => '未知',
self::CUSTOMER_SEX_MAN => '男',
self::CUSTOMER_SEX_WOMAN => '女',
];
public const SEX_EN = [
self::CUSTOMER_SEX_UNKNOWN => 'unknown',
self::CUSTOMER_SEX_MAN => 'man',
self::CUSTOMER_SEX_WOMAN => 'woman',
];
public const SEX_RU = [
self::CUSTOMER_SEX_UNKNOWN => 'неизвестный',
self::CUSTOMER_SEX_MAN => 'мужской',
self::CUSTOMER_SEX_WOMAN => 'женский',
];
public const SIGN = [
['id' => 'xinyouhe', 'name' => '新友合'],
['id' => 'mbaf', 'name' => 'MAGIC BRIDGE AUTO FZE']
];
public const LANGUAGE = ['en', 'zh_CN','ru'];
}
+22 -2
View File
@@ -25,8 +25,6 @@ use Webman\Route;
// POST /admin/user/{user_id}/kickout // body: {"tokens": ["..."]} // 踢下线
// POST /admin/user/{user_id}/kickout_all // 一键踢全部
// 调试
Route::any('/perm', [app\controller\IndexController::class, 'perm']);
Route::group('/common', function () {
Route::group('/auth', function () {
@@ -66,7 +64,10 @@ Route::group('/ticket', function () {
Route::any('/ticket/add', [app\ticket\controller\TicketController::class, 'add']);
Route::any('/ticket/edit', [app\ticket\controller\TicketController::class, 'edit']);
Route::any('/ticket/list', [app\ticket\controller\TicketController::class, 'index']);
<<<<<<< HEAD
Route::any('/ticket/self', [app\ticket\controller\TicketController::class, 'self']);
=======
>>>>>>> feature/crmAdmin0715
Route::any('/ticket/status', [app\ticket\controller\TicketController::class, 'status']);
Route::any('/ticket/getStatus', [app\ticket\controller\TicketController::class, 'getStatus']);
@@ -87,3 +88,22 @@ Route::group('/ticket', function () {
});
//crm
Route::group('/crm', function () {
//跟进
Route::any('/follow/list', [app\crm\controller\FollowController::class, 'list']);
Route::any('/follow/detail', [app\crm\controller\FollowController::class, 'detail']);
Route::any('/follow/add', [app\crm\controller\FollowController::class, 'save']);
Route::any('/follow/getFollowType', [app\crm\controller\FollowController::class, 'getFollowType']);
//客户
Route::any('/customer/list', [app\crm\controller\CustomerController::class, 'list']);
Route::any('/customer/detail', [app\crm\controller\CustomerController::class, 'detail']);
Route::any('/customer/add', [app\crm\controller\CustomerController::class, 'save']);
Route::any('/customer/claim', [app\crm\controller\CustomerController::class, 'claim']);
Route::any('/customer/restore', [app\crm\controller\CustomerController::class, 'restore']);
Route::any('/customer/delete', [app\crm\controller\CustomerController::class, 'delete']);
Route::any('/customer/waitFollow', [app\crm\controller\CustomerController::class, 'waitFollow']);
Route::any('/customer/getChannel', [app\crm\controller\CustomerController::class, 'getChannel']);
});
+31
View File
@@ -2,4 +2,35 @@
return [
'status_not_modified' => 'الدولة لا يمكن تعديلها عكسيا',
'success' => ' النجاح',
'fail' => ' الفشل',
'login_success' => ' تم تسجيل الدخول بنجاح',
'password_error' => ' كلمة المرور غير صحيحة',
'user_none' => ' المستخدم غير موجود',
'user_id' => ' معرف المستخدم',
'user_login' => ' الرجاء تسجيل الدخول أولاً',
'not_null' => ' لا يمكن أن يكون فارغًا',
'code_error' => ' رمز التحقق غير صحيح',
'format_error' => ' تنسيق غير صحيح',
'email_repeat' => ' البريد الإلكتروني مكرر',
'email_error' => ' تنسيق البريد الإلكتروني غير صحيح',
'save_success' => ' تم الحفظ بنجاح',
'save_fail' => ' فشل الحفظ',
'register_success' => ' تم التسجيل بنجاح',
'register_fail' => ' فشل التسجيل',
'language_success' => ' تم تغيير اللغة بنجاح',
'language_fail' => ' فشل تغيير اللغة',
'param_error' => ' خطأ في المعلمات',
'required_not_null' => ' الحقول المطلوبة لا يمكن أن تكون فارغة',
'picture_not_null' => ' لا يمكن أن تكون الصورة فارغة',
'contacts_not_null' => ' لا يمكن أن يكون جهة الاتصال أو البريد الإلكتروني أو الهاتف فارغين جميعًا في نفس الوقت',
'car_type_not_null' => ' لا يمكن أن تكون التفضيلات فارغة',
'channel_not_null' => ' لا يمكن أن تكون القناة فارغة',
'email_consistent' => ' البريد الإلكتروني غير متطابق',
'sign_not_null' => ' لم يتم تعيين الشركة التابعة',
'password_short' => ' لا يمكن أن تكون كلمة المرور أقل من 6 أحرف',
'id_empty' => ' لا يمكن أن يكون المعرف فارغًا',
'customer_id' => ' معرف العميل',
'tag_group_id' => ' معرف مجموعة العلامات',
'claim_fail' => ' لا يمكن استلام العملاء الذين لديهم مالك بالفعل',
];
+31
View File
@@ -2,4 +2,35 @@
return [
'status_not_modified' => 'The status cannot be reversed for modification',
'success' => ' success',
'fail' => ' fail',
'login_success' => ' login success',
'password_error' => ' password error',
'user_none' => ' user none',
'user_id' => ' user ID',
'user_login' => ' Please log in first',
'not_null' => ' not null',
'code_error' => ' code error',
'format_error' => ' format error',
'email_repeat' => ' email repeat',
'email_error' => ' Email format error',
'save_success' => ' save success',
'save_fail' => ' save fail',
'register_success' => ' register success',
'register_fail' => ' register fail',
'language_success' => ' Successfully switched languages',
'language_fail' => ' Language switching failed',
'param_error' => ' param error',
'required_not_null' => ' required not_ ull',
'picture_not_null' => ' picture not null',
'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',
'email_consistent' => ' Inconsistent email addresses',
'sign_not_null' => ' No affiliated company has been set',
'password_short' => ' Password cannot be less than 6 characters',
'id_empty' => ' ID cannot be empty',
'customer_id' => ' customer ID',
'tag_group_id' => ' tag group ID',
'claim_fail' => ' Cannot claim ownership of customers',
];
+31
View File
@@ -2,4 +2,35 @@
return [
'status_not_modified' => 'Статус не может быть изменен в обратном направлении',
'success' => ' Успех',
'fail' => ' Неудача',
'login_success' => ' Регистрация завершена',
'password_error' => ' Ошибка пароля',
'user_none' => ' Пользователь не существует',
'user_id' => ' Идентификатор пользователя',
'user_login' => ' Пожалуйста, войдите.',
'not_null' => ' Нельзя быть пустым',
'code_error' => ' Ошибка кода проверки',
'format_error' => ' Ошибка формата',
'email_repeat' => ' Почтовый ящик повторяется',
'email_error' => ' Ошибка формата почтового ящика',
'save_success' => ' Сохранить успешно',
'save_fail' => ' Ошибка сохранения',
'register_success' => ' Регистрация успешно',
'register_fail' => ' Ошибка регистрации',
'language_success' => ' Переключить язык успешно',
'language_fail' => ' Ошибка переключения языка',
'param_error' => ' Ошибка параметра',
'required_not_null' => ' Обязательное заполнение не может быть пустым.',
'picture_not_null' => ' Фотографии не могут быть пустыми',
'contacts_not_null' => ' Контакты, почтовый ящик, телефон не может быть одновременно пустым',
'car_type_not_null' => ' Предпочтения не могут быть пустыми',
'channel_not_null' => ' Каналы не могут быть пустыми',
'email_consistent' => ' Почтовый ящик не совпадает',
'sign_not_null' => ' Компания не создана',
'password_short' => ' Пароль не может быть меньше 6.',
'id_empty' => ' ID не может быть пустым',
'customer_id' => ' Идентификатор клиента',
'tag_group_id' => ' Идентификатор группы меток',
'claim_fail' => ' Невозможность получения принадлежащего клиенту',
];
+31
View File
@@ -2,4 +2,35 @@
return [
'status_not_modified' => '状态不能逆向修改',
'success' => ' 成功',
'fail' => ' 失败',
'login_success' => ' 登录成功',
'password_error' => ' 密码错误',
'user_none' => ' 用户不存在',
'user_id' => ' 用户ID',
'user_login' => ' 请先登录',
'not_null' => ' 不能为空',
'code_error' => ' 验证码错误',
'format_error' => ' 格式错误',
'email_repeat' => ' 邮箱重复',
'email_error' => ' 邮箱格式错误',
'save_success' => ' 保存成功',
'save_fail' => ' 保存失败',
'register_success' => ' 注册成功',
'register_fail' => ' 注册失败',
'language_success' => ' 切换语言成功',
'language_fail' => ' 切换语言失败',
'param_error' => ' 参数错误',
'required_not_null' => ' 必填项不能为空',
'picture_not_null' => ' 图片不能为空',
'contacts_not_null' => ' 联系人,邮箱,电话不能同时为空',
'car_type_not_null' => ' 偏好不能为空',
'channel_not_null' => ' 渠道不能为空',
'email_consistent' => ' 邮箱不一致',
'sign_not_null' => ' 未设置所属公司',
'password_short' => ' 密码不能少于6位',
'id_empty' => ' id不能为空',
'customer_id' => ' 客户ID',
'tag_group_id' => ' 标签组ID',
'claim_fail' => ' 不能领取有归属客户',
];