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.

492 lines
17 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);
});
});
}
/**
* 根据区域名称筛选公司
* 支持区域名称映射和特殊逻辑(如"苏州市外"
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string|array $companyArea 区域名称,多个用英文逗号分隔或传入数组
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeFilterByArea($query, $companyArea)
{
if (empty($companyArea)) {
return $query;
}
// 如果是字符串,转换为数组
if (is_string($companyArea)) {
$company_area = array_filter(array_map('trim', explode(',', $companyArea)));
} else {
$company_area = array_filter(array_map('trim', (array) $companyArea));
}
if (empty($company_area)) {
return $query;
}
// 区域名称映射:搜索参数 -> 数据库值
$areaMapping = [
'高新区' => '虎丘',
];
// 检查是否包含"苏州市外"
$hasSuzhouOut = in_array('苏州市外', $company_area);
if ($hasSuzhouOut) {
// 苏州市外:排除 company_area 参数接口返回的苏州市内选项,以及虎丘区
$excludeAreas = Parameter::where('number', 'company_area')
->with(['detail' => fn ($q) => $q->orderBy('sort', 'asc')])
->first();
$excludeList = [];
if ($excludeAreas && $excludeAreas->detail) {
foreach ($excludeAreas->detail as $d) {
$v = trim((string) ($d->value ?? ''));
if ($v !== '' && $v !== '苏州市外') {
$excludeList[] = $v;
// 高新区映射为虎丘(数据库可能存虎丘)
if ($v === '高新区') {
$excludeList[] = '虎丘';
}
}
}
}
$excludeList = array_unique(array_merge($excludeList, ['虎丘区']));
$query->whereNotNull('company_area')->where('company_area', '!=', '');
if (!empty($excludeList)) {
foreach ($excludeList as $v) {
$query->where('company_area', 'not like', '%' . $v . '%');
}
}
return $query;
} else {
// 将搜索参数转换为数据库值
$company_area = array_map(function ($v) use ($areaMapping) {
return isset($areaMapping[$v]) ? $areaMapping[$v] : $v;
}, $company_area);
return $query->where(function ($q) use ($company_area) {
foreach ($company_area as $v) {
$q->orWhere('company_area', 'like', '%' . $v . '%');
}
});
}
}
/**
* 地址转经纬度
*/
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 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' => ''];
}
}