BodyProfile.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. <?php
  2. namespace app\admin\controller;
  3. use app\common\controller\Backend;
  4. use app\common\model\BodyProfile as BodyProfileModel;
  5. use app\common\model\BodyMeasurements;
  6. use app\common\model\BodyTypeConfig;
  7. use app\common\model\BodyTypeSelection;
  8. use app\common\model\BodyAiReport;
  9. use think\Db;
  10. use think\exception\ValidateException;
  11. /**
  12. * 身体档案管理
  13. */
  14. class BodyProfile extends Backend
  15. {
  16. protected $model = null;
  17. protected $noNeedLogin = [];
  18. protected $noNeedRight = [];
  19. protected $searchFields = 'profile_name,relation';
  20. public function _initialize()
  21. {
  22. parent::_initialize();
  23. $this->model = new BodyProfileModel;
  24. }
  25. /**
  26. * 查看列表
  27. */
  28. public function index()
  29. {
  30. if ($this->request->isAjax()) {
  31. // 分页参数
  32. $page = $this->request->get('page/d', 1);
  33. $limit = $this->request->get('limit/d', 10);
  34. $search = $this->request->get('search', '');
  35. $gender = $this->request->get('gender', '');
  36. $is_own = $this->request->get('is_own', '');
  37. $where = [];
  38. // 搜索条件
  39. if ($search) {
  40. $where[] = ['profile_name|relation', 'like', '%' . $search . '%'];
  41. }
  42. if ($gender !== '') {
  43. $where[] = ['gender', '=', $gender];
  44. }
  45. if ($is_own !== '') {
  46. $where[] = ['is_own', '=', $is_own];
  47. }
  48. // 查询数据
  49. $list = $this->model
  50. ->with(['user', 'latestMeasurement'])
  51. ->where($where)
  52. ->order('id DESC')
  53. ->paginate($limit)
  54. ->each(function($item) {
  55. // 添加额外信息
  56. $item['bmi'] = $item->calculateBMI();
  57. $item['bmi_level'] = $item->getBMILevel();
  58. return $item;
  59. });
  60. return json(['code' => 0, 'msg' => '', 'count' => $list->total(), 'data' => $list->items()]);
  61. }
  62. return $this->view->fetch();
  63. }
  64. /**
  65. * 添加档案
  66. */
  67. public function add()
  68. {
  69. if ($this->request->isPost()) {
  70. $params = $this->request->post('row/a');
  71. if (empty($params)) {
  72. $this->error(__('Parameter %s can not be empty', ''));
  73. }
  74. // 验证必填字段
  75. if (empty($params['profile_name']) || empty($params['user_id'])) {
  76. $this->error('档案名称和用户ID不能为空');
  77. }
  78. // 处理身体照片
  79. if (isset($params['body_photos']) && is_array($params['body_photos'])) {
  80. $params['body_photos'] = json_encode($params['body_photos']);
  81. }
  82. Db::startTrans();
  83. try {
  84. $result = $this->model->save($params);
  85. if ($result === false) {
  86. throw new \Exception($this->model->getError());
  87. }
  88. Db::commit();
  89. } catch (\Throwable $e) {
  90. Db::rollback();
  91. $this->error($e->getMessage());
  92. }
  93. $this->success();
  94. }
  95. // 获取用户列表
  96. $userList = \app\common\model\User::field('id,username,nickname')->select();
  97. $this->assign('userList', $userList);
  98. return $this->view->fetch();
  99. }
  100. /**
  101. * 编辑档案
  102. */
  103. public function edit($ids = null)
  104. {
  105. $row = $this->model->get($ids);
  106. if (!$row) {
  107. $this->error(__('No Results were found'));
  108. }
  109. if ($this->request->isPost()) {
  110. $params = $this->request->post('row/a');
  111. if (empty($params)) {
  112. $this->error(__('Parameter %s can not be empty', ''));
  113. }
  114. // 处理身体照片
  115. if (isset($params['body_photos']) && is_array($params['body_photos'])) {
  116. $params['body_photos'] = json_encode($params['body_photos']);
  117. }
  118. Db::startTrans();
  119. try {
  120. $result = $row->save($params);
  121. if ($result === false) {
  122. throw new \Exception($row->getError());
  123. }
  124. Db::commit();
  125. } catch (\Throwable $e) {
  126. Db::rollback();
  127. $this->error($e->getMessage());
  128. }
  129. $this->success();
  130. }
  131. // 获取用户列表
  132. $userList = \app\common\model\User::field('id,username,nickname')->select();
  133. $this->assign('userList', $userList);
  134. $this->assign('row', $row);
  135. return $this->view->fetch();
  136. }
  137. /**
  138. * 删除档案
  139. */
  140. public function del($ids = null)
  141. {
  142. if (!$this->request->isPost()) {
  143. $this->error(__("Invalid parameters"));
  144. }
  145. $ids = $ids ? $ids : $this->request->post("ids");
  146. if (empty($ids)) {
  147. $this->error(__('Parameter %s can not be empty', 'ids'));
  148. }
  149. $pk = $this->model->getPk();
  150. $adminIds = $this->getDataLimitAdminIds();
  151. if (is_array($adminIds)) {
  152. $this->model->where($this->dataLimitField, 'in', $adminIds);
  153. }
  154. $list = $this->model->where($pk, 'in', $ids)->select();
  155. $count = 0;
  156. Db::startTrans();
  157. try {
  158. foreach ($list as $item) {
  159. // 删除相关数据
  160. BodyMeasurements::where('profile_id', $item->id)->delete();
  161. BodyTypeSelection::where('profile_id', $item->id)->delete();
  162. BodyAiReport::where('profile_id', $item->id)->delete();
  163. $count += $item->delete();
  164. }
  165. Db::commit();
  166. } catch (\Throwable $e) {
  167. Db::rollback();
  168. $this->error($e->getMessage());
  169. }
  170. $this->success();
  171. }
  172. /**
  173. * 查看档案详情
  174. */
  175. public function detail($ids = null)
  176. {
  177. $profile = $this->model->with(['user', 'latestMeasurement', 'bodyTypeSelections.typeConfig', 'latestAiReport'])->find($ids);
  178. if (!$profile) {
  179. $this->error('档案不存在');
  180. }
  181. // 获取完整档案数据
  182. $profileData = $profile->getFullProfileData();
  183. $this->assign('profile', $profileData);
  184. return $this->view->fetch();
  185. }
  186. /**
  187. * 测量数据管理
  188. */
  189. public function measurements($profile_id = null)
  190. {
  191. if (!$profile_id) {
  192. $this->error('档案ID不能为空');
  193. }
  194. $profile = $this->model->find($profile_id);
  195. if (!$profile) {
  196. $this->error('档案不存在');
  197. }
  198. if ($this->request->isAjax()) {
  199. // 获取测量记录
  200. $list = BodyMeasurements::where('profile_id', $profile_id)
  201. ->order('measurement_date DESC')
  202. ->paginate(10);
  203. return json(['code' => 0, 'msg' => '', 'count' => $list->total(), 'data' => $list->items()]);
  204. }
  205. $this->assign('profile', $profile);
  206. return $this->view->fetch();
  207. }
  208. /**
  209. * 添加测量数据
  210. */
  211. public function addMeasurement($profile_id = null)
  212. {
  213. if (!$profile_id) {
  214. $this->error('档案ID不能为空');
  215. }
  216. $profile = $this->model->find($profile_id);
  217. if (!$profile) {
  218. $this->error('档案不存在');
  219. }
  220. if ($this->request->isPost()) {
  221. $params = $this->request->post('row/a');
  222. if (empty($params)) {
  223. $this->error(__('Parameter %s can not be empty', ''));
  224. }
  225. $params['profile_id'] = $profile_id;
  226. // 处理测量日期
  227. if (isset($params['measurement_date']) && $params['measurement_date']) {
  228. $params['measurement_date'] = strtotime($params['measurement_date']);
  229. } else {
  230. $params['measurement_date'] = time();
  231. }
  232. $measurement = new BodyMeasurements();
  233. $result = $measurement->save($params);
  234. if ($result === false) {
  235. $this->error($measurement->getError());
  236. }
  237. $this->success();
  238. }
  239. // 获取测量字段
  240. $measurementFields = BodyMeasurements::getMeasurementFields($profile->gender);
  241. $this->assign('profile', $profile);
  242. $this->assign('measurementFields', $measurementFields);
  243. return $this->view->fetch();
  244. }
  245. /**
  246. * 体型选择管理
  247. */
  248. public function bodyTypes($profile_id = null)
  249. {
  250. if (!$profile_id) {
  251. $this->error('档案ID不能为空');
  252. }
  253. $profile = $this->model->find($profile_id);
  254. if (!$profile) {
  255. $this->error('档案不存在');
  256. }
  257. if ($this->request->isPost()) {
  258. $selections = $this->request->post('selections/a');
  259. if (empty($selections)) {
  260. $this->error('请选择体型');
  261. }
  262. $result = BodyTypeSelection::saveUserSelections($profile_id, $selections);
  263. if (!$result) {
  264. $this->error('保存失败');
  265. }
  266. $this->success();
  267. }
  268. // 获取所有体型分类和选项
  269. $bodyTypeCategories = BodyTypeConfig::getAllCategories($profile->gender);
  270. // 获取用户已选择的体型
  271. $userSelections = BodyTypeSelection::getUserSelections($profile_id);
  272. $this->assign('profile', $profile);
  273. $this->assign('bodyTypeCategories', $bodyTypeCategories);
  274. $this->assign('userSelections', $userSelections);
  275. return $this->view->fetch();
  276. }
  277. /**
  278. * 生成AI报告
  279. */
  280. public function generateReport($profile_id = null)
  281. {
  282. if (!$profile_id) {
  283. $this->error('档案ID不能为空');
  284. }
  285. $profile = $this->model->find($profile_id);
  286. if (!$profile) {
  287. $this->error('档案不存在');
  288. }
  289. $report = BodyAiReport::generateReport($profile_id);
  290. if (!$report) {
  291. $this->error('生成报告失败');
  292. }
  293. $this->success('报告生成成功', null, ['report_id' => $report->id]);
  294. }
  295. /**
  296. * 查看AI报告
  297. */
  298. public function viewReport($report_id = null)
  299. {
  300. if (!$report_id) {
  301. $this->error('报告ID不能为空');
  302. }
  303. $report = BodyAiReport::with(['profile'])->find($report_id);
  304. if (!$report) {
  305. $this->error('报告不存在');
  306. }
  307. $this->assign('report', $report);
  308. return $this->view->fetch();
  309. }
  310. /**
  311. * AI测量页面
  312. */
  313. public function aiMeasurement($profile_id = null)
  314. {
  315. if (!$profile_id) {
  316. $this->error('档案ID不能为空');
  317. }
  318. $profile = $this->model->with(['user'])->find($profile_id);
  319. if (!$profile) {
  320. $this->error('档案不存在');
  321. }
  322. $this->assign('profile', $profile);
  323. return $this->view->fetch();
  324. }
  325. /**
  326. * 统计数据
  327. */
  328. public function statistics()
  329. {
  330. // 档案统计
  331. $profileStats = [
  332. 'total' => $this->model->count(),
  333. 'male' => $this->model->where('gender', 1)->count(),
  334. 'female' => $this->model->where('gender', 2)->count(),
  335. 'own' => $this->model->where('is_own', 1)->count(),
  336. 'others' => $this->model->where('is_own', 0)->count(),
  337. ];
  338. // BMI分布统计
  339. $bmiStats = [];
  340. $profiles = $this->model->where('height', '>', 0)->where('weight', '>', 0)->select();
  341. foreach ($profiles as $profile) {
  342. $bmi = $profile->calculateBMI();
  343. $level = $profile->getBMILevel();
  344. $bmiStats[$level] = isset($bmiStats[$level]) ? $bmiStats[$level] + 1 : 1;
  345. }
  346. // 体型选择统计
  347. $bodyTypeStats = BodyTypeSelection::getSelectionStatistics();
  348. $this->assign('profileStats', $profileStats);
  349. $this->assign('bmiStats', $bmiStats);
  350. $this->assign('bodyTypeStats', $bodyTypeStats);
  351. return $this->view->fetch();
  352. }
  353. }