You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

521 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Models;
use App\Repositories\YuanheRepository;
class Company extends SoftDeletesModel
{
protected $casts = ['project_users' => 'json', 'partners' => 'json'];
protected $appends = ['is_yh_invested_text'];
public function getIsYhInvestedTextAttribute()
{
if (empty($this->is_yh_invested)) {
return '';
}
return $this->is_yh_invested == 1 ? '被投企业' : '';
}
public function users()
{
return $this->hasMany(User::class, 'company_id', 'id');
}
/**
* 限制只返回有关联学员且至少有一条审核通过的报名记录的公司
* 用于列表查询和统计查询
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeApprovedStudents($query)
{
return $query->whereHas('users', function ($q) {
$q->whereHas('courseSigns', function ($signQuery) {
$signQuery->where('status', 1);
});
});
}
/**
* 地址转经纬度
*/
public static function addressTolocation($address)
{
$map = Config::getValueByKey('map_server');
$map = json_decode($map, true);
$url = "https://restapi.amap.com/v3/geocode/geo";
$params = [
'key' => $map['key'],
'address' => $address,
];
try {
$result = httpCurl($url, 'GET', $params);
$result = json_decode($result, true);
if ($result['status'] == 1) {
$location = $result['geocodes'][0]['location'];
$location = explode(',', $location);
return [
'lng' => $location[0],
'lat' => $location[1],
];
}
return [
'lng' => null,
'lat' => null,
];
} catch (\Exception $e) {
return [
'lng' => null,
'lat' => null,
];
}
}
/**
* 累计被投企业统计
* @param string|null $start_date 开始日期
* @param string|null $end_date 结束日期
* @param array|null $course_ids 课程ID仅在自定义时间时生效
* @param bool $retList 是否返回列表
*/
public static function yhInvestedTotal($end_date = null, $retList = false)
{
// 获取这些学员所在的被投企业
$companies = Company::approvedStudents()->where('is_yh_invested', 1)->get();
// 自定义时间:需要按被投时间筛选
// 筛选出被投时间在范围内的企业
$filteredCompanies = [];
foreach ($companies as $company) {
$projectUsers = $company->project_users ?? [];
$hasValidInvestDate = false;
$allInvestDatesNull = true;
foreach ($projectUsers as $item) {
$investDate = $item['investDate'] ?? null;
// 检查是否有有效的被投时间
if ($investDate) {
$allInvestDatesNull = false;
// 检查被投时间是否在范围内
if ($investDate <= $end_date) {
$hasValidInvestDate = true;
break; // 只要有一条满足就加入
}
}
}
// 如果有有效的被投时间在范围内或者所有被投时间都是null则加入结果
if ($hasValidInvestDate || $allInvestDatesNull) {
$filteredCompanies[] = $company;
}
}
$companies = collect($filteredCompanies);
// 返回结果
if ($retList) {
return $companies->values();
} else {
return $companies->count();
}
}
/**
* 今年被投企业统计(统计或列表)- 按年份范围统计
* @param string|null $start_date 开始日期
* @param string|null $end_date 结束日期
* @param array|null $course_ids 课程ID数组不传则统计所有课程
* @param bool $retList 是否返回列表false返回数量true返回列表包含学员、课程信息
* @return int|array
*/
public static function companyInvestedYear($start_date = null, $end_date = null, $retList = false)
{
// 计算年份范围
$years = [];
if ($start_date && $end_date) {
// 从开始和结束日期中提取年份范围
$startYear = (int) date('Y', strtotime($start_date));
$endYear = (int) date('Y', strtotime($end_date));
// 生成所有年份的数组
for ($year = $startYear; $year <= $endYear; $year++) {
$years[] = $year;
}
} else {
// 如果没有提供日期,使用当前年份
$years[] = (int) date('Y');
}
// 获取这些公司中标记为被投的公司
$allInvestedCompanies = Company::approvedStudents()->where('is_yh_invested', 1)->get();
// 筛选出被投时间在年份范围内的企业
$companies = [];
foreach ($allInvestedCompanies as $company) {
$projectUsers = $company->project_users ?? [];
$hasInvestInYears = false;
foreach ($projectUsers as $item) {
$investDate = $item['investDate'] ?? null;
if ($investDate) {
$investYear = (int) date('Y', strtotime($investDate));
if (in_array($investYear, $years)) {
$hasInvestInYears = true;
break;
}
}
}
if ($hasInvestInYears) {
$companies[$company->id] = $company;
}
}
$companies = collect($companies);
// 返回结果
if ($retList) {
return $companies->values();
} else {
return $companies->count();
}
}
/**
* 根据用户信息更新/同步公司信息
* @param User $user 用户对象
* @return array 返回结果 ['success' => bool, 'message' => string, 'company' => Company|null]
*/
public static function updateCompanyFromUser($user)
{
if (!$user || empty($user->company_name)) {
return ['success' => false, 'message' => '用户或公司名称为空', 'company' => null];
}
// 如果已经有有效的公司关联company_id > 0跳过
// 允许处理 company_id = -1待更新或 null初始状态的情况
if ($user->company_id && $user->company_id > 0) {
return ['success' => false, 'message' => '用户已有公司关联', 'company' => null];
}
// 清理公司名称:去除首尾空格、换行符、制表符等异常字符
$cleanedCompanyName = trim($user->company_name);
// 去除换行符、回车符、制表符等空白字符
$cleanedCompanyName = preg_replace('/[\r\n\t]+/', '', $cleanedCompanyName);
// 将多个连续空格替换为单个空格
$cleanedCompanyName = preg_replace('/\s+/', ' ', $cleanedCompanyName);
// 再次去除首尾空格
$cleanedCompanyName = trim($cleanedCompanyName);
// 如果清理后为空,返回错误
if (empty($cleanedCompanyName)) {
return ['success' => false, 'message' => '公司名称无效', 'company' => null];
}
$YuanheRepository = new YuanheRepository();
// 获取公司详细信息,使用清理后的公司名称
$result = $YuanheRepository->companyInfo(['enterpriseName' => $cleanedCompanyName]);
if (!$result) {
// 标识一下未匹配到公司,后续可以根据这个字段筛选出未匹配到公司的用户
$user->company_id = 0;
$user->save();
return ['success' => false, 'message' => '公司不存在', 'company' => null];
}
// 如果$result['enterpriseName']存在数字,跳过
if (preg_match('/\d/', $result['enterpriseName'])) {
$user->company_id = 0;
$user->save();
return ['success' => false, 'message' => '公司名称包含数字,跳过', 'company' => null];
}
if ($result['status'] == '未注册') {
$user->company_id = 0;
$user->save();
return ['success' => false, 'message' => '公司未注册,跳过', 'company' => null];
}
$where = ['company_name' => $result['enterpriseName']];
$data = [
'business_scope' => $result['businessScope'],
'company_city' => $result['city'],
'contact_mail' => $result['contactMail'],
'contact_phone' => $result['contactPhone'],
'company_area' => $result['country'],
'credit_code' => $result['creditCode'],
'enterprise_id' => $result['enterpriseId'],
'company_name' => $result['enterpriseName'],
'is_abroad' => $result['isAbroad'],
'company_market' => $result['isOnStock'],
'is_yh_invested' => $result['isYhInvested'],
'logo' => $result['logo'],
'company_legal_representative' => $result['operName'],
'company_province' => $result['province'],
'company_industry' => combineKeyValue($result['qccIndustry']),
'regist_amount' => $result['registAmount'],
'regist_capi_type' => $result['registCapiType'],
'company_date' => $result['startDate'],
'status' => $result['status'],
'stock_date' => $result['stockDate'],
'currency_type' => $result['currencyType'],
'stock_number' => $result['stockNumber'],
'stock_type' => $result['stockType'],
'company_tag' => implode(',', $result['tagList']),
// 更新日期
'update_date' => $result['updatedDate'] ?? null,
// 管理平台
'project_users' => $result['projectUsers'] ?? null,
// 股东信息
'partners' => $result['partners'] ?? null,
// 更新地址
'company_address' => $result['address'],
];
$company = Company::updateOrCreate($where, $data);
// 更新用户关联
$user->company_id = $company->id;
$user->save();
// 更新上市状态
self::updateMarketStatus($company->id);
// 更新位置(经纬度)
self::updateLocation($company->id);
return ['success' => true, 'message' => '更新成功', 'company' => $company];
}
/**
* 直接同步公司信息(根据公司名称从接口获取最新信息更新)
* @param Company $company 公司对象
* @return array 返回结果 ['success' => bool, 'message' => string, 'company' => Company|null]
*/
public static function syncCompanyInfo($company)
{
if (!$company || empty($company->company_name)) {
return ['success' => false, 'message' => '公司或公司名称为空', 'company' => null];
}
// 清理公司名称
$cleanedCompanyName = trim($company->company_name);
$cleanedCompanyName = preg_replace('/[\r\n\t]+/', '', $cleanedCompanyName);
$cleanedCompanyName = preg_replace('/\s+/', ' ', $cleanedCompanyName);
$cleanedCompanyName = trim($cleanedCompanyName);
if (empty($cleanedCompanyName)) {
return ['success' => false, 'message' => '公司名称无效', 'company' => null];
}
$YuanheRepository = new YuanheRepository();
// 获取公司详细信息
$result = $YuanheRepository->companyInfo(['enterpriseName' => $cleanedCompanyName]);
if (!$result) {
return ['success' => false, 'message' => '公司不存在', 'company' => null];
}
// 如果$result['enterpriseName']存在数字,跳过
if (preg_match('/\d/', $result['enterpriseName'])) {
return ['success' => false, 'message' => '公司名称包含数字,跳过', 'company' => null];
}
if ($result['status'] == '未注册') {
return ['success' => false, 'message' => '公司未注册,跳过', 'company' => null];
}
// 更新公司数据(不包含地址和经纬度)
$data = [
'business_scope' => $result['businessScope'],
'company_city' => $result['city'],
'contact_mail' => $result['contactMail'],
'contact_phone' => $result['contactPhone'],
'company_area' => $result['country'],
'credit_code' => $result['creditCode'],
'enterprise_id' => $result['enterpriseId'],
'company_name' => $result['enterpriseName'],
'is_abroad' => $result['isAbroad'],
'company_market' => $result['isOnStock'],
'is_yh_invested' => $result['isYhInvested'],
'logo' => $result['logo'],
'company_legal_representative' => $result['operName'],
'company_province' => $result['province'],
'company_industry' => combineKeyValue($result['qccIndustry']),
'regist_amount' => $result['registAmount'],
'regist_capi_type' => $result['registCapiType'],
'company_date' => $result['startDate'],
'status' => $result['status'],
'stock_date' => $result['stockDate'],
'currency_type' => $result['currencyType'],
'stock_number' => $result['stockNumber'],
'stock_type' => $result['stockType'],
'company_tag' => implode(',', $result['tagList']),
'update_date' => $result['updatedDate'] ?? null,
'project_users' => $result['projectUsers'] ?? null,
'partners' => $result['partners'] ?? null,
];
$company->fill($data);
$company->save();
// 更新上市状态
self::updateMarketStatus($company->id);
return ['success' => true, 'message' => '更新成功', 'company' => $company];
}
/**
* 更新经纬度信息
* @param int $companyId 公司ID
* @return bool
*/
public static function updateLocation($companyId)
{
$company = Company::find($companyId);
if (!$company || empty($company->company_address) || $company->company_longitude) {
return false;
}
$local = Company::addressTolocation($company->company_address);
$company->company_longitude = $local['lng'];
$company->company_latitude = $local['lat'];
$company->save();
return true;
}
/**
* 根据 company_tag 更新上市状态
* 判断是否包含上市代码标签,如 688001.SH、000001.SZ、830001.BJ 等
* @param int $companyId 公司ID
* @return bool 是否更新成功
*/
public static function updateMarketStatus($companyId)
{
$company = Company::find($companyId);
if (!$company || empty($company->company_tag)) {
return false;
}
// 上市代码正则:匹配全球各地上市公司股票代码后缀
$stockCodePattern = '/\.(SWR|SW|WR|SS|RS|SB|PK|TO|AX|WS|PR|DB|UN|RT|WT|SH|SZ|BJ|TW|HK|SG|US|DE|FR|JP|KR|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|U|V|W|X|Y|Z)(?![A-Za-z0-9])/i';
$hasStockCode = preg_match($stockCodePattern, $company->company_tag);
// 不属于新三板上市公司的关键字(需要排除)
$excludeXinsanbanKeywords = [
'新三板摘牌',
'新三板挂牌审核',
'新三板终止',
'新三板退市',
'新三板撤销',
'新三板注销',
'新三板中止',
];
// 检查是否包含排除关键字
$hasExcludeKeyword = array_reduce($excludeXinsanbanKeywords, function ($carry, $keyword) use ($company) {
return $carry || strpos($company->company_tag, $keyword) !== false;
}, false);
// 检查是否包含"新三板",且不包含排除关键字
$hasXinsanban = !$hasExcludeKeyword && strpos($company->company_tag, '新三板') !== false;
// 如果匹配到股票代码或包含"新三板"(且非排除关键字),则标记为上市
$newMarketStatus = ($hasStockCode || $hasXinsanban) ? 1 : 0;
// 只有状态变化才更新
if ($company->company_market != $newMarketStatus) {
$company->company_market = $newMarketStatus;
$company->save();
return true;
}
return false;
}
/**
* 验证公司名称是否包含特殊符号
* @param string $companyName 公司名称
* @return array 返回结果 ['valid' => bool, 'message' => string]
*/
public static function validateCompanyName($companyName)
{
if (empty($companyName)) {
return ['valid' => true, 'message' => ''];
}
// 定义不允许的特殊符号(包含中英文标点符号,键盘上能打出来的所有标点符号)
$forbiddenChars = [
// 英文标点符号
'/',
'\\',
'.',
',',
';',
':',
"'",
'"',
'?',
'!',
'@',
'#',
'$',
'%',
'^',
'&',
'*',
'(',
')',
'[',
']',
'{',
'}',
'|',
'`',
'~',
'-',
'_',
'+',
'=',
'<',
'>',
// 中文标点符号
'。',
'',
'、',
'',
'',
'',
'',
'…',
'—',
'·',
'',
'¥',
'',
'',
'【',
'】',
'《',
'》',
'〈',
'〉',
'「',
'」',
'『',
'』',
'',
'',
];
// 添加中文引号字符(使用十六进制编码避免语法错误)
$chineseQuotes = ["\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x98", "\xE2\x80\x99"]; // " " ' '
$forbiddenChars = array_merge($forbiddenChars, $chineseQuotes);
foreach ($forbiddenChars as $char) {
if (strpos($companyName, $char) !== false) {
return ['valid' => false, 'message' => '公司名称不能包含特殊符号'];
}
}
return ['valid' => true, 'message' => ''];
}
}