diff --git a/app/Console/Commands/PushCourses.php b/app/Console/Commands/PushCourses.php new file mode 100755 index 0000000..d998d6c --- /dev/null +++ b/app/Console/Commands/PushCourses.php @@ -0,0 +1,70 @@ +whereHas('typeDetail', function ($query) { + $query->where('is_push', 1); + })->get(); + if ($courses->isEmpty()) { + $this->info('没有可推送的课程'); + return; + } + $YuanheRepository = new YuanheRepository(); + foreach ($courses as $course) { + // 所有报名审核成功的用户id + $userIds = $course->courseSigns()->where('status', 1)->pluck('user_id'); + $users = User::whereIn('id', $userIds)->whereNotNull('company_id')->get(); + foreach ($users as $user) { + $result = $YuanheRepository->pushCourses($course, $user); + if ($result) { + $this->info("推送成功:{$course->name}-{$user->name}"); + } else { + $this->info("推送失败:{$course->name}-{$user->name}"); + } + } + } + return $this->info('全部更新完成'); + } +} diff --git a/app/Console/Commands/UpdateCompany.php b/app/Console/Commands/UpdateCompany.php index 4d8bbe9..fbfb4a9 100755 --- a/app/Console/Commands/UpdateCompany.php +++ b/app/Console/Commands/UpdateCompany.php @@ -2,8 +2,10 @@ namespace App\Console\Commands; +use App\Models\Company; use App\Models\User; use App\Repositories\MeetRepository; +use App\Repositories\YuanheRepository; use Illuminate\Console\Command; @@ -14,7 +16,7 @@ class UpdateCompany extends Command * * @var string */ - protected $signature = 'update_company'; + protected $signature = 'update_company {--user_id=}'; /** * The console command description. @@ -40,10 +42,103 @@ class UpdateCompany extends Command */ public function handle() { - $users = User::get(); + $user_id = $this->option('user_id'); + // 更新公司信息 + $this->compnay($user_id); + // 更新经纬度信息 + $this->local($user_id); + return $this->info('全部更新完成'); + } - return $this->info('更新完成'); + /** + * 更新公司信息 + */ + public function compnay($user_id = null) + { + if ($user_id) { + // 强制单个更新 + $users = User::where('id', $user_id)->get(); + } else { + // 批量更新 + $users = User::whereDoesntHave('company')->get(); + } + $YuanheRepository = new YuanheRepository(); + foreach ($users as $user) { + // 获取公司详细信息 + $result = $YuanheRepository->companyInfo(['enterpriseName' => $user->company_name]); + if (!$result) { + $this->info($user->company_name . '公司不存在'); + continue; + } + $where = ['company_name' => $result['enterpriseName']]; + $data = [ + 'company_address' => $result['address'], + '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' => $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' => $result['tagList'], + ]; + $company = Company::updateOrCreate($where, $data); + // 更新用户关联 + $user->company_id = $company->id; + $user->save(); + $this->info($result['enterpriseName'] . '-更新成功'); + } + return $this->info('公司信息-全部更新完成'); } + /** + * 更新经纬度信息 + */ + public function local($user_id = null) + { + if ($user_id) { + // 强制单个更新 + $user = User::find($user_id); + if (empty($user->company_id)) { + return false; + } + $companys = Company::where('id', $user->company_id)->get(); + } else { + // 批量更新 + $companys = Company::whereNull('company_longitude') + ->whereNotNUll('company_address') + ->where('company_address', '!=', '') + ->get(); + } + // 每3个数据分一个chunk 。接口限制了一秒只能3次请求 + $companys = $companys->chunk(3); + foreach ($companys as $company) { + foreach ($company as $item) { + $local = Company::addressTolocation($item->company_address); + $item->company_longitude = $local['lng']; + $item->company_latitude = $local['lat']; + $item->save(); + $this->info($item->company_name . "-{$local['lng']}-{$local['lat']}-经纬度信息更新成功"); + } + sleep(1); + } + return true; + } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index c669aed..4d120d6 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -24,6 +24,8 @@ class Kernel extends ConsoleKernel $schedule->command('check_birthday')->dailyAt('09:00'); // 邮件群发 $schedule->command('send_email')->everyMinute(); + // 推送课程人员信息 + $schedule->command('push_courses')->dailyAt('23:00'); } /** diff --git a/app/Http/Controllers/Admin/CourseContentController.php b/app/Http/Controllers/Admin/CourseContentController.php index ddd85cc..7af167d 100755 --- a/app/Http/Controllers/Admin/CourseContentController.php +++ b/app/Http/Controllers/Admin/CourseContentController.php @@ -150,8 +150,8 @@ class CourseContentController extends BaseController * @OA\Parameter(name="id", in="query", @OA\Schema(type="integer"), required=true, description="课程ID(存在则更新,不存在则新增)"), * @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="验证token"), * @OA\Parameter(name="course_id", in="query", @OA\Schema(type="integer"), description="课程ID"), - * @OA\Parameter(name="start_time", in="query", @OA\Schema(type="string"), description="开始时间"), - * @OA\Parameter(name="end_time", in="query", @OA\Schema(type="string"), description="结束时间"), + * @OA\Parameter(name="start_time", in="query", @OA\Schema(type="string"), description="开始时间,例如:11:00"), + * @OA\Parameter(name="end_time", in="query", @OA\Schema(type="string"), description="结束时间,例如:11:00"), * @OA\Parameter(name="date", in="query", @OA\Schema(type="string", format="date"), description="日期"), * @OA\Parameter(name="teacher_id", in="query", @OA\Schema(type="integer"), description="老师ID"), * @OA\Parameter(name="address", in="query", @OA\Schema(type="string"), description="地址"), diff --git a/app/Http/Controllers/Admin/CourseController.php b/app/Http/Controllers/Admin/CourseController.php index 813af48..04b30e3 100755 --- a/app/Http/Controllers/Admin/CourseController.php +++ b/app/Http/Controllers/Admin/CourseController.php @@ -11,6 +11,7 @@ use App\Models\CustomForm; use App\Models\User; use App\Notifications\CourseContentNotify; use EasyWeChat\Factory; +use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Validator; diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 6ebdb1f..6b02290 100755 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -242,9 +242,19 @@ class UserController extends BaseController $q->where('year', $year); }); })->count(); + // 年度培养学员 + $year_training_total = $this->model->whereHas('courseSigns', function ($query) use ($year) { + $query->where('status', 1)->whereHas('course', function ($q) use ($year) { + $q->where('year', $year); + }); + })->count(); + // 累计培养学员 + $training_total = $this->model->whereHas('courseSigns', function ($query) use ($year) { + $query->where('status', 1); + })->count(); $list = $list->paginate($all['page_size'] ?? 20); } - return $this->success(['list' => $list, 'year_total' => $year_total, 'total' => $total]); + return $this->success(['list' => $list, 'year_total' => $year_total, 'total' => $total, 'year_training_total' => $year_training_total, 'training_total' => $training_total]); } /** diff --git a/app/Http/Controllers/Mobile/CourseController.php b/app/Http/Controllers/Mobile/CourseController.php index b49e1d2..cf5ed68 100755 --- a/app/Http/Controllers/Mobile/CourseController.php +++ b/app/Http/Controllers/Mobile/CourseController.php @@ -450,6 +450,46 @@ class CourseController extends CommonController return $this->success(compact('list')); } + /** + * @OA\Get( + * path="/api/mobile/course/distance", + * tags={"小程序-计算距离"}, + * summary="签到", + * @OA\Parameter(name="course_content_id", in="query", @OA\Schema(type="string"), required=false, description="课表id"), + * @OA\Parameter(name="longitude", in="query", @OA\Schema(type="string"), required=false, description="longitude"), + * @OA\Parameter(name="latitude", in="query", @OA\Schema(type="string"), required=false, description="latitude"), + * @OA\Response( + * response=200, + * description="操作成功" + * ) + * ) + */ + public function distance() + { + $all = \request()->all(); + $messages = [ + 'longitude.required' => '经度必填', + 'latitude.required' => '纬度必填', + 'course_content_id.required' => '课程id必填', + ]; + $validator = Validator::make($all, [ + 'longitude' => 'required', + 'latitude' => 'required', + 'course_content_id' => 'required' + ], $messages); + if ($validator->fails()) { + return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); + } + // 获取打卡范围,千米 + $content_check_range = Config::getValueByKey('content_check_range'); + $courseContent = CourseContent::find($all['course_content_id']); + $distance = getDistance($courseContent->longitude, $courseContent->latitude, $all['longitude'], $all['latitude']); + if ($distance > $content_check_range) { + return $this->fail([ResponseCode::ERROR_BUSINESS, '超出打卡范围']); + } + return $this->success('成功'); + } + /** * @OA\Get( * path="/api/mobile/course/content-check", @@ -487,6 +527,10 @@ class CourseController extends CommonController if ($distance > $content_check_range) { return $this->fail([ResponseCode::ERROR_BUSINESS, '超出打卡范围']); } + // 判断当天才能签到 + if (date('Y-m-d') != $courseContent->date) { + return $this->fail([ResponseCode::ERROR_BUSINESS, '不在签到时间']); + } CourseContentCheck::create([ 'course_content_id' => $all['course_content_id'], 'user_id' => $this->getUserId(), diff --git a/app/Http/Controllers/Mobile/OtherController.php b/app/Http/Controllers/Mobile/OtherController.php index 6939c15..137b970 100755 --- a/app/Http/Controllers/Mobile/OtherController.php +++ b/app/Http/Controllers/Mobile/OtherController.php @@ -5,10 +5,13 @@ namespace App\Http\Controllers\Mobile; +use App\Helpers\ResponseCode; use App\Models\AppointmentConfig; use App\Models\AppointmentType; use App\Models\Banner; use App\Models\Config; +use App\Repositories\YuanheRepository; +use Illuminate\Support\Facades\Validator; class OtherController extends CommonController { @@ -62,5 +65,36 @@ class OtherController extends CommonController return $this->success($config); } + /** + * @OA\Get( + * path="/api/mobile/other/company", + * tags={"小程序-其他"}, + * summary="公司搜索", + * @OA\Parameter(name="company_name", in="query", @OA\Schema(type="integer"), required=true, description="公司名字"), + * @OA\Response( + * response=200, + * description="操作成功" + * ) + * ) + */ + public function company() + { + $all = \request()->all(); + $messages = [ + 'company_name.required' => '公司名称必填', + ]; + $validator = Validator::make($all, [ + 'company_name' => 'required', + ], $messages); + if ($validator->fails()) { + return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]); + } + $YuanheRepository = new YuanheRepository(); + $result = $YuanheRepository->companyInfo(['enterpriseName' => $all['company_name']]); + if (!$result) { + return $this->fail([ResponseCode::ERROR_PARAMETER, '获取失败']); + } + return $this->success($result); + } } diff --git a/app/Http/Controllers/Mobile/UserController.php b/app/Http/Controllers/Mobile/UserController.php index 76ab4ab..c1729a3 100755 --- a/app/Http/Controllers/Mobile/UserController.php +++ b/app/Http/Controllers/Mobile/UserController.php @@ -17,7 +17,9 @@ use App\Models\ScoreLog; use App\Models\ThirdAppointmentLog; use App\Models\User; use App\Repositories\DoorRepository; +use App\Repositories\YuanheRepository; use EasyWeChat\Factory; +use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; @@ -176,6 +178,11 @@ class UserController extends CommonController } $model->fill($all); $model->save(); + // 如果有公司信息,就更新一下公司 + if (isset($all['company_name']) && !empty($all['company_name'])) { + // 调用命令行更新 + Artisan::call("update_company --user_id={$model->id}"); + } // 判断下,如果用户新加入车牌号,并且有未开始或者进行中的预约,则直接预约车牌号 $appointmentModel = Appointment::where('user_id', $this->getUserId()) ->where('status', 1) diff --git a/app/Models/Company.php b/app/Models/Company.php index 61828b1..5b63378 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -9,4 +9,41 @@ class Company extends SoftDeletesModel return $this->hasMany(User::class, 'company_id', 'id'); } + + /** + * 地址转经纬度 + */ + 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, + ]; + } + } + + } diff --git a/app/Models/User.php b/app/Models/User.php index 09c92b2..570d36d 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -164,6 +164,11 @@ class User extends Authenticatable implements Auditable return $this->hasMany(Appointment::class, 'user_id', 'id'); } + public function company() + { + return $this->hasOne(Company::class, 'id', 'company_id'); + } + /** * 获取预约剩余次数 */ diff --git a/app/Repositories/YuanheRepository.php b/app/Repositories/YuanheRepository.php new file mode 100755 index 0000000..9da24e5 --- /dev/null +++ b/app/Repositories/YuanheRepository.php @@ -0,0 +1,97 @@ +baseUrl = 'https://uat.oriza.com'; + $this->customerId = '1947941625517604864'; + $this->authKey = '59C8ED8584EE4BA7BC22FC63BE45C73D'; + } + + + public function getHeader() + { + $timestamp = time() * 1000; + $token = $this->customerId . $timestamp . $this->authKey; + $token = md5($token); + $token = strtoupper($token); + + $header[] = 'Content-Type: application/json'; + $header[] = "customerId: {$this->customerId}"; + $header[] = "timestamp: {$timestamp}"; + $header[] = "token: {$token}"; + return $header; + } + + + /** + * 公司查询 + */ + public function companyInfo($params) + { + $params = json_encode($params); + $url = $this->baseUrl . '/master-service/openapi/businessCollege/enterprise/info'; + $header = $this->getHeader(); + try { + $result = httpCurl($url, 'POST', $params, $header); + $result = json_decode($result, true); + if ($result['code'] == 200) { + return $result['data']; + } else { + return false; + } + } catch (\Exception $e) { + return false; + } + } + + /** + * 数据推送 + */ + public function pushCourses(Course $course, User $user) + { + if (empty($user->company)) { + return false; + } + $params = [ + 'classTeacher' => $course->teacher->name, + 'courseName' => $course->name, + 'description' => $user->company->businessScope, + 'enterpriseName' => $user->company->company_name, + 'creditCode' => $user->company->credit_code, + 'groupId' => '1030004', + 'openTime' => $course->start_date + ]; + $params = json_encode($params, JSON_UNESCAPED_UNICODE); + $url = $this->baseUrl . '/master-service/openapi/businessCollege/shareInfo/push'; + $header = $this->getHeader(); + try { + $result = httpCurl($url, 'POST', $params, $header); + $result = json_decode($result, true); + if ($result['code'] == 200) { + return true; + } else { + return false; + } + } catch (\Exception $e) { + return false; + } + } + +} diff --git a/database/migrations/2025_06_24_111502_alert_course_contents_table.php b/database/migrations/2025_06_24_111502_alert_course_contents_table.php index 13ad53d..e95daca 100644 --- a/database/migrations/2025_06_24_111502_alert_course_contents_table.php +++ b/database/migrations/2025_06_24_111502_alert_course_contents_table.php @@ -19,9 +19,9 @@ return new class extends Migration // 纬度 $table->string('latitude')->nullable()->comment('纬度'); // 开始时间 - $table->dateTime('start_time')->nullable()->comment('开始时间'); + $table->string('start_time')->nullable()->comment('开始时间'); // 结束时间 - $table->dateTime('end_time')->nullable()->comment('结束时间'); + $table->string('end_time')->nullable()->comment('结束时间'); $table->json('file_ids')->nullable()->comment('文件id数组'); // 详细地址 $table->string('address_detail')->nullable()->comment('详细地址'); diff --git a/database/migrations/2025_07_17_162734_create_companies_table.php b/database/migrations/2025_07_17_162734_create_companies_table.php index 7f483f4..893de6a 100644 --- a/database/migrations/2025_07_17_162734_create_companies_table.php +++ b/database/migrations/2025_07_17_162734_create_companies_table.php @@ -46,6 +46,10 @@ return new class extends Migration { $table->boolean('company_need_fund')->nullable()->comment('公司是否需要融资-0否1是'); // 股东信息 $table->string('company_shareholder')->nullable()->comment('股东信息'); + // 经度 + $table->string('company_longitude')->nullable()->comment('经度'); + // 纬度 + $table->string('company_latitude')->nullable()->comment('纬度'); $table->string('company_industry')->nullable()->comment('公司所属行业'); $table->text('company_introduce')->nullable()->comment('公司简介'); @@ -55,6 +59,37 @@ return new class extends Migration { $table->string('sales_volume')->nullable()->comment('销售额'); $table->timestamps(); $table->softDeletes(); + + // 业务范围 + $table->string('business_scope')->nullable()->comment('业务范围'); + // 联系邮箱 + $table->string('contact_mail')->nullable()->comment('联系邮箱'); + // 联系手机 + $table->string('contact_phone')->nullable()->comment('联系手机'); + // 统一社会代码 + $table->string('credit_code')->nullable()->comment('统一社会代码'); + // 企业id + $table->string('enterprise_id')->nullable()->comment('企业id'); + // 是否境外 + $table->tinyInteger('is_abroad')->nullable()->comment('是否境外'); + // 是否元禾已投 + $table->tinyInteger('is_yh_invested')->nullable()->comment('是否元禾已投'); + // 企业logo + $table->string('logo')->nullable()->comment('企业logo'); + // 注册金额 + $table->string('regist_amount')->nullable()->comment('注册金额'); + // 注册资本币种 + $table->string('regist_capi_type')->nullable()->comment('注册资本币种'); + // 企业状态 + $table->string('status')->nullable()->comment('企业状态'); + // 上市日期 + $table->string('stock_date')->nullable()->comment('上市日期'); + // 股票代码 + $table->string('stock_number')->nullable()->comment('股票代码'); + // 上市类型 + $table->string('stock_type')->nullable()->comment('上市类型'); + // 注册资本币种 + $table->string('currency_type')->nullable()->comment('注册资本币种'); }); } diff --git a/database/migrations/2025_07_18_102212_alert_course_types_table.php b/database/migrations/2025_07_18_102212_alert_course_types_table.php index 0b2c1ff..b707dfe 100644 --- a/database/migrations/2025_07_18_102212_alert_course_types_table.php +++ b/database/migrations/2025_07_18_102212_alert_course_types_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. * @@ -16,6 +15,8 @@ return new class extends Migration Schema::table('course_types', function (Blueprint $table) { // 是否参与统计 $table->boolean('is_chart')->default(true)->comment('是否参与统计0否1是'); + // 是否推送 + $table->boolean('is_push')->default(false)->comment('是否推送0否1是'); }); } diff --git a/routes/api.php b/routes/api.php index 09f7642..7c26c4b 100755 --- a/routes/api.php +++ b/routes/api.php @@ -238,6 +238,8 @@ Route::group(["namespace" => "Mobile", "prefix" => "mobile"], function () { Route::get('other/config', [\App\Http\Controllers\Mobile\OtherController::class, "config"]); // 轮播图 Route::get('other/banner', [\App\Http\Controllers\Mobile\OtherController::class, "banner"]); + // 公司查询 + Route::get('other/company', [\App\Http\Controllers\Mobile\OtherController::class, "company"]); // 通知 Route::get('course/notices', [\App\Http\Controllers\Mobile\CourseController::class, "notices"]); // 课程 @@ -277,6 +279,8 @@ Route::group(["namespace" => "Mobile", "prefix" => "mobile"], function () { Route::get('course/pay', [\App\Http\Controllers\Mobile\CourseController::class, "pay"]); Route::get('course/contents', [\App\Http\Controllers\Mobile\CourseController::class, "contents"]); + // 计算距离 + Route::get('course/distance', [\App\Http\Controllers\Mobile\CourseController::class, "distance"]); // 签到 Route::get('course/content-check', [\App\Http\Controllers\Mobile\CourseController::class, "contentCheck"]); // 签到列表