Browse Source

feat(app): 添加资源自动分配功能并优化客户资源模块

- 新增资源自动分配任务和相关配置
- 客户资源添加和编辑接口增加角色类型和体验课数量字段
- 统计接口增加已分配资源数量统计
- 优化客户资源查询和日志列表接口
master
王泽彦 10 months ago
parent
commit
a76319e490
  1. 42
      admin/src/app/views/setting/system.vue
  2. 3
      niucloud/app/adminapi/controller/sys/Config.php
  3. 108
      niucloud/app/api/controller/apiController/CustomerResources.php
  4. 23
      niucloud/app/api/controller/apiController/Statistics.php
  5. 51
      niucloud/app/common.php
  6. 13
      niucloud/app/dict/schedule/schedule.php
  7. 20
      niucloud/app/job/custmer/ResourceAutoAllocation.php
  8. 3
      niucloud/app/listener/custom/SalesPerformanceListener.php
  9. 70
      niucloud/app/service/admin/customer_resources/CustomerResourcesService.php

42
admin/src/app/views/setting/system.vue

@ -87,25 +87,36 @@
</el-form-item>
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('serviceInformation') }}</h3>
<!-- <el-card class="box-card mt-[15px] !border-none" shadow="never">-->
<!-- <h3 class="panel-title !text-sm">{{ t('serviceInformation') }}</h3>-->
<el-form-item :label="t('contactsTel')">
<el-input
v-model.trim="formData.tel"
:placeholder="t('contactsTelPlaceholder')"
class="input-width"
<!-- <el-form-item :label="t('contactsTel')">-->
<!-- <el-input-->
<!-- v-model.trim="formData.tel"-->
<!-- :placeholder="t('contactsTelPlaceholder')"-->
<!-- class="input-width"-->
<!-- clearable-->
<!-- maxlength="20"-->
<!-- show-word-limit-->
<!-- />-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="t('wechatCode')">-->
<!-- <upload-image v-model="formData.wechat_code" />-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="t('customerServiceCode')">-->
<!-- <upload-image v-model="formData.enterprise_wechat" />-->
<!-- </el-form-item>-->
<!-- </el-card>-->
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<h3 class="panel-title !text-sm">市场配置</h3>
<el-form-item label="资源自动分配时间">
<el-time-picker
v-model="formData.auto_distribution_time"
placeholder="选择时间"
value-format="HH:mm:ss"
clearable
maxlength="20"
show-word-limit
/>
</el-form-item>
<el-form-item :label="t('wechatCode')">
<upload-image v-model="formData.wechat_code" />
</el-form-item>
<el-form-item :label="t('customerServiceCode')">
<upload-image v-model="formData.enterprise_wechat" />
</el-form-item>
</el-card>
</el-form>
@ -154,6 +165,7 @@ const formData = reactive<Record<string, string>>({
wechat_code: '',
enterprise_wechat: '',
tel: '',
auto_distribution_time: '',
})
const setFormData = async () => {

3
niucloud/app/adminapi/controller/sys/Config.php

@ -49,7 +49,8 @@ class Config extends BaseAdminController
[ "front_end_name", "" ],
[ "front_end_logo", "" ],
[ "front_end_icon", "" ],
[ "icon", "" ]
[ "icon", "" ],
[ "auto_distribution_time", "" ],
]);
(new ConfigService())->setWebSite($data);

108
niucloud/app/api/controller/apiController/CustomerResources.php

@ -11,6 +11,7 @@
namespace app\api\controller\apiController;
use app\model\personnel\Personnel;
use app\Request;
use app\service\api\apiService\CustomerResourcesService;
use core\base\BaseApiService;
@ -25,44 +26,50 @@ class CustomerResources extends BaseApiService
//获取全部客户资源
public function getAll(Request $request){
public function getAll(Request $request)
{
$name = $request->param('name', '');//客户姓名
$phone_number = $request->param('phone_number', '');//客户手机号
if(empty($name) && empty($phone_number)){
if (empty($name) && empty($phone_number)) {
return fail("缺少查询参数");
}
$where = [
'name'=>$name,
'phone_number'=>$phone_number
'name' => $name,
'phone_number' => $phone_number
];
$res = (new CustomerResourcesService())->getAll($where);
if(!$res['code']){
if (!$res['code']) {
return fail($res['msg']);
}
return success($res['data']);
}
//客户资源添加
public function add(Request $request){
public function add(Request $request)
{
$date = date('Y-m-d');
$promised_visit_time = $request->param('promised_visit_time', '');
if($promised_visit_time){
$promised_visit_time = date('Y-m-d H:i:s',strtotime($promised_visit_time));
if ($promised_visit_time) {
$promised_visit_time = date('Y-m-d H:i:s', strtotime($promised_visit_time));
}
$optional_class_time = $request->param('optional_class_time', '');
if($optional_class_time){
$optional_class_time = date('Y-m-d H:i:s',strtotime($optional_class_time));
}
if ($optional_class_time) {
$optional_class_time = date('Y-m-d H:i:s', strtotime($optional_class_time));
}
$personnel = new Personnel();
$role_id = $personnel->alias("a")
->join(['school_campus_person_role' => 'b'], 'a.id = b.person_id', 'left')
->where(['a.id' => $request->param('consultant', '')])
->value('b.role_id');
$customer_resources_data = [
"create_year_month"=>date('Y-m'),
"create_date"=>date('Y-m-d'),
"create_year_month" => date('Y-m'),
"create_date" => $date,
"source_channel" => $request->param('source_channel', ''),
"source" => $request->param('source', ''),
"consultant" => $request->param('consultant', ''),
@ -78,6 +85,9 @@ class CustomerResources extends BaseApiService
"cognitive_idea" => $request->param('cognitive_idea', ''),
"optional_class_time" => $optional_class_time,
"distance" => $request->param('distance', ''),
// 新资源有2节体验课
"trial_class_count" => 2,
"rf_type" => get_role_type($role_id),
];
$six_speed_data = [
@ -90,61 +100,56 @@ class CustomerResources extends BaseApiService
"staff_id" => $request->param('staff_id', ''),//人员ID
];
foreach($customer_resources_data as $k=>$v){
if(!isset($v) || $v === ''){
return fail("缺少必填项{$k}");
}
}
if (strlen($customer_resources_data['phone_number']) > 12) {
return fail("联系电话不能超过12位");
}
foreach($six_speed_data as $k=>$v){
if(!isset($v) || $v === ''){
foreach ($six_speed_data as $k => $v) {
if (!isset($v) || $v === '') {
return fail("缺少必填项{$k}");
}
}
//验证手机号是否存在
$info = (new CustomerResourcesService())->getInfo(['phone_number'=>$customer_resources_data['phone_number']]);
if(!empty($info['data']['id'])){
$info = (new CustomerResourcesService())->getInfo(['phone_number' => $customer_resources_data['phone_number']]);
if (!empty($info['data']['id'])) {
return fail("手机号已存在");
}
$res = (new CustomerResourcesService())->addData($customer_resources_data,$six_speed_data);
if(!$res['code']){
$res = (new CustomerResourcesService())->addData($customer_resources_data, $six_speed_data);
if (!$res['code']) {
return fail($res['msg']);
}
return success([]);
}
//客户资源-编辑
public function edit(Request $request){
public function edit(Request $request)
{
$resource_sharing_id = $request->param('resource_sharing_id', '');//资源共享id
$customer_resources_id = $request->param('id', '');//客户资源表id
$promised_visit_time = $request->param('promised_visit_time', '');
if($promised_visit_time){
$promised_visit_time = date('Y-m-d H:i:s',strtotime($promised_visit_time));
if ($promised_visit_time) {
$promised_visit_time = date('Y-m-d H:i:s', strtotime($promised_visit_time));
}
$optional_class_time = $request->param('optional_class_time', '');
if($optional_class_time){
$optional_class_time = date('Y-m-d H:i:s',strtotime($optional_class_time));
if ($optional_class_time) {
$optional_class_time = date('Y-m-d H:i:s', strtotime($optional_class_time));
}
if(empty($customer_resources_id)){
if (empty($customer_resources_id)) {
return fail("缺少客户id");
}
$where=[
'id'=>$customer_resources_id
$where = [
'id' => $customer_resources_id
];
//客户资源数据
@ -179,8 +184,8 @@ class CustomerResources extends BaseApiService
"second_visit_status" => $request->param('second_visit_status', null),//二访情况
];
foreach($customer_resources_data as $k=>$v){
if(!isset($v) || $v === ''){
foreach ($customer_resources_data as $k => $v) {
if (!isset($v) || $v === '') {
return fail("缺少必填项{$k}");
}
}
@ -190,51 +195,52 @@ class CustomerResources extends BaseApiService
}
//如果六要素人员id没有就是当前登录人的员工id
if(empty($six_speed_data['staff_id'])){
if (empty($six_speed_data['staff_id'])) {
$six_speed_data['staff_id'] = $this->member_id;
}
foreach($six_speed_data as $k=>$v){
foreach ($six_speed_data as $k => $v) {
// 排除 first_visit_status 和 second_visit_status 的必填校验
if (in_array($k, ['first_visit_status', 'second_visit_status'])) {
continue;
}
if(!isset($v) || $v === ''){
if (!isset($v) || $v === '') {
return fail("缺少必填项{$k}");
}
}
$res = (new CustomerResourcesService())->editData($where,$customer_resources_data,$six_speed_data);
if(!$res['code']){
$res = (new CustomerResourcesService())->editData($where, $customer_resources_data, $six_speed_data);
if (!$res['code']) {
return fail($res['msg']);
}
return success([]);
}
//客户资源-修改记录列表
public function getEditLogList(Request $request){
public function getEditLogList(Request $request)
{
//前端要传递分页(page,limit)参数
$customer_resource_id = $request->param('customer_resource_id','');
$type = $request->param('type','resource');//查询类型|resource=客户资源,six_speed=六要素
$customer_resource_id = $request->param('customer_resource_id', '');
$type = $request->param('type', 'resource');//查询类型|resource=客户资源,six_speed=六要素
if(empty($customer_resource_id) || empty($type)){
if (empty($customer_resource_id) || empty($type)) {
return fail('缺少必要参数');
}
if(!in_array($type,['resource','six_speed'])){
if (!in_array($type, ['resource', 'six_speed'])) {
return fail('类型不正确');
}
$where = [
'customer_resource_id' =>$customer_resource_id
'customer_resource_id' => $customer_resource_id
];
if($type == 'resource'){
if ($type == 'resource') {
//resource=客户资源
$res =(new CustomerResourcesService())->getCustomerResourceChangesEditLog($where);
}else{
$res =(new CustomerResourcesService())->getSixSpeedModificationEditLog($where);
$res = (new CustomerResourcesService())->getCustomerResourceChangesEditLog($where);
} else {
$res = (new CustomerResourcesService())->getSixSpeedModificationEditLog($where);
}
return success($res);

23
niucloud/app/api/controller/apiController/Statistics.php

@ -14,6 +14,8 @@ namespace app\api\controller\apiController;
use app\Request;
use app\service\api\apiService\CommonService;
use core\base\BaseApiService;
use app\model\customer_resources\CustomerResources;
use think\facade\Db;
/**
* 统计控制器相关接口
@ -43,15 +45,28 @@ class Statistics extends BaseApiService
if(in_array('market',$role_key_arr) || in_array('market_manager',$role_key_arr)){
//市场人员统计数据(地推拉人头的)
$monthtotal = CustomerResources::where('consultant',$personnel_id)->where('create_time','between',[$firstDayOfMonth,$lastDayOfMonth])->count('id');
$alltotal = CustomerResources::where('consultant',$personnel_id)->count('id');
//已分配的数量
$yfpsl = Db::table('school_resource_sharing')
->whereIn('resource_id', function($query) {
$query->table('school_customer_resources')
->where('consultant', 1)
->field('id');
})
->where('user_id', '!=', 1)
->group('resource_id')
->count();
$data = [
'date_range'=>$dateRange,//拼接日期范围字符串
'role_type'=>'market_type',//角色类型|market_type=市场,sale_type=销售
// 本月总数
//本月
'month' => [
'new_total' => ($newTotal = rand(100, 500)),//拉新总人数
'new_total_rate' => min(100, round($newTotal / 1000 * 100, 2)),//拉新总人数比例(不超过100%)
'assigned_sales' => ($assignedSales = rand(50, 300)),//已分配给销售的人数
'assigned_sales_rate' => min(100, round($assignedSales / $newTotal * 100, 2)),//已分配给销售的人数比例(不超过100%)
'new_total' => $monthtotal,//拉新总人数
'new_total_rate' => $alltotal ? round($monthtotal/$alltotal*100,2) : 0,//拉新总人数比例(不超过100%)
'assigned_sales' => $yfpsl,
'assigned_sales_rate' => $alltotal ? round($yfpsl/$alltotal*100,2) : 0,//已分配给销售的人数比例(不超过100%)
'yesterday_new' => ($yesterdayNew = rand(5, 50)),//昨日拉新
'yesterday_new_rate' => min(100, round($yesterdayNew / $newTotal * 100, 2)),//昨日拉新比例(不超过100%)
'today_new' => ($todayNew = rand(1, 20)),//今日拉新

51
niucloud/app/common.php

@ -1030,18 +1030,18 @@ function get_last_time($time = null)
return $text;
}
function get_campus_where($user_id,$field="campus_id")
function get_campus_where($user_id, $field = "campus_id")
{
$where = [];
$person = new Personnel();
$campusPersonRole = new CampusPersonRole();
if($user_id > 1){
if ($user_id > 1) {
$person_id = $person->where(['sys_user_id' => $user_id])->value("id");
$role_info = $campusPersonRole->where(['person_id' => $person_id])->find();
if (!in_array($role_info['role_id'], [1,7,8])) {
if($role_info['campus_id']){
$where[] = [$field,'=',$role_info['campus_id']];
if (!in_array($role_info['role_id'], [1, 7, 8])) {
if ($role_info['campus_id']) {
$where[] = [$field, '=', $role_info['campus_id']];
}
}
}
@ -1144,6 +1144,7 @@ function decryptWechatPayNotify($ciphertext, $nonce, $associatedData, $key)
$associatedData
);
}
/**
* 判断是否为手机号
*/
@ -1154,12 +1155,13 @@ function isPhone($mobile)
/**
* 体测评分
* @param int $age年龄
* @param int $gender性别( 1:男,2:女)
* @param int $height身高
* @param int $weight体重
* @param int $age年龄
* @param int $gender性别( 1:男,2:女)
* @param int $height身高
* @param int $weight体重
*/
function calculateChildHealthScore($age, $gender, $height, $weight) {
function calculateChildHealthScore($age, $gender, $height, $weight)
{
// WHO 标准数据(简化版)——单位:cm/kg
// 示例使用中位数及上下限估算(实际可使用更精确的Z-score或百分位表)
@ -1220,13 +1222,13 @@ function calculateChildHealthScore($age, $gender, $height, $weight) {
}
function get_dict_value($key,$value){
function get_dict_value($key, $value)
{
$dict = new \app\model\dict\Dict();
$field = 'id,name,key,dictionary,memo,create_time,update_time';
$info = $dict->field($field)->where([['key', '=', $key]])->findOrEmpty()->toArray();
if($info['dictionary'] == null)
{
if ($info['dictionary'] == null) {
$info['dictionary'] = [];
}
@ -1242,11 +1244,26 @@ function get_dict_value($key,$value){
//$date = 年月 $campus_person_role_id 人员角色表id
//$field complete_num 完成数量 expire_num 到期数量 renew_num 续费数量 resource_num 分配的资源数量 visit_num 到访数量
//$num 增加数量 默认1
function set_summary($date,$campus_person_role_id,$field,$num=1){
function set_summary($date, $campus_person_role_id, $field, $num = 1)
{
$personnel_summary = new PersonnelSummary();
$personnel_summary->where([
['task_date','=',$date],
['campus_person_role_id','=',$campus_person_role_id]
])->inc($field,$num)->update();
['task_date', '=', $date],
['campus_person_role_id', '=', $campus_person_role_id]
])->inc($field, $num)->update();
return true;
}
/**
* 根据角色id获取角色类型
*/
function get_role_type($role_id)
{
$role_type = [
1 => 'market',
2 => 'sale',
3 => 'teacher'
];
$dept_id = \app\model\sys\SysRole::find($role_id)->value('dept_id');
return $role_type[$dept_id] ?? 'other';
}

13
niucloud/app/dict/schedule/schedule.php

@ -22,5 +22,18 @@ return [
],
'class' => 'app\job\transfer\schedule\CheckFinish',
'function' => ''
],
[
'key' => 'resource_auto_allocation',
'name' => '自动分配资源',
'desc' => '',
'time' => [
'type' => 'day',
'day' => 1,
'hour' => 0,
'min' => 5
],
'class' => 'app\job\transfer\schedule\CheckFinish',
'function' => ''
]
];

20
niucloud/app/job/custmer/ResourceAutoAllocation.php

@ -0,0 +1,20 @@
<?php
namespace app\job\custmer;
use app\model\customer_resources\CustomerResources;
use app\model\resource_sharing\ResourceSharing;
use core\base\BaseJob;
class ResourceAutoAllocation extends BaseJob
{
public function doJob()
{
//获取当天的资源列表模型CustomerResources
// CustomerResources::where('created_at')
//获取当天销售获取资源的数量ResourceSharing
// ResourceSharing::where('shared_at')->
//遍历资源列表按照资源数量的最少的人员优先分配
return true;
}
}

3
niucloud/app/listener/custom/SalesPerformanceListener.php

@ -107,12 +107,13 @@ class SalesPerformanceListener
if ($exists) {
// 更新已有记录
return (new PerformanceRecords())->where('id', $exists['id'])->update([
(new PerformanceRecords())->where('id', $exists['id'])->update([
'performance_value' => $data['performance_value'],
'order_status' => $data['order_status'],
'remarks' => $data['remarks'],
'updated_at' => date('Y-m-d H:i:s')
]);
return true;
} else {
// 创建新记录
$data['created_at'] = date('Y-m-d H:i:s');

70
niucloud/app/service/admin/customer_resources/CustomerResourcesService.php

@ -27,6 +27,7 @@ use app\model\six_speed_modification_log\SixSpeedModificationLog;
use app\model\student_courses\StudentCourses;
use app\service\admin\member\MemberLabelService;
use core\base\BaseAdminService;
use think\facade\Event;
/**
@ -94,7 +95,7 @@ class CustomerResourcesService extends BaseAdminService
}
$search_model->where(get_campus_where($this->uid,'campus'));
$search_model->where(get_campus_where($this->uid, 'campus'));
return $this->pageQuery($search_model, function ($item, $key) {
$item = $this->makeUp($item);
@ -111,8 +112,6 @@ class CustomerResourcesService extends BaseAdminService
}
/**
* 获取客户资源信息
* @param int $id
@ -156,19 +155,30 @@ class CustomerResourcesService extends BaseAdminService
return fail("超级管理员不允许添加资源");
}
if($this->model->where(['phone_number' => $data['phone_number']])->find()){
if ($this->model->where(['phone_number' => $data['phone_number']])->find()) {
return fail("资源重复添加");
}
$sixSpeed = new SixSpeed();
// $data['member_label'] = json_encode($data['member_label']);
$role_id = $personnel->alias("a")
->join(['school_campus_person_role' => 'b'], 'a.id = b.person_id', 'left')
->where(['a.id' => $data['consultant']])
->value('b.role_id');
$data['rf_type'] = get_role_type($role_id);
// $data['member_label'] = json_encode($data['member_label']);
// 新资源有2节体验课
$data['trial_class_count'] = 2;
$res = $this->model->create($data);
$event_data = [
'customer_resources_id' => $res->id,//客户资源表id
'event_type' => 'add'//事件类型"add=添加,edit=修改
];//事件类型"add=添加,edit=修改
Event::trigger('CalculatePerformance', $event_data, true);
$role_id = $personnel->alias("a")->join(['school_campus_person_role' => 'b'], 'a.id = b.person_id', 'left')
->where(['a.id' => $data['consultant']])->value('b.role_id');
$resourceSharing->insert([
'resource_id' => $res->id,
'user_id' => $data['consultant'],
@ -218,10 +228,10 @@ class CustomerResourcesService extends BaseAdminService
// return fail("操作失败");
// }
if($this->model->where([
['phone_number','=',$data['phone_number']],
['id','<>',$id]
])->find()){
if ($this->model->where([
['phone_number', '=', $data['phone_number']],
['id', '<>', $id]
])->find()) {
return fail("资源重复添加");
}
@ -443,7 +453,6 @@ class CustomerResourcesService extends BaseAdminService
}
public function log_list(array $data = [])
{
$customer_resource_changes = new CustomerResourceChanges();
@ -453,7 +462,6 @@ class CustomerResourcesService extends BaseAdminService
}
$search_model = $customer_resource_changes
->alias("a")
->join(['school_personnel' => 'b'], 'a.operator_id = b.id', 'left')
@ -463,26 +471,26 @@ class CustomerResourcesService extends BaseAdminService
return $this->pageQuery($search_model, function ($item, $key) {
$fieldZhArr = CustomerResources::FieldZh;
$fieldZhArr = CustomerResources::FieldZh;
$modified_fields = json_decode($item['modified_fields'], true);
$type = [];
foreach ($modified_fields as $key => $value) {
$type[] = $fieldZhArr[$value];
}
$item['type'] = implode("<br>",$type);
$item['type'] = implode("<br>", $type);
$old_values = json_decode($item['old_values'], true);
$new_values = json_decode($item['new_values'], true);
$values = [];
foreach ($old_values as $key => $value) {
$old_value = $this->fields($key,$value);
$new_value = $this->fields($key,$new_values[$key]);
$old_value = $this->fields($key, $value);
$new_value = $this->fields($key, $new_values[$key]);
$values[] = $old_value.'->'.$new_value;
$values[] = $old_value . '->' . $new_value;
}
$item['values'] = implode("<br>",$values);
$item['values'] = implode("<br>", $values);
});
@ -497,7 +505,6 @@ class CustomerResourcesService extends BaseAdminService
}
$search_model = $studentCourses
->alias("a")
->join(['school_student' => 'b'], 'a.student_id = b.id', 'left')
@ -542,41 +549,42 @@ class CustomerResourcesService extends BaseAdminService
return $this->pageQuery($search_model, function ($item, $key) {
$arr = ['cash' => '现金支付','scan_code' => '扫码支付','subscription' => '订阅支付'];
$arr = ['cash' => '现金支付', 'scan_code' => '扫码支付', 'subscription' => '订阅支付'];
$item['type'] = $arr[$item['payment_type']];
$item['order_status'] = $item['order_status'] == 'pending' ? '待支付' : '已支付';
});
}
public function fields($filed,$value){
if(!$value){
public function fields($filed, $value)
{
if (!$value) {
return '空';
}
$campus = new Campus();
$member_label = new MemberLabel();
switch ($filed) {
case 'initial_intent':
return get_dict_value('preliminarycustomerintention',$value);
return get_dict_value('preliminarycustomerintention', $value);
break;
case 'source':
return get_dict_value('source',$value);
return get_dict_value('source', $value);
break;
case 'purchasing_power':
return get_dict_value('customer_purchasing_power',$value);
return get_dict_value('customer_purchasing_power', $value);
break;
case 'cognitive_idea':
return get_dict_value('cognitive_concept',$value);
return get_dict_value('cognitive_concept', $value);
break;
case 'source_channel':
return get_dict_value('SourceChannel',$value);
return get_dict_value('SourceChannel', $value);
break;
case 'status':
return get_dict_value('kh_status',$value);
return get_dict_value('kh_status', $value);
break;
case 'member_label':
$label_name = $member_label->where('label_id','in',$value)->column('label_name');
return implode("-",$label_name);
$label_name = $member_label->where('label_id', 'in', $value)->column('label_name');
return implode("-", $label_name);
break;
case 'campus':
return $campus->where(['id' => $value])->value("campus_name");

Loading…
Cancel
Save