CoachUser.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <?php
  2. namespace App\Models;
  3. use App\Models\Order;
  4. use App\Models\Wallet;
  5. use App\Models\ShopInfo;
  6. use App\Models\CoachScore;
  7. use App\Models\MemberUser;
  8. use App\Enums\ProjectStatus;
  9. use App\Models\CoachProject;
  10. use App\Models\OrderComment;
  11. use App\Traits\HasStateText;
  12. use App\Models\CoachLocation;
  13. use App\Models\CoachStatistic;
  14. use App\Enums\TechnicianStatus;
  15. use App\Models\CoachInfoRecord;
  16. use App\Models\CoachQualRecord;
  17. use App\Models\CoachRealRecord;
  18. use App\Models\OrderGrabRecord;
  19. use App\Models\ShopCoachService;
  20. use App\Enums\TechnicianLocationType;
  21. use Illuminate\Database\Eloquent\SoftDeletes;
  22. use Slowlyo\OwlAdmin\Models\BaseModel as Model;
  23. use App\Models\SettingValue;
  24. use App\Enums\TechnicianWorkStatus;
  25. use Illuminate\Database\Eloquent\Casts\Attribute;
  26. /**
  27. * 技师用户模型
  28. */
  29. class CoachUser extends Model
  30. {
  31. use HasStateText, SoftDeletes;
  32. protected $table = 'coach_users';
  33. protected $guarded = [];
  34. protected $casts = [
  35. 'vip_time' => 'datetime',
  36. ];
  37. /**
  38. * 状态枚举类
  39. */
  40. protected string $stateEnumClass = TechnicianStatus::class;
  41. /**
  42. * 创建技师时创建钱包
  43. *
  44. * @return void
  45. */
  46. protected static function booted()
  47. {
  48. static::created(function ($coach) {
  49. $coach->wallet()->create([
  50. 'owner_type' => CoachUser::class,
  51. 'owner_id' => $coach->id,
  52. ]);
  53. });
  54. }
  55. /**
  56. * @Author FelixYin
  57. *
  58. * @description 技师关联信息
  59. */
  60. public function info()
  61. {
  62. return $this->hasOne(CoachInfoRecord::class, 'id', 'info_record_id');
  63. }
  64. /**
  65. * 获取技师关联的用户
  66. */
  67. public function user()
  68. {
  69. return $this->belongsTo(MemberUser::class, 'user_id');
  70. }
  71. /**
  72. * 获取技师的统计数据
  73. */
  74. public function statistic()
  75. {
  76. return $this->hasOne(CoachStatistic::class, 'coach_id');
  77. }
  78. /**
  79. * @Author FelixYin
  80. *
  81. * @description 技师所属会员
  82. */
  83. public function member()
  84. {
  85. return $this->belongsTo(MemberUser::class, 'user_id', 'id');
  86. }
  87. /**
  88. * @Author FelixYin
  89. *
  90. * @description 基本信息认证记录
  91. */
  92. public function infoRecords()
  93. {
  94. return $this->hasMany(CoachInfoRecord::class, 'coach_id', 'id');
  95. }
  96. /**
  97. * @Author FelixYin
  98. *
  99. * @description 技师关联资质信息
  100. */
  101. public function qual()
  102. {
  103. return $this->hasOne(CoachQualRecord::class, 'id', 'qualification_record_id');
  104. }
  105. /**
  106. * @Author FelixYin
  107. *
  108. * @description 技师关联评分
  109. */
  110. public function score()
  111. {
  112. return $this->hasOne(CoachScore::class, 'coach_id', 'id');
  113. }
  114. /**
  115. * @Author FelixYin
  116. *
  117. * @description 技师关联定位
  118. */
  119. public function locations()
  120. {
  121. return $this->hasMany(CoachLocation::class, 'coach_id', 'id');
  122. }
  123. /**
  124. * @Author FelixYin
  125. *
  126. * @description 技师关联订单
  127. */
  128. public function orders()
  129. {
  130. return $this->hasMany(Order::class, 'coach_id', 'id');
  131. }
  132. /**
  133. * @Author FelixYin
  134. *
  135. * @description 技师关联抢单记录
  136. */
  137. public function grabRecords()
  138. {
  139. return $this->hasMany(OrderGrabRecord::class, 'coach_id', 'id');
  140. }
  141. /**
  142. * @Author FelixYin
  143. *
  144. * @description 技师关联评论
  145. */
  146. public function comments()
  147. {
  148. return $this->hasMany(OrderComment::class, 'coach_id', 'id');
  149. }
  150. /**
  151. * @Author FelixYin
  152. *
  153. * @description 技师关联钱包
  154. */
  155. public function wallet()
  156. {
  157. return $this->morphOne(Wallet::class, 'owner', 'owner_type', 'owner_id', 'id');
  158. }
  159. /**
  160. * @Author FelixYin
  161. *
  162. * @description 技师所属店铺
  163. */
  164. public function shop()
  165. {
  166. return $this->belongsTo(ShopInfo::class, 'shop_id', 'shop_id', 'id');
  167. }
  168. /**
  169. * @Author FelixYin
  170. *
  171. * @description 技师关联店铺开通服务
  172. */
  173. public function shopOpenService()
  174. {
  175. return $this->hasMany(ShopCoachService::class, 'coach_id', 'id');
  176. }
  177. /**
  178. * @Author FelixYin
  179. *
  180. * @description 技师关联实名信息
  181. */
  182. public function real()
  183. {
  184. return $this->hasOne(CoachRealRecord::class, 'id', 'real_auth_record_id');
  185. }
  186. /**
  187. * @Author FelixYin
  188. *
  189. * @description 技师开通项目
  190. */
  191. public function projects()
  192. {
  193. return $this->hasMany(CoachProject::class, 'coach_id', 'id');
  194. }
  195. /**
  196. * @Author FelixYin
  197. *
  198. * @description 技师关联资质记录
  199. */
  200. public function qualRecords()
  201. {
  202. return $this->hasMany(CoachQualRecord::class, 'coach_id', 'id');
  203. }
  204. /**
  205. * @Author FelixYin
  206. *
  207. * @description 技师关联实名认证记录
  208. */
  209. public function realAuthRecords()
  210. {
  211. return $this->hasMany(CoachRealRecord::class, 'coach_id', 'id');
  212. }
  213. /**
  214. * 验证技师状态是否正常
  215. *
  216. * @param string|null $message 自定义错误消息
  217. * @throws \Illuminate\Http\Exceptions\HttpResponseException 当技师状态异常时抛出异常
  218. */
  219. public function validateActiveStatus(?string $message = null): void
  220. {
  221. abort_if(
  222. $this->state != TechnicianStatus::ACTIVE->value,
  223. 422,
  224. $message ?? '技师状态异常'
  225. );
  226. }
  227. /**
  228. * 创建或更新技师项目关联
  229. *
  230. * @param int $projectId 项目ID
  231. * @param int $state 项目状态
  232. * @return CoachProject 返回创建或更新的技师项目关联实例
  233. */
  234. public function updateOrCreateProjectRelation(int $projectId, int $state): CoachProject
  235. {
  236. return $this->projects()->updateOrCreate(
  237. ['project_id' => $projectId],
  238. [
  239. 'state' => $state,
  240. 'discount_amount' => 0.00, // 默认折扣金额
  241. 'service_gender' => 0, // 默认服务性别(不限)
  242. 'service_distance' => 0, // 默认服务距离
  243. 'traffic_fee_type' => 2, // 默认交通费类型(双程)
  244. ]
  245. );
  246. }
  247. /**
  248. * 获取最新的基本信息审核记录
  249. * 通过 latest() 获取最新的一条记录
  250. *
  251. * 业务逻辑:
  252. * 1. 建立与基本信息记录表的一对一关联
  253. * 2. 按创建时间倒序排序
  254. * 3. 获取最新的一条记录
  255. *
  256. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  257. */
  258. public function latestInfoRecord()
  259. {
  260. // 一对一关联,并按创建时间倒序获取最新记录
  261. return $this->hasOne(CoachInfoRecord::class, 'coach_id')
  262. ->latest();
  263. }
  264. /**
  265. * 获取最新的实名认证审核记录
  266. * 通过 latest() 获取最新的一条记录
  267. *
  268. * 业务逻辑:
  269. * 1. 建立与实名认证记录表的一对一关联
  270. * 2. 按创建时间倒序排序
  271. * 3. 获取最新的一条记录
  272. *
  273. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  274. */
  275. public function latestRealRecord()
  276. {
  277. // 一对一关联,并按创建时间倒序获取最新记录
  278. return $this->hasOne(CoachRealRecord::class, 'coach_id')
  279. ->latest();
  280. }
  281. /**
  282. * 获取最新的资质认证审核记录
  283. * 通过 latest() 获取最新的一条记录
  284. *
  285. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  286. */
  287. public function latestQualRecord()
  288. {
  289. // 一对一关联,并按创建时间倒序获取最新记录
  290. return $this->hasOne(CoachQualRecord::class, 'coach_id')
  291. ->latest();
  292. }
  293. /**
  294. * 获取常用位置
  295. * 只获取类型为常用位置的记录
  296. *
  297. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  298. */
  299. public function commonLocation()
  300. {
  301. // 一对一关联,并限定位置类型为常用位置
  302. return $this->hasOne(CoachLocation::class, 'coach_id')
  303. ->where('type', TechnicianLocationType::COMMON->value);
  304. }
  305. /**
  306. * 获取技师统计数据
  307. * 包含订单数、评分等统计信息
  308. *
  309. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  310. */
  311. public function statistics()
  312. {
  313. // 一对一关联技师统计数据
  314. return $this->hasOne(CoachStatistic::class, 'coach_id');
  315. }
  316. /**
  317. * @Author FelixYin
  318. *
  319. * @description 技师关联设置
  320. */
  321. public function settings()
  322. {
  323. return $this->morphMany(SettingValue::class, 'object');
  324. }
  325. /**
  326. * 获取工作状态的文字说明
  327. */
  328. protected function workStatusText(): Attribute
  329. {
  330. return Attribute::make(
  331. get: function ($value, array $attributes) {
  332. if (!isset($attributes['work_status'])) {
  333. return '';
  334. }
  335. $status = TechnicianWorkStatus::tryFrom($attributes['work_status']);
  336. return $status ? $status->label() : '';
  337. }
  338. );
  339. }
  340. }