true, // 经期前提醒 'ovulation' => true, // 排卵日提醒 'record' => true, // 每日记录提醒 'care' => true, // 关怀提醒(家人共护) 'time' => '21:00', ]; /** * 读取设置 */ public function index() { $setting = Db::name('user_setting')->where('user_id', $this->auth->id)->find(); if (!$setting) { $setting = [ 'app_lock' => 'none', 'local_only' => 0, 'remind_json' => json_encode($this->defaultRemind), ]; } $remind = json_decode($setting['remind_json'], true) ?: $this->defaultRemind; $remind = array_merge($this->defaultRemind, $remind); $this->success('', [ 'app_lock' => $setting['app_lock'], 'has_lock_code' => !empty($setting['app_lock_value']), 'local_only' => (int)$setting['local_only'], 'remind' => $remind, ]); } /** * 保存设置 * POST app_lock=none|password|biometric, app_lock_code=1234, local_only=0|1, remind={"period":true,...} */ public function save() { $userId = $this->auth->id; $exist = Db::name('user_setting')->where('user_id', $userId)->find(); $data = ['updatetime' => time()]; $appLock = $this->request->post('app_lock', null); if ($appLock !== null) { if (!in_array($appLock, ['none', 'password', 'biometric'])) { $this->error('应用锁类型不正确'); } $data['app_lock'] = $appLock; } $code = (string)$this->request->post('app_lock_code', ''); if ($code !== '') { if (!preg_match('/^\d{4,6}$/', $code)) { $this->error('应用锁密码需为 4-6 位数字'); } // 仅存哈希,不落明文 $data['app_lock_value'] = md5($code . 'huāqī'); } $localOnly = $this->request->post('local_only', null); if ($localOnly !== null) { $data['local_only'] = (int)!!$localOnly; } $remind = $this->readRemind(); if ($remind !== null) { $merged = array_merge($this->defaultRemind, array_intersect_key($remind, $this->defaultRemind)); if (isset($merged['time']) && !preg_match('/^\d{2}:\d{2}$/', $merged['time'])) { $merged['time'] = '21:00'; } $data['remind_json'] = json_encode($merged, JSON_UNESCAPED_UNICODE); } if ($exist) { Db::name('user_setting')->where('id', $exist['id'])->update($data); } else { $data['user_id'] = $userId; $data['createtime'] = time(); Db::name('user_setting')->insert($data); } $this->success('已保存'); } /** * 读取提醒配置:支持 remind_xxx 独立字段(推荐,兼容 request filter)与 remind JSON * @return array|null */ protected function readRemind() { $keys = ['period', 'ovulation', 'record', 'care', 'time']; $result = []; $hasField = false; foreach ($keys as $key) { $value = $this->request->post('remind_' . $key, null); if ($value !== null) { $hasField = true; $result[$key] = $key === 'time' ? (string)$value : (bool)(int)$value; } } if ($hasField) { return $result; } // 兼容 JSON 传参(注意:全局 filter 会把引号转义,需还原) $json = $this->request->post('remind', null); if ($json === null || $json === '') { return null; } $arr = is_array($json) ? $json : json_decode(htmlspecialchars_decode((string)$json, ENT_QUOTES), true); return is_array($arr) ? $arr : null; } /** * 自定义提醒列表(M1/M6 智能提醒) */ public function reminderList() { $rows = \think\Db::name('reminder') ->where('user_id', $this->auth->id) ->whereNull('deletetime') ->order('enabled desc, remind_time asc') ->select(); $this->success('', ['list' => array_map([$this, 'formatReminder'], $rows)]); } /** * 新增 / 修改提醒 * POST id, title, type, remind_time, repeat_rule, remind_date, enabled */ public function reminderSave() { $userId = $this->auth->id; $id = (int)$this->request->post('id', 0); $title = mb_substr(trim((string)$this->request->post('title', '')), 0, 60); if (!$title) { $this->error('请填写提醒内容'); } $time = (string)$this->request->post('remind_time', '21:00'); if (!preg_match('/^\d{2}:\d{2}$/', $time)) { $this->error('提醒时间格式不正确'); } $repeat = $this->request->post('repeat_rule', 'daily'); if (!in_array($repeat, ['daily', 'once', 'weekly'], true)) { $repeat = 'daily'; } $date = (string)$this->request->post('remind_date', ''); $data = [ 'user_id' => $userId, 'title' => $title, 'type' => $this->request->post('type', 'custom'), 'remind_time' => $time, 'repeat_rule' => $repeat, 'remind_date' => $date && strtotime($date) ? $date : null, 'enabled' => (int)$this->request->post('enabled', 1), 'updatetime' => time(), ]; if ($id) { $row = \think\Db::name('reminder')->where('id', $id)->where('user_id', $userId)->find(); if (!$row) { $this->error('提醒不存在'); } \think\Db::name('reminder')->where('id', $id)->update($data); } else { $data['createtime'] = time(); $id = \think\Db::name('reminder')->insertGetId($data); } $this->success('已保存', ['id' => (int)$id]); } /** * 删除提醒 */ public function reminderRemove() { $id = (int)$this->request->post('id', 0); $row = \think\Db::name('reminder')->where('id', $id)->where('user_id', $this->auth->id)->find(); if (!$row) { $this->error('提醒不存在'); } \think\Db::name('reminder')->where('id', $id)->update(['deletetime' => time()]); $this->success('已删除'); } /** * 数据备份:创建快照(本地/云端同步的前提能力) */ public function backupCreate() { $userId = $this->auth->id; $payload = $this->collectData($userId); $json = json_encode($payload, JSON_UNESCAPED_UNICODE); $id = \think\Db::name('user_backup')->insertGetId([ 'user_id' => $userId, 'name' => '备份 ' . date('Y-m-d H:i'), 'size' => strlen($json), 'payload' => $json, 'createtime' => time(), ]); // 仅保留最近 5 份,避免存储膨胀 $ids = \think\Db::name('user_backup') ->where('user_id', $userId) ->order('id', 'desc') ->limit(100) ->column('id'); if (count($ids) > 5) { \think\Db::name('user_backup')->whereIn('id', array_slice($ids, 5))->delete(); } $this->success('备份完成', [ 'id' => (int)$id, 'size' => strlen($json), 'counts' => $payload['counts'], ]); } /** * 备份列表 */ public function backupList() { $rows = \think\Db::name('user_backup') ->where('user_id', $this->auth->id) ->order('id', 'desc') ->field('id,name,size,createtime') ->select(); $list = array_map(function ($row) { return [ 'id' => (int)$row['id'], 'name' => $row['name'], 'size' => (int)$row['size'], 'size_text' => round($row['size'] / 1024, 1) . ' KB', 'date' => date('Y-m-d H:i', (int)$row['createtime']), ]; }, $rows); $this->success('', ['list' => $list]); } /** * 从备份恢复(仅恢复本人数据,先清理同范围记录) */ public function backupRestore() { $userId = $this->auth->id; $id = (int)$this->request->post('id', 0); $row = \think\Db::name('user_backup')->where('id', $id)->where('user_id', $userId)->find(); if (!$row) { $this->error('备份不存在'); } $payload = json_decode($row['payload'], true); if (!is_array($payload)) { $this->error('备份内容损坏'); } $restored = []; $map = [ 'cyle' => 'cycle', ]; $this->restoreTable('cycle', $payload['cycle'] ?? [], $userId, $restored); $this->restoreTable('cycle_log', $payload['cycle_log'] ?? [], $userId, $restored); $this->restoreTable('mood_log', $payload['mood_log'] ?? [], $userId, $restored); $this->restoreTable('bbt_record', $payload['bbt_record'] ?? [], $userId, $restored); $this->restoreTable('task', $payload['task'] ?? [], $userId, $restored); $this->restoreTable('memory', $payload['memory'] ?? [], $userId, $restored); $this->success('恢复完成', ['restored' => $restored]); } protected function collectData($userId) { $data = [ 'cycle' => \think\Db::name('cycle')->where('user_id', $userId)->whereNull('deletetime')->select(), 'cycle_log' => \think\Db::name('cycle_log')->where('user_id', $userId)->whereNull('deletetime')->select(), 'mood_log' => \think\Db::name('mood_log')->where('user_id', $userId)->whereNull('deletetime')->select(), 'bbt_record' => \think\Db::name('bbt_record')->where('user_id', $userId)->select(), 'task' => \think\Db::name('task')->where('user_id', $userId)->whereNull('deletetime')->select(), 'memory' => \think\Db::name('memory')->where('user_id', $userId)->whereNull('deletetime')->select(), 'reminder' => \think\Db::name('reminder')->where('user_id', $userId)->whereNull('deletetime')->select(), 'setting' => \think\Db::name('user_setting')->where('user_id', $userId)->find(), ]; $data['counts'] = [ 'cycle' => count($data['cycle']), 'cycle_log' => count($data['cycle_log']), 'mood_log' => count($data['mood_log']), 'bbt' => count($data['bbt_record']), 'task' => count($data['task']), 'memory' => count($data['memory']), ]; $data['version'] = 'v1'; $data['created'] = date('Y-m-d H:i:s'); return $data; } protected function restoreTable($table, $rows, $userId, &$restored) { if (!is_array($rows)) { return; } \think\Db::name($table)->where('user_id', $userId)->delete(); foreach ($rows as $row) { unset($row['id']); $row['user_id'] = $userId; \think\Db::name($table)->insert($row); } $restored[$table] = count($rows); } protected function formatReminder($row) { return [ 'id' => (int)$row['id'], 'title' => $row['title'], 'type' => $row['type'], 'remind_time' => $row['remind_time'], 'repeat_rule' => $row['repeat_rule'], 'remind_date' => $row['remind_date'], 'enabled' => (int)$row['enabled'], ]; } }