Merge branch 'develop' into feature/mzl_dev_curd

This commit is contained in:
mzl
2025-07-21 15:10:14 +08:00
parent 45679bc5e4
commit 149de9d6fb
35 changed files with 2112 additions and 16 deletions
+53
View File
@@ -22,4 +22,57 @@ class UserController extends BaseController
}
}
/**
* 用户信息
*/
public function getUser(Request $request)
{
try {
$uid = $request->user->uid;
$corp_abbr = $request->basic_data['corp_abbr'];
$result = (new UserService())->getUser($uid,$corp_abbr);
return $this->success($result, '获取用户信息');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 编辑用户
*/
public function edit(Request $request)
{
try {
$uid = $request->user->uid;
$data = $request->all();
$result = (new UserService())->edit($uid,$data);
unset($result['password']);
unset($result['union_id']);
unset($result['open_id']);
unset($result['tenant_key']);
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 修改密码
* @param Request $request
* @return \support\Response
* @throws \Random\RandomException
*/
public function savePassword(Request $request)
{
try {
$uid = $request->user->uid;
$data = $request->all();
$result = (new UserService())->savePassword($uid,$data);;
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
+1 -1
View File
@@ -102,7 +102,7 @@ class PermLogic
// component 字段规则:
// 1. 存在子菜单,固定 '/index/index'
// 2. 否则为当前完整路径(或其它你想要的)
$componentValue = $hasChildrenMenu ? '/index/index' : $currentPath;
$componentValue = ($hasChildrenMenu && !$node->url && $node->type !== 'directory') ? '/index/index' : $currentPath;
$tree[] = [
'id' => $node->id,
+29
View File
@@ -24,4 +24,33 @@ class UserLogic
return $user;
}
/**
* 用户信息
*/
public function edit(int $uid, array $data)
{
$user = User::find($uid);
if (!$user) {
throw new BusinessException('账户不存在', 404);
}
$user->fill($data);
$user->save();
return $user;
}
/**
* 修改密码
*/
public function savePassword(int $uid, array $data)
{
$password = password_hash($data['password_new'], PASSWORD_DEFAULT);
$res = User::where('id',$uid)->update(['password' => $password]);
return $res;
}
}
+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');
}
}
+6 -1
View File
@@ -54,6 +54,7 @@ class User extends Model
'sn',
'name',
'avatar',
'sex',
'password',
'phone',
'email',
@@ -63,7 +64,11 @@ class User extends Model
'union_id',
'user_id',
'open_id',
'tenant_key'
'tenant_key',
'country_id',
'position',
'socialize',
'socialize_pic',
];
// 用户所有角色
+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)
// }
}
+62
View File
@@ -3,6 +3,10 @@
namespace app\common\service;
use app\common\logic\UserLogic;
use app\common\model\Corp;
use app\common\model\User;
use app\crm\model\Country;
use support\exception\BusinessException;
class UserService
{
@@ -37,4 +41,62 @@ class UserService
'permissions' => $permissionCodes
];
}
/**
* 获取用户信息(包括菜单和权限)
*/
public function getUser(int $uid, $corp_abbr)
{
$user = $this->logic->userInfo($uid);
$corp_name = Corp::where('abbr', $corp_abbr)->value('name');
$ossservice = new OssService();
return [
'id' => $user->id,
'name' => $user->name,
'position' => $user->position,
'socialize' => $user->socialize,
'socialize_pic' => $user->socialize_pic,
'socialize_pic_full' => (!empty($user->socialize_pic))?$ossservice->generatePrivateUrl($user->socialize_pic):'',
'country_id' => $user->country_id,
'country_txt' => Country::where('id',$user->country_id)->value('cn_addr'),
'avatar' => $user->avatar,
'avatar_full' => (!empty($user->avatar))?$ossservice->generatePrivateUrl($user->avatar):'',
'phone' => $user->phone,
'corp_name' => $corp_name,
'email' => $user->email,
'status' => $user->status,
'roles' => $user->roles->pluck('name')
];
}
/**
* 编辑用户信息
*/
public function edit(int $uid, array $data)
{
unset($data['password']);
unset($data['phone']);
unset($data['email']);
unset($data['sn']);
$user = $this->logic->edit($uid,$data);
return $user;
}
/**
* 修改用户密码
*/
public function savePassword(int $uid, array $data)
{
// 对密码进行哈希处理
$password = $data['password'];
$user = User::find($uid);
if (!password_verify($password, $user->password)) {
throw new BusinessException('密码错误');
}
$user = $this->logic->savePassword($uid,$data);
return $user;
}
}
+10
View File
@@ -64,4 +64,14 @@ class IndexController
return json(['code' => 1, 'msg' => $e->getMessage()]);
}
}
// test function
public function test(Request $request)
{
$user = User::find(1);
$user->roles()->sync([1]);
$user->systems()->sync([1]);
return json(['code' => 0, 'msg' => 'success']);
}
}
+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);
}
}
+527
View File
@@ -0,0 +1,527 @@
<?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
{
/**
* 不需要头信息的方法
*/
protected $noNeedHeader = [];
protected $noNeedLogin = ['getChannel', 'getCountry'];
/**
* 客户分级
* @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().$e->getFile().$e->getLine());
}
}
/**
* 客户恢复
* @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 = CustomerService::getCustomer($res, 'zh_CN', 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)->value('created_at');
$created_at = strtotime($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'));
}
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace app\crm\controller;
use app\crm\controller\BaseController;
use app\crm\model\Customer;
use app\crm\model\CustomerTag;
use app\crm\model\TagGroup;
use app\crm\model\Tag;
use app\crm\utils\Util;
use config\constants\Constants;
use support\Request;
class TagGroupController extends BaseController
{
// 列表
public function list(Request $request)
{
$uid = $request->user->uid;
$scene = $request->input("scene");
$list = TagGroup::with('tags')->get();
$languageArr = Constants::LANGUAGE;
$language = 'zh_CN';
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];
}
if($scene == 'customers'){
// 统计每个标签关联的客户数量,增加user_id条件
$tag['count'] = $tag->customers()->where('user_id', $uid)->count();
}
}
}
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'));
}
try {
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]);
}
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
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;
}
}
+82
View File
@@ -0,0 +1,82 @@
<?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());
}
/**
* 客户的标签
*/
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');
}
}
+163
View File
@@ -0,0 +1,163 @@
<?php
namespace app\crm\service;
use app\crm\logic\CustomerLogic;
use app\crm\model\CarType;
use app\crm\model\Channel;
use app\crm\model\Country;
use app\crm\model\Customer;
use app\crm\model\Follow;
use app\crm\utils\Util;
use config\constants\Constants;
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;
$keyword = $params['keyword'] ?? '';
$recycle = $params['recycle'] ?? '';
$follow = $params['follow'] ?? '';
$follow_last = $params['follow_last'] ?? '';
$tagId = $params['tag_id'] ?? [];
$where = [];
$user_id =$params['user_id'];
if(empty($user_id) || $user_id == 0){
$where[] = ['user_id', '=', $user_id];
}
$seas = $params['seas'] ?? '';
if($seas == 'seas'){
$where[] = ['user_id', 0];
}
if($recycle == 'recycle'){
$where[] = ['user_id', $user_id];
}
$grade = [];
if (!Util::isEmpty($follow) && $follow == 0) {
$where[] = ['grade', '<=', 2];
} elseif (!Util::isEmpty($follow) && $follow == 1) {
$grade = [3, 4];
} elseif (!Util::isEmpty($follow) && $follow == 2) {
$where[] = ['grade', '=', 5];
}
if (!Util::isEmpty($follow_last) && $follow_last == 0) {
$where[] = ['follow_last', '>', strtotime('-1 day')];
} elseif (!Util::isEmpty($follow_last) && $follow_last == 1) {
$where[] = ['follow_last', '>', strtotime('-3 day')];
} elseif (!Util::isEmpty($follow_last) && $follow_last == 2) {
$where[] = ['follow_last', '>', strtotime('-7 day')];
}
$query = Customer::where($where);
if(!empty($tagId)){
//查询有$tagId 里任意一个标签的客户
//$query->whereHas('tags', function($query) use ($tagId){
// $query->whereIn ('tag.id', $tagId);
//});
//查询有$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) {
$query->where('contacts', 'like', "%{$keyword}%")
->orWhere('phone', 'like', "%{$keyword}%");
});
}
if (!Util::isEmpty($grade)) {
$query->whereIn('grade', $grade);
}
if ($recycle == 'recycle' || $seas == 'seas') {
$query->onlyTrashed();
}
$customer = Customer::where($where)->paginate($perPage, ['*'], 'page', $page);
foreach ($customer as &$item) {
$item = $this->getCustomer($item);
if($seas == 'seas'){
$item['seas_time'] = time()-strtotime($item['deleted_at']);
}
}
unset($item);
return $customer;
}
/**
* @param $customer
* @param $request
* @param $tag 是否返回客户标签
* @return mixed
*/
public static function getCustomer($customer, $language = 'zh_CN', $tag = false)
{
$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');
$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 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 (!empty($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['picture_full'] = $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'],
];
}
+1 -1
View File
@@ -14,7 +14,7 @@ class CategorieService
public function list($params)
{
$result = (new CategorieLogic())->list($params);
$value=$result[0]->toArray();
// $value=$result[0]->toArray();
foreach ($result as $key => &$value) {
$value['name'] = isset($value['translation']) ? $value['translation']['name'] : '';
unset($value['translation']);
+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'];
}
+32 -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 () {
@@ -43,6 +41,7 @@ Route::group('/common', function () {
Route::group('/user', function () {
Route::any('/info', [app\common\controller\UserController::class, 'info']);
Route::any('/get', [app\common\controller\UserController::class, 'getUser']);
});
Route::group('/preset', function () {
@@ -98,3 +97,34 @@ 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/statistics', [app\crm\controller\CustomerController::class, 'statistics']);
Route::any('/customer/getChannel', [app\crm\controller\CustomerController::class, 'getChannel']);
Route::any('/customer/getCountry', [app\crm\controller\CustomerController::class, 'getCountry']);
Route::any('/customer/followNext', [app\crm\controller\CustomerController::class, 'followNext']);
Route::any('/tagGroup/list', [app\crm\controller\TagGroupController::class, 'list']);
Route::any('/tagGroup/updatePivot', [app\crm\controller\TagGroupController::class, 'updatePivot']);
Route::any('/upload/image', [app\common\controller\UploadController::class, 'uploadImage']);
Route::any('/user/edit', [app\common\controller\UserController::class, 'edit']);
Route::any('/user/savePassword', [app\common\controller\UserController::class, 'savePassword']);
});
+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' => ' 不能领取有归属客户',
];