CoachController.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. <?php
  2. namespace App\Http\Controllers\Client;
  3. use Illuminate\Http\Request;
  4. use App\Http\Controllers\Controller;
  5. use Illuminate\Support\Facades\Auth;
  6. use App\Services\Client\CoachService;
  7. use App\Http\Requests\Client\Coach\ListNearbyCoachRequest;
  8. /**
  9. * @group 用户端
  10. *
  11. * 技师相关的API接口
  12. */
  13. class CoachController extends Controller
  14. {
  15. protected CoachService $service;
  16. public function __construct(CoachService $service)
  17. {
  18. $this->service = $service;
  19. }
  20. /**
  21. * [技师]获取附近技师列表
  22. *
  23. * @description 根据用户当前位置获取指定范围内的技师列表,支持分页和距离筛选
  24. *
  25. * 业务流程:
  26. * 1. 验证用户位置和分页参数
  27. * 2. 获取系统设置的最大搜索半径
  28. * 3. 查询指定范围内的技师
  29. * 4. 根据技师个人设置的接单距离筛选
  30. * 5. 返回分页后的技师列表
  31. *
  32. * 筛选规则:
  33. * - 技师必须通过基本信息认证
  34. * - 技师必须通过实名认证
  35. * - 技师必须通过资质认证
  36. * - 如果技师设置了接单距离,户距离必须在范围内
  37. * - 未设置接单距离的技师,只要在系统搜索半径内就显示
  38. *
  39. * 排序规则:
  40. * - 按照技师到用户的实际距离升序排序
  41. * - 未获取到距离的技师排在最后
  42. *
  43. * @authenticated 需要用户登录认证
  44. *
  45. * @queryParam page int 当前页码,默认1. Example: 1
  46. * @queryParam per_page int 每页数量,默认15. Example: 15
  47. * @queryParam latitude float required 用户当前纬度坐标(-90到90). Example: 39.9042
  48. * @queryParam longitude float required 用户当前经度坐标(-180到180). Example: 116.4074
  49. *
  50. * @response scenario=success {
  51. * "code": 200,
  52. * "message": "success",
  53. * "data": {
  54. * "items": [
  55. * {
  56. * "id": 6,
  57. * "user_id": 12,
  58. * "info_record_id": 53,
  59. * "real_auth_record_id": 1,
  60. * "qualification_record_id": 1,
  61. * "shop_id": null,
  62. * "level": 1,
  63. * "virtual_order": 0,
  64. * "score": "5.00",
  65. * "work_status": 2,
  66. * "virtual_status": 1,
  67. * "state": 2,
  68. * "created_at": "2024-11-19 18:25:04",
  69. * "updated_at": "2024-12-13 08:01:02",
  70. * "deleted_at": null,
  71. * "is_vip": 1,
  72. * "vip_time": null,
  73. * "invite_code": null,
  74. * "qr_code": null,
  75. * "formal_photo": null,
  76. * "formal_photo_remark": null,
  77. * "formal_photo_updated_at": null,
  78. * "formal_photo_admin_id": null,
  79. * "newcomer_sort": 0,
  80. * "newcomer_sort_updated_at": null,
  81. * "newcomer_sort_admin_id": null,
  82. * "distance": 0,
  83. * "state_text": "正常服务",
  84. * "info": {
  85. * "id": 53,
  86. * "nickname": "张三1",
  87. * "avatar": null,
  88. * "gender": "1",
  89. * "state_text": ""
  90. * },
  91. * "distance": 2.5 // 用户到技师的距离(公里)
  92. * }
  93. * ],
  94. * "total": 1 // 符合条件的技师总数
  95. * }
  96. * }
  97. *
  98. * @response status=401 scenario="未登录" {
  99. * "message": "用户未登录"
  100. * }
  101. * @response status=400 scenario="状态异常" {
  102. * "message": "用户状态异常"
  103. * }
  104. * @response status=422 scenario="参数错误" {
  105. * "message": "验证错误",
  106. * "errors": {
  107. * "latitude": ["纬度不能为空"],
  108. * "longitude": ["经度不能为空"]
  109. * }
  110. * }
  111. * @response status=500 scenario="系统错误" {
  112. * "message": "Redis服务不可用"
  113. * }
  114. */
  115. public function list(ListNearbyCoachRequest $request)
  116. {
  117. // 获取验证后的数据
  118. $validated = $request->validated();
  119. // 调用服务层获取技师列表
  120. return $this->success($this->service->getNearCoachList(
  121. Auth::user()->id, // 当前登录用户ID
  122. $validated['latitude'], // 用户当前纬度
  123. $validated['longitude'] // 用户当前经度
  124. ));
  125. }
  126. /**
  127. * [技师]获取技师详情
  128. *
  129. * @description 获取技师的详细信息,包括基本资料、认证状态、位置信息和距离计算
  130. *
  131. * 业务流程:
  132. * 1. 验证用户登录状态和权限
  133. * 2. 验证技师ID的有效性
  134. * 3. 获取技师基本信息和认证状态
  135. * 4. 如果提供了用户坐标,计算与技师的距离
  136. * 5. 返回完整的技师信息
  137. *
  138. * 数据验证:
  139. * - 技师必须存在且状态正常
  140. * - 技师必须通过各项认证
  141. * - 坐标格式必须正确(如果提供)
  142. *
  143. * @authenticated 需要用户登录认证
  144. *
  145. * @urlParam id required int 技师ID. Example: 6
  146. * @queryParam latitude float 用户当前纬度坐标(-90到90). Example: 39.9042
  147. * @queryParam longitude float 用户当前经度坐标(-180到180). Example: 116.4074
  148. *
  149. * @response scenario=success {
  150. * "code": 200,
  151. * "message": "success",
  152. * "data": {
  153. * "id": 6,
  154. * "user_id": 12,
  155. * "info_record_id": 53,
  156. * "real_auth_record_id": 1,
  157. * "qualification_record_id": 1,
  158. * "shop_id": null,
  159. * "level": 1,
  160. * "virtual_order": 0,
  161. * "score": "5.00",
  162. * "work_status": 2,
  163. * "virtual_status": 1,
  164. * "state": 2,
  165. * "created_at": "2024-11-19 18:25:04",
  166. * "updated_at": "2024-12-13 08:01:02",
  167. * "is_vip": 1,
  168. * "distance": 2.5, // 用户到技师的距离(公里)
  169. * "state_text": "正常服务",
  170. * "info": {
  171. * "id": 53,
  172. * "nickname": "张三1",
  173. * "avatar": null,
  174. * "gender": "1",
  175. * "state_text": ""
  176. * }
  177. * }
  178. * }
  179. *
  180. * @response status=401 scenario="未登录" {
  181. * "message": "用户未登录"
  182. * }
  183. * @response status=400 scenario="状态异常" {
  184. * "message": "用户状态异常"
  185. * }
  186. * @response status=404 scenario="不存在" {
  187. * "message": "技师不存在"
  188. * }
  189. * @response status=422 scenario="参数错误" {
  190. * "message": "无效的经纬度坐标"
  191. * }
  192. * @response status=500 scenario="系统错误" {
  193. * "message": "Redis服务不可用"
  194. * }
  195. */
  196. public function detail(Request $request, $id)
  197. {
  198. // 获取用户当前位置坐标
  199. $latitude = $request->input('latitude');
  200. $longitude = $request->input('longitude');
  201. // 调用服务层获取技师详情
  202. return $this->success($this->service->getCoachDetail($id, $latitude, $longitude));
  203. }
  204. /**
  205. * [技师]获取可预约时间段
  206. *
  207. * @description 获取指定技师的可预约时间段列表,包含日期、星期、时间段等信息
  208. *
  209. * @queryParam coach_id int required 技师ID Example: 6
  210. * @queryParam date string 日期(格式:Y-m-d) Example: 2024-03-22
  211. *
  212. * @response {
  213. * "data": {
  214. * "date": "2024-03-22",
  215. * "day_of_week": "星期五",
  216. * "is_today": false,
  217. * "time_slots": [
  218. * {
  219. * "start_time": "09:00",
  220. * "end_time": "09:30",
  221. * "is_available": true,
  222. * "duration": 30
  223. * }
  224. * ],
  225. * "total_slots": 1,
  226. * "updated_at": "2024-03-22 10:00:00"
  227. * }
  228. * }
  229. * @response 404 {
  230. * "message": "技师不存在"
  231. * }
  232. * @response 400 {
  233. * "message": "技师状态异常"
  234. * }
  235. * @response 400 {
  236. * "message": "不能查询过去的日期"
  237. * }
  238. * @response 400 {
  239. * "message": "只能查询未来30天内的时间段"
  240. * }
  241. */
  242. public function getSchedule(Request $request)
  243. {
  244. // 验证参数
  245. $validated = $request->validate([
  246. 'coach_id' => 'required|integer|exists:coach_users,id',
  247. 'date' => 'nullable|date_format:Y-m-d',
  248. ]);
  249. // 调用service获取技师可预约时间段
  250. return $this->success($this->service->getSchedule($validated['coach_id'], $validated['date'] ?? null));
  251. }
  252. }