BodyProfileService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <?php
  2. namespace app\common\Service;
  3. use app\common\model\BodyProfile;
  4. use app\common\model\BodyMeasurements;
  5. use app\common\model\BodyTypeSelection;
  6. use app\common\model\BodyAiReport;
  7. use think\Db;
  8. use app\common\exception\BusinessException;
  9. /**
  10. * 身体档案服务类
  11. */
  12. class BodyProfileService
  13. {
  14. /**
  15. * 创建身体档案
  16. */
  17. public static function createProfile($data)
  18. {
  19. // 处理身体照片
  20. if (isset($data['body_photos']) && is_array($data['body_photos'])) {
  21. $data['body_photos'] = json_encode($data['body_photos']);
  22. }
  23. Db::startTrans();
  24. try {
  25. $profile = new BodyProfile();
  26. $result = $profile->save($data);
  27. if ($result === false) {
  28. throw new BusinessException($profile->getError() ?: '创建档案失败');
  29. }
  30. Db::commit();
  31. return $profile;
  32. } catch (\Throwable $e) {
  33. Db::rollback();
  34. throw new BusinessException($e->getMessage());
  35. }
  36. }
  37. /**
  38. * 更新身体档案
  39. */
  40. public static function updateProfile($profileId, $userId, $data)
  41. {
  42. $profile = BodyProfile::where('id', $profileId)
  43. ->where('user_id', $userId)
  44. ->find();
  45. if (!$profile) {
  46. throw new BusinessException('档案不存在');
  47. }
  48. // 处理身体照片
  49. if (isset($data['body_photos']) && is_array($data['body_photos'])) {
  50. $data['body_photos'] = json_encode($data['body_photos']);
  51. }
  52. Db::startTrans();
  53. try {
  54. $result = $profile->save($data);
  55. if ($result === false) {
  56. throw new BusinessException($profile->getError() ?: '更新档案失败');
  57. }
  58. Db::commit();
  59. return $profile;
  60. } catch (\Throwable $e) {
  61. Db::rollback();
  62. throw new BusinessException($e->getMessage());
  63. }
  64. }
  65. /**
  66. * 删除身体档案
  67. */
  68. public static function deleteProfile($profileId, $userId)
  69. {
  70. $profile = BodyProfile::where('id', $profileId)
  71. ->where('user_id', $userId)
  72. ->find();
  73. if (!$profile) {
  74. throw new BusinessException('档案不存在');
  75. }
  76. Db::startTrans();
  77. try {
  78. // 删除相关数据
  79. BodyMeasurements::where('profile_id', $profileId)->delete();
  80. BodyTypeSelection::where('profile_id', $profileId)->delete();
  81. BodyAiReport::where('profile_id', $profileId)->delete();
  82. // 删除档案
  83. $profile->delete();
  84. Db::commit();
  85. return true;
  86. } catch (\Throwable $e) {
  87. Db::rollback();
  88. throw new BusinessException($e->getMessage());
  89. }
  90. }
  91. /**
  92. * 获取用户档案列表
  93. */
  94. public static function getUserProfiles($userId)
  95. {
  96. $profiles = BodyProfile::where('user_id', $userId)
  97. ->with(['latestMeasurement', 'latestAiReport'])
  98. ->order('is_own DESC, id DESC')
  99. ->select();
  100. $result = [];
  101. foreach ($profiles as $profile) {
  102. $data = $profile->toArray();
  103. $data['bmi'] = $profile->calculateBMI();
  104. $data['bmi_level'] = $profile->getBMILevel();
  105. $result[] = $data;
  106. }
  107. return $result;
  108. }
  109. /**
  110. * 获取档案详情
  111. */
  112. public static function getProfileDetail($profileId, $userId)
  113. {
  114. $profile = BodyProfile::where('id', $profileId)
  115. ->where('user_id', $userId)
  116. ->find();
  117. if (!$profile) {
  118. throw new BusinessException('档案不存在');
  119. }
  120. return $profile->getFullProfileData();
  121. }
  122. /**
  123. * 添加测量数据
  124. */
  125. public static function addMeasurement($profileId, $userId, $measurementData)
  126. {
  127. // 验证档案归属
  128. $profile = BodyProfile::where('id', $profileId)
  129. ->where('user_id', $userId)
  130. ->find();
  131. if (!$profile) {
  132. throw new BusinessException('档案不存在');
  133. }
  134. // 过滤测量数据字段
  135. $gender = $profile->gender ?? 0; // 确保gender不为null
  136. $measurementFieldsMap = BodyMeasurements::getMeasurementFields($gender);
  137. if (!$measurementFieldsMap || !is_array($measurementFieldsMap)) {
  138. throw new BusinessException('无法获取测量字段配置');
  139. }
  140. $measurementFields = array_keys($measurementFieldsMap);
  141. $filteredData = ['profile_id' => $profileId];
  142. foreach ($measurementFields as $field) {
  143. if (isset($measurementData[$field]) && $measurementData[$field] !== '') {
  144. $filteredData[$field] = floatval($measurementData[$field]);
  145. }
  146. }
  147. // 设置测量日期
  148. if (isset($measurementData['measurement_date']) && $measurementData['measurement_date']) {
  149. $filteredData['measurement_date'] = strtotime($measurementData['measurement_date']);
  150. } else {
  151. $filteredData['measurement_date'] = time();
  152. }
  153. $measurement = new BodyMeasurements();
  154. $result = $measurement->save($filteredData);
  155. if ($result === false) {
  156. throw new BusinessException($measurement->getError() ?: '添加测量数据失败');
  157. }
  158. return $measurement;
  159. }
  160. /**
  161. * 获取测量历史
  162. */
  163. public static function getMeasurementHistory($profileId, $userId, $page = 1, $limit = 10)
  164. {
  165. // 验证档案归属
  166. $profile = BodyProfile::where('id', $profileId)
  167. ->where('user_id', $userId)
  168. ->find();
  169. if (!$profile) {
  170. throw new BusinessException('档案不存在');
  171. }
  172. $list = BodyMeasurements::where('profile_id', $profileId)
  173. ->order('measurement_date DESC')
  174. ->paginate($limit, false, ['page' => $page]);
  175. return [
  176. 'list' => $list->items(),
  177. 'total' => $list->total(),
  178. 'page' => $page,
  179. 'limit' => $limit
  180. ];
  181. }
  182. /**
  183. * 保存体型选择
  184. */
  185. public static function saveBodyTypeSelection($profileId, $userId, $selections)
  186. {
  187. // 验证档案归属
  188. $profile = BodyProfile::where('id', $profileId)
  189. ->where('user_id', $userId)
  190. ->find();
  191. if (!$profile) {
  192. throw new BusinessException('档案不存在');
  193. }
  194. $result = BodyTypeSelection::saveUserSelections($profileId, $selections);
  195. if (!$result) {
  196. throw new BusinessException('保存体型选择失败');
  197. }
  198. return true;
  199. }
  200. /**
  201. * 生成AI测试报告
  202. */
  203. public static function generateAiReport($profileId, $userId, $reportType = 'comprehensive')
  204. {
  205. // 验证档案归属
  206. $profile = BodyProfile::where('id', $profileId)
  207. ->where('user_id', $userId)
  208. ->find();
  209. if (!$profile) {
  210. throw new BusinessException('档案不存在');
  211. }
  212. $report = BodyAiReport::generateReport($profileId, $reportType);
  213. if (!$report) {
  214. throw new BusinessException('生成报告失败,请确保已填写完整的身体数据');
  215. }
  216. return $report;
  217. }
  218. /**
  219. * 获取AI报告详情
  220. */
  221. public static function getAiReport($reportId, $userId)
  222. {
  223. $report = BodyAiReport::with(['profile'])
  224. ->where('id', $reportId)
  225. ->find();
  226. if (!$report || $report->profile->user_id != $userId) {
  227. throw new BusinessException('报告不存在');
  228. }
  229. return $report;
  230. }
  231. /**
  232. * 获取AI报告列表
  233. */
  234. public static function getAiReportList($profileId, $userId, $page = 1, $limit = 10)
  235. {
  236. // 验证档案归属
  237. $profile = BodyProfile::where('id', $profileId)
  238. ->where('user_id', $userId)
  239. ->find();
  240. if (!$profile) {
  241. throw new BusinessException('档案不存在');
  242. }
  243. $list = BodyAiReport::where('profile_id', $profileId)
  244. ->order('generated_time DESC')
  245. ->paginate($limit, false, ['page' => $page]);
  246. return [
  247. 'list' => $list->items(),
  248. 'total' => $list->total(),
  249. 'page' => $page,
  250. 'limit' => $limit
  251. ];
  252. }
  253. /**
  254. * 获取体型选择建议报告
  255. */
  256. public static function getSelectionReport($profileId, $userId)
  257. {
  258. // 验证档案归属
  259. $profile = BodyProfile::where('id', $profileId)
  260. ->where('user_id', $userId)
  261. ->find();
  262. if (!$profile) {
  263. throw new BusinessException('档案不存在');
  264. }
  265. return BodyTypeService::generateSelectionReport($profileId);
  266. }
  267. /**
  268. * 保存测量数据和体型选择(合并业务逻辑)
  269. */
  270. public static function saveMeasurementAndBodyType($profileId, $userId, $measurementData = [], $bodyTypeSelections = [], $profileUpdateData = [])
  271. {
  272. // 验证档案归属
  273. $profile = BodyProfile::where('id', $profileId)
  274. ->where('user_id', $userId)
  275. ->find();
  276. if (!$profile) {
  277. throw new BusinessException('档案不存在');
  278. }
  279. $result = [
  280. 'measurement_saved' => false,
  281. 'body_type_saved' => false,
  282. 'measurement_id' => null,
  283. 'profile_updated' => false
  284. ];
  285. Db::startTrans();
  286. try {
  287. // 保存测量数据
  288. if (!empty($measurementData)) {
  289. $measurement = self::addMeasurement($profileId, $userId, $measurementData);
  290. $result['measurement_saved'] = true;
  291. $result['measurement_id'] = $measurement->id;
  292. }
  293. // 保存体型选择
  294. if (!empty($bodyTypeSelections)) {
  295. $selectionResult = self::saveBodyTypeSelection($profileId, $userId, $bodyTypeSelections);
  296. $result['body_type_saved'] = $selectionResult;
  297. }
  298. // 更新档案信息(身高体重)
  299. if (!empty($profileUpdateData)) {
  300. $updateResult = $profile->save($profileUpdateData);
  301. if ($updateResult === false) {
  302. throw new BusinessException($profile->getError() ?: '更新档案信息失败');
  303. }
  304. $result['profile_updated'] = true;
  305. }
  306. Db::commit();
  307. return $result;
  308. } catch (\Throwable $e) {
  309. Db::rollback();
  310. throw new BusinessException($e->getMessage());
  311. }
  312. }
  313. /**
  314. * 更新测量数据和体型选择(合并业务逻辑)
  315. */
  316. public static function updateMeasurementAndBodyType($profileId, $userId, $measurementData = [], $bodyTypeSelections = [], $profileUpdateData = [])
  317. {
  318. // 验证档案归属
  319. $profile = BodyProfile::where('id', $profileId)
  320. ->where('user_id', $userId)
  321. ->find();
  322. if (!$profile) {
  323. throw new BusinessException('档案不存在');
  324. }
  325. $result = [
  326. 'measurement_updated' => false,
  327. 'body_type_updated' => false,
  328. 'profile_updated' => false,
  329. 'measurement_id' => null
  330. ];
  331. Db::startTrans();
  332. try {
  333. // 更新测量数据(创建新的测量记录)
  334. if (!empty($measurementData)) {
  335. $measurement = self::addMeasurement($profileId, $userId, $measurementData);
  336. $result['measurement_updated'] = true;
  337. $result['measurement_id'] = $measurement->id;
  338. }
  339. // 更新体型选择(覆盖现有选择)
  340. if (!empty($bodyTypeSelections)) {
  341. $selectionResult = self::saveBodyTypeSelection($profileId, $userId, $bodyTypeSelections);
  342. $result['body_type_updated'] = $selectionResult;
  343. }
  344. // 更新档案信息(身高体重)
  345. if (!empty($profileUpdateData)) {
  346. $updateResult = $profile->save($profileUpdateData);
  347. if ($updateResult === false) {
  348. throw new BusinessException($profile->getError() ?: '更新档案信息失败');
  349. }
  350. $result['profile_updated'] = true;
  351. }
  352. Db::commit();
  353. return $result;
  354. } catch (\Throwable $e) {
  355. Db::rollback();
  356. throw new BusinessException($e->getMessage());
  357. }
  358. }
  359. }